diff --git a/services/web/frontend/js/features/pdf-preview/util/compiler.ts b/services/web/frontend/js/features/pdf-preview/util/compiler.ts index d4695558e9..bd9eef3a40 100644 --- a/services/web/frontend/js/features/pdf-preview/util/compiler.ts +++ b/services/web/frontend/js/features/pdf-preview/util/compiler.ts @@ -260,4 +260,15 @@ export default class DocumentCompiler { ) { this.defaultOptions[option] = value } + + updateAutoCompileDebounce(debounceMs: number, maxWaitMs: number) { + this.debouncedAutoCompile.cancel() + this.debouncedAutoCompile = debounce( + () => { + this.compile({ isAutoCompileOnChange: true }) + }, + debounceMs, + { maxWait: maxWaitMs } + ) + } } diff --git a/services/web/frontend/js/shared/context/local-compile-context.tsx b/services/web/frontend/js/shared/context/local-compile-context.tsx index 8e2ed0242e..2ef1f08153 100644 --- a/services/web/frontend/js/shared/context/local-compile-context.tsx +++ b/services/web/frontend/js/shared/context/local-compile-context.tsx @@ -155,11 +155,6 @@ export const LocalCompileProvider: FC = ({ const { findEntityByPath } = useFileTreePathContext() const getRootDocInfo = useRootDoc() - // Captured once at mount; drives auto-compile defaults and debounce timing. - const [isTypstProject] = useState(() => - getRootDocInfo().rootResourcePath.endsWith('.typ') - ) - // whether a compile is in progress const [compiling, setCompiling] = useState(false) @@ -264,10 +259,10 @@ export const LocalCompileProvider: FC = ({ const [position, setPosition] = usePdfScrollPosition(lastCompileRootDocId) - // whether autocompile is switched on (Typst projects default to enabled) + // whether autocompile is switched on const [autoCompile, setAutoCompile] = usePersistedState( `autocompile_enabled:${projectId}`, - isTypstProject, + false, { listen: true } ) @@ -340,10 +335,6 @@ export const LocalCompileProvider: FC = ({ signal, openDocs, getRootDocInfo, - // Typst compiles are near-instant via `typst watch`; use a much tighter - // debounce so the PDF refreshes as the user types. - autoCompileDebounce: isTypstProject ? 300 : undefined, - autoCompileMaxWait: isTypstProject ? 1000 : undefined, }) }) @@ -352,6 +343,22 @@ export const LocalCompileProvider: FC = ({ compiler.getRootDocInfo = getRootDocInfo }, [compiler, getRootDocInfo]) + // Typst projects compile near-instantly via `typst watch`. + // getRootDocInfo() returns accurate paths only after the file tree loads + // (project joins via WebSocket), so we do detection in an effect that re-runs + // when its reference changes. window.localStorage is checked directly to + // distinguish "no stored preference" from an explicit user choice. + useEffect(() => { + const { rootResourcePath } = getRootDocInfo() + if (!rootResourcePath.endsWith('.typ')) return + // Default auto-compile ON for Typst — only if user has never set a pref. + if (window.localStorage.getItem(`autocompile_enabled:${projectId}`) === null) { + setAutoCompile(true) + } + // Tighten the debounce so the PDF updates as the user types. + compiler.updateAutoCompileDebounce(300, 1000) + }, [compiler, getRootDocInfo, projectId, setAutoCompile]) + // keep draft setting in sync with the compiler useEffect(() => { compiler.setOption('draft', draft)