Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.5 KiB
Frontend — Familienarchiv
Overview
SvelteKit 2 application providing the Familienarchiv web UI. Server-side rendered (SSR) where beneficial, with client-side interactivity for document viewing, transcription, annotation, and admin workflows.
Tech Stack
- Framework: SvelteKit 2 with Svelte 5 (runes mode)
- Language: TypeScript 5.9
- Styling: Tailwind CSS 4.1 + custom brand utilities
- Build Tool: Vite 7
- Adapter:
@sveltejs/adapter-node(Node.js server, not static) - i18n: Paraglide.js 2.5 (
@inlang/paraglide-js) — German (default), English, Spanish - API Client:
openapi-fetch+openapi-typescript(generated from backend OpenAPI spec) - PDF Rendering:
pdfjs-dist(PDF.js) - Testing:
- Unit/Server: Vitest 4 (Node environment)
- Component: Vitest Browser Mode with Playwright (Chromium)
- E2E: Playwright (
frontend/e2e/)
Project Structure
src/
├── routes/ # SvelteKit file-based routing
│ ├── +layout.svelte # Global layout: header, nav, auth state
│ ├── +layout.server.ts # Loads current user, injects auth cookie
│ ├── +page.svelte # Home / document search dashboard
│ ├── documents/ # Document CRUD, detail, edit, upload
│ ├── persons/ # Person directory, detail, edit, merge
│ ├── briefwechsel/ # Bilateral conversation timeline
│ ├── aktivitaeten/ # Unified activity feed (Chronik)
│ ├── admin/ # User, group, tag, OCR, system management
│ ├── api/ # Internal API proxies (server-side only)
│ ├── geschichten/ # Stories (list, [id], [id]/edit, new)
│ ├── stammbaum/ # Family tree
│ ├── enrich/ # Enrichment workflow ([id], done)
│ ├── hilfe/transkription/ # Transcription help page
│ ├── profile/ # User profile settings
│ ├── users/[id]/ # Public user profile page
│ ├── login/ logout/ register/
│ ├── forgot-password/ reset-password/
│ └── demo/ # Dev-only demos
├── lib/ # Domain-based package structure (mirrors backend)
│ ├── document/ # Document domain: components, stores, services, utils
│ │ ├── annotation/ # Annotation overlay components
│ │ ├── comment/ # Comment thread components
│ │ └── transcription/ # Transcription editor + block logic
│ ├── person/ # Person domain: chips, typeahead, avatar, format
│ │ ├── relationship/ # Relationship form + chip components
│ │ └── genealogy/ # Stammbaum (family tree) components
│ ├── tag/ # Tag domain: TagInput, TagChipList, TagParentPicker
│ ├── geschichte/ # Geschichte (story) domain: editor + card
│ ├── notification/ # Notification bell + dropdown + store
│ ├── activity/ # Activity feed (Chronik) components
│ ├── conversation/ # Bilateral conversation (Briefwechsel) components
│ ├── ocr/ # OCR progress, training cards, trigger
│ ├── user/ # User profile/password/groups section components
│ ├── shared/ # Cross-domain utilities and primitives
│ │ ├── actions/ # Svelte actions (clickOutside, etc.)
│ │ ├── hooks/ # Reusable Svelte state hooks (useTypeahead, etc.)
│ │ ├── server/ # Server-only utilities (locale, session)
│ │ ├── services/ # Client-side service helpers
│ │ ├── utils/ # Pure utility functions (date, search, etc.)
│ │ ├── primitives/ # Generic UI primitives (BackButton, ProgressRing, etc.)
│ │ ├── dashboard/ # Dashboard stat components
│ │ ├── discussion/ # CommentThread + shared discussion UI
│ │ ├── help/ # Help/FAQ page components
│ │ ├── api.server.ts # Typed API client factory
│ │ ├── errors.ts # Error code mapping (mirrors backend ErrorCode)
│ │ ├── types.ts # Shared TypeScript types
│ │ ├── relativeTime.ts # Relative time formatting
│ │ └── utils.ts # Top-level shared utilities
│ ├── generated/ # Auto-generated API types (openapi-typescript)
│ └── paraglide/ # Generated i18n code
├── hooks/ # SvelteKit hooks (handle, handleFetch)
└── ... # Other SvelteKit config files
For per-domain component inventories, see the domain READMEs in src/lib/<domain>/README.md.
API Client Pattern
→ See CONTRIBUTING.md §Frontend API client
LLM reminder: check !result.response.ok (not result.error — breaks when spec has no error responses); cast errors as result.error as unknown as { code?: string }; use result.data! after an ok check. For multipart/form-data (file uploads), bypass the typed client and use raw fetch.
Form Actions Pattern
// +page.server.ts
export const actions = {
default: async ({ request, fetch }) => {
const formData = await request.formData();
const name = formData.get('name') as string; // cast needed — FormData returns FormDataEntryValue
// ...
return fail(400, { error: 'message' }); // on error
throw redirect(303, '/target'); // on success
}
};
Date Handling
→ See CONTRIBUTING.md §Date handling
LLM reminder: always append T12:00:00 when constructing new Date() from an ISO date string — prevents UTC timezone off-by-one errors. Forms use German dd.mm.yyyy format via handleDateInput() with a hidden ISO input.
Styling Conventions (Tailwind CSS 4)
Brand color tokens (defined in layout.css):
| Token / Utility | CSS variable | Usage |
|---|---|---|
brand-navy |
--palette-navy |
Tailwind utility — buttons, headers, primary text |
brand-mint |
--palette-mint |
Tailwind utility — accents, hover underlines, icons |
--palette-sand |
--palette-sand |
Palette constant only — use bg-canvas or bg-surface |
Typography:
font-serif(Tinos) — body text, document titles, namesfont-sans(Montserrat) — labels, metadata, UI chrome
Card pattern for content sections:
<div class="rounded-sm border border-line bg-surface shadow-sm p-6">
<h2 class="text-xs font-bold uppercase tracking-widest text-ink-3 mb-5">Section</h2>
<!-- content -->
</div>
Key UI Components
→ See per-domain READMEs: src/lib/person/README.md, src/lib/tag/README.md, src/lib/document/README.md, src/lib/shared/README.md
LLM reminder: BackButton is at $lib/shared/primitives/BackButton.svelte — use it for all back navigation; never a static <a href>. API client is at $lib/shared/api.server.
How to Run
Development
cd frontend
npm install
npm run dev # Dev server on port 5173
Build & Preview
npm run build # Production build
npm run preview # Preview production build
Code Quality
npm run lint # Prettier + ESLint check
npm run format # Auto-fix formatting
npm run check # svelte-check (type checking)
Testing
npm run test # Vitest unit + server tests (headless)
npm run test:coverage # Coverage report (server project only)
npm run test:e2e # Playwright E2E tests
npm run test:e2e:headed # Playwright E2E with visible browser
npm run test:e2e:ui # Playwright UI mode
Regenerate API Types
Requires backend running with --spring.profiles.active=dev:
npm run generate:api
Vite Proxy
During development, /api calls are proxied to the Spring Boot backend. The proxy injects the Authorization header from the auth_token cookie automatically (see vite.config.ts).
i18n (Paraglide)
Translations live in messages/{de,en,es}.json. The compiler generates type-safe helpers in src/lib/paraglide/. Run compilation manually with:
npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide
Or let the Vite plugin handle it automatically during dev/build.