Skip to content

04. Frontend Deep Dive

App structure

Piece Detail
Framework React 19 + TypeScript strict
Bundler Vite 8, base: '/browser-rag/'
Routing TanStack Router file routes + generated routeTree.gen.ts
Server state TanStack Query
Styling Tailwind CSS v4, shadcn/ui (Radix), Lucide icons
Fonts Fraunces + Source Sans 3 variable fonts

Entrypoint: src/main.tsxThemeProviderSystemInitProviderApp (QueryClientProvider + RouterProvider).

Routing and layout

Route File Purpose
/ src/routes/index.tsx Chat + citations + debug
/documents src/routes/documents.tsx Upload, list, retry, chunk explorer
/history src/routes/history.tsx Past Q&A
/projects src/routes/projects.tsx Project CRUD
/settings src/routes/settings.tsx Models, chunk/hybrid, diagnostics, backup
__root src/routes/__root.tsx AppShell

Basepath /browser-rag matches Vite base and GitHub Pages project site layout.

Layout components: app-shell, sidebar, top-bar, project-switcher.

State management

graph TD
  classDef default fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
  classDef highlight fill:#065f46,stroke:#34d399,stroke-width:2px,color:#f0fdf4

  LS["localStorage preferences"]
  Ctx["SystemInitProvider"]:::highlight
  Hooks["useGemma4 / useWebLLM / useLfm2 / useQwen35"]
  TQ["TanStack Query caches"]
  Local["Chat message React state"]
  DB["PGlite projects/docs/history"]

  LS --> Ctx
  Ctx --> Hooks
  Ctx --> DB
  TQ --> DB
  Local --> Ctx

  linkStyle default stroke:#64748b,stroke-width:2px

SystemInitProvider owns active project, preference updates, LLM/embedding load progress, and exposes engine handles to the chat orchestrator.

Prefs quirk (Confirmed): defaults use llmModelId: 'qwen-3.5-0.8b' / llmVariantId: 'transformers-js', which do not match catalog ids (qwen35-0.8b). getLLMVariant falls back when unknown.

API and streaming clients

No REST client. UI imports service modules and consumes async generators:

  • generateRAGAnswer → chat event loop
  • indexDocument → upload/retry mutations
  • exportDb / importDb → settings backup UI

Markdown rendering: marked.parse into dangerouslySetInnerHTML on chat and history.

Component system

  • shadcn primitives under src/components/ui/
  • Domain: retrieval-debug-panel, upload-panel, chunk-explorer, logo
  • Theme via theme-provider

Chat / artifact / debug UX

Feature Behavior
Streaming answer Appends text_delta
Thinking Collapsible “Thinking Process” from thinking_delta
Citations Interactive source references from retrieval results
Debug panel Query rewrite, hit lists, RRF scores, timings
Empty retrieval Inline notice without LLM generation

Loading, error, empty states

  • Model load progress from context (llmProgress, embeddingProgress)
  • Document statuses: pending/processing/completed/failed with retry
  • Settings capability probes: WebGPU, Workers, IndexedDB, SharedArrayBuffer
  • Import errors surfaced in settings UI

Type safety and build quality

  • pnpm build = tsc -b && vite build
  • pnpm typecheck, pnpm lint, Prettier with Tailwind plugin
  • Path alias @./src

Frontend interview questions

Q: How do you keep the UI responsive during indexing?
A: Extract/chunk run in indexing.worker; embeddings in embedding.worker; status callbacks update the UI without blocking parse work on the main thread.

Q: How is global model state shared?
A: One SystemInitProvider instantiates all four engine hooks and exposes a unified load/switch API used by chat and settings.

Q: Why TanStack Router file routes?
A: Colocated route modules with typed createFileRoute, generated route tree, and basepath support for project Pages hosting.