# Web App TanStack Start full-stack React app (Vite + Nitro). Routes in `src/routes/`, auto-generated tree in `src/routeTree.gen.ts` (do not edit). - `/` — Landing page - `/editor` — Main design editor ## Canvas Engine (`src/canvas/`) 14 files + `skia/` subdir with 14 files. ### CanvasKit/Skia Architecture - **GPU-accelerated WASM rendering** — CanvasKit (Skia compiled to WASM) renders all canvas content via WebGL surface - **SkiaEngine class** (`skia-engine.ts`) is the core: owns the render loop, viewport transforms, node flattening, and `SpatialIndex` for hit testing - **Dirty-flag rendering** — `markDirty()` schedules a `requestAnimationFrame` redraw; no continuous rendering loop - **Node flattening** — `syncFromDocument()` walks the PenDocument tree, resolves auto-layout positions via layout engine, and produces flat `RenderNode[]` with absolute coordinates - **SpatialIndex** (`skia-hit-test.ts`) — R-tree backed spatial queries for `hitTest()` (click) and `searchRect()` (marquee selection) - **Coordinate conversion** — `screenToScene()` / `sceneToScreen()` in `skia-viewport.ts` handle viewport ↔ scene transforms - **Event handling** — mouse/keyboard events managed by `SkiaInteractionManager` (`skia-interaction.ts`); hit testing for resize/rotate/arc handles in `skia-hit-handlers.ts`; `skia-canvas.tsx` is the React component (lifecycle, sync, rendering) - **Parent-child transforms** — nodes are flattened to absolute coordinates; transforms propagate to descendants during drag/scale/rotate ### `skia/` Files - `skia-canvas.tsx` — React component: lifecycle, sync effects, wheel zoom, text editing overlay; delegates interaction to `SkiaInteractionManager` - `skia-interaction.ts` — `SkiaInteractionManager` class: all mouse/keyboard interaction state and handlers (select, drag, resize, rotate, draw, marquee, pen tool, arc editing, hover cursor) - `skia-hit-handlers.ts` — Hit test functions: `hitTestHandle` (resize), `hitTestRotation` (rotation zone), `hitTestArcHandle` (ellipse arc) - `skia-engine.ts` — Core rendering engine: `SkiaEngine` class, `syncFromDocument()`, viewport, node flattening, zoom/pan, dirty-flag loop - `skia-renderer.ts` — GPU draw calls: shapes, text, paths, images, selection handles, guides, agent indicators - `skia-init.ts` — CanvasKit WASM loader with CDN fallback - `skia-hit-test.ts` — `SpatialIndex` R-tree for spatial queries - `skia-viewport.ts` — Viewport math - `skia-paint-utils.ts` — Color parsing, gradient creation, text line wrapping - `skia-path-utils.ts` — SVG path to CanvasKit Path conversion - `skia-image-loader.ts` — Async image loading and caching - `skia-overlays.ts` — Selection overlays, hover highlights, dimension labels - `skia-pen-tool.ts` — Pen tool: anchor points, control handles, path building - `skia-font-manager.ts` — Font management ### Shared Canvas Modules - `canvas-sync-lock.ts` — Prevents circular sync loops - `canvas-sync-utils.ts` — `forcePageResync()` utility - `canvas-constants.ts` — Default colors, zoom limits, stroke widths - `canvas-node-creator.ts` — `createNodeForTool`, `isDrawingTool` - `canvas-layout-engine.ts` — Auto-layout (delegates to `@zseven-w/pen-core`) - `canvas-text-measure.ts` — Text width/height estimation, CJK detection - `font-utils.ts`, `node-helpers.ts` — Re-exports from pen-core - `insertion-indicator.ts`, `selection-context.ts`, `agent-indicator.ts`, `use-layout-indicator.ts`, `skia-engine-ref.ts` ## Zustand Stores (`src/stores/`) - `canvas-store.ts` — UI/tool/selection/viewport/clipboard/interaction state, `activePageId` - `document-store.ts` — PenDocument tree CRUD, variable CRUD, component management (all with history) - `document-store-pages.ts` — Page actions: add, remove, rename, reorder, duplicate - `document-tree-utils.ts` — Re-exports tree helpers and clone utilities from `@zseven-w/pen-core` - `history-store.ts` — Undo/redo (max 300 states), batch mode - `ai-store.ts` — Chat messages, streaming state, model selection - `agent-settings-store.ts` — AI provider config, MCP CLI integrations, localStorage persistence - `uikit-store.ts` — UIKit management - `theme-preset-store.ts` — Theme preset management ## Components (`src/components/`) - **`editor/`** — Editor UI: editor-layout, toolbar, boolean-toolbar, tool-button, shape-tool-dropdown, top-bar, status-bar, page-tabs, update-ready-banner - **`panels/`** — 32 files: layer panel, property panel, fill/stroke/corner/size/text/effects/export/layout/appearance sections, AI chat panel, code panel, component browser, variables panel - **`shared/`** — Reusable UI: ColorPicker, NumberInput, SectionHeader, ExportDialog, SaveDialog, AgentSettingsDialog, IconPickerDialog, VariablePicker, FigmaImportDialog, FontPicker, LanguageSelector - **`icons/`** — Provider/brand logos - **`ui/`** — shadcn/ui primitives ## AI Services (`src/services/ai/`) 35 files + `role-definitions/` + `design-principles/` subdirs: - `ai-service.ts` — Main AI chat API wrapper, model negotiation, provider selection - `ai-prompts.ts` — System prompts for design generation - `ai-types.ts` — ChatMessage, ChatAttachment, AIDesignRequest, OrchestratorPlan - `model-profiles.ts` — Adapts thinking mode, effort, timeouts per model tier - `design-generator.ts` — Top-level `generateDesign`/`generateDesignModification` - `design-parser.ts` — JSON/JSONL parsing - `design-canvas-ops.ts` — Canvas mutation operations - `design-node-sanitization.ts` — Node merging (re-exports `deepCloneNode` from pen-core) - `design-validation.ts` / `design-pre-validation.ts` / `design-validation-fixes.ts` — Post-generation validation - `icon-resolver.ts` — Auto-resolves icon names to Lucide SVG paths - `orchestrator.ts` / `orchestrator-sub-agent.ts` / `orchestrator-prompts.ts` — Spatial decomposition orchestrator - `context-optimizer.ts` — Chat history trimming ## Hooks (`src/hooks/`) - `use-keyboard-shortcuts.ts` — Global keyboard: tools, clipboard, undo/redo, save, z-order, boolean ops - `use-electron-menu.ts` — Electron native menu IPC listener - `use-figma-paste.ts` — Figma clipboard paste - `use-file-drop.ts` — File drag-and-drop - `use-mcp-sync.ts` — MCP live canvas sync - `use-system-fonts.ts` — System font detection ## MCP Server MCP server code lives in `packages/pen-mcp/`. The web app communicates with it via HTTP API routes (`server/api/mcp/*.ts`) using `server/utils/mcp-server-manager.ts` to spawn/manage the server process. ## UIKit (`src/uikit/`) - `built-in-registry.ts` — Default built-in UIKit - `kit-import-export.ts` — Import/export UIKits from .pen files - `kit-utils.ts` — Extract components, find reusable nodes (re-exports `deepCloneNode` from pen-core) ## Utilities (`src/utils/`) File operations: save/open .pen, export PNG/SVG, node clone (re-exports `cloneNodesWithNewIds` from pen-core), pen file normalization, SVG parser, syntax highlight, boolean operations, `app-storage.ts`, `arc-path.ts`, `theme-preset-io.ts`, `id.ts` ### AI Prompt Skill System Prompts for AI design generation live in `packages/pen-ai-skills/skills/` as Markdown files with YAML frontmatter. The skill engine loads prompts by phase and intent: - **Phases:** `planning`, `generation`, `validation`, `maintenance` — each phase loads different base skills - **Intent matching:** Domain skills (landing-page, dashboard, etc.) are loaded when keywords match the user message - **Budget control:** Token budget per phase prevents context overflow **Adding a new skill:** Create a `.md` file in the appropriate `skills/` subdirectory with frontmatter (name, phase, trigger, priority, budget, category). The Vite plugin auto-compiles on save. **Usage:** `import { resolveSkills } from '@zseven-w/pen-ai-skills'` → `resolveSkills('generation', userMessage, { flags, dynamicContent })` ## Server API (`server/`) - **`api/ai/`** — Nitro API (11 files): streaming chat, generation, agent connection, validation, MCP install, icon resolution, image generation/search. Supports Anthropic API key or Claude Agent SDK (local OAuth) - **`utils/`** — Server utilities: Claude CLI resolver, OpenCode/Codex/Copilot clients, MCP server manager, sync state, server logger