Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [Unreleased]

### Documentation

- Align the `docs/ui-ux/` demo appearance controls with the current Theme + Skin
model, replacing the stale `data-theme`-only buttons and legacy theme names.

## [v0.51.89] — 2026-05-18 — Release BM (stage-382 — 6-PR full sweep batch — runtime adapter approval/clarify seam + SOUL.md memory panel + #1855 resolve_model_provider fast-path + PWA sidebar spinner fix + /model active-provider preference + contributor contract docs index)

### Changed
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 39 additions & 14 deletions docs/ui-ux/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" data-theme="slate">
<html lang="en" class="dark" data-skin="slate">
<head>
<meta charset="utf-8">
<title>Hermes WebUI — Messages UI Inventory</title>
Expand Down Expand Up @@ -54,14 +54,21 @@
<div class="doc-title">Hermes WebUI — Messages UI Inventory<small>Every message-area element &amp; combination, wired to the real <code>static/style.css</code>. &nbsp;·&nbsp; <a href="./two-stage-proposal.html" style="color:var(--blue);text-decoration:none;">Two-stage proposal (#536) →</a></small></div>
<div class="doc-toggles">
<strong style="font-size:10px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;align-self:center;margin-right:4px;">Theme</strong>
<button data-theme-btn="default">Default</button>
<button data-theme-btn="slate" class="on">Slate</button>
<button data-theme-btn="light">Light</button>
<button data-theme-btn="solarized">Solarized</button>
<button data-theme-btn="monokai">Monokai</button>
<button data-theme-btn="nord">Nord</button>
<button data-theme-btn="oled">OLED</button>
<button data-mode-btn="system">System</button>
<button data-mode-btn="dark" class="on">Dark</button>
<button data-mode-btn="light">Light</button>
<span style="width:1px;height:18px;background:var(--border);margin:0 4px;align-self:center;"></span>
<strong style="font-size:10px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;align-self:center;margin-right:4px;">Skin</strong>
<button data-skin-btn="default">Default</button>
<button data-skin-btn="ares">Ares</button>
<button data-skin-btn="mono">Mono</button>
<button data-skin-btn="slate" class="on">Slate</button>
<button data-skin-btn="poseidon">Poseidon</button>
<button data-skin-btn="sisyphus">Sisyphus</button>
<button data-skin-btn="charizard">Charizard</button>
<button data-skin-btn="sienna">Sienna</button>
<button data-skin-btn="catppuccin">Catppuccin</button>
<button data-skin-btn="nous">Nous</button>
</div>
</header>

Expand Down Expand Up @@ -817,15 +824,33 @@ <h3 style="font-size:13px;color:var(--text);margin:14px 0 8px;">Data attributes
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false},{left:'$',right:'$',display:false}],throwOnError:false});"></script>

<script>
// Theme picker
document.querySelectorAll('[data-theme-btn]').forEach(btn => {
// Appearance picker (matches the real app contract: theme class + skin data attribute)
let docTheme = 'dark';
let docSkin = 'slate';
const docThemeMq = window.matchMedia('(prefers-color-scheme: dark)');
function applyDocAppearance() {
const resolvedTheme = docTheme === 'system' ? (docThemeMq.matches ? 'dark' : 'light') : docTheme;
document.documentElement.classList.toggle('dark', resolvedTheme === 'dark');
if (docSkin === 'default') document.documentElement.removeAttribute('data-skin');
else document.documentElement.dataset.skin = docSkin;
}
document.querySelectorAll('[data-mode-btn]').forEach(btn => {
btn.addEventListener('click', () => {
const t = btn.dataset.themeBtn;
if (t === 'default') document.documentElement.removeAttribute('data-theme');
else document.documentElement.setAttribute('data-theme', t);
document.querySelectorAll('[data-theme-btn]').forEach(b => b.classList.toggle('on', b === btn));
docTheme = btn.dataset.modeBtn;
applyDocAppearance();
document.querySelectorAll('[data-mode-btn]').forEach(b => b.classList.toggle('on', b === btn));
});
});
document.querySelectorAll('[data-skin-btn]').forEach(btn => {
btn.addEventListener('click', () => {
docSkin = btn.dataset.skinBtn;
applyDocAppearance();
document.querySelectorAll('[data-skin-btn]').forEach(b => b.classList.toggle('on', b === btn));
});
});
if (docThemeMq.addEventListener) {
docThemeMq.addEventListener('change', () => { if (docTheme === 'system') applyDocAppearance(); });
}
// Bubble-layout toggle

// Thinking / tool-card click-to-toggle (so the demo feels live)
Expand Down
54 changes: 40 additions & 14 deletions docs/ui-ux/two-stage-proposal.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" data-theme="slate">
<html lang="en" class="dark" data-skin="slate">
<head>
<meta charset="utf-8">
<title>Hermes WebUI — Two-Stage Chat Proposal (Issue #536)</title>
Expand Down Expand Up @@ -167,13 +167,21 @@
</div>
<div class="doc-toggles">
<strong style="font-size:10px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;align-self:center;margin-right:4px;">Theme</strong>
<button data-theme-btn="default">Default</button>
<button data-theme-btn="slate" class="on">Slate</button>
<button data-theme-btn="light">Light</button>
<button data-theme-btn="solarized">Solarized</button>
<button data-theme-btn="monokai">Monokai</button>
<button data-theme-btn="nord">Nord</button>
<button data-theme-btn="oled">OLED</button>
<button data-mode-btn="system">System</button>
<button data-mode-btn="dark" class="on">Dark</button>
<button data-mode-btn="light">Light</button>
<span style="width:1px;height:18px;background:var(--border);margin:0 4px;align-self:center;"></span>
<strong style="font-size:10px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;align-self:center;margin-right:4px;">Skin</strong>
<button data-skin-btn="default">Default</button>
<button data-skin-btn="ares">Ares</button>
<button data-skin-btn="mono">Mono</button>
<button data-skin-btn="slate" class="on">Slate</button>
<button data-skin-btn="poseidon">Poseidon</button>
<button data-skin-btn="sisyphus">Sisyphus</button>
<button data-skin-btn="charizard">Charizard</button>
<button data-skin-btn="sienna">Sienna</button>
<button data-skin-btn="catppuccin">Catppuccin</button>
<button data-skin-btn="nous">Nous</button>
</div>
</header>

Expand Down Expand Up @@ -698,15 +706,33 @@ <h3 style="font-size:13px;color:var(--text);margin:14px 0 8px;">Implementation n
</main>

<script>
// Theme picker (matches index.html)
document.querySelectorAll('[data-theme-btn]').forEach(btn => {
// Appearance picker (matches the real app contract: theme class + skin data attribute)
let docTheme = 'dark';
let docSkin = 'slate';
const docThemeMq = window.matchMedia('(prefers-color-scheme: dark)');
function applyDocAppearance() {
const resolvedTheme = docTheme === 'system' ? (docThemeMq.matches ? 'dark' : 'light') : docTheme;
document.documentElement.classList.toggle('dark', resolvedTheme === 'dark');
if (docSkin === 'default') document.documentElement.removeAttribute('data-skin');
else document.documentElement.dataset.skin = docSkin;
}
document.querySelectorAll('[data-mode-btn]').forEach(btn => {
btn.addEventListener('click', () => {
const t = btn.dataset.themeBtn;
if (t === 'default') document.documentElement.removeAttribute('data-theme');
else document.documentElement.setAttribute('data-theme', t);
document.querySelectorAll('[data-theme-btn]').forEach(b => b.classList.toggle('on', b === btn));
docTheme = btn.dataset.modeBtn;
applyDocAppearance();
document.querySelectorAll('[data-mode-btn]').forEach(b => b.classList.toggle('on', b === btn));
});
});
document.querySelectorAll('[data-skin-btn]').forEach(btn => {
btn.addEventListener('click', () => {
docSkin = btn.dataset.skinBtn;
applyDocAppearance();
document.querySelectorAll('[data-skin-btn]').forEach(b => b.classList.toggle('on', b === btn));
});
});
if (docThemeMq.addEventListener) {
docThemeMq.addEventListener('change', () => { if (docTheme === 'system') applyDocAppearance(); });
}

// Existing thinking/tool cards click-to-toggle.
document.querySelectorAll('.thinking-card-header, .tool-card-header').forEach(h => {
Expand Down
40 changes: 40 additions & 0 deletions tests/test_uiux_docs_theme_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from pathlib import Path


DEMO_DOCS = [
Path("docs/ui-ux/index.html"),
Path("docs/ui-ux/two-stage-proposal.html"),
]

THEME_VALUES = ("system", "dark", "light")
SKIN_VALUES = (
"default",
"ares",
"mono",
"slate",
"poseidon",
"sisyphus",
"charizard",
"sienna",
"catppuccin",
"nous",
)
LEGACY_THEME_LABELS = ("Solarized", "Monokai", "Nord", "OLED")


def test_uiux_demo_docs_use_current_theme_skin_axes():
for doc_path in DEMO_DOCS:
html = doc_path.read_text()
assert 'data-theme="' not in html, f"{doc_path} should not use legacy data-theme"
assert 'class="dark" data-skin="slate"' in html
assert "classList.toggle('dark'" in html
assert "dataset.skin" in html

for theme in THEME_VALUES:
assert f'data-mode-btn="{theme}"' in html

for skin in SKIN_VALUES:
assert f'data-skin-btn="{skin}"' in html

for label in LEGACY_THEME_LABELS:
assert f">{label}<" not in html
Loading