Skip to content

feat: implement progressive pagination for Asset Browser model assets#8212

Merged
DrJKL merged 18 commits intomainfrom
drjkl/progressive-pagination
Jan 22, 2026
Merged

feat: implement progressive pagination for Asset Browser model assets#8212
DrJKL merged 18 commits intomainfrom
drjkl/progressive-pagination

Conversation

@DrJKL
Copy link
Contributor

@DrJKL DrJKL commented Jan 21, 2026

Summary

Implements progressive pagination for model assets - returns the first batch immediately while loading remaining batches in the background.

Changes

Store (assetsStore.ts)

  • Adds ModelPaginationState tracking (assets Map, offset, hasMore, loading, error)
  • updateModelsForKey() returns first batch, then calls loadRemainingBatches() to fetch the rest
  • Accessor functions getAssets(key), isModelLoading(key) replace direct Map access

API (assetService.ts)

  • Adds PaginationOptions interface ({ limit?, offset? })

Components

  • AssetBrowserModal.vue uses new accessor API

Tests

  • Updated mocks for new accessor pattern

┆Issue is synchronized with this Notion page by Unito

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

Replaces per-node-type public maps with keyed accessors (getAssets, isModelLoading, getError, hasMore), implements per-key progressive pagination and batched loading in the assets store, updates assetService to use PaginationOptions, and migrates components and tests to the new keyed store API. (50 words)

Changes

Cohort / File(s) Summary
Core Store Refactor
src/stores/assetsStore.ts
Removed public maps; added per-key pagination state, accessors (getAssets, isLoading/isModelLoading, getError, hasMore), pending-request deduplication, progressive multi-batch loading, offset/hasMore tracking, and updated public actions (updateModelsForNodeType, updateModelsForTag).
Asset Service & Types
src/platform/assets/services/assetService.ts
Added PaginationOptions and AssetRequestOptions; centralized request construction in handleAssetRequest using options; updated getAssetsForNodeType and getAssetsByTag to accept pagination options and call the new handler.
Component update
src/platform/assets/components/AssetBrowserModal.vue
Replaced direct map lookups with assetStore.getAssets(cacheKey.value) and assetStore.isModelLoading(cacheKey.value); changed refreshAssets to Promise<void> and await store update actions.
Composables
src/renderer/extensions/vueNodes/widgets/composables/useAssetWidgetData.ts
Switched to accessors assetsStore.getAssets(type), assetsStore.isModelLoading(type), assetsStore.getError(type); adjusted existence checks and reactive watch to use getters.
Tests — mocks & migrations
src/platform/assets/components/AssetBrowserModal.test.ts, src/renderer/extensions/vueNodes/widgets/composables/useAssetWidgetData.test.ts, src/renderer/extensions/vueNodes/widgets/composables/useAssetWidgetData.desktop.test.ts
Updated test mocks to keyed getters (getAssets, isModelLoading, getError); replaced map-based setups/clears with mockAssetsByKey, mockLoadingByKey, mockErrorByKey; adjusted expectations and initializations.
Store tests & Cloud-mode
src/stores/assetsStore.test.ts
Introduced per-test mockable isCloud getter, mocked getAssetsForNodeType for batched responses, added comprehensive cloud-mode tests for batched loading, cache invalidation, concurrency, and reactivity.
Service tests
src/platform/assets/services/assetService.test.ts
Adjusted expected request strings (URL-encoded commas in include_tags and reordered query params) to match new query construction.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as AssetBrowserModal / Consumer
  participant Store as assetsStore
  participant Service as assetService
  participant API as External API

  Browser->>Store: updateModelsForNodeType(key)
  Store->>Service: getAssetsForNodeType(nodeType, {limit, offset})
  Service->>API: HTTP GET /assets?include_tags=...&limit=...&offset=...
  API-->>Service: batch response (items, hasMore)
  Service-->>Store: return batch
  Store->>Store: init/merge per-key state (assets, offset, hasMore)
  alt hasMore
    loop loadRemainingBatches
      Store->>Service: getAssetsForNodeType(nodeType, {limit, offset})
      Service->>API: HTTP GET /assets?limit=...&offset=...
      API-->>Service: next batch
      Service-->>Store: append batch -> update offset/hasMore
    end
  end
  Store-->>Browser: getAssets(key) returns aggregated assets
Loading

Possibly related PRs

Suggested reviewers

  • AustinMroz
  • shinshin86
  • luke-mino-altherr
  • KarryCharon
  • christian-byrne
  • Yorha4D
  • pythongosssss
✨ Finishing touches
  • 📝 Generate docstrings

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

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/22/2026, 01:46:44 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

🎭 Playwright Tests: ⚠️ Passed with flaky tests

Results: 503 passed, 0 failed, 2 flaky, 8 skipped (Total: 513)

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 493 / ❌ 0 / ⚠️ 1 / ⏭️ 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 21, 2026

Bundle Size Report

Summary

  • Raw size: 21.4 MB baseline 21.4 MB — 🔴 +1.95 kB
  • Gzip: 4.44 MB baseline 4.44 MB — 🔴 +646 B
  • Brotli: 3.29 MB baseline 3.29 MB — 🔴 +318 B
  • Bundles: 151 current • 151 baseline • 82 added / 82 removed

Category Glance
Data & Services 🔴 +2.03 kB (3.05 MB) · Other 🟢 -48 B (6.28 MB) · Vendor & Third-Party 🟢 -32 B (10.4 MB) · Graph Workspace ⚪ 0 B (1.02 MB) · Panels & Settings ⚪ 0 B (430 kB) · Views & Navigation ⚪ 0 B (80.7 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-BfImPK3G.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +6.73 kB 🔴 +5.91 kB
assets/index-CXV9y9mn.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -6.72 kB 🟢 -5.89 kB

Status: 1 added / 1 removed

Graph Workspace — 1.02 MB (baseline 1.02 MB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-D6y6gIMc.js (new) 1.02 MB 🔴 +1.02 MB 🔴 +201 kB 🔴 +153 kB
assets/GraphView-DxvvO-Zl.js (removed) 1.02 MB 🟢 -1.02 MB 🟢 -201 kB 🟢 -153 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-pHpSCBy8.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +3.61 kB 🔴 +3.07 kB
assets/CloudSurveyView-W2zYSC2G.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -3.61 kB 🟢 -3.06 kB
assets/CloudLoginView-BD_AumUC.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.09 kB 🟢 -2.72 kB
assets/CloudLoginView-BuyA5hLh.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.09 kB 🔴 +2.72 kB
assets/UserCheckView-BRF75L7j.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.44 kB 🟢 -2.14 kB
assets/UserCheckView-jjWR7HR3.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.44 kB 🔴 +2.13 kB
assets/CloudLayoutView-BjK0Yt4J.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.25 kB 🔴 +1.96 kB
assets/CloudLayoutView-Dyo94A2h.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.25 kB 🟢 -1.96 kB
assets/CloudSignupView-Cr2VCVmI.js (new) 8.18 kB 🔴 +8.18 kB 🔴 +2.33 kB 🔴 +2.03 kB
assets/CloudSignupView-DI_1LIgJ.js (removed) 8.18 kB 🟢 -8.18 kB 🟢 -2.33 kB 🟢 -2.02 kB
assets/CloudForgotPasswordView-C_aQHZci.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -1.92 kB 🟢 -1.69 kB
assets/CloudForgotPasswordView-PyFBvfex.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +1.92 kB 🔴 +1.69 kB
assets/UserSelectView-C8S_UX4d.js (removed) 5.28 kB 🟢 -5.28 kB 🟢 -1.76 kB 🟢 -1.57 kB
assets/UserSelectView-fU1BI_ZQ.js (new) 5.28 kB 🔴 +5.28 kB 🔴 +1.76 kB 🔴 +1.57 kB
assets/CloudSubscriptionRedirectView-B3rV26jM.js (removed) 5.27 kB 🟢 -5.27 kB 🟢 -1.73 kB 🟢 -1.53 kB
assets/CloudSubscriptionRedirectView-CMVSR_kC.js (new) 5.27 kB 🔴 +5.27 kB 🔴 +1.73 kB 🔴 +1.53 kB
assets/CloudAuthTimeoutView-BLdWjKXC.js (removed) 5.24 kB 🟢 -5.24 kB 🟢 -1.7 kB 🟢 -1.49 kB
assets/CloudAuthTimeoutView-s9HmEhZj.js (new) 5.24 kB 🔴 +5.24 kB 🔴 +1.7 kB 🔴 +1.48 kB
assets/CloudSorryContactSupportView-41RMhsFA.js (removed) 1.97 kB 🟢 -1.97 kB 🟢 -700 B 🟢 -624 B
assets/CloudSorryContactSupportView-B5mJ1Q98.js (new) 1.97 kB 🔴 +1.97 kB 🔴 +702 B 🔴 +632 B
assets/layout-COGqkVob.js (new) 500 B 🔴 +500 B 🔴 +309 B 🔴 +265 B
assets/layout-DT-CUejm.js (removed) 500 B 🟢 -500 B 🟢 -307 B 🟢 -267 B

Status: 11 added / 11 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-Ba2KKxyW.js (removed) 23.8 kB 🟢 -23.8 kB 🟢 -5.94 kB 🟢 -5.23 kB
assets/LegacyCreditsPanel-DLacsKs5.js (new) 23.8 kB 🔴 +23.8 kB 🔴 +5.94 kB 🔴 +5.23 kB
assets/SubscriptionPanel-CGSYp5OC.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -4.99 kB 🟢 -4.38 kB
assets/SubscriptionPanel-DD_z5-oO.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +4.99 kB 🔴 +4.38 kB
assets/KeybindingPanel-CC0Wfuno.js (new) 14.2 kB 🔴 +14.2 kB 🔴 +3.73 kB 🔴 +3.31 kB
assets/KeybindingPanel-Nqi9eICr.js (removed) 14.2 kB 🟢 -14.2 kB 🟢 -3.73 kB 🟢 -3.31 kB
assets/AboutPanel-D06JYWTJ.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.68 kB 🟢 -2.43 kB
assets/AboutPanel-DZGa8iY4.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.68 kB 🔴 +2.43 kB
assets/ExtensionPanel-C_huJWoH.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.71 kB 🔴 +2.4 kB
assets/ExtensionPanel-Dfqg7UFg.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.71 kB 🟢 -2.41 kB
assets/ServerConfigPanel-9kKuKbFQ.js (removed) 7.23 kB 🟢 -7.23 kB 🟢 -2.16 kB 🟢 -1.93 kB
assets/ServerConfigPanel-CZsTqxxK.js (new) 7.23 kB 🔴 +7.23 kB 🔴 +2.16 kB 🔴 +1.94 kB
assets/UserPanel-Caow4k_2.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +1.9 kB 🔴 +1.67 kB
assets/UserPanel-VA-IyRZo.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/cloudRemoteConfig-h_We1NbT.js (removed) 1.82 kB 🟢 -1.82 kB 🟢 -769 B 🟢 -664 B
assets/cloudRemoteConfig-PDDpbV9W.js (new) 1.82 kB 🔴 +1.82 kB 🔴 +770 B 🔴 +659 B
assets/remoteConfig-BEkdBLxH.js 1.07 kB 1.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/remoteConfig-D_gf6SLU.js 188 B 188 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-6DVADt2n.js 34.3 kB 34.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B0j03ezr.js 38.3 kB 38.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BHe-AJJN.js 29.6 kB 29.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BT2lfy0S.js 29.5 kB 29.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Cp0lF2Mp.js 31.2 kB 31.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CwdesOpm.js 32.1 kB 32.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-D3SeHgho.js 28.6 kB 28.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-D42m_JEJ.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-FF_vLB0C.js 25.2 kB 25.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-reUMVWRn.js 30.4 kB 30.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-s7kHNBdQ.js 28.9 kB 28.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 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-Bik7mwMu.js (new) 3.54 kB 🔴 +3.54 kB 🔴 +1.23 kB 🔴 +1.06 kB
assets/auth-CQ5VWzPp.js (removed) 3.54 kB 🟢 -3.54 kB 🟢 -1.24 kB 🟢 -1.06 kB
assets/firebaseAuthStore-6DnRBqpJ.js (removed) 217 B 🟢 -217 B 🟢 -137 B 🟢 -125 B
assets/firebaseAuthStore-Cf6WiLcu.js (new) 217 B 🔴 +217 B 🔴 +137 B 🔴 +119 B
assets/auth-DUeDVmm5.js (new) 178 B 🔴 +178 B 🔴 +141 B 🔴 +130 B
assets/auth-DzMrlFU7.js (removed) 178 B 🟢 -178 B 🟢 -142 B 🟢 -130 B

Status: 3 added / 3 removed

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-BUqRVcJI.js (removed) 2.62 kB 🟢 -2.62 kB 🟢 -1.24 kB 🟢 -1.09 kB
assets/useSubscriptionDialog-CBzUmL1V.js (new) 2.62 kB 🔴 +2.62 kB 🔴 +1.24 kB 🔴 +1.09 kB
assets/useSubscriptionDialog-BTo0aX3K.js (removed) 179 B 🟢 -179 B 🟢 -110 B 🟢 -104 B
assets/useSubscriptionDialog-BwfXp0Dz.js (new) 179 B 🔴 +179 B 🔴 +110 B 🔴 +95 B

Status: 2 added / 2 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SubscribeButton-BseNHL_d.js (new) 12.5 kB 🔴 +12.5 kB 🔴 +3 kB 🔴 +2.69 kB
assets/SubscribeButton-DRacDxAb.js (removed) 12.5 kB 🟢 -12.5 kB 🟢 -3.01 kB 🟢 -2.69 kB
assets/ComfyQueueButton-DSfxXkUo.js (new) 9.52 kB 🔴 +9.52 kB 🔴 +2.69 kB 🔴 +2.42 kB
assets/ComfyQueueButton-LBFAk5ga.js (removed) 9.52 kB 🟢 -9.52 kB 🟢 -2.69 kB 🟢 -2.42 kB
assets/Button-BrECiPIE.js (new) 3.75 kB 🔴 +3.75 kB 🔴 +1.37 kB 🔴 +1.21 kB
assets/Button-CKxdX0Yq.js (removed) 3.75 kB 🟢 -3.75 kB 🟢 -1.36 kB 🟢 -1.21 kB
assets/WidgetButton-COs_ZSm_.js (new) 2.41 kB 🔴 +2.41 kB 🔴 +978 B 🔴 +865 B
assets/WidgetButton-ftAKT8u9.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -982 B 🟢 -885 B
assets/CloudBadge-C321mpPS.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -726 B 🟢 -645 B
assets/CloudBadge-DinLPkj3.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +725 B 🔴 +649 B
assets/cloudFeedbackTopbarButton-CkI3N6Yw.js (removed) 866 B 🟢 -866 B 🟢 -526 B 🟢 -439 B
assets/cloudFeedbackTopbarButton-JqVaRrPU.js (new) 866 B 🔴 +866 B 🔴 +525 B 🔴 +440 B
assets/ComfyQueueButton-BGPvY5k6.js (removed) 181 B 🟢 -181 B 🟢 -118 B 🟢 -113 B
assets/ComfyQueueButton-C6yfnw7Z.js (new) 181 B 🔴 +181 B 🔴 +118 B 🔴 +120 B
assets/UserAvatar-Bj7TTp1S.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 removed

Data & Services — 3.05 MB (baseline 3.05 MB) • 🔴 +2.03 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-BkfyBZjE.js (new) 1.88 MB 🔴 +1.88 MB 🔴 +399 kB 🔴 +305 kB
assets/dialogService-Bb7g2d6M.js (removed) 1.88 MB 🟢 -1.88 MB 🟢 -398 kB 🟢 -305 kB
assets/api-Buf3aOHS.js (removed) 1.15 MB 🟢 -1.15 MB 🟢 -240 kB 🟢 -186 kB
assets/api-DUgdJ8Ti.js (new) 1.15 MB 🔴 +1.15 MB 🔴 +240 kB 🔴 +186 kB
assets/releaseStore-CjIQVPjR.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.41 kB 🔴 +2.12 kB
assets/releaseStore-DSMt5eVN.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.41 kB 🟢 -2.13 kB
assets/keybindingService-CH-ms5YW.js (new) 6.78 kB 🔴 +6.78 kB 🔴 +1.74 kB 🔴 +1.51 kB
assets/keybindingService-DpDVsorv.js (removed) 6.78 kB 🟢 -6.78 kB 🟢 -1.75 kB 🟢 -1.51 kB
assets/userStore-CMXNCUOQ.js (new) 2.16 kB 🔴 +2.16 kB 🔴 +813 B 🔴 +728 B
assets/userStore-XWhzecQg.js (removed) 2.16 kB 🟢 -2.16 kB 🟢 -812 B 🟢 -727 B
assets/audioService-BngqG14c.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -929 B 🟢 -823 B
assets/audioService-teJgTYV7.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +930 B 🔴 +821 B
assets/releaseStore-Da3x-TM-.js (removed) 140 B 🟢 -140 B 🟢 -106 B 🟢 -105 B
assets/releaseStore-Dg2105Ry.js (new) 140 B 🔴 +140 B 🔴 +106 B 🔴 +107 B
assets/serverConfigStore-sopnD88s.js 2.64 kB 2.64 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/colorUtil-BTZOs5h9.js (removed) 7.2 kB 🟢 -7.2 kB 🟢 -2.22 kB 🟢 -1.97 kB
assets/colorUtil-uBzFbZOf.js (new) 7.2 kB 🔴 +7.2 kB 🔴 +2.22 kB 🔴 +1.96 kB
assets/useErrorHandling-Bfegpret.js (removed) 5.08 kB 🟢 -5.08 kB 🟢 -1.5 kB 🟢 -1.3 kB
assets/useErrorHandling-Dg1-tVeI.js (new) 5.08 kB 🔴 +5.08 kB 🔴 +1.5 kB 🔴 +1.3 kB
assets/subscriptionCheckoutUtil-BFTymX5o.js (new) 1.98 kB 🔴 +1.98 kB 🔴 +859 B 🔴 +749 B
assets/subscriptionCheckoutUtil-DBgZcAcl.js (removed) 1.98 kB 🟢 -1.98 kB 🟢 -859 B 🟢 -751 B
assets/markdownRendererUtil-B9qvfWYq.js (removed) 1.78 kB 🟢 -1.78 kB 🟢 -884 B 🟢 -773 B
assets/markdownRendererUtil-QWiFuAoc.js (new) 1.78 kB 🔴 +1.78 kB 🔴 +883 B 🔴 +772 B
assets/audioUtils-BYbiu90h.js (removed) 970 B 🟢 -970 B 🟢 -548 B 🟢 -486 B
assets/audioUtils-CDgFihZy.js (new) 970 B 🔴 +970 B 🔴 +548 B 🔴 +493 B
assets/tailwindUtil-DEfdq6LJ.js (new) 487 B 🔴 +487 B 🔴 +298 B 🔴 +266 B
assets/tailwindUtil-DTv34axN.js (removed) 487 B 🟢 -487 B 🟢 -295 B 🟢 -269 B
assets/useCurrentUser-CerLyuA8.js (removed) 145 B 🟢 -145 B 🟢 -114 B 🟢 -106 B
assets/useCurrentUser-DRKE4765.js (new) 145 B 🔴 +145 B 🔴 +114 B 🔴 +103 B
assets/_plugin-vue_export-helper-xVPqUhAl.js 467 B 467 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 removed

Vendor & Third-Party — 10.4 MB (baseline 10.4 MB) • 🟢 -32 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-other-CpOC_out.js (removed) 3.92 MB 🟢 -3.92 MB 🟢 -832 kB 🟢 -644 kB
assets/vendor-other-CGljyUaV.js (new) 3.92 MB 🔴 +3.92 MB 🔴 +832 kB 🔴 +644 kB
assets/vendor-chart-Dr8GmMlH.js 408 kB 408 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-DvjPM_Lx.js 3.04 MB 3.04 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-BRjoKiJi.js 172 kB 172 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-Dqb1VEds.js 1.83 MB 1.83 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BxNhpyUI.js 650 kB 650 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-7XRCqLaG.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-CArXWFIl.js 398 kB 398 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

Other — 6.28 MB (baseline 6.28 MB) • 🟢 -48 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-_oh8W-65.js (new) 177 kB 🔴 +177 kB 🔴 +42.8 kB 🔴 +35.8 kB
assets/core-Bk3TPpwL.js (removed) 177 kB 🟢 -177 kB 🟢 -42.8 kB 🟢 -35.8 kB
assets/Load3D-34-rF7Mk.js (removed) 55.7 kB 🟢 -55.7 kB 🟢 -9.17 kB 🟢 -7.92 kB
assets/Load3D-CSq4kGas.js (new) 55.7 kB 🔴 +55.7 kB 🔴 +9.17 kB 🔴 +7.91 kB
assets/WidgetSelect-BEIT5x7S.js (removed) 50.5 kB 🟢 -50.5 kB 🟢 -11.1 kB 🟢 -9.68 kB
assets/WidgetSelect-Ct36U00X.js (new) 50.4 kB 🔴 +50.4 kB 🔴 +11.1 kB 🔴 +9.68 kB
assets/SubscriptionRequiredDialogContent-BmdSRMZf.js (removed) 28.7 kB 🟢 -28.7 kB 🟢 -6.78 kB 🟢 -5.91 kB
assets/SubscriptionRequiredDialogContent-DWG-e0xR.js (new) 28.7 kB 🔴 +28.7 kB 🔴 +6.78 kB 🔴 +5.91 kB
assets/WidgetRecordAudio-DHt_tE_k.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.96 kB 🟢 -4.42 kB
assets/WidgetRecordAudio-QkYvwoFe.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.96 kB 🔴 +4.43 kB
assets/WidgetInputNumber-BW7mlTor.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.49 kB 🟢 -4 kB
assets/WidgetInputNumber-qbh3hFXH.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.5 kB 🔴 +4.01 kB
assets/WidgetImageCrop-BTt7vR3r.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +4.14 kB 🔴 +3.63 kB
assets/WidgetImageCrop-DE4Xop3B.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -4.14 kB 🟢 -3.63 kB
assets/PanelTemplate-DbjjmHoz.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +5.45 kB 🔴 +4.79 kB
assets/PanelTemplate-Dxe43aT2.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -5.45 kB 🟢 -4.79 kB
assets/LazyImage-B_Li4vEN.js (new) 14.1 kB 🔴 +14.1 kB 🔴 +4 kB 🔴 +3.54 kB
assets/LazyImage-BuqR6Hu5.js (removed) 14.1 kB 🟢 -14.1 kB 🟢 -4 kB 🟢 -3.54 kB
assets/AudioPreviewPlayer-HAi4HJkN.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.98 kB 🔴 +2.65 kB
assets/AudioPreviewPlayer-JqamuX_O.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.65 kB
assets/WidgetWithControl-3M8t1E2n.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.65 kB 🟢 -2.38 kB
assets/WidgetWithControl-BIZhu_2P.js (new) 8.02 kB 🔴 +8.02 kB 🔴 +2.65 kB 🔴 +2.38 kB
assets/ValueControlPopover-BGB2J4ro.js (new) 4.86 kB 🔴 +4.86 kB 🔴 +1.55 kB 🔴 +1.38 kB
assets/ValueControlPopover-BvtmENLH.js (removed) 4.86 kB 🟢 -4.86 kB 🟢 -1.55 kB 🟢 -1.37 kB
assets/WidgetGalleria-Bb88GwVr.js (new) 4.57 kB 🔴 +4.57 kB 🔴 +1.57 kB 🔴 +1.43 kB
assets/WidgetGalleria-BekALFye.js (removed) 4.57 kB 🟢 -4.57 kB 🟢 -1.56 kB 🟢 -1.43 kB
assets/Slider-Buvb2eP-.js (removed) 4.21 kB 🟢 -4.21 kB 🟢 -1.51 kB 🟢 -1.33 kB
assets/Slider-CtzT4XB4.js (new) 4.21 kB 🔴 +4.21 kB 🔴 +1.51 kB 🔴 +1.34 kB
assets/WidgetImageCompare-BqwXGWo9.js (removed) 3.79 kB 🟢 -3.79 kB 🟢 -1.28 kB 🟢 -1.12 kB
assets/WidgetImageCompare-CgXMpJsU.js (new) 3.79 kB 🔴 +3.79 kB 🔴 +1.28 kB 🔴 +1.12 kB
assets/WidgetColorPicker-0mXGD_iR.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.38 kB 🔴 +1.25 kB
assets/WidgetColorPicker-E9_-OJ5u.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.38 kB 🟢 -1.25 kB
assets/WidgetTextarea-BlNSAutn.js (new) 3.52 kB 🔴 +3.52 kB 🔴 +1.33 kB 🔴 +1.18 kB
assets/WidgetTextarea-Dm4Ij7fB.js (removed) 3.52 kB 🟢 -3.52 kB 🟢 -1.33 kB 🟢 -1.17 kB
assets/WidgetMarkdown-BdG0Jp83.js (removed) 3.22 kB 🟢 -3.22 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetMarkdown-CekoUxWL.js (new) 3.22 kB 🔴 +3.22 kB 🔴 +1.28 kB 🔴 +1.14 kB
assets/WidgetToggleSwitch-Ajdp1YMA.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.19 kB 🔴 +1.07 kB
assets/WidgetToggleSwitch-jOzkIkDw.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.19 kB 🟢 -1.07 kB
assets/GlobalToast-BJlGLAmz.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.1 kB 🔴 +940 B
assets/GlobalToast-C5vO0U0P.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.1 kB 🟢 -940 B
assets/SubscribeToRun-5AG29n6H.js (new) 2.96 kB 🔴 +2.96 kB 🔴 +1.15 kB 🔴 +1.01 kB
assets/SubscribeToRun-CeCvzS9Q.js (removed) 2.96 kB 🟢 -2.96 kB 🟢 -1.15 kB 🟢 -1.01 kB
assets/cloudSessionCookie-I9vr-D2J.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +933 B 🔴 +807 B
assets/cloudSessionCookie-OCqD_DxI.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -932 B 🟢 -808 B
assets/WidgetLayoutField-BmL9OHU7.js (removed) 2.61 kB 🟢 -2.61 kB 🟢 -1.01 kB 🟢 -894 B
assets/WidgetLayoutField-Co8PKFO0.js (new) 2.61 kB 🔴 +2.61 kB 🔴 +1.01 kB 🔴 +894 B
assets/WidgetInputText-BDdQrvnC.js (new) 2.58 kB 🔴 +2.58 kB 🔴 +1.01 kB 🔴 +910 B
assets/WidgetInputText-BUBXlHZn.js (removed) 2.58 kB 🟢 -2.58 kB 🟢 -1.01 kB 🟢 -915 B
assets/BaseViewTemplate-BqFKZrxy.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.05 kB 🔴 +950 B
assets/BaseViewTemplate-DNGu5NFc.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -927 B
assets/MediaImageTop-CVYh1Dta.js (removed) 2.34 kB 🟢 -2.34 kB 🟢 -1 kB 🟢 -880 B
assets/MediaImageTop-DQfWsHKx.js (new) 2.34 kB 🔴 +2.34 kB 🔴 +1 kB 🔴 +872 B
assets/CloudRunButtonWrapper-BuUs-tBe.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +641 B 🔴 +575 B
assets/CloudRunButtonWrapper-DwpMID0e.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -642 B 🟢 -575 B
assets/cloudBadges-5Zb3Hbf_.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +537 B 🔴 +480 B
assets/cloudBadges-COXlvTRR.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -537 B 🟢 -484 B
assets/graphHasMissingNodes-9l9yW3Lg.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -459 B 🟢 -425 B
assets/graphHasMissingNodes-dA3Lz6Hs.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +460 B 🔴 +411 B
assets/cloudSubscription-CHQDbpKC.js (removed) 976 B 🟢 -976 B 🟢 -459 B 🟢 -401 B
assets/cloudSubscription-DhkXH3Mq.js (new) 976 B 🔴 +976 B 🔴 +460 B 🔴 +401 B
assets/WidgetInputNumber-CCfoplE1.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -122 B
assets/WidgetInputNumber-DR1oX1T2.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +122 B
assets/WidgetLegacy-Csf7s4Ud.js (new) 164 B 🔴 +164 B 🔴 +125 B 🔴 +107 B
assets/WidgetLegacy-mjyNvgz5.js (removed) 164 B 🟢 -164 B 🟢 -125 B 🟢 -109 B
assets/mixpanel.module-BAotZvHf.js (new) 143 B 🔴 +143 B 🔴 +125 B 🔴 +108 B
assets/mixpanel.module-DaKz7N-R.js (removed) 143 B 🟢 -143 B 🟢 -125 B 🟢 -109 B
assets/Load3D-BOPBBWwe.js (new) 131 B 🔴 +131 B 🔴 +107 B 🔴 +101 B
assets/Load3D-j78a5sKB.js (removed) 131 B 🟢 -131 B 🟢 -107 B 🟢 -117 B
assets/auto-Bv9cmrEd.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-6dIwsSNi.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BahwM9ZP.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BoJZgy7S.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BQtdp20P.js 20.6 kB 20.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CEMgeOuO.js 18.5 kB 18.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CnxND6sZ.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-coXkrooi.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CZcHDaAg.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DDPGTXy9.js 17.9 kB 17.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DsGC6118.js 17.8 kB 17.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-yZ2AjT4s.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-B_sPkRkB.js 121 kB 121 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BAkXMjmM.js 141 kB 141 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BENvLtOF.js 117 kB 117 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BNsn8_Lr.js 120 kB 120 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C9XUWxrh.js 105 kB 105 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Ch4S7NKM.js 105 kB 105 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DqG_JuMY.js 161 kB 161 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DSqO9eB5.js 124 kB 124 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-F6Zr8qmN.js 118 kB 118 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-kF4hEjF-.js 145 kB 145 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-KWKxelma.js 134 kB 134 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-DEJN4gIz.js 2.38 kB 2.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-D39-6tpk.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-D7WAsNtO.js 2.84 kB 2.84 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-42NP799-.js 358 kB 358 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-5Wl7kJtq.js 361 kB 361 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bfo_nHET.js 361 kB 361 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BIaHseXE.js 334 kB 334 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BqTE0tnV.js 437 kB 437 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CkFt94jg.js 365 kB 365 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-COXALCSv.js 374 kB 374 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cuv6XdUW.js 332 kB 332 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CyylrXT5.js 355 kB 355 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DN_FfVrO.js 403 kB 403 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dxwvr4db.js 403 kB 403 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/preservedQueryNamespaces-BsMrb3S_.js 3.23 kB 3.23 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-DZ1Jt5wB.js 838 B 838 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-CqTjxoQm.js 1.53 kB 1.53 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-Dneex3J5.js 518 B 518 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-BRX2wHPg.js 4.71 kB 4.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-BttrDPHP.js 186 B 186 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-D3w-Nwsc.js 2.79 kB 2.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-B0BM5Ibm.js 1.31 kB 1.31 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 34 added / 34 removed

@DrJKL DrJKL added the preview label Jan 21, 2026
@DrJKL DrJKL marked this pull request as ready for review January 21, 2026 08:09
@DrJKL DrJKL requested a review from a team as a code owner January 21, 2026 08:09
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jan 21, 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: 5

🤖 Fix all issues with AI agents
In `@src/platform/assets/services/assetService.ts`:
- Around line 4-8: Move the PaginationOptions interface so it appears after all
import statements (i.e., after the import block) to follow the import-first
convention; locate the current declaration of PaginationOptions in
assetService.ts and cut/paste it immediately below the last import, ensuring no
imports are interleaved with the interface and preserving its exact name and
shape.

In `@src/renderer/extensions/vueNodes/widgets/composables/useAssetWidgetData.ts`:
- Line 51: Remove the redundant null coalescing fallback in the return map
inside useAssetWidgetData: the computed assets already guarantees an array (via
getAssets(key) defaulting to []), so replace the expression that uses
(assets.value ?? []) with just assets.value in the return mapping; update the
return statement referencing assets (from the computed named assets) and ensure
no other code expects a nullable value.
- Around line 68-69: The code redundantly uses null coalescing when calling
assetsStore.getAssets(currentNodeType); since getAssets always returns an array,
remove the "?? []" and assign directly to existingAssets (const existingAssets =
assetsStore.getAssets(currentNodeType)), then keep hasData =
existingAssets.length > 0 unchanged; update references to existingAssets if any
follow.

In `@src/stores/assetsStore.ts`:
- Around line 354-381: The background loader loadRemainingBatches currently logs
errors but never updates the model state, so partial failures are invisible;
modify loadRemainingBatches to set an error on the per-key state
(modelStateByKey.value.get(key)?.error = err or similar) inside the catch, and
also set state.hasMore = false (or another partial-failure flag on the state) to
stop further attempts; after successfully processing each batch (after updating
state.offset and state.hasMore) insert a small await delay (e.g., await new
Promise(resolve => setTimeout(resolve, 50))) to throttle requests and avoid
rapid-fire fetching.
- Around line 298-300: getAssets currently does
Array.from(modelStateByKey.value.get(key)?.assets.values() ?? []), which
allocates a new array on every call and breaks downstream reactivity; update
getAssets to avoid creating a new array each call by either returning the Map
(or its values iterator) directly from modelStateByKey.value.get(key)?.assets so
callers can compute/memoize the array, or implement a simple memo cache keyed by
`key` (e.g., store the last array for each key and only rebuild it when the
underlying Map changes). Adjust callers that expect an array to use computed()
or to convert the iterator once; reference getAssets and modelStateByKey to
locate and apply the change.

@coderabbitai coderabbitai bot requested a review from Yorha4D January 21, 2026 18:10
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/stores/assetsStore.ts`:
- Around line 273-286: The state object returned by createInitialState is
currently wrapped with reactive(), causing Vue to deeply convert the assets Map;
change this to use shallowReactive(state) so state.assets (the Map replaced
later via assignments like state.assets = new Map(...)) is not deeply proxied.
Update the import to pull shallowReactive from 'vue' and keep the rest of the
ModelPaginationState usage (including state.assets replacements) unchanged.
- Around line 304-317: getAssets caches arrays by Map reference
(assetsArrayCache) but loadRemainingBatches mutates state.assets in-place via
state.assets.set(), creating a race where getAssets can return a stale array;
fix by invalidating the cache before in-place mutations: in the code path
loadRemainingBatches (or any code that calls state.assets.set/clear), call
assetsArrayCache.delete(key) immediately before mutating state.assets (or
alternatively implement a small version counter on the state and check it in
getAssets), so getAssets sees the deletion and will rebuild the array from the
mutated Map.

@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jan 21, 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: 1

🤖 Fix all issues with AI agents
In `@src/stores/assetsStore.ts`:
- Around line 335-405: Concurrent calls corrupt pagination because background
loadRemainingBatches mutates shared state; add a per-key request token to
modelStateByKey entries (e.g., state.requestId), generate a new token at the
start of updateModelsForKey (inside updateModelsForKey) and assign it to
state.requestId, pass that token into loadRemainingBatches (or have
loadRemainingBatches read state.requestId at each iteration) and before any
mutation of state.offset/hasMore/assets/error ensure the token matches the
current state.requestId; if it differs, stop the background loop so stale
loaders don't advance pagination. Also ensure when updateModelsForKey starts a
new background load (void loadRemainingBatches(...)) it uses the newly generated
token so previous loops exit.

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/stores/assetsStore.test.ts`:
- Around line 544-554: The test is brittle because it asserts reference equality
between firstArrayRef and secondArrayRef from store.getAssets; remove the
expect(secondArrayRef).toBe(firstArrayRef) assertion and instead assert
behavior: verify the array contains the new asset id (e.g., map(a =>
a.id).toContain('new-asset')) and that the asset count changed appropriately
(e.g., length increased by 1) after the mutation; if you still need to test
caching semantics separately, add a dedicated test that explicitly documents
expected caching behaviour for store.getAssets rather than coupling it to this
mutation test.
- Around line 463-471: Move the Pinia setup into the test suite lifecycle by
calling setActivePinia(createPinia()) inside the existing beforeEach and remove
inline setActivePinia/createPinia and useAssetsStore() calls from individual
tests; ensure afterEach still resets mockIsCloud.value and clear mocks as needed
and that tests now call useAssetsStore() only when needed without reinitializing
Pinia.
- Around line 482-514: Add a concurrency test to ensure
updateModelsForNodeType/updateModelsForKey cancels or ignores stale background
loads: mock assetService.getAssetsForNodeType to return a fast first batch, a
second hanging Promise (resolve later) and then an empty/fresh batch; call
store.updateModelsForNodeType twice (starting the second before the hanging
batch resolves), then resolve the slow promise and assert that the final
store.getAssets(nodeType) does not contain the stale asset from the cancelled
batch; reference updateModelsForNodeType/updateModelsForKey,
assetService.getAssetsForNodeType, and store.getAssets to locate the relevant
test hooks and store logic.
♻️ Duplicate comments (1)
src/stores/assetsStore.ts (1)

335-406: Concurrent refreshes can still corrupt pagination state.

If updateModelsForKey is called again while a prior loadRemainingBatches loop is running (e.g., from a download-triggered refresh), both loops mutate the same state.offset/hasMore, potentially skipping pages or advancing offsets twice.

A per-key request generation guard was suggested in a previous review but doesn't appear to be implemented. Consider adding a requestId to ModelPaginationState and checking it at each iteration of loadRemainingBatches:

🔧 Suggested fix (per-key requestId guard)
 interface ModelPaginationState {
   assets: Map<string, AssetItem>
   offset: number
   hasMore: boolean
   isLoading: boolean
   error?: Error
+  requestId: number
 }

 function createInitialState(): ModelPaginationState {
   const state: ModelPaginationState = {
     assets: new Map(),
     offset: 0,
     hasMore: true,
-    isLoading: false
+    isLoading: false,
+    requestId: 0
   }
   return shallowReactive(state)
 }

 async function updateModelsForKey(
   key: string,
   fetcher: (options: PaginationOptions) => Promise<AssetItem[]>
 ): Promise<AssetItem[]> {
   const state = getOrCreateState(key)
+  state.requestId += 1
+  const currentRequestId = state.requestId

   resetPaginationForKey(key)
   // ... existing code ...

   if (state.hasMore) {
-    void loadRemainingBatches(key, fetcher)
+    void loadRemainingBatches(key, fetcher, currentRequestId)
   }
   // ...
 }

 async function loadRemainingBatches(
   key: string,
-  fetcher: (options: PaginationOptions) => Promise<AssetItem[]>
+  fetcher: (options: PaginationOptions) => Promise<AssetItem[]>,
+  requestId: number
 ): Promise<void> {
   const state = modelStateByKey.value.get(key)
-  if (!state) return
+  if (!state || state.requestId !== requestId) return

   while (state.hasMore) {
+    if (state.requestId !== requestId) break
     // ... fetch logic ...
+    if (state.requestId !== requestId) break
     // ... mutation logic ...
   }
 }

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/stores/assetsStore.ts`:
- Around line 382-388: state.assets is mutated in-place with
state.assets.set(...) so shallowReactive won't detect changes and components
like the fetchedAssets computed won't update; instead of looping and calling
state.assets.set inside the for loop, build a new Map that merges existing
entries with the assetsToAdd (e.g. const newMap = new Map(state.assets); for
(const a of assetsToAdd) newMap.set(a.id, a);) and then assign state.assets =
newMap after assetsArrayCache.delete(key) so the reassignment triggers
reactivity; update the block that uses assetsToAdd and state.assets accordingly.
♻️ Duplicate comments (1)
src/stores/assetsStore.test.ts (1)

548-551: Reference-equality assertion is brittle.

As noted ранее, this couples the test to the current caching implementation. Prefer asserting the updated contents and length instead of toBe on array references.

@DrJKL DrJKL force-pushed the drjkl/progressive-pagination branch from eb8d043 to 83f5d49 Compare January 21, 2026 19:59
Comment on lines 209 to 226
const data = await handleAssetRequest(
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG},${category}&limit=${DEFAULT_LIMIT}`,
`${ASSETS_ENDPOINT}?${queryParams.toString()}`,
`assets for ${nodeType}`
)
Copy link
Contributor

Choose a reason for hiding this comment

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

small nit: should we modify handleAssetRequest signature to accept query params instead of doing string templating here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Would be good and ideally this is type-checked agains the types that are generated from openapi spec!

Copy link
Contributor

Choose a reason for hiding this comment

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

openapi spec should be merged sometime today, but i dont think that should block

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, updated. The interface there is a good target to swap out once we have the spec types.

state.hasMore = assets.length === MODEL_BATCH_SIZE

if (state.hasMore) {
void loadRemainingBatches(key, fetcher, state)
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of duplicating fetching and setting logic in this new function, can we have one while loop doing all the work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How's this version?

Copy link
Contributor

@luke-mino-altherr luke-mino-altherr left a comment

Choose a reason for hiding this comment

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

some nits, otherwise looks good

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/stores/assetsStore.ts`:
- Around line 387-396: On first-batch failure you must remove the orphaned
request from pendingRequestByKey and make the error visible to the UI: inside
the catch block in the batch loader (where you currently set state.error and
return) call pendingRequestByKey.delete(key) before returning, and then
propagate the error into the persisted model state so getError can see it (for
example, update modelStateByKey.get(key).error = state.error or create/update
the entry in modelStateByKey with the same error/hasMore/isLoading values). This
prevents the memory leak and ensures UI error visibility; use the existing
symbols pendingRequestByKey, state, key, and modelStateByKey to locate and
implement the change.

DrJKL and others added 12 commits January 21, 2026 17:45
- Move Pinia setup into beforeEach lifecycle hooks

- Replace createPinia with createTestingPinia

- Remove inline setActivePinia calls from individual tests
- Replace reference equality check with behavioral assertions

- Add dedicated test for getAssets caching semantics
- Add ?? [] fallback for getAssets in computed and watch handler

- Use shared EMPTY_ASSETS constant for stable cache reference

Amp-Thread-ID: https://ampcode.com/threads/T-019be204-2bc0-702f-904c-2f22f51b2280
Co-authored-by: Amp <amp@ampcode.com>
- Merge updateModelsForKey and loadRemainingBatches into one loadBatches function

- Change return type to void since callers use getAssets() instead

- Address PR review feedback

Amp-Thread-ID: https://ampcode.com/threads/T-019be255-b4d6-73ed-a9dd-62b92b0af1c4
Co-authored-by: Amp <amp@ampcode.com>
- Remove export from AssetRequestOptions (only used internally)

- Move assetsArrayCache.delete() inside loadBatches after first batch fetch

Amp-Thread-ID: https://ampcode.com/threads/T-019be270-a632-728b-843d-270d1971593e
Co-authored-by: Amp <amp@ampcode.com>
- Track pending requests separately from committed state

- Only replace modelStateByKey after first batch succeeds

- Update isStale() to check both committed and pending state

Amp-Thread-ID: https://ampcode.com/threads/T-019be270-a632-728b-843d-270d1971593e
Co-authored-by: Amp <amp@ampcode.com>
- If no existing data, commit new state right away for loading indicator

- If existing data, keep it visible until first batch succeeds

Amp-Thread-ID: https://ampcode.com/threads/T-019be270-a632-728b-843d-270d1971593e
Co-authored-by: Amp <amp@ampcode.com>
The while loop handles all batch iterations; recursive call was redundant.

Amp-Thread-ID: https://ampcode.com/threads/T-019be286-74b8-72c3-b177-07cfb7e14476
Co-authored-by: Amp <amp@ampcode.com>
Prevents memory leak by deleting orphaned request from pendingRequestByKey when first batch fails to load.

Amp-Thread-ID: https://ampcode.com/threads/T-019be291-311a-7339-b764-d7ded3487894
Co-authored-by: Amp <amp@ampcode.com>
@DrJKL DrJKL force-pushed the drjkl/progressive-pagination branch from 0fdc73e to c281278 Compare January 22, 2026 01:45
import { useModelToNodeStore } from '@/stores/modelToNodeStore'

export interface PaginationOptions {
limit?: number
Copy link
Member

Choose a reason for hiding this comment

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

Why are they optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are defaults set below, and the API itself doesn't require them.

}

interface AssetRequestOptions extends PaginationOptions {
includeTags: string[]
Copy link
Member

Choose a reason for hiding this comment

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

it could be optional, isnt it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one is always set. Not sure if it's required by the API. Once we get the spec synced we'll know that we are aligned with the backend expectations.

@DrJKL DrJKL merged commit 4821599 into main Jan 22, 2026
27 checks passed
@DrJKL DrJKL deleted the drjkl/progressive-pagination branch January 22, 2026 01:59
@DrJKL DrJKL added needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch cloud/1.37 Backport PRs for cloud 1.37 labels Jan 22, 2026
@github-actions
Copy link

⚠️ Backport to cloud/1.37 failed

Reason: Merge conflicts detected during cherry-pick of 4821599

📄 Conflicting files
src/stores/assetsStore.test.ts
🤖 Prompt for AI Agents
Backport PR #8212 (https://github.com/Comfy-Org/ComfyUI_frontend/pull/8212) to cloud/1.37.
Cherry-pick merge commit 482159957eb80c7dedc2e46b03bfbc9223200910 onto new branch
backport-8212-to-cloud-1.37 from origin/cloud/1.37.
Resolve conflicts in: src/stores/assetsStore.test.ts .
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport cloud/1.37] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.

cc @DrJKL

1 similar comment
@github-actions
Copy link

⚠️ Backport to cloud/1.37 failed

Reason: Merge conflicts detected during cherry-pick of 4821599

📄 Conflicting files
src/stores/assetsStore.test.ts
🤖 Prompt for AI Agents
Backport PR #8212 (https://github.com/Comfy-Org/ComfyUI_frontend/pull/8212) to cloud/1.37.
Cherry-pick merge commit 482159957eb80c7dedc2e46b03bfbc9223200910 onto new branch
backport-8212-to-cloud-1.37 from origin/cloud/1.37.
Resolve conflicts in: src/stores/assetsStore.test.ts .
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport cloud/1.37] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.

cc @DrJKL

limit = DEFAULT_LIMIT,
offset = 0
}: { limit?: number; offset?: number } = {}
{ limit = DEFAULT_LIMIT, offset = 0 }: PaginationOptions = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose we won't have the type generation in time for this PR? related comment above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not without some form of time travel

DrJKL added a commit that referenced this pull request Jan 22, 2026
…#8212)

Implements progressive pagination for model assets - returns the first
batch immediately while loading remaining batches in the background.

- Adds `ModelPaginationState` tracking (assets Map, offset, hasMore,
loading, error)
- `updateModelsForKey()` returns first batch, then calls
`loadRemainingBatches()` to fetch the rest
- Accessor functions `getAssets(key)`, `isModelLoading(key)` replace
direct Map access

- Adds `PaginationOptions` interface (`{ limit?, offset? }`)

- `AssetBrowserModal.vue` uses new accessor API

- Updated mocks for new accessor pattern

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8212-feat-implement-progressive-pagination-for-Asset-Browser-model-assets-2ef6d73d36508157af04d1264780997e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
DrJKL added a commit that referenced this pull request Jan 22, 2026
…#8212)

Implements progressive pagination for model assets - returns the first
batch immediately while loading remaining batches in the background.

- Adds `ModelPaginationState` tracking (assets Map, offset, hasMore,
loading, error)
- `updateModelsForKey()` returns first batch, then calls
`loadRemainingBatches()` to fetch the rest
- Accessor functions `getAssets(key)`, `isModelLoading(key)` replace
direct Map access

- Adds `PaginationOptions` interface (`{ limit?, offset? }`)

- `AssetBrowserModal.vue` uses new accessor API

- Updated mocks for new accessor pattern

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8212-feat-implement-progressive-pagination-for-Asset-Browser-model-assets-2ef6d73d36508157af04d1264780997e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
DrJKL added a commit that referenced this pull request Jan 22, 2026
godwiniheuwa pushed a commit to godwiniheuwa/ComfyUI_frontend that referenced this pull request Jan 22, 2026
…Comfy-Org#8212)

## Summary

Implements progressive pagination for model assets - returns the first
batch immediately while loading remaining batches in the background.

## Changes

### Store (`assetsStore.ts`)
- Adds `ModelPaginationState` tracking (assets Map, offset, hasMore,
loading, error)
- `updateModelsForKey()` returns first batch, then calls
`loadRemainingBatches()` to fetch the rest
- Accessor functions `getAssets(key)`, `isModelLoading(key)` replace
direct Map access

### API (`assetService.ts`)
- Adds `PaginationOptions` interface (`{ limit?, offset? }`)

### Components
- `AssetBrowserModal.vue` uses new accessor API

### Tests
- Updated mocks for new accessor pattern

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8212-feat-implement-progressive-pagination-for-Asset-Browser-model-assets-2ef6d73d36508157af04d1264780997e)
by [Unito](https://www.unito.io)

---------

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

Labels

cloud/1.37 Backport PRs for cloud 1.37 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch preview size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants