diff --git a/web/src/App.tsx b/web/src/App.tsx index 8bf6f4ee96e..419939329c3 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -59,7 +59,6 @@ import { SelectionSwitcher } from "@nous-research/ui/ui/components/selection-swi import { Spinner } from "@nous-research/ui/ui/components/spinner"; import { Typography } from "@nous-research/ui/ui/components/typography/index"; import { cn } from "@/lib/utils"; -import { Backdrop } from "@/components/Backdrop"; import { SidebarFooter } from "@/components/SidebarFooter"; import { SidebarStatusStrip, gatewayLine } from "@/components/SidebarStatusStrip"; import { useBelowBreakpoint } from "@nous-research/ui/hooks/use-below-breakpoint"; @@ -483,18 +482,16 @@ export default function App() {
- -
- + {t.app.brand}
@@ -529,7 +523,7 @@ export default function App() { onClick={closeMobile} className={cn( "lg:hidden fixed inset-0 z-40 p-0 block", - "bg-black/60 backdrop-blur-sm", + "bg-black/70", )} /> )} @@ -545,7 +539,7 @@ export default function App() { className={cn( "fixed top-0 left-0 z-50 flex h-dvh max-h-dvh w-64 min-h-0 flex-col", "border-r border-current/20", - "bg-background-base/95 backdrop-blur-sm", + "bg-background-base", "transition-[transform] duration-200 ease-out", mobileOpen ? "translate-x-0" : "-translate-x-full", "lg:sticky lg:top-0 lg:translate-x-0 lg:shrink-0 lg:overflow-hidden", @@ -573,10 +567,7 @@ export default function App() { > - + Hermes
Agent @@ -879,7 +870,6 @@ function SidebarNavLink({ )} @@ -1050,7 +1040,6 @@ function SystemActionButton({ )} @@ -1186,7 +1175,7 @@ function SidebarTooltip({ anchor, label, warmRef }: SidebarTooltipProps) { className={cn( "fixed z-[100] pointer-events-none", "px-2 py-1", - "bg-background-base/95 border border-current/20 backdrop-blur-sm shadow-lg", + "bg-background-base border border-current/20 shadow-lg", "font-mondwest text-display text-xs tracking-[0.1em] text-midground uppercase", )} style={{ diff --git a/web/src/components/Backdrop.tsx b/web/src/components/Backdrop.tsx deleted file mode 100644 index 278ff7b9e4c..00000000000 --- a/web/src/components/Backdrop.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { useGpuTier } from "@nous-research/ui/hooks/use-gpu-tier"; - -import fillerBgUrl from "@nous-research/ui/assets/filler-bg0.webp"; - -/** - * Replicates the visual layer stack of `` from - * `@nous-research/ui` without pulling in its leva / gsap / three peer deps. - * - * See `design-language/src/ui/components/overlays/index.tsx` for the source of - * truth. Defaults match LENS_0 (the Hermes teal dark preset); the deep canvas - * and the warm vignette both read theme-switchable CSS custom properties so - * `ThemeProvider` can repaint the stack without remounting. - * - * z-1 bg = `var(--background-base)`, mix-blend-mode driven by - * `--component-backdrop-bg-blend-mode` (default `difference`). - * Both LENS_0-style dark themes and the LENS_5I-style Nous Blue - * light theme keep `difference` here — the canvas is flipped by - * the z-200 FG inversion layer, not by changing this blend mode. - * The CSS var is exposed as a hook so future presets can override - * it (e.g. `multiply` to paint the bg as-is before inversion) - * without touching this component. - * z-2 bundled filler-bg WebP, inverted, opacity 0.033, difference - * z-99 warm top-left vignette (`var(--warm-glow)`), opacity 0.22, lighten - * z-200 FG inversion = `var(--foreground)` (opaque white in LENS_5I, - * alpha-0 in LENS_0), mix-blend-mode: difference. This is the - * layer that flips the dashboard into "light mode" for inverted - * themes; for normal dark themes its alpha is 0 so it's a no-op. - * Deliberately placed above every UI overlay z-index (modals, - * tooltips, and dropUp dropdowns all sit at z-[100]) so portaled - * elements get inverted along with the rest of the page instead - * of painting with pre-inversion colors on top of the lens. - * z-201 noise grain (SVG, ~55% opacity × `--noise-opacity-mul`, - * color-dodge) — gated on GPU tier. Sits above the inversion - * layer by design so the grain is not flipped. - * - * `useGpuTier` returns 0 when WebGL is unavailable, the renderer is a - * software rasterizer (SwiftShader/llvmpipe), or the user has - * `prefers-reduced-motion: reduce` set. We skip the animated noise layer - * in that case so low-power / accessibility-conscious sessions stay crisp, - * mirroring the DS `` component's own opt-out. - */ -export function Backdrop() { - const gpuTier = useGpuTier(); - - return ( - <> -
- -
hides itself when a CSS bg is set - // so the two don't double-darken. CSS var fallbacks keep the - // default behaviour unchanged when no theme customises these. - mixBlendMode: - "var(--component-backdrop-filler-blend-mode, difference)", - opacity: "var(--component-backdrop-filler-opacity, 0.033)", - backgroundImage: "var(--theme-asset-bg)", - backgroundSize: "var(--component-backdrop-background-size, cover)", - backgroundPosition: - "var(--component-backdrop-background-position, center)", - } as unknown as React.CSSProperties - } - > - -
- -
- - {/* Foreground inversion layer. Source-of-truth: LENS_5I.Lens.fgOpacity - + fgBlend: 'difference' in `design-language/src/ui/components/ - overlays/lens.ts`. With `--foreground-alpha: 0` (LENS_0 dark default) - the layer is fully transparent and contributes nothing; with - alpha 1 + opaque white it inverts the entire stack below it, - producing the LENS_5I "light mode" look without altering any - downstream component code. - - z-200 (not 100) so it sits above every portaled UI overlay — - sidebar tooltips, dropUp dropdowns, and modal dialogs all use - z-[100], which is what the DS Lens picks too; portals append - at the end of , so equal z-index + later DOM order means - they'd paint on top of the inversion and skip the flip. Inlined - z-index for the same reason the DS does it — Tailwind's JIT - scan sometimes drops non-default z utilities. */} -
- - {gpuTier > 0 && ( -
- )} - - ); -} diff --git a/web/src/components/ConfirmDialog.tsx b/web/src/components/ConfirmDialog.tsx index 9c257729b45..cc4fbf1b77b 100644 --- a/web/src/components/ConfirmDialog.tsx +++ b/web/src/components/ConfirmDialog.tsx @@ -66,7 +66,7 @@ export function ConfirmDialog({ onClick={(e) => { if (e.target === e.currentTarget) onCancel(); }} - className="fixed inset-0 z-[200] flex items-center justify-center bg-background/85 backdrop-blur-sm p-4" + className="fixed inset-0 z-[200] flex items-center justify-center bg-background/85 p-4" >
e.target === e.currentTarget && onClose()} role="dialog" aria-modal="true" diff --git a/web/src/components/OAuthLoginModal.tsx b/web/src/components/OAuthLoginModal.tsx index 060761c8334..f35fd81575b 100644 --- a/web/src/components/OAuthLoginModal.tsx +++ b/web/src/components/OAuthLoginModal.tsx @@ -164,7 +164,7 @@ export function OAuthLoginModal({ provider, onClose, onSuccess }: Props) { return (
{t.app.footer.org} diff --git a/web/src/components/ThemeSwitcher.tsx b/web/src/components/ThemeSwitcher.tsx index 9bbab6ef26b..350dc652bf7 100644 --- a/web/src/components/ThemeSwitcher.tsx +++ b/web/src/components/ThemeSwitcher.tsx @@ -121,7 +121,7 @@ export function ThemeSwitcher({ collapsed = false, dropUp = false }: ThemeSwitch aria-label={sheetTitle} className={cn( "min-w-[240px] max-h-[70dvh] overflow-y-auto", - "border border-current/20 bg-background-base/95 backdrop-blur-sm", + "border border-current/20 bg-background-base/95", "shadow-[0_12px_32px_-8px_rgba(0,0,0,0.6)]", dropUp ? "fixed z-[100]" : "absolute z-50 right-0 top-full mt-1", )} diff --git a/web/src/components/ToolsetConfigDrawer.tsx b/web/src/components/ToolsetConfigDrawer.tsx index a042a780ad5..bb5d6f87de6 100644 --- a/web/src/components/ToolsetConfigDrawer.tsx +++ b/web/src/components/ToolsetConfigDrawer.tsx @@ -214,7 +214,7 @@ export function ToolsetConfigDrawer({ toolset, profile, onClose, onChanged }: Pr return createPortal(
{ if (e.target === e.currentTarget) onClose(); }} diff --git a/web/src/contexts/PageHeaderProvider.tsx b/web/src/contexts/PageHeaderProvider.tsx index 9fdd6215e34..2ee2f19ac05 100644 --- a/web/src/contexts/PageHeaderProvider.tsx +++ b/web/src/contexts/PageHeaderProvider.tsx @@ -55,7 +55,7 @@ export function PageHeaderProvider({ className={cn( "z-1 w-full shrink-0", "box-border border-b border-current/20", - "bg-background-base/40 backdrop-blur-sm", + "bg-background-base", // Mobile stacks title + toolbar — fixed h-14 clips content; desktop stays one row. "min-h-0 overflow-x-hidden overflow-y-visible py-3 sm:h-14 sm:min-h-[3.5rem] sm:overflow-hidden sm:py-0", )} @@ -88,7 +88,6 @@ export function PageHeaderProvider({ ? "shrink truncate" : "truncate", )} - style={{ mixBlendMode: "plus-lighter" }} > {displayTitle} diff --git a/web/src/index.css b/web/src/index.css index 1bbb9c4ddca..30b601e6c57 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -43,10 +43,8 @@ } /* ------------------------------------------------------------------ */ -/* Hermes Agent — Nous DS with the LENS_0 (Hermes teal) lens applied */ -/* statically. Mirrors nousnet-web/(hermes-agent)/layout.tsx so the */ -/* canonical Hermes palette is the default — teal canvas + cream */ -/* accent — without relying on leva/gsap at runtime. */ +/* Hermes Agent — Nous DS with the LENS_0 (Hermes teal) palette applied + statically as the default dashboard theme. */ /* ------------------------------------------------------------------ */ :root { @@ -63,10 +61,6 @@ --background-base: #041c1c; --background-alpha: 1; - /* Consumed by ; also theme-switchable. */ - --warm-glow: rgba(255, 189, 56, 0.35); - --noise-opacity-mul: 1; - /* Typography tokens — rewritten by ThemeProvider. Defaults match the system stack so themes that don't override look native. */ --theme-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, @@ -228,11 +222,6 @@ code { font-size: 0.875rem; } display: none; } -/* Plus-lighter blend used by logos/titles for a subtle glow. */ -.blend-lighter { - mix-blend-mode: plus-lighter; -} - /* System UI-monospace stack — distinct from `font-courier` (Courier Prime), used for dense data readouts where the display font would break the grid. Routes through the theme's mono stack so themes @@ -256,14 +245,3 @@ code { font-size: 0.875rem; } 2px 2px; } -/* When a theme provides `assets.bg`, the backdrop's
renders it as - a CSS background; the default filler is hidden to prevent - double-compositing. Unset → initial → empty, so the :not() selector - matches and the default image stays visible. */ -:root:not([style*="--theme-asset-bg:"]) .theme-default-filler { - display: block; -} -:root[style*="--theme-asset-bg:"] .theme-default-filler { - display: none; -} - diff --git a/web/src/pages/ChannelsPage.tsx b/web/src/pages/ChannelsPage.tsx index 7658c0cd61a..baee6b87666 100644 --- a/web/src/pages/ChannelsPage.tsx +++ b/web/src/pages/ChannelsPage.tsx @@ -326,7 +326,7 @@ export default function ChannelsPage() { {editing && (
e.target === e.currentTarget && setEditing(null)} role="dialog" aria-modal="true" diff --git a/web/src/pages/ChatPage.tsx b/web/src/pages/ChatPage.tsx index a3297cb7cca..a2f1c189722 100644 --- a/web/src/pages/ChatPage.tsx +++ b/web/src/pages/ChatPage.tsx @@ -932,7 +932,7 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { onClick={closeMobilePanel} className={cn( "fixed inset-0 z-[55] p-0 block", - "bg-black/60 backdrop-blur-sm", + "bg-black/60", )} /> )} @@ -944,7 +944,7 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { className={cn( "font-mondwest fixed top-0 right-0 z-[60] flex h-dvh max-h-dvh w-64 min-w-0 flex-col antialiased", "border-l border-current/20 text-midground", - "bg-background-base/95 backdrop-blur-sm", + "bg-background-base/95", "transition-transform duration-200 ease-out", "[background:var(--component-sidebar-background)]", "[clip-path:var(--component-sidebar-clip-path)]", @@ -962,7 +962,6 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { {t.app.modelToolsSheetTitle}
@@ -1037,7 +1036,7 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { Offer an in-place restart so the user never has to refresh the whole page to get a working chat back. */} {sessionEnded && ( -
+
Session ended.
@@ -1060,7 +1059,7 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { "absolute z-10", "normal-case tracking-normal font-normal", "rounded border border-current/30", - "bg-black/20 backdrop-blur-sm", + "bg-black/20", "opacity-70 hover:opacity-100 hover:border-current/60", "transition-opacity duration-150", "bottom-2 right-2 px-2 py-1 text-xs sm:bottom-3 sm:right-3 sm:px-2.5 sm:py-1.5", diff --git a/web/src/pages/CronPage.tsx b/web/src/pages/CronPage.tsx index dda3db7871e..ee894c28e70 100644 --- a/web/src/pages/CronPage.tsx +++ b/web/src/pages/CronPage.tsx @@ -814,7 +814,7 @@ export default function CronPage() { {createModalOpen && (
e.target === e.currentTarget && setCreateModalOpen(false)} role="dialog" aria-modal="true" @@ -889,7 +889,7 @@ export default function CronPage() { {editJob && (
e.target === e.currentTarget && setEditJob(null)} role="dialog" aria-modal="true" diff --git a/web/src/pages/McpPage.tsx b/web/src/pages/McpPage.tsx index cc933645b71..0f808211bca 100644 --- a/web/src/pages/McpPage.tsx +++ b/web/src/pages/McpPage.tsx @@ -334,7 +334,7 @@ export default function McpPage() { {createModalOpen && (
e.target === e.currentTarget && setCreateModalOpen(false) } @@ -455,7 +455,7 @@ export default function McpPage() { {installEntry && (
e.target === e.currentTarget && setInstallEntry(null) } diff --git a/web/src/pages/ModelsPage.tsx b/web/src/pages/ModelsPage.tsx index 5bddab251ec..401e22c9122 100644 --- a/web/src/pages/ModelsPage.tsx +++ b/web/src/pages/ModelsPage.tsx @@ -575,7 +575,7 @@ function AuxiliaryTasksModal({ return (
e.target === e.currentTarget && onClose()} role="dialog" aria-modal="true" @@ -779,7 +779,7 @@ function MoaModelsModal({ if (!preset) return null; return ( -
+
Configure Mixture of Agents presets diff --git a/web/src/pages/ProfilesPage.tsx b/web/src/pages/ProfilesPage.tsx index 781ca0c7783..dd2deac61e9 100644 --- a/web/src/pages/ProfilesPage.tsx +++ b/web/src/pages/ProfilesPage.tsx @@ -804,7 +804,7 @@ export default function ProfilesPage() { {createModalOpen && (
e.target === e.currentTarget && setCreateModalOpen(false) } @@ -1231,7 +1231,7 @@ export default function ProfilesPage() { {editorName && (
e.target === e.currentTarget && closeEditor()} role="dialog" aria-modal="true" diff --git a/web/src/pages/SystemPage.tsx b/web/src/pages/SystemPage.tsx index 30db6cc3492..21679929c5d 100644 --- a/web/src/pages/SystemPage.tsx +++ b/web/src/pages/SystemPage.tsx @@ -684,7 +684,7 @@ export default function SystemPage() { {hookModalOpen && (
e.target === e.currentTarget && setHookModalOpen(false)} role="dialog" aria-modal="true" diff --git a/web/src/pages/WebhooksPage.tsx b/web/src/pages/WebhooksPage.tsx index 30470f45259..f1990e70b9d 100644 --- a/web/src/pages/WebhooksPage.tsx +++ b/web/src/pages/WebhooksPage.tsx @@ -303,7 +303,7 @@ export default function WebhooksPage() { {createModalOpen && (
e.target === e.currentTarget && closeCreateModal()} role="dialog" aria-modal="true" diff --git a/web/src/plugins/slots.ts b/web/src/plugins/slots.ts index 2d3a04277c8..72d382bce75 100644 --- a/web/src/plugins/slots.ts +++ b/web/src/plugins/slots.ts @@ -19,7 +19,7 @@ import React, { Fragment, useEffect, useState } from "react"; * these in their manifest's `slots` field get wired in automatically. * * Shell-wide slots: - * - `backdrop` — rendered inside ``, above the noise layer + * - `backdrop` — optional full-viewport background decoration * - `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/presets.ts b/web/src/themes/presets.ts index 6e1b05384fb..6ae7e9f94e2 100644 --- a/web/src/themes/presets.ts +++ b/web/src/themes/presets.ts @@ -184,98 +184,26 @@ export const roseTheme: DashboardTheme = { }, }; -/** - * Nous Blue — the inverted "light mode" Hermes look, ported from the - * LENS_5I overlay preset in `@nous-research/ui`. - * - * Unlike the other built-ins (which paint dark color directly on the - * canvas), this theme relies on ``'s foreground inversion - * layer: an opaque white sheet at z-200 with `mix-blend-mode: difference` - * that flips the entire stack below it. Authoring colors stay dark - * (`#170d02` brown background, `#FFAC02` orange midground), and the - * inversion converts them to their visual complements at paint time — - * the orange midground reads as #0053FD Nous-blue on screen, against a - * cream `#E8F2FD` canvas. - * - * Note on bg blend mode: the DS Lens uses `multiply` for LENS_5I because - * nousnet-web's is white; hermes-agent's App root is `bg-black`, - * so we leave the bg layer's blend mode at the `difference` default — - * `difference(#170d02, #000)` passes the bg through unchanged, and the - * subsequent FG-difference layer then inverts it to cream. Using - * `multiply` here would collapse the bg to pure black against the - * `bg-black` root and produce a plain-white canvas instead of the - * intended cream-blue. - * - * Source of truth for the palette: `design-language/src/ui/components/ - * overlays/lens.ts` (LENS_5I export). - */ +/** Light mode — vivid Nous-blue accents on a cream canvas. */ export const nousBlueTheme: DashboardTheme = { name: "nous-blue", label: "Nous Blue", description: "Light mode — vivid Nous-blue accents on cream canvas", palette: { - background: { hex: "#170d02", alpha: 1 }, - midground: { hex: "#FFAC02", alpha: 1 }, - foreground: { hex: "#FFFFFF", alpha: 1 }, - // Same warm-amber as nousnet-web's overlay glow; after the FG - // inversion it reads as a cool ultraviolet vignette in the top-left. - warmGlow: "rgba(255, 172, 2, 0.18)", - // Noise sits above the FG inversion and is NOT flipped, so a softer - // multiplier keeps it from speckling over the bright post-inversion - // canvas. - noiseOpacity: 0.4, + background: { hex: "#E8F2FD", alpha: 1 }, + midground: { hex: "#0053FD", alpha: 1 }, + foreground: { hex: "#170d02", alpha: 0 }, + warmGlow: "rgba(0, 83, 253, 0.12)", + noiseOpacity: 0, }, typography: DEFAULT_TYPOGRAPHY, layout: DEFAULT_LAYOUT, - // Inverted page: the embedded terminal is below the FG layer too, so - // a `#000000` source paints as visual white — i.e. a proper light-mode - // terminal pane. xterm picks lighter palette colors against the "black" - // canvas, which then read as dark text on screen post-inversion. - terminalBackground: "#000000", - componentStyles: { - backdrop: { - // Lower than LENS_5I.Lens.fillerOpacity (0.06). The filler texture - // gets amplified post-inversion: small variations against the deep - // `#170d02` source bg are barely visible, but those same variations - // against the bright `#E8F2FD` post-inversion canvas read as a - // heavy cloud/marble pattern — especially on near-empty pages - // (loading spinners, blank states). 0.02 keeps subtle grain - // without overwhelming the canvas. - fillerOpacity: "0.02", - }, - }, - // Pre-invert absolute-hex tokens so they read as their familiar colors - // through the FG difference layer. e.g. source #04D3C9 (cyan) is what - // gets painted, and `255 - channel` flips it to #FB2C36 (red) on screen. - // Without these, the default destructive/success/warning tokens would - // appear as their unintuitive complements. - colorOverrides: { - destructive: "#04d3c9", - destructiveForeground: "#000000", - success: "#b5217f", - warning: "#0042c7", - }, - // Pre-inverted data-series accents for the Analytics/Models token - // charts. The defaults (#ffe6cb cream + #34d399 emerald) would render - // through the FG difference layer as dark navy + hot-coral on the - // bright Nous-blue canvas — the coral is the "red" users see for - // Output values without these overrides. Source → on-screen: - // Input: #ffe6cb → #001934 (dark navy) ← unchanged - // Output: #ffac02 → #0053fd (vivid Nous-blue) ← brand accent - // Input keeps the cream source so it stays a neutral, low-contrast - // dark-blue against the cream canvas; output paints as the brand - // Nous-blue so the "primary" series in token-flow charts reads as - // the highlight color, matching the rest of the inverted UI chrome. + terminalBackground: "#f5f8fc", seriesColors: { - inputTokenAccent: "#ffe6cb", - outputTokenAccent: "#ffac02", + inputTokenAccent: "#001934", + outputTokenAccent: "#0053fd", }, - // Explicit picker swatch — the raw palette hex (`#170d02`, `#FFAC02`, - // amber rgba) doesn't reflect what users see after the FG inversion, - // so we paint the post-inversion visual triplet directly: - // white → vivid Nous-blue → cream/light-blue - // matching the actual on-screen rendering of the theme. - swatchColors: ["#FFFFFF", "#0053FD", "#E8F2FD"], + swatchColors: ["#170d02", "#0053FD", "#E8F2FD"], }; /** diff --git a/web/src/themes/types.ts b/web/src/themes/types.ts index 811d67c3c49..8a1462c48e4 100644 --- a/web/src/themes/types.ts +++ b/web/src/themes/types.ts @@ -4,10 +4,9 @@ * Themes customise three orthogonal layers: * * 1. `palette` — the 3-layer color triplet (background/midground/ - * foreground) + warm-glow + noise opacity. The - * design-system cascade in `src/index.css` derives - * every shadcn-compat token (card, muted, border, - * primary, etc.) from this triplet via `color-mix()`. + * foreground). Legacy `warmGlow` / `noiseOpacity` + * fields remain for theme YAML compat but are unused + * by the lightweight shell. * 2. `typography` — font families, base font size, line height, * letter spacing. An optional `fontUrl` is injected * as `` so self-hosted and @@ -33,10 +32,9 @@ export interface ThemePalette { /** Top-layer highlight. In LENS_0 this is white @ alpha 0 — invisible by * default but still drives `--color-ring`-style accents. */ foreground: ThemeLayer; - /** Warm vignette color for , as an rgba() string. */ + /** Legacy palette field — kept for theme YAML compat. */ warmGlow: string; - /** Scalar multiplier (0–1.2) on the noise overlay. Lower for softer themes - * like Mono and Rosé, higher for grittier themes like Cyberpunk. */ + /** Legacy palette field — kept for theme YAML compat. */ noiseOpacity: number; } @@ -79,12 +77,10 @@ export interface ThemeLayout { export type ThemeLayoutVariant = "standard" | "cockpit" | "tiled"; /** Named hero/background assets a theme can populate. Each value is - * emitted as a CSS var (`--theme-asset-`). The default shell - * consumes `bg` in `` when present; other slots are - * plugin-facing — a cockpit sidebar plugin reads `--theme-asset-hero` - * to render its hero render without coupling to the theme name. */ + * 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, injected under the noise layer. */ + /** Full-viewport background image URL. */ bg?: string; /** Hero render (Gundam, mascot, wallpaper) — for plugin sidebars/overlays. */ hero?: string; @@ -103,7 +99,7 @@ export interface ThemeAssets { /** Component-style override buckets. Each bucket's entries become CSS * vars (`--component--`) that shell components - * (Card, Backdrop, App header/footer, etc.) read. Values are plain CSS + * (Card, App header/footer, etc.) read. Values are plain CSS * strings — we don't parse them, so themes can use `clip-path`, * `border-image`, `background`, `box-shadow`, and anything else CSS * accepts. */