diff --git a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.test.ts b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.test.ts
index b75560e871..ce9b449fa1 100644
--- a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.test.ts
+++ b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.test.ts
@@ -3,9 +3,8 @@ import { render, screen } from '@testing-library/vue'
import { computed } from 'vue'
import { describe, expect, it, vi } from 'vitest'
+import type { ProcessedWidget } from '@/renderer/extensions/vueNodes/composables/useProcessedWidgets'
import type { ComfyNodeDef as ComfyNodeDefV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
-import { useWidgetValueStore } from '@/stores/widgetValueStore'
-import type { WidgetId } from '@/types/widgetId'
import LGraphNodePreview from '@/renderer/extensions/vueNodes/components/LGraphNodePreview.vue'
import { fromPartial } from '@total-typescript/shoehorn'
@@ -13,12 +12,15 @@ vi.mock('@/stores/widgetStore', () => ({
useWidgetStore: () => ({ inputIsWidget: () => true })
}))
-const NodeWidgetsProbe = {
- props: ['widgetIds'],
- setup(props: { widgetIds?: readonly WidgetId[] }) {
- const widgetValueStore = useWidgetValueStore()
+const WidgetGridProbe = {
+ props: ['processedWidgets'],
+ setup(props: { processedWidgets?: ProcessedWidget[] }) {
const widgets = computed(() =>
- (props.widgetIds ?? []).map((id) => widgetValueStore.getWidget(id))
+ (props.processedWidgets ?? []).map((widget) => ({
+ name: widget.name,
+ value: widget.simplified.value,
+ options: { values: widget.simplified.options?.values }
+ }))
)
return { widgets }
},
@@ -52,7 +54,7 @@ function renderedWidgets(
stubs: {
NodeHeader: true,
NodeSlots: true,
- NodeWidgets: NodeWidgetsProbe
+ WidgetGrid: WidgetGridProbe
}
}
})
diff --git a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue
index 2722938298..2b37dd5d4e 100644
--- a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue
+++ b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue
@@ -19,10 +19,12 @@
>
-
@@ -30,7 +32,7 @@
diff --git a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue
index 617a156f54..bcfa63ecfc 100644
--- a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue
+++ b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue
@@ -2,82 +2,23 @@
{{ st('nodeErrors.widgets', 'Node Widgets Error') }}
-
+ />
diff --git a/src/renderer/extensions/vueNodes/composables/usePreviewWidgets.ts b/src/renderer/extensions/vueNodes/composables/usePreviewWidgets.ts
new file mode 100644
index 0000000000..597d74a503
--- /dev/null
+++ b/src/renderer/extensions/vueNodes/composables/usePreviewWidgets.ts
@@ -0,0 +1,92 @@
+import type { TooltipOptions } from 'primevue'
+import { computed } from 'vue'
+import type { Component } from 'vue'
+
+import type { IWidgetOptions } from '@/lib/litegraph/src/types/widgets'
+import { useSettingStore } from '@/platform/settings/settingStore'
+import type { ProcessedWidget } from '@/renderer/extensions/vueNodes/composables/useProcessedWidgets'
+import { isWidgetVisible } from '@/renderer/extensions/vueNodes/composables/useProcessedWidgets'
+import WidgetLegacy from '@/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue'
+import {
+ getComponent,
+ shouldExpand
+} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
+import type { ComfyNodeDef as ComfyNodeDefV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import { useWidgetStore } from '@/stores/widgetStore'
+import type { WidgetValue } from '@/types/simplifiedWidget'
+
+const EMPTY_TOOLTIP: TooltipOptions = {}
+const noop = () => {}
+
+/**
+ * Builds render-ready widgets for a static node preview straight from its
+ * schema, without registering anything in the widget stores. Previews have no
+ * live graph, so nothing here needs (or should touch) store-backed state.
+ */
+export function usePreviewWidgets(
+ nodeDefGetter: () => ComfyNodeDefV2,
+ widgetValuesGetter: () => Record | undefined
+) {
+ const widgetStore = useWidgetStore()
+ const settingStore = useSettingStore()
+ const showAdvanced = computed(() =>
+ Boolean(settingStore.get('Comfy.Node.AlwaysShowAdvancedWidgets'))
+ )
+
+ const processedWidgets = computed(() => {
+ const nodeDef = nodeDefGetter()
+ const widgetValues = widgetValuesGetter()
+ return Object.entries(nodeDef.inputs || {})
+ .filter(([, input]) => widgetStore.inputIsWidget(input))
+ .map(([name, input]): ProcessedWidget => {
+ const comboValues =
+ input.type === 'COMBO' && Array.isArray(input.options)
+ ? input.options
+ : undefined
+ const leadValue = widgetValues?.[name]
+ const value = (leadValue ??
+ (input.default !== undefined
+ ? input.default
+ : (comboValues?.[0] ?? ''))) as WidgetValue
+ const options = {
+ hidden: input.hidden,
+ advanced: input.advanced,
+ values:
+ leadValue && comboValues
+ ? [
+ leadValue,
+ ...comboValues.filter((option) => option !== leadValue)
+ ]
+ : comboValues
+ } satisfies IWidgetOptions
+ const type = input.widgetType || input.type
+ const vueComponent: Component = getComponent(type) ?? WidgetLegacy
+ return {
+ advanced: Boolean(input.advanced),
+ handleContextMenu: noop,
+ hasLayoutSize: false,
+ hasError: false,
+ hidden: Boolean(input.hidden),
+ name,
+ renderKey: `preview:${nodeDef.name}:${name}`,
+ simplified: { name, type, value, options, spec: input },
+ tooltipConfig: EMPTY_TOOLTIP,
+ type,
+ updateHandler: noop,
+ value,
+ visible: isWidgetVisible(options, showAdvanced.value),
+ vueComponent,
+ slotMetadata: undefined
+ }
+ })
+ })
+
+ const gridTemplateRows = computed(() =>
+ processedWidgets.value
+ .filter((widget) => widget.visible)
+ .map((widget) => (shouldExpand(widget.type) ? 'auto' : 'min-content'))
+ .join(' ')
+ )
+
+ return { processedWidgets, gridTemplateRows }
+}
diff --git a/src/renderer/extensions/vueNodes/composables/useProcessedWidgets.ts b/src/renderer/extensions/vueNodes/composables/useProcessedWidgets.ts
index 514fb2a437..29c74a079d 100644
--- a/src/renderer/extensions/vueNodes/composables/useProcessedWidgets.ts
+++ b/src/renderer/extensions/vueNodes/composables/useProcessedWidgets.ts
@@ -60,7 +60,7 @@ function isTooltipValueType(val: unknown): val is TooltipValueType {
return TOOLTIP_VALUE_TYPES.includes(val as TooltipValueType)
}
-interface WidgetSlotMetadata {
+export interface WidgetSlotMetadata {
index: number
linked: boolean
originNodeId?: NodeId
@@ -78,7 +78,7 @@ interface WidgetErrorTarget {
widgetName: string
}
-interface ProcessedWidget {
+export interface ProcessedWidget {
advanced: boolean
handleContextMenu: (e: PointerEvent) => void
hasLayoutSize: boolean