Commit Graph
1272 Commits
Author SHA1 Message Date
Marco Arnulfo 90d40e2ecc Show: refuse a show.start from that is not a slide
show.start {from} turned a negative or fractional index into slide 0 and an index past the end into a show that opened on the end screen, over the command and the control channel alike. Both now refuse it, as the slide commands do since #1063; a hidden slide still plays.
2026-10-23 14:48:45 +02:00
Marco Arnulfo da9bbac5a0 Play From: skip triggered effects, reject an invalid from
- The pane picks the first effect of the selected shape that is on the click sequence, so a
  triggered effect before it no longer turns the button back into Play All; a clicked triggered
  row falls back the same way.
- animation.preview rejects a from that is not an effect index instead of playing all.
- target-app-parity: the Animation Pane timeline is no longer a shortfall.
2026-10-23 10:22:51 +02:00
fchinuandClaude Sonnet 5.5 da3e89695f Format Painter: paint a drag release only if the drag changed the selection
A Shift-press at the caret inside the edited text starts a text drag
without changing the selection; releasing it no longer repaints the
existing range.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 09:34:39 +02:00
fchinuandClaude Sonnet 5.5 6e201094c6 Format Painter: compare the whole text target when a click selects text
A double or triple click that moves the selection to another shape or
cell at the same offsets now counts as a change and paints.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 09:20:40 +02:00
Marco Arnulfo c0b405c42a Presenter View: speaker notes keep their bullets, numbers and formatting (fixes #1128) 2026-10-23 03:54:04 +02:00
xc4pr ced5dc04ed Design Ideas: recognise the band by its exact fill, not a saved tag (#1201) 2026-10-23 02:41:46 +02:00
xc4pr 6d312e0722 Design Ideas: an empty text box that shows also blocks the ideas (#1200) 2026-10-23 02:27:59 +02:00
xc4pr f330a02970 Design Ideas: mark the Full-bleed band so a fill the user set is kept (#1201) 2026-10-23 02:19:55 +02:00
xc4pr b61898bba5 Theme: text fields show in the light theme (#1204)
In the light theme a text field had the same white fill as the dialog,
pane or ribbon it sits on and no outline (nor a visible focus outline),
so only its label or hint showed. Fill text fields with a light grey, as
the dark theme already sets them apart with a darker well. The File tab
keeps its outlined white fields.
2026-10-23 02:00:59 +02:00
xc4pr adcfa70b4a Design Ideas: no ideas for slides with diagrams, connectors or other graphics (#1200) 2026-10-23 00:45:54 +02:00
xc4pr 7858d589cd Design Ideas: switching ideas removes the Full-bleed title band (#1201) 2026-10-23 00:45:54 +02:00
NetterSilverandClaude Haiku 5.5 53972cf37c Make chart axis flatness thresholds relative to data magnitude
Found while testing crates/model/src/chart.rs:775-794 (axis_scale); same pattern in nice_scale :817

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 21:07:43 +00:00
Marco Arnulfo 19ebd9c2dc Animation Pane: Play From the selected effect
With an effect selected the pane's play button reads Play From and the
canvas preview starts at that effect on the play-all clock, so the
effects before it have already played. With nothing selected it is
still Play All. animation.preview takes an optional from index.
2026-10-22 23:03:35 +02:00
MarekSpodBiedry bde35fab11 Show All slides in the Presenter View window, not on the audience screen (#1130) 2026-10-22 21:10:16 +02:00
MarekSpodBiedry c4a459d1ca Cap the PDF slide raster by total pixel count (#1178) 2026-10-22 21:03:42 +02:00
NetterSilverandClaude Haiku 5.5 c0128a0eb8 Use scale-relative degeneracy check in linear trendline fit
Found while testing crates/model/src/trendline.rs:281-313 (linreg)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 19:21:18 +02:00
NetterSilverandClaude Haiku 5.5 0ce545fcb8 Reject invalid row/col index in table row and column commands
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>
2026-10-22 18:46:51 +02:00
Samir Hanna Verza 07c8e2d77f M3: apply fill and outline edits to group members (#1079) 2026-10-22 13:20:11 -03:00
Keresztes BarnaandClaude Opus 5.5 df0fab857d show: keep the document a slide backdrop was rendered from
The backdrop cache knew its document by address only: after an edit
during the show, a new copy could reuse the freed address and get the
old backdrop. The cache now holds that document and matches on it.
(CodeRabbit review on #1161.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:59:53 +02:00
Hamza 42e89b347d Thumbnails: count the renders, not only the thumbnails held (#969)
Follow-up to #1011, as asked on #1038: a counter of thumbnails actually
rendered, and tests that use it.

`Textures::thumb_count` is the number of thumbnails *held*. It settles at the
rows in view whether the pane rendered them once or a thousand times, so it
cannot see a pane that renders the same rows over and over -- which is what
#969 was: the flashing white was the rendering, not the holding.

`Textures::thumb_renders` counts the renders, and a settled pane is one that
has stopped asking for them.

The two new tests assert what the held count cannot:

- every thumbnail held was rendered, and not many more than that were rendered
  and thrown away (measured: 8 held, 8 rendered at rest);
- ten more frames of the same view render nothing at all;
- after a 40,000 pt scroll of an 800-slide deck, 24 renders in total -- the
  rows it landed on and the ones it passed over, not the deck -- and then it
  settles again.

With the cache made to thrash (its 600-entry limit cut to 4, so the rows in
view are evicted and re-rendered every frame), the merged test for the Slide
Sorter still passes -- the held count settles at 4 -- while this one fails with
"255 renders for 4 thumbnails". That is the hole the counter closes.
2026-10-22 19:20:07 +05:00
skylar taylor-barrick a2435383b1 Merge pull request #996 from Silvertree2010/fix/h2800-deleting-a-shape-that-is-an-anim
Clear animation triggers that point at deleted shapes
2026-10-22 07:50:16 -06:00
skylar taylor-barrick d01254cf2d Merge pull request #1071 from Silvertree2010/fix/h923-opc-target-resolution-cuts-a-par
Strip URI fragment before percent-decoding OPC targets
2026-10-22 07:50:09 -06:00
skylar taylor-barrick 3f399dd450 Merge pull request #1063 from Silvertree2010/fix/h886-slide-delete-with-an-invalid-neg
Reject invalid slide index params instead of using the selection
2026-10-22 07:50:01 -06:00
skylar taylor-barrick 8f04a999b9 Merge pull request #790 from Hamza0101/fix/empty-line-height
Text: an empty line takes the size of the mark that ends it
2026-10-22 07:49:55 -06:00
skylar taylor-barrick f011313a3a Merge pull request #1011 from Faabyy/fix/thumbs-visible-only-969
Thumbnails: only render the slides in view (#969)
2026-10-22 07:49:48 -06:00
skylar taylor-barrick 792829ec41 Merge pull request #982 from alexeyvolkoff/pr-fix-wasm-startup
fix(wasm): avoid std services that panic on wasm32-unknown-unknown at…
2026-10-22 07:49:40 -06:00
Keresztes BarnaandClaude Opus 5.5 31a828b027 show: keep the slide backdrop while animations play
Each animation frame re-rendered the whole slide, backdrop included. A
full-slide layout picture with an 18 pt glow cost ~345 ms of the ~390 ms
frame (2-3 fps at 1080p). Frames now draw only the slide's shapes over
the backdrop, which is rendered once, ahead of time while the slide
rests: ~28 ms a frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:11:09 +02:00
NetterSilverandClaude Opus 5.5 1dd96d3ddc Use a relative coefficient threshold in trendline equations
Found while testing crates/model/src/trendline.rs:~507 (poly_equation)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 10:03:48 +02:00
kinowerandClaude Opus 5.5 dffef33960 UI: drop the wheel carry whenever another presentation is drawn
The presentation check on the leftover wheel fraction ran only while the
canvas was hovered and fitted, so switching to another presentation from
the tabs and back kept the old fraction. It now runs every frame the canvas
draws a presentation, before the hover and overflow branches.

Test: 0.75 of a line in one presentation, a switch away and back with the
pointer off the canvas, then 0.25 does not turn the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:52:50 +02:00
Dominik StrebelandClaude Opus 5.5 7f2955be1c Text: a table in a content placeholder keeps the other-text style
A table can sit in a content placeholder (its graphicFrame carries
<p:ph idx="1"/>). PowerPoint for Mac still draws its text in the master's
other-text style: in a synthetic deck with a bulleted 36 pt bodyStyle, a
14 pt otherStyle and a 26 pt defaultTextStyle, a table in the placeholder
and one outside it both draw 14 pt cells with no bullet or body indent,
also when the layout's and master's body placeholders carry 30 and 32 pt
list styles.

master_list_style now checks for a table before a placeholder, and
level_style takes nothing from placeholder parents for a table, so the
table shape itself (its formatting state) resolves as its cells do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:10:13 +02:00
Dominik StrebelandClaude Opus 5.5 08b7b5eb15 Charts: bars draw their series' pattern fill and no fill
A bar series' own fill was read only when solid or a gradient: a pattern
(a:pattFill) was dropped, and an a:noFill series was read but drawn in the
palette's colour anyway, because the chart renderer turned every fill into
one colour and fell back to the palette for anything but solid. PowerPoint
for Mac draws the pattern, leaves an a:noFill bar empty and draws its
outline (c:spPr/a:ln) where it has one: a synthetic column chart with a
red-on-yellow dkDnDiag series, an empty series with a 3 pt blue outline
and an empty one without, exported as PDF, shows exactly that, legend
keys included.

- The chart reader reads a:pattFill (dml::pattern, shared with shapes);
  the chart writer writes it back for an edited chart.
- Bars and their legend keys paint with the series' or point's own fill:
  a colour, a preset pattern (pattern::fill) or nothing, then the series'
  solid outline at its width.
- Lines, areas and markers, which take one colour, use a pattern's
  foreground colour instead of the palette's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:02:09 +02:00
Dominik StrebelandClaude Opus 5.5 aa6f45ba2c Text: table text takes the master's other-text style
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>
2026-10-22 08:49:42 +02:00
kinowerandClaude Opus 5.5 e11c7e4d7e UI: reset the fractional wheel carry when the presentation changes
The leftover fraction of a wheel line is now stored with the active
document's uid and dropped when another presentation becomes active, so a
quarter line left in one deck can no longer turn a slide in another.

Test: 0.75 of a line in one presentation followed by 0.25 in a new one
does not turn the page; a whole line in the new one does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:09:23 +02:00
kinowerandClaude Opus 5.5 8ddb977733 UI: accumulate fractional mouse-wheel lines into whole slide steps
High-resolution and free-spin wheels on Windows send fractions of a line
(e.g. 0.125 per event); rounding each one up turned a single notch into
several slides. The fractional line delta now accumulates in ui.data temp
state, resets when the direction changes, and each whole line is one
slide. slide.go is skipped when the target is the current slide.

Test: eight 0.125-line events turn exactly one slide, and a reversal
drops the leftover fraction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:01:48 +02:00
kinower 7dce06dca4 UI: keep rapid mouse-wheel slide navigation steps 2026-10-22 00:43:03 +02:00
Dominik StrebelandClaude Opus 5.5 30d00468e2 PPTX: a theme's spDef anchor is for new shapes, not the file's own
A shape whose text has no anchor of its own (`<a:bodyPr/>`) was given the
anchor of the theme's shape defaults (`a:objectDefaults/a:spDef/a:bodyPr`),
which Office's themes set to `ctr`, so its text was centred in the box.
PowerPoint for Mac draws such text at the top: a synthetic deck with an
spDef anchor="ctr" and three rectangles (`<a:bodyPr/>`, anchor="t",
anchor="ctr"), exported as PDF, puts the first two at the top. The spDef
defaults are for shapes inserted later.

Opening a file now anchors every shape without an anchor at the top, as
the standard has it; the model's middle default is still written as ctr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:05:25 +02:00
Dominik StrebelandClaude Opus 5.5 7919429652 Render: measure 3-D point sizes without the camera's turn
shape_at folds a 3-D camera's turn into the shape's transform, so
per_point() measured its foreshortening as if it were a group's
child-coordinate scale: in a 1:1 group, a shape under a turned camera
got a factor other than 1 and drew its extrusion depth, contour and
bevel at the wrong size. The factor is now measured on the transform
before the turn (m with the camera's affine taken back out), and the
turned transform still draws the geometry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:02:17 +02:00
Dominik StrebelandClaude Opus 5.5 48dbdffe07 Render: 3-D sizes in a scaled group stay in points
A group's child coordinates scale its shapes' boxes, but extrusion
depths, contour widths and bevel sizes are points, as PowerPoint draws
them. The renderer applied them in the child space, so the group's scale
multiplied them: a 1 pt contour on a small extruded cube in a legacy
group (1360 child units across a 61 pt group) was drawn hundreds of
points wide, a band across the whole slide, and the cube's extrusion ran
off towards the vanishing point.

per_point() gives the shape-space units per point (the inverse of the
child-coordinate scale that line_space already separates for line
widths); the extrusion depth, contour width and bevel width and height
are converted with it before drawing. Outside a group it is 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:47:33 +02:00
fchinuandClaude Sonnet 5.5 f71ce3fa9d Format Painter: paint paragraph formatting per covered paragraph
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>
2026-10-21 13:46:05 +02:00
FaabyyandClaude fdfdb9e058 Reuse Slides: choose which slides of the other file to insert (#1103)
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>
2026-10-21 08:04:19 +00:00
FaabyyandClaude e03debfd4b Media: engine requests resolve on the editor's slide (#994)
The media.* commands look the shape id up on the editor's current slide, so
the request has to be carried out against that slide too. Passing the show's
slide instead could play another slide's clip under a repeated id.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-21 06:07:58 +00:00
FaabyyandClaude 906898f662 Canvas: a flipped box snaps the edge under the dragged handle (#961)
The resize handles sit in the box's unflipped frame, so in a flipped box
(also one that a drag past the opposite edge just flipped) the dragged
handle is on the other side. Snapping looked at, and moved, the fixed
edge instead.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-21 06:07:58 +00:00
Faabyy 2741d3236b Thumbnails: only render the slides in view (#969)
The slide thumbnail pane and the Slide Sorter asked the texture cache for a
thumbnail on every row, in view or not, three or four new ones a frame. The
cache keeps at most 600 thumbnails and drops the 200 oldest beyond that, so
on a deck of more than 600 slides the pane evicted its own thumbnails before
it reached the end: it kept rendering, the rows in view went white again and
again, and it never finished.

Now only rows within the scroll area's clip (plus two rows of margin) ask for
a thumbnail and keep the pane repainting; the others are still laid out, so
scroll height, section headers, clicks and drag-to-reorder are unchanged.
Small decks behave as before.

Co-Authored-By: Claude <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 a31b990
# Last command done (1 command done):
#    pick 03fce2a Thumbnails: only render the slides in view (#969)
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
#	modified:   ROADMAP.md
#	modified:   crates/ui-egui/src/huge_deck_tests.rs
#	modified:   crates/ui-egui/src/sorter.rs
#	modified:   crates/ui-egui/src/thumbs.rs
#
2026-10-21 06:07:58 +00:00
FaabyyandClaude 96697f7612 Media: the playing clip decides whether it carries over (#994)
The carry-over check looked the clip up again on the previous slide. If
that slide held the same media under the same id without Play Across
Slides, the voice stopped one slide later. A slide without the id fell
back to the first match in the deck. Use the clip the voice was started
with; the next slide is only checked for another clip under that id.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-21 06:07:58 +00:00
Faabyy 7b0252f291 Canvas: resizing snaps the edge being dragged (#961)
snap_resize ran snap_rect on the whole box, which looks at all three
edges on each axis and takes the nearest line. A fixed edge already on a
line (distance 0) always won, so the dragged edge never snapped; a fixed
edge near a line moved the dragged edge by its own distance. Resizing now
looks only at the dragged edges (snap_edges), also for the grid fallback.

Co-Authored-By: Claude <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 a31b990
# Last command done (1 command done):
#    pick 92df608 Canvas: resizing snaps the edge being dragged (#961)
# Next command to do (1 remaining command):
#    pick abb0e09 Canvas: a flipped box snaps the edge under the dragged handle (#961)
# You are currently rebasing.
#
# Changes to be committed:
#	modified:   ROADMAP.md
#	modified:   crates/engine/src/tests.rs
#	modified:   crates/engine/src/tools.rs
#
2026-10-21 06:07:58 +00:00
FaabyyandClaude 2303efe7d5 Media: stop a clip across slides when the next slide reuses its id (#994)
A clip set to play across slides kept its voice even when the next slide
holds a different clip under the same shape id, so that slide's own clip
never started. Keep the voice only when the next slide has no clip under
that id or the same media.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-21 06:07:58 +00:00
Faabyy 2d6582163f Media: look a clip up on the slide it belongs to (#994)
The PPTX import numbers shapes per slide, so two slides can hold different
videos under one shape id. find_clip searched the whole deck and took the
first match, so the show's media cues, the keep-across-slides filter and
the media.* requests all got the first slide's clip: from slide 3 on, video 2
played. find_clip now takes the slide the request comes from and searches it
first, then the rest of the deck.

Co-Authored-By: Claude <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 a31b990
# Last command done (1 command done):
#    pick 1ab070b Media: look a clip up on the slide it belongs to (#994)
# Next commands to do (3 remaining commands):
#    pick ad4e3f0 Media: stop a clip across slides when the next slide reuses its id (#994)
#    pick 0957b58 Media: the playing clip decides whether it carries over (#994)
# You are currently rebasing.
#
# Changes to be committed:
#	modified:   ROADMAP.md
#	modified:   crates/ui-egui/src/lib.rs
#	modified:   crates/ui-egui/src/media.rs
#	modified:   crates/ui-egui/src/show.rs
#
2026-10-21 06:07:58 +00:00
NetterSilverandClaude Haiku 5.5 e89a893fe1 Add angle adjust handles to partial-circle presets
Fixes #1094

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:04:04 +00:00
NetterSilverandClaude Haiku 5.5 72a9e4456e Fix scientific number format for subnormal values
Found while testing crates/model/src/numfmt.rs:204-211 (sci)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 03:34:30 +00:00
NetterSilverandClaude Haiku 5.5 309d9f5b2c Treat CRLF as a single paragraph break in text.set
Found while testing crates/engine/src/cmd/text.rs:1219 (set), via crates/model/src/text.rs:605 (TextBody::from_text)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 00:13:45 +00:00