feat(ui): theme system — amber/slate/rose, profile picker, full token migration
Some checks failed
CI / Backend (pull_request) Successful in 44s
CI / UI (pull_request) Successful in 25s
CI / UI (push) Successful in 27s
Release / Test backend (push) Successful in 42s
CI / Backend (push) Successful in 44s
Release / Check ui (push) Successful in 24s
Release / Docker / caddy (push) Failing after 1m4s
Release / Docker / backend (push) Failing after 44s
Release / Docker / ui (push) Failing after 29s
Release / Docker / runner (push) Failing after 54s
Release / Gitea Release (push) Has been skipped

- Add CSS custom property token system in app.css (@theme + [data-theme] overrides)
- Three themes: amber (default), slate (indigo/dark), rose (dark pink)
- Flash prevention via inline <script> in <svelte:head> sets data-theme before paint
- Theme context (setContext/getContext) in +layout.svelte for live preview
- Theme persisted via PocketBase user_settings (PBUserSettings.theme field)
- /api/settings GET/PUT updated to handle theme field alongside existing settings
- Profile page: new Appearance section with 3 colour-swatch theme picker
- Full token migration across all 36 route/component files:
  zinc/amber hardcoded Tailwind classes → CSS var utilities (bg-(--color-surface), etc.)
- UI primitives (Badge, Button, Card, Dialog, Separator, Textarea) migrated
- accent-amber-400 replaced with inline style accent-color: var(--color-brand)
This commit is contained in:
Admin
2026-03-28 23:57:16 +05:00
parent 801928aadf
commit 7a2a4fc755
36 changed files with 846 additions and 726 deletions

View File

@@ -5,7 +5,7 @@ import { log } from '$lib/server/logger';
/**
* GET /api/settings
* Returns the current user's settings (auto_next, voice, speed).
* Returns the current user's settings (auto_next, voice, speed, theme).
* Returns defaults if no settings record exists yet.
*/
export const GET: RequestHandler = async ({ locals }) => {
@@ -14,7 +14,8 @@ export const GET: RequestHandler = async ({ locals }) => {
return json({
autoNext: settings?.auto_next ?? false,
voice: settings?.voice ?? 'af_bella',
speed: settings?.speed ?? 1.0
speed: settings?.speed ?? 1.0,
theme: settings?.theme ?? 'amber'
});
} catch (e) {
log.error('settings', 'GET failed', { err: String(e) });
@@ -24,7 +25,7 @@ export const GET: RequestHandler = async ({ locals }) => {
/**
* PUT /api/settings
* Body: { autoNext: boolean, voice: string, speed: number }
* Body: { autoNext: boolean, voice: string, speed: number, theme?: string }
* Saves user preferences.
*/
export const PUT: RequestHandler = async ({ request, locals }) => {
@@ -39,6 +40,12 @@ export const PUT: RequestHandler = async ({ request, locals }) => {
error(400, 'Invalid body — expected { autoNext, voice, speed }');
}
// theme is optional — if provided it must be a known value
const validThemes = ['amber', 'slate', 'rose'];
if (body.theme !== undefined && !validThemes.includes(body.theme)) {
error(400, `Invalid theme — must be one of: ${validThemes.join(', ')}`);
}
try {
await saveSettings(locals.sessionId, body, locals.user?.id);
} catch (e) {