Add admin HK color customization fields to theme editor
Local Build and Deploy / deploy (push) Successful in 58s

- Add 6 new admin color DB keys (admin_canvas, admin_surface, admin_text,
  admin_text_muted, admin_border, admin_sidebar_bg) that override the
  derived admin palette
- Extract adminPaletteCss() from themePaletteCss() for reuse
- Generate admin CSS variables in both :root and html.dark with overrides
- Persist admin color settings via saveTheme action
- Add Admin panel (HK) section to /admin/theme with color pickers
This commit is contained in:
openhands committed 2026-07-13 19:49:19 +02:00
1 parent 4dcf6fdce8
commit a16d9859e8
5 files changed
+134 -57

No files matched your search

+38 -4
View File
@@ -1,7 +1,7 @@
import { siteSettings } from "@/lib/services/site-settings";
import { themePaletteCss } from "@/lib/theme-css";
import { adminPaletteCss, themePaletteCss } from "@/lib/theme-css";
import { FONTS, PRESETS, THEME_COLOR_KEYS, type ThemePalette } from "@/lib/theme-presets";
import { derivePublicForegrounds, readableColor } from "@/lib/theme-contrast";
import { deriveAdminPalette, derivePublicForegrounds, readableColor } from "@/lib/theme-contrast";
// Injects the DB-driven CSS custom properties into :root, exactly like
// AtomCMS's app.blade.php. Falls back to the atom defaults when no DB. Covers
@@ -41,6 +41,12 @@ export async function ThemeVars() {
h2,
h3,
customCss,
adminCanvas,
adminSurface,
adminText,
adminTextMuted,
adminBorder,
adminSidebarBg,
] = await Promise.all([
g("color_primary", "#f59e0b"),
g("color_background", "#f8fafc"),
@@ -74,6 +80,12 @@ export async function ThemeVars() {
g("size_heading_h2", "22"),
g("size_heading_h3", "18"),
g("custom_css", ""),
g("admin_canvas", ""),
g("admin_surface", ""),
g("admin_text", ""),
g("admin_text_muted", ""),
g("admin_border", ""),
g("admin_sidebar_bg", ""),
]);
// Only hex/keyword color values are admin-controlled — sanitize defensively.
@@ -99,6 +111,15 @@ export async function ThemeVars() {
const readableNavbarText = readableColor(safe(navbarText, "#1e293b"), [safe(navbar, "#ffffff")]);
const readablePrimaryForeground = readableColor(safe(buttonText, "#1e293b"), [safe(primary, "#f59e0b")]);
const readableAccentForeground = readableColor("#ffffff", [safe(accent, "#10b981")]);
const adminOverrides = {
...(adminCanvas ? { canvas: safe(adminCanvas, "") } : {}),
...(adminSurface ? { surface: safe(adminSurface, "") } : {}),
...(adminText ? { text: safe(adminText, "") } : {}),
...(adminTextMuted ? { muted: safe(adminTextMuted, "") } : {}),
...(adminBorder ? { border: safe(adminBorder, "") } : {}),
...(adminSidebarBg ? { sidebar: safe(adminSidebarBg, "") } : {}),
};
const semantic = derivePublicForegrounds({
color_background: safeBackground,
color_surface: safeSurface,
@@ -132,9 +153,22 @@ export async function ThemeVars() {
),
),
) as ThemePalette;
const darkCss = themePaletteCss("html.dark", darkPalette);
const darkCss = themePaletteCss("html.dark", darkPalette, adminOverrides);
const css = `:root{
const lightAdmin = deriveAdminPalette({
color_background: safeBackground,
color_surface: safeSurface,
color_text: safe(text, "#0f172a"),
color_text_muted: safe(textMuted, "#64748b"),
color_primary: safe(primary, "#f59e0b"),
color_accent: safe(accent, "#10b981"),
color_success: safe(success, "#16a34a"),
color_warning: safe(warning, "#eab308"),
color_error: safe(error, "#ef4444"),
color_info: safe(info, "#0ea5e9"),
} as ThemePalette, adminOverrides);
const css = `:root{${adminPaletteCss(lightAdmin)}
--color-primary:${safe(primary, "#f59e0b")};
--color-background:${safeBackground};
--color-surface:${safeSurface};