diff --git a/docs/api/system.md b/docs/api/system.md index b175a49..101db53 100644 --- a/docs/api/system.md +++ b/docs/api/system.md @@ -3,7 +3,7 @@ type: api title: System Endpoints description: Bootstrap payload, public event status, public/authenticated SSE streams, and public event-window timestamps. tags: [api, system, bootstrap, event, sse, settings] -timestamp: 2026-07-21T22:19:08Z +timestamp: 2026-07-22T13:40:00Z --- # Endpoints @@ -52,6 +52,10 @@ SPA's `BootstrapService`: * `passwordPolicy` is derived from `PASSWORD_MIN_LENGTH` / `PASSWORD_REQUIRE_MIXED` env vars (see [Change Password Guide](/guides/change-password.md)). +* The frontend `BootstrapService` caches the initial bootstrap request through + `load()`/`ready()`. Its `refresh()` method fetches the payload again, updates + signals, and reapplies the theme; `/admin/general` calls it after a successful + settings update. # `GET /api/v1/event/status` (legacy public snapshot) diff --git a/docs/architecture/key-files.md b/docs/architecture/key-files.md index 5f8cf24..283b5da 100644 --- a/docs/architecture/key-files.md +++ b/docs/architecture/key-files.md @@ -1,9 +1,9 @@ --- type: architecture title: Key Files Index -description: One-line responsibility for important source files, including authenticated event streaming and validated site-logo uploads. +description: One-line responsibility for important source files, including bootstrap payloads, runtime theme application, authenticated event streaming, and validated site-logo uploads. tags: [architecture, index, key-files] -timestamp: 2026-07-22T10:32:00Z +timestamp: 2026-07-22T13:40:00Z --- # Backend @@ -19,7 +19,9 @@ timestamp: 2026-07-22T10:32:00Z | `backend/src/common/services/event-status.service.ts` | Computes the event-window state machine. | | `backend/src/common/services/sse-hub.service.ts` | In-process pub/sub for server-sent event streams. | | `backend/src/modules/system/system.controller.ts` | Registers bootstrap, event status, settings, and SSE endpoints. | -| `backend/src/modules/system/system.service.ts` | Builds the public bootstrap payload. | +| `backend/src/modules/system/system.service.ts` | Builds the public bootstrap payload from admin existence, settings, the theme loader, and password-policy configuration. | +| `backend/src/modules/admin/admin-general.controller.ts` | Registers admin general-settings and available-theme endpoints. | +| `backend/src/modules/admin/general.service.ts` | Reads/writes global settings, filters available theme files, and publishes the `general` SSE notification after updates. | | `backend/src/modules/auth/auth.controller.ts` | Registers authentication and account endpoints. | | `backend/src/modules/auth/auth.service.ts` | Handles sessions, authentication, registration, and password changes. | | `backend/src/modules/uploads/uploads.controller.ts` | Registers admin-only multipart uploads, including Sharp-backed site-logo format validation. | @@ -30,7 +32,9 @@ timestamp: 2026-07-22T10:32:00Z |---|---| | `frontend/src/main.ts` | Bootstraps Angular and registers HTTP interceptors. | | `frontend/src/app/app.routes.ts` | Defines public, shell, child, and admin routes. | -| `frontend/src/app/app.component.ts` | Loads bootstrap data and restores the session. | +| `frontend/src/app/core/services/bootstrap.service.ts` | Caches and exposes bootstrap state, refreshes it after admin updates, and applies the resolved theme. | +| `frontend/src/app/core/services/bootstrap.types.ts` | Defines bootstrap/theme payload types and maps theme tokens to CSS custom properties. | +| `frontend/src/app/core/services/admin.service.ts` | Calls admin settings, theme-list, category, and upload APIs. | | `frontend/src/app/features/home/home.component.ts` | Authenticated shell container; starts user and event state services. | | `frontend/src/app/core/services/auth.service.ts` | Signal-backed access token and current-user state; owns the cross-tab invalidation `BroadcastChannel` + `storage`-event fallback. | | `frontend/src/app/core/services/auth-session-events.pure.ts` | Pure cross-tab message encoding/validation and namespaced constants used by the auth broadcast channel. | diff --git a/docs/guides/theming.md b/docs/guides/theming.md index 06a3095..ef07049 100644 --- a/docs/guides/theming.md +++ b/docs/guides/theming.md @@ -1,50 +1,84 @@ --- type: guide title: Theming -description: How the 10 canonical themes are loaded, validated, and applied to the SPA via CSS custom properties. -tags: [guide, theming, design] -timestamp: 2026-07-21T18:28:00Z +description: How the canonical themes are loaded, selected by administrators, and applied to the SPA via CSS custom properties. +tags: [guide, theming, design, admin] +timestamp: 2026-07-22T13:40:00Z --- # Theme catalog -HIPCTF ships 10 canonical themes defined as JSON files in -`backend/themes/` (`01-classic.json` … `10-monochrome.json`). The full -list lives in `backend/src/common/types/theme-ids.ts`: +HIPCTF ships canonical themes defined as JSON files in `backend/themes/`. The +catalog is identified by `THEME_IDS` in +`backend/src/common/types/theme-ids.ts`; the default is `classic`. -`classic`, `midnight`, `sunset`, `forest`, `cyber`, `paper`, `crimson`, -`ocean`, `neon`, `monochrome`. +Each theme contains an `id`, display `name`, and token set containing colors, +font family, radii, and spacing values. The frontend maps the supported visual +tokens to CSS custom properties. -Each theme is a flat map of CSS custom properties (colors, radii, font -family) consumed by `frontend/src/styles.css`. +# Administrator workflow + +1. Sign in as an administrator and open **Admin area → General**, or navigate +to `/admin/general`. +2. Wait for the settings and available-theme requests to finish. +3. Choose a value from **Global theme** (`data-testid="general-themeKey"`). +4. Click **Save** (`data-testid="general-save"`). +5. Verify that `Saved.` appears and that the page uses the selected colors, +font, and corner radii. Reloading the application should preserve the theme. + +The selector lists themes exposed by `GET /api/v1/admin/general/themes`. Only +theme JSON files present in the configured `THEMES_DIR` are listed, while the +loader still maintains canonical fallback themes for bootstrap and runtime +safety. # Backend loading `ThemeLoaderService` (`backend/src/common/utils/theme-loader.service.ts`) -runs on module init: +runs on module initialization: -1. Reads every `*.json` file in `backend/themes/`. -2. Validates each against the `Theme` interface - (`backend/src/common/types/theme.ts`) — unknown theme ids, malformed - JSON, or missing tokens throw `ApiError(THEME_INVALID)`. -3. Backfills any of the 10 canonical themes that are missing from disk - (safety net so a partial checkout still boots). -4. Reads `SETTINGS_KEYS.THEME_KEY` (default `'classic'`). If the value is - not in `THEME_IDS`, the loader falls back to `classic`, logs a - warning, and persists `'classic'` back to settings so the next request - is self-healing. +1. Reads theme JSON files from `THEMES_DIR` (default `./themes`). +2. Validates IDs and tokens; malformed files are skipped or reported through +the loader's validation path. +3. Backfills missing canonical themes so the application always has a valid +catalog. +4. Validates the configured `SETTINGS_KEYS.THEME_KEY`; an unknown value falls +back to `classic` and is persisted as the repaired setting. -The resolved theme object is sent to the SPA in the -`GET /api/v1/bootstrap` payload (see [System Endpoints](/api/system.md)). +The selected theme is sent to the SPA in `GET /api/v1/bootstrap` (see +[System Endpoints](/api/system.md)). Administrators can read and update the +selection through [Admin Endpoints](/api/admin.md). A successful general-settings +update emits a `general` SSE event containing the new `themeKey`. # Frontend application `BootstrapService` (`frontend/src/app/core/services/bootstrap.service.ts`) -writes each theme token onto `document.documentElement` (e.g. -`--color-primary`, `--radius-md`, `--font-family`). Because `styles.css` -references the tokens via `var(--token)`, swapping themes is instant. +fetches `/api/v1/bootstrap` once per application lifecycle, stores the payload +in signals, and applies the returned theme before the UI is rendered. The +`applyThemeToCss` helper in +`frontend/src/app/core/services/bootstrap.types.ts` writes these tokens to +`document.documentElement`: + +| Theme token | CSS custom property | +|---|---| +| `primary`, `secondary`, `accent`, `surface`, `text` | `--color-*` | +| `success`, `warning`, `danger` | `--color-*` | +| `fontFamily` | `--font-family` | +| `radii.sm`, `radii.md`, `radii.lg` | `--radius-sm`, `--radius-md`, `--radius-lg` | + +Reapplying a theme replaces the existing values. `clearThemeCss` removes every +property managed by the helper. Components consume the properties through +`var(--token)` in the global stylesheet and component styles. + +# Verification + +The frontend theme tests in `tests/frontend/bootstrap-theme.spec.ts` verify +that all tokens are written, missing tokens are a no-op, reapplication replaces +values, zero-valued radii are preserved, and cleanup removes every managed +property. # See also - [System Endpoints](/api/system.md) +- [Admin — General Settings](/guides/admin-general-settings.md) +- [Admin Endpoints](/api/admin.md) - [Backend Module Map](/architecture/backend-modules.md) diff --git a/docs/index.md b/docs/index.md index b7da900..03a78f2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin controls. The docs below are organized by purpose so agents can pull just the slice -they need. Last regenerated 2026-07-22T12:44:45Z. +they need. Last regenerated 2026-07-22T13:40:00Z. # Architecture @@ -84,7 +84,7 @@ they need. Last regenerated 2026-07-22T12:44:45Z. * [Landing Page](/guides/landing-page.md) - Public landing page (logo, welcome Markdown, blog list) and the login + registration modal rendered at `/login` once the instance is initialized. -* [Theming](/guides/theming.md) - How themes are loaded and applied. +* [Theming](/guides/theming.md) - How canonical themes are listed for admins, persisted through general settings, and applied to CSS custom properties. * [Event Window](/guides/event-window.md) - How the 4-state event window and live countdown work, including the authenticated SSE stream. * [Event LED Color Mapping](/guides/event-led-colors.md) - Source of