Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
59 changes: 41 additions & 18 deletions src/components/graph/GraphCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,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 @@ -92,7 +93,8 @@
<SelectionOverlay v-if="selectionToolboxEnabled">
<SelectionToolbox />
</SelectionOverlay>
<DomWidgets />
<!-- Render legacy DOM widgets only when Vue nodes are disabled -->
<DomWidgets v-if="!shouldRenderVueNodes" />
</template>
</template>

Expand Down Expand Up @@ -194,7 +196,14 @@ const minimap = useMinimap()
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 @@ -274,6 +283,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 @@ -296,31 +306,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 @@ -700,11 +728,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 @@ -256,6 +256,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 @@ -893,24 +893,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 @@ -4947,6 +4947,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.Node.AllowImageSizeDraw': z.boolean(),
'Comfy.Minimap.Visible': 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
Loading