diff --git a/src/composables/graph/useGraphNodeManager.ts b/src/composables/graph/useGraphNodeManager.ts index 33822d1c5..3bbadfb93 100644 --- a/src/composables/graph/useGraphNodeManager.ts +++ b/src/composables/graph/useGraphNodeManager.ts @@ -16,6 +16,7 @@ import type { IWidgetOptions } from '@/lib/litegraph/src/types/widgets' import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations' +import { layoutStore } from '@/renderer/core/layout/store/layoutStore' import { LayoutSource } from '@/renderer/core/layout/types' import type { NodeId } from '@/renderer/core/layout/types' import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' @@ -322,7 +323,7 @@ export function extractVueNodeData(node: LGraphNode): VueNodeData { export function useGraphNodeManager(graph: LGraph): GraphNodeManager { // Get layout mutations composable - const { createNode, deleteNode, setSource } = useLayoutMutations() + const { createNode, deleteNode, resizeNode, setSource } = useLayoutMutations() // Safe reactive data extracted from LiteGraph nodes const vueNodeData = reactive(new Map()) @@ -396,6 +397,14 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager { // Store non-reactive reference to original node nodeRefs.set(id, node) + // Chain onResize to propagate extension-driven size changes to the store + node.onResize = useChainCallback(node.onResize, (size) => { + const current = layoutStore.getNodeLayoutRef(id).value?.size + if (current?.width === size[0] && current?.height === size[1]) return + setSource(LayoutSource.Canvas) + resizeNode(id, { width: size[0], height: size[1] }) + }) + // Extract initial data for Vue (may be incomplete during graph configure) vueNodeData.set(id, extractVueNodeData(node)) diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index d602e4062..5c01e2b83 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -164,6 +164,7 @@ import { nextTick, onErrorCaptured, onMounted, + onUnmounted, ref, watch } from 'vue' @@ -186,6 +187,7 @@ import { useTelemetry } from '@/platform/telemetry' import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions' import { layoutStore } from '@/renderer/core/layout/store/layoutStore' +import { LayoutSource } from '@/renderer/core/layout/types' import SlotConnectionDot from '@/renderer/extensions/vueNodes/components/SlotConnectionDot.vue' import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers' import { useNodePointerInteractions } from '@/renderer/extensions/vueNodes/composables/useNodePointerInteractions' @@ -343,6 +345,25 @@ function initSizeStyles() { el.style.setProperty(`--node-height${suffix}`, `${height}px`) } +// React to extension-driven resize operations (e.g., KJNodes spline editor) +const unsubscribeResize = layoutStore.onChange((change) => { + if (change.source !== LayoutSource.Canvas) return + if (change.operation.type !== 'resizeNode') return + if (!change.nodeIds.includes(nodeData.id)) return + if (isCollapsed.value) return + + const el = nodeContainerRef.value + if (!el) return + + const newSize = size.value + el.style.setProperty('--node-width', `${newSize.width}px`) + el.style.setProperty('--node-height', `${newSize.height}px`) +}) + +onUnmounted(() => { + unsubscribeResize() +}) + const baseResizeHandleClasses = 'absolute h-3 w-3 opacity-0 pointer-events-auto focus-visible:outline focus-visible:outline-2 focus-visible:outline-white/40'