mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 15:10:06 +00:00
Add vue node feature flag (#4927)
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
@@ -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
|
||||
)
|
||||
@@ -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 }>>(
|
||||
@@ -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 }
|
||||
@@ -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()
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -74,7 +63,6 @@ export const useFeatureFlags = () => {
|
||||
|
||||
return {
|
||||
isVueNodesEnabled,
|
||||
isVueWidgetsEnabled,
|
||||
isDevModeEnabled,
|
||||
shouldRenderVueNodes,
|
||||
syncVueNodesFlag
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user