fix: prevent Vue reactivity from breaking LiteGraph widget private fields

Use toRaw() and markRaw() when modifying widget objects to prevent Vue's
reactive proxy from wrapping them. This fixes errors when LiteGraph tries
to access private class members like #value in BaseWidget.
This commit is contained in:
Johnpaul
2025-11-24 20:44:32 +01:00
parent 0ac768722c
commit 7fc51c6d96

View File

@@ -15,7 +15,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { Button } from 'primevue' import { Button } from 'primevue'
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue' import {
computed,
markRaw,
nextTick,
onMounted,
onUnmounted,
ref,
toRaw
} from 'vue'
import { t } from '@/i18n' import { t } from '@/i18n'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
@@ -44,48 +52,49 @@ function storeOriginalWidgets() {
const node = litegraphNode.value const node = litegraphNode.value
if (!node?.widgets) return if (!node?.widgets) return
// Deep clone the original widgets to preserve their state // Store raw widgets to preserve their state without reactivity
originalWidgets.value = [...node.widgets] originalWidgets.value = node.widgets.map((w) => toRaw(w))
} }
function hideWidgets() { function hideWidgets() {
const node = litegraphNode.value const node = litegraphNode.value
if (!node?.widgets) return if (!node?.widgets) return
// Create completely new widget objects to trigger shallowReactive // Use toRaw to unwrap reactive proxies, then markRaw to prevent re-wrapping
const newWidgets = node.widgets.map((widget) => { const newWidgets = node.widgets.map((widget) => {
const rawWidget = toRaw(widget)
const shouldHide = ['height', 'width', 'capture_on_queue'].includes( const shouldHide = ['height', 'width', 'capture_on_queue'].includes(
widget.name rawWidget.name
) )
if (shouldHide) { if (shouldHide) {
// Special handling for capture_on_queue widget // Special handling for capture_on_queue widget
if (widget.name === 'capture_on_queue') { if (rawWidget.name === 'capture_on_queue') {
return { return markRaw({
...widget, ...rawWidget,
type: 'selectToggle', type: 'selectToggle',
label: 'Capture Image', label: 'Capture Image',
value: widget.value ?? false, value: rawWidget.value ?? false,
options: { options: {
...widget.options, ...rawWidget.options,
hidden: true, hidden: true,
values: [ values: [
{ label: 'On Run', value: true }, { label: 'On Run', value: true },
{ label: 'Manually', value: false } { label: 'Manually', value: false }
] ]
} }
} })
} }
return { return markRaw({
...widget, ...rawWidget,
options: { options: {
...widget.options, ...rawWidget.options,
hidden: true hidden: true
} }
} })
} }
return widget return rawWidget
}) })
node.widgets = newWidgets node.widgets = newWidgets
@@ -95,48 +104,49 @@ function restoreWidgets() {
const node = litegraphNode.value const node = litegraphNode.value
if (!node?.widgets || originalWidgets.value.length === 0) return if (!node?.widgets || originalWidgets.value.length === 0) return
// Restore the original widgets // Restore the original widgets (already raw from storage)
node.widgets = originalWidgets.value node.widgets = originalWidgets.value.map((w) => toRaw(w))
} }
function showWidgets() { function showWidgets() {
const node = litegraphNode.value const node = litegraphNode.value
if (!node?.widgets) return if (!node?.widgets) return
// Create completely new widget objects to trigger shallowReactive // Use toRaw to unwrap reactive proxies, then markRaw to prevent re-wrapping
const newWidgets = node.widgets.map((widget) => { const newWidgets = node.widgets.map((widget) => {
const rawWidget = toRaw(widget)
const shouldShow = ['height', 'width', 'capture_on_queue'].includes( const shouldShow = ['height', 'width', 'capture_on_queue'].includes(
widget.name rawWidget.name
) )
if (shouldShow) { if (shouldShow) {
// Special handling for capture_on_queue widget // Special handling for capture_on_queue widget
if (widget.name === 'capture_on_queue') { if (rawWidget.name === 'capture_on_queue') {
return { return markRaw({
...widget, ...rawWidget,
type: 'selectToggle', type: 'selectToggle',
label: 'Capture Image', label: 'Capture Image',
value: widget.value ?? false, value: rawWidget.value ?? false,
options: { options: {
...widget.options, ...rawWidget.options,
hidden: false, hidden: false,
values: [ values: [
{ label: 'On Run', value: true }, { label: 'On Run', value: true },
{ label: 'Manually', value: false } { label: 'Manually', value: false }
] ]
} }
} })
} }
return { return markRaw({
...widget, ...rawWidget,
options: { options: {
...widget.options, ...rawWidget.options,
hidden: false hidden: false
} }
} })
} }
return widget return rawWidget
}) })
node.widgets = newWidgets node.widgets = newWidgets