# CLAUDE.md — app/ ## Nuxt Frontend (SPA Mode) This is a **client-side only** Nuxt 4 app (`ssr: false`). No server routes, no `server/` directory. All data comes from PocketBase via its SDK. ## PocketBase Client Access ```ts const { pb, authStore } = usePocketBase() ``` - `pb` — PocketBase SDK instance (configured in `plugins/pocketbase.ts`) - `authStore` — reactive Vue ref wrapping PocketBase's auth state (`.isValid`, `.record`, `.token`) **Never instantiate PocketBase directly.** Always use the `usePocketBase()` composable. ## Stores (Pinia) All backend interaction goes through Pinia stores in `stores/`: | Store | Collection | Key patterns | |-------|-----------|--------------| | `user.ts` | `users` | Auth flows (OTP, OAuth), profile CRUD, `isAuthenticated` getter | | `counter.ts` | `counters` | Realtime subscription, increment/reset | | `notifications.ts` | `notifications` + `fcm_tokens` | Realtime subscription, FCM token registration | | `avatar.ts` | `users` (file field) | Avatar URL getter, file upload | **Realtime pattern:** Stores call `pb.collection('x').subscribe('*', callback)` in a `subscribeToChanges()` action. Components call this on mount and `unsubscribe()` on unmount. ## Routing & Middleware Pages use Nuxt file-based routing. Two middleware: - `middleware/auth.ts` — route-level guard, add via `definePageMeta({ middleware: 'auth' })` - `middleware/00.fetchUser.global.ts` — runs on every navigation, refreshes auth token Two layouts: `default` (with AppHeader) and `empty` (login/confirm pages). ## Components Organized by feature domain: `App/` (header, notifications), `Counter/` (widget), `Profile/` (avatar, upload, lang select). Components use Nuxt UI v3 primitives (`UButton`, `UCard`, `UForm`, `UModal`, etc.). ## Styling Tailwind CSS v4 + Nuxt UI. Theme colors set in `app.config.ts` (primary: teal, neutral: slate). Custom font: Public Sans (defined in `assets/css/main.css`). ## i18n Strategy: `no_prefix` — URLs are not prefixed with locale. Two locales: `en`, `de`. Translation files in `i18n/locales/`. Use `$t('key')` in templates or `const { t } = useI18n()` in setup. `LanguageCode` type defined in `types/i18n.types.ts`. ## Types - `types/pocketbase.types.ts` — **auto-generated**, do not edit. Contains typed collection names (`Collections` enum) and record interfaces. Regenerate with `pnpm pocketbase:types`. - `types/i18n.types.ts` — manual type for supported language codes. ## Mobile (Capacitor) `app.vue` handles Capacitor-specific setup: deep link listener (`App.addListener('appUrlOpen')`), push notification registration, and safe-area CSS. Check `Capacitor.isNativePlatform()` before using native APIs.