refactor: centralized node mode management

This commit is contained in:
Rizumu Ayaka
2026-01-14 16:58:27 +08:00
parent 6382b1e099
commit a2940d6a18
11 changed files with 223 additions and 50 deletions

View File

@@ -150,6 +150,7 @@ export function useLayoutMutations(): LayoutMutations {
size: layout.size ?? { width: 200, height: 100 },
zIndex: layout.zIndex ?? 0,
visible: layout.visible ?? true,
mode: layout.mode ?? 0, // Default to ALWAYS
bounds: {
x: layout.position?.x ?? 0,
y: layout.position?.y ?? 0,

View File

@@ -17,6 +17,7 @@ describe('layoutStore CRDT operations', () => {
size: { width: 200, height: 100 },
zIndex: 0,
visible: true,
mode: 0,
bounds: { x: 100, y: 100, width: 200, height: 100 }
})

View File

@@ -37,6 +37,7 @@ import type {
RerouteId,
RerouteLayout,
ResizeNodeOperation,
SetNodeModeOperation,
SetNodeZIndexOperation,
SlotLayout
} from '@/renderer/core/layout/types'
@@ -295,6 +296,18 @@ class LayoutStoreImpl implements LayoutStore {
actor: this.currentActor
})
}
if (existingLayout.mode !== newLayout.mode) {
this.applyOperation({
type: 'setNodeMode',
entity: 'node',
nodeId,
mode: newLayout.mode,
previousMode: existingLayout.mode,
timestamp: Date.now(),
source: this.currentSource,
actor: this.currentActor
})
}
}
}
trigger()
@@ -870,6 +883,9 @@ class LayoutStoreImpl implements LayoutStore {
case 'setNodeZIndex':
this.handleSetNodeZIndex(operation as SetNodeZIndexOperation, change)
break
case 'setNodeMode':
this.handleSetNodeMode(operation as SetNodeModeOperation, change)
break
case 'createNode':
this.handleCreateNode(operation as CreateNodeOperation, change)
break
@@ -969,7 +985,12 @@ class LayoutStoreImpl implements LayoutStore {
* Initialize store with existing nodes
*/
initializeFromLiteGraph(
nodes: Array<{ id: string; pos: [number, number]; size: [number, number] }>
nodes: Array<{
id: string
pos: [number, number]
size: [number, number]
mode?: number
}>
): void {
this.ydoc.transact(() => {
this.ynodes.clear()
@@ -993,6 +1014,7 @@ class LayoutStoreImpl implements LayoutStore {
size: { width: node.size[0], height: node.size[1] },
zIndex: index,
visible: true,
mode: node.mode ?? 0, // Default to ALWAYS if not provided
bounds: {
x: node.pos[0],
y: node.pos[1],
@@ -1078,6 +1100,17 @@ class LayoutStoreImpl implements LayoutStore {
change.nodeIds.push(operation.nodeId)
}
private handleSetNodeMode(
operation: SetNodeModeOperation,
change: LayoutChange
): void {
const ynode = this.ynodes.get(operation.nodeId)
if (!ynode) return
ynode.set('mode', operation.mode)
change.nodeIds.push(operation.nodeId)
}
private handleCreateNode(
operation: CreateNodeOperation,
change: LayoutChange
@@ -1428,6 +1461,43 @@ class LayoutStoreImpl implements LayoutStore {
return Y.encodeStateAsUpdate(this.ydoc)
}
/**
* Set the execution mode for a single node.
* Applies the node's changeMode method if available and notifies the graph.
*/
setNodeMode(nodeId: NodeId, mode: number): void {
const ynode = this.ynodes.get(nodeId)
if (!ynode) return
const currentLayout = yNodeToLayout(ynode)
if (currentLayout.mode === mode) return // No change needed
this.applyOperation({
type: 'setNodeMode',
entity: 'node',
nodeId,
mode,
previousMode: currentLayout.mode,
timestamp: Date.now(),
source: this.currentSource,
actor: this.currentActor
})
}
/**
* Set the execution mode for multiple nodes.
* Applies the mode to all nodes atomically.
*/
setNodesMode(nodeIds: NodeId[], mode: number): void {
if (nodeIds.length === 0) return
// Apply mode to each node
// Note: We could create a batch operation type if needed for better performance
nodeIds.forEach((nodeId) => {
this.setNodeMode(nodeId, mode)
})
}
/**
* Batch update node bounds using Yjs transaction for atomicity.
*/

View File

@@ -48,6 +48,7 @@ export interface NodeLayout {
size: Size
zIndex: number
visible: boolean
mode: number // LGraphEventMode: 0=ALWAYS, 2=NEVER, 4=BYPASS, etc.
// Computed bounds for hit testing
bounds: Bounds
}
@@ -120,6 +121,7 @@ type OperationType =
| 'moveNode'
| 'resizeNode'
| 'setNodeZIndex'
| 'setNodeMode'
| 'createNode'
| 'deleteNode'
| 'setNodeVisibility'
@@ -157,6 +159,15 @@ export interface SetNodeZIndexOperation extends NodeOpBase {
previousZIndex: number
}
/**
* Set node mode operation
*/
export interface SetNodeModeOperation extends NodeOpBase {
type: 'setNodeMode'
mode: number
previousMode: number
}
/**
* Create node operation
*/
@@ -243,6 +254,7 @@ export type LayoutOperation =
| MoveNodeOperation
| ResizeNodeOperation
| SetNodeZIndexOperation
| SetNodeModeOperation
| CreateNodeOperation
| DeleteNodeOperation
| SetNodeVisibilityOperation

View File

@@ -62,6 +62,7 @@ describe('layoutMath utils', () => {
size: { width, height },
zIndex: 0,
visible: true,
mode: 0,
bounds: { x, y, width, height }
})

View File

@@ -10,6 +10,7 @@ export const NODE_LAYOUT_DEFAULTS: NodeLayout = {
size: { width: 100, height: 50 },
zIndex: 0,
visible: true,
mode: 0, // LGraphEventMode.ALWAYS
bounds: { x: 0, y: 0, width: 100, height: 50 }
}
@@ -20,6 +21,7 @@ export function layoutToYNode(layout: NodeLayout): NodeLayoutMap {
ynode.set('size', layout.size)
ynode.set('zIndex', layout.zIndex)
ynode.set('visible', layout.visible)
ynode.set('mode', layout.mode)
ynode.set('bounds', layout.bounds)
return ynode
}
@@ -40,6 +42,7 @@ export function yNodeToLayout(ynode: NodeLayoutMap): NodeLayout {
size: getOr(ynode, 'size', NODE_LAYOUT_DEFAULTS.size),
zIndex: getOr(ynode, 'zIndex', NODE_LAYOUT_DEFAULTS.zIndex),
visible: getOr(ynode, 'visible', NODE_LAYOUT_DEFAULTS.visible),
mode: getOr(ynode, 'mode', NODE_LAYOUT_DEFAULTS.mode),
bounds: getOr(ynode, 'bounds', NODE_LAYOUT_DEFAULTS.bounds)
}
}

View File

@@ -34,6 +34,7 @@ describe('MinimapDataSource', () => {
size: { width: 100, height: 50 },
zIndex: 0,
visible: true,
mode: 0,
bounds: { x: 0, y: 0, width: 100, height: 50 }
}
]

View File

@@ -80,6 +80,7 @@ const mockData = vi.hoisted(() => {
size: { width: 100, height: 100 },
zIndex: 1,
visible: true,
mode: 0,
bounds: {
x: 0,
y: 0,