The renderer caps a picture at MAX_SIDE (16,000 px) by clipping, so an image export whose
slide × scale passed that (4× of a 4032 pt slide) came out cropped. file.export and file.render
now lower the scale to fit (cmd::file::image_scale). When exporting every slide and one file
can't be written, the error says which slides already were, and slide numbers are checked
before anything is written.
Co-authored-by: Cursor <cursoragent@cursor.com>
Shape Format › Size and the shape menu open a dialog for position,
size, rotation, scale and lock aspect ratio. One command writes them
together, so undo restores the whole change.
Co-authored-by: Cursor <cursoragent@cursor.com>
Insert › Icons places seventy Lucide 1.56.0 stroke icons as custom
geometry: no fill, round caps, and a line width scaled from the
24-unit design. The gallery searches by name. SVG paths, including
arcs, are converted when the icon is inserted so the renderer strokes
the outline.
Co-authored-by: Cursor <cursoragent@cursor.com>
animation.add stored any `option` string as given, and the renderer reads
direction codes letter by letter, so "fromBottom" (r, t, b) flew in from the
bottom right. The option is now checked against the effect's list; direction
words ("fromBottom", "topLeft", "upRight") map to the stored codes ("b", "lt",
"ru"), and anything else is an error listing the valid options.
animation.options applies the option to each target in its own effect's terms
and leaves targets without such an option alone.
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>
An owned corpus of those element kinds survives open, save and reopen.
Paragraph default run properties, including a link, and action sounds
were dropped on save, so import and export now keep them.
The Bullets and Numbering menus only offered a fixed list of styles.
"Bullets and Numbering…" at the foot of both menus opens PowerPoint's
dialog: Bulleted and Numbered tabs with their styles, a custom bullet
character, Start at, Size (% of text, 25-400) and Color (automatic,
text or an accent), and None.
format.numbering now takes color and size like format.bullets, and
both clamp the size to PowerPoint's 25-400 %.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds Basic (stars, heart, crescent moon, teardrop, ...), Lines & Curves
(zigzag, waves, S curves, spirals, bounces, ...) and Special (figure 8,
bean, peanut, neutron, ...) motion paths with PowerPoint preset ids, so
they appear in the Motion Paths gallery, play back, and round-trip
through PPTX.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Picture Format > Remove Background button was a disabled
placeholder. picture.removeBackground now flood-fills from every edge
pixel through similar colours: each step must be close to the pixel
before it (so gradients and soft shadows follow) and to the edge colour
it started from (so it can't leak into the subject). That region turns
transparent with a one-pixel soft edge, and the picture points at a new
PNG; undo brings the original back. Tolerance 0-100 (default 20); the
ribbon offers normal, looser and tighter. Pictures whose edges are all
subject are left alone, and pictures over 16 megapixels are refused.
Co-authored-by: Cursor <cursoragent@cursor.com>
load() ignored every failed media read, so a .deckcraft with a missing,
oversized or bad-CRC media member opened silently with empty pictures,
and the next Save overwrote the original with that loss.
load_checked() now lists each unreadable member (linked media saved
without bytes is not damage). file.open/openBytes return damagedMedia,
show a message, and open the deck as an untitled "(Repaired)" copy so
Save asks for a new name and the original file stays intact.
Co-authored-by: Cursor <cursoragent@cursor.com>
Follow-up to the transition sound picker. The Sound box now lists
[Stop Previous Sound] and six built-in sounds (Chime, Click, Camera,
Whoosh, Drum Roll, Laser) that are synthesised as small WAVs when
chosen, so no sound files ship with the app; the same built-in sound is
embedded once however many slides use it. Loop Until Next Sound repeats
a sound in the show until another slide's sound or Stop Previous Sound.
PPTX reads and writes p:stSnd loop="1" and p:endSnd, keeps the sound's
file name in p:snd/@name, and setting a slide's transition to None no
longer drops its sound.
Co-authored-by: Cursor <cursoragent@cursor.com>
shape.pointType makes a vertex smooth (handles in line and equal),
straight (in line) or corner (independent), turning its segments into
curves so it has handles. shape.moveHandle drags a control handle; the
other one follows as the point's type says (given, or read from the
handles). shape.points also reports each vertex's type.
Canvas: clicking a vertex shows its handles, which drag (one undo step
per drag) and follow the type chosen for it. Right-click a vertex for
Smooth / Straight / Corner Point and Delete Point, or anywhere for Exit
Edit Points; a shape's context menu gets Edit Points. The selection box
is hidden while points are edited.
Co-authored-by: Cursor <cursoragent@cursor.com>
New cmd::points with shape.editPoints (enter/leave; a preset becomes a
freeform in shape-local points), shape.points (vertices in slide points),
shape.movePoint, shape.addPoint (splits a line, quadratic or cubic
segment) and shape.deletePoint. After each change the box is fitted
around the outline and kept in place on the slide, rotation and flips
included. Closing edges are made explicit so they take points too.
On the canvas the edited shape shows its vertices as handles: drag one
(one undo step per drag), Cmd/Ctrl-click a vertex to delete it or the
outline to add one; Escape, Enter or a click elsewhere leaves Edit
Points. Shape Format ▸ Edit Shape gets an Edit Points item.
Not yet: smooth/straight/corner point types and Bézier control handles.
Co-authored-by: Cursor <cursoragent@cursor.com>
ui_soak_tests.rs drives the app through the egui test harness with seeded
random clicks, drags, keys (plain, Ctrl, Shift), typing and scrolling over the
sample deck. Every ten actions it checks that every document validates, the
selection is in range, undo, journal, thumbnail and texture caches stay
bounded, and no command or render panic was caught; at the end, that input
really reached the editor and the mean frame stays under 250 ms. Two seeds x
150 actions run in CI; DECKCRAFT_UI_SOAK_STEPS/SEED run it long.
Panics swallowed by the guards were invisible to tests: Session now counts
them (panics_caught) and deckcraft_render::panics_caught() counts render
panics drawn as blank. Textures::thumb_count exposes the thumbnail cache size.
Co-authored-by: Cursor <cursoragent@cursor.com>
Transition sounds were read from and written to .pptx but could not be
set and never played. Transitions › Timing gets a Sound box ([No Sound]
or Other Sound…), backed by a new transition.sound command that embeds
an audio file on the selected slides (or removes it with none). In the
slide show the sound plays as its slide comes in and, as in PowerPoint,
keeps playing into the next slide unless that slide has a sound of its
own. Built-in sounds, Stop Previous Sound and Loop Until Next Sound are
still missing; the parity docs say so.
Co-authored-by: Cursor <cursoragent@cursor.com>
Shape Format ▸ Text Effects lists the 40 prstTxWarp presets and
No Transform. format.textWarp sets or clears that warp on the
selected text, with optional adjust values.
Co-authored-by: Cursor <cursoragent@cursor.com>
There was no way to make text see-through: the only Transparency
slider was the shape fill's. The new format.textTransparency command
keeps the text's colour and sets its alpha (replacing any it had, and
dropping it at 0). A run without its own colour takes the colour it
shows now, so inherited placeholder or theme colours stay as they are.
Gradient text gets the alpha on every stop. The Format Shape pane's
Text Options page gains a Text Fill section (colour and Transparency),
and the Shape Format › Text Fill dropdown has the same slider.
Co-authored-by: Cursor <cursoragent@cursor.com>
shape.effects gains shadowProps / glowProps / reflectionProps that adjust
the current effect (or add a default one): shadow colour, transparency,
blur, angle and distance; glow colour, transparency and size; reflection
transparency, size, distance and blur. Values are clamped, non-finite ones
ignored. The Effects page of the Format Shape pane exposes them as sliders
and colour pickers.
Co-authored-by: Cursor <cursoragent@cursor.com>
soak_tests.rs drives every non-file command with plausible and hostile
parameters over a ~100-slide deck, checking after each step that the
document validates, the selection is in range, the undo stack is bounded,
undo then redo restores the exact document, and every 100 steps that the
deck saves and reopens as .deckcraft and .pptx. Four seeds x 400 steps run
in CI (under a second); DECKCRAFT_SOAK_STEPS/SEED run it long.
The long soak found that a chart a few points wide panicked in f64::clamp
(label gutter min above max), and the render guard then blanked the whole
slide. chart.rs now uses a non-panicking clamp_soft for the gutters and the
zero line; a render test draws tiny charts next to a shape without the
guard.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Format Shape pane's Line page had only colour, width and dash. It now
has PowerPoint's compound type, cap type, join type, begin/end arrow type
and begin/end arrow size drop-downs, and the Size page has Lock aspect
ratio for the width and height fields. shape.line gains compound,
headSize and tailSize, and changing an arrow's kind keeps its size.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Print page always laid notes pages and handouts out on portrait US
Letter. It now offers Letter, A4, Legal and A3 in portrait or landscape,
as PowerPoint's Print settings do, and the preview shows the chosen
page. file.export, app.print and the MCP export tool take the same
`paper` and `orientation` params; unknown values are an error. A notes
page keeps its slide within the top half of the page, so landscape
paper still leaves room for the notes. Full-page slides still print on
slide-shaped pages.
Co-authored-by: Cursor <cursoragent@cursor.com>
The font menu lists every family the scan of the system font folders finds,
but on Linux the scan only looked in /usr/share/fonts, /usr/local/share/fonts,
~/.fonts and ~/.local/share/fonts. Fonts under XDG data dirs, in folders
fontconfig is configured with (NixOS, Guix, custom setups) and, inside
Flatpak, the host's fonts never showed.
- system_font_dirs adds $XDG_DATA_HOME/fonts and each $XDG_DATA_DIRS/fonts,
Flatpak's /run/host/{fonts,local-fonts,user-fonts}, and the <dir> entries
of /etc/fonts/fonts.conf, local.conf and conf.d; duplicates are dropped.
- font.add {data} adds a font from its bytes for the session; font.rescan
catalogs the installed fonts again, and the font menu has a
"Rescan Installed Fonts" item for fonts installed while DeckCraft runs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Design Ideas showed the current slide once per built-in theme, and picking
one ran design.theme on the whole presentation; with a large picture every
idea looked the same. The pane also re-rendered eight slides every frame.
- New commands design.ideas (query) and design.applyIdea {id, slide?}
(cmd/ideas.rs). A slide with a picture gets full-bleed with the title on a
band, picture left or right with the text beside it, and a framed picture;
a text slide gets a title column, a centred title and a title at the foot.
An idea moves the slide's own shapes, crops the picture to cover its new
box, sends it to the back, and changes only that slide (one undo step).
- The pane shows the layout ideas first, then the themes under a "whole
presentation" heading, and caches the thumbnails until the deck revision,
slide or pane width changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
Document uids started at 1 in every process, so two instances sharing
the recovery folder overwrote and deleted each other's entries, and
file.recovery.open deleted each entry as soon as it was loaded, leaving
nothing if the app died before the next checkpoint.
- Uids are a random per-process base plus a counter (below 2^53).
- Each instance holds a lock on owner-<id>.lock in the folder; entries
record their owner, and recovery open skips a running owner's entries
and removes stale lock files.
- Opening an entry no longer deletes it: the recovered presentation keeps
the entry's uid and takes ownership, so the snapshot stays until the
next recovery save replaces it or a save/close removes it.
- Entry files and metadata are both written via temp file + rename.
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>
A deck that embeds a TrueType or OpenType face, including one wrapped as Embedded OpenType, now draws with that face and saves it back. MicroType Express subsets stay in the file and still draw in a stand-in.
Co-authored-by: Cursor <cursoragent@cursor.com>
A read-only command, {slide?, x, y (slide units), scale?}: the slide is rendered and
the pixel at the point is returned as {color: "RRGGBB", alpha, x, y}. This is what the
Eyedropper takes, and what scripts and MCP use to read a colour off a slide. A point
outside the slide, a missing or non-numeric coordinate or an unknown slide is an error.
Gradient tile rectangles were dropped on import. PowerPoint's "from
corner" radial presets depend on them: fillToRect puts the focus on a
corner and tileRect stretches the gradient's box a whole size past it, so
the circle is centred on that corner and reaches the far one. Without it
(after the two-point radial change) such fills washed out. A linear
gradient now spans its tile rectangle; a radial one ends on the circle
around it, with the focus still placed in the shape's own box (fitted to
PowerPoint's export of the corpus deck tdf114848: mean diff over the
filled box 0.77). Round-trips through pptx.
Wild synthetic set: tile-rect gradients 16.74 -> 2.48.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A right click on an unselected shape opened the slide's context menu
(Layout, Reset Slide...) because only the primary button reached the
pointer tool; the shape had to be left-clicked first. The right-button
press now goes through Session::pointer as PointerKind::ContextClick
(`contextclick` on the control channel and MCP) and selects like
PowerPoint before the menu opens: an unselected shape is selected alone
(a text box or placeholder is not put into text editing, nothing is
dragged), a selected shape keeps the selection, multiple selection
included, and empty slide clears it. In the box whose text is being
edited, the caret and text selection stay, and the context menu offers
text items (Paragraph, Bullets, Numbering, Link) next to Cut, Copy and
Paste, which act on the selected text.
* Engine: file.info and PowerPoint-style print slide ranges (#76)
file.info reports what File › Info shows (name, path, format, unsaved
changes, slide and hidden-slide counts, sections, size on disk,
properties). file.export's PDF `slides` also takes the range typed in
PowerPoint's Print page ("1-3, 5", 1-based); bad or out-of-range text is
an error instead of a silent full export.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* UI: the File tab (Backstage) like PowerPoint's (#76)
File (first ribbon tab) or Alt+F opens a full-window view in place of the
ribbon, slides and status bar: a side menu (Back, Home, New, Open, Info,
Save, Save As, Print, Export, Close; Account and Options at the bottom)
and the chosen page. Esc or Back returns; Up/Down walk the side menu and
show each page as it gets the focus; Enter runs Save/Close/Options.
Laid out after PowerPoint for Windows as observed: 160-pt menu a step
darker than the page, outline icons, current page in the accent colour.
The start screen is now File › Home (as in PowerPoint), with only Home,
New and Open at the top and no Back. Pages call existing commands and
file helpers: file.new {theme} and the sample deck (Home, New); recent
files with search, Browse and Recover Unsaved (Open); file.info and
file.properties, Accessibility Checker, Revert (Info); file.save /
file.saveAs / file.export by type (Save, Save As); a PDF with the Print
page's slides, layout (full page, notes, handouts 1-9), hidden slides and
frames, opened in the system viewer or downloaded on the web, with a
page preview (Print, Ctrl+P); Create PDF, Create Handouts and Change File
Type (Export); appearance and About (Account); Preferences (Options).
Share (cloud) is left out.
Close and Ctrl+W now ask Save / Don't Save / Cancel about unsaved
changes instead of closing at once. The Export dialog shares
SlideApp::export_as with the Export page. New UI commands: app.backstage
{page}, app.closePresentation, app.print; ui.inspect reports the page.
New icons drawn in code: back, save as, account, recent, new file, open
folder.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Docs: File tab (Backstage) in ui-parity, gaps, checklist and progress log (#76)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Text: Ctrl+arrows, Ctrl+Backspace/Delete and the other text keys work like PowerPoint (#268)
Observed black-box in PowerPoint for Windows on synthetic text:
- Word stops (crates/model/src/words.rs): words, punctuation runs, symbol
runs, tabs, line breaks and each CJK character are tokens; trailing spaces
join the token before. it's, a-b, 3.14, 1,000 are one word; e.g., a_b and
x@y.com are not. Grapheme clusters never split.
- Ctrl+Left/Right and Ctrl+Shift+Left/Right move by those stops, across
paragraphs; with a selection they go on from its start/end. In a
right-to-left paragraph Left and Right swap.
- Ctrl+Backspace deletes to the previous stop; Ctrl+Delete deletes the rest
of the word, or spaces plus the next word, then applies PowerPoint's smart
spacing. With a selection both work from its start.
- Ctrl+Up/Down by paragraph, Ctrl+Home/End to the text's ends.
- End on a wrapped line goes past the trailing space and the caret stays
drawn at that line's end (TextSel::line_end); End again stays, Home
returns to the same line.
- Ctrl+]/[ and Ctrl+Shift+>/< change font size (egui's Period/Comma/
bracket key names now match registry shortcuts).
- Shift+F3 cycles lower -> Title -> UPPER; a caret changes its word.
- Double-click selects the word without trailing spaces; triple-click
includes the paragraph mark.
- Mac keeps Option for words and Cmd for line/text ends (Session::mac_keys).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* rustfmt
* Canvas: one caret, drawn at a wrapped line's end after End
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #133: Selection pane engine: restack inside groups, Show/Hide All, Lock like PowerPoint
- arrange.bringForward/sendBackward/bringToFront/sendToBack and arrange.reorder work on a
group's children, which stay in their group (as PowerPoint's pane arrows and drag do);
steps that change nothing leave no undo step.
- selection.select (pane click: Ctrl toggles, children and top-level objects don't mix,
picking a hidden object shows it), selection.showAll (everything, children too) and
selection.hideAll (top level).
- shape.lock cascades through groups; locked objects can be selected, formatted and typed
in, but not moved, sized, rotated, flipped, reshaped or cropped (commands and canvas).
- PPTX: Lock reads and writes PowerPoint's full locking attribute set per element kind
(observed by saving locked objects from PowerPoint); partial locks aren't Lock.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Front-most first with groups folding; drag a row to restack among its siblings (accent
insertion line); Bring Forward / Send Backward arrows; Show All / Hide All; per-row eye and
padlock; double-click or F2 renames in place; Ctrl+click toggles, Shift+click selects a run;
arrows, Home, End and Esc in the list; right-click menu. Lock/Unlock on the canvas menu,
greyed handles on locked objects. egui_kittest tests drive the pane.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Text: input method composition as commands, one undo step per composition (#101)
text.compose shows an input method's preedit in place at the insertion point and replaces it on
each update; an empty preedit cancels and restores the text and selection. text.commit replaces
the composition with the result as one undo step. Any other edit, key or click settles an open
composition first. The composition is underlined under the baseline (thicker under the part being
converted), and the caret line is measured for empty placeholders too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* UI: Korean, Japanese and Chinese input and interface text (#101)
The slide turns the input method on while it takes typing, with the candidate window at the
caret, and forwards egui Ime events to text.compose / text.commit, so a Korean keyboard composes
syllables instead of separate jamo. Interface text in scripts the interface fonts lack (Hangul,
Simplified Chinese, Thai...) borrows faces from the font database (installed fonts such as Malgun
Gothic; nothing bundled): file names, titles, notes, outline, sections, comments and typed text.
Ported and widened from WordCraft's #565/#840 fallback. The notes pane keeps text typed before
focus leaves in the same frame (Escape, another slide). Control channel: ui.ime.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PowerPoint's Normal view draws shapes, pictures and text that sit partly
or wholly off the slide on the area around it, at full strength, with
their effects, master and layout graphics included; the slide show,
exports and thumbnails clip to the slide. DeckCraft clipped everything to
the slide in the editor too.
- render: `slide_extent`/`layout_extent` estimate how far a slide's
drawing reaches (boxes, outlines, shadow, glow, reflection);
`render_slide_drawn` also reports what a render actually drew (text
spilling out of its box, table rows that grew); `pasteboard_region`
picks the pixels the editor renders: the slide plus what reaches off
it and is in view, rounded to 256 px tiles so scrolling rarely
re-renders. A slide with nothing off it renders exactly as before.
- ui-egui: the canvas texture covers that region (slide and master
views); it re-renders only when the slide, zoom or the region changes,
and a second time only if text drew beyond the estimate.
- Hit testing, selection, marquee and moves already worked in slide
space; tests now cover them off the slide.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Theme background pictures (Office 2007-era themes such as Treck) carry an
a:duotone recolour in phClr; DeckCraft read it but drew the picture
unchanged, so those backgrounds came out grey. The painter now resolves the
duotone's colours against the slide's colours and placeholder colour, and
the decoded-image cache applies them (luminance picks a colour between dark
and light) and keys on them.
Layouts and slides can replace the master's colour map
(p:clrMapOvr/a:overrideClrMapping, e.g. dark section layouts mapping bg1 to
dk1). They were read as the master's map; now Layout and Slide carry an
optional colour map, resolve::Ctx uses slide > layout > master, and the
writer keeps them.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
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>
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>
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>
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.