* feat(web): add collapsible sidebar for the dashboard
The desktop sidebar can now be collapsed to an icon-only rail via a
toggle button in the sidebar header. State is persisted in
localStorage so it survives page reloads.
When collapsed (lg+ only):
- Sidebar shrinks from w-64 to w-14 with a smooth width transition
- Nav items show only their icon with a native title tooltip
- Brand text, plugin headings, system actions, theme/language
switchers, auth widget, and footer are hidden
- Mobile drawer behavior is unchanged (always full-width)
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web): align sidebar tooltips to sidebar edge consistently
Tooltip left position now uses the sidebar's right edge instead of the
anchor element's right edge, so narrow anchors (theme/language switchers)
align with full-width anchors (nav links, system actions).
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(web): add tooltip animations, restore theme label, rename Sessions tab
- Sidebar tooltips now animate in with a subtle 120ms ease-out slide;
subsequent tooltips within the same hover sequence appear instantly
(no delay/animation) following Emil Kowalski's tooltip pattern
- Restore theme name label when sidebar is expanded
- Rename Sessions segment tab to "History" across all 16 locales
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web): smooth sidebar collapse animation
- Remove icon centering on collapse; icons stay left-aligned at px-5
so they don't jump during the width transition
- Text labels fade out with opacity transition instead of instant
display:none, clipped naturally by overflow-hidden
- Slow collapse duration from 450ms to 600ms for a more relaxed feel
- Gateway dot always rendered with opacity toggle so it doesn't
slide in from the right on collapse
- Pin gateway dot at fixed left offset (pl-[1.625rem]) to align
with nav icons
- Align header toggle button with justify-center when collapsed
- Bottom switchers use items-start when collapsed to prevent reflow
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Removes the global `uppercase` + `font-mondwest` from the App.tsx root
that forced every page to opt-out, replaces stacked-alpha text colors
with semantic tokens for WCAG-AA contrast across all 7 themes, and
applies the new `text-display` utility from @nous-research/ui@0.16.0
on intentional brand chrome (page titles, sidebar headings, segmented
filters) only. Bumps every sub-12px arbitrary text size to text-xs.
Also widens the dashboard plugin routes (/api/dashboard/agent-plugins/
{name:path}/...) so category-namespaced plugins like observability/
langfuse and image_gen/openai can be enable/disabled from the dashboard
— previously the FE encodeURIComponent-ed the slash and the backend
{name} route rejected it. _validate_plugin_name still blocks .. and
backslash, and strips leading/trailing slash.
Touches sessions/env/keys page chrome and adds two new i18n keys
(`overview`, `showMore`/`showLess`) across all 18 locales.
Squashes 19 commits from PR #28832.
Co-authored-by: Hermes <noreply@nousresearch.com>
* feat(web): mobile dashboard UX polish
Bottom sheets for sidebar theme/language pickers on narrow viewports with
enter/exit animation and drag-to-close; inline header badges beside titles;
bottom padding on the route outlet for scroll clearance; profiles loading uses a
unicode braille spinner; align profile/cron card actions to the top; viewport-fit
cover and supporting layout tweaks across dashboard pages.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix Nix web npm hash and mobile sheet accessibility.
Align fetchNpmDeps in nix/web.nix with web/package-lock.json for CI. Improve BottomPickSheet backdrop labeling, avoid aria-hidden on the dialog during exit animation, and wire theme/language sheets with listbox semantics and localized dismiss labels.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
The header theme picker (`ThemeSwitcher`) renders a `role="listbox"` popup
with no `max-height` or overflow. With 20+ community themes installed under
`~/.hermes/dashboard-themes/`, the list extends past the viewport and themes
at the top or bottom are unreachable — the user reports only 15 of 26 themes
visible, with no scrollbar to access the rest.
Sibling switchers (`LanguageSwitcher`, `SlashPopover`) already cap their
listboxes (`max-h-80 overflow-y-auto` / `max-h-64 overflow-y-auto`); this
just brings the theme picker into line. Scoped to the component instead of
a global `div[role="listbox"]` CSS rule so other dropdowns aren't affected.
`70dvh` matches the user's tested workaround and the `dvh` unit handles
mobile browser UI chrome correctly (unlike `vh`).
Fixes#25213.
Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a theme engine for the web dashboard that mirrors the CLI skin
engine philosophy — pure data, no code changes needed for new themes.
Frontend:
- ThemeProvider context that loads active theme from backend on mount
and applies CSS variable overrides to document.documentElement
- ThemeSwitcher dropdown component in the header (next to language
switcher) with instant preview on click
- 6 built-in themes: Hermes Teal (default), Midnight, Ember, Mono,
Cyberpunk, Rosé — each defines all 21 color tokens + overlay settings
- Theme types, presets, and context in web/src/themes/
Backend:
- GET /api/dashboard/themes — returns available themes + active name
- PUT /api/dashboard/theme — persists selection to config.yaml
- User custom themes discoverable from ~/.hermes/dashboard-themes/*.yaml
- Theme list endpoint added to public API paths (no auth needed)
Config:
- dashboard.theme key in DEFAULT_CONFIG (default: 'default')
- Schema override for select dropdown in config page
- Category merged into 'display' tab in config UI
i18n: theme switcher strings added for en + zh.