|
|
@@ -1,92 +0,0 @@
|
|
|
-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 };
|
|
|
-}
|
|
|
-
|