ashlar-rs 0.2.0

Build fast desktop and web apps in Rust: store, commands, UI, native, agent control
Documentation

ashlar

A lightweight, modern application framework for Rust, powered by egui.

CI crates.io docs.rs MSRV License: BSD-3-Clause

ashlar gives a desktop (and, for its headless core, web) application the parts every app rebuilds: documents with undo and autosave; commands that drive menus, shortcuts, a command palette, a CLI and an MCP server; background jobs; files and settings; native OS features; a component library on egui; and crash safety and telemetry. An idle ashlar app does no work at all.

Status: early (0.1). The API will change between minor versions until 1.0.

Why ashlar

  • Everything is a command. Menus, shortcuts, the palette, the CLI, the control port and MCP all go through one registry and one execute chokepoint, so anything a user can do, a script or an AI agent can do too.
  • Never crashes. No unwrap, panic! or unsafe outside tests (enforced by lints and a CI gate). Panics in a view, a command or a job are caught; the session survives and unsaved work is recovered on the next launch.
  • Zero work when idle. Frames are drawn only when something asks for one: input, a document change, a finished job, a running animation. No polling. A focused text field stops its caret after 10 s, and a settled animation asks for nothing.
  • Fast where it matters. It scrolls a 1,000-row list in 0.10 ms of CPU per frame with list::Row, and a virtualized list in 0.035 ms whatever its length. It draws a static 52 KB markdown document in 0.03 ms. Allocations per frame and frame time are gated in CI. The numbers, the method and a comparison with another native Rust UI framework are in docs/benchmarks.md.
  • Built for agents. Every app takes --headless --snap out.png --tree tree.json (real pixels, no window). .ui scripts run in process on a virtual clock, and app.clock pauses and steps time. Golden screenshots, named scenes, a motion and jank gate, and record and replay come with it.
  • Motion and accessibility. Animations use springs that keep their velocity when retargeted, and follow the OS reduced-motion, accent and contrast settings. Every component reports AccessKit roles and states, and the gallery's accessibility trees are golden-tested.
  • Headless core. Documents, commands, jobs and files never touch the UI, so they run in tests, in a CLI, behind MCP and in the browser.
  • Small crates. Use the umbrella crate, or pick only the layers you need: each ashlar-* crate works on its own.

Quick start

cargo add ashlar-rs

The package is published as ashlar-rs; the library is still imported as ashlar.

use ashlar::prelude::*;

#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
struct Doc {
    notes: Vec<String>,
}

fn add(s: &mut Session<Doc>, p: &Value) -> Result<Value, CommandError> {
    let text = p.get("text").and_then(Value::as_str).unwrap_or("New note").to_string();
    s.edit("Add Note", |d| d.notes.push(text)); // one undo step per command
    Ok(json!(s.doc().notes.len()))
}

fn main() -> Result<(), ashlar::app::Error> {
    App::<Doc>::new("jot", env!("CARGO_PKG_VERSION"))
        .format(Format::new("example.jot", "jot 0.1"))
        .commands([cmd!("note.add", "Add Note", ["Edit"], Some("Mod+Shift+N"), [Param::string("text")], always, add)])
        .view(|ui, cx: &mut Cx<'_, Doc>| {
            for n in &cx.doc().notes {
                ui.label(n);
            }
            if ui.button("Add").clicked() {
                cx.dispatch("note.add", json!({}));
            }
        })
        .run() // undo/redo, save/open/autosave, menus, palette, crash guard, control port
}

App::cli() turns the same definition into a -cli binary with commands, exec, run, mcp and serve. --control <port> lets an agent drive the window. Try the examples:

cargo run -p ashlar-example-notes      # a small document app
cargo run -p ashlar-example-gallery    # every UI component

The umbrella crate's features:

Feature Default What it adds
accesskit on Screen readers and other assistive technology see the window (AccessKit)
headless on --headless on the app's command line: no window, .ui scripts, --snap, --tree
native-menu off The app's commands in the macOS and Windows menu bar
tray off A tray or status-bar icon with a menu (App::tray)
hotkey off System-wide shortcuts (App::global_shortcut)
telemetry off Crash, performance and usage reports to a server you host
cargo add ashlar-rs --features native-menu,telemetry

Binary size: the smallest app (examples/hello) is 13.6 MB stripped in a release build on macOS arm64. cargo build --profile release-small (a profile in this workspace: fat LTO, one codegen unit, opt-level = "s", stripped) brings it to 9.0 MB; copy the profile into your own Cargo.toml. default-features = false saves about 1 MB more, at the cost of accessibility and --headless. cargo xtask bench tracks the number as hello_stripped_bytes.

Crates

Every crate is published on crates.io under the same version, with its API docs at https://docs.rs/<crate>. Depend on a single layer when that's all you need, for example cargo add ashlar-store for documents and undo in a headless tool.

Crate What it is
ashlar-rs The umbrella crate (use ashlar::…): re-exports the layers below behind features
ashlar-app The runtime: App builder, frame scheduler, crash guard, logging, agent control
ashlar-ui UI components on egui: OKLCH theme tokens, motion, controls, overlays, tabs, forms, lists and trees, markdown, code and diff views, a chat kit, docking
ashlar-motion Springs, tweens and decay that keep velocity when retargeted; OKLab colour; reduced-motion rules (headless, wasm32)
ashlar-syntax Syntax highlighting for 18 languages, incremental and linear-time (headless, wasm32)
ashlar-diff Myers diffs with word-level emphasis, and a unified-patch reader and writer (headless, wasm32)
ashlar-store Documents: Arc snapshots, revisions, undo history, versioned formats, autosave
ashlar-command Commands: registry, typed params, one execute chokepoint, discovery
ashlar-jobs Background jobs: progress, cancel, panic isolation, apply on the UI thread
ashlar-fs Files and settings: app folders, atomic writes, keyed settings, recent files
ashlar-log Logging and crash safety: filters, rotating file, in-memory ring, panic hook
ashlar-native OS features behind one API: dialogs, menus, clipboard, power, notifications, secrets
ashlar-control The agent control port: a hardened JSON-lines server and client
ashlar-cli Standard CLI verbs over the command registry
ashlar-mcp, ashlar-mcp-http An MCP server generated from the command registry (stdio, or HTTP on loopback)
ashlar-agents Connect coding agents to an app: MCP config, skills and rules
ashlar-assistant An in-app AI assistant harness, independent of the model provider
ashlar-integrations Code-signed tool servers installed from a catalog and run over MCP
ashlar-debug Drive a running app's UI from the shell: snapshot, click, wait, screenshot
ashlar-docs Documentation for agents, embedded at build time
ashlar-update Update checks against a releases API
ashlar-print Send a PDF to the system print spooler (CUPS, Windows)
ashlar-i18n Translations: JSON catalogs, plural rules, number formatting, locale negotiation
ashlar-obfuscate Compile-time string obfuscation
ashlar-telemetry, ashlar-trace, ashlar-trace-server, ashlar-trace-proto Self-hosted crash, performance and usage telemetry (docs)

How the crates stack is in docs/architecture.md.

Compatibility

Supported
Rust 1.95 or newer (edition 2024). CI tests on the latest stable and checks the MSRV.
egui / eframe 0.36, wgpu renderer
macOS Apple silicon and Intel (CI: macos-latest, arm64)
Windows 10 or newer, x86_64 (CI: windows-latest)
Linux x86_64, X11 and Wayland (CI: ubuntu-latest). Native dialogs use the XDG desktop portal.
Web wasm32-unknown-unknown for the headless core (commands, store, files, jobs, logging; checked in CI). The windowed runtime on the web is experimental: examples/web-hello builds in the gate, and App::canvas_id picks the canvas; a start failure shows a visible error next to it.

Linux build dependencies (Debian/Ubuntu names):

sudo apt-get install libgtk-3-dev libxdo-dev libxkbcommon-dev libdbus-1-dev

Native features differ by platform. Where one isn't available, the call returns NativeError::Unsupported and nothing panics. Some highlights (the full matrix is in crates/native):

Feature macOS Windows Linux Web
File dialogs yes yes yes (portal) open only
Native menu bar yes yes in-window menu in-window menu
Clipboard (text, image) yes yes yes no
Notifications bundled apps no yes (D-Bus) no
Secrets (OS keychain) Keychain Credential Manager Secret Service no
Printing CUPS Windows spooler CUPS no

Built on egui, eframe, wgpu, rfd, muda and rustls.

Built with ashlar

  • bay and bay-effects by Marl Studio are built on top of ashlar.

Building something with ashlar? Open a pull request to add it here.

Documentation

The guide, developer docs and changelog are at ashlar.himanshuat.com (llms-full.txt for AI agents).

Contributing

Contributions are welcome. Read CONTRIBUTING.md first. Every change must pass cargo xtask ci. Please follow the Code of Conduct, and report security issues privately as described in SECURITY.md.

License

ashlar is licensed under the BSD 3-Clause License. Unless you say otherwise, any contribution you submit for inclusion in ashlar is licensed the same way.