From 0f455c73bb424e7b081c4d9a8a8a744979366af5 Mon Sep 17 00:00:00 2001 From: Hunter Date: Tue, 24 Feb 2026 04:05:44 -0500 Subject: [PATCH] fix: sync DOM widget values to widgetValueStore on registration (#9166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Override `setNodeId` in `BaseDOMWidgetImpl` to sync the DOM-resolved value into the widget value store, fixing empty system prompts in Vue nodes (Nodes 2.0). ## Changes - **What**: DOM widgets (e.g. textarea for Gemini system_prompt) resolve their value through `options.getValue()` / DOM elements, not `_state.value`. When `BaseWidget.setNodeId` registers with the store, it spreads `_state.value` which is `undefined` for DOM widgets. The override captures the DOM-resolved value before registration and syncs it into the store afterward — keeping the fix in the DOM widget layer where the mismatch originates, leaving `BaseWidget` unchanged. ## Review Focus - Whether capturing `this.value` before `super.setNodeId()` and writing it after is the right sequencing - Whether this correctly handles all DOM widget subtypes (`DOMWidgetImpl`, `ComponentWidgetImpl`) Supersedes #9164 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9166-fix-sync-DOM-widget-values-to-widgetValueStore-on-registration-3116d73d3650816f8cece866a9272baa) by [Unito](https://www.unito.io) --- src/scripts/domWidget.ts | 14 +++++++++ src/scripts/domWidgetStore.test.ts | 48 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/scripts/domWidgetStore.test.ts diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index 38979bb81b..11c9c5d730 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -7,6 +7,7 @@ import { LegacyWidget, LiteGraph } from '@/lib/litegraph/src/litegraph' +import type { NodeId } from '@/lib/litegraph/src/litegraph' import type { IBaseWidget, IWidgetOptions @@ -14,6 +15,7 @@ import type { import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' import { useDomWidgetStore } from '@/stores/domWidgetStore' import { usePromotionStore } from '@/stores/promotionStore' +import { useWidgetValueStore } from '@/stores/widgetValueStore' import { generateUUID } from '@/utils/formatUtil' export interface BaseDOMWidget< @@ -150,6 +152,18 @@ abstract class BaseDOMWidgetImpl this.callback?.(this.value) } + override setNodeId(nodeId: NodeId): void { + // Capture the DOM-resolved value before registration, since the base class + // registers _state.value which is undefined for DOM widgets (their value + // lives in the DOM element / options.getValue). + const resolvedValue = this.value + super.setNodeId(nodeId) + const graphId = this.node.graph?.rootGraph.id + if (!graphId) return + const state = useWidgetValueStore().getWidget(graphId, nodeId, this.name) + if (state) state.value = resolvedValue + } + get margin(): number { return this.options.margin ?? BaseDOMWidgetImpl.DEFAULT_MARGIN } diff --git a/src/scripts/domWidgetStore.test.ts b/src/scripts/domWidgetStore.test.ts new file mode 100644 index 0000000000..8cb75e4fb5 --- /dev/null +++ b/src/scripts/domWidgetStore.test.ts @@ -0,0 +1,48 @@ +import { createTestingPinia } from '@pinia/testing' +import { setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it } from 'vitest' + +import { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph' +import { DOMWidgetImpl } from '@/scripts/domWidget' +import { useWidgetValueStore } from '@/stores/widgetValueStore' + +describe('DOMWidgetImpl store integration', () => { + let graph: LGraph + let node: LGraphNode + let store: ReturnType + + beforeEach(() => { + setActivePinia(createTestingPinia({ stubActions: false })) + store = useWidgetValueStore() + graph = new LGraph() + node = new LGraphNode('TestNode') + node.id = 1 + graph.add(node) + }) + + it('registers DOM-resolved value in store via setNodeId', () => { + const defaultValue = 'You are an expert image-generation engine.' + const element = document.createElement('textarea') + element.value = defaultValue + + const widget = new DOMWidgetImpl({ + node, + name: 'system_prompt', + type: 'customtext', + element, + options: { + getValue: () => element.value as string, + setValue: (v: string) => { + element.value = v + const state = store.getWidget(graph.id, node.id, 'system_prompt') + if (state) state.value = v + } + } + }) + + widget.setNodeId(node.id) + + const state = store.getWidget(graph.id, node.id, 'system_prompt') + expect(state?.value).toBe(defaultValue) + }) +})