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>
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>
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>
Conflicts resolved onto main's newer shape, and the PR's policy applied to
every alloc_id/add_media/new_slide call site main has grown since: commands
answer bad(cmd, NO_ID_SPACE) (slide.reuse, insert.zoom, SmartArt, photo
album, review.accept, handout/notes masters, master placeholders,
background/artistic/compress pictures, transition sound, ink to shape,
paste special, file.new templates); reid/bring_media/renumber/regenerate
and compare::adopt/accept are fallible; outline import keeps what fit.
Tests use expect() on the now-Option results.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy puts HTML on the pasteboard with the same plain words, so Pages keeps
bold, italic and underline, and a paste of that HTML keeps them here. A
shape with no text copies out as a picture.
Co-authored-by: Cursor <cursoragent@cursor.com>
An owned corpus opens, saves and reopens SmartArt and OLE kept as XML, audio and video bookmarks and trims, a section with a hidden slide, slide-number and date fields, ink, .glb models and animation repeat, bounce end and triggers. Relationship-bearing SmartArt and OLE, playback options and rewind stay with their open PRs.
Adds edit.pasteSpecial. Keep Source Formatting bakes the copied shapes'
theme colours and theme fonts into fixed values (captured at copy time),
Picture draws the copied shapes into one PNG picture, and Keep Text Only
pastes plain text. Ctrl+Alt+V, Edit › Paste Special… and the Paste menu
open a Paste Special dialog.
Co-authored-by: Cursor <cursoragent@cursor.com>
review.compare {path | name, data} reads a revised presentation (native or
PPTX) and lists slide-level revisions against the open one: inserted,
deleted and changed slides, with what changed (text, text formatting,
moved, resized, rotated, formatting, picture, objects inserted/deleted,
layout, notes, transition, animations, background, hidden). Slides match by
id, or by position when the decks share none (fixed for the session so
accepting doesn't switch the matching half way). Shapes match by id, then
name.
review.accept {index | all} applies revisions as one undo step: a revised
slide's media is copied (remapped on an id clash), its shape ids kept unless
another slide uses them (animations follow), its layout matched by id, then
name. review.revisions and review.endCompare complete the set.
Review tab: Compare (file picker), Accept All, End Review; the Revisions pane
lists changes with Go-to and Accept. Parity row: Compare presentations M → P.
Co-authored-by: Cursor <cursoragent@cursor.com>
Paste added clashing media under a fresh id but left the pasted shapes on the
old id, reid did not touch connector ends despite its comment, pasted slides
kept animations on the original shape ids, and slide.duplicate remapped
animation targets but not triggers.
- reid returns the old-to-new shape map; connector ends on a copied shape
follow the copy, ends on a shape outside the copied set are detached.
- remap_animations points targets and triggers at the copies (an animation
whose target wasn't copied is dropped, an uncopied trigger cleared); used
by slide paste and slide.duplicate.
- bring_media returns the media ids it had to change and paste rewrites the
shapes (and a pasted slide's picture background) to use them; slide copy
now also carries a picture background's media.
Co-authored-by: Cursor <cursoragent@cursor.com>
alloc_id bumped next_id with saturating_add: a presentation whose
next_id reached u32::MAX (a hostile file can arrive that way;
fix_next_id imports it) handed out that same id to every new slide,
shape, layout and media - duplicate ids, ambiguous references, silent
corruption on every later insert.
alloc_id now returns Option<u32> via checked_add, and every caller
refuses: the engine's insert/duplicate/media/layout commands answer
with a clear error naming the command, paste and duplicate re-id
helpers propagate it, media addition is fallible, and a fresh slide is
Option. The pptx export's synthesized blank layout is guarded the same
way. Loading keeps such a deck viewable: repair re-ids duplicate
slides while it can, and a duplicate it cannot re-id stays as it is -
growth is refused, and only that duplicate keeps its ambiguous id.
Renumbering the loaded graph was rejected: it needs the
cross-document reference remapping that issue #406 tracks.
Tests: model exhaustion (alloc_id None, fresh media refused, a new
slide refused), engine (slide.new, shape.insert, slide.duplicate and
edit.duplicate all refuse; the document holds no duplicate ids),
format repair (an unre-id-able duplicate stays visible).
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.
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.
- engine: add animation.painter (Alt+Shift+C) and animation.painterApply (Alt+Shift+V) supporting single-use and sticky modes
- tools: apply animation painter on canvas shape click
- edit: disarm animation painter on deselect (Escape)
- ui-egui: add Animation Painter button in Advanced Animation ribbon group
- tests: unit tests for single and sticky animation painting
- docs/parity: mark Animation Painter as done (breadth parity to 80%)
- contributors: add Johannesgauss to contributors/people.toml
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>