feat: add refreshVueWidgets to sync widget changes with Vue state

- Add refreshVueWidgets function to GraphNodeManager for manually syncing
  LiteGraph widget changes with Vue reactive state
- Use refreshVueWidgets after widget modifications (capture, retake, show)
- Fixes retake button not appearing after capturing an image
This commit is contained in:
Johnpaul
2025-11-26 20:46:00 +01:00
parent 4b628724bd
commit 68b6159f99
3 changed files with 49 additions and 2 deletions

View File

@@ -82,6 +82,9 @@ export interface GraphNodeManager {
options: Record<string, unknown>
): void
// Refresh Vue widgets from LiteGraph node - use after modifying node.widgets
refreshVueWidgets(nodeId: string): void
// Lifecycle methods
cleanup(): void
}
@@ -334,6 +337,38 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
}
}
/**
* Refreshes Vue widget state from LiteGraph node widgets.
* Use this after directly modifying node.widgets to sync Vue state.
*/
const refreshVueWidgets = (nodeId: string): void => {
try {
const node = nodeRefs.get(nodeId)
const currentData = vueNodeData.get(nodeId)
if (!node || !currentData) return
// Re-extract widgets from node
const slotMetadata = new Map<string, WidgetSlotMetadata>()
node.inputs?.forEach((input, index) => {
if (!input?.widget?.name) return
slotMetadata.set(input.widget.name, {
index,
linked: input.link != null
})
})
const freshWidgets =
node.widgets?.map(safeWidgetMapper(node, slotMetadata)) ?? []
vueNodeData.set(nodeId, {
...currentData,
widgets: freshWidgets
})
} catch (error) {
// Ignore refresh errors
}
}
/**
* Creates a wrapped callback for a widget that maintains LiteGraph/Vue sync
*/
@@ -661,6 +696,7 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
vueNodeData,
getNode,
updateVueWidgetOptions,
refreshVueWidgets,
cleanup
}
}

View File

@@ -133,7 +133,9 @@ const processedWidgets = computed((): ProcessedWidget[] => {
for (const widget of widgets) {
// Skip if widget is in the hidden list for this node type
if (widget.options?.hidden) continue
if (widget.options?.hidden) {
continue
}
if (widget.options?.canvasOnly) continue
if (!widget.type) continue
if (!shouldRenderAsVue(widget)) continue

View File

@@ -197,7 +197,8 @@ function createActionWidget({
y: 100,
options: {
iconClass,
serialize: false
serialize: false,
hidden: false
},
callback: onClick
}
@@ -256,6 +257,8 @@ function removeWidgetsByName(names: string[]) {
updateNodeWidgets(node, (widgets) =>
widgets.filter((widget) => !names.includes(widget.name))
)
// Refresh Vue state to pick up widget removal
nodeManager.value?.refreshVueWidgets(String(node.id))
})
}
@@ -375,6 +378,9 @@ function showWidgets() {
return [...sanitizedWidgets, captureWidget]
})
// Refresh Vue state to pick up the new widgets
nodeManager.value?.refreshVueWidgets(String(node.id))
// Set up watcher to toggle capture button visibility when mode changes
setupCaptureOnQueueWatcher()
})
@@ -475,6 +481,9 @@ async function captureImage(node: LGraphNode) {
return [...preserved, retakeWidget]
})
// Refresh Vue state to pick up the new widgets
nodeManager.value?.refreshVueWidgets(String(node.id))
}
async function handleRetake() {