mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 15:01:34 +08:00
docs: consolidate dashboard themes and plugins into Extending the Dashboard (#15530)
The web-dashboard.md and dashboard-plugins.md pages had overlapping, partial coverage of the theme and plugin systems. Themes were split across two pages; the plugin docs had a minimal manifest reference but no step-by-step guide, no slot catalog, and no theme+plugin demo. New: user-guide/features/extending-the-dashboard.md — single navigable reference for all three extension layers (themes, UI plugins, backend plugins). Includes: - Theme quick-start + full schema (palette, typography, layout, layout variants, assets, componentStyles, colorOverrides, customCSS) - Plugin quick-start + full schema (manifest, SDK, slots, tab.override, tab.hidden, backend routes, custom CSS) - 10-slot shell catalog with locations - Plugin discovery + load lifecycle - Combined theme+plugin walkthrough (Strike Freedom cockpit demo) - API reference + troubleshooting web-dashboard.md: trimmed to core tool docs (pages, REST API, CORS, development). Theme/plugin content now points to the new page with a built-in themes summary table. dashboard-plugins.md: deleted (merged into extending-the-dashboard.md). sidebars.ts: swap 'dashboard-plugins' → 'extending-the-dashboard' under the Management group. No user-facing behavior change; docs-only.
This commit is contained in:
@@ -321,274 +321,27 @@ The frontend is built with React 19, TypeScript, Tailwind CSS v4, and shadcn/ui-
|
||||
|
||||
When you run `hermes update`, the web frontend is automatically rebuilt if `npm` is available. This keeps the dashboard in sync with code updates. If `npm` isn't installed, the update skips the frontend build and `hermes dashboard` will build it on first launch.
|
||||
|
||||
## Themes
|
||||
## Themes & plugins
|
||||
|
||||
Themes control the dashboard's visual presentation across three layers:
|
||||
The dashboard ships with six built-in themes and can be extended with user-defined themes, plugin tabs, and backend API routes — all drop-in, no repo clone needed.
|
||||
|
||||
- **Palette** — colors (background, text, accents, warm glow, noise)
|
||||
- **Typography** — font families, base size, line height, letter spacing
|
||||
- **Layout** — corner radius and density (spacing multiplier)
|
||||
**Switch themes live** from the header bar — click the palette icon next to the language switcher. Selection persists to `config.yaml` under `dashboard.theme` and is restored on page load.
|
||||
|
||||
Switch themes live from the header bar — click the palette icon next to the language switcher. Selection persists to `config.yaml` under `dashboard.theme` and is restored on page load.
|
||||
Built-in themes:
|
||||
|
||||
### Built-in themes
|
||||
| Theme | Character |
|
||||
|-------|-----------|
|
||||
| **Hermes Teal** (`default`) | Dark teal + cream, system fonts, comfortable spacing |
|
||||
| **Midnight** (`midnight`) | Deep blue-violet, Inter + JetBrains Mono |
|
||||
| **Ember** (`ember`) | Warm crimson + bronze, Spectral serif + IBM Plex Mono |
|
||||
| **Mono** (`mono`) | Grayscale, IBM Plex, compact |
|
||||
| **Cyberpunk** (`cyberpunk`) | Neon green on black, Share Tech Mono |
|
||||
| **Rosé** (`rose`) | Pink + ivory, Fraunces serif, spacious |
|
||||
|
||||
Each built-in ships its own palette, typography, and layout — switching produces visible changes beyond color alone.
|
||||
To build your own theme, add a plugin tab, inject into shell slots, or expose plugin-specific REST endpoints, see **[Extending the Dashboard](./extending-the-dashboard)** — the complete guide covers:
|
||||
|
||||
| Theme | Palette | Typography | Layout |
|
||||
|-------|---------|------------|--------|
|
||||
| **Hermes Teal** (`default`) | Dark teal + cream | System stack, 15px | 0.5rem radius, comfortable |
|
||||
| **Midnight** (`midnight`) | Deep blue-violet | Inter + JetBrains Mono, 14px | 0.75rem radius, comfortable |
|
||||
| **Ember** (`ember`) | Warm crimson / bronze | Spectral (serif) + IBM Plex Mono, 15px | 0.25rem radius, comfortable |
|
||||
| **Mono** (`mono`) | Grayscale | IBM Plex Sans + IBM Plex Mono, 13px | 0 radius, compact |
|
||||
| **Cyberpunk** (`cyberpunk`) | Neon green on black | Share Tech Mono everywhere, 14px | 0 radius, compact |
|
||||
| **Rosé** (`rose`) | Pink and ivory | Fraunces (serif) + DM Mono, 16px | 1rem radius, spacious |
|
||||
|
||||
Themes that reference Google Fonts (everything except Hermes Teal) load the stylesheet on demand — the first time you switch to them, a `<link>` tag is injected into `<head>`.
|
||||
|
||||
### Custom themes
|
||||
|
||||
Drop a YAML file in `~/.hermes/dashboard-themes/` and it appears in the picker automatically. The file can be as minimal as a name plus the fields you want to override — every missing field inherits a sane default.
|
||||
|
||||
Minimal example (colors only, bare hex shorthand):
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/dashboard-themes/neon.yaml
|
||||
name: neon
|
||||
label: Neon
|
||||
description: Pure magenta on black
|
||||
colors:
|
||||
background: "#000000"
|
||||
midground: "#ff00ff"
|
||||
```
|
||||
|
||||
Full example (every knob):
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/dashboard-themes/ocean.yaml
|
||||
name: ocean
|
||||
label: Ocean Deep
|
||||
description: Deep sea blues with coral accents
|
||||
|
||||
palette:
|
||||
background:
|
||||
hex: "#0a1628"
|
||||
alpha: 1.0
|
||||
midground:
|
||||
hex: "#a8d0ff"
|
||||
alpha: 1.0
|
||||
foreground:
|
||||
hex: "#ffffff"
|
||||
alpha: 0.0
|
||||
warmGlow: "rgba(255, 107, 107, 0.35)"
|
||||
noiseOpacity: 0.7
|
||||
|
||||
typography:
|
||||
fontSans: "Poppins, system-ui, sans-serif"
|
||||
fontMono: "Fira Code, ui-monospace, monospace"
|
||||
fontDisplay: "Poppins, system-ui, sans-serif" # optional, falls back to fontSans
|
||||
fontUrl: "https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap"
|
||||
baseSize: "15px"
|
||||
lineHeight: "1.6"
|
||||
letterSpacing: "-0.003em"
|
||||
|
||||
layout:
|
||||
radius: "0.75rem" # 0 | 0.25rem | 0.5rem | 0.75rem | 1rem | any length
|
||||
density: comfortable # compact | comfortable | spacious
|
||||
|
||||
# Optional — pin individual shadcn tokens that would otherwise derive from
|
||||
# the palette. Any key listed here wins over the palette cascade.
|
||||
colorOverrides:
|
||||
destructive: "#ff6b6b"
|
||||
ring: "#ff6b6b"
|
||||
```
|
||||
|
||||
Refresh the dashboard after creating the file.
|
||||
|
||||
### Palette model
|
||||
|
||||
The palette is a 3-layer triplet — **background**, **midground**, **foreground** — plus a warm-glow rgba() string and a noise-opacity multiplier. Every shadcn token (card, muted, border, primary, popover, etc.) is derived from this triplet via CSS `color-mix()` in the dashboard's stylesheet, so overriding three colors cascades into the whole UI.
|
||||
|
||||
- `background` — deepest canvas color (typically near-black). The page background and card fill come from this.
|
||||
- `midground` — primary text and accent. Most UI chrome reads this.
|
||||
- `foreground` — top-layer highlight. In the default theme this is white at alpha 0 (invisible); themes that want a bright accent on top can raise its alpha.
|
||||
- `warmGlow` — rgba() vignette color used by the ambient backdrop.
|
||||
- `noiseOpacity` — 0–1.2 multiplier on the grain overlay. Lower = softer, higher = grittier.
|
||||
|
||||
Each layer accepts `{hex, alpha}` or a bare hex string (alpha defaults to 1.0).
|
||||
|
||||
### Typography model
|
||||
|
||||
| Key | Type | Description |
|
||||
|-----|------|-------------|
|
||||
| `fontSans` | string | CSS font-family stack for body copy (applied to `html`, `body`) |
|
||||
| `fontMono` | string | CSS font-family stack for code blocks, `<code>`, `.font-mono` utilities, dense readouts |
|
||||
| `fontDisplay` | string | Optional heading/display font stack. Falls back to `fontSans` |
|
||||
| `fontUrl` | string | Optional external stylesheet URL. Injected as `<link rel="stylesheet">` in `<head>` on theme switch. Same URL is never injected twice. Works with Google Fonts, Bunny Fonts, self-hosted `@font-face` sheets, anything you can link |
|
||||
| `baseSize` | string | Root font size — controls the rem scale for the whole dashboard. Example: `"14px"`, `"16px"` |
|
||||
| `lineHeight` | string | Default line-height, e.g. `"1.5"`, `"1.65"` |
|
||||
| `letterSpacing` | string | Default letter-spacing, e.g. `"0"`, `"0.01em"`, `"-0.01em"` |
|
||||
|
||||
### Layout model
|
||||
|
||||
| Key | Values | Description |
|
||||
|-----|--------|-------------|
|
||||
| `radius` | any CSS length | Corner-radius token. Cascades into `--radius-sm/md/lg/xl` so every rounded element shifts together. |
|
||||
| `density` | `compact` \| `comfortable` \| `spacious` | Spacing multiplier. Compact = 0.85×, comfortable = 1.0× (default), spacious = 1.2×. Scales Tailwind's base spacing, so padding, gap, and space-between utilities all shift proportionally. |
|
||||
|
||||
### Color overrides (optional)
|
||||
|
||||
Most themes won't need this — the 3-layer palette derives every shadcn token. But if you want a specific accent that the derivation won't produce (a softer destructive red for a pastel theme, a specific success green for a brand), pin individual tokens here.
|
||||
|
||||
Supported keys: `card`, `cardForeground`, `popover`, `popoverForeground`, `primary`, `primaryForeground`, `secondary`, `secondaryForeground`, `muted`, `mutedForeground`, `accent`, `accentForeground`, `destructive`, `destructiveForeground`, `success`, `warning`, `border`, `input`, `ring`.
|
||||
|
||||
Any key set here overrides the derived value for the active theme only — switching to another theme clears the overrides.
|
||||
|
||||
### Layout variants
|
||||
|
||||
`layoutVariant` selects the overall shell layout. Defaults to `standard`.
|
||||
|
||||
| Variant | Behaviour |
|
||||
|---------|-----------|
|
||||
| `standard` | Single column, 1600px max-width (default) |
|
||||
| `cockpit` | Left sidebar rail (260px) + main content. Populated by plugins via the `sidebar` slot |
|
||||
| `tiled` | Drops the max-width clamp so pages can use the full viewport |
|
||||
|
||||
```yaml
|
||||
layoutVariant: cockpit
|
||||
```
|
||||
|
||||
The current variant is exposed as `document.documentElement.dataset.layoutVariant` so custom CSS can target it via `:root[data-layout-variant="cockpit"]`.
|
||||
|
||||
### Theme assets
|
||||
|
||||
Ship artwork URLs with a theme. Each named slot becomes a CSS var (`--theme-asset-<name>`) that plugins and the built-in shell read; the `bg` slot is automatically wired into the backdrop.
|
||||
|
||||
```yaml
|
||||
assets:
|
||||
bg: "https://example.com/hero-bg.jpg" # full-viewport background
|
||||
hero: "/my-images/strike-freedom.png" # for plugin sidebars
|
||||
crest: "/my-images/crest.svg" # for header slot plugins
|
||||
logo: "/my-images/logo.png"
|
||||
sidebar: "/my-images/rail.png"
|
||||
header: "/my-images/header-art.png"
|
||||
custom:
|
||||
scanLines: "/my-images/scanlines.png" # → --theme-asset-custom-scanLines
|
||||
```
|
||||
|
||||
Values accept bare URLs (wrapped in `url(...)` automatically), pre-wrapped `url(...)`/`linear-gradient(...)`/`radial-gradient(...)` expressions, and `none`.
|
||||
|
||||
### Component chrome overrides
|
||||
|
||||
Themes can restyle individual shell components without writing CSS selectors via the `componentStyles` block. Each bucket's entries become CSS vars (`--component-<bucket>-<kebab-property>`) that the shell's shared components read — so `card:` overrides apply to every `<Card>`, `header:` to the app bar, etc.
|
||||
|
||||
```yaml
|
||||
componentStyles:
|
||||
card:
|
||||
clipPath: "polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px)"
|
||||
background: "linear-gradient(180deg, rgba(10, 22, 52, 0.85), rgba(5, 9, 26, 0.92))"
|
||||
boxShadow: "inset 0 0 0 1px rgba(64, 200, 255, 0.28)"
|
||||
header:
|
||||
background: "linear-gradient(180deg, rgba(16, 32, 72, 0.95), rgba(5, 9, 26, 0.9))"
|
||||
tab:
|
||||
clipPath: "polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%)"
|
||||
sidebar: {...}
|
||||
backdrop: {...}
|
||||
footer: {...}
|
||||
progress: {...}
|
||||
badge: {...}
|
||||
page: {...}
|
||||
```
|
||||
|
||||
Supported buckets: `card`, `header`, `footer`, `sidebar`, `tab`, `progress`, `badge`, `backdrop`, `page`. Property names use camelCase (`clipPath`) and are emitted as kebab (`clip-path`). Values are plain CSS strings — anything CSS accepts (`clip-path`, `border-image`, `background`, `box-shadow`, animations, etc.).
|
||||
|
||||
### Custom CSS
|
||||
|
||||
For selector-level chrome that doesn't fit `componentStyles` — pseudo-elements, animations, media queries, theme-scoped overrides — drop raw CSS into the `customCSS` field:
|
||||
|
||||
```yaml
|
||||
customCSS: |
|
||||
:root[data-layout-variant="cockpit"] body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
transparent 0px, transparent 2px,
|
||||
rgba(64, 200, 255, 0.035) 3px, rgba(64, 200, 255, 0.035) 4px);
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
```
|
||||
|
||||
The CSS is injected as a single scoped `<style data-hermes-theme-css>` tag on theme apply and cleaned up on theme switch. Capped at 32 KiB per theme.
|
||||
|
||||
## Dashboard plugins
|
||||
|
||||
Plugins live in `~/.hermes/plugins/<name>/dashboard/` (user) or repo `plugins/<name>/dashboard/` (bundled). Each ships a `manifest.json` plus a plain JS bundle that uses the plugin SDK exposed on `window.__HERMES_PLUGIN_SDK__`.
|
||||
|
||||
### Manifest
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"label": "My Plugin",
|
||||
"icon": "Sparkles",
|
||||
"version": "1.0.0",
|
||||
"tab": {
|
||||
"path": "/my-plugin",
|
||||
"position": "after:skills",
|
||||
"override": "/",
|
||||
"hidden": false
|
||||
},
|
||||
"slots": ["sidebar", "header-left"],
|
||||
"entry": "dist/index.js",
|
||||
"css": "dist/index.css",
|
||||
"api": "api.py"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `tab.path` | Route path the plugin component renders at |
|
||||
| `tab.position` | `end`, `after:<tab>`, or `before:<tab>` |
|
||||
| `tab.override` | When set to a built-in path (`/`, `/sessions`, etc.), this plugin replaces that page instead of adding a new tab |
|
||||
| `tab.hidden` | When true, register component + slots but skip the nav entry. Used by slot-only plugins |
|
||||
| `slots` | Shell slots this plugin populates (documentation aid; actual registration happens from the JS bundle) |
|
||||
|
||||
### Shell slots
|
||||
|
||||
Plugins inject components into named shell locations by calling `window.__HERMES_PLUGINS__.registerSlot(pluginName, slotName, Component)`. Multiple plugins can populate the same slot — they render stacked in registration order.
|
||||
|
||||
| Slot | Location |
|
||||
|------|----------|
|
||||
| `backdrop` | Inside the backdrop layer stack |
|
||||
| `header-left` | Before the Hermes brand in the top bar |
|
||||
| `header-right` | Before the theme/language switchers |
|
||||
| `header-banner` | Full-width strip below the nav |
|
||||
| `sidebar` | Cockpit sidebar rail (only rendered when `layoutVariant === "cockpit"`) |
|
||||
| `pre-main` | Above the route outlet |
|
||||
| `post-main` | Below the route outlet |
|
||||
| `footer-left` / `footer-right` | Footer cell content (replaces default) |
|
||||
| `overlay` | Fixed-position layer above everything else |
|
||||
|
||||
### Plugin SDK
|
||||
|
||||
Exposed on `window.__HERMES_PLUGIN_SDK__`:
|
||||
|
||||
- `React` + `hooks` (useState, useEffect, useCallback, useMemo, useRef, useContext, createContext)
|
||||
- `components` — Card, Badge, Button, Input, Label, Select, Separator, Tabs, **PluginSlot**
|
||||
- `api` — Hermes API client, plus raw `fetchJSON`
|
||||
- `utils` — `cn()`, `timeAgo()`, `isoTimeAgo()`
|
||||
- `useI18n` — i18n hook for multi-language plugins
|
||||
|
||||
### Demo: Strike Freedom Cockpit
|
||||
|
||||
`plugins/strike-freedom-cockpit/` ships a complete skin demo showing every extension point — cockpit layout variant, theme-supplied hero/crest assets, notched card corners via `componentStyles`, scanlines via `customCSS`, and a slot-only plugin that populates the sidebar, header, and footer. Copy the theme YAML into `~/.hermes/dashboard-themes/` and the plugin directory into `~/.hermes/plugins/` to try it.
|
||||
|
||||
### Theme API
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/dashboard/themes` | GET | List available themes + active name. Built-ins return `{name, label, description}`; user themes also include a `definition` field with the full normalised theme object. |
|
||||
| `/api/dashboard/theme` | PUT | Set active theme. Body: `{"name": "midnight"}` |
|
||||
- Theme YAML schema — palette, typography, layout, assets, componentStyles, colorOverrides, customCSS
|
||||
- Layout variants — `standard`, `cockpit`, `tiled`
|
||||
- Plugin manifest, SDK, shell slots, backend FastAPI routes
|
||||
- A full combined theme-plus-plugin walkthrough (Strike Freedom cockpit demo)
|
||||
- Discovery, reload, and troubleshooting
|
||||
|
||||
Reference in New Issue
Block a user