Skip to content

feat: add naque-tauri GUI frontend with keyring-backed LLM credentials - #2

Open
assafvayner wants to merge 1 commit into
mainfrom
feat/tauri-gui
Open

feat: add naque-tauri GUI frontend with keyring-backed LLM credentials#2
assafvayner wants to merge 1 commit into
mainfrom
feat/tauri-gui

Conversation

@assafvayner

Copy link
Copy Markdown
Owner

Summary

Adds a Tauri 2 (Svelte + Vite) desktop GUI as a second frontend over the existing headless naque::App engine. The TUI binary and GUI share assembly via naque::setup::build_app, so both frontends build the engine identically. Full TUI parity: chat, streaming, result tables with the <bytes> column convention, SQL approval (accept/reject/edit), path-grant modal, slash/db command palette, profile launcher picker, in-session switcher, status bar.

The GUI can't read shell env vars, so LLM API keys entered in Settings are stored in the system keyring (service naque, same store already used for DB passwords) and resolved by build_app at connect time. Env vars still win when present (TUI behavior is byte-identical); keyring only kicks in when no provider's env var is set.

Changes

Shared assembly (naque::setup)

  • Extract build_app into a pub module with a serde-serializable LaunchConfig + SetupError; both binaries call it.
  • Serde derives on IPC-bound types (AgentEvent, Usage, TranscriptEntry, StepStatus); serde/serde_json moved to real deps.
  • New App accessors (profile_name, max_iterations, iteration, engine); list_profiles/list_environments made public.

naque-tauri crate (crates/naque-tauri/)

  • pump.rs — owns App, mirrors the TUI event_loop over IPC: 250ms heartbeat re-emits state during a turn so the GUI stays live during LLM latency; streams agent_event + authoritative state snapshots; id-based approval reply map; queued submissions drain on turn finish.
  • commands.rs — submit, cancel, approvals, profiles, connect, launcher, and LLM credential management (llm_status / set_llm_credentials / clear_llm_credentials).
  • ipc.rsAppState, StateSnapshot (with spinner tick), DTOs.
  • cli.rs — clap args → LaunchConfig.

LLM credentials (naque-profile/secrets.rs)

  • resolve_api_key (env → keyring), set_api_key/clear_api_key writers, conventional keyring accounts per provider.
  • In-process cache so the OS keychain is read at most once per account per session (avoids the repeated macOS Keychain access prompts — the connect flow probes the keyring several times).
  • detect_provider scans env first (unchanged TUI behavior), then keyring.
  • Enable keyring platform-native backends in the workspace dep — v3 has no default backend; without features it silently falls back to a non-persistent in-memory mock (the original cause of saved tokens vanishing).

Frontend (crates/naque-tauri/ui/)

  • App.svelte — launcher picker (profiles/envs/URL), LLM provider settings panel (key entry → keyring), streaming chat transcript, result tables with byte-column formatting, SQL approval modal (accept/reject/edit), path-grant modal, slash command autocomplete, in-session profile switcher, status bar with animated working indicator.
  • bindings.ts — typed IPC wrappers + event listeners.
  • format.ts — byte-size rendering + <bytes> prose convention + HTML escaping.

Build/dev

  • tauri / tauri-build added to [workspace.dependencies].
  • Generated icon derivatives and gen/schemas/ gitignored (regenerable via cargo tauri icon / tauri-build).

Build & run

npm run build --prefix crates/naque-tauri/ui
cargo build --release -p naque-tauri
./target/release/naque-gui

With connection flags (skip the launcher): naque-gui --url postgres://user@localhost/mydb or naque-gui myproj --env dev.

Verification

  • cargo +nightly fmt --all -- --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace --all-targets + --doc — 538 tests pass
  • npm run build (vite) — builds clean (a11y warnings only)
  • cargo build --release -p naque-tauri — links

Letting CI run the full suite.

Adds a Tauri 2 (Svelte + Vite) desktop GUI as a second frontend over the
existing headless naque::App engine, sharing assembly via naque::setup.

Shared assembly:
- Extract build_app into naque::setup as a pub mod with a serde-serializable
  LaunchConfig + SetupError enum; the TUI binary and GUI both call it.
- Add serde derives to IPC-bound types (AgentEvent, Usage, TranscriptEntry,
  StepStatus) and move serde/serde_json to real deps in naque's Cargo.toml.
- Add App accessors (profile_name, max_iterations, iteration, engine) and
  make list_profiles/list_environments public for the launcher/switcher.

naque-tauri crate (crates/naque-tauri):
- pump.rs: owns App, mirrors the TUI event_loop over IPC; 250ms heartbeat
  re-emits state during a turn so the GUI stays live during LLM latency;
  streams agent_event + authoritative state snapshots; id-based approval
  reply map; queued submissions drain on turn finish.
- commands.rs: submit, cancel_turn, approvals, profiles, connect, launcher,
  and LLM credential management (llm_status/set/clear).
- cli.rs: clap args -> LaunchConfig.
- ipc.rs: AppState, StateSnapshot (with spinner tick), DTOs.

LLM credentials for the GUI (can't read shell env vars):
- naque-profile/secrets.rs: resolve_api_key (env -> keyring), set/clear
  writers, conventional keyring accounts per provider, in-process cache so
  the OS keychain is read at most once per account per session (avoids
  repeated macOS Keychain prompts).
- detect_provider scans env first (unchanged TUI behavior), then keyring so
  a GUI launch picks up a key the user stored via Settings.
- build_app builds providers from the resolved key via Provider::new.
- Enable keyring platform-native backends in the workspace dep (v3 has no
  default backend; without features it silently uses a non-persistent mock).

Frontend (Svelte + Vite):
- App.svelte: launcher picker (profiles/envs/URL), LLM provider settings
  panel (key entry -> keyring), streaming chat transcript, result tables
  with byte-column formatting, SQL approval modal (accept/reject/edit),
  path-grant modal, slash command autocomplete, in-session profile switcher,
  status bar with animated working indicator.
- bindings.ts: typed IPC wrappers + event listeners.
- format.ts: byte-size rendering + <bytes> prose convention + HTML escaping.

Also refreshes AGENTS.md to reflect the shared setup module and workspace
layout. Generated Tauri icon derivatives and gen/schemas/ are gitignored
(regenerable via cargo tauri icon / tauri-build).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant