diff --git a/docs/architecture/05-rendering-theming-preview.md b/docs/architecture/05-rendering-theming-preview.md index 70f134d..da16082 100644 --- a/docs/architecture/05-rendering-theming-preview.md +++ b/docs/architecture/05-rendering-theming-preview.md @@ -384,49 +384,23 @@ export interface DebouncedRenderer { cancel(): void; } -export function createDebouncedRenderer(opts: { - /** Current debounce delay in ms; read fresh each schedule so settings apply live. */ - delayMs: () => number; - /** Performs one render. Reads the current spec/theme; awaits the embed. */ - render: () => Promise; - /** Toggle the non-blocking busy indicator. */ - setBusy: (busy: boolean) => void; -}): DebouncedRenderer { - let timer: ReturnType | null = null; - let generation = 0; // guards against a stale in-flight render finishing late - +// The factory wires the three operations over one timer. schedule() does +// `setTimeout(run, delayMs())`, clearing any pending timer first (delayMs read fresh +// so a settings change applies live); flush() clears the timer and runs now; cancel() +// clears it and bumps `generation`. The non-obvious part is out-of-order protection: +function createDebouncedRenderer(opts): DebouncedRenderer { + let generation = 0; const run = async () => { - timer = null; - const mine = ++generation; + const mine = ++generation; // capture this render's turn opts.setBusy(true); try { await opts.render(); } finally { - // Only the most recent render clears the indicator. - if (mine === generation) opts.setBusy(false); + if (mine === generation) opts.setBusy(false); // only the latest render clears it } }; - - return { - schedule() { - if (timer) clearTimeout(timer); // cancel the pending render - timer = setTimeout(run, opts.delayMs()); - }, - flush() { - if (timer) { - clearTimeout(timer); - timer = null; - } - void run(); - }, - cancel() { - if (timer) { - clearTimeout(timer); - timer = null; - } - generation++; // abandon any in-flight result - }, - }; + // …schedule/flush/cancel as above. A slow render that resolves after a newer one + // fails the `mine === generation` check, so it can't clobber the fresh view/indicator. } ```