Commit graph

35 commits

Author SHA1 Message Date
Fini ab2a32a806 feat(mcp): add K() batch_design op to instantiate built-in UI kits
Generation could only hand-draw components: the built-in shadcn (31) and
starter (6) UI-kit components were reachable solely through the retired
insert_* MCP tools, never from the design pipeline. The batch_design DSL
already instantiates via C(), but C() clones only nodes already present
in the document, so it can't reach the kits.

Add a K(kitComponentId, parent, overrides) op that routes through the
existing (tested) InstantiateKitComponent command, now extended with
parent-aware placement + overrides. Kit ids use a shadcn/<id> and
starter/<id> scheme. Overrides support top-level keys, recursive
descendants matching by template id (reusing ref_resolve::apply_overrides),
and a label/text convenience; applied before the fresh-id remap. The
sandbox script runner records K() like I()/C(). design-agent.md gains a
compact 37-entry catalog nudging the model to prefer K().

Note: mechanism is verified (unit tests + full gate run); whether the
model adopts K() and improves output is a pending self-loop measurement.
2026-07-04 19:51:59 +08:00
Kayshen-X 686536a15e feat(openpencil): add code-to-design conversion flow 2026-07-04 13:23:28 +08:00
Fini 4bd7cfb7d2 feat(editor): show an edited marker for unsaved changes
Document-content commands allocate a revision from a monotonic
counter (never revision+1: after save, undo, then a new edit that
would collide with the saved value and report a clean file over
divergent content); undo back to the save point turns clean again.
Selection, viewport and panel state do not count. The top bar paints
a muted edited label next to the file name; load/save/save-as reset
the baseline.
2026-07-04 02:17:36 +08:00
Kayshen-X 8901f7209f fix(editor): treat a no-op MergeAppState as success
MergeAppState is additive by design — doc-owned keys win and lower
plan_idx wins, so "nothing to add" is the designed outcome, not a
failure. Returning false made Batch[merge, insert] reject the entire
generated insert whenever the target document already carried every
declared key (regenerating a section over an opened file), and made
batch_program report a line failed after its insert had already
landed. The return now signals "command processed"; regression tests
cover the batch-survival and pre-seeded-doc cases.
2026-07-03 23:14:00 +08:00
Kayshen-X b1aebc3252 feat(ai): agentic design tool-loop, multi-chat tabs, #27 panel restyle
Built-in design generation now runs as an agentic MCP tool-loop (reusing the
agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the
Settings experimental toggle; the orchestrator stays the default.

- design-agent system prompt + in-process design toolset (parity-locked with
  the MCP surface) + flag-gated Intent::Design routing
- spawn_agents execution as sequential sub-loops + live creation-mode badges
  (per-agent glow + 'N/M designing...' header)
- new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state,
  export_nodes, spawn_agents; style-guide local audit
- #27 AI panel restyle: rounded tool cards + green check-rings, gray user
  bubbles, model-pill bottom toolbar, header, empty-state pills, the
  PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown
- multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI
  (switch / close / + / Cmd+T) with each run bound to its tab

Large checkpoint commit spanning the working tree (Rust shell crates).
2026-06-25 00:12:45 +08:00
Kayshen-X ec5ba9c283 fix(editor): repair strokes and legacy render layout 2026-06-21 16:52:31 +08:00
Kayshen-X 1d86fade59 feat(editor): support generated widgets and design tools 2026-06-14 20:25:45 +08:00
Kayshen-X da3b029bc7 feat(editor): TS-parity engine batch — refine passes, atomic command batch, path editing, paste anchoring, IME state, kit model, web-sync seam
Render-time $variable/ref resolution, design-refine deterministic passes,
EditorCommand::Batch with document-only allowlist + snapshot rollback,
pen/bezier path_edit model, TS paste anchoring (container/sibling/instance
quirk), ime_state for native preedit, uikit kit-file plumbing, web_sync
protocol core. Child coords are parent-relative: translate_subtree now
shifts the root only.
2026-06-12 23:25:38 +08:00
Kayshen-X 7a1e7531de fix(canvas): keep auto-layout children in flow 2026-06-06 12:16:12 +08:00
Kayshen-X 0282283cb6 feat(editor): command-layer ops for MCP layered design
Add EditorCommand support powering the MCP layered-design workflow: authored-id subtree insertion for design skeletons, deterministic RefineDesign cleanup, plus batch-page and replace-node apply paths and an InsertSubtree page_id target. Includes command_apply/command_node wiring and tests.
2026-06-02 09:42:04 +08:00
Kayshen-X a26e1b0f48 fix(mcp): support ts move copy targets 2026-06-02 09:42:01 +08:00
Kayshen-X dd34ca8526 fix(mcp): accept ts delete page targets 2026-06-02 09:42:00 +08:00
Kayshen-X 4f251c138c fix(mcp): accept ts update payloads 2026-06-02 09:41:59 +08:00
Kayshen-X 79a2d0830c fix(mcp): support insert parent and page targets 2026-06-02 09:41:57 +08:00
Kayshen-X bbb26b86e8 fix(mcp): support svg import page targets 2026-06-02 09:41:56 +08:00
Kayshen-X aefdf1800f fix(mcp): support svg import parent 2026-06-02 09:41:55 +08:00
Kayshen-X eb083784c9 fix(mcp): add style operation parity 2026-06-02 09:41:42 +08:00
Kayshen-X 8f9b220a27 fix(mcp): add design-md parity 2026-06-02 09:41:35 +08:00
Kayshen-X 1055c8c1b3 fix(mcp): add theme preset parity 2026-06-02 09:41:34 +08:00
Kayshen-X 67ea8c3e25 fix(mcp): add bulk variable parity 2026-06-02 09:41:32 +08:00
Kayshen-X e5f22c0397 fix(mcp): accept page id operations 2026-05-31 22:04:18 +08:00
Kayshen-X 20a6327546 fix(git): blink the create-branch input caret
The inline 新建分支 name input painted a static '|' and the host never woke
the loop for branch_create_focused, so the caret sat frozen. Blink it on the
shared commit-caret cadence (paint_menu_input), wake next_animation_deadline
for branch_create_focused, and seed/reset commit_caret_anchor_ms on focus +
keypress so it starts solid then blinks.
2026-05-31 22:03:59 +08:00
Kayshen-X 059c261028 fix(editor): wire rust ui interactions 2026-05-24 12:57:32 +08:00
Kayshen-X 0948e97de9 feat(panels,canvas): editable gradients/effects + SVG/image import + locale-aware dialogs
Continues the gradient + property-panel polish from the previous
commit and rounds out two new flows the TS app already has:

Gradient stops + effects:
- ColorTarget gains GradientStop(i) + EffectColor(i); HSV picker
  preserves alpha across hue/SV drags so a transparent stop stays
  transparent. Hex pill stays 6-char; alpha is reattached at commit
  and the swatch sits on a 2x2 alpha checker so #00000000 reads as
  transparent rather than empty.
- Effects section reflowed into card-style blocks (image #9 spec):
  title + minus, X/Y and Blur/Spread 2-col grids, color row with
  swatch + rgba(...) text; clicking the swatch opens an HSV picker
  bound to that effect index via SetEffectColor.
- Press dispatch on both hosts anchors picker overlays at the
  clicked y so they pop adjacent to the swatch instead of the top.

Image + SVG import (toolbar + Fill section "图片" row):
- New FileAction::ImportImageOrSvg / PickFillImage; persistence_image
  pops rfd, decodes raster as data: URL, inserts an Image node or
  rewrites the selected node's primary fill.
- ImageNode actually renders on the canvas: NodePayload + SceneNode
  carry image_src, canvas_viewport_paint.rs decodes the data URL
  once and hands raw bytes to RenderBackend::draw_image with a
  src-hash cache id. Grey placeholder paints only when decode fails
  so transparent PNGs don't get a grey matte underneath.
- SVG import ported to TS-parity (packages/pen-engine svg-parser):
  recursive <g> tree walk with inherited fill/stroke/style="...",
  viewBox-aware scaling with maxDim cap, multi-subpath split, raw
  d preserved on PathNode. Imports land wrapped in a Group named
  after the source file.

Locale-aware first run:
- settings_io detects the OS locale (LC_ALL/LANG/LC_MESSAGES with
  zh-Hans/zh-Hant heuristics) and seeds editor_ui.locale before
  settings.json is read; persisted user choice still wins.
- macOS bundle declares CFBundleLocalizations + AllowMixedLocalizations
  so NSOpenPanel / NSSavePanel render in the same language as the
  rest of the chrome.

Web host kept exhaustive across the new variants (PickFillImage,
OpenEffectColorPicker, ColorTarget::EffectColor, GradientStop). Two
new files: persistence_image.rs (file-pick handlers, ≤120 lines) and
svg_path_data.rs (path-d tokenizer + bbox + normaliser, split from
svg_import.rs to stay under the 800-line cap). 277 op-editor-core
tests pass.
2026-05-23 23:11:38 +08:00
Fini 3ff8d92601 Merge branch 'v0.8.0-new' of github.com:ZSeven-W/openpencil into v0.8.0-new 2026-05-23 14:29:26 +08:00
Kayshen-X 2efca5aa3b feat(topbar): sun/moon theme toggle icon + traffic-light reposition fixes
Theme-toggle button now paints a Sun glyph in dark mode (click → light) and a Moon glyph in light mode (click → dark); the Sun icon was hardcoded before. Adds Icon::Moon (lucide crescent) and threads theme_mode into TopBar.

Bumps the casement submodule with a fix for the native macOS traffic-light reposition: idempotent absolute placement against resize, baseline invalidation on fullscreen exit, and a poison guard so a transitional re-capture can't drop the lights below their default position.
2026-05-23 12:59:02 +08:00
Fini 17f960b8d7 feat(orchestrator): validation_fixes — apply property fixes + parent-layout auto-fix
Port applyValidationFixes (TS lines 163-301) and
autoFixParentLayoutAfterAddChild (TS lines 378-433) to Rust.

- New `EditorCommand::SetNodeLayoutProp` + `LayoutPropValue` enum in
  op-editor-core for generic layout/text property writes (gap, padding,
  alignItems, justifyContent, textAlign, textGrowth, opacity, etc.)
- `apply_validation_fixes`: whitelist + value guard, fit_content→pixel
  guard, virtual-property translation (fillColor/strokeColor/strokeWidth),
  status-bar protection on removeNode, addChild deferred to B3
- `auto_fix_parent_layout_after_add_child`: sibling name-base matching,
  child-count guard, justifyContent + gap alignment
- Split to validation_fixes_apply.rs + validation_fixes_b2_tests.rs to
  stay under 800-line ceiling
- 469 tests pass; clippy + fmt clean
2026-05-23 08:36:34 +08:00
Fini 1fa2d9415a Merge branch 's3a-orchestrator' into v0.8.0-new 2026-05-23 01:27:30 +08:00
Fini b1c3136deb feat(editor): EditorCommand::InsertSubtree — nested PenNode subtree insert
Existing EditorCommand variants are leaf-only (BatchInsertItem carries
only kind/name/x/y/w/h/fill_hex). The design orchestrator (S3a) must
apply rich nested designs — frames with children, layout, text — so
add InsertSubtree { nodes: Vec<PenNode>, parent_id }.

cmd_insert_subtree validates the parent is a container (or NONE = page
root), remaps every incoming node id to a fresh editor id via
remap_subtree_ids (so an externally-authored subtree can't collide
with live ids), and appends under the parent. The apply arm wraps it
in a history snapshot so the insert is one undo step.

NOT verified locally: op-editor-core does not currently build —
vendor/jian is pinned to unpushed commit 80121906 whose DesignMd*
types op-editor-core depends on are absent from every available jian.
The 8 InsertSubtree tests in command_subtree_tests.rs run once the
jian build is restored.

S3a Plan A.
2026-05-22 22:56:10 +08:00
Kayshen-X e8cf002a7f feat(mcp): expose UIKit components as insert_<comp> MCP tools
Closes the architectural piece of the "MCP element toolset" P1 gap
(TS pen-mcp ships ~100 add_card_*/add_toast_* element tools).

- op-editor-core: new `EditorCommand::InstantiateKitComponent`
  variant + applier branch that calls
  `EditorState::instantiate_kit_component` with the requested
  drop point (defaults to (0, 0)).
- op-mcp: `element_tools.rs` — `InsertKitComponent` per-component
  tool returning `OkWithCommand(_, InstantiateKitComponent)`;
  `insert_kit_component_tools(state)` walks every loaded kit;
  `element_tool_schemas(state)` emits the matching tools/list
  JSON. Tool names sanitize dashes: `insert_btn_primary`,
  `insert_card_basic`, etc.
- op-host-desktop: `rebuild_registry` chains the dynamic tools
  in; `tools_list_response` takes EditorState and appends the
  dynamic schemas next to TOOL_SCHEMAS.
- op-editor-core: tidies the empty-pages `ensure_pages` guard to
  `is_none_or`.

Result: 6 starter-kit components → 6 working MCP tools. The 100-tool
catalog parity is now a data fill-in (more components in op-editor-
core/uikit.rs auto-register as more MCP tools).
2026-05-21 21:55:44 +08:00
Kayshen-X 722445b017 feat(panels): shadow/blur parameter steppers in the Effects section
Completes the Effects-controls gap — each effect row now exposes its
editable scalar parameters:

- op-editor-core: `EffectField` enum + `EditorCommand::SetEffectParam`
  + `cmd_set_effect_param` — writes one shadow param (offset X/Y,
  blur, spread) or a blur/background-blur radius; blur values clamp
  to >= 0, field/effect mismatches reject.
- panels: each effect block paints a parameter row per field —
  `<label> <value> [-] [+]`; the "-"/"+" steppers emit
  `AdjustEffectParam` (the walker computes the post-step value from
  the current one). `effects_section_height` / the action-rect
  walker now take the effects slice so the variable per-kind block
  height stays aligned with paint (`VisibleSections.effect_count`
  retired in favour of the slice).
- both hosts dispatch `AdjustEffectParam` via `SetEffectParam`,
  history-committed.

op-editor-core 233 / op-editor-ui 140 / op-host-desktop 64 tests
green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-17 14:38:26 +08:00
Kayshen-X aca1af5447 feat(editor): add node-effect add/remove commands + MCP tools
Effects were inert — the schema carries `PenEffect` (Shadow / Blur /
BackgroundBlur) but nothing could add or drop one.

- editor: `EditorCommand::AddNodeEffect` / `RemoveNodeEffect` +
  appliers. `node_effects_slot` reaches the `effects` field on every
  variant that has one (Frame/Group/Rectangle via `container`, the
  leaf shapes directly); `add` appends a default-parameter effect,
  `remove` drops by index and clears the list to `None` when empty.
- mcp: `add_node_effect` / `remove_node_effect` tools, registered on
  the host server (catalog 80 -> 82).

This is the command + MCP layer of the Effects gap; the property-
panel editing UI is the remaining piece. op-editor-core 227 /
op-mcp 138 / op-host-desktop mcp tests green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-17 13:48:31 +08:00
Kayshen-X c18ad5a776 feat: close six TS-parity gaps in the Rust shell
Closes six verified gaps from the 2026-05-17 TS-vs-Rust gap analysis,
each build- and test-green:

- editor: SetNodeFlip + SetEllipseArc commands (+ set_node_flip /
  set_ellipse_arc MCP tools) — schema already had the fields, only
  the command path was missing.
- export: export_node_raster crops a raster to one node's bbox;
  File -> Export is now selection-aware (single selection -> layer).
- editor: SVG import — hand-rolled parser (shapes + path M/L/H/V/
  C/S/Q/T/Z) in svg_import.rs; cubic curves flatten to dense straight
  anchors at import time so the renderer/pen-tool stay on their 1:1
  straight-segment model. + EditorCommand::ImportSvg + import_svg tool.
- mcp: HTTP transport — mcp_serve::run_http serves MCP over a
  TcpListener (--mcp-http <port> <path>); process_message is shared
  with the stdio path.
- cli: new op-cli crate (binary `op`) — a dependency-free HTTP MCP
  client driving every tool via `op <tool> key=value...`.
- ai: ChatRequest gains thinking/effort fields (ThinkingMode /
  EffortLevel, defaults Adaptive/Low to match the TS runtime config).

MCP tool catalog 77 -> 80. Oversized files split to honour the
800-line cap (svg_import, export, mcp_serve, adapter).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-17 13:03:11 +08:00
Kayshen-X 90c1cc4360 style: apply cargo fmt across the workspace 2026-05-17 00:26:15 +08:00
Kayshen-X 6cd2b5cd8c feat(op-editor-core): EditorCommand DTO + EditorState::apply 2026-05-16 16:32:44 +08:00