Commit graph

1474 commits

Author SHA1 Message Date
Fini 050fac23cd fix(agent): separate CLI generation and canvas modes 2026-07-15 21:10:11 +08:00
Fini fff96c8ad5 feat(agent): unify generation UX and CLI providers 2026-07-15 03:48:29 +08:00
Fini 652afc9031 fix(orchestrator): preserve and repair progress rings 2026-07-15 03:29:39 +08:00
Kayshen-X 3a41f2ea7f feat(web): make credential persistence deployment-aware 2026-07-14 23:36:37 +08:00
Fini a02b124aae fix(desktop): align design session identity and lifecycle 2026-07-14 00:30:01 +08:00
Fini 6e2bbd379a refactor(agent): make design repair explicit and bounded 2026-07-14 00:30:00 +08:00
Fini eea8901042 fix(mcp): harden batch design and image semantics 2026-07-14 00:29:59 +08:00
Fini 44a4fe4e24 feat(editor): align canvas layout and sizing behavior 2026-07-14 00:29:58 +08:00
Fini c7e7e45810 fix(desktop): find image slots by geometry, not by name
DeepSeek ships a card's photo area as an UNNAMED rectangle sized
fill_container x fill_container - no keyword, no number - so every
name-and-size heuristic missed it and the whole page came out as grey
boxes. What a slot IS is a question about geometry, so the pass now asks
the real layout: an empty painted box that RESOLVES to picture size, in a
card that has words in it, is a photo slot. The words come from the card's
own subtree (a title nested in an info frame still names the picture), the
nearest ancestor that names the subject wins over them, and a cousin card
is never consulted.
2026-07-14 00:29:57 +08:00
Fini f6fe94bc1a fix(agent): explain the fresh-sandbox rule, and paint rich text at real widths
A batch died on "header is not defined": the model referenced a const from
the PREVIOUS batch's script, but every script runs in a fresh sandbox. The
error now says so and points at the fix (reference the node by its id
string), and the program-DSL contract states it up front.

The transcript's markdown also painted every span at the wrap ESTIMATE
(6.6px per unit) while skia draws real glyph advances, so each span after
the first sat at a slightly wrong x - code chips drifted off their words
and a sentence read as fragments. Paint now advances by the measured width;
wrapping stays estimate-based, since it must stay backend-free.
2026-07-14 00:29:56 +08:00
Fini f369e91390 fix(desktop): recognize the abbreviations weak models name image slots with
MiniMax-M3 shipped a whole travel page as grey boxes: it builds every card
around a rectangle named "img" (or a "ph" rectangle inside an "img"
wrapper), and neither word was in the image-slot keyword table, so the
enrichment pass never saw a single slot. Both are slots now - and because
such a name carries no subject of its own, the query falls through to the
card that names the picture ("Santorini").
2026-07-14 00:29:55 +08:00
Fini f04e0a8fb3 fix(editor): give the cursor a clean white rim that cannot jag
The rim was a polygon STROKE, and the trait's fallback draws a polygon
stroke as one capped line per edge - so every vertex of the densely
sampled arc notched and the rim's width visibly wobbled. Two fixes: the
native backend gains a real single-path, round-joined, antialiased
polygon stroke (every caller benefits), and the cursor's rim is now
FILLED geometry - the silhouette outset by an exact 1.6px, painted white,
then covered by the body. The halo layers clear the rim so the outline
reads crisp against both light and dark designs.
2026-07-14 00:29:54 +08:00
Fini 8c07452298 fix(agent): teach the token vocabulary, and stop broken refs from painting nothing
The search bar came out grey with an invisible filter icon. Two causes,
both mine to fix.

The model painted the bar $--accent believing it was the brand colour -
in the shadcn vocabulary --accent is a quiet NEUTRAL surface and the brand
colour is --primary. The design-system teaching I added told the model to
use tokens but never said what they MEAN; it now carries the semantics
table, with the neutral-vs-brand trap called out.

The filter glyph was painted $--white - a token in no table. An unresolvable
reference silently rendered as an invisible glyph. A broken reference is now
repaired against the surface it sits on: a glyph on a soft tint takes that
tint's colour at full strength, a glyph on a token surface takes that token's
-foreground partner, and a container's unknown fill is dropped rather than
guessed.
2026-07-14 00:29:53 +08:00
Fini f1178c76c1 fix(desktop): one subject, one photo - a rebuilt card keeps its picture
Watching a run frame by frame: a real Bali temple photo landed, then
halfway through it turned into a plain blue sky. The model had rebuilt
the section (fresh node ids), the same query searched again, and the
session-wide dedup - there to stop two cards sharing one picture - now
skipped the very photo that query had already chosen, leaving the junk
below it. A query this session already answered now resolves from a memo:
no network call, and no downgrade. Dedup still guards different subjects
from sharing a photo.
2026-07-14 00:29:52 +08:00
Fini c5feaeec01 fix(desktop): open a multi-page file where its design actually is
A 16-page document whose first page was a blank cover opened onto empty
canvas ("content bbox None") and read as a load failure - the design was
on page 2. With no editor metadata to restore, the file now lands on the
first page that has content; a file that is empty everywhere still opens
on page 1.
2026-07-14 00:29:51 +08:00
Fini 985654e913 feat(editor): render the narration as typed markdown, not a grey wall
The model writes markdown - bold labels, hex codes in backticks, bulleted
build summaries - and the panel flattened all of it: markers stripped,
bullets faked by gluing a dot into the string, code indistinguishable
from prose. The transcript now renders a deliberate subset (**strong**,
`code`, and - bullets) as real typography: labels in bold on the
foreground tone, body in the muted one, code in a tinted chip, bullets
with a hanging indent so wrapped lines align under the text. Unclosed
markers stay literal - a syntax that silently eats characters is worse
than none.
2026-07-14 00:29:50 +08:00
Fini 5df84f0591 fix(agent): an omitted container layout stacks instead of turning into a row
The engine's flex default is a ROW, so a section frame that never declared
layout laid its title beside its card rail and pushed the cards off the
screen (measured: every section in a DS run). An omission is not a request
for a row - a container with flow children and no layout now stacks, and
the program-DSL contract says to declare it.
2026-07-14 00:29:49 +08:00
Fini 03e54a7a8b fix(mcp): an empty input slot is not a competing input
A batch died on "provide only one of script, operations, or nodes_json"
because the model sent the unused slots along as empty placeholders. An
empty value ("", [], {}, null) is not a choice of input: the batch now
runs whichever slot the caller actually filled, while a lone empty
program still reports rather than reading as applied.
2026-07-14 00:29:48 +08:00
Fini 084d5a8840 fix(agent): reunite stray photos with their slots, unstretch control rows
Three defects from one document, all the same failure: the design was
authored right next to where it belongs, and nothing put it back.

- A photo parented to the CARD instead of the empty slot inside its image
  band painted below the price row while the band showed a lone badge on
  white. It now moves into the slot that was waiting for it (only when
  there is exactly one candidate - ambiguity stays the model's call).
- The band takes the height of a photo nested one level down, not just a
  direct child, so a 56px band around a 160x180 photo box stops cropping
  every card to a sliver of sky.
- A painted row of fixed-size controls given fill_container width stretched
  across the header and painted as one capsule around the bell and the
  avatar; a cluster of fixed controls hugs them. Flex nav tabs and unpainted
  rows are untouched.

MoveNode/CopyNode also stop refusing a childless container as a target: a
container that has never held a child still has room for one, which is
exactly what an empty media slot is.
2026-07-14 00:29:47 +08:00
Fini 46a082df2f fix(agent): stop the image repairs from reading an oversized plate as intent
Both of today's image repairs trusted an authored fixed size as the
design's intent. A weak model routinely drops a 400x300 plate into a
390px phone card and CROPS it - which looks right - so the rail repair
widened every card to 400 (one card ate the screen, the rest clipped
away) and the band repair grew every photo band to 300 (a wall of empty
space under each deal). Both now demand that the number be plausible for
the slot it is in: a card is starved only when it is genuinely unusable
and its demand still leaves the rail scrollable, and a band only takes a
photo's height when the result is not taller than the band is wide.
Oversized plates go back to the fixer that shrinks them.
2026-07-14 00:29:46 +08:00
Fini d992449a1c feat(editor): queued shells keep their skeleton, quietly
Suppressing the queue left holes in the middle of the skeleton while the
model was working the top - the skeleton-first effect is wanted, only one
shell may look ACTIVE. The on-deck shell (first empty in fill order)
keeps the radar sweep; every queued shell now paints a still wireframe -
outline plus a whisper of wash, no band - so the whole page reads as
planned-but-unwritten instead of half missing, and nothing downstream
pretends to be under the cursor.
2026-07-14 00:29:45 +08:00
Fini 2649cf7335 style(editor): even out the chip gaps and make the expander read as a select
A chip sat 6px under the paragraph it followed but 14px above the next
one, so it read as belonging to the story below it rather than the one
it came from; both sides now take the same breathing room. The expander
also becomes the reference's stacked up/down caret pair instead of a
navigational chevron-right.
2026-07-14 00:29:44 +08:00
Fini f3b52854bb style(editor): quiet the tool-chip status glyph
The check read as a second focal point next to the verb; the glyph drops
to 11px with a tighter ring and stroke.
2026-07-14 00:29:43 +08:00
Fini 929ea1dbf6 fix(agent): one image per slot, and a photo band that shows the photo
Five destination cards rendered as five identical blue bands. The photos
were fine and all different - the cards were the problem. Each card's
image slot carried TWO images (an absolutely-positioned image-filled
plate AND the real image node) inside a band authored 56px tall against
a 130px photo, so all that survived the clip was each photo's sky.

The duplicate plate is dropped (the image node wins - it is what the
image pipeline fills and re-searches), the band grows to the photo's own
declared height when it would hide more than half of it, and the starved
rail-card repair now reads fixed-width DESCENDANTS and assigns the
demand as a definite width: an out-of-flow photo plate contributes
nothing to a hug, so fit_content re-starved the very cards it was meant
to rescue (126px against a 200px plate). Replaying the measured document
through the repair loop: 4 geometry diagnostics to 0, cards 79px to
200px.
2026-07-14 00:29:42 +08:00
Fini 364859bf49 fix(editor): slim tool chips, clean the cursor halo, fix skeleton fill order
Three defects from one live run. The tool chips were 48px slabs with a
right-flushed status glyph; the reference reads as a 28px row with the
check inline after the verb. The cursor halo was three concentric
STROKES, whose joins banded into a dirty grey ring on the pencil's
corners - it is now a feathered stack of filled expansions (no joins,
no banding) that doubles as the contact shadow. And the work-order gate
was per-container, so a root's trailing bottom-nav shell lit up and took
the cursor while the model was still filling the header nested inside
the content wrapper; fill order is document order, so the deck is now
global to the generating root.
2026-07-14 00:29:41 +08:00
Fini d95b4ef425 refactor(editor): keep ai_chat_transcript under the 800-line cap
Flow paint and the narration markdown normalizer move into the
interleave module they belong to.
2026-07-14 00:29:40 +08:00
Fini 39a3e700b8 feat(editor): interleave design-loop narration with per-call tool chips
Design-loop turns used to render as one collapsed 'N tool calls' row
above a monolithic prose wall. The transcript now splits the narration
at each call's stamped content offset and lays prose paragraphs and
headerless verb-chip panels in chronological order, so the story reads
top-to-bottom the way it happened; chips stay visible (nothing folded
away) and each card toggles its own expand override via the original
call index. Plain chat turns keep the grouped panel.
2026-07-14 00:29:39 +08:00
Fini e2a883ac2f fix(agent): unstarve horizontal rail cards squeezed below their own content
A rail whose fill-width cards resolve skinnier than the fixed-width
content they carry paints clipped image slivers and truncated labels
(measured: a 5-card destination rail sharing one screen width at ~58px
per card around 160px images). The authored fixed content proves the
intended card width, so cards hug it and the rail becomes a clipped
scroller - the clip also keeps the overfull-row flexifier from undoing
the repair next round. Forensic harness gains OP_FORENSIC_FIX=1 to
replay the repair loop on a saved document.
2026-07-14 00:29:38 +08:00
Fini 33a93dedcc feat(ai): design-system bootstrap teaching and end-to-end preset test
New designs are taught to apply a built-in design system first and build
on $--tokens (own variables reuse the shadcn vocabulary); the preset
tool is covered by a runtime test proving the full table lands, the Mode
axis registers and themed tokens flip between Light and Dark.
2026-07-14 00:29:37 +08:00
Fini 7714b66835 fix(agent): design tool-loop HTTP rides the shared throttle and backoff
The loop's own request path posted raw - a provider rate limit killed
the design run instantly with a raw JSON error, bypassing the adaptive
throttle, the retry ladder and the humanized message that the plain
chat path already had.
2026-07-14 00:29:36 +08:00
Fini be8fb65c0e fix(canvas): feather the cursor halo
A single wide 28% band read as a dirty grey ring - the glow is now three
concentric strokes with falling alpha, approximating a soft blur with
the primitives available.
2026-07-14 00:29:35 +08:00
Kayshen-X 20bf8f123b refactor(cli): split app control tests 2026-07-13 21:41:23 +08:00
Danny Ahn f8cfcab7fd fix(cli): validate document before starting headless MCP server
Preflight headless document loading so malformed or binary archives fail with a clear, actionable error before the MCP server starts.

Keep the CLI parser dependency lightweight by disabling op-pen-loader default features and include the updated lockfile plus regression coverage.
2026-07-13 14:29:48 +08:00
Kayshen-X 7ec373c9a7 feat(cli): export pages nodes and live selection 2026-07-12 17:52:36 +08:00
Kayshen-X 1215633e54 feat(mcp): export pages nodes and live selection 2026-07-12 17:48:45 +08:00
Kayshen-X 6fc717546a feat(export): render resolved pages and nodes to bytes 2026-07-12 17:42:52 +08:00
Kayshen-X 4542d62a98 fix(web): handle pencil cursor settings selection 2026-07-12 15:02:05 +08:00
Fini 2ed6f512ee feat(canvas): halo outline and fattened rounded pencil
User-picked O4 treatment: a wide translucent slate halo OUTSIDE the
white rim keeps the cursor silhouette readable on light designs while
the white edge stays crisp on dark ones (contact shadow relaxed to
compensate). The default rounded silhouette is additionally fattened
1.28x across the pencil axis with the tip hotspot fixed.
2026-07-12 13:21:34 +08:00
Fini 1875248224 fix(ui): normalize narration markdown after design-block extraction
Stripping backticks before the extractor destroyed the json fences it
keys on - three transcript hit tests caught it.
2026-07-12 13:11:16 +08:00
Fini c7931fb9a2 feat(mcp): apply_design_system tool and readable narration prose
apply_design_system applies one of the bundled presets (full variable
table + Mode theme axis) as a single undoable batch on both the MCP and
design-agent surfaces. The transcript additionally normalizes streamed
narration markdown - emphasis/backtick markers stripped, glued bold
headings re-broken onto their own lines, dash bullets rendered as
bullets - so the design-loop commentary reads as prose instead of
asterisk soup.
2026-07-12 13:10:15 +08:00
Fini 0c02e957fc feat(ai): bundle four built-in design-system variable presets
Complete shadcn-vocabulary token tables (background/card/primary/muted/
sidebar families plus font and radius tokens) with Light+Dark theme
values, parsed once from a bundled asset and exposed with a
set_variables-ready payload per preset. Groundwork for the
apply-design-system tool and shadcn-aligned variable naming.
2026-07-12 12:59:57 +08:00
Fini 208327e8c3 fix(orchestrator): search-shell normalizer no longer strips real search bars
The nested-shell pass misfired on a search bar whose children are bare
icon/text leaves (the bar IS the input): its authored background was
stripped, the search glyph painted white and the filter glyph re-inked
with a dangling $color-accent that rendered the fallback blue. The pass
now requires container children and, for the legit nested case, moves
the button's own demoted accent hex onto the glyph instead of a symbolic
ref.
2026-07-12 12:59:57 +08:00
Fini e63eb05bba feat(ui): user-selectable pencil cursor styles and Pencil-style narration flow
Settings > System gains a pencil-cursor picker - five silhouettes
(classic, rounded, chubby with a pink eraser, crayon, round-nib marker)
drawn as live swatches, rounded as the default, choice persisted in
~/.openpencil/ui.json and restored on launch. Design-loop narration
additionally streams as visible prose instead of folding into the
collapsed thinking area, and every tool call stamps the content offset
where it landed so the transcript can interleave prose with per-call
verb chips.
2026-07-12 12:32:59 +08:00
Kayshen-X bd7e28dadc fix(desktop): parse login shell paths consistently 2026-07-12 10:26:18 +08:00
Kayshen-X 206bae5b2b test(renderer): satisfy clippy for glyph coverage 2026-07-12 09:15:49 +08:00
Kayshen-X a6bb3b24ac test(editor): cover explicit center alignment controls 2026-07-12 09:14:49 +08:00
Kayshen-X 7a6f11a7f9 fix(web): refresh text rasters for effective scale 2026-07-12 09:13:49 +08:00
Kayshen-X 689c7cfd45 test(export): cover selected-node svg routing 2026-07-12 09:12:49 +08:00
Kayshen-X fb95d6b8b8 fix(editor): preserve scoped instance history states 2026-07-12 09:11:49 +08:00
Kayshen-X cb0842a5c9 feat(panels): reorder multiple fill layers 2026-07-12 09:10:49 +08:00