fix: RAF-batch canvas ResizeObserver to stop link overlay redraw lag#12300
fix: RAF-batch canvas ResizeObserver to stop link overlay redraw lag#12300christian-byrne wants to merge 1 commit into
Conversation
The canvas-element ResizeObserver runs resizeCanvas() (which does canvas.width = NaN, getBoundingClientRect, and a full draw(true, true)) on every fire. During an animated bottom-panel splitter open or window drag, the RO fires many times per second, producing visible link overlay redraw lag because the link overlay canvas re-syncs its size via useRafFn the frame after each main canvas resize. Wrap the callback in createRafBatch() so at most one resize+draw runs per animation frame. Multiple RO fires within the same frame coalesce into a single draw using the latest container dimensions.
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1608 passed, 0 failed📊 Browser Reports
|
📝 WalkthroughWalkthroughThe PR refactors canvas resize handling in the app by batching ResizeObserver callbacks per animation frame using a new ChangesCanvas Resize Optimization
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error)
✅ Passed checks (6 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
📦 Bundle: 5.36 MB gzip 🟢 -29 BDetailsSummary
Category Glance App Entry Points — 26.1 kB (baseline 26.1 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.24 MB (baseline 1.24 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 82.9 kB (baseline 82.9 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 527 kB (baseline 527 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 14 unchanged User & Accounts — 17.8 kB (baseline 17.8 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 112 kB (baseline 112 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed UI Components — 58 kB (baseline 58 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 3.16 MB (baseline 3.16 MB) • 🔴 +147 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 4 unchanged Utilities & Hooks — 366 kB (baseline 366 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 13 added / 13 removed / 18 unchanged Vendor & Third-Party — 9.94 MB (baseline 9.94 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 9.16 MB (baseline 9.16 MB) • ⚪ 0 BBundles that do not match a named category
Status: 57 added / 57 removed / 86 unchanged ⚡ Performance Report
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-05-15T21:32:12.224Z",
"gitSha": "0e4b0b6993f112fa2419f18e5f40c2f891ede4b5",
"branch": "glary/raf-batch-canvas-resize-observer",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2046.811000000048,
"styleRecalcs": 8,
"styleRecalcDurationMs": 9.142000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 454.46799999999996,
"heapDeltaBytes": 18918904,
"heapUsedBytes": 68970516,
"domNodes": -264,
"jsHeapTotalBytes": 16474112,
"scriptDurationMs": 18.631999999999994,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2038.0049999999983,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.552999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 443.269,
"heapDeltaBytes": -2331992,
"heapUsedBytes": 50323760,
"domNodes": 20,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 28.04500000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1933.2630000000108,
"styleRecalcs": 77,
"styleRecalcDurationMs": 46.404999999999994,
"layouts": 12,
"layoutDurationMs": 4.394,
"taskDurationMs": 965.982,
"heapDeltaBytes": 13995784,
"heapUsedBytes": 62897604,
"domNodes": -265,
"jsHeapTotalBytes": 21622784,
"scriptDurationMs": 142.467,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1848.4940000000734,
"styleRecalcs": 72,
"styleRecalcDurationMs": 42.185,
"layouts": 12,
"layoutDurationMs": 3.783,
"taskDurationMs": 816.691,
"heapDeltaBytes": -3570284,
"heapUsedBytes": 62722952,
"domNodes": 55,
"jsHeapTotalBytes": 20803584,
"scriptDurationMs": 133.71099999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1728.4569999999349,
"styleRecalcs": 32,
"styleRecalcDurationMs": 20.484999999999996,
"layouts": 6,
"layoutDurationMs": 0.7679999999999998,
"taskDurationMs": 332.753,
"heapDeltaBytes": 850436,
"heapUsedBytes": 50043704,
"domNodes": 80,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 27.937000000000005,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1760.458999999969,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.936000000000003,
"layouts": 6,
"layoutDurationMs": 0.813,
"taskDurationMs": 360.568,
"heapDeltaBytes": 23510264,
"heapUsedBytes": 74255424,
"domNodes": 76,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 21.609,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 628.3239999999068,
"styleRecalcs": 13,
"styleRecalcDurationMs": 11.693999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 418.543,
"heapDeltaBytes": 15175660,
"heapUsedBytes": 65027116,
"domNodes": 22,
"jsHeapTotalBytes": 17825792,
"scriptDurationMs": 79.87599999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 594.3180000000439,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.451,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 371.522,
"heapDeltaBytes": -4465140,
"heapUsedBytes": 63855320,
"domNodes": 20,
"jsHeapTotalBytes": 21233664,
"scriptDurationMs": 66.372,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2029.4169999999667,
"styleRecalcs": 7,
"styleRecalcDurationMs": 7.887999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 610.334,
"heapDeltaBytes": 8295360,
"heapUsedBytes": 66844372,
"domNodes": -267,
"jsHeapTotalBytes": 290816,
"scriptDurationMs": 116.176,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-idle",
"durationMs": 2062.575000000038,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.891,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 667.3459999999999,
"heapDeltaBytes": 43115424,
"heapUsedBytes": 102086180,
"domNodes": -263,
"jsHeapTotalBytes": 32796672,
"scriptDurationMs": 116.29799999999999,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2211.2059999999474,
"styleRecalcs": 68,
"styleRecalcDurationMs": 18.664000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1313.382,
"heapDeltaBytes": 42118532,
"heapUsedBytes": 102481196,
"domNodes": -271,
"jsHeapTotalBytes": 41971712,
"scriptDurationMs": 453.463,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2246.846000000005,
"styleRecalcs": 68,
"styleRecalcDurationMs": 18.698999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1274.2870000000003,
"heapDeltaBytes": 33685068,
"heapUsedBytes": 95523972,
"domNodes": -265,
"jsHeapTotalBytes": 39407616,
"scriptDurationMs": 431.131,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3196.5959999999995,
"styleRecalcs": 66,
"styleRecalcDurationMs": 20.876999999999995,
"layouts": 60,
"layoutDurationMs": 8.248,
"taskDurationMs": 1461.3899999999999,
"heapDeltaBytes": 12399528,
"heapUsedBytes": 73400400,
"domNodes": -268,
"jsHeapTotalBytes": 290816,
"scriptDurationMs": 551.415,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3250.1060000000734,
"styleRecalcs": 65,
"styleRecalcDurationMs": 19.724,
"layouts": 60,
"layoutDurationMs": 8.484,
"taskDurationMs": 1557.33,
"heapDeltaBytes": 52421132,
"heapUsedBytes": 113771008,
"domNodes": -268,
"jsHeapTotalBytes": 36466688,
"scriptDurationMs": 571.9889999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2067.4209999999675,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.687,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 670.7259999999999,
"heapDeltaBytes": 40096464,
"heapUsedBytes": 101438792,
"domNodes": -263,
"jsHeapTotalBytes": 33320960,
"scriptDurationMs": 115.427,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2027.7690000000348,
"styleRecalcs": 7,
"styleRecalcDurationMs": 7.7669999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 583.0600000000002,
"heapDeltaBytes": -1948688,
"heapUsedBytes": 57739884,
"domNodes": -268,
"jsHeapTotalBytes": 4222976,
"scriptDurationMs": 106.02600000000001,
"eventListeners": -157,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 564.583999999968,
"styleRecalcs": 48,
"styleRecalcDurationMs": 13.333999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 391.924,
"heapDeltaBytes": 15190292,
"heapUsedBytes": 65314752,
"domNodes": 21,
"jsHeapTotalBytes": 17825792,
"scriptDurationMs": 131.464,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 556.697999999983,
"styleRecalcs": 48,
"styleRecalcDurationMs": 12.331000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 375.105,
"heapDeltaBytes": 7511432,
"heapUsedBytes": 60393188,
"domNodes": 22,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 122.717,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2039.0810000000101,
"styleRecalcs": 10,
"styleRecalcDurationMs": 11.039,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 499.065,
"heapDeltaBytes": 20393536,
"heapUsedBytes": 69261548,
"domNodes": -260,
"jsHeapTotalBytes": 19263488,
"scriptDurationMs": 23.516999999999996,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2046.6679999999542,
"styleRecalcs": 9,
"styleRecalcDurationMs": 10.039000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 472.639,
"heapDeltaBytes": 19843024,
"heapUsedBytes": 68737072,
"domNodes": -262,
"jsHeapTotalBytes": 19787776,
"scriptDurationMs": 15.601,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1714.7029999999859,
"styleRecalcs": 76,
"styleRecalcDurationMs": 44.795,
"layouts": 16,
"layoutDurationMs": 5.041,
"taskDurationMs": 753.689,
"heapDeltaBytes": 15225208,
"heapUsedBytes": 64821312,
"domNodes": 63,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 109.07799999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 2014.8110000000088,
"styleRecalcs": 86,
"styleRecalcDurationMs": 56.799,
"layouts": 16,
"layoutDurationMs": 5.3309999999999995,
"taskDurationMs": 1013.514,
"heapDeltaBytes": -7078636,
"heapUsedBytes": 59405636,
"domNodes": 71,
"jsHeapTotalBytes": 20017152,
"scriptDurationMs": 110.25700000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1016.0809999999856,
"styleRecalcs": 15,
"styleRecalcDurationMs": 27.997000000000007,
"layouts": 4,
"layoutDurationMs": 12.850999999999997,
"taskDurationMs": 753.3090000000002,
"heapDeltaBytes": 29740824,
"heapUsedBytes": 95041032,
"domNodes": 13513,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 32.622000000000014,
"eventListeners": 2527,
"totalBlockingTimeMs": 154,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8549.244999999928,
"styleRecalcs": 250,
"styleRecalcDurationMs": 58.849999999999994,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4522.292,
"heapDeltaBytes": 79834836,
"heapUsedBytes": 140088664,
"domNodes": -264,
"jsHeapTotalBytes": 64487424,
"scriptDurationMs": 1440.434,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8204.911000000038,
"styleRecalcs": 249,
"styleRecalcDurationMs": 56.757000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4087.885999999999,
"heapDeltaBytes": 14630296,
"heapUsedBytes": 73250052,
"domNodes": -263,
"jsHeapTotalBytes": 1282048,
"scriptDurationMs": 1381.464,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 16578.474000000027,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16547.034,
"heapDeltaBytes": 9664012,
"heapUsedBytes": 266536920,
"domNodes": -8331,
"jsHeapTotalBytes": 4517888,
"scriptDurationMs": 770.923,
"eventListeners": -16490,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 13669.700000000033,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13614.712999999998,
"heapDeltaBytes": -14318716,
"heapUsedBytes": 190765888,
"domNodes": -8331,
"jsHeapTotalBytes": 21819392,
"scriptDurationMs": 752.655,
"eventListeners": -16461,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 15505.609999999933,
"styleRecalcs": 77,
"styleRecalcDurationMs": 20.281999999999968,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15479.907999999998,
"heapDeltaBytes": -39301564,
"heapUsedBytes": 161166532,
"domNodes": -8331,
"jsHeapTotalBytes": 13168640,
"scriptDurationMs": 1030.859,
"eventListeners": -16460,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 15649.185999999987,
"styleRecalcs": 76,
"styleRecalcDurationMs": 20.19700000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15616.107,
"heapDeltaBytes": -37047044,
"heapUsedBytes": 177584360,
"domNodes": -8331,
"jsHeapTotalBytes": -18112512,
"scriptDurationMs": 1064.484,
"eventListeners": -16456,
"totalBlockingTimeMs": 67,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 450.8489999999483,
"styleRecalcs": 16,
"styleRecalcDurationMs": 22.502,
"layouts": 4,
"layoutDurationMs": 1.2400000000000002,
"taskDurationMs": 123.20900000000002,
"heapDeltaBytes": 5303200,
"heapUsedBytes": 57066812,
"domNodes": 166,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 25.741,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 463.89600000020437,
"styleRecalcs": 16,
"styleRecalcDurationMs": 25.846999999999998,
"layouts": 5,
"layoutDurationMs": 1.515,
"taskDurationMs": 126.49700000000003,
"heapDeltaBytes": 5045300,
"heapUsedBytes": 55157896,
"domNodes": 153,
"jsHeapTotalBytes": 524288,
"scriptDurationMs": 26.486000000000004,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
}
]
} |
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #12300 +/- ##
===========================================
- Coverage 74.67% 59.55% -15.12%
===========================================
Files 1526 1412 -114
Lines 95359 71881 -23478
Branches 27134 19026 -8108
===========================================
- Hits 71212 42811 -28401
- Misses 23285 28597 +5312
+ Partials 862 473 -389
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1022 files with indirect coverage changes 🚀 New features to boost your workflow:
|
PR Created by the Glary-Bot Agent
Summary
The canvas-element
useResizeObserverinapp.tscallsresizeCanvas()(which doescanvas.width = NaN,getBoundingClientRect, and a fulldraw(true, true)) on every fire with no batching. During an animated bottom-panel splitter open or window drag, the RO fires many times per second, producing visible link-overlay redraw lag — the link overlay canvas re-syncs its dimensions on the next RAF after each main canvas resize (viauseRafFninLinkOverlayCanvas.vue), so each extra resize buys an extra frame of misalignment.Change
Wrap the callback in
createRafBatch(...)from@/utils/rafBatch. Multiple RO fires within the same animation frame coalesce into a singleresizeCanvas()call using the canvas's current container dimensions at flush time.Verification
pnpm typecheckclean (pre-commit)oxfmtcleanapp.ts(existing convention)No unit test added —
app.tsis a monolithic singleton without test infrastructure, and the change is a mechanical one-line wrap around an existing pattern already used heavily elsewhere in the codebase (useSlotElementTracking, the sibling node tracking, etc.).Note
This is one of three planned PRs that work together to fix the bottom-panel/node-displacement bug. The other two: (1) RAF-batch the per-node Vue ResizeObserver writes (#12299), and (3) an explicit
bottomPanelVisiblewatcher as belt-and-braces for the splitter transition (separate PR).┆Issue is synchronized with this Notion page by Unito