Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
f42a4dd
[feat] Add core Vue widget infrastructure
christian-byrne Jun 24, 2025
2dcfe84
[feat] Add Vue widget registry system
christian-byrne Jun 24, 2025
06d0a63
[feat] Add Vue input widgets
christian-byrne Jun 24, 2025
ac60d1c
[feat] Add Vue selection widgets
christian-byrne Jun 24, 2025
9c4d782
[feat] Add Vue visual widgets
christian-byrne Jun 24, 2025
6e04cb7
[feat] Add Vue action widgets
christian-byrne Jun 24, 2025
19084e2
[feat] TransformPane - Viewport synchronization layer for Vue nodes (…
christian-byrne Aug 6, 2025
9db96f2
Update locales [skip ci]
invalid-email-address Aug 7, 2025
fc6943c
Fix TransformPane pos/size (#4826)
christian-byrne Aug 8, 2025
ac17752
Update locales [skip ci]
invalid-email-address Aug 8, 2025
301355e
refactor(litegraph): decouple render-time state from models for rerou…
benceruleanlu Aug 8, 2025
4171219
Revert "refactor(litegraph): decouple render-time state from models f…
benceruleanlu Aug 8, 2025
2b9a9e2
test(ci): skip transformPerformance suite on CI (#4843)
benceruleanlu Aug 9, 2025
8df41ab
Add vue node feature flag (#4927)
benceruleanlu Aug 12, 2025
c773230
feat: Implement CRDT-based layout system for Vue nodes (#4959)
christian-byrne Aug 17, 2025
889d136
[chore] Extract link rendering out of LGraphCanvas (#4994)
benceruleanlu Aug 18, 2025
0dd4ff2
refactor: Reorganize layout system into new renderer architecture (#5…
christian-byrne Aug 18, 2025
bfcbcf4
[refactor] Reorganize Vue nodes to domain-driven design architecture …
christian-byrne Aug 18, 2025
d1ed5ec
fix: Initialize Vue node manager when first node is added to empty gr…
christian-byrne Aug 19, 2025
b2a828d
[bugfix] Fix Vue node import path after refactoring
christian-byrne Aug 19, 2025
934c650
Remove layout logging noise from console (#5101)
christian-byrne Aug 19, 2025
5a74c01
remove logging from vue node layouting modules (#5111)
christian-byrne Aug 19, 2025
5171dec
feat: Add slot registration and spatial indexing for hit detection
benceruleanlu Aug 20, 2025
ba1fa1b
Revert "feat: Add slot registration and spatial indexing for hit dete…
benceruleanlu Aug 20, 2025
4a7c955
[bugfix] Fix link center dot hit detection when marker is disabled (#…
benceruleanlu Aug 20, 2025
1447b15
[bugfix] Hide center dot when dragging links (#5133)
benceruleanlu Aug 21, 2025
57db10f
feat: v3 style of node body (#5169)
LittleSound Aug 23, 2025
3982f29
Update lockfile after rebase (#5254)
benceruleanlu Aug 30, 2025
0830959
Fix lodash import (#5269)
benceruleanlu Aug 30, 2025
2a5e0d2
Decouple link and slot hit-testing out of Litegraph (#5134)
benceruleanlu Sep 1, 2025
62096d4
chore: Empty commit to trigger CI checks
benceruleanlu Sep 1, 2025
da042ae
[refactor] Remove unused legacy mutation types from layout system (#5…
christian-byrne Sep 1, 2025
3ce3b67
feat: localization fields (#5318)
LittleSound Sep 3, 2025
c6fc8e6
fix: remove clipping by removing unnecessary css contain (#5327)
simula-r Sep 4, 2025
969c8e6
[bugfix] Remove placeholder IMAGE widget to restore previous function…
benceruleanlu Sep 4, 2025
f83801e
- Convert class-based LayoutMutations to useLayoutMutations() composa…
christian-byrne Sep 4, 2025
1dbbf20
feat: widget styles for V3 UI (#5320)
LittleSound Sep 4, 2025
32cffa6
refactor: v3 ui slots connection dots (#5316)
LittleSound Sep 4, 2025
8a10387
add explicit typing on component IDs (#5352)
christian-byrne Sep 4, 2025
c30f5a4
Remove IMAGE widget cont. (#5355)
benceruleanlu Sep 4, 2025
6a3c075
Removes node's dependency on LGraph for access to layout mutations co…
christian-byrne Sep 4, 2025
2a64f53
Merge remote-tracking branch 'origin/main' into vue-nodes-migration
benceruleanlu Sep 4, 2025
f99c9de
[fix] Disable link markers on dragged connections (#5358)
benceruleanlu Sep 4, 2025
1480dd7
[bugfix] Fix NodeHeader test workflow path (#5359)
benceruleanlu Sep 4, 2025
2425f65
[Vue Nodes] Fix Node Header Tests (#5360)
benceruleanlu Sep 4, 2025
0f5315f
Update test expectations [skip ci]
invalid-email-address Sep 4, 2025
7b7f9bb
remove crdt ADR (moved to separate PR)
christian-byrne Sep 5, 2025
7130794
update adr README
christian-byrne Sep 5, 2025
73a1fee
removed unused IMAGE widget enum value
christian-byrne Sep 5, 2025
b0f2a1d
remove all unused (knip pass)
christian-byrne Sep 5, 2025
2f512b8
remove debug overlay panel
christian-byrne Sep 5, 2025
4fc8984
simplify unit tests
christian-byrne Sep 5, 2025
7149af6
change name "transformPaneEnabled" => "isVueNodesEnabled"
christian-byrne Sep 5, 2025
0b94158
remove debug viewport visualizer
christian-byrne Sep 5, 2025
3e5effe
remove debug viewport visualizer prop
christian-byrne Sep 5, 2025
9ab075f
remove outdated README
christian-byrne Sep 5, 2025
1e30756
skip all vue node operations if feature is turned off
christian-byrne Sep 5, 2025
e8dae57
remove debug logging and setting
christian-byrne Sep 5, 2025
f6051f6
remove event forwarding hack. todo: add link moving in vue
christian-byrne Sep 5, 2025
df36693
cleanup comments
christian-byrne Sep 5, 2025
358d98e
cleanup comments
christian-byrne Sep 5, 2025
b7fd1f4
add missing translations
christian-byrne Sep 5, 2025
0aed837
use camelCase for all non-component files
christian-byrne Sep 5, 2025
7d8bdcb
remove debug viewport test
christian-byrne Sep 5, 2025
6eeba70
- Fix memory leaks in node deletion (#5345)
christian-byrne Sep 5, 2025
817f4d0
remove redundant comment
christian-byrne Sep 5, 2025
07b7ed9
use camelcase for layoutStore filename
christian-byrne Sep 5, 2025
85fa2f4
removed unused type guards
christian-byrne Sep 5, 2025
8e098fc
simplify widget registration
christian-byrne Sep 5, 2025
896e44f
move back test that was mistakenly moved
christian-byrne Sep 5, 2025
f6ae1b6
remove unused typeguards
christian-byrne Sep 5, 2025
c52c798
removed unused node def type guards
christian-byrne Sep 5, 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
Prev Previous commit
Next Next commit
Add vue node feature flag (#4927)
  • Loading branch information
benceruleanlu committed Aug 29, 2025
commit 8df41ab040417e7be40f4302ef8a918f213917cf
4 changes: 0 additions & 4 deletions src/components/graph/DomWidgets.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { computed } from 'vue'

import DomWidget from '@/components/graph/widgets/DomWidget.vue'
import { useChainCallback } from '@/composables/functional/useChainCallback'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { useDomWidgetStore } from '@/stores/domWidgetStore'
import { useCanvasStore } from '@/stores/graphStore'

Expand All @@ -28,9 +27,6 @@ const updateWidgets = () => {
const lgCanvas = canvasStore.canvas
if (!lgCanvas) return

// Skip updating DOM widgets when Vue nodes mode is enabled
if (LiteGraph.vueNodesMode) return

const lowQuality = lgCanvas.low_quality
const currentGraph = lgCanvas.graph

Expand Down
64 changes: 45 additions & 19 deletions src/components/graph/GraphCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@

<!-- Debug Panel (Development Only) -->
<VueNodeDebugPanel
v-if="debugPanelVisible"
v-model:debug-override-vue-nodes="debugOverrideVueNodes"
v-model:show-performance-overlay="showPerformanceOverlay"
:canvas-viewport="canvasViewport"
Expand All @@ -86,8 +87,11 @@
canvasStore.canvas to be initialized. -->
<template v-if="comfyAppReady">
<TitleEditor />
<SelectionToolbox v-if="selectionToolboxEnabled" />
<DomWidgets />
<SelectionOverlay v-if="selectionToolboxEnabled">
<SelectionToolbox />
</SelectionOverlay>
<!-- Render legacy DOM widgets only when Vue nodes are disabled -->
<DomWidgets v-if="!shouldRenderVueNodes" />
</template>
</template>

Expand All @@ -110,6 +114,7 @@ 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 @@ -189,7 +194,14 @@ const minimapEnabled = computed(() => settingStore.get('Comfy.Minimap.Visible'))
const { shouldRenderVueNodes, isDevModeEnabled } = useFeatureFlags()

// TransformPane enabled when Vue nodes are enabled OR debug override
const debugOverrideVueNodes = ref(true) // Default to true for development
const debugOverrideVueNodes = ref(false)
// Persist debug panel visibility in settings so core commands can toggle it
const debugPanelVisible = computed({
get: () => settingStore.get('Comfy.VueNodes.DebugPanel.Visible') ?? false,
set: (v: boolean) => {
void settingStore.set('Comfy.VueNodes.DebugPanel.Visible', v)
}
})
const transformPaneEnabled = computed(
() => shouldRenderVueNodes.value || debugOverrideVueNodes.value
)
Expand Down Expand Up @@ -269,6 +281,7 @@ watch(canvasRef, () => {

// Vue node lifecycle management - initialize after graph is ready
let nodeManager: ReturnType<typeof useGraphNodeManager> | null = null
let cleanupNodeManager: (() => void) | 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 All @@ -291,31 +304,49 @@ const performanceMetrics = reactive({
const nodeDataTrigger = ref(0)

const initializeNodeManager = () => {
if (!comfyApp.graph || nodeManager) {
return
}

if (!comfyApp.graph || nodeManager) return
nodeManager = useGraphNodeManager(comfyApp.graph)

cleanupNodeManager = nodeManager.cleanup
// Use the manager's reactive maps directly
vueNodeData.value = nodeManager.vueNodeData
nodeState.value = nodeManager.nodeState
nodePositions.value = nodeManager.nodePositions
nodeSizes.value = nodeManager.nodeSizes

detectChangesInRAF = nodeManager.detectChangesInRAF
Object.assign(performanceMetrics, nodeManager.performanceMetrics)

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

// Watch for graph availability
const disposeNodeManager = () => {
if (!nodeManager) return
try {
cleanupNodeManager?.()
} catch {
/* empty */
}
nodeManager = null
cleanupNodeManager = null
// Reset reactive maps to inert defaults
vueNodeData.value = new Map()
nodeState.value = new Map()
nodePositions.value = new Map()
nodeSizes.value = new Map()
// Reset metrics
performanceMetrics.frameTime = 0
performanceMetrics.updateTime = 0
performanceMetrics.nodeCount = 0
performanceMetrics.culledCount = 0
}

// Watch for transformPaneEnabled to gate the node manager lifecycle
watch(
() => comfyApp.graph,
(graph) => {
if (graph) {
() => transformPaneEnabled.value && Boolean(comfyApp.graph),
(enabled) => {
if (enabled) {
initializeNodeManager()
} else {
disposeNodeManager()
}
},
{ immediate: true }
Expand Down Expand Up @@ -696,11 +727,6 @@ onMounted(async () => {

comfyAppReady.value = true

// Initialize node manager after setup is complete
if (comfyApp.graph) {
initializeNodeManager()
}

comfyApp.canvas.onSelectionChange = useChainCallback(
comfyApp.canvas.onSelectionChange,
() => canvasStore.updateSelectedItems()
Expand Down
27 changes: 27 additions & 0 deletions src/composables/useCoreCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,33 @@ export function useCoreCommands(): ComfyCommand[] {
app.canvas.setDirty(true, true)
}
},
{
id: 'Experimental.ToggleVueNodes',
label: () =>
`Experimental: ${
useSettingStore().get('Comfy.VueNodes.Enabled') ? 'Disable' : 'Enable'
} Vue Nodes`,
function: async () => {
const settingStore = useSettingStore()
const current = settingStore.get('Comfy.VueNodes.Enabled') ?? false
await settingStore.set('Comfy.VueNodes.Enabled', !current)
}
},
{
id: 'Experimental.ToggleVueNodeDebugPanel',
label: () =>
`Experimental: ${
useSettingStore().get('Comfy.VueNodes.DebugPanel.Visible')
? 'Hide'
: 'Show'
} Vue Node Debug Panel`,
function: async () => {
const settingStore = useSettingStore()
const current =
settingStore.get('Comfy.VueNodes.DebugPanel.Visible') ?? false
await settingStore.set('Comfy.VueNodes.DebugPanel.Visible', !current)
}
},
{
id: 'Comfy.Canvas.FitView',
icon: 'pi pi-expand',
Expand Down
18 changes: 3 additions & 15 deletions src/composables/useFeatureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,10 @@ export const useFeatureFlags = () => {
*/
const isVueNodesEnabled = computed(() => {
try {
return settingStore.get('Comfy.VueNodes.Enabled' as any) ?? true // Default to true for development
// Off by default: ensure Vue nodes are disabled unless explicitly enabled
return settingStore.get('Comfy.VueNodes.Enabled') ?? false
} catch {
return true // Default to true for development
}
})

/**
* Enable Vue widget rendering within Vue nodes
* When disabled, Vue nodes render without widgets (structure only)
*/
const isVueWidgetsEnabled = computed(() => {
try {
return settingStore.get('Comfy.VueNodes.Widgets' as any) ?? true
} catch {
return true
return false
}
})

Expand Down Expand Up @@ -74,7 +63,6 @@ export const useFeatureFlags = () => {

return {
isVueNodesEnabled,
isVueWidgetsEnabled,
isDevModeEnabled,
shouldRenderVueNodes,
syncVueNodesFlag
Expand Down
29 changes: 14 additions & 15 deletions src/constants/coreSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,24 +930,23 @@ export const CORE_SETTINGS: SettingParams[] = [
defaultValue: 0
},

// Vue Node System Settings
/**
* Vue Node System Settings
*/
{
id: 'Comfy.VueNodes.Enabled' as any,
category: ['Comfy', 'Vue Nodes'],
experimental: true,
name: 'Enable Vue node rendering',
id: 'Comfy.VueNodes.Enabled',
name: 'Enable Vue node rendering (hidden)',
type: 'hidden',
tooltip:
'Render nodes as Vue components instead of canvas elements. Experimental feature.',
type: 'boolean',
defaultValue: false
'Render nodes as Vue components instead of canvas. Hidden; toggle via Experimental keybinding.',
defaultValue: false,
experimental: true
},
{
id: 'Comfy.VueNodes.Widgets' as any,
category: ['Comfy', 'Vue Nodes', 'Widgets'],
experimental: true,
name: 'Enable Vue widgets',
tooltip: 'Render widgets as Vue components within Vue nodes.',
type: 'boolean',
defaultValue: true
id: 'Comfy.VueNodes.DebugPanel.Visible',
name: 'Vue Nodes Debug Panel Visible (hidden)',
type: 'hidden',
defaultValue: false,
experimental: true
}
]
13 changes: 13 additions & 0 deletions src/lib/litegraph/src/LGraphCanvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4999,6 +4999,19 @@ export class LGraphCanvas
drawNode(node: LGraphNode, ctx: CanvasRenderingContext2D): void {
this.current_node = node

// When Vue nodes mode is enabled, LiteGraph should not draw node chrome or widgets.
// We still need to keep slot metrics and layout in sync for hit-testing and links.
// Interaction system changes coming later, chances are vue nodes mode will be mostly broken on land
if (LiteGraph.vueNodesMode) {
// Prepare concrete slots and compute layout measures without rendering visuals.
node._setConcreteSlots()
if (!node.collapsed) {
node.arrange()
}
// Skip all node body/widget/title rendering. Vue overlay handles visuals.
return
}

const color = node.renderingColor
const bgcolor = node.renderingBgColor

Expand Down
2 changes: 2 additions & 0 deletions src/schemas/apiSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ const zSettings = z.object({
'Comfy.Minimap.RenderBypassState': z.boolean(),
'Comfy.Minimap.RenderErrorState': z.boolean(),
'Comfy.Canvas.NavigationMode': z.string(),
'Comfy.VueNodes.Enabled': z.boolean(),
'Comfy.VueNodes.DebugPanel.Visible': z.boolean(),
'Comfy-Desktop.AutoUpdate': z.boolean(),
'Comfy-Desktop.SendStatistics': z.boolean(),
'Comfy-Desktop.WindowStyle': z.string(),
Expand Down