# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview stammtisch-hersbruck.de is a SvelteKit 5 application using Svelte 5's new runes syntax ($state, $props, etc.) with PocketBase as the backend. The app manages events, runs, riders, times and teams for a motorsport/cycling event organization. The repository is a monorepo: the SvelteKit app lives in `frontend/`, the PocketBase instance in `backend/`. ## Development Commands Alle Frontend-Befehle laufen aus `frontend/`: ```bash cd frontend # Start development server (runs on http://stammtisch-hersbruck.de.localhost:31337) npm run dev # Build for production npm run build # Preview production build npm run preview # Type-check Svelte files npm run check # Type-check with watch mode npm run check:watch # Generate TypeScript types from the schema migration in backend/pb_migrations # (no running instance, no token, no network access needed) npm run generate-pocketbase-types ``` Backend (PocketBase) aus `backend/`: ```bash cd backend # PocketBase lokal starten (Admin-UI auf http://127.0.0.1:8090/_/) docker compose up -d --build # Stoppen docker compose down ``` ## Architecture ### Backend Integration (PocketBase) - **API Base URL**: konfigurierbar über `PUBLIC_PB_URL` in `frontend/.env`. Default ist die produktive Instanz `https://api.stammtisch-hersbruck.de`; für das lokale Backend aus `backend/` auf `http://127.0.0.1:8090` umstellen. - **Schema**: versioniert als Migration in `backend/pb_migrations/`, kommt per `COPY` ins Image (kein Bind-Mount — der würde das Image-Verzeichnis überdecken). Im Admin-UI erzeugte Migrationen liegen deshalb zunächst nur im Container und müssen mit `docker compose cp` ins Repo geholt werden; siehe `backend/README.md`. - **Type-safe client**: The PocketBase client is typed using auto-generated types in `frontend/src/lib/types.d.ts` - **Collections**: users, teams, team_invites, events, runs, riders, times, trails, trail_versions, trail_flags, trail_markers, trail_comments, event_participants, event_series - **Regeln**: Jede Zugriffsregel, die `@request.auth.id` erwähnt, beginnt mit `@request.auth.id != "" && (…)`. Ohne diesen Vorspann ist eine *leere* Relation gleich dem leeren `@request.auth.id` einer anonymen Anfrage — ein Team ohne Admins stünde damit offen im Netz. Siehe `backend/pb_migrations/1754501700_rules_require_login.js`. - **Hooks**: `backend/pb_hooks/` enthält serverseitiges JS für das, was keine Collection-Regel abbilden kann — derzeit die öffentlichen Routen der Einladungslinks. Jeder Route-Handler läuft in einer eigenen JS-Laufzeit; gemeinsamer Code gehört in ein Modul und wird per ``require(`${__hooks}/…`)`` im Handler geholt. Details in `backend/README.md`. - **Authentication**: Handled through `src/lib/stores/pocketbase.svelte.ts` with the `AuthStore` class - **File handling**: Use `getFileURL(record, file, options)` helper for PocketBase file URLs ### State Management Pattern The app uses Svelte 5 runes for state management with a custom store pattern: 1. **Global stores** in `src/lib/stores/`: - `pocketbase.svelte.ts`: PocketBase client, auth, and collection operations - `app.svelte.ts`: Global app state (theme, navigation, confirm dialogs, hotkeys) - `teams.svelte.ts`: Example of collection-specific store pattern 2. **Collection store pattern**: - Each collection has a context-based store (see `teams.svelte.ts` as template) - Use `setTeamContext()` in parent and `getTeamContext()` in children - Stores provide: `records`, `refresh()`, `create()`, `edit()`, `remove()` - The `collections` helper in `pocketbase.svelte.ts` provides reusable CRUD operations 3. **Auth flow**: - Auth state lives in `auth` store from `pocketbase.svelte.ts` - Cookie-based persistence available via `auth.cookie` flag - Auth store syncs with PocketBase `authStore.onChange()` ### UI Components - **UI Library**: Using shadcn-svelte components (bits-ui based) - **Component path alias**: `@/components/ui/*` maps to `$lib/components/ui/*` - **Styling**: Tailwind CSS 4.x with custom configuration - **Dark mode**: Handled by `mode-watcher` package, toggle with `toggleMode()` - **Tooltips**: Global `tooltip` action available from `app.svelte.ts` using tippy.js ### Important Patterns **Svelte 5 Runes**: This project uses Svelte 5 syntax exclusively: - `$state()` for reactive state (not `let` with `$:`) - `$props()` for component props - `$derived()` for computed values - `{@render children?.()}` for slot content **Type Generation**: After modifying the schema migration in `backend/pb_migrations/`, run `npm run generate-pocketbase-types` to update `frontend/src/lib/types.d.ts`. The script reads the collections array straight out of the migration (`frontend/scripts/schema-to-json.mjs`) — schema and types come from the same versioned source and cannot drift apart. Commit the regenerated file. **Async Data Loading**: Root layout (`src/routes/+layout.svelte`) shows pattern: ```svelte {#await load()} {:then _} {/await} ``` **Confirm Dialogs**: Use `app.confirm.request()` for user confirmations (see `events.svelte.ts` remove pattern). **Hotkeys**: Use `app.hotkey(event, condition, callback)` which auto-ignores input fields and contenteditable elements. **Tooltips**: Never use the `title` attribute for tooltips — appwide. `