diff --git a/web/src/App.tsx b/web/src/App.tsx
index 419939329c3..9a599f66eb0 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -486,6 +486,13 @@ export default function App() {
>
+
+
({ ...TERMINAL_THEME_STATIC, background: terminalBg }),
- [terminalBg],
+ () => buildTerminalTheme(terminalBg, terminalFg),
+ [terminalBg, terminalFg],
);
// The dashboard keeps ChatPage mounted persistently so the PTY survives tab
@@ -897,12 +903,12 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) {
}, [isActive]);
// Keep the live xterm theme in sync when the active theme's terminal
- // background changes (e.g. user switches to a custom YAML theme mid-session).
+ // colors change (e.g. user switches to a custom YAML theme mid-session).
useEffect(() => {
const term = termRef.current;
if (!term) return;
- term.options.theme = { ...TERMINAL_THEME_STATIC, background: terminalBg };
- }, [terminalBg]);
+ term.options.theme = terminalTheme;
+ }, [terminalTheme]);
// Layout:
// outer flex column — sits inside the dashboard's content area
@@ -1065,7 +1071,7 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) {
"bottom-2 right-2 px-2 py-1 text-xs sm:bottom-3 sm:right-3 sm:px-2.5 sm:py-1.5",
"lg:bottom-4 lg:right-4",
)}
- style={{ color: TERMINAL_THEME_STATIC.foreground }}
+ style={{ color: terminalFg }}
>
diff --git a/web/src/plugins/slots.ts b/web/src/plugins/slots.ts
index 72d382bce75..f040a589a03 100644
--- a/web/src/plugins/slots.ts
+++ b/web/src/plugins/slots.ts
@@ -19,7 +19,8 @@ import React, { Fragment, useEffect, useState } from "react";
* these in their manifest's `slots` field get wired in automatically.
*
* Shell-wide slots:
- * - `backdrop` — optional full-viewport background decoration
+ * - `backdrop` — optional full-viewport background decoration;
+ * mounted behind shell chrome at z-0
* - `header-left` — injected before the Hermes brand in the top bar
* - `header-right` — injected before the theme/language switchers
* - `header-banner` — injected below the top nav bar, full-width
diff --git a/web/src/themes/context.tsx b/web/src/themes/context.tsx
index 3cf2d97b48b..b991d8a4184 100644
--- a/web/src/themes/context.tsx
+++ b/web/src/themes/context.tsx
@@ -82,8 +82,6 @@ function paletteVars(palette: ThemePalette): Record {
...layerVars("background", palette.background),
...layerVars("midground", palette.midground),
...layerVars("foreground", palette.foreground),
- "--warm-glow": palette.warmGlow,
- "--noise-opacity-mul": String(palette.noiseOpacity),
};
}
@@ -391,11 +389,15 @@ function applyTheme(theme: DashboardTheme) {
applyCustomCSS(theme.customCSS);
applyLayoutVariant(theme.layoutVariant);
- // Terminal background — read by ChatPage via useTheme(); also available as CSS var.
+ // Terminal colors — read by ChatPage via useTheme(); also available as CSS vars.
root.style.setProperty(
"--theme-terminal-background",
theme.terminalBackground ?? "#000000",
);
+ root.style.setProperty(
+ "--theme-terminal-foreground",
+ theme.terminalForeground ?? "#f0e6d2",
+ );
// Re-assert the font override last: theme application just rewrote
// --theme-font-sans/-display, so an active override has to win again.
diff --git a/web/src/themes/presets.ts b/web/src/themes/presets.ts
index 6ae7e9f94e2..fdf1593fe86 100644
--- a/web/src/themes/presets.ts
+++ b/web/src/themes/presets.ts
@@ -199,6 +199,7 @@ export const nousBlueTheme: DashboardTheme = {
typography: DEFAULT_TYPOGRAPHY,
layout: DEFAULT_LAYOUT,
terminalBackground: "#f5f8fc",
+ terminalForeground: "#170d02",
seriesColors: {
inputTokenAccent: "#001934",
outputTokenAccent: "#0053fd",
diff --git a/web/src/themes/types.ts b/web/src/themes/types.ts
index 8a1462c48e4..ebf9476c51b 100644
--- a/web/src/themes/types.ts
+++ b/web/src/themes/types.ts
@@ -80,7 +80,8 @@ export type ThemeLayoutVariant = "standard" | "cockpit" | "tiled";
* emitted as a CSS var (`--theme-asset-`). Plugin slots and
* shell chrome may consume these via CSS. */
export interface ThemeAssets {
- /** Full-viewport background image URL. */
+ /** Full-viewport background image URL. Exposed as `--theme-asset-bg` for
+ * the `backdrop` plugin slot or theme `customCSS`. */
bg?: string;
/** Hero render (Gundam, mascot, wallpaper) — for plugin sidebars/overlays. */
hero?: string;
@@ -120,13 +121,7 @@ export interface ThemeComponentStyles {
* `--series-input-token` / `--series-output-token` CSS vars consumed
* inline by pages that render input-vs-output token flows. Themes can
* omit either field to inherit the default token defined in
- * `index.css` (Hermes-teal `#ffe6cb` for input, `#34d399` for output).
- *
- * Inverted-lens themes (e.g. Nous Blue) must pre-invert these hex
- * values so they read as their intended visual color after the FG
- * difference layer flips them (`out = 255 − channel`). E.g. to make
- * output paint as Nous-blue `#0053FD` on screen, set
- * `outputTokenAccent: "#FFAC02"` — the difference math reverses it. */
+ * `index.css` (Hermes-teal `#ffe6cb` for input, `#34d399` for output). */
export interface ThemeSeriesColors {
/** Input-tokens series accent (Analytics chart bars + table values). */
inputTokenAccent?: string;
@@ -177,18 +172,17 @@ export interface DashboardTheme {
/** Per-component CSS-var overrides. See `ThemeComponentStyles`. */
componentStyles?: ThemeComponentStyles;
colorOverrides?: ThemeColorOverrides;
- /** Data-series accent colors for Analytics/Models token charts.
- * See `ThemeSeriesColors` for inversion-aware values. */
+ /** Data-series accent colors for Analytics/Models token charts. */
seriesColors?: ThemeSeriesColors;
- /** Explicit 3-color swatch override for the theme picker. Use when the
- * palette's raw hex values don't reflect what users see on screen —
- * e.g. inverted "lens" themes whose foreground-difference layer flips
- * the authored colors to their visual complements. Order matches the
+ /** Explicit 3-color swatch override for the theme picker. Order matches the
* default swatch cells: [background, midground, warmGlow]. */
swatchColors?: [string, string, string];
/** Background color for the embedded terminal pane (xterm.js).
* Hex string. Defaults to `"#000000"` when absent. */
terminalBackground?: string;
+ /** Default text/cursor color for the embedded terminal pane (xterm.js).
+ * Hex string. Defaults to `"#f0e6d2"` when absent. */
+ terminalForeground?: string;
}
/**