Skip to content

Commit 2599136

Browse files
authored
Implement DOMWidget for vue (#6006)
![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)
1 parent d7796fc commit 2599136

File tree

4 files changed

+40
-6
lines changed

4 files changed

+40
-6
lines changed

src/composables/graph/useGraphNodeManager.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
1313
import { LayoutSource } from '@/renderer/core/layout/types'
1414
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
15+
import { isDOMWidget } from '@/scripts/domWidget'
1516
import { useNodeDefStore } from '@/stores/nodeDefStore'
1617
import type { WidgetValue } from '@/types/simplifiedWidget'
1718

@@ -38,6 +39,7 @@ export interface SafeWidgetData {
3839
callback?: ((value: unknown) => void) | undefined
3940
spec?: InputSpec
4041
slotMetadata?: WidgetSlotMetadata
42+
isDOMWidget?: boolean
4143
}
4244

4345
export interface VueNodeData {
@@ -156,7 +158,8 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
156158
options: widget.options ? { ...widget.options } : undefined,
157159
callback: widget.callback,
158160
spec,
159-
slotMetadata: slotInfo
161+
slotMetadata: slotInfo,
162+
isDOMWidget: isDOMWidget(widget)
160163
}
161164
} catch (error) {
162165
return {

src/renderer/extensions/vueNodes/components/NodeWidgets.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import type {
6262
import { useErrorHandling } from '@/composables/useErrorHandling'
6363
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
6464
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
65+
import WidgetDOM from '@/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue'
6566
import WidgetInputText from '@/renderer/extensions/vueNodes/widgets/components/WidgetInputText.vue'
6667
import {
6768
getComponent,
@@ -127,7 +128,8 @@ const processedWidgets = computed((): ProcessedWidget[] => {
127128
if (!shouldRenderAsVue(widget)) continue
128129
129130
const vueComponent =
130-
getComponent(widget.type, widget.name) || WidgetInputText
131+
getComponent(widget.type, widget.name) ||
132+
(widget.isDOMWidget ? WidgetDOM : WidgetInputText)
131133
132134
const slotMetadata = widget.slotMetadata
133135
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script setup lang="ts">
2+
import { onMounted, ref } from 'vue'
3+
4+
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
5+
import { isDOMWidget } from '@/scripts/domWidget'
6+
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
7+
8+
// Button widgets don't have a v-model value, they trigger actions
9+
const props = defineProps<{
10+
widget: SimplifiedWidget<void>
11+
nodeId: string
12+
readonly?: boolean
13+
}>()
14+
15+
const domEl = ref<HTMLElement>()
16+
17+
const { canvas } = useCanvasStore()
18+
onMounted(() => {
19+
if (!domEl.value) return
20+
const node = canvas?.graph?.getNodeById(props.nodeId) ?? undefined
21+
if (!node) return
22+
const widget = node.widgets?.find((w) => w.name === props.widget.name)
23+
if (!widget || !isDOMWidget(widget)) return
24+
domEl.value.replaceChildren(widget.element)
25+
})
26+
</script>
27+
<template>
28+
<div ref="domEl" />
29+
</template>

src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44
import type { Component } from 'vue'
55

6+
import type { SafeWidgetData } from '@/composables/graph/useGraphNodeManager'
7+
68
import WidgetAudioUI from '../components/WidgetAudioUI.vue'
79
import WidgetButton from '../components/WidgetButton.vue'
810
import WidgetChart from '../components/WidgetChart.vue'
@@ -169,11 +171,9 @@ export const isEssential = (type: string): boolean => {
169171
return widgets.get(canonicalType)?.essential || false
170172
}
171173

172-
export const shouldRenderAsVue = (widget: {
173-
type?: string
174-
options?: Record<string, unknown>
175-
}): boolean => {
174+
export const shouldRenderAsVue = (widget: Partial<SafeWidgetData>): boolean => {
176175
if (widget.options?.canvasOnly) return false
176+
if (widget.isDOMWidget) return true
177177
if (!widget.type) return false
178178
return isSupported(widget.type)
179179
}

0 commit comments

Comments
 (0)