Skip to content

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Oct 14, 2025

Summary

  • Keep all drafts in localStorage, mirroring the logic from VSCode.

  • Fix a bug where newly created blank workflow tabs would incorrectly restore as defaultGraph instead of blankGraph after page refresh.

Resolves Comfy-Org/desktop#910, Resolves #4057, Fixes #3665

Changes

What

  • Fix restoreWorkflowTabsState to parse and pass workflow data from drafts when recreating temporary workflows
  • Add error handling for invalid draft data with fallback to default workflow
  • Fix E2E test should not serialize color adjustments in workflow to wait for workflow persistence before assertions
  • Add proper validation for workflow nodes array in test assertions

Breaking

  • None

Dependencies

  • No new dependencies added

Review Focus

  1. Workflow restoration: Verify that blank workflows correctly restore as blankGraph after page refresh
  2. Error handling: Check that invalid draft data gracefully falls back to default workflow
  3. Test coverage: Ensure E2E test correctly waits for workflow persistence before checking node properties
  4. Edge cases: Test with multiple tabs, switching between tabs, and rapid refresh scenarios

@christian-byrne
Copy link
Contributor Author

christian-byrne commented Oct 14, 2025

Just a little worried about giving the client more ownership over the workflow state - given future plans for possible CRDT (in particular, syncing when going offline->online) and some other features, but in general it should be fine.

@christian-byrne christian-byrne added area:workflows claude-review Add to trigger a PR code review from Claude Code labels Oct 19, 2025
@jtydhr88 jtydhr88 self-assigned this Nov 11, 2025
@jtydhr88 jtydhr88 added claude-review Add to trigger a PR code review from Claude Code and removed claude-review Add to trigger a PR code review from Claude Code labels Nov 11, 2025
@Yourz Yourz force-pushed the workflow-persistence branch from 5bbaf17 to eaf1b2c Compare January 14, 2026 15:41
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

📝 Walkthrough

Walkthrough

Introduces draft-based workflow persistence: an LRU draft cache persisted to localStorage, a Pinia draft store with save/load/move/remove semantics and quota handling, and integration into workflow persistence, store, and service layers to save and restore open/temporary workflows.

Changes

Cohort / File(s) Summary
Draft Cache Infrastructure
src/platform/workflow/persistence/base/draftCache.ts, src/platform/workflow/persistence/base/draftCache.test.ts
New immutable-style LRU draft cache (MAX_DRAFTS=32) with upsert/remove/move/touch utilities and tests for eviction, ordering, and path retrieval.
Draft Store (Pinia)
src/platform/workflow/persistence/stores/workflowDraftStore.ts
New persistent draft store backed by localStorage with API: saveDraft, removeDraft, moveDraft, markDraftUsed, getDraft, loadPersistedWorkflow, reset. Handles quota errors by evicting oldest drafts and retrying; supports fallback loading strategies.
Workflow Store Integration
src/platform/workflow/management/stores/workflowStore.ts, src/platform/workflow/management/stores/workflowStore.test.ts
Integrates draft retrieval/pruning into load/save/rename/delete/close flows: apply valid drafts, discard stale/invalid drafts, mark modified state, move/remove drafts on rename/delete/close. Tests added for draft precedence, staleness, and cleanup.
Persistence Composable
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts, src/platform/workflow/persistence/composables/useWorkflowPersistence.test.ts
Persist now writes drafts before storage and restores via draftStore.loadPersistedWorkflow; per-path dedupe, debouncing, enhanced error handling, and filtering by Comfy.Workflow.basePath. Tests cover draft saves, LRU eviction, restoration of temporary tabs, and save-failure handling.
Service Layer
src/platform/workflow/core/services/workflowService.ts
Saves active workflow to draft in beforeLoadNewGraph when persistence enabled; shows toast on draft-save failure.
Tests & L10n
browser_tests/tests/colorPalette.spec.ts, src/locales/en/main.json
Browser test updated to asynchronously and defensively parse workflow drafts from localStorage; added translation toastMessages.failedToSaveDraft.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant DraftStore
    participant LocalStorage
    participant WorkflowStore

    User->>App: Trigger save (manual/auto)
    App->>DraftStore: saveDraft(path, snapshot)
    activate DraftStore
    DraftStore->>LocalStorage: write draft (Comfy.Workflow.Drafts)
    alt QuotaExceeded
        DraftStore->>DraftStore: evict oldest draft (LRU)
        DraftStore->>LocalStorage: retry write
    end
    DraftStore-->>App: confirm draft saved
    deactivate DraftStore

    User->>App: Restart / open app
    App->>DraftStore: loadPersistedWorkflow(preferredPath, fallbackToLatestDraft)
    activate DraftStore
    DraftStore->>LocalStorage: read drafts & order
    DraftStore->>DraftStore: parse/validate draft, mark used
    alt Draft available and valid
        DraftStore->>WorkflowStore: provide draft content
        WorkflowStore-->>App: open workflow tab (marked modified if draft applied)
    else No valid draft
        DraftStore-->>App: fallback (session/localStorage or none)
    end
    deactivate DraftStore
    App->>User: restore open workflows/tabs
Loading

Possibly related PRs

Suggested reviewers

  • shinshin86
  • KarryCharon
  • Yorha4D
  • christian-byrne
  • comfyui-wiki
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The pull request implements workflow draft persistence and restoration across tabs, addressing issue #910's requirement to save and restore all open workflow tabs on restart/refresh.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: draft persistence infrastructure, workflow state management, E2E test updates for persistence timing, and translation strings for error messages.

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

✨ Finishing touches
  • 📝 Generate docstrings

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 Jan 14, 2026

🎭 Playwright Tests: ⚠️ Passed with flaky tests

Results: 505 passed, 0 failed, 1 flaky, 8 skipped (Total: 514)

❌ Failed Tests

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

@github-actions
Copy link

github-actions bot commented Jan 14, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/26/2026, 06:08:14 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 14, 2026

Bundle Size Report

Summary

  • Raw size: 22 MB baseline 22 MB — 🔴 +8.24 kB
  • Gzip: 4.57 MB baseline 4.57 MB — 🔴 +1.96 kB
  • Brotli: 3.39 MB baseline 3.39 MB — 🔴 +1.69 kB
  • Bundles: 170 current • 170 baseline • 82 added / 82 removed

Category Glance
Data & Services 🔴 +6.8 kB (3.19 MB) · Graph Workspace 🔴 +1.65 kB (958 kB) · Other 🟢 -192 B (6.49 MB) · Panels & Settings 🟢 -8 B (466 kB) · Vendor & Third-Party ⚪ 0 B (10.7 MB) · Views & Navigation ⚪ 0 B (80.7 kB) · + 5 more

Per-category breakdown
App Entry Points — 22.8 kB (baseline 22.8 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-AJY1qwdD.js (removed) 22.8 kB 🟢 -22.8 kB 🟢 -6.85 kB 🟢 -6.04 kB
assets/index-DXdQGXWf.js (new) 22.8 kB 🔴 +22.8 kB 🔴 +6.84 kB 🔴 +6.02 kB

Status: 1 added / 1 removed

Graph Workspace — 958 kB (baseline 957 kB) • 🔴 +1.65 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-Cr6Qnv3E.js (new) 958 kB 🔴 +958 kB 🔴 +194 kB 🔴 +147 kB
assets/GraphView-D78QTAjW.js (removed) 957 kB 🟢 -957 kB 🟢 -194 kB 🟢 -146 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-D3bRGB9Q.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -3.6 kB 🟢 -3.06 kB
assets/CloudSurveyView-D6BsxqCM.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +3.6 kB 🔴 +3.05 kB
assets/CloudLoginView-Br2K9FQM.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.09 kB 🟢 -2.72 kB
assets/CloudLoginView-C_u-trQ7.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.09 kB 🔴 +2.72 kB
assets/UserCheckView-CLUKdwGi.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.45 kB 🟢 -2.13 kB
assets/UserCheckView-DgMUFOJ1.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.45 kB 🔴 +2.13 kB
assets/CloudLayoutView-CPwn6mfy.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.24 kB 🔴 +1.96 kB
assets/CloudLayoutView-DhWE1cUb.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.25 kB 🟢 -1.95 kB
assets/CloudSignupView-6JW2q6ME.js (removed) 8.18 kB 🟢 -8.18 kB 🟢 -2.33 kB 🟢 -2.02 kB
assets/CloudSignupView-MAz3sPT7.js (new) 8.18 kB 🔴 +8.18 kB 🔴 +2.33 kB 🔴 +2.03 kB
assets/CloudForgotPasswordView-Dka5vg5q.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -1.93 kB 🟢 -1.69 kB
assets/CloudForgotPasswordView-DWTvVz57.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +1.93 kB 🔴 +1.68 kB
assets/UserSelectView-B9RWVL87.js (removed) 5.28 kB 🟢 -5.28 kB 🟢 -1.76 kB 🟢 -1.57 kB
assets/UserSelectView-DSl81_Ao.js (new) 5.28 kB 🔴 +5.28 kB 🔴 +1.76 kB 🔴 +1.57 kB
assets/CloudSubscriptionRedirectView-3fIR3hdT.js (removed) 5.27 kB 🟢 -5.27 kB 🟢 -1.73 kB 🟢 -1.54 kB
assets/CloudSubscriptionRedirectView-C3UtIgCh.js (new) 5.27 kB 🔴 +5.27 kB 🔴 +1.73 kB 🔴 +1.54 kB
assets/CloudAuthTimeoutView-D6Zx7DlE.js (new) 5.24 kB 🔴 +5.24 kB 🔴 +1.7 kB 🔴 +1.49 kB
assets/CloudAuthTimeoutView-DWnbjQKS.js (removed) 5.24 kB 🟢 -5.24 kB 🟢 -1.71 kB 🟢 -1.48 kB
assets/CloudSorryContactSupportView-B5gYo5Lv.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-MvRmLoAK.js 500 B 500 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 removed

Panels & Settings — 466 kB (baseline 466 kB) • 🟢 -8 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WorkspacePanel-Dw_rhfAX.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -5.9 kB 🟢 -5.14 kB
assets/WorkspacePanel-DWzsg2sU.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +5.89 kB 🔴 +5.14 kB
assets/LegacyCreditsPanel-BHDXHrQH.js (new) 23.8 kB 🔴 +23.8 kB 🔴 +5.95 kB 🔴 +5.22 kB
assets/LegacyCreditsPanel-C0J4KxtR.js (removed) 23.8 kB 🟢 -23.8 kB 🟢 -5.95 kB 🟢 -5.22 kB
assets/SubscriptionPanel-Cz6tAwBq.js (removed) 20.9 kB 🟢 -20.9 kB 🟢 -5.01 kB 🟢 -4.42 kB
assets/SubscriptionPanel-D2_vB5DW.js (new) 20.9 kB 🔴 +20.9 kB 🔴 +5.02 kB 🔴 +4.44 kB
assets/KeybindingPanel-lIW9ITvQ.js (removed) 14.2 kB 🟢 -14.2 kB 🟢 -3.74 kB 🟢 -3.31 kB
assets/KeybindingPanel-W3i_nl5i.js (new) 14.2 kB 🔴 +14.2 kB 🔴 +3.74 kB 🔴 +3.31 kB
assets/AboutPanel-CMFi6LsF.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.68 kB 🔴 +2.43 kB
assets/AboutPanel-dSrYZIxe.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.68 kB 🟢 -2.43 kB
assets/ExtensionPanel-CDkmk7Gg.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.71 kB 🟢 -2.4 kB
assets/ExtensionPanel-CHIU9x9Z.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.71 kB 🔴 +2.4 kB
assets/ServerConfigPanel-BEVtwJDh.js (new) 7.23 kB 🔴 +7.23 kB 🔴 +2.17 kB 🔴 +1.94 kB
assets/ServerConfigPanel-CK-OUb-_.js (removed) 7.23 kB 🟢 -7.23 kB 🟢 -2.17 kB 🟢 -1.94 kB
assets/UserPanel-BNbGrdEo.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/UserPanel-C1Q06hJF.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +1.91 kB 🔴 +1.67 kB
assets/config-DNV554LM.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -610 B 🟢 -531 B
assets/config-DY08RQ2n.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +605 B 🔴 +535 B
assets/refreshRemoteConfig-6qKExbUE.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +524 B 🔴 +454 B
assets/refreshRemoteConfig-CYOpPXzC.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -524 B 🟢 -488 B
assets/cloudRemoteConfig-C-jzjva6.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +508 B 🔴 +437 B
assets/cloudRemoteConfig-CYYfW8_u.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -509 B 🟢 -449 B
assets/refreshRemoteConfig-BnDWPRqL.js (removed) 169 B 🟢 -169 B 🟢 -108 B 🟢 -106 B
assets/refreshRemoteConfig-DIKhaICY.js (new) 169 B 🔴 +169 B 🔴 +108 B 🔴 +102 B
assets/remoteConfig-w1E3DdAQ.js 536 B 536 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-2qMok0ac.js 29 kB 29 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BcDLsbiI.js 29.2 kB 29.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BCMsrMhl.js 29.9 kB 29.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BQM5iiEY.js 25.5 kB 25.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C5JeSK4j.js 26.2 kB 26.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CcVqrXbK.js 38.8 kB 38.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Ckoj43MZ.js 30 kB 30 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DFBj0AYW.js 32.5 kB 32.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Dh5pVx6O.js 34.8 kB 34.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DJRV_PVY.js 30.8 kB 30.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-jbT3QFC8.js 31.6 kB 31.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 12 added / 12 removed

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-0AeowxZZ.js (new) 3.54 kB 🔴 +3.54 kB 🔴 +1.24 kB 🔴 +1.06 kB
assets/auth-lwT_m5uS.js (removed) 3.54 kB 🟢 -3.54 kB 🟢 -1.24 kB 🟢 -1.06 kB
assets/firebaseAuthStore-DNFgtJqM.js (new) 217 B 🔴 +217 B 🔴 +136 B 🔴 +116 B
assets/firebaseAuthStore-q8kzsJXb.js (removed) 217 B 🟢 -217 B 🟢 -138 B 🟢 -116 B
assets/auth-DElZLMvn.js (removed) 178 B 🟢 -178 B 🟢 -141 B 🟢 -129 B
assets/auth-DTBY4pLX.js (new) 178 B 🔴 +178 B 🔴 +142 B 🔴 +131 B

Status: 3 added / 3 removed

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-CRBz2MpT.js (removed) 2.65 kB 🟢 -2.65 kB 🟢 -1.25 kB 🟢 -1.12 kB
assets/useSubscriptionDialog-DT0L0E56.js (new) 2.65 kB 🔴 +2.65 kB 🔴 +1.25 kB 🔴 +1.11 kB
assets/useSubscriptionDialog-C9ty3oS4.js (removed) 179 B 🟢 -179 B 🟢 -110 B 🟢 -95 B
assets/useSubscriptionDialog-DRPv8G-X.js (new) 179 B 🔴 +179 B 🔴 +110 B 🔴 +98 B

Status: 2 added / 2 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-BEPgtsZU.js (new) 9.52 kB 🔴 +9.52 kB 🔴 +2.69 kB 🔴 +2.42 kB
assets/ComfyQueueButton-BiMxupud.js (removed) 9.52 kB 🟢 -9.52 kB 🟢 -2.69 kB 🟢 -2.41 kB
assets/SubscribeButton-CPDfVLOz.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.57 kB 🔴 +1.39 kB
assets/SubscribeButton-Mlom76NS.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.57 kB 🟢 -1.39 kB
assets/CloudBadge-B79P5Vhm.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +724 B 🔴 +645 B
assets/CloudBadge-gqk8F8J0.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -725 B 🟢 -650 B
assets/cloudFeedbackTopbarButton-BSh2Ksk7.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +678 B 🔴 +576 B
assets/cloudFeedbackTopbarButton-BYIk9s8D.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -678 B 🟢 -577 B
assets/ComfyQueueButton-B80orF-M.js (removed) 181 B 🟢 -181 B 🟢 -118 B 🟢 -106 B
assets/ComfyQueueButton-Dv9GpFMz.js (new) 181 B 🔴 +181 B 🔴 +118 B 🔴 +113 B
assets/Button-B9mYP1x0.js 3.82 kB 3.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-DHZYSmi1.js 8.36 kB 8.36 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-CIuPULbC.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-BFtcBv-z.js 2.41 kB 2.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Data & Services — 3.19 MB (baseline 3.18 MB) • 🔴 +6.8 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-C-B8WVmu.js (new) 2 MB 🔴 +2 MB 🔴 +423 kB 🔴 +323 kB
assets/dialogService-Hco2y5H6.js (removed) 2 MB 🟢 -2 MB 🟢 -422 kB 🟢 -322 kB
assets/api-DWPCchTG.js (new) 1.16 MB 🔴 +1.16 MB 🔴 +243 kB 🔴 +188 kB
assets/api-xjId7H1A.js (removed) 1.16 MB 🟢 -1.16 MB 🟢 -243 kB 🟢 -188 kB
assets/releaseStore-DPZnzcGX.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.4 kB 🔴 +2.12 kB
assets/releaseStore-LYH25ltb.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.4 kB 🟢 -2.12 kB
assets/keybindingService-BEvpTY2O.js (removed) 6.78 kB 🟢 -6.78 kB 🟢 -1.74 kB 🟢 -1.52 kB
assets/keybindingService-swyG56MJ.js (new) 6.78 kB 🔴 +6.78 kB 🔴 +1.74 kB 🔴 +1.52 kB
assets/userStore-DK6BTBdb.js (removed) 2.16 kB 🟢 -2.16 kB 🟢 -814 B 🟢 -723 B
assets/userStore-DLLlAsoM.js (new) 2.16 kB 🔴 +2.16 kB 🔴 +813 B 🔴 +724 B
assets/audioService-BsbcpnDC.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -931 B 🟢 -821 B
assets/audioService-DuP4Lz1n.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +931 B 🔴 +821 B
assets/teamWorkspaceStore-C9wHalqE.js (removed) 165 B 🟢 -165 B 🟢 -123 B 🟢 -108 B
assets/teamWorkspaceStore-CCF8GNah.js (new) 165 B 🔴 +165 B 🔴 +123 B 🔴 +108 B
assets/releaseStore-BJAbp1Bj.js (removed) 140 B 🟢 -140 B 🟢 -106 B 🟢 -109 B
assets/releaseStore-CvEG16tA.js (new) 140 B 🔴 +140 B 🔴 +106 B 🔴 +104 B
assets/serverConfigStore-B2LzN8g1.js 2.64 kB 2.64 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useErrorHandling-BqlwCpbB.js (new) 5.08 kB 🔴 +5.08 kB 🔴 +1.5 kB 🔴 +1.31 kB
assets/useErrorHandling-N3zSSUgK.js (removed) 5.08 kB 🟢 -5.08 kB 🟢 -1.5 kB 🟢 -1.31 kB
assets/useWorkspaceUI-DPYE8BtH.js (new) 3.42 kB 🔴 +3.42 kB 🔴 +975 B 🔴 +835 B
assets/useWorkspaceUI-DR3wUL8u.js (removed) 3.42 kB 🟢 -3.42 kB 🟢 -975 B 🟢 -837 B
assets/useSubscriptionActions-BE6PF7B4.js (new) 2.22 kB 🔴 +2.22 kB 🔴 +872 B 🔴 +762 B
assets/useSubscriptionActions-BM5QfUyZ.js (removed) 2.22 kB 🟢 -2.22 kB 🟢 -873 B 🟢 -761 B
assets/subscriptionCheckoutUtil-B9F4YifI.js (removed) 2 kB 🟢 -2 kB 🟢 -864 B 🟢 -749 B
assets/subscriptionCheckoutUtil-CX7x_4Rh.js (new) 2 kB 🔴 +2 kB 🔴 +864 B 🔴 +757 B
assets/useSubscriptionCredits-D8PVcIWH.js (new) 1.39 kB 🔴 +1.39 kB 🔴 +598 B 🔴 +527 B
assets/useSubscriptionCredits-X4C_WPD8.js (removed) 1.39 kB 🟢 -1.39 kB 🟢 -598 B 🟢 -525 B
assets/audioUtils-BfjQPu0p.js (removed) 970 B 🟢 -970 B 🟢 -547 B 🟢 -460 B
assets/audioUtils-JEJAKUBc.js (new) 970 B 🔴 +970 B 🔴 +547 B 🔴 +487 B
assets/useCurrentUser-BlbsVvYX.js (removed) 145 B 🟢 -145 B 🟢 -114 B 🟢 -104 B
assets/useCurrentUser-YW9nC0Cd.js (new) 145 B 🔴 +145 B 🔴 +114 B 🔴 +102 B
assets/_plugin-vue_export-helper-DLRTaeJK.js 467 B 467 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-8brfHtOx.js 7.2 kB 7.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-DBMaRy6q.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/tailwindUtil-Hy0jY5OA.js 488 B 488 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-DdBDBwvF.js 408 kB 408 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-Dk4vQph5.js 4.1 MB 4.1 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-BK91gQps.js 3.04 MB 3.04 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-Bh_PdEOO.js 256 kB 256 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-BKpliY5_.js 1.83 MB 1.83 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BFRBT3RT.js 650 kB 650 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-DMjbEcx7.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-PDw3y6Aq.js 398 kB 398 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 6.49 MB (baseline 6.49 MB) • 🟢 -192 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-DeRaKxpc.js (removed) 178 kB 🟢 -178 kB 🟢 -43 kB 🟢 -36 kB
assets/core-BWVmFxu0.js (new) 178 kB 🔴 +178 kB 🔴 +43 kB 🔴 +35.9 kB
assets/WidgetSelect-BS_ddL4L.js (removed) 50.7 kB 🟢 -50.7 kB 🟢 -11.2 kB 🟢 -9.76 kB
assets/WidgetSelect-D5gQwM6e.js (new) 50.7 kB 🔴 +50.7 kB 🔴 +11.2 kB 🔴 +9.78 kB
assets/Load3DControls-C-9n4WmO.js (removed) 35.9 kB 🟢 -35.9 kB 🟢 -5.87 kB 🟢 -5.09 kB
assets/Load3DControls-CUVc6K6m.js (new) 35.9 kB 🔴 +35.9 kB 🔴 +5.87 kB 🔴 +5.09 kB
assets/SubscriptionRequiredDialogContent-D3CQbtzL.js (new) 28.7 kB 🔴 +28.7 kB 🔴 +6.78 kB 🔴 +5.91 kB
assets/SubscriptionRequiredDialogContent-Dk0pQwY_.js (removed) 28.7 kB 🟢 -28.7 kB 🟢 -6.78 kB 🟢 -5.9 kB
assets/CurrentUserPopoverWorkspace-Ct7JzDmp.js (removed) 22.1 kB 🟢 -22.1 kB 🟢 -4.95 kB 🟢 -4.39 kB
assets/CurrentUserPopoverWorkspace-DAyNAwRw.js (new) 22.1 kB 🔴 +22.1 kB 🔴 +4.94 kB 🔴 +4.38 kB
assets/Load3D-BYxkuMQU.js (new) 20.9 kB 🔴 +20.9 kB 🔴 +4.59 kB 🔴 +4.03 kB
assets/Load3D-DtCyJHIM.js (removed) 20.9 kB 🟢 -20.9 kB 🟢 -4.59 kB 🟢 -4.02 kB
assets/WidgetInputNumber-_dETxv6U.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.51 kB 🟢 -4.01 kB
assets/WidgetInputNumber-BWDqFAeU.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.51 kB 🔴 +4.01 kB
assets/WidgetRecordAudio-BQp8VXR-.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.96 kB 🔴 +4.43 kB
assets/WidgetRecordAudio-CVz7f9PR.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.96 kB 🟢 -4.43 kB
assets/SubscriptionPanelContentWorkspace-BNhX6MG1.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.48 kB 🟢 -3.92 kB
assets/SubscriptionPanelContentWorkspace-UrhZz8hp.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.47 kB 🔴 +3.9 kB
assets/WidgetImageCrop-BXqPd00z.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -4.14 kB 🟢 -3.63 kB
assets/WidgetImageCrop-DA8B7WiC.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +4.14 kB 🔴 +3.63 kB
assets/PanelTemplate-Cb1X1CUk.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -5.45 kB 🟢 -4.79 kB
assets/PanelTemplate-DbKEiWLk.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +5.45 kB 🔴 +4.79 kB
assets/AudioPreviewPlayer-BnuFZg0L.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.65 kB
assets/AudioPreviewPlayer-DrFTV76N.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.97 kB 🔴 +2.65 kB
assets/InviteMemberDialogContent-BTrocS_f.js (new) 8.36 kB 🔴 +8.36 kB 🔴 +2.5 kB 🔴 +2.17 kB
assets/InviteMemberDialogContent-DnpPePXu.js (removed) 8.36 kB 🟢 -8.36 kB 🟢 -2.5 kB 🟢 -2.17 kB
assets/WidgetWithControl-D2bpW7nS.js (new) 8.02 kB 🔴 +8.02 kB 🔴 +2.64 kB 🔴 +2.38 kB
assets/WidgetWithControl-Dyj1Ar_K.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.64 kB 🟢 -2.38 kB
assets/CreateWorkspaceDialogContent-BAUsWHF-.js (removed) 5.93 kB 🟢 -5.93 kB 🟢 -1.93 kB 🟢 -1.68 kB
assets/CreateWorkspaceDialogContent-C5EZz0H0.js (new) 5.93 kB 🔴 +5.93 kB 🔴 +1.93 kB 🔴 +1.68 kB
assets/EditWorkspaceDialogContent-C5bRaI_l.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.88 kB 🔴 +1.65 kB
assets/EditWorkspaceDialogContent-DdR-rhRi.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.88 kB 🟢 -1.65 kB
assets/ValueControlPopover-BFrAHo_x.js (removed) 4.86 kB 🟢 -4.86 kB 🟢 -1.55 kB 🟢 -1.37 kB
assets/ValueControlPopover-DL8WRIcW.js (new) 4.86 kB 🔴 +4.86 kB 🔴 +1.55 kB 🔴 +1.37 kB
assets/DeleteWorkspaceDialogContent-6C1gz2cY.js (new) 4.59 kB 🔴 +4.59 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/DeleteWorkspaceDialogContent-BjlVxRz6.js (removed) 4.59 kB 🟢 -4.59 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/LeaveWorkspaceDialogContent-BfuluSNE.js (new) 4.41 kB 🔴 +4.41 kB 🔴 +1.5 kB 🔴 +1.3 kB
assets/LeaveWorkspaceDialogContent-BuW3HDA5.js (removed) 4.41 kB 🟢 -4.41 kB 🟢 -1.5 kB 🟢 -1.31 kB
assets/RemoveMemberDialogContent-0dB1451n.js (new) 4.38 kB 🔴 +4.38 kB 🔴 +1.45 kB 🔴 +1.27 kB
assets/RemoveMemberDialogContent-BnIK7Zzb.js (removed) 4.38 kB 🟢 -4.38 kB 🟢 -1.45 kB 🟢 -1.27 kB
assets/RevokeInviteDialogContent-BZiFZV7p.js (new) 4.29 kB 🔴 +4.29 kB 🔴 +1.47 kB 🔴 +1.29 kB
assets/RevokeInviteDialogContent-CV9F7dwx.js (removed) 4.29 kB 🟢 -4.29 kB 🟢 -1.47 kB 🟢 -1.29 kB
assets/GlobalToast-BjXpCNg4.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.1 kB 🟢 -946 B
assets/GlobalToast-CxmnjXyy.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.1 kB 🔴 +943 B
assets/SubscribeToRun-BgZfEGc8.js (new) 2.96 kB 🔴 +2.96 kB 🔴 +1.16 kB 🔴 +1.01 kB
assets/SubscribeToRun-DPT6snZA.js (removed) 2.96 kB 🟢 -2.96 kB 🟢 -1.16 kB 🟢 -1.01 kB
assets/cloudSessionCookie-C4aqRCE-.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -935 B 🟢 -804 B
assets/cloudSessionCookie-CGfcE960.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +934 B 🔴 +803 B
assets/BaseViewTemplate-Df6oDeuu.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +938 B
assets/BaseViewTemplate-DIhbg0X6.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.05 kB 🟢 -937 B
assets/CloudRunButtonWrapper-a_gwoHPA.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -641 B 🟢 -561 B
assets/CloudRunButtonWrapper-DcGgqTCZ.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +642 B 🔴 +597 B
assets/cloudBadges-CCDpB7kC.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -541 B 🟢 -488 B
assets/cloudBadges-DoFgppd4.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +539 B 🔴 +485 B
assets/graphHasMissingNodes-8tV6mES3.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -462 B 🟢 -414 B
assets/graphHasMissingNodes-Bv_EwwW4.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +462 B 🔴 +425 B
assets/cloudSubscription-Dt_yhQgY.js (removed) 976 B 🟢 -976 B 🟢 -466 B 🟢 -400 B
assets/cloudSubscription-DtmfoX1X.js (new) 976 B 🔴 +976 B 🔴 +461 B 🔴 +396 B
assets/nightlyBadges-BJ-ijG-Z.js (new) 594 B 🔴 +594 B 🔴 +357 B 🔴 +311 B
assets/nightlyBadges-CI7Ochnl.js (removed) 594 B 🟢 -594 B 🟢 -357 B 🟢 -312 B
assets/SubscriptionPanelContentWorkspace-BDIskf-E.js (removed) 266 B 🟢 -266 B 🟢 -136 B 🟢 -117 B
assets/SubscriptionPanelContentWorkspace-BwNFRfWd.js (new) 266 B 🔴 +266 B 🔴 +136 B 🔴 +117 B
assets/WidgetInputNumber-AN_LJRt7.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -122 B
assets/WidgetInputNumber-D3q0oWby.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +111 B
assets/WidgetLegacy-BC7ArsN_.js (removed) 164 B 🟢 -164 B 🟢 -125 B 🟢 -113 B
assets/WidgetLegacy-BPBYT5Q4.js (new) 164 B 🔴 +164 B 🔴 +125 B 🔴 +107 B
assets/Load3D-BrUjFsFJ.js (removed) 131 B 🟢 -131 B 🟢 -107 B 🟢 -107 B
assets/Load3D-Y0odO4eE.js (new) 131 B 🔴 +131 B 🔴 +107 B 🔴 +107 B
assets/auto-BmypP-XQ.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-27PChCGl.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-B52_zgXW.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BC_Q8we6.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BZlNQPg2.js 17.8 kB 17.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CgVf6wUK.js 17.9 kB 17.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CuidUnsD.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-D5OfZ3bv.js 18.5 kB 18.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Dmj47WTl.js 20.6 kB 20.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DUK2nLuH.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-K3soE7da.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-MVcGSIbM.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LazyImage-rWQpxwnM.js 14.1 kB 14.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-3-ad-MUk.js 127 kB 127 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BBLNw8xJ.js 129 kB 129 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BHVFfeo5.js 125 kB 125 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BICThk_R.js 132 kB 132 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C0B5rjtK.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CBm-XJ4H.js 172 kB 172 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CiYkb6QV.js 111 kB 111 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DNur9seF.js 154 kB 154 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DtCmAq24.js 150 kB 150 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-kSNuyyLo.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-WfrQSHBO.js 125 kB 125 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-8_T22Isd.js 2.38 kB 2.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-CBP8th-j.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-1THG_DJM.js 2.34 kB 2.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-bsLx-Wg6.js 2.84 kB 2.84 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/mixpanel.module-BiPjOPVW.js 143 B 143 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-AWEAv70v.js 442 kB 442 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BbERBXTz.js 335 kB 335 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bg0VvRsL.js 338 kB 338 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bou_oU4e.js 364 kB 364 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BwPth-go.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BWX3iBBy.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dfh39t4u.js 361 kB 361 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DqfOW2sF.js 378 kB 378 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DyDPn66V.js 364 kB 364 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-MJExKdEs.js 368 kB 368 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-x4cF477r.js 358 kB 358 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-B-XG7lU7.js 838 B 838 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-cVp-94Rc.js 1.96 kB 1.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Slider-BAEfKuro.js 4.21 kB 4.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-CD3JnB1i.js 518 B 518 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-Bp7B7z1s.js 186 B 186 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-CgNPbPqq.js 4.71 kB 4.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-3mC7A_va.js 2.79 kB 2.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-x8L_rIAb.js 3.71 kB 3.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-f72WaoQY.js 4.57 kB 4.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-Dd0p7l7V.js 3.79 kB 3.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-DOFSeOZG.js 2.58 kB 2.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-CZXFB71F.js 2.61 kB 2.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-bP5F2-qx.js 3.22 kB 3.22 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-DOe9Bb1I.js 1.31 kB 1.31 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-DwdEJ3mA.js 3.52 kB 3.52 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-BIK7gguT.js 3.08 kB 3.08 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 34 added / 34 removed

@jtydhr88 jtydhr88 removed their assignment Jan 14, 2026
@Yourz Yourz force-pushed the workflow-persistence branch 2 times, most recently from 697beec to 21c480d Compare January 15, 2026 16:40
@Yourz Yourz marked this pull request as ready for review January 15, 2026 16:50
@Yourz Yourz requested a review from a team as a code owner January 15, 2026 16:50
@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jan 15, 2026
@Yourz Yourz changed the title [WIP] Persist all unsaved workflow tabs (make tabs behave like vscode) Feat: Persist all unsaved workflow tabs Jan 15, 2026
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: 3

🤖 Fix all issues with AI agents
In `@src/platform/workflow/persistence/base/draftCache.ts`:
- Around line 26-44: upsertDraft can produce an orphaned draft when called with
limit=0; add a minimum-limit guard at the start of upsertDraft (e.g., coerce
limit = Math.max(1, limit) or throw for invalid limits) so the eviction loop can
never empty order while leaving a draft in drafts; update references in the
function (touchEntry, the eviction while loop, and the final
createDraftCacheState call) to use the sanitized limit value.

In `@src/platform/workflow/persistence/composables/useWorkflowPersistence.ts`:
- Around line 192-197: The code unsafely casts JSON.parse results for
getStorageValue('Comfy.OpenWorkflowsPaths') and
getStorageValue('Comfy.ActiveWorkflowIndex') to string[] and number respectively
(storedWorkflows, storedActiveIndex); instead add runtime validation after
parsing: ensure storedWorkflows is an array of strings (Array.isArray and every
item typeof === 'string') and default to [] if invalid, and ensure
storedActiveIndex is a finite number (typeof === 'number' and Number.isFinite)
and default to -1 if invalid; update any downstream usage to rely on these
validated values.

In `@src/platform/workflow/persistence/stores/workflowDraftStore.ts`:
- Around line 82-97: Update the tryLoadGraph function call to include inline
parameter comments for the boolean args of comfyApp.loadGraphData so it's clear
what they control: annotate the second argument as /* clean= */ and the third as
/* restore_view= */ while keeping the same values and order (i.e.,
comfyApp.loadGraphData(workflow, /* clean= */ true, /* restore_view= */ true,
workflowName)); locate this in tryLoadGraph in workflowDraftStore.ts and apply
the same commenting pattern used in workflowService.ts.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23694f3 and 21c480d.

📒 Files selected for processing (8)
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
🧰 Additional context used
📓 Path-based instructions (14)
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (browser_tests/CLAUDE.md)

browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx}: Test user workflows in browser tests
Use Playwright fixtures for browser tests
Follow naming conventions for browser tests
Check assets/ directory for test data when writing tests
Prefer specific selectors in browser tests
Test across multiple viewports

Files:

  • browser_tests/tests/colorPalette.spec.ts
browser_tests/**/*.spec.ts

📄 CodeRabbit inference engine (AGENTS.md)

browser_tests/**/*.spec.ts: E2E tests use Playwright and belong in browser_tests/**/*.spec.ts
Follow Playwright best practices for E2E testing
Do not use waitForTimeout in Playwright tests - use Locator actions and retrying assertions instead
Use tags like @mobile, @2x in Playwright tests for configuration-aware test selection

Files:

  • browser_tests/tests/colorPalette.spec.ts
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/stores/**/*.{ts,tsx}: Maintain clear public interfaces and restrict extension access in stores
Use TypeScript for type safety in state management stores

Files:

  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
🧠 Learnings (42)
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test user workflows in browser tests

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Do not write change detector tests - avoid tests that only assert default values

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests dependent on non-behavioral features like utility classes or styles

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Unit and component tests belong in `tests-ui/` or `src/**/*.test.ts` using Vitest

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
📚 Learning: 2025-12-09T03:39:54.501Z
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.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-13T11:03:11.264Z
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.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-17T00:40:09.635Z
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).

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-30T22:22:33.836Z
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().

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2025-12-11T12:25:15.470Z
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.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-12-22T21:36:38.905Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts:189-194
Timestamp: 2025-12-22T21:36:38.905Z
Learning: In tests-ui feature tests, do not stub primitive UI components or customized primitives (e.g., Button). Instead, import and register the real components in the test setup so tests reflect production behavior and component API usage. This improves test reliability and fidelity. If a test must avoid rendering the full component, justify the approach and consider higher-level mocks or integration tests rather than replacing the primitive itself.

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/draftCache.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2025-12-13T05:54:35.779Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7415
File: browser_tests/tests/mobileBaseline.spec.ts:17-22
Timestamp: 2025-12-13T05:54:35.779Z
Learning: In browser_tests tests for the Comfy-Org/ComfyUI_frontend repository, the `comfyPage.loadWorkflow()` method already handles all necessary synchronization and waiting. No additional `await comfyPage.nextFrame()` call is needed before taking screenshots after loading a workflow.

Applied to files:

  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to browser_tests/**/*.spec.ts : Do not use waitForTimeout in Playwright tests - use Locator actions and retrying assertions instead

Applied to files:

  • browser_tests/tests/colorPalette.spec.ts
📚 Learning: 2025-12-12T23:02:37.473Z
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.

Applied to files:

  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to browser_tests/**/*.spec.ts : Follow Playwright best practices for E2E testing

Applied to files:

  • browser_tests/tests/colorPalette.spec.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
📚 Learning: 2025-12-13T05:34:15.488Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7415
File: browser_tests/tests/mobileBaseline.spec.ts:7-15
Timestamp: 2025-12-13T05:34:15.488Z
Learning: In Playwright tests for the ComfyUI frontend, the toPass() assertion uses incremental backoff during retries. When a test may involve async operations, increasing the timeout (e.g., to 5000 ms) can be sufficient instead of aggressively extending timeouts. Apply this understanding to tests under browser_tests/tests/; if not resolved, review the toPass() backoff behavior and ensure timeouts align with expected async completion without masking issues.

Applied to files:

  • browser_tests/tests/colorPalette.spec.ts
📚 Learning: 2026-01-12T17:39:27.738Z
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.

Applied to files:

  • src/platform/workflow/persistence/base/draftCache.ts
  • src/platform/workflow/management/stores/workflowStore.ts
  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
  • src/platform/workflow/management/stores/workflowStore.test.ts
  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/stores/**/*.ts : Name Pinia stores as `*Store.ts` (e.g., `nodeStore.ts`)

Applied to files:

  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in stores

Applied to files:

  • src/platform/workflow/persistence/stores/workflowDraftStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Don't Mock What You Don't Own

Applied to files:

  • tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Use `ref` for reactive state, `computed()` for derived values, and `watch`/`watchEffect` for side effects in Composition API

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Avoid using `ref` with `watch` if a `computed` would suffice - minimize refs and derived state

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Leverage VueUse functions for performance-enhancing composables

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use watch and watchEffect for side effects

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Implement lifecycle hooks with onMounted, onUpdated, etc.

Applied to files:

  • src/platform/workflow/persistence/composables/useWorkflowPersistence.ts
🧬 Code graph analysis (5)
browser_tests/tests/colorPalette.spec.ts (1)
src/stores/queueStore.ts (1)
  • workflow (315-317)
src/platform/workflow/management/stores/workflowStore.ts (2)
src/platform/workflow/validation/schemas/workflowSchema.ts (1)
  • ComfyWorkflowJSON (459-461)
src/scripts/changeTracker.ts (1)
  • ChangeTracker (28-446)
src/platform/workflow/persistence/stores/workflowDraftStore.ts (1)
src/platform/workflow/persistence/base/draftCache.ts (9)
  • WorkflowDraftSnapshot (1-6)
  • mostRecentDraftPath (76-77)
  • DraftCacheState (8-11)
  • createDraftCacheState (15-18)
  • upsertDraft (26-44)
  • MAX_DRAFTS (13-13)
  • removeDraft (46-55)
  • moveDraft (57-74)
  • touchEntry (20-24)
src/platform/workflow/management/stores/workflowStore.test.ts (2)
src/platform/workflow/persistence/stores/workflowDraftStore.ts (1)
  • useWorkflowDraftStore (30-154)
src/scripts/defaultGraph.ts (2)
  • defaultGraph (3-142)
  • defaultGraphJSON (144-144)
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (4)
src/platform/workflow/persistence/stores/workflowDraftStore.ts (1)
  • useWorkflowDraftStore (30-154)
src/scripts/api.ts (1)
  • api (1336-1336)
src/platform/workflow/management/stores/workflowStore.ts (2)
  • key (56-58)
  • ComfyWorkflow (35-167)
src/scripts/utils.ts (1)
  • getStorageValue (96-102)
🔇 Additional comments (33)
browser_tests/tests/colorPalette.spec.ts (1)

235-258: Improved synchronization approach for workflow persistence validation.

The change from a fixed delay to waitForFunction polling localStorage is a reasonable approach for this test case. The defensive validation (checking parsed.nodes exists and is an array before iteration) properly handles edge cases.

However, note that per coding guidelines, waitForTimeout should be avoided in favor of Locator actions and retrying assertions. While waitForFunction is better than a fixed timeout, consider whether a retrying assertion pattern could work here in the future.

tests-ui/tests/platform/workflow/persistence/draftCache.test.ts (1)

1-77: Good behavioral coverage for draft cache operations.

The test suite properly covers the core behaviors of the draft cache module:

  • touchEntry moves existing entries to end and adds new entries
  • upsertDraft enforces LRU eviction when exceeding MAX_DRAFTS
  • removeDraft clears both the draft entry and order
  • moveDraft renames entries and updates order
  • mostRecentDraftPath returns the last entry or null

The tests are parsimonious and focus on behavioral outcomes rather than implementation details.

tests-ui/tests/platform/workflow/persistence/useWorkflowPersistence.test.ts (4)

35-61: Well-structured mock setup using vi.hoisted().

The use of vi.hoisted() for maintaining mock state is the correct pattern per coding guidelines. This ensures the mocks are properly hoisted and avoids issues with global mutable state.

The mock API surface is comprehensive, covering the necessary methods for workflow persistence testing.


112-144: Thorough multi-workflow persistence test.

This test properly validates that the persistence system:

  1. Saves drafts for multiple workflows independently
  2. Preserves the correct workflow data in each draft
  3. Maintains the isTemporary flag

The use of fake timers with advanceTimersByTimeAsync(800) correctly simulates the debounced persistence behavior.


146-167: Correct LRU eviction boundary test.

The test validates that when 33 drafts are created:

  • Only 32 remain (respecting MAX_DRAFTS)
  • The oldest draft (Draft0) is evicted
  • The newest draft (Draft32) is retained

The time advancement between saves ensures proper recency ordering.


169-196: Validates temporary workflow restoration from drafts.

This test covers a critical user scenario: restoring unsaved temporary workflows after a page refresh. It properly verifies that:

  1. The workflow is recreated from the cached draft
  2. The isTemporary flag is preserved
  3. The workflow is included in openWorkflows
src/platform/workflow/management/stores/workflowStore.test.ts (4)

14-14: Import added for draft store integration.

The import of useWorkflowDraftStore is correctly added to support the new draft-related test cases.


65-82: Proper test isolation with draft store reset.

The addition of localStorage.clear(), sessionStorage.clear(), and useWorkflowDraftStore().reset() ensures each test starts with a clean slate, preventing cross-test contamination for draft-related functionality.


242-268: Validates draft precedence over remote content.

The test correctly verifies that when a local draft exists, it takes precedence over the remote server content. The workflow is properly marked as modified (isModified: true) when loaded from a draft.

One minor observation: The draftGraph object spreads defaultGraph.nodes into a new array, which is fine for this test since the comparison uses toEqual (deep equality).


270-294: Validates stale draft handling.

This test covers an important edge case: when the server version is newer than the local draft, the draft should be discarded to avoid overwriting more recent changes. The test correctly:

  1. Creates a draft with a timestamp
  2. Sets lastModified to be 1 second later than the draft
  3. Verifies the draft is removed and workflow is not marked as modified
src/platform/workflow/management/stores/workflowStore.ts (6)

16-16: Import added for draft store integration.

The import is correctly placed with other persistence-related imports.


87-128: Draft integration in workflow loading is well-implemented.

The load() method now correctly:

  1. Retrieves drafts when not force-loading
  2. Discards stale drafts (when draft.updatedAt < this.lastModified)
  3. Handles JSON parse errors gracefully with a warning and draft removal
  4. Applies valid drafts by setting activeState, content, and marking as modified
  5. Updates LRU order via markDraftUsed()

The logic flow is sound: draft validity is checked before the main load, and draft application happens after the original content is loaded, ensuring originalContent remains the server version while content and activeState reflect the draft.


135-145: Draft cleanup after save is correctly placed.

The draft is removed after the save operation succeeds, ensuring that:

  • If save fails, the draft is preserved as a backup
  • Successful saves clear the draft to avoid stale data on next load

152-158: Verify draft handling in saveAs.

The implementation removes the draft at the new path (path), which is correct to ensure the newly saved file doesn't have a stale draft. However, consider whether the draft at this.path (the original workflow's path) should also be removed or retained.

If the user saves a modified workflow to a new file, the original workflow remains modified but its draft may now be orphaned (the active state was already captured in the new file).

Is it intentional that the original workflow's draft is preserved when using saveAs? If the original workflow should be considered "clean" after saveAs (since changes were saved elsewhere), consider removing its draft as well.


590-620: Draft movement correctly integrated into rename flow.

The draft is moved after the workflow rename succeeds, using moveDraft(oldPath, newPath, workflow.key). This maintains draft continuity when a workflow is renamed.

The placement after the try-finally block ensures the draft is only moved if the rename operation completes successfully.


622-636: Draft removal on workflow deletion is correct.

The draft is removed after the workflow is successfully deleted, preventing orphaned drafts in storage.

src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (5)

52-103: LGTM - Draft persistence flow is well-structured.

The deduplication via lastSavedJsonByPath prevents redundant writes, and the error handling correctly aborts persistence if draft saving fails. The legacy localStorage/sessionStorage writes provide backward compatibility.


105-115: LGTM - Clean delegation to draft store.

The path construction correctly uses ComfyWorkflow.basePath prefix, and the fallback logic appropriately uses the latest draft only when no specific workflow was previously active.


173-189: LGTM - Path filtering aligns with draft persistence scope.

The filter ensures only workflows with the correct base path are persisted, which correctly constrains the restoration scope to draft-managed workflows.


211-225: Edge case: Non-temporary workflows are not restored from drafts.

The loop skips drafts where !draft?.isTemporary, meaning saved workflows won't be recreated from drafts if they're missing from the store. This is likely intentional (saved workflows load from server), but verify this aligns with expected behavior when saved workflows are deleted server-side.


206-231: The createTemporary method signature correctly accepts an optional workflowData parameter. Both calls in the code—with and without the parameter—are valid and properly handled by the implementation.

src/platform/workflow/persistence/stores/workflowDraftStore.ts (7)

1-19: LGTM - Imports follow repository conventions.

Type imports are correctly separated, and renamed imports (moveDraftEntry, removeDraftEntry) avoid collision with store methods.


30-45: LGTM - Store setup correctly uses VueUse storage and computed.

The useStorage integration provides automatic localStorage sync, and the currentState/updateState pattern cleanly bridges reactive state with pure cache functions.


47-65: Single retry on quota exceeded is pragmatic but limited.

The quota handling removes the oldest draft and retries once. If the retry also fails (e.g., single large workflow exceeds available space), the error propagates. Consider whether logging a warning about storage constraints would help users debug persistent save failures.


75-78: LGTM - Efficient touch-only operation.

Directly updating storedOrder.value rather than calling updateState is appropriate here since only the order changes, avoiding unnecessary drafts object reassignment.


99-109: LGTM - Clean draft loading with automatic cleanup.

The pattern of removing corrupted drafts via the onFailure callback ensures invalid entries don't persist and cause repeated failures.


111-141: LGTM - Multi-stage loading cascade is well-structured.

The priority order (preferred path → latest draft → session → local storage) provides robust fallback behavior, and the initialClientId ?? clientId pattern aligns with getStorageValue in utils.ts.


143-154: LGTM - Clean public interface with good encapsulation.

The store exposes only the necessary methods while keeping storedDrafts and storedOrder internal. The reset method is useful for testing scenarios.

src/platform/workflow/persistence/base/draftCache.ts (5)

1-11: LGTM - Clean type definitions.

The WorkflowDraftSnapshot interface captures all necessary metadata for draft management, and DraftCacheState provides a clear shape for the cache structure.


20-24: LGTM - Correct LRU touch semantics.

The function correctly moves the touched path to the end of the order array, maintaining recency ordering for the LRU cache.


46-55: LGTM - Correct removal with early return optimization.

The early return for non-existent paths avoids unnecessary object creation, and the implementation maintains immutability by creating new objects.


57-74: LGTM - Move operation correctly handles order updates.

The implementation properly removes both old and new paths from the order before touching the new path, preventing duplicate entries and handling edge cases where the new path might have previously existed.


76-77: LGTM - Simple and correct most-recent accessor.

Returns the last entry in the order array, which is correctly maintained as the most recently accessed draft by touchEntry.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@christian-byrne christian-byrne added the preview-cpu Creates a preview ephemeral environment for this PR (CPU only) label Jan 16, 2026
@Yourz Yourz force-pushed the workflow-persistence branch from 6b42c0f to 0339e96 Compare January 20, 2026 11:17
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

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/persistence/composables/useWorkflowPersistence.ts (1)

50-103: Don’t mark drafts as “saved” before a successful write.

lastSavedJsonByPath is updated before the draft/local/session storage writes. If saveDraft or storage throws, subsequent saves of the same JSON are skipped and the draft may never persist. Move the assignment to after successful storage writes (or reset it on failure).

🔧 Proposed fix
-    if (workflowJson === lastSavedJsonByPath.value[workflowPath]) return
-    lastSavedJsonByPath.value[workflowPath] = workflowJson
+    if (workflowJson === lastSavedJsonByPath.value[workflowPath]) return
@@
-    try {
+    try {
       workflowDraftStore.saveDraft(activeWorkflow.path, {
         data: workflowJson,
         updatedAt: Date.now(),
         name: activeWorkflow.key,
         isTemporary: activeWorkflow.isTemporary
       })
     } catch (error) {
       console.error('Failed to save draft', error)
       // If draft store fails, don't continue saving to storage
       return
     }
 
     try {
       localStorage.setItem('workflow', workflowJson)
       if (api.clientId) {
         sessionStorage.setItem(`workflow:${api.clientId}`, workflowJson)
       }
     } catch (error) {
@@
       throw error
     }
+
+    lastSavedJsonByPath.value[workflowPath] = workflowJson
🤖 Fix all issues with AI agents
In `@src/platform/workflow/persistence/composables/useWorkflowPersistence.ts`:
- Around line 156-159: The comment above the direct call to
persistCurrentWorkflow() is misleading because it claims a debounced save is
used while the code calls the function immediately; either replace the call with
the debounced variant (e.g., useDebouncedPersist or
debouncedPersistCurrentWorkflow if such a wrapper exists/was intended) to
actually debounce saves, or update/remove the comment to accurately reflect the
direct save behavior—change the comment and ensure the code path around
persistCurrentWorkflow() (and any activeWorkflow change handling) matches the
chosen behavior.
- Around line 211-224: When restoring storedWorkflows, currently failed JSON
parses leave the corrupted draft in storage; update the catch block after
JSON.parse(draft.data) to remove the bad draft (use
workflowDraftStore.removeDraft or the equivalent delete method) after calling
workflowStore.createTemporary(draft.name) so the draft won't keep failing on
subsequent restores; reference storedWorkflows, workflowStore.getWorkflowByPath,
workflowDraftStore.getDraft and workflowStore.createTemporary to locate the
logic and ensure the removal runs only on parse failure.
♻️ Duplicate comments (1)
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (1)

191-197: Validate parsed storage values before use.

The JSON.parse results are cast directly to string[]/number. If storage is corrupted, restoration logic can break. Add runtime validation and fall back to safe defaults.

🔧 Proposed fix
-  const storedWorkflows = JSON.parse(
-    getStorageValue('Comfy.OpenWorkflowsPaths') || '[]'
-  ) as string[]
-  const storedActiveIndex = JSON.parse(
-    getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
-  ) as number
+  const parsedWorkflows = JSON.parse(
+    getStorageValue('Comfy.OpenWorkflowsPaths') || '[]'
+  )
+  const storedWorkflows = Array.isArray(parsedWorkflows)
+    ? parsedWorkflows.filter((path) => typeof path === 'string')
+    : []
+  const parsedIndex = JSON.parse(
+    getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
+  )
+  const storedActiveIndex =
+    typeof parsedIndex === 'number' && Number.isFinite(parsedIndex)
+      ? parsedIndex
+      : -1
As per coding guidelines, validate trusted sources before processing data.

@Yourz Yourz force-pushed the workflow-persistence branch from 0339e96 to a7d4579 Compare January 24, 2026 10:03
@github-actions
Copy link

🔧 Auto-fixes Applied

This PR has been automatically updated to fix linting and formatting issues.

⚠️ Important: Your local branch is now behind. Run git pull before making additional changes to avoid conflicts.

Changes made:

  • ESLint auto-fixes
  • Oxfmt formatting

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: 4

🤖 Fix all issues with AI agents
In `@browser_tests/tests/colorPalette.spec.ts`:
- Around line 235-255: The current waitForFunction only validates and you
re-read localStorage again; update comfyPage.page.waitForFunction so its
predicate returns the parsed workflow object (instead of true/false) after
successfully JSON.parse-ing localStorage.getItem('workflow'), then capture that
returned value (the parsed workflow) from the waitForFunction call and use it
directly (replace the subsequent page.evaluate and JSON.parse). Keep existing
error handling in the predicate (try/catch) and ensure the returned value is the
parsed object so parsed.nodes checks and the for (const node of parsed.nodes)
loop operate on that returned object.

In
`@src/platform/workflow/persistence/composables/useWorkflowPersistence.test.ts`:
- Around line 164-175: The test uses "as Record<string, any>" for drafts—replace
the any with a concrete typed shape (e.g., define a DraftEntry type { data:
string; isTemporary: boolean } and parse into Record<string, DraftEntry>) or
parse into unknown and validate with a simple type guard before assertions;
update the variable "drafts" assignment in useWorkflowPersistence.test.ts and
apply the same pattern to the other parse/assert block referenced (lines
~191-193) so subsequent JSON.parse(drafts['workflows/DraftA.json'].data) and
isTemporary checks are type-safe.

In `@src/platform/workflow/persistence/stores/draftCache.test.ts`:
- Around line 14-19: createSnapshot uses Date.now(), which makes tests
non-deterministic; change createSnapshot (returning WorkflowDraftSnapshot) to
use a deterministic timestamp by either accepting an optional timestamp
parameter or using a fixed constant (e.g., FIXED_TEST_TIME) or an incrementing
counter, and update tests to pass/expect that deterministic value so
snapshot.updatedAt is stable across runs; locate the createSnapshot helper in
draftCache.test.ts and adjust its implementation and any test assertions
accordingly.

In `@src/platform/workflow/persistence/stores/workflowDraftStore.ts`:
- Around line 47-65: In saveDraft, when a QuotaExceededError occurs and
currentState().order is empty the catch silently swallows the error and the
draft is lost; modify the catch in saveDraft to rethrow the error if there is
nothing to evict (i.e. state.order.length === 0) so callers can surface a UI
error, otherwise keep the existing eviction logic that calls removeDraftEntry
and retries upsertDraft using currentState(), ensuring you still throw the
original error when eviction cannot free space.
♻️ Duplicate comments (1)
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (1)

199-207: Harden storage parsing beyond Array.isArray / typeof number.

Storage can be corrupted; this still allows non-string entries and NaN/Infinity. Consider validating string items and using Number.isFinite.

♻️ Suggested tightening
-  const storedWorkflows = Array.isArray(parsedWorkflows) ? parsedWorkflows : []
+  const storedWorkflows = Array.isArray(parsedWorkflows)
+    ? parsedWorkflows.filter((entry): entry is string => typeof entry === 'string')
+    : []
   const parsedIndex = JSON.parse(
     getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
   )
-  const storedActiveIndex = typeof parsedIndex === 'number' ? parsedIndex : -1
+  const storedActiveIndex =
+    typeof parsedIndex === 'number' && Number.isFinite(parsedIndex)
+      ? parsedIndex
+      : -1

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: 1

🤖 Fix all issues with AI agents
In `@src/platform/workflow/persistence/stores/draftCache.test.ts`:
- Around line 14-19: Change the helper createSnapshot from a const arrow
function to a function declaration named createSnapshot that returns a
WorkflowDraftSnapshot; keep the same parameters (name: string), body (returns {
data: JSON.stringify({ name }), updatedAt: Date.now(), name, isTemporary: true
}) and type semantics so it still produces the same snapshot object and hoists
like other pure helpers.
♻️ Duplicate comments (1)
src/platform/workflow/persistence/composables/useWorkflowPersistence.ts (1)

200-208: Harden parsing of stored tab state (avoid startup crash on corrupted JSON).

JSON.parse can throw; also validate element types and finite index values before use.

🔧 Proposed fix
-  const parsedWorkflows = JSON.parse(
-    getStorageValue('Comfy.OpenWorkflowsPaths') || '[]'
-  )
-  const storedWorkflows = Array.isArray(parsedWorkflows) ? parsedWorkflows : []
-  const parsedIndex = JSON.parse(
-    getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
-  )
-  const storedActiveIndex = typeof parsedIndex === 'number' ? parsedIndex : -1
+  let storedWorkflows: string[] = []
+  let storedActiveIndex = -1
+  try {
+    const parsedWorkflows = JSON.parse(
+      getStorageValue('Comfy.OpenWorkflowsPaths') || '[]'
+    )
+    storedWorkflows =
+      Array.isArray(parsedWorkflows) &&
+      parsedWorkflows.every((path) => typeof path === 'string')
+        ? parsedWorkflows
+        : []
+    const parsedIndex = JSON.parse(
+      getStorageValue('Comfy.ActiveWorkflowIndex') || '-1'
+    )
+    storedActiveIndex =
+      typeof parsedIndex === 'number' && Number.isFinite(parsedIndex)
+        ? parsedIndex
+        : -1
+  } catch (err) {
+    console.warn('Failed to parse workflow tab state', err)
+  }

As per coding guidelines, implement proper error handling for untrusted storage data.

pythongosssss
pythongosssss previously approved these changes Jan 24, 2026
Copy link
Member

@pythongosssss pythongosssss left a comment

Choose a reason for hiding this comment

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

lgtm

@Yourz Yourz force-pushed the workflow-persistence branch from 99452d7 to 24514f7 Compare January 26, 2026 04:46
@Yourz Yourz enabled auto-merge (squash) January 26, 2026 04:46
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: 1

🤖 Fix all issues with AI agents
In `@src/platform/workflow/core/services/workflowService.ts`:
- Around line 296-306: Wrap the call to workflowDraftStore.saveDraft inside a
try/catch so failures (quota/serialization errors) don’t abort
beforeLoadNewGraph and block the tab switch; specifically, in the block guarded
by settingStore.get('Comfy.Workflow.Persist') and when activeWorkflow.path and
activeWorkflow.activeState exist, catch any error from
workflowDraftStore.saveDraft(activeWorkflow.path, {...}) and handle it by
logging and showing a user toast/notification (e.g., toast.error or
notificationService) while allowing execution to continue.
♻️ Duplicate comments (1)
src/platform/workflow/persistence/composables/useWorkflowPersistence.test.ts (1)

191-193: Remove any from drafts parsing.
Tests should avoid any; use a concrete draft entry type instead.

♻️ Suggested fix
    const drafts = JSON.parse(
      localStorage.getItem('Comfy.Workflow.Drafts') ?? '{}'
-    ) as Record<string, any>
+    ) as Record<string, { data: string; isTemporary: boolean }>

@Yourz Yourz requested a review from pythongosssss January 26, 2026 14:54
@Yourz
Copy link
Collaborator

Yourz commented Jan 26, 2026

@pythongosssss I update the code for coderabbit review. PTAL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:workflows preview-cpu Creates a preview ephemeral environment for this PR (CPU only) size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

4 participants