Commit graph

4 commits

Author SHA1 Message Date
Daniel
89f1aca8ed fix: a deck reply that closed a slide early is repaired, and a modification keeps the theme
Some checks failed
Forgejo Docker Build / Build Docker image (push) Has been skipped
Forgejo Docker Build / End-to-end (browser) (push) Has been skipped
Forgejo Docker Build / Root app tests (push) Failing after 47s
Three generations in a row fell back to plain slides for the same reason,
visible once a failed reply was kept whole: the model wrote "rows":[[…]]}
and then ,"notes":"…"} — the slide closed before its notes, which is not
JSON. On parse failure the one premature brace is removed, bounded to the
schema's slide keys; a brace that legitimately closes an inner object is
left alone. Both test generations now come back as designed decks.

Modify used to hand the model's new deck back without its theme; the look
is the author's, so it is carried over, and the column agrees.

Failed deck replies are kept under data/logs/deck-failures for the next
time "the reply was not a deck" needs reading rather than guessing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-13 13:43:21 +02:00
Daniel
0163d40811 fix: every presentation takes a theme, not only the ones the model designed
Some checks failed
Forgejo Docker Build / Root app tests (push) Successful in 47s
Forgejo Docker Build / Build Docker image (push) Successful in 6s
Forgejo Docker Build / End-to-end (browser) (push) Failing after 8s
"Change template" answered "no slide layout" for 28 of the 41 presentations
in production: the theme lived only inside the deck JSON, and a presentation
whose deck reply failed twice and fell back to markdown slides had nowhere
to keep one. The theme is a column now, written at generation and by the
picker, and the markdown slide builder carries it to the same renderer field
a designed deck uses. A deck's own theme field is kept in step. Articles are
the only thing refused — they have no slides.

A deck reply that fails to parse is logged with its first 240 characters, so
the next "the reply was not a deck" can be read rather than guessed at.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-13 13:32:24 +02:00
Daniel
5f738fbe30 feat: a theme is shown by a sample deck you download, not a picture
A picture of one slide answers a narrower question than the one a theme
picker is asked. What a person wants to know is what a deck will look
like in this theme — all of it, at the size it will be shown, with the
fonts substituted the way they will be. One rendered slide showed one
layout, in content someone then read instead of looking at.

So: a sample deck per theme. Every layout the renderer can draw — title,
bullets with a sub-point, both two-column forms, table, callout, figure,
full-slide figure, section divider, a custom slide with shapes, an arrow
and a chart, and a references slide — with filler text throughout.
Download it, open it, see the theme.

The text is deliberately meaningless. Clinical content in a specimen
invites you to read it, and then you are judging the teaching rather
than the type; that is what the old croup slide got wrong.

No engine. The previous preview needed a Gotenberg round trip and a
pdftoppm to produce a PNG, cached on disk because of what it cost, and
could fail in ways a missing picture cannot explain. python-pptx builds
the file in ~330ms and PowerPoint draws it. The link is a plain anchor,
so it is there whether or not anything on the server is well.

The figure placeholder rides the same path a generated figure does —
attachFigures downgrades a figure slide with no picture to bullets and
an image slide to a section, so without it the sample would silently
stop showing those two layouts.

Verified: all five themes build, 11 slides, the chart is a real chart
and the placeholder embeds as real media.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 22:24:05 +02:00
Daniel
59226f2109 feat: deck themes — a palette you pick, previewed by the renderer itself
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 46s
Forgejo Docker Build / Root app tests (push) Successful in 46s
Forgejo Android APK / Build signed APK (push) Successful in 2m13s
Forgejo Docker Build / Build Docker image (push) Successful in 9s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
A deck had exactly one look. The slide vocabulary is structural — bullets,
compare, table, callout, figure — and none of it carries a colour, so "make it
yellow" had nowhere to land but the image prompts, and produced yellow figures
on a blue deck.

A theme is a palette and a typeface in assets/deck-themes.json. render_pptx.py
rebinds INK, MUTED, ACCENT, RULE and PAPER from it in one place, so every slide
builder follows without a line changing in any of them — five themes restyle ten
slide types for free. An unusable theme leaves the default standing, because a
deck in the wrong colours beats a deck that will not render.

The theme rides on the deck, which is already the renderer's spec, so nothing
has to thread it through. It is validated against the same catalogue the
renderer reads: an id the renderer would ignore is never stored, so a deck
cannot claim a look it does not have.

PUT /my-resources/:id/theme re-skins a stored deck — a column write, no model
call, nothing that can reword a slide — and the next download is in the new
colours. Offered in the library only on rows that have a deck; flat markdown has
no palette.

Previews are rendered by the renderer, one representative compare slide per
theme, cached because each costs a pptx render, a Gotenberg round trip and a
rasterise. Drawn rather than mocked up: a hand-made swatch drifts the moment a
palette or a layout changes, and a preview that is not true is worse than none.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 19:40:19 +02:00