Skip to content

Phase 7g.3: Desktop "Ask…" button#62

Merged
joaoh82 merged 1 commit into
mainfrom
feat/desktop-ask-button
May 1, 2026
Merged

Phase 7g.3: Desktop "Ask…" button#62
joaoh82 merged 1 commit into
mainfrom
feat/desktop-ask-button

Conversation

@joaoh82
Copy link
Copy Markdown
Owner

@joaoh82 joaoh82 commented Apr 30, 2026

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.

Editor toolbar:  [ Ask… ]  [ Run ]
                    ↓ click
                 ┌──────────────────────────────────────────────────┐
                 │ Ask                  ⌘↵ submit · Esc close   [×] │
                 │ ┌────────────────────────────────────────────┐  │
                 │ │ How many users are over 30?                │  │
                 │ └────────────────────────────────────────────┘  │
                 │ Generated SQL replaces the editor —     [Generate SQL] │
                 │ review before running.                            │
                 └──────────────────────────────────────────────────┘
                                    ↓ submit
       Editor textarea now contains: SELECT COUNT(*) FROM users WHERE age > 30
       Explanation slot shows: Counts users older than thirty.

Why the API key stays out of the webview

The ask_sql Tauri 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

Layer Change
sqlrite::ask (engine) Re-exports the public sqlrite-ask types (AskConfig, AskResponse, AskError, Provider, etc.) so downstream crates that already depend on the engine don't need a separate sqlrite-ask dep just for type names.
desktop/src-tauri/src/main.rs New ask_sql Tauri command (~30 LOC) — locks Database, reads AskConfig::from_env(), calls sqlrite::ask::ask_with_database, returns { sql, explanation }. Registered in the invoke handler.
desktop/src/App.svelte New $state vars + handlers (onAskClick, onAskSubmit, onAskKey) + composer markup (~110 LOC). Cmd/Ctrl+Enter submits, Esc closes.
desktop/src/app.css New styles for .ask-panel, .ask-header, .ask-actions, .ask-explanation, .ask-button, .ask-close using existing CSS variables (~85 LOC).
Docs 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

  • Generated SQL is NOT auto-executed. Drops into the editor textarea; user reviews + clicks Run. Mirrors the REPL's confirm-and-run UX.
  • Empty SQL response handled. When the model declines (schema can't answer the question), the model's explanation surfaces in the panel instead of erasing the editor or doing nothing silent.
  • Errors surface in the existing error slot. Missing API key, network failure, API errors all produce clean messages — no popups or alerts.
  • Focus moves back to the editor on success so Cmd/Ctrl+Enter just runs.
  • API key sourcing. AskConfig::from_env() reads SQLRITE_LLM_API_KEY from the process env. Tauri inherits the parent shell's env when launched via npm run tauri dev or directly. Production app: users set the env var via system-level config or a future settings panel.

Test plan

  • cargo fmt --all -- --check — clean
  • cargo check --workspace --all-targets — clean (including sqlrite-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 pass
  • npm run check (svelte-check) — 0 errors / 0 warnings / 84 files
  • CI: rust matrix (linux/macos/windows), python-sdk, nodejs-sdk, go-sdk, wasm-build, desktop-build all green
  • Manual smoke: export 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 result

Next up after merge

  1. Merge → dispatch release-pr.yml at v0.1.20 → desktop installers (Linux deb / AppImage / rpm + macOS dmg + Windows msi/exe) all carry the Ask… button.
  2. 7g.4-7g.6 — Python / Node / Go SDK adapters. Each adds a conn.ask() method on the existing connection class plus an ask_run() convenience wrapper. Parallelizable but small enough to do sequentially.
  3. 7g.7 — WASM SDK with the JS-callback shape per Q9 (browser tab does prompt construction in-page; HTTP call goes through caller-supplied JS function so the API key stays in the user's backend).
  4. 7g.8 — MCP ask tool (after 7h lays the MCP server framework).

🤖 Generated with Claude Code

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>
@joaoh82 joaoh82 merged commit db45090 into main May 1, 2026
16 checks passed
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