Skip to content

feat(settings): bring the V1 Plan & Credits tabs to local - #15219

Closed
comfydesigner wants to merge 6 commits into
comfydesigner/activity-tab-stopgapfrom
comfydesigner/local-v1-plan-credits
Closed

feat(settings): bring the V1 Plan & Credits tabs to local#15219
comfydesigner wants to merge 6 commits into
comfydesigner/activity-tab-stopgapfrom
comfydesigner/local-v1-plan-credits

Conversation

@comfydesigner

@comfydesigner comfydesigner commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Stacked on #15206. Brings the V1 Plan & Credits tab organization to local and converges the settings sidebar grammar, as part of the local/cloud UI consolidation. Follow-ups tracked as FE-1619 (post-switcher: header, Members, workspace overview, state-dependent label) and FE-1618 (credits-tile no-plan state).

1. Local gets the shared tab shell. The sidebar entry "Credits" (coins icon; "Plan & Credits" arrives with real plan state, FE-1619) mounts the shared PlanCreditsPanelContent:

  • Credits tab (local): the legacy credits body wrapped in the designed "Workspace credits" card with a Manage billing button (legacy portal). CreditsPanel gains an embedded prop; the workspace plan card is not used off-cloud — it renders misleading data against the legacy balance shape.
  • Activity tab (local): the same UsageLogsTable (useBillingRouting returns legacy off-cloud → /customers/events), plus a "Full activity ↗" footer button to platform.comfy.org/profile/usage.
  • The table sizes rows-per-page to the dialog via useAutoPageSize, extended with a reserved-height hook because this table's paginator sits inside the measured container. Approximate with mixed row heights; opening the tab issues two fetches (initial + fitted size), deduped by the existing load token.

2. One sidebar grammar. Local's ACCOUNT / APPLICATION SETTINGS / SPECIAL SETTINGS groups are replaced with cloud's Workspace / General / Other; the General and Other groups are now shared computeds consumed by both trees (User under General on both platforms, custom-node settings under Other, desktop Server Config at the end of General). Billing belongs to the workspace on the backend, so the group is named Workspace; it becomes navigable with #15164.

3. Footers unified. Both tabs end in SubscriptionFooterLinks; the embedded credits body drops its bespoke FAQs row. Note for reviewers: the footer's top divider is removed globally (design call), so the cloud subscription overview loses the line too — intentional, not incidental.

Cloud behavior is otherwise unchanged (the Credits tab body forks on isCloud; flag-off is untouched). Coordination: #14811/#14813 and #15151 touch the same panel/rail seams — merge order decides who rebases.

  • Verified: unit tests covering the tab fork per distribution; typecheck + lint via hooks; mocked-boot Playwright rigs in OSS and cloud modes; live passes on testcloud (?ff=billing_control_enabled) and local (OSS backend) by @comfydesigner.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1e3b3e04-3ae8-4793-9d9c-5783125831aa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 08/14/2026, 12:38:42 AM UTC

Links

🎭 Playwright: ❌ 1810 passed, 1 failed

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 1789 / ❌ 1 / ⚠️ 0 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 18 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.3 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.7 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 70.0 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 50.8 MB heap
large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.3 MB heap
large-graph-pan: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 75.4 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 80.7 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 63.9 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.6 MB heap
subgraph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.3 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 58.6 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 75ms TBT · 95.9 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 74.4 MB heap
vue-large-graph-idle: · 57.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 159.3 MB heap
vue-large-graph-pan: · 57.1 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 157.3 MB heap
workflow-execution: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.8 MB heap

ℹ️ No baseline found — significance unavailable.

Absolute values
Metric Value
canvas-idle: avg frame time 17ms
canvas-idle: p95 frame time 17ms
canvas-idle: layout duration 0ms
canvas-idle: style recalc duration 6ms
canvas-idle: layout count 0
canvas-idle: style recalc count 10
canvas-idle: task duration 396ms
canvas-idle: script duration 14ms
canvas-idle: TBT 0ms
canvas-idle: heap used 67.3 MB
canvas-idle: DOM nodes 20
canvas-idle: event listeners 6
canvas-mouse-sweep: avg frame time 17ms
canvas-mouse-sweep: p95 frame time 17ms
canvas-mouse-sweep: layout duration 3ms
canvas-mouse-sweep: style recalc duration 27ms
canvas-mouse-sweep: layout count 12
canvas-mouse-sweep: style recalc count 73
canvas-mouse-sweep: task duration 667ms
canvas-mouse-sweep: script duration 89ms
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: heap used 53.7 MB
canvas-mouse-sweep: DOM nodes -113
canvas-mouse-sweep: event listeners -74
canvas-zoom-sweep: avg frame time 17ms
canvas-zoom-sweep: p95 frame time 17ms
canvas-zoom-sweep: layout duration 1ms
canvas-zoom-sweep: style recalc duration 12ms
canvas-zoom-sweep: layout count 6
canvas-zoom-sweep: style recalc count 31
canvas-zoom-sweep: task duration 298ms
canvas-zoom-sweep: script duration 15ms
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: heap used 70.0 MB
canvas-zoom-sweep: DOM nodes 78
canvas-zoom-sweep: event listeners 20
dom-widget-clipping: avg frame time 17ms
dom-widget-clipping: p95 frame time 17ms
dom-widget-clipping: layout duration 0ms
dom-widget-clipping: style recalc duration 9ms
dom-widget-clipping: layout count 0
dom-widget-clipping: style recalc count 12
dom-widget-clipping: task duration 301ms
dom-widget-clipping: script duration 45ms
dom-widget-clipping: TBT 0ms
dom-widget-clipping: heap used 50.8 MB
dom-widget-clipping: DOM nodes 19
dom-widget-clipping: event listeners 2
large-graph-idle: avg frame time 17ms
large-graph-idle: p95 frame time 17ms
large-graph-idle: layout duration 0ms
large-graph-idle: style recalc duration 7ms
large-graph-idle: layout count 0
large-graph-idle: style recalc count 11
large-graph-idle: task duration 534ms
large-graph-idle: script duration 73ms
large-graph-idle: TBT 0ms
large-graph-idle: heap used 66.3 MB
large-graph-idle: DOM nodes -280
large-graph-idle: event listeners -149
large-graph-pan: avg frame time 17ms
large-graph-pan: p95 frame time 17ms
large-graph-pan: layout duration 0ms
large-graph-pan: style recalc duration 13ms
large-graph-pan: layout count 0
large-graph-pan: style recalc count 69
large-graph-pan: task duration 966ms
large-graph-pan: script duration 313ms
large-graph-pan: TBT 0ms
large-graph-pan: heap used 75.4 MB
large-graph-pan: DOM nodes -282
large-graph-pan: event listeners -179
large-graph-zoom: avg frame time 17ms
large-graph-zoom: p95 frame time 17ms
large-graph-zoom: layout duration 7ms
large-graph-zoom: style recalc duration 14ms
large-graph-zoom: layout count 60
large-graph-zoom: style recalc count 66
large-graph-zoom: task duration 1147ms
large-graph-zoom: script duration 411ms
large-graph-zoom: TBT 0ms
large-graph-zoom: heap used 80.7 MB
large-graph-zoom: DOM nodes 15
large-graph-zoom: event listeners 6
minimap-idle: avg frame time 17ms
minimap-idle: p95 frame time 17ms
minimap-idle: layout duration 0ms
minimap-idle: style recalc duration 6ms
minimap-idle: layout count 0
minimap-idle: style recalc count 10
minimap-idle: task duration 532ms
minimap-idle: script duration 75ms
minimap-idle: TBT 0ms
minimap-idle: heap used 63.9 MB
minimap-idle: DOM nodes -283
minimap-idle: event listeners -164
subgraph-dom-widget-clipping: avg frame time 17ms
subgraph-dom-widget-clipping: p95 frame time 17ms
subgraph-dom-widget-clipping: layout duration 0ms
subgraph-dom-widget-clipping: style recalc duration 9ms
subgraph-dom-widget-clipping: layout count 0
subgraph-dom-widget-clipping: style recalc count 48
subgraph-dom-widget-clipping: task duration 313ms
subgraph-dom-widget-clipping: script duration 94ms
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: heap used 51.6 MB
subgraph-dom-widget-clipping: DOM nodes 21
subgraph-dom-widget-clipping: event listeners 8
subgraph-idle: avg frame time 17ms
subgraph-idle: p95 frame time 17ms
subgraph-idle: layout duration 0ms
subgraph-idle: style recalc duration 7ms
subgraph-idle: layout count 0
subgraph-idle: style recalc count 11
subgraph-idle: task duration 361ms
subgraph-idle: script duration 11ms
subgraph-idle: TBT 0ms
subgraph-idle: heap used 67.3 MB
subgraph-idle: DOM nodes 22
subgraph-idle: event listeners 6
subgraph-mouse-sweep: avg frame time 17ms
subgraph-mouse-sweep: p95 frame time 17ms
subgraph-mouse-sweep: layout duration 3ms
subgraph-mouse-sweep: style recalc duration 26ms
subgraph-mouse-sweep: layout count 16
subgraph-mouse-sweep: style recalc count 76
subgraph-mouse-sweep: task duration 571ms
subgraph-mouse-sweep: script duration 67ms
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: heap used 58.6 MB
subgraph-mouse-sweep: DOM nodes 63
subgraph-mouse-sweep: event listeners 4
subgraph-transition-enter: avg frame time 17ms
subgraph-transition-enter: p95 frame time 17ms
subgraph-transition-enter: layout duration 9ms
subgraph-transition-enter: style recalc duration 23ms
subgraph-transition-enter: layout count 14
subgraph-transition-enter: style recalc count 18
subgraph-transition-enter: task duration 691ms
subgraph-transition-enter: script duration 18ms
subgraph-transition-enter: TBT 75ms
subgraph-transition-enter: heap used 95.9 MB
subgraph-transition-enter: DOM nodes 13673
subgraph-transition-enter: event listeners 2373
viewport-pan-sweep: avg frame time 17ms
viewport-pan-sweep: p95 frame time 17ms
viewport-pan-sweep: layout duration 0ms
viewport-pan-sweep: style recalc duration 36ms
viewport-pan-sweep: layout count 0
viewport-pan-sweep: style recalc count 249
viewport-pan-sweep: task duration 3389ms
viewport-pan-sweep: script duration 1035ms
viewport-pan-sweep: TBT 0ms
viewport-pan-sweep: heap used 74.4 MB
viewport-pan-sweep: DOM nodes -280
viewport-pan-sweep: event listeners -148
vue-large-graph-idle: avg frame time 18ms
vue-large-graph-idle: p95 frame time 17ms
vue-large-graph-idle: layout duration 0ms
vue-large-graph-idle: style recalc duration 0ms
vue-large-graph-idle: layout count 0
vue-large-graph-idle: style recalc count 0
vue-large-graph-idle: task duration 13959ms
vue-large-graph-idle: script duration 443ms
vue-large-graph-idle: TBT 0ms
vue-large-graph-idle: heap used 159.3 MB
vue-large-graph-idle: DOM nodes -8312
vue-large-graph-idle: event listeners -16391
vue-large-graph-pan: avg frame time 17ms
vue-large-graph-pan: p95 frame time 17ms
vue-large-graph-pan: layout duration 0ms
vue-large-graph-pan: style recalc duration 13ms
vue-large-graph-pan: layout count 0
vue-large-graph-pan: style recalc count 100
vue-large-graph-pan: task duration 16535ms
vue-large-graph-pan: script duration 697ms
vue-large-graph-pan: TBT 0ms
vue-large-graph-pan: heap used 157.3 MB
vue-large-graph-pan: DOM nodes -8312
vue-large-graph-pan: event listeners -16388
workflow-execution: avg frame time 17ms
workflow-execution: p95 frame time 17ms
workflow-execution: layout duration 1ms
workflow-execution: style recalc duration 16ms
workflow-execution: layout count 3
workflow-execution: style recalc count 16
workflow-execution: task duration 93ms
workflow-execution: script duration 8ms
workflow-execution: TBT 0ms
workflow-execution: heap used 65.8 MB
workflow-execution: DOM nodes 138
workflow-execution: event listeners 98
Raw data
{
  "timestamp": "2026-08-14T00:50:46.394Z",
  "gitSha": "5f8277894675eb317874a1c95a8ca569c1919654",
  "branch": "comfydesigner/local-v1-plan-credits",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2073.6719999999877,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.330000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 428.21600000000007,
      "heapDeltaBytes": 5877956,
      "heapUsedBytes": 70553232,
      "domNodes": 20,
      "jsHeapTotalBytes": 24117248,
      "scriptDurationMs": 15.482000000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-idle",
      "durationMs": 2012.911000000031,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.1080000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 363.09200000000004,
      "heapDeltaBytes": 5907844,
      "heapUsedBytes": 70573736,
      "domNodes": 20,
      "jsHeapTotalBytes": 24117248,
      "scriptDurationMs": 13.124,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1695.446000000004,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 26.371,
      "layouts": 12,
      "layoutDurationMs": 2.984,
      "taskDurationMs": 685.845,
      "heapDeltaBytes": -17337096,
      "heapUsedBytes": 47346924,
      "domNodes": -280,
      "jsHeapTotalBytes": 22515712,
      "scriptDurationMs": 88.688,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1726.72799999998,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 27.485000000000003,
      "layouts": 12,
      "layoutDurationMs": 3.2339999999999995,
      "taskDurationMs": 647.333,
      "heapDeltaBytes": 419076,
      "heapUsedBytes": 65179340,
      "domNodes": 54,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 90.169,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1685.0239999999985,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 12.122000000000002,
      "layouts": 6,
      "layoutDurationMs": 0.5690000000000001,
      "taskDurationMs": 293.421,
      "heapDeltaBytes": 8245640,
      "heapUsedBytes": 72913948,
      "domNodes": 77,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 15.069999999999999,
      "eventListeners": 21,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1667.6909999999907,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 11.862,
      "layouts": 6,
      "layoutDurationMs": 0.499,
      "taskDurationMs": 302.49,
      "heapDeltaBytes": 9105724,
      "heapUsedBytes": 73894304,
      "domNodes": 78,
      "jsHeapTotalBytes": 24117248,
      "scriptDurationMs": 15.539,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 509.0120000000411,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 13.254999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 310.404,
      "heapDeltaBytes": -11376860,
      "heapUsedBytes": 53285856,
      "domNodes": 20,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 46.148,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 510.627999999997,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 5.688000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 292.254,
      "heapDeltaBytes": -11556072,
      "heapUsedBytes": 53158644,
      "domNodes": 18,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 44.75899999999999,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2011.3660000000095,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.916999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 531.871,
      "heapDeltaBytes": 7428080,
      "heapUsedBytes": 69744052,
      "domNodes": -280,
      "jsHeapTotalBytes": 1933312,
      "scriptDurationMs": 71.57900000000001,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 1994.066000000089,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.834,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 536.006,
      "heapDeltaBytes": 7173656,
      "heapUsedBytes": 69194356,
      "domNodes": -280,
      "jsHeapTotalBytes": 3244032,
      "scriptDurationMs": 73.551,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2082.456000000036,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 13.217999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 966.7900000000001,
      "heapDeltaBytes": 18208020,
      "heapUsedBytes": 78941276,
      "domNodes": -282,
      "jsHeapTotalBytes": 3960832,
      "scriptDurationMs": 317.109,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2090.9359999999424,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 12.757000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 964.478,
      "heapDeltaBytes": 15954492,
      "heapUsedBytes": 79241340,
      "domNodes": -281,
      "jsHeapTotalBytes": 3960832,
      "scriptDurationMs": 309.08799999999997,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3068.187000000023,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 12.878,
      "layouts": 60,
      "layoutDurationMs": 6.603,
      "taskDurationMs": 1124.2730000000001,
      "heapDeltaBytes": 22639296,
      "heapUsedBytes": 85623900,
      "domNodes": 14,
      "jsHeapTotalBytes": 7340032,
      "scriptDurationMs": 405.87,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3121.436000000017,
      "styleRecalcs": 67,
      "styleRecalcDurationMs": 14.309999999999999,
      "layouts": 60,
      "layoutDurationMs": 6.810000000000001,
      "taskDurationMs": 1169.155,
      "heapDeltaBytes": 21504424,
      "heapUsedBytes": 83716184,
      "domNodes": 16,
      "jsHeapTotalBytes": 7602176,
      "scriptDurationMs": 415.607,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2006.386999999961,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 5.485,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 527.1539999999999,
      "heapDeltaBytes": 3511900,
      "heapUsedBytes": 65222400,
      "domNodes": -283,
      "jsHeapTotalBytes": 3506176,
      "scriptDurationMs": 72.19300000000001,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2002.4970000000621,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.590000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 536.667,
      "heapDeltaBytes": 7675112,
      "heapUsedBytes": 68746556,
      "domNodes": -282,
      "jsHeapTotalBytes": 4030464,
      "scriptDurationMs": 77.938,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 519.9259999999981,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 9.812999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 323.147,
      "heapDeltaBytes": -10755448,
      "heapUsedBytes": 53939368,
      "domNodes": 22,
      "jsHeapTotalBytes": 25952256,
      "scriptDurationMs": 97.61200000000001,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666682,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 452.50399999997626,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 8.332999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 303.38599999999997,
      "heapDeltaBytes": -10442608,
      "heapUsedBytes": 54229048,
      "domNodes": 20,
      "jsHeapTotalBytes": 26476544,
      "scriptDurationMs": 91.36699999999999,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1999.6380000000045,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.277000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 362.763,
      "heapDeltaBytes": 5686964,
      "heapUsedBytes": 70362252,
      "domNodes": 22,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 10.726999999999999,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2004.240999999979,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 6.783000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 360.128,
      "heapDeltaBytes": 5890816,
      "heapUsedBytes": 70842712,
      "domNodes": 22,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 11.233000000000002,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1671.8060000000037,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 26.154000000000003,
      "layouts": 16,
      "layoutDurationMs": 3.162,
      "taskDurationMs": 573.922,
      "heapDeltaBytes": -3236968,
      "heapUsedBytes": 61522200,
      "domNodes": 64,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 68.36,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1668.703999999991,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 25.466,
      "layouts": 16,
      "layoutDurationMs": 3.051,
      "taskDurationMs": 568.5600000000001,
      "heapDeltaBytes": -3575208,
      "heapUsedBytes": 61455320,
      "domNodes": 62,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 66.399,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 850.291000000027,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 23.073999999999998,
      "layouts": 14,
      "layoutDurationMs": 8.812,
      "taskDurationMs": 690.6600000000001,
      "heapDeltaBytes": 8915632,
      "heapUsedBytes": 100606276,
      "domNodes": 13673,
      "jsHeapTotalBytes": 10747904,
      "scriptDurationMs": 17.912000000000003,
      "eventListeners": 2373,
      "totalBlockingTimeMs": 75,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8123.5350000000035,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 35.752,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3357.4139999999998,
      "heapDeltaBytes": 23633200,
      "heapUsedBytes": 85675516,
      "domNodes": -280,
      "jsHeapTotalBytes": 6057984,
      "scriptDurationMs": 1038.6369999999997,
      "eventListeners": -163,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8107.009999999946,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 36.704,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3421.227,
      "heapDeltaBytes": 8269832,
      "heapUsedBytes": 70253964,
      "domNodes": -280,
      "jsHeapTotalBytes": 5271552,
      "scriptDurationMs": 1031.52,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 13806.887000000017,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 13788.861,
      "heapDeltaBytes": -33862604,
      "heapUsedBytes": 166119728,
      "domNodes": -8312,
      "jsHeapTotalBytes": -13832192,
      "scriptDurationMs": 416.154,
      "eventListeners": -16389,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 14181.376999999997,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14129.159999999998,
      "heapDeltaBytes": -31421464,
      "heapUsedBytes": 168013220,
      "domNodes": -8312,
      "jsHeapTotalBytes": -10948608,
      "scriptDurationMs": 469.742,
      "eventListeners": -16393,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 16417.700999999965,
      "styleRecalcs": 91,
      "styleRecalcDurationMs": 12.439000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16399.097,
      "heapDeltaBytes": -51806904,
      "heapUsedBytes": 164792028,
      "domNodes": -8312,
      "jsHeapTotalBytes": -9900032,
      "scriptDurationMs": 678.861,
      "eventListeners": -16389,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 16697.497999999996,
      "styleRecalcs": 108,
      "styleRecalcDurationMs": 13.274000000000008,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16670.37,
      "heapDeltaBytes": -33992632,
      "heapUsedBytes": 165022860,
      "domNodes": -8312,
      "jsHeapTotalBytes": -11735040,
      "scriptDurationMs": 714.586,
      "eventListeners": -16387,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.219999999999953,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 445.30400000002146,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 15.703000000000001,
      "layouts": 3,
      "layoutDurationMs": 0.945,
      "taskDurationMs": 93.833,
      "heapDeltaBytes": 5180208,
      "heapUsedBytes": 69124916,
      "domNodes": 150,
      "jsHeapTotalBytes": 4980736,
      "scriptDurationMs": 8.005000000000003,
      "eventListeners": 97,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "workflow-execution",
      "durationMs": 435.771999999929,
      "styleRecalcs": 14,
      "styleRecalcDurationMs": 16.607999999999997,
      "layouts": 2,
      "layoutDurationMs": 0.3749999999999999,
      "taskDurationMs": 91.84700000000001,
      "heapDeltaBytes": 5062032,
      "heapUsedBytes": 68957320,
      "domNodes": 125,
      "jsHeapTotalBytes": 5505024,
      "scriptDurationMs": 7.496999999999998,
      "eventListeners": 99,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    }
  ]
}

@github-actions github-actions Bot added the risk:R3 PR risk grade (advisory shadow check; grader-owned) label Aug 13, 2026
@comfydesigner

Copy link
Copy Markdown
Contributor Author

Superseded by #15239, which lands the same alignment from main with the tree fork fully collapsed. The deltas from this PR move there via review comments (local Credits tab fork, Members gating, sidebar grammar) and the stacked #15268 / #15271.

pull Bot pushed a commit to Penguinjanator/ComfyUI_frontend that referenced this pull request Aug 22, 2026
…g#15239)

## Why

Settings currently builds separate local and cloud navigation trees, and
cloud's V1 workspace sections are additionally gated by
`billing_control_enabled`. This leaves local and flag-off users on the
legacy Workspace/Credits organization and forces billing work to target
two layouts.

The [latest product direction in
Slack](https://comfy-organization.slack.com/archives/C0BNGQG2LCW/p1786668631144379?thread_ts=1786649682.983289&cid=C0BNGQG2LCW)
clarifies that Activity should reuse `UsageLogsTable` for both personal
and team workspaces until the backend-backed workspace activity feed is
available.

## What changed

- Use the V1 Workspace / Account / General / Other sidebar organization
for logged-in local and cloud users.
- Always expose Plan & Credits and Members as workspace sections; keep
the owner-only Allowlist policy unchanged.
- Keep the legacy local Credits entry reachable so existing profile-menu
and top-up flows continue to work.
- Render the existing `UsageLogsTable` in Activity for personal and team
workspaces, remove the obsolete search field, and report refresh
failures.
- Preserve `WorkspaceActivityContent` unchanged for FE-1249.

## AS IS / TO BE

### AS IS — current local Credits settings

![Current local Credits settings with legacy Account and Application
Settings
grouping](https://ampcode.com/user-content/artifacts/736a6cfaafead9868352c7f1a5fcd4c9850ae2d21c7d994f37bf7aa8932b8975-file.png)

Source: [current local settings screenshot from the product
thread](https://comfy-organization.slack.com/archives/C0BNGQG2LCW/p1786651103082259?thread_ts=1786649682.983289&cid=C0BNGQG2LCW).

### TO BE — local V1 workspace layout

![Local V1 settings with Workspace and General
grouping](https://ampcode.com/user-content/artifacts/d62d83edd3f6f0c0f5b27d209ec340dab403afa9546cade29daef46a9282c9c1-file.png)

Source: [local unsubscribed V1 frame in
Figma](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=5874-71646).

### IMPLEMENTED — local Activity tab

![Local Plan & Credits Activity tab using
UsageLogsTable](https://ampcode.com/user-content/artifacts/bb6000311c626dc9faba1a444fd5d3017e288bd3bece120968a8aa7988aab282-file.png)

Captured from the local distribution with mocked activity events.

| Area | AS IS | TO BE |
| --- | --- | --- |
| Settings navigation | Local and flag-off users use the legacy grouping
and Workspace panel, while cloud users can receive the V1 structure. |
All logged-in local and cloud users share the V1 Workspace / Account /
General / Other organization. |
| Workspace billing | Plan & Credits and Members depend on cloud/billing
configuration. | Plan & Credits and Members are consistently surfaced
under Workspace; existing owner-only policy rules remain unchanged. |
| Activity | Uses the placeholder workspace ledger and search path. |
Reuses the paginated `UsageLogsTable` backed by billing events for
personal and team workspaces, with the obsolete search removed. |
| Existing local flows | Credits navigation is coupled to the legacy
local settings structure. | The existing local Credits route remains
reachable for profile-menu and top-up flows while the V1 workspace
sections are also available. |
| Future workspace activity | The backend-dependent activity
implementation is blocked. | `WorkspaceActivityContent` stays intact for
FE-1249 and can replace the stopgap once backend data is available. |

## Design references

- [Unified V1 Tab Layout for Local &
Cloud](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=6258-41203)
— local unsubscribed, cloud unsubscribed, and subscribed settings
variants
- [Credits / Plan & Credits tab
variants](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=5874-72767)
— Overview/Credits and Activity states
- [Profile menu
updates](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=6258-41204)
— aligned billing navigation labels and entry points
- [Settings sidebar
reference](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=5999-37953)

Based on the approach in draft PRs Comfy-Org#15206 and Comfy-Org#15219, rebuilt from
`main`.

## Verification

- `pnpm test:unit src/platform/settings/composables/useSettingUI.test.ts
src/platform/workspace/components/dialogs/settings/PlanCreditsPanelContent.test.ts`
(18 passed)
- `pnpm test:unit src/platform/workspace` (1,118 passed)
- `pnpm typecheck`
- Targeted oxlint and ESLint (0 warnings/errors)
- `pnpm format:check` for changed files

FE-1609

---------

Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:R3 PR risk grade (advisory shadow check; grader-owned)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant