Commit graph

2034 commits

Author SHA1 Message Date
Fini 2e21d94ff6 feat(ai): make script-gen the only subagent output protocol
Retire the flat-JSONL retry rung. Every subagent rung now emits a JS
program (script-gen); reduced_complexity and minimal_skills only
narrow the loaded skill set — they no longer switch the output format
to positional _parent JSONL, whose omittable parent field collapsed a
whole tree into flat siblings when a model skipped it. script-gen's
I(parent, node) makes parenting a positional argument that cannot be
dropped, and the reasoning-harvest fix made it robust across models.
parse_nodes stays for the modify/chat paths that still consume flat
node JSON; the jsonl-format generation skills are removed.
2026-07-04 17:31:22 +08:00
Fini eaa979d01c fix(renderer): paint the children of rectangle containers
A rectangle is a container in the canonical schema — it carries
clipContent like Frame/Group and models nest content inside one (an
image-area rectangle wrapping a photo, a card body, a badge holder).
The painter's NodeKind::Rect branch drew the rectangle's own fill and
returned without recursing, so every child of a rectangle vanished
behind that fill. Measured: an AI-generated travel page whose seven
destination photos each sat inside an image-area rectangle rendered
as blank cards despite the photos being fetched and embedded. Recurse
into the children (honouring clipContent) after the rectangle's own
paint, mirroring the Frame branch.
2026-07-04 17:25:58 +08:00
Kayshen-X 9471b7b1ab fix(figma): skip swapped instances in derived pre-seeding
A component-swapped instance (overriddenSymbolID present) carries
derivedSymbolData for the swapped-in component, not its base symbolID.
Pooling or geometry-seeding it under the base component's cache pinned
wrong pk→node mappings that poisoned genuine base-component instances
reusing that cache. Skip swapped instances in both seeding passes.
2026-07-04 16:48:14 +08:00
Kayshen-X 7885e25ab4 fix(figma): drop stale pre-swap derived so swapped icons render fully
A nested instance swapped via overriddenSymbolID keeps the pre-swap
component's derivedSymbolData alongside the swapped-in component's. When
the two frames are the same size the fingerprint can't tell them apart
and the stale (earlier-listed) cluster hijacks the mapping, sizing the
swapped frame wrong and clipping its icon. Cluster the derived entries
by localID and keep only the one that geometrically fits the swapped
subtree. Adds a near-exact geometric-match bonus so a near-perfect size
match outweighs the walk-order prior a stale sibling would otherwise
win on. Splits instance.rs (swap_filter.rs) and fingerprint_tests.rs
(foreign_tests.rs) to honor the 800-line cap.

Renders the Test.fig Sales-card pie icon and the other swapped card
icons at their correct size instead of clipped.
2026-07-04 16:37:48 +08:00
Kayshen-X eacc2ccbf0 fix(figma): evidence-based instance override routing + strong-fill fingerprint
Replace the walk-order virtual-GUID guessing in Strategy 2 with an
evidence-based fingerprint (size / transform / text-class / fill hints)
plus cross-instance pooled + geometry pre-seeding, so nested instance
overrides land on the right nodes. Adds foreign-session subtree
anchoring with a uniform-family fallback, nested-derived field merge,
strong fill-hint routing (image / rare-solid) with conflict-vs-
inapplicable rejection handling, and a single-axis transform-drift
score tier. Splits instance.rs walk/apply helpers into apply.rs and the
test module into fingerprint_tests.rs to honor the 800-line cap.

Renders Test.fig order-row thumbnails, breadcrumb, sidebar logo,
summary-card filters, and status chips to match the Figma source.
2026-07-04 16:37:47 +08:00
Fini 05e3326336 chore(ai): drop the now-unused loop opt-in flag parser
Flipping the loop to default-on left parse_loop_flag (the truthy
parser) with only test callers; parse_loop_off carries the opt-out.
2026-07-04 16:26:14 +08:00
Fini 9de2249164 fix(ai): stop a stray selection hijacking a new-design prompt
A section-heavy new-design prompt ('Design a … page. Include a search
section …') trips is_section_add_request, so requests_new_whole_screen
returns false; the selection-modify bias then routed the whole prompt
into run_modify_turn, where M3's flat-JSONL output was renest-dropped
to nothing ('Could not parse design nodes'). Add a section-add-blind
creation-signal veto to both routing gates so a new-design request
reaches the design pipeline regardless of an active selection.
2026-07-04 16:26:13 +08:00
Fini 0dd7277b78 fix(mcp): harvest the script past reasoning and prose preamble
The script runner only stripped a code fence anchored at position
zero, and never stripped <think> reasoning at all. A reasoning model
that keeps its thinking (MiniMax-M3 rides Adaptive) prefixes the
program with a <think> block full of draft JS plus a prose lead-in;
that went to QuickJS verbatim as source, threw a syntax error, and
dropped the model onto the fragile flat-JSONL retry rung where
omitted _parent fields collapse the whole tree into flat siblings
under the root (measured: a full travel page, 44 nodes, all piled at
origin). Strip reasoning first, then extract the fenced block from
anywhere in the response. Models with thinking disabled (GLM) were
unaffected, which is why this read as GLM-only handling.
2026-07-04 16:04:47 +08:00
Fini aa6857365a fix(ai): disable reasoning on direct-modify turns
Reasoning models burn the whole output budget inside think blocks and
emit zero design nodes on a modify turn (measured on MiniMax-M3: the
turn died in analysis prose). Same policy the design subtasks already
use; the HTTP layer maps it per provider.
2026-07-04 15:42:50 +08:00
Fini 29a5801621 feat(ai): make the design agent loop the default generation path
The A/B measured the loop ahead of the single-shot orchestrator on
audit cleanliness (11 vs 33 issues over 10 prompt pairs) at
comparable wall time, with the artboard seed guard closing its one
failure mode. OPENPENCIL_DESIGN_AGENT_LOOP=0|false|off opts back into
the orchestrator; CLI providers keep their existing path.
2026-07-04 15:42:49 +08:00
Fini 3994d0aee2 test(ai): restore the web-app negative regression for artboard seeding 2026-07-04 15:17:20 +08:00
Fini c110ca2387 test(ai): align the stale mobile-seeding assertion with the web-app guard
A delayed write from a dead editing session re-added an assertion
encoding the pre-guard behavior (dashboard 'web app' treated as a
mobile ask); the negative case is speced by the sibling test.
2026-07-04 15:14:09 +08:00
Fini abbabd7662 fix(ai): restore the web-app guard in mobile artboard detection
The guard landed in the previous commit's tests but its function body
got clobbered by a stale editing session between validation and
commit; a dashboard 'web app' seeded 390x844 again.
2026-07-04 15:03:37 +08:00
Fini 3b15add38c feat(ai): seed a numeric artboard when the first loop batch skips it
Weak models sometimes ignore the seed-the-artboard-first instruction
and leave the root frame sizeless, collapsing the whole screen into a
thin strip (measured on a travel-app A/B cell). The executor now
seeds missing root axes deterministically on the first applied batch
— 390x844 for mobile asks, 1440x900 otherwise (a dashboard 'web app'
is not a mobile ask) — tells the model about the contract in the tool
result, and leaves authored numeric axes alone.
2026-07-04 14:34:55 +08:00
Fini a8201226d9 fix(orchestrator): stop ring labels wrapping inside their forced width
The 0.56em/char estimate under-measures wider platform font stacks;
on Linux the centered label wrapped inside its authored width and
sank 6px below the ring center. Give the forced width single-line
headroom and let the sentinel tolerate single-line metric variance.
2026-07-04 14:27:36 +08:00
Kayshen-X 04e3cbcc69 chore(jian): update submodule to rebased main 2026-07-04 13:31:12 +08:00
Kayshen-X c7fc20be90 fix(canvas): seed bound widgets on screen switch so persisted values render on remount 2026-07-04 13:23:31 +08:00
Kayshen-X 9a11464a40 test(canvas): lock screen-marker round-trip + cross-screen bind persistence + exit reset 2026-07-04 13:23:30 +08:00
Kayshen-X 9d0cbf7964 fix(canvas): gate preview centering to app mode + resilient screen-switch rebuild 2026-07-04 13:23:29 +08:00
Kayshen-X 686536a15e feat(openpencil): add code-to-design conversion flow 2026-07-04 13:23:28 +08:00
Fini 3916caee1f test(web): re-anchor the web host's footer probes too
Same two shifts as the native mirror: the palette slot removal moved
the right cluster 28px, and the input-width wrap basis fix lifts the
band when the probe text wraps.
2026-07-04 13:20:51 +08:00
Fini 1a5f411805 fix(web): handle the clear-selection chat hit in the web host
The new AIChatHit variant broke the wasm-only exhaustive match that
the default workspace build never compiles.
2026-07-04 12:56:54 +08:00
Fini c918dc9be7 test(editor): re-anchor footer hover probe after layout fixes
The palette slot removal shifted the right cluster 28px right, and
the input-height width basis fix (measuring wrap against the real
inner width) made the old probe text wrap and lift the footer band;
probe with a single-line text at the new offsets.
2026-07-04 12:30:27 +08:00
Fini a3540ac2e6 feat(editor): selection targets the chat turn and shows in the panel
An active canvas selection now biases intent routing to modify (the
selection IS the target — only an explicit new-whole-screen request
or a plain chat question escapes it), restoring select-then-ask
editing. The panel paints a selected-count chip above the input with
a clear affordance, so the armed state is visible. Chat-question CJK
keywords keep questions about a selection conversational, and the
footer hover math reserves the chip row like paint does.
2026-07-04 12:30:26 +08:00
Fini e891552970 feat(orchestrator): restack landing navbars misdelivered into sidebars
A sidebar subtask can come back as a landing-page archetype — a
horizontal nav row (brand + links + actions) plus a display-size hero
headline — inside a 260px rail, where everything overlaps and clips.
When geometry proves the squeeze (fixed rail <= 320, link row wider
than its slot), restack the row vertically, clamp display text to
rail scale, relax oversized rail padding, and emit a diagnostic for
the agent loop. Planning and dashboard skills state the rule
outright: a sidebar is a vertical rail, never a navbar or hero.
2026-07-04 03:30:14 +08:00
Fini 20cdec460f fix(editor): give the settings connect button a real inset
56x28 left the centered label ~3px from each edge; 76x30 matches the
reference chrome. Long install-guidance lines may now ellipsize at
the tail — the actionable prefix still paints.
2026-07-04 03:25:05 +08:00
Fini 6e41b68474 chore(editor): remove the inert palette button from the chat footer
The affordance has no feature behind it yet; ship it when it does.
2026-07-04 03:21:13 +08:00
Fini a963502d1a fix(editor): inherit the model registry in fresh chat tabs
The + button pushed a defaulted ChatState whose empty discovered and
available model lists painted 'No models connected' until the next
provider probe. Model discovery is app-level state — carry it (and
the selected index) into new tabs and the last-tab in-place reset.
2026-07-04 03:17:40 +08:00
Fini 4f0776c775 fix(editor): anchor the edited marker on measured name width
The 9px/char estimate overshoots narrow Latin glyphs, leaving a dead
gap between the file name and the marker.
2026-07-04 03:13:08 +08:00
Fini 204febe9aa fix(editor): unpark the agent cursor from the page root
The section-granularity dwell comparison was reversed: the page root
reveals first at scaffold time, so 'ancestor earlier than child' held
for every descendant and all waypoints were suppressed — the cursor
sat on the root center for the whole run. Suppress a child only when
it pops in within the dwell window AFTER its nearest revealed
ancestor, and propagate that nearest ancestor instead of the
min-start (which pinned every window to the root).
2026-07-04 02:45:04 +08:00
Fini 2de3356a07 fix(ai): drop retired element tool from the reveal gate
The loop reveal gate still matched the removed emit_elements constant
after rebasing onto its retirement; batch_design is the only
canvas-writing loop tool left.
2026-07-04 02:19:29 +08:00
Fini 550712b05f feat(editor): move the agent cursor at section granularity
Every revealed node used to become a cursor waypoint, so dense batch
reveals sent the pointer darting between leaves. Children of a
just-revealed ancestor no longer schedule waypoints (the cursor parks
on the section while its contents pop in), tiny standalone leaves are
not chased, and stops closer than a dwell window collapse to the last
placement so each move stays readable.
2026-07-04 02:17:38 +08:00
Fini ea4912663e chore(editor): refresh quick examples with a travel booking prompt
Replace the sports dashboard example with a detailed travel-app
explore page brief and move it to the first slot.
2026-07-04 02:17:37 +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
Fini 53ad362f8b fix(desktop): keep discovered models on transient probe failure
Clearing the provider's models outside the success guard emptied the
chat model picker whenever a single refresh probe hiccuped.
2026-07-04 02:16:39 +08:00
Fini 22737dbcad fix(desktop): forward terminal proxy env to CLI agents
CLI subprocesses fell back to the macOS system proxy, whose
stream-reconnect path some CLIs reject mid-turn; pass through the
HTTP(S)/NO_PROXY vars users actually validate in their shell and keep
socks-scheme all_proxy out.
2026-07-04 02:16:38 +08:00
Fini a384a19fae feat(ai): stream per-batch reveals and progress in the design loop
Loop-applied batches now register node reveals against the live run
epoch (no more whole-sections popping in silently), the checklist
surfaces tool-call progress per turn, per-batch layout feedback keeps
flowing, and the design loop gets a bounded turn budget sized for
section-by-section batching plus repair turns.
2026-07-04 02:16:37 +08:00
Fini 974a1d1f2e feat(mcp): rebind redrafted names and auto-bind bindless lines
A re-bound binding now updates in place instead of duplicating its
result row, and bindless container lines get monotonic auto bindings
so later lines can still parent onto them.
2026-07-04 02:16:36 +08:00
Fini b77a5d35a3 feat(ai): tighten domain anatomy and agent batching guidance
Dashboard/mobile constants measured from strong references (row
rhythm, KPI card anatomy, chip specs, badge content rules); the agent
phase seeds the artboard full-size in its first batch, keeps batches
small, fixes reported layout issues before continuing, and moves
misplaced sections instead of rebuilding them; planning appends the
bottom-nav subtask for app home screens.
2026-07-04 02:16:35 +08:00
Fini 4c021ac32c fix(orchestrator): fail fast on deterministic CLI transport errors
A transport-config rejection repeats identically on every attempt;
burning the retry ladder and salvage pass on it costs minutes and
ends the same way.
2026-07-04 02:15:59 +08:00
Fini eb7724d48f feat(orchestrator): drain the reveal sweep before finalize restructures
finalize's ReplaceSubtree allocates ids the reveal overlay never saw,
so a mid-animation section snapped in at once and the agent cursor
lost its target. Wait (worker thread, abort-aware, capped) for the
scheduled sweep to finish first.
2026-07-04 02:15:58 +08:00
Fini a9845e6239 fix(orchestrator): append a bottom-nav subtask to app home plans
Multi-section mobile home/feed/dashboard plans that omit the tab bar
ship looking amputated; single-task flows stay untouched.
2026-07-04 02:15:57 +08:00
Fini dbf6bc0d1d fix(orchestrator): remap text tokens painted on containers
A container filled with a $color-text-* token is a slot-category
error (a search pill filled text-primary rendered as a white capsule
on a dark theme); remap to the surface token family.
2026-07-04 02:15:56 +08:00
Fini aec565f3b3 feat(orchestrator): grow the geometry repair pass family
New finalize passes measured on real generations: radial stacks
(donut/ring segments authored as flow siblings) recentre under a
layout:none parent; empty decorated stub frames are removed unless
positioned (a pinned overlay is intent, not an abandoned shell);
corner badges and notification dots adopt into their image/icon
anchors; pill-chip rows clip instead of flexifying; card overflow
gains deep-descendant clip; row-gap, table-scale and overfull-row
repairs get sturdier floors so they cannot compound a design into
mush. Diagnostics for starved fills, over-packed rows and vertical
overlap feed the agent feedback loop.
2026-07-04 02:15:55 +08:00
Fini fd7fe7509d fix(renderer): carry authored corner radius on text inputs
text_input payloads dropped cornerRadius (text_area already applied
it), so a rounded search field painted square. Adds loader-level
regression tests for input styling and path flex sizing.
2026-07-04 02:15:54 +08:00
Fini ba497758cd fix(renderer): honor explicitly authored absolute layouts
An authored layout:none whose children carry real x/y is deliberate
overlay placement; the horizontal-layout inference pass flattened it
(a pinned badge dot snapped into flow). Legacy frames with absent
layout and stale 0,0 children still infer.
2026-07-04 02:15:53 +08:00
Fini acfcb9f864 feat(renderer): resolve measure and paint text through one font chain
Bump jian for the shared font resolver, path/line flex sizing, and
input-chrome measurement; route the native backend's draw_text and
measure_text_weighted through the resolver so fit_content boxes match
what actually paints (synthetic bold and multi-line heights included).
2026-07-04 02:15:52 +08:00
Fini 79b6d3bb33 fix(editor): paint new-chat tooltip above transcript bubbles
Hovering + painted the tooltip inside the header pass, so transcript
bubbles drawn later covered it on busy sessions.
2026-07-04 02:14:54 +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 845ff8fd33 test(loader): mark the mutable-Desktop pencil-demo regression opt-in
The fixture is a live file on the developer Desktop (v2.8, loaded
best-effort) and currently trips a REAL main-axis overflow (tab section
bottom 2456 > root bottom 2418) in the jian growth path being reworked
around jian 57068a6 — permanently red on this machine through no fault
of the loader. Keep the net, run it on demand with --ignored.
2026-07-03 22:36:29 +08:00