Commit Graph
919 Commits
Author SHA1 Message Date
Brandon Thomas 0f00a94c23 Merge pull request #906 from MarekSpodBiedry/issue-843 2026-10-23 17:09:50 -04:00
Brandon Thomas 5bebcd9075 Merge pull request #872 from kinower/fix/responsive-mouse-wheel-navigation 2026-10-23 17:09:49 -04:00
Brandon Thomas 998429fb9e Merge pull request #820 from Silvertree2010/fix/h1617-paste-from-html-a-newline-inside 2026-10-23 17:09:49 -04:00
Brandon Thomas f5e9cf5b0a Merge pull request #999 from Faabyy/fix/media-clip-per-slide-994 2026-10-23 17:09:41 -04:00
Brandon Thomas 9057bb7ef0 Merge pull request #1009 from MarekSpodBiedry/issue-1005 2026-10-23 17:09:40 -04:00
Brandon Thomas 6498809e29 Merge pull request #1161 from kbarni/faster-transition 2026-10-23 17:09:40 -04:00
Brandon Thomas eefb1ac380 Merge pull request #1211 from marcoarnulfo/fix/presenter-notes-bullets 2026-10-23 17:09:39 -04:00
Brandon Thomas ddeef2d9a6 Merge pull request #1228 from marcoarnulfo/feat/show-start-from 2026-10-23 17:09:39 -04:00
Brandon Thomas 1cb436bcbb Merge pull request #1235 from MarekSpodBiedry/issue-1229 2026-10-23 17:09:38 -04:00
Brandon Thomas e128a09caa Merge pull request #988 from Zetenypapp/fix/smart-duplicate-966
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-23 17:09:38 -04:00
Brandon Thomas 3084f5b5f4 Merge pull request #989 from Zetenypapp/fix/find-focus-951 2026-10-23 17:09:37 -04:00
Brandon Thomas 532d88cda1 Merge pull request #1003 from Faabyy/fix/resize-snaps-dragged-edge-961 2026-10-23 17:09:37 -04:00
Brandon Thomas e676b823f9 Merge pull request #1007 from MarekSpodBiedry/issue-971 2026-10-23 17:09:36 -04:00
Brandon Thomas 7d80f052fb Merge pull request #1064 from Silvertree2010/fix/h848-xml-text-escaping-writes-carriag 2026-10-23 17:09:24 -04:00
Brandon Thomas 86f6b365f3 Merge pull request #1145 from pick2510/fix/table-text-other-style 2026-10-23 17:09:24 -04:00
aiongg f001f37906 CI: one test binary per crate, nextest, line tables only (#1213)
* Build: line tables only for workspace crates, no debug info for dependencies

* Tests: one integration-test binary per crate

* xtask ci: nextest, doctests, test build without examples, one wasm check
2026-10-23 12:43:34 -04:00
MarekSpodBiedry ac0130d2d7 Keep the slide show in a window for "Browsed by an individual" (#1229) 2026-10-23 17:31:00 +02:00
Dylan M. Taylor 0f4fffcf6c Use chrono instead of hand-rolled UTC conversion (#1116)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the hand-rolled civil_from_days / civil copies and the hand-written
weekday / leap-year / days-in-month helpers with chrono. chrono is already a
workspace dependency (used by crates/text), so this reuses that entry and just
adds `chrono = { workspace = true }` to the engine and desktop app rather than
a second version.

- apps/deckcraft logging `timestamp` now formats through chrono (private
  civil_from_days deleted).
- engine cmd::design `civil` (days since 1970 -> date, used for comment
  timestamps in review.rs) uses chrono::NaiveDate.
- text datetime `DateTime::is_valid` / `weekday` use chrono::NaiveDate instead
  of a hand-rolled leap-year table and days-since-1970 weekday.

Output is byte-identical and pinned by the existing logging, design and
datetime-field tests; each converted helper keeps a proleptic-Gregorian
fallback for dates outside chrono's NaiveDate range.

Follow-up to storytold/cadcraft#1785

* text: make chrono available on wasm32

is_valid/weekday in crates/text/src/datetime.rs use chrono unconditionally,
but chrono was only declared under the non-wasm target, so the crate (and
engine/pdf/render/ui-egui depending on it) failed the wasm32-unknown-unknown
check in `cargo xtask ci`.

- crates/text: declare chrono unconditionally (wasm-safe `std` baseline),
  and keep `clock` in a separate cfg(not(target_arch="wasm32")) entry for
  now_local()'s local clock, which is already wasm-gated to return None.
- workspace: drop `clock` from the shared chrono entry so it is no longer
  forced onto wasm builds (engine uses chrono unconditionally too); `clock`
  is now requested per-crate only where the local clock is read.

* Never panic on a clock outside chrono's range; harden the civil fallback

logging.rs timestamp(): chrono's From<SystemTime> unwraps, so a clock past
year 262143 panicked inside the logger. Use the fallible DateTime::from_timestamp
and read an out-of-range or pre-epoch clock as the epoch, as the old saturating
code did. Years past 9999 are written with {:04} like before, because %Y adds a
'+'. Tests cover both cases.

cmd/design.rs civil_fallback(): make the hand-rolled fallback's year arithmetic
saturating so civil() cannot overflow or panic at i64::MAX/MIN (the engine rule
forbids crashes and unchecked arithmetic). Behaviour is unchanged for every real
input; a test exercises both extremes. The output year is formatted with {y:04}
by review.rs, so no %Y prefix issue.

crates/text/src/datetime.rs: checked after review; is_valid already limited the
year to 1..=9999 before this PR, weekday() is only reached for valid dates, and
all its arithmetic is checked/euclidean, so no change is needed.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* Make civil() exact for every i64 instead of saturating

The out-of-range fallback saturated z + 719_468, so civil(i64::MAX)
returned a date 719,468 days early. civil() now has chrono convert the day
within its 400-year Gregorian cycle and adds whole cycles back as years
(i128 for the extremes), so the hand-rolled fallback is gone. Exact results
for i64::MAX/MIN and both sides of chrono's range are tested against Hinnant's formula.

* time: preserve out-of-range years in the log timestamp instead of the epoch

`DateTime::from_timestamp(..).unwrap_or_default()` changed the existing output: a
clock set far ahead (an input the old hand-rolled civil-from-days converted)
collapsed to `1970-01-01`. Restore the old conversion domain with a cycle-based
helper: reduce by whole 400-year Gregorian cycles (146_097 days), convert the
in-range remainder with chrono, and carry the year back as i128. Only the year
leaves chrono's range; month/day/time stay exact. Pre-1970 still clamps to the
epoch as before. chrono stays the only calendar logic (no new features).

apps/deckcraft logging.rs `timestamp` (SystemTime, u64 secs), pinned via a
platform-safe `UNIX_EPOCH.checked_add`:
  u64::MAX/2 s → "292277026596-12-04T15:30:07.000Z" (was "1970-01-01..."),
  10e12 s → "318857-05-20T17:46:40.000Z", 253_402_300_800 s → "10000-01-01...".

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-23 11:20:42 -04:00
Dylan M. Taylor bdcb532608 packaging: [[ ]] tests, named positional parameters, explicit returns and a default case (SonarCloud S7688, S7679, S7682, S131) (#1212)
Use [[ ]] for conditional tests in the bash packaging scripts, give positional
parameters a name before use, end the shared helpers in common.sh with an explicit
return (bare return keeps the last command's status), and give the FreeBSD arch
case a no-op default. No behaviour change.
2026-10-23 11:16:44 -04:00
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 c0b405c42a Presenter View: speaker notes keep their bullets, numbers and formatting (fixes #1128) 2026-10-23 03:54:04 +02: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
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
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 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
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 e37267f548 Strip URI fragment before percent-decoding OPC targets
Found while testing crates/pptx/src/opc.rs:283-285 (resolve)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 18:36:13 +02:00
NetterSilverandClaude Haiku 5.5 18f5e17b36 Escape carriage return as &#13; in XML text content
Found while testing crates/pptx/src/xml.rs:363 (esc)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 14:54:55 +00:00
NetterSilverandClaude Haiku 5.5 65564cfc1b Reject invalid slide index params instead of using the selection
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>
2026-10-20 16:29:18 +02:00
Zetenypapp 0dc39b9efc Merge remote-tracking branch 'origin/main' into fix/smart-duplicate-966
# Conflicts:
#	ROADMAP.md
2026-10-19 21:33:38 +02:00
Zetenypapp 57d65faf4f Merge remote-tracking branch 'origin/main' into fix/find-focus-951
# Conflicts:
#	ROADMAP.md
2026-10-19 21:33:38 +02:00
Mexandskylar taylor-barrick f34f4121c8 Format: retain imported chart parts through native saves (#851)
* M2.1: preserve chart parts through native saves

Serialize retained chart relationships and payloads in native files.
Add native and headless file-command round-trip regressions.

Contribution on behalf of @MMexicann.

* Format: bound opaque chart parts as native ZIP entries

Address maintainer feedback on the existing PR. Preserve upstream work and document tested scope and validation limits.

* Model: track transient native chart part entries

Address maintainer feedback on the existing PR. Preserve upstream work and document tested scope and validation limits.

* PPTX: initialize native chart entry metadata

Address maintainer feedback on the existing PR. Preserve upstream work and document tested scope and validation limits.

* Engine: adapt chart native roundtrip fixtures

Address maintainer feedback on the existing PR. Preserve upstream work and document tested scope and validation limits.

* Docs: describe bounded chart sidecars and validation

Address maintainer feedback on the existing PR. Preserve upstream work and document tested scope and validation limits.

* Roadmap: record bounded chart persistence evidence

Address maintainer feedback on the existing PR. Preserve upstream work and document tested scope and validation limits.

---------

Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
2026-10-19 11:44:16 -06:00
MarekSpodBiedry 5ac14f2015 Test: modify the slide through Arc::make_mut (#1005) 2026-10-19 00:44:03 +02:00
MarekSpodBiedry 9b56f83929 Show: draw shapes stacked above a playing video over its frame (#1005) 2026-10-19 00:37:51 +02:00