# 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 (filtered, paginated), detail, edit, merge, review (triage)
│   ├── 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
│   ├── zeitstrahl/      # Global timeline (Zeitstrahl) — SSR loads /api/timeline, renders lib/timeline; events/new + events/[id]/edit curator editor (WRITE_ALL-gated)
│   ├── 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/
├── 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
│   ├── timeline/        # Timeline (Zeitstrahl) domain: TimelineView, YearBand, EventPill, WorldBand, LetterCard, YearLetterStrip, GapSpan; dateLabel + timelineDensity + eventCardConfig (imports $lib/shared only, never document/)
│   ├── geschichte/      # Geschichte (story) domain: editor + card
│   ├── notification/    # Notification bell + dropdown + store
│   ├── activity/        # Activity feed (Chronik) 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, monthBuckets — month-bucket math shared by document chart + timeline strip)
│   │   ├── primitives/  # Generic UI primitives (BackButton, ProgressRing, Sparkline, 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](../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

```typescript
// +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](../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, names
- `font-sans` (Montserrat) — labels, metadata, UI chrome

Card pattern for content sections:

```svelte
<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/person/README.md), [`src/lib/tag/README.md`](src/lib/tag/README.md), [`src/lib/document/README.md`](src/lib/document/README.md), [`src/lib/shared/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

```bash
cd frontend
npm install
npm run dev         # Dev server on port 5173
```

### Build & Preview

```bash
npm run build       # Production build
npm run preview     # Preview production build
```

### Code Quality

```bash
npm run lint        # Prettier + ESLint check
npm run format      # Auto-fix formatting
npm run check       # svelte-check (type checking)
```

### Testing

```bash
npm run test              # Vitest unit + server tests (headless)
npm run test:coverage     # Coverage report (server + client)
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`:

```bash
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:

```bash
npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide
```

Or let the Vite plugin handle it automatically during dev/build.
