From 4b628724bd7b3149e2614fa42199ee4276f154cd Mon Sep 17 00:00:00 2001 From: Johnpaul Date: Wed, 26 Nov 2025 17:38:38 +0100 Subject: [PATCH] feat: hide capture button when 'On Run' mode is selected in webcam widget - Add updateVueWidgetOptions to GraphNodeManager to update widget options and trigger Vue reactivity - Implement capture button visibility toggle based on capture_on_queue value - Default capture mode to 'Manually' (false) - Use Vue watch to reactively hide/show capture button when mode changes --- src/composables/graph/useGraphNodeManager.ts | 37 +++++++++ .../widgets/components/WidgetWebcam.vue | 75 ++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/composables/graph/useGraphNodeManager.ts b/src/composables/graph/useGraphNodeManager.ts index 23edf6ea6..687016971 100644 --- a/src/composables/graph/useGraphNodeManager.ts +++ b/src/composables/graph/useGraphNodeManager.ts @@ -75,6 +75,13 @@ export interface GraphNodeManager { // Access to original LiteGraph nodes (non-reactive) getNode(id: string): LGraphNode | undefined + // Update widget options (e.g., hidden, disabled) - triggers Vue reactivity + updateVueWidgetOptions( + nodeId: string, + widgetName: string, + options: Record + ): void + // Lifecycle methods cleanup(): void } @@ -298,6 +305,35 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager { } } + /** + * Updates Vue state when widget options change (e.g., hidden, disabled) + */ + const updateVueWidgetOptions = ( + nodeId: string, + widgetName: string, + options: Record + ): void => { + try { + const currentData = vueNodeData.get(nodeId) + if (!currentData?.widgets) return + + const updatedWidgets = currentData.widgets.map((w) => + w.name === widgetName + ? { ...w, options: { ...w.options, ...options } } + : w + ) + // Create a completely new object to ensure Vue reactivity triggers + const updatedData = { + ...currentData, + widgets: updatedWidgets + } + + vueNodeData.set(nodeId, updatedData) + } catch (error) { + // Ignore widget update errors to prevent cascade failures + } + } + /** * Creates a wrapped callback for a widget that maintains LiteGraph/Vue sync */ @@ -624,6 +660,7 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager { return { vueNodeData, getNode, + updateVueWidgetOptions, cleanup } } diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetWebcam.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetWebcam.vue index 0dc81ba79..d8c3a9279 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetWebcam.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetWebcam.vue @@ -58,9 +58,11 @@ import { onMounted, onUnmounted, ref, - toRaw + toRaw, + watch } from 'vue' +import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle' import { t } from '@/i18n' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets' @@ -70,6 +72,8 @@ import { api } from '@/scripts/api' import { app } from '@/scripts/app' import type { SimplifiedWidget } from '@/types/simplifiedWidget' +const { nodeManager } = useVueNodeLifecycle() + const props = defineProps<{ widget: SimplifiedWidget readonly?: boolean @@ -149,7 +153,10 @@ function applyWidgetVisibility( // Mutate in place to preserve object identity for serializeValue closure widget.type = 'selectToggle' widget.label = 'Capture Image' - widget.value = widget.value ?? false + // Default to false (Manual mode) - only set if undefined/null + if (widget.value === undefined || widget.value === null) { + widget.value = false + } widget.options = { ...widget.options, hidden, @@ -196,6 +203,53 @@ function createActionWidget({ } } +function updateCaptureButtonVisibility(isOnRunMode: boolean) { + withLitegraphNode((node) => { + // Update the LiteGraph widget options + const captureWidget = node.widgets?.find( + (w) => w.name === CAPTURE_WIDGET_NAME + ) + if (captureWidget) { + captureWidget.options = { + ...captureWidget.options, + hidden: isOnRunMode + } + } + + // Update Vue state directly to trigger reactivity + nodeManager.value?.updateVueWidgetOptions( + String(node.id), + CAPTURE_WIDGET_NAME, + { hidden: isOnRunMode } + ) + + app.graph.setDirtyCanvas(true, true) + }) +} + +// Computed to get capture_on_queue widget value from Vue state +const captureOnQueueValue = computed(() => { + const vueNodeData = nodeManager.value?.vueNodeData.get(props.nodeId) + const widget = vueNodeData?.widgets?.find( + (w) => w.name === 'capture_on_queue' + ) + return widget?.value === true +}) + +function setupCaptureOnQueueWatcher() { + // Set initial visibility + updateCaptureButtonVisibility(captureOnQueueValue.value) + + // Watch for changes using Vue reactivity + watch( + captureOnQueueValue, + (isOnRunMode) => { + updateCaptureButtonVisibility(isOnRunMode) + }, + { immediate: false } + ) +} + function removeWidgetsByName(names: string[]) { withLitegraphNode((node) => { if (!node.widgets?.length) return @@ -288,6 +342,12 @@ function setupSerializeValue() { function showWidgets() { withLitegraphNode((node) => { + // Get current capture_on_queue value to determine initial button visibility + const captureOnQueueWidget = node.widgets?.find( + (w) => w.name === 'capture_on_queue' + ) + const isOnRunMode = captureOnQueueWidget?.value === true + updateNodeWidgets(node, (widgets) => { const sanitizedWidgets = widgets .map((widget) => applyWidgetVisibility(widget, false)) @@ -304,8 +364,19 @@ function showWidgets() { onClick: () => captureImage(node) }) + // Hide capture button if in "On Run" mode + if (isOnRunMode) { + captureWidget.options = { + ...captureWidget.options, + hidden: true + } + } + return [...sanitizedWidgets, captureWidget] }) + + // Set up watcher to toggle capture button visibility when mode changes + setupCaptureOnQueueWatcher() }) }