feat(theming): in-UI theme editor (2.10.0)
Recolour the whole UI from Settings — 12 palette colour pickers with live preview, presets (Ember/Frost/Verdant/Amethyst), and reset to the default Blackflame. Overrides persist in app_settings (key 'theme') via a hex-validated /api/theme route and apply to :root on boot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ import { router as clusterRouter } from './routes/cluster.js';
|
||||
import { router as storageRouter } from './routes/storage.js';
|
||||
import { router as backupsRouter } from './routes/backups.js';
|
||||
import { router as kuttRouter } from './routes/kutt.js';
|
||||
import { router as themeRouter } from './routes/theme.js';
|
||||
|
||||
export function mountApi(app) {
|
||||
const api = Router();
|
||||
@@ -71,6 +72,7 @@ export function mountApi(app) {
|
||||
api.use('/tags', tagsRouter);
|
||||
api.use('/links', linksRouter);
|
||||
api.use('/kutt', kuttRouter);
|
||||
api.use('/theme', themeRouter);
|
||||
api.use('/pending-changes', pendingChangesRouter);
|
||||
api.use('/audit', auditRouter);
|
||||
api.use('/search', searchRouter);
|
||||
|
||||
21
lib/api/routes/theme.js
Normal file
21
lib/api/routes/theme.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { asyncWrap } from '../errors.js';
|
||||
import { requireOwner } from '../cap.js';
|
||||
import { validate } from '../validate.js';
|
||||
import * as settings from '../../db/repos/app_settings.js';
|
||||
export const router = Router();
|
||||
|
||||
// Theme = a small map of palette-var overrides, e.g. { accent: '#ff4f2e' }.
|
||||
// Keys are short slugs (mapped to --<key> on the client); values must be hex,
|
||||
// so a saved theme can never inject arbitrary CSS.
|
||||
const themeSchema = z.record(
|
||||
z.string().regex(/^[a-z0-9-]{1,24}$/),
|
||||
z.string().regex(/^#[0-9a-fA-F]{3,8}$/)
|
||||
);
|
||||
|
||||
router.get('/', asyncWrap(async (_req, res) => res.json(await settings.get('theme', {}))));
|
||||
|
||||
router.put('/', requireOwner, validate({ body: themeSchema }), asyncWrap(async (req, res) => {
|
||||
res.json(await settings.set('theme', req.body));
|
||||
}));
|
||||
Reference in New Issue
Block a user