Skip to content

Conversation

@benceruleanlu
Copy link
Member

@benceruleanlu benceruleanlu commented Jan 23, 2026

Add expandable output stacks to the assets list view.

Monolith ver. of #8298 and its children

List view currently collapses multi-output jobs into a single row, which makes sibling outputs easy to miss and causes selection/zoom behavior to drift once items are expanded elsewhere. This change adds a stack toggle to list rows, expands child outputs derived from job data, and keeps list-view selection and gallery navigation aligned with the expanded list. Output mapping and “load full outputs” checks are centralized so folder view and stacks share the same helper, and job-detail parsing now yields previewable outputs for the list view. Asset actions now prefer metadata prompt IDs to support the composite IDs used by stacked outputs.

┆Issue is synchronized with this Notion page by Unito

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

📝 Walkthrough

Walkthrough

Adds hierarchical output stacks with lazy-loaded children, updates sidebar list/grid to consume stacked assets and expose stack controls, moves anchor-based selection and reconciliation into selection composables/store (persisting lastSelectedAssetId), introduces utilities/tests for resolving previewable outputs, and wires UI to persist last-selected asset on select.

Changes

Cohort / File(s) Summary
Output Stacks Core
src/platform/assets/composables/useOutputStacks.ts, src/platform/assets/composables/useOutputStacks.test.ts, src/platform/assets/utils/outputAssetUtil.ts, src/platform/assets/utils/outputAssetUtil.test.ts
Add new useOutputStacks composable, OutputStackListItem type, and output resolution utilities; implement lazy child loading, expansion state, mapping outputs → AssetItem, and comprehensive tests.
Sidebar Integration
src/components/sidebar/tabs/AssetsSidebarTab.vue, src/components/sidebar/tabs/AssetsSidebarListView.vue, src/components/sidebar/tabs/AssetsSidebarListView.stories.ts
Refactor list/tab to accept assetItems (OutputStackListItem[]), add selectable-assets, is-stack-expanded, toggle-stack props, update selection and gallery mapping to use visibleAssets, and update story wiring.
Stack UI Indicator
src/platform/assets/components/AssetsListItem.vue
Add optional stack indicator UI: props stackCount, stackIndicatorLabel, stackExpanded, and emit stack-toggle; render a count button when stackCount > 1.
Selection & Store
src/platform/assets/composables/useAssetSelection.ts, src/platform/assets/composables/useAssetSelection.test.ts, src/platform/assets/composables/useAssetSelectionStore.ts, src/platform/assets/composables/useAssetSelectionStore.test.ts
Introduce anchor management and reconcileSelection(assets) to prune/sync selection with visible assets; persist lastSelectedAssetId in store via setLastSelectedAssetId; adjust tests for reconciliation and store behavior.
Output Resolution & Caching
src/services/jobOutputCache.ts, src/services/jobOutputCache.test.ts
Add getPreviewableOutputsFromJobDetail + internal getPreviewableOutputs filtering logic for previewable outputs; include tests covering filtering and edge cases.
Folder View / Grid Adjustments
src/components/sidebar/tabs/AssetsSidebarGridView.vue
Add optional isInFolderView prop and hide Active Jobs grid when in folder view.
Small Integrations & Actions
src/components/queue/QueueProgressOverlay.vue, src/platform/assets/composables/useMediaAssetActions.ts
Persist last-selected asset id after sidebar selection (setLastSelectedAssetId call); change promptId lookup precedence to prefer metadata.promptId for outputs.
Misc Tests
src/platform/assets/composables/useOutputStacks.test.ts, src/platform/assets/composables/useAssetSelection.test.ts
Add and extend tests covering stack resolution, toggle behavior, selection reconciliation, and store clearing of lastSelectedAssetId.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SidebarUI as Sidebar List View
    participant OutputStacks as UseOutputStacks
    participant JobCache as Job Output Cache
    participant Store as AssetSelectionStore

    User->>SidebarUI: Click stack indicator (toggle)
    SidebarUI->>OutputStacks: toggleStack(asset)
    alt first expansion (children not loaded)
        OutputStacks->>JobCache: resolveOutputAssetItems(metadata)
        JobCache-->>OutputStacks: AssetItem[] (children)
        OutputStacks->>OutputStacks: store children, mark promptId expanded
    end
    OutputStacks->>SidebarUI: update assetItems & selectableAssets
    SidebarUI-->>User: render expanded items
    User->>SidebarUI: select asset
    SidebarUI->>Store: setLastSelectedAssetId(assetId)
    Note over OutputStacks,Store: visibleAssets change
    OutputStacks->>Store: reconcileSelection(visibleAssets)
Loading

Possibly related PRs

Suggested reviewers

  • shinshin86
  • KarryCharon
  • Myestery
  • christian-byrne
  • Yorha4D
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bl-stacked-assets

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 23, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/30/2026, 06:06:52 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 23, 2026

🎭 Playwright Tests: ✅ Passed

Results: 507 passed, 0 failed, 0 flaky, 8 skipped (Total: 515)

📊 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 (✅ 9 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@benceruleanlu benceruleanlu changed the base branch from main to bl-inline-progress January 23, 2026 23:15
@benceruleanlu benceruleanlu changed the title Surface QPO V2 progress inline and expand asset stacks in list view Add expandable output stacks to assets list view Jan 23, 2026
@github-actions
Copy link

github-actions bot commented Jan 23, 2026

Bundle Size Report

Summary

  • Raw size: 22.1 MB baseline 22.1 MB — 🔴 +9.04 kB
  • Gzip: 4.61 MB baseline 4.61 MB — 🔴 +1.91 kB
  • Brotli: 3.42 MB baseline 3.42 MB — 🔴 +1.56 kB
  • Bundles: 173 current • 173 baseline • 81 added / 81 removed

Category Glance
Data & Services 🔴 +9.19 kB (2.72 MB) · Other 🟢 -198 B (7.1 MB) · Graph Workspace 🔴 +57 B (974 kB) · Panels & Settings 🟢 -8 B (471 kB) · Vendor & Third-Party ⚪ 0 B (10.7 MB) · Views & Navigation ⚪ 0 B (80.7 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-0o8eSuaX.js (removed) 26 kB 🟢 -26 kB 🟢 -7.5 kB 🟢 -6.61 kB
assets/index-DkpdJioS.js (new) 26 kB 🔴 +26 kB 🔴 +7.5 kB 🔴 +6.58 kB

Status: 1 added / 1 removed

Graph Workspace — 974 kB (baseline 974 kB) • 🔴 +57 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-Bg7dYJbM.js (new) 974 kB 🔴 +974 kB 🔴 +197 kB 🔴 +149 kB
assets/GraphView-BbQqs2Ly.js (removed) 974 kB 🟢 -974 kB 🟢 -197 kB 🟢 -149 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-B8LkFGIm.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -3.61 kB 🟢 -3.05 kB
assets/CloudSurveyView-COYDq9AF.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +3.61 kB 🔴 +3.05 kB
assets/CloudLoginView-C9R0_Ugu.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.09 kB 🟢 -2.71 kB
assets/CloudLoginView-cIuGUsEt.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.09 kB 🔴 +2.72 kB
assets/UserCheckView-CLwAHgYQ.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.44 kB 🟢 -2.13 kB
assets/UserCheckView-DVX0XIyq.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.44 kB 🔴 +2.13 kB
assets/CloudLayoutView-BB2Oum5f.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.24 kB 🟢 -1.96 kB
assets/CloudLayoutView-DnJzRMa5.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.24 kB 🔴 +1.95 kB
assets/CloudSignupView-B3qZgE2h.js (removed) 8.18 kB 🟢 -8.18 kB 🟢 -2.32 kB 🟢 -2.02 kB
assets/CloudSignupView-BOZJfZ9q.js (new) 8.18 kB 🔴 +8.18 kB 🔴 +2.33 kB 🔴 +2.02 kB
assets/CloudForgotPasswordView-B1msQ3bg.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -1.93 kB 🟢 -1.69 kB
assets/CloudForgotPasswordView-CyR4_gUr.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +1.92 kB 🔴 +1.69 kB
assets/UserSelectView-D0PLhTti.js (removed) 5.28 kB 🟢 -5.28 kB 🟢 -1.76 kB 🟢 -1.57 kB
assets/UserSelectView-DyFCxbQr.js (new) 5.28 kB 🔴 +5.28 kB 🔴 +1.76 kB 🔴 +1.57 kB
assets/CloudSubscriptionRedirectView-BovhS7Jl.js (new) 5.27 kB 🔴 +5.27 kB 🔴 +1.73 kB 🔴 +1.54 kB
assets/CloudSubscriptionRedirectView-DPadqUKi.js (removed) 5.27 kB 🟢 -5.27 kB 🟢 -1.73 kB 🟢 -1.54 kB
assets/CloudAuthTimeoutView-BevAbWnL.js (removed) 5.24 kB 🟢 -5.24 kB 🟢 -1.7 kB 🟢 -1.48 kB
assets/CloudAuthTimeoutView-yVgPWMWS.js (new) 5.24 kB 🔴 +5.24 kB 🔴 +1.71 kB 🔴 +1.48 kB
assets/CloudSorryContactSupportView-cm9oKn4s.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-CUzumK-h.js 500 B 500 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WorkspacePanel-22RIfqld.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -5.89 kB 🟢 -5.14 kB
assets/WorkspacePanel-B7tSy9kf.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +5.89 kB 🔴 +5.14 kB
assets/LegacyCreditsPanel-DSHi1D4z.js (removed) 23.8 kB 🟢 -23.8 kB 🟢 -5.94 kB 🟢 -5.22 kB
assets/LegacyCreditsPanel-Ek5pth5G.js (new) 23.8 kB 🔴 +23.8 kB 🔴 +5.94 kB 🔴 +5.22 kB
assets/SubscriptionPanel-DpViEyFm.js (removed) 21 kB 🟢 -21 kB 🟢 -5.04 kB 🟢 -4.44 kB
assets/SubscriptionPanel-un7q2pC8.js (new) 21 kB 🔴 +21 kB 🔴 +5.04 kB 🔴 +4.45 kB
assets/KeybindingPanel-BoIpi4dt.js (new) 14.3 kB 🔴 +14.3 kB 🔴 +3.76 kB 🔴 +3.34 kB
assets/KeybindingPanel-Cqh2zhF5.js (removed) 14.3 kB 🟢 -14.3 kB 🟢 -3.76 kB 🟢 -3.34 kB
assets/AboutPanel-GAvjswsi.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.68 kB 🔴 +2.44 kB
assets/AboutPanel-nAPeX13O.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.68 kB 🟢 -2.43 kB
assets/ExtensionPanel-BN17h4C0.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.71 kB 🟢 -2.4 kB
assets/ExtensionPanel-VlyZM4kQ.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.71 kB 🔴 +2.4 kB
assets/ServerConfigPanel-Drtk3Cj9.js (removed) 7.23 kB 🟢 -7.23 kB 🟢 -2.17 kB 🟢 -1.94 kB
assets/ServerConfigPanel-Mn-b4cRl.js (new) 7.23 kB 🔴 +7.23 kB 🔴 +2.17 kB 🔴 +1.94 kB
assets/UserPanel-CX_ZP5HA.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -1.91 kB 🟢 -1.68 kB
assets/UserPanel-CxAJOuQ6.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +1.91 kB 🔴 +1.67 kB
assets/refreshRemoteConfig-BvAnKw7S.js (removed) 1.31 kB 🟢 -1.31 kB 🟢 -571 B 🟢 -496 B
assets/refreshRemoteConfig-CTPflSYs.js (new) 1.31 kB 🔴 +1.31 kB 🔴 +572 B 🔴 +503 B
assets/config-CQmXnrXP.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -609 B 🟢 -539 B
assets/config-CqT45jwM.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +604 B 🔴 +532 B
assets/cloudRemoteConfig-BpfcOE1q.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -509 B 🟢 -439 B
assets/cloudRemoteConfig-BS4ckOs7.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +510 B 🔴 +444 B
assets/refreshRemoteConfig-Bckc2EMm.js (removed) 169 B 🟢 -169 B 🟢 -108 B 🟢 -102 B
assets/refreshRemoteConfig-Bfj7u45b.js (new) 169 B 🔴 +169 B 🔴 +108 B 🔴 +106 B
assets/remoteConfig-B0mlVvm7.js 788 B 788 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-2UNjEj6k.js 32.9 kB 32.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B2OMGvh7.js 31.2 kB 31.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BcujOfpn.js 29.6 kB 29.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BI09_t23.js 29.4 kB 29.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BKamuseh.js 25.8 kB 25.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BlTun9tZ.js 26.4 kB 26.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CZ62uO3e.js 30.2 kB 30.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DaK-NByz.js 35.2 kB 35.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DaS3cSXp.js 39.4 kB 39.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DWbMuaAa.js 32 kB 32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-S7pA60Hj.js 30.4 kB 30.4 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-D3luOwL2.js (removed) 3.54 kB 🟢 -3.54 kB 🟢 -1.24 kB 🟢 -1.07 kB
assets/auth-w-CPdtOq.js (new) 3.54 kB 🔴 +3.54 kB 🔴 +1.24 kB 🔴 +1.07 kB
assets/firebaseAuthStore-C9ug-EtH.js (new) 217 B 🔴 +217 B 🔴 +138 B 🔴 +121 B
assets/firebaseAuthStore-vCBMphFP.js (removed) 217 B 🟢 -217 B 🟢 -138 B 🟢 -121 B
assets/auth-BPMO3atV.js (removed) 178 B 🟢 -178 B 🟢 -142 B 🟢 -143 B
assets/auth-CiLQrjiq.js (new) 178 B 🔴 +178 B 🔴 +142 B 🔴 +131 B

Status: 3 added / 3 removed

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-CIo1o0YT.js (new) 2.71 kB 🔴 +2.71 kB 🔴 +1.29 kB 🔴 +1.14 kB
assets/useSubscriptionDialog-nhvd3Qi8.js (removed) 2.71 kB 🟢 -2.71 kB 🟢 -1.28 kB 🟢 -1.15 kB
assets/useSubscriptionDialog-1nPIsiTW.js (removed) 179 B 🟢 -179 B 🟢 -110 B 🟢 -101 B
assets/useSubscriptionDialog-Ct-kaeFS.js (new) 179 B 🔴 +179 B 🔴 +110 B 🔴 +95 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-B7AzI4p_.js (removed) 9.52 kB 🟢 -9.52 kB 🟢 -2.69 kB 🟢 -2.42 kB
assets/ComfyQueueButton-CTl_5FzX.js (new) 9.52 kB 🔴 +9.52 kB 🔴 +2.68 kB 🔴 +2.41 kB
assets/SubscribeButton-CgElFdpH.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.57 kB 🟢 -1.39 kB
assets/SubscribeButton-Cn7kf1F-.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.57 kB 🔴 +1.39 kB
assets/cloudFeedbackTopbarButton-BFQoPxBi.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -675 B 🟢 -573 B
assets/cloudFeedbackTopbarButton-DNBh__Dl.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +674 B 🔴 +570 B
assets/ComfyQueueButton-BwV5Ycsr.js (new) 181 B 🔴 +181 B 🔴 +118 B 🔴 +111 B
assets/ComfyQueueButton-DhH2bjjk.js (removed) 181 B 🟢 -181 B 🟢 -118 B 🟢 -124 B
assets/Button-DbRyW27H.js 3.82 kB 3.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudBadge-DdpkQvvD.js 1.85 kB 1.85 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-BFw4kSAY.js 8.36 kB 8.36 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-D80lITos.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-CVau1vM3.js 2.41 kB 2.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 4 added / 4 removed

Data & Services — 2.72 MB (baseline 2.71 MB) • 🔴 +9.19 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-BnwkNlJU.js (new) 2.02 MB 🔴 +2.02 MB 🔴 +427 kB 🔴 +326 kB
assets/dialogService-C0jZxvrW.js (removed) 2.01 MB 🟢 -2.01 MB 🟢 -425 kB 🟢 -324 kB
assets/api-D2tys8TC.js (new) 675 kB 🔴 +675 kB 🔴 +149 kB 🔴 +119 kB
assets/api-0_xJMVsr.js (removed) 675 kB 🟢 -675 kB 🟢 -149 kB 🟢 -119 kB
assets/releaseStore-DI-nLc1U.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.4 kB 🔴 +2.12 kB
assets/releaseStore-Msq5KIWQ.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.4 kB 🟢 -2.12 kB
assets/keybindingService-DHzYM5eR.js (new) 6.74 kB 🔴 +6.74 kB 🔴 +1.75 kB 🔴 +1.53 kB
assets/keybindingService-DyZNHzol.js (removed) 6.74 kB 🟢 -6.74 kB 🟢 -1.75 kB 🟢 -1.53 kB
assets/bootstrapStore-CdZwEdCw.js (new) 2.69 kB 🔴 +2.69 kB 🔴 +1.03 kB 🔴 +965 B
assets/bootstrapStore-CT9MfHnC.js (removed) 2.69 kB 🟢 -2.69 kB 🟢 -1.03 kB 🟢 -967 B
assets/userStore-BaDfQrih.js (new) 2.16 kB 🔴 +2.16 kB 🔴 +810 B 🔴 +723 B
assets/userStore-Bbs7yE1D.js (removed) 2.16 kB 🟢 -2.16 kB 🟢 -811 B 🟢 -724 B
assets/audioService-C4ihUw-I.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -929 B 🟢 -808 B
assets/audioService-Dr3CH2nj.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +929 B 🔴 +808 B
assets/releaseStore-CU6pHwJ7.js (new) 140 B 🔴 +140 B 🔴 +106 B 🔴 +107 B
assets/releaseStore-RM6-Y5p0.js (removed) 140 B 🟢 -140 B 🟢 -106 B 🟢 -110 B
assets/serverConfigStore-DOoqLe5c.js 2.64 kB 2.64 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useErrorHandling-BJ4-7oLQ.js (removed) 5.21 kB 🟢 -5.21 kB 🟢 -1.53 kB 🟢 -1.34 kB
assets/useErrorHandling-D83iQRB8.js (new) 5.21 kB 🔴 +5.21 kB 🔴 +1.53 kB 🔴 +1.34 kB
assets/useWorkspaceUI-B2-_Yu8k.js (new) 3.42 kB 🔴 +3.42 kB 🔴 +975 B 🔴 +840 B
assets/useWorkspaceUI-u-o6kaJ8.js (removed) 3.42 kB 🟢 -3.42 kB 🟢 -974 B 🟢 -843 B
assets/useSubscriptionActions-5UaLJ7kT.js (new) 2.22 kB 🔴 +2.22 kB 🔴 +867 B 🔴 +764 B
assets/useSubscriptionActions-C_-bV1aq.js (removed) 2.22 kB 🟢 -2.22 kB 🟢 -867 B 🟢 -763 B
assets/subscriptionCheckoutUtil-CDQ8THBd.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -871 B 🟢 -771 B
assets/subscriptionCheckoutUtil-DQGaT47a.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +868 B 🔴 +762 B
assets/useSubscriptionCredits-Chj6K3LE.js (removed) 1.39 kB 🟢 -1.39 kB 🟢 -600 B 🟢 -529 B
assets/useSubscriptionCredits-Cx_DHVY0.js (new) 1.39 kB 🔴 +1.39 kB 🔴 +598 B 🔴 +530 B
assets/audioUtils-C8pEGjiZ.js (new) 970 B 🔴 +970 B 🔴 +548 B 🔴 +480 B
assets/audioUtils-CJEpTYmi.js (removed) 970 B 🟢 -970 B 🟢 -547 B 🟢 -459 B
assets/useCurrentUser-_XKMqbeH.js (removed) 145 B 🟢 -145 B 🟢 -114 B 🟢 -104 B
assets/useCurrentUser-BX0OUcoB.js (new) 145 B 🔴 +145 B 🔴 +114 B 🔴 +100 B
assets/_plugin-vue_export-helper-DuK_Fly3.js 467 B 467 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-DfMUHmsF.js 7.2 kB 7.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-DM9z_tTX.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/tailwindUtil-BWBAZ7f9.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-DHGfk3hn.js 408 kB 408 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-B3TsI6ya.js 4.1 MB 4.1 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-4Jj8eU28.js 3.04 MB 3.04 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-aCG649nF.js 263 kB 263 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-CERwhPwK.js 1.83 MB 1.83 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BxrEVL6s.js 650 kB 650 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-Dwii0E-t.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-IX6P8SWv.js 398 kB 398 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 7.1 MB (baseline 7.1 MB) • 🟢 -198 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-Bpml2FxC.js (removed) 180 kB 🟢 -180 kB 🟢 -43.3 kB 🟢 -36.2 kB
assets/core-CnrsczZJ.js (new) 180 kB 🔴 +180 kB 🔴 +43.3 kB 🔴 +36.2 kB
assets/WidgetSelect-CAtMnQpb.js (removed) 52.2 kB 🟢 -52.2 kB 🟢 -11.5 kB 🟢 -10 kB
assets/WidgetSelect-Dl54XW4U.js (new) 52.2 kB 🔴 +52.2 kB 🔴 +11.5 kB 🔴 +10 kB
assets/Load3DControls-B0Pm0c8L.js (new) 35.9 kB 🔴 +35.9 kB 🔴 +5.87 kB 🔴 +5.08 kB
assets/Load3DControls-visAYFcK.js (removed) 35.9 kB 🟢 -35.9 kB 🟢 -5.87 kB 🟢 -5.09 kB
assets/SubscriptionRequiredDialogContent-CVDKv41m.js (new) 28.7 kB 🔴 +28.7 kB 🔴 +6.79 kB 🔴 +5.92 kB
assets/SubscriptionRequiredDialogContent-NC2Wm-qi.js (removed) 28.7 kB 🟢 -28.7 kB 🟢 -6.79 kB 🟢 -5.92 kB
assets/CurrentUserPopoverWorkspace-IK21Y1TX.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -4.99 kB 🟢 -4.42 kB
assets/CurrentUserPopoverWorkspace-M_FzN4XQ.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +4.99 kB 🔴 +4.43 kB
assets/Load3D-DM6Rqpu-.js (removed) 19.2 kB 🟢 -19.2 kB 🟢 -4.38 kB 🟢 -3.84 kB
assets/Load3D-TZOG2PSe.js (new) 19.2 kB 🔴 +19.2 kB 🔴 +4.38 kB 🔴 +3.85 kB
assets/WidgetInputNumber-C1zzD6EW.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.53 kB 🟢 -4.03 kB
assets/WidgetInputNumber-DWujZRE1.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.53 kB 🔴 +4.03 kB
assets/WidgetRecordAudio-BSi4vYaL.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.97 kB 🔴 +4.45 kB
assets/WidgetRecordAudio-CNAWyAke.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.97 kB 🟢 -4.44 kB
assets/SubscriptionPanelContentWorkspace-D_0MeVoR.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.47 kB 🟢 -3.9 kB
assets/SubscriptionPanelContentWorkspace-D7zH30IN.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.47 kB 🔴 +3.9 kB
assets/WidgetImageCrop-B8vN0f5F.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -4.14 kB 🟢 -3.63 kB
assets/WidgetImageCrop-ZZvtbsEk.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +4.14 kB 🔴 +3.62 kB
assets/PanelTemplate-2JgpsoeK.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -5.45 kB 🟢 -4.8 kB
assets/PanelTemplate-BeJA6Fi-.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +5.45 kB 🔴 +4.8 kB
assets/AudioPreviewPlayer-2DcfxmZo.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.97 kB 🔴 +2.65 kB
assets/AudioPreviewPlayer-Brfeorqe.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.65 kB
assets/InviteMemberDialogContent-CKS29RXr.js (new) 8.36 kB 🔴 +8.36 kB 🔴 +2.5 kB 🔴 +2.17 kB
assets/InviteMemberDialogContent-Cns2g9Rm.js (removed) 8.36 kB 🟢 -8.36 kB 🟢 -2.5 kB 🟢 -2.17 kB
assets/WidgetWithControl-BhNk_cqm.js (removed) 8.07 kB 🟢 -8.07 kB 🟢 -2.68 kB 🟢 -2.41 kB
assets/WidgetWithControl-DUwK5Drt.js (new) 8.07 kB 🔴 +8.07 kB 🔴 +2.68 kB 🔴 +2.41 kB
assets/CreateWorkspaceDialogContent-Bg3B71vi.js (removed) 5.93 kB 🟢 -5.93 kB 🟢 -1.92 kB 🟢 -1.68 kB
assets/CreateWorkspaceDialogContent-DSGc7lVE.js (new) 5.93 kB 🔴 +5.93 kB 🔴 +1.92 kB 🔴 +1.68 kB
assets/EditWorkspaceDialogContent-BUzR5x5W.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.88 kB 🟢 -1.65 kB
assets/EditWorkspaceDialogContent-CrwvdQK8.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.88 kB 🔴 +1.64 kB
assets/ValueControlPopover-D7RxdN8A.js (new) 5.17 kB 🔴 +5.17 kB 🔴 +1.69 kB 🔴 +1.5 kB
assets/ValueControlPopover-DdL0wMpV.js (removed) 5.17 kB 🟢 -5.17 kB 🟢 -1.69 kB 🟢 -1.5 kB
assets/DeleteWorkspaceDialogContent-Do8LHd_w.js (removed) 4.59 kB 🟢 -4.59 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/DeleteWorkspaceDialogContent-DpgHJaiL.js (new) 4.59 kB 🔴 +4.59 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/LeaveWorkspaceDialogContent-C5ayjfdf.js (removed) 4.41 kB 🟢 -4.41 kB 🟢 -1.5 kB 🟢 -1.31 kB
assets/LeaveWorkspaceDialogContent-DAMpXdy9.js (new) 4.41 kB 🔴 +4.41 kB 🔴 +1.5 kB 🔴 +1.31 kB
assets/RemoveMemberDialogContent-B7nFLQyc.js (new) 4.38 kB 🔴 +4.38 kB 🔴 +1.45 kB 🔴 +1.27 kB
assets/RemoveMemberDialogContent-jR-phpTt.js (removed) 4.38 kB 🟢 -4.38 kB 🟢 -1.45 kB 🟢 -1.27 kB
assets/RevokeInviteDialogContent-B_NRigYz.js (removed) 4.29 kB 🟢 -4.29 kB 🟢 -1.47 kB 🟢 -1.29 kB
assets/RevokeInviteDialogContent-tLE9xWIO.js (new) 4.29 kB 🔴 +4.29 kB 🔴 +1.47 kB 🔴 +1.29 kB
assets/GlobalToast-Bfq1l41B.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.1 kB 🟢 -941 B
assets/GlobalToast-BxvpGIgB.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.1 kB 🔴 +945 B
assets/SubscribeToRun--fi5rVxw.js (new) 2.96 kB 🔴 +2.96 kB 🔴 +1.15 kB 🔴 +1.01 kB
assets/SubscribeToRun-BNX1ZG14.js (removed) 2.96 kB 🟢 -2.96 kB 🟢 -1.15 kB 🟢 -1.01 kB
assets/cloudSessionCookie-CKA3TjVz.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +929 B 🔴 +799 B
assets/cloudSessionCookie-DwHfnsk8.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -930 B 🟢 -800 B
assets/BaseViewTemplate-B2Ree9mF.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -941 B
assets/BaseViewTemplate-BrTfSP4B.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +936 B
assets/CloudRunButtonWrapper-Bs2DX50S.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +644 B 🔴 +562 B
assets/CloudRunButtonWrapper-CPvudAWU.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -646 B 🟢 -597 B
assets/cloudBadges-CA0gE6bJ.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +536 B 🔴 +478 B
assets/cloudBadges-CLORO5-B.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -537 B 🟢 -498 B
assets/graphHasMissingNodes--5sI-TK8.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +461 B 🔴 +410 B
assets/graphHasMissingNodes-BaIjF0fM.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -461 B 🟢 -419 B
assets/cloudSubscription-CdjFYj--.js (removed) 976 B 🟢 -976 B 🟢 -464 B 🟢 -404 B
assets/cloudSubscription-Ckkack06.js (new) 976 B 🔴 +976 B 🔴 +464 B 🔴 +401 B
assets/nightlyBadges-CsYTRK23.js (new) 595 B 🔴 +595 B 🔴 +355 B 🔴 +307 B
assets/nightlyBadges-wXTaIV9p.js (removed) 595 B 🟢 -595 B 🟢 -356 B 🟢 -309 B
assets/SubscriptionPanelContentWorkspace-CFHGZeNa.js (removed) 266 B 🟢 -266 B 🟢 -136 B 🟢 -125 B
assets/SubscriptionPanelContentWorkspace-DnM2gP5n.js (new) 266 B 🔴 +266 B 🔴 +136 B 🔴 +127 B
assets/WidgetInputNumber-Cb7219Ev.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +111 B
assets/WidgetInputNumber-DxdSVo7u.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -110 B
assets/WidgetLegacy-Bimhxf4p.js (new) 164 B 🔴 +164 B 🔴 +125 B 🔴 +109 B
assets/WidgetLegacy-DyaDuMR_.js (removed) 164 B 🟢 -164 B 🟢 -125 B 🟢 -113 B
assets/Load3D-CgfyUqU3.js (removed) 131 B 🟢 -131 B 🟢 -107 B 🟢 -113 B
assets/Load3D-D3DmBkiy.js (new) 131 B 🔴 +131 B 🔴 +107 B 🔴 +117 B
assets/auto-DWs2ctGL.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BEw5ErI4.js 18.5 kB 18.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BGeHkplA.js 17.9 kB 17.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV0l36Iz.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C_Y3D6Cn.js 17.8 kB 17.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C6piRza5.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Cf8Zq1td.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CiziP3Xs.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-D1595tOr.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DXauvccL.js 20.6 kB 20.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-P5QCEfZc.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-WbYP_D61.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-DKbrI9yU.js 500 kB 500 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-Do-i5KgH.js 188 B 188 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LazyImage-ooHoQZNd.js 14.1 kB 14.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BHtk4Fg_.js 174 kB 174 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BMSlgLcp.js 155 kB 155 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BQCWi9e4.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CJicmTR7.js 113 kB 113 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CNOkBy-u.js 126 kB 126 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CySb1R5_.js 151 kB 151 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D0g10ZKf.js 131 kB 131 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DMUPIFMF.js 133 kB 133 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DpsGU4si.js 126 kB 126 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Dz6IPJXM.js 144 kB 144 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-lrEzMywH.js 128 kB 128 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-DUmUhXD6.js 2.38 kB 2.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-CD66_Mw_.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-D5feRGXX.js 2.34 kB 2.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-De3MzVmp.js 2.82 kB 2.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/mixpanel.module-DLR992B1.js 143 B 143 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-A7pvB7zM.js 370 kB 370 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BIVjUijC.js 345 kB 345 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Chkn0HaI.js 343 kB 343 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CK_6GHao.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CToVAwnT.js 373 kB 373 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DDabdWgx.js 417 kB 417 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DgvJyE3d.js 386 kB 386 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DjxaeFt_.js 416 kB 416 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DslnWEGg.js 377 kB 377 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-EPAM3kwk.js 373 kB 373 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-NrulhNyH.js 366 kB 366 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-CmezY7As.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-C87scEAV.js 4.21 kB 4.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-BJiJuR5i.js 518 B 518 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-CUtab2CB.js 4.71 kB 4.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-D79nBMxa.js 186 B 186 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-CiXfBVBH.js 2.79 kB 2.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-BEfQQjV6.js 3.71 kB 3.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-jm_gz6R2.js 4.57 kB 4.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DnZ84Utk.js 3.79 kB 3.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-WCHoBOIV.js 2.58 kB 2.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-BHrU_4qY.js 2.7 kB 2.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-3U-WuCE_.js 3.49 kB 3.49 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-ERx8czR8.js 1.31 kB 1.31 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-BXRA46js.js 3.87 kB 3.87 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-DYS14Ar3.js 3.26 kB 3.26 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetTypes-KPj-zM0O.js 573 B 573 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 34 added / 34 removed

@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

Comment on lines 205 to +209

// Try asset.id first (OSS), then fall back to metadata (Cloud)
const metadata = getOutputAssetMetadata(targetAsset.user_metadata)
const promptId = targetAsset.id || metadata?.promptId
const promptId =
metadata?.promptId ||
(getAssetType(targetAsset) === 'output' ? targetAsset.id : undefined)
Copy link
Member Author

Choose a reason for hiding this comment

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

metadata.promptId is set by the frontend mappers here, so the comment was outdated. In cloud, asset.id is the asset UUID (not the job/prompt ID), so preferring metadata.promptId is the correct behavior.

@benceruleanlu benceruleanlu requested a review from a team as a code owner January 28, 2026 20:20
@coderabbitai coderabbitai bot requested a review from Myestery January 30, 2026 00:01
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

🤖 Fix all issues with AI agents
In `@src/components/sidebar/tabs/AssetsSidebarListView.vue`:
- Around line 101-102: The template calls the async method toggleStack without
handling its promise; update the prop/type and callsite to avoid floating
promises by changing the prop type for toggleStack from () => void to () =>
Promise<void> (or PropType<(asset: Asset) => Promise<void>>) and then explicitly
discard the returned promise in the template call sites (e.g. replace
`@stack-toggle`="toggleStack(item.asset)" with `@stack-toggle`="void
toggleStack(item.asset)"); apply the same change to the other occurrence around
lines 158-159 so all async invocations are typed as Promise<void> and their
promises are explicitly handled/discarded.

In `@src/components/sidebar/tabs/AssetsSidebarTab.vue`:
- Around line 489-492: The helper currently defined as a const arrow function
`handleAssetSelect` should be converted to a named function declaration to match
project conventions and the nearby `handleAssetContextMenu`; rewrite it as
`function handleAssetSelect(asset: AssetItem, assets?: AssetItem[]) { ... }`
preserving the logic that sets `const assetList = assets ??
visibleAssets.value`, finds `index` via `assetList.findIndex(a => a.id ===
asset.id)`, and calls `handleAssetClick(asset, index, assetList)` so references
to `handleAssetClick` and `visibleAssets` remain unchanged.

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/assets/composables/useOutputStacks.ts`:
- Around line 65-103: The toggleStack function can leave loadingStackPromptIds
stuck if resolveStackChildren throws; wrap the await resolveStackChildren(asset)
in a try/finally so the block that removes promptId from loadingStackPromptIds
always runs (move the creation of nextLoading before the try, set
loadingStackPromptIds = nextLoading, await resolveStackChildren inside try, and
in finally remove promptId from loadingStackPromptIds), then proceed to set
stackChildrenByPromptId and expandedStackPromptIds only after a successful
children result; reference toggleStack, resolveStackChildren,
loadingStackPromptIds, stackChildrenByPromptId, expandedStackPromptIds, and
promptId when making the change.

Comment on lines +65 to +103
async function toggleStack(asset: AssetItem) {
const promptId = getStackPromptId(asset)
if (!promptId) return

if (expandedStackPromptIds.value.has(promptId)) {
const next = new Set(expandedStackPromptIds.value)
next.delete(promptId)
expandedStackPromptIds.value = next
return
}

if (!stackChildrenByPromptId.value[promptId]?.length) {
if (loadingStackPromptIds.value.has(promptId)) {
return
}
const nextLoading = new Set(loadingStackPromptIds.value)
nextLoading.add(promptId)
loadingStackPromptIds.value = nextLoading

const children = await resolveStackChildren(asset)

const afterLoading = new Set(loadingStackPromptIds.value)
afterLoading.delete(promptId)
loadingStackPromptIds.value = afterLoading

if (!children.length) {
return
}

stackChildrenByPromptId.value = {
...stackChildrenByPromptId.value,
[promptId]: children
}
}

const nextExpanded = new Set(expandedStackPromptIds.value)
nextExpanded.add(promptId)
expandedStackPromptIds.value = nextExpanded
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Ensure loading flags clear even on unexpected errors.
If resolveStackChildren throws unexpectedly, the loading set can be left stuck. A try/finally around the await keeps the guard consistent.

♻️ Suggested defensive update
-      const children = await resolveStackChildren(asset)
-
-      const afterLoading = new Set(loadingStackPromptIds.value)
-      afterLoading.delete(promptId)
-      loadingStackPromptIds.value = afterLoading
+      let children: AssetItem[] = []
+      try {
+        children = await resolveStackChildren(asset)
+      } finally {
+        const afterLoading = new Set(loadingStackPromptIds.value)
+        afterLoading.delete(promptId)
+        loadingStackPromptIds.value = afterLoading
+      }
🤖 Prompt for AI Agents
In `@src/platform/assets/composables/useOutputStacks.ts` around lines 65 - 103,
The toggleStack function can leave loadingStackPromptIds stuck if
resolveStackChildren throws; wrap the await resolveStackChildren(asset) in a
try/finally so the block that removes promptId from loadingStackPromptIds always
runs (move the creation of nextLoading before the try, set loadingStackPromptIds
= nextLoading, await resolveStackChildren inside try, and in finally remove
promptId from loadingStackPromptIds), then proceed to set
stackChildrenByPromptId and expandedStackPromptIds only after a successful
children result; reference toggleStack, resolveStackChildren,
loadingStackPromptIds, stackChildrenByPromptId, expandedStackPromptIds, and
promptId when making the change.

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

🤖 Fix all issues with AI agents
In `@src/platform/assets/composables/useOutputStacks.ts`:
- Around line 68-101: The toggleStack flow refetches when children are empty
because you only store non-empty arrays; change it to cache empty results and
check for key existence instead of length. Specifically, in toggleStack use a
presence check on stackChildrenByPromptId.value (e.g.,
Object.prototype.hasOwnProperty.call or promptId in
stackChildrenByPromptId.value) to skip fetching, and after awaiting
resolveStackChildren(asset) always assign stackChildrenByPromptId.value = {
...stackChildrenByPromptId.value, [promptId]: children } (even when
children.length === 0); keep the existing loadingStackPromptIds add/remove logic
around the fetch to prevent concurrent requests. Ensure you still early-return
if loadingStackPromptIds.value.has(promptId) and preserve expandedStackPromptIds
behavior.

In `@src/platform/assets/utils/outputAssetUtil.ts`:
- Around line 40-49: The getOutputKey function treats nodeId with a falsy check
which drops valid numeric ids like 0; update the guard to only treat nodeId as
missing when null or undefined (use nodeId == null) while keeping the existing
falsy checks for subfolder and filename, i.e., change the if condition in
getOutputKey to check nodeId == null || !subfolder || !filename so numeric 0 is
accepted when building the `${nodeId}-${subfolder}-${filename}` key.

Comment on lines +68 to +101
async function toggleStack(asset: AssetItem) {
const promptId = getStackPromptId(asset)
if (!promptId) return

if (expandedStackPromptIds.value.has(promptId)) {
const next = new Set(expandedStackPromptIds.value)
next.delete(promptId)
expandedStackPromptIds.value = next
return
}

if (!stackChildrenByPromptId.value[promptId]?.length) {
if (loadingStackPromptIds.value.has(promptId)) {
return
}
const nextLoading = new Set(loadingStackPromptIds.value)
nextLoading.add(promptId)
loadingStackPromptIds.value = nextLoading

const children = await resolveStackChildren(asset)

const afterLoading = new Set(loadingStackPromptIds.value)
afterLoading.delete(promptId)
loadingStackPromptIds.value = afterLoading

if (!children.length) {
return
}

stackChildrenByPromptId.value = {
...stackChildrenByPromptId.value,
[promptId]: children
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Cache “no-children” results to avoid repeated fetches.
Currently, a stack with zero children refetches on every expand attempt because empty results aren’t stored. Cache the empty array and switch the guard to “key exists” rather than “length > 0”.

♻️ Suggested update
-    if (!stackChildrenByPromptId.value[promptId]?.length) {
+    if (!Object.hasOwn(stackChildrenByPromptId.value, promptId)) {
       if (loadingStackPromptIds.value.has(promptId)) {
         return
       }
       const nextLoading = new Set(loadingStackPromptIds.value)
       nextLoading.add(promptId)
       loadingStackPromptIds.value = nextLoading

       const children = await resolveStackChildren(asset)

       const afterLoading = new Set(loadingStackPromptIds.value)
       afterLoading.delete(promptId)
       loadingStackPromptIds.value = afterLoading

-      if (!children.length) {
-        return
-      }
-
       stackChildrenByPromptId.value = {
         ...stackChildrenByPromptId.value,
         [promptId]: children
       }
+
+      if (!children.length) {
+        return
+      }
     }
🤖 Prompt for AI Agents
In `@src/platform/assets/composables/useOutputStacks.ts` around lines 68 - 101,
The toggleStack flow refetches when children are empty because you only store
non-empty arrays; change it to cache empty results and check for key existence
instead of length. Specifically, in toggleStack use a presence check on
stackChildrenByPromptId.value (e.g., Object.prototype.hasOwnProperty.call or
promptId in stackChildrenByPromptId.value) to skip fetching, and after awaiting
resolveStackChildren(asset) always assign stackChildrenByPromptId.value = {
...stackChildrenByPromptId.value, [promptId]: children } (even when
children.length === 0); keep the existing loadingStackPromptIds add/remove logic
around the fetch to prevent concurrent requests. Ensure you still early-return
if loadingStackPromptIds.value.has(promptId) and preserve expandedStackPromptIds
behavior.

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

Caution

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

⚠️ Outside diff range comments (1)
src/components/sidebar/tabs/AssetsSidebarTab.vue (1)

428-439: ⚠️ Potential issue | 🟠 Major

Reset gallery index when the active asset disappears.

If a stack collapses (or filtering removes the current asset), currentGalleryAssetId won’t be found and the gallery can show a different item at the old index. Consider resetting the index when the asset is missing to keep the gallery aligned.

🛠️ Suggested fix
    if (newIndex !== -1) {
      galleryActiveIndex.value = newIndex
+   } else {
+     galleryActiveIndex.value = -1
    }
🤖 Fix all issues with AI agents
In `@src/platform/assets/composables/useAssetSelection.test.ts`:
- Around line 47-65: The test's AssetItem mocks used in
describe('reconcileSelection') are missing required fields; update the inline
asset objects (used with useAssetSelection(), useAssetSelectionStore(), and
selection.reconcileSelection([...])) to either include the required properties
(size, created_at, preview_url) or explicitly cast them as partials using the
pattern as Partial<AssetItem> as AssetItem so the test types remain correct and
resilient to schema changes.

Comment on lines +47 to +65
describe('reconcileSelection', () => {
it('prunes selection to visible assets', () => {
const selection = useAssetSelection()
const store = useAssetSelectionStore()
const assets: AssetItem[] = [
{ id: 'a', name: 'a.png', tags: [] },
{ id: 'b', name: 'b.png', tags: [] }
]

store.setSelection(['a', 'b'])
store.setLastSelectedIndex(1)
store.setLastSelectedAssetId('b')

selection.reconcileSelection([assets[1]])

expect(Array.from(store.selectedAssetIds)).toEqual(['b'])
expect(store.lastSelectedIndex).toBe(0)
expect(store.lastSelectedAssetId).toBe('b')
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using more complete mock objects or explicit partial casting.

The inline AssetItem objects are missing required fields (size, created_at, preview_url) defined in the schema. While this works because TypeScript doesn't enforce runtime shapes, using as Partial<AssetItem> as AssetItem makes the incomplete mock explicit and prevents type drift if the schema gains stricter validation.

♻️ Example for explicit partial casting
       const assets: AssetItem[] = [
-        { id: 'a', name: 'a.png', tags: [] },
-        { id: 'b', name: 'b.png', tags: [] }
+        { id: 'a', name: 'a.png', tags: [] } as Partial<AssetItem> as AssetItem,
+        { id: 'b', name: 'b.png', tags: [] } as Partial<AssetItem> as AssetItem
       ]

Based on learnings: "In test files, when you create mock objects that partially implement an interface, prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe('reconcileSelection', () => {
it('prunes selection to visible assets', () => {
const selection = useAssetSelection()
const store = useAssetSelectionStore()
const assets: AssetItem[] = [
{ id: 'a', name: 'a.png', tags: [] },
{ id: 'b', name: 'b.png', tags: [] }
]
store.setSelection(['a', 'b'])
store.setLastSelectedIndex(1)
store.setLastSelectedAssetId('b')
selection.reconcileSelection([assets[1]])
expect(Array.from(store.selectedAssetIds)).toEqual(['b'])
expect(store.lastSelectedIndex).toBe(0)
expect(store.lastSelectedAssetId).toBe('b')
})
describe('reconcileSelection', () => {
it('prunes selection to visible assets', () => {
const selection = useAssetSelection()
const store = useAssetSelectionStore()
const assets: AssetItem[] = [
{ id: 'a', name: 'a.png', tags: [] } as Partial<AssetItem> as AssetItem,
{ id: 'b', name: 'b.png', tags: [] } as Partial<AssetItem> as AssetItem
]
store.setSelection(['a', 'b'])
store.setLastSelectedIndex(1)
store.setLastSelectedAssetId('b')
selection.reconcileSelection([assets[1]])
expect(Array.from(store.selectedAssetIds)).toEqual(['b'])
expect(store.lastSelectedIndex).toBe(0)
expect(store.lastSelectedAssetId).toBe('b')
})
🤖 Prompt for AI Agents
In `@src/platform/assets/composables/useAssetSelection.test.ts` around lines 47 -
65, The test's AssetItem mocks used in describe('reconcileSelection') are
missing required fields; update the inline asset objects (used with
useAssetSelection(), useAssetSelectionStore(), and
selection.reconcileSelection([...])) to either include the required properties
(size, created_at, preview_url) or explicitly cast them as partials using the
pattern as Partial<AssetItem> as AssetItem so the test types remain correct and
resilient to schema changes.

@benceruleanlu benceruleanlu removed their assignment Jan 30, 2026
@Myestery
Copy link
Contributor

Myestery commented Feb 2, 2026

👀

}

type OutputKeyParts = {
nodeId?: string | number | null
Copy link
Contributor

Choose a reason for hiding this comment

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

we have a NodeId type we could use here

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I'll add that in a followup PR

Copy link
Member Author

Choose a reason for hiding this comment

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

items.push({
id: `${promptId}-${outputKey}`,
name: output.filename,
size: 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why the size is hardcoded to 0 here

Copy link
Member Author

Choose a reason for hiding this comment

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

It’s hardcoded because the output objects we map (ResultItemImpl) simply don’t carry file size, and the history/job APIs don’t provide it yet. AssetItem.size is optional with a “TBD…provided by history API in the future” note, so we’re effectively using 0 as “unknown” and to keep the shape consistent. Also, the UI only shows size if it’s truthy, so 0 suppresses display.

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.

4 participants