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 { ListItem } from "@nous-research/ui/ui/components/list-item";
import { Typography } from "@/components/NouiTypography"; import { Typography } from "@/components/NouiTypography";
import { BUILTIN_THEMES, useTheme } from "@/themes"; import { BUILTIN_THEMES, useTheme } from "@/themes";
import type { DashboardTheme } from "@/themes";
import { useI18n } from "@/i18n"; import { useI18n } from "@/i18n";
import { cn } from "@/lib/utils"; 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. * Compact theme picker mounted next to the language switcher in the header.
* Each dropdown row shows a 3-stop swatch (background / midground / warm * Each dropdown row shows a 3-stop swatch (background / midground / warm
* glow) so users can preview the palette before committing. User-defined * glow) so users can preview the palette before committing. User-defined
* themes from `~/.hermes/dashboard-themes/*.yaml` that aren't in * themes from `~/.hermes/dashboard-themes/*.yaml` use their API-provided
* `BUILTIN_THEMES` render without swatches and apply the default palette. * 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 * 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 * `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) => { {availableThemes.map((th) => {
const isActive = th.name === themeName; const isActive = th.name === themeName;
const preset = BUILTIN_THEMES[th.name]; const paletteTheme = BUILTIN_THEMES[th.name] ?? th.definition;
return ( return (
<ListItem <ListItem
@ -109,8 +110,8 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
}} }}
className="gap-3" className="gap-3"
> >
{preset ? ( {paletteTheme ? (
<ThemeSwatch theme={preset.name} /> <ThemeSwatch theme={paletteTheme} />
) : ( ) : (
<PlaceholderSwatch /> <PlaceholderSwatch />
)} )}
@ -144,10 +145,8 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
); );
} }
function ThemeSwatch({ theme }: { theme: string }) { function ThemeSwatch({ theme }: { theme: DashboardTheme }) {
const preset = BUILTIN_THEMES[theme]; const { background, midground, warmGlow } = theme.palette;
if (!preset) return <PlaceholderSwatch />;
const { background, midground, warmGlow } = preset.palette;
return ( return (
<div <div
aria-hidden 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 /** All selectable themes (shown in the picker). Starts with just the
* built-ins; the API call below merges in user themes. */ * built-ins; the API call below merges in user themes. */
const [availableThemes, setAvailableThemes] = useState< const [availableThemes, setAvailableThemes] = useState<ThemeSummary[]>(() =>
Array<{ description: string; label: string; name: string }>
>(() =>
Object.values(BUILTIN_THEMES).map((t) => ({ Object.values(BUILTIN_THEMES).map((t) => ({
name: t.name, name: t.name,
label: t.label, label: t.label,
@ -360,6 +358,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
name: t.name, name: t.name,
label: t.label, label: t.label,
description: t.description, description: t.description,
definition: t.definition,
})), })),
); );
// Index any definitions the server shipped (user themes). // Index any definitions the server shipped (user themes).
@ -430,8 +429,15 @@ const ThemeContext = createContext<ThemeContextValue>({
}); });
interface ThemeContextValue { interface ThemeContextValue {
availableThemes: Array<{ description: string; label: string; name: string }>; availableThemes: ThemeSummary[];
setTheme: (name: string) => void; setTheme: (name: string) => void;
theme: DashboardTheme; theme: DashboardTheme;
themeName: string; themeName: string;
} }
interface ThemeSummary {
description: string;
label: string;
name: string;
definition?: DashboardTheme;
}