From 25991362965acd2c1dc9d4c9a3230444147e489b Mon Sep 17 00:00:00 2001 From: AustinMroz Date: Fri, 10 Oct 2025 14:11:38 -0700 Subject: [PATCH] Implement DOMWidget for vue (#6006) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ![vue-dom-widget](https://github.com/user-attachments/assets/d0c0e5f6-bacb-4fd9-957e-4f19e8071c3d) Did testing on about a dozen custom nodes. Most just work. - Some custom nodes have copy/pasted the `addDOMWidget` call with types like `customtext` and get converted to textareas -> Not feasible to fix here. Can open PRs into custom nodes if complaints arise. - Only the KJNodes spline editor had mouse issues -> Can investigate/open PR into KJNodes later. - Many nodes don't resize gracefully. Probably best handled in a future PR. - Some expect to be handled like textareas. These currently have minsize and don't scale. - Others, like VHS previews, scale self properly, but don't update height inside a drag operation -> node height can be set to less than fit. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6006-Implement-DOMWidget-for-vue-2886d73d3650817ca497c15d87d70f4f) by [Unito](https://www.unito.io) --- src/composables/graph/useGraphNodeManager.ts | 5 +++- .../vueNodes/components/NodeWidgets.vue | 4 ++- .../vueNodes/widgets/components/WidgetDOM.vue | 29 +++++++++++++++++++ .../widgets/registry/widgetRegistry.ts | 8 ++--- 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 src/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue diff --git a/src/composables/graph/useGraphNodeManager.ts b/src/composables/graph/useGraphNodeManager.ts index 9858e331d..be96d85cc 100644 --- a/src/composables/graph/useGraphNodeManager.ts +++ b/src/composables/graph/useGraphNodeManager.ts @@ -12,6 +12,7 @@ import type { import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations' import { LayoutSource } from '@/renderer/core/layout/types' import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' +import { isDOMWidget } from '@/scripts/domWidget' import { useNodeDefStore } from '@/stores/nodeDefStore' import type { WidgetValue } from '@/types/simplifiedWidget' @@ -38,6 +39,7 @@ export interface SafeWidgetData { callback?: ((value: unknown) => void) | undefined spec?: InputSpec slotMetadata?: WidgetSlotMetadata + isDOMWidget?: boolean } export interface VueNodeData { @@ -156,7 +158,8 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager { options: widget.options ? { ...widget.options } : undefined, callback: widget.callback, spec, - slotMetadata: slotInfo + slotMetadata: slotInfo, + isDOMWidget: isDOMWidget(widget) } } catch (error) { return { diff --git a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue index 0d6764e89..9685a2a9c 100644 --- a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue +++ b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue @@ -62,6 +62,7 @@ import type { import { useErrorHandling } from '@/composables/useErrorHandling' import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions' import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips' +import WidgetDOM from '@/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue' import WidgetInputText from '@/renderer/extensions/vueNodes/widgets/components/WidgetInputText.vue' import { getComponent, @@ -127,7 +128,8 @@ const processedWidgets = computed((): ProcessedWidget[] => { if (!shouldRenderAsVue(widget)) continue const vueComponent = - getComponent(widget.type, widget.name) || WidgetInputText + getComponent(widget.type, widget.name) || + (widget.isDOMWidget ? WidgetDOM : WidgetInputText) const slotMetadata = widget.slotMetadata diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue new file mode 100644 index 000000000..b780fce07 --- /dev/null +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue @@ -0,0 +1,29 @@ + + diff --git a/src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts b/src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts index 8bda86865..6cab2dbdd 100644 --- a/src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts +++ b/src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts @@ -3,6 +3,8 @@ */ import type { Component } from 'vue' +import type { SafeWidgetData } from '@/composables/graph/useGraphNodeManager' + import WidgetAudioUI from '../components/WidgetAudioUI.vue' import WidgetButton from '../components/WidgetButton.vue' import WidgetChart from '../components/WidgetChart.vue' @@ -169,11 +171,9 @@ export const isEssential = (type: string): boolean => { return widgets.get(canonicalType)?.essential || false } -export const shouldRenderAsVue = (widget: { - type?: string - options?: Record -}): boolean => { +export const shouldRenderAsVue = (widget: Partial): boolean => { if (widget.options?.canvasOnly) return false + if (widget.isDOMWidget) return true if (!widget.type) return false return isSupported(widget.type) }