⚡ trigger-performance

Pathological DOM patterns for PixieBrix's jQueryInitialize selector watcher — the shared MutationObserver behind both trigger and button starter bricks — reworked in the 3.2.6 milestone.

Why this exists. 3.2.6 shipped a series of performance overhauls to the watcher. A customer reported the changes made some pages crash. These pages reproduce the cases the work targeted, so each can be observed — and the old vs new watcher behavior compared — on the same DOM.

Two ways to use each page:
1. With the extension — install a real trigger/button mod against the listed selector and load the page.
2. Without it — use the built-in harness panel (top-right). It runs the real, vendored jQueryInitialize (bundled from pixiebrix-source; see source/trigger-performance/vendor) and reports FPS, long-tasks/sec, max task time, and callback matches. Switch the watcher mode to compare the actual code paths:
  off baseline · legacy real pre-3.2.6 · optimized real 3.2.6.

ScenarioPathological patternTarget selectorPR / tier
mutation-storm High-rate attribute thrash + node add/remove; background-tab scanning .storm-item #8170 T1
throttle, visibility
route-swap SPA route change replacing 500+ siblings at once; ancestor-gated masking targets body.route-active .pii #8170 burst
#8202 T1S re-sweep
infinite-scroll-burst Large burst appends on a timer; unbounded growth → memory pressure .feed-row #8170 T1
rIC burst flush
many-selectors Hundreds–thousands of watched selectors / observers at once (O(N²) legacy) .sel-0.sel-N #8207 T2
token index
attribute-cascade Insert then mutate value/aria-*/data-* + id/class [data-testid="row"], .loaded .field #8207 T2
attributeFilter
detached-subtree Add-then-remove subtree in one task (React portal / failed diff) .portal-content #8170 T1
isConnected skip
deep-dom Very large / deeply nested DOM; expensive full-document scans .wrapper .deep-target scan cost
(optional)

Adversarial cases — optimized still degrades

Pages that are smooth on their own and run fine with the right watcher, but land in a blind spot of the 3.2.6 optimizations — so optimized stays slow (or is worse than legacy). Each isolates one recent PR's limit. Useful as regression signals, not just legacy-vs-new contrasts.

ScenarioPathological patternTarget selectorDefeats
has-sweep Large static grid + light class toggles; :has() selector forces a full-document Sizzle scan every throttle window .card:has(.badge.active) #8207 token index
(complex fallback)
history-thrash Static DOM, zero mutations; unique URL per frame (scroll/filter sync) → unthrottled full-document sweep per frame. Worse than legacy .route-active .pii #8202 T1S
route re-sweep
attr-escalation Legitimate per-frame style churn; one co-installed [data-state] mod escalates the shared observer to unfiltered attributes:true for the whole page .benign-item + [data-state="open"] #8207 T2
attributeFilter

React ports

The same patterns driven by real React / react-router-dom, so the watcher runs against genuine React commits — the way these cases occur in customer apps.

ScenarioWhat React doesTarget selectorPR / tier
react / render-storm Rapid setState → reconciliation churn (remount or in-place patch) .storm-item #8170 T1
react / route-swap react-router navigation mounts/unmounts a large route subtree in one commit .route-active .pii #8170 burst
#8202 re-sweep
react / infinite-scroll Growing list reconciled on each burst; unbounded with keep=0 .feed-row #8170 rIC flush
react / portal-churn createPortal subtree mounted/unmounted rapidly (modal/tooltip) .portal-content #8170 add/remove

React ports — adversarial cases

The "optimized still degrades" cases above, driven through real React commits (memoized static trees, ref-based animation, setState reconciliation) — the way these patterns occur in customer apps.

ScenarioWhat React doesTarget selectorDefeats
react / has-sweep setState toggles .active on a large grid; :has() selector forces a full-document Sizzle scan per window .card:has(.badge.active) #8207 token index
(complex fallback)
react / history-thrash Memoized static tree; URL synced per frame → unthrottled full-document sweep. Worse than legacy .route-active .pii #8202 T1S
route re-sweep
react / attr-escalation Ref-based per-frame style animation; one [data-state] mod escalates the shared observer to attributes:true .benign-item + [data-state="open"] #8207 T2
attributeFilter

Every knob lives in the URL — a crashing configuration is a shareable link, e.g. mutation-storm.html?harness=legacy&rate=2000&nodes=20000&autostart=1.