| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- import { MarkerType, Position, type Edge, type Node } from "@xyflow/react";
- import type { FlowNodeRecord, Phase, PhaseFrame, RunGraph } from "@/lib/types";
- export type CardData = {
- kind: "card";
- item: FlowNodeRecord;
- dimmed: boolean;
- } & Record<string, unknown>;
- export type FrameData = {
- kind: "frame";
- frame: PhaseFrame;
- } & Record<string, unknown>;
- export type CanvasData = CardData | FrameData;
- export const PHASE_LABELS: Record<Phase, string> = {
- "phase-1": "Phase 1",
- "phase-2": "Phase 2",
- "phase-3": "Phase 3",
- delivery: "Final Delivery",
- };
- const EDGE_COLOR = {
- control: "#68707f",
- artifact: "#4ca6d8",
- validation: "#b68cff",
- publication: "#5ec48d",
- };
- export function matchesNode(item: FlowNodeRecord, query: string): boolean {
- const value = query.trim().toLocaleLowerCase();
- if (!value) return true;
- return [
- item.title, item.subtitle, item.id, item.lane, item.status,
- item.task_kind ?? "", item.agent_role ?? "", item.record.model_name,
- ...item.tags,
- ].some((part) => part.toLocaleLowerCase().includes(value));
- }
- export function buildCanvas(
- graph: RunGraph,
- enabledPhases: Set<Phase>,
- query: string,
- ): { nodes: Node<CanvasData>[]; edges: Edge[] } {
- const visibleIds = new Set(
- graph.nodes.filter((item) => enabledPhases.has(item.phase)).map((item) => item.id),
- );
- const frameNodes: Node<CanvasData>[] = graph.frames.map((frame) => {
- const phase = frame.id.replace("frame-", "") as Phase;
- return {
- id: frame.id,
- type: "phaseFrame",
- position: { x: frame.x, y: frame.y },
- data: { kind: "frame", frame },
- style: { width: frame.width, height: frame.height },
- selectable: false,
- draggable: false,
- connectable: false,
- hidden: !enabledPhases.has(phase),
- zIndex: -10,
- };
- });
- const cardNodes: Node<CanvasData>[] = graph.nodes.map((item) => ({
- id: item.id,
- type: "flowCard",
- position: { x: item.x, y: item.y },
- data: { kind: "card", item, dimmed: !matchesNode(item, query) },
- sourcePosition: Position.Right,
- targetPosition: Position.Left,
- style: { width: item.width },
- hidden: !visibleIds.has(item.id),
- zIndex: 2,
- }));
- const edges: Edge[] = graph.edges.map((edge) => ({
- id: edge.id,
- source: edge.source,
- target: edge.target,
- label: edge.label ?? undefined,
- type: "smoothstep",
- animated: edge.animated,
- hidden: !visibleIds.has(edge.source) || !visibleIds.has(edge.target),
- markerEnd: { type: MarkerType.ArrowClosed, width: 16, height: 16, color: EDGE_COLOR[edge.kind] },
- style: { stroke: EDGE_COLOR[edge.kind], strokeWidth: edge.kind === "publication" ? 2.2 : 1.45 },
- labelStyle: { fill: "#9aa0ab", fontSize: 10, fontWeight: 600 },
- labelBgStyle: { fill: "#15171b", fillOpacity: 0.92 },
- labelBgPadding: [5, 3] as [number, number],
- labelBgBorderRadius: 5,
- }));
- return { nodes: [...frameNodes, ...cardNodes], edges };
- }
|