Skip to content

The Svelte frontend (front/)

Svelte 5 SPA built with SvelteKit's adapter-static. Talks to the backend over /api/v1/ and to the SFU over /ws. Bring your own UI if you prefer — the frontend is one product, not the product.

Stack

Layer Choice
Framework Svelte 5 (runes: $state, $derived, $effect)
Meta-framework SvelteKit (SPA mode via @sveltejs/adapter-static)
Build Vite 6
TypeScript strict, via svelte-check
Styling plain scoped <style> blocks (no Tailwind, no Panda)
i18n i18next (4 languages × 10 namespaces)
Testing (unit) Vitest 4 + @testing-library/svelte + jsdom
Testing (e2e) Playwright (headless Chromium)
WebRTC protocol Galene's protocol.js (vendored, unchanged)

Module map

front/src/
├── app.html
├── lib/
│   ├── api/                 ← typed HTTP client for /api/v1/
│   │   ├── fetchApi.ts      ← fetch wrapper + ApiError
│   │   ├── auth.ts          ← login, logout, fetchMe
│   │   ├── config.ts        ← public config + resolveGaleneWsUrl
│   │   ├── events.ts        ← joinEvent, fetchParticipants, …
│   │   ├── rooms.ts         ← createEphemeralRoom
│   │   └── schema.d.ts      ← generated from openapi.json
│   ├── components/          ← app-specific components (PreJoin, ChatPanel, VideoTile, …)
│   ├── primitives/          ← reusable UI (Button, Input, Field, Select, …)
│   ├── layout/              ← shell (Header, Footer, Toast, …)
│   ├── protocol/            ← Galene's WebSocket protocol (vendored)
│   ├── stores/              ← reactive state (21 .svelte.ts modules)
│   │   ├── connection.svelte.ts  ← ServerConnection wrapper, 99% covered
│   │   ├── session.svelte.ts     ← login/logout/hydrate
│   │   ├── chat.svelte.ts        ← message queue
│   │   ├── media.svelte.ts       ← device enumeration, local/remote streams
│   │   ├── notifications.svelte.ts  ← toast queue
│   │   └── …
│   ├── i18n/                ← i18next bootstrap + locale JSON
│   └── utils/
├── routes/
│   ├── +layout.svelte       ← app shell
│   ├── +page.svelte         ← home (New meeting button)
│   ├── auth/login/          ← login page
│   └── m/[slug]/            ← meeting route (PreJoin → Conference)
└── styles/
    └── global.css

State management

No Svelte stores in the classic sense — the app uses .svelte.ts modules with Svelte 5 runes:

export const connectionState = $state({
  state: 'disconnected' as 'disconnected' | 'connecting' | 'connected' | 'joined',
  users: {} as Record<string, User>,
  permissions: [] as string[],
  // …
});

Each store exports its state object directly. Components import it and read fields as normal object properties — Svelte 5's reactivity does the tracking.

Testing

cd front
npm run test                 # Vitest (157 pass)
npm run coverage             # with v8 coverage report
npx playwright test          # e2e (2 pass: single-guest + two-guest-with-chat)

From the repo root:

make test-front              # Vitest only
make e2e-all                 # full stack + Playwright
make e2e-alt                 # same, but against alt-backend

Line coverage on src/lib/ is ~21%, heavily weighted toward api/ (100%), utils/ (91%), and a handful of stores (connection.svelte.ts at 99%, notifications.svelte.ts at 97%). Components and primitives are mostly at 0% — the next push target is render tests for PreJoin, VideoTile, and ChatPanel.

Type generation

The TypeScript types for /api/v1/ come from the backend's OpenAPI spec via openapi-typescript:

# Refresh after the backend's OpenAPI changes
cd backend && uv run python -c "from app.app import create_app; ..." > ../front/openapi.json
cd ../front && npm run generate:api

front/openapi.json + front/src/lib/api/schema.d.ts are committed to the repo — regenerating needs the backend running, which is friction for frontend-only changes. A CI guard that re-generates and diffs is on the roadmap.

Dev proxy

vite.config.ts proxies from the dev server (:5173) to:

From To Why
/api/* http://localhost:8000 Backend (Litestar or alt-backend)
/ws (WebSocket) http://localhost:8443 SFU signalling
/galene-api http://localhost:8443 (legacy path kept for dev-only back-compat — not used in v2 flow)

Further reading

  • notes/v2/frontend.md — design doc
  • API contracts/api/v1/ shape
  • front/README.md — operational notes