openpencil/CLAUDE.md

75 lines
5 KiB
Markdown
Raw Normal View History

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
> **The TypeScript OpenPencil has been retired.** `apps/web`, `apps/desktop`, `apps/cli`, and the `pen-*` packages (pen-types/core/engine/renderer/figma/mcp/ai-skills/sdk/react/acp) are gone. The product is now implemented in **Rust** (`crates/`) with a thin **wasm-backed web SDK** (`packages/op-web-sdk*`). Historical `// ported from pen-*` comments in the Rust sources name the retired TS as their origin — that code no longer exists in-tree; consult git history (last TS tag `v0.7.5`) if you need it.
Detailed module docs load automatically in subdirectories:
V0.5.0 (#67) * docs: add image search & generation design spec and implementation plan - Spec: dual-source image search (Openverse + Wikimedia), multi-provider image generation - Plan: 16 tasks covering types, server endpoints, settings UI, property panel, auto-search pipeline, MCP integration * feat(types): add image service types and imagePrompt to ImageNode * feat(server): add image service API key validation endpoint Adds POST /api/ai/image-service-test that validates credentials for openverse (client_credentials), openai/custom (Bearer + /v1/models), gemini (API key + v1beta/models), and replicate (Bearer + /v1/models). * feat(server): add multi-provider image generation endpoint * feat(server): add dual-source image search endpoint (Openverse + Wikimedia) POST /api/ai/image-search searches freely-licensed images via Openverse with automatic fallback to Wikimedia Commons on 429 rate-limit responses. Supports optional OAuth credentials for authenticated Openverse requests. * feat(store): add imageSearchStatuses to canvas store for runtime status tracking * feat(store): add image generation config and Openverse OAuth to agent settings * feat(editor): add Images tab to agent settings dialog Adds Popover primitive, ImagesPage component with Image Search (Openverse OAuth, test) and Image Generation (provider select, API key, model, base URL) sections, and wires them into the settings dialog sidebar. * feat(panels): add image search popover with Openverse/Wikimedia results grid * feat(panels): add image generate popover with multi-provider support * feat(panels): add Search and Generate buttons to image property section * feat(ai): update prompts to use imagePrompt instead of src for image nodes * feat(ai): add auto-search pipeline with Openverse/Wikimedia fallback * feat(ai): trigger auto image search after design generation completes * feat(mcp): implement G() operation for image search in batch design DSL Adds the G(parent, mode, prompt) operation to batch_design DSL that creates an image node and optionally fetches a real image URL via the image-search API when mode is "search". Converts executeLine to async to support the network call. * feat(mcp): auto-fill images after design refinement in layered pipeline * feat(ai): split imageSearchQuery and imagePrompt for search vs generation - ImageNode now has both imageSearchQuery (short keywords for search) and imagePrompt (long description for AI image generation) - AI prompts instruct LLM to generate both fields - Search pipeline and popovers use imageSearchQuery - Generate popover uses imagePrompt - Server-side simplifySearchQuery kept as fallback for manual input * fix(ai): hook auto image search into orchestrator completion path The primary generation path uses executeOrchestration -> insertStreamingNode, not applyNodesToCanvas/animateNodesToCanvas. Added scanAndFillImages call to orchestrator.ts after all sub-agents complete. Added debug logging. Removed plan/spec docs from git. * style(editor): remove provider names from image search ready status * fix(panels): clean up image gen error display and settings UI - Parse API error response to show concise message instead of raw JSON - Limit error text to 2 lines with line-clamp - Fix image gen test button sending wrong service name - Inline Image Search ready indicator with section header - Remove debug logging from image search pipeline * style(panels): allow up to 4 lines for image gen error message * fix: avoid 1-frame delay when resizing canvas (#60) rAF callbacks run before ResizeObserver in the same frame. Scheduling render in ResizeObserver via rAF defers it to the next frame. Invoke render() synchronously to leverage ResizeObserver's pre-paint timing and ensure immediate visual update. * feat(electron): implement desktop application structure and auto-updater - Introduced a new Electron desktop application with a structured directory for apps and packages. - Added auto-updater functionality to manage application updates seamlessly. - Created a comprehensive menu system for the desktop app. - Implemented logging capabilities for better debugging and error tracking. - Configured build settings for various platforms (macOS, Windows, Linux) using electron-builder. - Established TypeScript configurations for both the desktop and web applications. - Integrated Vite for the web application with support for React and Tailwind CSS. - Added icons and assets for the desktop application. * chore: update package versions to 0.5.0 across all package.json files and add pre-commit hook for version synchronization - Bumped version to 0.5.0 in package.json files for the main project, desktop app, web app, and all packages. - Introduced a pre-commit hook to automatically sync version numbers from branch names to all package.json files. * chore: update package versions to 0.5.0 and refactor Skia components - Bumped version to 0.5.0 in bun.lock and all relevant package.json files. - Refactored Skia components to utilize shared functionality from @zseven-w/pen-renderer, including image loading, hit testing, and path utilities. - Removed redundant code and improved modularity by re-exporting necessary functions and classes from the renderer package. * fix(panels): handle string fill values in icon nodes (#61) AI-generated icon/path nodes may have fill stored as a raw string instead of a PenFill[] array, causing "Cannot use 'in' operator" crash when selecting the node in the property panel. * chore: update documentation and project structure for monorepo organization - Added a new version bump command to synchronize all package.json files. - Updated the project structure to reflect a monorepo setup with organized workspaces for apps and packages. - Enhanced README files in multiple languages to include the new structure and commands. - Adjusted image paths in documentation to point to the correct locations for the desktop application. * feat(ai): incremental image search and improved image generation prompts - Refactor image search from batch post-generation to incremental queue: enqueueImageForSearch() triggers as each image node is inserted during streaming, so images appear progressively instead of all at once after generation completes. scanAndFillImages() remains as a final sweep. - Update imagePrompt guidance to avoid "transparent background" and similar phrases that many models cannot reliably produce. - Pass node width/height from image panel to generation endpoint for aspect-ratio-aware output (Gemini aspect ratio mapping, OpenAI size selection, Replicate dimensions). * feat(ai): multi-profile image generation config and cleaner error messages - Support multiple image generation profiles with active selection; first configured profile becomes default. Old single-config migrated automatically on hydrate. - Fix Gemini aspect ratio: move to generationConfig.imageConfig per API spec. - Extract clean error messages from provider JSON responses (Gemini error.message, OpenAI error.message, Replicate detail) instead of returning raw JSON text. - Remove destructive client-side regex that mangled error display. * feat(design-md): integrate design system panel and functionality - Added a new DesignMdPanel component for managing design system specifications. - Implemented functionality to toggle the design system panel in the editor layout and toolbar. - Introduced new commands for importing, exporting, and auto-generating design.md content. - Updated AI chat handlers to utilize design.md data for enhanced design generation. - Enhanced localization support for design system features across multiple languages. * perf(canvas): skip draw calls for nodes outside the viewport (#64) Add viewport culling in render() to avoid issuing CanvasKit draw calls for off-screen nodes. A 64px screen-space buffer is kept around the viewport edges so nearby nodes are pre-rendered, preventing pop-in during fast panning. * feat(utils): enhance Windows process spawning for CLI scripts - Updated the buildSpawnClaudeCodeProcess function to handle .cmd and .ps1 scripts appropriately. - Implemented PowerShell invocation for .ps1 files and ensured safe defaults for .cmd and .exe files. - Improved handling of command execution to avoid limitations of cmd.exe. * feat(ai): add support for Gemini CLI integration - Extended the AI provider options to include 'gemini' across various components and APIs. - Implemented functions for generating, validating, and connecting to the Gemini CLI. - Added Gemini-specific error handling and model fetching logic. - Updated UI components to display Gemini as a selectable provider with appropriate icons and labels. - Enhanced localization support for Gemini-related features in multiple languages. * feat(editor): warn before closing with unsaved changes Intercept window/tab close when isDirty is true: - Electron: native dialog with Save / Don't Save / Cancel - Web: beforeunload handler + confirm on New/Open actions - i18n: close-confirm strings for all 15 locales * feat(ipc): extract IPC handlers to a dedicated module - Moved IPC dialog handling and updater functions from main.ts to ipc-handlers.ts for better organization and maintainability. - Implemented file open/save dialogs, theme setting, and preferences management through IPC. - Enhanced updater functionality with state management and auto-update settings. - Improved code structure by separating concerns, making it easier to manage IPC-related logic. * feat(docs): update CLAUDE documentation and add new files for desktop and web apps - Enhanced CLAUDE.md with detailed module documentation references for `packages/` and `apps/`. - Updated `pen-core` description to include clone utilities in `pen-core`. - Added new documentation files for the desktop and web applications, outlining their structure, components, and functionalities. - Included IPC handler details in the desktop app documentation for better clarity on file dialogs and theme synchronization. * feat(docker): add Gemini CLI support and update documentation - Introduced a new Docker build stage for the Gemini CLI, allowing users to install and run it. - Updated the Dockerfile to include the installation of the Gemini CLI alongside existing CLI tools. - Enhanced README files in multiple languages to document the new `openpencil-gemini` image variant. - Added Gemini CLI connection instructions to the main README for better user guidance. * feat(docs): add Gemini CLI connection instructions to multiple language READMEs - Updated README files in German, Spanish, French, Hindi, Indonesian, Japanese, Korean, Portuguese, Russian, Thai, Turkish, Vietnamese, and both Traditional and Simplified Chinese to include connection instructions for the Gemini CLI. - Enhanced documentation to improve user guidance for connecting the Gemini CLI in agent settings. * perf(renderer): replace count-based text cache limits with memory-based eviction (#66) previous limits (PARA_CACHE_MAX=200, TEXT_CACHE_MAX=300) were too small for scenes with many nodes, causing constant cache churn and paragraph rebuilds every frame, which dropped FPS significantly during canvas pan. - switch to byte-budget limits (64 MB paragraphs, 256 MB bitmaps) - bitmap size measured exactly as cw*ch*4; paragraph WASM heap estimated as content.length*64+4096 - eviction uses Map insertion order (FIFO) instead of a separate string[] array, replacing O(n) array.shift() with O(1) Map.entries().next() - evict before insert so the budget check includes the incoming entry --------- Co-authored-by: Fini <fini.yang@gmail.com> Co-authored-by: leinaldo <60176594+leinaldo@users.noreply.github.com>
2026-03-22 01:44:04 +00:00
- **`crates/CLAUDE.md`** — Rust shell: crate layout, editor core, widgets, hosts (native/web/desktop), MCP, AI, orchestrator, codegen. **The canonical architecture doc.**
- **`packages/CLAUDE.md`** — Remaining packages (op-web-sdk web viewer SDK family).
## Commands
Tooling is **Cargo** (Rust — the product). The root has **no `package.json`**; the JS/**Bun** tooling for the web SDK lives under `packages/` — run SDK/JS scripts from there.
- **Web dev server (Rust):** `bash scripts/start-web-rust.sh`
- **Build (Rust):** `cargo build --workspace --release`
- **Run all tests (Rust):** `cargo test --workspace`; single crate: `cargo test -p <crate>`
- **Type check:** `cargo check --workspace`; wasm: `cargo check --target wasm32-unknown-unknown -p op-host-web --no-default-features --features web`
- **Lint / format (Rust):** `cargo clippy --workspace --all-targets -- -D warnings` / `cargo fmt --all`
- **Lint / format (remaining TS SDK):** from `packages/`: `bun run lint` (oxlint) / `bun run format` (oxfmt)
- **Desktop app:** `cargo build -p op-host-desktop` → binary `openpencil-desktop` (live MCP on `127.0.0.1:<port>/mcp`)
- **CLI:** `cargo build -p op-cli` → binary `op`
- **MCP server:** built into the desktop/web host (`--mcp <path>`); crate `op-mcp`
- **Iconify catalog (Rust assets):** from `packages/`: `bun run generate-iconify-catalog`
- **Sync all managed versions:** `scripts/sync-version.sh` reads the canonical version from root `Cargo.toml`; verify without writing via `tools/check-version-sync.sh`
## Architecture
OpenPencil is an open-source, AI-native vector design tool (Design-as-Code). The editor — canvas engine, chrome, stores, MCP, AI — is Rust, built on the vendored **jian** skia/widget/render/event toolkit (`vendor/jian`) and **casement** winit fork (`vendor/casement`). See **`crates/CLAUDE.md`** for the authoritative crate map; the essentials:
V0.1.0 (#13) * feat(editor): implement Figma import functionality - Added a new FigmaImportDialog component for importing .fig files. - Integrated file processing and conversion logic to handle Figma files. - Enhanced the editor layout to include a button for opening the Figma import dialog. - Introduced page tabs for better navigation between document pages. - Updated various components to utilize active page children for rendering and interactions. - Added new dependencies: fzstd and kiwi-schema for improved functionality. * fix(ai): harden Claude agent env variable resolution - Filter empty strings and object values from settings.json env - Validate ANTHROPIC_CUSTOM_HEADERS is valid JSON before passing - Remove incorrect ANTHROPIC_CUSTOM_HEADERS auto-generation from auth token * refactor(ai): remove silent fallback logic, surface errors in AI panel - Remove direct-stream fallback in generateDesign, delegate to orchestrator - Remove orchestrator planner fallback plan (buildFallbackPlanFromPrompt) - Remove sub-agent retry logic, minimal mode retry, and placeholder insertion - Throw on sub-agent error instead of silently degrading - Call updateLastMessage in catch block so errors display immediately - Clean up unused imports, functions, and console.log/warn statements * fix(ai): add 30s API connect timeout for faster error detection - Set maxRetries: 0 on Anthropic SDK to prevent retry delays on 429 - Add AbortController-based 30s timeout for initial API connection - If the provider doesn't respond within 30s, abort and send SSE error - Refactor streamViaAnthropicSDK with shared send() helper * fix(editor): improve Figma import visual fidelity - Extract image files from .fig ZIP archive and resolve by SHA-1 hash - Map Figma textCase property (UPPER/LOWER/TITLE) to text content - Convert ellipse nodes with image fills to image nodes - Decode Figma vector paths and match icons via Lucide icon registry - Fix z-order: move background rectangles behind content layers - Set clipContent=true for Figma frames (default clip behavior) - Unwrap single-artboard pages to use artboard as root frame * fix(canvas): preserve clipPath during async image load and invalidate cache - Copy clipPath from placeholder rect to FabricImage in onload callback - Set obj.dirty=true after applying/removing clipPath so Fabric.js re-renders with the updated clip region instead of using stale cache * fix(ai): use page-aware empty frame detection and place new designs as siblings - Fix isCanvasOnlyEmptyFrame() to use getActivePageChildren() instead of document.children which is empty after page migration - When canvas is empty, replace default frame (existing behavior restored) - When canvas has content, add generated frame as top-level sibling positioned to the right with 100px gap - Track generationRootFrameId so height adjustment targets correct frame - Add WIDTH SELECTION rule: app screens use 375x812 mobile dimensions - Include selected node dimensions in AI context string * feat(electron): implement application menu with native actions - Added a native application menu for macOS and Windows, including File, Edit, View, and Window menus. - Integrated menu actions for creating, opening, saving files, and editing operations like undo/redo. - Enhanced the editor layout to handle Electron menu actions through a custom hook. - Updated icon components in the toolbar and top bar for consistent styling. - Refactored padding and layout components for improved structure and readability. * fix(canvas): use page-aware children for selection depth resolution getSelectableNodeIds() used doc.children which is empty under the pages architecture. Switch to getActivePageChildren() so canvas selection correctly syncs to the layers panel. * fix(panels): adjust property panel sections for image nodes Hide fill/stroke sections (not applicable to images) and show corner radius section for image elements. * feat(canvas): implement image corner radius via clipPath FabricImage does not support rx/ry natively. Use a rounded-rect clipPath instead, with objectCaching disabled to ensure immediate re-render on radius changes. * fix(canvas): use page-aware forcePageResync for canvas re-sync The old pattern { ...doc, children: [...doc.children] } only touched root-level children which are empty under pages architecture. Extract forcePageResync() into canvas-sync-utils and replace all 7 occurrences across canvas-object-modified, drag-into-layout, and layout-reorder. * fix(canvas): preserve image corner radius clipPath during drag Three issues caused corner radius loss on image drag: 1. Scale baking in object:modified changed FabricImage width from natural to display dimensions, creating a clipPath size mismatch. Exclude images from scale baking (like paths/polygons). 2. object:moving unconditionally cleared all clipPaths to avoid stale frame clipping. Now only clears absolutePositioned (frame) clips, preserving object-level clips like image corner radius. 3. Post-drag re-sync overwrote the corner radius clipPath with the parent frame clip. Now skips frame clip when the object already has its own non-absolutePositioned clipPath. * feat(ai): enhance chat functionality with image attachments support - Added support for image attachments in chat messages, allowing users to send images alongside text. - Implemented temporary file handling for attachments, ensuring they are accessible to the AI processing. - Updated chat message structure to include attachments, and modified the prompt building logic to reference these images. - Enhanced the chat panel UI to allow users to select and preview images before sending. - Introduced a new state management for pending attachments in the AI store. - Refactored related components to accommodate the new attachment feature, improving user experience and interaction. * feat(multi-page): introduce multi-page architecture and enhance agent settings UI - Added multi-page architecture to the document structure, allowing for page CRUD actions such as add, remove, rename, reorder, and duplicate. - Implemented `activePageId` state management in the canvas store for better page handling. - Enhanced the top bar with an `AgentStatusButton` to display connected AI providers and their statuses. - Updated the agent settings dialog to improve the UI for managing AI integrations, including better layout and connection indicators. - Refactored related components for improved usability and visual consistency. * docs(README): add contributors section with dynamic contributor image * feat(ai): improve chat streaming and error handling - Added functionality to hide the checklist when streaming stops with no completed items. - Implemented abort controller management to handle user-initiated stops during chat generation. - Enhanced error handling to prevent displaying errors for user-initiated stops while preserving partial content. - Updated orchestration and design generation functions to support abort signals, improving responsiveness during streaming. * fix(canvas): align z-order with layer panel convention (top = front) Establish consistent z-order: children[0] = top of layer panel = frontmost on canvas. This matches the standard design tool convention (Figma, Sketch). - Reverse flattenNodes iteration so children[0] is added last to canvas - Add z-order reconciliation via moveObjectTo after each sync pass - Figma import: sort children descending so front items are at children[0] - addNode defaults to prepend (index 0) so new items appear at top - duplicateNode inserts clone above original (at idx instead of idx+1) - drag-reparent inserts at index 0 (frontmost) instead of childCount - Async image loading preserves z-order via insertAt instead of add - Add FigmaImportLayoutMode toggle (preserve vs openpencil auto-layout) * chore: resolve .gitignore merge conflict * fix(canvas): clamp corner radius to half the element height * chore: update version to 0.1.0 and modify mac artifact naming in electron-builder configuration --------- Co-authored-by: Fini <fini.yang@gmail.com>
2026-03-01 01:33:52 +00:00
```text
crates/
├── op-editor-core/ Canonical `.op` (PenDocument) editor state + EditorCommand + design-variable resolution
├── op-editor-ui/ Platform-free widgets + RenderBackend facade (wasm32-clean)
├── op-editor-host-core/ Transport-free host state machines shared by all hosts
├── op-host-native/ Native host lib (winit + skia-safe GL) — desktop + mobile
├── op-host-web/ Browser bundle: wasm32 cdylib, CanvasKit renderer
├── op-host-desktop/ Desktop binary `openpencil-desktop`; also the `--serve-web` daemon
├── op-host-services/ Headless serve-web / MCP daemon lib (shared by desktop + web-server)
├── op-host-web-server/ Thin GL-free web-server binary
├── op-cli/ `op` command-line tool
└── op-mcp / op-ai / op-ai-skills / op-codegen / op-orchestrator / op-figma /
op-git / op-opmerge / op-pen-loader / op-design-lint / op-i18n / op-smoke / …
packages/
├── op-web-sdk/ Read-only `.op` web viewer SDK (wraps the op-host-web wasm bundle)
├── op-web-sdk-react/ React 19 adapter for op-web-sdk
└── op-web-sdk-vue/ Vue 3 adapter for op-web-sdk
V0.1.0 (#13) * feat(editor): implement Figma import functionality - Added a new FigmaImportDialog component for importing .fig files. - Integrated file processing and conversion logic to handle Figma files. - Enhanced the editor layout to include a button for opening the Figma import dialog. - Introduced page tabs for better navigation between document pages. - Updated various components to utilize active page children for rendering and interactions. - Added new dependencies: fzstd and kiwi-schema for improved functionality. * fix(ai): harden Claude agent env variable resolution - Filter empty strings and object values from settings.json env - Validate ANTHROPIC_CUSTOM_HEADERS is valid JSON before passing - Remove incorrect ANTHROPIC_CUSTOM_HEADERS auto-generation from auth token * refactor(ai): remove silent fallback logic, surface errors in AI panel - Remove direct-stream fallback in generateDesign, delegate to orchestrator - Remove orchestrator planner fallback plan (buildFallbackPlanFromPrompt) - Remove sub-agent retry logic, minimal mode retry, and placeholder insertion - Throw on sub-agent error instead of silently degrading - Call updateLastMessage in catch block so errors display immediately - Clean up unused imports, functions, and console.log/warn statements * fix(ai): add 30s API connect timeout for faster error detection - Set maxRetries: 0 on Anthropic SDK to prevent retry delays on 429 - Add AbortController-based 30s timeout for initial API connection - If the provider doesn't respond within 30s, abort and send SSE error - Refactor streamViaAnthropicSDK with shared send() helper * fix(editor): improve Figma import visual fidelity - Extract image files from .fig ZIP archive and resolve by SHA-1 hash - Map Figma textCase property (UPPER/LOWER/TITLE) to text content - Convert ellipse nodes with image fills to image nodes - Decode Figma vector paths and match icons via Lucide icon registry - Fix z-order: move background rectangles behind content layers - Set clipContent=true for Figma frames (default clip behavior) - Unwrap single-artboard pages to use artboard as root frame * fix(canvas): preserve clipPath during async image load and invalidate cache - Copy clipPath from placeholder rect to FabricImage in onload callback - Set obj.dirty=true after applying/removing clipPath so Fabric.js re-renders with the updated clip region instead of using stale cache * fix(ai): use page-aware empty frame detection and place new designs as siblings - Fix isCanvasOnlyEmptyFrame() to use getActivePageChildren() instead of document.children which is empty after page migration - When canvas is empty, replace default frame (existing behavior restored) - When canvas has content, add generated frame as top-level sibling positioned to the right with 100px gap - Track generationRootFrameId so height adjustment targets correct frame - Add WIDTH SELECTION rule: app screens use 375x812 mobile dimensions - Include selected node dimensions in AI context string * feat(electron): implement application menu with native actions - Added a native application menu for macOS and Windows, including File, Edit, View, and Window menus. - Integrated menu actions for creating, opening, saving files, and editing operations like undo/redo. - Enhanced the editor layout to handle Electron menu actions through a custom hook. - Updated icon components in the toolbar and top bar for consistent styling. - Refactored padding and layout components for improved structure and readability. * fix(canvas): use page-aware children for selection depth resolution getSelectableNodeIds() used doc.children which is empty under the pages architecture. Switch to getActivePageChildren() so canvas selection correctly syncs to the layers panel. * fix(panels): adjust property panel sections for image nodes Hide fill/stroke sections (not applicable to images) and show corner radius section for image elements. * feat(canvas): implement image corner radius via clipPath FabricImage does not support rx/ry natively. Use a rounded-rect clipPath instead, with objectCaching disabled to ensure immediate re-render on radius changes. * fix(canvas): use page-aware forcePageResync for canvas re-sync The old pattern { ...doc, children: [...doc.children] } only touched root-level children which are empty under pages architecture. Extract forcePageResync() into canvas-sync-utils and replace all 7 occurrences across canvas-object-modified, drag-into-layout, and layout-reorder. * fix(canvas): preserve image corner radius clipPath during drag Three issues caused corner radius loss on image drag: 1. Scale baking in object:modified changed FabricImage width from natural to display dimensions, creating a clipPath size mismatch. Exclude images from scale baking (like paths/polygons). 2. object:moving unconditionally cleared all clipPaths to avoid stale frame clipping. Now only clears absolutePositioned (frame) clips, preserving object-level clips like image corner radius. 3. Post-drag re-sync overwrote the corner radius clipPath with the parent frame clip. Now skips frame clip when the object already has its own non-absolutePositioned clipPath. * feat(ai): enhance chat functionality with image attachments support - Added support for image attachments in chat messages, allowing users to send images alongside text. - Implemented temporary file handling for attachments, ensuring they are accessible to the AI processing. - Updated chat message structure to include attachments, and modified the prompt building logic to reference these images. - Enhanced the chat panel UI to allow users to select and preview images before sending. - Introduced a new state management for pending attachments in the AI store. - Refactored related components to accommodate the new attachment feature, improving user experience and interaction. * feat(multi-page): introduce multi-page architecture and enhance agent settings UI - Added multi-page architecture to the document structure, allowing for page CRUD actions such as add, remove, rename, reorder, and duplicate. - Implemented `activePageId` state management in the canvas store for better page handling. - Enhanced the top bar with an `AgentStatusButton` to display connected AI providers and their statuses. - Updated the agent settings dialog to improve the UI for managing AI integrations, including better layout and connection indicators. - Refactored related components for improved usability and visual consistency. * docs(README): add contributors section with dynamic contributor image * feat(ai): improve chat streaming and error handling - Added functionality to hide the checklist when streaming stops with no completed items. - Implemented abort controller management to handle user-initiated stops during chat generation. - Enhanced error handling to prevent displaying errors for user-initiated stops while preserving partial content. - Updated orchestration and design generation functions to support abort signals, improving responsiveness during streaming. * fix(canvas): align z-order with layer panel convention (top = front) Establish consistent z-order: children[0] = top of layer panel = frontmost on canvas. This matches the standard design tool convention (Figma, Sketch). - Reverse flattenNodes iteration so children[0] is added last to canvas - Add z-order reconciliation via moveObjectTo after each sync pass - Figma import: sort children descending so front items are at children[0] - addNode defaults to prepend (index 0) so new items appear at top - duplicateNode inserts clone above original (at idx instead of idx+1) - drag-reparent inserts at index 0 (frontmost) instead of childCount - Async image loading preserves z-order via insertAt instead of add - Add FigmaImportLayoutMode toggle (preserve vs openpencil auto-layout) * chore: resolve .gitignore merge conflict * fix(canvas): clamp corner radius to half the element height * chore: update version to 0.1.0 and modify mac artifact naming in electron-builder configuration --------- Co-authored-by: Fini <fini.yang@gmail.com>
2026-03-01 01:33:52 +00:00
```
Data flow, canvas engine, Document/EditorCommand model, MCP layered-design workflow, design variables, and the run/debug recipe are all documented in **`crates/CLAUDE.md`**.
## Code Style
- Single files must not exceed **800 lines** — split into smaller modules when they grow beyond this.
- One component/widget per file, single responsibility.
- `.rs` filenames use snake_case; `.ts`/`.tsx` (SDK) use kebab-case.
- Source comments (`.rs`/`.ts`/`.toml`) in **English** (spec/plan markdown + test CJK fixtures may keep Chinese).
- Rust widgets paint against jian's `Painter`; `draw_text` is **baseline-relative** (label components center via `centered_text_baseline_y`, not `(h-fs)/2`).
## Git Commit Convention
Use [Conventional Commits](https://www.conventionalcommits.org/): `<type>(<scope>): <subject>`
V0.5.0 (#67) * docs: add image search & generation design spec and implementation plan - Spec: dual-source image search (Openverse + Wikimedia), multi-provider image generation - Plan: 16 tasks covering types, server endpoints, settings UI, property panel, auto-search pipeline, MCP integration * feat(types): add image service types and imagePrompt to ImageNode * feat(server): add image service API key validation endpoint Adds POST /api/ai/image-service-test that validates credentials for openverse (client_credentials), openai/custom (Bearer + /v1/models), gemini (API key + v1beta/models), and replicate (Bearer + /v1/models). * feat(server): add multi-provider image generation endpoint * feat(server): add dual-source image search endpoint (Openverse + Wikimedia) POST /api/ai/image-search searches freely-licensed images via Openverse with automatic fallback to Wikimedia Commons on 429 rate-limit responses. Supports optional OAuth credentials for authenticated Openverse requests. * feat(store): add imageSearchStatuses to canvas store for runtime status tracking * feat(store): add image generation config and Openverse OAuth to agent settings * feat(editor): add Images tab to agent settings dialog Adds Popover primitive, ImagesPage component with Image Search (Openverse OAuth, test) and Image Generation (provider select, API key, model, base URL) sections, and wires them into the settings dialog sidebar. * feat(panels): add image search popover with Openverse/Wikimedia results grid * feat(panels): add image generate popover with multi-provider support * feat(panels): add Search and Generate buttons to image property section * feat(ai): update prompts to use imagePrompt instead of src for image nodes * feat(ai): add auto-search pipeline with Openverse/Wikimedia fallback * feat(ai): trigger auto image search after design generation completes * feat(mcp): implement G() operation for image search in batch design DSL Adds the G(parent, mode, prompt) operation to batch_design DSL that creates an image node and optionally fetches a real image URL via the image-search API when mode is "search". Converts executeLine to async to support the network call. * feat(mcp): auto-fill images after design refinement in layered pipeline * feat(ai): split imageSearchQuery and imagePrompt for search vs generation - ImageNode now has both imageSearchQuery (short keywords for search) and imagePrompt (long description for AI image generation) - AI prompts instruct LLM to generate both fields - Search pipeline and popovers use imageSearchQuery - Generate popover uses imagePrompt - Server-side simplifySearchQuery kept as fallback for manual input * fix(ai): hook auto image search into orchestrator completion path The primary generation path uses executeOrchestration -> insertStreamingNode, not applyNodesToCanvas/animateNodesToCanvas. Added scanAndFillImages call to orchestrator.ts after all sub-agents complete. Added debug logging. Removed plan/spec docs from git. * style(editor): remove provider names from image search ready status * fix(panels): clean up image gen error display and settings UI - Parse API error response to show concise message instead of raw JSON - Limit error text to 2 lines with line-clamp - Fix image gen test button sending wrong service name - Inline Image Search ready indicator with section header - Remove debug logging from image search pipeline * style(panels): allow up to 4 lines for image gen error message * fix: avoid 1-frame delay when resizing canvas (#60) rAF callbacks run before ResizeObserver in the same frame. Scheduling render in ResizeObserver via rAF defers it to the next frame. Invoke render() synchronously to leverage ResizeObserver's pre-paint timing and ensure immediate visual update. * feat(electron): implement desktop application structure and auto-updater - Introduced a new Electron desktop application with a structured directory for apps and packages. - Added auto-updater functionality to manage application updates seamlessly. - Created a comprehensive menu system for the desktop app. - Implemented logging capabilities for better debugging and error tracking. - Configured build settings for various platforms (macOS, Windows, Linux) using electron-builder. - Established TypeScript configurations for both the desktop and web applications. - Integrated Vite for the web application with support for React and Tailwind CSS. - Added icons and assets for the desktop application. * chore: update package versions to 0.5.0 across all package.json files and add pre-commit hook for version synchronization - Bumped version to 0.5.0 in package.json files for the main project, desktop app, web app, and all packages. - Introduced a pre-commit hook to automatically sync version numbers from branch names to all package.json files. * chore: update package versions to 0.5.0 and refactor Skia components - Bumped version to 0.5.0 in bun.lock and all relevant package.json files. - Refactored Skia components to utilize shared functionality from @zseven-w/pen-renderer, including image loading, hit testing, and path utilities. - Removed redundant code and improved modularity by re-exporting necessary functions and classes from the renderer package. * fix(panels): handle string fill values in icon nodes (#61) AI-generated icon/path nodes may have fill stored as a raw string instead of a PenFill[] array, causing "Cannot use 'in' operator" crash when selecting the node in the property panel. * chore: update documentation and project structure for monorepo organization - Added a new version bump command to synchronize all package.json files. - Updated the project structure to reflect a monorepo setup with organized workspaces for apps and packages. - Enhanced README files in multiple languages to include the new structure and commands. - Adjusted image paths in documentation to point to the correct locations for the desktop application. * feat(ai): incremental image search and improved image generation prompts - Refactor image search from batch post-generation to incremental queue: enqueueImageForSearch() triggers as each image node is inserted during streaming, so images appear progressively instead of all at once after generation completes. scanAndFillImages() remains as a final sweep. - Update imagePrompt guidance to avoid "transparent background" and similar phrases that many models cannot reliably produce. - Pass node width/height from image panel to generation endpoint for aspect-ratio-aware output (Gemini aspect ratio mapping, OpenAI size selection, Replicate dimensions). * feat(ai): multi-profile image generation config and cleaner error messages - Support multiple image generation profiles with active selection; first configured profile becomes default. Old single-config migrated automatically on hydrate. - Fix Gemini aspect ratio: move to generationConfig.imageConfig per API spec. - Extract clean error messages from provider JSON responses (Gemini error.message, OpenAI error.message, Replicate detail) instead of returning raw JSON text. - Remove destructive client-side regex that mangled error display. * feat(design-md): integrate design system panel and functionality - Added a new DesignMdPanel component for managing design system specifications. - Implemented functionality to toggle the design system panel in the editor layout and toolbar. - Introduced new commands for importing, exporting, and auto-generating design.md content. - Updated AI chat handlers to utilize design.md data for enhanced design generation. - Enhanced localization support for design system features across multiple languages. * perf(canvas): skip draw calls for nodes outside the viewport (#64) Add viewport culling in render() to avoid issuing CanvasKit draw calls for off-screen nodes. A 64px screen-space buffer is kept around the viewport edges so nearby nodes are pre-rendered, preventing pop-in during fast panning. * feat(utils): enhance Windows process spawning for CLI scripts - Updated the buildSpawnClaudeCodeProcess function to handle .cmd and .ps1 scripts appropriately. - Implemented PowerShell invocation for .ps1 files and ensured safe defaults for .cmd and .exe files. - Improved handling of command execution to avoid limitations of cmd.exe. * feat(ai): add support for Gemini CLI integration - Extended the AI provider options to include 'gemini' across various components and APIs. - Implemented functions for generating, validating, and connecting to the Gemini CLI. - Added Gemini-specific error handling and model fetching logic. - Updated UI components to display Gemini as a selectable provider with appropriate icons and labels. - Enhanced localization support for Gemini-related features in multiple languages. * feat(editor): warn before closing with unsaved changes Intercept window/tab close when isDirty is true: - Electron: native dialog with Save / Don't Save / Cancel - Web: beforeunload handler + confirm on New/Open actions - i18n: close-confirm strings for all 15 locales * feat(ipc): extract IPC handlers to a dedicated module - Moved IPC dialog handling and updater functions from main.ts to ipc-handlers.ts for better organization and maintainability. - Implemented file open/save dialogs, theme setting, and preferences management through IPC. - Enhanced updater functionality with state management and auto-update settings. - Improved code structure by separating concerns, making it easier to manage IPC-related logic. * feat(docs): update CLAUDE documentation and add new files for desktop and web apps - Enhanced CLAUDE.md with detailed module documentation references for `packages/` and `apps/`. - Updated `pen-core` description to include clone utilities in `pen-core`. - Added new documentation files for the desktop and web applications, outlining their structure, components, and functionalities. - Included IPC handler details in the desktop app documentation for better clarity on file dialogs and theme synchronization. * feat(docker): add Gemini CLI support and update documentation - Introduced a new Docker build stage for the Gemini CLI, allowing users to install and run it. - Updated the Dockerfile to include the installation of the Gemini CLI alongside existing CLI tools. - Enhanced README files in multiple languages to document the new `openpencil-gemini` image variant. - Added Gemini CLI connection instructions to the main README for better user guidance. * feat(docs): add Gemini CLI connection instructions to multiple language READMEs - Updated README files in German, Spanish, French, Hindi, Indonesian, Japanese, Korean, Portuguese, Russian, Thai, Turkish, Vietnamese, and both Traditional and Simplified Chinese to include connection instructions for the Gemini CLI. - Enhanced documentation to improve user guidance for connecting the Gemini CLI in agent settings. * perf(renderer): replace count-based text cache limits with memory-based eviction (#66) previous limits (PARA_CACHE_MAX=200, TEXT_CACHE_MAX=300) were too small for scenes with many nodes, causing constant cache churn and paragraph rebuilds every frame, which dropped FPS significantly during canvas pan. - switch to byte-budget limits (64 MB paragraphs, 256 MB bitmaps) - bitmap size measured exactly as cw*ch*4; paragraph WASM heap estimated as content.length*64+4096 - eviction uses Map insertion order (FIFO) instead of a separate string[] array, replacing O(n) array.shift() with O(1) Map.entries().next() - evict before insert so the budget check includes the incoming entry --------- Co-authored-by: Fini <fini.yang@gmail.com> Co-authored-by: leinaldo <60176594+leinaldo@users.noreply.github.com>
2026-03-22 01:44:04 +00:00
**Types:** `feat`, `fix`, `refactor`, `perf`, `style`, `docs`, `test`, `chore`
**Scopes:** `editor`, `canvas`, `panels`, `history`, `ai`, `codegen`, `store`, `types`, `variables`, `figma`, `mcp`, `desktop`, `web`, `renderer`, `sdk`, `cli`, `agent`, `i18n`
**Rules:** Subject in English, lowercase start, no period, imperative mood. Body optional; explain **why** not what. One commit per change.
## License
MIT License. See [LICENSE](./LICENSE) for details.