Multi-candidate selection picked the candidate with the most nodes, but balanced_arrays also collected nested children arrays. For nested-format output (a single root frame whose children array holds 3 nodes), the inner array could outrank the outer (1 node), returning the children and dropping the real root. Flat _parent-style output (what M2.7 emitted in testing) hid it. balanced_arrays now collects only top-level arrays, skipping past each captured span. Adds a regression test. Found by Codex stop-time review.
Adds a fully-headless pipeline to benchmark built-in-Agent design generation across models, with no GUI or live canvas.
op-smoke: OPENPENCIL_SMOKE_OUT saves the generated PenDocument to a .op; OPENPENCIL_SMOKE_DUMP dumps the raw LLM response for diagnosing weak-model JSON malformations. openpencil-desktop --render-shots FILE OUTDIR SCALE loads a .op, runs the jian layout pass, and exports one cropped PNG per top-level node via export_node_raster (skia CPU raster) for node-only benchmark screenshots.
M2.7 and other weak models emit malformed PenNode JSON the strict parser rejected, losing whole subtasks (Food Delivery restaurants; Dashboard charts-row dropped to 0 nodes and aborted downstream subtasks).
Multi-candidate extraction scans every balanced bracket span and keeps the result with the most nodes instead of grabbing the first open bracket, so reasoning-prose brackets (e.g. step markers) no longer trigger 'expected ident'. Per-element tolerant deserialize keeps valid PenNodes and skips+logs bad elements instead of failing the whole array on one stray node (e.g. a fill of type 'solid' written where a node was expected). Both are strict generalizations; fully-valid JSON is unchanged. Adds tests for both classes; 35 parse tests pass.
The Maximize icon (rightmost of the TopBar right cluster) was painted
but had no hit-test mapping, so clicking it did nothing.
- Add TopBarHit::ToggleFullscreen + a hit-test rect matching its paint
position; add a regression test.
- Native: the host raises a pending_fullscreen_toggle intent (it can't
reach the winit window); app_handler consumes it after the press and
routes through the existing handle_menu_action(ToggleFullscreen) ->
window.set_fullscreen(), the same path the macOS menu item used.
- Web: the WASM host toggles the browser Fullscreen API directly
(document request_fullscreen / exit_fullscreen) — no runner round-trip
and no unconsumed intent.
Right-press menu-open + Delete/Duplicate dispatch kept the multi-selection instead of collapsing to the right-clicked row (web + native). Adds a multi-delete core test.
Extend the op-orchestrator phased design runner (single-screen scaffold, sequential sub-agent execution, dashboard columns, validation-fix apply) and adapt the headless smoke runner to the new InsertSubtree page_id field.
Expand the Rust MCP server toward pen-mcp parity: layered batch_design helpers with TS-shaped structured args, semantic element-alias builders for high-frequency elements, insert/update node-data parsers, and desktop mcp_serve/runtime wiring (incl. a file-path tool) with tests.
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.
Bring the Rust git panel to TS parity across four surfaces:
- Commit rows expand inline into a semantic diff card (ported
diffDocuments / indexNodesById / engineDiff over serde_json::Value)
instead of navigating to a separate diff page; the card shows the
"~modify N nodes" summary plus a per-node patch list. blob_at_commit
reads the {rev} and {rev}^ blobs to compute base-vs-next.
- Overflow menu (...) with the 5 TS actions — switch tracked file, clear
commit author, remote settings, SSH keys, close repository — backed by
GitOverflowView subviews (tracked-file picker + SSH-keys list) and
remote-settings rows (origin URL + ahead/behind + fetch; no HTTPS token
field, matching TS).
- Commit signature form: empty commits are refused, and committing with no
configured author opens an inline name/email form (Enter saves, Escape
cancels) that writes the repo-local identity before retrying the commit.
- Polish: taller commit-detail card, removed panel shadow, lengthened the
ready panel, and unified the commit-textarea caret blink to the app's
500 ms cadence.
New overflow/SSH/author/remote labels resolve via Document::t with an
English fallback for keys not yet in the locale tables.
The TS top-bar shows the active provider glyph inside a rounded bg-muted
box; the Rust chip painted the bare icon, so a spinning/active provider
read as a floating glyph with no chip affordance. Paint a theme.muted
rounded backdrop (pad 4, radius 5) behind the brand icon(s) to match.
The expanded 里程碑详情 card was title+buttons only (60px) and showed no
diff context. Match TS image: grow it to 84px and add the diff status line
between title and buttons. The root commit shows '初始提交 — 没有父提交可对比'
(git.history.diff.initialCommit); later commits leave it blank until the
semantic node-diff summary lands.
- op-git: Commit.is_initial (parent_count == 0)
- op-editor-core: GitCommitSummary.is_initial
- op-host-desktop: plumb is_initial into the snapshot
- op-editor-ui: CARD_H 60→84, button row drops to +52, conditional diff line