Phase 7g.3: Desktop "Ask…" button#62
Merged
Merged
Conversation
Adds a natural-language → SQL composer to the Tauri desktop app's
query editor. Click "Ask…", type a question (e.g. "How many users
are over 30?"), submit, generated SQL drops into the editor textarea
for the user to review + Run themselves. Same engine surface the
REPL's `.ask` command uses (Phase 7g.2), with a Tauri command bridge.
## UX
The composer is a slide-in panel above the editor surface, opened
by an "Ask…" button on the editor toolbar (sits next to "Run" with
a subtle accent so it reads as a new feature without shouting).
Inside the panel: a question textarea, a "Generate SQL" button, an
explanation slot, and a close button. Cmd/Ctrl+Enter submits; Esc
closes.
* Generated SQL replaces the editor contents — does NOT auto-run.
The user reviews and clicks Run when they're ready. Matches the
REPL's "confirm-and-run" UX one layer up.
* The model's one-sentence rationale shows in the explanation slot
(helpful for sanity-checking the generated query).
* Empty SQL response — when the model decides the schema can't
answer the question — surfaces the model's explanation in the
same slot rather than silently doing nothing or erasing the
editor.
* Errors (missing API key, network failure, API error) land in the
existing error slot.
* On successful generation, focus moves back to the editor textarea
so Cmd/Ctrl+Enter just runs.
## Backend (`ask_sql` Tauri command, ~30 LOC)
* Reads `AskConfig::from_env()` — `SQLRITE_LLM_API_KEY` etc.
Tauri inherits the parent shell's env on launch.
* Locks the app's `Database` (so the schema dump is consistent
with what the user would see if they ran `.tables`).
* Calls `sqlrite::ask::ask_with_database` — schema introspection +
cache-friendly prompt + sync HTTP POST happen entirely in the
Rust backend.
* **The API key never crosses into the webview.** That's the
security story — same reasoning as Q9's WASM JS-callback shape,
applied here as a natural side effect of how Tauri's command
bridge works. Webview only ever sees `{ sql, explanation }`.
* Returns a small `AskCommandResult` struct (deliberately doesn't
serialize token usage yet — no UI for it; future "show me the
cost of this call" panel can pick it up).
## Engine-side ergonomic re-exports (~10 LOC)
`src/ask/mod.rs` now re-exports the public sqlrite-ask types
(`AskConfig`, `AskResponse`, `AskError`, `Provider`, `AnthropicProvider`,
`CacheTtl`, `ProviderKind`, `Request`, `Response`, `Usage`) so any
crate that already depends on `sqlrite-engine` with the `ask` feature
(the Tauri app, future SDK adapters, library embedders) can reach
them via `use sqlrite::ask::AskConfig` without dragging `sqlrite-ask`
in as a separate Cargo dep just for type names. Keeps downstream
manifests slim.
## Frontend (~110 LOC of Svelte + ~85 LOC of CSS)
Single-file changes in `desktop/src/App.svelte` and `desktop/src/app.css`:
* New $state vars (`askVisible`, `askQuestion`, `askExplanation`,
`askInputRef`, `asking`) backing the composer.
* `onAskClick` (toggle), `onAskSubmit` (invoke `ask_sql`), `onAskKey`
(Cmd/Ctrl+Enter / Esc).
* `.ask-panel` / `.ask-header` / `.ask-actions` / `.ask-explanation`
/ `.ask-button` / `.ask-close` styles using existing CSS variables
so the composer fits the dark theme.
Frontend type-check (`svelte-check`) clean. svelte-check sees 0
errors / 0 warnings across 84 files.
## Tests
301 Rust tests pass on the workspace as before:
* 20 sqlrite-ask unit + 4 integration + 1 doctest
* 251 engine main + 5 doctests
* 12 binary tests
* 8 FFI
The desktop binary doesn't have unit tests (frontend behavior is
manual/E2E territory); the new Tauri command is exercised through
`cargo check -p sqlrite-desktop` for type correctness, and the UI
through `npm run check` for Svelte/TS correctness — both clean.
## Docs
* docs/desktop.md — `ask_sql` row added to the commands table; new
"Ask…" UX bullet in the layout section with the API-key note +
auto-execute caveat + Esc-to-close detail.
* docs/phase-7-plan.md — 7g.3 marked ✅ with the actually-shipped
surface.
* docs/roadmap.md — 7g bullet updated to reflect 7g.1/7g.2/7g.3
all done.
* README.md — Phase 7 checklist 7g.3 ticked.
cargo fmt clean. cargo check workspace clean (including
sqlrite-desktop). cargo test --workspace 301/301 pass. svelte-check
clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a natural-language → SQL composer to the Tauri desktop app's query editor. Click Ask…, type a question, submit, generated SQL drops into the editor textarea for the user to review + Run themselves.
Why the API key stays out of the webview
The
ask_sqlTauri command runs schema introspection + the LLM HTTP POST entirely in the Rust backend. The webview only ever sees{ sql, explanation }. Same security story as Q9's WASM JS-callback shape, achieved here as a natural side effect of how Tauri's command bridge works.What landed
sqlrite::ask(engine)sqlrite-asktypes (AskConfig,AskResponse,AskError,Provider, etc.) so downstream crates that already depend on the engine don't need a separatesqlrite-askdep just for type names.desktop/src-tauri/src/main.rsask_sqlTauri command (~30 LOC) — locksDatabase, readsAskConfig::from_env(), callssqlrite::ask::ask_with_database, returns{ sql, explanation }. Registered in the invoke handler.desktop/src/App.svelte$statevars + handlers (onAskClick,onAskSubmit,onAskKey) + composer markup (~110 LOC). Cmd/Ctrl+Enter submits, Esc closes.desktop/src/app.css.ask-panel,.ask-header,.ask-actions,.ask-explanation,.ask-button,.ask-closeusing existing CSS variables (~85 LOC).docs/desktop.md(commands table + UX bullet),docs/phase-7-plan.md(✅ 7g.3),docs/roadmap.md,README.md(Phase 7 checklist).Behavior details worth calling out
AskConfig::from_env()readsSQLRITE_LLM_API_KEYfrom the process env. Tauri inherits the parent shell's env when launched vianpm run tauri devor directly. Production app: users set the env var via system-level config or a future settings panel.Test plan
cargo fmt --all -- --check— cleancargo check --workspace --all-targets— clean (includingsqlrite-desktop)cargo check -p sqlrite-engine --no-default-features— minimal lib build still clean (ask feature opt-out works)cargo test --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs— 301 / 301 passnpm run check(svelte-check) — 0 errors / 0 warnings / 84 filesexport SQLRITE_LLM_API_KEY=...→cd desktop && npm run tauri dev→ New… → CREATE a small users table → click Ask… → "How many users are over 30?" → confirm SQL pastes into editor → Run → verify resultNext up after merge
release-pr.ymlatv0.1.20→ desktop installers (Linux deb / AppImage / rpm + macOS dmg + Windows msi/exe) all carry the Ask… button.conn.ask()method on the existing connection class plus anask_run()convenience wrapper. Parallelizable but small enough to do sequentially.asktool (after 7h lays the MCP server framework).🤖 Generated with Claude Code