Uh oh!
There was an error while loading. Please reload this page.
perf(workflow): scope resize CSS-var writes to the container, not :root - #5738
Conversation
Resizing the editor panel, terminal, output panel, and sidebar was still laggy on large workflows even after the drag handles stopped re-rendering React per frame. A CDP trace showed the cost was style recalculation, not JS or layout: ~3.9s of Document::recalcStyle over a 50-move panel drag. Root cause: each drag frame wrote its CSS variable to document.documentElement. On a large document (~42k elements) any inline custom-property write on the <html> root recalculates style for the whole tree — measured at ~77ms per write, independent of what actually reads the variable (an unused var costs the same). Writing the same variable to the element that consumes it recalcs only that subtree (~0.5ms) — a ~150x reduction. useDragResize now writes the variable to a caller-provided target element during the drag and reconciles to :root once on release (so on-demand readers and the pre-hydration script are unchanged): panel -> .panel-container, terminal -> .terminal-container, output panel -> .terminal-container (both consumers inherit it), sidebar -> .sidebar-shell-outer. The terminal's expanded-threshold sync writes store state directly instead of setTerminalHeight so it no longer touches :root mid-drag. Measured (near-empty canvas, 50-move drag): panel style+layout 3891ms -> 56ms, frame p95 91.6ms -> 8.4ms, main-thread blocking 4566ms -> 0ms; terminal recalc 3899ms -> 16ms, blocking 5558ms -> 0ms; sidebar recalc ~77ms/frame -> ~1ms/frame.
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview
Panel, terminal, output panel, and sidebar hooks are wired to
Reviewed by Cursor Bugbot for commit 4a8b2b9. Configure here. |
Uh oh!
There was an error while loading. Please reload this page.
Greptile SummaryThis PR reduces workflow-canvas resize costs by scoping live CSS-variable updates to their consuming containers. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (5): Last reviewed commit: "fix(workflow): sidebar flick-safety + cl..." | Re-trigger Greptile |
Uh oh!
There was an error while loading. Please reload this page.
The resize hooks now write their CSS variable to the consuming container element during a drag (not :root), so use-float-boundary-sync's MutationObserver on :root no longer fired mid-drag and open floats (chat, search-replace, variables) only re-clamped on release. Read each boundary dimension from its scoped element with a :root fallback, and observe the container elements as well as :root, so an open float tracks the drag live exactly as before.
waleedlatif1
commented
Jul 17, 2026
Added a second commit: the resize hooks now write the CSS var to the consuming container mid-drag, so |
waleedlatif1
commented
Jul 17, 2026
waleedlatif1
commented
Jul 17, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
…idth - useDragResize: unmounting mid-drag now runs endDrag (commit + drop the scoped override) instead of a bare cleanup, so navigating away can neither lose the resize nor strand an inline override on a surviving target element. - terminal output-panel clamp: read the live --output-panel-width from the terminal element first (where a drag writes its scoped override), then :root, then the store, so a mid-drag terminal/window resize can't clamp against a stale value.
waleedlatif1
commented
Jul 17, 2026
waleedlatif1
commented
Jul 17, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ount, clamp skip Addresses three review findings on the scoped-resize change: - useDragResize: track the last applied value and commit that on release; only recompute from the pointer event while the target is still connected. On an unmount the target's rect can be detached, so a layout-reading compute (e.g. the output panel's) would return a degenerate MIN — committing the last shown value avoids persisting the wrong width, and keeps flick-safety on a normal release. - use-sidebar-resize: finalize on unmount (run endDrag, not bare cleanup) so a drag interrupted by unmount persists the width and drops the scoped override. This matters more than for the panel/terminal because .sidebar-shell-outer lives in the workspace chrome and outlives the sidebar, so a stranded override would win over :root. - terminal output-panel clamp: skip while an output-panel drag is active (its scoped inline override is present). That drag's own compute clamps every frame against the live terminal rect, and a store-driven :root write here would be masked by the inline override anyway.
waleedlatif1
commented
Jul 17, 2026
waleedlatif1
commented
Jul 17, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- use-sidebar-resize: compute the clamped width synchronously on each pointer move (storing lastWidth) and defer only the DOM write to rAF, so a fast flick released before the frame runs still commits the final pointer position instead of a stale frame or nothing. - terminal output-panel clamp: when not mid-drag, read the committed --output-panel-width from :root (written synchronously by the store setter) rather than the React store value, which lags a render behind the commit; fall back to the store before any commit. Comment corrected to match.
waleedlatif1
commented
Jul 17, 2026
waleedlatif1
commented
Jul 17, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4a8b2b9. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Summary
Document::recalcStyleover a 50-move panel drag.document.documentElement. On a large document (~42k elements) any inline custom-property write on the<html>root recalculates style for the whole tree — measured at ~77ms per write, independent of what reads the variable (an unused--zzzvar costs the same 77ms). Writing the same variable to the element that consumes it recalcs only that subtree (~0.5ms — a ~150x reduction).useDragResizenow writes the variable to a caller-provided target element during the drag and reconciles to:rootonce on release (so on-demand readers, the pre-hydration blocking script, and rehydration are all unchanged). panel →.panel-container, terminal →.terminal-container, output panel →.terminal-container(both the panel and logs column inherit it), sidebar →.sidebar-shell-outer.setTerminalHeightso it no longer touches:rootmid-drag (that was a ~77ms hitch right at the collapse threshold).Measured (near-empty canvas, 50-move drag, CDP trace + rAF frame timing)
Frames drop from ~11fps (91ms) to a solid 120fps (8.3ms) with zero long-tasks.
Type of Change
Testing
Verified with Playwright + CDP against a live dev workflow: all four handles engage, drive their element's width/height live during the drag (scoped inline var), commit the final value to
:root+ the store on release, remove the scoped override (reconciled), and persist. Before/after CPU profiles and traces captured for each handle.Checklist