Commit Graph
542 Commits
Author SHA1 Message Date
Nikita RakitinandCursor 5acf9eeebf Review: compare with a revised copy and accept its slides
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>
2026-10-13 22:49:55 +03:00
Nikita RakitinandCursor e615188f8b Text: AutoCorrect options, case rules and your own entries
AutoCorrect gains PowerPoint's options: correct TWo INitial CApitals,
capitalize the first letter of sentences and the names of days, fix
accidental cAPS LOCK, and replace 1/2, 1/4, 3/4 with fraction
characters. "word..." becomes "word…", "word," and "word." are corrected
too, and the built-in list grows from 13 to ~70 entries. The case rules
apply only to typing, not pasted text.

New text.autocorrect sets the options and adds or deletes replace
entries (your own win over the built-in list; deleted built-ins stay
deleted) and returns the list. Preferences ▸ AutoCorrect Options… opens
an AutoCorrect dialog with the options and the editable list.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:43:32 +03:00
Nikita RakitinandCursor 5007c6919e Insert: Summary, Section and Slide Zoom with Return to Zoom
Insert › Links gains Zoom, with PowerPoint's three kinds: Slide Zoom
(pictures of chosen slides on the current slide), Section Zoom (one per
chosen section) and Summary Zoom (a new Title Only slide in front of
the chosen parts). Each zoom is a picture of its target that jumps to
it in the show. With Return to Zoom, on by default for section and
summary zooms as in PowerPoint, the show comes back to the zoom slide,
fully built, after the target slide or the section's last slide; going
elsewhere (a slide number, See All Slides) forgets it. Hyperlink gains
zoom_return for this; .pptx saves zooms as the linked pictures
PowerPoint writes as their fallback, so the return flag is DeckCraft-
only for now. Live thumbnails, the zoom-in animation and the native
p166:sldZm element are still missing; gaps.md says so.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:40:37 +03:00
Nikita RakitinandCursor a7bc932cc8 UI: WordArt adjust handles drag a text warp
A selected text warp shows one yellow handle per adjust.
Dragging it sets that guide value in one undo step.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:38:16 +03:00
Nikita RakitinandCursor 9af5a7d17b Pictures: Artistic Effects — apply, change, remove and keep in PPTX
New picture.artisticEffect applies one of 7 effects (blur, pencil
grayscale, line drawing, film grain, cutout, glow edges, photocopy) to a
picture's pixels, as PowerPoint does: the processed PNG becomes the
picture and the original is kept on PictureFill::artistic, so another
effect starts from the original and `none` (or Reset Picture) restores
it. Change Picture drops the effect.

PPTX: written and read as PowerPoint does, the blip being the processed
picture with the original and the effect name in
a:extLst/a14:imgProps/a14:imgLayer, so a deck from PowerPoint keeps its
effect, original and all, when saved again.

Picture Format gets an Artistic Effects menu.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:35:55 +03:00
Nikita RakitinandCursor 69a4424e75 Transitions: built-in sounds, Stop Previous Sound and Loop Until Next
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>
2026-10-13 22:30:19 +03:00
Nikita RakitinandCursor 4ea79d4c5e Shapes: Edit Points — point types, Bézier handles, right-click menu
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>
2026-10-13 22:29:57 +03:00
Nikita RakitinandCursor 733522d528 Shapes: Edit Points — drag, add and delete a shape's vertices
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>
2026-10-13 22:22:57 +03:00
Nikita RakitinandCursor 5eef9fb55a Pictures: compress pictures to a target resolution
Picture Format › Adjust gains Compress Pictures, with PowerPoint's
options: HD 330, Print 220, Web 150 or E-mail 96 ppi, apply to the
selected pictures or all, and delete cropped areas. The new
picture.compress command downsamples each PNG, JPEG, BMP, TIFF or WebP
picture to the pixels its shown size needs at that resolution (JPEG
stays JPEG at quality 85, the rest become PNG), cuts away cropped areas
when asked, and only replaces a picture when the result is smaller or a
crop was removed. Originals nothing else uses are dropped so the saved
file shrinks; one shared with a picture that wasn't compressed stays.
One undo step restores everything.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:21:57 +03:00
Nikita RakitinandCursor 37bd53c62e Home: Replace Fonts dialog and font.replace command
font.replace {from, to} renames a font family everywhere it is named
explicitly: runs (latin, east Asian, complex script and symbol faces), end
marks, bullet fonts, list styles of shapes, layouts and masters, the master
text styles, the deck default style, notes, tables and the theme fonts, so
placeholders that follow the theme change too. Names compare
case-insensitively, theme references (+mn-lt) are left alone, a replace that
finds nothing adds no undo step, and missing fonts are recomputed.
font.used lists the families the slides draw in, for the dialog's Replace
list.

Home gets a Replace Fonts… button opening the dialog: Replace (fonts in the
deck), With (type or pick from installed fonts), and it stays open with a
confirmation like PowerPoint's. Ribbon small buttons now expose their label
to accessibility, which the UI test uses to click it.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:20:09 +03:00
Nikita RakitinandCursor 7432c61665 Slides: reuse slides from another presentation
Home › New Slide gains Reuse Slides… and Reuse Slides (Keep Source
Formatting)…, backed by a new slide.reuse command that inserts the
slides of a .pptx or .deckcraft file (all, a list, or a range) after the
current slide in one undo step. By default each slide moves to the
destination layout with the same name or kind, so it takes this deck's
theme; keepFormatting brings the source master and its layouts along.
Pictures, media and sounds are copied under fresh media ids, so ids
that clash between the two decks no longer mix up pictures, and shape
ids are renumbered with animations, triggers and connector ends
following them.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:14:46 +03:00
Nikita RakitinandCursor 36ec1dc0ca Format Shape: 3-D Format and 3-D Rotation controls
shape.effects gains `threeD`: top and bottom bevel (preset, width,
height), depth, contour, material, lighting, camera preset, X/Y/Z
rotation, distance from ground, and reset. The settings are read from and
written back to the shape's kept a:scene3d / a:sp3d XML (new
cmd::three_d), so they render through the existing camera and bevel code
and survive a PPTX save; extrusion and contour colours are kept as is.

The `bevel` key used to set only Effects::bevel, which neither the
renderer nor the PPTX writer reads, so a bevel set from the app vanished
on save; it now goes through the same path.

The Effects page of the Format Shape pane gets 3-D Format and 3-D
Rotation sections.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:13:36 +03:00
Nikita RakitinandCursor e9315bc141 UI: seeded whole-window soak with memory and timing bounds
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>
2026-10-13 22:09:48 +03:00
Nikita RakitinandCursor ad24f545e2 Format Shape: picture or texture fill options
shape.fill gains pictureProps for a picture fill: tile on/off, tile
offset X/Y (pt), scale X/Y, alignment and mirror type, stretch offsets
(left/top/right/bottom), and rotate with shape. `transparency` now also
sets a picture fill's opacity. Values are clamped and unknown keywords or
non-finite numbers ignored. The Fill page of the Format Shape pane gets a
"Picture or texture fill" option with a picture source button and these
controls. The model, renderer and PPTX reader/writer already carry the
fields; a new engine test checks they survive a PPTX save and reopen.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:09:33 +03:00
Nikita RakitinandCursor 460a2712e4 Transitions: choose a transition sound and play it in the show
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>
2026-10-13 22:07:06 +03:00
Nikita RakitinandCursor 283b30f12d UI: WordArt Transform gallery applies a text warp
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>
2026-10-13 21:58:35 +03:00
Nikita RakitinandCursor 607ffc5591 Text: set text transparency from Text Fill (#32)
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>
2026-10-13 21:55:21 +03:00
Nikita RakitinandCursor 14bc6a7ef9 Format Shape: shadow, glow and reflection detail controls
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>
2026-10-13 21:52:56 +03:00
Nikita RakitinandCursor 85833cf5f5 Engine: seeded editor soak tests; tiny charts no longer blank the slide
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>
2026-10-13 21:50:23 +03:00
Nikita RakitinandCursor 16c5d6a6a3 Format Shape: line compound, cap, join, arrows and sizes; lock aspect ratio
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>
2026-10-13 21:45:42 +03:00
Nikita RakitinandCursor e6920f8ebe Print: choose paper size and orientation for notes and handouts
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>
2026-10-13 21:43:54 +03:00
Nikita RakitinandCursor 2a79cb7671 Shapes: smooth a rectangle's corners into a squircle (#286)
deckcraft_geom::squircle builds a rectangle outline whose corners start
curving earlier along the edge and keep a shorter circular arc, as design
tools' corner smoothing does. shape.smoothCorners turns rectangles and
rounded rectangles into that outline (custom geometry, so it round-trips
through PPTX), and Edit Shape gains a Smooth Corners slider.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 21:17:39 +03:00
Nikita RakitinandCursor eec67560df Fonts: find fonts where Linux and Flatpak keep them; add and rescan commands (#277)
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>
2026-10-13 21:16:23 +03:00
Nikita RakitinandCursor ede2ca69b6 Designer: layout ideas that rearrange the current slide (#314)
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>
2026-10-13 21:10:59 +03:00
Nikita RakitinandCursor c7a4930c4c UI: open presentations in the background with a progress window (#350)
Opening from the file picker or the Backstage recent list read and parsed
the file on the UI thread, so a large PPTX froze the window. The read now
runs on a thread (engine: read_presentation_file / add_presentation_file),
and a modal shows the file name, elapsed time and Cancel; Escape cancels
too. A cancelled read is dropped when it finishes. Scripted opens (control,
MCP, CLI) and the web build stay synchronous.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 21:08:47 +03:00
Nikita RakitinandCursor 241ecd59d7 Recovery: unique uids, per-instance ownership, keep snapshots until replaced (#390)
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>
2026-10-13 20:56:21 +03:00
Nikita RakitinandCursor 272ab5f96f Edit: copies remap media, connector ends, animation targets and triggers (#406)
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>
2026-10-13 20:47:27 +03:00
MarekSpodBiedry 007741718e Slide: leading tabs in a new slide's body set the outline level (#370) 2026-10-13 17:52:00 +02:00
MarekSpodBiedry 97855c70a0 File: save through a unique, exclusively created temporary file (#391) 2026-10-13 17:48:28 +02:00
MarekSpodBiedry 33ade63687 Table: split and cell fill stay inside the table (#400) 2026-10-13 17:38:20 +02:00
MarekSpodBiedry 27d73db0d9 Engine: a command that fails after editing puts the presentation back (#405) 2026-10-13 17:38:10 +02:00
MarekSpodBiedry 863f7cbd87 Engine: moving the selection to another text body settles the composition (#421) 2026-10-13 17:37:44 +02:00
NikitaandCursor 525b7ed3f9 Palette: skip commands that need a value
Searching for Export ran file.export with an empty object and reported a missing path. The palette now offers only commands that accept that call, so Export opens the dialog.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 18:31:02 +03:00
NikitaandCursor 82dcd86c88 Pptx: read and write embedded fonts
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>
2026-10-13 18:22:48 +03:00
Sazid Al Bayazid e70fce996b Model: an exhausted id space refuses growth instead of duplicating ids (#407)
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).
2026-10-13 21:13:25 +06:00
NetterSilver 35312529e1 slide.colorAt: the colour the slide is drawn with at a point (#367)
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.
2026-10-13 16:41:57 +02:00
Brandon ThomasandClaude Opus 5.5 0c5ddb5dea Gradients: read, write and draw a:tileRect
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>
2026-10-13 08:16:41 -04:00
NikitaandCursor 503ee5d67e A tabbed slide body is an outline, and --help exits (#360)
slide.new kept a leading tab as text, while text.set already turned it into a bullet level. The desktop --help flag was opened as a missing file.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 12:38:57 +03:00
pierrevbc 44a032d3de Tables: editing tests for tables with a file-defined style
A table whose style the file defines (its parts and background drawn
since #292 and #294) edits like the others: the caret is laid out with
the file style's lettering (a regular header where the built-in style
bolds it), a double-click selects the word drawn there, a click moves
the caret to the clicked cell and a border drag resizes the columns
while the cell stays edited.
2026-10-13 11:30:13 +02:00
pierrevbc d63d250d58 Tables: Tab and Shift+Tab skip cells hidden under a merge
Tab moved into the cells covered by a merged cell, editing text that is
never drawn. It now goes to the next visible cell; Tab in the last
visible cell still adds a row, and Shift+Tab in the first cell stays.
2026-10-13 11:30:13 +02:00
pierrevbc 456c5c5373 Tables: drag row and column borders to resize them
Hovering a table border shows a resize cursor (new hit kinds
TableColumn and TableRow) and dragging it resizes, one undo step per
drag, as in PowerPoint:

- an inner column border moves between its two columns and the table
  keeps its width;
- the right edge widens or narrows the last column and the table;
- a row's bottom border (the last one is the table's bottom edge) sets
  that row's height, which stays a minimum: rows still grow to fit
  their text.

Columns stop at 12 pt. Borders hidden inside merged cells aren't hit;
the left and top edges still move the table. A border drag while a
cell is edited keeps editing it. The drag goes through
table.columnWidth, which takes a new keepTotal option (the next column
absorbs the change), and table.rowHeight. Hit testing is done in the
table's own frame, so rotated tables resize along their axes.
2026-10-13 11:30:13 +02:00
pierrevbc 70314fa828 Tables: clicks place the caret in the clicked cell
- A double-click in a cell puts the caret where clicked and selects the
  word there, as in shapes (it used to put the caret at the end).
- While a cell is edited, a click in the same table moves the caret to
  the clicked cell and position; a drag selects text within the cell
  and Shift+click extends only within it.
- A click in an already selected table enters the clicked cell at the
  click; dragging the table still moves it.
- The release that follows a double-click no longer re-enters the text
  at the click, which collapsed the word just selected (shapes too).

Cells are found from the drawn cell rects, so merged cells and rows
that grew to fit their text are hit where they are drawn.
2026-10-13 11:30:13 +02:00
pierrevbc b5a8642417 Tables: show the caret and text selection in the cell being edited
Editing a table cell outlined the cell but drew no caret or selection,
and Up/Down/Home/End fell back to paragraph moves, because the text
layout used for drawing, hit testing and caret movement only existed
for shapes.

text::text_frame now lays out the edited text as it is drawn and gives
the transform to the slide, for shapes and cells alike: a cell's text is
laid out in its rect (merges honoured) with the cell's margins, anchor
and the table style's look, through the renderer's own cell_body and
cell_rects. layout_for and tools::text_pos use it, and the canvas draws
the caret and selection from it. The cell outline follows merged cells.
2026-10-13 11:28:22 +02:00
pierrevbc ab81f6bc73 Canvas: right click selects the shape under the pointer
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.
2026-10-13 11:26:20 +02:00
pierrevbc 9d185aeb68 Canvas: drag the frame of a text box being edited to move it
While a text box or placeholder is being edited, every press inside its
box placed the caret, so the box could not be moved without leaving
editing first. Like PowerPoint, a band on either side of the box edge
(the hit tolerance wide, in the shape's rotated frame) is now its frame:
hit_test reports Hit::Frame there, the canvas shows the move cursor, and
a press leaves text editing, selects the box and drags it as one undo
step. A click without a drag just selects the whole box. Inside the box
the caret and text selection work as before; resize and adjust handles
still win. The text-click zone now follows the rotated box instead of
its axis-aligned bounds.
2026-10-13 11:26:11 +02:00
lloyd 720b66fbd3 Merge remote-tracking branch 'upstream/main' into fix-211-markdown-outlines
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-13 08:58:03 +02:00
Yevhenii Hurin 922445300b describe keeps a remark with its option and allows a bare call
animation.remove's remark belongs to its `ids` option, so describe shows
it there instead of as a note on the whole command. A command with an
all-optional set of parameters is suggested as a bare id.

[claude-opus-5-5] on behalf of hoblin
2026-10-13 09:55:19 +03:00
Marco Arnulfo 85345aaa13 Open: a password-protected presentation says so, and open errors show on the start screen (part of #346) 2026-10-13 06:44:47 +02:00
Marco Arnulfo e6a41d1ada Review: the Language dialog marks the selected text with the language you pick (fixes #339) 2026-10-13 06:04:28 +02:00
Brandon ThomasandClaude Opus 5.5 1660982e58 File tab: PowerPoint's File view (Home, New, Open, Info, Save As, Print, Export, Close) (#76) (#344)
* 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>
2026-10-12 23:56:02 -04:00