From da9a3f911c0ac3accbf02e682155eefb4620389b Mon Sep 17 00:00:00 2001 From: enteresanlikk <23117378+enteresanlikk@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:36:55 +0300 Subject: [PATCH] feat(flow-editor): offer every upstream node as a data source --- .../components/data-source-popover.tsx | 9 ++- .../flow-editor/hooks/use-flow-nodes.ts | 57 ++++++++++++++----- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/apps/platform/src/features/flow-editor/components/data-source-popover.tsx b/apps/platform/src/features/flow-editor/components/data-source-popover.tsx index c7f2c64..bcaabd8 100644 --- a/apps/platform/src/features/flow-editor/components/data-source-popover.tsx +++ b/apps/platform/src/features/flow-editor/components/data-source-popover.tsx @@ -74,10 +74,15 @@ export function DataSourcePopover({ ))} diff --git a/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts b/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts index 8abe11e..eb05527 100644 --- a/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts +++ b/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts @@ -287,33 +287,64 @@ export function useFlowNodes(apiNodes: ResolvedNode[] = []) { }) contextMenu = cNew } else if (source) { - const edges = source.edges.filter((e) => e.target === ref) - for (let i = 0; i < edges.length; i++) { - const edge = edges[i] - if (edge.source === '0') { - continue + const currentApiNodes = apiNodesArg || apiOriginRef.current + const nodeName = ( + node: { title?: string; data?: { title?: string } }, + fallback: string, + ) => node.title || node.data?.title || fallback + + const directParents = source.edges.filter((e) => e.target === ref) + if (directParents.length === 1 && directParents[0].source !== '0') { + const parentNode = source.nodes.find( + (n) => n.id === directParents[0].source, + ) + const parentApiNode = currentApiNodes?.find( + (n) => n.id === parentNode?.nodeId, + ) + for (const outputType of parentApiNode?.outputTypes ?? []) { + contextMenu.push({ + label: `${t('ui.text.input', 'Input')} ${outputType.key}`, + value: `$input.${outputType.key}`, + isEditable: outputType.isEditable ?? false, + }) + } + } + + const ancestors: string[] = [] + const seenNodes = new Set([ref]) + const queue = [ref] + + while (queue.length > 0) { + const current = queue.shift()! + for (const edge of source.edges) { + if (edge.target !== current || seenNodes.has(edge.source)) { + continue + } + seenNodes.add(edge.source) + queue.push(edge.source) + if (edge.source !== '0') { + ancestors.push(edge.source) + } } - const sourceNode = source.nodes.find((n) => n.id === edge.source) + } + + for (const ancestorID of ancestors) { + const sourceNode = source.nodes.find((n) => n.id === ancestorID) if (!sourceNode) { continue } - const currentApiNodes = apiNodesArg || apiOriginRef.current const apiNode = currentApiNodes?.find( (n) => n.id === sourceNode.nodeId, ) - if (apiNode === undefined) { continue } - const sourceContextMenu = sourceNode.data?.contextMenu - if (sourceContextMenu && sourceContextMenu.length > 0) { - contextMenu = contextMenu.concat(sourceContextMenu) - } + const outputs = apiNode.outputTypes ?? [] for (let j = 0; j < outputs.length; j++) { const outputType = outputs[j] contextMenu.push({ - label: `${t(sourceNode.data?.title || apiNode.name)} ${outputType.key}`, + label: `${t(nodeName(sourceNode, apiNode.name))} ${outputType.key}`, value: `$${apiNode.id}_${sourceNode.id}.${outputType.key}`, isEditable: outputType.isEditable ?? false, })