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
2026-10-10 06:17:49 -04:00
2026-10-22 17:57:31 +07:00

ArtCraft

DeckCraft icon: a presentation screen with a bar chart on an orange tile

DeckCraft

Presentations and slide shows; an open-source, modern presentation and slide designer, built in pure Rust.

Build decks with themes, layouts, shapes, pictures, tables, charts, transitions and animations, open and save .pptx presentation files, and present them full screen. Native on macOS, Windows, Linux and FreeBSD, and in the browser. Every button is also a command that the CLI and AI agents (through MCP) can drive.

Status: early development License: MIT OR Apache-2.0 Written in Rust Platforms
Scanned by SonarCloud Reviewed by CodeRabbit

Join the ArtCraft community on Discord

DeckCraft on getartcraft.com · ArtCraft · All Crafting Apps

Note

ArtCraft is a community of artists from all walks of life. Painters, photographers, filmmakers, illustrators, designers, animators, hobbyists, and people who picked up a pencil last week. If you make things, you're one of us. Come say hi on Discord.

DeckCraft editing a slide: ribbon, slide thumbnails with sections, a process diagram on the canvas, notes and status bar

What works · Getting started · Agents, CLI and MCP · Roadmap · Downloads · The Crafting Apps · License

What works today

DeckCraft is in alpha, heading for beta (see the roadmap). Today it can:

  • Make decks: slide masters and eleven layouts with placeholders that inherit position and formatting, eight original themes with colour and font schemes, sections, notes, comments, headers and footers, custom slide sizes.
  • Draw: 150+ preset shapes with adjust handles, connectors that stay glued to shapes, freeform, curve and scribble tools, merge shapes (union, combine, fragment, intersect, subtract), gradient, picture and pattern fills, outlines with dashes and arrowheads, shadows, glow, soft edges and reflections.
  • Write: in-place text editing with full font and paragraph formatting, bullets and numbering, columns, autofit, vertical text, fields and hyperlinks.
  • Add content: pictures with crop and corrections, tables with styles, charts, SmartArt basics, audio and video (MP3, AAC, FLAC, ALAC, Ogg, Opus, WAV, AIFF; H.264, HEVC, VP9, AV1).
  • Present: transitions (including Morph), entrance/emphasis/exit and motion-path animations, by-paragraph builds, full-screen slide show, presenter view, pen, rehearse timings, media playback.
  • Exchange files: open and save .pptx presentations, export PDF (slides, notes pages, handouts with selectable text), PNG/JPEG, an animated GIF of the deck and outlines. Unsaved work is kept by AutoRecover.
  • Automate: 200+ commands with undo, all scriptable from the CLI, a JSON control channel and an MCP server for AI agents.
A selected shape with the Shape Format tab
Shape Format contextual tab and selection handles
The Format Shape pane editing a gradient fill
Format Shape pane: gradient type, direction and stops
Slide Sorter view with transitions under each slide
Slide Sorter with each slide's transition
DeckCraft in dark mode showing a chart slide
Dark mode, with a native chart

All eight slides of the Northern Lights sample deck
The built-in sample deck, rendered by deckcraft-cli render --sample --all

Getting started

PDF export uses the shared craft-pdf writer with import features disabled. See crates/pdf/README.md for the dependency and validation requirements.

git clone https://github.com/storytold/deckcraft
cd deckcraft
cargo run --release -p deckcraft-cli -- render --sample --all --scale 1 out/

On Linux, the build also needs the ALSA headers for slide-show sound: alsa-lib-devel on Fedora, libasound2-dev on Debian and Ubuntu, alsa-lib on Arch. Without them it stops at alsa-sys.

Fonts come from craft-fonts: clone it next to this repo (../craft-fonts) and local builds pick it up automatically; without it DeckCraft uses your system fonts.

Each GitHub release has ready-made builds, on Linux as an AppImage, a .deb, an .rpm and a tarball. On Gentoo, the community ::snakebyte overlay packages the Linux release as app-office/deckcraft-bin (not maintained by the DeckCraft team):

eselect repository add snakebyte git https://github.com/switch87/snakebyte-overlay.git
emaint sync -r snakebyte
echo 'app-office/deckcraft-bin ~amd64' >> /etc/portage/package.accept_keywords/deckcraft
emerge --ask app-office/deckcraft-bin

Logs

The desktop app writes its log records to standard error and to logs/deckcraft.log in the settings directory, beside ui.json and prefs.json (Linux ~/.config/deckcraft/logs/, or $XDG_CONFIG_HOME/deckcraft/logs/; macOS ~/Library/Application Support/DeckCraft/logs/; Windows %APPDATA%\DeckCraft\logs\). A start from a desktop menu or the Dock has no terminal, so this file is what to attach to a bug report: the crash guard's panic report, a failed crash-recovery save, Parts skipped on import and audio output problems land there. Each launch moves the previous log to deckcraft.1.log (and that one to deckcraft.2.log), so the log of a run that crashed survives the next start. The file stops growing at 16 MiB. --version writes no file.

Variable Effect
RUST_LOG Log levels for standard error and the log file. Default: info for DeckCraft's own crates, warn for everything else. env_logger-style directives replace that, e.g. RUST_LOG=debug, RUST_LOG=warn,deckcraft_pptx=trace or RUST_LOG=info,wgpu_core=warn; a directive ending in * covers every target starting with it (deckcraft*=debug).

The logger is apps/deckcraft/src/logging.rs; the realtime audio callback does not log. The web app logs to the browser console instead.

Agents, CLI and MCP

Every action in DeckCraft is a command with a stable id, so people, scripts and AI agents use the same verbs:

deckcraft-cli commands format.          # list commands
deckcraft-cli run --sample --cmd 'slide.new={"layout":"titleOnly","title":"Hello"}' --save hello.deckcraft
deckcraft-cli mcp                       # MCP server over stdio (headless)
deckcraft-cli mcp --connect 7990        # drive the running app (deckcraft --control 7990)
deckcraft-cli mcp --automation-read-root DIR --automation-write-root DIR   # confine the headless agent to folders

Roadmap

DeckCraft is in alpha. It covers 84% of the features tracked in docs/target-app-parity.md by breadth (92% of the core ones, measured) and is about 55% ready for real work across all tracked capabilities (estimated). See ROADMAP.md for the stage, numbers by dimension and what's next, docs/target-app-parity.md for the full assessment, docs/gaps.md for every known shortfall and docs/parity-checklist.md for the feature-by-feature checklist.

Downloads

Download DeckCraft from GitHub: the latest release has every build listed below, and all releases has earlier versions and their notes. <ver> in the file names is the version number, and SHA256SUMS.txt lists a checksum for every file.

Windows

Build Installer Portable
x64 (64-bit Intel/AMD) deckcraft-<ver>-windows-x64.msi deckcraft-<ver>-windows-x64-portable.zip
arm64 (Snapdragon and other ARM PCs) deckcraft-<ver>-windows-arm64.msi deckcraft-<ver>-windows-arm64-portable.zip
x86 (32-bit) deckcraft-<ver>-windows-x86.msi deckcraft-<ver>-windows-x86-portable.zip

Installers and executables are code-signed.

If the app doesn't open on Windows: the desktop app initializes only DirectX 12 by default. Letting wgpu also create an OpenGL instance can crash some graphics drivers (AMD's atio6axx.dll) before the window appears, so the app would flash in Task Manager and quit. WGPU_BACKEND overrides the default for troubleshooting (for example dx12 or vulkan). In PowerShell, from the folder containing the executable:

$env:WGPU_BACKEND = "vulkan"
& .\deckcraft.exe
Remove-Item Env:WGPU_BACKEND                     # restore the default for later launches

An explicit gl override can bring the driver crash back on affected systems. The macOS, Linux and web backend defaults are unchanged.

DirectX 12 shaders are compiled with FXC, which is part of Windows, so no dxcompiler.dll or dxil.dll is needed ("Parent device is lost" at start, #153). $env:WGPU_DX12_COMPILER = "dxc" (or "auto") selects wgpu's DXC handling again for troubleshooting.

macOS

Build File Notes
App, universal (Apple silicon + Intel) deckcraft-<ver>-macos-universal.dmg Signed and notarized
Command-line tool, universal deckcraft-cli-<ver>-macos-universal.zip Signed and notarized

Linux

Format x86_64 aarch64 (ARM64) Notes
AppImage deckcraft-<ver>-linux-x86_64.AppImage deckcraft-<ver>-linux-aarch64.AppImage Runs anywhere; updates itself with AppImageUpdate (.zsync files)
Flatpak deckcraft-<ver>-linux-x86_64.flatpak deckcraft-<ver>-linux-aarch64.flatpak Sandboxed; flatpak install --user <file>
Debian/Ubuntu deckcraft-<ver>-linux-x86_64.deb deckcraft-<ver>-linux-aarch64.deb
Fedora/RHEL/openSUSE deckcraft-<ver>-linux-x86_64.rpm deckcraft-<ver>-linux-aarch64.rpm
Tarball deckcraft-<ver>-linux-x86_64.tar.gz deckcraft-<ver>-linux-aarch64.tar.gz Unpack anywhere

RISC-V 64 (riscv64): deckcraft-<ver>-linux-riscv64.tar.gz, a tarball only (cross-compiled; needs glibc 2.39+, e.g. Ubuntu 24.04+).

FreeBSD

Build File
x86_64 deckcraft-<ver>-freebsd-x86_64.tar.gz

Web (WebAssembly)

Build File Notes
Static site deckcraft-web-<ver>.zip Runs in a modern browser; host it on any static server

The Crafting Apps

DeckCraft is one of the Crafting Apps: free, open-source creative tools from the ArtCraft team, each written from scratch in Rust and each able to stand on its own.

App What it's for Code Learn more
PhotoCraft Image editing: layers, masks, type and real PSD files GitHub Website
VectorCraft Vector illustration GitHub Website
FilmCraft Video editing, color and sound GitHub Website
LightCraft Photo library and raw development GitHub Website
PdfCraft Reading, organizing and protecting PDFs GitHub Website
EffectCraft Motion graphics and visual effects GitHub Website
DesignCraft Page layout and publishing GitHub Website
DeckCraft Presentations and slide shows · you are here GitHub Website

And ArtCraft itself, our AI image and video studio for artists who want real control.


Join the ArtCraft community on Discord

Come make things with us

Our Discord is where artists of every kind hang out: people who paint, shoot, draw, cut film, set type, and people still figuring out what they like to make. Share what you're working on, ask for help, tell us what's broken, or tell us what you wish these tools could do. Whatever your medium and however long you've been at it, you're welcome here.

discord.gg/artcraft · getartcraft.com · The Crafting Apps · DeckCraft

License and credits

DeckCraft is dual-licensed under MIT or Apache-2.0, at your option. Copyright (c) 2026 ArtCraft Team and the DeckCraft contributors. Required notices are in NOTICE.

Bundled fonts, icons, images and other assets keep their own open licenses; each one is listed with its author, source and license in ATTRIBUTION.md.

DeckCraft's themes, layouts, shape geometry, icons and sample decks are original work, drawn or generated in code; no proprietary artwork, fonts, themes or templates are used.

The ArtCraft name, wordmark and logos in docs/brand/ are trademarks of the ArtCraft Team and are not covered by this license. They may be used only unmodified, and only as part of this repository and DeckCraft, under docs/brand/LICENSE-brand.txt. Forks and modified versions must remove them.

ArtCraft
Made by the ArtCraft team and community.

S
Description
No description provided
Readme
48 MiB
Languages
Rust 99.5%
Shell 0.3%
PowerShell 0.1%