mirror of
https://github.com/ZSeven-W/openpencil.git
synced 2026-06-01 03:14:29 +07:00
* fix(desktop,web): rebuild Electron dev sync + bitmap dragging fix on v0.7.1 (#99) Re-appliesb046a0dfrom the closed PR #97 now that the base is v0.7.1. Original conflict against v0.7.0 came from the release branch churn — the cherry-pick onto v0.7.1 applies cleanly. Keeps dev-startup, sync-noise, and bitmap-dragging fixes; drops the loopback proxy helper scripts upstream rejected in PR #92. Readiness probe now does direct socket checks inside the existing dev entrypoint; sync hardening stays focused on request diagnostics, backpressure, and drag-time clip-rect correctness. Original commit:b046a0dSupersedes: #97 (closed, head branch deleted) Co-authored-by: Rais <vdcoolzi@gmail.com> * fix(canvas): use ImageFill.url in skia-interaction test The image-backed rectangle fixture in the skia-interaction test used `{ type: 'image', src: '…' }`, but `ImageFill` in pen-types declares the field as `url`. `npx tsc --noEmit` flagged it as TS2352 on the `as PenNode` cast. One-word rename. The squash-merge of #99 captured an earlier snapshot that did not include this fix, so re-apply directly on v0.7.1. * feat(cli): add `op install` / `op uninstall` for openpencil-skill Bundle skill files at build time (scripts/bundle-skill.ts → skill-bundle.json) so users without GitHub access can install directly. Falls back to git clone when the bundle is empty. Supports auto-detection of: Claude Code, Codex, Cursor, Gemini CLI, OpenCode. CI workflows updated to checkout openpencil-skill before cli:compile. * fix(panels): allow reparenting nodes into rectangle in layer panel CONTAINER_TYPES was missing 'rectangle', preventing drag-drop into rectangles even though the data model (ContainerProps) and store (moveNode) both support it. * fix(agent,ai): tool_exec reset + insert_node with after + move_node + CRUD tools - fix(agent): reset StreamingToolExecutor between turns — prevents stale tool_use IDs that caused 400 errors on multi-turn tool calls (MiniMax etc.) - feat(ai): add insert_node "after" parameter — auto-resolves sibling's parent and position for intuitive node insertion - feat(ai): add move_node and insert_node to CRUD tool set - feat(ai): add Chinese keywords (增加/添加/插入) to design intent detection - fix(ai): insert_node uses addNode directly for existing parents instead of streaming pipeline, fixing parent resolution * feat(ai): route CRUD intents to lightweight prompt and tool set CRUD operations (read/update/delete) now get a focused system prompt without design generation instructions, and use getCrudToolDefs() (which includes insert_node and move_node) instead of the full design set. * fix(agent,mcp): submodule update + MCP tool improvements - Update agent-native submodule (tool_exec reset, HTTP error diagnostics) - Improve MCP tool descriptions and parameter schemas - Enhance agent.ts error handling * feat(ai): add PenNode examples to CRUD prompt for complete node generation The CRUD system prompt now includes button and text node examples showing the full structure (fills, children, icons, layout) so models generate complete nodes instead of empty frames. * Update agent-native submodule to commit f9633a8, ensuring compatibility with recent changes and improvements in the agent's functionality. * fix(ci,agent): generate skill-bundle before type check + fix moveNode arg - Add `bun run cli:bundle-skill` step in CI before `tsc --noEmit` so skill-bundle.json exists when type-checking the CLI - Fix moveNode index parameter: default to -1 when undefined * fix(ci): add pen-engine and pen-react to npm publish workflow Insert pen-engine and pen-react in topological order between pen-renderer and pen-mcp so they are published before pen-sdk. * docs: add MIT LICENSE and README to all packages - Add MIT LICENSE to pen-ai-skills, pen-core, pen-engine, pen-figma, pen-mcp, pen-react, pen-renderer, pen-sdk, pen-types - Add README.md to pen-engine, pen-react, pen-mcp, pen-ai-skills * docs: comprehensive package metadata, README, CLAUDE.md, and LICENSE - Add author, license, repository, bugs, homepage to all package.json - Homepage points to each package's own directory on GitHub - Rewrite README for pen-engine, pen-react, pen-mcp, pen-ai-skills with full API docs, usage examples, and feature tables - Add CLAUDE.md to pen-types, pen-core, pen-engine, pen-figma, pen-mcp, pen-react, pen-renderer, pen-sdk - Add MIT LICENSE to all packages - Update root CLAUDE.md with index of all sub-CLAUDE.md files - Fix git URL from nicepkg → ZSeven-W * docs: package metadata, README, LICENSE, and CLAUDE.md for all packages - Add author, license, repository, bugs, homepage to all package.json - Homepage points to each package's own directory on GitHub - Rewrite README for pen-engine, pen-react, pen-mcp, pen-ai-skills with full API docs, usage examples, and feature tables - Add MIT LICENSE to all packages missing it - Add CLAUDE.md to pen-types - Update root CLAUDE.md with index of all sub-CLAUDE.md files - Fix git URL from nicepkg to ZSeven-W * docs: rewrite README for pen-core, pen-figma, pen-renderer, pen-sdk Comprehensive READMEs with full API reference, usage examples, feature tables, and architecture overview for each package. * feat(acp): acpAgents store — persist, hydrate, CRUD actions * feat(acp): pen-acp package + agent settings types + store - pen-acp/types.ts: AcpAgentConfig, AcpAgentInfo, AcpConnectResult, AcpConnectionState - pen-acp/client.ts: connectAcpAgent (local stdio + remote WebSocket), disconnectAcpAgent - pen-acp/event-adapter.ts: acpUpdateToSSE (ACP session/update → SSE events) - agent-settings.ts: AcpAgentConfig type, widen ModelGroup/GroupedModel.provider - agent-settings-store.ts: acpAgents persist/hydrate/CRUD + acpConnectionStatus * fix(acp): remove unused type imports in client.ts * feat(acp): ACP agent settings UI — form, cards, connect/disconnect * feat(acp): add AcpAgentSection to Agents settings tab * refactor(agent): AgentSession as discriminated union (native | acp) * feat(acp): connection manager — connect, disconnect, cleanup * feat(acp): connect/disconnect API route * feat(acp): ACP branch in agent result handler * fix(agent): use NativeAgentSession type for runDelegateMember * feat(acp): ACP prompt SSE stream in agent endpoint * feat(acp): ACP agents in model list + request routing * i18n(acp): add ACP agent translation keys for all 15 locales * fix(i18n): translate ACP keys for all 15 locales + fix missing key references - zh.ts/zh-tw.ts: proper Chinese translations (Agent not translated) - All other locales: translated from English placeholders - Fix acp.add → acp.addAgent, acp.disconnected → acp.notConnected * chore: bump agent-native submodule — surface upstream HTTP errors * feat(acp,build,codegen): comprehensive fixes for ACP integration + prod build ACP agent integration: - Rewrite system prompt to enforce layered design pipeline (get_design_prompt → design_skeleton → design_content → design_refine) for higher quality output - Use correct PenNode field names: content for text, iconFontName for icons - Strict JSON rules to prevent empty-key / trailing-comma / smart-quote errors - Prefer icon_font over path icons (standalone MCP has no hooks registered) - Auto-start MCP server before ACP session (lazy bootstrap) - Auto-reconnect ACP on stale connection (dev server restart scenario) - Auto-approve tool permission requests (trust model: user configured agent) - Use type: 'http' + headers: [] for MCP server config (SDK schema requirement) - Persist ACP connections via globalThis so they survive Vite HMR Build / packaging: - Place agent-native under server/node_modules for Nitro to resolve at runtime - Copy agent_napi.node to napi/ as extraResource - Kill detached MCP server on Electron quit (before-quit + dev SIGINT handlers) - Capture drag-dropped filesystem path via webUtils.getPathForFile so recent files entries are clickable after reopening Codegen: - Compact JSON (no indent) + strip noise fields (id, parentId, default rotation /opacity/visible, layout-managed x/y) to reduce request body size by 60-70% so proxies don't reject with 403 'Request not allowed' MCP batch_design robustness: - splitOperations tracks bracket/quote balance → multi-line JSON now works - Auto-normalize fill/stroke shorthand forms - Collect per-line errors instead of aborting whole batch - Repair empty keys, trailing commas, smart quotes in JSON - Bindless I(...) form supported (auto-generates binding) UI: - ModelDropdown / ChatInput handle ACP model icons (Plug) - Reset streaming state + abort controller on ACP error path - Strip h3 JSON error wrapper so chat shows clean error messages - ACP agent settings form + cards + connect/disconnect * fix(types): resolve TS errors in CI typecheck - acp-connection-manager.ts: correct relative import path (utils/ → src/types) - ai-chat-handlers.ts: cast currentProvider to AIProviderType at design-generator callsites - ai-chat-panel.tsx: explicitly type groups as ModelGroup[] so 'acp' string fits the widened union - acp-agent-settings.tsx: cast window through unknown for Record lookup - electron.d.ts: add getPathForFile to ElectronAPI declaration - builtin-provider-presets.ts: drop now-redundant config.preset !== 'custom' check (handled by early return) - pen-acp/client.ts: cast Writable/Readable.toWeb to typed Streams; coerce nullish agentInfo fields to undefined --------- Co-authored-by: Rais <vdcoolzi@gmail.com>
168 lines
9.2 KiB
Markdown
168 lines
9.2 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
Detailed module docs are loaded automatically when working in subdirectories:
|
||
|
||
- **`packages/CLAUDE.md`** — Package overview (all packages at a glance)
|
||
- **`packages/pen-types/CLAUDE.md`** — Type definitions for PenDocument model
|
||
- **`packages/pen-core/CLAUDE.md`** — Document tree ops, layout engine, variables, boolean ops
|
||
- **`packages/pen-engine/CLAUDE.md`** — Headless design engine (document, selection, history, viewport)
|
||
- **`packages/pen-react/CLAUDE.md`** — React UI SDK (provider, hooks, panels, canvas)
|
||
- **`packages/pen-figma/CLAUDE.md`** — Figma .fig file parser and converter
|
||
- **`packages/pen-renderer/CLAUDE.md`** — Standalone CanvasKit/Skia renderer
|
||
- **`packages/pen-mcp/CLAUDE.md`** — MCP server (tools, routes, document manager)
|
||
- **`packages/pen-ai-skills/CLAUDE.md`** — AI prompt skill engine (phase-driven loading)
|
||
- **`packages/pen-sdk/CLAUDE.md`** — Umbrella SDK (re-exports all packages)
|
||
- **`packages/agent-native/CLAUDE.md`** — Zig agent runtime (NAPI addon)
|
||
- **`apps/web/CLAUDE.md`** — Web app (canvas engine, stores, components, AI services)
|
||
- **`apps/desktop/CLAUDE.md`** — Electron desktop app (IPC, file association, auto-updater)
|
||
- **`apps/cli/CLAUDE.md`** — CLI tool (`op` commands, input methods, connection)
|
||
|
||
## Commands
|
||
|
||
- **Dev server:** `bun --bun run dev` (runs on port 3000)
|
||
- **Build:** `bun --bun run build`
|
||
- **Preview production build:** `bun --bun run preview`
|
||
- **Run all tests:** `bun --bun run test` (Vitest)
|
||
- **Run a single test:** `bun --bun vitest run path/to/test.ts`
|
||
- **Type check:** `npx tsc --noEmit`
|
||
- **Lint:** `bun run lint` (oxlint)
|
||
- **Format:** `bun run format` (oxfmt)
|
||
- **Install dependencies:** `bun install`
|
||
- **Bump version:** `bun run bump <version>` (syncs all package.json files)
|
||
- **Electron dev:** `bun run electron:dev` (starts Vite + Electron together)
|
||
- **Electron compile:** `bun run electron:compile` (esbuild electron/ to out/desktop/)
|
||
- **Electron build:** `bun run electron:build` (full web build + compile + electron-builder package)
|
||
- **CLI compile:** `bun run cli:compile` (esbuild CLI to apps/cli/dist/)
|
||
- **CLI dev:** `bun run cli:dev` (run CLI from source via Bun)
|
||
- **MCP dev:** `bun run mcp:dev` (run MCP server from source)
|
||
- **Publish beta:** `bun run publish:beta [N]` (publish all npm packages with beta tag)
|
||
|
||
## Architecture
|
||
|
||
OpenPencil is an open-source vector design tool (alternative to Pencil.dev) with a Design-as-Code philosophy. Organized as a **Bun monorepo** with workspaces:
|
||
|
||
```text
|
||
openpencil/
|
||
├── apps/
|
||
│ ├── web/ TanStack Start full-stack React app (Vite + Nitro)
|
||
│ ├── desktop/ Electron desktop app (macOS, Windows, Linux)
|
||
│ └── cli/ CLI tool — control the design tool from the terminal
|
||
├── packages/
|
||
│ ├── pen-types/ Type definitions for PenDocument model
|
||
│ ├── pen-core/ Document tree ops, layout engine, variables, boolean ops, clone utilities
|
||
│ ├── pen-engine/ Headless design engine — framework-free document, selection, history, viewport
|
||
│ ├── pen-react/ React UI SDK — DesignProvider, DesignCanvas, hooks, panels, toolbar
|
||
│ ├── pen-codegen/ Multi-platform code generators
|
||
│ ├── pen-figma/ Figma .fig file parser and converter
|
||
│ ├── pen-renderer/ Standalone CanvasKit/Skia renderer
|
||
│ ├── pen-mcp/ MCP server — tools, routes, document manager for external CLI integration
|
||
│ ├── pen-sdk/ Umbrella SDK (re-exports all packages)
|
||
│ ├── pen-ai-skills/ AI prompt skill engine (phase-driven prompt loading + design memory)
|
||
│ └── agent-native/ Native AI agent runtime (Zig NAPI, multi-provider, agent teams)
|
||
├── scripts/ Build and publish scripts
|
||
└── .githooks/ Pre-commit version sync from branch name
|
||
```
|
||
|
||
**Key technologies:** React 19, CanvasKit/Skia WASM (canvas engine), Paper.js (boolean path operations), Zustand v5 (state management), TanStack Router (file-based routing), Tailwind CSS v4, shadcn/ui (UI primitives), Vite 7, Nitro (server), Electron 35 (desktop), Vercel AI SDK v6 (agent framework), i18next (15 locales), TypeScript (strict mode), oxlint/oxfmt (linting & formatting).
|
||
|
||
### Data Flow
|
||
|
||
```text
|
||
React Components (Toolbar, LayerPanel, PropertyPanel)
|
||
│ Zustand hooks
|
||
▼
|
||
┌─────────────────┐ ┌───────────────────┐
|
||
│ canvas-store │ │ document-store │ ← single source of truth
|
||
│ (UI state: │ │ (PenDocument) │
|
||
│ tool/selection │ │ CRUD / tree ops │
|
||
│ /viewport) │ │ │
|
||
└────────┬────────┘ └────────┬──────────┘
|
||
│ │
|
||
▼ ▼
|
||
CanvasKit/Skia canvas-sync-lock
|
||
(GPU-accelerated (prevents circular sync)
|
||
WASM renderer)
|
||
```
|
||
|
||
- **document-store** is the single source of truth. CanvasKit only renders.
|
||
- User edits on canvas → SkiaEngine events → update document-store
|
||
- User edits in panels → update document-store → SkiaEngine `syncFromDocument()` re-renders
|
||
- `canvas-sync-lock.ts` prevents circular updates when canvas events write to the store
|
||
|
||
### Multi-Page Architecture
|
||
|
||
```text
|
||
PenDocument
|
||
├── pages?: PenPage[] (id, name, children)
|
||
└── children: PenNode[] (default/single-page fallback)
|
||
```
|
||
|
||
### Design Variables Architecture
|
||
|
||
- **`$variable` references are preserved** in the document store (e.g. `$color-1` in fill color)
|
||
- `resolveNodeForCanvas()` resolves `$refs` on-the-fly before CanvasKit rendering
|
||
- Code generators output `var(--name)` for `$ref` values
|
||
- Multiple theme axes supported (e.g. Theme-1 with Light/Dark, Theme-2 with Compact/Comfortable)
|
||
|
||
### MCP Layered Design Workflow
|
||
|
||
External LLMs (Claude Code, Codex, Gemini CLI, etc.) can generate designs via MCP:
|
||
|
||
- **Single-shot**: `batch_design` or `insert_node` — one call
|
||
- **Layered**: `design_skeleton` → `design_content` × N → `design_refine` — phased generation with focused context
|
||
- **Segmented prompts**: `get_design_prompt(section=...)` loads focused subsets (schema, layout, roles, icons, etc.)
|
||
|
||
### Path Aliases
|
||
|
||
`@/*` maps to `./src/*` (configured in `apps/web/tsconfig.json` and `apps/web/vite.config.ts`).
|
||
|
||
### Styling
|
||
|
||
Tailwind CSS v4 imported via `apps/web/src/styles.css`. UI primitives from shadcn/ui. Icons from `lucide-react`.
|
||
|
||
### CLI (`apps/cli/`)
|
||
|
||
The `op` command-line tool controls the desktop app or web server from the terminal. Arguments that accept JSON or DSL support three input methods: inline string, `@filepath` (read from file), or `-` (read from stdin).
|
||
|
||
- **App control:** `op start [--desktop|--web]`, `op stop`, `op status`
|
||
- **Design:** `op design <dsl|@file|->` — batch design DSL operations
|
||
- **Document:** `op open`, `op save`, `op get`, `op selection`
|
||
- **Nodes:** `op insert`, `op update`, `op delete`, `op move`, `op copy`, `op replace`
|
||
- **Export:** `op export <react|html|vue|svelte|flutter|swiftui|compose|rn|css>`
|
||
- **Cross-platform:** macOS, Windows (NSIS/portable), Linux (AppImage/deb/snap/flatpak)
|
||
|
||
### CI / CD
|
||
|
||
- **`.github/workflows/ci.yml`** — Push/PR on `main` and `v*` branches: type check, tests, web build
|
||
- **`.github/workflows/build-electron.yml`** — Tag push (`v*`) or manual: builds Electron for all platforms, creates draft GitHub Release
|
||
- **`.github/workflows/publish-cli.yml`** — Tag push (`v*`) or manual: publishes all `@zseven-w/*` npm packages in topological order
|
||
- **`.github/workflows/docker.yml`** — Docker image build and push
|
||
|
||
### Version Sync
|
||
|
||
- **Pre-commit hook** (`.githooks/pre-commit`): extracts version from branch name (e.g. `v0.5.0` → `0.5.0`) and syncs to all `package.json` files
|
||
- **Manual bump:** `bun run bump <version>` to set a specific version across all workspaces
|
||
- Requires `git config core.hooksPath .githooks` (one-time setup per clone)
|
||
|
||
## Code Style
|
||
|
||
- Single files must not exceed 800 lines. Split into smaller modules when they grow beyond this limit.
|
||
- One component per file, each with a single responsibility.
|
||
- `.ts` and `.tsx` files use kebab-case naming, e.g. `canvas-store.ts`, `use-keyboard-shortcuts.ts`.
|
||
- UI components must use shadcn/ui design tokens (`bg-card`, `text-foreground`, `border-border`, etc.). No hardcoded Tailwind colors like `gray-*`, `blue-*`.
|
||
- Toolbar button active state uses `isActive` conditional className (`bg-primary text-primary-foreground`), not Radix Toggle's `data-[state=on]:` selector (has twMerge conflicts).
|
||
|
||
## Git Commit Convention
|
||
|
||
Use [Conventional Commits](https://www.conventionalcommits.org/) format: `<type>(<scope>): <subject>`
|
||
|
||
**Types:** `feat`, `fix`, `refactor`, `perf`, `style`, `docs`, `test`, `chore`
|
||
|
||
**Scopes:** `editor`, `canvas`, `panels`, `history`, `ai`, `codegen`, `store`, `types`, `variables`, `figma`, `mcp`, `electron`, `renderer`, `sdk`, `cli`, `agent`, `i18n`
|
||
|
||
**Rules:** Subject in English, lowercase start, no period, imperative mood. Body is optional; explain **why** not what. One commit per change.
|
||
|
||
## License
|
||
|
||
MIT License. See [LICENSE](./LICENSE) for details.
|