diff --git a/PUBLIC_EXPORT_MANIFEST.json b/PUBLIC_EXPORT_MANIFEST.json index 476e63ee..45fd74a6 100644 --- a/PUBLIC_EXPORT_MANIFEST.json +++ b/PUBLIC_EXPORT_MANIFEST.json @@ -4477,10 +4477,10 @@ "sha256": "0325855bfc97842a259690e0fa165372b7ada9572e923882b45c448164083a93" }, { - "bytes": 6028, + "bytes": 6193, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/lineage/LineageCanvas.tsx", - "sha256": "ca1e4fcd588d076dbc8c142913d49f15d89e5ce470a3d377ba1e99524fd7f485" + "sha256": "1b04e14779128f644afba1950cbda2643c3373bfba4c49b7832f65907e4ad706" }, { "bytes": 4426, @@ -4489,10 +4489,10 @@ "sha256": "d9297b9b9d3a6f7a166271c9d2ab4fdd7c64a660a2dfb10a00cead13663fbfe2" }, { - "bytes": 792, + "bytes": 946, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/lineage/orderLineageNodes.ts", - "sha256": "e78e1ea806367f75f49baa5471ffaff8c7b254057b55e702477ed98caa0e2fcc" + "sha256": "52e307bcbe82fc9f2448fc6d622090eb286d29c74227f72cf5f1502e0ae3c07c" }, { "bytes": 4430, @@ -4927,10 +4927,10 @@ "sha256": "96691c2e1878393f8b976800452b2c45b52cccf46612a1e33689d3a7ad3d8067" }, { - "bytes": 3767, + "bytes": 3949, "mode": "100644", "path": "apps/synth_desktop/src/renderer/src/runtime/traceInspector.ts", - "sha256": "4f9303259d8bcedb47f7a9b50aa3daa1378f072fa0a02253bc753fcb38c7701d" + "sha256": "71f46bb2e6c07b97568af54a59957bccb7b76793a3e1cff8c2a05c04c4f69a4e" }, { "bytes": 945, @@ -7690,7 +7690,7 @@ "policySha256": "f5ef8fbb4560ee9e401c26c94da6f6189732be76291915027bd7ee3f5f5684dd", "schema": "workshop.public-export-manifest.v1", "source": { - "commit": "543aec726c8d54565d82faaf9f35399e89e879d0", - "tree": "a7f439abe88e252972ccc0edd5d6784d3796c606" + "commit": "b39c7f686cb51522179a3c938d1ab7756026c89c", + "tree": "bb76f53df76aa90e60f8565b52d6e1700497b8f5" } } diff --git a/apps/synth_desktop/src/renderer/src/lineage/LineageCanvas.tsx b/apps/synth_desktop/src/renderer/src/lineage/LineageCanvas.tsx index a00060d2..19e70052 100644 --- a/apps/synth_desktop/src/renderer/src/lineage/LineageCanvas.tsx +++ b/apps/synth_desktop/src/renderer/src/lineage/LineageCanvas.tsx @@ -26,6 +26,11 @@ export function LineageCanvas({ label: string; }) { const ranked = useMemo(() => rankDag(nodes, edges), [nodes, edges]); + const rankedById = useMemo(() => { + const byId = new Map(); + for (const node of ranked) if (!byId.has(node.id)) byId.set(node.id, node); + return byId; + }, [ranked]); const ordered = useMemo(() => rankedOrder(ranked), [ranked]); const [view, setView] = useState({ x: 0, y: 0, scale: 1 }); const drag = useRef<{ x: number; y: number; originX: number; originY: number } | null>(null); @@ -141,8 +146,8 @@ export function LineageCanvas({ > {edges.map((edge) => { - const source = ranked.find((node) => node.id === edge.sourceId); - const target = ranked.find((node) => node.id === edge.targetId); + const source = rankedById.get(edge.sourceId); + const target = rankedById.get(edge.targetId); if (!source || !target) return null; const x1 = source.x + NODE_WIDTH / 2; const y1 = source.y + NODE_HEIGHT; diff --git a/apps/synth_desktop/src/renderer/src/lineage/orderLineageNodes.ts b/apps/synth_desktop/src/renderer/src/lineage/orderLineageNodes.ts index 78ac1148..bff5eb17 100644 --- a/apps/synth_desktop/src/renderer/src/lineage/orderLineageNodes.ts +++ b/apps/synth_desktop/src/renderer/src/lineage/orderLineageNodes.ts @@ -7,11 +7,15 @@ export function orderLineageNodes( const byId = new Map(nodes.map((node) => [node.id, node])); const incoming = new Set(edges.map((edge) => edge.targetNodeId)); const ordered: ExperimentNode[] = []; + const visited = new Set(); + const firstEdge = new Map(); + for (const edge of edges) if (!firstEdge.has(edge.sourceNodeId)) firstEdge.set(edge.sourceNodeId, edge); let current: ExperimentNode | undefined = nodes.find((node) => !incoming.has(node.id)) ?? nodes[0]; - while (current && !ordered.some((node) => node.id === current?.id)) { + while (current && !visited.has(current.id)) { ordered.push(current); - const edge = edges.find((item) => item.sourceNodeId === current?.id); + visited.add(current.id); + const edge = firstEdge.get(current.id); current = edge ? byId.get(edge.targetNodeId) : undefined; } - return [...ordered, ...nodes.filter((node) => !ordered.some((item) => item.id === node.id))]; + return [...ordered, ...nodes.filter((node) => !visited.has(node.id))]; } diff --git a/apps/synth_desktop/src/renderer/src/runtime/traceInspector.ts b/apps/synth_desktop/src/renderer/src/runtime/traceInspector.ts index 151c25a9..fd554936 100644 --- a/apps/synth_desktop/src/renderer/src/runtime/traceInspector.ts +++ b/apps/synth_desktop/src/renderer/src/runtime/traceInspector.ts @@ -42,11 +42,15 @@ export function traceInspectability(trace: TraceV5Record): TraceInspectability { return { eligible: true, label: "Inspect" }; } -/** The digest a visual's projection slot is bound to, or null if it is not a trace inspector. */ +/** The digest bound to the projection input (or legacy slot), or null for other visuals. */ export function traceDigestBinding(visual: VisualRecord): string | null { if (visual.templateId !== TRACE_INSPECTOR_TEMPLATE) return null; - const bindings = visual.bindings as { slots?: Array<{ slot?: string; kind?: string; source?: string }> }; - const projection = bindings?.slots?.find((slot) => slot.slot === "projection" && slot.kind === "trace_v5"); + const bindings = visual.bindings as { + inputs?: Array<{ input?: string; kind?: string; source?: string }>; + slots?: Array<{ slot?: string; kind?: string; source?: string }>; + }; + const projection = bindings?.inputs?.find((binding) => binding.input === "projection" && binding.kind === "trace_v5") + ?? bindings?.slots?.find((binding) => binding.slot === "projection" && binding.kind === "trace_v5"); return typeof projection?.source === "string" ? projection.source : null; }