Desktop interface for GitNapse. Built with Tauri 2 + React 19/Vite
(src/) on top of a pure-Rust bridge crate; GitHub data flows through
gitnapse-server (HTTP) and local git/config/auth run in-process through the
gitnapse core SDK. The frontend never talks to GitHub or git directly.
desktop/
Cargo.toml cargo workspace (members: bridge, src-tauri)
bridge/ PURE Rust lib: all desktop logic, no tauri/webview
src/api/client.rs typed async wrapper over gitnapse-client
src/api/server.rs gitnapse-server sidecar lifecycle
src/auth.rs token status/store + OAuth device flow (core)
src/clone.rs git clone with clone://progress parsing
src/config.rs clone-dir preference (shared account config)
src/dto.rs bridge payloads + re-exported core/protocol DTOs
src/git.rs typed local git wrappers (gitnapse::git)
src-tauri/ THIN shell: commands only delegate to bridge
src/main.rs AppState (sidecar manager + shared API client)
src/commands/ auth, local, git, remote, platform
src/platforms/ per-OS: xdg-open / open / explorer + cmd start
tauri.conf.json window 1280x800, CSP, native window effects
capabilities/ core defaults + event + dialog grants
src/ React app (features, ui, styles)
Rule: bridge compiles and unit-tests without webview/system packages
(cargo test -p gitnapse-bridge), so the logic is verified in CI/containers.
src-tauri registers the frozen command surface from WORKSPACE.md §4 and
forwards arguments/events — nothing else.
- Remote GitHub data (search, repos, issues, PRs, releases, actions,
profile, notifications) goes through
gitnapse-serverover HTTP usinggitnapse-client. The app owns the sidecar lifecycle. - Local git + config + auth token store run in-process through the
gitnapsecore SDK (headless:default-features = false, TUI excluded).
- Binary resolution:
GITNAPSE_SERVER_BIN→ sibling of the app executable (Tauri sidecar location) →gitnapse-serverinPATH→ clear error. server_start/ServerManager::ensure_running():GET /healthwith retries; spawnsgitnapse-server --host 127.0.0.1 --port 8787when nothing answers, remembersowned = true.server_stop/ drop: kills only processes the app spawned; external servers are left running.- URL:
GITNAPSE_SERVER_URLoverrides the defaulthttp://127.0.0.1:8787.
The GitHub token lives in the core secure store (same ~/.config/GitNapse
used by the CLI/TUI). Both paths converge: auth_set_token writes it
in-process, api_set_token writes it through the server, and the server
re-reads the store at runtime. OAuth device flow runs in-process, step-wise
(auth_login_begin / auth_login_poll), no TTY.
- Node 24+ (LTS) and Rust 1.85+ (edition 2024).
- Linux system packages for Tauri (Ubuntu/Debian):
sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev- The
gitnapseandapirepos must live next to this repo (../gitnapse,../api): the crates are wired withpath =dependencies.
# bridge: pure Rust, no webview packages needed (CI-safe)
cargo fmt --all
cargo clippy -p gitnapse-bridge --all-targets -- -D warnings
cargo test -p gitnapse-bridge
# Tauri shell (needs the system packages above)
cd src-tauri && cargo check # or: npm run tauri buildFrontend (browser preview with mocked commands, no Rust needed):
npm install
npm run dev # http://localhost:1420 — MOCKS, no real GitHub dataThe simplest path (Linux; works even without a system webkit — it bootstraps a
private webkit2gtk-4.1 under ~/.cache and, if the GitHub CLI is signed in,
reuses its token):
./run.shFlags: --build (release bundle), --gpu (full WebKit acceleration),
--compat (software rendering fallback), --no-server,
--no-github-token, --help. On Arch, sudo pacman -S webkit2gtk-4.1 gives
the native path instead of the private bootstrap.
Performance: the first run downloads webkit (~40 MB) and compiles the Rust
workspace (minutes); later runs reuse both and start in seconds. By default it
disables only WebKit's DMA-BUF renderer, which avoids a Wayland protocol crash
while keeping accelerated compositing. Use --gpu for full acceleration or
--compat if you see a blank frame. Ctrl+C shuts down the app, its sidecar and
the dev server.
The real path is desktop → bridge → gitnapse-server (HTTP) → gitnapse core.
Every remote command ensures the managed gitnapse-server is running
(spawns and health-checks it) before it talks to GitHub, and the app owns the
sidecar lifecycle — you do not start a server by hand.
npm run server:dev # builds ../api and stages gitnapse-server next to the app
npm run tauri:dev # real desktop window against live GitHub dataserver:dev (scripts/dev-server.sh) puts the binary where the bridge looks
first (sibling of the executable). Alternatively set GITNAPSE_SERVER_BIN=/path/to/gitnapse-server
or have gitnapse-server on PATH.
Authentication (single source of truth = the core secure store in
~/.config/GitNapse):
- paste a personal access token in Settings → Authentication, or
- run the OAuth device flow there, or
- export
GITHUB_TOKENin the shell that launches the app (env token).
The app activates the token on the running server automatically (and recycles it after a device-flow sign-in), so remote data starts working without a manual restart.
npm run tauri:build # release bundle (stages the release server too)npm run tauri build produces an app that resolves gitnapse-server from
GITNAPSE_SERVER_BIN or PATH. To make the bundle self-sufficient, stage the
server binary and enable the Tauri sidecar:
./scripts/build-sidecar.sh # builds ../api and stages binaries/gitnapse-server-<target-triple>Then add "externalBin": ["binaries/gitnapse-server"] to the bundle section
of src-tauri/tauri.conf.json (kept off by default so builds work without the
API repo checked out next door).
Invoke args are camelCase in JS; payloads are snake_case.
- Local/auth (
commands/auth.rs):auth_status,auth_set_token,auth_clear_token,auth_login_begin,auth_login_poll - Local/clone (
commands/local.rs):clone_dir,set_clone_dir,clone_repo(+clone://progressevents{ phase, message, percent? }) - Local/git (
commands/git.rs):git_repo_info,git_status,git_log,git_diff({kind, path?, rev?, from?, to?}),git_stage,git_unstage,git_discard,git_commit,git_push,git_pull,git_fetch,git_branches,git_checkout,git_branch_create,git_branch_delete,git_merge,git_reset,git_stash_list,git_stash_push,git_stash_pop,git_stash_drop,git_tags,git_tag_create,git_tag_delete,git_remotes,git_remote_add,git_remote_remove,git_remote_rename - Remote (
commands/remote.rs): server (server_status,server_start,server_stop), API auth (api_auth_status,api_set_token,api_clear_token), user/profile/activity (8), search (3), repos (8), issues (7), pull requests (12), releases/actions/repos (5) - Platform (
commands/platform.rs):open_in_file_manager,open_external
Window chrome uses Tauri native effects (mica on Windows 11,
underWindowBackground on macOS) via windowEffects; Linux has no native
vibrancy and falls back to the CSS glass. No macOSPrivateApi (public APIs
only). In-page glass surfaces stay CSS backdrop-filter.
See ROADMAP.md for the app-specific roadmap and docs/DESIGN.md for the
glass audit lines.