Commit Graph
1670 Commits
Author SHA1 Message Date
Marco Arnulfo 8a75773988 Canvas: only the primary button drags a scroll bar thumb, the grab ends with the drag and follows a zoom mid-drag (#1157) 2026-10-23 19:12:35 +02:00
xc4pr 8a61586585 Design Ideas: store the Full-bleed band's opacity as PPTX does, so switching ideas still removes it after a save (#1201) 2026-10-23 19:08:09 +02:00
Marco Arnulfo 9a3db3840a Canvas: a scroll bar press decides thumb or track where it went down, and the thumb follows the grab point (#1157) 2026-10-23 19:04:30 +02:00
aiongg 5bdea79f5c CI: run the gate through craft-common-actions' shared gate.yml; FreeBSD nightly and on release (#1214)
ci.yml calls the shared workflow (cached apt packages, nextest, main-only cache saves, Cargo.lock check). FreeBSD and Windows ARM64 run nightly and on pushes to release instead of on pull requests. Depends on storytold/craft-common-actions#69.
2026-10-23 12:59:34 -04:00
xc4pr d86f7aed75 Theme: the field test only counts a fill or outline that is actually painted (#1204) 2026-10-23 18:52:55 +02: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
xc4pr 49f296cca1 Design Ideas: Full-bleed puts the band on the visible title, not a hidden one (#1201) 2026-10-23 18:25:46 +02:00
Marco Arnulfo f43d42d535 Canvas: scroll bars when the zoomed slide is larger than the window (fixes #1157) 2026-10-23 18:21:28 +02:00
xc4pr e126fad9a7 Design Ideas: decide whether an empty box shows from its resolved fill and outline (#1200) 2026-10-23 18:17:39 +02: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 de18659978 Pictures: Set Transparent Color refuses a spot that is already transparent (#1217) 2026-10-23 17:14:51 +02:00
Marco Arnulfo 58d746bc17 Shape Outline button sets only the colour; docs row under Revision history; tests anchor on the hex field's label and check the scheme slots 2026-10-23 17:07:21 +02:00
Marco Arnulfo 9eacd994bb Pictures: Set Transparent Color picks the colour by clicking the picture, as in PowerPoint (fixes #1217) 2026-10-23 17:06:58 +02:00
Marco Arnulfo 40f6f1756b Shape Fill and Shape Outline: the button applies the last colour picked from its menu (part of #1080) 2026-10-23 17:01:26 +02:00
MarekSpodBiedry 3a917bb4e0 Keep each window's Esc, G and auto-advance to its own All slides grid (#1130) 2026-10-23 16:08:15 +02:00
kinowerandClaude Opus 5.5 811758f791 UI: a triple click plays a video once, like a double click
The third click of a triple click is a click too; it no longer pauses the
video the first click started. The test also follows the video when a double
click opens the Format pane and moves it on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:20:57 +02:00
kinowerandClaude Opus 5.5 c3981daeb6 UI: a click the Eyedropper takes does not play a video
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 14:59:14 +02: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
kinowerandClaude Opus 5.5 300872fb7d UI: let the editor's media bar take its clicks; click a video to play it
A press on the play/pause bar under a selected audio or video reached the
editor canvas, which started a selection marquee there; the bar hid while the
marquee was dragging, so its buttons never got the click and media only played
in the slide show. The bar's buttons and background now sense drags too, as
the GIF play button does, so the press stays on the bar.

A click on a video in the editor now also plays or pauses it (it selects it as
before; dragging still only moves it, and a modifier key leaves the click to
the selection).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 14:08:47 +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
Brandon ThomasandClaude Opus 5.5 b47195feec Release 0.10.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.10.0
2026-10-22 15:37:20 -04: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
BFlat 4a1d27a227 Merge pull request #1158 from storytold/readme/app-icon
README: the app icon above the title
2026-10-22 18:01:23 +07:00
bflatastic 0b8a4cc23e README: the app icon above the title 2026-10-22 17:57:31 +07: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