venice / src /lib /canvas-model.ts
divyamagrawal06's picture
Mirror Venice frontend
6fffa8d verified
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<VeniceNodeData, "veniceImage">;
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<StoredCanvas>;
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,
},
};
}