docs: v0.34 release — themes CHANGELOG, README, add light to picker

- CHANGELOG: v0.34 Sprint 26 entry (6 themes, /theme command, settings UX)
- README: themes section, updated slash commands, THEMES.md in docs list
- THEMES.md: added Slate to theme table, matches actual CSS/dropdown
- commands.js: added 'light' to /theme valid list, updated description
- index.html: added Light option to theme dropdown, version v0.34
- SPRINTS/CHANGELOG footers updated to v0.34 / 433 tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nathan Esquenazi
2026-04-04 21:13:01 -07:00
parent 805fa296c8
commit 82a942a2b1
6 changed files with 55 additions and 9 deletions

View File

@@ -5,6 +5,42 @@
--- ---
## [v0.34] Sprint 26 -- Pluggable UI Themes
*April 5, 2026 | 433 tests*
### Features
- **6 built-in themes.** Dark (default), Light, Slate, Solarized Dark, Monokai,
Nord. Defined as CSS variable overrides on `:root[data-theme="name"]` — the
entire UI adapts automatically.
- **Theme picker in Settings.** Dropdown with instant live preview. Changes
apply immediately as you click through options.
- **`/theme` slash command.** `/theme dark`, `/theme light`, etc.
- **Theme persistence.** Saved server-side in `settings.json` and client-side
in `localStorage` for flicker-free loading on page refresh.
- **Flash prevention.** Inline `<script>` in `<head>` reads localStorage before
the stylesheet loads — no flash of the wrong theme.
- **Custom theme support.** Any theme name is accepted (no enum gate). Create a
`:root[data-theme="name"]` CSS block and it works. See `THEMES.md`.
- **Unsaved changes guard.** Settings panel now tracks dirty state and shows a
"You have unsaved changes" bar with Save/Discard buttons when closing with
unpersisted changes. Theme preview reverts on discard.
### Architecture
- `static/style.css`: 6 theme blocks using CSS variable overrides. Light theme
includes scrollbar and selection overrides.
- `static/commands.js`: `/theme` command with validation.
- `static/panels.js`: Settings dirty tracking, revert-on-discard, unsaved bar.
- `static/boot.js`: Theme applied from server settings on boot.
- `api/config.py`: `theme` field in `_SETTINGS_DEFAULTS` (no enum gate).
- `THEMES.md`: Full documentation for creating custom themes.
### Tests
- 9 new tests in `test_sprint26.py`: default theme, round-trip persistence for
all 6 built-in themes, custom theme acceptance, settings isolation.
Total: **433 tests**.
---
## [v0.33] /insights Sync + state.db Bridge Fix ## [v0.33] /insights Sync + state.db Bridge Fix
*April 5, 2026 | 424 tests* *April 5, 2026 | 424 tests*
@@ -1152,4 +1188,4 @@ Three-panel layout: sessions sidebar, chat area, workspace panel.
--- ---
*Last updated: v0.32, April 5, 2026 | Tests: 424* *Last updated: v0.34, April 5, 2026 | Tests: 433*

View File

@@ -314,17 +314,24 @@ across 22 test files.
- 20MB POST body size limit - 20MB POST body size limit
- CDN resources pinned with SRI integrity hashes - CDN resources pinned with SRI integrity hashes
### Themes
- 6 built-in themes: Dark (default), Light, Slate, Solarized Dark, Monokai, Nord
- Switch via Settings panel dropdown (instant live preview) or `/theme` command
- Persists across reloads (server-side in settings.json + localStorage for flicker-free loading)
- Custom themes: define a `:root[data-theme="name"]` CSS block and it works — see [THEMES.md](THEMES.md)
### Settings and configuration ### Settings and configuration
- Settings panel (gear icon) -- default model, default workspace, send key preference - Settings panel (gear icon) -- default model, default workspace, send key, theme
- Send key: Enter (default) or Ctrl/Cmd+Enter - Send key: Enter (default) or Ctrl/Cmd+Enter
- Show/hide CLI sessions toggle (enabled by default) - Show/hide CLI sessions toggle (enabled by default)
- Token usage display toggle (off by default, also via `/usage` command) - Token usage display toggle (off by default, also via `/usage` command)
- Unsaved changes guard -- discard/save prompt when closing with unpersisted changes
- Cron completion alerts -- toast notifications and unread badge on Tasks tab - Cron completion alerts -- toast notifications and unread badge on Tasks tab
- Background agent error alerts -- banner when a non-active session encounters an error - Background agent error alerts -- banner when a non-active session encounters an error
### Slash commands ### Slash commands
- Type `/` in the composer for autocomplete dropdown - Type `/` in the composer for autocomplete dropdown
- Built-in: `/help`, `/clear`, `/model <name>`, `/workspace <name>`, `/new`, `/usage` - Built-in: `/help`, `/clear`, `/model <name>`, `/workspace <name>`, `/new`, `/usage`, `/theme`, `/compact`
- Arrow keys navigate, Tab/Enter select, Escape closes - Arrow keys navigate, Tab/Enter select, Escape closes
- Unrecognized commands pass through to the agent - Unrecognized commands pass through to the agent
@@ -393,6 +400,7 @@ State lives outside the repo at `~/.hermes/webui-mvp/` by default
- `TESTING.md` -- manual browser test plan and automated coverage reference - `TESTING.md` -- manual browser test plan and automated coverage reference
- `CHANGELOG.md` -- release notes per sprint - `CHANGELOG.md` -- release notes per sprint
- `SPRINTS.md` -- forward sprint plan with CLI + Claude parity targets - `SPRINTS.md` -- forward sprint plan with CLI + Claude parity targets
- `THEMES.md` -- theme system documentation, custom theme guide
## Repo ## Repo

View File

@@ -1,6 +1,6 @@
# Hermes Web UI -- Forward Sprint Plan # Hermes Web UI -- Forward Sprint Plan
> Current state: v0.32 | 424 tests | Daily driver ready > Current state: v0.34 | 433 tests | Daily driver ready
> This document plans the path from here to two targets: > This document plans the path from here to two targets:
> >
> Target A: 1:1 feature parity with the Hermes CLI (everything you can do from the > Target A: 1:1 feature parity with the Hermes CLI (everything you can do from the
@@ -1156,6 +1156,6 @@ New test cases in `tests/test_sprint26.py`:
--- ---
*Last updated: April 5, 2026* *Last updated: April 5, 2026*
*Current version: v0.33 | 424 tests* *Current version: v0.34 | 433 tests*
*Next sprint: Sprint 24 (Web Polish + Bug Fix Pass)* *Next sprint: Sprint 24 (Web Polish + Bug Fix Pass)*
*Horizon sprint: Sprint 26 (Pluggable UI Themes)* *Horizon sprint: Sprint 26 (Pluggable UI Themes)*

View File

@@ -22,7 +22,8 @@ preview is instant — the UI updates as you click through options.
| Theme | Description | | Theme | Description |
|-------|-------------| |-------|-------------|
| **Dark** (default) | Deep navy/indigo with muted blue accents. Easy on the eyes for long sessions. | | **Dark** (default) | Deep navy/indigo with muted blue accents. Easy on the eyes for long sessions. |
| **Light** | Clean white/gray with dark text. High contrast for bright environments. | | **Light** | Warm off-white with dark text. High contrast for bright environments. |
| **Slate** | Warm charcoal, lighter than Dark. Easier on the eyes for extended use. |
| **Solarized Dark** | Ethan Schoonover's classic dark palette. Teal background, warm accents. | | **Solarized Dark** | Ethan Schoonover's classic dark palette. Teal background, warm accents. |
| **Monokai** | Warm dark theme inspired by the Monokai editor scheme. Green/pink accents. | | **Monokai** | Warm dark theme inspired by the Monokai editor scheme. Green/pink accents. |
| **Nord** | Arctic blue-gray palette from the Nord color system. Calm and minimal. | | **Nord** | Arctic blue-gray palette from the Nord color system. Calm and minimal. |

View File

@@ -10,7 +10,7 @@ const COMMANDS=[
{name:'workspace', desc:'Switch workspace by name', fn:cmdWorkspace, arg:'name'}, {name:'workspace', desc:'Switch workspace by name', fn:cmdWorkspace, arg:'name'},
{name:'new', desc:'Start a new chat session', fn:cmdNew}, {name:'new', desc:'Start a new chat session', fn:cmdNew},
{name:'usage', desc:'Toggle token usage display on/off', fn:cmdUsage}, {name:'usage', desc:'Toggle token usage display on/off', fn:cmdUsage},
{name:'theme', desc:'Switch theme (dark/light/solarized/monokai/nord)', fn:cmdTheme, arg:'name'}, {name:'theme', desc:'Switch theme (dark/light/slate/solarized/monokai/nord)', fn:cmdTheme, arg:'name'},
]; ];
function parseCommand(text){ function parseCommand(text){
@@ -124,7 +124,7 @@ async function cmdUsage(){
} }
async function cmdTheme(args){ async function cmdTheme(args){
const themes=['dark','slate','solarized','monokai','nord']; const themes=['dark','light','slate','solarized','monokai','nord'];
if(!args||!themes.includes(args.toLowerCase())){ if(!args||!themes.includes(args.toLowerCase())){
showToast('Usage: /theme '+themes.join('|')); showToast('Usage: /theme '+themes.join('|'));
return; return;

View File

@@ -14,7 +14,7 @@
<body> <body>
<div class="layout"> <div class="layout">
<aside class="sidebar"> <aside class="sidebar">
<div class="sidebar-header"><div class="logo">H</div><div><h1 style="margin:0;font-size:15px;font-weight:700;letter-spacing:-.01em">Hermes</h1><div style="font-size:10px;color:var(--muted);opacity:.8;margin-top:1px">v0.33</div></div></div> <div class="sidebar-header"><div class="logo">H</div><div><h1 style="margin:0;font-size:15px;font-weight:700;letter-spacing:-.01em">Hermes</h1><div style="font-size:10px;color:var(--muted);opacity:.8;margin-top:1px">v0.34</div></div></div>
<div class="sidebar-nav"> <div class="sidebar-nav">
<button class="nav-tab active" data-panel="chat" data-label="Chat" onclick="switchPanel('chat')" title="Chat">&#128172;</button> <button class="nav-tab active" data-panel="chat" data-label="Chat" onclick="switchPanel('chat')" title="Chat">&#128172;</button>
<button class="nav-tab" data-panel="tasks" data-label="Tasks" onclick="switchPanel('tasks')" title="Tasks">&#128197;</button> <button class="nav-tab" data-panel="tasks" data-label="Tasks" onclick="switchPanel('tasks')" title="Tasks">&#128197;</button>
@@ -334,6 +334,7 @@
<label for="settingsTheme">Theme</label> <label for="settingsTheme">Theme</label>
<select id="settingsTheme" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px" onchange="document.documentElement.dataset.theme=this.value;localStorage.setItem('hermes-theme',this.value)"> <select id="settingsTheme" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px" onchange="document.documentElement.dataset.theme=this.value;localStorage.setItem('hermes-theme',this.value)">
<option value="dark">Dark (default)</option> <option value="dark">Dark (default)</option>
<option value="light">Light</option>
<option value="slate">Slate (charcoal)</option> <option value="slate">Slate (charcoal)</option>
<option value="solarized">Solarized Dark</option> <option value="solarized">Solarized Dark</option>
<option value="monokai">Monokai</option> <option value="monokai">Monokai</option>