Commit graph

6 commits

Author SHA1 Message Date
Kayshen Xu
3c697d817d
V0.6.0 (#88)
* fix(renderer): skip paragraph image cache when zoomed in

* refactor(publish-cli): streamline version publishing process

- Removed the check for already published versions to simplify the workflow.
- Updated npm publish commands to handle already published versions gracefully, allowing the process to continue without aborting.
- Added publishing step for the new pen-ai-skills package, ensuring it is included in the deployment process.

* fix(mcp): wire openpencil codex install to config toml (#85)

Co-authored-by: Kaiiiiiiiii <182183652+Smile232323@users.noreply.github.com>

* refactor(mcp): reorganize codex-cli handling in installation process

- Removed the codex-cli configuration from CLI_CONFIGS and adjusted the installation logic to handle codex-cli actions directly.
- Improved error handling for unknown CLI tools and streamlined the installation process for codex-cli, ensuring it uses its own commands for adding/removing components.

* feat(agent): add built-in agent SDK and web integration

Introduces @zseven-w/agent — a domain-agnostic agent SDK with:
- Agent loop (plan→act→observe) with dual execution model
- Anthropic + OpenAI-compatible provider adapters via Vercel AI SDK
- Tool registry with auth levels (read/create/modify/delete/orchestrate)
- Agent team orchestration with delegate tool
- SSE streaming protocol encoder/decoder
- Sliding window context management

Web app integration:
- Built-in provider config UI with presets (Anthropic, OpenAI, OpenRouter, DeepSeek)
- Model search via provider API proxy endpoint
- Built-in models appear in the model dropdown alongside CLI agents
- Server-side agent SSE endpoint with session management
- Client-side tool executor with undo batch support
- Inline tool call blocks in chat UI

* feat(api): implement keep-alive pings for long-running LLM calls

- Added a keep-alive mechanism in the SSE stream for the agent API to prevent timeouts during long-running LLM calls.
- Updated the keep-alive interval to 8 seconds to align with Bun.serve's default idle timeout of 10 seconds.
- Ensured proper cleanup of the ping timer when the stream is closed.

* feat(mcp): enhance live sync diagnostics and port file management

- Implemented a mechanism to probe the live sync server's availability, providing clearer diagnostics for connection issues.
- Updated the port file structure to include a unique token for better management and cleanup.
- Enhanced the `getSyncUrl` and `getLiveSyncState` functions to return detailed status messages based on the server's response.
- Added tests for live sync diagnostics to ensure accurate reporting of server states.

* feat(mcp): implement sync URL caching for improved performance

- Added caching mechanism for sync URLs to reduce repeated reads and health checks, enhancing performance during live document and selection fetches.
- Introduced functions to set and clear the cached sync URL, improving connection management.
- Updated existing functions to utilize the cached URL when available, streamlining the fetching process.

* feat(skia): enhance syncFromDocument error handling and normalize document structure

- Wrapped the syncFromDocument method in a try-catch block to log errors during synchronization, improving debugging capabilities.
- Introduced normalization of external documents in the document store to ensure consistent structure before processing.
- Updated text content resolution to handle both 'content' and 'text' fields, enhancing compatibility with various node formats.
- Refactored text measurement functions to streamline text handling and improve layout consistency.

* chore: update package versions to 0.6.0 across all modules and enhance code generation pipeline

- Bumped version from 0.5.3 to 0.6.0 in package.json files for all modules including core, agent, and various code generation packages.
- Refactored code generation functions to utilize a new AI code generation pipeline, marking previous functions as deprecated with plans for removal in v1.0.0.
- Introduced new export nodes functionality in the MCP server for raw PenNode data export.

* fix(mcp): clear stale sync cache on page load to prevent false dirty state

When refreshing the page or opening a new file, the SSE document:init
event would echo back the old cached document, causing applyExternalDocument
to unconditionally set isDirty=true and trigger a spurious save prompt.

* style(panels): improve AI checklist visual design

Add progress bar, spinner animation, highlighted active items, and
pill-shaped counter badge for a more polished orchestrator progress UI.

* feat(ci): add Homebrew Cask update workflow for OpenPencil releases

- Implemented a new GitHub Actions job to update the Homebrew Cask for OpenPencil upon new version tags.
- The workflow fetches the latest macOS binaries, computes their SHA256 checksums, and updates the cask formula accordingly.
- Includes steps for committing and pushing changes to the tap repository.

* docs: update installation instructions in README and enhance CI workflow for Scoop bucket updates

- Added detailed installation instructions for macOS, Windows, and Linux in the README.
- Introduced a new GitHub Actions job to update the Scoop bucket with the latest OpenPencil release, including versioning and SHA256 checksum computation for Windows binaries.

* docs: add OpenPencil Skill plugin link to all i18n READMEs

Add localized LLM Skill link in CLI section across all 15 README
files, pointing to zseven-w/openpencil-skill repository.

* docs: add same-name project disclaimer to all i18n READMEs

Distinguish from open-pencil/open-pencil (Figma-compatible visual
design with real-time collaboration) — this project focuses on
AI-native design-to-code workflows.

* fix(agent): resolve SSE timeout, routing, and message format issues

- Add 5s keep-alive ping to agent SSE stream (prevents Bun 10s idle timeout)
- Consolidate tool-result and abort into single agent endpoint (?action=result|abort)
- Fix assistant message history: include text alongside tool-call parts
- Fix ToolResultPart format: use 'output' field with {type:'text',value} structure
- Use openai.chat() instead of openai() for Chat Completions API (OpenRouter compat)
- Use proper TOOL_SCHEMAS instead of z.any() for tool parameters

* fix(agent): prevent sliding window from splitting assistant+tool groups

The sliding window context strategy was naively slicing by message count,
which could leave orphaned tool_result messages without their preceding
assistant tool_use — causing Claude API to reject the request.

Now counts "logical turns" (user/assistant starts) and never cuts inside
an assistant→tool group. Also increased default window from 10 to 50 turns.

* fix(agent): use correct AI SDK v6 parameter name maxOutputTokens

streamText() in AI SDK v6 renamed maxTokens to maxOutputTokens.
Default 4096 prevents OpenRouter credit limit errors from requesting
the model's full 65536 token capacity.

* feat(agent): add API error classification and auto-retry without tools

When a model doesn't support function calling (400 errors, missing
arguments), the agent loop now classifies the error and automatically
retries without tools on the first turn. Also provides user-friendly
error messages for common failures: privacy restrictions, credit limits,
rate limiting, and incompatible models.

* fix(agent): improve system prompt with PenNode schema and workflow guidance

- Add PenNode property schema to agent system prompt so models know
  valid properties (fills, cornerRadius, padding array, etc.)
- Explicitly forbid CSS properties (backgroundColor, boxShadow, etc.)
- Add workflow steps: plan before creating, avoid create-then-delete
- Change max turns reached from error to normal done event

* feat(agent): align insert_node with CLI batch_design capability

- insert_node now supports nested children arrays (recursive ID generation)
- Post-processing pipeline runs after insertion: role resolution, icon
  resolution, layout sanitization, unique ID enforcement — same as MCP
  batch_design with postProcess=true
- System prompt teaches model to create entire designs in ONE insert_node
  call with nested children, instead of 20+ individual calls
- Includes concrete example of nested PenNode structure in prompt

* fix(agent): deep-clone node before post-processing to avoid Zustand mutation

Zustand state is immutable — post-processing functions mutate in-place.
JSON.parse(JSON.stringify()) creates a mutable copy. Each post-processing
step is individually try-caught so partial failures don't break insertion.

* fix(agent): return node count in insert_node result to prevent model retries

insert_node now returns {id, nodesCreated, message} so the model knows
the full tree was created. System prompt reinforces: when insert_node
succeeds, do NOT retry. Reduces 5+ duplicate insert calls to exactly 1.

* fix(agent): prevent duplicate root-level inserts from weaker models

Track the first root-level insert_node ID per session. Subsequent
root-level insert_node calls return the existing ID with a message
instead of creating duplicates. Fixes M2.5 and similar models that
ignore "do not retry" prompt instructions.

* feat(agent): support Anthropic API format in custom provider settings

Custom provider preset now has an API Format toggle: "OpenAI Compatible"
or "Anthropic". This allows users to configure custom Anthropic-compatible
endpoints (proxies, mirrors) in addition to OpenAI-compatible ones.
Anthropic provider also now accepts optional baseURL for proxy support.

* fix(agent): update Custom option label in provider dropdown

* fix(agent): match 'invalid function arguments' in error classifier for auto-retry

* fix(agent): always auto-retry without tools on first turn errors

Many providers (MiniMax, StepFun) have broken tool call implementations.
Instead of pattern-matching specific error messages, any streaming error
on turn 0 now triggers auto-retry without tools. The model falls back
to generating a text-only response instead of failing entirely.

* fix(agent): use clean JSON Schema for tool definitions (no $schema field)

Replaced Zod schemas with jsonSchema() from AI SDK for tool parameter
definitions. Zod's zodToJsonSchema adds "$schema" and "additionalProperties"
fields that strict OpenAI-compatible APIs (MiniMax, StepFun) reject with
"invalid function arguments". Clean JSON Schema only has type/properties/
required — compatible with all providers.

Also added text-mode fallback prompt for when tools are truly unsupported.

* fix(agent): ensure tool call args is always {} and fix turn>0 retry

Two fixes for MiniMax "invalid function arguments" error:
1. Ensure args is always {} (never undefined/null) when building
   conversation history — undefined args causes strict APIs to reject
2. Auto-retry without tools now works on ANY turn (not just turn 0)
   by resetting history to original user messages and restarting

* fix(agent): use SDK response.messages for history instead of manual construction

Root cause of MiniMax "invalid function arguments" error: we were manually
constructing ModelMessage[] for conversation history, which the SDK then
failed to convert correctly to OpenAI format (dropping function.arguments).

Fix: use response.response.messages from the SDK — it produces correctly
formatted messages that the provider knows how to convert. Only manually
add tool result messages for client-executed tools (no execute() function).

Also keeps a fetch interceptor as safety net to patch any remaining
edge cases with missing/malformed arguments.

* fix(agent): parse stringified JSON in insert_node data parameter

Some models (MiniMax M2.5) send insert_node data as a JSON string
instead of an object. Now handleInsertNode detects string data and
JSON.parse it before processing. Fixes nodesCreated:1 when model
sends nested children as a stringified object.

* fix(agent): sanitize node data and catch addNode side-effect errors

- Convert model's 'border' property to valid 'strokes' array
- Filter null/non-object children before processing
- Wrap docStore.addNode in try-catch — canvas sync side-effects
  (e.g., renderer hitting unknown properties) may throw but the node
  IS still inserted. Return success regardless.

* fix(agent): auto-zoom to fit after insert_node to show new design

* fix(agent): align insert_node with batch_design — auto-replace empty root frame

When inserting a frame at root level and an empty root frame exists,
replace it and inherit its position (x:0, y:0). This matches the MCP
batch_design behavior (line 146-161) so the design lands in the visible
viewport instead of off-screen at x:1250.

* fix(agent): use updateNode for empty frame replacement (no duplicate keys)

The remove+add pattern caused React duplicate key errors because two
separate Zustand state updates could leave the node in two places.
Now uses a single updateNode call to replace the empty frame's
properties in-place — atomic operation, no duplicates.

* fix(agent): set replaced=true BEFORE updateNode to prevent duplicate on side-effect throw

* fix(agent): use atomic tree swap for empty frame replacement

updateNode caused canvas sync crash (map on undefined) because the
renderer couldn't handle a complete property replacement on an existing
node. Now uses removeNodeFromTree + insertNodeInTree in a single
setState — same atomic approach as MCP batch_design. The Skia engine
sees the new node as a fresh addition, not an update to an existing one.

* fix(agent): simplify empty frame replacement to removeNode+addNode via store API

* refactor(agent): use applyNodesToCanvas instead of raw addNode

The agent's insert_node was bypassing the design pipeline's
sanitization and canvas sync code, causing blank canvas rendering.
Now delegates to the SAME applyNodesToCanvas function used by the
CLI/MCP design pipeline. This handles:
- sanitizeNodesForInsert (role defaults, layout fixes, unique IDs)
- isCanvasOnlyEmptyFrame → replaceEmptyFrame (empty frame at 0,0)
- icon resolution and image scanning
- proper Skia canvas sync

Removed the custom postProcessNode method — no longer needed since
applyNodesToCanvas does the same work correctly.

* feat(agent): add generate_design tool using internal CLI design pipeline

generate_design delegates to the SAME generateDesign() function the
standard chat uses — orchestrator, sub-agents, streaming insertion,
post-processing. Finds the first connected CLI provider for LLM calls.
Also uses insertStreamingNode with resetGenerationRemapping() for
insert_node, matching the CLI streaming pattern exactly.

* feat(agent): replace insert_node with generate_design as primary design tool

Remove insert_node and find_empty_space from agent tool set — models
should use generate_design for creating designs, not raw PenNode JSON.
generate_design accepts natural language prompts and delegates to the
full internal pipeline. Keeps update_node/delete_node for modifications.

* feat(agent): simplify system prompt to direct models to generate_design

Clear instruction: when user asks to create/design, MUST call
generate_design tool. No JSON output, no manual node construction.

* fix(agent): strip <think> tags from model text output in chat UI

Models like MiniMax M2.5 and DeepSeek output <think>...</think> as
plain text (not via the thinking SSE event). Strip both closed and
unclosed <think> tags from the displayed text during streaming.

* fix(renderer): skip paragraph image cache when zoomed out below 0.5x

Bitmap text cache at fixed DPR resolution produces jagged edges when
scaled down significantly. Now only uses cache at 0.5x–1x zoom range.
Below 0.5x and above 1x, falls back to vector Paragraph API rendering.

* fix(ai): prevent model from using OpenPencil as brand name in generated designs

* fix(agent): strip <think> tags from final message, not just during streaming

The regex was only applied during text-delta events but the final
message (returned by runAgentStream and set in done/error handlers)
used the raw accumulated text. Now stripThinkTags() is applied at
the return point so the final displayed message is always clean.

* fix(ai): reduce keepalive ping interval from 15s to 5s for Bun compatibility

Bun.serve has a 10s idle timeout. The previous 15s ping interval meant
the first ping arrived AFTER Bun killed the connection, causing
"socket connection was closed unexpectedly" errors when waiting for
slow LLM responses (extended thinking, TTFT > 10s). Now pings at 5s.

* fix(renderer): rasterize paragraph image cache at 2x minimum for crisp text on low-DPR

* fix(agent): remove hardcoded TOOL_SCHEMAS and maxOutputTokens

- Server uses client-provided JSON schemas (single source of truth)
- maxOutputTokens configurable per provider, defaults to model limit
- turnTimeout increased to 5min for generate_design pipeline
- Session cleanup wrapped in try-catch
- Re-export streamText from agent SDK for consumer use

* feat(agent): add builtin provider support to /api/ai/chat endpoint

streamViaBuiltin() uses Vercel AI SDK to call LLM directly with API
key — no CLI tool required. ai-service.ts attaches builtin credentials
from agent settings store when provider is 'builtin'. This enables
generate_design to work without any CLI provider connected.

* fix(agent): align generate_design params with CLI pipeline and handle field name variants

- Pass concurrency, variables, themes, designMd to generateDesign()
  (was missing, causing lower quality output vs CLI mode)
- Accept both 'prompt' and 'description' field names from models
- Fall back to builtin provider when no CLI provider connected
- Enable animated node insertion

* feat(agent): dynamic system prompt via pen-ai-skills + misc fixes

- Replace hardcoded AGENT_SYSTEM_PROMPT with buildAgentSystemPrompt()
  that loads design knowledge from pen-ai-skills (same as CLI pipeline)
- Validate builtin provider apiKey before agent connection
- Client-side tool schema serialization for server (no duplication)
- Update tool_call block status locally as fallback for lost SSE events
- Pass maxOutputTokens from provider config

* refactor(agent): extract builtin provider settings to separate file

Split BuiltinProviderForm, BuiltinProviderCard, BuiltinProvidersSection
from agent-settings-dialog.tsx (1279→730 lines) into
builtin-provider-settings.tsx (394 lines). Both under 800 line limit.

* refactor(agent): extract model selector to separate file

Split ModelDropdown, ConcurrencyButton, resolveNextModel, PROVIDER_ICON
from ai-chat-panel.tsx (915→690 lines) into ai-chat-model-selector.tsx
(183 lines). Both under 800 line limit.

* fix(renderer): guard against undefined fill entries in resolveFillColor/resolveStrokeColor

* feat(ai): extend BuiltinProviderPreset with 9 new providers

Add MiniMax, Zhipu, Kimi, Bailian, DouBao, Xiaomi MiMo, ModelScope,
StepFun and NVIDIA NIM to the builtin provider preset type union.

* feat(ai): add provider presets and region switcher for builtin providers

Add preset configs for MiniMax, Zhipu, Kimi, Bailian (DashScope),
DouBao Seed, Xiaomi MiMo, ModelScope, StepFun and NVIDIA NIM with
correct API base URLs and model placeholders.

Providers with separate China/Global endpoints (MiniMax, Zhipu, Kimi,
Bailian, StepFun) get a region toggle that auto-switches the base URL.
Region inference works both for new providers and when editing existing
ones via REGION_URLS reverse-lookup table.

* style(ai): increase settings dialog height for more provider entries

* feat(ai): add i18n for builtin provider settings (15 locales)

Extract all hardcoded strings in builtin-provider-settings.tsx to
i18n keys under the builtin.* namespace. Add translations for all
15 supported languages: en, zh, zh-TW, ja, ko, fr, es, de, pt,
ru, hi, tr, th, vi, id.

* fix(ai): remove remaining hardcoded strings in builtin provider UI

- Extract error messages, badge text, tooltip, and placeholder to i18n
- Remove unused region label fields from PresetRegion interface
- Add 8 new builtin.* keys across all 15 locales
- Fix ai-chat-model-selector API Key badge and parallel agents tooltip
- Fix ai-chat-panel provider description template
- Fix ai-chat-handlers error messages via i18n.t()

* fix(agent): clean up pending tool calls on exit and improve error handling

P0: Wrap agent loop generator in try-finally to reject all pending tool
call promises and clear timeout timers when the loop exits. Prevents
timeout leaks when sessions end early.

P1: Add HTTP status code matching (429, 402, 403) before regex fallback
in classifyAPIError for more reliable error classification.

P1: Clone tool registry in createTeam instead of mutating the caller's
config.lead.tools. Add doc comment for unused _maxTokens parameter in
sliding-window strategy.

* fix(ai): add error handling and retry for tool result POST

The POST to /api/ai/agent?action=result had no error handling. If it
failed, the agent loop would hang forever waiting for the tool result.
Now retries once on failure and logs the error.

* fix(ai): use lastActivity for session TTL and guard stream init

P1: Add lastActivity timestamp to AgentSession, updated on every event
and tool result. TTL cleanup now checks lastActivity instead of
createdAt, preventing active long-running sessions from being killed.

P1: Wrap ReadableStream construction in try-catch to ensure session
cleanup if stream initialization fails.

* chore: add docs/ to gitignore

* fix(ai): update model lists and fix model search for providers

- Update Anthropic models to include Claude 4.6 (Opus + Sonnet)
- Update model placeholders: OpenAI→gpt-5.4, MiniMax→M2.7,
  Zhipu→glm-5, Kimi→kimi-k2.5, Xiaomi→mimo-v2-pro
- Add hardcoded model lists for MiniMax and DouBao (no /models API)
- Generalize ANTHROPIC_MODELS to BUILTIN_MODEL_LISTS lookup table
- Fix provider-models proxy to handle { models: [...] } and array
  response formats in addition to OpenAI's { data: [...] }

* docs: update CLAUDE.md and README files for new AI agent SDK and i18n support

- Added new sections in CLAUDE.md detailing the AI agent SDK and its multi-provider capabilities.
- Updated README files in multiple languages to include information about the built-in AI agent SDK and full interface localization in 15 languages.
- Adjusted key technologies and scopes to reflect recent changes in the project structure and features.

* docs(agent): add Agent Team web integration design spec

Phase 3 design: model routing via Agent Team. Lead agent uses fast
model for chat, designer member uses capable model for generation.
SDK changes: source tracking on events, member_start/end events,
auto team suffix. Web changes: team toggle + design model selector
in settings, conditional createTeam in server endpoint.

* fix(electron): fix Scoop/Homebrew tap auto-update in CI

- Add pre_install to Scoop manifest to rename versioned exe to OpenPencil.exe
- Add mkdir -p for Casks/ and bucket/ dirs in tap repos
- Use curl -fSL to fail fast on HTTP errors instead of silent mode

* docs(agent): add Agent Team web integration implementation plan

9 tasks: SDK event types → SSE tests → team source injection →
server team endpoint → settings store → team UI → i18n →
chat handler wiring → e2e verification

* feat(agent): add source field and team events to AgentEvent type

* test(agent): add SSE encoder/decoder tests for source and team events

* feat(agent): inject source field and team events in agent-team

* feat(ai): extend agent endpoint to support team mode with members

* feat(ai): add teamEnabled and teamDesignModel to agent settings store

* feat(ai): wire team mode in chat handlers with member events

* feat(ai): add i18n keys for team settings (15 locales)

* feat(ai): add Team section UI with design model selector

* fix(agent): route resolveToolResult to correct agent in team mode

Member tool calls (e.g. generate_design) were routed to leadAgent
which didn't have them in its pending map. Added toolCallOwners map
to track which agent (lead or member) issued each tool call and
route resolveToolResult accordingly.

* docs(agent): clarify resolveToolResult routing, member tools, and source handling

* fix(agent): force delegation in team mode and add turnTimeout support

Two fixes for team mode generation failures:
1. Remove generate_design from lead's tool registry in team mode,
   forcing the lead to delegate design work to the designer member
2. Add turnTimeout to TeamMemberConfig/TeamConfig and pass 5min
   timeout to member agents (generate_design needs it)

* fix(ai): make team mode visually distinct in chat UI

- Add source badge on tool call blocks (shows "designer" pill for
  member-initiated tool calls)
- Pass evt.source to ToolCallBlockData
- Use blockquote format for member_start/end events so team activity
  is clearly visible in the chat stream

* fix(agent): scope designer tools, streamline delegation, add model hint

1. Designer member only gets generate_design + snapshot_layout
   (was: all tools, causing 7 wasteful batch_get calls)
2. Team suffix tells lead to delegate immediately without verbose
   planning text
3. UI shows amber tip when only 2 providers configured, suggesting
   a more capable model for design

* fix(ai): clean up partial nodes on generate_design failure

When generate_design fails midway, partial nodes were left on the
canvas and rootInsertId was not set, allowing a retry that created
a duplicate design. Now:
1. Set rootInsertId='generating' BEFORE calling generateDesign
2. Snapshot node IDs before generation
3. On failure, remove all new nodes and reset rootInsertId to null
4. Remove incorrect same-model warning from Team UI

* fix(ai): show Team section with 1 enabled provider

* refactor(ai): smart team mode — auto-enable when design model is set

Remove manual Team toggle. Selecting a Design Model automatically
enables team mode; "None (single agent)" disables it. Simpler UX:
one dropdown replaces toggle + dropdown. Updated descriptions and
removed obsolete teamTitle/teamSameModelWarning i18n keys.

* fix(agent): properly serialize non-Error objects in error messages

String(err) on plain objects produces "[object Object]". Changed to
err instanceof Error ? err.message : JSON.stringify(err) for readable
error messages in tool results and design generation failures.

* refactor(ai): auto team mode using current chat model

Remove Design Model dropdown from settings. Team mode is now fully
automatic: the designer member always uses the same provider/model
as the current chat selection. Zero configuration needed — the value
is in role separation (different prompts + scoped tools), not model
selection.

* style(ai): use min/max height for settings dialog instead of fixed height

* feat(ai): add Google Gemini to builtin provider presets

Base URL: generativelanguage.googleapis.com/v1beta/openai (OpenAI
compatible). Hardcoded model list: Gemini 2.5 Pro, 2.5 Flash, 2.0
Flash. API key placeholder: AIza...

* feat(ai): update Gemini models to 3.x series

---------

Co-authored-by: Fini <fini.yang@gmail.com>
Co-authored-by: Kaiiiiiiiii <2761362118@qq.com>
Co-authored-by: Kaiiiiiiiii <182183652+Smile232323@users.noreply.github.com>
2026-03-28 23:12:37 +08:00
Kayshen Xu
fac5ec0293
V0.5.0 (#71)
* 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

* feat(docker): update Dockerfile to include additional package.json files

- Added package.json files for multiple packages (pen-types, pen-core, pen-codegen, pen-figma, pen-renderer, pen-sdk) and apps (web, desktop) to the Docker build context.
- Ensured all necessary dependencies are included for a complete build process.

* refactor(docker): update Dockerfile to use new output directory structure

- Changed the output directory from `/.output` to `/out/web` for all build stages in the Dockerfile.
- Updated CMD instructions to reflect the new path for the server entry point.
- Ensured consistency across all CLI variants for improved maintainability.

* fix(main): update server entry point paths for development and production

- Changed the development server entry point path from `.output/server/index.mjs` to `out/web/server/index.mjs`.
- Updated the production comment to reflect the new output directory structure for consistency.

* chore(docker): increase Node.js memory limit in Dockerfile

- Set NODE_OPTIONS to increase the maximum old space size to 4096 MB for better performance during builds.

* refactor(docker): remove Gemini CLI build stage from Dockerfile

- Eliminated the build stage for the Gemini CLI from the Dockerfile.
- Updated the CMD instruction to reflect the new output directory structure.
- Ensured consistency across all CLI variants for improved maintainability.

* refactor: update ignore patterns for build output

- Changed `.output` to `out` in both `.dockerignore` and `.gitignore` to align with the new output directory structure.
- Ensured consistency in ignoring build artifacts across the project.

---------

Co-authored-by: Fini <fini.yang@gmail.com>
Co-authored-by: leinaldo <60176594+leinaldo@users.noreply.github.com>
2026-03-22 10:09:26 +08:00
Kayshen Xu
03d4433693
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 09:44:04 +08:00
Kayshen Xu
80636b0d5b
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 09:33:52 +08:00
Kayshen-X
d51510d7e9 feat(electron): integrate Electron framework for desktop application support
- Add Electron configuration and main process setup for building a desktop application.
- Implement IPC communication for file operations (open, save) between the renderer and main processes.
- Create a preload script to expose Electron APIs to the renderer.
- Update package.json to include Electron and related dependencies.
- Enhance the build process with electron-builder for packaging the application.
- Introduce a new electron-builder.yml configuration file for build settings.
- Modify Vite configuration to support Electron-specific builds.
- Update UI components to accommodate Electron's window management and drag regions.
2026-02-20 20:19:06 +08:00
Kayshen-X
c009ab7925 Initialize OpenPencil project with essential files and configurations
- Added .cta.json for project settings including TypeScript, Tailwind, and Bun as package manager.
- Created .gitignore to exclude common build and environment files.
- Introduced bun.lock for dependency management.
- Added CLAUDE.md for guidance on using Claude Code with the project.
- Set up package.json with scripts for development, build, and testing.
- Created README.md outlining project features, tech stack, and getting started instructions.
- Configured TypeScript with tsconfig.json for strict type checking.
- Established Vite configuration in vite.config.ts for project bundling.
- Added VSCode settings to exclude generated files from linting.
- Included public assets such as favicon and logos.
- Implemented initial routing structure in src/router.tsx and generated route tree in src/routeTree.gen.ts.
- Developed canvas functionality with multiple files for object management and event handling in src/canvas.
- Styled the application using Tailwind CSS in src/styles.css.
2026-02-17 21:14:16 +08:00