- crates/CLAUDE.md MCP catalog was stale at 21 tools; replace the
outdated table with the current 77-tool categorized summary and
point at mcp_serve.rs TOOL_SCHEMAS as the source of truth.
- Refresh the mcp/ file-layout list (node_attr_tools,
selected_ops_tools, extra_read_tools, json_serializer, …).
- New "AI chat (real provider integration)" section documenting
begin_send / ChatProvider / chat_session / the model chip and
the Codex/OpenCode honest-error behavior.
- chat_session: gate the test-only `finished()` accessor behind
`#[cfg(test)]` to drop a dead-code warning.
Adds a new "MCP server (shell-core)" section to
crates/CLAUDE.md covering:
- The 14-tool catalog (6 read + 8 write) with arg shapes and
emitted McpCommand variants per tool.
- Pre-validate-then-mutate discipline at the apply layer.
- `replace_node`'s `drop_children` destructive-swap guard.
- Wire-format stop-gates (structured-arg rejection, top-level
`arguments` walker, no-hang on parse failure, read-only path
refusing write tools).
- mcp/ file layout including the sibling test files for
copy_node and replace_node.
- Pending: host wiring, batch_design / design_skeleton tools,
HttpServer transport.
Pivot the desktop's Open path to the canonical `jian-ops-schema`
parser and route layout through `jian-core::LayoutEngine` so files
saved by the TS editor, Jian apps, or any tool emitting the
canonical schema load through the shared parser + paragraph shaper.
Loader (pen_doc_adapter.rs + pen_doc_path_bounds.rs)
- All 12 PenNode variants → NodePayload, with each root's authored
(base.x, base.y) added to harvested rects so multi-design files
(e.g. pencil-demo.op's 14 mockups) spread across the canvas.
- Path anchors port `getPathBoundsFromAnchors` — endpoints + Bezier
handles + cubic-derivative extrema — so curved paths scale into
their (width, height) the way the canonical renderer paints.
- jian-skia's `SkiaMeasure` plugged in via
`LayoutEngine::with_backend(...)`, replacing the ~10% character-
count heuristic with real paragraph-shaper metrics. Wrap/layout
now agree with paint instead of cascading 10% errors.
- Numeric-string fontWeight (`"700"`, `"normal"`, ...) parsed in
both jian-core and the desktop adapter; expanded keyword table
covers black/heavy/extralight/extrabold/demibold/hairline/etc.
- Version-tolerant `load_canonical` retries with `version` rewritten
to `"1.0"` so legacy `version: "2.8"` files still load.
Text + icon rendering
- `Node.text_wrap` gated on `textGrowth: fixed-width` — single-line
by default so font-fallback overshoot doesn't break lines the TS
app shows on one line.
- CJK-aware `wrap_text` (canvas_viewport_overlay.rs) — per-char CJK
breaks, word breaks for Latin, blank-line preservation, explicit
`\n` splits. Takes a weight param.
- `RenderBackend::measure_text_weighted` added with NativeBackend +
WebBackend overrides so wrap measurement matches weighted paint.
- icons.rs + new icons_data.rs sibling cover ~75 lucide variants
for first-party `iconFontName` names from pen-core element-builders
(trending-up/down, compass, refresh-cw, layout-dashboard, users,
package, zap, sliders-horizontal, activity, loader, focus,
chart-line, settings-2, arrow-right, check-circle, alert-triangle,
alert-octagon, sticky-note, bar-chart-2, bold/italic/underline,
shopping-cart/bag, send, message-circle, rocket, menu, credit-card,
x-circle, mail, smartphone, chrome, apple, user, ...). Unknown
names stroke a dot fallback (FALLBACK_ICON_D) instead of a block.
All d-strings copied from lucide-react@0.545.0.
- `Icon::from_name(&str)` resolves kebab-case + common aliases.
- Synthetic bold via PaintStyle::StrokeAndFill for weights ≥600 on
both native and web (single-weight bundles can't serve a real
bold variant).
Chrome polish
- Hover state on file menu / locale picker / shape picker / layer
panel rows / AgentSettings nav + provider cards. Host's
apply_cursor_move updates each per its open state.
- File menu compacted (row 30, header 22, no `…` suffix on actions),
recent file names truncate with a CJK-aware helper.
- `rfd::MessageDialog` on every failed Open / OpenRecent / Save /
SaveAs / ExportImage with bilingual (EN/ZH) title + path + detail.
OpenRecent failures prune the stale entry.
- `figma_import.rs` modal honest-stub (Coming soon copy, brand glyph),
TopBar Folder+Chevron compound + Figma button.
- Settings sidebar nav + provider cards tinted on hover.
- Recent-files panel polished to single-line names with age column.
Tests
- pen_doc_adapter_tests.rs (sibling via #[path]) — 19 cases covering
multi-root canvas offsets, shape size fallbacks, path anchor
absolutize + Bezier extrema, fixed-width wrap, numeric-string
weights, login.op + pencil-demo.op fixture loads.
- canvas_viewport_overlay.rs wrap_tests — 7 cases: ASCII / CJK /
CJK+Latin / explicit-newline / blank-line / weighted advances.
- icons.rs first_party_icon_font_names_all_resolve guards 27+
authored names against placeholder regressions.
File-cap discipline
- pen_doc_adapter.rs split into mod + path-bounds sibling + tests
sibling.
- icons.rs split into mod + icons_data.rs sibling so the catalogue
can grow without busting the cap.
- canvas_viewport_overlay.rs absorbs wrap_text + UniformBackend /
WeightedBackend test stubs.
Sub-modules
- vendor/jian advanced for `resolve_weight` numeric-string parsing.
The 800-line cap section in `crates/CLAUDE.md` listed `widget_host`
and `property_panel` as the two split files; `document.rs` joined
them on 2026-05-11 (commit 512ed9b7). Updated the file-cap bullet
and the Document model heading to point at the directory.
The native runner outgrew the `examples/` slot — it owns DPI tracking,
caret-blink animation timer, panel-resize cursor, the full Cmd+wheel /
PinchGesture / Pixel/LineDelta dispatch table, etc. None of that is a
sample, so it's been promoted to a real crate.
* New crate `crates/openpencil-desktop/` with a single `[[bin]]`
target. Depends on `openpencil-shell-native` (lib) + winit +
skia-safe (gl), gated to macOS / Linux / Windows.
* `examples/inspector_window.rs` removed; equivalent code lives at
`crates/openpencil-desktop/src/main.rs` with the structs renamed
(DesktopApp / paint) and the doc-block rewritten as a runner spec.
* Run command: `cargo run -p openpencil-desktop --release`. Old
command (`--example inspector_window`) is gone.
* Workspace glob `crates/*` already picks up the new crate, no
Cargo.toml workspace edit needed.
* Docs: crates/CLAUDE.md updated with the new crate row and runner
section retitled "Desktop binary". Top-bar layout test renamed +
uses the TOP_BAR_HEIGHT constant so future height tweaks stop
breaking it.
crates/CLAUDE.md gains an 'i18n' row + 'Theme + i18n' section that
covers Document::theme()/t() + the 15 locale tables generated
from TS. Includes the convert-locales.py re-run command for
contributors who change TS strings.
Recent codex stop-hook iterations exposed three sites where input
hit-test hardcoded LAYER_PANEL_WIDTH while paint followed
canvas_region (which collapses to 0 when sidebar is closed):
over_canvas, apply_wheel cursor offset, toolbar hit rect. Document
the invariant so future widgets don't re-introduce the drift.