Skip to content

feat(workspace): expose workspace switcher on desktop/local for credit selection - #15051

Draft
comfydesigner wants to merge 7 commits into
mainfrom
comfydesigner/fe-1584-local-workspace-switcher
Draft

feat(workspace): expose workspace switcher on desktop/local for credit selection#15051
comfydesigner wants to merge 7 commits into
mainfrom
comfydesigner/fe-1584-local-workspace-switcher

Conversation

@comfydesigner

@comfydesigner comfydesigner commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Local/desktop users can't switch workspaces, so credits purchased on any non-Personal workspace are unusable locally (Pylon 15986). This exposes the existing cloud workspace switcher on non-cloud distributions, scoped to credit-wallet selection only — no asset/file semantics, no settings-surface changes.

  • WorkspaceAuthGate: on non-cloud, hydrate the team workspace store in the background after Firebase auth resolves — never blocks rendering, and the app is untouched if it fails (offline keeps the cached last selection)
  • CurrentUserPopoverLegacy: workspace row + switcher panel (same components as cloud), shown once the store is ready
  • WorkspaceSwitcherPopover: create-workspace footer is now cloud-only — a workspace created from local would be an empty wallet with no local way to subscribe or manage it
  • CurrentUserButton: topbar shows the workspace avatar whenever a non-personal workspace is active (was cloud-only)

No new strings. Switch behavior is the existing store path: context clear + reload, selection persisted per device (personal default).

Design: stripped MVP board · popover + menu
Linear: FE-1584

Review notes

  • Draft pending desktop verification of the acceptance criteria on FE-1584 — especially that balance reads, Add credits, and run submission follow the selected workspace context, and that the workspace token exchange works on the desktop client
  • Existing CurrentUserPopoverLegacy tests gained a testing Pinia (component now reads the team workspace store); switcher tests pin isCloud and cover the hidden footer

┆Linear: FE-1584

Status & handoff (2026-08-11)

Verified working end-to-end on localhost dev (switcher renders, switch reloads into the selected workspace, balance follows) via the opt-in dev proxy: DISTRIBUTION=localhost WORKSPACE_DEV_PROXY=https://testcloud.comfy.org pnpm dev with a local ComfyUI on 8188. Dev builds pair with staging Firebase, so testcloud is the only valid gateway for local testing.

Merged as-is this ships dormant: production non-cloud builds cannot reach the workspace routes (gateway-only + CORS), so init fails silently and the switcher stays hidden. Remaining to activate and finish:

  1. BE dependency (ship blocker): expose /workspaces, /auth/token, /auth/session, workspace /billing/* to non-cloud clients — on api.comfy.org (pattern local billing already uses) or via gateway CORS for desktop origins; prod-Firebase tokens must validate. Then swap these clients to a distribution-aware absolute base instead of relative api.apiURL() paths.
  2. Open bug: Settings → Credits activity spins indefinitely for some workspaces on the non-cloud workspace-billing path (suspect token exchange or getBillingEvents; needs a network capture).
  3. Follow-up fix: customerEventsService uses getAuthHeader() (now workspace-preferring) for a user-scoped /customers/* endpoint — should use getFirebaseAuthHeader() per authStore's contract.
  4. Verify: top-up lands in the selected workspace; a partner-node run from local debits the selected team pool (backend node auth now receives the workspace token — unverified end-to-end).

Details and acceptance criteria on FE-1584.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

🎭 Playwright: ✅ 1802 passed, 0 failed · 2 flaky

📊 Browser Reports
  • chromium: View Report (✅ 1781 / ❌ 0 / ⚠️ 2 / ⏭️ 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)

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 08/11/2026, 09:59:51 PM UTC

Links

📦 Bundle: 8.7 MB gzip 🔴 +1.87 kB

Details

Summary

  • Raw size: 36.7 MB baseline 36.7 MB — 🔴 +5.65 kB
  • Gzip: 8.7 MB baseline 8.69 MB — 🔴 +1.87 kB
  • Brotli: 6.06 MB baseline 6.06 MB — 🔴 +1.58 kB
  • Bundles: 437 current • 436 baseline • 146 added / 145 removed

Category Glance
Graph Workspace 🔴 +2.58 kB (1.36 MB) · Other 🔴 +1.89 kB (14 MB) · Data & Services 🔴 +1.17 kB (3.5 MB) · Vendor & Third-Party ⚪ 0 B (16.3 MB) · Panels & Settings ⚪ 0 B (565 kB) · Utilities & Hooks ⚪ 0 B (550 kB) · + 5 more

App Entry Points — 3.67 kB (baseline 3.67 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-B0rYMiwS.js (new) 3.67 kB 🔴 +3.67 kB 🔴 +1.84 kB 🔴 +1.59 kB
assets/index-XOO5r9j7.js (removed) 3.67 kB 🟢 -3.67 kB 🟢 -1.84 kB 🟢 -1.59 kB

Status: 1 added / 1 removed

Graph Workspace — 1.36 MB (baseline 1.36 MB) • 🔴 +2.58 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-xmS5LdfR.js (new) 1.36 MB 🔴 +1.36 MB 🔴 +295 kB 🔴 +222 kB
assets/GraphView-D3yn5Mn5.js (removed) 1.35 MB 🟢 -1.35 MB 🟢 -294 kB 🟢 -221 kB
assets/WidgetCompositor-CDwnhaC9.js (removed) 8.17 kB 🟢 -8.17 kB 🟢 -2.75 kB 🟢 -2.45 kB
assets/WidgetCompositor-DWFedBUW.js (new) 8.17 kB 🔴 +8.17 kB 🔴 +2.75 kB 🔴 +2.46 kB

Status: 2 added / 2 removed / 1 unchanged

Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-CY8r7DJc.js (removed) 25 kB 🟢 -25 kB 🟢 -6.24 kB 🟢 -5.52 kB
assets/CloudSurveyView-ZKxqCXGw.js (new) 25 kB 🔴 +25 kB 🔴 +6.24 kB 🔴 +5.53 kB
assets/CloudLayoutView-BxcOQq_n.js (new) 21.8 kB 🔴 +21.8 kB 🔴 +6.56 kB 🔴 +5.74 kB
assets/CloudLayoutView-iHzQiZCP.js (removed) 21.8 kB 🟢 -21.8 kB 🟢 -6.56 kB 🟢 -5.74 kB
assets/UserCheckView-D2K_Nm52.js (new) 8.75 kB 🔴 +8.75 kB 🔴 +2.19 kB 🔴 +1.89 kB
assets/UserCheckView-D3_s53Wm.js (removed) 8.75 kB 🟢 -8.75 kB 🟢 -2.19 kB 🟢 -1.9 kB
assets/CloudLoginView-Ba_t4Vm6.js (removed) 8.74 kB 🟢 -8.74 kB 🟢 -2.55 kB 🟢 -2.26 kB
assets/CloudLoginView-DT3oUesn.js (new) 8.74 kB 🔴 +8.74 kB 🔴 +2.55 kB 🔴 +2.26 kB
assets/useCloudAuthPage-CzgDi3Hd.js (removed) 7.08 kB 🟢 -7.08 kB 🟢 -2.51 kB 🟢 -2.2 kB
assets/useCloudAuthPage-Dpa56JkY.js (new) 7.08 kB 🔴 +7.08 kB 🔴 +2.51 kB 🔴 +2.21 kB
assets/CloudSignupView-BgcK04uR.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -2.17 kB 🟢 -1.91 kB
assets/CloudSignupView-DC6y6Cl8.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +2.17 kB 🔴 +1.91 kB
assets/WidgetTextPreview-BIGStghl.js (removed) 6.07 kB 🟢 -6.07 kB 🟢 -2.13 kB 🟢 -1.88 kB
assets/WidgetTextPreview-C5Sizsqh.js (new) 6.07 kB 🔴 +6.07 kB 🔴 +2.13 kB 🔴 +1.89 kB
assets/CloudSubscriptionRedirectView-9G_mZQ15.js (removed) 6.05 kB 🟢 -6.05 kB 🟢 -2.25 kB 🟢 -1.96 kB
assets/CloudSubscriptionRedirectView-CIJjiwRe.js (new) 6.05 kB 🔴 +6.05 kB 🔴 +2.25 kB 🔴 +1.96 kB
assets/UserSelectView-C0QBqdCK.js (new) 5.49 kB 🔴 +5.49 kB 🔴 +1.95 kB 🔴 +1.71 kB
assets/UserSelectView-DBwXFkTr.js (removed) 5.49 kB 🟢 -5.49 kB 🟢 -1.95 kB 🟢 -1.71 kB
assets/CloudForgotPasswordView-C2pKdZfD.js (new) 4.97 kB 🔴 +4.97 kB 🔴 +1.72 kB 🔴 +1.49 kB
assets/CloudForgotPasswordView-CZ23-tXW.js (removed) 4.97 kB 🟢 -4.97 kB 🟢 -1.72 kB 🟢 -1.49 kB
assets/CloudAuthTimeoutView-BKP03nHE.js (removed) 4.43 kB 🟢 -4.43 kB 🟢 -1.54 kB 🟢 -1.33 kB
assets/CloudAuthTimeoutView-DbN57Fd6.js (new) 4.43 kB 🔴 +4.43 kB 🔴 +1.54 kB 🔴 +1.35 kB
assets/OAuthLayoutView-BWAGWxP3.js (new) 1.31 kB 🔴 +1.31 kB 🔴 +702 B 🔴 +598 B
assets/OAuthLayoutView-CZH2_cVN.js (removed) 1.31 kB 🟢 -1.31 kB 🟢 -696 B 🟢 -600 B
assets/WidgetTextPreview-CLz7Xeia.js (removed) 131 B 🟢 -131 B 🟢 -100 B 🟢 -90 B
assets/WidgetTextPreview-CO6jYfhs.js (new) 131 B 🔴 +131 B 🔴 +100 B 🔴 +88 B

Status: 13 added / 13 removed / 4 unchanged

Panels & Settings — 565 kB (baseline 565 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-Bb0BveK4.js (removed) 49.4 kB 🟢 -49.4 kB 🟢 -9.94 kB 🟢 -8.81 kB
assets/KeybindingPanel-BqXppPo8.js (new) 49.4 kB 🔴 +49.4 kB 🔴 +9.94 kB 🔴 +8.81 kB
assets/SecretsPanel-Ce1E7eE3.js (removed) 33.7 kB 🟢 -33.7 kB 🟢 -7.87 kB 🟢 -6.9 kB
assets/SecretsPanel-g79_tLee.js (new) 33.7 kB 🔴 +33.7 kB 🔴 +7.86 kB 🔴 +6.9 kB
assets/CreditsPanel-CLSz0mkq.js (new) 11.5 kB 🔴 +11.5 kB 🔴 +3.2 kB 🔴 +2.8 kB
assets/CreditsPanel-CP1ioakN.js (removed) 11.5 kB 🟢 -11.5 kB 🟢 -3.2 kB 🟢 -2.82 kB
assets/AboutPanel-BwjYnm24.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.02 kB 🟢 -2.73 kB
assets/AboutPanel-zgv3jsoT.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.03 kB 🔴 +2.74 kB
assets/ExtensionPanel-b_48rOHH.js (removed) 9.19 kB 🟢 -9.19 kB 🟢 -2.51 kB 🟢 -2.22 kB
assets/ExtensionPanel-VlcqQ5ZA.js (new) 9.19 kB 🔴 +9.19 kB 🔴 +2.51 kB 🔴 +2.22 kB
assets/ServerConfigPanel-BOmWzSJ8.js (new) 6.09 kB 🔴 +6.09 kB 🔴 +1.93 kB 🔴 +1.72 kB
assets/ServerConfigPanel-X09tpH8M.js (removed) 6.09 kB 🟢 -6.09 kB 🟢 -1.93 kB 🟢 -1.72 kB
assets/UserPanel-ClDKUWD7.js (removed) 5.73 kB 🟢 -5.73 kB 🟢 -1.78 kB 🟢 -1.54 kB
assets/UserPanel-CW9dDznT.js (new) 5.73 kB 🔴 +5.73 kB 🔴 +1.78 kB 🔴 +1.54 kB
assets/refreshRemoteConfig-CDEoYvY1.js (new) 3.44 kB 🔴 +3.44 kB 🔴 +1.33 kB 🔴 +1.17 kB
assets/refreshRemoteConfig-D8EXd-u5.js (removed) 3.44 kB 🟢 -3.44 kB 🟢 -1.33 kB 🟢 -1.17 kB
assets/cloudRemoteConfig-Dht7jFO3.js (new) 951 B 🔴 +951 B 🔴 +514 B 🔴 +436 B
assets/cloudRemoteConfig-oHmgZext.js (removed) 951 B 🟢 -951 B 🟢 -514 B 🟢 -420 B
assets/refreshRemoteConfig-B5UCpeyA.js (removed) 110 B 🟢 -110 B 🟢 -89 B 🟢 -77 B
assets/refreshRemoteConfig-BDuMgpnI.js (new) 110 B 🔴 +110 B 🔴 +89 B 🔴 +83 B

Status: 10 added / 10 removed / 16 unchanged

User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SignUpForm-BOzs0amc.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -4.31 kB 🟢 -3.76 kB
assets/SignUpForm-Bs38oiA_.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +4.31 kB 🔴 +3.76 kB
assets/auth-0nNimKqm.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.28 kB 🔴 +1.1 kB
assets/auth-BokyovlP.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.28 kB 🟢 -1.1 kB
assets/UpdatePasswordContent-1lMjX_u4.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -842 B 🟢 -733 B
assets/UpdatePasswordContent-BpQ3Xow1.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +843 B 🔴 +733 B
assets/authStore-BjvU-qBZ.js (new) 128 B 🔴 +128 B 🔴 +104 B 🔴 +105 B
assets/authStore-Cd8-yMM0.js (removed) 128 B 🟢 -128 B 🟢 -104 B 🟢 -105 B
assets/workspaceAuthStore-B1BgnXaZ.js (new) 108 B 🔴 +108 B 🔴 +99 B 🔴 +108 B
assets/workspaceAuthStore-DPLbsuaA.js (removed) 108 B 🟢 -108 B 🟢 -99 B 🟢 -106 B
assets/auth-DvQ4w72A.js (new) 105 B 🔴 +105 B 🔴 +96 B 🔴 +87 B
assets/auth-DxTd3LTI.js (removed) 105 B 🟢 -105 B 🟢 -96 B 🟢 -80 B

Status: 6 added / 6 removed / 4 unchanged

Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-iHePV07Q.js (removed) 90.1 kB 🟢 -90.1 kB 🟢 -19.3 kB 🟢 -16.5 kB
assets/ComfyHubPublishDialog-O-whTaxo.js (new) 90.1 kB 🔴 +90.1 kB 🔴 +19.3 kB 🔴 +16.5 kB
assets/useShareDialog-BVVdvFbb.js (new) 23.9 kB 🔴 +23.9 kB 🔴 +5.69 kB 🔴 +5.03 kB
assets/useShareDialog-n0DdFgM7.js (removed) 23.9 kB 🟢 -23.9 kB 🟢 -5.69 kB 🟢 -5.03 kB
assets/feedbackDialog-BNSdbFLj.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.86 kB 🔴 +1.59 kB
assets/feedbackDialog-CG9J8L_R.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.86 kB 🟢 -1.59 kB
assets/useRangeEditor-BCIPeND7.js (removed) 3.29 kB 🟢 -3.29 kB 🟢 -1.14 kB 🟢 -1.04 kB
assets/useRangeEditor-BEgPkRyO.js (new) 3.29 kB 🔴 +3.29 kB 🔴 +1.14 kB 🔴 +1.03 kB
assets/useLayerEditor-BjT0vKIQ.js (new) 1.01 kB 🔴 +1.01 kB 🔴 +490 B 🔴 +406 B
assets/useLayerEditor-BOvxNR3T.js (removed) 1.01 kB 🟢 -1.01 kB 🟢 -491 B 🟢 -407 B
assets/ComfyHubPublishDialog-CO2CuIaJ.js (removed) 143 B 🟢 -143 B 🟢 -105 B 🟢 -88 B
assets/ComfyHubPublishDialog-XYxVGcLH.js (new) 143 B 🔴 +143 B 🔴 +105 B 🔴 +91 B
assets/useSubscriptionDialog-BK2o4dyY.js (new) 108 B 🔴 +108 B 🔴 +102 B 🔴 +85 B
assets/useSubscriptionDialog-DSn_VhDE.js (removed) 108 B 🟢 -108 B 🟢 -102 B 🟢 -86 B

Status: 7 added / 7 removed / 1 unchanged

UI Components — 67.1 kB (baseline 67.1 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-D06o20ss.js (removed) 14.6 kB 🟢 -14.6 kB 🟢 -3.96 kB 🟢 -3.51 kB
assets/ComfyQueueButton-OLvtsWUK.js (new) 14.6 kB 🔴 +14.6 kB 🔴 +3.96 kB 🔴 +3.51 kB
assets/useTerminalTabs-DHzn_Wcq.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.69 kB 🔴 +3.27 kB
assets/useTerminalTabs-UiXlktCL.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.69 kB 🟢 -3.26 kB
assets/InviteMembersForm-CkBxrXk4.js (removed) 8.2 kB 🟢 -8.2 kB 🟢 -2.72 kB 🟢 -2.44 kB
assets/InviteMembersForm-DthViqKq.js (new) 8.2 kB 🔴 +8.2 kB 🔴 +2.72 kB 🔴 +2.43 kB
assets/SubscribeButton-B-AzjpNE.js (removed) 2.15 kB 🟢 -2.15 kB 🟢 -966 B 🟢 -845 B
assets/SubscribeButton-Bj93KqFL.js (new) 2.15 kB 🔴 +2.15 kB 🔴 +967 B 🔴 +846 B
assets/cloudFeedbackTopbarButton-BoTDeLiQ.js (removed) 705 B 🟢 -705 B 🟢 -423 B 🟢 -360 B
assets/cloudFeedbackTopbarButton-BQTk6xRt.js (new) 705 B 🔴 +705 B 🔴 +421 B 🔴 +361 B
assets/ComfyQueueButton-CBDOA0CA.js (new) 128 B 🔴 +128 B 🔴 +99 B 🔴 +92 B
assets/ComfyQueueButton-CWaJOYj3.js (removed) 128 B 🟢 -128 B 🟢 -99 B 🟢 -95 B

Status: 6 added / 6 removed / 8 unchanged

Data & Services — 3.5 MB (baseline 3.5 MB) • 🔴 +1.17 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/settingStore-3DEkdbHm.js (new) 3.22 MB 🔴 +3.22 MB 🔴 +747 kB 🔴 +562 kB
assets/settingStore-C6eSZaMK.js (removed) 3.22 MB 🟢 -3.22 MB 🟢 -747 kB 🟢 -562 kB
assets/load3dService-C2w-Rv8A.js (removed) 132 kB 🟢 -132 kB 🟢 -29.3 kB 🟢 -24.6 kB
assets/load3dService-DFpIFgY1.js (new) 132 kB 🔴 +132 kB 🔴 +29.3 kB 🔴 +24.6 kB
assets/api-2RYZVF2F.js (new) 98.3 kB 🔴 +98.3 kB 🔴 +27.2 kB 🔴 +23.4 kB
assets/api-BtSYSJKL.js (removed) 98.3 kB 🟢 -98.3 kB 🟢 -27.2 kB 🟢 -23.4 kB
assets/workflowShareService-BMGV7tny.js (removed) 16.5 kB 🟢 -16.5 kB 🟢 -4.91 kB 🟢 -4.35 kB
assets/workflowShareService-BoDhpdbp.js (new) 16.5 kB 🔴 +16.5 kB 🔴 +4.91 kB 🔴 +4.35 kB
assets/keybindingService-B1C-oswk.js (new) 6.89 kB 🔴 +6.89 kB 🔴 +1.73 kB 🔴 +1.5 kB
assets/keybindingService-BtuMAxfn.js (removed) 6.89 kB 🟢 -6.89 kB 🟢 -1.73 kB 🟢 -1.5 kB
assets/releaseStore-odR82fVp.js (removed) 6.72 kB 🟢 -6.72 kB 🟢 -2.03 kB 🟢 -1.78 kB
assets/releaseStore-rhTVWFqA.js (new) 6.72 kB 🔴 +6.72 kB 🔴 +2.03 kB 🔴 +1.78 kB
assets/systemStatsStore-BMuWp6yw.js (removed) 4.93 kB 🟢 -4.93 kB 🟢 -1.74 kB 🟢 -1.47 kB
assets/systemStatsStore-DX3Th6Qy.js (new) 4.93 kB 🔴 +4.93 kB 🔴 +1.74 kB 🔴 +1.47 kB
assets/userStore-CCco9OUK.js (removed) 2.38 kB 🟢 -2.38 kB 🟢 -898 B 🟢 -791 B
assets/userStore-FLJIu2Ak.js (new) 2.38 kB 🔴 +2.38 kB 🔴 +899 B 🔴 +792 B
assets/audioService-BOnvRqrX.js (new) 1.71 kB 🔴 +1.71 kB 🔴 +828 B 🔴 +719 B
assets/audioService-EbeBKLtk.js (removed) 1.71 kB 🟢 -1.71 kB 🟢 -828 B 🟢 -720 B
assets/dialogService-JnNETSk1.js (new) 98 B 🔴 +98 B 🔴 +97 B 🔴 +80 B
assets/dialogService-mxysN6Uy.js (removed) 98 B 🟢 -98 B 🟢 -97 B 🟢 -90 B
assets/releaseStore-8bvpyGIp.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -87 B
assets/releaseStore-CyYmvIOc.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +79 B
assets/settingStore-bDpzPirU.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -81 B
assets/settingStore-vg3GIY1v.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +85 B
assets/assetsStore-B9lwfspg.js (removed) 94 B 🟢 -94 B 🟢 -92 B 🟢 -82 B
assets/assetsStore-CwHH0AKH.js (new) 94 B 🔴 +94 B 🔴 +92 B 🔴 +85 B
assets/api-MSJy3Z6w.js (new) 62 B 🔴 +62 B 🔴 +74 B 🔴 +66 B
assets/api-qf1_O0Bw.js (removed) 62 B 🟢 -62 B 🟢 -74 B 🟢 -66 B

Status: 14 added / 14 removed / 3 unchanged

Utilities & Hooks — 550 kB (baseline 550 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-BakqdX_0.js (removed) 236 kB 🟢 -236 kB 🟢 -53 kB 🟢 -43.1 kB
assets/useConflictDetection-D3yzpdhj.js (new) 236 kB 🔴 +236 kB 🔴 +53 kB 🔴 +43.1 kB
assets/useLayerEditorSession-D94DHfvX.js (removed) 158 kB 🟢 -158 kB 🟢 -40.8 kB 🟢 -34.3 kB
assets/useLayerEditorSession-DikYVUP1.js (new) 158 kB 🔴 +158 kB 🔴 +40.8 kB 🔴 +34.3 kB
assets/useLoad3d-Cwn3WDWy.js (new) 25.8 kB 🔴 +25.8 kB 🔴 +5.8 kB 🔴 +5.14 kB
assets/useLoad3d-DXAQH_sr.js (removed) 25.8 kB 🟢 -25.8 kB 🟢 -5.8 kB 🟢 -5.16 kB
assets/useLoad3dViewer-BThKgXOw.js (removed) 21.2 kB 🟢 -21.2 kB 🟢 -4.98 kB 🟢 -4.36 kB
assets/useLoad3dViewer-DKvlsjg2.js (new) 21.2 kB 🔴 +21.2 kB 🔴 +4.98 kB 🔴 +4.36 kB
assets/useImageCrop-Ct_z8J6z.js (new) 14.9 kB 🔴 +14.9 kB 🔴 +3.42 kB 🔴 +2.99 kB
assets/useImageCrop-Cv3kU56Q.js (removed) 14.9 kB 🟢 -14.9 kB 🟢 -3.42 kB 🟢 -2.98 kB
assets/useDowngradeToPersonal-BOguv6q7.js (new) 10.9 kB 🔴 +10.9 kB 🔴 +2.75 kB 🔴 +2.36 kB
assets/useDowngradeToPersonal-DSGiBluc.js (removed) 10.9 kB 🟢 -10.9 kB 🟢 -2.75 kB 🟢 -2.37 kB
assets/useFeatureFlags-CBc6yI0b.js (removed) 6.99 kB 🟢 -6.99 kB 🟢 -2 kB 🟢 -1.7 kB
assets/useFeatureFlags-CPfz8XKl.js (new) 6.99 kB 🔴 +6.99 kB 🔴 +2 kB 🔴 +1.7 kB
assets/useCompositorLayers-2ckUZK39.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -897 B 🟢 -797 B
assets/useCompositorLayers-DxFEo6w9.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +897 B 🔴 +797 B
assets/assetPreviewUtil-b45vsYrl.js (new) 2.35 kB 🔴 +2.35 kB 🔴 +971 B 🔴 +837 B
assets/assetPreviewUtil-nawzfN9J.js (removed) 2.35 kB 🟢 -2.35 kB 🟢 -970 B 🟢 -839 B
assets/useUpstreamValue-DHi_08Ey.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -758 B 🟢 -676 B
assets/useUpstreamValue-ipPonldl.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +757 B 🔴 +678 B
assets/useWorkspaceTierLabel-BkZ4Yo0h.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +814 B 🔴 +698 B
assets/useWorkspaceTierLabel-CaLlMNgj.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -812 B 🟢 -698 B
assets/subscriptionCheckoutUtil-CUWNcaS9.js (removed) 877 B 🟢 -877 B 🟢 -523 B 🟢 -458 B
assets/subscriptionCheckoutUtil-D_mt-uJQ.js (new) 877 B 🔴 +877 B 🔴 +522 B 🔴 +437 B
assets/useSessionCookie-CzzAFMkQ.js (removed) 652 B 🟢 -652 B 🟢 -337 B 🟢 -294 B
assets/useSessionCookie-D1n0PNSz.js (new) 652 B 🔴 +652 B 🔴 +339 B 🔴 +293 B
assets/useLoad3d-_W4Nprvn.js (removed) 311 B 🟢 -311 B 🟢 -165 B 🟢 -152 B
assets/useLoad3d-mdRSpZfS.js (new) 311 B 🔴 +311 B 🔴 +163 B 🔴 +147 B
assets/useSessionCookie-C2iaf0y8.js (new) 101 B 🔴 +101 B 🔴 +86 B 🔴 +85 B
assets/useSessionCookie-DwbBTZmM.js (removed) 101 B 🟢 -101 B 🟢 -86 B 🟢 -83 B
assets/useFeatureFlags-0w2BKQOz.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +81 B
assets/useFeatureFlags-CYqPkbIz.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -80 B
assets/useLoad3dViewer-CeRScJaD.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -86 B
assets/useLoad3dViewer-DlMLXDaL.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +82 B
assets/useCurrentUser-C98fKHQs.js (removed) 94 B 🟢 -94 B 🟢 -95 B 🟢 -80 B
assets/useCurrentUser-CPLuFUNz.js (new) 94 B 🔴 +94 B 🔴 +95 B 🔴 +84 B

Status: 18 added / 18 removed / 20 unchanged

Vendor & Third-Party — 16.3 MB (baseline 16.3 MB) • ⚪ 0 B

External libraries and shared vendor chunks

Status: 17 unchanged

Other — 14 MB (baseline 14 MB) • 🔴 +1.89 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-CoD7rvs5.js (new) 115 kB 🔴 +115 kB 🔴 +29.7 kB 🔴 +25.1 kB
assets/core-IfjwmzEB.js (removed) 115 kB 🟢 -115 kB 🟢 -29.7 kB 🟢 -25.1 kB
assets/WidgetSelect-BoxRJI2g.js (new) 88.8 kB 🔴 +88.8 kB 🔴 +20.1 kB 🔴 +17.2 kB
assets/WidgetSelect-CGP-sVfH.js (removed) 88.8 kB 🟢 -88.8 kB 🟢 -20.1 kB 🟢 -17.2 kB
assets/SubscriptionPanelContentWorkspace-C3Sm4Hk-.js (new) 80 kB 🔴 +80 kB 🔴 +15.8 kB 🔴 +13.6 kB
assets/SubscriptionPanelContentWorkspace-VC791dP6.js (removed) 80 kB 🟢 -80 kB 🟢 -15.8 kB 🟢 -13.6 kB
assets/Load3D-DoJ1FAeV.js (new) 71.3 kB 🔴 +71.3 kB 🔴 +11.7 kB 🔴 +9.98 kB
assets/Load3D-SyLusnyO.js (removed) 71.3 kB 🟢 -71.3 kB 🟢 -11.7 kB 🟢 -9.98 kB
assets/WidgetVideoEdit-Bf50Gzps.js (new) 67.5 kB 🔴 +67.5 kB 🔴 +15.7 kB 🔴 +13.9 kB
assets/WidgetVideoEdit-siTEbM2M.js (removed) 67.5 kB 🟢 -67.5 kB 🟢 -15.7 kB 🟢 -13.9 kB
assets/SubscriptionTransitionPreviewWorkspace-BRFybEgN.js (new) 66.4 kB 🔴 +66.4 kB 🔴 +13.4 kB 🔴 +11.7 kB
assets/SubscriptionTransitionPreviewWorkspace-BTX__cuG.js (removed) 66.4 kB 🟢 -66.4 kB 🟢 -13.4 kB 🟢 -11.7 kB
assets/WorkspaceSettingsPanelContent-BfE2n1Mr.js (new) 61.7 kB 🔴 +61.7 kB 🔴 +13.2 kB 🔴 +11.5 kB
assets/WorkspaceSettingsPanelContent-BpSK5x4J.js (removed) 61.7 kB 🟢 -61.7 kB 🟢 -13.2 kB 🟢 -11.5 kB
assets/Preview3d-BaqJE-wC.js (new) 50.9 kB 🔴 +50.9 kB 🔴 +8.31 kB 🔴 +7.25 kB
assets/Preview3d-CsuS6AoK.js (removed) 50.9 kB 🟢 -50.9 kB 🟢 -8.31 kB 🟢 -7.24 kB
assets/main-ylTrdbEe.js (new) 46.3 kB 🔴 +46.3 kB 🔴 +13.5 kB 🔴 +11.7 kB
assets/main-BtnR_D1H.js (removed) 45.1 kB 🟢 -45.1 kB 🟢 -13.1 kB 🟢 -11.4 kB
assets/SubscriptionRequiredDialogContentUnified-BFRN3wY0.js (new) 42.6 kB 🔴 +42.6 kB 🔴 +9.35 kB 🔴 +8.16 kB
assets/SubscriptionRequiredDialogContentUnified-BSj0Uuf-.js (removed) 42.6 kB 🟢 -42.6 kB 🟢 -9.35 kB 🟢 -8.16 kB
assets/LayerEditorContent-BQUtJdke.js (new) 42.5 kB 🔴 +42.5 kB 🔴 +9.61 kB 🔴 +8.43 kB
assets/LayerEditorContent-DeRAge7Q.js (removed) 42.5 kB 🟢 -42.5 kB 🟢 -9.61 kB 🟢 -8.44 kB
assets/WidgetBoundingBoxes-Tf03FEb8.js (new) 33.7 kB 🔴 +33.7 kB 🔴 +9.16 kB 🔴 +8.13 kB
assets/WidgetBoundingBoxes-zJMzry6i.js (removed) 33.7 kB 🟢 -33.7 kB 🟢 -9.16 kB 🟢 -8.13 kB
assets/WidgetPainter-CHWx7yEr.js (removed) 32.6 kB 🟢 -32.6 kB 🟢 -7.87 kB 🟢 -6.97 kB
assets/WidgetPainter-m6JaFoT3.js (new) 32.6 kB 🔴 +32.6 kB 🔴 +7.87 kB 🔴 +6.97 kB
assets/Load3dViewerContent-Bw4tkWPn.js (new) 30.8 kB 🔴 +30.8 kB 🔴 +6.28 kB 🔴 +5.45 kB
assets/Load3dViewerContent-Cz_E2U6S.js (removed) 30.8 kB 🟢 -30.8 kB 🟢 -6.28 kB 🟢 -5.45 kB
assets/SubscriptionRequiredDialogContent-BrTDIhWR.js (removed) 27 kB 🟢 -27 kB 🟢 -6.37 kB 🟢 -5.61 kB
assets/SubscriptionRequiredDialogContent-CfIhTYCr.js (new) 27 kB 🔴 +27 kB 🔴 +6.37 kB 🔴 +5.61 kB
assets/SubscriptionRequiredDialogContentWorkspace--yb2Qt7c.js (removed) 25.1 kB 🟢 -25.1 kB 🟢 -5.79 kB 🟢 -5.1 kB
assets/SubscriptionRequiredDialogContentWorkspace-DYhHjAfc.js (new) 25.1 kB 🔴 +25.1 kB 🔴 +5.79 kB 🔴 +5.11 kB
assets/CreditsTile-B7gJ9S_F.js (new) 24.9 kB 🔴 +24.9 kB 🔴 +6.64 kB 🔴 +5.83 kB
assets/CreditsTile-Cqtmisr1.js (removed) 24.9 kB 🟢 -24.9 kB 🟢 -6.64 kB 🟢 -5.83 kB
assets/load3d-BPUqCOP5.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.4 kB 🟢 -4.67 kB
assets/load3d-c4ed_wNW.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.41 kB 🔴 +4.67 kB
assets/CurrentUserPopoverWorkspace-DHOUrG5t.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -4.84 kB 🟢 -4.32 kB
assets/SignInContent-2R47x_3D.js (new) 20.2 kB 🔴 +20.2 kB 🔴 +5.09 kB 🔴 +4.46 kB
assets/SignInContent-DbvrRLzs.js (removed) 20.2 kB 🟢 -20.2 kB 🟢 -5.09 kB 🟢 -4.45 kB
assets/WidgetRecordAudio-Bfp2S3nj.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.59 kB 🟢 -4.1 kB
assets/WidgetRecordAudio-CpideHLh.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.6 kB 🔴 +4.1 kB
assets/CurrentUserPopoverWorkspace-D2uhWBSt.js (new) 15 kB 🔴 +15 kB 🔴 +3.56 kB 🔴 +3.15 kB
assets/WidgetInputNumber-CZ4Vn5-Z.js (removed) 13.9 kB 🟢 -13.9 kB 🟢 -3.63 kB 🟢 -3.21 kB
assets/WidgetInputNumber-DxiotXSN.js (new) 13.9 kB 🔴 +13.9 kB 🔴 +3.63 kB 🔴 +3.21 kB
assets/WidgetRange-CtoBFvqg.js (new) 13.7 kB 🔴 +13.7 kB 🔴 +3.55 kB 🔴 +3.11 kB
assets/WidgetRange-CuSSDGRt.js (removed) 13.7 kB 🟢 -13.7 kB 🟢 -3.55 kB 🟢 -3.13 kB
assets/WaveAudioPlayer-BmmnPBtz.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.47 kB 🔴 +3.03 kB
assets/WaveAudioPlayer-Z2lpyUlE.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.47 kB 🟢 -3.05 kB
assets/WidgetCurve-BYvp7tMs.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.47 kB 🟢 -3.15 kB
assets/WidgetCurve-Cdhr_eXc.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.47 kB 🔴 +3.14 kB
assets/TeamWorkspacesDialogContent-DgPIfeyE.js (removed) 10.3 kB 🟢 -10.3 kB 🟢 -2.96 kB 🟢 -2.63 kB
assets/TeamWorkspacesDialogContent-PRU9B05X.js (new) 10.3 kB 🔴 +10.3 kB 🔴 +2.96 kB 🔴 +2.63 kB
assets/onboardingCloudRoutes-BGrOH1MC.js (removed) 9.33 kB 🟢 -9.33 kB 🟢 -2.84 kB 🟢 -2.44 kB
assets/onboardingCloudRoutes-KlwIC7sF.js (new) 9.33 kB 🔴 +9.33 kB 🔴 +2.84 kB 🔴 +2.43 kB
assets/Load3DConfiguration-jIe-3JNL.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.61 kB 🔴 +2.3 kB
assets/Load3DConfiguration-SyeEznWo.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.61 kB 🟢 -2.29 kB
assets/WidgetImageCrop-D5Rutrip.js (removed) 8.49 kB 🟢 -8.49 kB 🟢 -2.65 kB 🟢 -2.34 kB
assets/WidgetImageCrop-D85IbFBV.js (new) 8.49 kB 🔴 +8.49 kB 🔴 +2.65 kB 🔴 +2.34 kB
assets/SetMemberCreditLimitDialogContent-BlwNHYQc.js (new) 8.47 kB 🔴 +8.47 kB 🔴 +2.34 kB 🔴 +2.05 kB
assets/SetMemberCreditLimitDialogContent-CCVJ6KhL.js (removed) 8.47 kB 🟢 -8.47 kB 🟢 -2.34 kB 🟢 -2.05 kB
assets/nodeTemplates-BnUw0jTc.js (new) 8.32 kB 🔴 +8.32 kB 🔴 +2.85 kB 🔴 +2.5 kB
assets/nodeTemplates-DRfIdtiu.js (removed) 8.32 kB 🟢 -8.32 kB 🟢 -2.85 kB 🟢 -2.5 kB
assets/NightlySurveyController-7w0dEn5K.js (new) 7.5 kB 🔴 +7.5 kB 🔴 +2.55 kB 🔴 +2.24 kB
assets/NightlySurveyController-DggerIr_.js (removed) 7.5 kB 🟢 -7.5 kB 🟢 -2.55 kB 🟢 -2.25 kB
assets/WorkspaceSwitcherPopover-D29s2ML4.js (new) 7.19 kB 🔴 +7.19 kB 🔴 +2.15 kB 🔴 +1.92 kB
assets/CloudRunButtonWrapper-BwBXlWZX.js (removed) 6.84 kB 🟢 -6.84 kB 🟢 -2.17 kB 🟢 -1.91 kB
assets/CloudRunButtonWrapper-Q77yk6md.js (new) 6.84 kB 🔴 +6.84 kB 🔴 +2.18 kB 🔴 +1.92 kB
assets/WidgetWithControl-3HwBdJy_.js (removed) 6.41 kB 🟢 -6.41 kB 🟢 -2.61 kB 🟢 -2.31 kB
assets/WidgetWithControl-B7IeDkNP.js (new) 6.41 kB 🔴 +6.41 kB 🔴 +2.61 kB 🔴 +2.31 kB
assets/missingModelMetadata--OsqJpnT.js (removed) 6.17 kB 🟢 -6.17 kB 🟢 -2.13 kB 🟢 -1.86 kB
assets/missingModelMetadata-CV61FvkB.js (new) 6.17 kB 🔴 +6.17 kB 🔴 +2.13 kB 🔴 +1.86 kB
assets/CancelSubscriptionDialogContent-BwUV97Hz.js (removed) 5.97 kB 🟢 -5.97 kB 🟢 -1.98 kB 🟢 -1.75 kB
assets/CancelSubscriptionDialogContent-DC1tmFlG.js (new) 5.97 kB 🔴 +5.97 kB 🔴 +1.98 kB 🔴 +1.75 kB
assets/load3dPreviewExtensions-DkjUfR1v.js (removed) 5.88 kB 🟢 -5.88 kB 🟢 -1.81 kB 🟢 -1.59 kB
assets/load3dPreviewExtensions-jSqhAsHE.js (new) 5.88 kB 🔴 +5.88 kB 🔴 +1.81 kB 🔴 +1.6 kB
assets/launchCancellationFlow-Buos0hN2.js (new) 5.18 kB 🔴 +5.18 kB 🔴 +1.78 kB 🔴 +1.56 kB
assets/launchCancellationFlow-DCzGkWLN.js (removed) 5.18 kB 🟢 -5.18 kB 🟢 -1.78 kB 🟢 -1.56 kB
assets/CreateWorkspaceDialogContent-B0t9YgLK.js (new) 5.12 kB 🔴 +5.12 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/CreateWorkspaceDialogContent-Mf-k7H0E.js (removed) 5.12 kB 🟢 -5.12 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/ChangeMemberRoleDialogContent-CtDMgvxm.js (new) 4.97 kB 🔴 +4.97 kB 🔴 +1.64 kB 🔴 +1.42 kB
assets/ChangeMemberRoleDialogContent-Cx42NEp_.js (removed) 4.97 kB 🟢 -4.97 kB 🟢 -1.64 kB 🟢 -1.43 kB
assets/InviteMemberDialogContent-Cvk_w6XX.js (removed) 4.96 kB 🟢 -4.96 kB 🟢 -1.64 kB 🟢 -1.43 kB
assets/InviteMemberDialogContent-jsrHcU_R.js (new) 4.96 kB 🔴 +4.96 kB 🔴 +1.64 kB 🔴 +1.44 kB
assets/EditWorkspaceDialogContent-CbKl7AZe.js (new) 4.93 kB 🔴 +4.93 kB 🔴 +1.76 kB 🔴 +1.53 kB
assets/EditWorkspaceDialogContent-pyVjw357.js (removed) 4.93 kB 🟢 -4.93 kB 🟢 -1.76 kB 🟢 -1.53 kB
assets/WidgetTextarea-OSf793IC.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.87 kB 🟢 -1.63 kB
assets/WidgetTextarea-Y4dEakM8.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.87 kB 🔴 +1.63 kB
assets/saveMesh-BG5sxkLj.js (new) 4.76 kB 🔴 +4.76 kB 🔴 +1.52 kB 🔴 +1.35 kB
assets/saveMesh-BQKpZLus.js (removed) 4.76 kB 🟢 -4.76 kB 🟢 -1.52 kB 🟢 -1.34 kB
assets/WorkspacePanelContent-Cz6if3Xe.js (removed) 4.74 kB 🟢 -4.74 kB 🟢 -1.63 kB 🟢 -1.43 kB
assets/WorkspacePanelContent-DgNUQzqU.js (new) 4.74 kB 🔴 +4.74 kB 🔴 +1.62 kB 🔴 +1.43 kB
assets/ValueControlPopover-CJ7Kz7HS.js (new) 4.49 kB 🔴 +4.49 kB 🔴 +1.55 kB 🔴 +1.38 kB
assets/ValueControlPopover-Vq-K8l7e.js (removed) 4.49 kB 🟢 -4.49 kB 🟢 -1.55 kB 🟢 -1.38 kB
assets/DeleteWorkspaceDialogContent-BBZNgoLf.js (removed) 3.84 kB 🟢 -3.84 kB 🟢 -1.44 kB 🟢 -1.24 kB
assets/DeleteWorkspaceDialogContent-goX3NJKV.js (new) 3.84 kB 🔴 +3.84 kB 🔴 +1.44 kB 🔴 +1.26 kB
assets/RemoveMemberDialogContent-CAlEp2Ek.js (removed) 3.76 kB 🟢 -3.76 kB 🟢 -1.38 kB 🟢 -1.19 kB
assets/RemoveMemberDialogContent-y8KCFdPC.js (new) 3.76 kB 🔴 +3.76 kB 🔴 +1.38 kB 🔴 +1.2 kB
assets/RevokeInviteDialogContent-CrDfR0D6.js (removed) 3.67 kB 🟢 -3.67 kB 🟢 -1.39 kB 🟢 -1.21 kB
assets/RevokeInviteDialogContent-GwElZ9OU.js (new) 3.67 kB 🔴 +3.67 kB 🔴 +1.39 kB 🔴 +1.21 kB
assets/LeaveWorkspaceDialogContent-BdSCJg0D.js (removed) 3.67 kB 🟢 -3.67 kB 🟢 -1.38 kB 🟢 -1.19 kB
assets/LeaveWorkspaceDialogContent-vptF3PYs.js (new) 3.67 kB 🔴 +3.67 kB 🔴 +1.38 kB 🔴 +1.19 kB
assets/InviteMemberUpsellDialogContent-BusvtrXU.js (removed) 3.47 kB 🟢 -3.47 kB 🟢 -1.24 kB 🟢 -1.09 kB
assets/InviteMemberUpsellDialogContent-DazDrfm3.js (new) 3.47 kB 🔴 +3.47 kB 🔴 +1.24 kB 🔴 +1.07 kB
assets/workspaceCheckoutTelemetry-C_ms-dTB.js (removed) 3.4 kB 🟢 -3.4 kB 🟢 -1.52 kB 🟢 -1.33 kB
assets/workspaceCheckoutTelemetry-DCN7cKzN.js (new) 3.4 kB 🔴 +3.4 kB 🔴 +1.52 kB 🔴 +1.33 kB
assets/GlobalToast-BCpqrdh1.js (removed) 3.25 kB 🟢 -3.25 kB 🟢 -1.3 kB 🟢 -1.11 kB
assets/GlobalToast-CtGR7gyZ.js (new) 3.25 kB 🔴 +3.25 kB 🔴 +1.3 kB 🔴 +1.11 kB
assets/Media3DTop-BqgEyeU8.js (new) 3.21 kB 🔴 +3.21 kB 🔴 +1.26 kB 🔴 +1.09 kB
assets/Media3DTop-C6sxIv0T.js (removed) 3.21 kB 🟢 -3.21 kB 🟢 -1.26 kB 🟢 -1.1 kB
assets/load3dAdvanced-BrMJV0Th.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.1 kB 🔴 +955 B
assets/load3dAdvanced-CvnkN6Gg.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.09 kB 🟢 -950 B
assets/SubscribeToRun-Bn9pERSU.js (new) 2.39 kB 🔴 +2.39 kB 🔴 +1.03 kB 🔴 +901 B
assets/SubscribeToRun-CcMFEBB-.js (removed) 2.39 kB 🟢 -2.39 kB 🟢 -1.03 kB 🟢 -928 B
assets/MediaAudioTop-BYxnQHb-.js (removed) 1.62 kB 🟢 -1.62 kB 🟢 -806 B 🟢 -670 B
assets/MediaAudioTop-Dx9kiekf.js (new) 1.62 kB 🔴 +1.62 kB 🔴 +805 B 🔴 +670 B
assets/cloudSessionCookie-DWJb_UPc.js (new) 933 B 🔴 +933 B 🔴 +433 B 🔴 +379 B
assets/cloudSessionCookie-O88oAbcC.js (removed) 933 B 🟢 -933 B 🟢 -434 B 🟢 -379 B
assets/cloudBadges-B-2fjCg0.js (new) 922 B 🔴 +922 B 🔴 +515 B 🔴 +465 B
assets/cloudBadges-B5KcUySW.js (removed) 922 B 🟢 -922 B 🟢 -514 B 🟢 -464 B
assets/Load3DAdvanced-CMzm_sf8.js (removed) 761 B 🟢 -761 B 🟢 -423 B 🟢 -358 B
assets/Load3DAdvanced-M3K55sPj.js (new) 761 B 🔴 +761 B 🔴 +424 B 🔴 +361 B
assets/nightlyBadges-KKnzwogI.js (removed) 411 B 🟢 -411 B 🟢 -273 B 🟢 -268 B
assets/nightlyBadges-w5jUtarQ.js (new) 411 B 🔴 +411 B 🔴 +273 B 🔴 +232 B
assets/Load3dViewerContent-C2pzTVu7.js (new) 137 B 🔴 +137 B 🔴 +103 B 🔴 +91 B
assets/Load3dViewerContent-DdR0dMEO.js (removed) 137 B 🟢 -137 B 🟢 -103 B 🟢 -109 B
assets/missingModelMetadata-DDKqFXXd.js (new) 125 B 🔴 +125 B 🔴 +103 B 🔴 +112 B
assets/missingModelMetadata-WvbBZ2Dv.js (removed) 125 B 🟢 -125 B 🟢 -103 B 🟢 -103 B
assets/Load3DAdvanced-Bym4NPd7.js (removed) 122 B 🟢 -122 B 🟢 -97 B 🟢 -97 B
assets/Load3DAdvanced-iE3eZEfT.js (new) 122 B 🔴 +122 B 🔴 +97 B 🔴 +87 B
assets/WidgetLegacy-BoJESfdO.js (new) 117 B 🔴 +117 B 🔴 +106 B 🔴 +93 B
assets/WidgetLegacy-k4c7QbpV.js (removed) 117 B 🟢 -117 B 🟢 -106 B 🟢 -102 B
assets/workflowDraftStoreV2-6Se1MhqZ.js (new) 112 B 🔴 +112 B 🔴 +101 B 🔴 +108 B
assets/workflowDraftStoreV2-Dh-pHubc.js (removed) 112 B 🟢 -112 B 🟢 -101 B 🟢 -109 B
assets/Load3D-DoZGfqa_.js (new) 98 B 🔴 +98 B 🔴 +89 B 🔴 +91 B
assets/Load3D-ktcbRmW8.js (removed) 98 B 🟢 -98 B 🟢 -89 B 🟢 -78 B
assets/changeTracker-CigvPHiU.js (new) 91 B 🔴 +91 B 🔴 +93 B 🔴 +85 B
assets/changeTracker-Dlp2Fuy8.js (removed) 91 B 🟢 -91 B 🟢 -93 B 🟢 -91 B

Status: 69 added / 68 removed / 217 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.0 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 50.3 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 69.7 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 50.7 MB heap
large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.3 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.9 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.5 MB heap
minimap-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.1 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.6 MB heap
subgraph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.9 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 58.3 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 149ms TBT · 94.0 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 74.0 MB heap
vue-large-graph-idle: · 55.4 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 159.6 MB heap
vue-large-graph-pan: · 56.2 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 473ms TBT · 152.5 MB heap
workflow-execution: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.4 MB heap

⚠️ 12 regressions detected

Show regressions
Metric Baseline PR (median) Δ Sig
canvas-idle: task duration 454ms 565ms +24% ⚠️ z=5.5
canvas-mouse-sweep: task duration 862ms 1030ms +19% ⚠️ z=2.8
canvas-zoom-sweep: task duration 366ms 449ms +23% ⚠️ z=5.3
dom-widget-clipping: task duration 364ms 422ms +16% ⚠️ z=3.5
large-graph-idle: task duration 668ms 766ms +15% ⚠️ z=4.1
large-graph-idle: script duration 104ms 128ms +24% ⚠️ z=2.5
large-graph-pan: task duration 1303ms 1393ms +7% ⚠️ z=7.2
large-graph-pan: script duration 449ms 462ms +3% ⚠️ z=2.7
minimap-idle: task duration 659ms 742ms +13% ⚠️ z=4.5
minimap-idle: script duration 99ms 122ms +22% ⚠️ z=2.4
subgraph-dom-widget-clipping: task duration 396ms 436ms +10% ⚠️ z=3.2
subgraph-idle: task duration 463ms 500ms +8% ⚠️ z=4.2
All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms +0% z=0.4
canvas-idle: p95 frame time 17ms 17ms -0%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 8ms 10ms +20% z=-1.3
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 9 10 +6% z=-2.9
canvas-idle: task duration 454ms 565ms +24% ⚠️ z=5.5
canvas-idle: script duration 15ms 22ms +48% z=-1.3
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 66.9 MB 67.0 MB +0%
canvas-idle: DOM nodes 18 19 +6% z=-2.8
canvas-idle: event listeners 4 4 +0% z=-1.6
canvas-mouse-sweep: avg frame time 17ms 17ms -0% z=-0.9
canvas-mouse-sweep: p95 frame time 17ms 17ms +0%
canvas-mouse-sweep: layout duration 4ms 4ms -0% z=0.8
canvas-mouse-sweep: style recalc duration 38ms 48ms +24% z=1.6
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 74 77 +3% z=-0.9
canvas-mouse-sweep: task duration 862ms 1030ms +19% ⚠️ z=2.8
canvas-mouse-sweep: script duration 120ms 141ms +17% z=0.8
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 49.4 MB 50.3 MB +2%
canvas-mouse-sweep: DOM nodes -278 -281 +1% z=-132.7
canvas-mouse-sweep: event listeners -151 -153 +1% z=-38.7
canvas-zoom-sweep: avg frame time 17ms 17ms -0% z=-0.3
canvas-zoom-sweep: p95 frame time 17ms 17ms +1%
canvas-zoom-sweep: layout duration 1ms 1ms +25% z=1.5
canvas-zoom-sweep: style recalc duration 16ms 20ms +27% z=0.9
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 31 32 +3% z=1.6
canvas-zoom-sweep: task duration 366ms 449ms +23% ⚠️ z=5.3
canvas-zoom-sweep: script duration 18ms 26ms +39% z=-0.5
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 69.9 MB 69.7 MB -0%
canvas-zoom-sweep: DOM nodes 77 78 +1% z=-1.6
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
dom-widget-clipping: p95 frame time 17ms 17ms -0%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 6ms 9ms +46% z=-1.2
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 9 12 +33% z=-2.2
dom-widget-clipping: task duration 364ms 422ms +16% ⚠️ z=3.5
dom-widget-clipping: script duration 58ms 68ms +18% z=0.1
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 50.7 MB 50.7 MB -0%
dom-widget-clipping: DOM nodes 14 20 +43% z=-1.5
dom-widget-clipping: event listeners 0 2 variance too high
large-graph-idle: avg frame time 17ms 17ms -0% z=-0.6
large-graph-idle: p95 frame time 17ms 17ms +1%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 8ms 9ms +14% z=-3.0
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 8 9 +6% z=-9.9
large-graph-idle: task duration 668ms 766ms +15% ⚠️ z=4.1
large-graph-idle: script duration 104ms 128ms +24% ⚠️ z=2.5
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 64.2 MB 64.3 MB +0%
large-graph-idle: DOM nodes -284 -282 -1% z=-339.3
large-graph-idle: event listeners -147 -149 +1% z=-28.7
large-graph-pan: avg frame time 17ms 17ms -0% z=-0.2
large-graph-pan: p95 frame time 17ms 17ms +0%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 15ms 14ms -2% z=-3.7
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 69 68 -1% z=-2.4
large-graph-pan: task duration 1303ms 1393ms +7% ⚠️ z=7.2
large-graph-pan: script duration 449ms 462ms +3% ⚠️ z=2.7
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 62.8 MB 61.9 MB -1%
large-graph-pan: DOM nodes -284 -285 +0% z=-184.3
large-graph-pan: event listeners -149 -150 +1% z=-187.1
large-graph-zoom: avg frame time 17ms 17ms -0%
large-graph-zoom: p95 frame time 17ms 17ms +0%
large-graph-zoom: layout duration 7ms 8ms +9%
large-graph-zoom: style recalc duration 14ms 16ms +9%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 65 -2%
large-graph-zoom: task duration 1504ms 1667ms +11%
large-graph-zoom: script duration 536ms 589ms +10%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 57.2 MB 67.5 MB +18%
large-graph-zoom: DOM nodes -288 -290 +1%
large-graph-zoom: event listeners -153 -152 -1%
minimap-idle: avg frame time 17ms 17ms -0% z=0.1
minimap-idle: p95 frame time 17ms 17ms -1%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 7ms 8ms +20% z=-1.4
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 8 8 +0% z=-2.3
minimap-idle: task duration 659ms 742ms +13% ⚠️ z=4.5
minimap-idle: script duration 99ms 122ms +22% ⚠️ z=2.4
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 63.8 MB 64.1 MB +1%
minimap-idle: DOM nodes -284 -283 -0% z=-220.9
minimap-idle: event listeners -151 -164 +9% z=-255.3
subgraph-dom-widget-clipping: avg frame time 17ms 17ms -0% z=-0.4
subgraph-dom-widget-clipping: p95 frame time 17ms 17ms +1%
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 11ms 12ms +13% z=-0.3
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 48 46 -4% z=-3.3
subgraph-dom-widget-clipping: task duration 396ms 436ms +10% ⚠️ z=3.2
subgraph-dom-widget-clipping: script duration 116ms 127ms +9% z=-0.2
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 51.0 MB 51.6 MB +1%
subgraph-dom-widget-clipping: DOM nodes 22 18 -18% z=-3.7
subgraph-dom-widget-clipping: event listeners 6 8 +33% z=-1.4
subgraph-idle: avg frame time 17ms 17ms -0% z=-0.2
subgraph-idle: p95 frame time 17ms 17ms +0%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 8ms 9ms +8% z=-1.8
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 9 9 +0% z=-2.9
subgraph-idle: task duration 463ms 500ms +8% ⚠️ z=4.2
subgraph-idle: script duration 12ms 17ms +40% z=-1.0
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 67.1 MB 66.9 MB -0%
subgraph-idle: DOM nodes 18 18 +0% z=-2.5
subgraph-idle: event listeners 4 4 +0% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.4
subgraph-mouse-sweep: p95 frame time 17ms 17ms +0%
subgraph-mouse-sweep: layout duration 4ms 5ms +13% z=0.2
subgraph-mouse-sweep: style recalc duration 36ms 40ms +11% z=-0.7
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 76 77 +1% z=-1.6
subgraph-mouse-sweep: task duration 810ms 856ms +6% z=1.3
subgraph-mouse-sweep: script duration 92ms 103ms +11% z=0.3
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 46.4 MB 58.3 MB +26%
subgraph-mouse-sweep: DOM nodes 1 63 +6150% z=-2.0
subgraph-mouse-sweep: event listeners -153 4 -103% variance too high
subgraph-transition-enter: avg frame time 17ms 17ms -0%
subgraph-transition-enter: p95 frame time 17ms 17ms +0%
subgraph-transition-enter: layout duration 13ms 19ms +46%
subgraph-transition-enter: style recalc duration 31ms 33ms +7%
subgraph-transition-enter: layout count 14 14 +0%
subgraph-transition-enter: style recalc count 18 18 +0%
subgraph-transition-enter: task duration 907ms 1034ms +14%
subgraph-transition-enter: script duration 32ms 44ms +37%
subgraph-transition-enter: TBT 139ms 149ms +7%
subgraph-transition-enter: heap used 72.8 MB 94.0 MB +29%
subgraph-transition-enter: DOM nodes 13673 13673 +0%
subgraph-transition-enter: event listeners 2375 2375 +0%
viewport-pan-sweep: avg frame time 17ms 17ms -0%
viewport-pan-sweep: p95 frame time 17ms 17ms -0%
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 36ms 39ms +7%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 250 249 -0%
viewport-pan-sweep: task duration 4467ms 5000ms +12%
viewport-pan-sweep: script duration 1389ms 1535ms +10%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 57.1 MB 74.0 MB +30%
viewport-pan-sweep: DOM nodes -283 -281 -1%
viewport-pan-sweep: event listeners -163 -131 -20%
vue-large-graph-idle: avg frame time 18ms 18ms +2%
vue-large-graph-idle: p95 frame time 17ms 17ms +0%
vue-large-graph-idle: layout duration 0ms 0ms +0%
vue-large-graph-idle: style recalc duration 0ms 0ms +0%
vue-large-graph-idle: layout count 0 0 +0%
vue-large-graph-idle: style recalc count 0 0 +0%
vue-large-graph-idle: task duration 16903ms 17409ms +3%
vue-large-graph-idle: script duration 578ms 575ms -1%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 159.4 MB 159.6 MB +0%
vue-large-graph-idle: DOM nodes -8312 -8312 +0%
vue-large-graph-idle: event listeners -16387 -16387 +0%
vue-large-graph-pan: avg frame time 18ms 18ms +0%
vue-large-graph-pan: p95 frame time 17ms 17ms -0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 16ms 21ms +32%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 139 155 +12%
vue-large-graph-pan: task duration 20354ms 21649ms +6%
vue-large-graph-pan: script duration 884ms 954ms +8%
vue-large-graph-pan: TBT 117ms 473ms +304%
vue-large-graph-pan: heap used 159.8 MB 152.5 MB -5%
vue-large-graph-pan: DOM nodes -8312 -8312 +0%
vue-large-graph-pan: event listeners -16383 -16381 -0%
workflow-execution: avg frame time 17ms 17ms -0% z=0.1
workflow-execution: p95 frame time 17ms 17ms +0%
workflow-execution: layout duration 1ms 1ms +34% z=-4.0
workflow-execution: style recalc duration 21ms 22ms +8% z=-0.9
workflow-execution: layout count 3 4 +17% z=-2.7
workflow-execution: style recalc count 13 13 +0% z=-2.3
workflow-execution: task duration 109ms 116ms +7% z=-0.6
workflow-execution: script duration 9ms 10ms +11% z=-6.4
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 65.3 MB 65.4 MB +0%
workflow-execution: DOM nodes 123 124 +0% z=-5.2
workflow-execution: event listeners 97 98 +1% z=10.6
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-08-11T22:10:56.982Z",
  "gitSha": "83d8f6a6d15bad477129aab23fb5666c097fda88",
  "branch": "comfydesigner/fe-1584-local-workspace-switcher",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2045.0559999999882,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.201,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 561.4639999999999,
      "heapDeltaBytes": 5989944,
      "heapUsedBytes": 70315452,
      "domNodes": 20,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 22.306,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-idle",
      "durationMs": 2016.9049999999515,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 10.136,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 568.1160000000001,
      "heapDeltaBytes": 6006764,
      "heapUsedBytes": 70232740,
      "domNodes": 18,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 22.617,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2094.067999999993,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 50.63099999999999,
      "layouts": 12,
      "layoutDurationMs": 4.091,
      "taskDurationMs": 1088.768,
      "heapDeltaBytes": -10486720,
      "heapUsedBytes": 53878976,
      "domNodes": -282,
      "jsHeapTotalBytes": 24354816,
      "scriptDurationMs": 149.18699999999998,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1900.7250000000795,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 44.63100000000001,
      "layouts": 12,
      "layoutDurationMs": 3.4250000000000003,
      "taskDurationMs": 970.3620000000001,
      "heapDeltaBytes": -12550200,
      "heapUsedBytes": 51691940,
      "domNodes": -280,
      "jsHeapTotalBytes": 23044096,
      "scriptDurationMs": 131.90099999999998,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1773.3159999999657,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 20.518,
      "layouts": 6,
      "layoutDurationMs": 0.6769999999999998,
      "taskDurationMs": 428.353,
      "heapDeltaBytes": 8598476,
      "heapUsedBytes": 73001224,
      "domNodes": 79,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 25.511,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1737.6779999999599,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 20.474,
      "layouts": 6,
      "layoutDurationMs": 0.747,
      "taskDurationMs": 470.49800000000005,
      "heapDeltaBytes": 8827064,
      "heapUsedBytes": 73226020,
      "domNodes": 77,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 25.701,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 610.5919999999969,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 10.067000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 421.234,
      "heapDeltaBytes": -11184920,
      "heapUsedBytes": 53097440,
      "domNodes": 22,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 67.11500000000001,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 639.6180000000413,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 7.708999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 422.395,
      "heapDeltaBytes": -11198528,
      "heapUsedBytes": 53139032,
      "domNodes": 18,
      "jsHeapTotalBytes": 25952256,
      "scriptDurationMs": 68.996,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2053.2670000000053,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.514000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 759.6659999999999,
      "heapDeltaBytes": 7931824,
      "heapUsedBytes": 67388352,
      "domNodes": -281,
      "jsHeapTotalBytes": 3772416,
      "scriptDurationMs": 128.962,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2028.2959999999548,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.570999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 772.8560000000001,
      "heapDeltaBytes": 7315504,
      "heapUsedBytes": 67426108,
      "domNodes": -283,
      "jsHeapTotalBytes": 3772416,
      "scriptDurationMs": 127.856,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2211.041000000023,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 14.542,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1370.4809999999998,
      "heapDeltaBytes": 4112868,
      "heapUsedBytes": 64795976,
      "domNodes": -285,
      "jsHeapTotalBytes": 3440640,
      "scriptDurationMs": 449.815,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2324.4309999998904,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 14.087999999999996,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1414.5259999999998,
      "heapDeltaBytes": 4568184,
      "heapUsedBytes": 65043300,
      "domNodes": -285,
      "jsHeapTotalBytes": 3178496,
      "scriptDurationMs": 474.96,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3329.906000000051,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 15.160999999999998,
      "layouts": 60,
      "layoutDurationMs": 7.607000000000001,
      "taskDurationMs": 1633.671,
      "heapDeltaBytes": 5074084,
      "heapUsedBytes": 67603264,
      "domNodes": -290,
      "jsHeapTotalBytes": 7180288,
      "scriptDurationMs": 569.212,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3463.3580000000848,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 16.011,
      "layouts": 60,
      "layoutDurationMs": 8.043,
      "taskDurationMs": 1699.995,
      "heapDeltaBytes": 11456128,
      "heapUsedBytes": 73869692,
      "domNodes": -290,
      "jsHeapTotalBytes": 5869568,
      "scriptDurationMs": 608.7819999999999,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2030.8469999999943,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.486999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 727.1990000000001,
      "heapDeltaBytes": 6644808,
      "heapUsedBytes": 67220668,
      "domNodes": -284,
      "jsHeapTotalBytes": 4558848,
      "scriptDurationMs": 118.28500000000001,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2037.341999999967,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.295,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 756.6910000000001,
      "heapDeltaBytes": 6492348,
      "heapUsedBytes": 67214568,
      "domNodes": -282,
      "jsHeapTotalBytes": 4558848,
      "scriptDurationMs": 124.811,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 638.8699999999972,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 11.195,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 428.20000000000005,
      "heapDeltaBytes": -10076284,
      "heapUsedBytes": 54164772,
      "domNodes": 18,
      "jsHeapTotalBytes": 26214400,
      "scriptDurationMs": 126.689,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 635.7199999999921,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 13.677000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 444.75499999999994,
      "heapDeltaBytes": -10419016,
      "heapUsedBytes": 54099264,
      "domNodes": 18,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 127.806,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1997.1950000000334,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.95,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 492.417,
      "heapDeltaBytes": 5930488,
      "heapUsedBytes": 70138780,
      "domNodes": 20,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 16.229,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2005.3449999999202,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.02,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 508.34299999999996,
      "heapDeltaBytes": 5726892,
      "heapUsedBytes": 70136368,
      "domNodes": 16,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 18.636,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1707.2880000000055,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 39.862,
      "layouts": 16,
      "layoutDurationMs": 4.6979999999999995,
      "taskDurationMs": 835.56,
      "heapDeltaBytes": -3360580,
      "heapUsedBytes": 61146212,
      "domNodes": 62,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 102.228,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1733.3889999999883,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 40.457,
      "layouts": 16,
      "layoutDurationMs": 4.819,
      "taskDurationMs": 876.405,
      "heapDeltaBytes": -3372308,
      "heapUsedBytes": 61142220,
      "domNodes": 63,
      "jsHeapTotalBytes": 25427968,
      "scriptDurationMs": 103.14,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1435.325999999975,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 32.657000000000004,
      "layouts": 14,
      "layoutDurationMs": 18.842000000000002,
      "taskDurationMs": 1033.5059999999999,
      "heapDeltaBytes": 30953496,
      "heapUsedBytes": 98607352,
      "domNodes": 13673,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 44.21299999999999,
      "eventListeners": 2375,
      "totalBlockingTimeMs": 149,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 9057.923000000017,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 38.907,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 5084.524,
      "heapDeltaBytes": 18785864,
      "heapUsedBytes": 78354532,
      "domNodes": -283,
      "jsHeapTotalBytes": 5275648,
      "scriptDurationMs": 1577.7179999999998,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8653.067999999963,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 38.681,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4915.958,
      "heapDeltaBytes": 16409940,
      "heapUsedBytes": 76931272,
      "domNodes": -279,
      "jsHeapTotalBytes": 6062080,
      "scriptDurationMs": 1492.086,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 17701.514000000032,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17663.918,
      "heapDeltaBytes": -59489640,
      "heapUsedBytes": 167211992,
      "domNodes": -8312,
      "jsHeapTotalBytes": -5709824,
      "scriptDurationMs": 601.652,
      "eventListeners": -16389,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.77333333333336,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 17170.84900000009,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17153.961,
      "heapDeltaBytes": -58251968,
      "heapUsedBytes": 167420036,
      "domNodes": -8312,
      "jsHeapTotalBytes": -6758400,
      "scriptDurationMs": 548.406,
      "eventListeners": -16385,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.330000000000048,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 22112.638000000003,
      "styleRecalcs": 161,
      "styleRecalcDurationMs": 22.020999999999958,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 22061.575999999997,
      "heapDeltaBytes": -49699440,
      "heapUsedBytes": 156416044,
      "domNodes": -8312,
      "jsHeapTotalBytes": -9641984,
      "scriptDurationMs": 996.4300000000002,
      "eventListeners": -16383,
      "totalBlockingTimeMs": 502,
      "frameDurationMs": 17.776666666666763,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 21262.534999999956,
      "styleRecalcs": 149,
      "styleRecalcDurationMs": 19.782999999999994,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 21236.424,
      "heapDeltaBytes": -51856120,
      "heapUsedBytes": 163416248,
      "domNodes": -8312,
      "jsHeapTotalBytes": -11214848,
      "scriptDurationMs": 910.9870000000001,
      "eventListeners": -16379,
      "totalBlockingTimeMs": 443,
      "frameDurationMs": 17.779999999999927,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "workflow-execution",
      "durationMs": 460.11699999996836,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 20.921999999999997,
      "layouts": 3,
      "layoutDurationMs": 0.538,
      "taskDurationMs": 112.65299999999999,
      "heapDeltaBytes": 5064960,
      "heapUsedBytes": 68617628,
      "domNodes": 124,
      "jsHeapTotalBytes": 4980736,
      "scriptDurationMs": 9.951000000000002,
      "eventListeners": 99,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 473.97399999999834,
      "styleRecalcs": 14,
      "styleRecalcDurationMs": 23.769,
      "layouts": 4,
      "layoutDurationMs": 1.4129999999999998,
      "taskDurationMs": 119.41099999999999,
      "heapDeltaBytes": 5048976,
      "heapUsedBytes": 68558504,
      "domNodes": 123,
      "jsHeapTotalBytes": 4980736,
      "scriptDurationMs": 10.195999999999998,
      "eventListeners": 97,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    }
  ]
}

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Non-cloud distributions now hydrate workspace context in the background. The legacy user popover displays a workspace selector when workspace data is ready. Workspace authentication and billing use workspace context after hydration. Workspace creation controls remain limited to cloud distributions. Vite supports an optional development gateway for workspace APIs.

Changes

Workspace flow

Layer / File(s) Summary
Background workspace hydration
src/platform/workspace/auth/WorkspaceAuthGate.vue
Non-cloud distributions initialize workspace context asynchronously after authentication. Sign-in events trigger hydration without blocking rendering.
Workspace authentication and billing routing
src/stores/authStore.ts, src/composables/billing/useBillingRouting.ts, src/composables/billing/useBillingRouting.test.ts
Non-cloud contexts can use active workspace tokens. Billing routing selects legacy billing while workspace context is unavailable and workspace billing after a team workspace loads.
Topbar workspace selector
src/components/topbar/CurrentUserPopoverLegacy.vue, src/components/topbar/CurrentUserButton.vue, src/components/topbar/CurrentUserPopoverLegacy.test.ts
The legacy user popover displays the current workspace and opens the workspace switcher after initialization. The button shows the workspace icon for ready, non-personal workspaces.
Distribution-specific switcher rules
src/platform/workspace/components/WorkspaceSwitcherPopover.vue, src/platform/workspace/components/WorkspaceSwitcherPopover.test.ts
Workspace creation controls are hidden outside cloud distributions. Tests cover the non-cloud behavior.
Workspace development proxy
vite.config.mts
When WORKSPACE_DEV_PROXY is set, Vite routes workspace, billing, and authentication API paths to the configured gateway before the fallback /api proxy.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkspaceAuthGate
  participant Firebase
  participant TeamWorkspaceStore
  participant CurrentUserPopoverLegacy
  participant WorkspaceSwitcherPopover
  participant BillingRouter
  WorkspaceAuthGate->>Firebase: wait for authentication initialization
  WorkspaceAuthGate->>TeamWorkspaceStore: initialize workspace mode
  TeamWorkspaceStore-->>CurrentUserPopoverLegacy: expose ready workspaces
  CurrentUserPopoverLegacy->>WorkspaceSwitcherPopover: open on workspace selection
  BillingRouter->>TeamWorkspaceStore: read workspace type
  TeamWorkspaceStore-->>BillingRouter: return workspace billing context
Loading

Possibly related PRs

Suggested labels: size:L

Suggested reviewers: christian-byrne


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore (reviewers only)

❌ Failed checks (1 error)

Check name Status Explanation Resolution
End-To-End Regression Coverage For Fixes ❌ Error Commit subject fix(workspace): ... signals a bug fix; the PR changes frontend files under src/ but no browser_tests/ file, and the description gives no concrete reason for omitting E2E coverage. Add or update a Playwright regression test under browser_tests/, or add a concrete explanation in the PR description for why an end-to-end test is not practical.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes exposing the workspace switcher on non-cloud distributions for credit-wallet selection.
Description check ✅ Passed The description explains the purpose, changes, review focus, verification status, dependencies, and known limitations in sufficient detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Adr Compliance For Entity/Litegraph Changes ✅ Passed The changed-file list contains no files under src/lib/litegraph/, src/ecs/, or graph-entity paths, so this check does not apply.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch comfydesigner/fe-1584-local-workspace-switcher

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/topbar/CurrentUserPopoverLegacy.test.ts`:
- Line 179: Add behavioral Vitest coverage in CurrentUserPopoverLegacy.test.ts
for the workspace selector: configure createTestingPinia with
teamWorkspace.initState set to ready and supplied workspaces, verify the
selector is visible only when available, use Testing Library user actions to
open it, and verify it closes after selecting a workspace. Retain coverage for
the unavailable state and assert rendered behavior rather than mocks.

In `@src/components/topbar/CurrentUserPopoverLegacy.vue`:
- Around line 35-51: Replace the clickable workspace-switcher trigger div with
the shared Button component, preserving its click behavior, styling, tooltip,
and workspace-name content. Ensure the button exposes the visible workspace name
as its accessible name and binds aria-expanded to isWorkspaceSwitcherOpen so
keyboard users can activate it with Enter or Space.
🪄 Autofix

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: 1acf40a2-51b0-4f8d-be3d-ed268edbceda

📥 Commits

Reviewing files that changed from the base of the PR and between 8bd27f9 and 959d083.

📒 Files selected for processing (6)
  • src/components/topbar/CurrentUserButton.vue
  • src/components/topbar/CurrentUserPopoverLegacy.test.ts
  • src/components/topbar/CurrentUserPopoverLegacy.vue
  • src/platform/workspace/auth/WorkspaceAuthGate.vue
  • src/platform/workspace/components/WorkspaceSwitcherPopover.test.ts
  • src/platform/workspace/components/WorkspaceSwitcherPopover.vue

Comment thread src/components/topbar/CurrentUserPopoverLegacy.test.ts Outdated
Comment thread src/components/topbar/CurrentUserPopoverLegacy.vue Outdated
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.00000% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/components/topbar/CurrentUserPopoverLegacy.vue 58.33% 4 Missing and 1 partial ⚠️
src/platform/workspace/auth/WorkspaceAuthGate.vue 72.72% 3 Missing ⚠️
@@            Coverage Diff             @@
##             main   #15051      +/-   ##
==========================================
- Coverage   81.39%   81.39%   -0.01%     
==========================================
  Files        1873     1873              
  Lines      107225   107273      +48     
  Branches    33329    33347      +18     
==========================================
+ Hits        87273    87310      +37     
+ Misses      19593    19592       -1     
- Partials      359      371      +12     
Flag Coverage Δ
unit 72.53% <68.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/components/topbar/CurrentUserButton.vue 89.74% <100.00%> (ø)
.../workspace/components/WorkspaceSwitcherPopover.vue 75.86% <100.00%> (ø)
src/platform/workspace/auth/WorkspaceAuthGate.vue 97.32% <72.72%> (-2.68%) ⬇️
src/components/topbar/CurrentUserPopoverLegacy.vue 89.90% <58.33%> (-3.91%) ⬇️

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@vite.config.mts`:
- Around line 168-169: Update the WORKSPACE_DEV_PROXY configuration flow so the
value is loaded from .env.local as well as .env, or explicitly document that
only the current environment source is supported. Apply the change around
dotenvConfig() and the WORKSPACE_DEV_PROXY/workspaceDevProxyConfig symbols.
- Around line 169-181: Update the workspaceDevProxyConfig condition so it only
creates the workspace development proxy when WORKSPACE_DEV_PROXY is set and
DISTRIBUTION is not 'cloud'. Preserve the existing route mappings and
empty-object fallback for cloud distributions or when the proxy is unset.
- Line 178: Update the proxy configuration for WORKSPACE_DEV_PROXY to set secure
to true, preserving upstream TLS certificate verification; do not use this
setting to support self-signed local targets, which should require a separate
explicit opt-in.
🪄 Autofix

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: c12a13a0-09ec-46cd-b104-a2a318549340

📥 Commits

Reviewing files that changed from the base of the PR and between 959d083 and 839d4ec.

📒 Files selected for processing (2)
  • src/platform/workspace/auth/WorkspaceAuthGate.vue
  • vite.config.mts

Comment thread vite.config.mts Outdated
Comment on lines +168 to +169
const WORKSPACE_DEV_PROXY = process.env.WORKSPACE_DEV_PROXY
const workspaceDevProxyConfig = WORKSPACE_DEV_PROXY

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expectation: the repository either loads this value with loadEnv
# or documents a shell-exported configuration.
rg -n --hidden --glob '!.git' 'WORKSPACE_DEV_PROXY|loadEnv|dotenv' .

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1888


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- vite.config.mts ---'
sed -n '1,35p;155,190p;280,300p' vite.config.mts

printf '%s\n' '--- environment files and documentation references ---'
find . -maxdepth 3 -type f \( -name '.env*' -o -name 'README*' -o -name '*example*' \) -print
rg -n --hidden --glob '!.git' 'WORKSPACE_DEV_PROXY|\.env\.local|dotenvConfig|loadEnv' \
  README* docs .github package.json vite.config.mts apps 2>/dev/null || true

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 4622


Load WORKSPACE_DEV_PROXY from .env.local or document the supported source. dotenvConfig() loads .env, but not .env.local.

🤖 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 `@vite.config.mts` around lines 168 - 169, Update the WORKSPACE_DEV_PROXY
configuration flow so the value is loaded from .env.local as well as .env, or
explicitly document that only the current environment source is supported. Apply
the change around dotenvConfig() and the
WORKSPACE_DEV_PROXY/workspaceDevProxyConfig symbols.

Source: MCP tools

Comment thread vite.config.mts
Comment thread vite.config.mts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/composables/billing/useBillingRouting.test.ts`:
- Around line 45-63: Update the billing routing tests around useBillingRouting
to initialize createTestingPinia according to docs/testing/vitest-patterns.md,
using stubActions: false when real actions are exercised. Replace the
hand-written activeWorkspace getter setup with the useTeamWorkspaceStore public
interface, including the state required for activeWorkspaceBillingRail, while
preserving both existing behavioral assertions.
🪄 Autofix

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: e6590d72-2bd6-42a7-95b2-50fa8325b2dd

📥 Commits

Reviewing files that changed from the base of the PR and between 839d4ec and d5d1ee4.

📒 Files selected for processing (4)
  • src/composables/billing/useBillingRouting.test.ts
  • src/composables/billing/useBillingRouting.ts
  • src/stores/authStore.ts
  • vite.config.mts

Comment on lines +45 to +63
it('uses legacy billing off Cloud until a workspace context loads', () => {
mockIsCloud.value = false
mockActiveWorkspace.value = team
mockActiveWorkspace.value = null

const { type, shouldUseWorkspaceBilling } = useBillingRouting()

expect(type.value).toBe('legacy')
expect(shouldUseWorkspaceBilling.value).toBe(false)
})

it('uses workspace billing off Cloud once a workspace context loads', () => {
mockIsCloud.value = false
mockActiveWorkspace.value = team

const { type, shouldUseWorkspaceBilling } = useBillingRouting()

expect(type.value).toBe('workspace')
expect(shouldUseWorkspaceBilling.value).toBe(true)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the required Testing Pinia setup for these cases.

The new tests drive activeWorkspace through the hand-written getter shown at Lines 26-28. This bypasses the real useTeamWorkspaceStore contract, including activeWorkspaceBillingRail, so the assertions can pass while store integration is broken.

As per path instructions: docs/testing/vitest-patterns.md requires store-dependent tests to initialize with createTestingPinia; use stubActions: false when exercising real actions. Initialize a testing Pinia and set state through the store's public interface. Keep the behavioral assertions.

🤖 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/composables/billing/useBillingRouting.test.ts` around lines 45 - 63,
Update the billing routing tests around useBillingRouting to initialize
createTestingPinia according to docs/testing/vitest-patterns.md, using
stubActions: false when real actions are exercised. Replace the hand-written
activeWorkspace getter setup with the useTeamWorkspaceStore public interface,
including the state required for activeWorkspaceBillingRail, while preserving
both existing behavioral assertions.

Source: Path instructions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/topbar/CurrentUserPopoverLegacy.vue`:
- Around line 91-92: Update the permission gates in
CurrentUserPopoverLegacy.vue: at lines 91-92, render add-credits-button only
when the user has the top-up capability; at lines 145-146, render
manage-plan-menu-item only when the user can manage the active workspace plan.
Do not use workspace or subscription availability as authorization, and ensure
read-only team members see neither control.
🪄 Autofix

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: 24319a7d-0639-44ef-b4e4-9c1bc04bb53e

📥 Commits

Reviewing files that changed from the base of the PR and between d5d1ee4 and b679c80.

📒 Files selected for processing (1)
  • src/components/topbar/CurrentUserPopoverLegacy.vue

Comment on lines +91 to +92
<Button
v-else
v-else-if="canAccessSubscriptionFeatures"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use action-specific permissions for billing controls.

The new conditions use workspace or subscription availability as a proxy for authorization. Keep each action gated by its own capability.

  • src/components/topbar/CurrentUserPopoverLegacy.vue#L91-L92: Render add-credits-button only when the user can top up.
  • src/components/topbar/CurrentUserPopoverLegacy.vue#L145-L146: Render manage-plan-menu-item only when the user can manage the active workspace plan.

The related workspace tests require read-only team members to have neither control.

📍 Affects 1 file
  • src/components/topbar/CurrentUserPopoverLegacy.vue#L91-L92 (this comment)
  • src/components/topbar/CurrentUserPopoverLegacy.vue#L145-L146
🤖 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/topbar/CurrentUserPopoverLegacy.vue` around lines 91 - 92,
Update the permission gates in CurrentUserPopoverLegacy.vue: at lines 91-92,
render add-credits-button only when the user has the top-up capability; at lines
145-146, render manage-plan-menu-item only when the user can manage the active
workspace plan. Do not use workspace or subscription availability as
authorization, and ensure read-only team members see neither control.

@comfydesigner

Copy link
Copy Markdown
Contributor Author

Review dispositions for the CodeRabbit pass:

Addressed in d397f7a:

  • Workspace switcher trigger is now the shared Button component (keyboard focusable, Enter/Space, aria-expanded bound to open state).
  • WORKSPACE_DEV_PROXY is ignored on the cloud distribution and the proxy now uses secure: true (both targets have valid public certs).
  • Add credits is gated on permissions.canTopUp when a workspace context is active — team members without billing rights no longer see a button that would 403. Legacy (no workspace context) behavior unchanged.
  • Added behavioral tests for the selector: hidden while the store is not hydrated; trigger renders, opens the panel, and toggles aria-expanded when ready.

Declined, with reasoning:

  • useBillingRouting.test.ts harness: the new cases follow the file's existing hand-written store mock; converting the whole file to createTestingPinia is a pre-existing-pattern refactor out of scope for this PR.
  • WORKSPACE_DEV_PROXY env loading: read via process.env at config time, identical to the existing DEV_SERVER_COMFYUI_URL / DISTRIBUTION pattern in this file; it's a shell-exported dev-only flag, documented in the PR description.
  • manage-plan-menu-item permission gate: on non-cloud this item is labeled Credits and opens the read-only Settings → Credits panel (balance + activity), not plan management — viewing is appropriate for every member, so no capability gate is needed. Cloud keeps its existing gating.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/topbar/CurrentUserPopoverLegacy.test.ts`:
- Around line 579-597: Add a test alongside the existing workspace switcher
visibility cases using a ready workspace state with workspaces set to an empty
array. Render the component with that state and assert
workspace-switcher-trigger remains absent, covering the showWorkspaceSwitcher
condition for ready-but-empty stores.
🪄 Autofix

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: 1a5738d1-9db4-4d90-856d-f05da4bd50ea

📥 Commits

Reviewing files that changed from the base of the PR and between b679c80 and d397f7a.

📒 Files selected for processing (3)
  • src/components/topbar/CurrentUserPopoverLegacy.test.ts
  • src/components/topbar/CurrentUserPopoverLegacy.vue
  • vite.config.mts

Comment on lines +579 to +597
it('stays hidden while the workspace store is not hydrated', () => {
renderComponent()

expect(screen.queryByTestId('workspace-switcher-trigger')).toBeNull()
})

it('shows the trigger and opens the switcher once the store is ready', async () => {
const { user } = renderComponent(readyWorkspaceState)

const trigger = screen.getByTestId('workspace-switcher-trigger')
expect(trigger).toHaveAttribute('aria-expanded', 'false')
expect(screen.queryByTestId('workspace-switcher-panel')).toBeNull()

await user.click(trigger)

expect(screen.getByTestId('workspace-switcher-panel')).toBeInTheDocument()
expect(trigger).toHaveAttribute('aria-expanded', 'true')
})
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add coverage for the ready-but-empty workspace state.

The suite tests an unhydrated store and a populated ready store. It does not test initState: 'ready' with workspaces: [].

Add a case that verifies the selector remains hidden for that state. This covers the workspaces.value.length > 0 condition in showWorkspaceSwitcher.

As per path instructions, “Cover unloaded, ready, empty, error, and permission edge cases.”

🤖 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/topbar/CurrentUserPopoverLegacy.test.ts` around lines 579 -
597, Add a test alongside the existing workspace switcher visibility cases using
a ready workspace state with workspaces set to an empty array. Render the
component with that state and assert workspace-switcher-trigger remains absent,
covering the showWorkspaceSwitcher condition for ready-but-empty stores.

Source: Path instructions

pull Bot pushed a commit to Penguinjanator/ComfyUI_frontend that referenced this pull request Aug 22, 2026
…omfy-Org#15151)

## Summary

Settings → Credits still assumed the legacy `/customers` rail, so its
activity table never loaded when the balance came from anywhere else,
and Invoice History opened the wrong Stripe customer.

## Changes

- **What**:
- `UsageLogsTable` owns its first load (`immediate` watch on the billing
route). It previously started at `loading = true` and waited for a
parent `refresh()`, which `CreditsPanel` only fired from
`authStore.lastBalanceUpdateTime` — written solely by the legacy
`/customers/balance` success path (`authStore.ts:374`). Any path that
does not write it leaves the spinner up forever.
- `CreditsPanel` refreshes activity off the balance it renders instead
of that legacy-only timestamp, so a top-up refreshes the table on both
rails. First hydration is skipped, since the table now loads itself.
- Invoice History routes through
`useBillingContext().manageSubscription()`, matching
`SubscriptionFooterLinks`. The legacy `/customers` portal bills a
different Stripe customer than the workspace on screen.

## Review Focus

Two independent ways to reach the stuck spinner on `main`:

1. **Local/Desktop, new account** — `authStore.fetchBalance()` returns
early on a `404` from `/customers/balance` (`authStore.ts:355-358`), so
`lastBalanceUpdateTime` is never written. A user with no customer record
yet gets a Credits tab that spins forever.
2. **Cloud, workspace rail** — with `billing_control_enabled` off the
standalone Credits panel stays in the sidebar (`useSettingUI.ts:325`)
while `useWorkspaceBilling.fetchBalance()` supplies the balance from
`/billing/balance` and never touches the timestamp. This also becomes
the default failure once the workspace switcher is exposed on
Local/Desktop (Comfy-Org#15051).

Other notes:

- `AutoRefreshWrapper` is gone from the `UsageLogsTable` tests; the
component no longer needs a parent to drive its first fetch. Reverting
the `immediate` flag alone fails 19 of the 21 tests.
- Legacy `manageSubscription()` also starts the cancellation watcher,
which the raw `accessBillingPortal()` call did not.
`SubscriptionFooterLinks` already accepts that for the same action, and
`browser_tests/tests/localCreditsNoSubscribeUi.spec.ts` (which asserts
Invoice History issues `POST /customers/billing`) still passes.

## Screenshots

Captured from the same local dev-server setup and viewport with a fully
mocked Personal workspace on the workspace billing rail.
`billing_control_enabled` is off so the standalone Settings → Credits
panel is visible. AS-IS uses base `085c6ec51`; TO-BE uses this PR's head
`e9fcd6adc`.

| AS-IS | TO-BE |
| --- | --- |
| Activity stays on the loading indicator because no initial request is
issued. | Activity loads from `/api/billing/events` on mount. |
| ![AS-IS — Credits activity stuck
loading](https://ampcode.com/user-content/artifacts/50a4a56fc1f0a33cf6c00e5fb3bfd64b4365b2261217eace1e01ec243a61aa7d-file.png)
| ![TO-BE — Credits activity
loaded](https://ampcode.com/user-content/artifacts/cb6f3f0d3dda7ca1f329ce34517f851dc451afccd8748bd7ccdc0e0e1479a762-file.png)
|

Linear:
[FE-1584](https://linear.app/comfyorg/issue/FE-1584/add-workspace-switcher-to-localdesktop-credits-only-no-settings)
Prototype this supports: Comfy-Org#15051
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants