import type { Edge, Node } from "@xyflow/react"; export type VeniceSource = { title: string; url: string; snippet: string; }; export type VeniceVisualFact = { label: string; detail: string; }; export type GenerateNodeResponse = { id: string; title: string; imageUrl: string; imagePrompt: string; summary: string; visualFacts: VeniceVisualFact[]; query: string; sources: VeniceSource[]; suggestedBranches: string[]; }; export type VeniceNodeData = { title: string; imageUrl: string; imagePrompt: string; summary: string; visualFacts: VeniceVisualFact[]; query: string; sources: VeniceSource[]; suggestedBranches: string[]; parentId?: string; }; export type VeniceCanvasNode = Node; export type StoredCanvas = { nodes: VeniceCanvasNode[]; edges: Edge[]; }; export const NODE_WIDTH = 760; export const NODE_HEIGHT = 820; export const BRANCH_X_GAP = 880; export const BRANCH_Y_GAP = 560; export function createCanvasNodeFromGeneration( generation: GenerateNodeResponse, position: { x: number; y: number }, parentId?: string, ): VeniceCanvasNode { return { id: generation.id, type: "veniceImage", position, data: { title: generation.title, imageUrl: generation.imageUrl, imagePrompt: generation.imagePrompt, summary: generation.summary, visualFacts: generation.visualFacts, query: generation.query, sources: generation.sources, suggestedBranches: generation.suggestedBranches, parentId, }, }; } export function getBranchPosition( parentPosition: { x: number; y: number }, siblingIndex: number, ): { x: number; y: number } { return { x: parentPosition.x + BRANCH_X_GAP, y: parentPosition.y + siblingIndex * BRANCH_Y_GAP, }; } export function buildCanvasEdge(parentId: string, childId: string): Edge { return { id: `${parentId}-${childId}`, source: parentId, target: childId, type: "smoothstep", animated: true, style: { stroke: "#7ec8c2", strokeWidth: 2, }, }; } export function normalizeStoredCanvas(stored: unknown): StoredCanvas { if (!stored || typeof stored !== "object") { return { nodes: [], edges: [] }; } const canvas = stored as Partial; return { nodes: Array.isArray(canvas.nodes) ? canvas.nodes.map(normalizeStoredNode) : [], edges: Array.isArray(canvas.edges) ? canvas.edges : [], }; } function normalizeStoredNode(node: VeniceCanvasNode): VeniceCanvasNode { const data = node.data || ({} as VeniceNodeData); const query = data.query || data.title || "this topic"; return { ...node, type: "veniceImage", data: { title: data.title || query, imageUrl: data.imageUrl || "", imagePrompt: data.imagePrompt || "", summary: data.summary || `Generated visual search node for ${query}.`, visualFacts: Array.isArray(data.visualFacts) ? data.visualFacts : [], query, sources: Array.isArray(data.sources) ? data.sources : [], suggestedBranches: Array.isArray(data.suggestedBranches) ? data.suggestedBranches : [], parentId: data.parentId, }, }; }