Merge main (as of 10-06-2025) into rh-test (#5965)

## Summary

Merges latest changes from `main` as of 10-06-2025.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5965-Merge-main-as-of-10-06-2025-into-rh-test-2856d73d3650812cb95fd8917278a770)
by [Unito](https://www.unito.io)

---------

Signed-off-by: Marcel Petrick <mail@marcelpetrick.it>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Benjamin Lu <benceruleanlu@proton.me>
Co-authored-by: Terry Jia <terryjia88@gmail.com>
Co-authored-by: snomiao <snomiao@gmail.com>
Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
Co-authored-by: Marcel Petrick <mail@marcelpetrick.it>
Co-authored-by: Alexander Brown <DrJKL0424@gmail.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Alexander Piskun <13381981+bigcat88@users.noreply.github.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
Co-authored-by: JakeSchroeder <jake@axiom.co>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: ComfyUI Wiki <contact@comfyui-wiki.com>
This commit is contained in:
Arjan Singh
2025-10-08 19:06:40 -07:00
committed by GitHub
parent 529a4de583
commit 5869b04e57
454 changed files with 32333 additions and 37002 deletions

View File

@@ -84,8 +84,8 @@ describe('LGraphNode', () => {
}))
}
node.configure(configureData)
expect(node.pos).toEqual(new Float32Array([50, 60]))
expect(node.size).toEqual(new Float32Array([70, 80]))
expect(node.pos).toEqual(new Float64Array([50, 60]))
expect(node.size).toEqual(new Float64Array([70, 80]))
})
test('should configure inputs correctly', () => {
@@ -266,7 +266,7 @@ describe('LGraphNode', () => {
const node = new LGraphNode('TestNode') as unknown as Omit<
LGraphNode,
'boundingRect'
> & { boundingRect: Float32Array }
> & { boundingRect: Float64Array }
node.pos = [100, 100]
node.size = [100, 100]
node.boundingRect[0] = 100
@@ -335,7 +335,7 @@ describe('LGraphNode', () => {
const node = new LGraphNode('TestNode') as unknown as Omit<
LGraphNode,
'boundingRect'
> & { boundingRect: Float32Array }
> & { boundingRect: Float64Array }
node.pos = [100, 100]
node.size = [100, 100]
node.boundingRect[0] = 100
@@ -367,7 +367,7 @@ describe('LGraphNode', () => {
const node = new LGraphNode('TestNode') as unknown as Omit<
LGraphNode,
'boundingRect'
> & { boundingRect: Float32Array }
> & { boundingRect: Float64Array }
node.pos = [100, 100]
node.size = [100, 100]
node.boundingRect[0] = 100
@@ -400,7 +400,7 @@ describe('LGraphNode', () => {
const node = new LGraphNode('TestNode') as unknown as Omit<
LGraphNode,
'boundingRect'
> & { boundingRect: Float32Array }
> & { boundingRect: Float64Array }
node.pos = [100, 100]
node.size = [100, 100]
node.boundingRect[0] = 100
@@ -571,7 +571,7 @@ describe('LGraphNode', () => {
name: 'test_in',
type: 'string',
link: null,
boundingRect: new Float32Array([0, 0, 0, 0])
boundingRect: [0, 0, 0, 0]
}
})
test('should return position based on title height when collapsed', () => {
@@ -590,11 +590,11 @@ describe('LGraphNode', () => {
test('should return default vertical position when input.pos is undefined and not collapsed', () => {
node.flags.collapsed = false
const inputSlot2 = {
const inputSlot2: INodeInputSlot = {
name: 'test_in_2',
type: 'number',
link: null,
boundingRect: new Float32Array([0, 0, 0, 0])
boundingRect: [0, 0, 0, 0]
}
node.inputs = [inputSlot, inputSlot2]
const slotIndex = 0

View File

@@ -0,0 +1,152 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { LGraphNodeProperties } from '@/lib/litegraph/src/LGraphNodeProperties'
describe('LGraphNodeProperties', () => {
let mockNode: any
let mockGraph: any
beforeEach(() => {
mockGraph = {
trigger: vi.fn()
}
mockNode = {
id: 123,
title: 'Test Node',
flags: {},
graph: mockGraph
}
})
describe('property tracking', () => {
it('should track changes to existing properties', () => {
new LGraphNodeProperties(mockNode)
mockNode.title = 'New Title'
expect(mockGraph.trigger).toHaveBeenCalledWith('node:property:changed', {
nodeId: mockNode.id,
property: 'title',
oldValue: 'Test Node',
newValue: 'New Title'
})
})
it('should track changes to nested properties', () => {
new LGraphNodeProperties(mockNode)
mockNode.flags.collapsed = true
expect(mockGraph.trigger).toHaveBeenCalledWith('node:property:changed', {
nodeId: mockNode.id,
property: 'flags.collapsed',
oldValue: undefined,
newValue: true
})
})
it("should not emit events when value doesn't change", () => {
new LGraphNodeProperties(mockNode)
mockNode.title = 'Test Node' // Same value as original
expect(mockGraph.trigger).toHaveBeenCalledTimes(0)
})
it('should not emit events when node has no graph', () => {
mockNode.graph = null
new LGraphNodeProperties(mockNode)
// Should not throw
expect(() => {
mockNode.title = 'New Title'
}).not.toThrow()
})
})
describe('isTracked', () => {
it('should correctly identify tracked properties', () => {
const propManager = new LGraphNodeProperties(mockNode)
expect(propManager.isTracked('title')).toBe(true)
expect(propManager.isTracked('flags.collapsed')).toBe(true)
expect(propManager.isTracked('untracked')).toBe(false)
})
})
describe('serialization behavior', () => {
it('should not make non-existent properties enumerable', () => {
new LGraphNodeProperties(mockNode)
// flags.collapsed doesn't exist initially
const descriptor = Object.getOwnPropertyDescriptor(
mockNode.flags,
'collapsed'
)
expect(descriptor?.enumerable).toBe(false)
})
it('should make properties enumerable when set to non-default values', () => {
new LGraphNodeProperties(mockNode)
mockNode.flags.collapsed = true
const descriptor = Object.getOwnPropertyDescriptor(
mockNode.flags,
'collapsed'
)
expect(descriptor?.enumerable).toBe(true)
})
it('should make properties non-enumerable when set back to undefined', () => {
new LGraphNodeProperties(mockNode)
mockNode.flags.collapsed = true
mockNode.flags.collapsed = undefined
const descriptor = Object.getOwnPropertyDescriptor(
mockNode.flags,
'collapsed'
)
expect(descriptor?.enumerable).toBe(false)
})
it('should keep existing properties enumerable', () => {
// title exists initially
const initialDescriptor = Object.getOwnPropertyDescriptor(
mockNode,
'title'
)
expect(initialDescriptor?.enumerable).toBe(true)
new LGraphNodeProperties(mockNode)
const afterDescriptor = Object.getOwnPropertyDescriptor(mockNode, 'title')
expect(afterDescriptor?.enumerable).toBe(true)
})
it('should only include non-undefined values in JSON.stringify', () => {
new LGraphNodeProperties(mockNode)
// Initially, flags.collapsed shouldn't appear
let json = JSON.parse(JSON.stringify(mockNode))
expect(json.flags.collapsed).toBeUndefined()
// After setting to true, it should appear
mockNode.flags.collapsed = true
json = JSON.parse(JSON.stringify(mockNode))
expect(json.flags.collapsed).toBe(true)
// After setting to false, it should still appear (false is not undefined)
mockNode.flags.collapsed = false
json = JSON.parse(JSON.stringify(mockNode))
expect(json.flags.collapsed).toBe(false)
// After setting back to undefined, it should disappear
mockNode.flags.collapsed = undefined
json = JSON.parse(JSON.stringify(mockNode))
expect(json.flags.collapsed).toBeUndefined()
})
})
})

View File

@@ -4,7 +4,7 @@ exports[`LGraph > supports schema v0.4 graphs > oldSchemaGraph 1`] = `
LGraph {
"_groups": [
LGraphGroup {
"_bounding": Float32Array [
"_bounding": Rectangle [
20,
20,
1,
@@ -12,11 +12,11 @@ LGraph {
],
"_children": Set {},
"_nodes": [],
"_pos": Float32Array [
"_pos": Float64Array [
20,
20,
],
"_size": Float32Array [
"_size": Float64Array [
1,
3,
],
@@ -39,11 +39,11 @@ LGraph {
LGraphNode {
"_collapsed_width": undefined,
"_level": undefined,
"_pos": Float32Array [
"_pos": Float64Array [
10,
10,
],
"_posSize": Float32Array [
"_posSize": Rectangle [
10,
10,
140,
@@ -51,7 +51,7 @@ LGraph {
],
"_relative_id": undefined,
"_shape": undefined,
"_size": Float32Array [
"_size": Float64Array [
140,
60,
],
@@ -111,11 +111,11 @@ LGraph {
"1": LGraphNode {
"_collapsed_width": undefined,
"_level": undefined,
"_pos": Float32Array [
"_pos": Float64Array [
10,
10,
],
"_posSize": Float32Array [
"_posSize": Rectangle [
10,
10,
140,
@@ -123,7 +123,7 @@ LGraph {
],
"_relative_id": undefined,
"_shape": undefined,
"_size": Float32Array [
"_size": Float64Array [
140,
60,
],
@@ -184,11 +184,11 @@ LGraph {
LGraphNode {
"_collapsed_width": undefined,
"_level": undefined,
"_pos": Float32Array [
"_pos": Float64Array [
10,
10,
],
"_posSize": Float32Array [
"_posSize": Rectangle [
10,
10,
140,
@@ -196,7 +196,7 @@ LGraph {
],
"_relative_id": undefined,
"_shape": undefined,
"_size": Float32Array [
"_size": Float64Array [
140,
60,
],

View File

@@ -61,6 +61,7 @@ LiteGraphGlobal {
"NODE_COLLAPSED_WIDTH": 80,
"NODE_DEFAULT_BGCOLOR": "#353535",
"NODE_DEFAULT_BOXCOLOR": "#666",
"NODE_DEFAULT_BYPASS_COLOR": "#FF00FF",
"NODE_DEFAULT_COLOR": "#333",
"NODE_DEFAULT_SHAPE": 2,
"NODE_ERROR_COLOUR": "#E00",
@@ -79,6 +80,7 @@ LiteGraphGlobal {
"#224",
"#626",
],
"NODE_OPACITY": 0.9,
"NODE_SELECTED_TITLE_COLOR": "#FFF",
"NODE_SLOT_HEIGHT": 20,
"NODE_SUBTEXT_SIZE": 12,
@@ -134,6 +136,7 @@ LiteGraphGlobal {
"WIDGET_BGCOLOR": "#222",
"WIDGET_DISABLED_TEXT_COLOR": "#666",
"WIDGET_OUTLINE_COLOR": "#666",
"WIDGET_PROMOTED_OUTLINE_COLOR": "#BF00FF",
"WIDGET_SECONDARY_TEXT_COLOR": "#999",
"WIDGET_TEXT_COLOR": "#DDD",
"allow_multi_output_for_events": true,
@@ -160,6 +163,8 @@ LiteGraphGlobal {
"macTrackpadGestures": false,
"middle_click_slot_add_default_node": false,
"mouseWheelScroll": "panning",
"nodeLightness": undefined,
"nodeOpacity": 1,
"node_box_coloured_by_mode": false,
"node_box_coloured_when_on": false,
"node_images_path": "",