- Rename `system.health` → `admin.systemHealth` and gate it behind the admin middleware
- Remove `tmdbConfigured` from `system.status` response; update description to reflect its purpose
- Update all call sites on web and native to use `orpc.admin.systemHealth`
- Rename and relocate the docs page from `system/system.health.mdx` to `admin/admin.systemHealth.mdx`
All explore, discover, search, recommendation, and person-credit listing procedures now include the internal database `id` on every item so clients can navigate and act without a separate resolve round-trip.
- Remove `titles.resolve` and `people.resolve` mutation calls from the native search screen, hero banners, poster rows, and cast cards; replace with direct `Link` navigation using the pre-returned `id`.
- Change `titles.quickAdd` to accept `{ id }` instead of `{ tmdbId, type }` and update every call site on native and web.
- Key all user-status and episode-progress lookups by `id` instead of `tmdbId-type` composite strings across `PosterCard`, `HorizontalPosterRow`, `FilterableTitleRow`, and `usePosterActions`; drop the `tmdbId` prop from `PosterCard` entirely.
- Remove the `titles.hydrateSeasons` auto-trigger from the title detail screen; season hydration now happens server-side on resolve.
- Delete the `browse-thumbhashes` and `browse-title-ids` server procedures and remove them from the router.
- Extend `@sofa/api` schemas with an `id` field on all listing-item types; update `packages/core` services and add a DB migration accordingly.
Convert trending, search, people.detail, and dashboard.library to page-based responses (`page` / `totalPages`) so clients can load results incrementally.
Update `@sofa/core` discovery and person services to accept a `page` / `limit` input and slice results accordingly. Add a new DB migration to persist the data needed to back paginated filmography queries.
On the web, introduce a `useInfiniteScroll` hook and wire up `useInfiniteQuery` in the explore, person detail, and filterable title row components with an intersection-observer sentinel. On native, switch the same screens from `useQuery` to `useInfiniteQuery` with `onEndReached` / `ListFooterComponent` loading indicators. Also replace remaining `FlatList` usages with `FlashList` in the home and title detail screens.
Replace the static `https://sofa.example.com/api/v1` server entry with a variable-based URL (`{protocol}://{host}:{port}/api/v1`) so the spec works against any instance out of the box. Also move the output path from `docs/openapi.json` to `docs/public/openapi.json` so the file is served as a static asset by the docs site.
Replace the standalone `ChangePasswordSection` card with a `ChangePasswordDialog` triggered from a button next to "Sign out" in the account section. This removes the separate card, merges the dialog logic into `account-section.tsx`, and cleans up the settings page layout accordingly.
Replace the single MDX file per API tag (e.g. `admin.mdx`, `titles.mdx`) with individual files for each procedure (e.g. `admin/admin.backups.create.mdx`). Each file renders a single `<APIPage>` with one operation, includes an inline description, and carries its own `_openapi` frontmatter.
Update `generate-api-docs.ts` to emit this per-procedure structure and adjust `source.ts` and `next.config.mjs` to resolve the new paths correctly.
Add `purgeMetadataCache` and `purgeImageCache` procedures that let admins free disk space on demand. Metadata purge removes un-enriched stub titles not in any user's library plus orphaned person records; image purge deletes all cached TMDB files from disk.
Expose both as POST endpoints under `/admin/cache/` via the oRPC contract, implement the core logic in a new `@sofa/core/cache` module, and surface them in a new "Danger Zone" section on the Settings page with individual confirmation dialogs and a combined "Purge all" action.
Extract spec generation into `openapi-spec.ts` with a `generateOpenApiSpec` function that registers common `$ref` schemas (Title, Person, Episode, Season, etc.) to deduplicate inline definitions across the spec. Add a `/api/v1/spec.json` interceptor that returns the fully normalized document, and point the Scalar docs UI at it instead of the plugin's raw generator output.
Also strip oRPC's impossible `{ not: {} }` void placeholders from request/response bodies via `normalizeOpenApiSpec`, and fix the handler registration so both `/api/v1` and `/api/v1/*` routes are matched.
Generate an OpenAPI 3.1 spec from the oRPC contract at build time
using @orpc/openapi, then use fumadocs-openapi to render interactive
API reference pages grouped by tag (Titles, Episodes, Seasons, etc.).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swap the generic `fumadocs-ui/og/takumi` default for a hand-crafted OG image using the Sofa cinema theme — dark background, amber accent, DM Serif Display title, DM Sans body, and a radial gradient — matching the app's visual identity.
- Load DM Sans (400/500) and DM Serif Display (400) at runtime from Google Fonts via old Safari UA to get TrueType URLs
- Render header with Sofa logo SVG + "Sofa Docs" label, centered title/description block, and an amber accent bar
- Scale title font size based on length (68px → 56px for longer strings)
- Define theme constants (BG, FG, PRIMARY, MUTED) from the app's oklch palette
- Add `docs/` — Next.js 15 app powered by Fumadocs with a landing page, docs layout, search, OG image generation, and LLM text routes
- Cover getting started, configuration, integrations (Plex, Emby, Jellyfin, Radarr, Sonarr), mobile app setup, and telemetry in MDX
- Exclude `docs/` from root Biome config since it has its own `biome.json`