Skip to content
crmnePublic

About

egui on rails: the shared foundation for native Rust apps built with egui

Topics

Resources

Stars

173 stars

Watchers

1 watching

Forks

Repository files navigation

fastframe

egui on rails. Documentation: fastframe.dev.

fastframe is the shared foundation of a family of native desktop apps built with Rust and egui: ZapFast, Spotifast, Solco, TonePush, and the Chat with Work local agent. Each piece here was written for one of those apps first and moved here once another app needed it.

Principles

  • Extract from working apps, never design up front. Code arrives here after it has shipped in an app, not before.
  • Shared, then it moves. A piece moves in once at least two apps have it, or once one app has shipped it and the maintainer judges it general enough for the others.
  • Small crates, one workspace. Each piece is its own fastframe-* crate, so an app takes only what it uses.
  • Good defaults, few knobs. A crate does the right thing with no configuration. Apps keep their own decisions (branding, layout, product choices); fastframe does not make them.
  • No telemetry, no hosted services. Nothing here phones home or depends on a server run by us.
  • Fix upstream. Bugs in egui, winit and friends are fixed in those projects, not carried here as patches forever.

Crates

Crate What it does
fastframe-text Follows the desktop's font rendering settings (hinting, antialiasing, sub-pixel positioning, text weight) in egui, and snaps hand-placed text to whole pixels.
fastframe-fonts Bundled Inter (tabular figures) or the platform's own interface face at the app's weights, and installed fonts for every script it lacks, aligned to its baseline.
fastframe-emoji Colour emoji in the platform's own style (Apple, Segoe UI, the desktop's) with a bundled fallback, in every egui text through an egui plugin or inline as selectable placeholders, drawn off the interface thread.
fastframe-icons Embedded SVG icon sets for egui: the icons! macro, a bytes loader that survives reduce_texture_memory, and the Lucide icons the apps share.
fastframe-theme JSON colour palettes, a catalogue loaded off the interface thread, the eight shared palettes, following the Omarchy desktop's theme with filesystem notifications, and revealing a change of colours from the middle of the window outwards.
fastframe-i18n Bundled gettext catalogs: a build-time PO compiler for build.rs, gettext/pgettext/ngettext lookups, system language detection, and the template update script.
fastframe-log Logs to stderr and a per-run file for bug reports, rewrites private targets, and records panics without their payload.
fastframe-tray A tray item with the app's own menu: StatusNotifier on Linux, the notification area on Windows, the menu bar on macOS, with native menus and Dock reopening across window lifecycles.
fastframe-dock An embedded Dock icon for unbundled macOS runs, and unread badges on the macOS Dock, compatible Linux launchers and the Windows taskbar.
fastframe-titlebar The title bar an app draws itself: a header that moves the window and answers a double-click as each platform does, room kept for the buttons (the traffic lights on macOS), and on Windows and Linux caption buttons in the desktop's layout and resizable edges.
fastframe-shell Keeps an app running without a window around eframe::run_native, starts hidden, reopens windows at their remembered size, place and zoom, brings back windows restored off-screen, asks niri to raise the window, and logs updates that freeze it.
fastframe-audio Audio output with the app's own renderer: a stream that gets no callbacks while paused, follows the default device, reopens after failures, and keeps a clock of what has played.
fastframe-scroll Scrolling that feels like the platform's: a 120-point wheel step, and Linux touchpad gestures that keep their speed, glide after the lift, and hold one axis.
fastframe-instance One running copy per user: a crash-safe lock, and a private channel a second launch hands its request over (show the window, open a link, any line the app understands).
fastframe-now-playing The desktop's media controls: what is playing, with artwork, in MPRIS, the Windows media overlay and the macOS Now Playing panel, and the media keys and buttons back.
fastframe-power The device's power, session and network state: energy saver, battery, display, lock, the network's reach and cost, sleep and wake with time to save first, EcoQoS in the background on Windows, and keeping audio out of App Nap on macOS.
fastframe-file Replaces a file without ever leaving it half written: a temporary file beside it, flushed, then moved over it, privately if asked, on a thread of its own so the window never waits for the disk.
fastframe-desktop Who the app is to the desktop and what it says: the desktop entry a Flatpak exports, whether the session is GNOME, a desktop setting read through the portal (no gsettings process), and macOS notification permission guarded by the real application bundle.
fastframe-notifications Native notifications for GUI apps, TUIs and daemons: bounded delivery, click actions, replacement, withdrawal, permissions and platform identity.
fastframe-update Self-update from GitHub releases: detects package-managed installs, verifies signed checksums, installs through a helper with rollback, keeps the handoff format older app versions use, moves renamed installations onto the new name with their launchers, and offers an opt-in pre-release channel.

Status

Early, extracted piece by piece. APIs will change while the first crates settle; nothing is on crates.io yet.

Using it

Depend on a git revision for now:

[dependencies]
fastframe-text = { git = "https://github.com/crmne/fastframe", rev = "<commit>" }

Pin a revision rather than a branch, and update it deliberately.

egui and winit forks

The apps build against forks of egui and winit that carry fixes waiting for upstream releases. A library cannot pin those: Cargo applies [patch] only in the root workspace of the app being built, so each app keeps its own [patch.crates-io] section. fastframe crates depend on the published egui versions, and they must compile against both the release and the apps' fork.

Every app on fastframe should use the revisions the apps use, so it gets the same fixes. Copy this into the app's root Cargo.toml:

[patch.crates-io]
ecolor = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
eframe = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
egui = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
egui-wgpu = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
egui-winit = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
egui_extras = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
egui_glow = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
emath = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
epaint = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
epaint_default_fonts = { git = "https://github.com/crmne/egui", rev = "ba6790fe7cf46e58e8d27ce1524cbfdee745e938" }
winit = { git = "https://github.com/crmne/winit", rev = "ed7caa9023f10b397f5b6ec8284a840cbd8a6f65" }

Patch every egui crate the app uses from the same revision, so they share one emath and epaint (add egui_kittest if the app's tests use it), and move egui and winit together: the egui revision is built against that winit, and moving one alone does not build. An app that compensated for the zoom shrink itself (a window_builder that scales the restored size) drops that code when it moves to egui ba6790fe or later, or the size is scaled twice. Cargo warns about a patch for a crate the app does not use (egui_extras, say); leave that line out. With egui ba6790fe or later (emilk/egui#8621), a window or menu is left out of the accessibility tree on the frame that sizes it, so a test that reads the tree right after opening one runs one more frame first.

What the forks carry, and where each patch stands upstream. Each patch goes upstream as a pull request and stays in the fork until an egui or winit release includes it; then the fork drops it.

Fix Fork Upstream
Right-to-left text shaped in its direction egui 6147de7b, 8e592348 emilk/egui#8577, open
No busy loop while waiting for a redraw egui f14640be emilk/egui#8398, merged, not yet released
No freeze when a Wayland window is hidden: frames paced by the compositor's callbacks egui 41ff9ddf emilk/egui#8631, open
The same, eframe's side of winit's Occluded egui 0b431145 emilk/egui#8660, a draft that waits for winit#4710
The xdg-shell suspended state reported as Occluded winit a51e41b2 rust-windowing/winit#4709 and #4710 (0.30), open
Paste and file drops together on Wayland: one data device for both (crmne/spotifast#614) winit 1a8306ad, fb8b24c3; egui 2ab31332 rust-windowing/winit#4729 (0.30), open; on winit's main branch, the clipboard pull request #4658 uses the same data device. emilk/egui#8657, a draft that waits for winit#4729
Each emoji one emoji wide (families, skin tones, keycaps, flags) egui 73e8b6c3 not proposed: egui's main branch already draws each of these one emoji wide through the system's colour emoji fonts, and the patch only changes the monochrome fonts an app opts into
No runaway resizing on Windows when a window moves between monitors of different scale winit f4fed12c on winit's main branch (39c4009c); the fork carries it for 0.30
macOS Quit through close requests, behind a feature winit a7b78b27 rust-windowing/winit#4692, open
A focus request for a widget not drawn yet no longer crashes accessibility on Windows and macOS egui 4c143c1d emilk/egui#8621, merged, not yet released
Windows starts on OpenGL drivers older than 3.3: a compatibility-profile fallback egui 57eb5714 emilk/egui#8655, open
A window keeps its size across restarts at any interface zoom egui 56e7ac3c emilk/egui#8656, open
A closed window leaves the screen on Touch Bar Macs winit ed7caa90 rust-windowing/winit#4728, a draft until confirmed on a Touch Bar Mac

On stock egui and winit, fastframe still builds and works, without these fixes.

A future forks.toml, with a generator that writes each app's patch section and a CI check that they agree, will keep those pins in one place. It does not exist yet; until then, this section has the current revisions, and a fastframe release that needs newer ones says so in its notes.

Development

cargo fmt --all --check
cargo clippy --locked --all-targets -- -D warnings
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-targets --all-features
RUSTDOCFLAGS='-D warnings' cargo doc --locked --all-features --no-deps

MIGRATION.md lists what each app replaces with each crate.

See AGENTS.md for the rules contributors and coding agents follow.

License

MIT. See LICENSE.

About

egui on rails: the shared foundation for native Rust apps built with egui

Topics

Resources

Stars

173 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages