diff --git a/CLAUDE.md b/CLAUDE.md index 67147827a..ea7f52c44 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,8 +68,9 @@ PenDocument (source of truth) ### Key Modules -- **`src/canvas/`** — Fabric.js integration (16 files): +- **`src/canvas/`** — Fabric.js integration (25 files): - `fabric-canvas.tsx` — Canvas component initialization + - `use-fabric-canvas.ts` — Canvas initialization hook - `canvas-object-factory.ts` — Creates Fabric objects from PenNodes (rect, ellipse, line, polygon, path, text, image, frame, group) - `canvas-object-sync.ts` — Syncs individual object properties between Fabric and store - `canvas-sync-lock.ts` — Prevents circular sync loops @@ -79,12 +80,20 @@ PenDocument (source of truth) - `use-canvas-sync.ts` — Bidirectional PenDocument ↔ Fabric.js sync, node flattening with parent offsets, variable resolution via `resolveNodeForCanvas()` - `use-canvas-viewport.ts` — Wheel zoom, space+drag panning, tool cursor switching, selection toggling per tool - `use-canvas-selection.ts` — Selection sync between Fabric objects and canvas-store + - `use-canvas-hover.ts` — Hover state management for objects - `use-canvas-guides.ts` — Smart alignment guides with snapping - `guide-utils.ts` — Guide calculation and rendering - `pen-tool.ts` — Bezier pen tool: anchor points, control handles, path closure, preview rendering - `parent-child-transform.ts` — Propagates parent transforms (move/scale/rotate) to children proportionally - `use-dimension-label.ts` — Shows size/position labels during object manipulation - `use-frame-labels.ts` — Renders frame names and boundaries on canvas + - `use-entered-frame-overlay.ts` — Visual overlay when entering a frame for editing + - `use-layout-indicator.ts` — Layout indicator rendering during drag operations + - `insertion-indicator.ts` — Insertion point indicator for layout drop targets + - `drag-into-layout.ts` — Drag-and-drop into auto-layout frames with insertion detection + - `drag-reparent.ts` — Reparenting nodes during drag operations + - `layout-reorder.ts` — Reorder children within layout frames during drag + - `selection-context.ts` — Selection context management for multi-select operations - **`src/variables/`** — Design variables system (2 files): - `resolve-variables.ts` — Core resolution utilities: `resolveVariableRef`, `resolveNodeForCanvas`, `getDefaultTheme`, `isVariableRef`; resolves `$variable` references to concrete values for canvas rendering with circular reference guards - `replace-refs.ts` — `replaceVariableRefsInTree`: recursively walk node tree to replace/resolve `$refs` when renaming or deleting variables (covers opacity, gap, padding, fills, strokes, effects, text) @@ -94,14 +103,16 @@ PenDocument (source of truth) - `history-store.ts` — Undo/redo (max 300 states), batch mode for grouped operations - `ai-store.ts` — Chat messages, streaming state, generated code, model selection - `agent-settings-store.ts` — AI provider config (Anthropic/OpenAI), MCP CLI integrations, localStorage persistence -- **`src/types/`** — Type system: +- **`src/types/`** — Type system (7 files): - `pen.ts` — PenDocument/PenNode (frame, group, rectangle, ellipse, line, polygon, path, text, image, ref), ContainerProps; `PenDocument.variables` and `PenDocument.themes` - - `canvas.ts` — ToolType (select, frame, rectangle, ellipse, line, polygon, path, text, hand), ViewportState, SelectionState - - `styles.ts` — PenFill (solid, linear_gradient, radial_gradient), PenStroke, PenEffect (shadow, blur) + - `canvas.ts` — ToolType (select, frame, rectangle, ellipse, line, polygon, path, text, hand), ViewportState, SelectionState, CanvasInteraction + - `styles.ts` — PenFill (solid, linear_gradient, radial_gradient), PenStroke, PenEffect (shadow, blur), BlendMode, StyledTextSegment - `variables.ts` — `VariableDefinition` (type + value), `ThemedValue` (value per theme), `VariableValue` - - `agent-settings.ts` — AI provider config types + - `agent-settings.ts` — AI provider config types (`AIProviderType`, `AIProviderConfig`, `MCPCliIntegration`, `GroupedModel`) + - `electron.d.ts` — Electron IPC bridge types (file dialogs, save operations) + - `opencode-sdk.d.ts` — Type declarations for @opencode-ai/sdk - **`src/components/editor/`** — Editor UI (6 files): editor-layout, toolbar (with variables panel toggle), tool-button, shape-tool-dropdown (rectangle/ellipse/line/path + icon picker + image import), top-bar, status-bar -- **`src/components/panels/`** — Panels (17 files): +- **`src/components/panels/`** — Panels (18 files): - `layer-panel.tsx` / `layer-item.tsx` / `layer-context-menu.tsx` — Tree view with drag-and-drop reordering and drop-into-children (above/below/inside), visibility/lock toggles, context menu, rename - `property-panel.tsx` — Unified property panel - `fill-section.tsx` — Solid + gradient fill, variable picker integration for color binding @@ -110,6 +121,7 @@ PenDocument (source of truth) - `size-section.tsx` — Position, size, rotation - `text-section.tsx` — Font, size, weight, spacing, alignment - `effects-section.tsx` — Shadow and blur + - `export-section.tsx` — Per-layer export to PNG/SVG with scale options (1x/2x/3x) - `layout-section.tsx` — Auto-layout (none/vertical/horizontal), gap, padding, justify, align; variable picker for gap/padding binding - `appearance-section.tsx` — Opacity, visibility, lock, flip; variable picker for opacity binding - `ai-chat-panel.tsx` / `chat-message.tsx` — AI chat with markdown, design block collapse, apply design @@ -117,14 +129,25 @@ PenDocument (source of truth) - `variables-panel.tsx` — Design variables management: theme axes as tabs, variant columns, resizable floating panel, add/rename/delete themes and variants - `variable-row.tsx` — Individual variable row: type icon, editable name, per-theme-variant value cells (color picker, number input, text input), context menu - **`src/components/shared/`** — Reusable UI (9 files): ColorPicker, NumberInput, DropdownSelect, SectionHeader, ExportDialog, SaveDialog, AgentSettingsDialog, IconPickerDialog, VariablePicker -- **`src/components/icons/`** — Provider logos: ClaudeLogo, OpenAILogo +- **`src/components/icons/`** — Provider logos: ClaudeLogo, OpenAILogo, OpenCodeLogo - **`src/components/ui/`** — shadcn/ui primitives: Button, Select, Separator, Slider, Switch, Toggle, Tooltip -- **`src/services/ai/`** — AI chat service, design prompts, design-to-node generation, AI types +- **`src/services/ai/`** — AI services (8 files): + - `ai-service.ts` — Main AI chat API wrapper, model negotiation, provider selection + - `ai-prompts.ts` — System prompts for design generation, context building + - `ai-types.ts` — `ChatMessage`, `AIDesignRequest`, streaming response types + - `design-generator.ts` — Converts AI JSONL to PenNodes, applies variables, animation coordination + - `design-animation.ts` — Fade-in animation coordination for generated design nodes + - `orchestrator.ts` — Parallel design generation: decomposes requests into spatial sub-tasks, JSONL streaming, fallback to single-call + - `orchestrator-prompts.ts` — Ultra-lightweight orchestrator prompt for spatial decomposition + - `context-optimizer.ts` — Chat history trimming, sliding window to prevent unbounded context growth - **`src/services/codegen/`** — React+Tailwind and HTML+CSS code generators (output `var(--name)` for `$variable` refs), CSS variables generator - **`src/hooks/`** — `use-keyboard-shortcuts` (global keyboard event handling: tools, clipboard, undo/redo, save, select all, delete, arrow nudge, z-order) - **`src/lib/`** — Utility functions (`utils.ts` with `cn()` for class merging) - **`src/utils/`** — File operations (save/open .pen), export (PNG/SVG), node clone, pen file normalization (format fixes only, preserves `$variable` refs), SVG parser (import SVG to editable PenNodes), syntax highlight -- **`server/api/ai/`** — Nitro server API: `chat.ts` (streaming SSE with thinking state), `generate.ts` (non-streaming generation), `connect-agent.ts` (Claude Code/Codex CLI connection), `models.ts` (model definitions). Supports Anthropic API key or Claude Agent SDK (local OAuth) as dual providers +- **`server/api/ai/`** — Nitro server API: `chat.ts` (streaming SSE with thinking state), `generate.ts` (non-streaming generation), `connect-agent.ts` (Claude Code/Codex CLI/OpenCode connection), `models.ts` (model definitions). Supports Anthropic API key or Claude Agent SDK (local OAuth) as dual providers +- **`server/utils/`** — Server utilities: + - `resolve-claude-cli.ts` — Resolves standalone `claude` binary path (handles Nitro bundling issues with SDK's `import.meta.url`) + - `opencode-client.ts` — Shared OpenCode client manager, reuses server on port 4096 with random port fallback ### Fabric.js v7 Gotchas @@ -160,7 +183,7 @@ Tailwind CSS v4 imported via `src/styles.css`. UI primitives from shadcn/ui (`sr ### Electron Desktop App -- **`electron/main.ts`** — Main process: window creation, Nitro server fork, IPC for native file dialogs, macOS traffic-light padding +- **`electron/main.ts`** — Main process: window creation, Nitro server fork, IPC for native file dialogs, macOS traffic-light padding (auto-hidden in fullscreen) - **`electron/preload.ts`** — Context bridge for renderer ↔ main IPC - **`electron-builder.yml`** — Packaging config: macOS (dmg/zip), Windows (nsis/portable), Linux (AppImage/deb) - **`scripts/electron-dev.ts`** — Dev workflow: starts Vite → waits for port 3000 → compiles electron/ with esbuild → launches Electron diff --git a/README.md b/README.md index 7c9b1bdc1..aa183f64e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Available as a **web app** and **desktop app** (macOS / Windows / Linux via Elec - Smart alignment guides with edge, center, and distance snapping - Dimension labels during object manipulation - Frame labels and boundary visualization +- Double-click to enter frames with visual overlay +- Advanced drag-and-drop: drag into auto-layout frames with insertion indicators, reparenting, reorder within layout ### Drawing Tools @@ -32,6 +34,7 @@ Available as a **web app** and **desktop app** (macOS / Windows / Linux via Elec - Effects: shadow and blur - Auto-layout: direction, gap, padding, justify-content, align-items - Variable binding: bind any property to a design variable via variable picker +- Per-layer export: export individual layers to PNG/SVG with scale options (1x/2x/3x) ### Design Variables & Tokens @@ -85,10 +88,12 @@ Available as a **web app** and **desktop app** (macOS / Windows / Linux via Elec - Built-in AI chat panel (Cmd+J) - AI-powered design generation from text prompts +- Orchestrator-based parallel design generation: decomposes requests into spatial sub-tasks for faster output - Design block preview with "Apply Design" action -- Streaming responses with thinking state +- Streaming responses with thinking state and JSONL real-time canvas insertion +- Context optimizer: sliding window history trimming to prevent unbounded context growth - Dual provider: Anthropic API or local Claude Code (OAuth) -- Multi-provider settings: Claude Code, Codex CLI +- Multi-provider settings: Claude Code, Codex CLI, OpenCode ### Editor UI @@ -135,7 +140,7 @@ Available as a **web app** and **desktop app** (macOS / Windows / Linux via Elec - **Icons:** [Lucide React](https://lucide.dev/) - **Server:** [Nitro](https://nitro.build/) (API routes) - **Desktop:** [Electron](https://www.electronjs.org/) 35 + [electron-builder](https://www.electron.build/) -- **AI:** [Anthropic SDK](https://docs.anthropic.com/) + [Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk) +- **AI:** [Anthropic SDK](https://docs.anthropic.com/) + [Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk) + [OpenCode SDK](https://github.com/opencode-ai/sdk) - **Runtime:** [Bun](https://bun.sh/) - **Build:** [Vite](https://vite.dev/) 7 - **CI/CD:** GitHub Actions @@ -163,10 +168,11 @@ bun run electron:build ### AI Configuration -The AI assistant works in two modes: +The AI assistant works in multiple modes: - **Anthropic API**: Set `ANTHROPIC_API_KEY` in `.env` - **Local Claude Code**: No config needed — uses Claude Agent SDK with OAuth login as fallback +- **OpenCode**: Connect via OpenCode SDK for additional model support ## Scripts @@ -211,28 +217,29 @@ git push origin v0.1.0 ```text src/ - canvas/ # Fabric.js canvas engine - variables/ # Design variables/tokens system + canvas/ # Fabric.js canvas engine (25 files: sync, events, guides, drag-drop, pen tool, etc.) + variables/ # Design variables/tokens system (resolve, replace refs) components/ - editor/ # Editor layout, toolbar, tool buttons, status bar - panels/ # Layer panel, property panel, AI chat, code panel, variables panel + editor/ # Editor layout, toolbar, tool buttons, top bar, status bar + panels/ # Layer panel, property panel, AI chat, code panel, variables panel, export section shared/ # ColorPicker, NumberInput, VariablePicker, ExportDialog, etc. - icons/ # Provider logos (Claude, OpenAI) + icons/ # Provider logos (Claude, OpenAI, OpenCode) ui/ # shadcn/ui primitives (Button, Select, Slider, Switch, etc.) hooks/ # Keyboard shortcuts lib/ # Utility functions (cn class merging) services/ - ai/ # AI chat service, prompts, design generation + ai/ # AI chat, orchestrator, context optimizer, design generation, prompts codegen/ # React+Tailwind, HTML+CSS, and CSS variables generators stores/ # Zustand stores (canvas, document, history, AI, agent-settings) - types/ # PenDocument/PenNode types, style types, variables, agent settings + types/ # PenDocument/PenNode types, style types, variables, agent settings, Electron IPC utils/ # File operations, export, node clone, SVG parser, syntax highlight routes/ # TanStack Router pages (/, /editor) electron/ - main.ts # Electron main process (window, Nitro server, IPC) + main.ts # Electron main process (window, Nitro server, IPC, fullscreen handling) preload.ts # Context bridge for renderer ↔ main IPC server/ api/ai/ # Nitro API: streaming chat, generation, agent connection, models + utils/ # Server utilities: Claude CLI resolver, OpenCode client manager .github/ workflows/ ci.yml # CI: type check, test, web build diff --git a/electron/main.ts b/electron/main.ts index 40ba926e6..46e774c00 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -155,12 +155,27 @@ function createWindow(): void { if (!mainWindow) return if (process.platform === 'darwin') { await mainWindow.webContents.insertCSS( - '.electron-traffic-light-pad { margin-left: 74px; }', + '.electron-traffic-light-pad { margin-left: 74px; }' + + '.electron-fullscreen .electron-traffic-light-pad { margin-left: 0; }', ) } mainWindow.show() }) + // Toggle fullscreen class to remove traffic-light padding in fullscreen + if (process.platform === 'darwin') { + mainWindow.on('enter-full-screen', () => { + mainWindow?.webContents.executeJavaScript( + 'document.body.classList.add("electron-fullscreen")', + ) + }) + mainWindow.on('leave-full-screen', () => { + mainWindow?.webContents.executeJavaScript( + 'document.body.classList.remove("electron-fullscreen")', + ) + }) + } + mainWindow.loadURL(url) if (isDev) { diff --git a/package.json b/package.json index dd43a2569..1f7cfc79b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "dev": "bun --bun vite dev --port 3000", "build": "bun --bun vite build", "preview": "bun --bun vite preview", - "test": "bun --bun vitest run", + "test": "bun --bun vitest run --passWithNoTests", "electron:dev": "bun run scripts/electron-dev.ts", "electron:compile": "esbuild electron/main.ts electron/preload.ts --bundle --platform=node --target=node20 --outdir=electron-dist --external:electron --format=cjs --out-extension:.js=.cjs --sourcemap", "electron:build": "BUILD_TARGET=electron bun --bun run build && bun run electron:compile && npx electron-builder --config electron-builder.yml" diff --git a/vite.config.ts b/vite.config.ts index 73a418400..ca181701f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,6 +10,9 @@ import { nitro } from 'nitro/vite' const isElectronBuild = process.env.BUILD_TARGET === 'electron' const config = defineConfig({ + test: { + teardownTimeout: 1000, + }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)),