Files
Miguel Ángel 9549e038c4 perf(studio): a paused editor stops redrawing its overlays sixty times a second (#3846)
* perf(studio): run the editor overlays on one parkable frame loop

The composition rect, the selection and hover boxes, the off-canvas indicators
and the snap guides each owned an animation-frame loop that re-armed
unconditionally. On a paused, untouched editor that is four callbacks per frame
reading layout, and the snap-guide loop writes style on every one of them, so
the compositor kept committing 55 frames a second with nothing moving.

None of the four has a clock of its own; each only changes when something
observable happens. They now share one loop that runs at full rate for a moment
after a pointer, key, wheel, scroll, resize or visibility change, after a
preview message reporting a frame the overlays have not drawn, or after a
mutation inside the preview document, and otherwise polls four times a second
so a wake source nobody thought of costs a quarter second of staleness rather
than a frozen overlay.

The frame comparison on preview messages is load-bearing rather than an
optimisation: the paused preview posts an unchanged status message every 80 ms
so any listener can confirm its position, and treating that as news held the
overlays at 60 fps for the life of the tab.

* fix(studio): one overlay's throw must not stop the other four

The shared frame loop re-armed after running its subscribers, so a subscriber
that threw took down the loop and its idle-poll safety net permanently. The
four loops it replaced each re-armed first, which kept that failure to the one
overlay that caused it. It now re-arms before running anything and isolates
each subscriber, rethrowing out of band so the error still reaches the page's
error reporting.

The wake also sat outside the recognised-message check, so postMessage traffic
from an extension, devtools or any other embed on the page held the overlays
awake for 400ms at a time. Only the preview's own messages wake it now, and a
repeated paused status post still does not.

useMotionPathData had the same unconditional loop and is live whenever a
keyframed element is selected; it joins the shared one.

* fix(studio): wake the overlays only for the preview
2026-09-10 18:20:37 +00:00
..