feat: add bottom docking area for run button (FE-1122)#13304
feat: add bottom docking area for run button (FE-1122)#13304WhatDreamsCost wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThe action bar now uses a three-value dock state: ChangesActionbar Bottom Dock Support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎭 Playwright: ✅ 1691 passed, 0 failed · 1 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 7.77 MB gzip 🔴 +482 BDetailsSummary
Category Glance App Entry Points — 47.3 kB (baseline 47.3 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.25 MB (baseline 1.25 MB) • 🔴 +2.56 kBGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 97.7 kB (baseline 97.7 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 3 unchanged Panels & Settings — 546 kB (baseline 546 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 11 added / 11 removed / 16 unchanged User & Accounts — 26.9 kB (baseline 26.9 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 7 added / 7 removed / 3 unchanged Editors & Dialogs — 117 kB (baseline 117 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed / 1 unchanged UI Components — 57.2 kB (baseline 57.2 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 269 kB (baseline 269 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 3 unchanged Utilities & Hooks — 3.36 MB (baseline 3.36 MB) • 🔴 +192 BHelpers, composables, and utility bundles
Status: 19 added / 19 removed / 13 unchanged Vendor & Third-Party — 15.3 MB (baseline 15.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 11.7 MB (baseline 11.7 MB) • 🔴 +35 BBundles that do not match a named category
Status: 74 added / 74 removed / 91 unchanged ⚡ Performance Report
✅ No regressions detected. All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-06-30T04:01:40.236Z",
"gitSha": "219cd2368a508829e2a8246229ed5fe6084047fb",
"branch": "whatdreamscost/fe-1122-docking-bottom-center",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2059.5069999999964,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.092,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 446.256,
"heapDeltaBytes": -1362632,
"heapUsedBytes": 57369252,
"domNodes": 18,
"jsHeapTotalBytes": 26214400,
"scriptDurationMs": 22.027999999999995,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2005.9760000000324,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.863,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 394.79600000000005,
"heapDeltaBytes": -1602248,
"heapUsedBytes": 57231668,
"domNodes": 18,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 20.253000000000004,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1838.7940000000071,
"styleRecalcs": 75,
"styleRecalcDurationMs": 40.166,
"layouts": 12,
"layoutDurationMs": 3.44,
"taskDurationMs": 779.47,
"heapDeltaBytes": 19816352,
"heapUsedBytes": 70992348,
"domNodes": 57,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 127.847,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1858.919999999955,
"styleRecalcs": 76,
"styleRecalcDurationMs": 40.574,
"layouts": 12,
"layoutDurationMs": 3.7789999999999995,
"taskDurationMs": 780.8109999999999,
"heapDeltaBytes": -6405840,
"heapUsedBytes": 52380416,
"domNodes": 58,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 125.523,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1724.5270000000232,
"styleRecalcs": 30,
"styleRecalcDurationMs": 17.621,
"layouts": 6,
"layoutDurationMs": 0.631,
"taskDurationMs": 320.297,
"heapDeltaBytes": 2235116,
"heapUsedBytes": 61262208,
"domNodes": 75,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 20.097,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1722.990999999979,
"styleRecalcs": 33,
"styleRecalcDurationMs": 18.461,
"layouts": 6,
"layoutDurationMs": 0.659,
"taskDurationMs": 342.43600000000004,
"heapDeltaBytes": 2285872,
"heapUsedBytes": 61270200,
"domNodes": 78,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 24.79,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 570.6470000000081,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.732999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 368.2300000000001,
"heapDeltaBytes": 6928560,
"heapUsedBytes": 65952504,
"domNodes": 18,
"jsHeapTotalBytes": 17301504,
"scriptDurationMs": 62.213,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 592.343000000028,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.891000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 337.75600000000003,
"heapDeltaBytes": 13159488,
"heapUsedBytes": 70787288,
"domNodes": 18,
"jsHeapTotalBytes": 18087936,
"scriptDurationMs": 57.230000000000004,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2024.2579999999748,
"styleRecalcs": 10,
"styleRecalcDurationMs": 11.55,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 585.9049999999999,
"heapDeltaBytes": -9645588,
"heapUsedBytes": 62615792,
"domNodes": 20,
"jsHeapTotalBytes": 9580544,
"scriptDurationMs": 110.936,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2018.617000000006,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.888000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 590.686,
"heapDeltaBytes": -9724416,
"heapUsedBytes": 62596252,
"domNodes": 18,
"jsHeapTotalBytes": 9056256,
"scriptDurationMs": 111.009,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2122.62800000002,
"styleRecalcs": 69,
"styleRecalcDurationMs": 18.873000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1106.546,
"heapDeltaBytes": 11249848,
"heapUsedBytes": 84450820,
"domNodes": 16,
"jsHeapTotalBytes": 10805248,
"scriptDurationMs": 401.552,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2118.2979999999816,
"styleRecalcs": 69,
"styleRecalcDurationMs": 18.653,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1120.531,
"heapDeltaBytes": 10484448,
"heapUsedBytes": 83752944,
"domNodes": 16,
"jsHeapTotalBytes": 11853824,
"scriptDurationMs": 403.894,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3152.2059999999783,
"styleRecalcs": 66,
"styleRecalcDurationMs": 20.432,
"layouts": 60,
"layoutDurationMs": 7.761,
"taskDurationMs": 1342.368,
"heapDeltaBytes": 16167612,
"heapUsedBytes": 71285252,
"domNodes": 14,
"jsHeapTotalBytes": 6029312,
"scriptDurationMs": 491.244,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3167.1590000000265,
"styleRecalcs": 65,
"styleRecalcDurationMs": 18.891000000000002,
"layouts": 60,
"layoutDurationMs": 7.966000000000001,
"taskDurationMs": 1381.1609999999998,
"heapDeltaBytes": -5329480,
"heapUsedBytes": 60765608,
"domNodes": -267,
"jsHeapTotalBytes": 6557696,
"scriptDurationMs": 491.80100000000004,
"eventListeners": -118,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2019.8410000000422,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.124999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 500.97700000000003,
"heapDeltaBytes": -9281040,
"heapUsedBytes": 63936836,
"domNodes": 18,
"jsHeapTotalBytes": 8269824,
"scriptDurationMs": 93.822,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2015.105999999946,
"styleRecalcs": 8,
"styleRecalcDurationMs": 9.555000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 595.839,
"heapDeltaBytes": -8986440,
"heapUsedBytes": 64449996,
"domNodes": 16,
"jsHeapTotalBytes": 8794112,
"scriptDurationMs": 112.125,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 564.4800000000032,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.988,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 369.535,
"heapDeltaBytes": 7791148,
"heapUsedBytes": 66663740,
"domNodes": 22,
"jsHeapTotalBytes": 19922944,
"scriptDurationMs": 124.48100000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 545.8449999999857,
"styleRecalcs": 46,
"styleRecalcDurationMs": 11.341,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 358.95899999999995,
"heapDeltaBytes": 7650672,
"heapUsedBytes": 66496012,
"domNodes": 18,
"jsHeapTotalBytes": 18350080,
"scriptDurationMs": 119.438,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2023.4470000000044,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.093,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 382.25399999999996,
"heapDeltaBytes": -1502716,
"heapUsedBytes": 57294464,
"domNodes": 18,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 17.336000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1993.9640000000054,
"styleRecalcs": 11,
"styleRecalcDurationMs": 10.483999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 408.22300000000007,
"heapDeltaBytes": -1539060,
"heapUsedBytes": 57316264,
"domNodes": 22,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 19.419999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1693.4310000000323,
"styleRecalcs": 76,
"styleRecalcDurationMs": 37.125,
"layouts": 16,
"layoutDurationMs": 4.145,
"taskDurationMs": 695.8230000000001,
"heapDeltaBytes": -10335460,
"heapUsedBytes": 48529288,
"domNodes": 62,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 97.21000000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1681.036000000006,
"styleRecalcs": 75,
"styleRecalcDurationMs": 36.894,
"layouts": 16,
"layoutDurationMs": 4.098,
"taskDurationMs": 712.186,
"heapDeltaBytes": -10144260,
"heapUsedBytes": 48664572,
"domNodes": 63,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 98.181,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1300.0920000000065,
"styleRecalcs": 16,
"styleRecalcDurationMs": 29.348,
"layouts": 4,
"layoutDurationMs": 14.581999999999997,
"taskDurationMs": 830.7139999999999,
"heapDeltaBytes": 4595100,
"heapUsedBytes": 81360316,
"domNodes": 13833,
"jsHeapTotalBytes": 17301504,
"scriptDurationMs": 38.099,
"eventListeners": 2533,
"totalBlockingTimeMs": 175,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8239.36900000001,
"styleRecalcs": 250,
"styleRecalcDurationMs": 56.37800000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3942.681,
"heapDeltaBytes": -7608500,
"heapUsedBytes": 54178460,
"domNodes": -260,
"jsHeapTotalBytes": 5533696,
"scriptDurationMs": 1262.648,
"eventListeners": -106,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8158.01799999997,
"styleRecalcs": 251,
"styleRecalcDurationMs": 56.894,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4152.857,
"heapDeltaBytes": 4224436,
"heapUsedBytes": 76838120,
"domNodes": 20,
"jsHeapTotalBytes": 21028864,
"scriptDurationMs": 1435.565,
"eventListeners": 20,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 12751.799999999947,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12726.730999999998,
"heapDeltaBytes": -44200824,
"heapUsedBytes": 168310364,
"domNodes": -3302,
"jsHeapTotalBytes": 18583552,
"scriptDurationMs": 555.5680000000001,
"eventListeners": -16376,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 12993.123999999967,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12972.054,
"heapDeltaBytes": -16614688,
"heapUsedBytes": 169044136,
"domNodes": -3302,
"jsHeapTotalBytes": 18845696,
"scriptDurationMs": 553.651,
"eventListeners": -16373,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 15160.530999999992,
"styleRecalcs": 74,
"styleRecalcDurationMs": 19.50200000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15133.742000000002,
"heapDeltaBytes": -59246268,
"heapUsedBytes": 156011996,
"domNodes": -3302,
"jsHeapTotalBytes": 17797120,
"scriptDurationMs": 830.3149999999999,
"eventListeners": -16370,
"totalBlockingTimeMs": 41,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 14815.567000000101,
"styleRecalcs": 66,
"styleRecalcDurationMs": 18.20099999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14790.984999999999,
"heapDeltaBytes": -30480844,
"heapUsedBytes": 184915460,
"domNodes": -3303,
"jsHeapTotalBytes": 16924672,
"scriptDurationMs": 794.759,
"eventListeners": -16389,
"totalBlockingTimeMs": 4,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 467.6380000000222,
"styleRecalcs": 17,
"styleRecalcDurationMs": 23.442999999999998,
"layouts": 4,
"layoutDurationMs": 1.1360000000000001,
"taskDurationMs": 122.932,
"heapDeltaBytes": 5399532,
"heapUsedBytes": 65312708,
"domNodes": 168,
"jsHeapTotalBytes": 3145728,
"scriptDurationMs": 17.757,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 461.48599999992257,
"styleRecalcs": 18,
"styleRecalcDurationMs": 25.366000000000003,
"layouts": 5,
"layoutDurationMs": 1.422,
"taskDurationMs": 119.28099999999999,
"heapDeltaBytes": 5187672,
"heapUsedBytes": 65176432,
"domNodes": 155,
"jsHeapTotalBytes": 3407872,
"scriptDurationMs": 20.896,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/actionbar/ComfyActionbar.vue`:
- Around line 176-180: The legacy `Comfy.MenuPosition.Docked` value in
`ComfyActionbar.vue` is being updated incorrectly for bottom-docked users. In
the `watch` on `dockState`, update the `legacyDockedSetting` assignment so it
stays `true` for both docked states (`top` and `bottom`) and only becomes
`false` when `dockState` is `floating`, keeping it consistent with
`getActionbarDockState` and other consumers of the legacy key.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 9424642f-f2c2-49a6-a13d-2f63bcca0a02
📒 Files selected for processing (4)
src/components/actionbar/ComfyActionbar.vuesrc/locales/en/main.jsonsrc/platform/telemetry/utils/getActionbarDockState.test.tssrc/platform/telemetry/utils/getActionbarDockState.ts
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #13304 +/- ##
==========================================
- Coverage 78.32% 78.31% -0.01%
==========================================
Files 1633 1633
Lines 114406 114565 +159
Branches 39245 39303 +58
==========================================
+ Hits 89609 89724 +115
- Misses 23895 23934 +39
- Partials 902 907 +5
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 13 files with indirect coverage changes 🚀 New features to boost your workflow:
|
f4a0a86 to
4ca075d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/actionbar/ComfyActionbar.vue`:
- Around line 167-174: The dock-state persistence logic in ComfyActionbar.vue is
duplicated with the same legacy Comfy.MenuPosition.Docked migration used by
TopMenuSection.vue, so extract that useLocalStorage initializer into a shared
composable such as useActionbarDockState. Keep the persisted key and legacy
fallback behavior in one place, expose the ActionbarDock type there, and update
both components to call the shared composable instead of maintaining separate
migration logic.
In `@src/platform/telemetry/utils/getActionbarDockState.test.ts`:
- Around line 24-37: Add a precedence test in getActionbarDockState.test to
verify that getActionbarDockState prefers Comfy.MenuPosition.DockState over the
legacy Comfy.MenuPosition.Docked key when both are present. Extend the existing
test cases around getActionbarDockState by setting both localStorage entries in
the same test and asserting the value comes from DockState, using the current
helper expectations already used in the docked/floating cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 251c0c3f-484a-4eb3-a5f6-e3bac393c571
📒 Files selected for processing (5)
src/components/TopMenuSection.vuesrc/components/actionbar/ComfyActionbar.vuesrc/locales/en/main.jsonsrc/platform/telemetry/utils/getActionbarDockState.test.tssrc/platform/telemetry/utils/getActionbarDockState.ts
| const dockState = useLocalStorage<'top' | 'bottom' | 'floating'>( | ||
| 'Comfy.MenuPosition.DockState', | ||
| () => { | ||
| const legacy = localStorage.getItem('Comfy.MenuPosition.Docked') | ||
| if (legacy === 'false') return 'floating' | ||
| return 'top' | ||
| } | ||
| ) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Extract the dock-state + legacy migration into a shared composable.
This exact useLocalStorage<'top' | 'bottom' | 'floating'>('Comfy.MenuPosition.DockState', () => { ... }) block (including the legacy Comfy.MenuPosition.Docked migration) is duplicated verbatim in src/components/TopMenuSection.vue (lines 187-194). Both components must agree on the same persisted contract, so any future change to the migration logic risks diverging between the two readers. Consolidate into one composable.
♻️ Proposed shared composable
// src/composables/useActionbarDockState.ts
import { useLocalStorage } from '`@vueuse/core`'
export type ActionbarDock = 'top' | 'bottom' | 'floating'
export function useActionbarDockState() {
return useLocalStorage<ActionbarDock>(
'Comfy.MenuPosition.DockState',
() => {
const legacy = localStorage.getItem('Comfy.MenuPosition.Docked')
return legacy === 'false' ? 'floating' : 'top'
}
)
}Then both components call const dockState = useActionbarDockState().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/actionbar/ComfyActionbar.vue` around lines 167 - 174, The
dock-state persistence logic in ComfyActionbar.vue is duplicated with the same
legacy Comfy.MenuPosition.Docked migration used by TopMenuSection.vue, so
extract that useLocalStorage initializer into a shared composable such as
useActionbarDockState. Keep the persisted key and legacy fallback behavior in
one place, expose the ActionbarDock type there, and update both components to
call the shared composable instead of maintaining separate migration logic.
| it('returns docked when stored state is top', () => { | ||
| localStorage.setItem('Comfy.MenuPosition.DockState', 'top') | ||
| expect(getActionbarDockState()).toBe('docked') | ||
| }) | ||
|
|
||
| it('returns docked when stored state is bottom', () => { | ||
| localStorage.setItem('Comfy.MenuPosition.DockState', 'bottom') | ||
| expect(getActionbarDockState()).toBe('docked') | ||
| }) | ||
|
|
||
| it('returns floating when stored state is floating', () => { | ||
| localStorage.setItem('Comfy.MenuPosition.DockState', 'floating') | ||
| expect(getActionbarDockState()).toBe('floating') | ||
| }) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Add a precedence test.
The key new behavior is that Comfy.MenuPosition.DockState is read before the legacy Comfy.MenuPosition.Docked key. None of the cases set both keys, so the precedence contract isn't actually exercised.
💚 Suggested case
+ it('prefers DockState over the legacy Docked key', () => {
+ localStorage.setItem('Comfy.MenuPosition.Docked', 'true')
+ localStorage.setItem('Comfy.MenuPosition.DockState', 'floating')
+ expect(getActionbarDockState()).toBe('floating')
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('returns docked when stored state is top', () => { | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'top') | |
| expect(getActionbarDockState()).toBe('docked') | |
| }) | |
| it('returns docked when stored state is bottom', () => { | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'bottom') | |
| expect(getActionbarDockState()).toBe('docked') | |
| }) | |
| it('returns floating when stored state is floating', () => { | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'floating') | |
| expect(getActionbarDockState()).toBe('floating') | |
| }) | |
| it('returns docked when stored state is top', () => { | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'top') | |
| expect(getActionbarDockState()).toBe('docked') | |
| }) | |
| it('returns docked when stored state is bottom', () => { | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'bottom') | |
| expect(getActionbarDockState()).toBe('docked') | |
| }) | |
| it('returns floating when stored state is floating', () => { | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'floating') | |
| expect(getActionbarDockState()).toBe('floating') | |
| }) | |
| it('prefers DockState over the legacy Docked key', () => { | |
| localStorage.setItem('Comfy.MenuPosition.Docked', 'true') | |
| localStorage.setItem('Comfy.MenuPosition.DockState', 'floating') | |
| expect(getActionbarDockState()).toBe('floating') | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/platform/telemetry/utils/getActionbarDockState.test.ts` around lines 24 -
37, Add a precedence test in getActionbarDockState.test to verify that
getActionbarDockState prefers Comfy.MenuPosition.DockState over the legacy
Comfy.MenuPosition.Docked key when both are present. Extend the existing test
cases around getActionbarDockState by setting both localStorage entries in the
same test and asserting the value comes from DockState, using the current helper
expectations already used in the docked/floating cases.
Summary
Adds a new docking area for the action bar/run button positioned bottom center. Also adds telemetry to track feature usage.
Changes
actionbar_dock_topandactionbar_dock_bottomclicks.Tests
Video
Desktop.2026.06.29.-.20.52.09.02_1.mp4
Resolves FE-1122