Commit Graph
958 Commits
Author SHA1 Message Date
Isaac Levine dc6ea04a8f Engine: check automation read and write roots on every path a command reads or writes 2026-10-12 09:58:07 -04:00
justinjohn0306andClaude Opus 5.5 aa65bb8af3 Animations: PowerPoint's Float Down (preset 47) imports as Float, down (#147)
PowerPoint writes Float In / Float Out with the Float Down option as its
own preset, 47 (Descend), not as preset 42 with a direction. DeckCraft
only knew 42, so a Float Down entrance or exit imported as a plain fade,
which is what the issue's video shows: the buildings fade out in place
instead of sinking. Preset 47 now imports as float / floatOut with the
"d" option and is written back as 47.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:31:10 +05:30
Магда М.Г. f2c27cbba1 Text: draw shrink-on-overflow at the scale the file was saved with
PowerPoint draws normAutofit text at its saved fontScale/lnSpcReduction;
we ignored them and only shrank when our own layout overflowed, so saved
decks showed larger text and wider line spacing than PowerPoint, with text
running past placeholders. The layout now starts from the saved scale
(nonsense values ignored) and still shrinks further when the text doesn't
fit. Editing the text resets the saved scale so the new text is fitted
afresh, as PowerPoint refits on edit.
2026-10-12 14:52:34 +02:00
Магда М.Г. 8ac6dc2f47 Render: an unfilled shape's glow follows its line
A glow always filled the shape's whole silhouette, so a frame with no fill
(a rounded-rectangle border with a soft glow, common in corporate decks)
came out as a solid tinted panel over the slide. PowerPoint glows such a
shape along its outline only and leaves the inside clear; a filled shape's
glow is unchanged.

Checked against PowerPoint's own PDF export of 31 real decks (597 slides):
the affected slides' mean pixel difference drops from ~29 to ~4 (of 255).
2026-10-12 14:52:32 +02:00
justinjohn0306andClaude Opus 5.5 b22d88c845 Notes and Presenter View: long notes scroll, presenter controls show (#221)
Long speaker notes grew out of the notes pane and over the slide: the
editor now scrolls inside the pane. In Presenter View the Previous, Next
and End Show buttons drew dark text on the dark window, so they looked
missing; they are now light on a visible fill, and the timer gets Pause /
Resume and Reset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:18:51 +05:30
Marco Arnulfo 0167c0c31f Title bar: on macOS the window buttons sit centred in it (#71)
AppKit draws the traffic lights 16 pt from the top of the window on macOS 27,
but the title bar was 38 pt, so they sat above its centre line. On macOS the
bar is now 32 pt, centred on them: content starts after the lights with the gap
they keep from the top, and the Present button shrinks to 22 pt to keep a
margin. Same approach as wordcraft #255 and gridcraft #323, with the light
metrics measured on macOS 27. Linux, Windows and web are unchanged.
2026-10-12 14:47:41 +02:00
justinjohn0306andClaude Opus 5.5 bd61308cf9 Animations: nine PowerPoint exit effects import as themselves (#147)
Exit effects DeckCraft had no entry for (Peek Out, Strips, Blinds, Box,
Checkerboard, Diamond, Sink Down, Spinner, Contract) imported as a plain
Fade and lost their direction, so a Peek Out or Sink Down didn't move.
Each mirrors an entrance DeckCraft already plays, so it is played as that
entrance in reverse (Sink Down as Rise Up, leaving downward); the import
keeps PowerPoint's direction and saving writes a matching filter or
motion. Float Out now also writes its motion.

The preset ids and directions come from PowerPoint: every effect, as
entrance and exit, in every direction it accepts, saved and read back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:59:59 +05:30
lloydandClaude Opus 5.5 9c64754f20 Read Markdown outlines as Markdown (#211)
File ▸ Open's Outline filter accepts `.md`, but a `.md` was read with the
plain outline format (unindented line = slide, indent = bullet), so a
Markdown outline written the usual way kept `#` in its titles and made
every top-level list item a slide of its own.

`deckcraft_format::markdown_to_slides` reads ATX headings (`#`–`######`,
closing `#`s dropped) as slide titles, list items (`-`, `*`, `+`, `1.`,
`1)`) as bullets nested by their indentation, and other lines under a
heading as body paragraphs. A `.md` without headings is still read as a
plain outline, so existing indented `.md` outlines open as before; `.txt`
is unchanged. Opening a `.md` and dropping one on the window
(`slide.fromOutline {markdown: true}`) use it. Both readers now share the
slide builder.

Tests: the outline from the issue (2 slides, nested bullets), numbered and
`*`/`+` lists with text under a heading, `slide.fromOutline` with
`markdown`, plain `.txt`/`.md` outlines unchanged, and heading / list-item
edge cases (not-a-heading forms, closing hashes, Unicode, long digit runs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:22:47 +02:00
justinjohn0306andClaude Opus 5.5 aaec22c681 PPTX: a lone guide keeps the name its preset uses, adj1 or adj (#214)
The writer named a preset's only guide `adj` whenever the preset has one
adjust value, but bentConnector3, curvedConnector3, frame, mathPlus,
mathMinus and mathMultiply call it `adj1`, and PowerPoint refuses to open
a file that renames it. The names come from PowerPoint itself: every
autoshape and connector created with an adjusted first value and saved;
these six are the only single-guide presets named `adj1`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:30:03 +05:30
justinjohn0306andClaude Opus 5.5 21249df5da PPTX: .potx and .ppsx declare a template and slide show main part (#210)
Saving with a .potx or .ppsx name wrote a byte-identical .pptx: the
main part was always presentationml.presentation.main+xml, because
format_for_path folded all three extensions into "pptx". It now keeps
potx/ppsx, and deckcraft_pptx::export_as picks the template or
slideshow content type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:21:58 +05:30
justinjohn0306andClaude Opus 5.5 f58f4f425f PPTX: legacy OLE objects are saved in PowerPoint's fallback form (#203)
A legacy OLE object finds its preview in the slide's VML drawing through
spid; DeckCraft doesn't write that drawing, and PowerPoint refused to
open a file whose oleObj has a spid but no VML shape. The reader now
takes the preview picture from the VML drawing (so DeckCraft shows it
too), and the writer drops spid and adds that preview as a p:pic, the
form PowerPoint itself writes as the fallback.

Checked in PowerPoint: the issue's 45545_Comment.pptx saved by DeckCraft
opens with its three embedded objects and draws slide 8's table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:17:55 +05:30
justinjohn0306andClaude Opus 5.5 c462859f6f PPTX: SmartArt and OLE objects we can't draw keep their parts on save (#203)
An object DeckCraft doesn't model kept its XML, but the parts that XML
points to (diagram data, layout, style and colours; an OLE embedding)
were not kept, so a save wrote it as an empty rectangle or its preview.

Opaque now carries those parts (relationship id, full relationship type,
the part as a media item) and the writer re-adds them under the slide's
new relationship ids. A part with relationships of its own, or an
external link, still falls back to the preview with the existing
warning. A diagram's data part loses its link to the cached drawing,
which is a slide relationship id a save renumbers; only diagrams whose
drawing is missing or empty reach this path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:09:09 +05:30
justinjohn0306andClaude Opus 5.5 99c1e062aa Home: a Selection Pane button that works with nothing selected (#132)
The Selection Pane was only under Home ▸ Arrange, which is disabled until
a shape is selected, so it couldn't be opened on an empty selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:20:48 +05:30
justinjohn0306andClaude Opus 5.5 2ff4285be6 Pictures: Change Picture replaces the selected picture (#115)
The Picture Format ▸ Change Picture button ran the insert-picture dialog,
so it added a second picture instead of replacing the selected one. A new
app.changePictureDialog picks a file and runs picture.change, which keeps
the picture's position, size and formatting; the canvas right-click menu
offers it on pictures too. On the web, where the file arrives later
through the inbox, the pending change is remembered for that pick.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:12:55 +05:30
justinjohn0306andClaude Opus 5.5 1601fd250a Slide Size: choose Portrait or Landscape (#198)
The Slide Size dialog gets an Orientation choice that swaps width and
height, and design.slideSize takes orientation: portrait|landscape so a
preset can be made portrait from the CLI and MCP too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:39:40 +05:30
Brandon ThomasandClaude Opus 5.5 0ec0cc5e81 Release prep: 0.6.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:22 -04:00
Isaac Levine be019fd615 Slides: Ctrl+A in the slide pane selects every slide (#188)
The UI remembers whether the last press landed in the thumbnail pane (the sorter always counts) and passes scope slides to edit.selectAll in that case.
2026-10-11 22:53:29 -04:00
Isaac Levine 96abeba71b Slides: edit.selectAll takes scope slides to select every slide (#188)
The current slide stays put and any shape or text selection is cleared. A scope other than slides is an error; without a scope the command still selects shapes.
2026-10-11 22:53:29 -04:00
Claudio d5c62b640a Ctrl+C then Ctrl+V pastes objects that carry no text (Fixes #182)
egui-winit drops Ctrl+V when the system clipboard is empty. Copying a
rectangle or picture puts no text on it, so the shortcut never reached
the session and only the context menu could paste objects. Copy and cut
now leave an invisible word joiner on the system clipboard in that case,
and paste maps it back to the session's own clipboard.
2026-10-11 19:54:21 -06:00
MarekSpodBiedry dd404af8b5 PDF: map each glyph to its shaping cluster's text in ToUnicode (#164) 2026-10-11 23:39:32 +02:00
MarekSpodBiedry c1ab5d3c43 CLI: --help prints usage and unknown render flags are rejected (#163) 2026-10-11 23:37:38 +02:00
Matúš LacoandClaude Opus 5.5 c871c5f634 Export: animated GIF of the deck (Export… dialog, CLI, MCP) (#171)
There was no way to turn a presentation into an animated GIF. file.export now writes one: format
`gif` (or a .gif path), one frame per slide drawn by the slide renderer and shown for
secondsPerSlide, encoded with the `image` crate's GIF encoder (already a dependency, no new
crates) with a 256-colour palette per frame: exact when a slide has 256 colours or fewer,
NeuQuant otherwise. Slides are composited onto white.

Options: width (small 640, medium 960 (default), large 1280, extraLarge 1920, or 16-3840 px; the
height follows the slide's aspect ratio, no side over 3840), secondsPerSlide (0.1-600, default 5,
written in GIF hundredths), loop (forever by default, or once: no NETSCAPE2.0 block), slides (a
list of indices, in play order) and includeHidden (hidden slides are skipped by default).
Out-of-range or wrongly typed values, NaN and infinities are BadParams errors, never panics; at
most 500 frames and 200 million pixels in total, checked before anything is rendered.

- Engine: crates/engine/src/gif_export.rs (options, frame list, size, encoder);
  cmd::file::gif_bytes, save_bytes("gif"), format_for_path(".gif").
- UI: the Export… dialog (Command palette; the app has no File menu yet) gets "Animated GIF (.gif)" with Size, Seconds per slide, From/To slide,
  Loop forever and Include hidden slides; an invalid range or value keeps the dialog open with a
  message. The web build downloads the bytes (image/gif).
- CLI: convert IN OUT.gif, run --export OUT.gif --export-params JSON; usage lists the format and
  options.
- MCP: the export tool lists gif and width/secondsPerSlide/loop (slides and includeHidden now
  apply to PDF and GIF).
- Docs: file-format parity GIF row, gaps (video export remains), parity checklist row M -> P
  (P2 breadth 36% -> 37%, overall 79%), control protocol, MCP, README, ROADMAP progress log.

Transitions and build animations are not animated in the GIF (tracked with video export).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:00:33 +02:00
Fabio LussiandClaude Opus 5.5 b14cf1da70 Windows: compile DX12 shaders with FXC
wgpu's default DX12 compiler (Auto) loads whichever dxcompiler.dll the DLL
search path finds. DeckCraft ships none, and without a matching dxil.dll the
unsigned-DXIL warning fails wgpu's indirect-validation pipeline, the device
is lost and the app exits before the first frame. Pin the compiler to FXC,
which is part of Windows; WGPU_DX12_COMPILER still overrides it.

Fixes #153

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:25:06 +02:00
Hevar Hemin f033c76913 Bidi: auto-detect RTL paragraph direction and keep typing caret advancing smoothly
When typing in Kurdish, Arabic or Hebrew in a new document or text shape, default
paragraph direction (props.rtl: None) was resolving to LTR. In an LTR
paragraph, visual reordering pinned newly typed characters and the caret to the
left margin, resetting to the start of the line with every keystroke and jumping on
space characters due to UAX #9 rule L1.

- Add first_strong_is_rtl() in deckcraft-model following UAX #9 rules P2/P3.
- Auto-resolve unassigned paragraph direction to RTL in shape_para layout when the
  paragraph starts with an RTL script, defaulting alignment to Align::Right.
- Auto-promote paragraph to RTL (props.rtl = Some(true), props.align = Some(Align::Right))
  when typing or inserting text starting with an RTL character.
- Add regression tests in deckcraft-model, deckcraft-text, and deckcraft-engine.
2026-10-11 22:28:45 +03:00
Matúš LacoandClaude Opus 5.5 1ce5f686e8 Notes: speaker notes show their formatting and keep it when edited
Notes from a .pptx were read and written with their formatting, but the notes pane and Notes
Page drew them as plain text (Slide::notes_text in a plain TextEdit), and any edit there saved
the text back through slide.notes as TextBody::from_text, dropping every bold run, level,
indent and bullet of the slide's notes.

- ui-egui: a notes editor (notes.rs) shared by the notes pane and Notes Page. It draws bold,
  italic, underline, strikethrough and size from the runs, and each paragraph's margin, hanging
  indent and bullet or number (Wingdings/Symbol bullets mapped as on slides); paragraphs are
  laid out one by one so wrapped lines align with the text, not the bullet. Each edit is applied
  to a formatted draft, so typed text takes the formatting where it lands (Enter in a bulleted
  point starts another), and the draft is saved once the field loses focus or the slide changes.
- engine: slide.notes takes `body` (formatted notes, as slide.inspect's new `notesBody`), and
  `text` now changes only what was retyped (model edit::replace_text); text sharing nothing
  with the old notes is still plain.
- text: map_symbol_bullet is public for the notes editor.

PDF notes pages still print the notes as plain text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:21:33 +02:00
MarekSpodBiedry a3fae2c031 Ribbon: transition and animation tiles follow the dark theme (#159) 2026-10-11 19:13:27 +02:00
Dylan M. Taylor be14248577 ci: refactor workflows to use storytold/craft-common-actions (#152)
* ci: refactor workflows to use reusable artcraft-common-actions

* ci: point reusable workflows to storytold/craft-common-actions

* ci: pin storytold/craft-common-actions to commit 27a978f and pass secrets explicitly

* ci: use @main (unpinned) for craft-common-actions with explanation comment
2026-10-11 11:52:15 -04:00
lloydandClaude Opus 5.5 6a91164a5b Issue #144: automatic value axes scale like Office
The value axis used a generic "nice" scale: about five intervals and no
headroom, so data with a maximum of 42.1 got 0-50 in steps of 10, while
PowerPoint and LibreOffice draw 0-45 in steps of 5.

`auto_scale` now follows the rule Office documents for automatic limits:
the maximum is the first major unit at or above max + (max - min)/20, the
minimum is zero when all values are positive and the smallest is below 5/6
of the largest (otherwise the first major unit at or below min - the same
headroom), and all-negative data mirrors that. The major unit is the
smallest 1, 2 or 5 x 10^n that gives at most 10 intervals. Fixed
`<c:min>`/`<c:max>` limits are kept as they are and no longer get rounded
or padded; values beyond them stop at the plot edge. 100% charts get
0-1 in steps of 0.1.

Fixes #144

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:05:17 +02:00
lloydandClaude Opus 5.5 e999424c79 Issue #106: zooming keeps the point under the pointer
Ctrl/Cmd + wheel and pinch changed the zoom but left the scroll offset
alone, so the slide grew from the canvas corner (from its top-left once it
was larger than the window) and the spot under the pointer slid away. The
pointer position was read but never used. Zoom In/Out, the status-bar
slider and the Zoom dialog had the same effect.

The canvas now remembers last frame's slide rect and scale. When the scale
changes it sets the offset so the slide point under the anchor stays put:
the pointer for wheel and pinch zoom, the middle of the canvas for the
commands. Once the slide fits the window it is centred as before. Wheel
and pinch zoom take effect in the same frame instead of the next.

Fixes #106

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:57:53 +02:00
MarekSpodBiedry 1c8992fdbe Charts: a missing varyColors varies single-series bar colours by point (#143) 2026-10-11 13:54:44 +02:00
MarekSpodBiedry 44ddb579da Charts: a single-series chart without an explicit title shows the series name as its automatic title (#142) 2026-10-11 13:53:47 +02:00
Alexander Alber 5ac5223946 PPTX import: a transition without spd is 0.5 s, not the effect's default (#117)
PowerPoint 2021/2024 write a 0.5 s transition with neither spd nor
p14:dur, since "fast" is the schema default for spd. The reader fell back
to the effect's own default instead (Morph 2 s, Fade 0.7 s, most 1 s).

generated with Claude Code
2026-10-11 13:40:58 +02:00
Alexander Alber 208c163647 Slide show: media set to play in the click sequence starts on Next (#116)
PPTX import already read PowerPoint's media effects (presetClass
mediacall: Play, Pause, Stop) into the timeline, but the show never
carried them out: media started only with autoplay or a click on it.
The show now sets off a step's media effects as the step reaches them,
and also when a click cuts the step short.

A step also no longer lasts as long as the media it starts: the video
plays on by itself and the next click goes on at once. An After Previous
effect behind it still waits for the media to end.

generated with Claude Code
2026-10-11 09:55:36 +02:00
Alexander Alber 3bb284cfdd Text: typing goes on after Tab in a text box (#78)
The engine handled Tab (a tab character, or a list level at the start of
a paragraph), but egui also treated it as focus navigation: with no
widget focused it focused the first ribbon widget, and since the slide
only takes keys while nothing is focused, every key after Tab was lost.
When the slide has the keyboard, the keyboard handler now cancels that
focus move.

This also covers the "can't type after indenting" part of #93.

generated with Claude Code
2026-10-11 09:26:42 +02:00
Alexander Alber 7db770d91d Morph: a picture cropped differently on the next slide morphs its crop (#118)
Two paired pictures that differed only in crop counted as changed
content, so the new one faded in over the old one on the moving box.
Now they stay one picture whose crop morphs: the whole image moves and
scales linearly between where it lies on either slide, and each frame's
crop is what the interpolated box shows of it (morph_crop).

generated with Claude Code
2026-10-11 09:16:27 +02:00
Alexander Alber 04069e1968 Morph: text boxes without a partner fade in place instead of moving (#119)
The last matching pass paired any two shapes of one kind and preset
geometry, so a text box leaving the slide was paired with an unrelated
one arriving and flew across to it. That pass now only pairs shapes
without text: a text box needs its name or its text on the other slide
to morph, otherwise it fades out (or in) where it is, as in PowerPoint.

generated with Claude Code
2026-10-11 09:03:01 +02:00
Brandon ThomasandClaude Opus 5.5 a98df49b21 Release prep: 0.5.0 and Linux riscv64 release build (#102)
- release.yml: new linux-riscv64 job, ported from GridCraft. Cross-compiles on
  ubuntu-24.04 against ports.ubuntu.com riscv64 libs (incl. libasound2-dev:riscv64),
  packages a tar.gz and smoke-tests deckcraft-cli --version under QEMU. The release
  job needs it, so the draft and SHA256SUMS include it.
- packaging/linux/package.sh: CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE/EMULATOR support.
- README Downloads: list the riscv64 tarball.
- Version 0.4.0 -> 0.5.0 (cargo xtask version set).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
v0.5.0
2026-10-10 19:51:07 -04:00
Dylan M. Taylor 35d265891c docs(readme): percent-encode middle dot in platforms badge url 2026-10-10 17:28:25 -04:00
Mikkel Georgsen 736dd52b51 Commands: an invalid shape id is an error instead of the selection
A shape id that was present but not a valid id (negative, not an integer,
larger than u32) was treated as absent, so commands such as text.set and
text.edit silently edited the selected shape instead. id_param now tells three
cases apart: absent (the selection fallback is unchanged), valid, and invalid
(an error naming the command). arrange, edit (select), inspect, review, table,
text and the animation painter pick this up.
2026-10-10 22:05:22 +02:00
Mikkel Georgsen d34b5df775 Slides: going to a slide that does not exist is an error
slide.go clamped an out-of-range index to the last slide and reported
success, so a request for slide 99 of a short deck landed on the last slide
without a word. It now returns an error and stays on the current slide. A slide
id beyond u32 is reported as an unknown slide instead of being read as id 0.
2026-10-10 22:05:13 +02:00
Mikkel Georgsen 20366a8ea9 Export: an unknown format is an error instead of a deckcraft file
file.export with an explicit format outside png, jpeg, jpg, pdf, pptx, potx,
ppsx, deckcraft, outline and txt fell through to the native deckcraft zip, so a
request for "docx" reported success and wrote a deckcraft archive under that
name. It now returns an error and writes nothing. A format inferred from the
file extension behaves as before.
2026-10-10 22:04:58 +02:00
Brandon ThomasandClaude Opus 5.5 f44857624e Progress docs: target-app parity, gaps, alpha gate, readiness by audience (#89)
Progress docs per craftrules standards/progress-docs.md: target-app parity, gaps, roadmap with alpha gate, readiness by audience (full / mainstream / essentials) with Opus 5.5 hours, and feature, format, hardware, UI and localization parity docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:09:21 -04:00
MarekSpodBiedry cebf667579 Paste: Ctrl+V pastes an image-only clipboard as a picture (#53) 2026-10-10 19:09:02 +02:00
MarekSpodBiedry 7afa9ea133 Command palette: one Export… entry that opens the dialog, no path-only duplicate (#79) 2026-10-10 19:03:28 +02:00
MarekSpodBiedry d2acdf07db Comments: browser-created comments and replies get a creation timestamp (#72) 2026-10-10 19:01:56 +02:00
MarekSpodBiedry 6ff5b274ea PPTX: animation rewind survives save and reopen (#77) 2026-10-10 19:00:06 +02:00
MarekSpodBiedry 6be9817693 Export: PNG images (all slides) in the browser downloads every slide (#64) 2026-10-10 18:59:34 +02:00
lloydandClaude Opus 5.5 074e090f05 Fonts: a lookup finds an installed family whichever thread loads it first
Two threads resolving a family nobody had loaded yet raced. The one that
lost the race saw add_font add nothing and fell back to another face, and
a lookup made while a family was being added saw only part of it (Arial
Bold Italic loads first, so "Regular" came back bold italic). The same
text was then laid out in different fonts depending on timing, which made
clicks_in_flipped_shapes_hit_the_text_where_it_is_drawn flaky when the
theme font isn't bundled.

Loading installed fonts now happens under one lock and rechecks once it
holds it, and a family's faces are added in one step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:50:21 +02:00
Alexander Alber a6000f18fd Morph: align an outline morph once per transition, not every frame
Follow-up to #27. render_blend built a GeometryMorph (contours, alignment)
for every morphing shape on every frame, though only t and the current box
change while a transition plays. The Renderer now keeps the aligned morphs,
keyed by both geometries and their own sizes, and drops those a frame did
not use, so the cache ends with the transition.

- render: morph_path::Cache replaces morph_path::geometry; blend sweeps it.
- test: one alignment per transition, same geometry as a fresh one.

generated with Claude Code
2026-10-10 12:51:31 +02:00
d556ff98b5 Fields: date fields show today's date in their own format (#36)
Every datetime field showed the deck-wide fixed date text instead of a date: empty when no slide
had a fixed date, and that text on every slide when one did. A fixed date is plain text in its
slide's placeholder; a field is an automatic date, so it now shows the local date and time in the
format of its type (ECMA-376 datetime1 to datetime13 as PowerPoint lists them for English,
datetime and datetimeFigureOut the short date). Numeric dates follow the run's language
(en-GB 09/10/2026, de-DE 09.10.2026...); formats with names stay English-only, so other languages
keep the field's saved text, as do unknown types and builds without a clock (wasm). The saved
text is kept on save.

- text: datetime module (field types, formats, local clock via chrono, not on wasm);
  Fields::field_in passes the run's language.
- render: RenderOpts::now injects the clock (tests, deterministic output); the PDF text layer
  uses the same fields.
- engine: Header & Footer turns existing date placeholders into fixed text or back into a
  field; new date fields are saved with the date they show (local, not UTC).

Co-authored-by: lloyd <lloyd@stackworx.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 06:25:32 -04:00