Skip to content

feat: App mode saving rework#9338

Merged
pythongosssss merged 10 commits intomainfrom
pysssss/app-mode-data-saving-refactor
Mar 3, 2026
Merged

feat: App mode saving rework#9338
pythongosssss merged 10 commits intomainfrom
pysssss/app-mode-data-saving-refactor

Conversation

@pythongosssss
Copy link
Member

@pythongosssss pythongosssss commented Mar 2, 2026

Summary

Change app mode changes to be written directly to the workflow on change instead of requiring explicit save via builder.
Temporary: Adds .app.json file extension to app files for identification since we don't currently have a way to identify them with metadata
Removes app builder save dialog and replaces it with default mode selection

Changes

  • What:

  • ensure all save locations handle app mode

  • remove dirtyLinearData and flushing

  • Breaking:

  • if people are relying on workflow names and are converting to/from app mode in the same workflow, they will gain/lose the .app part of the extension

Screenshots (if applicable)

image image

┆Issue is synchronized with this Notion page by Unito

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🎭 Playwright: ✅ 547 passed, 0 failed · 6 flaky

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Adds workflow-aware filename utilities (.app.json support), replaces the builder "save" flow with a "default view" feature (removing save-success UI and useBuilderSave), updates workflow save/overwrite logic to be mode-aware, and moves linear-mode selection persistence into active workflow extra.linearData.

Changes

Cohort / File(s) Summary
Filename Utility Functions
packages/shared-frontend-utils/src/formatUtil.ts, packages/shared-frontend-utils/src/formatUtil.test.ts
Add compound-extension support and new APIs: getFilenameDetails, getPathDetails, appendJsonExt, getWorkflowSuffix, appendWorkflowJsonExt, ensureWorkflowSuffix, WorkflowSuffix type; tests for .app.json and edge cases.
Builder Default View Feature
src/components/builder/BuilderMenu.vue, src/components/builder/BuilderToolbar.vue, src/components/builder/DefaultViewDialogContent.vue, src/components/builder/useAppSetDefaultView.ts, src/components/builder/useAppSetDefaultView.test.ts
Replace save UI/flow with "default view": new useAppSetDefaultView hook, toolbar step setDefaultView, dialog emits apply/close; Menu save now calls workflowService asynchronously. Tests added for dialog and apply behavior.
Builder Save Removal
src/components/builder/BuilderSaveSuccessDialogContent.vue, src/components/builder/useBuilderSave.ts
Remove BuilderSaveSuccessDialogContent.vue and delete useBuilderSave hook (save orchestration and related dialogs removed).
Workflow Service Updates
src/platform/workflow/core/services/workflowService.ts, src/platform/workflow/core/services/workflowService.test.ts
Refactor save/rename/overwrite flows to be app-mode aware: use appendWorkflowJsonExt(..., isApp), add confirmOverwrite, remove initialMode option from saveWorkflowAs, centralize overwrite/rename logic and update tests for mode/extension/overwrite scenarios.
Workflow Store Refactor
src/platform/workflow/management/stores/comfyWorkflow.ts
Remove dirtyLinearData, LinearModeTarget and syncLinearMode API and associated persistence logic.
App Mode Store Updates & Tests
src/stores/appModeStore.ts, src/stores/appModeStore.test.ts
Remove flushSelections() and old dirtyLinearData paths; watch changeTracker.activeState?.extra?.linearData; persist selections into app.rootGraph.extra.linearData while in builder mode; tests updated to real store interactions and linearData sync.
Filename Handling Propagation
src/components/breadcrumb/SubgraphBreadcrumbItem.vue, src/components/sidebar/tabs/WorkflowsSidebarTab.vue, src/composables/useCoreCommands.ts
Replace appendJsonExt usage with getWorkflowSuffix + ensureWorkflowSuffix to preserve/normalize .app.json vs .json on renames and path construction.
Localization Updates
src/locales/en/main.json
Remove save-related keys; add default view keys (defaultView*, defaultViewTitle, defaultViewLabel) and workflowService keys (savedAsApp, savedAsWorkflow).

Sequence Diagram

sequenceDiagram
    actor User
    participant BuilderToolbar as BuilderToolbar
    participant Hook as useAppSetDefaultView
    participant Dialog as DefaultViewDialog
    participant WorkflowStore as WorkflowStore
    participant WorkflowService as WorkflowService

    User->>BuilderToolbar: Click "Set default view"
    BuilderToolbar->>Hook: showDialog()
    Hook->>Dialog: open(initialOpenAsApp)
    User->>Dialog: Choose view -> Apply
    Dialog->>Hook: emit('apply', openAsApp)
    Hook->>WorkflowStore: update activeWorkflow.initialMode\nand app.rootGraph.extra.linearMode
    Hook->>WorkflowService: persist/rename as needed
    WorkflowService->>WorkflowStore: persist changes / emit toasts
    Hook->>Dialog: close()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

size:XL

Suggested reviewers

  • AustinMroz

Poem

🐇 I trimmed the save dance, let suffixes play,

.app.json and .json now find their way,
The builder sets views, dialogs gently close,
Linear bits nest where the workflows grow,
A happy rabbit hops — the code feels lighter today.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: App mode saving rework' directly reflects the main change: a rework of how app mode changes are saved to workflows.
Description check ✅ Passed The description covers the main purpose and includes sections for Summary, Changes with What and Breaking details, and Screenshots, mostly following the template structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pysssss/app-mode-data-saving-refactor

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 and usage tips.

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 03/03/2026, 01:23:07 PM UTC

Links

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

📦 Bundle: 4.48 MB gzip 🟢 -544 B

Details

Summary

  • Raw size: 21 MB baseline 21 MB — 🟢 -3.49 kB
  • Gzip: 4.48 MB baseline 4.48 MB — 🟢 -544 B
  • Brotli: 3.45 MB baseline 3.45 MB — 🟢 -434 B
  • Bundles: 228 current • 228 baseline • 113 added / 113 removed

Category Glance
Graph Workspace 🟢 -4.08 kB (976 kB) · Data & Services 🔴 +752 B (2.64 MB) · Other 🟢 -153 B (7.89 MB) · Vendor & Third-Party ⚪ 0 B (8.84 MB) · Panels & Settings ⚪ 0 B (436 kB) · Views & Navigation ⚪ 0 B (72.1 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-6pXBDswu.js (new) 17.9 kB 🔴 +17.9 kB 🔴 +6.36 kB 🔴 +5.51 kB
assets/index-CCPQ3idI.js (removed) 17.9 kB 🟢 -17.9 kB 🟢 -6.36 kB 🟢 -5.48 kB

Status: 1 added / 1 removed

Graph Workspace — 976 kB (baseline 980 kB) • 🟢 -4.08 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-DzAWkJmX.js (removed) 980 kB 🟢 -980 kB 🟢 -208 kB 🟢 -158 kB
assets/GraphView-BB973BaX.js (new) 976 kB 🔴 +976 kB 🔴 +208 kB 🔴 +157 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-CN-n4GHg.js (removed) 15.5 kB 🟢 -15.5 kB 🟢 -3.32 kB 🟢 -2.82 kB
assets/CloudSurveyView-DEOxplR4.js (new) 15.5 kB 🔴 +15.5 kB 🔴 +3.32 kB 🔴 +2.82 kB
assets/CloudLoginView-DQbO4vLd.js (removed) 11.4 kB 🟢 -11.4 kB 🟢 -3.19 kB 🟢 -2.82 kB
assets/CloudLoginView-DXEv0xYQ.js (new) 11.4 kB 🔴 +11.4 kB 🔴 +3.19 kB 🔴 +2.83 kB
assets/CloudSignupView-BTLdfKP-.js (new) 9.37 kB 🔴 +9.37 kB 🔴 +2.7 kB 🔴 +2.37 kB
assets/CloudSignupView-CT-rKEwK.js (removed) 9.37 kB 🟢 -9.37 kB 🟢 -2.7 kB 🟢 -2.37 kB
assets/UserCheckView-B4Kx5KbW.js (new) 8.41 kB 🔴 +8.41 kB 🔴 +2.23 kB 🔴 +1.94 kB
assets/UserCheckView-DebZGnQ9.js (removed) 8.41 kB 🟢 -8.41 kB 🟢 -2.23 kB 🟢 -1.94 kB
assets/CloudLayoutView-D6SIHT2w.js (removed) 6.43 kB 🟢 -6.43 kB 🟢 -2.1 kB 🟢 -1.83 kB
assets/CloudLayoutView-JNbSLL9H.js (new) 6.43 kB 🔴 +6.43 kB 🔴 +2.1 kB 🔴 +1.83 kB
assets/CloudForgotPasswordView-1iJYVnYG.js (removed) 5.56 kB 🟢 -5.56 kB 🟢 -1.93 kB 🟢 -1.73 kB
assets/CloudForgotPasswordView-L97b842l.js (new) 5.56 kB 🔴 +5.56 kB 🔴 +1.93 kB 🔴 +1.72 kB
assets/CloudAuthTimeoutView-B14qu1x1.js (removed) 4.91 kB 🟢 -4.91 kB 🟢 -1.77 kB 🟢 -1.55 kB
assets/CloudAuthTimeoutView-BkNBi_9u.js (new) 4.91 kB 🔴 +4.91 kB 🔴 +1.77 kB 🔴 +1.55 kB
assets/CloudSubscriptionRedirectView-BbZ-HJv0.js (removed) 4.75 kB 🟢 -4.75 kB 🟢 -1.79 kB 🟢 -1.58 kB
assets/CloudSubscriptionRedirectView-DR1BugkC.js (new) 4.75 kB 🔴 +4.75 kB 🔴 +1.79 kB 🔴 +1.59 kB
assets/UserSelectView-BR-S-EaN.js (new) 4.5 kB 🔴 +4.5 kB 🔴 +1.64 kB 🔴 +1.46 kB
assets/UserSelectView-DnPPZtY2.js (removed) 4.5 kB 🟢 -4.5 kB 🟢 -1.64 kB 🟢 -1.46 kB
assets/CloudSorryContactSupportView-oBJgqwAb.js 1.02 kB 1.02 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-C7HwWHX2.js 296 B 296 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SecretsPanel-D7iSbo4C.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -5.3 kB 🟢 -4.65 kB
assets/SecretsPanel-LYexsUBM.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +5.3 kB 🔴 +4.66 kB
assets/LegacyCreditsPanel-3UWV6D8W.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +5.56 kB 🔴 +4.91 kB
assets/LegacyCreditsPanel-6BS-4ASV.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -5.56 kB 🟢 -4.89 kB
assets/SubscriptionPanel-DF6IgdZb.js (removed) 18.6 kB 🟢 -18.6 kB 🟢 -4.72 kB 🟢 -4.15 kB
assets/SubscriptionPanel-DOhJFj4i.js (new) 18.6 kB 🔴 +18.6 kB 🔴 +4.72 kB 🔴 +4.16 kB
assets/KeybindingPanel-B-4fEwBT.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +3.52 kB 🔴 +3.12 kB
assets/KeybindingPanel-D695iRw1.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -3.51 kB 🟢 -3.12 kB
assets/AboutPanel-BuBU4e7A.js (new) 9.79 kB 🔴 +9.79 kB 🔴 +2.73 kB 🔴 +2.46 kB
assets/AboutPanel-H7gH6udW.js (removed) 9.79 kB 🟢 -9.79 kB 🟢 -2.73 kB 🟢 -2.47 kB
assets/ExtensionPanel-CFL6v1Z3.js (new) 9.38 kB 🔴 +9.38 kB 🔴 +2.65 kB 🔴 +2.36 kB
assets/ExtensionPanel-DA_LecXL.js (removed) 9.38 kB 🟢 -9.38 kB 🟢 -2.65 kB 🟢 -2.36 kB
assets/ServerConfigPanel-CXhfDX5t.js (removed) 6.44 kB 🟢 -6.44 kB 🟢 -2.13 kB 🟢 -1.93 kB
assets/ServerConfigPanel-kM0EBWOp.js (new) 6.44 kB 🔴 +6.44 kB 🔴 +2.13 kB 🔴 +1.94 kB
assets/UserPanel-Befm_AEC.js (removed) 6.16 kB 🟢 -6.16 kB 🟢 -1.99 kB 🟢 -1.75 kB
assets/UserPanel-CLbNNIPi.js (new) 6.16 kB 🔴 +6.16 kB 🔴 +2 kB 🔴 +1.75 kB
assets/cloudRemoteConfig-BAUEiZ-J.js (new) 1.44 kB 🔴 +1.44 kB 🔴 +704 B 🔴 +607 B
assets/cloudRemoteConfig-d-ndmZqE.js (removed) 1.44 kB 🟢 -1.44 kB 🟢 -705 B 🟢 -607 B
assets/refreshRemoteConfig-Czm1j6mQ.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +520 B 🔴 +456 B
assets/refreshRemoteConfig-Du81YMEk.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -523 B 🟢 -469 B
assets/config-CGn5JFmU.js 996 B 996 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B5oF6TeI.js 29.9 kB 29.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BVYOg4dh.js 24.5 kB 24.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CBEvSL1z.js 38.5 kB 38.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CGx1t8IZ.js 27.8 kB 27.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CNcb_4nC.js 30.5 kB 30.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Cx1dZM6H.js 23.9 kB 23.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Dw-QS6Nb.js 27.9 kB 27.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DXxgnCSn.js 32.4 kB 32.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-GRFn4guL.js 34.2 kB 34.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-mgwKIVQ2.js 28.8 kB 28.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-s83B801I.js 28.7 kB 28.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 10 added / 10 removed

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-BnJKERPl.js (removed) 3.4 kB 🟢 -3.4 kB 🟢 -1.18 kB 🟢 -993 B
assets/auth-Cx-8gLZm.js (new) 3.4 kB 🔴 +3.4 kB 🔴 +1.18 kB 🔴 +991 B
assets/SignUpForm-BurElsyq.js (new) 3.01 kB 🔴 +3.01 kB 🔴 +1.23 kB 🔴 +1.1 kB
assets/SignUpForm-Cd9-b0pj.js (removed) 3.01 kB 🟢 -3.01 kB 🟢 -1.23 kB 🟢 -1.1 kB
assets/UpdatePasswordContent-BgBq4_lz.js (removed) 2.37 kB 🟢 -2.37 kB 🟢 -1.07 kB 🟢 -945 B
assets/UpdatePasswordContent-DrXoCHOP.js (new) 2.37 kB 🔴 +2.37 kB 🔴 +1.07 kB 🔴 +944 B
assets/firebaseAuthStore-Cofe6i8Y.js (new) 788 B 🔴 +788 B 🔴 +383 B 🔴 +344 B
assets/firebaseAuthStore-ZxWmvxn1.js (removed) 788 B 🟢 -788 B 🟢 -384 B 🟢 -344 B
assets/auth-B7VIlV9t.js (new) 357 B 🔴 +357 B 🔴 +226 B 🔴 +218 B
assets/auth-DCpcVCM6.js (removed) 357 B 🟢 -357 B 🟢 -223 B 🟢 -191 B
assets/PasswordFields-D2N9Ml9Z.js 4.51 kB 4.51 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WorkspaceProfilePic-D9lCv5xq.js 1.57 kB 1.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Editors & Dialogs — 736 B (baseline 736 B) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-CcOhhVTf.js (removed) 736 B 🟢 -736 B 🟢 -380 B 🟢 -353 B
assets/useSubscriptionDialog-D6oJVcKf.js (new) 736 B 🔴 +736 B 🔴 +375 B 🔴 +324 B

Status: 1 added / 1 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useTerminalTabs-BCTF61Om.js (removed) 9.84 kB 🟢 -9.84 kB 🟢 -3.4 kB 🟢 -3 kB
assets/useTerminalTabs-CI8XoArf.js (new) 9.84 kB 🔴 +9.84 kB 🔴 +3.4 kB 🔴 +3 kB
assets/ComfyQueueButton-B6tDv3xW.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.49 kB 🟢 -2.22 kB
assets/ComfyQueueButton-BAv-RonG.js (new) 8.02 kB 🔴 +8.02 kB 🔴 +2.48 kB 🔴 +2.21 kB
assets/SubscribeButton-BQ5bR7Av.js (removed) 2.34 kB 🟢 -2.34 kB 🟢 -1.01 kB 🟢 -882 B
assets/SubscribeButton-CcaGuxEp.js (new) 2.34 kB 🔴 +2.34 kB 🔴 +1.01 kB 🔴 +887 B
assets/cloudFeedbackTopbarButton-TdPqd60A.js (new) 1.59 kB 🔴 +1.59 kB 🔴 +853 B 🔴 +761 B
assets/cloudFeedbackTopbarButton-y2S5zwT8.js (removed) 1.59 kB 🟢 -1.59 kB 🟢 -851 B 🟢 -761 B
assets/ComfyQueueButton-6SVmvdwb.js (new) 793 B 🔴 +793 B 🔴 +394 B 🔴 +350 B
assets/ComfyQueueButton-CJG4yDcP.js (removed) 793 B 🟢 -793 B 🟢 -395 B 🟢 -352 B
assets/Button-B1gb-xyl.js 3.07 kB 3.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudBadge-y8d0lfIa.js 1.16 kB 1.16 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/FormSearchInput-C6wi2TDa.js 3.73 kB 3.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ScrubableNumberInput-BJjaWLBx.js 5.94 kB 5.94 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-BpSw2jyq.js 7.44 kB 7.44 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-4quz6WWI.js 1.17 kB 1.17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-DXRV2UQg.js 1.84 kB 1.84 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Data & Services — 2.64 MB (baseline 2.64 MB) • 🔴 +752 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-Dt7Q9mwJ.js (removed) 1.82 MB 🟢 -1.82 MB 🟢 -410 kB 🟢 -308 kB
assets/dialogService-BVCGsgTW.js (new) 1.82 MB 🔴 +1.82 MB 🔴 +410 kB 🔴 +308 kB
assets/api-CkUE2KBb.js (removed) 688 kB 🟢 -688 kB 🟢 -155 kB 🟢 -123 kB
assets/api-DpX0gh5d.js (new) 688 kB 🔴 +688 kB 🔴 +155 kB 🔴 +124 kB
assets/load3dService-DqMqY5eo.js (new) 91 kB 🔴 +91 kB 🔴 +19.1 kB 🔴 +16.4 kB
assets/load3dService-iaWHBij1.js (removed) 91 kB 🟢 -91 kB 🟢 -19.1 kB 🟢 -16.4 kB
assets/extensionStore-LbTc259l.js (new) 13 kB 🔴 +13 kB 🔴 +4.42 kB 🔴 +3.91 kB
assets/extensionStore-B7RKpxBV.js (removed) 12.1 kB 🟢 -12.1 kB 🟢 -4.21 kB 🟢 -3.69 kB
assets/releaseStore-BFoBeBKQ.js (removed) 7.96 kB 🟢 -7.96 kB 🟢 -2.23 kB 🟢 -1.95 kB
assets/releaseStore-Bq4rgpmK.js (new) 7.96 kB 🔴 +7.96 kB 🔴 +2.22 kB 🔴 +1.95 kB
assets/keybindingService-B-DkFgGF.js (removed) 6.52 kB 🟢 -6.52 kB 🟢 -1.71 kB 🟢 -1.47 kB
assets/keybindingService-O9Q6fAja.js (new) 6.52 kB 🔴 +6.52 kB 🔴 +1.71 kB 🔴 +1.47 kB
assets/bootstrapStore-CkWJROF8.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -876 B 🟢 -798 B
assets/bootstrapStore-DU9fGa7C.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +877 B 🔴 +795 B
assets/userStore-Cb0pTb5j.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +719 B 🔴 +671 B
assets/userStore-DXtXIELG.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -721 B 🟢 -673 B
assets/audioService-BndS-16J.js (removed) 1.73 kB 🟢 -1.73 kB 🟢 -850 B 🟢 -724 B
assets/audioService-Dhki1lv7.js (new) 1.73 kB 🔴 +1.73 kB 🔴 +849 B 🔴 +725 B
assets/releaseStore-B78S6HWm.js (removed) 760 B 🟢 -760 B 🟢 -382 B 🟢 -336 B
assets/releaseStore-CwUhVpOL.js (new) 760 B 🔴 +760 B 🔴 +381 B 🔴 +337 B
assets/settingStore-BIYIe81E.js (new) 744 B 🔴 +744 B 🔴 +384 B 🔴 +340 B
assets/settingStore-CCtEiJJb.js (removed) 744 B 🟢 -744 B 🟢 -387 B 🟢 -342 B
assets/workflowDraftStore-BgGl6hr_.js (new) 736 B 🔴 +736 B 🔴 +376 B 🔴 +331 B
assets/workflowDraftStore-BRgGb9pI.js (removed) 736 B 🟢 -736 B 🟢 -377 B 🟢 -329 B
assets/dialogService-Bsyf3OjT.js (new) 725 B 🔴 +725 B 🔴 +367 B 🔴 +325 B
assets/dialogService-hyR129bh.js (removed) 725 B 🟢 -725 B 🟢 -368 B 🟢 -326 B
assets/serverConfigStore-CjFLR4Tj.js 2.32 kB 2.32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 13 added / 13 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useLoad3d-BNz9gNpV.js (new) 14.6 kB 🔴 +14.6 kB 🔴 +3.63 kB 🔴 +3.21 kB
assets/useLoad3d-CaYBdI0b.js (removed) 14.6 kB 🟢 -14.6 kB 🟢 -3.63 kB 🟢 -3.21 kB
assets/useLoad3dViewer-CRZx4oKk.js (new) 14.1 kB 🔴 +14.1 kB 🔴 +3.15 kB 🔴 +2.8 kB
assets/useLoad3dViewer-CYDUrjc6.js (removed) 14.1 kB 🟢 -14.1 kB 🟢 -3.15 kB 🟢 -2.8 kB
assets/useFeatureFlags-Cf3zIcJJ.js (new) 4.14 kB 🔴 +4.14 kB 🔴 +1.24 kB 🔴 +1.06 kB
assets/useFeatureFlags-uB8BXZFd.js (removed) 4.14 kB 🟢 -4.14 kB 🟢 -1.24 kB 🟢 -1.06 kB
assets/useWorkspaceUI-BGO--0KL.js (removed) 3 kB 🟢 -3 kB 🟢 -823 B 🟢 -703 B
assets/useWorkspaceUI-CTLRZXZ6.js (new) 3 kB 🔴 +3 kB 🔴 +824 B 🔴 +709 B
assets/subscriptionCheckoutUtil-8FoiZZ_y.js (removed) 2.53 kB 🟢 -2.53 kB 🟢 -1.06 kB 🟢 -959 B
assets/subscriptionCheckoutUtil-eVIPaHg6.js (new) 2.53 kB 🔴 +2.53 kB 🔴 +1.06 kB 🔴 +954 B
assets/useExternalLink-C0MVPIP2.js (new) 1.66 kB 🔴 +1.66 kB 🔴 +773 B 🔴 +679 B
assets/useExternalLink-DOKIicl6.js (removed) 1.66 kB 🟢 -1.66 kB 🟢 -770 B 🟢 -676 B
assets/useErrorHandling-BybeUcjn.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +628 B 🔴 +533 B
assets/useErrorHandling-sQ1vmR5p.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -630 B 🟢 -534 B
assets/useWorkspaceSwitch-Bc89Ltj-.js (new) 1.25 kB 🔴 +1.25 kB 🔴 +542 B 🔴 +482 B
assets/useWorkspaceSwitch-cpLT7AR0.js (removed) 1.25 kB 🟢 -1.25 kB 🟢 -542 B 🟢 -489 B
assets/useLoad3d-bIZfEVKf.js (removed) 859 B 🟢 -859 B 🟢 -426 B 🟢 -378 B
assets/useLoad3d-CAO1JRRb.js (new) 859 B 🔴 +859 B 🔴 +423 B 🔴 +379 B
assets/audioUtils-cJYt3tPg.js (new) 858 B 🔴 +858 B 🔴 +501 B 🔴 +410 B
assets/audioUtils-DL78OoBq.js (removed) 858 B 🟢 -858 B 🟢 -501 B 🟢 -406 B
assets/useLoad3dViewer-BajNGtE0.js (removed) 838 B 🟢 -838 B 🟢 -410 B 🟢 -367 B
assets/useLoad3dViewer-msfsxJke.js (new) 838 B 🔴 +838 B 🔴 +410 B 🔴 +369 B
assets/useCurrentUser-Cegi7Aod.js (new) 722 B 🔴 +722 B 🔴 +371 B 🔴 +327 B
assets/useCurrentUser-K2kS8pvP.js (removed) 722 B 🟢 -722 B 🟢 -375 B 🟢 -325 B
assets/_plugin-vue_export-helper-ralzwvFM.js 315 B 315 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-BdnFBncR.js 7 kB 7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/envUtil-Clzmwvt4.js 466 B 466 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-BwTAJqBg.js 1.56 kB 1.56 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SkeletonUtils-BputJAFn.js 133 B 133 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 12 added / 12 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-axios-Cp6hch1I.js 70.7 kB 70.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-chart-BxkFiWzp.js 399 kB 399 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-firebase-BvMr43CG.js 836 kB 836 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-i18n-DccD0mxo.js 133 kB 133 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-markdown-D5S6AC80.js 103 kB 103 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-5I_5Qfg0.js 1.52 MB 1.52 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-BleJN4K0.js 1.73 MB 1.73 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-Cvp9N2RA.js 384 kB 384 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-sentry-SQwstEKc.js 182 kB 182 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-LBLOE6BD.js 1.8 MB 1.8 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-z9_RK3Ae.js 634 kB 634 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-core-CmHHRvL9.js 311 kB 311 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vueuse-B4hGe0IQ.js 113 kB 113 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-MKpa1ZAW.js 374 kB 374 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-yjs-CP_4YO8u.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-zod-DcCUUPIi.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 7.89 MB (baseline 7.89 MB) • 🟢 -153 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/i18n-CdNCTD2C.js (removed) 534 kB 🟢 -534 kB 🟢 -103 kB 🟢 -80.3 kB
assets/i18n-TaGTrLYY.js (new) 534 kB 🔴 +534 kB 🔴 +103 kB 🔴 +80.3 kB
assets/core--LQrOLav.js (removed) 73.6 kB 🟢 -73.6 kB 🟢 -19 kB 🟢 -16.2 kB
assets/core-aQ7-bpT_.js (new) 73.6 kB 🔴 +73.6 kB 🔴 +19 kB 🔴 +16.3 kB
assets/groupNode-B2SqWPRM.js (new) 71.8 kB 🔴 +71.8 kB 🔴 +17.7 kB 🔴 +15.5 kB
assets/groupNode-BvsumVgQ.js (removed) 71.8 kB 🟢 -71.8 kB 🟢 -17.7 kB 🟢 -15.6 kB
assets/WidgetSelect-CIe9mNKH.js (removed) 58.2 kB 🟢 -58.2 kB 🟢 -12.4 kB 🟢 -10.7 kB
assets/WidgetSelect-Dunszw4j.js (new) 58.2 kB 🔴 +58.2 kB 🔴 +12.4 kB 🔴 +10.7 kB
assets/SubscriptionRequiredDialogContentWorkspace-BqjFwuEQ.js (new) 46.3 kB 🔴 +46.3 kB 🔴 +8.66 kB 🔴 +7.5 kB
assets/SubscriptionRequiredDialogContentWorkspace-DQZwE7we.js (removed) 46.3 kB 🟢 -46.3 kB 🟢 -8.65 kB 🟢 -7.5 kB
assets/WidgetPainter-0aGtrGv4.js (new) 32.5 kB 🔴 +32.5 kB 🔴 +7.96 kB 🔴 +7.06 kB
assets/WidgetPainter-CLBgVN-Q.js (removed) 32.5 kB 🟢 -32.5 kB 🟢 -7.96 kB 🟢 -7.07 kB
assets/Load3DControls-2_tq-TgM.js (removed) 30.9 kB 🟢 -30.9 kB 🟢 -5.34 kB 🟢 -4.64 kB
assets/Load3DControls-DCRQXim_.js (new) 30.9 kB 🔴 +30.9 kB 🔴 +5.34 kB 🔴 +4.64 kB
assets/WorkspacePanelContent-B4Vo0YMX.js (removed) 29.3 kB 🟢 -29.3 kB 🟢 -6.15 kB 🟢 -5.4 kB
assets/WorkspacePanelContent-C5E_bj1k.js (new) 29.3 kB 🔴 +29.3 kB 🔴 +6.15 kB 🔴 +5.38 kB
assets/SubscriptionRequiredDialogContent-B8d2Pf3s.js (removed) 25.7 kB 🟢 -25.7 kB 🟢 -6.57 kB 🟢 -5.79 kB
assets/SubscriptionRequiredDialogContent-EaD_6vkj.js (new) 25.7 kB 🔴 +25.7 kB 🔴 +6.57 kB 🔴 +5.78 kB
assets/Load3dViewerContent-B17bWBgF.js (new) 23 kB 🔴 +23 kB 🔴 +5.18 kB 🔴 +4.49 kB
assets/Load3dViewerContent-Bj_4hH8B.js (removed) 23 kB 🟢 -23 kB 🟢 -5.18 kB 🟢 -4.49 kB
assets/WidgetImageCrop-CBp18Auc.js (new) 22.1 kB 🔴 +22.1 kB 🔴 +5.51 kB 🔴 +4.84 kB
assets/WidgetImageCrop-CnURnrDj.js (removed) 22.1 kB 🟢 -22.1 kB 🟢 -5.51 kB 🟢 -4.85 kB
assets/SubscriptionPanelContentWorkspace-BVB4Y2VJ.js (new) 22 kB 🔴 +22 kB 🔴 +5.11 kB 🔴 +4.5 kB
assets/SubscriptionPanelContentWorkspace-DnlabUur.js (removed) 22 kB 🟢 -22 kB 🟢 -5.11 kB 🟢 -4.5 kB
assets/CurrentUserPopoverWorkspace-BFjOfpWM.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -4.94 kB 🟢 -4.42 kB
assets/CurrentUserPopoverWorkspace-CBRj0aE2.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +4.94 kB 🔴 +4.41 kB
assets/SignInContent-BZXg2B4J.js (new) 18.9 kB 🔴 +18.9 kB 🔴 +4.76 kB 🔴 +4.16 kB
assets/SignInContent-Ddr_Jvek.js (removed) 18.9 kB 🟢 -18.9 kB 🟢 -4.76 kB 🟢 -4.16 kB
assets/WidgetInputNumber-B5SYQ5yi.js (new) 18.7 kB 🔴 +18.7 kB 🔴 +4.75 kB 🔴 +4.23 kB
assets/WidgetInputNumber-CAvku62l.js (removed) 18.7 kB 🟢 -18.7 kB 🟢 -4.75 kB 🟢 -4.22 kB
assets/WidgetRecordAudio-DLjsWBeC.js (removed) 17.4 kB 🟢 -17.4 kB 🟢 -4.99 kB 🟢 -4.47 kB
assets/WidgetRecordAudio-gL2tIPIn.js (new) 17.4 kB 🔴 +17.4 kB 🔴 +4.99 kB 🔴 +4.47 kB
assets/Load3D-CLXj6ZOx.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +4.03 kB 🔴 +3.52 kB
assets/Load3D-DZx3ZcjN.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -4.03 kB 🟢 -3.51 kB
assets/load3d-BPEt5SrH.js (new) 14.7 kB 🔴 +14.7 kB 🔴 +4.19 kB 🔴 +3.64 kB
assets/load3d-Cl3-b_Qh.js (removed) 14.7 kB 🟢 -14.7 kB 🟢 -4.19 kB 🟢 -3.63 kB
assets/AudioPreviewPlayer-BtCN7T3R.js (new) 10.9 kB 🔴 +10.9 kB 🔴 +3.24 kB 🔴 +2.9 kB
assets/AudioPreviewPlayer-Dlm9w4Xy.js (removed) 10.9 kB 🟢 -10.9 kB 🟢 -3.24 kB 🟢 -2.9 kB
assets/changeTracker-Cz-5JGge.js (new) 9.38 kB 🔴 +9.38 kB 🔴 +2.89 kB 🔴 +2.55 kB
assets/changeTracker-ll27V_yU.js (removed) 9.38 kB 🟢 -9.38 kB 🟢 -2.9 kB 🟢 -2.55 kB
assets/nodeTemplates-D_98-O2o.js (new) 9.29 kB 🔴 +9.29 kB 🔴 +3.26 kB 🔴 +2.86 kB
assets/nodeTemplates-DMAC8yhU.js (removed) 9.29 kB 🟢 -9.29 kB 🟢 -3.26 kB 🟢 -2.86 kB
assets/InviteMemberDialogContent-a2IuNZyY.js (new) 7.38 kB 🔴 +7.38 kB 🔴 +2.29 kB 🔴 +2 kB
assets/InviteMemberDialogContent-rIY0PP2K.js (removed) 7.38 kB 🟢 -7.38 kB 🟢 -2.3 kB 🟢 -2 kB
assets/Load3DConfiguration-Crme-eeY.js (new) 6.27 kB 🔴 +6.27 kB 🔴 +1.91 kB 🔴 +1.68 kB
assets/Load3DConfiguration-CY9ny15A.js (removed) 6.27 kB 🟢 -6.27 kB 🟢 -1.92 kB 🟢 -1.68 kB
assets/CreateWorkspaceDialogContent-Btjnf_nT.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -1.99 kB 🟢 -1.75 kB
assets/CreateWorkspaceDialogContent-BVISAR1m.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +1.99 kB 🔴 +1.74 kB
assets/onboardingCloudRoutes-DgOLLE_l.js (new) 5.41 kB 🔴 +5.41 kB 🔴 +1.84 kB 🔴 +1.59 kB
assets/onboardingCloudRoutes-MYheJ154.js (removed) 5.41 kB 🟢 -5.41 kB 🟢 -1.84 kB 🟢 -1.6 kB
assets/FreeTierDialogContent-DvhNyDh1.js (new) 5.39 kB 🔴 +5.39 kB 🔴 +1.9 kB 🔴 +1.68 kB
assets/FreeTierDialogContent-GmBcXjRt.js (removed) 5.39 kB 🟢 -5.39 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/EditWorkspaceDialogContent-BCa7voQm.js (removed) 5.33 kB 🟢 -5.33 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/EditWorkspaceDialogContent-DpNTWLTZ.js (new) 5.33 kB 🔴 +5.33 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/ValueControlPopover-BbYut119.js (new) 4.92 kB 🔴 +4.92 kB 🔴 +1.77 kB 🔴 +1.58 kB
assets/ValueControlPopover-DeHaA3uc.js (removed) 4.92 kB 🟢 -4.92 kB 🟢 -1.76 kB 🟢 -1.58 kB
assets/Preview3d-D-Hy1sTv.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.57 kB 🔴 +1.37 kB
assets/Preview3d-D0RsppoK.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.56 kB 🟢 -1.37 kB
assets/CancelSubscriptionDialogContent-BfOcL4pY.js (removed) 4.79 kB 🟢 -4.79 kB 🟢 -1.78 kB 🟢 -1.56 kB
assets/CancelSubscriptionDialogContent-CUdtMXUC.js (new) 4.79 kB 🔴 +4.79 kB 🔴 +1.78 kB 🔴 +1.57 kB
assets/DeleteWorkspaceDialogContent-CtdaMJ_-.js (new) 4.23 kB 🔴 +4.23 kB 🔴 +1.63 kB 🔴 +1.43 kB
assets/DeleteWorkspaceDialogContent-FB1HTlNR.js (removed) 4.23 kB 🟢 -4.23 kB 🟢 -1.63 kB 🟢 -1.42 kB
assets/WidgetWithControl-CBCh9_ei.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.78 kB 🔴 +1.61 kB
assets/WidgetWithControl-snKZGWTu.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.78 kB 🟢 -1.59 kB
assets/LeaveWorkspaceDialogContent-C92STtmY.js (new) 4.06 kB 🔴 +4.06 kB 🔴 +1.58 kB 🔴 +1.38 kB
assets/LeaveWorkspaceDialogContent-CudSrgwq.js (removed) 4.06 kB 🟢 -4.06 kB 🟢 -1.58 kB 🟢 -1.38 kB
assets/RemoveMemberDialogContent-0WXP-I5X.js (removed) 4.04 kB 🟢 -4.04 kB 🟢 -1.53 kB 🟢 -1.33 kB
assets/RemoveMemberDialogContent-BIGF5Nb1.js (new) 4.04 kB 🔴 +4.04 kB 🔴 +1.53 kB 🔴 +1.33 kB
assets/RevokeInviteDialogContent-BcZdh33L.js (new) 3.95 kB 🔴 +3.95 kB 🔴 +1.54 kB 🔴 +1.35 kB
assets/RevokeInviteDialogContent-C4hOHsKh.js (removed) 3.95 kB 🟢 -3.95 kB 🟢 -1.54 kB 🟢 -1.36 kB
assets/InviteMemberUpsellDialogContent-CMgrI-Hu.js (removed) 3.82 kB 🟢 -3.82 kB 🟢 -1.4 kB 🟢 -1.23 kB
assets/InviteMemberUpsellDialogContent-CpU3Ruaw.js (new) 3.82 kB 🔴 +3.82 kB 🔴 +1.4 kB 🔴 +1.23 kB
assets/tierBenefits-B-RKZkD7.js (removed) 3.66 kB 🟢 -3.66 kB 🟢 -1.3 kB 🟢 -1.17 kB
assets/tierBenefits-GEyVE0gQ.js (new) 3.66 kB 🔴 +3.66 kB 🔴 +1.3 kB 🔴 +1.16 kB
assets/saveMesh-DjCVNlca.js (removed) 3.38 kB 🟢 -3.38 kB 🟢 -1.46 kB 🟢 -1.29 kB
assets/saveMesh-Yt7GtNew.js (new) 3.38 kB 🔴 +3.38 kB 🔴 +1.46 kB 🔴 +1.29 kB
assets/cloudSessionCookie-dne8NqDy.js (new) 3.1 kB 🔴 +3.1 kB 🔴 +1.08 kB 🔴 +943 B
assets/cloudSessionCookie-MjWrGps5.js (removed) 3.1 kB 🟢 -3.1 kB 🟢 -1.09 kB 🟢 -955 B
assets/GlobalToast-C4jgUg3X.js (new) 2.91 kB 🔴 +2.91 kB 🔴 +1.21 kB 🔴 +1.07 kB
assets/GlobalToast-CdEkK3A_.js (removed) 2.91 kB 🟢 -2.91 kB 🟢 -1.21 kB 🟢 -1.06 kB
assets/ApiNodesSignInContent-BPb4lgr_.js (new) 2.69 kB 🔴 +2.69 kB 🔴 +1.05 kB 🔴 +919 B
assets/ApiNodesSignInContent-Cb0hEeKo.js (removed) 2.69 kB 🟢 -2.69 kB 🟢 -1.05 kB 🟢 -954 B
assets/SubscribeToRun-DIKD6u2V.js (new) 2.06 kB 🔴 +2.06 kB 🔴 +950 B 🔴 +842 B
assets/SubscribeToRun-pvG3dLkV.js (removed) 2.06 kB 🟢 -2.06 kB 🟢 -949 B 🟢 -842 B
assets/CloudRunButtonWrapper-CFKrrv1y.js (removed) 1.68 kB 🟢 -1.68 kB 🟢 -784 B 🟢 -706 B
assets/CloudRunButtonWrapper-DfAXTAl4.js (new) 1.68 kB 🔴 +1.68 kB 🔴 +786 B 🔴 +711 B
assets/signInSchema-B-h-sQvp.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -563 B 🟢 -517 B
assets/signInSchema-CPY-jhLU.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +562 B 🔴 +515 B
assets/previousFullPath-1uQaEFDU.js (removed) 1.39 kB 🟢 -1.39 kB 🟢 -652 B 🟢 -581 B
assets/previousFullPath-B3qMA33Z.js (new) 1.39 kB 🔴 +1.39 kB 🔴 +652 B 🔴 +577 B
assets/cloudBadges-BG1w5fmL.js (removed) 1.36 kB 🟢 -1.36 kB 🟢 -707 B 🟢 -615 B
assets/cloudBadges-udO4dPds.js (new) 1.36 kB 🔴 +1.36 kB 🔴 +705 B 🔴 +613 B
assets/cloudSubscription-B5PP-6zr.js (new) 1.33 kB 🔴 +1.33 kB 🔴 +653 B 🔴 +564 B
assets/cloudSubscription-Cy3u9j7S.js (removed) 1.33 kB 🟢 -1.33 kB 🟢 -655 B 🟢 -572 B
assets/Load3D-B0PxEN29.js (new) 1.07 kB 🔴 +1.07 kB 🔴 +499 B 🔴 +443 B
assets/Load3D-C-C4RoUb.js (removed) 1.07 kB 🟢 -1.07 kB 🟢 -501 B 🟢 -445 B
assets/nightlyBadges-CwFUteFw.js (new) 1 kB 🔴 +1 kB 🔴 +529 B 🔴 +480 B
assets/nightlyBadges-DL616cft.js (removed) 1 kB 🟢 -1 kB 🟢 -531 B 🟢 -475 B
assets/Load3dViewerContent-BqwyQtbL.js (new) 993 B 🔴 +993 B 🔴 +469 B 🔴 +417 B
assets/Load3dViewerContent-DN04RJa8.js (removed) 993 B 🟢 -993 B 🟢 -467 B 🟢 -415 B
assets/SubscriptionPanelContentWorkspace-C27U6pJu.js (new) 920 B 🔴 +920 B 🔴 +440 B 🔴 +375 B
assets/SubscriptionPanelContentWorkspace-DirsQNzn.js (removed) 920 B 🟢 -920 B 🟢 -438 B 🟢 -378 B
assets/graphHasMissingNodes-B5ERQV8x.js (removed) 761 B 🟢 -761 B 🟢 -376 B 🟢 -318 B
assets/graphHasMissingNodes-DQPdynOS.js (new) 761 B 🔴 +761 B 🔴 +375 B 🔴 +333 B
assets/changeTracker-B_ereviZ.js (removed) 757 B 🟢 -757 B 🟢 -386 B 🟢 -340 B
assets/changeTracker-CFh3lQrs.js (new) 757 B 🔴 +757 B 🔴 +381 B 🔴 +336 B
assets/WidgetLegacy-B_t9oHKd.js (removed) 744 B 🟢 -744 B 🟢 -385 B 🟢 -337 B
assets/WidgetLegacy-DLLC8qOJ.js (new) 744 B 🔴 +744 B 🔴 +381 B 🔴 +330 B
assets/WidgetInputNumber-CKHpa4KL.js (removed) 469 B 🟢 -469 B 🟢 -266 B 🟢 -229 B
assets/WidgetInputNumber-Cp50mQPq.js (new) 469 B 🔴 +469 B 🔴 +263 B 🔴 +226 B
assets/i18n-C5ZzBUOD.js (new) 199 B 🔴 +199 B 🔴 +161 B 🔴 +139 B
assets/i18n-CuFcu2Sy.js (removed) 199 B 🟢 -199 B 🟢 -161 B 🟢 -138 B
assets/AnimationControls-CKHXO1yi.js 4.61 kB 4.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/auto-BTnZwrs2.js 1.7 kB 1.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/BaseViewTemplate-BNRU5hKJ.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/comfy-logo-single-D9MrYETV.js 198 B 198 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ComfyOrgHeader-BbfHqQlJ.js 910 B 910 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-B-AdR9IA.js 17.5 kB 17.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CbkxT8K8.js 16.1 kB 16.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CJGmjcIS.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CMaLgTTb.js 16.7 kB 16.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Cw07MMbJ.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-D7EtdE6o.js 16.9 kB 16.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DknEFpK3.js 15.2 kB 15.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Ds6WuXnw.js 16.1 kB 16.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Dvq-F-mb.js 17.5 kB 17.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-pUOay9Eo.js 15.1 kB 15.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-u2AZ8xU4.js 16.1 kB 16.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/constants-htt0vt7m.js 579 B 579 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-B4gaX-yc.js 148 kB 148 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BDkWXD94.js 131 kB 131 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BMRaSx8g.js 132 kB 132 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BQewqC5f.js 152 kB 152 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CAdoKrN8.js 154 kB 154 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CDYHbD1q.js 179 kB 179 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DEgKa3D5.js 172 kB 172 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-EWELmaLr.js 157 kB 157 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Mk6d58ld.js 208 kB 208 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-o4jORuCB.js 149 kB 149 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-r2f5phvM.js 186 kB 186 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-DjGAidD-.js 1.82 kB 1.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-CXjvquTX.js 1.43 kB 1.43 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-BXQSEpUj.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaOtherTop-z-FoiFro.js 1.02 kB 1.02 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaTextTop-BUmyBrmT.js 1.01 kB 1.01 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-C5eK56v1.js 2.77 kB 2.77 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BRiV_JqD.js 451 kB 451 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BRNi4BQJ.js 404 kB 404 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BYxgBGHq.js 400 kB 400 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CRJ1lnRc.js 396 kB 396 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CYplVYdX.js 364 kB 364 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Czw9YrJ_.js 368 kB 368 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dp7_2zi-.js 391 kB 391 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DY1M9TFM.js 399 kB 399 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-EIvY3SXE.js 491 kB 491 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-TppPBr5S.js 416 kB 416 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-WGHIzLau.js 450 kB 450 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Popover-BkQMC6X8.js 3.65 kB 3.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-DLICfi3-.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SelectValue-CHY6yGRE.js 8.94 kB 8.94 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Slider-D-A2OlTc.js 3.52 kB 3.52 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/src-DqM23qfs.js 251 B 251 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SubscriptionBenefits-CC52XK_P.js 2.01 kB 2.01 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/telemetry-zZf2dHJ2.js 226 B 226 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/types-DT3N7am7.js 204 B 204 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/VideoPlayOverlay-D_dHFUc0.js 1.35 kB 1.35 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-NeEr3XWN.js 586 B 586 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-qaVo77FH.js 283 B 283 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-wUl6fvix.js 3.19 kB 3.19 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BF6Uqf9O.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-DfzN4kvf.js 2.9 kB 2.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetCurve-D_JEKVtF.js 9.36 kB 9.36 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-BHbklsS7.js 3.61 kB 3.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DK9b41HQ.js 7 kB 7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-DPIziO_S.js 1.86 kB 1.86 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-CIhDMEfL.js 1.98 kB 1.98 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-BArFg9m6.js 2.93 kB 2.93 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-DN03zIgB.js 1.1 kB 1.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-CgbX1pnn.js 3.96 kB 3.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-CiiU3Ykg.js 6.8 kB 6.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetTypes-DYLaC2lj.js 393 B 393 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 56 added / 56 removed

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

⚡ Performance Report

Metric Baseline PR Δ
canvas-idle: style recalcs 123 126 +2% ⚪
canvas-idle: layouts 0 1 new 🔴
canvas-idle: task duration 477ms 443ms -7% ⚪
canvas-mouse-sweep: style recalcs 165 184 +12% 🟠
canvas-mouse-sweep: layouts 12 12 +0% ⚪
canvas-mouse-sweep: task duration 807ms 1071ms +33% 🔴
dom-widget-clipping: style recalcs 41 43 +5% ⚪
dom-widget-clipping: layouts 0 0 +0% ⚪
dom-widget-clipping: task duration 364ms 387ms +6% ⚪
Raw data
{
  "timestamp": "2026-03-03T13:24:39.238Z",
  "gitSha": "a4ed56777112c54583e4c31c00d7b17f841dddc8",
  "branch": "pysssss/app-mode-data-saving-refactor",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2033.1959999999754,
      "styleRecalcs": 126,
      "styleRecalcDurationMs": 27.67,
      "layouts": 1,
      "layoutDurationMs": 0.18400000000000014,
      "taskDurationMs": 442.5350000000001,
      "heapDeltaBytes": -3178616
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2080.454999999972,
      "styleRecalcs": 184,
      "styleRecalcDurationMs": 52.87599999999999,
      "layouts": 12,
      "layoutDurationMs": 2.988,
      "taskDurationMs": 1070.827,
      "heapDeltaBytes": -2359596
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 594.2120000000273,
      "styleRecalcs": 43,
      "styleRecalcDurationMs": 13.328,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 387.04499999999996,
      "heapDeltaBytes": 7020640
    }
  ]
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/platform/workflow/core/services/workflowService.ts (1)

157-191: ⚠️ Potential issue | 🟡 Minor

Propagate save/cancel outcome from saveWorkflow to callers.

For temporary workflows, saveWorkflowAs() can return false (cancel), but saveWorkflow() currently returns void. That prevents caller flows from distinguishing cancel vs success.

Proposed fix
-  const saveWorkflow = async (workflow: ComfyWorkflow) => {
+  const saveWorkflow = async (workflow: ComfyWorkflow): Promise<boolean> => {
     if (workflow.isTemporary) {
-      await saveWorkflowAs(workflow)
+      return await saveWorkflowAs(workflow)
     } else {
       syncLinearMode(workflow, [app.rootGraph])
       workflow.changeTracker?.checkState()
@@
           if ((await confirmOverwrite(expectedPath)) !== true) {
             await workflowStore.saveWorkflow(workflow)
-            return
+            return true
           }
           await deleteWorkflow(existing, true)
         }
@@
       }
 
       await workflowStore.saveWorkflow(workflow)
+      return true
     }
   }
// caller example (outside this line range)
const saved = await workflowService.saveWorkflow(workflow)
if (saved) close()

As per coding guidelines: src/**/*.{js,ts,vue}: Implement proper error propagation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/workflow/core/services/workflowService.ts` around lines 157 -
191, saveWorkflow currently returns void so callers can't detect a user-cancel
from saveWorkflowAs; change saveWorkflow to return a boolean (true on successful
save, false on cancel) by capturing the result of saveWorkflowAs(workflow) when
workflow.isTemporary and returning false if that call returns false, and return
true after successful flows in the non-temporary branch (after
renameWorkflow/workflowStore.saveWorkflow completes); ensure all internal
branches that short-circuit (e.g., the confirmOverwrite path where you call
workflowStore.saveWorkflow and return) return an appropriate boolean, and update
any callers of saveWorkflow to await and handle the boolean result.
🧹 Nitpick comments (3)
src/components/builder/DefaultViewDialogContent.vue (1)

80-93: Consider making viewTypeOptions reactive for locale changes.

The viewTypeOptions array is defined once at component setup using t(). If the user changes their locale while the dialog is open, these translated strings won't update. This is a minor edge case, but for consistency with other components, consider using a computed property.

♻️ Optional: Make viewTypeOptions a computed
-const viewTypeOptions = [
+const viewTypeOptions = computed(() => [
   {
     value: true,
     icon: 'icon-[lucide--app-window]',
     title: t('builderToolbar.app'),
     subtitle: t('builderToolbar.appDescription')
   },
   {
     value: false,
     icon: 'icon-[comfy--workflow]',
     title: t('builderToolbar.nodeGraph'),
     subtitle: t('builderToolbar.nodeGraphDescription')
   }
-]
+])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/builder/DefaultViewDialogContent.vue` around lines 80 - 93,
The viewTypeOptions array is built once using t() so translations won't update
on locale change; change viewTypeOptions from a static constant to a computed
property (e.g., computed(() => [ ... ]) ) that calls t() for title/subtitle so
it reacts to i18n locale updates; update any references to viewTypeOptions in
the template or setup to use the new computed ref, preserving the same
value/icon structure.
packages/shared-frontend-utils/src/formatUtil.ts (1)

42-46: Consider documenting the getWorkflowSuffix fallback behavior.

The function silently falls back to 'json' for any value other than 'app.json'. While this is likely intentional, a brief JSDoc comment would clarify the design decision for future maintainers.

📝 Optional: Add JSDoc for clarity
+/**
+ * Maps a suffix string to a valid WorkflowSuffix.
+ * Returns 'app.json' only for exact match; all other values default to 'json'.
+ */
 export function getWorkflowSuffix(
   suffix: string | null | undefined
 ): WorkflowSuffix {
   return suffix === 'app.json' ? 'app.json' : 'json'
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared-frontend-utils/src/formatUtil.ts` around lines 42 - 46, Add a
short JSDoc comment above getWorkflowSuffix explaining that the function returns
'app.json' only when the input exactly equals 'app.json' and otherwise silently
falls back to 'json' (covering null/undefined and any other strings), and
mention the intended return type WorkflowSuffix so future maintainers understand
the fallback behavior and accepted inputs.
src/components/builder/useAppSetDefaultView.ts (1)

49-56: Minor: reset() is called twice when applying.

When handleApply calls closeDialog(), the closeDialog function calls reset(). This is fine since setting settingView.value = false twice is idempotent, but you could simplify by removing reset() from closeDialog if it's always paired with explicit reset scenarios.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/builder/useAppSetDefaultView.ts` around lines 49 - 56, The
closeDialog function currently calls reset(), causing reset() to be invoked
redundantly when handleApply calls closeDialog and then also calls reset() —
remove the reset() call from closeDialog so that closing the dialog only calls
dialogStore.closeDialog({ key: DIALOG_KEY }), and ensure callers that need to
clear state (e.g., handleApply and any other places relying on reset) call
reset() explicitly to preserve existing behavior for settingView.value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/platform/workflow/core/services/workflowService.ts`:
- Around line 157-191: saveWorkflow currently returns void so callers can't
detect a user-cancel from saveWorkflowAs; change saveWorkflow to return a
boolean (true on successful save, false on cancel) by capturing the result of
saveWorkflowAs(workflow) when workflow.isTemporary and returning false if that
call returns false, and return true after successful flows in the non-temporary
branch (after renameWorkflow/workflowStore.saveWorkflow completes); ensure all
internal branches that short-circuit (e.g., the confirmOverwrite path where you
call workflowStore.saveWorkflow and return) return an appropriate boolean, and
update any callers of saveWorkflow to await and handle the boolean result.

---

Nitpick comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 42-46: Add a short JSDoc comment above getWorkflowSuffix
explaining that the function returns 'app.json' only when the input exactly
equals 'app.json' and otherwise silently falls back to 'json' (covering
null/undefined and any other strings), and mention the intended return type
WorkflowSuffix so future maintainers understand the fallback behavior and
accepted inputs.

In `@src/components/builder/DefaultViewDialogContent.vue`:
- Around line 80-93: The viewTypeOptions array is built once using t() so
translations won't update on locale change; change viewTypeOptions from a static
constant to a computed property (e.g., computed(() => [ ... ]) ) that calls t()
for title/subtitle so it reacts to i18n locale updates; update any references to
viewTypeOptions in the template or setup to use the new computed ref, preserving
the same value/icon structure.

In `@src/components/builder/useAppSetDefaultView.ts`:
- Around line 49-56: The closeDialog function currently calls reset(), causing
reset() to be invoked redundantly when handleApply calls closeDialog and then
also calls reset() — remove the reset() call from closeDialog so that closing
the dialog only calls dialogStore.closeDialog({ key: DIALOG_KEY }), and ensure
callers that need to clear state (e.g., handleApply and any other places relying
on reset) call reset() explicitly to preserve existing behavior for
settingView.value.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31a4dce and 6f48fc0.

📒 Files selected for processing (18)
  • packages/shared-frontend-utils/src/formatUtil.test.ts
  • packages/shared-frontend-utils/src/formatUtil.ts
  • src/components/breadcrumb/SubgraphBreadcrumbItem.vue
  • src/components/builder/BuilderMenu.vue
  • src/components/builder/BuilderSaveSuccessDialogContent.vue
  • src/components/builder/BuilderToolbar.vue
  • src/components/builder/DefaultViewDialogContent.vue
  • src/components/builder/useAppSetDefaultView.test.ts
  • src/components/builder/useAppSetDefaultView.ts
  • src/components/builder/useBuilderSave.ts
  • src/components/sidebar/tabs/WorkflowsSidebarTab.vue
  • src/composables/useCoreCommands.ts
  • src/locales/en/main.json
  • src/platform/workflow/core/services/workflowService.test.ts
  • src/platform/workflow/core/services/workflowService.ts
  • src/platform/workflow/management/stores/comfyWorkflow.ts
  • src/stores/appModeStore.test.ts
  • src/stores/appModeStore.ts
💤 Files with no reviewable changes (2)
  • src/components/builder/BuilderSaveSuccessDialogContent.vue
  • src/components/builder/useBuilderSave.ts

@pythongosssss pythongosssss marked this pull request as ready for review March 2, 2026 21:55
@pythongosssss pythongosssss requested review from a team as code owners March 2, 2026 21:55
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Mar 2, 2026
Copy link
Collaborator

@AustinMroz AustinMroz left a comment

Choose a reason for hiding this comment

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

This one should really have a lot more deletions. Shouldn't syncLinearMode be removed entirely?

}

export function appendWorkflowJsonExt(path: string, isApp: boolean): string {
return ensureWorkflowSuffix(path, isApp ? 'app.json' : 'json')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not blocking, but the disparity of when, and how infrequently the EXT constants are used isn't great.

Copy link
Member Author

Choose a reason for hiding this comment

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

added consts for JSON and APP_JSON for the non dot-prefixed ones

}
}
const dotIndex = fullFilename.lastIndexOf('.')
if (dotIndex > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can invert the conditional to further polish to

Suggested change
if (dotIndex > 0) {
if (dotIndex <= 0) return { filename: fullFilename, suffix: null }

but I'm happy enough with the current performance improvements.

initialOpenAsApp?: boolean
}>()

const emit = defineEmits<{
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const emit = defineEmits<{
defineEmits<{

Also very nit, you can do an empty defineEmits and use $emit in the template

const dialogService = useDialogService()
const dialogStore = useDialogStore()

const settingView = ref(false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems kinda smelly to be reimplementing the visibility guards here when dialogStore/dialogService already check for duplicate dialogues through the group key and provides a isDialogOpen

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 137-141: The .app.json special-case returns an empty filename when
there is no basename; update the branch that checks lower.endsWith(APP_JSON_EXT)
to only treat it as a suffix if there is a real basename (e.g., ensure dotIndex
> 0 or fullFilename has characters before the dot) — otherwise fall through to
the existing dotfile handling. Modify the logic around APP_JSON_EXT/APP_JSON and
fullFilename so you compute dotIndex (or reuse the existing dotIndex variable)
and require dotIndex > 0 before returning { filename: fullFilename.slice(0,
-APP_JSON_EXT.length), suffix: APP_JSON }.

In `@src/platform/workflow/core/services/workflowService.ts`:
- Around line 130-133: The current flow deletes existingWorkflow via
deleteWorkflow when isSelfOverwrite is false before the replacement is durably
saved; change to a save-first-then-delete or transactional replace: write the
new workflow to a temporary/alternate key or use a persistence-level
atomic/overwrite API (e.g. persistWorkflow/saveWorkflow/replaceWorkflow) and
only call deleteWorkflow(existingWorkflow, true) after the new content has been
successfully persisted and committed; ensure any write or commit errors abort
the delete, propagate errors up (do not swallow), and add proper
try/catch/finally around the save+delete sequence so failures leave the original
workflow intact.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f48fc0 and 6415759.

📒 Files selected for processing (8)
  • packages/shared-frontend-utils/src/formatUtil.ts
  • src/components/builder/BuilderToolbar.vue
  • src/components/builder/DefaultViewDialogContent.vue
  • src/components/builder/useAppSetDefaultView.test.ts
  • src/components/builder/useAppSetDefaultView.ts
  • src/platform/workflow/core/services/workflowService.test.ts
  • src/platform/workflow/core/services/workflowService.ts
  • src/platform/workflow/management/stores/comfyWorkflow.ts
💤 Files with no reviewable changes (1)
  • src/platform/workflow/management/stores/comfyWorkflow.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/builder/useAppSetDefaultView.test.ts
  • src/components/builder/useAppSetDefaultView.ts

Comment on lines 130 to 133
if (!isSelfOverwrite) {
const deleted = await deleteWorkflow(existingWorkflow, true)
if (!deleted) return false
}
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Delete-before-save ordering risks destructive overwrite failures.

Line 131 and Line 173 remove the existing target workflow before the
replacement is durably saved. If a later rename/open/save step fails, the old
workflow is already gone.

Please switch this flow to an atomic/transactional replacement strategy (or an
explicit overwrite API in persistence) so deletion only occurs after successful
write of the new content.

As per coding guidelines: "Implement proper error handling" and "Implement proper error propagation".

Also applies to: 168-174

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/workflow/core/services/workflowService.ts` around lines 130 -
133, The current flow deletes existingWorkflow via deleteWorkflow when
isSelfOverwrite is false before the replacement is durably saved; change to a
save-first-then-delete or transactional replace: write the new workflow to a
temporary/alternate key or use a persistence-level atomic/overwrite API (e.g.
persistWorkflow/saveWorkflow/replaceWorkflow) and only call
deleteWorkflow(existingWorkflow, true) after the new content has been
successfully persisted and committed; ensure any write or commit errors abort
the delete, propagate errors up (do not swallow), and add proper
try/catch/finally around the save+delete sequence so failures leave the original
workflow intact.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a pre-existing pattern for workflow overwriting, however you are correct that we should clean this up, but not in this PR. Can you add a new issue for resolving this, probably by allowing for the overwrite flag to be passed through when saving the workflow

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@pythongosssss, understood! I'll create a follow-up issue for implementing atomic workflow overwrite.


🏁 Script executed:

#!/bin/bash
# Create a GitHub issue for atomic workflow overwrite

gh issue create \
  --title "Implement atomic workflow overwrite via persistence-level flag" \
  --body "## Problem

The current workflow save/overwrite flow deletes the existing workflow before the replacement is durably saved (e.g., in \`saveWorkflowAs\` and \`saveWorkflow\`). If a subsequent rename, open, or save step fails, the original workflow is already gone, risking data loss.

Affected locations:
- \`src/platform/workflow/core/services/workflowService.ts\` Line 131 (in \`saveWorkflowAs\`)
- \`src/platform/workflow/core/services/workflowService.ts\` Line 173 (in \`saveWorkflow\`)

## Proposed Solution

Add an \`overwrite\` flag (or similar) to the workflow persistence layer (\`saveWorkflow\` / \`persistWorkflow\` API) that allows atomic replacement:
- Write the new workflow content first
- Only delete/replace the old workflow after successful commit
- Ensure any write failures abort the delete and propagate errors up

This would replace the current pattern of explicit \`deleteWorkflow\` calls before saving.

## References

- PR: https://github.com/Comfy-Org/ComfyUI_frontend/pull/9338
- Comment: https://github.com/Comfy-Org/ComfyUI_frontend/pull/9338#discussion_r2878008918
- Requested by: `@pythongosssss`" \
  --assignee pythongosssss

Length of output: 57


@pythongosssss, done! I've created issue #9355 to track implementing atomic workflow overwrite via a persistence-level flag.


🧠 Learnings used
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 9345
File: src/platform/workflow/management/stores/workflowStore.ts:488-491
Timestamp: 2026-03-03T01:32:49.646Z
Learning: In `src/platform/workflow/management/stores/workflowStore.ts`, `openWorkflowPaths` maintains unique workflow paths by design. Operations like `openWorkflowsInBackground` use `_.union` to enforce uniqueness. When updating paths (e.g., during rename), replacing the first occurrence with `indexOf`/`splice` is sufficient because duplicates should not exist.

Learnt from: Myestery
Repo: Comfy-Org/ComfyUI_frontend PR: 7422
File: .github/workflows/pr-update-playwright-expectations.yaml:131-135
Timestamp: 2025-12-12T23:02:37.473Z
Learning: In the `.github/workflows/pr-update-playwright-expectations.yaml` workflow in the Comfy-Org/ComfyUI_frontend repository, the snapshot update process is intentionally scoped to only add and update snapshot images. Deletions of snapshot files are handled explicitly outside this workflow and should not be suggested as part of this automation.

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-23T21:34:45.361Z
Learning: Development workflow: make code changes, run relevant tests, run `pnpm typecheck`, `pnpm lint`, `pnpm format`, check if README updates are needed, suggest docs.comfy.org updates for user-facing changes

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8248
File: src/platform/assets/schemas/assetSchema.ts:109-118
Timestamp: 2026-01-22T23:44:59.888Z
Learning: Do not export declarations (e.g., schemas, constants, types) unless they are actually used elsewhere in the codebase. Keep the public API surface minimal by exporting only items with concrete usage. This reduces coupling and keeps imports explicit. In src/platform/assets/schemas/assetSchema.ts, ensure only necessary exports are exposed; remove unused exports or switch to non-exported/internal where appropriate.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8722
File: src/composables/maskeditor/useBrushDrawing.ts:5-5
Timestamp: 2026-02-08T03:19:12.009Z
Learning: Treat unresolved import warnings caused by pnpm workspace catalog mappings (pnpm-workspace.yaml) and unplugin-typegpu in vite.config.mts as false positives. Do not flag these in PR reviews; they are expected in sandboxes without node_modules and are not indicative of actual missing dependencies in the TS codebase.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/load3d.ts:427-430
Timestamp: 2026-02-19T02:06:23.468Z
Learning: In TypeScript, you can use typeof SomeClass in type annotation positions (e.g., param: typeof LGraphNode) even when SomeClass is imported with import type. This is a type query that only exists at compile time and is erased in runtime, so it is safe to combine with type-only imports. Apply this pattern to TS files broadly when you need a type that references the shape of a class or constructor function without importing the value at runtime.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/uploadAudio.ts:91-94
Timestamp: 2026-02-19T02:06:38.395Z
Learning: In TypeScript files, you can use a type annotation like 'nodeType: typeof MyClass' even if MyClass is imported via 'import type'. Both the type-only import and 'typeof' operate at the type level and are erased at compile time. This pattern is commonly used for constructor types (e.g., 'nodeType: typeof LGraphNode'). Apply this pattern across TypeScript files in the repository (src/**/*.ts) as appropriate, ensuring the imported symbol is a type-only import when possible for clarity and to avoid runtime imports.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8992
File: src/lib/litegraph/src/widgets/GradientSliderWidget.ts:18-18
Timestamp: 2026-02-20T21:08:19.814Z
Learning: When drawing with CanvasRenderingContext2D in TypeScript/JavaScript, wrap the drawing logic with ctx.save() at the start and ctx.restore() at the end to preserve and restore the canvas state. Do not manually destructure and restore individual properties (e.g., fillStyle, strokeStyle); rely on save/restore to manage state changes in a scoped manner. This should be applied to all TS files that perform canvas drawing.

Learnt from: dante01yoon
Repo: Comfy-Org/ComfyUI_frontend PR: 9075
File: src/scripts/api.featureFlags.test.ts:237-268
Timestamp: 2026-02-22T04:27:33.379Z
Learning: In Vite/Vitest, import.meta.env.DEV is true for any mode that is not 'production' (i.e., DEV is the opposite of PROD, and can be true in 'test', 'development', etc.). Do not assume DEV implies only 'development' mode. When reviewing code and tests, treat DEV as a non-production flag and verify environment-specific logic accordingly. Reference: https://vite.dev/guide/env-and-mode#modes

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/shared-frontend-utils/src/formatUtil.ts (1)

137-141: ⚠️ Potential issue | 🟡 Minor

Guard .app.json special-case when basename is empty

On Line 137, .app.json is classified as { filename: '', suffix: 'app.json' } instead of falling through to dotfile handling. That can leak an empty filename into rename/save paths.

Suggested fix
 export function getFilenameDetails(fullFilename: string) {
   const lower = fullFilename.toLowerCase()
   if (lower.endsWith(APP_JSON_EXT)) {
+    const filename = fullFilename.slice(0, -APP_JSON_EXT.length)
+    if (filename.length > 0) {
       return {
-      filename: fullFilename.slice(0, -APP_JSON_EXT.length),
-      suffix: APP_JSON
+        filename,
+        suffix: APP_JSON
+      }
     }
   }
   const dotIndex = fullFilename.lastIndexOf('.')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared-frontend-utils/src/formatUtil.ts` around lines 137 - 141, The
.app.json branch incorrectly returns { filename: '', suffix: APP_JSON } for
inputs like ".app.json"; update the guard in the block that checks
lower.endsWith(APP_JSON_EXT) (using symbols APP_JSON_EXT and APP_JSON and the
existing fullFilename slice) to compute const base = fullFilename.slice(0,
-APP_JSON_EXT.length) and only return the special-case when base !== ''
(otherwise fall through to the dotfile handling); ensure the code uses base for
the filename in the returned object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 137-141: The .app.json branch incorrectly returns { filename: '',
suffix: APP_JSON } for inputs like ".app.json"; update the guard in the block
that checks lower.endsWith(APP_JSON_EXT) (using symbols APP_JSON_EXT and
APP_JSON and the existing fullFilename slice) to compute const base =
fullFilename.slice(0, -APP_JSON_EXT.length) and only return the special-case
when base !== '' (otherwise fall through to the dotfile handling); ensure the
code uses base for the filename in the returned object.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6415759 and abeb0fc.

📒 Files selected for processing (1)
  • packages/shared-frontend-utils/src/formatUtil.ts

fix .app.json file giving empty filename
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/shared-frontend-utils/src/formatUtil.ts (1)

44-48: Make getWorkflowSuffix comparison case-insensitive for defensive inputs.

If a caller passes 'APP.JSON', this currently falls back to 'json'.
A lowercase-normalized compare makes this utility safer as a public API.

Suggested patch
 export function getWorkflowSuffix(
   suffix: string | null | undefined
 ): WorkflowSuffix {
-  return suffix === APP_JSON_SUFFIX ? APP_JSON_SUFFIX : JSON_SUFFIX
+  return suffix?.toLowerCase() === APP_JSON_SUFFIX
+    ? APP_JSON_SUFFIX
+    : JSON_SUFFIX
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared-frontend-utils/src/formatUtil.ts` around lines 44 - 48,
getWorkflowSuffix currently compares suffix to APP_JSON_SUFFIX case-sensitively;
normalize suffix (e.g., const normalized = suffix?.toLowerCase()) and compare
normalized to APP_JSON_SUFFIX.toLowerCase() so inputs like 'APP.JSON' match;
keep null/undefined handling and still return APP_JSON_SUFFIX or JSON_SUFFIX as
before, referencing getWorkflowSuffix, APP_JSON_SUFFIX, and JSON_SUFFIX when you
update the comparison.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 44-48: getWorkflowSuffix currently compares suffix to
APP_JSON_SUFFIX case-sensitively; normalize suffix (e.g., const normalized =
suffix?.toLowerCase()) and compare normalized to APP_JSON_SUFFIX.toLowerCase()
so inputs like 'APP.JSON' match; keep null/undefined handling and still return
APP_JSON_SUFFIX or JSON_SUFFIX as before, referencing getWorkflowSuffix,
APP_JSON_SUFFIX, and JSON_SUFFIX when you update the comparison.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abeb0fc and 5368de1.

📒 Files selected for processing (3)
  • packages/shared-frontend-utils/src/formatUtil.test.ts
  • packages/shared-frontend-utils/src/formatUtil.ts
  • src/components/builder/useAppSetDefaultView.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/builder/useAppSetDefaultView.ts
  • packages/shared-frontend-utils/src/formatUtil.test.ts

@pythongosssss pythongosssss enabled auto-merge (squash) March 3, 2026 15:58
@pythongosssss pythongosssss merged commit 68b16e3 into main Mar 3, 2026
33 checks passed
@pythongosssss pythongosssss deleted the pysssss/app-mode-data-saving-refactor branch March 3, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants