App shell
replaces hand-rolled chrome
Custom title bar with drag regions and traffic lights, collapsible sidebar, status bar, route-based pages.
impl Render for PageRead the docsRouting, 24 themes, Fluent i18n, SQLite, keyring, tray, signed updates and a Cmd+K launcher are already wired. Run cargo run, then start deleting what you don't need.
Clone gpui-starter v0.3.0
git clone https://github.com/freeoxide/gpui-starter.git$ cargo run Compiling gpui-starter v0.3.0 Finished `dev` profile Running `target/debug/gpui-starter` ✓ lifecycle first_run = true ✓ single-instance lock acquired ✓ themes 24 loaded, watching ./themes ✓ i18n en, zh-CN ✓ sqlite app support dir ✓ tray menu bar registered window ready
$ cargo test test lifecycle::first_run ............... ok test config::migrate_v1_to_v3 ........... ok test updater::rejects_bad_signature ..... ok test i18n::all_keys_resolve_zh_cn ....... ok test query::stale_while_revalidate ...... ok test result: ok. 0 failed
$ cp themes/gruvbox.json themes/mine.json watcher themes/mine.json added ✓ theme reloaded no restart · 25 themes $ vim i18n/zh-CN/app.ftl watcher i18n/zh-CN/app.ftl changed ✓ fluent bundle rebuilt
illustrative output · your run logs the same subsystems
get startedBuilt on
Each one lives behind its own module. tray andtelemetry come out cleanly. Nothing else breaks.
replaces hand-rolled chrome
Custom title bar with drag regions and traffic lights, collapsible sidebar, status bar, route-based pages.
impl Render for PageRead the docsreplaces sqlite glue · dirs · keyring
rusqlite storage, versioned JSON config with a migration chain, OS keyring for secrets.
db.migrate_to(V3)Read the docsreplaces tauri plugins
Tray menu, native notifications with toast fallback, global hotkey, deep links through the instance guard.
gpui-starter://openRead the docsreplaces bespoke async state
TanStack-style caching for entities: stale-while-revalidate, retries, invalidation, infinite queries.
use_query("projects")Read the docsNot a hello-world
Sidebar routing, theme switching, form validation and the command palette are all live the first time you run it. Delete the pages you don't need.
Landing page for the shell, wired to gpui-query so you can see cached async data on first paint.
gpui-form inputs bound to a struct, koruma validation rules, errors localized through Fluent.
Theme switcher, locale picker and telemetry consent gate, all persisted to versioned JSON config.
Version, build metadata and update check, backed by the ed25519-signed updater.
Live subsystem health, active entities, theme metadata, and debug actions for support.
Native OS notifications with in-app toast fallback and a persistent inbox the user can reopen.
See it running
This cycles through five screens of it: sidebar routing, the theme picker, the Cmd+K launcher, a localized settings page, and form validation catching a bad email.
A minute with gpui-starter
Install, develop, test and ship with the same toolchain. Every step below is a real command from this repo.
$ cargo run Compiling gpui-starter v0.3.0 Finished `dev` profile Running `target/debug/gpui-starter` ✓ lifecycle first_run = true ✓ single-instance lock acquired ✓ themes 24 loaded, watching ./themes ✓ i18n en, zh-CN ✓ sqlite app support dir ✓ tray menu bar registered window ready
$ cp themes/gruvbox.json themes/mine.json watcher themes/mine.json added ✓ theme reloaded no restart · 25 themes $ vim i18n/zh-CN/app.ftl watcher i18n/zh-CN/app.ftl changed ✓ fluent bundle rebuilt
$ cargo test test lifecycle::first_run ............... ok test config::migrate_v1_to_v3 ........... ok test updater::rejects_bad_signature ..... ok test i18n::all_keys_resolve_zh_cn ....... ok test result: ok. 0 failed
$ bash scripts/macos-dev-app.sh bundling GPUI Starter.app codesign adhoc, hardened runtime ✓ target/app/GPUI Starter.app $ open "$(bash scripts/macos-dev-app.sh)"
$ just release 0.3.0 signing manifest.json (ed25519) uploading gh release v0.3.0 assets 3 platforms ✓ clients verify and update on next check
By the numbers
Batteries included
A page is an entity plus a route entry. Sidebar, Cmd+K launcher and deep links all read the same registry.
Routing docs// register once: sidebar, Cmd+K and deep links follow pub fn routes(cx: &mut App) -> Vec<Route> vec![ Route::new(Page::Home).icon("house"), Route::new(Page::Form).icon("file-text"), Route::new(Page::Settings).icon("settings"), Route::new(Page::Diagnostics).dev_only(), ]
24 themes ship as plain JSON in themes/. The watcher picks up a new file while the app runs.
Built on es-fluent, so message keys are typed at compile time. Validation errors resolve through the same bundle.
gpui-form binds inputs to a struct;koruma derives the rules. Messages come back localized, per field.
Cache policy per query, plus retries, invalidation and cancellation, all observable from an entity.
gpui-query docsIt uses the native OS backend where one exists and falls back to an in-app toast where it doesn't. Either way the message lands in an inbox the user can reopen.
Notifications docsTheming
Every theme is a plain JSON file in themes/. Drop one in while the app is running and it shows up in the switcher without a rebuild.
The unglamorous half
None of this is a differentiator on its own. All of it is a week you don't spend.
features/command_palette.rsCmd+K opens a floating palette with fuzzy search across every registered action. Full keyboard navigation.
services/undo_stack.rsCommand-pattern stack behind Cmd+Z / Cmd+Y. One trait to implement per action, works across any operation.
services/crash_report.rsPanic hooks capture stack traces and write structured reports to disk. Surface or upload them on next launch.
services/telemetry/Three modes (disabled, local-only, remote) behind an explicit consent gate. You decide what leaves the machine.
services/logging.rsFile-based logging through tracing-appender, with env-filter levels wired to the same config.
services/accessibility.rsAccessKit integration so the tree is exposed to VoiceOver, Narrator and Orca.
services/secure_storage.rsOS keyring for secrets: macOS Keychain, Windows Credential Manager, Linux Secret Service.
platform/ipc/URL-scheme handling forwarded through the single-instance guard, so a link reaches the running window.
services/first_run.rsStartup, shutdown and crash state machine, including first-run detection for onboarding.
services/connectivity.rsOnline/offline awareness so async work can back off instead of piling up retries.
services/tasks.rsA task registry over the tokio runtime, cancelled cleanly on shutdown.
services/error_surface.rsOne place errors are collected and rendered, instead of unwrap() scattered through the UI.
Comparison
| Capability | gpui-starter | bare GPUI | Tauri | Electron |
|---|---|---|---|---|
| Native GPU rendering | ✓ | ✓ | webview | webview |
| Sidebar + page routing | ✓ | — | — | — |
| Theme system, hot-reload | 24 built in | — | CSS | CSS |
| i18n with typed keys | ✓ Fluent | — | plugin | library |
| Command launcher (Cmd+K) | ✓ | — | — | — |
| Signed auto-updater | ✓ ed25519 | — | ✓ | ✓ |
| Keyring / secure storage | ✓ | — | plugin | plugin |
| Crash reports + diagnostics | ✓ | — | — | ✓ |
| Screen reader support | ✓ AccessKit | manual | ✓ | ✓ |
| Ships a browser engine | no | no | system | bundled |
Documentation
The docs explain how each subsystem is configured. The blog works through longer builds end to end. The FAQ answers the things people ask before they clone anything.
Lifecycle, entities, themes, i18n, tray, updater. What each subsystem is and how to configure it.
Procedural walkthroughs and framework comparisons. Title, context, code, command, done.
The questions that come up before the first build. One answer each, no preamble.
Clone it, delete what you don't need, and spend the week on your product instead of the update manifest.
cargo run