diff --git a/src/components/admin/narrative-flow/FormatNode.tsx b/src/components/admin/narrative-flow/FormatNode.tsx index 197e428..a193d2f 100644 --- a/src/components/admin/narrative-flow/FormatNode.tsx +++ b/src/components/admin/narrative-flow/FormatNode.tsx @@ -3,13 +3,30 @@ import { type NodeProps } from "@xyflow/react"; import { AlignLeftIcon } from "lucide-react"; import { NodeHandles } from "./NodeHandles"; +import { NODE_SOCKETS, type SocketDef } from "./types"; + +function parseTemplateArgs(template: string): SocketDef[] { + const matches = template.match(/\{(\d+)\}/g); + if (!matches) return []; + const unique = [...new Set(matches)].sort(); + return unique.map((m) => ({ + id: `arg-${m.slice(1, -1)}`, + type: "any" as const, + label: m, + })); +} export function FormatNode({ data }: NodeProps) { const d = data as { template?: string }; + const template = d.template ?? "{0}"; + const dynamicInputs: SocketDef[] = [ + ...(NODE_SOCKETS.format?.inputs.filter((s) => s.id === "template") ?? []), + ...parseTemplateArgs(template), + ]; return (
- +
@@ -17,7 +34,7 @@ export function FormatNode({ data }: NodeProps) {
- {d.template || "{0} {1}"} + {template}
); diff --git a/src/components/admin/narrative-flow/NarrativeFlowEditor.tsx b/src/components/admin/narrative-flow/NarrativeFlowEditor.tsx index fdbcf1a..e328ed8 100644 --- a/src/components/admin/narrative-flow/NarrativeFlowEditor.tsx +++ b/src/components/admin/narrative-flow/NarrativeFlowEditor.tsx @@ -119,10 +119,14 @@ export function NarrativeFlowEditor() { const sourceSocket = sourceSockets.outputs.find((s) => s.id === connection.sourceHandle); const targetSocket = targetSockets.inputs.find((s) => s.id === connection.targetHandle); - if (!sourceSocket || !targetSocket) return false; - if (sourceSocket.type === "any" || targetSocket.type === "any") return true; - return sourceSocket.type === targetSocket.type; + // Dynamic handles (arg-*, option-*) are treated as "any" + const sourceType = sourceSocket?.type ?? (connection.sourceHandle.startsWith("arg-") || connection.sourceHandle.startsWith("option-") ? "any" : null); + const targetType = targetSocket?.type ?? (connection.targetHandle.startsWith("arg-") || connection.targetHandle.startsWith("option-") ? "any" : null); + + if (sourceType === null || targetType === null) return false; + if (sourceType === "any" || targetType === "any") return true; + return sourceType === targetType; }, [nodes], ); diff --git a/src/components/admin/narrative-flow/NodeHandles.tsx b/src/components/admin/narrative-flow/NodeHandles.tsx index afbc823..5cf33c6 100644 --- a/src/components/admin/narrative-flow/NodeHandles.tsx +++ b/src/components/admin/narrative-flow/NodeHandles.tsx @@ -1,21 +1,20 @@ "use client"; import { TypedHandle } from "./TypedHandle"; -import { NODE_SOCKETS } from "./types"; +import { NODE_SOCKETS, type SocketDef } from "./types"; interface NodeHandlesProps { nodeType: string; - dynamicOutputs?: Array<{ id: string }>; + dynamicInputs?: SocketDef[]; + dynamicOutputs?: SocketDef[]; } -export function NodeHandles({ nodeType, dynamicOutputs }: NodeHandlesProps) { +export function NodeHandles({ nodeType, dynamicInputs, dynamicOutputs }: NodeHandlesProps) { const config = NODE_SOCKETS[nodeType]; if (!config) return null; - const inputs = config.inputs; - const outputs = dynamicOutputs - ? dynamicOutputs.map((o) => ({ ...o, type: "flow" as const })) - : config.outputs; + const inputs = dynamicInputs ?? config.inputs; + const outputs = dynamicOutputs ?? config.outputs; const maxSlots = Math.max(inputs.length, outputs.length, 1); @@ -28,6 +27,7 @@ export function NodeHandles({ nodeType, dynamicOutputs }: NodeHandlesProps) { key={socket.id} id={socket.id} socketType={socket.type} + label={socket.label} isInput={true} style={{ top: `${topPercent}%` }} /> @@ -40,6 +40,7 @@ export function NodeHandles({ nodeType, dynamicOutputs }: NodeHandlesProps) { key={socket.id} id={socket.id} socketType={socket.type} + label={socket.label} isInput={false} style={{ top: `${topPercent}%` }} /> diff --git a/src/components/admin/narrative-flow/TypedHandle.tsx b/src/components/admin/narrative-flow/TypedHandle.tsx index 1741081..0593e23 100644 --- a/src/components/admin/narrative-flow/TypedHandle.tsx +++ b/src/components/admin/narrative-flow/TypedHandle.tsx @@ -7,26 +7,48 @@ interface TypedHandleProps { id: string; socketType: SocketType; isInput: boolean; + label?: string; style?: React.CSSProperties; } -export function TypedHandle({ id, socketType, isInput, style }: TypedHandleProps) { +export function TypedHandle({ id, socketType, isInput, label, style }: TypedHandleProps) { const color = SOCKET_COLORS[socketType]; const isFlow = socketType === "flow"; return ( - + > + + {label && ( + + {label} + + )} + ); }