Commit Graph
38 Commits
Author SHA1 Message Date
Brandon Thomas e8e36be177 Merge pull request #894 from kk376/fix/apply-to-all-master-background 2026-10-23 17:09:50 -04: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
Kushagra Kumar 0a0ad04f47 fix(design): ensure Apply to All clears layout overrides when slides and masters match 2026-10-18 23:11:24 +05:30
Kushagra Kumar 74bc27f27e fix(design): preserve master and layout backgrounds on reset and copy (#891) 2026-10-18 22:28:15 +05:30
Kushagra Kumar 432016fc96 Propagate Format Background Apply to All to slide masters (#891) 2026-10-17 00:06:26 +05:30
Brandon ThomasandClaude Opus 5.5 67e1ab6373 Merge PR #438 from albayazid: Model: an exhausted id space refuses growth instead of duplicating ids (#407)
Conflicts resolved onto main's newer shape, and the PR's policy applied to
every alloc_id/add_media/new_slide call site main has grown since: commands
answer bad(cmd, NO_ID_SPACE) (slide.reuse, insert.zoom, SmartArt, photo
album, review.accept, handout/notes masters, master placeholders,
background/artistic/compress pictures, transition sound, ink to shape,
paste special, file.new templates); reid/bring_media/renumber/regenerate
and compare::adopt/accept are fallible; outline import keeps what fit.
Tests use expect() on the now-Option results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:24:26 -04:00
Brandon Thomas 081c9fdc5e Merge the landing branch into land-r7
# Conflicts:
#	apps/deckcraft-cli/src/main.rs
#	apps/deckcraft-cli/tests/cli.rs
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/text.rs
#	crates/engine/src/lib.rs
#	crates/engine/src/tests.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/textures.rs
2026-10-14 15:34:15 -04:00
Brandon ThomasandClaude Opus 5.5 0dfc9ccf6f Fix up #247: route main's other file access through the checked io layer
design.browseTheme reads its path with io::read_file, and File › Info sizes the file with
io::file_len (which checks the read root), so commands_do_not_touch_the_file_system_themselves
passes. spell.rs is allow-listed beside recovery.rs: it touches only the app's own dictionary
folders. main's render_slide MCP tool refuses a `path` argument (#371), so the root tests
render to a file through call_app ui.render instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:27:46 -04:00
Brandon Thomas 91eb9d8884 Merge the landing branch into land-r6
# Conflicts:
#	crates/engine/src/tests.rs
#	crates/engine/src/tools.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/panes.rs
#	crates/ui-egui/src/ribbon.rs
#	crates/ui-egui/src/status.rs
#	crates/ui-egui/src/textures.rs
#	crates/ui-egui/src/thumbs.rs
2026-10-14 15:14:21 -04:00
Brandon Thomas f8949f28cd Merge PR #692 from NikitkaRa: UI: Format Background picture fill sets transparency and tiling
# Conflicts:
#	crates/engine/src/cmd/design.rs
#	crates/ui-egui/src/panes.rs
2026-10-14 14:55:58 -04:00
Brandon Thomas 8587be6af9 Merge PR #542 from NikitkaRa: Design: Variants gallery with four colour variations of the theme
# Conflicts:
#	crates/engine/src/cmd/design.rs
#	crates/engine/src/tests.rs
2026-10-14 14:37:07 -04:00
Brandon Thomas b590cb0b0b Merge PR #303 from hoblin: deckcraft-cli help explains the CLI to an agent
# Conflicts:
#	apps/deckcraft-cli/src/main.rs
#	crates/engine/src/cmd/design.rs
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/format.rs
#	crates/engine/src/tests.rs
2026-10-14 14:36:13 -04:00
Brandon Thomas a4fa978903 Merge PR #703 from NikitkaRa: UI: Slide Master Preserve and Title or Footers placeholders
# Conflicts:
#	crates/engine/src/cmd/design.rs
#	crates/ui-egui/src/ribbon.rs
2026-10-14 14:06:42 -04:00
Brandon Thomas 7266b30018 Merge PR #698 from NikitkaRa: UI: Design Effects applies a theme effect scheme
# Conflicts:
#	crates/engine/src/cmd/design.rs
#	crates/ui-egui/src/ribbon.rs
2026-10-14 14:04:24 -04:00
Brandon Thomas b3c9cc099c Merge the PPTX writer, notes/handout master, PDF and slide-show PRs integrated on land-r3 (#518 #531 #535 #533 #572 #573 #577 #580 #583 #592 #685 #528 #483 #490 #441 #671 #679 #649 #656 #660 #664 #224)
# Conflicts:
#	crates/engine/src/tests.rs
#	crates/model/src/lib.rs
#	crates/pptx/src/write/mod.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/thumbs.rs
2026-10-14 13:56:41 -04:00
Brandon Thomas f52bcfa1c8 Merge PR #530 from NikitkaRa: Design: Browse for Themes from a .thmx, .pptx or .potx file
# Conflicts:
#	crates/ui-egui/src/lib.rs
2026-10-14 13:36:45 -04:00
Brandon Thomas 62fe8571f6 Merge PR #687 from NikitkaRa: UI: Create New Theme Fonts dialog edits the theme in one step
# Conflicts:
#	crates/engine/src/cmd/design.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-14 13:33:36 -04:00
Brandon Thomas 63c32b89b8 Merge PR #573 from NikitkaRa: Add the Notes Master view (stacked on #572 / #533)
# Conflicts:
#	crates/pptx/src/write/mod.rs
2026-10-14 13:29:52 -04:00
Brandon Thomas 875df7674c Merge PR #572 from NikitkaRa: Add the Handout Master view (stacked on #533)
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-14 13:29:33 -04:00
Brandon Thomas d534343d6e Merge PR #683 from NikitkaRa: UI: Create New Theme Colors dialog edits the theme in one step
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
2026-10-14 13:26:11 -04:00
Brandon Thomas 2ae8fe1839 Merge PR #201 from justinjohn0306: Slide Size: choose Portrait or Landscape (#198) 2026-10-14 12:57:20 -04:00
Nikita RakitinandCursor fa4b80ed65 UI: Slide Master Preserve and Title or Footers placeholders
Preserve the open master or layout, and turn its title and footer
placeholders on or off. Each change is one undo step, and a value that
is already set is not.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 06:40:11 +03:00
Nikita RakitinandCursor 26d8bfdd3a UI: Design Effects applies a theme effect scheme
design.effects replaces the theme's format scheme (fills, lines and
effects) with DeckCraft, Soft, Crisp, Lift, Glow or Plain. Colours and
fonts stay. The scheme already in use is not an undo step.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 06:21:07 +03:00
Nikita RakitinandCursor 1e6292306a UI: Format Background picture fill sets transparency and tiling
A picture background can set transparency, tile, offset, scale,
alignment and mirror. A solid fill can set transparency. Apply to All
copies the current slide's background, including a picture, onto every
slide. Unchanged options are not an undo step.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 06:08:40 +03:00
Nikita RakitinandCursor 0fcaabc370 UI: Create New Theme Fonts dialog edits the theme in one step
Design › Fonts › Customize Fonts… opens Create New Theme Fonts.
Empty design.fonts params open the dialog; a name and heading or body
faces apply to the open master in one undo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 05:45:53 +03:00
Nikita RakitinandCursor 65e465a611 UI: Create New Theme Colors dialog edits the theme in one step
Design › Colors › Customize Colors… opens the dialog when design.colors
is called with no name and no colours. Save applies that scheme name and
the twelve theme colours together, as one undo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 05:24:47 +03:00
Nikita Rakitin 443fe300d0 Add the Notes Master view
View ▸ Notes Master edits the notes master on a notes-size page (creating
PowerPoint's default placeholders if the deck has none); its tab toggles
header, date, slide image, body, footer and page number, sets the
background and closes the view. The PPTX writer now writes an edited
notes master as it is, even in a deck without notes. Stacked on #572.
2026-10-14 00:31:14 +03:00
Nikita Rakitin bba398f60f Add the Handout Master view
View ▸ Handout Master opens the handout master (creating one with header,
date, footer and page-number placeholders if the deck has none) on a page
at the notes size, with a dashed preview of where 1-9 slides go. Its
contextual tab toggles the placeholders, sets the background and closes
the view. Stacked on #533, which reads and writes the handout master.
2026-10-14 00:25:38 +03:00
Nikita RakitinandCursor 5a060c8b48 Design: Variants gallery with four colour variations of the theme
PowerPoint's Design tab shows the current theme in four variants. The
Variants group had only the Colors and Fonts menus. theme_variants builds
four colour schemes from the theme's built-in colours (as designed, two
accent rotations, and a dark variant with light and dark swapped), so
switching between variants doesn't drift. design.variant applies one to
the master; design.variants reports them and which is applied. The
Design tab shows them as a gallery before Colors and Fonts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 23:22:31 +03:00
Nikita RakitinandCursor 30c2fe4b6d Design: Browse for Themes from a .thmx, .pptx or .potx file
deckcraft_pptx::import_theme reads the theme of a .thmx (its main document is
a theme part) or of a presentation's first master, with the media its format
scheme uses. design.browseTheme {path | name, data} applies it to the current
master as one undo step, giving the theme's pictures ids of this deck.

Design tab: Browse for Themes… next to the theme gallery and in the Slide
Master Themes menu; the desktop picker offers Office Theme (.thmx) and
presentations. Ribbon small buttons expose their label to accessibility (same
change as #512). Parity row Browse themes M → D.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 23:04:41 +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
Brandon ThomasandClaude Opus 5.5 4d13652f0f Pictures: duotone recolours, and layout/slide colour map overrides apply (#269)
Theme background pictures (Office 2007-era themes such as Treck) carry an
a:duotone recolour in phClr; DeckCraft read it but drew the picture
unchanged, so those backgrounds came out grey. The painter now resolves the
duotone's colours against the slide's colours and placeholder colour, and
the decoded-image cache applies them (luminance picks a colour between dark
and light) and keys on them.

Layouts and slides can replace the master's colour map
(p:clrMapOvr/a:overrideClrMapping, e.g. dark section layouts mapping bg1 to
dk1). They were read as the master's map; now Layout and Slide carry an
optional colour map, resolve::Ctx uses slide > layout > master, and the
writer keeps them.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 17:35:52 -04:00
Yevhenii Hurin 717e855cec deckcraft-cli help explains the CLI to an agent
`deckcraft-cli help` and each subcommand's `--help` say what it does,
with a working example; `--help` never runs the subcommand. `describe`
spells out a command's parameters and what it acts on, `commands` lists
one command per line, and the MCP instructions say what format.* acts on.

[claude-opus-5-5] on behalf of hoblin
2026-10-12 23:41:42 +03:00
justinjohn0306andClaude Opus 5.5 1601fd250a Slide Size: choose Portrait or Landscape (#198)
The Slide Size dialog gets an Orientation choice that swaps width and
height, and design.slideSize takes orientation: portrait|landscape so a
preset can be made portrait from the CLI and MCP too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:39:40 +05:30
d556ff98b5 Fields: date fields show today's date in their own format (#36)
Every datetime field showed the deck-wide fixed date text instead of a date: empty when no slide
had a fixed date, and that text on every slide when one did. A fixed date is plain text in its
slide's placeholder; a field is an automatic date, so it now shows the local date and time in the
format of its type (ECMA-376 datetime1 to datetime13 as PowerPoint lists them for English,
datetime and datetimeFigureOut the short date). Numeric dates follow the run's language
(en-GB 09/10/2026, de-DE 09.10.2026...); formats with names stay English-only, so other languages
keep the field's saved text, as do unknown types and builds without a clock (wasm). The saved
text is kept on save.

- text: datetime module (field types, formats, local clock via chrono, not on wasm);
  Fields::field_in passes the run's language.
- render: RenderOpts::now injects the clock (tests, deterministic output); the PDF text layer
  uses the same fields.
- engine: Header & Footer turns existing date placeholders into fixed text or back into a
  field; new date fields are saved with the date they show (local, not UTC).

Co-authored-by: lloyd <lloyd@stackworx.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 06:25:32 -04:00
Brandon ThomasandClaude Opus 5.5 ae9d525bce Rename SlideCraft to DeckCraft: crates, apps, binaries, app id, file extension (.deckcraft; .slidecraft files still open), packaging, docs; ROADMAP alpha and parity estimates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:46:40 -04:00
Brandon ThomasandClaude Opus 5.5 56f31158c7 Gradient editor (type, direction, angle, stops with colour/position/transparency) for shape fill and Format Background; pattern backgrounds; painted dropdown chevrons
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:34:50 -04:00
Brandon ThomasandClaude Opus 5.5 6078b68e97 M0: workspace, model, geometry, fonts, text, renderer, engine, MCP and CLI
Clean-room PowerPoint-class presentation engine in pure Rust:
- geom: units, transforms and 150+ re-derived preset shapes with adjust handles
- color: theme slots and colour transforms
- model: presentations, masters, 11 layouts, placeholder inheritance, themes,
  text, tables, charts, transitions, animations, text edit operations
- fonts: font DB with craft-fonts, system fonts and open substitutes
- text: shaping, line breaking, bullets/numbering, alignment, autofit, caret hit tests
- render: vello_cpu slides with fills, gradients, pictures, effects, tables, charts
- format: native .slidecraft (zip + JSON)
- engine: ~200 commands, undo/redo, clipboard, pointer tools, keys, sample deck
- mcp: MCP server (headless or bridged to the app); slidecraft-cli

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:27:20 -04:00