d109785bb3
Desktop shipped the same bug class four times in one week: a decorative animation loop that never sleeps (bots face clock #88543, pixel egg #88406, diffusion placeholder #88564, plus the #77651 hidden-renderer wave). Each fix hand-rolled the same four behaviors. This extracts them into one helper in src/lib/budgeted-loop.ts: - fps budget (default 15) on top of rAF - observability pause via the existing createRendererLoopPauseController - idle dormancy: idleWhen() true after a draw parks the loop with zero pending work until wake() — the piece every hand-rolled loop forgot - teardown: dispose() cancels the frame, disposes the controller, and makes wake() a no-op DiffusionCanvas migrates onto it as the first consumer (net -40 lines at the call site); its existing scheduling/budget/instance-cap tests pass unchanged against the migrated implementation. Helper suite covers budget, pause, park/wake, dormancy-survives-focus-churn, and dispose idempotency; sabotage run (budget+dormancy stripped) fails 4/5.