fix(dashboard): show custom theme palette swatches

This commit is contained in:
MichaelWDanko 2026-04-26 00:25:57 -04:00 committed by Teknium
parent 239ea1bdea
commit da8654bb41
2 changed files with 18 additions and 13 deletions

View file

@ -4,6 +4,7 @@ import { Button } from "@nous-research/ui/ui/components/button";
import { ListItem } from "@nous-research/ui/ui/components/list-item";
import { Typography } from "@/components/NouiTypography";
import { BUILTIN_THEMES, useTheme } from "@/themes";
import type { DashboardTheme } from "@/themes";
import { useI18n } from "@/i18n";
import { cn } from "@/lib/utils";
@ -11,8 +12,8 @@ import { cn } from "@/lib/utils";
* Compact theme picker mounted next to the language switcher in the header.
* Each dropdown row shows a 3-stop swatch (background / midground / warm
* glow) so users can preview the palette before committing. User-defined
* themes from `~/.hermes/dashboard-themes/*.yaml` that aren't in
* `BUILTIN_THEMES` render without swatches and apply the default palette.
* themes from `~/.hermes/dashboard-themes/*.yaml` use their API-provided
* definitions so they show real palette swatches just like built-ins.
*
* When placed at the bottom of a container (e.g. the sidebar rail), pass
* `dropUp` so the menu opens above the trigger instead of clipping below
@ -95,7 +96,7 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
{availableThemes.map((th) => {
const isActive = th.name === themeName;
const preset = BUILTIN_THEMES[th.name];
const paletteTheme = BUILTIN_THEMES[th.name] ?? th.definition;
return (
<ListItem
@ -109,8 +110,8 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
}}
className="gap-3"
>
{preset ? (
<ThemeSwatch theme={preset.name} />
{paletteTheme ? (
<ThemeSwatch theme={paletteTheme} />
) : (
<PlaceholderSwatch />
)}
@ -144,10 +145,8 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
);
}
function ThemeSwatch({ theme }: { theme: string }) {
const preset = BUILTIN_THEMES[theme];
if (!preset) return <PlaceholderSwatch />;
const { background, midground, warmGlow } = preset.palette;
function ThemeSwatch({ theme }: { theme: DashboardTheme }) {
const { background, midground, warmGlow } = theme.palette;
return (
<div
aria-hidden

View file

@ -311,9 +311,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
/** All selectable themes (shown in the picker). Starts with just the
* built-ins; the API call below merges in user themes. */
const [availableThemes, setAvailableThemes] = useState<
Array<{ description: string; label: string; name: string }>
>(() =>
const [availableThemes, setAvailableThemes] = useState<ThemeSummary[]>(() =>
Object.values(BUILTIN_THEMES).map((t) => ({
name: t.name,
label: t.label,
@ -360,6 +358,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
name: t.name,
label: t.label,
description: t.description,
definition: t.definition,
})),
);
// Index any definitions the server shipped (user themes).
@ -430,8 +429,15 @@ const ThemeContext = createContext<ThemeContextValue>({
});
interface ThemeContextValue {
availableThemes: Array<{ description: string; label: string; name: string }>;
availableThemes: ThemeSummary[];
setTheme: (name: string) => void;
theme: DashboardTheme;
themeName: string;
}
interface ThemeSummary {
description: string;
label: string;
name: string;
definition?: DashboardTheme;
}