Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9228b94
[feat] TransformPane - Viewport synchronization layer for Vue nodes (…
christian-byrne Aug 6, 2025
b8ca5d5
Update locales [skip ci]
invalid-email-address Aug 7, 2025
39d6f5c
Update locales [skip ci]
invalid-email-address Aug 8, 2025
3a29a20
Add vue node feature flag (#4927)
benceruleanlu Aug 12, 2025
6f07f6d
feat: Implement CRDT-based layout system for Vue nodes (#4959)
christian-byrne Aug 17, 2025
7dbbb75
[chore] Extract link rendering out of LGraphCanvas (#4994)
benceruleanlu Aug 18, 2025
b7b9bb3
feat: Add slot registration and spatial indexing for hit detection
benceruleanlu Aug 20, 2025
8206534
Revert "feat: Add slot registration and spatial indexing for hit dete…
benceruleanlu Aug 20, 2025
3dd3ea4
feat: Add slot registration and spatial indexing for hit detection
benceruleanlu Aug 20, 2025
d7dd3a7
relocate slot update to layoutstore
benceruleanlu Aug 21, 2025
db287d1
Revert "relocate slot update to layoutstore"
benceruleanlu Aug 21, 2025
3640366
add useSlotLayoutSync
benceruleanlu Aug 21, 2025
2123795
feat: Extend Layout Store with CRDT support for links and reroutes
benceruleanlu Aug 21, 2025
2a2e623
Scuffed diff, change to dirty later
benceruleanlu Aug 21, 2025
612be1d
Fix reroute move desync
benceruleanlu Aug 21, 2025
7f9f079
Terrible reroute fixes
benceruleanlu Aug 21, 2025
023af03
Use LinkId for LinkLayout
benceruleanlu Aug 22, 2025
5a8d41d
refactor: Remove unused duplicate layout type files
benceruleanlu Aug 22, 2025
a58162e
refactor: Extract layout source strings into LayoutSource enum
benceruleanlu Aug 22, 2025
afddeaa
refactor: Unify CRDT layout operations under type-safe entity bases
benceruleanlu Aug 22, 2025
9d5f983
Fix initial link seeding
benceruleanlu Aug 22, 2025
c11dcd2
fix: Fix reroute hit detection and type consistency issues
benceruleanlu Aug 22, 2025
c20e2e6
Add debug logs
benceruleanlu Aug 23, 2025
ec9620d
Add missing reroute path
benceruleanlu Aug 23, 2025
736fd35
cleanup
benceruleanlu Aug 25, 2025
77b7d05
feat: Implement event-driven link layout sync
benceruleanlu Aug 25, 2025
a5fc834
feat: Implement DOM-based slot registration with unified position system
benceruleanlu Aug 27, 2025
3b5a3d7
Remove unused files
benceruleanlu Aug 29, 2025
7d7e967
Remove duplicated markdown file
benceruleanlu Aug 29, 2025
350faad
Remove duplicated files and address knip concerns
benceruleanlu Aug 29, 2025
745d8d7
Merge branch 'vue-nodes-migration' into bl-move-hit-detection-v2
benceruleanlu Sep 1, 2025
6aac753
Remove outdated test
benceruleanlu Sep 1, 2025
5771e5d
warning comment
benceruleanlu Sep 1, 2025
a5471aa
Update test snapshots
benceruleanlu Sep 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 67 additions & 7 deletions src/components/graph/GraphCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@
canvasStore.canvas to be initialized. -->
<template v-if="comfyAppReady">
<TitleEditor />
<SelectionOverlay v-if="selectionToolboxEnabled">
<SelectionToolbox />
</SelectionOverlay>
<SelectionToolbox v-if="selectionToolboxEnabled" />
<!-- Render legacy DOM widgets only when Vue nodes are disabled -->
<DomWidgets v-if="!shouldRenderVueNodes" />
</template>
Expand All @@ -114,7 +112,6 @@ import DomWidgets from '@/components/graph/DomWidgets.vue'
import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue'
import MiniMap from '@/components/graph/MiniMap.vue'
import NodeTooltip from '@/components/graph/NodeTooltip.vue'
import SelectionOverlay from '@/components/graph/SelectionOverlay.vue'
import SelectionToolbox from '@/components/graph/SelectionToolbox.vue'
import TitleEditor from '@/components/graph/TitleEditor.vue'
import TransformPane from '@/components/graph/TransformPane.vue'
Expand Down Expand Up @@ -145,6 +142,9 @@ import type { LGraphCanvas, LGraphNode } from '@/lib/litegraph/src/litegraph'
import { layoutStore } from '@/renderer/core/layout/store/LayoutStore'
import { useLayout } from '@/renderer/core/layout/sync/useLayout'
import { useLayoutSync } from '@/renderer/core/layout/sync/useLayoutSync'
import { useLinkLayoutSync } from '@/renderer/core/layout/sync/useLinkLayoutSync'
import { useSlotLayoutSync } from '@/renderer/core/layout/sync/useSlotLayoutSync'
import { LayoutSource } from '@/renderer/core/layout/types'
import VueGraphNode from '@/renderer/extensions/vueNodes/components/LGraphNode.vue'
import { UnauthorizedError, api } from '@/scripts/api'
import { app as comfyApp } from '@/scripts/app'
Expand Down Expand Up @@ -286,6 +286,10 @@ watch(canvasRef, () => {
// Vue node lifecycle management - initialize after graph is ready
let nodeManager: ReturnType<typeof useGraphNodeManager> | null = null
let cleanupNodeManager: (() => void) | null = null

// Slot layout sync management
let slotSync: ReturnType<typeof useSlotLayoutSync> | null = null
let linkSync: ReturnType<typeof useLinkLayoutSync> | null = null
const vueNodeData = ref<ReadonlyMap<string, VueNodeData>>(new Map())
const nodeState = ref<ReadonlyMap<string, NodeState>>(new Map())
const nodePositions = ref<ReadonlyMap<string, { x: number; y: number }>>(
Expand Down Expand Up @@ -327,15 +331,46 @@ const initializeNodeManager = () => {
}))
layoutStore.initializeFromLiteGraph(nodes)

// Seed reroutes into the Layout Store so hit-testing uses the new path
for (const reroute of comfyApp.graph.reroutes.values()) {
const [x, y] = reroute.pos
const parent = reroute.parentId ?? undefined
const linkIds = Array.from(reroute.linkIds)
layoutMutations.createReroute(reroute.id, { x, y }, parent, linkIds)
}

// Seed existing links into the Layout Store (topology only)
for (const link of comfyApp.graph._links.values()) {
layoutMutations.createLink(
link.id,
link.origin_id,
link.origin_slot,
link.target_id,
link.target_slot
)
}

// Initialize layout sync (one-way: Layout Store → LiteGraph)
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) {
linkSync.start(canvasStore.canvas as LGraphCanvas)
}

// Force computed properties to re-evaluate
nodeDataTrigger.value++
}

const disposeNodeManager = () => {
const disposeNodeManagerAndSyncs = () => {
if (!nodeManager) return
try {
cleanupNodeManager?.()
Expand All @@ -344,6 +379,19 @@ const disposeNodeManager = () => {
}
nodeManager = null
cleanupNodeManager = null

// Clean up slot layout sync
if (slotSync) {
slotSync.stop()
slotSync = null
}

// Clean up link layout sync
if (linkSync) {
linkSync.stop()
linkSync = null
}

// Reset reactive maps to inert defaults
vueNodeData.value = new Map()
nodeState.value = new Map()
Expand All @@ -363,7 +411,7 @@ watch(
if (enabled) {
initializeNodeManager()
} else {
disposeNodeManager()
disposeNodeManagerAndSyncs()
}
},
{ immediate: true }
Expand Down Expand Up @@ -512,7 +560,7 @@ const handleNodeSelect = (event: PointerEvent, nodeData: VueNodeData) => {
// Bring node to front when clicked (similar to LiteGraph behavior)
// Skip if node is pinned
if (!node.flags?.pinned) {
layoutMutations.setSource('vue')
layoutMutations.setSource(LayoutSource.Vue)
layoutMutations.bringNodeToFront(nodeData.id)
}
node.selected = true
Expand Down Expand Up @@ -830,5 +878,17 @@ onUnmounted(() => {
nodeManager.cleanup()
nodeManager = null
}

// Clean up slot layout sync
if (slotSync) {
slotSync.stop()
slotSync = null
}

// Clean up link layout sync
if (linkSync) {
linkSync.stop()
linkSync = null
}
})
</script>
Loading
Loading