[backport core/1.41] fix: stabilize subgraph promoted widget identity and rendering (#9928)

## Summary
- Backport of #9896 to `core/1.41` via cherry-pick of merge commit
`74a48ab2aa6de160ca38496c843a19a8fcf0f77b`
- Resolved conflicts in:
  - `src/composables/graph/useGraphNodeManager.test.ts`
  - `src/lib/litegraph/src/subgraph/SubgraphNode.ts`
  - `src/renderer/extensions/vueNodes/components/NodeWidgets.vue`
- Applied `core/1.41` compatibility resolution for non-obvious conflict:
retained identity/render-key stabilization while keeping
execution-error-only widget error detection (no missing-model-store
dependency introduced)

## Testing
- `pnpm exec vitest run
src/composables/graph/useGraphNodeManager.test.ts
src/lib/litegraph/src/subgraph/SubgraphNode.test.ts
src/renderer/extensions/vueNodes/components/NodeWidgets.test.ts`
- `pnpm typecheck`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9928-backport-core-1-41-fix-stabilize-subgraph-promoted-widget-identity-and-rendering-3236d73d365081f5a0a8e3d549b2e19b)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Alexander Brown
2026-03-14 15:42:33 -07:00
committed by GitHub
parent 2d3de22150
commit 49bec80f12
23 changed files with 2557 additions and 154 deletions

View File

@@ -7,6 +7,7 @@ import { reactive, shallowReactive } from 'vue'
import { useChainCallback } from '@/composables/functional/useChainCallback'
import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes'
import { matchPromotedInput } from '@/core/graph/subgraph/matchPromotedInput'
import { resolveConcretePromotedWidget } from '@/core/graph/subgraph/resolveConcretePromotedWidget'
import { resolvePromotedWidgetSource } from '@/core/graph/subgraph/resolvePromotedWidgetSource'
import { resolveSubgraphInputTarget } from '@/core/graph/subgraph/resolveSubgraphInputTarget'
@@ -234,16 +235,17 @@ function safeWidgetMapper(
}
}
const promotedInputName = node.inputs?.find((input) => {
if (input.name === widget.name) return true
if (input._widget === widget) return true
return false
})?.name
const matchedInput = matchPromotedInput(node.inputs, widget)
const promotedInputName = matchedInput?.name
const displayName = promotedInputName ?? widget.name
const promotedSource = resolvePromotedSourceByInputName(displayName) ?? {
const directSource = {
sourceNodeId: widget.sourceNodeId,
sourceWidgetName: widget.sourceWidgetName
}
const promotedSource =
matchedInput?._widget === widget
? (resolvePromotedSourceByInputName(displayName) ?? directSource)
: directSource
return {
displayName,