feat(workspace): scope framing for the local workspace switcher - #15438
Conversation
… the local switcher
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1835 passed, 0 failed📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-20T00:10:40.133Z",
"gitSha": "8533ba08317f576c7285e64a5539e5887fda0e6e",
"branch": "comfydesigner/switcher-credits-menu",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2090.7580000000507,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.037999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 582.8800000000001,
"heapDeltaBytes": 1711252,
"heapUsedBytes": 62583804,
"domNodes": -281,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 10.400999999999998,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-idle",
"durationMs": 2038.8239999999769,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.752,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 420.09000000000003,
"heapDeltaBytes": -1733672,
"heapUsedBytes": 58976060,
"domNodes": -279,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 7.422999999999999,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1745.4819999999245,
"styleRecalcs": 72,
"styleRecalcDurationMs": 33.31,
"layouts": 12,
"layoutDurationMs": 3.5649999999999995,
"taskDurationMs": 820.018,
"heapDeltaBytes": 2113284,
"heapUsedBytes": 62127264,
"domNodes": -280,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 98.17799999999998,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1693.3890000000247,
"styleRecalcs": 71,
"styleRecalcDurationMs": 27.458000000000002,
"layouts": 12,
"layoutDurationMs": 3.28,
"taskDurationMs": 679.1769999999999,
"heapDeltaBytes": 368336,
"heapUsedBytes": 61087796,
"domNodes": -279,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 85.467,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1708.825999999931,
"styleRecalcs": 30,
"styleRecalcDurationMs": 15.367999999999999,
"layouts": 6,
"layoutDurationMs": 0.5349999999999999,
"taskDurationMs": 297.09200000000004,
"heapDeltaBytes": 2723124,
"heapUsedBytes": 62921748,
"domNodes": 77,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 9.4,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1663.7379999999666,
"styleRecalcs": 31,
"styleRecalcDurationMs": 13.483,
"layouts": 6,
"layoutDurationMs": 0.5599999999999999,
"taskDurationMs": 277.354,
"heapDeltaBytes": 3053604,
"heapUsedBytes": 63702956,
"domNodes": 77,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 8.005000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 555.3309999999101,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.282,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 333.616,
"heapDeltaBytes": 10646596,
"heapUsedBytes": 70721080,
"domNodes": 18,
"jsHeapTotalBytes": 4456448,
"scriptDurationMs": 49.739,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 495.03100000003997,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.273999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 295.014,
"heapDeltaBytes": 10685348,
"heapUsedBytes": 70999832,
"domNodes": 18,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 43.658,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2028.09400000001,
"styleRecalcs": 9,
"styleRecalcDurationMs": 10.489999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 501.803,
"heapDeltaBytes": 2465228,
"heapUsedBytes": 76474936,
"domNodes": -262,
"jsHeapTotalBytes": -2101248,
"scriptDurationMs": 13.869000000000003,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2031.725999999935,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.842,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 493.574,
"heapDeltaBytes": 2341556,
"heapUsedBytes": 76429080,
"domNodes": -266,
"jsHeapTotalBytes": -2363392,
"scriptDurationMs": 13.317999999999996,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2125.630000000001,
"styleRecalcs": 70,
"styleRecalcDurationMs": 15.946000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1013.3230000000002,
"heapDeltaBytes": -7548612,
"heapUsedBytes": 67478224,
"domNodes": -271,
"jsHeapTotalBytes": -565248,
"scriptDurationMs": 264.392,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2068.870000000061,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.518999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 841.5879999999999,
"heapDeltaBytes": 10074752,
"heapUsedBytes": 84154312,
"domNodes": -278,
"jsHeapTotalBytes": 4939776,
"scriptDurationMs": 262.53700000000003,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3095.324000000005,
"styleRecalcs": 66,
"styleRecalcDurationMs": 14.966,
"layouts": 60,
"layoutDurationMs": 7.234000000000001,
"taskDurationMs": 1168.087,
"heapDeltaBytes": 770544,
"heapUsedBytes": 76943984,
"domNodes": -267,
"jsHeapTotalBytes": -2363392,
"scriptDurationMs": 325.218,
"eventListeners": -145,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3032.4889999999414,
"styleRecalcs": 63,
"styleRecalcDurationMs": 11.942000000000002,
"layouts": 60,
"layoutDurationMs": 7.079999999999999,
"taskDurationMs": 1083.671,
"heapDeltaBytes": 14879360,
"heapUsedBytes": 76629496,
"domNodes": 8,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 313.539,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 2156.782000000021,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.309999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1206.237,
"heapDeltaBytes": 11433348,
"heapUsedBytes": 94157420,
"domNodes": 16,
"jsHeapTotalBytes": 8581120,
"scriptDurationMs": 372.612,
"eventListeners": 186,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "legacy-node-drag",
"durationMs": 2188.4619999999586,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.349,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1220.448,
"heapDeltaBytes": -21486048,
"heapUsedBytes": 63004820,
"domNodes": -267,
"jsHeapTotalBytes": 7143424,
"scriptDurationMs": 379.077,
"eventListeners": 33,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2018.3350000000928,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.932999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 478.87300000000005,
"heapDeltaBytes": 1791764,
"heapUsedBytes": 77520164,
"domNodes": -264,
"jsHeapTotalBytes": -3411968,
"scriptDurationMs": 14.092999999999998,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2023.2479999999669,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.655,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 463.435,
"heapDeltaBytes": 1785656,
"heapUsedBytes": 77554356,
"domNodes": -264,
"jsHeapTotalBytes": -3149824,
"scriptDurationMs": 11.553999999999998,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 523.3050000000503,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.494,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 332.886,
"heapDeltaBytes": 11804880,
"heapUsedBytes": 72927792,
"domNodes": 22,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 100.65599999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 512.3670000000402,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.763999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 314.039,
"heapDeltaBytes": 10937388,
"heapUsedBytes": 71776260,
"domNodes": 20,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 97.292,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2011.7180000000872,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.894000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 459.46999999999997,
"heapDeltaBytes": 3582580,
"heapUsedBytes": 63716408,
"domNodes": -281,
"jsHeapTotalBytes": 4186112,
"scriptDurationMs": 8.447999999999999,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2018.1929999999966,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.937,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 395.425,
"heapDeltaBytes": -304912,
"heapUsedBytes": 60438000,
"domNodes": -281,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 5.788999999999999,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1715.7399999999825,
"styleRecalcs": 75,
"styleRecalcDurationMs": 33.04899999999999,
"layouts": 16,
"layoutDurationMs": 4.32,
"taskDurationMs": 716.561,
"heapDeltaBytes": -1902760,
"heapUsedBytes": 59193908,
"domNodes": -282,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 79.15700000000001,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1709.57599999997,
"styleRecalcs": 75,
"styleRecalcDurationMs": 29.717,
"layouts": 16,
"layoutDurationMs": 4.035,
"taskDurationMs": 639.783,
"heapDeltaBytes": -2191688,
"heapUsedBytes": 58629416,
"domNodes": -281,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 67.86,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-transition-enter",
"durationMs": 883.764000000042,
"styleRecalcs": 20,
"styleRecalcDurationMs": 25.09,
"layouts": 15,
"layoutDurationMs": 10.657,
"taskDurationMs": 718.9159999999999,
"heapDeltaBytes": 20087560,
"heapUsedBytes": 96376216,
"domNodes": 13673,
"jsHeapTotalBytes": 16515072,
"scriptDurationMs": 20.471999999999998,
"eventListeners": 2373,
"totalBlockingTimeMs": 89,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8142.555000000016,
"styleRecalcs": 251,
"styleRecalcDurationMs": 40.476,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3437.4599999999996,
"heapDeltaBytes": 7224544,
"heapUsedBytes": 81388224,
"domNodes": -260,
"jsHeapTotalBytes": -1351680,
"scriptDurationMs": 841.8319999999999,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333338,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8136.683999999832,
"styleRecalcs": 251,
"styleRecalcDurationMs": 40.933,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3328.7259999999997,
"heapDeltaBytes": 6873380,
"heapUsedBytes": 80932856,
"domNodes": -258,
"jsHeapTotalBytes": -827392,
"scriptDurationMs": 837.456,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 16419.788999999924,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15203.030000000002,
"heapDeltaBytes": -48101988,
"heapUsedBytes": 176829780,
"domNodes": -8312,
"jsHeapTotalBytes": -14356480,
"scriptDurationMs": 126.12300000000002,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 14962.98999999999,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14450.098,
"heapDeltaBytes": -46230028,
"heapUsedBytes": 174083976,
"domNodes": -8312,
"jsHeapTotalBytes": -11440128,
"scriptDurationMs": 98.201,
"eventListeners": -16391,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 18153.511999999977,
"styleRecalcs": 160,
"styleRecalcDurationMs": 15.66999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17613.051,
"heapDeltaBytes": -40989788,
"heapUsedBytes": 178870076,
"domNodes": -8312,
"jsHeapTotalBytes": -14163968,
"scriptDurationMs": 351.594,
"eventListeners": -16385,
"totalBlockingTimeMs": 5,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 17904.952000000092,
"styleRecalcs": 162,
"styleRecalcDurationMs": 16.009999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17365.211,
"heapDeltaBytes": -28685952,
"heapUsedBytes": 190609560,
"domNodes": -8312,
"jsHeapTotalBytes": -13377536,
"scriptDurationMs": 353.815,
"eventListeners": -16387,
"totalBlockingTimeMs": 9,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 93.42800000001716,
"styleRecalcs": 6,
"styleRecalcDurationMs": 12.235,
"layouts": 5,
"layoutDurationMs": 1.48,
"taskDurationMs": 62.482000000000006,
"heapDeltaBytes": 3038016,
"heapUsedBytes": 63428444,
"domNodes": 126,
"jsHeapTotalBytes": 786432,
"scriptDurationMs": 5.1850000000000005,
"eventListeners": 25,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 464.6420000001399,
"styleRecalcs": 13,
"styleRecalcDurationMs": 19.339,
"layouts": 2,
"layoutDurationMs": 0.397,
"taskDurationMs": 97.733,
"heapDeltaBytes": 4932384,
"heapUsedBytes": 65835764,
"domNodes": 123,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 6.237999999999999,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## dante01yoon/fe-1584-local-workspace-switcher #15438 +/- ##
================================================================================
+ Coverage 80.92% 80.97% +0.05%
================================================================================
Files 1884 1883 -1
Lines 123653 122977 -676
Branches 38468 38317 -151
================================================================================
- Hits 100063 99584 -479
+ Misses 23089 22892 -197
Partials 501 501
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 23 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Focused design delta on top of #15164These screenshots were captured from the actual running local ComfyUI app with the same authenticated user and workspace data on both branches. This PR is stacked directly on #15164 and is intended to be reviewed as the local-only scope-framing change. It does not change workspace switching, authentication, or API behavior. AS IS — #15164 running appThe local switcher already reuses the Cloud row/selection pattern, but the UI does not explain that selecting a workspace affects only the credit source. TO BE — #15438 running appThis PR adds only:
Review scope: the switching implementation remains in #15164; this PR can be reviewed and merged as the focused design/copy layer stacked on it. |
ff4b46d
into
dante01yoon/fe-1584-local-workspace-switcher


Summary
Stacked on #15164. Adds the local-only scope framing to the workspace switcher menu, per DES-787 / FE-1584. Design: Menu States (6245-41540).
Behavioral spec
isCloudby feat(workspace): enable local credit workspace switching FE-1584 #15164; unchanged here).Deferred: per-row credit balances
Earlier commits on this branch built the designed per-row balances (read-only token mint + balance fetch per workspace, 60s cache) and then removed them — deferred post-V1 so local rows stay in parity with cloud's switcher. Deferring also drops this PR's interim fetch pattern and its two open dependencies: confirming the balance endpoint applies the member min-rule per identity, and the eventual balance field on the workspace list response. The implementation is intact in this branch's history for the follow-up.
Verification
pnpm test:unit— popover suite 10 passed (scope framing off-cloud, rows/roles, cloud unaffected).!isCloudonly; no balance fetches on any distribution; create-workspace button untouched.