From 997f14ee5ca64b190a85d57d07ce458dcd51d926 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Sat, 6 Sep 2025 01:49:01 -0700 Subject: [PATCH] Implement proper invalidation on switch (#5383) --- src/components/graph/GraphCanvas.vue | 51 +++++++++++++------ src/renderer/core/layout/store/layoutStore.ts | 9 ++++ .../core/layout/sync/useSlotLayoutSync.ts | 15 +++--- src/renderer/core/layout/types.ts | 1 + 4 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 467da7e5d..5b893b8d2 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -183,6 +183,7 @@ let cleanupNodeManager: (() => void) | null = null // Slot layout sync management let slotSync: ReturnType | null = null +let slotSyncStarted = false let linkSync: ReturnType | null = null const vueNodeData = ref>(new Map()) const nodeState = ref>(new Map()) @@ -240,12 +241,6 @@ const initializeNodeManager = () => { const { startSync } = useLayoutSync() startSync(canvasStore.canvas) - // Initialize slot layout sync for hit detection - slotSync = useSlotLayoutSync() - if (canvasStore.canvas) { - slotSync.start(canvasStore.canvas as LGraphCanvas) - } - // Initialize link layout sync for event-driven updates linkSync = useLinkLayoutSync() if (canvasStore.canvas) { @@ -266,12 +261,6 @@ const disposeNodeManagerAndSyncs = () => { nodeManager = null cleanupNodeManager = null - // Clean up slot layout sync - if (slotSync) { - slotSync.stop() - slotSync = null - } - // Clean up link layout sync if (linkSync) { linkSync.stop() @@ -298,6 +287,37 @@ watch( { immediate: true } ) +// Consolidated watch for slot layout sync management +watch( + [() => canvasStore.canvas, () => isVueNodesEnabled.value], + ([canvas, vueMode], [, oldVueMode]) => { + const modeChanged = vueMode !== oldVueMode + + // Clear stale slot layouts when switching modes + if (modeChanged) { + layoutStore.clearAllSlotLayouts() + } + + // Switching to Vue + if (vueMode && slotSyncStarted) { + slotSync?.stop() + slotSyncStarted = false + } + + // Switching to LG + const shouldRun = Boolean(canvas?.graph) && !vueMode + if (shouldRun && !slotSyncStarted && canvas) { + // Initialize slot sync if not already created + if (!slotSync) { + slotSync = useSlotLayoutSync() + } + const started = slotSync.attemptStart(canvas as LGraphCanvas) + slotSyncStarted = started + } + }, + { immediate: true } +) + // Transform state for viewport culling const { syncWithCanvas } = useTransformState() @@ -723,10 +743,11 @@ onUnmounted(() => { nodeManager.cleanup() nodeManager = null } - if (slotSync) { - slotSync.stop() - slotSync = null + if (slotSyncStarted) { + slotSync?.stop() + slotSyncStarted = false } + slotSync = null if (linkSync) { linkSync.stop() linkSync = null diff --git a/src/renderer/core/layout/store/layoutStore.ts b/src/renderer/core/layout/store/layoutStore.ts index 20b862b46..997254d38 100644 --- a/src/renderer/core/layout/store/layoutStore.ts +++ b/src/renderer/core/layout/store/layoutStore.ts @@ -410,6 +410,15 @@ class LayoutStoreImpl implements LayoutStore { } } + /** + * Clear all slot layouts and their spatial index (O(1) operations) + * Used when switching rendering modes (Vue ↔ LiteGraph) + */ + clearAllSlotLayouts(): void { + this.slotLayouts.clear() + this.slotSpatialIndex.clear() + } + /** * Update reroute layout data */ diff --git a/src/renderer/core/layout/sync/useSlotLayoutSync.ts b/src/renderer/core/layout/sync/useSlotLayoutSync.ts index faaebb21d..05ea87508 100644 --- a/src/renderer/core/layout/sync/useSlotLayoutSync.ts +++ b/src/renderer/core/layout/sync/useSlotLayoutSync.ts @@ -16,7 +16,7 @@ import { layoutStore } from '@/renderer/core/layout/store/layoutStore' * Compute and register slot layouts for a node * @param node LiteGraph node to process */ -function computeAndRegisterSlots(node: LGraphNode): void { +export function computeAndRegisterSlots(node: LGraphNode): void { const nodeId = String(node.id) const nodeLayout = layoutStore.getNodeLayoutRef(nodeId).value @@ -57,17 +57,18 @@ export function useSlotLayoutSync() { let restoreHandlers: (() => void) | null = null /** - * Start slot layout sync with full event-driven functionality + * Attempt to start slot layout sync with full event-driven functionality * @param canvas LiteGraph canvas instance + * @returns true if sync was actually started, false if early-returned */ - function start(canvas: LGraphCanvas): void { + function attemptStart(canvas: LGraphCanvas): boolean { // When Vue nodes are enabled, slot DOM registers exact positions. // Skip calculated registration to avoid conflicts. if (LiteGraph.vueNodesMode) { - return + return false } const graph = canvas?.graph - if (!graph) return + if (!graph) return false // Initial registration for all nodes in the current graph for (const node of graph.nodes) { @@ -135,6 +136,8 @@ export function useSlotLayoutSync() { graph.onTrigger = origTrigger || undefined graph.onAfterChange = origAfterChange || undefined } + + return true } /** @@ -157,7 +160,7 @@ export function useSlotLayoutSync() { }) return { - start, + attemptStart, stop } } diff --git a/src/renderer/core/layout/types.ts b/src/renderer/core/layout/types.ts index 8ff25ea25..c672f3e94 100644 --- a/src/renderer/core/layout/types.ts +++ b/src/renderer/core/layout/types.ts @@ -297,6 +297,7 @@ export interface LayoutStore { deleteSlotLayout(key: string): void deleteNodeSlotLayouts(nodeId: NodeId): void deleteRerouteLayout(rerouteId: RerouteId): void + clearAllSlotLayouts(): void // Get layout data getLinkLayout(linkId: LinkId): LinkLayout | null