Skip to content

Conversation

@AustinMroz
Copy link
Collaborator

@AustinMroz AustinMroz commented Feb 11, 2026

Tests are failing on #8282. Cursory investigation suggests that this is an issue of token management and not the branch in question, so I'm opening a copy of the branch that's in-repo

┆Issue is synchronized with this Notion page by Unito

@github-actions
Copy link

github-actions bot commented Feb 11, 2026

Playwright: ✅ 523 passed, 0 failed · 2 flaky

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

@github-actions
Copy link

github-actions bot commented Feb 11, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 02/11/2026, 11:45:52 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

This PR introduces batch image file handling with async node creation, enabling drag-and-drop of multiple images to create connected LoadImage nodes and a BatchImagesNode. It exports clipboard cloning utilities, makes image pasting asynchronous, and updates graph null-handling to support the new workflows.

Changes

Cohort / File(s) Summary
Image & File Handling
src/scripts/app.ts, src/utils/eventUtils.ts, src/utils/eventUtils.test.ts
Enhanced drag-and-drop to differentiate single File vs FileList drops; added handleFileList and positionBatchNodes to process multiple image files, create batch nodes, and position them; extractFileFromDragEvent now returns FileList for multi-file drops with image types.
Node Creation Utilities
src/utils/litegraphUtil.ts, src/utils/litegraphUtil.test.ts
Exported createNode helper that asynchronously creates LiteGraph nodes at canvas mouse position, adds them to graph, triggers redraws, and handles errors with toast notifications; added comprehensive test coverage.
Paste & Clipboard Operations
src/composables/usePaste.ts, src/composables/usePaste.test.ts
Exported cloneDataTransfer for safe clipboard data copying; made pasteImageNode async with optional node creation; added pasteImageNodes for batch file processing; updated tests with mocked async behavior and cloneDataTransfer validation.
Graph Core Updates
src/lib/litegraph/src/LGraph.ts, src/lib/litegraph/src/LGraph.test.ts
Updated LGraph.add overload to accept null nodes (LGraphNode | LGraphGroup | null); added test validating null inputs return gracefully without modifying graph.
Service & Test Updates
src/services/litegraphService.ts, src/scripts/app.test.ts
Removed TypeScript suppression comments; added comprehensive app.test.ts suite covering handleFileList, positionBatchNodes, and handleFile with mocked graph operations and drag-drop interactions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Canvas
    participant EventUtils
    participant App
    participant NodeCreation
    participant Graph

    User->>Canvas: Drop multiple image files
    Canvas->>EventUtils: extractFileFromDragEvent
    EventUtils-->>Canvas: FileList (multiple images)
    Canvas->>App: handleFileList(fileList)
    
    App->>NodeCreation: createNode for each file
    NodeCreation->>Graph: add LoadImage node
    Graph-->>NodeCreation: LGraphNode
    NodeCreation-->>App: LGraphNode[]
    
    App->>NodeCreation: pasteImageNodes(fileList)
    NodeCreation-->>App: Promise<LGraphNode[]>
    
    App->>NodeCreation: createNode(BatchImagesNode)
    NodeCreation->>Graph: add BatchImagesNode
    Graph-->>NodeCreation: BatchNode
    NodeCreation-->>App: BatchNode
    
    App->>App: positionBatchNodes(imageNodes, batchNode)
    App->>Graph: Connect image outputs to batch
    Graph-->>App: Connections established
    
    App-->>User: Batch + image nodes created and positioned
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Hop, hop, huzzah! Multiple images now fall like carrots from the sky,
Async nodes spring up where once they stood alone, all positioned neat and high,
DataTransfer clones protect our clipboard treasures bright,
Batch nodes weave them all together, a beautiful draggy-drop delight! 🥕✨

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete; it does not follow the provided template structure and lacks required sections like Summary, Changes (What/Breaking/Dependencies), and Review Focus. Fill out the PR description template with a one-sentence summary, detailed changes, any breaking changes, dependencies, and key review focus areas.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Copy of Batch Drag & Drop Images' is vague and does not clearly convey the primary changes; it references that this is a copy of another PR without explaining what the feature actually does. Consider using a more descriptive title like 'Add batch image node creation for drag-and-drop' or 'Support multiple file drag-and-drop with batch image node'

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

✨ 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 austin/batch-drag-and-drop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link

Bundle Size Report

Summary

  • Raw size: 19.8 MB baseline 19.8 MB — 🔴 +2.29 kB
  • Gzip: 4.23 MB baseline 4.22 MB — 🔴 +623 B
  • Brotli: 3.28 MB baseline 3.28 MB — 🔴 +408 B
  • Bundles: 235 current • 235 baseline • 111 added / 111 removed

Category Glance
Data & Services 🔴 +2.29 kB (2.11 MB) · Vendor & Third-Party ⚪ 0 B (8.67 MB) · Other ⚪ 0 B (7.29 MB) · Graph Workspace ⚪ 0 B (872 kB) · Panels & Settings ⚪ 0 B (454 kB) · Utilities & Hooks ⚪ 0 B (237 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-BD_pLD8B.js (removed) 22 kB 🟢 -22 kB 🟢 -7.17 kB 🟢 -6.25 kB
assets/index-rrYbhGBD.js (new) 22 kB 🔴 +22 kB 🔴 +7.17 kB 🔴 +6.25 kB

Status: 1 added / 1 removed

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

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-CFagFvcj.js (removed) 872 kB 🟢 -872 kB 🟢 -188 kB 🟢 -143 kB
assets/GraphView-Ciqm1h8c.js (new) 872 kB 🔴 +872 kB 🔴 +188 kB 🔴 +143 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-CXsYSceV.js (removed) 15.4 kB 🟢 -15.4 kB 🟢 -3.3 kB 🟢 -2.81 kB
assets/CloudSurveyView-DsIKEcjO.js (new) 15.4 kB 🔴 +15.4 kB 🔴 +3.3 kB 🔴 +2.82 kB
assets/CloudLoginView-DPV5RAV5.js (removed) 10.1 kB 🟢 -10.1 kB 🟢 -2.94 kB 🟢 -2.58 kB
assets/CloudLoginView-Exeld3ng.js (new) 10.1 kB 🔴 +10.1 kB 🔴 +2.93 kB 🔴 +2.58 kB
assets/UserCheckView-B_-Uk_vV.js (removed) 8.38 kB 🟢 -8.38 kB 🟢 -2.21 kB 🟢 -1.92 kB
assets/UserCheckView-soTNkz8V.js (new) 8.38 kB 🔴 +8.38 kB 🔴 +2.21 kB 🔴 +1.92 kB
assets/CloudSignupView-D8rjlySB.js (removed) 7.46 kB 🟢 -7.46 kB 🟢 -2.35 kB 🟢 -2.06 kB
assets/CloudSignupView-DJEBuNRg.js (new) 7.46 kB 🔴 +7.46 kB 🔴 +2.35 kB 🔴 +2.06 kB
assets/CloudLayoutView-CPN_iOK_.js (removed) 6.45 kB 🟢 -6.45 kB 🟢 -2.11 kB 🟢 -1.83 kB
assets/CloudLayoutView-CTlfeTwN.js (new) 6.45 kB 🔴 +6.45 kB 🔴 +2.11 kB 🔴 +1.85 kB
assets/CloudForgotPasswordView-DkDgYEEf.js (removed) 5.57 kB 🟢 -5.57 kB 🟢 -1.95 kB 🟢 -1.72 kB
assets/CloudForgotPasswordView-SHu1VxoD.js (new) 5.57 kB 🔴 +5.57 kB 🔴 +1.94 kB 🔴 +1.71 kB
assets/CloudAuthTimeoutView-DLsliBFw.js (new) 4.92 kB 🔴 +4.92 kB 🔴 +1.78 kB 🔴 +1.56 kB
assets/CloudAuthTimeoutView-Dqxqes91.js (removed) 4.92 kB 🟢 -4.92 kB 🟢 -1.78 kB 🟢 -1.56 kB
assets/CloudSubscriptionRedirectView-D57XMKoc.js (new) 4.72 kB 🔴 +4.72 kB 🔴 +1.79 kB 🔴 +1.58 kB
assets/CloudSubscriptionRedirectView-DJdFzZal.js (removed) 4.72 kB 🟢 -4.72 kB 🟢 -1.79 kB 🟢 -1.58 kB
assets/UserSelectView-C70Wj_Oa.js (new) 4.46 kB 🔴 +4.46 kB 🔴 +1.62 kB 🔴 +1.45 kB
assets/UserSelectView-CyUUBUFh.js (removed) 4.46 kB 🟢 -4.46 kB 🟢 -1.62 kB 🟢 -1.46 kB
assets/CloudSorryContactSupportView-SQIXD_6I.js 1.02 kB 1.02 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-DymeLvkK.js 296 B 296 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WorkspacePanel-CRpgmfDY.js (new) 26.8 kB 🔴 +26.8 kB 🔴 +5.63 kB 🔴 +4.93 kB
assets/WorkspacePanel-Q-vV7Ehz.js (removed) 26.8 kB 🟢 -26.8 kB 🟢 -5.62 kB 🟢 -4.95 kB
assets/SecretsPanel-CgFuNysc.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -5.29 kB 🟢 -4.63 kB
assets/SecretsPanel-t98C8Hft.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +5.29 kB 🔴 +4.64 kB
assets/LegacyCreditsPanel-BacvXzNb.js (new) 20.8 kB 🔴 +20.8 kB 🔴 +5.61 kB 🔴 +4.93 kB
assets/LegacyCreditsPanel-DVbw72Uu.js (removed) 20.8 kB 🟢 -20.8 kB 🟢 -5.61 kB 🟢 -4.94 kB
assets/SubscriptionPanel-B7RHiL9B.js (new) 18.7 kB 🔴 +18.7 kB 🔴 +4.75 kB 🔴 +4.22 kB
assets/SubscriptionPanel-BXSHeW9J.js (removed) 18.7 kB 🟢 -18.7 kB 🟢 -4.76 kB 🟢 -4.22 kB
assets/KeybindingPanel-3Bv70jmH.js (new) 12.6 kB 🔴 +12.6 kB 🔴 +3.63 kB 🔴 +3.22 kB
assets/KeybindingPanel-CQVmovWJ.js (removed) 12.6 kB 🟢 -12.6 kB 🟢 -3.63 kB 🟢 -3.21 kB
assets/ExtensionPanel-C04xrWoc.js (removed) 9.51 kB 🟢 -9.51 kB 🟢 -2.69 kB 🟢 -2.38 kB
assets/ExtensionPanel-LzJIX_v0.js (new) 9.51 kB 🔴 +9.51 kB 🔴 +2.69 kB 🔴 +2.4 kB
assets/AboutPanel-C6_n2K47.js (new) 8.62 kB 🔴 +8.62 kB 🔴 +2.45 kB 🔴 +2.22 kB
assets/AboutPanel-xN00_Wts.js (removed) 8.62 kB 🟢 -8.62 kB 🟢 -2.45 kB 🟢 -2.23 kB
assets/ServerConfigPanel-BpC6VzWJ.js (new) 6.65 kB 🔴 +6.65 kB 🔴 +2.17 kB 🔴 +1.94 kB
assets/ServerConfigPanel-DMbNfn8_.js (removed) 6.65 kB 🟢 -6.65 kB 🟢 -2.17 kB 🟢 -1.97 kB
assets/UserPanel-C4iLT1th.js (removed) 6.29 kB 🟢 -6.29 kB 🟢 -2.03 kB 🟢 -1.78 kB
assets/UserPanel-DHgvWpkz.js (new) 6.29 kB 🔴 +6.29 kB 🔴 +2.03 kB 🔴 +1.78 kB
assets/cloudRemoteConfig-CkF4Efwp.js (removed) 1.45 kB 🟢 -1.45 kB 🟢 -718 B 🟢 -620 B
assets/cloudRemoteConfig-jXCkFDKr.js (new) 1.45 kB 🔴 +1.45 kB 🔴 +717 B 🔴 +619 B
assets/refreshRemoteConfig-BbzUAJMJ.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +520 B 🔴 +458 B
assets/refreshRemoteConfig-CZm8o3xY.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -523 B 🟢 -469 B
assets/config-DTkEo7Es.js 996 B 996 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B9-DRLUq.js 33.3 kB 33.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BBhF1Ra7.js 23.9 kB 23.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BPmk7MJq.js 28 kB 28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C6ThYRJH.js 27.1 kB 27.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CDls4Tnk.js 29.2 kB 29.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CXVW-KKN.js 27.3 kB 27.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CZBqKC2c.js 28.1 kB 28.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CzQJLe5j.js 29.8 kB 29.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-IyjFFWib.js 31.6 kB 31.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-LLMHzl4C.js 37.6 kB 37.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-XT2QcP7J.js 23.3 kB 23.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 11 added / 11 removed

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-DJt7FeDo.js (new) 3.4 kB 🔴 +3.4 kB 🔴 +1.18 kB 🔴 +986 B
assets/auth-DmjGlC6-.js (removed) 3.4 kB 🟢 -3.4 kB 🟢 -1.18 kB 🟢 -993 B
assets/SignUpForm-Bt196e3d.js (removed) 3.01 kB 🟢 -3.01 kB 🟢 -1.23 kB 🟢 -1.1 kB
assets/SignUpForm-DPCC5LkE.js (new) 3.01 kB 🔴 +3.01 kB 🔴 +1.23 kB 🔴 +1.09 kB
assets/UpdatePasswordContent-C8A7-DjR.js (removed) 2.38 kB 🟢 -2.38 kB 🟢 -1.08 kB 🟢 -955 B
assets/UpdatePasswordContent-kDmbZ6Wk.js (new) 2.38 kB 🔴 +2.38 kB 🔴 +1.08 kB 🔴 +953 B
assets/firebaseAuthStore-CtG55T08.js (new) 803 B 🔴 +803 B 🔴 +398 B 🔴 +359 B
assets/firebaseAuthStore-CX4CajM3.js (removed) 803 B 🟢 -803 B 🟢 -396 B 🟢 -360 B
assets/auth-CKi4sHX6.js (removed) 317 B 🟢 -317 B 🟢 -202 B 🟢 -177 B
assets/auth-DcISKSme.js (new) 317 B 🔴 +317 B 🔴 +203 B 🔴 +192 B
assets/PasswordFields-Dz_bXydQ.js 4.51 kB 4.51 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WorkspaceProfilePic-D2K-asRs.js 1.57 kB 1.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Editors & Dialogs — 751 B (baseline 751 B) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-BqZ7usoW.js (new) 751 B 🔴 +751 B 🔴 +387 B 🔴 +335 B
assets/useSubscriptionDialog-DJsbujgs.js (removed) 751 B 🟢 -751 B 🟢 -388 B 🟢 -335 B

Status: 1 added / 1 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useTerminalTabs-40ChfepZ.js (removed) 9.86 kB 🟢 -9.86 kB 🟢 -3.41 kB 🟢 -3 kB
assets/useTerminalTabs-ChbOFSZi.js (new) 9.86 kB 🔴 +9.86 kB 🔴 +3.41 kB 🔴 +3 kB
assets/ComfyQueueButton-3PMYkmWJ.js (new) 7.17 kB 🔴 +7.17 kB 🔴 +2.32 kB 🔴 +2.07 kB
assets/ComfyQueueButton-k93wIPoD.js (removed) 7.17 kB 🟢 -7.17 kB 🟢 -2.32 kB 🟢 -2.07 kB
assets/SubscribeButton-BVwxMllh.js (removed) 2.35 kB 🟢 -2.35 kB 🟢 -1.02 kB 🟢 -892 B
assets/SubscribeButton-fAiWrUgB.js (new) 2.35 kB 🔴 +2.35 kB 🔴 +1.02 kB 🔴 +891 B
assets/cloudFeedbackTopbarButton-C0KrAOkP.js (new) 1.61 kB 🔴 +1.61 kB 🔴 +867 B 🔴 +772 B
assets/cloudFeedbackTopbarButton-D9zIdkrZ.js (removed) 1.61 kB 🟢 -1.61 kB 🟢 -868 B 🟢 -780 B
assets/ComfyQueueButton-B4ATxxv_.js (removed) 808 B 🟢 -808 B 🟢 -401 B 🟢 -359 B
assets/ComfyQueueButton-HT4exooq.js (new) 808 B 🔴 +808 B 🔴 +401 B 🔴 +361 B
assets/Button-DJ97zW2s.js 3 kB 3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudBadge-bwJLQpsu.js 1.24 kB 1.24 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-C7EnSn9A.js 7.45 kB 7.45 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-ki0RgWjr.js 1.17 kB 1.17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-C5FBfFE1.js 1.84 kB 1.84 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Data & Services — 2.11 MB (baseline 2.1 MB) • 🔴 +2.29 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-Ct_j7d8M.js (new) 1.33 MB 🔴 +1.33 MB 🔴 +299 kB 🔴 +231 kB
assets/dialogService-l0Q7BbCf.js (removed) 1.33 MB 🟢 -1.33 MB 🟢 -298 kB 🟢 -231 kB
assets/api-BKWPKldg.js (new) 647 kB 🔴 +647 kB 🔴 +146 kB 🔴 +116 kB
assets/api-D4t0n0VP.js (removed) 647 kB 🟢 -647 kB 🟢 -146 kB 🟢 -116 kB
assets/load3dService-Bm1xyZ1j.js (new) 91 kB 🔴 +91 kB 🔴 +19 kB 🔴 +16.4 kB
assets/load3dService-CaG09PFS.js (removed) 91 kB 🟢 -91 kB 🟢 -19 kB 🟢 -16.4 kB
assets/systemStatsStore-BYpB5qLm.js (new) 12.2 kB 🔴 +12.2 kB 🔴 +4.27 kB 🔴 +3.74 kB
assets/systemStatsStore-DdFLEu6N.js (removed) 12.2 kB 🟢 -12.2 kB 🟢 -4.26 kB 🟢 -3.74 kB
assets/releaseStore-Bsv5jsae.js (new) 7.96 kB 🔴 +7.96 kB 🔴 +2.22 kB 🔴 +1.95 kB
assets/releaseStore-DYAOu3cf.js (removed) 7.96 kB 🟢 -7.96 kB 🟢 -2.22 kB 🟢 -1.95 kB
assets/keybindingService-DnIo12ed.js (new) 6.57 kB 🔴 +6.57 kB 🔴 +1.72 kB 🔴 +1.49 kB
assets/keybindingService-T4BoKKLl.js (removed) 6.57 kB 🟢 -6.57 kB 🟢 -1.72 kB 🟢 -1.49 kB
assets/bootstrapStore-BeZLF5sj.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +871 B 🔴 +788 B
assets/bootstrapStore-CiGBinqD.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -873 B 🟢 -791 B
assets/userStore-CfCEHrv-.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +718 B 🔴 +670 B
assets/userStore-D2oIDvf8.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -720 B 🟢 -678 B
assets/audioService-B8lV2dgy.js (removed) 1.73 kB 🟢 -1.73 kB 🟢 -845 B 🟢 -731 B
assets/audioService-CXX23-5U.js (new) 1.73 kB 🔴 +1.73 kB 🔴 +846 B 🔴 +724 B
assets/releaseStore-D3YQ7eYy.js (removed) 775 B 🟢 -775 B 🟢 -393 B 🟢 -346 B
assets/releaseStore-DJM2NRAq.js (new) 775 B 🔴 +775 B 🔴 +391 B 🔴 +342 B
assets/workflowDraftStore-BNcWhgTb.js (new) 751 B 🔴 +751 B 🔴 +388 B 🔴 +340 B
assets/workflowDraftStore-nz8AWtTW.js (removed) 751 B 🟢 -751 B 🟢 -388 B 🟢 -341 B
assets/dialogService-DA-ekNgY.js (removed) 740 B 🟢 -740 B 🟢 -376 B 🟢 -336 B
assets/dialogService-Dbc9NFc2.js (new) 740 B 🔴 +740 B 🔴 +380 B 🔴 +338 B
assets/dialogStore-SMYg02Jv.js 4.1 kB 4.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/serverConfigStore-De5AGM62.js 2.32 kB 2.32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 12 added / 12 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-B2ZdNRzT.js (new) 178 kB 🔴 +178 kB 🔴 +39.4 kB 🔴 +32.8 kB
assets/useConflictDetection-DP747Wei.js (removed) 178 kB 🟢 -178 kB 🟢 -39.4 kB 🟢 -32.8 kB
assets/useLoad3d-C4Q67LSA.js (removed) 14.6 kB 🟢 -14.6 kB 🟢 -3.63 kB 🟢 -3.21 kB
assets/useLoad3d-E6HbD8qe.js (new) 14.6 kB 🔴 +14.6 kB 🔴 +3.63 kB 🔴 +3.21 kB
assets/useLoad3dViewer-ChKgd4TY.js (new) 14.1 kB 🔴 +14.1 kB 🔴 +3.15 kB 🔴 +2.8 kB
assets/useLoad3dViewer-DERE4uq2.js (removed) 14.1 kB 🟢 -14.1 kB 🟢 -3.15 kB 🟢 -2.8 kB
assets/useFeatureFlags-BQS4RWHj.js (removed) 3.32 kB 🟢 -3.32 kB 🟢 -1.04 kB 🟢 -904 B
assets/useFeatureFlags-CjeNa1TV.js (new) 3.32 kB 🔴 +3.32 kB 🔴 +1.04 kB 🔴 +897 B
assets/useWorkspaceUI-CbAch5ZJ.js (new) 3.21 kB 🔴 +3.21 kB 🔴 +901 B 🔴 +774 B
assets/useWorkspaceUI-DnobBOXu.js (removed) 3.21 kB 🟢 -3.21 kB 🟢 -900 B 🟢 -777 B
assets/useSubscriptionCredits-1WBVj0my.js (new) 2.75 kB 🔴 +2.75 kB 🔴 +1.04 kB 🔴 +900 B
assets/useSubscriptionCredits-BT0FXu-h.js (removed) 2.75 kB 🟢 -2.75 kB 🟢 -1.04 kB 🟢 -901 B
assets/subscriptionCheckoutUtil-BrhbrdRe.js (removed) 2.53 kB 🟢 -2.53 kB 🟢 -1.06 kB 🟢 -952 B
assets/subscriptionCheckoutUtil-DOq2V0qG.js (new) 2.53 kB 🔴 +2.53 kB 🔴 +1.06 kB 🔴 +957 B
assets/useErrorHandling-CWRYCQYl.js (removed) 1.34 kB 🟢 -1.34 kB 🟢 -556 B 🟢 -478 B
assets/useErrorHandling-CYa6zSnw.js (new) 1.34 kB 🔴 +1.34 kB 🔴 +555 B 🔴 +509 B
assets/useLoad3d-DRoYCfiA.js (removed) 874 B 🟢 -874 B 🟢 -434 B 🟢 -387 B
assets/useLoad3d-FfNb60IS.js (new) 874 B 🔴 +874 B 🔴 +432 B 🔴 +392 B
assets/audioUtils-B-eeN1mR.js (new) 858 B 🔴 +858 B 🔴 +498 B 🔴 +424 B
assets/audioUtils-CQeff1dy.js (removed) 858 B 🟢 -858 B 🟢 -498 B 🟢 -405 B
assets/useLoad3dViewer-BF4jMeEq.js (removed) 853 B 🟢 -853 B 🟢 -416 B 🟢 -377 B
assets/useLoad3dViewer-DXYAENAT.js (new) 853 B 🔴 +853 B 🔴 +418 B 🔴 +378 B
assets/useCurrentUser-C2ZO_Ezg.js (new) 737 B 🔴 +737 B 🔴 +382 B 🔴 +336 B
assets/useCurrentUser-DlgJZ7sK.js (removed) 737 B 🟢 -737 B 🟢 -381 B 🟢 -337 B
assets/_plugin-vue_export-helper-BYZQdlgo.js 315 B 315 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-C8II5OEy.js 7 kB 7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/envUtil-DiXnWsaz.js 466 B 466 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-CULTZH1z.js 1.56 kB 1.56 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/networkUtil-BSvcILBc.js 1.05 kB 1.05 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SkeletonUtils-BputJAFn.js 133 B 133 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useCopyToClipboard-JE-7X_Eb.js 1.57 kB 1.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/useExternalLink-CBYd7PkX.js 1.66 kB 1.66 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 12 added / 12 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-axios-C4mPrLmU.js 70.3 kB 70.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-chart-BxkFiWzp.js 399 kB 399 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-firebase-BvMr43CG.js 836 kB 836 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-i18n-BxsQv-KC.js 131 kB 131 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-markdown-kbZC6x-r.js 102 kB 102 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-LLCBDOBp.js 1.52 MB 1.52 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-B2JBnlqj.js 1.73 MB 1.73 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-C2SvlJE-.js 240 kB 240 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-sentry-SQwstEKc.js 182 kB 182 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-LBLOE6BD.js 1.8 MB 1.8 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BSO55ms7.js 625 kB 625 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-core-BjA-tjXK.js 311 kB 311 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vueuse-Bg1iMAgF.js 111 kB 111 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BGMLTxLX.js 374 kB 374 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-yjs-CBmk8HiD.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-zod-DcCUUPIi.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 7.29 MB (baseline 7.29 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-ByWpM8tT.js (new) 72.3 kB 🔴 +72.3 kB 🔴 +18.6 kB 🔴 +16 kB
assets/core-D41_8Ug9.js (removed) 72.3 kB 🟢 -72.3 kB 🟢 -18.6 kB 🟢 -16 kB
assets/groupNode-YUlDe4aD.js (removed) 72.1 kB 🟢 -72.1 kB 🟢 -17.7 kB 🟢 -15.6 kB
assets/groupNode-Z-bvpoXI.js (new) 72.1 kB 🔴 +72.1 kB 🔴 +17.7 kB 🔴 +15.6 kB
assets/WidgetSelect-C31rMdZZ.js (new) 57.5 kB 🔴 +57.5 kB 🔴 +12.2 kB 🔴 +10.5 kB
assets/WidgetSelect-DHR6AAtx.js (removed) 57.5 kB 🟢 -57.5 kB 🟢 -12.2 kB 🟢 -10.6 kB
assets/SubscriptionRequiredDialogContentWorkspace-C_dmkRQl.js (new) 46 kB 🔴 +46 kB 🔴 +8.59 kB 🔴 +7.44 kB
assets/SubscriptionRequiredDialogContentWorkspace-Ci3VUEnZ.js (removed) 46 kB 🟢 -46 kB 🟢 -8.6 kB 🟢 -7.46 kB
assets/SettingDialogContent-cuBOh8xH.js (removed) 31.9 kB 🟢 -31.9 kB 🟢 -8.33 kB 🟢 -7.32 kB
assets/SettingDialogContent-D0RTa4ki.js (new) 31.9 kB 🔴 +31.9 kB 🔴 +8.33 kB 🔴 +7.32 kB
assets/Load3DControls-Cqz7-yWW.js (removed) 30.9 kB 🟢 -30.9 kB 🟢 -5.34 kB 🟢 -4.65 kB
assets/Load3DControls-DcmEYWoL.js (new) 30.9 kB 🔴 +30.9 kB 🔴 +5.34 kB 🔴 +4.66 kB
assets/SubscriptionRequiredDialogContent-B8IGYtJn.js (new) 26.2 kB 🔴 +26.2 kB 🔴 +6.59 kB 🔴 +5.78 kB
assets/SubscriptionRequiredDialogContent-DeJUzEDI.js (removed) 26.2 kB 🟢 -26.2 kB 🟢 -6.59 kB 🟢 -5.8 kB
assets/Load3dViewerContent-C0niYWpQ.js (removed) 23.1 kB 🟢 -23.1 kB 🟢 -5.19 kB 🟢 -4.51 kB
assets/Load3dViewerContent-ldzRm5UT.js (new) 23.1 kB 🔴 +23.1 kB 🔴 +5.19 kB 🔴 +4.5 kB
assets/WidgetImageCrop-C7DzXMiq.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +5.52 kB 🔴 +4.87 kB
assets/WidgetImageCrop-PTCTmEXj.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -5.52 kB 🟢 -4.85 kB
assets/SubscriptionPanelContentWorkspace-Cfr84tbr.js (removed) 22 kB 🟢 -22 kB 🟢 -5.18 kB 🟢 -4.58 kB
assets/SubscriptionPanelContentWorkspace-PqEKMLTf.js (new) 22 kB 🔴 +22 kB 🔴 +5.19 kB 🔴 +4.58 kB
assets/CurrentUserPopoverWorkspace-CGFcQglZ.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -5.09 kB 🟢 -4.51 kB
assets/CurrentUserPopoverWorkspace-rR27Er8R.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +5.08 kB 🔴 +4.51 kB
assets/FormItem-Bdc8xjK4.js (removed) 20.2 kB 🟢 -20.2 kB 🟢 -4.86 kB 🟢 -4.25 kB
assets/FormItem-NWJwE0wy.js (new) 20.2 kB 🔴 +20.2 kB 🔴 +4.86 kB 🔴 +4.25 kB
assets/SignInContent-BO9o3HWn.js (removed) 19 kB 🟢 -19 kB 🟢 -4.82 kB 🟢 -4.2 kB
assets/SignInContent-BsDyrjio.js (new) 19 kB 🔴 +19 kB 🔴 +4.81 kB 🔴 +4.21 kB
assets/WidgetRecordAudio-Bx65ptXn.js (removed) 17.3 kB 🟢 -17.3 kB 🟢 -4.95 kB 🟢 -4.43 kB
assets/WidgetRecordAudio-Cb6ecaiJ.js (new) 17.3 kB 🔴 +17.3 kB 🔴 +4.95 kB 🔴 +4.43 kB
assets/MissingModelsWarning-CpeXrKnP.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -4.7 kB 🟢 -4.17 kB
assets/MissingModelsWarning-gfLuMNWo.js (new) 17.2 kB 🔴 +17.2 kB 🔴 +4.7 kB 🔴 +4.17 kB
assets/Load3D-CJ6SF-hc.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +4.04 kB 🔴 +3.53 kB
assets/Load3D-DmIhjOxw.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -4.04 kB 🟢 -3.53 kB
assets/WidgetInputNumber-BI9aAiBB.js (removed) 15.8 kB 🟢 -15.8 kB 🟢 -4.26 kB 🟢 -3.8 kB
assets/WidgetInputNumber-DJNi7450.js (new) 15.8 kB 🔴 +15.8 kB 🔴 +4.26 kB 🔴 +3.8 kB
assets/load3d-DMsFJgAk.js (new) 14.8 kB 🔴 +14.8 kB 🔴 +4.2 kB 🔴 +3.64 kB
assets/load3d-HYJGHjpo.js (removed) 14.8 kB 🟢 -14.8 kB 🟢 -4.2 kB 🟢 -3.64 kB
assets/AudioPreviewPlayer-BaHQM1G9.js (removed) 10.9 kB 🟢 -10.9 kB 🟢 -3.21 kB 🟢 -2.87 kB
assets/AudioPreviewPlayer-CAMfRV1n.js (new) 10.9 kB 🔴 +10.9 kB 🔴 +3.21 kB 🔴 +2.86 kB
assets/NodeConflictDialogContent-C4O3IYgY.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.35 kB 🟢 -2.07 kB
assets/NodeConflictDialogContent-ZbTcPBUV.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.35 kB 🔴 +2.07 kB
assets/changeTracker-BhOIOdD9.js (new) 9.38 kB 🔴 +9.38 kB 🔴 +2.89 kB 🔴 +2.54 kB
assets/changeTracker-C8H8vgz0.js (removed) 9.38 kB 🟢 -9.38 kB 🟢 -2.89 kB 🟢 -2.55 kB
assets/nodeTemplates-B3FcCudL.js (removed) 9.31 kB 🟢 -9.31 kB 🟢 -3.27 kB 🟢 -2.87 kB
assets/nodeTemplates-C1J6DRfF.js (new) 9.31 kB 🔴 +9.31 kB 🔴 +3.27 kB 🔴 +2.87 kB
assets/InviteMemberDialogContent-CqSj92ZM.js (new) 7.41 kB 🔴 +7.41 kB 🔴 +2.3 kB 🔴 +2 kB
assets/InviteMemberDialogContent-P-GuFWzJ.js (removed) 7.41 kB 🟢 -7.41 kB 🟢 -2.3 kB 🟢 -2.01 kB
assets/WidgetWithControl-C-bmeJEo.js (new) 7.04 kB 🔴 +7.04 kB 🔴 +2.63 kB 🔴 +2.35 kB
assets/WidgetWithControl-Dgd409wM.js (removed) 7.04 kB 🟢 -7.04 kB 🟢 -2.63 kB 🟢 -2.35 kB
assets/MissingNodesFooter-Cf0qsk-b.js (new) 6.73 kB 🔴 +6.73 kB 🔴 +2.29 kB 🔴 +2.04 kB
assets/MissingNodesFooter-kV_l7rug.js (removed) 6.73 kB 🟢 -6.73 kB 🟢 -2.29 kB 🟢 -2.04 kB
assets/Load3DConfiguration-CFSqAja7.js (removed) 6.27 kB 🟢 -6.27 kB 🟢 -1.92 kB 🟢 -1.68 kB
assets/Load3DConfiguration-Dqewr1Nq.js (new) 6.27 kB 🔴 +6.27 kB 🔴 +1.92 kB 🔴 +1.68 kB
assets/MissingNodesContent-BGyY0Slc.js (removed) 6.15 kB 🟢 -6.15 kB 🟢 -2.09 kB 🟢 -1.86 kB
assets/MissingNodesContent-h_03Drgr.js (new) 6.15 kB 🔴 +6.15 kB 🔴 +2.09 kB 🔴 +1.85 kB
assets/CreateWorkspaceDialogContent-DrFPZ29T.js (removed) 5.55 kB 🟢 -5.55 kB 🟢 -2 kB 🟢 -1.75 kB
assets/CreateWorkspaceDialogContent-Si6aI1vW.js (new) 5.55 kB 🔴 +5.55 kB 🔴 +2 kB 🔴 +1.74 kB
assets/EditWorkspaceDialogContent-BRZ2SN3_.js (new) 5.35 kB 🔴 +5.35 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/EditWorkspaceDialogContent-bscetnJW.js (removed) 5.35 kB 🟢 -5.35 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/ValueControlPopover-BD07fq6x.js (removed) 4.93 kB 🟢 -4.93 kB 🟢 -1.78 kB 🟢 -1.59 kB
assets/ValueControlPopover-BQwzIZuE.js (new) 4.93 kB 🔴 +4.93 kB 🔴 +1.78 kB 🔴 +1.59 kB
assets/Preview3d-Bv87qF-6.js (new) 4.83 kB 🔴 +4.83 kB 🔴 +1.58 kB 🔴 +1.38 kB
assets/Preview3d-CbTz76VK.js (removed) 4.83 kB 🟢 -4.83 kB 🟢 -1.58 kB 🟢 -1.38 kB
assets/CancelSubscriptionDialogContent-BTWHMc7l.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.79 kB 🔴 +1.57 kB
assets/CancelSubscriptionDialogContent-DtZNRnbL.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.79 kB 🟢 -1.57 kB
assets/DeleteWorkspaceDialogContent-KwrY7Epi.js (removed) 4.25 kB 🟢 -4.25 kB 🟢 -1.64 kB 🟢 -1.42 kB
assets/DeleteWorkspaceDialogContent-PDu0jcyz.js (new) 4.25 kB 🔴 +4.25 kB 🔴 +1.64 kB 🔴 +1.43 kB
assets/LeaveWorkspaceDialogContent-C5kpD4BN.js (new) 4.08 kB 🔴 +4.08 kB 🔴 +1.59 kB 🔴 +1.38 kB
assets/LeaveWorkspaceDialogContent-CPHZl3QL.js (removed) 4.08 kB 🟢 -4.08 kB 🟢 -1.59 kB 🟢 -1.37 kB
assets/RemoveMemberDialogContent-BlnWDCtB.js (removed) 4.06 kB 🟢 -4.06 kB 🟢 -1.54 kB 🟢 -1.34 kB
assets/RemoveMemberDialogContent-S70Y_Vzq.js (new) 4.06 kB 🔴 +4.06 kB 🔴 +1.54 kB 🔴 +1.34 kB
assets/RevokeInviteDialogContent-DLwgBaMT.js (new) 3.98 kB 🔴 +3.98 kB 🔴 +1.55 kB 🔴 +1.36 kB
assets/RevokeInviteDialogContent-klrWGDLk.js (removed) 3.98 kB 🟢 -3.98 kB 🟢 -1.55 kB 🟢 -1.36 kB
assets/saveMesh-BH6lx4zm.js (removed) 3.4 kB 🟢 -3.4 kB 🟢 -1.47 kB 🟢 -1.3 kB
assets/saveMesh-DU253FwW.js (new) 3.4 kB 🔴 +3.4 kB 🔴 +1.47 kB 🔴 +1.31 kB
assets/cloudSessionCookie-CUf1WXrH.js (removed) 3.12 kB 🟢 -3.12 kB 🟢 -1.1 kB 🟢 -1 kB
assets/cloudSessionCookie-kpCd45NB.js (new) 3.12 kB 🔴 +3.12 kB 🔴 +1.1 kB 🔴 +1 kB
assets/GlobalToast-BLeu-l5Q.js (new) 2.91 kB 🔴 +2.91 kB 🔴 +1.21 kB 🔴 +1.03 kB
assets/GlobalToast-Hs445Bao.js (removed) 2.91 kB 🟢 -2.91 kB 🟢 -1.21 kB 🟢 -1.03 kB
assets/SubscribeToRun-3FXodg8G.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -1.01 kB 🟢 -899 B
assets/SubscribeToRun-Dn7WTEuj.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +1.01 kB 🔴 +889 B
assets/SettingDialogHeader-CvkXafS0.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -894 B 🟢 -803 B
assets/SettingDialogHeader-H5JPWFMh.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +894 B 🔴 +803 B
assets/CloudRunButtonWrapper-CqEleG7w.js (new) 1.69 kB 🔴 +1.69 kB 🔴 +795 B 🔴 +704 B
assets/CloudRunButtonWrapper-K5QxXMOx.js (removed) 1.69 kB 🟢 -1.69 kB 🟢 -798 B 🟢 -723 B
assets/cloudBadges-DQJ4JY0X.js (removed) 1.38 kB 🟢 -1.38 kB 🟢 -719 B 🟢 -620 B
assets/cloudBadges-DS_95_6r.js (new) 1.38 kB 🔴 +1.38 kB 🔴 +715 B 🔴 +623 B
assets/cloudSubscription-2-wvqPE8.js (new) 1.34 kB 🔴 +1.34 kB 🔴 +669 B 🔴 +578 B
assets/cloudSubscription-Nt4cSeni.js (removed) 1.34 kB 🟢 -1.34 kB 🟢 -669 B 🟢 -574 B
assets/Load3D-CZqbpJ6q.js (removed) 1.09 kB 🟢 -1.09 kB 🟢 -504 B 🟢 -447 B
assets/Load3D-ixg74BlW.js (new) 1.09 kB 🔴 +1.09 kB 🔴 +507 B 🔴 +455 B
assets/nightlyBadges-BUCVYGrI.js (new) 1.02 kB 🔴 +1.02 kB 🔴 +543 B 🔴 +481 B
assets/nightlyBadges-Ly1_ym2U.js (removed) 1.02 kB 🟢 -1.02 kB 🟢 -545 B 🟢 -482 B
assets/Load3dViewerContent-CYhVGdh5.js (removed) 1.01 kB 🟢 -1.01 kB 🟢 -477 B 🟢 -424 B
assets/Load3dViewerContent-DVt-grNj.js (new) 1.01 kB 🔴 +1.01 kB 🔴 +479 B 🔴 +430 B
assets/SubscriptionPanelContentWorkspace-BQ9A4PF7.js (removed) 945 B 🟢 -945 B 🟢 -442 B 🟢 -388 B
assets/SubscriptionPanelContentWorkspace-NtUiLam0.js (new) 945 B 🔴 +945 B 🔴 +445 B 🔴 +390 B
assets/SettingDialogContent-CQ-J04Fh.js (new) 887 B 🔴 +887 B 🔴 +430 B 🔴 +384 B
assets/SettingDialogContent-D4lYj_v3.js (removed) 887 B 🟢 -887 B 🟢 -430 B 🟢 -383 B
assets/changeTracker-BDuS35m3.js (removed) 772 B 🟢 -772 B 🟢 -394 B 🟢 -344 B
assets/changeTracker-Bn6jS-aG.js (new) 772 B 🔴 +772 B 🔴 +393 B 🔴 +347 B
assets/graphHasMissingNodes-DBjiIhFi.js (new) 761 B 🔴 +761 B 🔴 +371 B 🔴 +321 B
assets/graphHasMissingNodes-dRSdV0CF.js (removed) 761 B 🟢 -761 B 🟢 -373 B 🟢 -321 B
assets/WidgetLegacy-Ce2PJVIB.js (removed) 760 B 🟢 -760 B 🟢 -393 B 🟢 -342 B
assets/WidgetLegacy-g0R8L86h.js (new) 760 B 🔴 +760 B 🔴 +393 B 🔴 +343 B
assets/WidgetInputNumber-beHnUyzL.js (new) 392 B 🔴 +392 B 🔴 +231 B 🔴 +196 B
assets/WidgetInputNumber-BOg0gMw1.js (removed) 392 B 🟢 -392 B 🟢 -231 B 🟢 -199 B
assets/SettingDialogHeader-vEqcepbh.js (new) 345 B 🔴 +345 B 🔴 +208 B 🔴 +200 B
assets/SettingDialogHeader-vXxKB9U-.js (removed) 345 B 🟢 -345 B 🟢 -208 B 🟢 -190 B
assets/AnimationControls-BsWySN5_.js 4.61 kB 4.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ApiNodesSignInContent-DTyiF6u-.js 2.69 kB 2.69 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/auto-BTnZwrs2.js 1.7 kB 1.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/BaseViewTemplate-BhZHTUKb.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/comfy-logo-single-CzGozBag.js 198 B 198 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ComfyOrgHeader-Gbby8E71.js 910 B 910 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BaWekgk-.js 14.7 kB 14.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BDDnqSnY.js 15.8 kB 15.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BoYLZfP6.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BQBGh0Ya.js 15.5 kB 15.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BRoxhQgK.js 15.8 kB 15.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C_sqX4RY.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Cd2uuEbd.js 17.1 kB 17.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CXPUqoOz.js 16.6 kB 16.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-ijoJFfEv.js 16.3 kB 16.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-UTYbUnOi.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-YFdvkJDc.js 18.4 kB 18.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/constants-BQ60oUwg.js 579 B 579 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-BBmGB05k.js 492 kB 492 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-BT4gcSgP.js 199 B 199 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ImportFailedNodeContent-CuQpbxoK.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ImportFailedNodeFooter-BVBaLbVz.js 1.88 kB 1.88 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ImportFailedNodeHeader-C5wjxnXc.js 1.08 kB 1.08 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LazyImage-V1y3sy9h.js 12.3 kB 12.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bk7Ph3VS.js 140 kB 140 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BsU_fTOy.js 118 kB 118 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BYY1tCjz.js 153 kB 153 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BzI-1UKY.js 117 kB 117 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-ciwZVtD5.js 133 kB 133 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CjTKnSQT.js 135 kB 135 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-ClVfAHiD.js 185 kB 185 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DtZZaQoT.js 137 kB 137 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DY5snELF.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Q_yKaq1U.js 166 kB 166 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-XVEzvvBB.js 132 kB 132 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-5HQboIyx.js 1.82 kB 1.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-Di0bN8lA.js 1.43 kB 1.43 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-ByvcQ9C2.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-D1667gDz.js 2.23 kB 2.23 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MissingNodesHeader-BAZcszup.js 1.09 kB 1.09 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/NodeConflictFooter-C7WLy5Sh.js 2.37 kB 2.37 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/NodeConflictHeader-CQeNaRiZ.js 1.09 kB 1.09 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BbE7kmki.js 413 kB 413 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-C4Vr6-Ih.js 367 kB 367 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CeGNv9ZW.js 450 kB 450 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CgbjMWnR.js 360 kB 360 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cj0eoGwS.js 371 kB 371 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CjZ27bLt.js 412 kB 412 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CPZbmaZd.js 367 kB 367 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cz-81naW.js 335 kB 335 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DknKGYI5.js 364 kB 364 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-elNKIbDk.js 381 kB 381 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-VAK4vMLf.js 338 kB 338 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/PanelTemplate-B9lPSLJe.js 1.2 kB 1.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-B7BRzEeG.js 665 B 665 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-DLICfi3-.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SelectValue-DBHUo1Qx.js 8.94 kB 8.94 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/signInSchema-Bqlb_LYM.js 1.53 kB 1.53 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Slider-eqGfZlVo.js 3.52 kB 3.52 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/src-C3JCjcCo.js 251 B 251 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/telemetry-Db2yG1o2.js 226 B 226 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/types-DT3N7am7.js 204 B 204 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-BhHc6T04.js 445 B 445 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-05_iDuzU.js 131 B 131 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-JezhrbLj.js 3.91 kB 3.91 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BlGCEirp.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-B6bpqk2C.js 2.9 kB 2.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-BTV9zowW.js 3.61 kB 3.61 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DTyqdFue.js 3.1 kB 3.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-DN2pTx0v.js 1.86 kB 1.86 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-1P4YwNzh.js 1.95 kB 1.95 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-DaTQe8EX.js 2.88 kB 2.88 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-C6ZYch5M.js 1.1 kB 1.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-BGDTDBYo.js 3.14 kB 3.14 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-TeVzKTow.js 2.5 kB 2.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetTypes-Cp8f93Pk.js 393 B 393 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 54 added / 54 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: 8

🤖 Fix all issues with AI agents
In `@src/scripts/app.test.ts`:
- Around line 39-49: The mapped type on createMockNode uses `any`, which
violates the no-explicit-any rule; change the signature to use `unknown` instead
(e.g., options: { [K in keyof LGraphNode]?: unknown } = {}) so the helper
remains type-safe while still allowing arbitrary overrides for the created
LGraphNode; update the createMockNode function declaration accordingly
(references: createMockNode, LGraphNode).

In `@src/scripts/app.ts`:
- Around line 1583-1596: The handleFileList function currently assumes
fileList[0] exists and is an image; add a guard at the top of handleFileList to
return early if fileList is empty, then build an array of image files by
filtering FileList items where file.type.startsWith('image'); if that filtered
array is empty return early; call pasteImageNodes(this.canvas, imageFiles)
(instead of passing the original FileList), only create the BatchImagesNode and
call positionBatchNodes/connect on the resulting imageNodes when imageNodes
length > 0 to avoid creating an empty batch node.
- Around line 1603-1620: positionBatchNodes currently assumes nodes is non-empty
and uses a hardcoded LoadImage height; first add an early guard in
positionBatchNodes to return immediately if nodes.length === 0 (so
nodes[0].getBounding() is never called), and ensure callers like
handleFileList/pasteImageNodes handle and propagate empty arrays from
createNode. Replace the hardcoded height logic by computing each node's height
from its bounding box (use node.getBounding() or node.size[1] as a fallback) and
use that per-node height when setting node.pos; keep setting batchNode.pos from
the first node's bounding result and call this.canvas.graph?.change() after
positioning.
- Around line 1592-1594: The loop attempts to connect multiple imageNodes to
batchImagesNode using increasing target indices, but BatchImagesNode only
declares a single input slot named "images", so connect(...) silently fails for
indices >0; update the connection logic to either add input slots dynamically on
the BatchImagesNode (use batchImagesNode.onBeforeConnectInput to append a new
input slot before each connect call so the target index exists) or change the
wiring to connect all imageNodes into the single "images" input (e.g., combine
into one array/source if the backend supports it); reference imageNodes,
batchImagesNode, connect(), and onBeforeConnectInput when making the change.

In `@src/utils/eventUtils.ts`:
- Around line 6-12: The single-file path rejects image/bmp but the multi-file
path uses hasImageType which allows bmp, causing inconsistent behavior; modify
the multi-file branch that uses Array.from(files).every(hasImageType) to also
exclude files with type === 'image/bmp' (e.g., change the predicate to check
hasImageType(file) && file.type !== 'image/bmp' or pre-filter the FileList), so
both the single-file return (files[0]) and the multi-file return (files)
consistently omit BMPs when inspecting event.dataTransfer.files.

In `@src/utils/litegraphUtil.test.ts`:
- Around line 31-89: Tests currently use explicit any for mockCanvas, mockGraph
and mock nodes which triggers typescript-eslint/no-explicit-any; replace those
anys with concrete mock types or unknown-to-specific casts. Define mock types
(e.g., type MockGraph = { add: (n: LGraphNode) => LGraphNode; change: () => void
} and type MockCanvas = { graph: MockGraph | null; graph_mouse: [number, number]
}) and type the mockNode as Partial<LGraphNode> or cast via unknown to
LGraphNode (e.g., mockNode as unknown as LGraphNode) when calling
LiteGraph.createNode; update references to mockCanvas, mockGraph, mockNode,
LiteGraph.createNode and createNode in the tests accordingly so no explicit any
or as any remains.

In `@src/utils/litegraphUtil.ts`:
- Around line 40-62: The createNode function issues two problems: remove the
redundant graph.change() call after graph.add(newNode) since graph.add already
invokes this.change() (refer to createNode, graph.add and graph.change), or if a
second notification is truly required document the reason above the call; also
remove the unnecessary await new Promise(r => setTimeout(r, 0)) between
LiteGraph.createNode and graph.add unless there's a documented asynchronous
side-effect—if an async yield is required, replace it with a clear comment
explaining why (e.g., waiting for node registration) so the intent is explicit.
- Around line 3-8: There are duplicate imports from the same module: the
type-only import of ColorOption, LGraph, LGraphCanvas and the value import of
LiteGraph; remove the duplicate by merging them into a single import statement
from '@/lib/litegraph/src/litegraph' that includes both the value symbol
LiteGraph and the type symbols ColorOption, LGraph, LGraphCanvas (e.g., a single
import that lists LiteGraph and the types), so only one import from that module
remains.
🧹 Nitpick comments (5)
src/lib/litegraph/src/LGraph.ts (1)

888-906: Overload asymmetry: only the deprecated overload accepts null.

The primary overload (line 889) still requires a non-null node, while only the deprecated overload (line 899) now accepts null. Callers using the modern options overload cannot pass a nullable node. If the intent is to allow null broadly (e.g., from createNode which may return null), consider also updating the primary overload, or at minimum the implementation signature on line 903, so the contract is consistent.

That said, the runtime guard on line 906 (if (!node) return) already handles falsy values safely, so this is purely a type-level concern.

src/utils/eventUtils.ts (1)

29-31: Prefer 'image/' prefix for stricter MIME type matching.

type.startsWith('image') works in practice since all image MIME types use image/, but using 'image/' would be more precise and guard against hypothetical non-standard types.

Suggested fix
 function hasImageType({ type }: File): boolean {
-  return type.startsWith('image')
+  return type.startsWith('image/')
 }
src/scripts/app.test.ts (1)

139-179: Position tests rely on exact pixel values derived from mock bounding boxes.

The assertions on lines 149, 165–166 use specific pixel values (500, 230, 594, 963) computed from the mock bounding data. This is fine for pinning behavior, but if the positioning algorithm changes (padding, margins), all these values need updating simultaneously. Consider adding a brief comment in the test explaining the calculation (e.g., // x: 100 + 300 (width) + 100 (gap) = 500).

src/composables/usePaste.test.ts (1)

259-276: Audio paste path still uses LiteGraph.createNode directly.

The image paste tests now mock createNode from litegraphUtil (line 254), but the audio paste test (line 261) still mocks LiteGraph.createNode. This is consistent with the source code where only the image path was migrated, but worth noting as a follow-up to migrate the audio path similarly for consistency.

src/scripts/app.ts (1)

557-563: Use else if instead of two separate if blocks.

File and FileList are mutually exclusive results from extractFileFromDragEvent. Using else if makes the exclusivity explicit and avoids evaluating the second condition unnecessarily.

Proposed fix
-          if (fileMaybe instanceof File) {
-              await this.handleFile(fileMaybe, 'file_drop')
-          }
-
-          if (fileMaybe instanceof FileList) {
+          if (fileMaybe instanceof File) {
+            await this.handleFile(fileMaybe, 'file_drop')
+          } else if (fileMaybe instanceof FileList) {
             await this.handleFileList(fileMaybe)
           }

Comment on lines +39 to +49
function createMockNode(options: { [K in keyof LGraphNode]?: any } = {}) {
return {
id: 1,
pos: [0, 0],
size: [200, 100],
type: 'LoadImage',
connect: vi.fn(),
getBounding: vi.fn(() => new Float64Array([0, 0, 200, 100])),
...options
} as LGraphNode
}
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 | 🟠 Major

Replace any with unknown in mock helper type.

Line 39 uses any in the mapped type, which will fail the no-explicit-any lint rule:

Suggested fix
-function createMockNode(options: { [K in keyof LGraphNode]?: any } = {}) {
+function createMockNode(options: Partial<Record<keyof LGraphNode, unknown>> = {}) {
   return {
     id: 1,
     pos: [0, 0],
     size: [200, 100],
     type: 'LoadImage',
     connect: vi.fn(),
     getBounding: vi.fn(() => new Float64Array([0, 0, 200, 100])),
     ...options
   } as LGraphNode
 }

As per coding guidelines: "Never use any type; use proper TypeScript types."

🤖 Prompt for AI Agents
In `@src/scripts/app.test.ts` around lines 39 - 49, The mapped type on
createMockNode uses `any`, which violates the no-explicit-any rule; change the
signature to use `unknown` instead (e.g., options: { [K in keyof LGraphNode]?:
unknown } = {}) so the helper remains type-safe while still allowing arbitrary
overrides for the created LGraphNode; update the createMockNode function
declaration accordingly (references: createMockNode, LGraphNode).

Comment on lines +1583 to +1596
async handleFileList(fileList: FileList) {
if (fileList[0].type.startsWith('image')) {
const imageNodes = await pasteImageNodes(this.canvas, fileList)
const batchImagesNode = await createNode(this.canvas, 'BatchImagesNode')
if (!batchImagesNode) return

this.positionBatchNodes(imageNodes, batchImagesNode)
this.canvas.selectItems([...imageNodes, batchImagesNode])

Array.from(imageNodes).forEach((imageNode, index) => {
imageNode.connect(0, batchImagesNode, index)
})
}
}
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 | 🟠 Major

Missing guard for empty fileList and non-image file types.

fileList[0].type will throw if fileList is empty. Additionally, only the first file's MIME type is checked—if a user drops a mix of images and non-images, all files are treated as images.

Proposed fix
   async handleFileList(fileList: FileList) {
+    if (fileList.length === 0) return
+
     if (fileList[0].type.startsWith('image')) {
🤖 Prompt for AI Agents
In `@src/scripts/app.ts` around lines 1583 - 1596, The handleFileList function
currently assumes fileList[0] exists and is an image; add a guard at the top of
handleFileList to return early if fileList is empty, then build an array of
image files by filtering FileList items where file.type.startsWith('image'); if
that filtered array is empty return early; call pasteImageNodes(this.canvas,
imageFiles) (instead of passing the original FileList), only create the
BatchImagesNode and call positionBatchNodes/connect on the resulting imageNodes
when imageNodes length > 0 to avoid creating an empty batch node.

Comment on lines +1592 to +1594
Array.from(imageNodes).forEach((imageNode, index) => {
imageNode.connect(0, batchImagesNode, index)
})
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg "BatchImagesNode" --type=ts -C 10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3417


🏁 Script executed:

rg "class.*BatchImagesNode|BatchImagesNode.*=" --type=ts -B 3 -A 10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

rg "BatchImagesNode" --type=json -C 5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 6179


🏁 Script executed:

fd -t f -e ts -e js | xargs rg "inputs|slots" | grep -i batch || echo "No batch-related slot definitions found"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 109


🏁 Script executed:

cat -n src/locales/en/nodeDefs.json | grep -A 30 '"BatchImagesNode"'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 816


🏁 Script executed:

rg "inputs.*images" src/locales/en/nodeDefs.json -A 20 | grep -A 20 "BatchImagesNode"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

rg "\.connect\(" src/scripts/app.ts -B 2 -A 2 | head -40

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 327


🏁 Script executed:

rg "connect.*function|function.*connect" --type=ts | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 916


🏁 Script executed:

rg "class.*Node|LNode" --type=ts -A 15 | grep -A 15 "connect" | head -40

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1052


🏁 Script executed:

rg "class LGraphNode" --type=ts -A 30 | head -60

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 4113


🏁 Script executed:

rg "connect\s*\(" --type=ts -B 3 -A 8 | grep -A 8 "connect\s*\(.*?" | head -50

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 93


🏁 Script executed:

fd -type f -name "*.ts" -path "*/lib/*" -o -name "litegraph*" 2>/dev/null | head -5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

rg "LGraphNode|litegraph" --type=ts | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2218


🏁 Script executed:

rg "connect\s*\(\s*[^)]*\)\s*\{" src/lib/litegraph/src/LGraphNode.ts -A 20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

grep -n "connect(" src/lib/litegraph/src/LGraphNode.ts | head -10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 477


🏁 Script executed:

sed -n '2726,2800p' src/lib/litegraph/src/LGraphNode.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2488


🏁 Script executed:

rg "onBeforeConnectInput|addInput|dynamic.*slot" src/ --type=ts | grep -i batch

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

rg "inputs\s*\[\s*\]|addInput|findInputSlot" src/ --type=ts | head -30

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3078


🏁 Script executed:

rg "BatchImagesNode" src/ --type=ts -B 5 -A 15 | grep -v "test\|locale\|mock"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1137


🏁 Script executed:

rg "node\s*\.\s*addInput|dynamicly.*add|createInputSlot" src/ --type=ts | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2094


🏁 Script executed:

rg "onBeforeConnectInput" src/ --type=ts -B 3 -A 10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2202


🏁 Script executed:

rg "BatchImagesNode.*onBeforeConnectInput|class.*BatchImages" src/ --type=ts -B 5 -A 30

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

rg "onBeforeConnectInput" src/ --type=ts --type=js | grep -v test | grep -v "\.ts-"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 309


🏁 Script executed:

rg "inputs\s*=" src/ --type=ts | grep -i batch

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

rg "\.inputs\s*\[" src/ --type=ts | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1906


🏁 Script executed:

python3 << 'EOF'
# Check if there's any code that validates or logs connection failures
import subprocess
result = subprocess.run(['rg', 'inputs.*length|targetIndex.*>=', 'src/', '--type=ts', '-A', '3'], 
                       capture_output=True, text=True)
print(result.stdout[:2000])
EOF

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2070


🏁 Script executed:

sed -n '2770,2810p' src/lib/litegraph/src/LGraphNode.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1364


🏁 Script executed:

rg "targetIndex >= target_node.inputs" src/lib/litegraph/src/LGraphNode.ts -A 5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 238


Fix silent connection failures when connecting multiple image nodes to BatchImagesNode.

The code assumes BatchImagesNode has sequential input slots (0, 1, 2...) indexed by image count, but the node definition declares only a single input slot named "images". The connect() call will silently fail (returning null without error unless debug mode is enabled) for all connections beyond the first, as the target slot indices exceed target_node.inputs.length.

Consider using onBeforeConnectInput to dynamically add input slots for each connection, or restructure to connect multiple images to a single slot if the backend supports it.

🤖 Prompt for AI Agents
In `@src/scripts/app.ts` around lines 1592 - 1594, The loop attempts to connect
multiple imageNodes to batchImagesNode using increasing target indices, but
BatchImagesNode only declares a single input slot named "images", so
connect(...) silently fails for indices >0; update the connection logic to
either add input slots dynamically on the BatchImagesNode (use
batchImagesNode.onBeforeConnectInput to append a new input slot before each
connect call so the target index exists) or change the wiring to connect all
imageNodes into the single "images" input (e.g., combine into one array/source
if the backend supports it); reference imageNodes, batchImagesNode, connect(),
and onBeforeConnectInput when making the change.

Comment on lines +1603 to +1620
positionBatchNodes(nodes: LGraphNode[], batchNode: LGraphNode): void {
const [x, y, width] = nodes[0].getBounding()
batchNode.pos = [ x + width + 100, y + 30 ]

// Retrieving Node Height is inconsistent
let height = 0;
if (nodes[0].type === 'LoadImage') {
height = 344
}

nodes.forEach((node, index) => {
if (index > 0) {
node.pos = [ x, y + (height * index) + (25 * (index + 1)) ]
}
});

this.canvas.graph?.change()
}
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 | 🟠 Major

positionBatchNodes will crash on an empty nodes array and uses a fragile hardcoded height.

  1. nodes[0].getBounding() throws if nodes is empty. handleFileList doesn't guard against pasteImageNodes returning [] (e.g., if all createNode calls fail).
  2. Hardcoding height = 344 for LoadImage is brittle—it will silently break if the node's rendered size changes. Consider computing the actual height from the node's bounding box or node.size[1].
Proposed fix
   positionBatchNodes(nodes: LGraphNode[], batchNode: LGraphNode): void {
+    if (nodes.length === 0) return
+
     const [x, y, width] = nodes[0].getBounding()
     batchNode.pos = [ x + width + 100, y + 30 ]
 
-    // Retrieving Node Height is inconsistent
-    let height = 0;
-    if (nodes[0].type === 'LoadImage') {
-      height = 344
-    }
+    const height = nodes[0].size[1]
 
     nodes.forEach((node, index) => {
       if (index > 0) {
         node.pos = [ x, y + (height * index) + (25 * (index + 1)) ]
       }
     });
 
     this.canvas.graph?.change()
   }
🤖 Prompt for AI Agents
In `@src/scripts/app.ts` around lines 1603 - 1620, positionBatchNodes currently
assumes nodes is non-empty and uses a hardcoded LoadImage height; first add an
early guard in positionBatchNodes to return immediately if nodes.length === 0
(so nodes[0].getBounding() is never called), and ensure callers like
handleFileList/pasteImageNodes handle and propagate empty arrays from
createNode. Replace the hardcoded height logic by computing each node's height
from its bounding box (use node.getBounding() or node.size[1] as a fallback) and
use that per-node height when setting node.pos; keep setting batchNode.pos from
the first node's bounding result and call this.canvas.graph?.change() after
positioning.

Comment on lines +6 to 12
const { files } = event.dataTransfer
// Dragging from Chrome->Firefox there is a file, but it's a bmp, so ignore it
if (files.length === 1 && files[0].type !== 'image/bmp') {
return files[0]
} else if (files.length > 1 && Array.from(files).every(hasImageType)) {
return files
}
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 | 🟡 Minor

BMP handling is inconsistent between single-file and multi-file paths.

A single image/bmp file is explicitly rejected (line 8), but when multiple files are dragged, hasImageType (line 10) accepts image/bmp because it only checks type.startsWith('image'). This means dragging one bmp from Chrome→Firefox is filtered, but dragging two bmps passes through.

If the bmp exclusion is intentional for cross-browser drag artifacts, consider filtering it in both paths:

Suggested fix
-  if (files.length === 1 && files[0].type !== 'image/bmp') {
-    return files[0]
-  } else if (files.length > 1 && Array.from(files).every(hasImageType)) {
+  const imageFiles = Array.from(files).filter(
+    (f) => hasImageType(f) && f.type !== 'image/bmp'
+  )
+  if (imageFiles.length === 1) {
+    return imageFiles[0]
+  } else if (imageFiles.length > 1) {
     return files
   }
🤖 Prompt for AI Agents
In `@src/utils/eventUtils.ts` around lines 6 - 12, The single-file path rejects
image/bmp but the multi-file path uses hasImageType which allows bmp, causing
inconsistent behavior; modify the multi-file branch that uses
Array.from(files).every(hasImageType) to also exclude files with type ===
'image/bmp' (e.g., change the predicate to check hasImageType(file) && file.type
!== 'image/bmp' or pre-filter the FileList), so both the single-file return
(files[0]) and the multi-file return (files) consistently omit BMPs when
inspecting event.dataTransfer.files.

Comment on lines +31 to +89
describe('createNode', () => {
let mockCanvas: any
let mockGraph: any

beforeEach(() => {
vi.clearAllMocks()

mockGraph = {
add: vi.fn((node) => node),
change: vi.fn()
}

mockCanvas = {
graph: mockGraph,
graph_mouse: [100, 200]
}
})

it('should create a node successfully', async () => {
const mockNode = { pos: [0, 0] }
vi.mocked(LiteGraph.createNode).mockReturnValue(mockNode as any)

const result = await createNode(mockCanvas, 'LoadImage')

expect(LiteGraph.createNode).toHaveBeenCalledWith('LoadImage')
expect(mockNode.pos).toEqual([100, 200])
expect(mockGraph.add).toHaveBeenCalledWith(mockNode)
expect(mockGraph.change).toHaveBeenCalled()
expect(result).toBe(mockNode)
})

it('should return null when name is empty', async () => {
const result = await createNode(mockCanvas, '')

expect(LiteGraph.createNode).not.toHaveBeenCalled()
expect(result).toBeNull()
})

it('should handle graph being null', async () => {
const mockNode = { pos: [0, 0] }
mockCanvas.graph = null
vi.mocked(LiteGraph.createNode).mockReturnValue(mockNode as any)

const result = await createNode(mockCanvas, 'LoadImage')

expect(mockNode.pos).toEqual([0, 0])
expect(result).toBeNull()
})

it('should set position based on canvas graph_mouse', async () => {
mockCanvas.graph_mouse = [250, 350]
const mockNode = { pos: [0, 0] }
vi.mocked(LiteGraph.createNode).mockReturnValue(mockNode as any)

await createNode(mockCanvas, 'LoadAudio')

expect(mockNode.pos).toEqual([250, 350])
})
})
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

Fix any type usage — CI lint failures on lines 32, 33, 51, 72, 83.

The pipeline reports typescript-eslint/no-explicit-any errors. Replace any with proper types or unknown:

Suggested approach
 describe('createNode', () => {
-  let mockCanvas: any
-  let mockGraph: any
+  let mockCanvas: { graph: unknown; graph_mouse: number[] }
+  let mockGraph: { add: ReturnType<typeof vi.fn>; change: ReturnType<typeof vi.fn> }

   beforeEach(() => {
     vi.clearAllMocks()
     // ...
   })

   it('should create a node successfully', async () => {
     const mockNode = { pos: [0, 0] }
-    vi.mocked(LiteGraph.createNode).mockReturnValue(mockNode as any)
+    vi.mocked(LiteGraph.createNode).mockReturnValue(mockNode as unknown as LGraphNode)
     // ...
   })

Apply similar as unknown as LGraphNode casts (or narrower mock types) for lines 72 and 83.

As per coding guidelines: "Never use any type; use proper TypeScript types" and "Never use as any type assertions; fix the underlying type issue instead."

🧰 Tools
🪛 GitHub Actions: CI: Lint Format

[error] 32-32: typescript-eslint/no-explicit-any: Unexpected any. Specify a different type. Use unknown instead, this will force you to explicitly, and safely, assert the type is correct.


[error] 33-33: typescript-eslint/no-explicit-any: Unexpected any. Specify a different type. Use unknown instead, this will force you to explicitly, and safely, assert the type is correct.


[error] 51-51: typescript-eslint/no-explicit-any: Unexpected any. Specify a different type. Use unknown instead, this will force you to explicitly, and safely, assert the type is correct.


[error] 72-72: typescript-eslint/no-explicit-any: Unexpected any. Specify a different type. Use unknown instead, this will force you to explicitly, and safely, assert the type is correct.


[error] 83-83: typescript-eslint/no-explicit-any: Unexpected any. Specify a different type. Use unknown instead, this will force you to explicitly, and safely, assert the type is correct.

🤖 Prompt for AI Agents
In `@src/utils/litegraphUtil.test.ts` around lines 31 - 89, Tests currently use
explicit any for mockCanvas, mockGraph and mock nodes which triggers
typescript-eslint/no-explicit-any; replace those anys with concrete mock types
or unknown-to-specific casts. Define mock types (e.g., type MockGraph = { add:
(n: LGraphNode) => LGraphNode; change: () => void } and type MockCanvas = {
graph: MockGraph | null; graph_mouse: [number, number] }) and type the mockNode
as Partial<LGraphNode> or cast via unknown to LGraphNode (e.g., mockNode as
unknown as LGraphNode) when calling LiteGraph.createNode; update references to
mockCanvas, mockGraph, mockNode, LiteGraph.createNode and createNode in the
tests accordingly so no explicit any or as any remains.

Comment on lines +3 to +8
import type {
ColorOption,
LGraph,
LGraphCanvas,
} from '@/lib/litegraph/src/litegraph'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
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

Fix duplicate import — CI lint failure.

The pipeline reports eslint-plugin-import(no-duplicates) on line 8. Lines 3–7 and line 8 both import from @/lib/litegraph/src/litegraph. Merge the value import into the existing value-import block on lines 9–14.

Suggested fix
 import type {
   ColorOption,
   LGraph,
   LGraphCanvas,
 } from '@/lib/litegraph/src/litegraph'
-import { LiteGraph } from '@/lib/litegraph/src/litegraph'
 import {
   LGraphGroup,
   LGraphNode,
+  LiteGraph,
   Reroute,
   isColorable
 } from '@/lib/litegraph/src/litegraph'
🧰 Tools
🪛 GitHub Actions: CI: Lint Format

[error] 8-8: eslint-plugin-import(no-duplicates): Modules should not be imported multiple times in the same file. Merge these imports into a single import statement.

🤖 Prompt for AI Agents
In `@src/utils/litegraphUtil.ts` around lines 3 - 8, There are duplicate imports
from the same module: the type-only import of ColorOption, LGraph, LGraphCanvas
and the value import of LiteGraph; remove the duplicate by merging them into a
single import statement from '@/lib/litegraph/src/litegraph' that includes both
the value symbol LiteGraph and the type symbols ColorOption, LGraph,
LGraphCanvas (e.g., a single import that lists LiteGraph and the types), so only
one import from that module remains.

Comment on lines +40 to +62
export async function createNode(
canvas: LGraphCanvas,
name: string
): Promise<LGraphNode | null> {
if (!name) {
return null
}

const { graph, graph_mouse: [ posX, posY ] } = canvas
const newNode = LiteGraph.createNode(name)
await new Promise(r => setTimeout(r, 0))

if (newNode && graph) {
newNode.pos = [ posX, posY ]
const addedNode = graph.add(newNode) ?? null

if (addedNode) graph.change()
return addedNode
} else {
useToastStore().addAlert(t('assetBrowser.failedToCreateNode'))
return null
}
}
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 | 🟠 Major

Double graph.change() call and unexplained setTimeout(0) yield.

Two concerns with createNode:

  1. Double graph.change(): graph.add(newNode) (LGraph.ts line 989) already calls this.change() internally. Line 56 calls graph.change() again, resulting in two change notifications per node creation. Remove the redundant call or document why a second notification is needed.

  2. await new Promise(r => setTimeout(r, 0)) (line 50): This yields to the event loop between LiteGraph.createNode and graph.add. The intent isn't documented. If it's needed for async node registration side effects, add a comment explaining why. Otherwise, this adds unnecessary latency to every node creation.

Suggested fix (remove redundant change call)
   if (newNode && graph) {
     newNode.pos = [ posX, posY ]
     const addedNode = graph.add(newNode) ?? null
-
-    if (addedNode) graph.change()
     return addedNode
   } else {
📝 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
export async function createNode(
canvas: LGraphCanvas,
name: string
): Promise<LGraphNode | null> {
if (!name) {
return null
}
const { graph, graph_mouse: [ posX, posY ] } = canvas
const newNode = LiteGraph.createNode(name)
await new Promise(r => setTimeout(r, 0))
if (newNode && graph) {
newNode.pos = [ posX, posY ]
const addedNode = graph.add(newNode) ?? null
if (addedNode) graph.change()
return addedNode
} else {
useToastStore().addAlert(t('assetBrowser.failedToCreateNode'))
return null
}
}
export async function createNode(
canvas: LGraphCanvas,
name: string
): Promise<LGraphNode | null> {
if (!name) {
return null
}
const { graph, graph_mouse: [ posX, posY ] } = canvas
const newNode = LiteGraph.createNode(name)
await new Promise(r => setTimeout(r, 0))
if (newNode && graph) {
newNode.pos = [ posX, posY ]
const addedNode = graph.add(newNode) ?? null
return addedNode
} else {
useToastStore().addAlert(t('assetBrowser.failedToCreateNode'))
return null
}
}
🤖 Prompt for AI Agents
In `@src/utils/litegraphUtil.ts` around lines 40 - 62, The createNode function
issues two problems: remove the redundant graph.change() call after
graph.add(newNode) since graph.add already invokes this.change() (refer to
createNode, graph.add and graph.change), or if a second notification is truly
required document the reason above the call; also remove the unnecessary await
new Promise(r => setTimeout(r, 0)) between LiteGraph.createNode and graph.add
unless there's a documented asynchronous side-effect—if an async yield is
required, replace it with a clear comment explaining why (e.g., waiting for node
registration) so the intent is explicit.

@AustinMroz AustinMroz closed this Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants