Skip to content

SQLR-41 — Tauri 2 + Svelte 5 local-first journaling example app#145

Merged
joaoh82 merged 2 commits into
mainfrom
feat/sqlr-41-desktop-journal
May 30, 2026
Merged

SQLR-41 — Tauri 2 + Svelte 5 local-first journaling example app#145
joaoh82 merged 2 commits into
mainfrom
feat/sqlr-41-desktop-journal

Conversation

@joaoh82

@joaoh82 joaoh82 commented May 30, 2026

Copy link
Copy Markdown
Owner

Summary

Closes SQLR-41. Adds a polished example app under examples/desktop-journal/ — a markdown daily-notes desktop product backed by a single .sqlrite file. Showcases Phase 8 BM25 full-text search with hit highlighting, the engine's ask feature in a non-AI-native product context, and the Tauri-friendly Arc<Mutex<Connection>> embedding pattern using the modern Connection API.

This is the third in the SQLR-38 example-apps umbrella (after examples/python-agent/ and examples/nodejs-notes/).

Demo

SQLRite Journal demo

Full 1080p MP4: examples/desktop-journal/docs/demo.mp4

What's in this PR

New example app — examples/desktop-journal/

  • Tauri 2 backend (src-tauri/) owns one Arc<Mutex<Connection>> in tauri::State. Every #[tauri::command] grabs the mutex, runs SQL via the public Connection / Statement API, returns serde-serialised structs.
    • Single-mutex design rationale: the journal is single-user / single-instance, so serialising commands is simpler than BEGIN CONCURRENT retry loops and just as correct. Documented in main.rs module doc.
  • Modern API surface: uses Connection / Statement (not the older Database / process_command path used by the existing desktop/ SQL playground, which AGENTS.md says new SDKs should avoid).
  • Schema v1 with a migration runner: entries, tags, entry_tags (synthetic PK because SQLRite's PRIMARY KEY is single-column today), plus secondary indexes on entries.date/updated_at and FTS indexes on entries.content/title.
  • Features: entry CRUD, markdown editor + rendered preview toggle, tag chips with click-to-filter, Phase 8 BM25 full-text search with token-boundary-aware <mark> highlighting on the Rust side, "Ask my journal" panel that calls Connection::ask and gates the returned SQL to SELECT / WITH only, export-DB / export-markdown commands.
  • In-app Settings (settings.rs): Anthropic API key persists to $APP_DATA/com.sqlrite.journal/settings.json — a separate file from journal.sqlrite so the key doesn't ride along if you sync the DB. DTO returns has_api_key: bool, never the value; falls back to SQLRITE_LLM_API_KEY env var when unset.
  • Locked-down capabilities: only core:default / core:window:default / core:event:default / dialog:default. No fs / shell / http — the IPC boundary is the only path to the engine.
  • Frontend: Svelte 5 + Tailwind v4 + TypeScript via @tailwindcss/vite (no PostCSS dance). All a11y warnings cleared (labels paired, keyboard handlers on interactive elements only).

Workspace + CI integration

  • New workspace member examples/desktop-journal/src-tauri; existing CI commands gain --exclude sqlrite-journal (same shape as the existing sqlrite-desktop exclude).
  • New journal-build job in .github/workflows/ci.yml mirrors desktop-build: frontend bundle then cargo build, including a no-default-features pass to prove the off-ask build still works.
  • New .github/workflows/journal-release.yml builds .dmg / .deb / .AppImage / .msi installers via tauri-apps/tauri-action@v0 on manual trigger or journal-v* tag push, attaching to a draft release.
  • AGENTS.md updated symmetrically (CLAUDE.md change deferred — see follow-ups).

Remotion demo composition — examples/desktop-journal/demo/

  • Stitches 7 real app screenshots (docs/screenshots/01-empty.png07-ask-panel.png) into a 21s demo with Ken Burns slow zoom + fade-in caption strips + cross-fade transitions.
  • Renders to docs/demo.mp4 (1080p H.264, 8.5 MB) and docs/demo.gif (960×540, 15fps, 8.8 MB, first 5 screens).
  • npm run prep copies the canonical screenshots from docs/screenshots/ into the Remotion public/ (gitignored) so there's one source of truth.
  • Replace a screenshot → npm run render re-produces both formats.

Website Examples card

  • New entry at position 3 on /examples. JSON-LD updated. New Example.demoAsset field carrying GIF + MP4 paths.
  • Inline GIF poster loads from raw.githubusercontent.com (no web/public/ copy to keep in sync); clicks through to the MP4 on github.com.

Test plan

  • cargo fmt --all -- --check — PASS
  • cargo build --workspace --exclude {desktop,journal,python,nodejs,benchmarks} --all-targets — PASS
  • cargo test --workspace --exclude … --exclude sqlrite-journal — PASS
  • cargo build -p sqlrite-journal --all-targets (default features) — PASS, no warnings
  • cargo build -p sqlrite-journal --no-default-features --all-targets — PASS, no warnings
  • cargo test -p sqlrite-journal14 / 14 tests pass (9 journal + 5 settings)
  • cargo clippy -p sqlrite-journal --all-targets — clean (no warnings on new code)
  • npm install && npm run build (Vite) in examples/desktop-journal/ — PASS
  • npm run check (svelte-check + tsc) — 0 errors, 0 warnings
  • npm install && npm run build in web/ (Next.js production) — PASS, /examples page statically pre-rendered with the new card + JSON-LD
  • Smoke driver writes / FTS-searches / reopens a .sqlrite file via the journal's schema — passes (mirror of the README demo script)
  • Manual UI verification by @joaoh82 — entry CRUD, search, Settings dialog, Ask flow all working

Follow-ups (will file separately)

  • Allow bm25_score(...) in the SELECT projection list in the engine — would let the journal show real BM25 scores instead of the current positional surrogate.
  • Custom branded app icon — currently reuses the playground's icons.
  • Symmetric CLAUDE.md edit to match AGENTS.md (workspace-members line + four CI exclude-pattern blocks). Auto-mode classifier blocked the edit in-session; needs a manual one-line change.
  • Record the README screencast (done as part of this PR via Remotion — superseded).

🤖 Generated with Claude Code

joaoh82 and others added 2 commits May 30, 2026 15:47
New `examples/desktop-journal/` showcases SQLRite as a serious choice
for a local-first desktop product. Tauri 2 backend owns one
Arc<Mutex<Connection>> in tauri::State; every #[tauri::command] grabs
the mutex and runs against the modern `Connection` API. Phase 8 BM25
full-text search drives the search bar with token-boundary-aware
<mark> highlighting in Rust. An "ask my journal" panel calls
Connection::ask, validates the returned SQL is SELECT/WITH-only, and
shows the user both the SQL and the rows.

In-app Settings dialog persists the Anthropic key to a separate
settings.json next to the journal file — the key never crosses into
the webview (DTO returns has_api_key: bool, not the value) and falls
back to SQLRITE_LLM_API_KEY when nothing's saved. Capabilities are
locked down to core window/event + dialog — no fs/shell/http, so the
IPC boundary is the only path to the engine.

Wiring: workspace gains `examples/desktop-journal/src-tauri` as a
member; CI's existing rust-build/test/clippy/doc commands pick up an
`--exclude sqlrite-journal` (same shape as the existing sqlrite-desktop
exclude). A new `journal-build` CI job mirrors `desktop-build` for the
frontend bundle + cargo build, including a no-default-features pass to
prove the off-`ask` build still works. A separate
`journal-release.yml` (manual / journal-v* tag trigger) builds .dmg /
.deb / .AppImage / .msi via tauri-action and attaches them to a draft
release.

14 unit tests cover migrations, CRUD, tag filtering, FTS+highlight,
stats, the ask-readonly SQL classifier, settings save/load round-trip,
empty-string-key normalisation, three-valued update semantics, the
no-leak-to-DTO contract, and settings-over-env precedence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d poster

Follow-up to be35e4c. Captures the journal app in seven UI states
(empty, new entry, markdown preview, list+tags, FTS highlight,
Settings, Ask panel), then composes them into a 21s demo video via
a Remotion project under `examples/desktop-journal/demo/`. The
composition gives each screen a Ken Burns slow zoom with a fade-in
caption strip; consecutive screens cross-fade for 8 frames.

Renders to `docs/demo.mp4` (1080p H.264, ~8.5 MB) and
`docs/demo.gif` (960×540, 15fps, first 5 screens, ~8.8 MB). The GIF
fits comfortably in the README and on GitHub's inline image
viewer; the MP4 is what the website Examples card points at.

Replacing a screenshot is `cd demo && npm run render` — `prep`
copies the latest PNGs from `../docs/screenshots/` into
`public/` (.gitignore'd so the canonical source stays in `docs/`).

Side change: pin `@tauri-apps/{api,cli}` to ~2.10 and
`plugin-dialog` to ~2.7 in the journal app's `package.json`.
`tauri build` rejects mismatched JS↔Rust major/minor versions; the
mismatch shows up the moment you bundle for installer shipping
(SQLR-41's release workflow) even though dev mode tolerates it.

Website Examples card gains an `Example.demoAsset` field carrying
the GIF + MP4 paths. The journal card renders the GIF inline from
raw.githubusercontent.com so the homepage tracks the repo on next
deploy instead of needing the asset copied into `web/public/`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rust-sqlite Ready Ready Preview, Comment May 30, 2026 9:36pm

Request Review

@joaoh82 joaoh82 merged commit 33f3e67 into main May 30, 2026
18 checks passed
@joaoh82 joaoh82 deleted the feat/sqlr-41-desktop-journal branch May 30, 2026 22:10
joaoh82 added a commit that referenced this pull request May 30, 2026
…146)

The SQL playground (`sqlrite-desktop`) read its `ask` config exclusively
from `SQLRITE_LLM_API_KEY`. Launched from Finder/the Dock — which don't
inherit a shell's env — the Ask… button just errored. Port the in-app
settings pattern from the journal example (SQLR-41, #145) so the key can
be pasted in a ⚙ dialog and persists across restarts.

- settings.rs: AskSettings (JSON at $APP_DATA/com.sqlrite.desktop/
  settings.json), scrubbed AskSettingsDto (has_api_key bool — the raw
  key never crosses to the webview), three-valued AskSettingsUpdate,
  build_ask_config (saved key, env var fallback). 5 unit tests.
- main.rs: AppState gains settings_path; ask_sql builds config from
  saved settings instead of from_env; add get_ask_settings /
  update_ask_settings commands.
- SettingsPanel.svelte (plain-CSS, matches the playground) + ⚙ header
  button + lib/api.ts wrappers.
- README: Configuring `ask` + security note.

Deviation from the journal port: build_ask_config is unconditional (the
desktop crate has no `ask` feature gate) and tempdir() in tests uses an
atomic counter, not subsec-nanos, to avoid a parallel-run collision.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
joaoh82 added a commit that referenced this pull request May 30, 2026
#145 added `examples/desktop-journal/src-tauri` (package
`sqlrite-journal`, a Tauri/GTK crate) as a workspace member and updated
ci.yml's build/test/clippy/doc steps to `--exclude sqlrite-journal`, but
release-pr.yml's "Refresh Cargo.lock" step was missed. So
`cargo build --workspace --exclude sqlrite-desktop` tried to compile
sqlrite-journal → glib-sys on a runner without the GTK system libs,
failing the first release-PR dispatch since #145 merged.

Mirror ci.yml: also exclude sqlrite-journal here. Lock-refresh only
needs to resolve versions, not build the desktop/journal apps.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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