Skip to content

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Nov 26, 2025

Summary

Changes the layout store to treat node sizes as body-only measurements while LiteGraph continues to reason about full heights. DOM-driven updates are tagged with LayoutSource.DOM, which lets the store strip the title height exactly once before persisting. That classification (a new mutation source - LayoutSource.DOM) is accurate because those mutations are triggered by the browser’s layout engine via ResizeObserver, rather than by direct calls into the layout APIs (e.g., moveNodeTo, useNodeDrag). So all sources are:

  • LayoutSource.DOM: browser layout/ResizeObserver measurements that include the title bar
  • LayoutSource.Vue: direct Vue-driven mutations routed through the layout store
  • LayoutSource.Canvas: legacy LiteGraph/canvas updates that will be phased out over time
  • LayoutSource.External: for multiplayer or syncing with a when going online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just in time for liteNode.setSize, so LiteGraph’s rendering stays unchanged. This makes Vue node resizing and workflow persistence deterministic - multiline widgets hold their dimensions across reloads because every path that crosses the layout/LiteGraph boundary performs the same normalization.

┆Issue is synchronized with this Notion page by Unito

@christian-byrne christian-byrne added the needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch label Nov 26, 2025
@christian-byrne christian-byrne requested a review from a team as a code owner November 26, 2025 21:28
@christian-byrne christian-byrne added area:vue-migration 1.32 cloud/1.32 core/1.33 Backport PRs for core 1.33 cloud/1.33 Backport PRs for cloud 1.33 labels Nov 26, 2025
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 26, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Note

Other AI code review bot(s) detected

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

📝 Walkthrough

Walkthrough

Adds utilities to account for LiteGraph node title height, introduces a new LayoutSource.DOM, and applies height normalization and title-height adjustments across initialization, paste, DOM-driven resize tracking, sync, and batch update paths; corresponding tests and mocks were updated.

Changes

Cohort / File(s) Summary
Node title height utils
src/renderer/core/layout/utils/nodeSizeUtil.ts
New helpers removeNodeTitleHeight() and addNodeTitleHeight() that adjust heights by LiteGraph.NODE_TITLE_HEIGHT.
Layout types & store
src/renderer/core/layout/types.ts, src/renderer/core/layout/store/layoutStore.ts
Added LayoutSource.DOM = 'dom'. layoutStore now conditionally normalizes heights for DOM-sourced batch updates by applying removeNodeTitleHeight() to bounds before storing; sets layout source when applying pasted bounds.
Sync logic
src/renderer/core/layout/sync/useLayoutSync.ts
Sync compares and writes LiteGraph node heights using addNodeTitleHeight(layout.size.height) so lite nodes receive heights including title offset.
Vue / node lifecycle & paste handling
src/composables/graph/useVueNodeLifecycle.ts, src/lib/litegraph/src/LGraphCanvas.ts, src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
Use removeNodeTitleHeight() when mapping node sizes for initialization and paste (respecting Vue nodes mode); DOM-driven resize tracking now computes DOM-sized bounds without title height and attributes updates as LayoutSource.DOM. Paste path guards setting layout source to Canvas only when new positions exist.
Tests & mocks
tests-ui/tests/renderer/core/layout/layoutStore.test.ts, tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
Added tests covering DOM-sourced height normalization and edge cases; updated imports and LiteGraph test import. Adjusted mock factory for useLayoutMutations in z-index test.

Sequence Diagram

sequenceDiagram
    participant DOM as DOM (browser)
    participant Resize as useVueNodeResizeTracking
    participant Lifecycle as useVueNodeLifecycle / Paste
    participant Store as layoutStore
    participant Sync as useLayoutSync
    participant Lite as LiteGraph Node

    DOM->>Resize: element resized (DOM bounds)
    Resize->>Resize: domHeight = bounds.height\nlayoutHeight = removeNodeTitleHeight(domHeight)
    Resize->>Store: batchUpdateNodeBounds(source: DOM, bounds with layoutHeight)
    Lifecycle->>Lifecycle: init/paste compute layoutHeight = removeNodeTitleHeight(node.size[1])
    Lifecycle->>Store: initializeFromLiteGraph / batchUpdateNodeBounds (source: Canvas when paste present)
    Store->>Store: shouldNormalize = (originalSource === DOM)
    alt shouldNormalize
        Store->>Store: store normalized bounds (height = removeNodeTitleHeight(height))
    else
        Store->>Store: store provided bounds
    end
    Store->>Sync: notify layout change
    Sync->>Sync: targetHeight = addNodeTitleHeight(layout.size.height)
    Sync->>Lite: write node.size = [width, targetHeight]
Loading

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch vue-node/fix-title-height-double-truncate

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

@github-actions
Copy link

github-actions bot commented Nov 26, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 11/27/2025, 12:32:48 AM UTC

📈 Summary

  • Total Tests: 497
  • Passed: 487 ✅
  • Failed: 0
  • Flaky: 1 ⚠️
  • Skipped: 9 ⏭️

📊 Test Reports by Browser

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

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

@github-actions
Copy link

github-actions bot commented Nov 26, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 11/27/2025, 12:24:10 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Nov 26, 2025

Bundle Size Report

Summary

  • Raw size: 17 MB baseline 17 MB — 🔴 +999 B
  • Gzip: 3.37 MB baseline 3.37 MB — 🔴 +358 B
  • Brotli: 2.58 MB baseline 2.58 MB — 🔴 +160 B
  • Bundles: 93 current • 93 baseline • 36 added / 36 removed

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

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-CP9LG9XD.js (new) 2.95 MB 🔴 +2.95 MB 🔴 +615 kB 🔴 +467 kB
assets/index-BaaiPcZN.js (removed) 2.95 MB 🟢 -2.95 MB 🟢 -615 kB 🟢 -467 kB
assets/index-CPPpRl_-.js (new) 227 kB 🔴 +227 kB 🔴 +48.6 kB 🔴 +40 kB
assets/index-CTa3wOBo.js (removed) 227 kB 🟢 -227 kB 🟢 -48.6 kB 🟢 -40 kB
assets/index-Dk_ERHfX.js (removed) 345 B 🟢 -345 B 🟢 -243 B 🟢 -208 B
assets/index-OfS87Dho.js (new) 345 B 🔴 +345 B 🔴 +246 B 🔴 +198 B

Status: 3 added / 3 removed

Graph Workspace — 946 kB (baseline 946 kB) • 🔴 +105 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-4luDHPpT.js (new) 946 kB 🔴 +946 kB 🔴 +183 kB 🔴 +140 kB
assets/GraphView-Bkp-XFJk.js (removed) 946 kB 🟢 -946 kB 🟢 -183 kB 🟢 -140 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BvWeibEy.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.9 kB
assets/UserSelectView-DHL7eme9.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.89 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CreditsPanel-AQIplI8P.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.15 kB 🔴 +4.5 kB
assets/CreditsPanel-DqTSyTFv.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.15 kB 🟢 -4.5 kB
assets/KeybindingPanel--iJlO0jP.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.01 kB
assets/KeybindingPanel-CGCoJolD.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3.01 kB
assets/ExtensionPanel-CJbQbTEE.js (new) 10.4 kB 🔴 +10.4 kB 🔴 +2.48 kB 🔴 +2.16 kB
assets/ExtensionPanel-DDmdS8ta.js (removed) 10.4 kB 🟢 -10.4 kB 🟢 -2.48 kB 🟢 -2.16 kB
assets/AboutPanel-BkYOfZlx.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.21 kB
assets/AboutPanel-DurkiqtF.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/ServerConfigPanel-BiA7GgF2.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +1.83 kB 🔴 +1.62 kB
assets/ServerConfigPanel-BtZBiC1L.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -1.83 kB 🟢 -1.62 kB
assets/UserPanel-CTT6hRAX.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.72 kB 🔴 +1.5 kB
assets/UserPanel-Ds_0RUCv.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.72 kB 🟢 -1.5 kB
assets/settings-BXTtSH4O.js 33.3 kB 33.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C9Pzn-NG.js 25.2 kB 25.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CCy2fA_h.js 27.3 kB 27.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CQpqEFfl.js 26.6 kB 26.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DHcnxypw.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DhFTK9fY.js 25.1 kB 25.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DlT4t_ui.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DRgSrIdD.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-tjkeqiZq.js 21.1 kB 21.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-DFMxMeDl.js (new) 53.9 kB 🔴 +53.9 kB 🔴 +8.52 kB 🔴 +7.32 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-Ds1rRI5t.js (removed) 53.9 kB 🟢 -53.9 kB 🟢 -8.52 kB 🟢 -7.32 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-Do6HAsls.js (new) 46.9 kB 🔴 +46.9 kB 🔴 +10.1 kB 🔴 +8.77 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-NqWI8a_U.js (removed) 46.9 kB 🟢 -46.9 kB 🟢 -10.1 kB 🟢 -8.77 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-CGZWUMY3.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.36 kB 🔴 +2.96 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-D-c1pnOW.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.36 kB 🟢 -2.96 kB
assets/ComfyQueueButton-_kxYDC23.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.48 kB 🟢 -2.21 kB
assets/ComfyQueueButton-Cy5jQ1oY.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.48 kB 🔴 +2.21 kB
assets/MediaTitle.vue_vue_type_script_setup_true_lang-DE206eke.js (removed) 897 B 🟢 -897 B 🟢 -503 B 🟢 -445 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-OzNLIZrz.js (new) 897 B 🔴 +897 B 🔴 +504 B 🔴 +462 B
assets/LazyImage.vue_vue_type_script_setup_true_lang-DfYZUWfZ.js 10.8 kB 10.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-lBvEY_Sb.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-D0RxLHdl.js 2.04 kB 2.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-BdcB0N2p.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

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

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-Ccy4bV0C.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.59 kB
assets/keybindingService-xLH_qWn-.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/audioService-Bssl0U7y.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -960 B 🟢 -823 B
assets/audioService-DLfFwlSi.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +961 B 🔴 +825 B
assets/serverConfigStore-BHFq9bew.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-BT027mhO.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -649 B 🟢 -547 B
assets/audioUtils-C885PvTP.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +652 B 🔴 +550 B
assets/mathUtil-CTARWQ-l.js 1.07 kB 1.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart--ViqMbDH.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-D3dKDPA9.js 3.98 MB 3.98 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-oJ2MsX9s.js 1.96 MB 1.96 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-aR6ntw5X.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-DkSGWC41.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-Dcaw_-2x.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BZLod3g9.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.84 MB (baseline 3.84 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-D-72WzMK.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.24 kB 🔴 +4.63 kB
assets/WidgetRecordAudio-DxNnoFS_.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.62 kB
assets/AudioPreviewPlayer-DZEYnLva.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.4 kB 🟢 -3.04 kB
assets/AudioPreviewPlayer-jlbzvXBi.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.4 kB 🔴 +3.04 kB
assets/WidgetGalleria-_EVh1VRf.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.45 kB 🟢 -1.3 kB
assets/WidgetGalleria-Bhgjmp2J.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.45 kB 🔴 +1.31 kB
assets/WidgetColorPicker-CKatVT-k.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-DuAAdtmC.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetMarkdown-DkMiMJGp.js (new) 3.1 kB 🔴 +3.1 kB 🔴 +1.29 kB 🔴 +1.13 kB
assets/WidgetMarkdown-wz_0nQ9V.js (removed) 3.1 kB 🟢 -3.1 kB 🟢 -1.29 kB 🟢 -1.13 kB
assets/WidgetAudioUI-A4mZnnpD.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.12 kB 🟢 -1.02 kB
assets/WidgetAudioUI-CpSmtiMB.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.12 kB 🔴 +1.01 kB
assets/WidgetTextarea-CvAb2wdo.js (new) 2.48 kB 🔴 +2.48 kB 🔴 +1.01 kB 🔴 +909 B
assets/WidgetTextarea-LLnkXYYL.js (removed) 2.48 kB 🟢 -2.48 kB 🟢 -1.01 kB 🟢 -906 B
assets/WidgetInputText-C7SfGiLN.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -916 B 🟢 -846 B
assets/WidgetInputText-ScRXaNx0.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +918 B 🔴 +828 B
assets/MediaImageBottom-BovKU3IX.js (removed) 1.57 kB 🟢 -1.57 kB 🟢 -735 B 🟢 -648 B
assets/MediaImageBottom-tLAMXkdc.js (new) 1.57 kB 🔴 +1.57 kB 🔴 +745 B 🔴 +647 B
assets/MediaAudioBottom-Be99Exa6.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +742 B 🔴 +657 B
assets/MediaAudioBottom-DKIj5lWL.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -739 B 🟢 -655 B
assets/MediaVideoBottom-CBu3U4ND.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -737 B 🟢 -653 B
assets/MediaVideoBottom-dewTSgE2.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +741 B 🔴 +655 B
assets/Media3DBottom-CxLWm8uq.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +733 B 🔴 +649 B
assets/Media3DBottom-DiUeuYlA.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -729 B 🟢 -652 B
assets/Media3DTop-B9xaZesa.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +763 B 🔴 +652 B
assets/Media3DTop-DXOa1jI9.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -762 B 🟢 -649 B
assets/WidgetSelect-BTYqIOX_.js (new) 655 B 🔴 +655 B 🔴 +346 B 🔴 +309 B
assets/WidgetSelect-Dit_Gt1K.js (removed) 655 B 🟢 -655 B 🟢 -342 B 🟢 -306 B
assets/WidgetInputNumber-CHY5HI2Z.js (removed) 595 B 🟢 -595 B 🟢 -331 B 🟢 -278 B
assets/WidgetInputNumber-D0KDMRm7.js (new) 595 B 🔴 +595 B 🔴 +333 B 🔴 +277 B
assets/Load3D-BjOiZ1y3.js (new) 424 B 🔴 +424 B 🔴 +266 B 🔴 +225 B
assets/Load3D-BTKaAD3c.js (removed) 424 B 🟢 -424 B 🟢 -266 B 🟢 -226 B
assets/WidgetLegacy-CaMFEMJP.js (new) 364 B 🔴 +364 B 🔴 +237 B 🔴 +194 B
assets/WidgetLegacy-CMBhx3wp.js (removed) 364 B 🟢 -364 B 🟢 -236 B 🟢 -192 B
assets/commands-_s-RvhJR.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BuUILW6P.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV4R6fLx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CLwPdnT6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CWMchBmd.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DazTQhtc.js 12.9 kB 12.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DmWrOe93.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwiH7Kr6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-mS3LCNPn.js 14.5 kB 14.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-5lOBdqcC.js 84.5 kB 84.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BOCuaVpE.js 73.4 kB 73.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-ClrEFGUz.js 72.4 kB 72.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CyNU0iQX.js 99.3 kB 99.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D7gwLxft.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DC8o4BCt.js 86.8 kB 86.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DKiesCV4.js 94.3 kB 94.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Hq2q-OtB.js 83.6 kB 83.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-USAlAlnj.js 82 kB 82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-DqRKCSyG.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-B_ZBEjC1.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-BFDrQq-5.js 2.76 kB 2.76 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-_Px5dSNW.js 306 kB 306 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-7z21KPoS.js 285 kB 285 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BWKZzBPK.js 346 kB 346 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CGbgH4Yl.js 320 kB 320 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CjjjdWkV.js 313 kB 313 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CVrNtxvj.js 288 kB 288 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DLRSA0IK.js 309 kB 309 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DQV2gnwA.js 372 kB 372 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-ofqLG5vz.js 310 kB 310 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-B2-gppzM.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-BOXn8KLW.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-C_oC_TUM.js 1.58 kB 1.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 17 added / 17 removed

@christian-byrne christian-byrne added the claude-review Add to trigger a PR code review from Claude Code label Nov 26, 2025
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: 0

🧹 Nitpick comments (2)
src/lib/litegraph/src/LGraphCanvas.ts (1)

10-11: Clarify paste→layout semantics: filter to nodes and force Canvas source before batchUpdateNodeBounds

The new paste path is directionally right (feeding layout with body-only heights in Vue mode), but two details are worth tightening up:

  1. Only emit bounds for real nodes

created can contain groups and reroutes, but batchUpdateNodeBounds is node-centric. Filtering keeps intent clear and types tighter:

-    const newPositions = created.map((node) => {
+    const newPositions = created
+      .filter((item): item is LGraphNode => item instanceof LGraphNode)
+      .map((node) => {
       const fullHeight = node.size?.[1] ?? 200
       const layoutHeight = LiteGraph.vueNodesMode
         ? removeNodeTitleHeight(fullHeight)
         : fullHeight
       return {
         nodeId: String(node.id),
         bounds: {
           x: node.pos[0],
           y: node.pos[1],
           width: node.size?.[0] ?? 100,
           height: layoutHeight
         }
       }
-      }
-    })
-
-    layoutStore.batchUpdateNodeBounds(newPositions)
+      })
+
+    if (newPositions.length) layoutStore.setSource(LayoutSource.Canvas)
+    layoutStore.batchUpdateNodeBounds(newPositions)
  1. Avoid accidental DOM normalization on paste

Because DOM updates now set LayoutSource.DOM before calling batchUpdateNodeBounds, calling layoutStore.batchUpdateNodeBounds here without first setting the source could cause this Canvas-origin path to be treated as DOM-origin if a ResizeObserver callback was the last writer, potentially re-stripping title height from an already body-normalized layoutHeight. Explicitly forcing LayoutSource.Canvas (as above) makes the origin unambiguous and keeps normalization single-responsibility in the store.

Also applies to: 4047-4064

tests-ui/tests/renderer/core/layout/layoutStore.test.ts (1)

306-335: Consider adding assertions for unchanged properties and edge cases.

The test correctly validates DOM height normalization. Consider enhancing coverage:

     const nodeRef = layoutStore.getNodeLayoutRef(nodeId)
     expect(nodeRef.value?.size.height).toBe(layout.size.height)
+    expect(nodeRef.value?.size.width).toBe(layout.size.width)
+    expect(nodeRef.value?.position).toEqual(layout.position)
   })
+
+  it('normalizes very small DOM-sourced heights safely', () => {
+    const nodeId = 'small-dom-node'
+    const layout = createTestNode(nodeId)
+    layout.size.height = 10 // Smaller than NODE_TITLE_HEIGHT
+    
+    layoutStore.applyOperation({
+      type: 'createNode',
+      entity: 'node',
+      nodeId,
+      layout,
+      timestamp: Date.now(),
+      source: LayoutSource.External,
+      actor: 'test'
+    })
+    
+    layoutStore.setSource(LayoutSource.DOM)
+    layoutStore.batchUpdateNodeBounds([
+      {
+        nodeId,
+        bounds: {
+          x: layout.bounds.x,
+          y: layout.bounds.y,
+          width: layout.size.width,
+          height: addNodeTitleHeight(layout.size.height)
+        }
+      }
+    ])
+    
+    const nodeRef = layoutStore.getNodeLayoutRef(nodeId)
+    // Should clamp to 0, not go negative
+    expect(nodeRef.value?.size.height).toBeGreaterThanOrEqual(0)
+  })
 })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b2c1fc and 88ed240.

📒 Files selected for processing (8)
  • src/composables/graph/useVueNodeLifecycle.ts (2 hunks)
  • src/lib/litegraph/src/LGraphCanvas.ts (2 hunks)
  • src/renderer/core/layout/store/layoutStore.ts (3 hunks)
  • src/renderer/core/layout/sync/useLayoutSync.ts (2 hunks)
  • src/renderer/core/layout/types.ts (1 hunks)
  • src/renderer/core/layout/utils/nodeSizeUtil.ts (1 hunks)
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts (2 hunks)
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Use es-toolkit for utility functions

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper error handling in components and services

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.{vue,ts}

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

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.ts

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

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

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

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/core/layout/types.ts
  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/renderer/core/layout/store/layoutStore.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)

tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks

Files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Unit and component tests should be located in tests-ui/ or co-located with components as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_tests/

Files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
**/composables/use*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables in the format useXyz.ts

Files:

  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
src/lib/litegraph/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the pnpm lint:fix command
Take advantage of TypedArray subarray when appropriate
The size and pos properties of Rectangle share the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single line if syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
Do not replace &&= or ||= with = when there is no reason to do so. If you do find a reason to remove either &&= or ||=, leave a comment explaining why the removal occurred
When writing methods, prefer returning idiomatic JavaScript undefined over null

Files:

  • src/lib/litegraph/src/LGraphCanvas.ts
src/lib/litegraph/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Files:

  • src/lib/litegraph/src/LGraphCanvas.ts
🧠 Learnings (20)
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : The `size` and `pos` properties of `Rectangle` share the same array buffer (`subarray`); they may be used to set the rectangle's size and position

Applied to files:

  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • src/renderer/core/layout/sync/useLayoutSync.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilities

Applied to files:

  • src/composables/graph/useVueNodeLifecycle.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/composables/graph/useVueNodeLifecycle.ts
  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in stores

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test across multiple viewports

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:48:23.088Z
Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E tests

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `test` instead of `it` for defining test cases in vitest

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `vitest` for unit testing in this project

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : When writing tests for subgraph-related code, always import from the barrel export at `@/lib/litegraph/src/litegraph` to avoid circular dependency issues

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test user workflows in browser tests

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3

Applied to files:

  • src/renderer/core/layout/store/layoutStore.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • src/renderer/core/layout/store/layoutStore.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers

Applied to files:

  • src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/lib/litegraph/src/LGraphCanvas.ts
🧬 Code graph analysis (7)
src/renderer/core/layout/sync/useLayoutSync.ts (1)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
  • addNodeTitleHeight (6-7)
src/composables/graph/useVueNodeLifecycle.ts (1)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
  • removeNodeTitleHeight (3-4)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
src/lib/litegraph/src/litegraph.ts (1)
  • LiteGraph (17-17)
tests-ui/tests/renderer/core/layout/layoutStore.test.ts (2)
src/renderer/core/layout/store/layoutStore.ts (1)
  • layoutStore (1467-1467)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
  • addNodeTitleHeight (6-7)
src/renderer/core/layout/store/layoutStore.ts (1)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
  • removeNodeTitleHeight (3-4)
src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts (1)
src/renderer/core/layout/store/layoutStore.ts (1)
  • layoutStore (1467-1467)
src/lib/litegraph/src/LGraphCanvas.ts (1)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
  • removeNodeTitleHeight (3-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test
  • GitHub Check: collect
  • GitHub Check: setup
  • GitHub Check: lint-and-format
🔇 Additional comments (9)
src/composables/graph/useVueNodeLifecycle.ts (1)

13-13: Initial layout seeding now uses body-only node heights consistently

Using removeNodeTitleHeight(node.size[1]) when building the initial nodes array keeps layout state in “body height” units while leaving pos in LiteGraph coordinates, matching the new normalization strategy used elsewhere in the PR. Import wiring is correct and locally type-safe.

Also applies to: 39-45

src/renderer/core/layout/types.ts (1)

10-15: LayoutSource.DOM enum extension looks correct

Adding LayoutSource.DOM = 'dom' cleanly extends the existing source taxonomy (Canvas/Vue/External) and matches how callers tag DOM-driven updates; no type or narrowing issues here.

src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts (1)

102-112: ResizeObserver now correctly tags DOM updates and defers height stripping to the layout store

Using height: Math.max(0, height) while keeping y offset by LiteGraph.NODE_TITLE_HEIGHT, then calling layoutStore.setSource(LayoutSource.DOM) before batchUpdateNodeBounds, lines up with the new contract where DOM-sourced heights are normalized (title removed) inside the store instead of at each caller. This should avoid double-subtraction and keeps the RO pipeline focused on raw browser measurements.

Also applies to: 126-126

src/renderer/core/layout/sync/useLayoutSync.ts (1)

11-11: Layout→LiteGraph sync now correctly restores title height before calling setSize

Computing targetHeight = addNodeTitleHeight(layout.size.height) and using that both in the equality check and liteNode.setSize keeps LiteGraph operating on full node heights while the layout store remains in body-only units. This matches the new initialization and DOM pipelines and avoids redundant setSize calls.

Also applies to: 47-54

src/renderer/core/layout/store/layoutStore.ts (3)

12-12: LGTM!

The import of removeNodeTitleHeight is properly utilized in the height normalization logic below.


1431-1439: LGTM!

The conditional normalization correctly creates a new bounds object when needed, preventing mutation of the original bounds parameter. The spread operator ensures only the height is modified when normalizing.


1420-1443: The height normalization logic is correct and intentional—no changes needed.

The assumption that Vue, Canvas, and External sources provide body-only measurements is verified and correct:

  • DOM source: ResizeObserver measurements include the title bar (30px), so batchUpdateNodeBounds removes it via removeNodeTitleHeight()
  • Canvas source: LiteGraph already strips the title height before calling batchUpdateNodeBounds when vueNodesMode is true (LGraphCanvas.ts:4050) ✓
  • External source: Multiplayer/offline sync data arrives pre-normalized ✓
  • Vue source: Direct Vue mutations don't need normalization (different measurement context) ✓

The logic is sound and the existing code doesn't require the suggested comment addition.

tests-ui/tests/renderer/core/layout/layoutStore.test.ts (1)

4-6: LGTM!

The import reorganization properly separates type and value imports, following TypeScript best practices. The new addNodeTitleHeight import is correctly utilized in the test case below.

src/renderer/core/layout/utils/nodeSizeUtil.ts (1)

1-7: No runtime validation needed — NODE_TITLE_HEIGHT is a stable class constant.

LiteGraph.NODE_TITLE_HEIGHT is defined in src/lib/litegraph/src/LiteGraphGlobal.ts as a class property initialized with the value 30 (line 42). It is not optional and is guaranteed to be a number at definition time. The codebase uses this constant 40+ times without any validation, demonstrating it reliably works as-is. The two utility functions in nodeSizeUtil.ts correctly follow this pattern and require no additional runtime checks.

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Comprehensive PR Review

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

Review Summary

PR: normalize height at sites (#6966)
Impact: 79 additions, 21 deletions across 8 files

Issue Distribution

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

Category Breakdown

  • Architecture: 1 issues
  • Security: 0 issues
  • Performance: 0 issues
  • Code Quality: 4 issues

Key Findings

Architecture & Design

The PR introduces a significant architectural change by adding a new LayoutSource.DOM enum value and using it to differentiate DOM-driven height updates from other sources. This creates a clear separation between browser-measured heights (which include title bars) and layout-store-managed heights (which are body-only). The approach is sound but creates implicit coupling between source type and data transformation logic.

The height normalization strategy is clever - using removeNodeTitleHeight when data flows into the layout store and addNodeTitleHeight when syncing back to LiteGraph. This ensures the layout system treats node sizes consistently as body-only measurements while preserving LiteGraph's expectation of full heights.

Security Considerations

No security issues identified. The changes are isolated to layout calculations and don't involve user input, external data sources, or authentication flows.

Performance Impact

The changes should have minimal performance impact. The new utility functions are simple mathematical operations. However, the ResizeObserver callback continues to call getBoundingClientRect() synchronously, which could cause layout thrashing with many simultaneous node resizes.

Integration Points

This change affects multiple critical systems:

  • LiteGraph Integration: Changes how heights are synchronized between Vue and LiteGraph
  • Layout Store: Adds source-dependent height normalization logic
  • Vue Node Lifecycle: Updates initialization to use normalized heights
  • Extension System: May affect how extensions interact with node sizing

The addition of comprehensive test coverage helps ensure these integration points work correctly.

Positive Observations

  • Clear Problem Solution: The PR addresses a real issue with height double-truncation that was causing layout inconsistencies
  • Comprehensive Implementation: Changes are applied consistently across all relevant subsystems
  • Good Test Coverage: Includes a test that validates the core normalization behavior
  • Clean Utility Functions: The nodeSizeUtil.ts functions provide a clean abstraction for height calculations
  • Detailed PR Description: The description clearly explains the problem, solution, and data flow

Next Steps

  1. Address the high-priority architectural coupling between LayoutSource.DOM and height normalization
  2. Add defensive checks for edge cases in the utility functions
  3. Consider adding more comprehensive test cases for boundary conditions
  4. Document the new LayoutSource.DOM classification clearly for future maintainers

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

@github-actions github-actions bot removed the claude-review Add to trigger a PR code review from Claude Code label Nov 26, 2025
DrJKL
DrJKL previously approved these changes Nov 26, 2025
Copy link
Contributor

Choose a reason for hiding this comment

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

Small nit: I'd rather have fewer of these one-off utility modules, if we can consolidate things like this into named domain utilities.

@DrJKL DrJKL added the New Browser Test Expectations New browser test screenshot should be set by github action label Nov 26, 2025
@github-actions
Copy link

Updating Playwright Expectations

@github-actions github-actions bot removed the New Browser Test Expectations New browser test screenshot should be set by github action label Nov 26, 2025
@christian-byrne christian-byrne changed the title normalize height at sites fix: Vue Node <-> Litegraph node height offset normalization Nov 26, 2025
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: 0

🧹 Nitpick comments (1)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)

3-7: Consider symmetric fallback handling in addNodeTitleHeight

You already guarded removeNodeTitleHeight against an undefined LiteGraph.NODE_TITLE_HEIGHT by defaulting to 0. For full symmetry and to avoid a potential NaN if NODE_TITLE_HEIGHT were ever unset (e.g. in tests/mocks), you could mirror that in addNodeTitleHeight:

-export const addNodeTitleHeight = (height: number) =>
-  height + LiteGraph.NODE_TITLE_HEIGHT
+export const addNodeTitleHeight = (height: number) => {
+  const titleHeight = LiteGraph.NODE_TITLE_HEIGHT || 0
+  return height + titleHeight
+}

Not critical given NODE_TITLE_HEIGHT is normally defined, but this keeps the pair robust under unusual setups.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88ed240 and 402f08f.

⛔ Files ignored due to path filters (15)
  • browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png is excluded by !**/*.png
  • browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png is excluded by !**/*.png
📒 Files selected for processing (4)
  • src/lib/litegraph/src/LGraphCanvas.ts (2 hunks)
  • src/renderer/core/layout/utils/nodeSizeUtil.ts (1 hunks)
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts (2 hunks)
  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

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

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Use es-toolkit for utility functions

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

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

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper error handling in components and services

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

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

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

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

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)

tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Unit and component tests should be located in tests-ui/ or co-located with components as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_tests/

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
**/composables/use*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables in the format useXyz.ts

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
src/**/*.{vue,ts}

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

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

Files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.ts

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

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

Files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
src/lib/litegraph/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the pnpm lint:fix command
Take advantage of TypedArray subarray when appropriate
The size and pos properties of Rectangle share the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single line if syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
Do not replace &&= or ||= with = when there is no reason to do so. If you do find a reason to remove either &&= or ||=, leave a comment explaining why the removal occurred
When writing methods, prefer returning idiomatic JavaScript undefined over null

Files:

  • src/lib/litegraph/src/LGraphCanvas.ts
src/lib/litegraph/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Files:

  • src/lib/litegraph/src/LGraphCanvas.ts
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilities

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebase

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilities

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Utilize provide/inject for dependency injection in Vue 3

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3

Applied to files:

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

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : When writing tests for subgraph-related code, always import from the barrel export at `@/lib/litegraph/src/litegraph` to avoid circular dependency issues

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test across multiple viewports

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in stores

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Check tests-ui/README.md for test guidelines

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:48:23.088Z
Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E tests

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{ts,tsx} : Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `vitest` for unit testing in this project

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `test` instead of `it` for defining test cases in vitest

Applied to files:

  • tests-ui/tests/renderer/core/layout/layoutStore.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : When writing methods, prefer returning idiomatic JavaScript `undefined` over `null`

Applied to files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : The `size` and `pos` properties of `Rectangle` share the same array buffer (`subarray`); they may be used to set the rectangle's size and position

Applied to files:

  • src/renderer/core/layout/utils/nodeSizeUtil.ts
  • src/lib/litegraph/src/LGraphCanvas.ts
🧬 Code graph analysis (2)
tests-ui/tests/renderer/core/layout/layoutStore.test.ts (2)
src/renderer/core/layout/store/layoutStore.ts (1)
  • layoutStore (1467-1467)
src/lib/litegraph/src/litegraph.ts (1)
  • LiteGraph (17-17)
src/renderer/core/layout/utils/nodeSizeUtil.ts (1)
src/lib/litegraph/src/litegraph.ts (1)
  • LiteGraph (17-17)
🪛 ESLint
tests-ui/tests/renderer/core/layout/layoutStore.test.ts

[error] 3-3: Unable to resolve path to module '@/lib/litegraph/src/litegraph'.

(import-x/no-unresolved)


[error] 4-4: Unable to resolve path to module '@/renderer/core/layout/store/layoutStore'.

(import-x/no-unresolved)


[error] 5-5: Unable to resolve path to module '@/renderer/core/layout/types'.

(import-x/no-unresolved)

src/renderer/core/layout/utils/nodeSizeUtil.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/plFJYpQPRG'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/namespace)


[error] 1-1: Resolve error: EACCES: permission denied, open '/tzmWJsbJsw'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-unresolved)


[error] 1-1: Resolve error: EACCES: permission denied, open '/ToCOyXFqTp'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-relative-packages)


[error] 1-1: Unable to resolve path to module '@/lib/litegraph/src/litegraph'.

(import-x/no-unresolved)

src/lib/litegraph/src/LGraphCanvas.ts

[error] 10-10: Unable to resolve path to module '@/renderer/core/layout/utils/nodeSizeUtil'.

(import-x/no-unresolved)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: deploy-and-comment
  • GitHub Check: test
  • GitHub Check: setup
  • GitHub Check: collect
  • GitHub Check: lint-and-format
🔇 Additional comments (4)
tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.ts (1)

8-10: Mock factory shape for layoutMutations is correct

The mock now correctly returns an object with a useLayoutMutations: vi.fn() export, which matches the named import and plays nicely with vi.mocked(useLayoutMutations). No further changes needed here.

src/lib/litegraph/src/LGraphCanvas.ts (1)

10-10: Paste path height normalization into layoutStore looks consistent with Vue-node semantics

This block does what the PR description calls for:

  • Filters created to LGraphNode instances, then for each node:
    • Uses node.size?.[1] ?? 200 as the full LiteGraph height.
    • When LiteGraph.vueNodesMode is enabled, converts that to a body-only height via removeNodeTitleHeight, otherwise passes the full height through.
    • Builds { nodeId, bounds } with the adjusted height and current pos/size for width.
  • Before writing, sets layoutStore’s source to LayoutSource.Canvas only if there is at least one node, then calls batchUpdateNodeBounds(newPositions).

This ensures that, in Vue nodes mode, pasted nodes don’t accumulate title height on repeated copy/paste cycles, while leaving non‑Vue behavior unchanged. The node filtering and use of stringified node.id also match existing layout store usage patterns.

Also applies to: 4047-4066

tests-ui/tests/renderer/core/layout/layoutStore.test.ts (2)

3-6: Imports for LiteGraph and layout types are well-structured

Using the litegraph barrel (@/lib/litegraph/src/litegraph) plus separating value and type imports keeps things clear and matches the existing patterns in this area. No changes needed here.


306-408: New DOM height normalization tests cover key edge cases

These three tests nicely exercise the new behavior:

  • Normal DOM-sourced heights are normalized back to body-only size while preserving width and position.
  • Very small DOM heights are clamped safely to non-negative values.
  • An undefined LiteGraph.NODE_TITLE_HEIGHT is handled without producing NaN, and the global is correctly restored with try/finally.

This directly addresses the earlier request for edge-case coverage around DOM heights and NODE_TITLE_HEIGHT.

Based on learnings, this aligns well with the tests-ui guidance to cover edge cases and use Vitest effectively.

@christian-byrne christian-byrne merged commit 29dbfa3 into main Nov 27, 2025
27 checks passed
@christian-byrne christian-byrne deleted the vue-node/fix-title-height-double-truncate branch November 27, 2025 00:37
@github-actions
Copy link

@christian-byrne Backport to core/1.32 failed: Merge conflicts detected.

Please manually cherry-pick commit 29dbfa3f60b6e4759d4fb6267e53c478af64484f to the core/1.32 branch.

Conflicting files
  • browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png
  • browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png
  • browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png
  • browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png
  • browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png

@github-actions
Copy link

@christian-byrne Backport to cloud/1.32 failed: Merge conflicts detected.

Please manually cherry-pick commit 29dbfa3f60b6e4759d4fb6267e53c478af64484f to the cloud/1.32 branch.

Conflicting files
  • browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png
  • browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png
  • browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png
  • browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png
  • browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png
  • browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png

@github-actions
Copy link

@christian-byrne Backport to core/1.33 failed: Merge conflicts detected.

Please manually cherry-pick commit 29dbfa3f60b6e4759d4fb6267e53c478af64484f to the core/1.33 branch.

Conflicting files
  • browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png

@github-actions
Copy link

@christian-byrne Backport to cloud/1.33 failed: Merge conflicts detected.

Please manually cherry-pick commit 29dbfa3f60b6e4759d4fb6267e53c478af64484f to the cloud/1.33 branch.

Conflicting files
  • browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png

@christian-byrne
Copy link
Contributor Author

I am doing manual backporting.

christian-byrne added a commit that referenced this pull request Nov 27, 2025
Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
…ormalization (#6975)

## Summary
Backport of #6966 onto core/1.32.

- cherry-picked 29dbfa3 and accepted upstream snapshot updates during
conflict resolution (used the commit's PNG expectations)
- no additional code changes beyond the original patch

## Testing
- pnpm typecheck

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6975-backport-core-1-32-fix-Vue-Node-Litegraph-node-height-offset-normalization-2b86d73d3650819a860dd922bdbc20ff)
by [Unito](https://www.unito.io)

Co-authored-by: github-actions <[email protected]>
christian-byrne added a commit that referenced this pull request Nov 27, 2025
Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
## Summary

Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
## Summary

Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <[email protected]>
(cherry picked from commit 29dbfa3)
christian-byrne added a commit that referenced this pull request Nov 27, 2025
…ormalization (#6979)

## Summary
Backport of #6966 onto core/1.33.

- cherry-picked 29dbfa3
- resolved the zoomed-in ctrl+shift snapshot conflict by taking upstream
expectations

## Testing
- pnpm typecheck

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6979-backport-core-1-33-fix-Vue-Node-Litegraph-node-height-offset-normalization-2b86d73d365081748ef2d6f47ce7b1d3)
by [Unito](https://www.unito.io)

Co-authored-by: github-actions <[email protected]>
christian-byrne added a commit that referenced this pull request Nov 27, 2025
…normalization (#6978)

## Summary
Backport of #6966 onto cloud/1.33.

- cherry-picked 29dbfa3
- accepted upstream snapshot updates (only zoomed-in ctrl+shift PNG
conflicted)

## Testing
- pnpm typecheck

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6978-backport-cloud-1-33-fix-Vue-Node-Litegraph-node-height-offset-normalization-2b86d73d365081a19a81f4fac0fd2e91)
by [Unito](https://www.unito.io)

Co-authored-by: github-actions <[email protected]>
christian-byrne added a commit that referenced this pull request Nov 27, 2025
…normalization (#6977)

## Summary
Backport of #6966 onto cloud/1.32.

- cherry-picked 29dbfa3
- resolved snapshot conflicts by taking the updated expectations from
the upstream commit

## Testing
- pnpm typecheck

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6977-backport-cloud-1-32-fix-Vue-Node-Litegraph-node-height-offset-normalization-2b86d73d36508119a3e8db7b27107215)
by [Unito](https://www.unito.io)

Co-authored-by: github-actions <[email protected]>
@coderabbitai coderabbitai bot mentioned this pull request Nov 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.32 area:vue-migration cloud/1.33 Backport PRs for cloud 1.33 core/1.33 Backport PRs for core 1.33 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants