Skip to content

Conversation

@viva-jinyi
Copy link
Member

@viva-jinyi viva-jinyi commented Nov 27, 2025

Summary

When downloading multiple selected assets, they are now bundled into a single ZIP file instead of downloading each file individually.

Changes

  • Use client-zip library to generate ZIP files in the browser
  • Use Promise.allSettled to include remaining files in ZIP even if some fail
  • Automatically add numbers to duplicate filenames
  • Notify users with toast messages based on download status
  • Auto-generate filename with timestamp to prevent filename collisions

Modified Files

  • src/platform/assets/composables/useMediaAssetActions.ts: Implement ZIP download logic
  • src/locales/en/main.json: Add new i18n strings
  • package.json: Add client-zip dependency
screen-capture.webm

🤖 Generated with Claude Code
Co-Authored-By: Claude [email protected]

┆Issue is synchronized with this Notion page by Unito

## Summary
When downloading multiple selected assets, they are now bundled into a single ZIP file instead of downloading each file individually.

## Changes
- Use `client-zip` library to generate ZIP files in the browser
- Use `Promise.allSettled` to include remaining files in ZIP even if some fail
- Automatically add numbers to duplicate filenames
- Notify users with toast messages based on download status
- Auto-generate filename with timestamp to prevent filename collisions

### Modified Files
- `src/platform/assets/composables/useMediaAssetActions.ts`: Implement ZIP download logic
- `src/locales/en/main.json`: Add new i18n strings
- `package.json`: Add `client-zip` dependency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 27, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

📝 Walkthrough

Walkthrough

This change adds ZIP-based bulk download functionality for multiple assets. It introduces the client-zip library as a new dependency in the workspace catalog, adds localization strings for ZIP preparation and download states, and refactors the downloadMultipleAssets function to fetch assets in parallel, package them into a ZIP file, and download them as a single archive with partial failure handling.

Changes

Cohort / File(s) Summary
Dependency Configuration
package.json, pnpm-workspace.yaml
Added client-zip library reference to project dependencies via catalog entry
Localization
src/locales/en/main.json
Added four new locale keys under assetBrowser.selection for ZIP download messaging: preparingZip, zipDownloadStarted, zipDownloadFailed, partialZipSuccess
Asset Download Functionality
src/platform/assets/composables/useMediaAssetActions.ts
Refactored downloadMultipleAssets to async function performing parallel asset fetches with Promise.allSettled, ZIP compression via dynamic client-zip import, filename deduplication, and granular user feedback through toasts for success, partial success, and failure states

Sequence Diagram

sequenceDiagram
    participant UI as User Interface
    participant Comp as useMediaAssetActions
    participant Fetch as Parallel Fetches
    participant Zip as client-zip
    participant DL as downloadBlob
    participant Toast as Toast Notifications

    UI->>Comp: downloadMultipleAssets(assets[])
    activate Comp
    Comp->>Toast: Show "Preparing ZIP" toast
    activate Toast
    Toast-->>Comp: 
    deactivate Toast
    
    Comp->>Fetch: Fetch all assets in parallel
    activate Fetch
    Fetch-->>Fetch: Promise.allSettled()
    Fetch-->>Comp: {name, input: Response} | failure
    deactivate Fetch
    
    Comp->>Comp: Deduplicate filenames<br/>Filter successful fetches
    
    Comp->>Zip: Collect files for ZIP
    activate Zip
    Zip->>Zip: Generate ZIP blob
    Zip-->>Comp: Zip blob
    deactivate Zip
    
    Comp->>DL: downloadBlob(zipBlob, filename)
    activate DL
    DL-->>Comp: 
    deactivate DL
    
    alt All successful
        Comp->>Toast: Success notification
    else Partial success
        Comp->>Toast: Partial success with counts
    else All failed
        Comp->>Toast: Error notification
    end
    
    deactivate Comp
Loading

Possibly related PRs

✨ 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 feature/multi-download

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

@github-actions
Copy link

github-actions bot commented Nov 27, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 11/27/2025, 02:03:01 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Nov 27, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 11/27/2025, 02:11:32 PM UTC

📈 Summary

  • Total Tests: 493
  • Passed: 479 ✅
  • Failed: 0
  • Flaky: 5 ⚠️
  • Skipped: 9 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 470 / ❌ 0 / ⚠️ 5 / ⏭️ 9
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 6 / ❌ 0 / ⚠️ 0 / ⏭️ 0

🎉 Click on the links above to view detailed test results for each browser configuration.

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedclient-zip@​2.5.01001009881100

View full report

@github-actions
Copy link

Bundle Size Report

Summary

  • Raw size: 17 MB baseline 17 MB — 🔴 +23.4 kB
  • Gzip: 3.37 MB baseline 3.37 MB — 🔴 +4.82 kB
  • Brotli: 2.58 MB baseline 2.58 MB — 🔴 +3.36 kB
  • Bundles: 93 current • 93 baseline • 52 added / 52 removed

Category Glance
Vendor & Third-Party 🔴 +20.5 kB (8.58 MB) · App Entry Points 🔴 +2.84 kB (3.18 MB) · Other ⚪ 0 B (3.84 MB) · Graph Workspace ⚪ 0 B (948 kB) · Panels & Settings ⚪ 0 B (298 kB) · UI Components ⚪ 0 B (139 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.18 MB (baseline 3.18 MB) • 🔴 +2.84 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-Cz7Oj2WK.js (new) 2.95 MB 🔴 +2.95 MB 🔴 +616 kB 🔴 +468 kB
assets/index-C-pwKqT8.js (removed) 2.95 MB 🟢 -2.95 MB 🟢 -615 kB 🟢 -467 kB
assets/index-CNi9b9Kn.js (new) 227 kB 🔴 +227 kB 🔴 +48.6 kB 🔴 +40 kB
assets/index-D5dNVKUd.js (removed) 227 kB 🟢 -227 kB 🟢 -48.6 kB 🟢 -40 kB
assets/index-1OA9mb6L.js (removed) 345 B 🟢 -345 B 🟢 -246 B 🟢 -233 B
assets/index-an3YnXf3.js (new) 345 B 🔴 +345 B 🔴 +246 B 🔴 +231 B

Status: 3 added / 3 removed

Graph Workspace — 948 kB (baseline 948 kB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-BcI_bDFR.js (removed) 948 kB 🟢 -948 kB 🟢 -184 kB 🟢 -140 kB
assets/GraphView-CoyymRB3.js (new) 948 kB 🔴 +948 kB 🔴 +184 kB 🔴 +140 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-b2-zZf_n.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.9 kB
assets/UserSelectView-D_ij-iyQ.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.9 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CreditsPanel-D7OutMO0.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.15 kB 🔴 +4.5 kB
assets/CreditsPanel-DChj3j8s.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.15 kB 🟢 -4.5 kB
assets/KeybindingPanel-B7A51x8n.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3.02 kB
assets/KeybindingPanel-DxT7sH1c.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.02 kB
assets/ExtensionPanel-B-AN_COE.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.57 kB 🟢 -2.25 kB
assets/ExtensionPanel-CjCCugFq.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.58 kB 🔴 +2.26 kB
assets/AboutPanel-BUqXiOax.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.21 kB
assets/AboutPanel-DiAxpnCZ.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/ServerConfigPanel-3VVp-aHL.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +1.83 kB 🔴 +1.63 kB
assets/ServerConfigPanel-Ch_CHclj.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -1.83 kB 🟢 -1.62 kB
assets/UserPanel-B23a2QkA.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.72 kB 🔴 +1.51 kB
assets/UserPanel-low54n9P.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.72 kB 🟢 -1.5 kB
assets/settings-BXTtSH4O.js 33.3 kB 33.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C9Pzn-NG.js 25.2 kB 25.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CCy2fA_h.js 27.3 kB 27.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CQpqEFfl.js 26.6 kB 26.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DHcnxypw.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DhFTK9fY.js 25.1 kB 25.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DlT4t_ui.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DRgSrIdD.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-tjkeqiZq.js 21.1 kB 21.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-BlsfUYFb.js (new) 53.9 kB 🔴 +53.9 kB 🔴 +8.52 kB 🔴 +7.33 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-BvYtyg__.js (removed) 53.9 kB 🟢 -53.9 kB 🟢 -8.52 kB 🟢 -7.32 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-BiBGmeSi.js (removed) 46.9 kB 🟢 -46.9 kB 🟢 -10.1 kB 🟢 -8.77 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-CzEQAPzA.js (new) 46.9 kB 🔴 +46.9 kB 🔴 +10.1 kB 🔴 +8.77 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-B95ZHhGo.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.35 kB 🟢 -2.96 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-CVRTpeMv.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.35 kB 🔴 +2.96 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-CFFbtdIM.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.97 kB 🔴 +2.59 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-Pm3jgX4e.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.59 kB
assets/ComfyQueueButton-C4fGeQbJ.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.48 kB 🟢 -2.21 kB
assets/ComfyQueueButton-Ce6O8O9B.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.48 kB 🔴 +2.21 kB
assets/WidgetButton-CsMa_P26.js (removed) 2.04 kB 🟢 -2.04 kB 🟢 -928 B 🟢 -810 B
assets/WidgetButton-Kc4j-U5n.js (new) 2.04 kB 🔴 +2.04 kB 🔴 +928 B 🔴 +826 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-Cf8dNyi2.js (removed) 2 kB 🟢 -2 kB 🟢 -768 B 🟢 -677 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-DNoaxZF7.js (new) 2 kB 🔴 +2 kB 🔴 +769 B 🔴 +675 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-Bs-EIQeq.js (removed) 1.34 kB 🟢 -1.34 kB 🟢 -686 B 🟢 -593 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-DqeZwSQL.js (new) 1.34 kB 🔴 +1.34 kB 🔴 +690 B 🔴 +598 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-B1ECbMsk.js (new) 897 B 🔴 +897 B 🔴 +505 B 🔴 +436 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-qLYLZPN5.js (removed) 897 B 🟢 -897 B 🟢 -505 B 🟢 -438 B

Status: 9 added / 9 removed

Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-Bml2BUAO.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.84 kB 🟢 -1.58 kB
assets/keybindingService-DiHdLLNE.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.84 kB 🔴 +1.58 kB
assets/serverConfigStore-B5NW8oSr.js (removed) 2.83 kB 🟢 -2.83 kB 🟢 -910 B 🟢 -789 B
assets/serverConfigStore-CfYWViYy.js (new) 2.83 kB 🔴 +2.83 kB 🔴 +906 B 🔴 +788 B
assets/audioService-DMHf0crI.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -960 B 🟢 -825 B
assets/audioService-KgKcBt5G.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +964 B 🔴 +831 B

Status: 3 added / 3 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-CNkDZbtF.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +653 B 🔴 +548 B
assets/audioUtils-Hn-WnSb1.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -651 B 🟢 -550 B
assets/mathUtil-CTARWQ-l.js 1.07 kB 1.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

Vendor & Third-Party — 8.58 MB (baseline 8.56 MB) • 🔴 +20.5 kB

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-other-BYuOkCEX.js (new) 4 MB 🔴 +4 MB 🔴 +847 kB 🔴 +676 kB
assets/vendor-other-DisjWJFj.js (removed) 3.98 MB 🟢 -3.98 MB 🟢 -843 kB 🟢 -673 kB
assets/vendor-primevue-DXgFbtRH.js (new) 1.96 MB 🔴 +1.96 MB 🔴 +336 kB 🔴 +202 kB
assets/vendor-primevue-LncEKxae.js (removed) 1.96 MB 🟢 -1.96 MB 🟢 -336 kB 🟢 -202 kB
assets/vendor-chart-CtWSYx_P.js (new) 452 kB 🔴 +452 kB 🔴 +99.1 kB 🔴 +81.1 kB
assets/vendor-chart-uhYFfslB.js (removed) 452 kB 🟢 -452 kB 🟢 -99.1 kB 🟢 -81 kB
assets/vendor-tiptap-C4yk8cCR.js (removed) 232 kB 🟢 -232 kB 🟢 -45.7 kB 🟢 -37.7 kB
assets/vendor-tiptap-D99OZYpy.js (new) 232 kB 🔴 +232 kB 🔴 +45.7 kB 🔴 +37.7 kB
assets/vendor-vue-B_GmYs1w.js (new) 160 kB 🔴 +160 kB 🔴 +37.3 kB 🔴 +31.6 kB
assets/vendor-vue-tw3tq7KH.js (removed) 160 kB 🟢 -160 kB 🟢 -37.3 kB 🟢 -31.5 kB
assets/vendor-three-aR6ntw5X.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BZLod3g9.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Other — 3.84 MB (baseline 3.84 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-bs35jE2t.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.24 kB 🔴 +4.63 kB
assets/WidgetRecordAudio-DhErcMj1.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.63 kB
assets/AudioPreviewPlayer-c0e-pp3u.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.4 kB 🔴 +3.04 kB
assets/AudioPreviewPlayer-CMsxc32-.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.4 kB 🟢 -3.04 kB
assets/WidgetGalleria-B12Q6xrZ.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.45 kB 🟢 -1.3 kB
assets/WidgetGalleria-CEPRDGYe.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.45 kB 🔴 +1.31 kB
assets/WidgetColorPicker-CLzDyOET.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-D8j1sw1L.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetMarkdown-CSp0l_bX.js (new) 3.1 kB 🔴 +3.1 kB 🔴 +1.29 kB 🔴 +1.13 kB
assets/WidgetMarkdown-Kuq-N2q7.js (removed) 3.1 kB 🟢 -3.1 kB 🟢 -1.29 kB 🟢 -1.13 kB
assets/WidgetAudioUI-B_tS9qYN.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.12 kB 🔴 +1.04 kB
assets/WidgetAudioUI-BhzHyehw.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.12 kB 🟢 -1.02 kB
assets/MediaVideoTop-6PQqfKb3.js (removed) 2.76 kB 🟢 -2.76 kB 🟢 -1.05 kB 🟢 -900 B
assets/MediaVideoTop-CcPtrwoF.js (new) 2.76 kB 🔴 +2.76 kB 🔴 +1.05 kB 🔴 +902 B
assets/WidgetChart-9GEh1LJF.js (new) 2.48 kB 🔴 +2.48 kB 🔴 +934 B 🔴 +820 B
assets/WidgetChart-C9sMH5oS.js (removed) 2.48 kB 🟢 -2.48 kB 🟢 -931 B 🟢 -816 B
assets/WidgetTextarea-CQs4PPPJ.js (new) 2.48 kB 🔴 +2.48 kB 🔴 +1.01 kB 🔴 +910 B
assets/WidgetTextarea-D5ogtg1r.js (removed) 2.48 kB 🟢 -2.48 kB 🟢 -1.01 kB 🟢 -900 B
assets/WidgetImageCompare-CzSHCIyF.js (new) 2.21 kB 🔴 +2.21 kB 🔴 +749 B 🔴 +661 B
assets/WidgetImageCompare-D4gTOuOo.js (removed) 2.21 kB 🟢 -2.21 kB 🟢 -747 B 🟢 -660 B
assets/WidgetInputText-BVmnlmCd.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -918 B 🟢 -844 B
assets/WidgetInputText-CTpCy0ZL.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +919 B 🔴 +847 B
assets/MediaImageTop-DbyT2UjV.js (removed) 1.75 kB 🟢 -1.75 kB 🟢 -843 B 🟢 -720 B
assets/MediaImageTop-G1Wi2cSz.js (new) 1.75 kB 🔴 +1.75 kB 🔴 +844 B 🔴 +715 B
assets/WidgetToggleSwitch-B7TR0teK.js (new) 1.58 kB 🔴 +1.58 kB 🔴 +759 B 🔴 +666 B
assets/WidgetToggleSwitch-CBRsIck7.js (removed) 1.58 kB 🟢 -1.58 kB 🟢 -760 B 🟢 -666 B
assets/MediaImageBottom-3DUl_vmE.js (new) 1.57 kB 🔴 +1.57 kB 🔴 +744 B 🔴 +645 B
assets/MediaImageBottom-BTLz49ad.js (removed) 1.57 kB 🟢 -1.57 kB 🟢 -740 B 🟢 -646 B
assets/MediaAudioBottom-3VJTpRxq.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -742 B 🟢 -657 B
assets/MediaAudioBottom-Djw_QCSz.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +742 B 🔴 +657 B
assets/MediaVideoBottom-C0STdYAL.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -741 B 🟢 -655 B
assets/MediaVideoBottom-HuXFmKmQ.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +740 B 🔴 +655 B
assets/Media3DBottom-D0oy3s2V.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -733 B 🟢 -652 B
assets/Media3DBottom-oFeOjjdS.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +733 B 🔴 +650 B
assets/Media3DTop-D3aTTsus.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -765 B 🟢 -650 B
assets/Media3DTop-MtxmIOxh.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +767 B 🔴 +654 B
assets/MediaAudioTop-BN13zUOF.js (new) 1.46 kB 🔴 +1.46 kB 🔴 +740 B 🔴 +617 B
assets/MediaAudioTop-Bqc4HR5L.js (removed) 1.46 kB 🟢 -1.46 kB 🟢 -741 B 🟢 -616 B
assets/WidgetSelect-8DKqv4fg.js (new) 655 B 🔴 +655 B 🔴 +346 B 🔴 +286 B
assets/WidgetSelect-D3mpMnZB.js (removed) 655 B 🟢 -655 B 🟢 -343 B 🟢 -307 B
assets/WidgetInputNumber-C5ftmA8L.js (new) 595 B 🔴 +595 B 🔴 +333 B 🔴 +277 B
assets/WidgetInputNumber-DNPkKYaR.js (removed) 595 B 🟢 -595 B 🟢 -331 B 🟢 -275 B
assets/Load3D-9ARANGk_.js (new) 424 B 🔴 +424 B 🔴 +269 B 🔴 +224 B
assets/Load3D-h72K_g1h.js (removed) 424 B 🟢 -424 B 🟢 -266 B 🟢 -221 B
assets/WidgetLegacy-A8SOsmWk.js (removed) 364 B 🟢 -364 B 🟢 -237 B 🟢 -193 B
assets/WidgetLegacy-D3r-SsJh.js (new) 364 B 🔴 +364 B 🔴 +239 B 🔴 +196 B
assets/commands-_s-RvhJR.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BuUILW6P.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV4R6fLx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CLwPdnT6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CWMchBmd.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DazTQhtc.js 12.9 kB 12.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DmWrOe93.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwiH7Kr6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-mS3LCNPn.js 14.5 kB 14.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-5lOBdqcC.js 84.5 kB 84.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BOCuaVpE.js 73.4 kB 73.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-ClrEFGUz.js 72.4 kB 72.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CyNU0iQX.js 99.3 kB 99.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D7gwLxft.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DC8o4BCt.js 86.8 kB 86.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DKiesCV4.js 94.3 kB 94.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Hq2q-OtB.js 83.6 kB 83.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-USAlAlnj.js 82 kB 82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-_Px5dSNW.js 306 kB 306 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-7z21KPoS.js 285 kB 285 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BWKZzBPK.js 346 kB 346 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CGbgH4Yl.js 320 kB 320 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CjjjdWkV.js 313 kB 313 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CVrNtxvj.js 288 kB 288 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DLRSA0IK.js 309 kB 309 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DQV2gnwA.js 372 kB 372 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-ofqLG5vz.js 310 kB 310 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 23 added / 23 removed

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

🧹 Nitpick comments (2)
src/platform/assets/composables/useMediaAssetActions.ts (2)

140-151: Duplicate filename numbering starts at 2.

The logic correctly handles duplicates, but the numbering starts at _2 for the first duplicate (skipping _1). This is a minor UX inconsistency - users might expect file.txt, file_1.txt, file_2.txt instead of file.txt, file_2.txt, file_3.txt.

If you prefer 0-indexed naming:

-            if (nameCount.has(filename)) {
-              const count = nameCount.get(filename)! + 1
-              nameCount.set(filename, count)
+            const count = nameCount.get(filename) ?? 0
+            nameCount.set(filename, count + 1)
+            if (count > 0) {
               const parts = filename.split('.')
               const ext = parts.length > 1 ? parts.pop() : ''
               filename = ext
                 ? `${parts.join('.')}_${count}.${ext}`
                 : `${filename}_${count}`
-            } else {
-              nameCount.set(filename, 1)
             }

176-177: Consider memory usage for large batches.

Using .blob() loads the entire ZIP into memory. For very large batches or high-resolution assets, this could cause memory pressure. The current implementation is acceptable for typical use cases, but consider adding a file count or size limit warning if users frequently download large batches.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d131a4 and efebd5f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/platform/assets/composables/useMediaAssetActions.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Use es-toolkit for utility functions

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

**/*.{ts,tsx}: Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper error handling in components and services

**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in .prettierrc
Organize imports by sorting and grouping by plugin, and run pnpm format before committing

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/*.{vue,ts}

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

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

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/*.ts

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

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

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for variable and setting names in TypeScript/Vue files

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
Use defaultsByInstallVersion property for gradual feature rollout based on version in settings configuration

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
**/composables/use*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables in the format useXyz.ts

Files:

  • src/platform/assets/composables/useMediaAssetActions.ts
🧠 Learnings (4)
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use provide/inject for dependency injection in Vue components

Applied to files:

  • src/platform/assets/composables/useMediaAssetActions.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize provide/inject for dependency injection

Applied to files:

  • src/platform/assets/composables/useMediaAssetActions.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Utilize provide/inject for dependency injection in Vue 3

Applied to files:

  • src/platform/assets/composables/useMediaAssetActions.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Use es-toolkit for utility functions instead of other utility libraries

Applied to files:

  • package.json
🧬 Code graph analysis (1)
src/platform/assets/composables/useMediaAssetActions.ts (3)
src/platform/assets/schemas/assetSchema.ts (1)
  • AssetItem (72-72)
src/platform/assets/utils/assetUrlUtil.ts (1)
  • getAssetUrl (21-29)
scripts/size-collect.js (1)
  • files (42-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: collect
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: setup
🔇 Additional comments (6)
pnpm-workspace.yaml (1)

49-49: LGTM!

The client-zip library is a good choice for browser-based ZIP creation - it's lightweight, supports streaming, and handles the use case of bundling multiple assets well.

package.json (1)

162-162: LGTM!

Dependency correctly added to dependencies (not devDependencies) since client-zip is used at runtime for ZIP creation in the browser.

src/locales/en/main.json (1)

2187-2190: LGTM!

The new i18n strings are well-structured with proper interpolation placeholders ({count}, {succeeded}, {failed}) and provide clear feedback for all ZIP download states (preparing, success, partial success, failure).

src/platform/assets/composables/useMediaAssetActions.ts (3)

118-162: Good use of Promise.allSettled for resilient parallel fetching.

The implementation correctly uses Promise.allSettled to handle partial failures, allowing the ZIP to include successfully fetched files even when some fail. The inner try-catch and warning logs provide good observability for debugging.


112-112: Good use of dynamic import for code splitting.

Dynamically importing client-zip only when needed reduces the initial bundle size for users who don't use the multi-download feature.


179-184: LGTM!

The timestamp format is filesystem-safe (avoids colons which aren't allowed on Windows) and provides sufficient granularity to prevent collisions.

Comment on lines +102 to +109
// Show loading toast
const loadingToast = {
severity: 'info' as const,
summary: t('g.loading'),
detail: t('mediaAsset.selection.preparingZip', { count: assets.length }),
life: 0 // Keep until manually removed
}
toast.add(loadingToast)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

PrimeVue useToast remove method documentation

💡 Result:

Short answer: useToast returns the Toast service (PrimeVueToast) which exposes add(...), removeGroup(group) and removeAllGroups() — there is no documented single-item remove(id/message) on the current PrimeVue Toast service API. [1][2][3]

Sources:

  • PrimeVue Toast docs (useToast / ToastService). [1] (primevue.org)
  • ToastService type definitions showing available methods (add, removeGroup, removeAllGroups). [2] (app.unpkg.com)
  • useToast implementation (injects PrimeVueToast symbol). [3] (app.unpkg.com)

If you want an API that removes a specific toast, tell me which PrimeVue version you use and I’ll check that version’s source/commit history.

Citations:


🏁 Script executed:

# Find and read the file to see the actual implementation
cat -n src/platform/assets/composables/useMediaAssetActions.ts | sed -n '95,120p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1103


🏁 Script executed:

# Search for other toast.remove() usage in the codebase to see patterns
rg -n 'toast\.remove' --type ts --type tsx -B 2 -A 2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 97


🏁 Script executed:

# Search for toast.remove() calls using correct type filtering
rg -n 'toast\.remove' --type ts -B 2 -A 2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 955


🏁 Script executed:

# Read more of the file to see where the toast is removed
cat -n src/platform/assets/composables/useMediaAssetActions.ts | sed -n '100,180p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3323


Toast removal mechanism will fail — toast.remove() doesn't accept message objects.

PrimeVue's toast.remove() method does not exist in the ToastService API. The only available removal methods are removeGroup(group) and removeAllGroups(). The current code at lines 190 and 216 attempts to call toast.remove(loadingToast), which will not work and the loading toast will persist indefinitely.

Use a message group to reliably remove the loading toast:

-    // Show loading toast
-    const loadingToast = {
-      severity: 'info' as const,
-      summary: t('g.loading'),
-      detail: t('mediaAsset.selection.preparingZip', { count: assets.length }),
-      life: 0 // Keep until manually removed
-    }
-    toast.add(loadingToast)
+    // Show loading toast with group for removal
+    const loadingGroup = 'zip-download-loading'
+    toast.add({
+      severity: 'info',
+      summary: t('g.loading'),
+      detail: t('mediaAsset.selection.preparingZip', { count: assets.length }),
+      life: 0, // Keep until manually removed
+      group: loadingGroup
+    })

Then update removal calls (lines 190 and 216):

-      toast.remove(loadingToast)
+      toast.removeGroup(loadingGroup)

and

-      toast.remove(loadingToast)
+      toast.removeGroup(loadingGroup)
📝 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
// Show loading toast
const loadingToast = {
severity: 'info' as const,
summary: t('g.loading'),
detail: t('mediaAsset.selection.preparingZip', { count: assets.length }),
life: 0 // Keep until manually removed
}
toast.add(loadingToast)
// Show loading toast with group for removal
const loadingGroup = 'zip-download-loading'
toast.add({
severity: 'info',
summary: t('g.loading'),
detail: t('mediaAsset.selection.preparingZip', { count: assets.length }),
life: 0, // Keep until manually removed
group: loadingGroup
})
🤖 Prompt for AI Agents
In src/platform/assets/composables/useMediaAssetActions.ts around lines 102-109
(and update calls at ~190 and ~216), the loading toast is created as a message
object but later removed with toast.remove(message) which is invalid; change the
loading toast to include a message group (e.g., group: 'loadingZip') when adding
it, then replace the failing removal calls with toast.removeGroup('loadingZip')
so the loading toast can be reliably cleared; keep life: 0 if you still want it
persistent until removal.

Copy link
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

This is a good idea but what are pros/cons of doing this on the client vs. doing this on the server?

@christian-byrne christian-byrne marked this pull request as draft December 1, 2025 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants