fix: subgraph output slot labels not updating in v2 renderer (#9266)

## Summary

Custom names set on subgraph output nodes are ignored in the v2 renderer
— it always shows the data type name (e.g. "texts") instead of the
user-defined label. Works correctly in v1.

## Changes

- **What**: Made `outputs` in `extractVueNodeData` reactive via
`shallowReactive` + `defineProperty` (matching the existing `inputs`
pattern). Added a `node:slot-label:changed` graph trigger that
`SubgraphNode` fires when input/output labels are renamed, so the Vue
layer picks up the change.

## Review Focus

- The `outputs` reactivity mirrors `inputs` exactly — same
`shallowReactive` + setter pattern. The new trigger event forces
`shallowReactive` to detect the deep property change by re-assigning the
array.
- Also handles input label renames for consistency, even though the
current bug report is output-specific.

## Screenshots

**v1 — output correctly shows custom label "output_text":**
<img width="1076" height="628" alt="Screenshot 2026-02-26 at 4 43 00 PM"
src="https://github.com/user-attachments/assets/b4d6ae4c-9970-4d99-a872-4ce1b28522f2"
/>

**v2 before fix — output shows type name "texts" instead of custom
label:**
<img width="808" height="298" alt="Screenshot 2026-02-26 at 4 43 30 PM"
src="https://github.com/user-attachments/assets/cf06aa6c-6d4d-4be9-9bcd-dcc072ed1907"
/>

**v2 after fix — output correctly shows "output_text":**
<img width="1013" height="292" alt="Screenshot 2026-02-26 at 5 14 44 PM"
src="https://github.com/user-attachments/assets/3c43fa9b-0615-4758-bee6-be3481168675"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9266-fix-subgraph-output-slot-labels-not-updating-in-v2-renderer-3146d73d365081979327fd775a6ef62b)
by [Unito](https://www.unito.io)
This commit is contained in:
Arthur R Longbottom
2026-03-13 09:58:13 -07:00
committed by GitHub
parent 9447a1f5d6
commit 5640eb7d92
5 changed files with 125 additions and 4 deletions

View File

@@ -396,7 +396,9 @@ export function extractVueNodeData(node: LGraphNode): VueNodeData {
},
set(v) {
reactiveWidgets.splice(0, reactiveWidgets.length, ...v)
}
},
configurable: true,
enumerable: true
})
}
const reactiveInputs = shallowReactive<INodeInputSlot[]>(node.inputs ?? [])
@@ -406,7 +408,20 @@ export function extractVueNodeData(node: LGraphNode): VueNodeData {
},
set(v) {
reactiveInputs.splice(0, reactiveInputs.length, ...v)
}
},
configurable: true,
enumerable: true
})
const reactiveOutputs = shallowReactive<INodeOutputSlot[]>(node.outputs ?? [])
Object.defineProperty(node, 'outputs', {
get() {
return reactiveOutputs
},
set(v) {
reactiveOutputs.splice(0, reactiveOutputs.length, ...v)
},
configurable: true,
enumerable: true
})
const safeWidgets = reactiveComputed<SafeWidgetData[]>(() => {
@@ -448,7 +463,7 @@ export function extractVueNodeData(node: LGraphNode): VueNodeData {
hasErrors: !!node.has_errors,
widgets: safeWidgets,
inputs: reactiveInputs,
outputs: node.outputs ? [...node.outputs] : undefined,
outputs: reactiveOutputs,
flags: node.flags ? { ...node.flags } : undefined,
color: node.color || undefined,
bgcolor: node.bgcolor || undefined,
@@ -746,6 +761,20 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
if (slotLinksEvent.slotType === NodeSlotType.INPUT) {
refreshNodeSlots(String(slotLinksEvent.nodeId))
}
},
'node:slot-label:changed': (slotLabelEvent) => {
const nodeId = String(slotLabelEvent.nodeId)
const nodeRef = nodeRefs.get(nodeId)
if (!nodeRef) return
// Force shallowReactive to detect the deep property change
// by re-assigning the affected array through the defineProperty setter.
if (slotLabelEvent.slotType !== NodeSlotType.OUTPUT && nodeRef.inputs) {
nodeRef.inputs = [...nodeRef.inputs]
}
if (slotLabelEvent.slotType !== NodeSlotType.INPUT && nodeRef.outputs) {
nodeRef.outputs = [...nodeRef.outputs]
}
}
}
@@ -760,6 +789,9 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
case 'node:slot-links:changed':
triggerHandlers['node:slot-links:changed'](event)
break
case 'node:slot-label:changed':
triggerHandlers['node:slot-label:changed'](event)
break
}
// Chain to original handler