-
Notifications
You must be signed in to change notification settings - Fork 448
fix: Vue Node <-> Litegraph node height offset normalization #6966
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Vue Node <-> Litegraph node height offset normalization #6966
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthroughAdds 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
Sequence DiagramsequenceDiagram
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]
Possibly related PRs
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
🎭 Playwright Test Results⏰ Completed at: 11/27/2025, 12:32:48 AM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 11/27/2025, 12:24:10 AM UTC 🔗 Links🎉 Your Storybook is ready for review! |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.18 MB (baseline 3.18 MB) • 🔴 +894 BMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 946 kB (baseline 946 kB) • 🔴 +105 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 139 kB (baseline 139 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 2.94 kB (baseline 2.94 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 3.84 MB (baseline 3.84 MB) • ⚪ 0 BBundles that do not match a named category
Status: 17 added / 17 removed |
There was a problem hiding this 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 batchUpdateNodeBoundsThe new paste path is directionally right (feeding layout with body-only heights in Vue mode), but two details are worth tightening up:
- Only emit bounds for real nodes
createdcan contain groups and reroutes, butbatchUpdateNodeBoundsis 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)
- Avoid accidental DOM normalization on paste
Because DOM updates now set
LayoutSource.DOMbefore callingbatchUpdateNodeBounds, callinglayoutStore.batchUpdateNodeBoundshere 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-normalizedlayoutHeight. Explicitly forcingLayoutSource.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
📒 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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/renderer/core/layout/types.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
src/renderer/core/layout/types.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/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 runpnpm formatbefore committing
Files:
src/renderer/core/layout/types.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce 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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
src/renderer/core/layout/types.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/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.tssrc/renderer/core/layout/sync/useLayoutSync.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/renderer/core/layout/store/layoutStore.tssrc/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.tssrc/lib/litegraph/src/LGraphCanvas.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directly
Files:
src/composables/graph/useVueNodeLifecycle.tssrc/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.tssrc/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.tssrc/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 assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_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 thepnpm lint:fixcommand
Take advantage ofTypedArraysubarraywhen appropriate
Thesizeandposproperties ofRectangleshare the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single lineifsyntax 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 JavaScriptundefinedovernull
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.tssrc/composables/graph/useVueNodeLifecycle.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/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.tssrc/renderer/core/layout/utils/nodeSizeUtil.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/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.tssrc/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.tssrc/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 consistentlyUsing
removeNodeTitleHeight(node.size[1])when building the initialnodesarray keeps layout state in “body height” units while leavingposin 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 correctAdding
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 storeUsing
height: Math.max(0, height)while keepingyoffset byLiteGraph.NODE_TITLE_HEIGHT, then callinglayoutStore.setSource(LayoutSource.DOM)beforebatchUpdateNodeBounds, 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 setSizeComputing
targetHeight = addNodeTitleHeight(layout.size.height)and using that both in the equality check andliteNode.setSizekeeps 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 redundantsetSizecalls.Also applies to: 47-54
src/renderer/core/layout/store/layoutStore.ts (3)
12-12: LGTM!The import of
removeNodeTitleHeightis 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
batchUpdateNodeBoundsremoves it viaremoveNodeTitleHeight()✓- Canvas source: LiteGraph already strips the title height before calling
batchUpdateNodeBoundswhenvueNodesModeis 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
addNodeTitleHeightimport 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_HEIGHTis defined insrc/lib/litegraph/src/LiteGraphGlobal.tsas a class property initialized with the value30(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 innodeSizeUtil.tscorrectly follow this pattern and require no additional runtime checks.
src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts
Show resolved
Hide resolved
There was a problem hiding this 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
- Address the high-priority architectural coupling between LayoutSource.DOM and height normalization
- Add defensive checks for edge cases in the utility functions
- Consider adding more comprehensive test cases for boundary conditions
- 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.
There was a problem hiding this comment.
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.
|
Updating Playwright Expectations |
There was a problem hiding this 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 inaddNodeTitleHeightYou already guarded
removeNodeTitleHeightagainst an undefinedLiteGraph.NODE_TITLE_HEIGHTby defaulting to0. For full symmetry and to avoid a potentialNaNifNODE_TITLE_HEIGHTwere ever unset (e.g. in tests/mocks), you could mirror that inaddNodeTitleHeight:-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_HEIGHTis normally defined, but this keeps the pair robust under unusual setups.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (15)
browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.pngis 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.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/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.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/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 runpnpm formatbefore committing
Files:
tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/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.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce 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.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/lib/litegraph/src/LGraphCanvas.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/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.tstests-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 assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/tests/renderer/extensions/vueNodes/composables/useNodeZIndex.test.tstests-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.tssrc/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.tssrc/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.tssrc/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.tssrc/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 thepnpm lint:fixcommand
Take advantage ofTypedArraysubarraywhen appropriate
Thesizeandposproperties ofRectangleshare the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single lineifsyntax 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 JavaScriptundefinedovernull
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.tstests-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.tstests-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.tstests-ui/tests/renderer/core/layout/layoutStore.test.tssrc/renderer/core/layout/utils/nodeSizeUtil.tssrc/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.tstests-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.tstests-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.tstests-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.tstests-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.tssrc/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.tssrc/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.tssrc/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 forlayoutMutationsis correctThe mock now correctly returns an object with a
useLayoutMutations: vi.fn()export, which matches the named import and plays nicely withvi.mocked(useLayoutMutations). No further changes needed here.src/lib/litegraph/src/LGraphCanvas.ts (1)
10-10: Paste path height normalization intolayoutStorelooks consistent with Vue-node semanticsThis block does what the PR description calls for:
- Filters
createdtoLGraphNodeinstances, then for each node:
- Uses
node.size?.[1] ?? 200as the full LiteGraph height.- When
LiteGraph.vueNodesModeis enabled, converts that to a body-only height viaremoveNodeTitleHeight, otherwise passes the full height through.- Builds
{ nodeId, bounds }with the adjusted height and currentpos/sizefor width.- Before writing, sets
layoutStore’s source toLayoutSource.Canvasonly if there is at least one node, then callsbatchUpdateNodeBounds(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.idalso 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-structuredUsing 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 casesThese 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_HEIGHTis handled without producingNaN, and the global is correctly restored withtry/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 Backport to Please manually cherry-pick commit Conflicting files
|
|
@christian-byrne Backport to Please manually cherry-pick commit Conflicting files
|
|
@christian-byrne Backport to Please manually cherry-pick commit Conflicting files
|
|
@christian-byrne Backport to Please manually cherry-pick commit Conflicting files
|
|
I am doing manual backporting. |
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)
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)
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)
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)
…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]>
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)
## 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)
## 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)
…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]>
…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]>
…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]>
## Summary Fixes drift when switching between modes introduced after #6966 ## Changes - **What**: ensureCorrectLayoutScale ## Screenshots (if applicable) https://github.com/user-attachments/assets/e40803f1-8ae5-4890-bc1a-3f2413a35c7d https://www.notion.so/Bug-Spamming-Nodes-2-0-button-causes-nodes-to-grow-longer-2bd6d73d3650818492a4e0cc53da7017 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7109-fix-layout-scale-to-handle-downscale-correctly-2be6d73d365081c29fe2c481feafebc1) by [Unito](https://www.unito.io)
## Summary Fixes drift when switching between modes introduced after #6966 ## Changes - **What**: ensureCorrectLayoutScale ## Screenshots (if applicable) https://github.com/user-attachments/assets/e40803f1-8ae5-4890-bc1a-3f2413a35c7d https://www.notion.so/Bug-Spamming-Nodes-2-0-button-causes-nodes-to-grow-longer-2bd6d73d3650818492a4e0cc53da7017 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7109-fix-layout-scale-to-handle-downscale-correctly-2be6d73d365081c29fe2c481feafebc1) by [Unito](https://www.unito.io)
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 barLayoutSource.Vue: direct Vue-driven mutations routed through the layout storeLayoutSource.Canvas: legacy LiteGraph/canvas updates that will be phased out over timeLayoutSource.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