mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 14:27:40 +00:00
## Summary
Backport of #6950 onto core/1.32.
- cherry-picked 4b87b1fdc
- resolved conflicts by taking the upstream PNG expectations and
removing LOD bits from WidgetMarkdown while preserving cloud-specific
padding
## Testing
- pnpm typecheck
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6981-backport-core-1-32-fix-remove-LOD-from-vue-nodes-2b86d73d365081ac95fddfb272a0e105)
by [Unito](https://www.unito.io)
Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
183 lines
5.6 KiB
TypeScript
183 lines
5.6 KiB
TypeScript
import { createSharedComposable, whenever } from '@vueuse/core'
|
|
import { shallowRef, watch } from 'vue'
|
|
|
|
import { useGraphNodeManager } from '@/composables/graph/useGraphNodeManager'
|
|
import type { GraphNodeManager } from '@/composables/graph/useGraphNodeManager'
|
|
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
|
import { useVueNodesMigrationDismissed } from '@/composables/useVueNodesMigrationDismissed'
|
|
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
|
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
|
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
|
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
|
import { useLayoutSync } from '@/renderer/core/layout/sync/useLayoutSync'
|
|
import { removeNodeTitleHeight } from '@/renderer/core/layout/utils/nodeSizeUtil'
|
|
import { ensureCorrectLayoutScale } from '@/renderer/extensions/vueNodes/layout/ensureCorrectLayoutScale'
|
|
import { app as comfyApp } from '@/scripts/app'
|
|
import { useToastStore } from '@/platform/updates/common/toastStore'
|
|
|
|
function useVueNodeLifecycleIndividual() {
|
|
const canvasStore = useCanvasStore()
|
|
const layoutMutations = useLayoutMutations()
|
|
const { shouldRenderVueNodes } = useVueFeatureFlags()
|
|
const nodeManager = shallowRef<GraphNodeManager | null>(null)
|
|
const { startSync } = useLayoutSync()
|
|
|
|
const isVueNodeToastDismissed = useVueNodesMigrationDismissed()
|
|
|
|
let hasShownMigrationToast = false
|
|
|
|
const initializeNodeManager = () => {
|
|
// Use canvas graph if available (handles subgraph contexts), fallback to app graph
|
|
const activeGraph = comfyApp.canvas?.graph
|
|
if (!activeGraph || nodeManager.value) return
|
|
|
|
// Initialize the core node manager
|
|
const manager = useGraphNodeManager(activeGraph)
|
|
nodeManager.value = manager
|
|
|
|
// Initialize layout system with existing nodes from active graph
|
|
const nodes = activeGraph._nodes.map((node: LGraphNode) => ({
|
|
id: node.id.toString(),
|
|
pos: [node.pos[0], node.pos[1]] as [number, number],
|
|
size: [node.size[0], removeNodeTitleHeight(node.size[1])] as [
|
|
number,
|
|
number
|
|
]
|
|
}))
|
|
layoutStore.initializeFromLiteGraph(nodes)
|
|
|
|
// Seed reroutes into the Layout Store so hit-testing uses the new path
|
|
for (const reroute of activeGraph.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 activeGraph._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)
|
|
startSync(canvasStore.canvas)
|
|
}
|
|
|
|
const disposeNodeManagerAndSyncs = () => {
|
|
if (!nodeManager.value) return
|
|
|
|
try {
|
|
nodeManager.value.cleanup()
|
|
} catch {
|
|
/* empty */
|
|
}
|
|
nodeManager.value = null
|
|
}
|
|
|
|
// Watch for Vue nodes enabled state changes
|
|
watch(
|
|
() => shouldRenderVueNodes.value && Boolean(comfyApp.canvas?.graph),
|
|
(enabled, wasEnabled) => {
|
|
if (enabled) {
|
|
initializeNodeManager()
|
|
ensureCorrectLayoutScale(
|
|
comfyApp.canvas?.graph?.extra.workflowRendererVersion
|
|
)
|
|
if (
|
|
wasEnabled === false &&
|
|
!isVueNodeToastDismissed.value &&
|
|
!hasShownMigrationToast
|
|
) {
|
|
hasShownMigrationToast = true
|
|
useToastStore().add({
|
|
group: 'vue-nodes-migration',
|
|
severity: 'info',
|
|
life: 0
|
|
})
|
|
}
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
whenever(
|
|
() => !shouldRenderVueNodes.value,
|
|
() => {
|
|
ensureCorrectLayoutScale(
|
|
comfyApp.canvas?.graph?.extra.workflowRendererVersion
|
|
)
|
|
disposeNodeManagerAndSyncs()
|
|
comfyApp.canvas?.setDirty(true, true)
|
|
}
|
|
)
|
|
|
|
// Consolidated watch for slot layout sync management
|
|
watch(
|
|
() => shouldRenderVueNodes.value,
|
|
(vueMode, oldVueMode) => {
|
|
const modeChanged = vueMode !== oldVueMode
|
|
|
|
// Clear stale slot layouts when switching modes
|
|
if (modeChanged) {
|
|
layoutStore.clearAllSlotLayouts()
|
|
}
|
|
},
|
|
{ immediate: true, flush: 'sync' }
|
|
)
|
|
|
|
// Handle case where Vue nodes are enabled but graph starts empty
|
|
const setupEmptyGraphListener = () => {
|
|
const activeGraph = comfyApp.canvas?.graph
|
|
if (
|
|
!shouldRenderVueNodes.value ||
|
|
nodeManager.value ||
|
|
activeGraph?._nodes.length !== 0
|
|
) {
|
|
return
|
|
}
|
|
const originalOnNodeAdded = activeGraph.onNodeAdded
|
|
activeGraph.onNodeAdded = function (node: LGraphNode) {
|
|
// Restore original handler
|
|
activeGraph.onNodeAdded = originalOnNodeAdded
|
|
|
|
// Initialize node manager if needed
|
|
if (shouldRenderVueNodes.value && !nodeManager.value) {
|
|
initializeNodeManager()
|
|
}
|
|
|
|
// Call original handler
|
|
if (originalOnNodeAdded) {
|
|
originalOnNodeAdded.call(this, node)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup function for component unmounting
|
|
const cleanup = () => {
|
|
if (nodeManager.value) {
|
|
nodeManager.value.cleanup()
|
|
nodeManager.value = null
|
|
}
|
|
}
|
|
|
|
return {
|
|
nodeManager,
|
|
|
|
// Lifecycle methods
|
|
initializeNodeManager,
|
|
disposeNodeManagerAndSyncs,
|
|
setupEmptyGraphListener,
|
|
cleanup
|
|
}
|
|
}
|
|
|
|
export const useVueNodeLifecycle = createSharedComposable(
|
|
useVueNodeLifecycleIndividual
|
|
)
|