Found while testing crates/engine/src/cmd/table.rs:234-238 (at), via crates/engine/src/cmd/mod.rs:318 (usize_param)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PowerPoint for Mac lays out a table's text in the master's other-text
style (p:otherStyle), while shapes and text boxes take the presentation's
default text style (p:defaultTextStyle). A synthetic deck with an
otherStyle of 14 pt and a defaultTextStyle of 26 pt, exported as PDF,
draws a table cell at 14 pt and a shape's and a text box's text at 26 pt.
DeckCraft laid cells out through a stand-in shape that wasn't a table, so
they took the default text style: a deck whose two styles differ (18 and
20 pt) drew its table text 2 pt too large and wrapped its cells.
master_list_style sends text in a non-placeholder table to otherStyle,
and Shape::table_cell builds the stand-in every cell layout uses (drawing,
row heights, text placement, the caret and selection in a cell) as a
table. The table shape itself resolves the same way, so the formatting
state of a selected table reports the size its cells draw at.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paragraph properties now go only onto paragraphs the selection covers
whole, so partly covered end paragraphs and the paragraph after a
triple-click keep their bullets. Double and triple clicks apply the
painter only when they changed the text selection.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reuse Slides… used to insert every slide of the picked presentation. It now opens a list
of that presentation's slides (number and title, all ticked), with Select All / Select None
and a Keep source formatting option, and inserts only the ticked ones after the current
slide. New query slide.reuseList {path | data} gives the list; slide.reuse already took
`slides`.
Co-Authored-By: Claude <noreply@anthropic.com>
The painter only applied when a shape was clicked. It now also applies
when a text range is selected by drag, double-click or triple-click.
Paragraph formatting (bullets, alignment) is copied only onto
paragraphs the selection covers whole.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/slide.rs:234 (delete); same pattern in index_of :182, move_slide :269, duplicate :193 and section_add :534
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/find.rs:107-121 (para_chars / para_matches; splice at :124)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/edit.rs:573-585 (delete)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A shape added after an Undo can get an undone copy's id; duplicating it then
used the move measured from the old original.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design › Browse for Themes… with a .pptx or .potx now does what PowerPoint
does: the file's first slide master and its layouts replace the master the
current slide uses (a preserved master stays beside it), stretched to this
deck's slide size. Each slide on the old master moves to the new layout with
the same name, else the same kind, and keeps its content as Layout does. One
undo step. A .thmx still brings its theme only.
Reuse Slides' master copy (media, fresh master, layout and shape ids) moves
into reuse::import_master so both commands share it.
Co-Authored-By: Claude Code <noreply@anthropic.com>
# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# interactive rebase in progress; onto 22db926
# Last command done (1 command done):
# pick 8992623#807: Browse for Themes brings a template's slide master and layouts
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
# modified: ROADMAP.md
# modified: crates/engine/src/cmd/design.rs
# modified: crates/engine/src/cmd/reuse.rs
# modified: docs/file-format-parity.md
#
Found while testing crates/engine/src/cmd/richclip.rs:257-283 (decode_text)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Duplicating a shape, moving the copy and duplicating that copy again places
the next copy the same distance further on. Anything else, an undone copy or
another slide gets the usual offset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An overflow to infinity (move x=1e308 dx=1e308) now lands on the limit
instead of resetting to 0, and a rotated shape re-centred after a capped
resize stays within POS_LIMIT (CodeRabbit review).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/shape.rs:215 (set_bounds; shape.resize at :~183 and shape.move at :~171 have the same problem)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/shape.rs:400-412 (flip; used by arrange.flipHorizontal / arrange.flipVertical)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The importer skipped a slide whose part was missing or unreadable and only
logged it, so `info` showed one slide fewer with exit code 0 and a save wrote
the shorter deck over the original.
`deckcraft_pptx::import_checked` returns the parts it left out (`import` is
unchanged). The engine carries them as `Damage::slides` next to the damaged
media of a `.deckcraft` file: `file.open` and `file.openBytes` return
`lostSlides`, the app shows a message and opens a repaired copy (so Save asks
for a new name), and `deckcraft-cli` warns on stderr.
Co-Authored-By: Claude Code <noreply@anthropic.com>
# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# interactive rebase in progress; onto 1dc6972
# Last command done (1 command done):
# pick d2e995c PPTX: a slide that can't be read is reported, not dropped in silence (#203)
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
# modified: Cargo.lock
# modified: ROADMAP.md
# modified: apps/deckcraft-cli/Cargo.toml
# modified: apps/deckcraft-cli/src/main.rs
# new file: apps/deckcraft-cli/tests/damaged.rs
# modified: crates/engine/src/cmd/file.rs
# modified: crates/engine/src/tests.rs
# modified: crates/pptx/src/lib.rs
# modified: crates/pptx/src/read/mod.rs
# modified: crates/pptx/tests/malformed.rs
# modified: docs/file-format-parity.md
#
# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# interactive rebase in progress; onto 22db926
# Last command done (1 command done):
# pick 31b0d33 PPTX: a slide that can't be read is reported, not dropped in silence (#203)
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
# modified: Cargo.lock
# modified: ROADMAP.md
# modified: apps/deckcraft-cli/Cargo.toml
# modified: apps/deckcraft-cli/src/main.rs
# new file: apps/deckcraft-cli/tests/damaged.rs
# modified: crates/engine/src/cmd/file.rs
# modified: crates/engine/src/tests.rs
# modified: crates/pptx/src/lib.rs
# modified: crates/pptx/src/read/mod.rs
# modified: crates/pptx/tests/malformed.rs
# modified: docs/file-format-parity.md
#
shape.editPoints was a noundo command, so the conversion from a preset to custom geometry was
never recorded and Ctrl+Z took back the step before it. It is undoable now (only an actual change
records a step), and Edit Points ends when Undo turns the shape back into a preset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/edit.rs:cut (about line 337) and delete (about line 523)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
`HeaderFooter::date_format` is documented as the `datetime1`…`datetime13`
format for automatic dates, and nothing read it: grep found one hit, the field's
own declaration. The Date placeholder was hardcoded to `datetime1`, so the
footer date was always the short date, and a `dateFormat` parameter was accepted
and silently dropped — the command returned Ok, so a caller could not tell.
Everything else was already there: `datetime::field_text` renders all thirteen
types and Insert ▸ Date & Time already offers them. Missing was the wiring from
a choice to the placeholder.
`design.headerFooter` now takes `dateFormat`, refuses anything outside the
thirteen rather than ignoring it, stores it, creates the placeholder with that
field type, and updates an existing one when the format changes — as it already
did for a changed `dateText`. A fixed date still wins as plain text, and going
back to automatic restores the remembered format.
`datetime::DATE_KINDS` is the canonical list; `dialogs.rs` uses it rather than
its own copy of the same thirteen strings, and the Header & Footer dialog gains
a Format dropdown listing each type by what it would show now, enabled while the
date is automatic.
Not here: reading a deck does not populate `date_format`, so the dialog shows
datetime1 preselected for a deck whose dates are something else until the user
picks. That is a pptx read-side change and wants its own PR.
Found while testing crates/engine/src/cmd/text.rs:1151 (select_para)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/review.rs:140-155 (delete)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/reuse.rs:~150 (reuse; slides get fresh ids but Action::Slide targets are never remapped)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds PowerPoint's Outline/RTF save type: slide titles at outline level 0
(bold, larger), body paragraphs indented at their outline level, bold,
italic and underline kept, non-ASCII as \u escapes. RTF outlines (ours,
PowerPoint's or a word processor's, by outline level or indent) open as
a presentation or drop in as slides.
Co-authored-by: Cursor <cursoragent@cursor.com>
The renderer lays every slide background over white (as PowerPoint does),
so a PNG export was always opaque. A new transparentBackground option leaves
the slide background out (both the white underlay and the background fill),
so only what is on the slide is drawn over transparency. Shapes filled with
the background (useBgFill) still show it.
- render: RenderOpts::transparent_background
- engine: file.export {transparentBackground} (PNG only; other formats
refuse it and write nothing), file.render {transparentBackground}
- UI: Transparent background checkbox in the Export dialog (PNG) and in
File > Export > Change File Type > PNG; the web download honours it
- CLI: render --transparent
Paragraph commands (align, case, insert, paste, selectParagraph) iterated
for pi in a.0..=b.0 with both ends taken verbatim from text.select, so a
control-channel selection with caret u64::MAX spun the loop over 2^64
entries on the UI thread, and text.selectParagraph's at.0 + 1 overflow-
panicked in debug builds. Clamp every range at the body's last paragraph
and use saturating_add for the single-index arithmetic.
Regression test: paragraph_commands_clamp_a_hostile_text_selection —
fails on main with 'attempt to add with overflow' at cmd/text.rs:1151,
passes with the clamp.