diff --git a/src/core/graph/subgraph/proxyWidget.test.ts b/src/core/graph/subgraph/proxyWidget.test.ts index 188aba33a..cbcd2d336 100644 --- a/src/core/graph/subgraph/proxyWidget.test.ts +++ b/src/core/graph/subgraph/proxyWidget.test.ts @@ -5,8 +5,8 @@ import { beforeEach, describe, expect, test, vi } from 'vitest' import { registerProxyWidgets } from '@/core/graph/subgraph/proxyWidget' import { promoteWidget } from '@/core/graph/subgraph/proxyWidgetUtils' import { parseProxyWidgets } from '@/core/schemas/proxyWidget' -import { LGraphNode } from '@/lib/litegraph/src/litegraph' -import type { LGraphCanvas, SubgraphNode } from '@/lib/litegraph/src/litegraph' +import { LGraph, LGraphNode, SubgraphNode } from '@/lib/litegraph/src/litegraph' +import type { LGraphCanvas } from '@/lib/litegraph/src/litegraph' import { createTestSubgraph, @@ -125,6 +125,94 @@ describe('Subgraph proxyWidgets', () => { subgraphNode.widgets[0].computedHeight = 10 expect(subgraphNode.widgets[0].value).toBe('value') }) + test('Proxy widget label shows widgetName, not "nodeId: widgetName"', () => { + const [subgraphNode, innerNodes] = setupSubgraph(1) + innerNodes[0].addWidget('text', 'seed', 'value', () => {}) + subgraphNode.properties.proxyWidgets = [['1', 'seed']] + + const proxyWidget = subgraphNode.widgets[0] + expect(proxyWidget.label).toBe('seed') + expect(proxyWidget.name).toBe('1: seed') + }) + + test('Proxy widget label reflects linked widget label', () => { + const [subgraphNode, innerNodes] = setupSubgraph(1) + innerNodes[0].addWidget('text', 'seed', 'value', () => {}) + subgraphNode.properties.proxyWidgets = [['1', 'seed']] + + const proxyWidget = subgraphNode.widgets[0] + expect(proxyWidget.label).toBe('seed') + + innerNodes[0].widgets![0].label = 'My Inner Label' + // Trigger re-resolve of linked widget + proxyWidget.computedHeight = 10 + expect(proxyWidget.label).toBe('My Inner Label') + }) + + test('Proxy widget user rename takes priority over linked widget label', () => { + const [subgraphNode, innerNodes] = setupSubgraph(1) + innerNodes[0].addWidget('text', 'seed', 'value', () => {}) + subgraphNode.properties.proxyWidgets = [['1', 'seed']] + + const proxyWidget = subgraphNode.widgets[0] + proxyWidget.label = 'My Custom Seed' + expect(proxyWidget.label).toBe('My Custom Seed') + + innerNodes[0].widgets![0].label = 'Inner Override' + proxyWidget.computedHeight = 10 + expect(proxyWidget.label).toBe('My Custom Seed') + }) + + test('Proxy widget label resets to linked widget on undefined', () => { + const [subgraphNode, innerNodes] = setupSubgraph(1) + innerNodes[0].addWidget('text', 'seed', 'value', () => {}) + subgraphNode.properties.proxyWidgets = [['1', 'seed']] + + const proxyWidget = subgraphNode.widgets[0] + proxyWidget.label = 'Custom' + expect(proxyWidget.label).toBe('Custom') + + proxyWidget.label = undefined + innerNodes[0].widgets![0].label = 'Inner Label' + proxyWidget.computedHeight = 10 + expect(proxyWidget.label).toBe('Inner Label') + }) + + test('Proxy widget labels are correct when loaded from serialized data', () => { + // Intentionally constructs SubgraphNode via constructor (not setupSubgraph) + // to exercise the deserialization/onConfigure path from blueprint JSON. + const subgraph = createTestSubgraph() + const innerNode = new LGraphNode('InnerNode') + subgraph.add(innerNode) + innerNode.addWidget('text', 'seed', 'value', () => {}) + innerNode.addWidget('text', 'steps', 'value', () => {}) + + const parentGraph = new LGraph() + const subgraphNode = new SubgraphNode(parentGraph, subgraph, { + id: 1, + type: subgraph.id, + pos: [100, 100], + size: [200, 100], + inputs: [], + outputs: [], + properties: { + proxyWidgets: [ + ['1', 'seed'], + ['1', 'steps'] + ] + }, + flags: {}, + mode: 0, + order: 0 + }) + + expect(subgraphNode.widgets).toHaveLength(2) + expect(subgraphNode.widgets[0].label).toBe('seed') + expect(subgraphNode.widgets[0].name).toBe('1: seed') + expect(subgraphNode.widgets[1].label).toBe('steps') + expect(subgraphNode.widgets[1].name).toBe('1: steps') + }) + test('Prevents duplicate promotion', () => { const [subgraphNode, innerNodes] = setupSubgraph(1) innerNodes[0].addWidget('text', 'stringWidget', 'value', () => {}) diff --git a/src/core/graph/subgraph/proxyWidget.ts b/src/core/graph/subgraph/proxyWidget.ts index 1370dfe49..4c27f81fe 100644 --- a/src/core/graph/subgraph/proxyWidget.ts +++ b/src/core/graph/subgraph/proxyWidget.ts @@ -154,7 +154,6 @@ function newProxyWidget( computedHeight: undefined, isProxyWidget: true, last_y: undefined, - label: name, name, node: subgraphNode, onRemove: undefined, @@ -202,12 +201,15 @@ function newProxyFromOverlay(subgraphNode: SubgraphNode, overlay: Overlay) { * and the value used as 'this' if property is a get/set method * @param {unknown} value - only used on set calls. The thing being assigned */ + let userLabel: string | undefined const handler = { get(_t: IBaseWidget, property: string, receiver: object) { let redirectedTarget: object = backingWidget let redirectedReceiver = receiver if (property == '_overlay') return overlay else if (property == 'value') redirectedReceiver = backingWidget + else if (property == 'label') + return userLabel ?? linkedWidget?.label ?? overlay.widgetName if (Object.prototype.hasOwnProperty.call(overlay, property)) { redirectedTarget = overlay redirectedReceiver = overlay @@ -215,6 +217,10 @@ function newProxyFromOverlay(subgraphNode: SubgraphNode, overlay: Overlay) { return Reflect.get(redirectedTarget, property, redirectedReceiver) }, set(_t: IBaseWidget, property: string, value: unknown) { + if (property == 'label') { + userLabel = value as string | undefined + return true + } let redirectedTarget: object = backingWidget if (property == 'computedHeight') { if (overlay.widgetName.startsWith('$$') && linkedNode) {