Implement Phase 2: Next.js + React Flow frontend MVP
- Scaffold Next.js 15 app with TypeScript, Tailwind, App Router - Install @xyflow/react, Zustand, Lucide icons, nanoid - Define council types (AgentNodeData, CouncilBlueprint, WSMessage, etc.) - Implement Zustand store for canvas and run state - Build custom AgentNode component (label, system prompt, model badge, tool chips, active pulse) - Build ConditionalEdge component (dashed indigo line with condition label) - Build NodeSidebar (drag-and-drop + click to add agents) - Build NodeSettingsPanel (name, system prompt, model selector, tool toggles) - Build ArchitectCanvas (React Flow canvas with drop zone, minimap, controls) - Build blueprint parser (React Flow JSON ↔ CouncilBlueprint JSON) - Build API client for FastAPI backend (CRUD + run endpoints) - Build useCouncilWebSocket hook for live agent status via WebSocket - Build Tab A: Rat-Architekt (canvas builder with save/export toolbar) - Build Tab B: Konferenzzimmer (execution view with live diagram + result panel) - Add NavTabs navigation with CouncilOS branding - All TypeScript checks passing https://claude.ai/code/session_01EkbecUVn7esdxLCXxVVRDX
This commit is contained in:
parent
06aec41a8a
commit
216fdd9589
30 changed files with 8237 additions and 0 deletions
71
frontend/app/utils/blueprint-parser.ts
Normal file
71
frontend/app/utils/blueprint-parser.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Parser: React Flow graph state → CouncilBlueprint JSON (backend format)
|
||||
import { Node, Edge } from "@xyflow/react";
|
||||
import { AgentNodeData, BlueprintEdge, BlueprintNode, CouncilBlueprint, EdgeType } from "@/app/types/council";
|
||||
|
||||
export function parseGraphToBlueprint(
|
||||
nodes: Node<AgentNodeData>[],
|
||||
edges: Edge[],
|
||||
name: string,
|
||||
existingId?: string
|
||||
): CouncilBlueprint {
|
||||
const blueprintNodes: BlueprintNode[] = nodes.map((node) => ({
|
||||
id: node.id,
|
||||
label: node.data.label,
|
||||
systemPrompt: node.data.systemPrompt,
|
||||
model: node.data.model,
|
||||
tools: node.data.tools,
|
||||
position: { x: node.position.x, y: node.position.y },
|
||||
}));
|
||||
|
||||
const blueprintEdges: BlueprintEdge[] = edges.map((edge) => {
|
||||
const edgeType: EdgeType = (edge.data?.type as EdgeType) ?? "linear";
|
||||
const result: BlueprintEdge = {
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
type: edgeType,
|
||||
};
|
||||
if (edgeType === "conditional" && edge.data?.condition) {
|
||||
result.condition = edge.data.condition as string;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
id: existingId,
|
||||
name,
|
||||
nodes: blueprintNodes,
|
||||
edges: blueprintEdges,
|
||||
};
|
||||
}
|
||||
|
||||
// Reverse: CouncilBlueprint → React Flow nodes/edges (for loading saved blueprints)
|
||||
export function parseBlueprintToGraph(blueprint: CouncilBlueprint): {
|
||||
nodes: Node<AgentNodeData>[];
|
||||
edges: Edge[];
|
||||
} {
|
||||
const nodes: Node<AgentNodeData>[] = blueprint.nodes.map((n) => ({
|
||||
id: n.id,
|
||||
type: "agentNode",
|
||||
position: n.position,
|
||||
data: {
|
||||
label: n.label,
|
||||
systemPrompt: n.systemPrompt,
|
||||
model: n.model,
|
||||
tools: n.tools,
|
||||
},
|
||||
}));
|
||||
|
||||
const edges: Edge[] = blueprint.edges.map((e) => ({
|
||||
id: e.id,
|
||||
source: e.source,
|
||||
target: e.target,
|
||||
type: e.type === "conditional" ? "conditionalEdge" : "default",
|
||||
data: { type: e.type, condition: e.condition },
|
||||
label: e.type === "conditional" ? e.condition : undefined,
|
||||
animated: e.type === "conditional",
|
||||
}));
|
||||
|
||||
return { nodes, edges };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue