Skip to content

Conversation

@LittleSound
Copy link
Collaborator

@LittleSound LittleSound commented Nov 18, 2025

related #5827 (comment)

┆Issue is synchronized with this Notion page by Unito

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 18, 2025
@github-actions
Copy link

github-actions bot commented Nov 18, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 11/21/2025, 10:23:10 PM UTC

📈 Summary

  • Total Tests: 497
  • Passed: 486 ✅
  • Failed: 0
  • Flaky: 2 ⚠️
  • Skipped: 9 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 477 / ❌ 0 / ⚠️ 2 / ⏭️ 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.

@github-actions
Copy link

github-actions bot commented Nov 18, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 11/21/2025, 10:12:41 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Nov 18, 2025

Bundle Size Report

Summary

  • Raw size: 13.7 MB baseline 13.7 MB — 🔴 +637 B
  • Gzip: 2.76 MB baseline 2.76 MB — 🔴 +177 B
  • Brotli: 2.16 MB baseline 2.16 MB — 🟢 -138 B
  • Bundles: 92 current • 92 baseline • 38 added / 38 removed

Category Glance
UI Components 🔴 +579 B (141 kB) · App Entry Points 🔴 +58 B (3.13 MB) · Vendor & Third-Party ⚪ 0 B (5.32 MB) · Other ⚪ 0 B (3.87 MB) · Graph Workspace ⚪ 0 B (945 kB) · Panels & Settings ⚪ 0 B (306 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.13 MB (baseline 3.13 MB) • 🔴 +58 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-USmYhX3x.js (new) 2.77 MB 🔴 +2.77 MB 🔴 +579 kB 🔴 +440 kB
assets/index-clsAj05E.js (removed) 2.77 MB 🟢 -2.77 MB 🟢 -579 kB 🟢 -440 kB
assets/index-BjZ2xkOi.js (new) 364 kB 🔴 +364 kB 🔴 +75.2 kB 🔴 +61.4 kB
assets/index-xC50uLW1.js (removed) 364 kB 🟢 -364 kB 🟢 -75.2 kB 🟢 -61.4 kB
assets/index-Dwhi54ub.js (removed) 345 B 🟢 -345 B 🟢 -245 B 🟢 -232 B
assets/index-IYnuzYli.js (new) 345 B 🔴 +345 B 🔴 +244 B 🔴 +230 B

Status: 3 added / 3 removed

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

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-BIAunDhS.js (removed) 945 kB 🟢 -945 kB 🟢 -183 kB 🟢 -140 kB
assets/GraphView-ByOky2R_.js (new) 945 kB 🔴 +945 kB 🔴 +183 kB 🔴 +140 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BshASX_C.js (removed) 7.97 kB 🟢 -7.97 kB 🟢 -2.43 kB 🟢 -2.13 kB
assets/UserSelectView-Y0QCfeeB.js (new) 7.97 kB 🔴 +7.97 kB 🔴 +2.43 kB 🔴 +2.13 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CreditsPanel-Bk4jyF2c.js (new) 22.9 kB 🔴 +22.9 kB 🔴 +5.46 kB 🔴 +4.79 kB
assets/CreditsPanel-Deoqr8rZ.js (removed) 22.9 kB 🟢 -22.9 kB 🟢 -5.46 kB 🟢 -4.78 kB
assets/KeybindingPanel-Cu_cNFHE.js (removed) 15.1 kB 🟢 -15.1 kB 🟢 -3.73 kB 🟢 -3.29 kB
assets/KeybindingPanel-R4H8Wogw.js (new) 15.1 kB 🔴 +15.1 kB 🔴 +3.73 kB 🔴 +3.29 kB
assets/ExtensionPanel-B3sWO1Lu.js (removed) 11.9 kB 🟢 -11.9 kB 🟢 -2.79 kB 🟢 -2.45 kB
assets/ExtensionPanel-D_7SYmDY.js (new) 11.9 kB 🔴 +11.9 kB 🔴 +2.79 kB 🔴 +2.45 kB
assets/AboutPanel-CEAEZJ7_.js (removed) 10.1 kB 🟢 -10.1 kB 🟢 -2.62 kB 🟢 -2.31 kB
assets/AboutPanel-D3XurI1G.js (new) 10.1 kB 🔴 +10.1 kB 🔴 +2.62 kB 🔴 +2.31 kB
assets/ServerConfigPanel-BolekE6L.js (new) 8.02 kB 🔴 +8.02 kB 🔴 +2.12 kB 🔴 +1.87 kB
assets/ServerConfigPanel-mYzCwJB2.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.12 kB 🟢 -1.88 kB
assets/UserPanel-DPHOR5Kt.js (new) 7.74 kB 🔴 +7.74 kB 🔴 +2.03 kB 🔴 +1.77 kB
assets/UserPanel-YgieMnDN.js (removed) 7.74 kB 🟢 -7.74 kB 🟢 -2.03 kB 🟢 -1.77 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 — 141 kB (baseline 141 kB) • 🔴 +579 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-D0GljFea.js (new) 53.9 kB 🔴 +53.9 kB 🔴 +8.43 kB 🔴 +7.23 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-sawjz_MJ.js (removed) 53.9 kB 🟢 -53.9 kB 🟢 -8.43 kB 🟢 -7.23 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-BpjWDT5q.js (new) 48.7 kB 🔴 +48.7 kB 🔴 +10.4 kB 🔴 +9.03 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-Dh5YSoAj.js (removed) 48.1 kB 🟢 -48.1 kB 🟢 -10.3 kB 🟢 -8.93 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-BQWvJ87q.js (new) 12.7 kB 🔴 +12.7 kB 🔴 +3.31 kB 🔴 +2.92 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-C-Mz_6bY.js (removed) 12.7 kB 🟢 -12.7 kB 🟢 -3.31 kB 🟢 -2.92 kB
assets/ComfyQueueButton--1nFzG0C.js (removed) 9.22 kB 🟢 -9.22 kB 🟢 -2.5 kB 🟢 -2.2 kB
assets/ComfyQueueButton-DWeXH72c.js (new) 9.22 kB 🔴 +9.22 kB 🔴 +2.5 kB 🔴 +2.2 kB
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-BJCN9JQP.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -794 B 🟢 -695 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-CSSr8cQZ.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +795 B 🔴 +684 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang--c02zpPU.js (new) 848 B 🔴 +848 B 🔴 +473 B 🔴 +413 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-CwUSjwB3.js (removed) 848 B 🟢 -848 B 🟢 -475 B 🟢 -416 B
assets/LazyImage.vue_vue_type_script_setup_true_lang-Wi-CcgaU.js 10.7 kB 10.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-D2s8tnS2.js 1.26 kB 1.26 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-ByrPd5jr.js 1.62 kB 1.62 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 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-CfhblIyx.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/keybindingService-DizUBIr0.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/audioService-BwCIqCGs.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +961 B 🔴 +823 B
assets/audioService-CWe2Lzx-.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -959 B 🟢 -820 B
assets/serverConfigStore-BoHtzifw.js 2.79 kB 2.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 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-C7kmPfw5.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +651 B 🔴 +547 B
assets/audioUtils-z7AmSZyt.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -650 B 🟢 -548 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 — 5.32 MB (baseline 5.32 MB) • ⚪ 0 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-other-z0ajCJVX.js 3.22 MB 3.22 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-PESgPnbc.js 517 B 517 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-aR6ntw5X.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-D2zb6Fg1.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-aBQ_uOio.js 92.6 kB 92.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BZLod3g9.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.87 MB (baseline 3.87 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-Bpumx4Kc.js (removed) 21.9 kB 🟢 -21.9 kB 🟢 -5.52 kB 🟢 -4.88 kB
assets/WidgetRecordAudio-R1MlJkh8.js (new) 21.9 kB 🔴 +21.9 kB 🔴 +5.52 kB 🔴 +4.88 kB
assets/AudioPreviewPlayer-BfuRupf7.js (removed) 14.9 kB 🟢 -14.9 kB 🟢 -3.69 kB 🟢 -3.29 kB
assets/AudioPreviewPlayer-DlzHmRZU.js (new) 14.9 kB 🔴 +14.9 kB 🔴 +3.69 kB 🔴 +3.3 kB
assets/WidgetGalleria-BhHWeN9A.js (new) 5.56 kB 🔴 +5.56 kB 🔴 +1.74 kB 🔴 +1.54 kB
assets/WidgetGalleria-BVda2Gnq.js (removed) 5.56 kB 🟢 -5.56 kB 🟢 -1.74 kB 🟢 -1.54 kB
assets/WidgetColorPicker-BO_vTH3b.js (new) 4.87 kB 🔴 +4.87 kB 🔴 +1.69 kB 🔴 +1.48 kB
assets/WidgetColorPicker-e_H1EW_6.js (removed) 4.87 kB 🟢 -4.87 kB 🟢 -1.69 kB 🟢 -1.48 kB
assets/WidgetMarkdown-B0wfZSFi.js (removed) 4.64 kB 🟢 -4.64 kB 🟢 -1.62 kB 🟢 -1.42 kB
assets/WidgetMarkdown-D-oja7_x.js (new) 4.64 kB 🔴 +4.64 kB 🔴 +1.62 kB 🔴 +1.41 kB
assets/WidgetAudioUI-BLak8LSy.js (removed) 4.33 kB 🟢 -4.33 kB 🟢 -1.44 kB 🟢 -1.29 kB
assets/WidgetAudioUI-vE0FR3pN.js (new) 4.33 kB 🔴 +4.33 kB 🔴 +1.44 kB 🔴 +1.29 kB
assets/WidgetTextarea-BbwvCPGH.js (new) 3.72 kB 🔴 +3.72 kB 🔴 +1.3 kB 🔴 +1.15 kB
assets/WidgetTextarea-Bkqjx0tO.js (removed) 3.72 kB 🟢 -3.72 kB 🟢 -1.3 kB 🟢 -1.15 kB
assets/WidgetInputText-Bi6LwiMd.js (removed) 3.45 kB 🟢 -3.45 kB 🟢 -1.23 kB 🟢 -1.09 kB
assets/WidgetInputText-BJOVXL3Q.js (new) 3.45 kB 🔴 +3.45 kB 🔴 +1.24 kB 🔴 +1.09 kB
assets/WidgetToggleSwitch-DEG0SFjw.js (removed) 3.23 kB 🟢 -3.23 kB 🟢 -1.14 kB 🟢 -1.02 kB
assets/WidgetToggleSwitch-SwIIMrl-.js (new) 3.23 kB 🔴 +3.23 kB 🔴 +1.14 kB 🔴 +1.01 kB
assets/MediaImageBottom-BK271slO.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.05 kB 🟢 -911 B
assets/MediaImageBottom-DgciMEDK.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.05 kB 🔴 +910 B
assets/MediaAudioBottom-BauBKYVv.js (new) 3 kB 🔴 +3 kB 🔴 +1.05 kB 🔴 +929 B
assets/MediaAudioBottom-DGXjvW53.js (removed) 3 kB 🟢 -3 kB 🟢 -1.05 kB 🟢 -915 B
assets/Media3DTop-CQmCiqWg.js (new) 3 kB 🔴 +3 kB 🔴 +1.08 kB 🔴 +912 B
assets/Media3DTop-DXfU3ca-.js (removed) 3 kB 🟢 -3 kB 🟢 -1.08 kB 🟢 -911 B
assets/MediaVideoBottom-BglfctFt.js (removed) 3 kB 🟢 -3 kB 🟢 -1.04 kB 🟢 -912 B
assets/MediaVideoBottom-BUWn-Jky.js (new) 3 kB 🔴 +3 kB 🔴 +1.05 kB 🔴 +925 B
assets/Media3DBottom-CAGE6bRj.js (new) 2.98 kB 🔴 +2.98 kB 🔴 +1.04 kB 🔴 +909 B
assets/Media3DBottom-DFpCiNMH.js (removed) 2.98 kB 🟢 -2.98 kB 🟢 -1.04 kB 🟢 -909 B
assets/WidgetSelect-BKIKBghl.js (new) 2.17 kB 🔴 +2.17 kB 🔴 +675 B 🔴 +570 B
assets/WidgetSelect-QPMkaEIX.js (removed) 2.17 kB 🟢 -2.17 kB 🟢 -675 B 🟢 -572 B
assets/WidgetInputNumber-0OU6Veve.js (new) 2.12 kB 🔴 +2.12 kB 🔴 +664 B 🔴 +558 B
assets/WidgetInputNumber-BsiPYzpB.js (removed) 2.12 kB 🟢 -2.12 kB 🟢 -663 B 🟢 -556 B
assets/Load3D-DyB8dnS5.js (new) 1.94 kB 🔴 +1.94 kB 🔴 +597 B 🔴 +534 B
assets/Load3D-SW_GPcIH.js (removed) 1.94 kB 🟢 -1.94 kB 🟢 -598 B 🟢 -499 B
assets/WidgetLegacy-CzIJUQnE.js (removed) 1.88 kB 🟢 -1.88 kB 🟢 -561 B 🟢 -506 B
assets/WidgetLegacy-Dr95JLYz.js (new) 1.88 kB 🔴 +1.88 kB 🔴 +561 B 🔴 +507 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/MediaAudioTop-RTI8pWy9.js 1.42 kB 1.42 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-Cxl4dc80.js 1.68 kB 1.68 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-BB0lT7C5.js 2.7 kB 2.7 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/WidgetChart-rDmYEWg5.js 2.39 kB 2.39 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-Ds3K3ULR.js 2.15 kB 2.15 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 18 added / 18 removed

@christian-byrne christian-byrne added preview area:widgets area:assets claude-review Add to trigger a PR code review from Claude Code labels Nov 19, 2025
})

if (!response.ok) {
console.error('Failed to fetch output files:', response.statusText)
Copy link

Choose a reason for hiding this comment

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

[architecture] medium Priority

Issue: Missing error handling for API failures - store returns empty array on error
Context: Users won't know when output file fetching fails, and dropdown will appear empty
Suggestion: Add error state tracking and user notification for fetch failures


const outputImages = computed(() => {
return outputs.value.filter((filename) => {
const ext = filename.toLowerCase().split('.').pop() || ''
Copy link

Choose a reason for hiding this comment

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

[performance] low Priority

Issue: Inefficient file extension parsing with repeated split operations
Context: Each computed property calls split('.').pop() for every file, causing O(n*m) complexity
Suggestion: Pre-process file extensions once or use more efficient parsing like filename.lastIndexOf('.')

return ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)
})
})
const outputVideos = computed(() => {
Copy link

Choose a reason for hiding this comment

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

[quality] medium Priority

Issue: Code duplication across outputImages, outputVideos, and outputAudios computed properties
Context: Same filtering logic repeated three times, violating DRY principle
Suggestion: Extract to a helper function: getFilesByExtensions(extensions) or use a shared utility

}
}

const fetchOutputFiles = async () => {
Copy link

Choose a reason for hiding this comment

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

[architecture] high Priority

Issue: Potential race condition with promise caching mechanism
Context: If multiple components call fetchOutputFiles() while promise is resolving, they all get the same promise but thePromise.value is set to null in finally block
Suggestion: Consider using a more robust caching mechanism or ensure promise is cleared only after all consumers are notified

}
// Fetch output files on component mount
onMounted(() => {
Copy link

Choose a reason for hiding this comment

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

[performance] medium Priority

Issue: Unconditional output files fetch on component mount regardless of widget type
Context: Every dropdown widget will fetch output files even if not needed (non-media widgets)
Suggestion: Only fetch output files when assetKind indicates media types (image/video/audio)

import { api } from '@/scripts/api'
import { useAssetsStore } from '@/stores/assetsStore'
import { useQueueStore } from '@/stores/queueStore'
import { useOutputsStore } from '@/stores/outputsStore'
Copy link

Choose a reason for hiding this comment

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

[architecture] critical Priority

Issue: Breaking change - removed queue store dependency without data migration
Context: Old implementation extracted outputs from queueStore.historyTasks but new implementation uses fresh API calls, potentially losing user data
Suggestion: Add migration path or ensure existing workflows aren't broken

}
})()
return outputFiles.map((filename, index) => {
Copy link

Choose a reason for hiding this comment

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

[architecture] medium Priority

Issue: Simplified output handling may not preserve subfolder structure from original queue-based implementation
Context: Old code constructed paths with subfolder info, new code uses direct filenames from API
Suggestion: Verify API returns files with full path or add subfolder handling

class="icon-[lucide--circle-off] size-30 text-zinc-500/20"
/>
</div>
<template v-if="items.length === 0">
Copy link

Choose a reason for hiding this comment

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

[quality] low Priority

Issue: Template structure change from div to template is good but missing semantic improvement
Context: Using template wrapper is more performant than div for conditional rendering
Suggestion: Consider adding proper ARIA attributes or role for better accessibility

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Comprehensive PR Review

This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another.

Review Summary

PR: fix: dropdown widget fetching output files (#6734)
Impact: 113 additions, 38 deletions across 3 files
Focus: Refactoring dropdown widget to use dedicated outputs store instead of queue store

Issue Distribution

  • Critical: 1
  • High: 1
  • Medium: 4
  • Low: 2

Category Breakdown

  • Architecture: 5 issues
  • Code Quality: 2 issues
  • Performance: 1 issues
  • Security: 0 issues

Key Findings

Architecture & Design

Critical: The migration from queue store to outputs store represents a breaking change that may lose existing user data or functionality. The old implementation extracted outputs from queue history with subfolder structure, while the new approach uses direct API calls.

High Priority: Race condition potential in the outputs store promise caching mechanism could lead to inconsistent state when multiple components fetch simultaneously.

Medium Priority: The new outputs store lacks proper error handling, which means users won't be notified when API calls fail, resulting in empty dropdowns without explanation.

Performance Impact

The unconditional fetching of output files on component mount for all dropdown widgets is inefficient - this should only happen for media-type widgets. The file extension filtering also uses inefficient string operations that could be optimized.

Code Quality

Several areas need improvement: code duplication in file type filtering logic, inconsistent type handling between string/number widget values and string-only output files, and missing proper error states.

Positive Observations

  • Good architectural direction separating concerns with dedicated outputs store
  • Improved template structure using template instead of div for conditional rendering
  • Clean separation of file types with computed properties
  • Proper TypeScript usage with explicit type definitions

References

  • ComfyUI Frontend Repository Guide
  • Vue 3 Best Practices
  • Pinia Store Patterns

Next Steps

  1. Critical: Address the breaking change concern - ensure data migration path exists
  2. High: Fix race condition in outputs store promise handling
  3. Medium: Add proper error handling and user feedback for API failures
  4. Medium: Optimize performance by conditional fetching based on widget type
  5. Add tests to verify output file fetching behavior
  6. Consider backward compatibility implications

Testing Recommendations

  • Test dropdown behavior with and without existing output files
  • Verify subfolder structure preservation
  • Test error scenarios (API failures, network issues)
  • Verify performance with large numbers of output files
  • Test concurrent widget mounting scenarios

This is a comprehensive automated review. For architectural decisions requiring human judgment, please request additional manual review.

@github-actions github-actions bot removed the claude-review Add to trigger a PR code review from Claude Code label Nov 19, 2025
Comment on lines +71 to +76
<div class="absolute inset-0 flex items-center justify-center">
<i
title="No items"
class="icon-[lucide--circle-off] size-30 text-zinc-500/20"
/>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

note: We also have this

(not blocking this PR).

Comment on lines 10 to 27
const outputImages = computed(() => {
return outputs.value.filter((filename) => {
const ext = filename.toLowerCase().split('.').pop() || ''
return ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)
})
})
const outputVideos = computed(() => {
return outputs.value.filter((filename) => {
const ext = filename.toLowerCase().split('.').pop() || ''
return ['mp4', 'webm', 'mov', 'avi', 'mkv'].includes(ext)
})
})
const outputAudios = computed(() => {
return outputs.value.filter((filename) => {
const ext = filename.toLowerCase().split('.').pop() || ''
return ['mp3', 'ogg', 'wav', 'flac'].includes(ext)
})
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it could be optimized to only need a single traversal?

import { defineStore } from 'pinia'
import { computed, ref } from 'vue'

export const useOutputsStore = defineStore('outputs', () => {
Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/src/platform/assets/composables/media/useMediaAssets.ts
const outputAssets = useMediaAssets('output')
It looks like most of the code in src/stores/outputsStore.ts could be replaced with something like const outputAssets = useMediaAssets('output'). I think it would be better to use the existing store. What do you think?

outputs.add(annotatedPath)
}
})
const outputFiles = ((): string[] => {
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

@viva-jinyi viva-jinyi left a comment

Choose a reason for hiding this comment

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

It doesn’t seem to cover both Cloud and OSS. Since internal/files can only fetch output files in the OSS environment, I think it would be better to use useMediaAssets.ts. It’s already implemented to work for both OSS and Cloud, and we can update it later when the new API becomes available.

@LittleSound
Copy link
Collaborator Author

I tried this API, but it doesn't work. I don't know why. you can see this image, so I use the fetch to get the list

However, I still did it according to your request. You can see it here #6809, and you can actually run the code to compare their differences.

CleanShot 2025-11-22 at 00 12 31@2x

@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Nov 21, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Output dropdown handling refactored to use media assets-based approach with client-side type filtering. FormDropdown's local state replaced with Vue defineModel for two-way binding. FormDropdownMenu's DOM structure adjusted for "No items" rendering.

Changes

Cohort / File(s) Summary
Output dropdown widget refinement
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue
Replaces queue-based output extraction with media assets-based approach. Adds getMediaTypeFromFilename and toAssertType helper for client-side asset type handling and filtering by image/video/audio/model. Introduces handleIsOpenUpdate trigger to refresh media assets on dropdown open. Extends output item construction with asset-backed preview URLs and annotated naming. Updates accept/types logic to include audio.
Dropdown form state synchronization
src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue
Replaces local reactive isOpen ref with defineModel<boolean>('isOpen', { default: false }) for two-way parent-child binding. All state reads/writes now use the model without functional logic changes.
Dropdown menu rendering structure
src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenu.vue
Wraps "No items" placeholder div in template element and introduces min-h-50 spacer div, changing DOM structure and height handling for empty state.

Sequence Diagram

sequenceDiagram
    participant User
    participant WidgetSelectDropdown
    participant useMediaAssets
    participant Asset Store
    
    User->>WidgetSelectDropdown: Opens dropdown
    WidgetSelectDropdown->>WidgetSelectDropdown: handleIsOpenUpdate triggered
    WidgetSelectDropdown->>useMediaAssets: Fetch media assets
    useMediaAssets->>Asset Store: Query assets
    Asset Store-->>useMediaAssets: Return asset list
    useMediaAssets-->>WidgetSelectDropdown: Provide media assets
    WidgetSelectDropdown->>WidgetSelectDropdown: Filter by media type<br/>(image/video/audio/model)
    WidgetSelectDropdown->>WidgetSelectDropdown: Map to AssetKind<br/>via toAssertType
    WidgetSelectDropdown->>WidgetSelectDropdown: Build output items<br/>with preview URLs
    WidgetSelectDropdown-->>User: Render dropdown with<br/>filtered outputs
Loading
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rizumu/fix/dropdown-widget-fetching-output-files

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

Copy link

@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/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue (1)

58-58: Switch to defineModel for isOpen looks good; consider external control behavior

Using defineModel('isOpen') cleanly exposes the open state so parents can observe it (as you do with @update:is-open), while keeping internal toggling logic unchanged.

If you ever intend to let parents control the open state via v-model:is-open, you'll likely want a small watcher that hides/shows the Popover when isOpen changes externally; right now, external updates wouldn’t drive the popover visibility, only internal handlers do.

src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue (1)

348-352: Open-state refresh hook is good; tighten conditions and check loading type

Wiring handleIsOpenUpdate to @update:is-open is a nice way to refresh outputs only when the dropdown opens. A couple of details to tighten:

  • If outputMediaAssets.loading is a Ref<boolean>, this check should use .value; otherwise it will always be truthy:

    if (isOpen && !outputMediaAssets.loading.value) {
      outputMediaAssets.refresh()
    }
  • To fully honor the earlier feedback about avoiding unnecessary output fetches for non-media widgets, consider short-circuiting when assetKind doesn’t need outputs (e.g., not image/video/audio/model):

    function handleIsOpenUpdate(isOpen: boolean) {
  • if (isOpen && !outputMediaAssets.loading) {

  • outputMediaAssets.refresh()

  • }

  • const kind = props.assetKind
  • if (
  • !isOpen ||
  • !kind ||
  • !['image', 'video', 'audio', 'model'].includes(kind)
  • ) {
  • return
  • }
  • if (!outputMediaAssets.loading /* or .loading.value if it's a ref */) {
  • outputMediaAssets.refresh()
  • }
    }

This keeps the refresh localized to dropdown-open events and avoids hitting the outputs API for non-media widgets.





Also applies to: 371-371

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7a0302ba7ab1fc2a3f6c437ab6c3688cd8951c1d and 705947dfd7e41e80e88958979ef7af21da8c8acc.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue` (6 hunks)
* `src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue` (1 hunks)
* `src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenu.vue` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue</summary>

[error] 8-8: Unable to resolve path to module '@/platform/assets/composables/media/useMediaAssets'.

(import-x/no-unresolved)

---

[error] 25-25: Unable to resolve path to module '@/utils/formatUtil'.

(import-x/no-unresolved)

</details>

</details>

</details>

<details>
<summary>⏰ 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). (3)</summary>

* GitHub Check: test
* GitHub Check: lint-and-format
* GitHub Check: setup

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenu.vue (1)</summary><blockquote>

`70-78`: **Empty-state template restructuring looks good**

Using a `<template v-if>` for the “No items” state plus the `min-h-50` spacer keeps the grid layout stable while avoiding an extra wrapper element. No functional or readability issues here.

</blockquote></details>
<details>
<summary>src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue (1)</summary><blockquote>

`8-9`: **Import paths are correctly configured via vite aliases; review comment is incorrect.**

The imports in WidgetSelectDropdown.vue are properly resolved. The vite configuration (vite.config.mts, lines 685–687) contains an alias that maps `@/utils/formatUtil` to `/packages/shared-frontend-utils/src/formatUtil.ts`, where `getMediaTypeFromFilename` is exported. Similarly, `useMediaAssets` and `useToastStore` both reference valid file paths that exist in the codebase. 

The ESLint import-x/no-unresolved errors referenced in the review appear to be false positives, likely due to ESLint not being configured to understand vite aliases. The imports themselves are not unresolved and follow the established pattern in the codebase.



> Likely an incorrect or invalid review comment.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +124 to 143
if (!['image', 'video', 'audio'].includes(props.assetKind ?? '')) return []
// Filter assets by media type using getMediaTypeFromFilename
const outputFiles = outputMediaAssets.media.value.filter((asset) => {
const mediaType = getMediaTypeFromFilename(asset.name)
return toAssertType(mediaType) === props.assetKind
})
return Array.from(outputs).map((output, index) => ({
id: `output-${index}`,
mediaSrc: getMediaUrl(output.replace(' [output]', ''), 'output'),
name: output,
label: getDisplayLabel(output),
metadata: ''
}))
return outputFiles.map((asset, index) => {
// Add [output] annotation so the preview component knows the type
const annotatedPath = `${asset.name} [output]`
return {
id: `output-${index}`,
mediaSrc: asset.preview_url || getMediaUrl(asset.name, 'output'),
name: annotatedPath,
label: getDisplayLabel(annotatedPath),
metadata: ''
}
})
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

outputItems guard prevents model outputs from ever showing

In outputItems, this guard:

if (!['image', 'video', 'audio'].includes(props.assetKind ?? '')) return []

short-circuits for assetKind === 'model', even though toAssertType explicitly maps a '3D' media type to 'model'. That means 3D/model outputs will never appear in the dropdown, despite having logic to support them.

A minimal fix is to include 'model' in the allowed list so those outputs can be surfaced:

-const outputItems = computed<DropdownItem[]>(() => {
-  if (!['image', 'video', 'audio'].includes(props.assetKind ?? '')) return []
+const outputItems = computed<DropdownItem[]>(() => {
+  if (!['image', 'video', 'audio', 'model'].includes(props.assetKind ?? '')) {
+    return []
+  }
@@
-  const outputFiles = outputMediaAssets.media.value.filter((asset) => {
-    const mediaType = getMediaTypeFromFilename(asset.name)
-    return toAssertType(mediaType) === props.assetKind
-  })
+  const assetKind = props.assetKind
+
+  const outputFiles = outputMediaAssets.media.value.filter((asset) => {
+    const mediaType = getMediaTypeFromFilename(asset.name)
+    return assetKind != null && toAssertType(mediaType) === assetKind
+  })

That keeps the filtering behavior but actually allows model/3D outputs to show up. If assetKind: 'unknown' should also see outputs, you may want to relax the guard further.

Also applies to: 145-158, 340-346


I'll help you verify the review comment about the WidgetSelectDropdown.vue file. Let me examine the code and understand the context.


---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the target file
cat src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue | head -200

Length of output: 5887


🏁 Script executed:

#!/bin/bash
# Search for toAssertType function definition
rg -n "toAssertType" --type=ts --type=js --type=vue -A 5 -B 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for getMediaTypeFromFilename to understand media type mapping
rg -n "getMediaTypeFromFilename" --type=ts --type=js --type=vue -A 5 -B 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Look for assetKind prop definition
rg -n "assetKind" src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue -B 3 -A 3

Length of output: 1553



---

🏁 Script executed:

```shell
#!/bin/bash
# Find AssetKind type definition
rg -n "type AssetKind" -A 5 -B 2

Length of output: 311


🏁 Script executed:

#!/bin/bash
# Get full context around getMediaUrl function
sed -n '338,347p' src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue

Length of output: 240


🏁 Script executed:

#!/bin/bash
# Get the complete outputItems and toAssertType functions
sed -n '123,158p' src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue

Length of output: 1047


Fix guards to allow model outputs to display

The outputItems computed property currently returns empty for any assetKind not in ['image', 'video', 'audio']. However, toAssertType explicitly maps the '3D' media type to 'model' (line 145), and AssetKind type definition includes 'model' as valid, yet the guard at line 124 prevents model outputs from ever being processed.

Additionally, getMediaUrl (line 344) has a similar guard that excludes both 'audio' and 'model' from media URL generation.

Required fixes:

  1. Line 124: Add 'model' to the allowed asset kinds in outputItems
  2. Line 344: Update getMediaUrl guard to include 'audio' and 'model' (or make it comprehensive based on intended support)

The suggested diff in the original review for line 124 is appropriate. Apply similar logic to the getMediaUrl guard to ensure model assets can display media previews.

🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue
around lines 124-143 and line ~344, the outputItems computed property and
getMediaUrl guard currently exclude 'model' (and getMediaUrl also excludes
'audio'), preventing model outputs from being processed/displayed; update the
guard at ~124 to include 'model' in the allowed asset kinds (e.g.,
['image','video','audio','model']) and adjust the getMediaUrl guard at ~344 to
include 'audio' and 'model' (or broaden the guard to allow all AssetKind values
your UI supports) so model assets are passed through to preview URL generation
and rendering.

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

Labels

area:assets area:widgets size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants