The launcher now looks like the apps page on getartcraft.com: the site's colour tokens, square corners, 1 px hairlines instead of shadows, app cards in a shared-hairline grid with numbered tabs and "Photo*Craft*" wordmarks, mono upper-case labels and buttons, and square badges for status. Fonts are the site's own, embedded in the binary: Archivo (620, 118 % width) for headings and app names, Inter for body text and Geist Mono for labels, all variable fonts from google/fonts (OFL-1.1) set to the site's weights. The window has a custom title bar on Windows and Linux (drag to move, double-click to maximize, minimize/maximize/close, resize from the edges) and a transparent one on macOS with the traffic lights in the header. The MSI used to flash a progress bar and close. Double-clicked, it now opens a WixUI wizard (Welcome, Ready, progress, Finish with "Launch ArtCraft Launcher"), with art drawn by package.ps1 from the ArtCraft mark and the Craft apps' icons. Self-update still runs it with /passive. CI installs the WiX UI and Util extensions. The app icon is now the blue "A" mark, getartcraft.com's favicon. packaging/icons.sh runs on any OS: `cargo xtask icns` packs the .icns. Also: two clippy fixes in crates/native that newer clippy rejects, and refreshed README screenshots. Co-authored-by: Claude Opus 5.5 <[email protected]>
8.3 KiB
AGENTS.md: guide for AI agents and contributors
ArtCraft Launcher installs, updates and launches the Craft App Suite (PhotoCraft, LightCraft, FilmCraft, DesignCraft, VectorCraft, PdfCraft, EffectCraft, WordCraft, DeckCraft, GridCraft, CADCraft, SoundCraft). It is written in Rust only (no JavaScript or TypeScript): the desktop app is native egui/eframe, and the web build is the same Rust compiled to WebAssembly (trunk + wasm-bindgen). No Tauri, Electron or webview shells, ever.
The product name is always ArtCraft Launcher in user-facing text: UI, window titles, About,
tray menu, notifications, installers, release names, docs prose. The suite is the Craft App
Suite. Machine names stay lowercase: crates and binaries (artcraft-launcher,
artcraft-launcher-*), file names, and the app id ai.storyteller.artcraft-launcher. Note the
GitHub repo is storytold/craft-launcher, while every file and package is artcraft-launcher-*.
Standards and learnings shared across the Craft apps live in ../craftrules (read its
README.md and AGENTS.md). Contribute reusable learnings there, never code.
1. Workspace map
crates/
core artcraft-launcher-core pure and wasm-safe: the app catalog, versions, GitHub
release parsing, picking the right asset per platform
native artcraft-launcher-native desktop only: HTTP, detecting installed apps, download,
verify and install
ui-egui artcraft-launcher-ui-egui the shared egui UI (desktop and web)
apps/
artcraft-launcher desktop shell (eframe): window, tray icon, notifications
artcraft-launcher-web the same UI in the browser (trunk; catalog and downloads only)
xtask/ cargo xtask ci | wasm | version | ico
packaging/ macOS, Windows, Linux, FreeBSD and web packaging scripts
assets/app-icon/ the launcher's own icon (master SVG, renders, tray icons)
assets/app-icons/ the Craft apps' icons, copied from each app's repo
assets/fonts/ Archivo, Inter, Geist Mono (OFL-1.1): getartcraft.com's faces
Layering: core depends on no other workspace crate and must build for wasm32-unknown-unknown.
native may use core. ui-egui may use core (and native only behind
cfg(not(target_arch = "wasm32")) or through a trait the desktop shell implements). Nothing below
the apps opens windows, tray icons or notifications.
2. Golden rules
Never crash (outranks feature work)
A bad network response, a malformed release, a half-downloaded file, a full disk or a corrupt
settings file must produce an error the user can act on, never a panic. Shared standard:
../craftrules/standards/never-crash.md.
- Non-test code never panics. No
unwrap(),expect(),panic!,unreachable!,todo!orunimplemented!. Return the crate's error type and propagate with?. - Every production crate root (
lib.rs, each binary'smain.rs, andxtask) carries#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]. - No
unsafe. The workspace setsunsafe_code = "forbid". - Everything from the network is hostile. Use
get()instead of indexing, checked arithmetic for sizes and offsets, and cap downloads and JSON sizes.
Installing other people's software safely
- Download only from
https://github.com/storytold/<app>/releases(and GitHub's asset CDN). - Verify each download against the release's
SHA256SUMS.txtbefore running or installing it. - Never run an installer with elevated rights silently; tell the user what is about to happen.
- Never delete a user's files outside what the launcher itself installed.
Look and feel
The UI matches the apps page on getartcraft.com (artcraft-website-next in artcraft-services):
hard square corners, 1 px hairlines instead of shadows, Archivo (620, 118 % width) for headings
and app names, Inter for body text, upper-case Geist Mono labels and buttons, and the site's
colour tokens by name (theme::Tokens). Build new UI from widgets.rs; never hard-code a colour.
Other rules
- Assets need attribution. Every bundled asset gets a row in
ATTRIBUTION.md(path, title, author, source, license) in the same change. The ArtCraft name, logo and "A" mark indocs/brand/(the header logo and the tray icon) are trademarks, not MIT/Apache (docs/brand/LICENSE-brand.txt): use them exactly as supplied, never redraw or recolor them. - Icons are generated. The app icon and the tray icon are the supplied ArtCraft "A" mark from
docs/brand/(a trademark, not MIT/Apache); never edit the renders by hand. Runpackaging/icons.sh(needsresvg; runs on any OS) and commit the outputs.assets/app-icons/is copied verbatim from each app's repo (assets/app-icon/hicolor/128x128/apps/ai.storyteller.<app>.png); refresh it from there, never redraw. - Never break wasm.
cargo xtask wasmmust pass. File-system, process and OS code iscfg(not(target_arch = "wasm32")). - Tests are the gate. Release parsing and asset picking in
coreget unit tests with real GitHub release JSON fixtures (small, committed undercrates/core/tests/fixtures/). - When fetching anything, use a generic
ArtCraft-Launcher/<version>User-Agent and never put a person's name, email or other personal details in requests.
3. Before you finish a task
cargo xtask ci # fmt --check, clippy -D warnings, test, wasm check
Or piecemeal: cargo test -p <crate>, cargo clippy -p <crate> --all-targets -- -D warnings,
cargo xtask wasm. If you touched packaging: shellcheck packaging/*.sh packaging/*/*.sh packaging/linux/AppRun and actionlint.
4. Releasing
The version lives only in [workspace.package] version in the root Cargo.toml
(cargo xtask version, cargo xtask version set X.Y.Z[-pre]). Pushing to the release branch runs
.github/workflows/release.yml, which builds every platform and creates the draft GitHub Release
ArtCraft Launcher v with SHA256SUMS.txt. The canonical recipe is
../craftrules/release/playbook.md; cutting a release is ../craftrules/release/cut-a-release.md.
Release assets (keep these names exact; the launcher's own self-update and the README rely on them):
| Platform | Files |
|---|---|
| Windows | artcraft-launcher-<ver>-windows-{x64,x86,arm64}.msi, …-windows-{x64,x86,arm64}-portable.zip |
| macOS | artcraft-launcher-<ver>-macos-universal.dmg |
| Linux | artcraft-launcher-<ver>-linux-{x86_64,aarch64}.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak} |
| FreeBSD | artcraft-launcher-<ver>-freebsd-x86_64.tar.gz |
| Web | artcraft-launcher-web-<ver>.zip |
Packaging facts the code relies on:
- The Windows MSI is per-user (
%LOCALAPPDATA%\Programs\ArtCraft Launcher, no UAC prompt). Double-clicked it opens a WixUI setup wizard (Welcome, Ready, progress, Finish with "Launch"), whose artpackage.ps1draws at build time; self-update runs it with/passive, no wizard. Its Start Menu shortcut sets AppUserModelIDai.storyteller.artcraft-launcher, which toast notifications need.portable.txtbeside the exe switches on portable mode (ArtCraftLauncherData\next to the exe). - The macOS bundle is
ArtCraft Launcher.app(executableContents/MacOS/ArtCraft Launcher), a normal Dock app that can also sit in the menu bar:LSUIElementmust never be set. - Linux: the tray uses StatusNotifierItem over D-Bus and notifications use
org.freedesktop.Notifications, both in pure Rust (no GTK, appindicator or libdbus). build.rsreadsARTCRAFT_LAUNCHER_BUILD_SHA/ARTCRAFT_LAUNCHER_BUILD_DATEand, on Windows, embedsassets/app-icon/artcraft-launcher.ico, failing the build whenARTCRAFT_LAUNCHER_REQUIRE_WINRES=1and it can't.
5. Parallel agents
- Use your own target dir (
CARGO_TARGET_DIR=target/agent-<name>) and edit only the files you own. - Keep every
Cargo.tomlvalid at all times: thecrates/*andapps/*globs mean one broken manifest breaks everyone's build. Write manifests to a temp file andmvthem into place. - If someone else's in-progress edit breaks the build, wait and retry; don't fix their files.