Side-by-side comparison UI for the BibleGet I/O semantic search API. Lets you submit a single query and see the top-K results under two embedding models — currently MiniLM and LaBSE — rendered in parallel and grouped by Bible version.
The companion to the discussion at BibleGet-I-O/endpoint#107.
| Layer | Choice |
|---|---|
| Framework | SvelteKit 2 (Svelte 5, runes mode) |
| Adapter | @sveltejs/adapter-static (SPA mode, fallback: index.html) |
| i18n | Paraglide JS — compile-time, type-safe, ICU MessageFormat |
| Styling | vanilla CSS + Open Props tokens, no Tailwind |
| Headless components | Melt UI (for combobox / dialog / listbox where accessibility-load-bearing) |
| Tooling | Biome (lint + format), TypeScript |
No backend lives here — every interaction is a fetch against query.bibleget.io. The API base URL is configurable via VITE_API_BASE.
Reference points: early-printed bibles, the Vatican Apostolic Library's digital viewer, the Perseus Digital Library. Concrete commitments:
- Paper-and-ink palette (warm cream + near-black, hairline rules), one rubric-red accent borrowed from medieval rubrication
- Source Serif 4 for verse text, Public Sans for chrome, IBM Plex Mono for model labels / score readouts / version sigla
- Conservative motion, generous whitespace, no purple gradients
Tokens live in src/styles/tokens.css.
npm install
npm run devThe UI is fully client-rendered. Out of the box it calls https://query.bibleget.io/v3. To point at a local endpoint:
echo 'VITE_API_BASE=http://localhost:8080/v3' >> .env.localFonts are not in this repo — drop the woff2 files into static/fonts/ after cloning:
- Source Serif 4 —
source-serif-4-{regular,italic,semibold}.woff2 - Public Sans —
public-sans-{regular,medium}.woff2 - IBM Plex Mono —
ibm-plex-mono-regular.woff2
Until then, the CSS falls back to system serif / sans / mono via the stacks in tokens.css.
src/
├── components/
│ ├── search/ # QueryBar, ModeTabs, VersionPicker, ReferenceInput
│ ├── results/ # SplitView shell, ModelPane, VersionGroup, VerseCard, ScoreMeter
│ └── shell/ # Header, Footer, ThemeToggle
├── lib/
│ ├── api/ # Thin fetch wrapper + endpoint functions + types
│ │ ├── client.ts
│ │ ├── endpoints.ts
│ │ └── types.ts
│ ├── state/ # Svelte 5 runes — query state, results state per model
│ │ ├── search.svelte.ts
│ │ └── results.svelte.ts
│ └── paraglide/ # auto-generated by Paraglide on `vite dev` — do not edit
├── routes/
│ ├── +layout.svelte # global shell (header, locale switcher)
│ ├── +layout.ts # SPA mode (ssr=false)
│ └── +page.svelte # main search UI
└── styles/
├── tokens.css # the palette + type stack + spacing scale
├── typography.css # @font-face for self-hosted fonts
└── base.css # modern minimal reset + global element styles
messages/
├── en.json # ICU MessageFormat
└── it.json
Paraglide compiles every message in messages/*.json to a typed function. Use them in components via:
<script>
import { m } from '$lib/paraglide/messages.js';
</script>
<h1>{m.app_title()}</h1>Adding a locale:
- Append the BCP-47 tag to
localesinproject.inlang/settings.json. - Drop a
messages/{tag}.jsonnext to the others. - Restart dev server — Paraglide regenerates types.
- Wait for
model=minilm|labsequery param on/v3/search/semanticand/v3/search/similar(issue BibleGet-I-O/endpoint) - Wire QueryBar → two concurrent
fetchcalls → ModelPane components - VersionGroup + VerseCard rendering with score meter
- URL state (
?q=…&versions=…&mode=…) for shareable comparisons - Similarity-search mode with ReferenceInput
- Keyword-search modes (fulltext, boolean, exact)
- Optional: BFF on Cloudflare Workers + SSE for progressive result rendering
| Command | What |
|---|---|
npm run dev |
Vite dev server on http://localhost:5173 |
npm run build |
Production build into build/ |
npm run preview |
Serve the production build locally |
npm run check |
svelte-check over the TypeScript |
npm run lint |
Biome lint |
npm run lint:fix |
Biome lint + format with --write |
npm run format |
Biome format only |
TBD — keep it open-source to match the rest of BibleGet I/O.