From 8069541cd985cb5876417df6020bc4b7d0988a01 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Fri, 7 Aug 2026 19:26:57 +0530 Subject: [PATCH] refactor(csr): one document observer with subscribers Overlay clamping, dialog focus, roving focus and scrollspy each ran their own MutationObserver over the same stream of records. They now share one, with the per-feature work registered as subscribers. Every subscriber already defers, so the extra callbacks are cheap and the bookkeeping is paid for once. The attribute filter stays explicit rather than observing everything: an unfiltered observer would see the tabindex the roving code writes and loop on its own output. This is better structured but it is not a fix for the size budget -- it buys 82 bytes of headroom, not room to grow. Splitting the runtime so a page pays only for the behaviour it uses is still the outstanding decision. Co-Authored-By: Claude Opus 5 --- packages/csr/src/reactive-runtime.ts | 75 +++++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/packages/csr/src/reactive-runtime.ts b/packages/csr/src/reactive-runtime.ts index 0dcdd0e6..ebb99d25 100644 --- a/packages/csr/src/reactive-runtime.ts +++ b/packages/csr/src/reactive-runtime.ts @@ -2886,6 +2886,32 @@ export const REACTIVE_RUNTIME = String.raw` }); } + /* + * One document observer, several subscribers. The filter stays explicit: + * observing every attribute would see the tabindex the roving code writes + * and loop on its own output. + */ + var documentWatchers = []; + + function watchDocument(handler) { + documentWatchers.push(handler); + } + + function startDocumentWatch() { + if (window.__wrnexusDocWatchBound || typeof MutationObserver !== "function") return; + window.__wrnexusDocWatchBound = true; + new MutationObserver(function () { + for (var index = 0; index < documentWatchers.length; index += 1) documentWatchers[index](); + }).observe(document.documentElement, { + subtree: true, + childList: true, + attributes: true, + // prettier-ignore + attributeFilter: ["data-open","data-show","class","style","hidden", + "data-placement","aria-selected","aria-current","disabled","aria-disabled"], + }); + } + function setupAnchoredOverlays() { if (window.__wrnexusAnchoredBound) return; window.__wrnexusAnchoredBound = true; @@ -2894,14 +2920,7 @@ export const REACTIVE_RUNTIME = String.raw` // root, data-show on the panel, a class), so watch for any attribute or // structural change and re-measure on the next frame instead of trying to // enumerate every signal. - if (typeof MutationObserver === "function") { - new MutationObserver(scheduleAnchoredReposition).observe(document.documentElement, { - subtree: true, - childList: true, - attributes: true, - attributeFilter: ["data-open", "data-show", "class", "data-placement"], - }); - } + watchDocument(scheduleAnchoredReposition); window.addEventListener("resize", scheduleAnchoredReposition); window.addEventListener("scroll", scheduleAnchoredReposition, true); @@ -3027,19 +3046,11 @@ export const REACTIVE_RUNTIME = String.raw` if (window.__wrnexusDialogsBound) return; window.__wrnexusDialogsBound = true; - if (typeof MutationObserver === "function") { - new MutationObserver(function () { - // Same deferral as the anchored clamp: the mutation that opens a - // dialog is the one that makes it visible, so it still measures as - // hidden until the panel has been laid out. - window.setTimeout(syncDialogs, 0); - }).observe(document.documentElement, { - subtree: true, - childList: true, - attributes: true, - attributeFilter: ["data-open", "data-show", "class", "style", "hidden"], - }); - } + // Deferred: the mutation that opens a dialog is the one that makes it + // visible, so it is not laid out yet when the record arrives. + watchDocument(function () { + window.setTimeout(syncDialogs, 0); + }); document.addEventListener("keydown", trapDialogTab, true); syncDialogs(); @@ -3157,16 +3168,9 @@ export const REACTIVE_RUNTIME = String.raw` document.addEventListener("keydown", handleRovingKeydown, true); - if (typeof MutationObserver === "function") { - new MutationObserver(function () { - window.setTimeout(syncRovingGroups, 0); - }).observe(document.documentElement, { - subtree: true, - childList: true, - attributes: true, - attributeFilter: ["aria-selected", "aria-current", "disabled", "aria-disabled", "hidden"], - }); - } + watchDocument(function () { + window.setTimeout(syncRovingGroups, 0); + }); syncRovingGroups(); } @@ -3240,11 +3244,9 @@ export const REACTIVE_RUNTIME = String.raw` for (var index = 0; index < navs.length; index += 1) wireScrollspy(navs[index]); }; wireAll(); - if (typeof MutationObserver === "function") { - new MutationObserver(function () { - window.setTimeout(wireAll, 0); - }).observe(document.documentElement, { subtree: true, childList: true }); - } + watchDocument(function () { + window.setTimeout(wireAll, 0); + }); } function hydrateScopes(root) { @@ -5466,6 +5468,7 @@ export const REACTIVE_RUNTIME = String.raw` setupModalDialogs(); setupRovingFocus(); setupScrollspy(); + startDocumentWatch(); window.__wrnexusRepositionAnchored = repositionAnchored; window.__wrnexusHydrateScopes = hydrateScopes; window.__wrnexusInvalidateClientModule = function (url) { clientModuleCache.delete(url); };