Projects bibleweb Docs plan.md

BibleWeb — Web App Migration Plan

Last modified March 22, 2026

BibleWeb — Web App Migration Plan

Context

The Bible study app is currently a MonoGame C# desktop application (13 views, 3 SQLite databases, 31K verses, Greek/Hebrew interlinear, commentaries, cross-references, personal notes, pixel art atmosphere). To distribute it to a wider audience, we're building a web-first SaaS version with user accounts, tiered pricing, and AI Bible Q&A. The desktop app continues to exist — this is a new project.

Decisions Made

Decision Choice
Frontend SvelteKit (Svelte 5 runes)
Content DB Turso (edge SQLite, same schema as bible.db)
User data / Auth Supabase (PostgreSQL + Auth + RLS)
Payments Stripe (via Supabase SvelteKit template)
AI model Qwen 3.5 4B (Q4_K_M quantized, Ollama)
AI hosting Hetzner CX32 (€7/mo, 4 vCPU, 8GB RAM, CPU inference)
Frontend hosting Vercel (free tier)
Project location ~/Code/BibleWeb/ (separate repo)
Styling Tailwind CSS + custom pixel art theme
ORM Drizzle (for Turso queries)
Search Orama (client-side FTS, replaces FTS5 which Turso doesn't support)

Tier Model

Feature Free Pro ($5/mo) Premium ($10/mo)
Bible reader (EN)
Jesus speech highlighting
Basic search
Themes + Parables browser
Notes (limit: 5)
Notes (unlimited)
Note cross-linking
Dutch Statenvertaling
Parallel Gospel reader
Interlinear Greek/Hebrew
Commentaries
Cross-reference graph
Offline download
AI Bible Q&A
Personal Translation Builder
Note export

Monthly Costs

Service At launch (0-100 users) Growing (100-1000)
Supabase $0 (free tier) $25/mo
Vercel $0 (free tier) $20/mo
Turso $0 (free tier) $0
Hetzner CX32 (AI) €7/mo €7-17/mo
Stripe % per transaction % per transaction
Total ~€7/mo ~€50/mo

Project Structure

~/Code/BibleWeb/
  package.json
  pnpm-workspace.yaml
  turso.toml
  drizzle.config.ts

  apps/
    web/                              # SvelteKit app → Vercel
      src/
        lib/
          server/
            db.ts                     # Turso + Drizzle client
            supabase.ts               # Supabase server client
            stripe.ts                 # Stripe helpers
            ai.ts                     # Ollama API client (calls Hetzner)
            tier.ts                   # Feature gating (free/pro/premium)
            queries/                  # Ported from BibleDatabase.cs (~30 methods)
              verses.ts
              greek-words.ts
              strongs.ts
              jesus-speech.ts
              commentaries.ts
              cross-refs.ts
              pericopes.ts
              themes.ts
              parables.ts
              search.ts
          components/
            bible/
              VerseText.svelte
              VerseList.svelte
              InterlinearPopup.svelte
              CommentaryDrawer.svelte
              NoteEditor.svelte
              SearchInput.svelte
              ChapterNav.svelte
              BookPicker.svelte
              CrossRefGraph.svelte    # Canvas 2D force-directed
              ParallelColumns.svelte
            layout/
              Sidebar.svelte
              CommandPalette.svelte
              TierGate.svelte
            ui/                       # Button, Modal, Drawer, etc.
          stores/
            user.ts                   # $state rune-based
            reader.ts                 # Current book/chapter/verse
            settings.ts
            notes.ts
          i18n/
            en.ts                     # Ported from Loc.cs (~200 keys)
            nl.ts
            index.ts
          theme/
            colors.ts                 # Ported from Theme.cs
            fonts.css                 # m5x7 WOFF2 + Noto Sans
          utils/
            text.ts                   # Ported from TextUtils.cs
            popup.ts                  # Ported from PopupBuilder.cs
        routes/
          +layout.svelte
          +layout.server.ts           # Supabase session + user tier
          (app)/
            read/[[book]]/[[chapter]]/+page.svelte     # ReaderView
            search/+page.svelte                         # SearchView
            parallel/+page.svelte                       # PericopeList
            parallel/[pericopeId]/+page.svelte          # ParallelView
            crossrefs/[book]/[chapter]/[verse]/+page.svelte  # CrossRefView
            themes/+page.svelte                         # ThemeBrowser
            themes/[id]/+page.svelte
            parables/+page.svelte                       # ParablesView
            parables/[id]/+page.svelte
            teachings/+page.svelte                      # JesusTeachingsView hub
            notes/+page.svelte                          # NotesView
            settings/+page.svelte                       # SettingsView
            ask/+page.svelte                            # AI Bible Q&A (Premium)
          (auth)/
            login/+page.svelte
            register/+page.svelte
            callback/+server.ts
          api/
            webhooks/stripe/+server.ts
      static/
        backgrounds/                  # Pre-rendered pixel art PNGs
        fonts/m5x7.woff2
      svelte.config.js
      tailwind.config.ts

  packages/
    db/                               # Shared Drizzle schema
      src/schema/
        bible.ts                      # Turso tables (books, verses, greek_words, etc.)
        user.ts                       # Supabase tables (notes, translations, settings)

  scripts/
    migrate-bible-db.ts               # Push bible.db → Turso
    build-search-index.ts             # Generate Orama index JSON from Turso
    export-backgrounds.sh             # Render pixel art scenes to PNGs

Key Architecture Decisions

View Mapping (MonoGame → SvelteKit)

MonoGame View SvelteKit Route Tier
MainMenu Sidebar (always visible) Free
Reader / JesusWords /read/[[book]]/[[chapter]] Free
Search /search Free
ChapterSelect BookPicker modal in reader Free
ThemeBrowser /themes + /themes/[id] Free
Parables /parables + /parables/[id] Free
JesusTeachings /teachings Free
ParallelView /parallel/[pericopeId] Pro
PericopeList /parallel Pro
CrossRefGraph /crossrefs/[book]/[chapter]/[verse] Pro
NotesView /notes Free (5 limit) / Pro
Settings /settings Free
(NEW) AI Chat /ask Premium

Pixel Art on Web

Pre-render MonoGame scenes to static PNGs + CSS effects:

  • Background images for Sea of Galilee, Jerusalem, desert, default
  • CSS vignette (box-shadow: inset), fire flicker (animation), subtle particle <canvas> overlay
  • Scene mapped by book/chapter (port SceneCatalog logic)

Search (FTS5 replacement)

Turso doesn't support FTS5. Use Orama (10KB, client-side):

  • Build a static JSON search index at deploy time from all 31K verses
  • Ship as ~5MB static file, cached in browser
  • Instant client-side search, works offline

AI Integration

  • Hetzner CX32 runs Ollama + Qwen 3.5 4B (Q4_K_M, ~2.5GB RAM)
  • SvelteKit /ask route calls Ollama's OpenAI-compatible API
  • RAG: FTS query on Turso → retrieve relevant verses + commentaries → inject into prompt
  • "Save to notes" button on AI answers → creates verse note in Supabase
  • Rate limit: 20 questions/day for Premium users

Offline

  • Service Worker via serwist SvelteKit plugin
  • Cache app shell + visited chapters progressively
  • Pro/Premium: "Download All" prefetches all 1,189 chapters (~15MB)
  • Notes write to localStorage immediately, sync to Supabase when online

Critical Source Files to Port

C# Source Port To What
BibleGame/BibleDatabase.cs (996 lines) lib/server/queries/*.ts All 30 database query methods → Drizzle
BibleGame/Models/DisplayModels.cs TypeScript interfaces Verse, GreekWord, StrongsEntry, etc.
BibleGame/PopupBuilder.cs lib/utils/popup.ts Interlinear popup construction logic
BibleGame/Loc.cs lib/i18n/en.ts + nl.ts ~200 localization strings + book names
BibleGame/Theme.cs lib/theme/colors.ts 15 named colors → CSS custom properties
BibleGame/TextUtils.cs lib/utils/text.ts SanitizeText, ExtractShortGloss, etc.
BibleGame/NoteStore.cs Supabase tables + RLS verse_notes, chapter_notes, note_links
BibleGame/TranslationStore.cs Supabase table + RLS word_selections
BibleGame/UserData.cs Supabase user_settings Reading position, preferences

Implementation Phases

✅ Phase 1: Foundation — Basic reader goes live

Goal: SvelteKit + Turso + Supabase auth + reader. Deployable.

  1. ✅ Init monorepo (pnpm, SvelteKit, Tailwind, Vercel adapter)
  2. ✅ Set up Turso: push bible.db dump, configure Drizzle schema
  3. ✅ Set up Supabase: auth (email/password), profiles, subscriptions tables with RLS
  4. ✅ Port Theme.cs → CSS custom properties, convert m5x7 to WOFF2
  5. ✅ Build VerseText.svelte, VerseList.svelte, ChapterNav.svelte
  6. ✅ Implement /read/[[book]]/[[chapter]] with server-side data loading
  7. ✅ Implement BookPicker modal, Jesus speech highlighting
  8. ✅ Port Loc.cs → i18n (EN/NL)
  9. ✅ Build Sidebar.svelte navigation
  10. ✅ Supabase auth flow (login/register)
  11. ✅ Deploy to Vercel

Deliverable: Live URL — users can read any chapter, create an account. ✅

✅ Phase 2: Search + Notes — Core interaction loop

  1. ✅ Build Orama search index (script at deploy time)
  2. /search page with history, Jesus-only toggle, grouped results
  3. ✅ Supabase user_settings table, sync reading position
  4. ✅ Verse notes + chapter notes CRUD via Supabase
  5. ✅ Note cross-linking (bidirectional)
  6. ✅ Notes browser (/notes)
  7. ✅ Bookmarks (localStorage + /bookmarks page)
  8. CommandPalette.svelte (Ctrl+K / Cmd+K, ~70 commands, all 66 books)
  9. ✅ Verse context menu (click → Commentary, Cross-refs, Greek/Hebrew, Note, Copy)

Deliverable: Users can search, take notes, bookmark. Position syncs across devices. ✅

⚠️ Phase 3: Pro Features + Stripe

  1. ⚠️ Stripe products + webhook integration — UI done, payment processing stubbed
    • ✅ Pricing page (/pricing) with 3-tier feature comparison
    • TierGate.svelte wrapper component
    • ✅ Tier logic (lib/tier.ts, lib/server/user-tier.ts, user store)
    • ✅ Stripe webhook route exists (/api/webhooks/stripe) — body is TODO, not yet functional
    • lib/server/stripe.ts stub — createCheckoutSession / handleWebhook throw "not configured yet"
    • ❌ Stripe package not installed, no checkout API route, no subscription DB table
  2. /parallel/[pericopeId] — multi-column responsive layout (EN/NL/Both toggle, tier-gated)
  3. ✅ Pericope list page (/parallel)
  4. InterlinearPopup.svelte (Greek/Hebrew + Strong's, personal translation builder)
  5. CommentaryDrawer.svelte (Henry, Gill, Kanttekeningen — tier-gated)
  6. ✅ Dutch Statenvertaling toggle (translation selector in settings)
  7. ✅ Themes browser (/themes), Parables browser (/parables), Jesus Teachings hub (/teachings)

Remaining for full Phase 3 completion:

  • Install Stripe SDK, implement createCheckoutSession, implement webhook handler to update user tier in Supabase, add checkout button to pricing page

Deliverable: All 13 views ported. Stripe payments work. Pro tier functional. (Views done; Stripe ❌)

✅ Phase 4: Production Hardening — Deploy blockers

  1. ✅ Database indexes — 17 indexes added (verses, cross-refs, user_notes, greek/hebrew words, commentaries, word_selections, etc.)
  2. ✅ Error pages (+error.svelte) — Custom error page with status code + error ID display
  3. ⚠️ API input validation — Manual type checks + .trim() on all inputs; Zod not used (acceptable for now)
  4. ✅ Cache headers — public, max-age=86400, s-maxage=604800 on commentaries, interlinear, cross-refs, OG images, translations; no-cache on search
  5. ⚠️ Prerender static pages — /teachings prerendered; /pricing and /themes list not yet prerendered

Deliverable: Production-ready API layer, fast queries, graceful error handling. ✅ (mostly)

✅ Phase 5: Multi-Theme Architecture

  1. ✅ Theme map in colors.ts — 4 palettes: dark, light, sepia, high-contrast
  2. ✅ Theme setting in settings store — Persisted in localStorage, reactive $state
  3. ✅ Reactive injection in layout — CSS custom properties applied to :root
  4. ✅ Theme switcher in /settings — Button group toggles all 4 themes
  5. ⚠️ prefers-color-scheme auto-detect — Not implemented (manual selection only)

Deliverable: Users can switch between dark/light/sepia/high-contrast themes. ✅

✅ Phase 6: Bible Translation Architecture

  1. verse_texts table in schema — Proper join table for multiple translations
  2. ✅ Translation selector in settings — Dropdown with available translations
  3. ✅ Query layer adapts — Verse queries accept translationId parameter
  4. ✅ Built-in translations: BSB (English), SV (Dutch Statenvertaling)
  5. ⚠️ Search index per translation — Currently English-only Orama index

Deliverable: Users can read in any supported translation. ✅ (search still EN-only)

✅ Phase 7: Cross-Refs + Personal Translation

  1. /crossrefs/[book]/[chapter]/[verse] — Interactive force-directed graph, tier-gated, vote filter (1–20)
  2. ✅ Personal Translation Builder — Word selection in InterlinearPopup, persisted to localStorage + server (word_selections table)
  3. ⚠️ Note export — TierGate exists in settings UI; export functionality status unclear

Deliverable: All Pro + Premium content features work. ✅ (note export uncertain)

✅ Phase 8: AI Integration

  1. ✅ Ollama integration (lib/server/ai.ts) — Streaming + non-streaming, configurable via OLLAMA_URL / OLLAMA_MODEL env vars (defaults: Qwen 3.5 4B)
  2. ✅ RAG pipeline (lib/server/rag.ts, 587 lines) — Reference parsing (60+ book aliases, EN+NL), keyword search, commentary retrieval, user notes context
  3. /ask chat page (Premium only) — Streaming responses, conversation sidebar, status phase markers
  4. ✅ "Save answer to notes" — markSavedToNotes() in chat store
  5. ✅ Conversation history — localStorage-persisted with timestamps, auto-titling, last 10 messages sent as context
  6. ✅ Rate limiting — 20 req/60s on /api/ask, 60 req/60s on /api/search (IP-based, in-memory)

Note: Conversation history is localStorage-based, not Supabase (no server-side persistence across devices).

Deliverable: Premium users can ask Bible questions, save AI answers to notes. ✅

✅ Phase 9: Offline + Polish

  1. ✅ Service Worker — Custom implementation (no serwist needed): cache-first for static assets, network-first for pages, skips /api/ and /auth/ routes
  2. ✅ Web App Manifest — manifest.json with icons, standalone mode, theme color, categories
  3. ✅ Offline note writing — Settings, progress, bookmarks, chat all persisted to localStorage
  4. ⚠️ "Download for Offline" (Pro/Premium) — Not implemented; no IndexedDB bulk download
  5. ⚠️ Pixel art backgrounds — Not exported from MonoGame yet
  6. ⚠️ Atmosphere CSS effects (vignette, glow) — Not implemented
  7. ⚠️ Page transitions — Not implemented
  8. ✅ Mobile responsive — Responsive layouts throughout
  9. ⚠️ Accessibility audit — Not formally done

Deliverable: Basic offline + installable PWA. ✅ (advanced offline/polish items pending)

⚠️ Phase 10: Launch Prep

  1. ❌ Analytics — PostHog not installed (packages present as transitive dep only)
  2. ❌ Error monitoring — Sentry not installed; custom /api/errors endpoint logs to console only
  3. ✅ Landing page — Full marketing page with hero, 6-feature grid, pricing table, Continue Reading widget
  4. ⚠️ Custom domain + DNS — Not configured (bibleweb.app referenced in sitemap/canonical but not verified)
  5. ✅ Onboarding flow — 6-step wizard (language, theme, translation, comfort profile); stored in localStorage
  6. ✅ E2E tests — 7 Playwright test files: navigation, explore, reading, settings, cross-refs, search, parables
  7. ✅ Sitemap — Dynamically generated for all 1,189 chapters + homepage + search; robots.txt present
  8. ⚠️ Beta testing — Not started

Remaining for launch:

  • Install PostHog or Plausible
  • Install Sentry (or configure log drain to Axiom/Logflare)
  • Verify/configure custom domain
  • Complete Stripe payment processing

Verification

After each phase, verify:

  • pnpm dev runs locally without errors
  • All routes load with correct data from Turso
  • Supabase auth flow works (register → login → session persists)
  • Vercel preview deployment succeeds
  • Lighthouse scores: 90+ performance, 90+ accessibility
  • Feature gating: free users see upgrade prompts on Pro/Premium features

Supabase Schema

Key tables (all with RLS — users can only access their own data):

  • profiles (extends auth.users)
  • user_settings (reading position, preferences)
  • bookmarks (book_id, chapter)
  • verse_notes (book_id, chapter, verse, content)
  • chapter_notes (book_id, chapter, content)
  • note_links (bidirectional from/to verse)
  • word_selections (personal translation word glosses)
  • subscriptions (Stripe customer/subscription IDs, tier)
  • ai_conversations + ai_messages (Premium chat history)