Commit graph

8 commits

Author SHA1 Message Date
Fini cc9b7889e2 feat(editor): grow the template library to five deck styles and a card tier
Adds a knowledge-card scene (vertical and square social cards) and
four presentation styles beside the original warm-white deck: a dark
pitch deck, a paper-white lecture deck, a minimal keynote and a
gradient-tech deck with glassmorphism built from opaque composited
stops — the linter drops alpha channels, and a truly translucent card
would exempt its text from contrast checking forever. Every palette
pair ships with measured WCAG ratios baked into the generators, and
low-contrast probe documents prove the audit actually fires on each
template family.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-05 00:47:50 +08:00
Fini e7016a3302 fix(canvas): attach frame-name labels to the frames they name
The label band was built from raw magic numbers — baseline 18 px above the
frame, hit box from 32 px above it — which left the text's visual bottom
15 px clear of the frame. Because the offset is screen-space and does not
scale with zoom, that gap is the same 15 px however far out the canvas is,
so on a zoomed-out board the name reads as floating in empty canvas rather
than belonging to the frame beneath it.

Shrink the band instead of scaling it with zoom. A constant screen-space
offset is the right model for chrome — it is why the label stays legible
at any zoom — and the defect was only that the constant was too big;
scaling by zoom would have kept the same too-tall band at 100% and made
the doc-space clearance a moving target that the generator cannot reserve
space for ahead of time. So the geometry is now derived from the type it
draws rather than picked: a 6 px gap from the text's descender to the
frame edge, plus the 12 px font's 9 px cap height and 3 px descent, plus
3 px of box padding. Baseline lands 9 px above the frame and the hit box
spans 21 px to 3 px above it — down from a 32 px band, with the visible
gap cut from 15 px to 6.

Paint, hit-test and cull all read the same derived constants, so they
cannot drift apart. Three invariants are now asserted rather than assumed:
the hit box never reaches the frame's top edge (otherwise a click just
inside the frame would select the root via its label instead of the node
under the cursor), the glyphs and the taller generating icon stay inside
the box that hit-tests them, and the text stays within ~8 px of its frame.

ROW_LABEL_HEADROOM stays at 240 doc px, re-derived against the narrower
band: the extra margin costs nothing on an infinite canvas and the
zoomed-out end of the range is worth over-serving. Comments there and in
the slide template are updated to the new number.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 23:43:33 +08:00
Fini e5e84ea932 fix(agent): give wrapped canvas rows headroom for frame labels
The canvas paints each top-level frame's name ABOVE the frame at a fixed
screen-space offset that does not scale with zoom (canvas_frame_labels.rs
puts the baseline at sy-18 and the label box top at sy-32). A row gap
equal to the column gap therefore cannot work: at the zoom where a
wrapped canvas is actually viewed, the gap is narrower than the label
itself, so every second-row label sat pressed against the bottom edge of
the row above.

For the 3x2 deck the numbers are exact: 6000 doc px of content framed by
zoom_to_fit into a 944-1424 px canvas region lands at zoom 0.14-0.22,
where a 120 doc px row gap is only 16-26 screen px against a label that
always occupies 32. It overlapped at every realistic window size.

Add 240 doc px of headroom to the VERTICAL step only — 33-52 screen px at
that zoom, clearing the label with room to spare. Columns are untouched:
labels are left-aligned to their own frame and never reach sideways. The
slide-deck template takes the same allowance on top of its own gap, so
both the generated canvas and the shipped template space rows the same
way.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 23:31:36 +08:00
Fini 2b01566aac feat(agent): wrap multi-screen roots into rows on the canvas
A fan-out laid every screen root in one unbroken horizontal strip. Six
1920px deck boards are ~12,000px across, so the result could only be read
by panning, and twenty boards would be unusable.

Wrap by BOARD WIDTH, not by design type or screen name: width is the fact
that makes a strip unreadable, whereas "is this a deck" is a guess that
misfires on every design whose naming we did not anticipate. A row budget
of 8200px lands where each device class wants it — 1920 decks wrap at 4
per row, 1200 desktop screens at 6, and 390 phone screens at 17, i.e. a
mobile fan-out is unchanged for any realistic count.

The row step reads the same resolved height the scaffold builds with, so
a plan asking for `height: 0` ("size me from content") steps down by the
device-class preset instead of overlapping row one.

The slide-deck template follows the same convention, wrapping at 3 per
row so the six-slide deck reads as the 3x2 grid its preview shows.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 21:27:19 +08:00
Fini ad2f0e4a6a fix(panels): scroll the template grid on trackpad, and lay the deck out
Two defects from the same blind spot: verifying a thing one way and
assuming the other way works.

Trackpad scrolling over the Scene Template Center moved the canvas while
the card grid sat still. Wheel deltas route through `apply_wheel_inner`,
trackpad pans through `apply_pan_gesture` — two ladders — and the panel
was wired into only the first. Every mouse-wheel check passed. The
Prompt Center's own host test already covered both paths; this adds its
twin, which fails without the fix.

The shipped deck template opened as one visible slide with five stacked
underneath it. Its generator never set `x`/`y`, so every board defaulted
to the origin. It went unnoticed because the deck was only ever
inspected through per-frame rendering, which draws each board alone —
the stacking exists solely in the assembled document. `knowledge-carousel`
has laid its frames out since it shipped, so this was breaking an
existing convention rather than lacking one.

The catalogue now asserts that every multi-frame template positions each
frame and that no two share a spot, so the next template cannot repeat
it. Fixed in the generator, not the artefact, so regenerating keeps it.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 21:21:13 +08:00
Fini 048d3cf36f feat(editor): add the six-page presentation template
The `slides` scene had a filter chip and no content. This fills it with a
deck that covers the shapes a report actually needs — cover, agenda,
three-point argument, metrics, chart with its takeaway spelled out, and a
closing — so a user replaces text rather than building structure.

Typography and layout follow the `slides` domain contract rather than
being eyeballed: 1920x1080 frames at a fixed size (never fit_content —
projector aspect is a hard constraint), 120px safe margins, body at
26-34, titles at 64, and the metric numbers at 140 so each data slide has
exactly one visual anchor. Frames are generated through `oplib` against
the canonical schema, like the other templates, so they stay reproducible.

The card baker learns to tile multiple renders into a grid. A 16:9 deck
composited as a 1x6 strip is a 10:1 image that shrinks each slide to
~100px inside the 640x400 card — visible as noise, not as slides. Column
count is picked to land nearest the card's own aspect, which puts this
deck at 3x2 and leaves each slide legible.

Also ignores `__pycache__` repo-wide; the generators are run in place and
were leaving bytecode next to their sources.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 11:00:34 +08:00
Fini 20cfbddd4b feat(editor): add the scene template catalogue
First layer of the scene template center: the data model behind "pick a
scene, get a finished document". Templates differ from Prompt Center
entries in what they produce — a document to edit, not text for the
model — so each entry embeds its `.op` and the catalogue refuses to load
when one is missing. A prompt without a preview still works; a template
without a document is a card that does nothing when clicked, which is
worse than no card at all.

The three step-0 documents move from `templates/` into the crate's
assets: they are shipped content now, and one authoritative copy beats a
source and an embedded duplicate that drift. `templates/step0` keeps the
generators and their full-resolution renders — the workshop, not the
shelf — and `previews.sh` follows the documents to their new home.

Card previews are baked to the prompt-center convention (640x400 JPEG,
13-15 KB) by a new generator. The panel paints every card through one
fixed rect with `ImageDrawMode::Fill`, so a 2160x2880 page render or a
5880x1440 overview would be cropped to an unreadable strip; the baker
fits the whole design inside the card and pads with the document's own
backdrop, sampled from the render's corners.

The TOML value parsers move to a shared `catalog_toml` rather than being
copied for the second catalogue. Each catalogue keeps its own error type
so the messages still name the right asset file; only the parsing is
shared. Prompt Center behaviour is unchanged (its 14 tests still pass).

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 11:00:34 +08:00
Fini d64a4ffe9b feat(editor): add step-0 scene templates for the mass-market track
Three finished scene documents — screenshot tutorial, before/after
comparison, and knowledge carousel — plus their preview renders and the
generator scripts that produced them.

These have lived outside version control since 2026-07-27 while the
mass-market direction was still provisional. They are about to become
the seed content for the scene-template entry point, so the documents
and the scripts that regenerate them need to travel together: a template
whose generator is lost can only be edited by hand from then on.

The `_generators` scripts build the documents through the canonical
schema rather than exporting from a live editor, which keeps them
reproducible and free of editor-session state.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-02 11:00:34 +08:00