Files
zui/ARCHITECTURE.md
2026-03-28 22:20:19 +01:00

9.2 KiB

Architecture Overview

1. Executive Summary

Zui is a node-based visual editor for composing configs, templates, variables, and AI-generated content. The frontend is a React 18 SPA backed by a thin Express API for AI agent calls. All graph data is persisted in localStorage; the backend is stateless.


2. Technology Stack

Layer Technology
Frontend framework React 18, TypeScript (strict), Vite 7
Graph canvas @xyflow/react v12 (React Flow)
State management Zustand 5 (command/reducer pattern)
Styling Tailwind CSS + Shadcn UI (@radix-ui/*)
Rich text BlockNote 0.36 with custom blocks
Templating Nunjucks
Graph traversal graphology + graphology-traversal
Routing React Router v6
Backend Node.js 20+, TypeScript, Express 4
AI SDK Vercel AI SDK v4 + @ai-sdk/openai
Testing Vitest, React Testing Library

3. Project Structure

frontend/src/
├── main.tsx                    # Entry: registers node types & config types, mounts app
├── app/
│   ├── canvas/                 # CanvasPage, canvasStore (Zustand), canvasStore.test.ts
│   ├── kosmos/                 # KosmosPage, sidebar, AI settings, KosmosContext
│   └── recollections/
│       ├── RecollectionsPage   # Workspace list
│       ├── RecollectionLayout  # Shared layout (sidebar + outlet)
│       ├── flux/               # Flux view (graph canvas)
│       ├── logos/              # Logos view (BlockNote rich text)
│       └── katalogos/          # Katalogos view (artifact gallery)
├── components/
│   ├── graph/                  # AnimatedEdge, BaseNode, handles, keyboard shortcuts
│   ├── nodes/                  # One folder per node type
│   └── ui/                     # Shadcn primitives
├── hooks/                      # useGraphStateWithHistory, useResizeHeight, …
└── lib/
    └── graph/
        ├── nodeRegistry.ts     # Mutable plugin registry
        ├── registerBuiltinNodes.tsx
        ├── nodeTypeBuilder.ts  # Fluent builder for node descriptors
        ├── configTypes.ts      # Output type registry (plantuml, markdown, wireframe)
        ├── rendering.ts        # 3-step rendering pipeline contract
        ├── state.ts            # Graph state types
        └── canvasStore/        # Reducer + selectors

backend/src/
├── index.ts                    # Express app, CORS, routes, error handler
├── routes/agentRoutes.ts       # POST /api/agent, POST /api/agent/stream
├── services/agentService.ts    # LLM call logic (Vercel AI SDK)
├── repositories/cacheRepository.ts
├── models/index.ts
└── middleware/rateLimiter.ts

4. Key Architectural Patterns

4.1 Command / Reducer Store (canvasStore)

The Zustand canvas store uses a pure reducer (canvasStoreReducer) with a discriminated union of CanvasCommand types. All mutations go through dispatch(command). In dev mode every command is logged to the console.

The store has three slices:

  • graph — nodes and edges (the React Flow state)
  • path — trigger pulse for edge animation
  • ui — renaming state, fullscreen node, pending connection

4.2 Delta-based Undo / Redo

useGraphStateWithHistory computes inverse deltas (only the nodes/edges that changed) rather than snapshotting the full graph. Maximum 100 history entries. Drag operations commit a pre-drag snapshot on onNodeDragStop.

4.3 Node Plugin Registry

Node types are registered via a mutable registry in nodeRegistry.ts. The built-ins are registered at app init from registerBuiltinNodes.tsx. Adding a new node type requires:

  1. Implement a React component for the node.
  2. Create a descriptor using NodeTypeBuilder.
  3. Call registerNodeType(descriptor) — no edits to core code.

4.4 Fluent Builder for Node Descriptors

NodeTypeBuilder provides a type-safe method-chaining API (idPrefix, withInputOutput, classification, allowedSourceTypes, help, menu, withFullscreen, sourceRenderingLogic, …). Required fields are validated at .build().

4.5 Three-Step Rendering Pipeline

Defined as an explicit contract in rendering.ts:

  1. Resolve — source node (Config/Agent) produces { resolved, outputTypeId, reasoning? }
  2. Render — output type handler converts the resolved string (PlantUML → Kroki SVG, Markdown → HTML, Wireframe → SVG)
  3. Display — the Rendering node shows an image viewport or scrollable HTML

4.6 Node Classification System

Four metaphorical classes drive the create menu grouping:

Class Node type Metaphor
psyche Variable source / input
physis Data (CSV) physical data
pneuma Config / Render transformation
archon Agent AI / LLM

4.7 Recollections (Workspaces)

Each workspace (recollection) is stored in localStorage:

Key Content
zui_platform_recollections Metadata index (id, name, icon, timestamps)
zui_graph_<id> Flux graph (nodes + edges)
zui_logos_<id> BlockNote rich text document
zui_render_cache_<id> Rendered artifact cache

Legacy key prefix emanations is migrated to recollections on first load.

4.8 User-configurable AI Connection

KosmosContext persists aiConnection (provider, baseURL, model, apiKey) in localStorage under zui_ai_connection. The Agent node forwards this to the backend with each request, so users can switch between OpenAI and local LLMs (LM Studio, Ollama) from the UI without env variable changes.

4.9 Diagram Rendering via Kroki

PlantUML diagrams are rendered by proxying to https://kroki.io via /api/kroki/plantuml/svg. This proxy is configured in both the Vite dev server and Nginx production config. Timeout: 15 seconds.


5. Data Flow

User interaction
  → dispatch(CanvasCommand)              [canvasStore reducer]
  → Zustand state update                 [graph / path / ui slices]
  → React re-render                      [React Flow nodes/edges]

Rendering node triggered
  → Resolve source node                  [Config/Agent → resolved string]
  → Render output type                   [plantuml/markdown/wireframe]
    → Kroki proxy  (PlantUML)            [GET /api/kroki/plantuml/svg]
    → Agent backend  (AI)               [POST /api/agent or /api/agent/stream]
  → Display artifact                     [image / HTML / SVG viewport]

Agent node (streaming)
  → POST /api/agent/stream               [Express → agentService]
  → Vercel AI SDK streams SSE            [backend → frontend]
  → useStreamingContent hook consumes    [updates node data]

6. Backend Architecture

The backend is intentionally thin and stateless. It has three responsibilities:

  1. Proxy AI calls — forwards prompts to the configured LLM (OpenAI or OpenAI-compatible).
  2. Stream responsesPOST /api/agent/stream uses pipeTextStreamToResponse from the Vercel AI SDK.
  3. Health checkGET /health for Docker/orchestration readiness probes.

The InMemoryCache and rateLimiter middleware are implemented but not yet wired into the agent routes.


7. Contribution Workflow

  1. Fork the repository and clone locally.
  2. Install dependencies: cd frontend && npm install and cd backend && npm install.
  3. Run the dev servers (see README).
  4. Create a feature branch: git checkout -b feat/your-feature.
  5. Follow the Coding Standards (see CONTRIBUTING.md).
  6. Submit a Pull Request with a descriptive title, linked issue(s), updated tests, and documentation changes if relevant.

PR Review Checklist

  • Readability: clear naming, minimal nesting
  • TypeScript: no any, strict types throughout
  • Performance: no unnecessary re-renders, proper memoization
  • Accessibility: semantic HTML, ARIA attributes where needed
  • Security: no hardcoded secrets, input validated at boundaries

8. Coding Standards

  • TypeScript: strict mode (strict, noImplicitAny, noUnusedLocals).
  • Naming: PascalCase for components, camelCase for functions/variables, UPPER_SNAKE_CASE for constants.
  • File structure: domain-grouped under src/app/<domain>/ and src/components/<category>/.
  • Formatting: Prettier via frontend/prettier.config.cjs; line length ≤ 120.
  • Comments: JSDoc for public APIs; inline only where logic is non-obvious.

9. Quick Reference for AI Agents

Topic Location
Entry point frontend/src/main.tsx
Canvas store frontend/src/app/canvas/canvasStore.ts
Store reducer frontend/src/app/canvas/canvasStoreReducer.ts
Node registry frontend/src/lib/graph/nodeRegistry.ts
Node builder frontend/src/lib/graph/nodeTypeBuilder.ts
Rendering pipeline frontend/src/lib/graph/rendering.ts
Built-in node registration frontend/src/lib/graph/registerBuiltinNodes.tsx
AI settings context frontend/src/app/kosmos/KosmosContext.tsx
Backend entry backend/src/index.ts
Agent service backend/src/services/agentService.ts

This document is a living reference; update it when architectural decisions change.