Skip to content

Range editor#10936

Merged
christian-byrne merged 3 commits intomainfrom
range-editor2
Apr 10, 2026
Merged

Range editor#10936
christian-byrne merged 3 commits intomainfrom
range-editor2

Conversation

@jtydhr88
Copy link
Copy Markdown
Collaborator

@jtydhr88 jtydhr88 commented Apr 7, 2026

BE change Comfy-Org/ComfyUI#13322

Summary

Add RANGE widget for image levels adjustment
- Add RangeEditor widget with three display modes: plain, gradient, and histogram

  • Support optional midpoint (gamma) control for non-linear midtone adjustment
  • Integrate histogram display from upstream node outputs

Screenshots (if applicable)

image image image

┆Issue is synchronized with this Notion page by Unito

@jtydhr88 jtydhr88 requested a review from a team April 7, 2026 15:13
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Apr 7, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Adds a new RangeEditor UI and related widget plumbing (component, composable, utils, types, schema, and tests). Moves histogramToPath from curve utilities into src/utils/histogramUtil.ts and updates imports/tests accordingly.

Changes

Cohort / File(s) Summary
Histogram Utility Migration
src/components/curve/curveUtils.ts, src/components/curve/curveUtils.test.ts, src/utils/histogramUtil.ts, src/utils/histogramUtil.test.ts
Moved histogramToPath out of curveUtils.ts into src/utils/histogramUtil.ts. Removed its test from the curve tests and added dedicated tests validating empty inputs, path shape, and percentile-based normalization.
CurveEditor Import Update
src/components/curve/CurveEditor.vue
Updated import to use @/utils/histogramUtil for histogramToPath; computed histogramPath logic unchanged.
Range Editor Component & Tests
src/components/range/RangeEditor.vue, src/components/range/RangeEditor.test.ts, src/components/range/WidgetRange.vue
Added RangeEditor.vue with SVG track, draggable min/max/midpoint, `plain
Range Utilities & Tests
src/components/range/rangeUtils.ts, src/components/range/rangeUtils.test.ts
Added positionToGamma, gammaToPosition, and isRangeValue with tests covering conversions, round-trip consistency, and type-guard behavior.
Pointer Interaction Composable
src/composables/useRangeEditor.ts
New useRangeEditor composable implementing pointer-driven editing (pointer capture, nearest-handle selection, clamping, midpoint handling, listener teardown, unmount cleanup).
Widget Type System & Mapping
src/lib/litegraph/src/types/widgets.ts, src/lib/litegraph/src/widgets/RangeWidget.ts, src/lib/litegraph/src/widgets/widgetMap.ts
Added RangeValue, IWidgetRangeOptions, IRangeWidget; implemented RangeWidget and registered 'range' in the widget map.
Vue Node Integration & Registry
src/renderer/extensions/vueNodes/widgets/composables/useRangeWidget.ts, src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts, src/scripts/widgets.ts
Added useRangeWidget constructor, registered WidgetRange in widget registry, added RANGE to ComfyWidgets, and included range in expanding behavior.
Schemas & Color Stop
src/schemas/nodeDef/nodeDefSchemaV2.ts, src/schemas/nodeDefSchema.ts
Added zRangeValue/zRangeInputSpec to accept RANGE inputs and exported RangeInputSpec. Introduced reusable zColorStop and switched gradient_stops to use it.
Math Utilities
src/utils/mathUtil.ts, src/utils/mathUtil.test.ts
Added normalize and denormalize functions and corresponding tests validating mapping, endpoints, and round-trip accuracy.

Sequence Diagram

sequenceDiagram
    participant User
    participant RangeEditor
    participant Composable as useRangeEditor
    participant Model as RangeValue
    participant Inputs as ScrubableNumberInput

    User->>RangeEditor: pointerdown on track or handle
    RangeEditor->>Composable: handleTrackPointerDown(event)
    Composable->>Composable: choose nearest handle (min/max/midpoint)
    Composable->>Composable: startDrag(handle, event)

    loop dragging
        User->>RangeEditor: pointermove
        RangeEditor->>Composable: pointermove event
        Composable->>Composable: compute normalized position, clamp & constrain
        Composable->>Model: update min/max/midpoint
        Model->>RangeEditor: re-render handles and visuals
        RangeEditor->>Inputs: update numeric inputs
    end

    User->>RangeEditor: pointerup / lostpointercapture
    Composable->>Composable: remove listeners, reset activeHandle
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰✨ I hopped along the histogram trail,
Dragged min and max with a floppy-tail,
Midpoint gamma twinkles like dawn,
Gradients shimmer, outside dims gone,
A tiny rabbit cheers: the range is drawn!


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore (reviewers only)

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
End-To-End Regression Coverage For Fixes ❌ Error PR contains bug-fix language but lacks browser_tests coverage or explanation for why e2e testing was not added. Add Playwright regression tests under browser_tests/ for Range editor functionality or provide concrete explanation in PR description.
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Range editor' is concise and directly reflects the main change: introduction of a new range editor widget component with multiple display modes.
Description check ✅ Passed The description provides a clear summary, lists key changes (three display modes, midpoint control, histogram integration), references the related backend PR, and includes helpful screenshots demonstrating the functionality.
Adr Compliance For Entity/Litegraph Changes ✅ Passed PR introduces new RANGE widget by extending existing litegraph patterns without violating ADR 0003/0008 principles: no command pattern violations, no god-object growth, proper ECS separation, no breaking extension API changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch range-editor2

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
Copy Markdown

github-actions Bot commented Apr 7, 2026

🎭 Playwright: ✅ 994 passed, 0 failed · 1 flaky

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 04/09/2026, 12:14:15 PM UTC

Links

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

📦 Bundle: 5.13 MB gzip 🔴 +5.6 kB

Details

Summary

  • Raw size: 23.6 MB baseline 23.6 MB — 🔴 +19.6 kB
  • Gzip: 5.13 MB baseline 5.12 MB — 🔴 +5.6 kB
  • Brotli: 3.97 MB baseline 3.96 MB — 🔴 +5.08 kB
  • Bundles: 252 current • 250 baseline • 210 added / 208 removed

Category Glance
Other 🔴 +16.9 kB (8.55 MB) · Data & Services 🔴 +2.52 kB (2.98 MB) · Utilities & Hooks 🔴 +153 B (339 kB) · Vendor & Third-Party ⚪ 0 B (9.8 MB) · Graph Workspace ⚪ 0 B (1.2 MB) · Panels & Settings ⚪ 0 B (484 kB) · + 5 more

App Entry Points — 22.3 kB (baseline 22.3 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-D1kOdyvL.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +7.95 kB 🔴 +6.8 kB
assets/index-DV7LerTk.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -7.95 kB 🟢 -6.83 kB

Status: 1 added / 1 removed

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

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-BoUl2JT5.js (new) 1.2 MB 🔴 +1.2 MB 🔴 +256 kB 🔴 +193 kB
assets/GraphView-D9pFCAct.js (removed) 1.2 MB 🟢 -1.2 MB 🟢 -256 kB 🟢 -193 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-DEI6Ryxb.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -3.4 kB 🟢 -2.9 kB
assets/CloudSurveyView-KG5rpmUj.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +3.4 kB 🔴 +2.9 kB
assets/CloudLoginView-D3NEO7zs.js (removed) 12 kB 🟢 -12 kB 🟢 -3.35 kB 🟢 -2.97 kB
assets/CloudLoginView-DWpYQ29b.js (new) 12 kB 🔴 +12 kB 🔴 +3.36 kB 🔴 +2.97 kB
assets/CloudSignupView-_G79Xcuz.js (new) 9.78 kB 🔴 +9.78 kB 🔴 +2.85 kB 🔴 +2.52 kB
assets/CloudSignupView-BgfnB45B.js (removed) 9.78 kB 🟢 -9.78 kB 🟢 -2.85 kB 🟢 -2.5 kB
assets/UserCheckView-C4qivT_k.js (new) 9.04 kB 🔴 +9.04 kB 🔴 +2.33 kB 🔴 +2.03 kB
assets/UserCheckView-Dcpixk8H.js (removed) 9.04 kB 🟢 -9.04 kB 🟢 -2.33 kB 🟢 -2.03 kB
assets/CloudLayoutView-CWMdlgbF.js (removed) 7.54 kB 🟢 -7.54 kB 🟢 -2.36 kB 🟢 -2.04 kB
assets/CloudLayoutView-Sw76g83b.js (new) 7.54 kB 🔴 +7.54 kB 🔴 +2.36 kB 🔴 +2.06 kB
assets/CloudForgotPasswordView-BMz0LZuy.js (removed) 5.94 kB 🟢 -5.94 kB 🟢 -2.09 kB 🟢 -1.86 kB
assets/CloudForgotPasswordView-ClkDFhrp.js (new) 5.94 kB 🔴 +5.94 kB 🔴 +2.09 kB 🔴 +1.84 kB
assets/CloudAuthTimeoutView-4fqSmBnT.js (removed) 5.31 kB 🟢 -5.31 kB 🟢 -1.93 kB 🟢 -1.7 kB
assets/CloudAuthTimeoutView-BWYxHiol.js (new) 5.31 kB 🔴 +5.31 kB 🔴 +1.93 kB 🔴 +1.69 kB
assets/CloudSubscriptionRedirectView-C30b2cCV.js (new) 5.08 kB 🔴 +5.08 kB 🔴 +1.91 kB 🔴 +1.69 kB
assets/CloudSubscriptionRedirectView-C4_r9Zgu.js (removed) 5.08 kB 🟢 -5.08 kB 🟢 -1.91 kB 🟢 -1.69 kB
assets/UserSelectView-C0UwEWqL.js (removed) 4.71 kB 🟢 -4.71 kB 🟢 -1.74 kB 🟢 -1.54 kB
assets/UserSelectView-CoYKitr4.js (new) 4.71 kB 🔴 +4.71 kB 🔴 +1.74 kB 🔴 +1.54 kB
assets/CloudSorryContactSupportView-CO9S63VP.js (new) 1.21 kB 🔴 +1.21 kB 🔴 +609 B 🔴 +532 B
assets/CloudSorryContactSupportView-Zo6nvFzV.js (removed) 1.21 kB 🟢 -1.21 kB 🟢 -608 B 🟢 -530 B
assets/layout-CbnMFmup.js (removed) 385 B 🟢 -385 B 🟢 -267 B 🟢 -217 B
assets/layout-lX3t8qwt.js (new) 385 B 🔴 +385 B 🔴 +269 B 🔴 +220 B

Status: 11 added / 11 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-Ci02AB8i.js (removed) 46.6 kB 🟢 -46.6 kB 🟢 -9.52 kB 🟢 -8.47 kB
assets/KeybindingPanel-DPj1WiXk.js (new) 46.6 kB 🔴 +46.6 kB 🔴 +9.52 kB 🔴 +8.47 kB
assets/settings-DEZ3RZmz.js (new) 39.3 kB 🔴 +39.3 kB 🔴 +9.61 kB 🔴 +7.91 kB
assets/settings-hUMJ3Clw.js (removed) 39.3 kB 🟢 -39.3 kB 🟢 -9.61 kB 🟢 -7.91 kB
assets/settings-BbHDTXXu.js (removed) 34.9 kB 🟢 -34.9 kB 🟢 -8.58 kB 🟢 -7.2 kB
assets/settings-DdACtCSy.js (new) 34.9 kB 🔴 +34.9 kB 🔴 +8.58 kB 🔴 +7.2 kB
assets/settings-BafyIWGA.js (new) 33.2 kB 🔴 +33.2 kB 🔴 +8.41 kB 🔴 +6.89 kB
assets/settings-DsZ7YsCq.js (removed) 33.2 kB 🟢 -33.2 kB 🟢 -8.41 kB 🟢 -6.89 kB
assets/settings-DABetgTO.js (new) 31.2 kB 🔴 +31.2 kB 🔴 +8.71 kB 🔴 +7.3 kB
assets/settings-DPFnTS4B.js (removed) 31.2 kB 🟢 -31.2 kB 🟢 -8.72 kB 🟢 -7.3 kB
assets/settings-BzwWoeWS.js (removed) 30.5 kB 🟢 -30.5 kB 🟢 -8.34 kB 🟢 -7.28 kB
assets/settings-lpzcom_5.js (new) 30.5 kB 🔴 +30.5 kB 🔴 +8.34 kB 🔴 +7.28 kB
assets/settings-6FWmUH7d.js (removed) 29.3 kB 🟢 -29.3 kB 🟢 -8.01 kB 🟢 -6.83 kB
assets/settings-Dev4mpPn.js (new) 29.3 kB 🔴 +29.3 kB 🔴 +8.01 kB 🔴 +6.83 kB
assets/settings-BzGksVti.js (new) 29.3 kB 🔴 +29.3 kB 🔴 +8.25 kB 🔴 +7.22 kB
assets/settings-C_Q5i0Do.js (removed) 29.3 kB 🟢 -29.3 kB 🟢 -8.25 kB 🟢 -7.21 kB
assets/settings-DeAvCGl7.js (removed) 28.4 kB 🟢 -28.4 kB 🟢 -7.93 kB 🟢 -6.89 kB
assets/settings-DuvMyarK.js (new) 28.4 kB 🔴 +28.4 kB 🔴 +7.93 kB 🔴 +6.89 kB
assets/settings-CQxMGzG_.js (new) 28.3 kB 🔴 +28.3 kB 🔴 +8.38 kB 🔴 +6.97 kB
assets/settings-CRgARs-6.js (removed) 28.3 kB 🟢 -28.3 kB 🟢 -8.38 kB 🟢 -7.01 kB
assets/settings-CU0eDkWg.js (removed) 24.9 kB 🟢 -24.9 kB 🟢 -8.13 kB 🟢 -6.59 kB
assets/settings-CwMw4_kx.js (new) 24.9 kB 🔴 +24.9 kB 🔴 +8.13 kB 🔴 +6.59 kB
assets/settings-B1YZs8zE.js (new) 24.3 kB 🔴 +24.3 kB 🔴 +7.91 kB 🔴 +6.16 kB
assets/settings-Bz_47nCg.js (removed) 24.3 kB 🟢 -24.3 kB 🟢 -7.91 kB 🟢 -6.15 kB
assets/SecretsPanel-0yEqWQrD.js (removed) 22.4 kB 🟢 -22.4 kB 🟢 -5.42 kB 🟢 -4.77 kB
assets/SecretsPanel-WY7GFKnx.js (new) 22.4 kB 🔴 +22.4 kB 🔴 +5.42 kB 🔴 +4.77 kB
assets/LegacyCreditsPanel-D0cvwmGl.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +5.81 kB 🔴 +5.13 kB
assets/LegacyCreditsPanel-vrXphi-e.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -5.81 kB 🟢 -5.13 kB
assets/SubscriptionPanel-BO8PrWN6.js (removed) 19.7 kB 🟢 -19.7 kB 🟢 -5.01 kB 🟢 -4.4 kB
assets/SubscriptionPanel-OhwiiLt6.js (new) 19.7 kB 🔴 +19.7 kB 🔴 +5.01 kB 🔴 +4.4 kB
assets/AboutPanel-B92uHqBo.js (new) 12 kB 🔴 +12 kB 🔴 +3.32 kB 🔴 +2.98 kB
assets/AboutPanel-cJMc04-s.js (removed) 12 kB 🟢 -12 kB 🟢 -3.33 kB 🟢 -2.98 kB
assets/ExtensionPanel-BMtQkhz8.js (new) 9.78 kB 🔴 +9.78 kB 🔴 +2.82 kB 🔴 +2.51 kB
assets/ExtensionPanel-C1bqwlCe.js (removed) 9.78 kB 🟢 -9.78 kB 🟢 -2.82 kB 🟢 -2.5 kB
assets/ServerConfigPanel-DFshAaUN.js (new) 6.85 kB 🔴 +6.85 kB 🔴 +2.28 kB 🔴 +2.04 kB
assets/ServerConfigPanel-DNa351tQ.js (removed) 6.85 kB 🟢 -6.85 kB 🟢 -2.27 kB 🟢 -2.03 kB
assets/UserPanel-DGo8uq0Z.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +2.16 kB 🔴 +1.89 kB
assets/UserPanel-DZzmq4PA.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -2.15 kB 🟢 -1.88 kB
assets/cloudRemoteConfig-BJ_Mga5a.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +902 B 🔴 +777 B
assets/cloudRemoteConfig-crcWP0Qe.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -903 B 🟢 -775 B
assets/config-BRXMTVls.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +866 B 🔴 +685 B
assets/config-BytgeQk3.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -866 B 🟢 -683 B
assets/refreshRemoteConfig-CKZMPh_9.js (removed) 1.45 kB 🟢 -1.45 kB 🟢 -649 B 🟢 -555 B
assets/refreshRemoteConfig-uBdzSMDb.js (new) 1.45 kB 🔴 +1.45 kB 🔴 +648 B 🔴 +555 B

Status: 22 added / 22 removed

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/PasswordFields-Bp7lkyAr.js (new) 4.68 kB 🔴 +4.68 kB 🔴 +1.42 kB 🔴 +1.25 kB
assets/PasswordFields-CrcBZL3E.js (removed) 4.68 kB 🟢 -4.68 kB 🟢 -1.42 kB 🟢 -1.26 kB
assets/auth-dwmiv1ks.js (removed) 3.57 kB 🟢 -3.57 kB 🟢 -1.26 kB 🟢 -1.07 kB
assets/auth-qVrtIOfe.js (new) 3.57 kB 🔴 +3.57 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/SignUpForm-D_iDh0qR.js (new) 3.16 kB 🔴 +3.16 kB 🔴 +1.29 kB 🔴 +1.15 kB
assets/SignUpForm-DdzryK9w.js (removed) 3.16 kB 🟢 -3.16 kB 🟢 -1.29 kB 🟢 -1.15 kB
assets/UpdatePasswordContent-D-GH7mYj.js (removed) 2.66 kB 🟢 -2.66 kB 🟢 -1.19 kB 🟢 -1.05 kB
assets/UpdatePasswordContent-NxqYBM-p.js (new) 2.66 kB 🔴 +2.66 kB 🔴 +1.19 kB 🔴 +1.05 kB
assets/WorkspaceProfilePic-BubLtH7k.js (new) 1.66 kB 🔴 +1.66 kB 🔴 +860 B 🔴 +783 B
assets/WorkspaceProfilePic-cG9UiDIS.js (removed) 1.66 kB 🟢 -1.66 kB 🟢 -861 B 🟢 -747 B
assets/authStore-CvsPoO_h.js (new) 989 B 🔴 +989 B 🔴 +486 B 🔴 +434 B
assets/authStore-DHb8ET3q.js (removed) 989 B 🟢 -989 B 🟢 -485 B 🟢 -432 B
assets/auth-BMTZmVdn.js (new) 348 B 🔴 +348 B 🔴 +216 B 🔴 +189 B
assets/auth-DznRALaW.js (removed) 348 B 🟢 -348 B 🟢 -214 B 🟢 -189 B

Status: 7 added / 7 removed

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useShareDialog-BuwB3D6E.js (removed) 108 kB 🟢 -108 kB 🟢 -22.4 kB 🟢 -18.9 kB
assets/useShareDialog-GHRhpjAX.js (new) 108 kB 🔴 +108 kB 🔴 +22.4 kB 🔴 +18.9 kB
assets/useSubscriptionDialog-CR2ZbmwC.js (new) 969 B 🔴 +969 B 🔴 +476 B 🔴 +419 B
assets/useSubscriptionDialog-DkTxlbHj.js (removed) 969 B 🟢 -969 B 🟢 -475 B 🟢 -415 B

Status: 2 added / 2 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-Bx_fBTiY.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/ComfyQueueButton-V-OLuJqE.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.79 kB 🟢 -3.38 kB
assets/useTerminalTabs-DW-7RqnV.js (removed) 10.7 kB 🟢 -10.7 kB 🟢 -3.6 kB 🟢 -3.17 kB
assets/useTerminalTabs-I0_fmTG8.js (new) 10.7 kB 🔴 +10.7 kB 🔴 +3.6 kB 🔴 +3.16 kB
assets/TopbarBadge-CilGCwUN.js (new) 7.62 kB 🔴 +7.62 kB 🔴 +1.87 kB 🔴 +1.64 kB
assets/TopbarBadge-CivLUvp6.js (removed) 7.62 kB 🟢 -7.62 kB 🟢 -1.87 kB 🟢 -1.64 kB
assets/FormSearchInput-C6zjCC36.js (removed) 5.11 kB 🟢 -5.11 kB 🟢 -2.12 kB 🟢 -1.84 kB
assets/FormSearchInput-CXAERpw5.js (new) 5.11 kB 🔴 +5.11 kB 🔴 +2.12 kB 🔴 +1.84 kB
assets/toggle-group-C9TyysMf.js (new) 4.03 kB 🔴 +4.03 kB 🔴 +1.41 kB 🔴 +1.25 kB
assets/toggle-group-DczfNkiV.js (removed) 4.03 kB 🟢 -4.03 kB 🟢 -1.41 kB 🟢 -1.25 kB
assets/SubscribeButton-B2sW59H-.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -913 B
assets/SubscribeButton-w63-imry.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +914 B
assets/WidgetButton-DJgaLQOW.js (new) 2.04 kB 🔴 +2.04 kB 🔴 +949 B 🔴 +860 B
assets/WidgetButton-TbcWum6K.js (removed) 2.04 kB 🟢 -2.04 kB 🟢 -950 B 🟢 -860 B
assets/cloudFeedbackTopbarButton-_BGZVVWv.js (new) 1.66 kB 🔴 +1.66 kB 🔴 +845 B 🔴 +748 B
assets/cloudFeedbackTopbarButton-CgEuPfnW.js (removed) 1.66 kB 🟢 -1.66 kB 🟢 -841 B 🟢 -744 B
assets/UserAvatar-CIq9FpNR.js (new) 1.28 kB 🔴 +1.28 kB 🔴 +673 B 🔴 +585 B
assets/UserAvatar-DcGdl_le.js (removed) 1.28 kB 🟢 -1.28 kB 🟢 -670 B 🟢 -576 B
assets/CloudBadge-0cQsiJvd.js (removed) 1.17 kB 🟢 -1.17 kB 🟢 -592 B 🟢 -520 B
assets/CloudBadge-QtgGTCc0.js (new) 1.17 kB 🔴 +1.17 kB 🔴 +592 B 🔴 +521 B
assets/ComfyQueueButton-14w4JPuI.js (removed) 1.03 kB 🟢 -1.03 kB 🟢 -489 B 🟢 -439 B
assets/ComfyQueueButton-D7ToMdTD.js (new) 1.03 kB 🔴 +1.03 kB 🔴 +489 B 🔴 +442 B

Status: 11 added / 11 removed / 2 unchanged

Data & Services — 2.98 MB (baseline 2.98 MB) • 🔴 +2.52 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-BNWJdTj6.js (new) 1.94 MB 🔴 +1.94 MB 🔴 +448 kB 🔴 +339 kB
assets/dialogService-DbmAnbLB.js (removed) 1.94 MB 🟢 -1.94 MB 🟢 -447 kB 🟢 -339 kB
assets/api-C6AGCdgy.js (new) 886 kB 🔴 +886 kB 🔴 +212 kB 🔴 +167 kB
assets/api-CxBvAfpl.js (removed) 885 kB 🟢 -885 kB 🟢 -211 kB 🟢 -167 kB
assets/load3dService-CozbCwYM.js (new) 92.5 kB 🔴 +92.5 kB 🔴 +19.7 kB 🔴 +16.9 kB
assets/load3dService-DxvH0CDR.js (removed) 92.5 kB 🟢 -92.5 kB 🟢 -19.7 kB 🟢 -16.9 kB
assets/workflowShareService-CgkVqDah.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.88 kB 🟢 -4.33 kB
assets/workflowShareService-D8lBtuns.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.88 kB 🔴 +4.32 kB
assets/keybindingService-C2DIc85K.js (new) 13.8 kB 🔴 +13.8 kB 🔴 +3.67 kB 🔴 +3.21 kB
assets/keybindingService-UrP9-Azh.js (removed) 13.8 kB 🟢 -13.8 kB 🟢 -3.66 kB 🟢 -3.22 kB
assets/releaseStore-Bth2Vr9y.js (removed) 8.12 kB 🟢 -8.12 kB 🟢 -2.28 kB 🟢 -2 kB
assets/releaseStore-DHHJRumv.js (new) 8.12 kB 🔴 +8.12 kB 🔴 +2.28 kB 🔴 +2 kB
assets/serverConfigStore-DpbKfCP_.js (new) 2.35 kB 🔴 +2.35 kB 🔴 +810 B 🔴 +707 B
assets/serverConfigStore-x4Y-WkJ3.js (removed) 2.35 kB 🟢 -2.35 kB 🟢 -810 B 🟢 -707 B
assets/userStore-CLhLSwal.js (removed) 2.24 kB 🟢 -2.24 kB 🟢 -866 B 🟢 -758 B
assets/userStore-CQHHsSVa.js (new) 2.24 kB 🔴 +2.24 kB 🔴 +870 B 🔴 +767 B
assets/audioService-BKZyzGOi.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -877 B 🟢 -756 B
assets/audioService-Bpbm__Q5.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +878 B 🔴 +766 B
assets/releaseStore-CbxGZUFW.js (new) 993 B 🔴 +993 B 🔴 +480 B 🔴 +430 B
assets/releaseStore-Cf4WeUHS.js (removed) 993 B 🟢 -993 B 🟢 -479 B 🟢 -429 B
assets/workflowDraftStore-CB5_O928.js (removed) 969 B 🟢 -969 B 🟢 -475 B 🟢 -419 B
assets/workflowDraftStore-CP0L25x4.js (new) 969 B 🔴 +969 B 🔴 +475 B 🔴 +427 B
assets/dialogService-14RWPnVn.js (new) 958 B 🔴 +958 B 🔴 +468 B 🔴 +417 B
assets/dialogService-CIIn8JPj.js (removed) 958 B 🟢 -958 B 🟢 -467 B 🟢 -418 B
assets/settingStore-BNhUvFaY.js (new) 956 B 🔴 +956 B 🔴 +470 B 🔴 +417 B
assets/settingStore-BUU2JSvi.js (removed) 956 B 🟢 -956 B 🟢 -470 B 🟢 -416 B
assets/assetsStore-53yKcz98.js (removed) 955 B 🟢 -955 B 🟢 -470 B 🟢 -419 B
assets/assetsStore-DMq8Vzig.js (new) 955 B 🔴 +955 B 🔴 +470 B 🔴 +418 B

Status: 14 added / 14 removed / 3 unchanged

Utilities & Hooks — 339 kB (baseline 338 kB) • 🔴 +153 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-CwrQ5n9t.js (removed) 232 kB 🟢 -232 kB 🟢 -51.3 kB 🟢 -41.8 kB
assets/useConflictDetection-jT59SxKF.js (new) 232 kB 🔴 +232 kB 🔴 +51.3 kB 🔴 +41.8 kB
assets/useLoad3dViewer-C8ecDc_x.js (new) 18.7 kB 🔴 +18.7 kB 🔴 +4.43 kB 🔴 +3.87 kB
assets/useLoad3dViewer-Dyi-CB9z.js (removed) 18.7 kB 🟢 -18.7 kB 🟢 -4.43 kB 🟢 -3.87 kB
assets/useLoad3d-DBOhUwqq.js (removed) 15 kB 🟢 -15 kB 🟢 -3.79 kB 🟢 -3.35 kB
assets/useLoad3d-v11wl9LG.js (new) 15 kB 🔴 +15 kB 🔴 +3.79 kB 🔴 +3.37 kB
assets/useFeatureFlags-C5ggZBfk.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.75 kB 🔴 +1.48 kB
assets/useFeatureFlags-XtbDbz3K.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.75 kB 🟢 -1.48 kB
assets/useCopyToClipboard-DBhJg_xP.js (new) 5.29 kB 🔴 +5.29 kB 🔴 +1.86 kB 🔴 +1.57 kB
assets/useCopyToClipboard-mai3w0Gm.js (removed) 5.29 kB 🟢 -5.29 kB 🟢 -1.86 kB 🟢 -1.58 kB
assets/downloadUtil-CNDF2jzR.js (removed) 4.68 kB 🟢 -4.68 kB 🟢 -1.85 kB 🟢 -1.54 kB
assets/downloadUtil-JmV9nJQD.js (new) 4.68 kB 🔴 +4.68 kB 🔴 +1.85 kB 🔴 +1.54 kB
assets/curveUtils-DrOuXIbn.js (removed) 4.65 kB 🟢 -4.65 kB 🟢 -1.63 kB 🟢 -1.42 kB
assets/curveUtils-s-_AdD9h.js (new) 3.83 kB 🔴 +3.83 kB 🔴 +1.29 kB 🔴 +1.13 kB
assets/useWorkspaceUI-B2f0GMZp.js (removed) 3.34 kB 🟢 -3.34 kB 🟢 -979 B 🟢 -809 B
assets/useWorkspaceUI-CsM4phog.js (new) 3.34 kB 🔴 +3.34 kB 🔴 +980 B 🔴 +812 B
assets/useExternalLink-Bif0QVC4.js (removed) 3.04 kB 🟢 -3.04 kB 🟢 -1.17 kB 🟢 -1.04 kB
assets/useExternalLink-Cc2BZe6V.js (new) 3.04 kB 🔴 +3.04 kB 🔴 +1.17 kB 🔴 +1.03 kB
assets/subscriptionCheckoutUtil-BWtec4Xy.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +1.31 kB 🔴 +1.14 kB
assets/subscriptionCheckoutUtil-CroXjK1U.js (removed) 2.97 kB 🟢 -2.97 kB 🟢 -1.3 kB 🟢 -1.14 kB
assets/assetPreviewUtil-9bnFdoUT.js (removed) 2.27 kB 🟢 -2.27 kB 🟢 -954 B 🟢 -839 B
assets/assetPreviewUtil-Bf9ZloJz.js (new) 2.27 kB 🔴 +2.27 kB 🔴 +959 B 🔴 +833 B
assets/useUpstreamValue-CvmXatvR.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -801 B 🟢 -719 B
assets/useUpstreamValue-CzMrX8CF.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +806 B 🔴 +724 B
assets/useErrorHandling-BEhSVWgV.js (removed) 1.54 kB 🟢 -1.54 kB 🟢 -646 B 🟢 -548 B
assets/useErrorHandling-F87GTlU5.js (new) 1.54 kB 🔴 +1.54 kB 🔴 +649 B 🔴 +554 B
assets/useWorkspaceTierLabel-CqNeL9mC.js (new) 1.35 kB 🔴 +1.35 kB 🔴 +668 B 🔴 +579 B
assets/useWorkspaceTierLabel-DZRMnzmo.js (removed) 1.35 kB 🟢 -1.35 kB 🟢 -667 B 🟢 -556 B
assets/useLoad3d-Bh4te6J_.js (removed) 1.13 kB 🟢 -1.13 kB 🟢 -538 B 🟢 -477 B
assets/useLoad3d-DRiFXgiP.js (new) 1.13 kB 🔴 +1.13 kB 🔴 +538 B 🔴 +480 B
assets/useLoad3dViewer-DkSFz_Nb.js (new) 1.07 kB 🔴 +1.07 kB 🔴 +504 B 🔴 +454 B
assets/useLoad3dViewer-qmO9m2r5.js (removed) 1.07 kB 🟢 -1.07 kB 🟢 -502 B 🟢 -451 B
assets/histogramUtil-BN5NEyxf.js (new) 972 B 🔴 +972 B 🔴 +574 B 🔴 +493 B
assets/useCurrentUser-BdT1677Z.js (removed) 955 B 🟢 -955 B 🟢 -469 B 🟢 -416 B
assets/useCurrentUser-GZj2AEbD.js (new) 955 B 🔴 +955 B 🔴 +471 B 🔴 +417 B
assets/useClickDragGuard-BD63kSRa.js (new) 828 B 🔴 +828 B 🔴 +408 B 🔴 +361 B
assets/useClickDragGuard-DNjf-Zpl.js (removed) 828 B 🟢 -828 B 🟢 -410 B 🟢 -360 B
assets/useWorkspaceSwitch-Bew27M0k.js (new) 747 B 🔴 +747 B 🔴 +386 B 🔴 +333 B
assets/useWorkspaceSwitch-BXMwyAmX.js (removed) 747 B 🟢 -747 B 🟢 -383 B 🟢 -329 B

Status: 20 added / 19 removed / 7 unchanged

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-xterm-r8rHkirJ.js (removed) 374 kB 🟢 -374 kB 🟢 -75.6 kB 🟢 -61.1 kB
assets/vendor-xterm-SU-jBs9b.js (new) 374 kB 🔴 +374 kB 🔴 +75.6 kB 🔴 +61.1 kB

Status: 1 added / 1 removed / 15 unchanged

Other — 8.55 MB (baseline 8.53 MB) • 🔴 +16.9 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/i18n-Bp7BgHdV.js (removed) 572 kB 🟢 -572 kB 🟢 -112 kB 🟢 -87.7 kB
assets/i18n-Cbv27rS8.js (new) 572 kB 🔴 +572 kB 🔴 +112 kB 🔴 +87.7 kB
assets/nodeDefs-BUWdGdn8.js (removed) 520 kB 🟢 -520 kB 🟢 -79.3 kB 🟢 -54.4 kB
assets/nodeDefs-CHLYXhay.js (new) 520 kB 🔴 +520 kB 🔴 +79.3 kB 🔴 +54.4 kB
assets/nodeDefs-CQHhCS4b.js (removed) 477 kB 🟢 -477 kB 🟢 -74 kB 🟢 -52.1 kB
assets/nodeDefs-Dri88Fyf.js (new) 477 kB 🔴 +477 kB 🔴 +74 kB 🔴 +52.1 kB
assets/nodeDefs-BCwmY3Tb.js (removed) 476 kB 🟢 -476 kB 🟢 -71.6 kB 🟢 -50.3 kB
assets/nodeDefs-BIb70T_n.js (new) 476 kB 🔴 +476 kB 🔴 +71.6 kB 🔴 +50.3 kB
assets/nodeDefs-Ba3AboKg.js (removed) 439 kB 🟢 -439 kB 🟢 -72 kB 🟢 -50.3 kB
assets/nodeDefs-D-9V5f58.js (new) 439 kB 🔴 +439 kB 🔴 +72 kB 🔴 +50.3 kB
assets/nodeDefs-B3iQFTeW.js (new) 427 kB 🔴 +427 kB 🔴 +70.3 kB 🔴 +49.8 kB
assets/nodeDefs-BQ5B7gBv.js (removed) 427 kB 🟢 -427 kB 🟢 -70.3 kB 🟢 -49.8 kB
assets/nodeDefs-Azf0Mc64.js (new) 423 kB 🔴 +423 kB 🔴 +68.9 kB 🔴 +50.4 kB
assets/nodeDefs-C7bm7rJP.js (removed) 423 kB 🟢 -423 kB 🟢 -68.9 kB 🟢 -50.4 kB
assets/nodeDefs-Cbeub3x3.js (removed) 421 kB 🟢 -421 kB 🟢 -70.2 kB 🟢 -51 kB
assets/nodeDefs-Ci8N7Jw1.js (new) 421 kB 🔴 +421 kB 🔴 +70.2 kB 🔴 +51 kB
assets/nodeDefs-B8oUt0Pc.js (removed) 419 kB 🟢 -419 kB 🟢 -67.3 kB 🟢 -49.2 kB
assets/nodeDefs-Cc0ZU18z.js (new) 419 kB 🔴 +419 kB 🔴 +67.3 kB 🔴 +49.2 kB
assets/nodeDefs-De5VbhZR.js (removed) 413 kB 🟢 -413 kB 🟢 -66.4 kB 🟢 -48.5 kB
assets/nodeDefs-DZq15-jq.js (new) 413 kB 🔴 +413 kB 🔴 +66.4 kB 🔴 +48.5 kB
assets/nodeDefs-CGt9Hkm4.js (removed) 387 kB 🟢 -387 kB 🟢 -69 kB 🟢 -48.4 kB
assets/nodeDefs-DUnFTfDb.js (new) 387 kB 🔴 +387 kB 🔴 +69 kB 🔴 +48.4 kB
assets/nodeDefs-B2hVyQ7p.js (new) 383 kB 🔴 +383 kB 🔴 +67.8 kB 🔴 +47.1 kB
assets/nodeDefs-DyPdgFht.js (removed) 383 kB 🟢 -383 kB 🟢 -67.8 kB 🟢 -47.1 kB
assets/main-BvZB1pJe.js (removed) 237 kB 🟢 -237 kB 🟢 -61.2 kB 🟢 -48.1 kB
assets/main-C4oP5dNF.js (new) 237 kB 🔴 +237 kB 🔴 +61.2 kB 🔴 +48.1 kB
assets/main-0OEcCXi9.js (new) 213 kB 🔴 +213 kB 🔴 +55.3 kB 🔴 +44.5 kB
assets/main-ByPBqTCb.js (removed) 213 kB 🟢 -213 kB 🟢 -55.3 kB 🟢 -44.5 kB
assets/main-CDJViQ0W.js (removed) 204 kB 🟢 -204 kB 🟢 -55.1 kB 🟢 -43.9 kB
assets/main-CFX25wei.js (new) 204 kB 🔴 +204 kB 🔴 +55.1 kB 🔴 +43.9 kB
assets/main-C3RBWjFD.js (new) 195 kB 🔴 +195 kB 🔴 +54.7 kB 🔴 +43.5 kB
assets/main-D4E2PpzE.js (removed) 195 kB 🟢 -195 kB 🟢 -54.7 kB 🟢 -43.5 kB
assets/main-2F8oug7t.js (removed) 178 kB 🟢 -178 kB 🟢 -53.2 kB 🟢 -44.4 kB
assets/main-DbICkggj.js (new) 178 kB 🔴 +178 kB 🔴 +53.2 kB 🔴 +44.4 kB
assets/main-D9FV_AXS.js (removed) 175 kB 🟢 -175 kB 🟢 -52.5 kB 🟢 -42.3 kB
assets/main-Ddl3q1n7.js (new) 175 kB 🔴 +175 kB 🔴 +52.5 kB 🔴 +42.3 kB
assets/main-DCQh1Iw2.js (removed) 172 kB 🟢 -172 kB 🟢 -52 kB 🟢 -43.1 kB
assets/main-jlYZBKyX.js (new) 172 kB 🔴 +172 kB 🔴 +52 kB 🔴 +43.1 kB
assets/main-BrqKIHDn.js (removed) 169 kB 🟢 -169 kB 🟢 -52.3 kB 🟢 -43.7 kB
assets/main-D2U6a6KE.js (new) 169 kB 🔴 +169 kB 🔴 +52.3 kB 🔴 +43.7 kB
assets/main-rfQkPQ48.js (new) 168 kB 🔴 +168 kB 🔴 +51.5 kB 🔴 +43.2 kB
assets/main-Y3xXgJ0W.js (removed) 168 kB 🟢 -168 kB 🟢 -51.5 kB 🟢 -43.2 kB
assets/main-Blnj5G6f.js (new) 150 kB 🔴 +150 kB 🔴 +51.3 kB 🔴 +41 kB
assets/main-DxUhia0N.js (removed) 150 kB 🟢 -150 kB 🟢 -51.3 kB 🟢 -41 kB
assets/main-CEjkgJ1A.js (new) 149 kB 🔴 +149 kB 🔴 +51.2 kB 🔴 +40.7 kB
assets/main-CnNR1VPj.js (removed) 149 kB 🟢 -149 kB 🟢 -51.2 kB 🟢 -40.7 kB
assets/core-BUgfFJT5.js (new) 77 kB 🔴 +77 kB 🔴 +20 kB 🔴 +17 kB
assets/core-DWQaO-9r.js (removed) 77 kB 🟢 -77 kB 🟢 -19.9 kB 🟢 -17 kB
assets/groupNode-CEQQqRlF.js (removed) 74 kB 🟢 -74 kB 🟢 -18.5 kB 🟢 -16.3 kB
assets/groupNode-DbePZQxs.js (new) 74 kB 🔴 +74 kB 🔴 +18.5 kB 🔴 +16.3 kB
assets/WidgetSelect-BGUO8I8d.js (new) 64.6 kB 🔴 +64.6 kB 🔴 +14.1 kB 🔴 +12.2 kB
assets/WidgetSelect-Bs2O-_Xh.js (removed) 64.6 kB 🟢 -64.6 kB 🟢 -14.1 kB 🟢 -12.2 kB
assets/SubscriptionRequiredDialogContentWorkspace-DNyr1DO6.js (new) 48.9 kB 🔴 +48.9 kB 🔴 +9.29 kB 🔴 +7.97 kB
assets/SubscriptionRequiredDialogContentWorkspace-lyJtvAiR.js (removed) 48.9 kB 🟢 -48.9 kB 🟢 -9.3 kB 🟢 -7.97 kB
assets/WidgetPainter-BMHokms-.js (new) 33.3 kB 🔴 +33.3 kB 🔴 +8.12 kB 🔴 +7.19 kB
assets/WidgetPainter-Cwglzk9k.js (removed) 33.3 kB 🟢 -33.3 kB 🟢 -8.11 kB 🟢 -7.19 kB
assets/WorkspacePanelContent-BMPB3uVB.js (new) 32.6 kB 🔴 +32.6 kB 🔴 +6.93 kB 🔴 +6.12 kB
assets/WorkspacePanelContent-EMBlxo2D.js (removed) 32.6 kB 🟢 -32.6 kB 🟢 -6.93 kB 🟢 -6.14 kB
assets/Load3DControls-BEcUgpj_.js (removed) 32.1 kB 🟢 -32.1 kB 🟢 -5.47 kB 🟢 -4.75 kB
assets/Load3DControls-gI8F5r-a.js (new) 32.1 kB 🔴 +32.1 kB 🔴 +5.47 kB 🔴 +4.75 kB
assets/SubscriptionRequiredDialogContent-7dUBS8gS.js (removed) 28.2 kB 🟢 -28.2 kB 🟢 -7.17 kB 🟢 -6.3 kB
assets/SubscriptionRequiredDialogContent-BJ9hLh0e.js (new) 28.2 kB 🔴 +28.2 kB 🔴 +7.17 kB 🔴 +6.3 kB
assets/Load3dViewerContent-DZDPjVKK.js (new) 24.5 kB 🔴 +24.5 kB 🔴 +5.33 kB 🔴 +4.64 kB
assets/Load3dViewerContent-HMiGtrwi.js (removed) 24.5 kB 🟢 -24.5 kB 🟢 -5.33 kB 🟢 -4.64 kB
assets/WidgetImageCrop-BLYovCzv.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.82 kB 🟢 -5.13 kB
assets/WidgetImageCrop-CPy0pvN8.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.83 kB 🔴 +5.14 kB
assets/SubscriptionPanelContentWorkspace-CFWxLdaW.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.18 kB 🔴 +4.56 kB
assets/SubscriptionPanelContentWorkspace-TwWfhDlN.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.18 kB 🟢 -4.56 kB
assets/SignInContent-D69_5kF6.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.29 kB 🔴 +4.62 kB
assets/SignInContent-DgVdtuGT.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.29 kB 🟢 -4.64 kB
assets/CurrentUserPopoverWorkspace-geOzZRb3.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -4.83 kB 🟢 -4.32 kB
assets/CurrentUserPopoverWorkspace-urmQj_0y.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +4.83 kB 🔴 +4.32 kB
assets/WidgetInputNumber--63ctwY4.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.84 kB 🔴 +4.31 kB
assets/WidgetInputNumber-C46NwRIF.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.84 kB 🟢 -4.3 kB
assets/commands-BFtpT1Rb.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.1 kB 🔴 +3.19 kB
assets/commands-CQwC5OO-.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.11 kB 🟢 -3.19 kB
assets/commands-Bq6jIr90.js (new) 17.8 kB 🔴 +17.8 kB 🔴 +3.79 kB 🔴 +2.94 kB
assets/commands-CdEZZDxE.js (removed) 17.8 kB 🟢 -17.8 kB 🟢 -3.78 kB 🟢 -2.94 kB
assets/commands-CS5QZIWR.js (removed) 17.8 kB 🟢 -17.8 kB 🟢 -3.85 kB 🟢 -3.04 kB
assets/commands-DU9xGUC9.js (new) 17.8 kB 🔴 +17.8 kB 🔴 +3.85 kB 🔴 +3.04 kB
assets/WidgetRecordAudio-BJhjmtPk.js (new) 17.2 kB 🔴 +17.2 kB 🔴 +4.92 kB 🔴 +4.41 kB
assets/WidgetRecordAudio-DToex9XS.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -4.92 kB 🟢 -4.41 kB
assets/commands-CIYimbnH.js (new) 17.2 kB 🔴 +17.2 kB 🔴 +3.88 kB 🔴 +3.05 kB
assets/commands-T3FSDQr4.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -3.88 kB 🟢 -3.05 kB
assets/commands-B6UvGOJ3.js (removed) 16.9 kB 🟢 -16.9 kB 🟢 -3.62 kB 🟢 -3 kB
assets/commands-guUlnidW.js (new) 16.9 kB 🔴 +16.9 kB 🔴 +3.61 kB 🔴 +3 kB
assets/WidgetRange-Br0lY88x.js (new) 16.9 kB 🔴 +16.9 kB 🔴 +4.53 kB 🔴 +4.04 kB
assets/Load3D-6cuDde2x.js (removed) 16.9 kB 🟢 -16.9 kB 🟢 -4.11 kB 🟢 -3.59 kB
assets/Load3D-CH380uQX.js (new) 16.9 kB 🔴 +16.9 kB 🔴 +4.12 kB 🔴 +3.59 kB
assets/commands-bzqV22ip.js (removed) 16.3 kB 🟢 -16.3 kB 🟢 -3.6 kB 🟢 -2.96 kB
assets/commands-jWr_gbPX.js (new) 16.3 kB 🔴 +16.3 kB 🔴 +3.6 kB 🔴 +3 kB
assets/commands-CoOTlXvN.js (removed) 16.3 kB 🟢 -16.3 kB 🟢 -3.5 kB 🟢 -2.86 kB
assets/commands-CvrNFvnH.js (new) 16.3 kB 🔴 +16.3 kB 🔴 +3.5 kB 🔴 +2.86 kB
assets/commands-bC49kgpF.js (new) 16.3 kB 🔴 +16.3 kB 🔴 +3.47 kB 🔴 +2.88 kB
assets/commands-BPXRO0Dy.js (removed) 16.3 kB 🟢 -16.3 kB 🟢 -3.47 kB 🟢 -2.88 kB
assets/WidgetColorPicker-C8pEXF6e.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -3.99 kB 🟢 -3.55 kB
assets/WidgetColorPicker-CFp2D_sI.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +3.99 kB 🔴 +3.56 kB
assets/commands-BE2Z2kW9.js (removed) 16.1 kB 🟢 -16.1 kB 🟢 -3.74 kB 🟢 -2.92 kB
assets/commands-BRxCSaWr.js (new) 16.1 kB 🔴 +16.1 kB 🔴 +3.74 kB 🔴 +2.92 kB
assets/commands-CBAiOa8q.js (removed) 15.4 kB 🟢 -15.4 kB 🟢 -3.67 kB 🟢 -2.77 kB
assets/commands-SLyeLVnX.js (new) 15.4 kB 🔴 +15.4 kB 🔴 +3.67 kB 🔴 +2.77 kB
assets/commands-CJd8-jkH.js (new) 15.3 kB 🔴 +15.3 kB 🔴 +3.63 kB 🔴 +2.71 kB
assets/commands-CuR4jKO9.js (removed) 15.3 kB 🟢 -15.3 kB 🟢 -3.63 kB 🟢 -2.71 kB
assets/load3d-CNjD4Sl3.js (removed) 15 kB 🟢 -15 kB 🟢 -4.31 kB 🟢 -3.74 kB
assets/load3d-CwaaUBij.js (new) 15 kB 🔴 +15 kB 🔴 +4.32 kB 🔴 +3.74 kB
assets/WaveAudioPlayer-BSCx3ijv.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.68 kB 🔴 +3.23 kB
assets/WaveAudioPlayer-fbw4lspv.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.68 kB 🟢 -3.22 kB
assets/WidgetCurve-B3biPHHg.js (new) 12 kB 🔴 +12 kB 🔴 +3.87 kB 🔴 +3.5 kB
assets/WidgetCurve-Cp9DDS7y.js (removed) 12 kB 🟢 -12 kB 🟢 -3.85 kB 🟢 -3.48 kB
assets/TeamWorkspacesDialogContent-BJKH0Hqo.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -3.33 kB 🟢 -2.97 kB
assets/TeamWorkspacesDialogContent-DGL-clJx.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +3.33 kB 🔴 +2.98 kB
assets/AudioPreviewPlayer-CHA7jAa8.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +3.13 kB 🔴 +2.8 kB
assets/AudioPreviewPlayer-Co2YbWgM.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -3.13 kB 🟢 -2.81 kB
assets/nodeTemplates-6OTQ7nPU.js (removed) 9.58 kB 🟢 -9.58 kB 🟢 -3.37 kB 🟢 -2.97 kB
assets/nodeTemplates-DyuI9idH.js (new) 9.58 kB 🔴 +9.58 kB 🔴 +3.37 kB 🔴 +2.97 kB
assets/NightlySurveyController-CQ8Ec_wU.js (new) 7.89 kB 🔴 +7.89 kB 🔴 +2.62 kB 🔴 +2.3 kB
assets/NightlySurveyController-Dm5HbqxD.js (removed) 7.89 kB 🟢 -7.89 kB 🟢 -2.62 kB 🟢 -2.3 kB
assets/WidgetImageCompare-B0Osy1TG.js (removed) 7.79 kB 🟢 -7.79 kB 🟢 -2.26 kB 🟢 -1.98 kB
assets/WidgetImageCompare-BQ-i9FB9.js (new) 7.79 kB 🔴 +7.79 kB 🔴 +2.26 kB 🔴 +1.98 kB
assets/InviteMemberDialogContent-b8lx5mi3.js (new) 7.77 kB 🔴 +7.77 kB 🔴 +2.45 kB 🔴 +2.14 kB
assets/InviteMemberDialogContent-DBzNaRT3.js (removed) 7.77 kB 🟢 -7.77 kB 🟢 -2.45 kB 🟢 -2.13 kB
assets/Load3DConfiguration-274WK9K7.js (new) 6.6 kB 🔴 +6.6 kB 🔴 +2.04 kB 🔴 +1.78 kB
assets/Load3DConfiguration-CnX8Ru8y.js (removed) 6.6 kB 🟢 -6.6 kB 🟢 -2.04 kB 🟢 -1.78 kB
assets/onboardingCloudRoutes-DTo77bP9.js (removed) 6.53 kB 🟢 -6.53 kB 🟢 -2.04 kB 🟢 -1.76 kB
assets/onboardingCloudRoutes-DTZoyQfB.js (new) 6.53 kB 🔴 +6.53 kB 🔴 +2.04 kB 🔴 +1.77 kB
assets/WidgetWithControl-Baqg7G7B.js (removed) 5.99 kB 🟢 -5.99 kB 🟢 -2.38 kB 🟢 -2.13 kB
assets/WidgetWithControl-DoIghDKZ.js (new) 5.99 kB 🔴 +5.99 kB 🔴 +2.38 kB 🔴 +2.12 kB
assets/CreateWorkspaceDialogContent-5jfMvLjK.js (removed) 5.95 kB 🟢 -5.95 kB 🟢 -2.15 kB 🟢 -1.88 kB
assets/CreateWorkspaceDialogContent-DV-0ZPoE.js (new) 5.95 kB 🔴 +5.95 kB 🔴 +2.15 kB 🔴 +1.88 kB
assets/FreeTierDialogContent-CFX79UpA.js (new) 5.82 kB 🔴 +5.82 kB 🔴 +2.05 kB 🔴 +1.82 kB
assets/FreeTierDialogContent-Da2m0win.js (removed) 5.82 kB 🟢 -5.82 kB 🟢 -2.05 kB 🟢 -1.81 kB
assets/EditWorkspaceDialogContent-C9BOt7If.js (new) 5.75 kB 🔴 +5.75 kB 🔴 +2.11 kB 🔴 +1.84 kB
assets/EditWorkspaceDialogContent-Dfitf0Bx.js (removed) 5.75 kB 🟢 -5.75 kB 🟢 -2.11 kB 🟢 -1.84 kB
assets/WidgetTextarea-9npCf7Vx.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +2.18 kB 🔴 +1.93 kB
assets/WidgetTextarea-DWz8KUii.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -2.17 kB 🟢 -1.93 kB
assets/Preview3d-Bg9fq5k4.js (new) 5.36 kB 🔴 +5.36 kB 🔴 +1.79 kB 🔴 +1.57 kB
assets/Preview3d-H6YZN5yX.js (removed) 5.36 kB 🟢 -5.36 kB 🟢 -1.79 kB 🟢 -1.56 kB
assets/ValueControlPopover-A0PKWsUh.js (removed) 5.33 kB 🟢 -5.33 kB 🟢 -1.93 kB 🟢 -1.72 kB
assets/ValueControlPopover-CAIqtcI8.js (new) 5.33 kB 🔴 +5.33 kB 🔴 +1.93 kB 🔴 +1.72 kB
assets/CancelSubscriptionDialogContent-C2QMbAAz.js (new) 5.22 kB 🔴 +5.22 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/CancelSubscriptionDialogContent-H7sxMBra.js (removed) 5.22 kB 🟢 -5.22 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/CloudNotificationContent-BUXuHwr2.js (new) 4.96 kB 🔴 +4.96 kB 🔴 +1.77 kB 🔴 +1.52 kB
assets/CloudNotificationContent-UOi0D9p7.js (removed) 4.96 kB 🟢 -4.96 kB 🟢 -1.77 kB 🟢 -1.52 kB
assets/AnimationControls-bGdONR1h.js (new) 4.78 kB 🔴 +4.78 kB 🔴 +1.66 kB 🔴 +1.46 kB
assets/AnimationControls-dEDCTTQj.js (removed) 4.78 kB 🟢 -4.78 kB 🟢 -1.66 kB 🟢 -1.46 kB
assets/DeleteWorkspaceDialogContent-CLbCbUGV.js (new) 4.65 kB 🔴 +4.65 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/DeleteWorkspaceDialogContent-e-1yYsJ1.js (removed) 4.65 kB 🟢 -4.65 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/missingModelDownload-ADmxy7k_.js (new) 4.5 kB 🔴 +4.5 kB 🔴 +1.76 kB 🔴 +1.55 kB
assets/missingModelDownload-C_WqyjKg.js (removed) 4.5 kB 🟢 -4.5 kB 🟢 -1.76 kB 🟢 -1.55 kB
assets/LeaveWorkspaceDialogContent-BtRvB7N-.js (removed) 4.48 kB 🟢 -4.48 kB 🟢 -1.73 kB 🟢 -1.5 kB
assets/LeaveWorkspaceDialogContent-C3TMl2LW.js (new) 4.48 kB 🔴 +4.48 kB 🔴 +1.73 kB 🔴 +1.51 kB
assets/RemoveMemberDialogContent-CI6CPc_Q.js (removed) 4.46 kB 🟢 -4.46 kB 🟢 -1.69 kB 🟢 -1.47 kB
assets/RemoveMemberDialogContent-D3oe1RGP.js (new) 4.46 kB 🔴 +4.46 kB 🔴 +1.69 kB 🔴 +1.47 kB
assets/tierBenefits-CAsK7WEV.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.58 kB 🟢 -1.36 kB
assets/tierBenefits-pwWnfKXJ.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.58 kB 🔴 +1.36 kB
assets/RevokeInviteDialogContent-BcCBwlNr.js (removed) 4.37 kB 🟢 -4.37 kB 🟢 -1.7 kB 🟢 -1.48 kB
assets/RevokeInviteDialogContent-CfcMtJ6b.js (new) 4.37 kB 🔴 +4.37 kB 🔴 +1.7 kB 🔴 +1.49 kB
assets/InviteMemberUpsellDialogContent-C9acr7Is.js (new) 4.27 kB 🔴 +4.27 kB 🔴 +1.56 kB 🔴 +1.37 kB
assets/InviteMemberUpsellDialogContent-Cpm8zQNX.js (removed) 4.27 kB 🟢 -4.27 kB 🟢 -1.56 kB 🟢 -1.37 kB
assets/cloudSessionCookie-CeFEYVsw.js (new) 4.12 kB 🔴 +4.12 kB 🔴 +1.49 kB 🔴 +1.3 kB
assets/cloudSessionCookie-ClOdM8ln.js (removed) 4.12 kB 🟢 -4.12 kB 🟢 -1.49 kB 🟢 -1.29 kB
assets/saveMesh-BMle0ZV3.js (new) 3.92 kB 🔴 +3.92 kB 🔴 +1.69 kB 🔴 +1.48 kB
assets/saveMesh-DD4xsFAf.js (removed) 3.92 kB 🟢 -3.92 kB 🟢 -1.68 kB 🟢 -1.48 kB
assets/Media3DTop-BPQMQAWf.js (new) 3.85 kB 🔴 +3.85 kB 🔴 +1.62 kB 🔴 +1.43 kB
assets/Media3DTop-BvSNZJPZ.js (removed) 3.85 kB 🟢 -3.85 kB 🟢 -1.62 kB 🟢 -1.42 kB
assets/WidgetGalleria-C0qGNNbn.js (removed) 3.8 kB 🟢 -3.8 kB 🟢 -1.47 kB 🟢 -1.31 kB
assets/WidgetGalleria-DPOhjnvg.js (new) 3.8 kB 🔴 +3.8 kB 🔴 +1.47 kB 🔴 +1.31 kB
assets/WidgetToggleSwitch-CtmlnYUv.js (new) 3.73 kB 🔴 +3.73 kB 🔴 +1.43 kB 🔴 +1.27 kB
assets/WidgetToggleSwitch-Dl70doCT.js (removed) 3.73 kB 🟢 -3.73 kB 🟢 -1.43 kB 🟢 -1.26 kB
assets/WidgetMarkdown-BdL0iL2Z.js (removed) 3.13 kB 🟢 -3.13 kB 🟢 -1.3 kB 🟢 -1.15 kB
assets/WidgetMarkdown-BYj8vCZn.js (new) 3.13 kB 🔴 +3.13 kB 🔴 +1.3 kB 🔴 +1.15 kB
assets/WidgetInputText-Bg3XysXg.js (removed) 3.09 kB 🟢 -3.09 kB 🟢 -1.31 kB 🟢 -1.18 kB
assets/WidgetInputText-BNMQGF_l.js (new) 3.09 kB 🔴 +3.09 kB 🔴 +1.31 kB 🔴 +1.18 kB
assets/GlobalToast-cDtlSo-z.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/GlobalToast-QTNhldGg.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.26 kB 🟢 -1.07 kB
assets/MediaVideoTop-3MyNjEw-.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +1.19 kB 🔴 +1.05 kB
assets/MediaVideoTop-CLB2jPxj.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -1.19 kB 🟢 -1.05 kB
assets/ApiNodesSignInContent-4G0S0m9W.js (new) 2.86 kB 🔴 +2.86 kB 🔴 +1.1 kB 🔴 +968 B
assets/ApiNodesSignInContent-Ck9Q7PUg.js (removed) 2.86 kB 🟢 -2.86 kB 🟢 -1.11 kB 🟢 -971 B
assets/WidgetChart-5Eddq4EY.js (new) 2.41 kB 🔴 +2.41 kB 🔴 +1.02 kB 🔴 +884 B
assets/WidgetChart-BtBLhGhg.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -1.02 kB 🟢 -884 B
assets/WidgetLayoutField-C_2JfGPv.js (new) 2.37 kB 🔴 +2.37 kB 🔴 +1.03 kB 🔴 +912 B
assets/WidgetLayoutField-Da15-q1j.js (removed) 2.37 kB 🟢 -2.37 kB 🟢 -1.03 kB 🟢 -909 B
assets/SubscribeToRun-C7ahgWBk.js (removed) 2.13 kB 🟢 -2.13 kB 🟢 -981 B 🟢 -881 B
assets/SubscribeToRun-CplYUKCW.js (new) 2.13 kB 🔴 +2.13 kB 🔴 +983 B 🔴 +855 B
assets/SubscriptionBenefits-mPQhBasQ.js (new) 2.07 kB 🔴 +2.07 kB 🔴 +708 B 🔴 +610 B
assets/SubscriptionBenefits-ydWOhZnh.js (removed) 2.07 kB 🟢 -2.07 kB 🟢 -708 B 🟢 -606 B
assets/MediaImageTop-CbsN_GeV.js (new) 2.05 kB 🔴 +2.05 kB 🔴 +998 B 🔴 +881 B
assets/MediaImageTop-DPlUogv8.js (removed) 2.05 kB 🟢 -2.05 kB 🟢 -998 B 🟢 -879 B
assets/MediaAudioTop-BjXFOvom.js (removed) 2.02 kB 🟢 -2.02 kB 🟢 -980 B 🟢 -833 B
assets/MediaAudioTop-CBUvamjK.js (new) 2.02 kB 🔴 +2.02 kB 🔴 +981 B 🔴 +873 B
assets/CloudRunButtonWrapper-BGs8LfoS.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -911 B 🟢 -804 B
assets/CloudRunButtonWrapper-nBtYrOTQ.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +911 B 🔴 +807 B
assets/BaseViewTemplate-Dkw7XRub.js (new) 1.92 kB 🔴 +1.92 kB 🔴 +979 B 🔴 +873 B
assets/BaseViewTemplate-DU0C8qed.js (removed) 1.92 kB 🟢 -1.92 kB 🟢 -978 B 🟢 -870 B
assets/graphHasMissingNodes-B8r3a6QP.js (removed) 1.83 kB 🟢 -1.83 kB 🟢 -860 B 🟢 -754 B
assets/graphHasMissingNodes-Cboq1TlR.js (new) 1.83 kB 🔴 +1.83 kB 🔴 +861 B 🔴 +753 B
assets/cloudBadges-C_r1p-ti.js (removed) 1.77 kB 🟢 -1.77 kB 🟢 -887 B 🟢 -766 B
assets/cloudBadges-CTUc9e19.js (new) 1.77 kB 🔴 +1.77 kB 🔴 +890 B 🔴 +784 B
assets/cloudSubscription-B--tyX6y.js (new) 1.68 kB 🔴 +1.68 kB 🔴 +813 B 🔴 +702 B
assets/cloudSubscription-CHXBHR9b.js (removed) 1.68 kB 🟢 -1.68 kB 🟢 -814 B 🟢 -712 B
assets/signInSchema-D8n92gyc.js (removed) 1.6 kB 🟢 -1.6 kB 🟢 -584 B 🟢 -514 B
assets/signInSchema-DjmZUM7d.js (new) 1.6 kB 🔴 +1.6 kB 🔴 +585 B 🔴 +514 B
assets/surveyRegistry-BQeKU5bw.js (new) 1.57 kB 🔴 +1.57 kB 🔴 +742 B 🔴 +634 B
assets/surveyRegistry-CxaF1waJ.js (removed) 1.57 kB 🟢 -1.57 kB 🟢 -742 B 🟢 -631 B
assets/previousFullPath-DFdoBfJl.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -693 B 🟢 -601 B
assets/previousFullPath-Dx57-yqT.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +695 B 🔴 +600 B
assets/widgetPropFilter-BK-zKZeC.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +704 B 🔴 +605 B
assets/widgetPropFilter-nQfJwFQD.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -704 B 🟢 -602 B
assets/Textarea-BGwgU3bO.js (new) 1.42 kB 🔴 +1.42 kB 🔴 +744 B 🔴 +650 B
assets/Textarea-DdwIdlE4.js (removed) 1.42 kB 🟢 -1.42 kB 🟢 -743 B 🟢 -650 B
assets/Load3D-D4pLToIy.js (removed) 1.34 kB 🟢 -1.34 kB 🟢 -608 B 🟢 -544 B
assets/Load3D-ibfx7DyL.js (new) 1.34 kB 🔴 +1.34 kB 🔴 +615 B 🔴 +542 B
assets/nightlyBadges-B2AkFUuK.js (new) 1.29 kB 🔴 +1.29 kB 🔴 +656 B 🔴 +582 B
assets/nightlyBadges-D8oD1zgb.js (removed) 1.29 kB 🟢 -1.29 kB 🟢 -655 B 🟢 -581 B
assets/Load3dViewerContent-DGwC1nPA.js (removed) 1.23 kB 🟢 -1.23 kB 🟢 -561 B 🟢 -496 B
assets/Load3dViewerContent-DM5ZOs1x.js (new) 1.23 kB 🔴 +1.23 kB 🔴 +564 B 🔴 +499 B
assets/SubscriptionPanelContentWorkspace-BqNhhubk.js (removed) 1.15 kB 🟢 -1.15 kB 🟢 -536 B 🟢 -467 B
assets/SubscriptionPanelContentWorkspace-CVWGlJIK.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +534 B 🔴 +467 B
assets/MediaOtherTop-D0abnfzL.js (removed) 1.07 kB 🟢 -1.07 kB 🟢 -601 B 🟢 -492 B
assets/MediaOtherTop-Wg9CNctd.js (new) 1.07 kB 🔴 +1.07 kB 🔴 +602 B 🔴 +492 B
assets/MediaTextTop-CwEtRr1h.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -597 B 🟢 -498 B
assets/MediaTextTop-DEqx6nVQ.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +597 B 🔴 +490 B
assets/WidgetLegacy-C5fOwruw.js (removed) 978 B 🟢 -978 B 🟢 -481 B 🟢 -423 B
assets/WidgetLegacy-DtTKOfWS.js (new) 978 B 🔴 +978 B 🔴 +481 B 🔴 +425 B
assets/ComfyOrgHeader-CBdCPGHB.js (removed) 960 B 🟢 -960 B 🟢 -527 B 🟢 -452 B
assets/ComfyOrgHeader-DNahK-az.js (new) 960 B 🔴 +960 B 🔴 +528 B 🔴 +451 B
assets/changeTracker-B2qKAQDJ.js (new) 952 B 🔴 +952 B 🔴 +471 B 🔴 +414 B
assets/changeTracker-vJJ3CHKO.js (removed) 952 B 🟢 -952 B 🟢 -470 B 🟢 -414 B
assets/constants-Bpul9-kz.js (new) 766 B 🔴 +766 B 🔴 +374 B 🔴 +305 B
assets/constants-CWGvtpeq.js (removed) 766 B 🟢 -766 B 🟢 -374 B 🟢 -304 B
assets/cloud-subscription-CyKNYXDv.js (removed) 279 B 🟢 -279 B 🟢 -186 B 🟢 -147 B
assets/cloud-subscription-TN02zk2k.js (new) 279 B 🔴 +279 B 🔴 +184 B 🔴 +146 B
assets/comfy-logo-single-Busbi-nj.js (removed) 272 B 🟢 -272 B 🟢 -183 B 🟢 -147 B
assets/comfy-logo-single-CBRy2v7X.js (new) 272 B 🔴 +272 B 🔴 +186 B 🔴 +149 B
assets/missingModelDownload-DkAXZInC.js (new) 267 B 🔴 +267 B 🔴 +183 B 🔴 +174 B
assets/missingModelDownload-DnfEZUBE.js (removed) 267 B 🟢 -267 B 🟢 -184 B 🟢 -177 B
assets/i18n-BXcke2Hz.js (removed) 137 B 🟢 -137 B 🟢 -122 B 🟢 -108 B
assets/i18n-CmEgvthO.js (new) 137 B 🔴 +137 B 🔴 +122 B 🔴 +108 B

Status: 120 added / 119 removed / 15 unchanged

⚡ Performance

⏳ Performance tests in progress…

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/range/RangeEditor.test.ts`:
- Around line 29-31: The test is brittle because it asserts exact stringified
float values for SVG geometry (attributes('x') and attributes('width') in
RangeEditor.test.ts and the similar assertions at lines 122-123); update these
assertions to parse the attribute values to numbers and assert numerically
(e.g., using toBeCloseTo or an absolute-delta check) so small floating-point
formatting differences won’t break the test—locate the assertions that call
highlight.attributes('x') and highlight.attributes('width') (and the matching
assertions later in the file) and replace the strict string equality checks with
numeric comparisons within a small tolerance.

In `@src/components/range/RangeEditor.vue`:
- Around line 231-236: Clamp the normalized coordinates returned by normalize
before using them: update the computed values minNorm and maxNorm (which call
normalize(modelValue.value.min, valueMin, valueMax) and
normalize(modelValue.value.max, valueMin, valueMax)) to constrain their outputs
to [0,1] (e.g., via a clamp using Math.max(0, Math.min(1, ...)) or a shared
clamp util) and apply the same clamping to the other nearby computed normalized
values referenced later in the file (the additional computed normalized
positions mentioned around the same block) so handles and overlays cannot render
outside the track when modelValue is out of bounds.

In `@src/components/range/rangeUtils.ts`:
- Around line 50-55: The type guard isRangeValue currently allows NaN/Infinity
and doesn't validate an optional midpoint; update it to reject non-finite
numbers by using Number.isFinite for v.min and v.max, and if v.midpoint exists
ensure typeof v.midpoint === 'number' && Number.isFinite(v.midpoint); keep the
existing object/null/array checks and the cast to Record<string, unknown>, but
replace the typeof checks for min/max with Number.isFinite checks to tighten
validation and avoid leaking invalid numeric payloads into rendering/math paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9d4bd94f-673e-4af5-8a98-2ded1594ebb2

📥 Commits

Reviewing files that changed from the base of the PR and between 858946b and 6ee9eb7.

📒 Files selected for processing (19)
  • src/components/curve/CurveEditor.vue
  • src/components/curve/curveUtils.test.ts
  • src/components/curve/curveUtils.ts
  • src/components/range/RangeEditor.test.ts
  • src/components/range/RangeEditor.vue
  • src/components/range/WidgetRange.vue
  • src/components/range/rangeUtils.test.ts
  • src/components/range/rangeUtils.ts
  • src/composables/useRangeEditor.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/RangeWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useRangeWidget.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/schemas/nodeDefSchema.ts
  • src/scripts/widgets.ts
  • src/utils/histogramUtil.test.ts
  • src/utils/histogramUtil.ts
💤 Files with no reviewable changes (1)
  • src/components/curve/curveUtils.ts

Comment thread src/components/range/RangeEditor.test.ts
Comment thread src/components/range/RangeEditor.vue
Comment thread src/components/range/rangeUtils.ts
Copy link
Copy Markdown
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.

🧹 Nitpick comments (5)
src/composables/useRangeEditor.ts (2)

31-34: Consider using clamp for consistency.

Since clamp is already imported from es-toolkit and used elsewhere (line 55), consider using it here as well for uniformity.

♻️ Suggested refactor
-    const normalized = Math.max(
-      0,
-      Math.min(1, (e.clientX - rect.left) / rect.width)
-    )
+    const normalized = clamp((e.clientX - rect.left) / rect.width, 0, 1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useRangeEditor.ts` around lines 31 - 34, The computation of
normalized uses Math.max/Math.min instead of the existing clamp utility; replace
the Math.max/Math.min expression that sets the normalized variable with a call
to clamp((e.clientX - rect.left) / rect.width, 0, 1) to keep consistency with
other uses (see clamp import and usages around line 55) and ensure normalized
remains between 0 and 1.

63-66: Same consistency opportunity with clamp.

The manual clamping on line 65 can use the already-imported clamp function.

♻️ Suggested refactor
       const midNorm =
         range > 0 ? normalize(clamped, current.min, current.max) : 0
-      const midpoint = Math.max(0, Math.min(1, midNorm))
+      const midpoint = clamp(midNorm, 0, 1)
       modelValue.value = { ...current, midpoint }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useRangeEditor.ts` around lines 63 - 66, Replace the manual
clamping of midpoint with the imported clamp utility: compute midNorm as before,
then set midpoint = clamp(midNorm, 0, 1) instead of using Math.max/Math.min;
update the assignment to modelValue.value = { ...current, midpoint } in the
useRangeEditor logic so it uses clamp consistently (referencing midNorm,
midpoint, and the clamp function).
src/components/range/RangeEditor.test.ts (1)

46-50: Histogram dimming assertions use exact string comparisons.

Same pattern as above - these exact string assertions ('0.2', '0.8') work because the values serialize consistently, but numeric comparisons would be more resilient.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/range/RangeEditor.test.ts` around lines 46 - 50, The histogram
dimming assertions in RangeEditor.test.ts rely on exact string matches for
attributes which is fragile; change the assertions to parse the attribute values
from the elements found by wrapper.find('[data-testid="range-dim-left"]') and
wrapper.find('[data-testid="range-dim-right"]') into numbers (e.g., via
Number(...) or parseFloat(...)) and assert numerically (use toBeCloseTo(...) for
floats or toBe(...) for exact numbers) instead of comparing to the string
literals '0.2' and '0.8'.
src/components/range/WidgetRange.vue (2)

44-52: Histogram array conversion lacks type validation.

new Uint32Array(data) assumes all elements in data are valid numeric values. If the backend sends unexpected data (strings, floats, etc.), the Uint32Array constructor will coerce them, potentially producing incorrect histogram bins.

Safer histogram conversion
   const key = `histogram_${widget.name}`
   const data = (output as Record<string, unknown>)?.[key]
   if (!Array.isArray(data) || data.length === 0) return null
-  return new Uint32Array(data)
+  if (!data.every((v) => typeof v === 'number' && Number.isFinite(v)))
+    return null
+  return new Uint32Array(data as number[])
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/range/WidgetRange.vue` around lines 44 - 52, The computed
histogram currently does new Uint32Array(data) which blindly coerces values;
update the histogram computed (and the local key/locatorId handling) to validate
and sanitize the extracted data first: check that data is an array, iterate over
elements and keep only finite numeric values, convert to integers (e.g.,
Math.floor) and clamp to the uint32 range (0..0xFFFFFFFF), or bail out (return
null) if elements are invalid; then construct the Uint32Array from this
sanitized numeric array so widget, nodeOutputStore.nodeOutputs and the key
`histogram_${widget.name}` produce a safe Uint32Array instead of relying on
implicit coercion.

65-69: Consider simplifying effectiveValue computed.

The { value: ... } wrapper object and immediate unwrap in the template (:model-value="effectiveValue.value") adds indirection without clear benefit.

Simplified approach
-const effectiveValue = computed(() =>
-  isDisabled.value && upstreamValue.value
-    ? { value: upstreamValue.value }
-    : { value: modelValue.value }
-)
+const effectiveValue = computed(() =>
+  isDisabled.value && upstreamValue.value
+    ? upstreamValue.value
+    : modelValue.value
+)

Then in template:

-    :model-value="effectiveValue.value"
+    :model-value="effectiveValue"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/range/WidgetRange.vue` around lines 65 - 69, The computed
effectiveValue currently returns an object wrapper ({ value: ... }) which forces
callers to unwrap with .value; simplify by making effectiveValue a computed that
returns the raw value (use upstreamValue.value when isDisabled.value &&
upstreamValue.value, otherwise modelValue.value) and update the template binding
from :model-value="effectiveValue.value" to :model-value="effectiveValue" (or
use effectiveValue directly where used). Update any other usages expecting the
object wrapper to use the plain value instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/range/RangeEditor.test.ts`:
- Around line 46-50: The histogram dimming assertions in RangeEditor.test.ts
rely on exact string matches for attributes which is fragile; change the
assertions to parse the attribute values from the elements found by
wrapper.find('[data-testid="range-dim-left"]') and
wrapper.find('[data-testid="range-dim-right"]') into numbers (e.g., via
Number(...) or parseFloat(...)) and assert numerically (use toBeCloseTo(...) for
floats or toBe(...) for exact numbers) instead of comparing to the string
literals '0.2' and '0.8'.

In `@src/components/range/WidgetRange.vue`:
- Around line 44-52: The computed histogram currently does new Uint32Array(data)
which blindly coerces values; update the histogram computed (and the local
key/locatorId handling) to validate and sanitize the extracted data first: check
that data is an array, iterate over elements and keep only finite numeric
values, convert to integers (e.g., Math.floor) and clamp to the uint32 range
(0..0xFFFFFFFF), or bail out (return null) if elements are invalid; then
construct the Uint32Array from this sanitized numeric array so widget,
nodeOutputStore.nodeOutputs and the key `histogram_${widget.name}` produce a
safe Uint32Array instead of relying on implicit coercion.
- Around line 65-69: The computed effectiveValue currently returns an object
wrapper ({ value: ... }) which forces callers to unwrap with .value; simplify by
making effectiveValue a computed that returns the raw value (use
upstreamValue.value when isDisabled.value && upstreamValue.value, otherwise
modelValue.value) and update the template binding from
:model-value="effectiveValue.value" to :model-value="effectiveValue" (or use
effectiveValue directly where used). Update any other usages expecting the
object wrapper to use the plain value instead.

In `@src/composables/useRangeEditor.ts`:
- Around line 31-34: The computation of normalized uses Math.max/Math.min
instead of the existing clamp utility; replace the Math.max/Math.min expression
that sets the normalized variable with a call to clamp((e.clientX - rect.left) /
rect.width, 0, 1) to keep consistency with other uses (see clamp import and
usages around line 55) and ensure normalized remains between 0 and 1.
- Around line 63-66: Replace the manual clamping of midpoint with the imported
clamp utility: compute midNorm as before, then set midpoint = clamp(midNorm, 0,
1) instead of using Math.max/Math.min; update the assignment to modelValue.value
= { ...current, midpoint } in the useRangeEditor logic so it uses clamp
consistently (referencing midNorm, midpoint, and the clamp function).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 835ecb2b-411e-41db-9f50-db18ec4cab3d

📥 Commits

Reviewing files that changed from the base of the PR and between 6ee9eb7 and b95636f.

📒 Files selected for processing (19)
  • src/components/curve/CurveEditor.vue
  • src/components/curve/curveUtils.test.ts
  • src/components/curve/curveUtils.ts
  • src/components/range/RangeEditor.test.ts
  • src/components/range/RangeEditor.vue
  • src/components/range/WidgetRange.vue
  • src/components/range/rangeUtils.test.ts
  • src/components/range/rangeUtils.ts
  • src/composables/useRangeEditor.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/RangeWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useRangeWidget.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/schemas/nodeDefSchema.ts
  • src/scripts/widgets.ts
  • src/utils/histogramUtil.test.ts
  • src/utils/histogramUtil.ts
💤 Files with no reviewable changes (1)
  • src/components/curve/curveUtils.ts
✅ Files skipped from review due to trivial changes (4)
  • src/components/curve/CurveEditor.vue
  • src/lib/litegraph/src/widgets/RangeWidget.ts
  • src/utils/histogramUtil.ts
  • src/components/range/rangeUtils.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/components/curve/curveUtils.test.ts
  • src/scripts/widgets.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/utils/histogramUtil.test.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useRangeWidget.ts
  • src/schemas/nodeDefSchema.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/components/range/RangeEditor.vue

coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 7, 2026
const bestDist = Math.min(dMin, dMax)
if (midpoint !== undefined) {
const midAbs = min + midpoint * (max - min)
if (Math.abs(value - midAbs) < bestDist) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: nearestHandle snaps to invisible midpoint when showMidpoint is false.

When a user clicks the track, nearestHandle checks if (midpoint !== undefined) but does not check whether the midpoint handle is actually visible. If showMidpoint=false but the model carries a midpoint value, clicking near the center silently modifies the hidden midpoint instead of min/max -- the visible handles appear frozen.

Suggested fix: pass showMidpoint as a Ref<boolean> into useRangeEditor and gate the midpoint branch:
if (midpoint !== undefined && showMidpoint.value)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/components/range/rangeUtils.ts Outdated
return position.toFixed(2)
}

export function constrainRange(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (non-blocking): constrainRange and formatMidpointLabel are defined but never called.

constrainRange exists to clamp values and enforce min<=max, but nothing invokes it. Deserialized workflows with min > max could cause inverted behavior. formatMidpointLabel is also unused -- the component inlines .toFixed(2) instead.

Consider either wiring constrainRange into the initialization path or removing both functions to avoid dead code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/composables/useRangeEditor.ts Outdated

import { clamp } from 'es-toolkit'

import { denormalize, normalize } from '@/components/range/rangeUtils'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (non-blocking): inverted dependency direction -- composable imports from component directory.

useRangeEditor (a shared composable under src/composables/) imports normalize/denormalize from @/components/range/rangeUtils. This inverts the expected dependency direction; lower-level composables should not depend on higher-level component internals. The CurveEditor composable avoids this pattern.

Would it make sense to move normalize/denormalize to src/utils/mathUtil.ts or similar?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

}
})

const effectiveValue = computed(() =>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (non-blocking): effectiveValue wraps in { value: ... } causing double .value.value indirection.

The computed returns { value: upstreamValue.value } / { value: modelValue.value }, so the template binds :model-value="effectiveValue.value" -- a double .value access. WidgetCurve returns the value directly from its computed without the wrapper object.

Returning RangeValue directly and binding :model-value="effectiveValue" would simplify this and be consistent with the Curve pattern.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/components/range/rangeUtils.ts Outdated

import type { RangeValue } from '@/lib/litegraph/src/types/widgets'

export { clamp }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick (non-blocking): clamp is re-exported from rangeUtils but useRangeEditor imports it directly from es-toolkit.

This creates two import paths for the same function. Removing the re-export and importing clamp directly from es-toolkit everywhere would be more consistent.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/lib/litegraph/src/types/widgets.ts Outdated
midpoint_scale?: 'linear' | 'gamma'
value_min?: number
value_max?: number
histogram?: Uint32Array | null
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick (non-blocking): histogram field on IWidgetRangeOptions appears unused.

The component fetches histogram data from nodeOutputStore at runtime, not from widget options. If this field is not used as static config, consider removing it to keep the options interface clean.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/composables/useRangeEditor.ts Outdated
const el = trackRef.value
if (!el) return valueMin.value
const rect = el.getBoundingClientRect()
const normalized = Math.max(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick (non-blocking): Math.max(0, Math.min(1, ...)) could be clamp(value, 0, 1).

clamp is already imported from es-toolkit on line 4.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

}

export function positionToGamma(position: number): number {
const clamped = clamp(position, 0.001, 0.999)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick (non-blocking): magic numbers 0.001 and 0.999 in positionToGamma are unexplained.

A short comment noting these prevent log2(0) / log2(1) edge cases would help readability.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Apr 9, 2026
@jtydhr88 jtydhr88 assigned christian-byrne and unassigned jtydhr88 Apr 9, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/utils/mathUtil.test.ts (1)

25-35: Add a degenerate-range test for denormalize as a guardrail.

A dedicated min === max test would lock expected behavior for future refactors.

Suggested test addition
   describe('denormalize', () => {
     it('converts normalized value back to range', () => {
       expect(denormalize(0.5, 0, 256)).toBe(128)
       expect(denormalize(0, 0, 255)).toBe(0)
       expect(denormalize(1, 0, 255)).toBe(255)
     })

+    it('returns min when min equals max', () => {
+      expect(denormalize(0, 5, 5)).toBe(5)
+      expect(denormalize(0.5, 5, 5)).toBe(5)
+      expect(denormalize(1, 5, 5)).toBe(5)
+    })
+
     it('round-trips with normalize', () => {
       expect(denormalize(normalize(100, 0, 255), 0, 255)).toBeCloseTo(100)
     })
   })
Based on learnings, "In tests for manually-coded math/geometry modules (e.g., interpolators, path generators, LUT generators like curveUtils), ensure extensive unit test coverage that exercises permutations, edge cases, and deterministic behavior."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/mathUtil.test.ts` around lines 25 - 35, Add a degenerate-range unit
test for denormalize to assert behavior when min === max: in
src/utils/mathUtil.test.ts add a case under the 'denormalize' suite that calls
denormalize with identical min and max (e.g., denormalize(0.0, 5, 5),
denormalize(1.0, 5, 5), and denormalize(0.5, 5, 5)) and expects the result to
equal that constant bound (5) for all inputs; reference the denormalize function
and the existing 'denormalize' describe block to place the test so future
refactors keep this guardrail.
src/components/range/WidgetRange.vue (1)

44-52: Consider validating histogram array element types.

The histogram data is cast from unknown and converted to Uint32Array. While Array.isArray(data) guards against non-arrays, it doesn't guarantee the elements are numeric. If the backend sends unexpected data, new Uint32Array(data) will silently coerce non-numbers to 0.

This is low risk since the backend contract should send numeric histogram bins, but for defensive coding you could add an element type check:

♻️ Optional: Add element validation
 const histogram = computed(() => {
   const locatorId = widget.nodeLocatorId
   if (!locatorId) return null
   const output = nodeOutputStore.nodeOutputs[locatorId]
   const key = `histogram_${widget.name}`
   const data = (output as Record<string, unknown>)?.[key]
-  if (!Array.isArray(data) || data.length === 0) return null
+  if (
+    !Array.isArray(data) ||
+    data.length === 0 ||
+    !data.every((v) => typeof v === 'number')
+  )
+    return null
   return new Uint32Array(data)
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/range/WidgetRange.vue` around lines 44 - 52, The computed
histogram currently trusts that data from
nodeOutputStore.nodeOutputs[locatorId][`histogram_${widget.name}`] is numeric
and constructs a new Uint32Array directly, which can silently coerce non-numeric
elements to 0; update the histogram computed to validate that data is an array
of numbers (e.g., check every element typeof === 'number' and isFinite) before
creating the Uint32Array, and return null (or filter/transform to numbers) if
validation fails so that new Uint32Array(data) only receives safe numeric input;
reference the histogram computed getter, locatorId, nodeOutputStore.nodeOutputs,
the key `histogram_${widget.name}`, and the Uint32Array construction when making
the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/composables/useRangeEditor.ts`:
- Around line 29-35: The pointerToValue function can produce NaN when the track
element has zero width; update pointerToValue to guard against rect.width === 0
(or <= 0) by returning a safe fallback (e.g., valueMin.value or the nearest
valid bound) before computing normalized and calling denormalize; refer to
pointerToValue, trackRef, valueMin/valueMax, clamp and denormalize when making
this change so the drag logic never divides by rect.width.
- Around line 74-81: The function startDrag currently sets activeHandle.value
before verifying trackRef.value exists, which can leave activeHandle stale if
trackRef is null; move the assignment to activeHandle.value so it occurs after
the null-check for trackRef (i.e., after const el = trackRef.value and the if
(!el) return), and keep cleanupDrag() call and other logic intact so
activeHandle is only set when a valid track element exists.

---

Nitpick comments:
In `@src/components/range/WidgetRange.vue`:
- Around line 44-52: The computed histogram currently trusts that data from
nodeOutputStore.nodeOutputs[locatorId][`histogram_${widget.name}`] is numeric
and constructs a new Uint32Array directly, which can silently coerce non-numeric
elements to 0; update the histogram computed to validate that data is an array
of numbers (e.g., check every element typeof === 'number' and isFinite) before
creating the Uint32Array, and return null (or filter/transform to numbers) if
validation fails so that new Uint32Array(data) only receives safe numeric input;
reference the histogram computed getter, locatorId, nodeOutputStore.nodeOutputs,
the key `histogram_${widget.name}`, and the Uint32Array construction when making
the change.

In `@src/utils/mathUtil.test.ts`:
- Around line 25-35: Add a degenerate-range unit test for denormalize to assert
behavior when min === max: in src/utils/mathUtil.test.ts add a case under the
'denormalize' suite that calls denormalize with identical min and max (e.g.,
denormalize(0.0, 5, 5), denormalize(1.0, 5, 5), and denormalize(0.5, 5, 5)) and
expects the result to equal that constant bound (5) for all inputs; reference
the denormalize function and the existing 'denormalize' describe block to place
the test so future refactors keep this guardrail.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ca0c3621-1fa8-453f-8aa3-6698192ab283

📥 Commits

Reviewing files that changed from the base of the PR and between b95636f and 2019704.

📒 Files selected for processing (8)
  • src/components/range/RangeEditor.vue
  • src/components/range/WidgetRange.vue
  • src/components/range/rangeUtils.test.ts
  • src/components/range/rangeUtils.ts
  • src/composables/useRangeEditor.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/utils/mathUtil.test.ts
  • src/utils/mathUtil.ts
✅ Files skipped from review due to trivial changes (1)
  • src/components/range/rangeUtils.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/range/RangeEditor.vue
  • src/lib/litegraph/src/types/widgets.ts

Comment on lines +29 to +35
function pointerToValue(e: PointerEvent): number {
const el = trackRef.value
if (!el) return valueMin.value
const rect = el.getBoundingClientRect()
const normalized = clamp((e.clientX - rect.left) / rect.width, 0, 1)
return denormalize(normalized, valueMin.value, valueMax.value)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard against zero-width track to prevent NaN model updates.

When rect.width is 0, normalization can become NaN/Infinity and propagate into modelValue during drag updates.

💡 Proposed fix
 function pointerToValue(e: PointerEvent): number {
   const el = trackRef.value
   if (!el) return valueMin.value
   const rect = el.getBoundingClientRect()
+  if (rect.width <= 0) return valueMin.value
   const normalized = clamp((e.clientX - rect.left) / rect.width, 0, 1)
   return denormalize(normalized, valueMin.value, valueMax.value)
 }
📝 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
function pointerToValue(e: PointerEvent): number {
const el = trackRef.value
if (!el) return valueMin.value
const rect = el.getBoundingClientRect()
const normalized = clamp((e.clientX - rect.left) / rect.width, 0, 1)
return denormalize(normalized, valueMin.value, valueMax.value)
}
function pointerToValue(e: PointerEvent): number {
const el = trackRef.value
if (!el) return valueMin.value
const rect = el.getBoundingClientRect()
if (rect.width <= 0) return valueMin.value
const normalized = clamp((e.clientX - rect.left) / rect.width, 0, 1)
return denormalize(normalized, valueMin.value, valueMax.value)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useRangeEditor.ts` around lines 29 - 35, The pointerToValue
function can produce NaN when the track element has zero width; update
pointerToValue to guard against rect.width === 0 (or <= 0) by returning a safe
fallback (e.g., valueMin.value or the nearest valid bound) before computing
normalized and calling denormalize; refer to pointerToValue, trackRef,
valueMin/valueMax, clamp and denormalize when making this change so the drag
logic never divides by rect.width.

Comment on lines +74 to +81
function startDrag(handle: HandleType, e: PointerEvent) {
if (e.button !== 0) return
cleanupDrag?.()

activeHandle.value = handle
const el = trackRef.value
if (!el) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Move activeHandle assignment after trackRef null-check.

If trackRef.value is null, the function returns early but leaves activeHandle set, causing stale drag state.

💡 Proposed fix
 function startDrag(handle: HandleType, e: PointerEvent) {
   if (e.button !== 0) return
   cleanupDrag?.()

-  activeHandle.value = handle
   const el = trackRef.value
   if (!el) return
+  activeHandle.value = handle

   el.setPointerCapture(e.pointerId)
📝 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
function startDrag(handle: HandleType, e: PointerEvent) {
if (e.button !== 0) return
cleanupDrag?.()
activeHandle.value = handle
const el = trackRef.value
if (!el) return
function startDrag(handle: HandleType, e: PointerEvent) {
if (e.button !== 0) return
cleanupDrag?.()
const el = trackRef.value
if (!el) return
activeHandle.value = handle
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useRangeEditor.ts` around lines 74 - 81, The function
startDrag currently sets activeHandle.value before verifying trackRef.value
exists, which can leave activeHandle stale if trackRef is null; move the
assignment to activeHandle.value so it occurs after the null-check for trackRef
(i.e., after const el = trackRef.value and the if (!el) return), and keep
cleanupDrag() call and other logic intact so activeHandle is only set when a
valid track element exists.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 49.48980% with 99 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/composables/useRangeEditor.ts 8.47% 54 Missing ⚠️
src/components/range/WidgetRange.vue 0.00% 23 Missing ⚠️
src/components/range/RangeEditor.vue 80.30% 13 Missing ⚠️
...ons/vueNodes/widgets/composables/useRangeWidget.ts 20.00% 8 Missing ⚠️
...nsions/vueNodes/widgets/registry/widgetRegistry.ts 50.00% 1 Missing ⚠️
@@           Coverage Diff           @@
##             main   #10936   +/-   ##
=======================================
  Coverage        ?   43.24%           
=======================================
  Files           ?     1342           
  Lines           ?    68396           
  Branches        ?    18031           
=======================================
  Hits            ?    29576           
  Misses          ?    38230           
  Partials        ?      590           
Files with missing lines Coverage Δ
src/components/curve/CurveEditor.vue 91.30% <ø> (ø)
src/components/curve/curveUtils.ts 76.76% <ø> (ø)
src/components/range/rangeUtils.ts 100.00% <100.00%> (ø)
src/schemas/nodeDef/nodeDefSchemaV2.ts 85.00% <100.00%> (ø)
src/schemas/nodeDefSchema.ts 87.30% <100.00%> (ø)
src/scripts/widgets.ts 6.97% <ø> (ø)
src/utils/histogramUtil.ts 100.00% <100.00%> (ø)
src/utils/mathUtil.ts 93.75% <100.00%> (ø)
...nsions/vueNodes/widgets/registry/widgetRegistry.ts 67.21% <50.00%> (ø)
...ons/vueNodes/widgets/composables/useRangeWidget.ts 20.00% <20.00%> (ø)
... and 3 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/utils/mathUtil.ts Outdated
Comment thread src/composables/useRangeEditor.ts Outdated
const range = current.max - current.min
const midNorm =
range > 0 ? normalize(clamped, current.min, current.max) : 0
const midpoint = Math.max(0, Math.min(1, midNorm))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Considering you've imported clamp and are using it above, should you standardize to use it here too

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/composables/useRangeEditor.ts Outdated
Comment thread src/components/range/WidgetRange.vue
@jtydhr88 jtydhr88 assigned pythongosssss and unassigned jtydhr88 Apr 9, 2026
Copy link
Copy Markdown
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.

♻️ Duplicate comments (2)
src/composables/useRangeEditor.ts (2)

78-81: ⚠️ Potential issue | 🟡 Minor

Set activeHandle only after confirming trackRef exists.

Line 78 assigns activeHandle before the null check on Line 80. If trackRef.value is null, drag state can remain stale.

💡 Proposed fix
 function startDrag(handle: HandleType, e: PointerEvent) {
   if (e.button !== 0) return
   cleanupDrag?.()

-  activeHandle.value = handle
   const el = trackRef.value
   if (!el) return
+  activeHandle.value = handle

   el.setPointerCapture(e.pointerId)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useRangeEditor.ts` around lines 78 - 81, The code sets
activeHandle.value before verifying the existence of trackRef.value which can
leave drag state stale if trackRef is null; move the assignment of
activeHandle.value = handle to occur after the null-check for trackRef (i.e.,
check const el = trackRef.value and return if falsy, then set
activeHandle.value), updating the logic in the useRangeEditor handler where
activeHandle and trackRef are referenced so the handle is only activated when
the track element exists.

32-34: ⚠️ Potential issue | 🟠 Major

Guard zero-width track before normalization.

On Line 33, dividing by rect.width without a guard can produce invalid normalized values when width is 0. This can propagate unstable updates to modelValue.

💡 Proposed fix
 function pointerToValue(e: PointerEvent): number {
   const el = trackRef.value
   if (!el) return valueMin.value
   const rect = el.getBoundingClientRect()
+  if (rect.width <= 0) return valueMin.value
   const normalized = clamp((e.clientX - rect.left) / rect.width, 0, 1)
   return denormalize(normalized, valueMin.value, valueMax.value)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useRangeEditor.ts` around lines 32 - 34, Guard against a
zero-width track before normalizing: after calling el.getBoundingClientRect()
check if rect.width === 0 and, if so, return denormalize(0, valueMin.value,
valueMax.value) (or another safe default) instead of dividing by rect.width;
otherwise compute normalized as before with clamp((e.clientX - rect.left) /
rect.width, 0, 1) and return denormalize(normalized, valueMin.value,
valueMax.value). Ensure you touch the code around el.getBoundingClientRect(),
the normalization expression using e.clientX and rect.width, and the denormalize
call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/composables/useRangeEditor.ts`:
- Around line 78-81: The code sets activeHandle.value before verifying the
existence of trackRef.value which can leave drag state stale if trackRef is
null; move the assignment of activeHandle.value = handle to occur after the
null-check for trackRef (i.e., check const el = trackRef.value and return if
falsy, then set activeHandle.value), updating the logic in the useRangeEditor
handler where activeHandle and trackRef are referenced so the handle is only
activated when the track element exists.
- Around line 32-34: Guard against a zero-width track before normalizing: after
calling el.getBoundingClientRect() check if rect.width === 0 and, if so, return
denormalize(0, valueMin.value, valueMax.value) (or another safe default) instead
of dividing by rect.width; otherwise compute normalized as before with
clamp((e.clientX - rect.left) / rect.width, 0, 1) and return
denormalize(normalized, valueMin.value, valueMax.value). Ensure you touch the
code around el.getBoundingClientRect(), the normalization expression using
e.clientX and rect.width, and the denormalize call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e55dca72-3340-4471-bd88-59e8e7dcdbd3

📥 Commits

Reviewing files that changed from the base of the PR and between 2019704 and cc406e7.

📒 Files selected for processing (2)
  • src/composables/useRangeEditor.ts
  • src/utils/mathUtil.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/mathUtil.ts

Copy link
Copy Markdown
Member

@pythongosssss pythongosssss left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Copy Markdown
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

LGTM

@christian-byrne christian-byrne merged commit 63eab15 into main Apr 10, 2026
35 checks passed
@christian-byrne christian-byrne deleted the range-editor2 branch April 10, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants