Skip to content

docs: align UI/UX demo theme controls - #2511

Merged
1 commit merged into
nesquena:masterfrom
franksong2702:franksong2702/fix-uiux-theme-demo-controls
May 18, 2026
Merged

1 commit merged into
nesquena:masterfrom
franksong2702:franksong2702/fix-uiux-theme-demo-controls

Conversation

@franksong2702

Copy link
Copy Markdown
Contributor

Thinking Path

  • The contributor contract index from docs: add contributor contract index #2503 now says UI/theme work should follow
    the current Theme + Skin model.
  • docs/ui-ux/index.html and docs/ui-ux/two-stage-proposal.html still showed
    the old data-theme-only picker with legacy theme names.
  • Those demo pages are linked from the new contract docs, so leaving them stale
    would keep a misleading contributor path alive.
  • This PR updates only the docs/demo controls and adds a small static regression
    test so the pages do not drift back to the old model.

Refs #2502 and #2503.

What Changed

  • Updated both docs/ui-ux/ demo pages to initialize as
    class="dark" data-skin="slate" instead of data-theme="slate".
  • Replaced the old single-axis buttons (Default, Slate, Light,
    Solarized, Monokai, Nord, OLED) with:
    • Theme: System, Dark, Light
    • Skin: Default, Ares, Mono, Slate, Poseidon, Sisyphus,
      Charizard, Sienna, Catppuccin, Nous
  • Updated the inline demo script to toggle .dark and data-skin, matching the
    current app contract.
  • Added tests/test_uiux_docs_theme_contract.py to lock the demo docs to the
    current theme/skin axes.
  • Added before/after screenshots for review.
  • Added an Unreleased changelog note.

Why It Matters

The contract docs now point contributors at these UI/UX demos. If the demos keep
teaching the old theme model, contributors can still copy stale guidance even
after reading the new contract index.

This follow-up makes the public docs agree with each other:

  • docs/CONTRACTS.md says the current appearance contract is Theme + Skin.
  • docs/UIUX-GUIDE.md warns against stale data-theme-only guidance.
  • The linked demo pages now show the same model.

Before / After

docs/ui-ux/index.html

Before index demo

After index demo

docs/ui-ux/two-stage-proposal.html

Before two-stage demo

After two-stage demo

Verification

  • git diff --check
  • rg -n "data-theme=|theme-btn|Solarized|Monokai|Nord|OLED" docs/ui-ux/index.html docs/ui-ux/two-stage-proposal.html
  • python3 -m pytest tests/test_uiux_docs_theme_contract.py tests/test_docs_gitignore_policy.py -q
  • npx playwright screenshot --channel=chrome --viewport-size=1280,260 ... for both demo pages before and after

Risks / Follow-ups

  • This does not change the shipped app appearance picker or runtime settings.
  • This does not implement PR-template or CI gates from the contract-index
    follow-up list.
  • The two-stage proposal page still remains a proposal/demo page; this PR only
    updates its appearance controls.

Model Used

OpenAI GPT-5.5 via Codex, with local shell/git/gh tooling.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Summary

Reading docs/ui-ux/index.html and docs/ui-ux/two-stage-proposal.html on this branch against origin/master, plus the real app's appearance contract at static/boot.js:1207-1255, the demo docs were several months out of date — they still wired the deprecated data-theme="slate|solarized|monokai|nord|oled" attribute API while the live app moved to a two-axis class="dark" + data-skin="..." model. This PR realigns the demos so contributors looking at the static inventory see the same shape they'll find in the real DOM.

Code reference

The new demo wiring at docs/ui-ux/index.html:826-855:

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;
}

This matches the live app's behavior at static/boot.js:1252-1254 (_setResolvedTheme):

function _setResolvedTheme(isDark){
  document.documentElement.classList.toggle('dark',!!isDark);
  ...
}

and the skin axis lookup at static/boot.js:1217-1224 (_normalizeAppearance), which uses _VALID_THEMES (light|dark|system) for the mode axis and _VALID_SKINS (default|ares|mono|slate|poseidon|sisyphus|charizard|sienna|catppuccin|nous) for the skin axis. The PR's skin button list at docs/ui-ux/index.html:65-74 enumerates exactly those ten skins.

Diagnosis / Recommendation

The shape is correct. The legacy data-theme -> {theme, skin} map at static/boot.js:1204-1213 (Solarized -> {theme:'light', skin:'default'}, Monokai -> {theme:'dark', skin:'sisyphus'}, Nord -> {theme:'dark', skin:'slate'}, OLED -> {theme:'dark', skin:'default'}) is the upstream rationale for dropping those buttons from the demo: they were never a real axis, only a legacy compatibility translation. Good call to drop them rather than translate them.

The contract test at tests/test_uiux_docs_theme_contract.py:21-39 pins the right invariants:

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

Worth adding one more assertion to that file: when a future skin is added to _VALID_SKINS in static/boot.js, the demo docs should stay in sync. A drift-detection test that loads static/boot.js for the _VALID_SKINS constant and asserts each entry has a data-skin-btn button in both demo HTML files would catch that. Not required for this PR, but a natural follow-up since both demos now share the same axis enumeration.

Two small nits:

  1. The mediaquery listener uses docThemeMq.addEventListener guarded path but the older .addListener fallback isn't added. Modern browsers only — fine for a demo doc, would matter if this was production.
  2. docTheme = 'dark' initial value is hardcoded, which matches class="dark" on <html>. If a maintainer wants the demo to honor prefers-color-scheme on first load (similar to how the live app handles initial appearance), it'd need a small docTheme = docThemeMq.matches ? 'dark' : 'light' setup before the listener wires up. Again, demo doc scope only.

Test plan

The new tests/test_uiux_docs_theme_contract.py covers the structural invariants. CI passes (per the diff stat + before/after screenshots in docs/ui-ux/uiux-theme-demo-controls/). For manual reproduction, open docs/ui-ux/index.html directly in a browser, click through System/Dark/Light + the ten skin buttons, and confirm the document re-paints to match each combination — which is exactly the workflow contributors use to preview a new skin without spinning up the app.

This is a small, scoped docs sync with a contract test guarding against drift back. Clean to merge.

@nesquena-hermes nesquena-hermes closed this pull request by merging all changes into nesquena:master in 4589dbe May 18, 2026
Charanis pushed a commit to Charanis/hermes-webui-beyond that referenced this pull request May 18, 2026
# Conflicts:
#	CHANGELOG.md
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request May 18, 2026
… 0.51.90) (#556)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.89` → `0.51.90` |

---

### Release Notes

<details>
<summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary>

### [`v0.51.90`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05190--2026-05-18--Release-BN-stage-383--10-PR-full-sweep-batch--empty-gateway-messaging-history-fix--previous-messaging-sessions-setting--Kanban-board-switcher-layout--UIUX-demo-theme-controls--Slice-3c-queuegoal-RFC-gate--keyless-custom-endpoints--custom-provider-remote-model-catalog-parity--auto-compression-elapsed-timer--new-conversation-cold-start-guard--Kanban-drag-drop-detail-open-fix)

[Compare Source](nesquena/hermes-webui@v0.51.89...v0.51.90)

##### Fixed

- **PR [#&#8203;2286](nesquena/hermes-webui#2286 by [@&#8203;junjunjunbong](https://github.com/junjunjunbong) (refs [#&#8203;2275](nesquena/hermes-webui#2275)) — Narrow messaging stale-session filtering to active gateway sessions that are visible in the current sidebar candidate set. Older Discord/messaging history is now preserved when the gateway advertises a fresh zero-message session that hasn't yet entered the visible projection, instead of being hidden as stale. Adds a regression test for an empty active Discord gateway row preserving prior history.
- **PR [#&#8203;2459](nesquena/hermes-webui#2459 by [@&#8203;franksong2702](https://github.com/franksong2702) (closes [#&#8203;2458](nesquena/hermes-webui#2458)) — Fix the Kanban board switcher menu when a board's icon slot carries a long text label (e.g. `layout-kanban`). The icon column changed from a fixed `18px` slot to a bounded flex cell with `min-width:18px;max-width:7.5rem`, with overflow ellipsis on the icon itself so long labels render fully when space allows and truncate cleanly when not. Title and count columns keep stable spacing. Adds before/after screenshots and a CSS contract regression in `tests/test_kanban_ui_static.py`.
- **PR [#&#8203;2522](nesquena/hermes-webui#2522 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;2271](nesquena/hermes-webui#2271)) — Treat named custom OpenAI-compatible endpoints with a configured `base_url` as key-optional at WebUI agent startup. Local keyless servers (llama-server / vLLM-style LAN deployments) no longer fail early with a synthetic `CUSTOM:<slug>_API_KEY` env-var prompt before the request reaches the endpoint; instead the OpenAI-compatible client initialises with a harmless placeholder key and real configured keys are still preferred when present. Refactors the three near-identical custom-provider rebuild blocks (initial agent setup + two retry/healing paths) through the existing `resolve_custom_provider_connection` helper.
- **PR [#&#8203;2515](nesquena/hermes-webui#2515 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2513](nesquena/hermes-webui#2513)) — Keep named custom-provider model pickers populated from each configured endpoint's live `/models` catalog even when `custom_providers[].model` is present. The singular `model` field now acts as a sticky/fallback entry appended *after* the remote catalog rather than collapsing the picker to just the configured model and hiding sibling named custom providers. Extracts reusable OpenAI-compatible `/models` parsing/fetching helpers and threads them through both the active-base-url and per-named-provider paths.
- **PR [#&#8203;2512](nesquena/hermes-webui#2512 by [@&#8203;dso2ng](https://github.com/dso2ng) (refs [#&#8203;2477](nesquena/hermes-webui#2477), Slice A) — Show an elapsed timer on the running automatic-compression card so long WebUI context-compression pauses no longer look frozen while the browser waits for the `compressed` event. Stamps `startedAt` on the `compressing` SSE event, ticks once per second, and switches to a `5+ min` cap label past the Slice A bound so the UI never frame-freezes at `05:00`. Browser-transient state only — no SSE contract change and no server-side resume reconstruction.
- **PR [#&#8203;2528](nesquena/hermes-webui#2528 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (closes [#&#8203;2518](nesquena/hermes-webui#2518)) — Guard New Conversation creation while a previous `/api/session/new` request is still in flight, so cold model/provider catalog resolution gives immediate pending feedback and rapid repeated clicks reuse the same create request instead of enqueueing duplicate blank sessions. Coalesces concurrent `newSession()` calls behind a single in-flight promise, disables the sidebar button with `aria-busy="true"`, and shows a localized `Creating new conversation…` composer status.
- **PR [#&#8203;2530](nesquena/hermes-webui#2530 by [@&#8203;franksong2702](https://github.com/franksong2702) (refs [#&#8203;2529](nesquena/hermes-webui#2529)) — Keep Kanban drag/drop status updates from also opening the task detail pane. Two failure paths were both producing detail-pane opens after drag/drop: the browser's trailing synthetic click after `drop`, and the generic task-update helper opening detail on every PATCH. The fix adds a time-windowed `_kanbanSuppressCardClickUntil` set on `ondragstart`/`ondragend`/`ondrop` and routes drag/drop status changes through a board-only update path. Explicit card click and keyboard activation remain unchanged.

##### Added

- **PR [#&#8203;2294](nesquena/hermes-webui#2294 by [@&#8203;junjunjunbong](https://github.com/junjunjunbong) — Add a `show_previous_messaging_sessions` setting so users can opt back into seeing previous messaging sessions that were replaced by `session_reset` or auto-compression. The preference is wired through boot, settings persistence, and the sidebar projection. Also adds a separate "Hide from list" action for imported messaging/CLI sessions that hides individual rows from the sidebar without deleting source history.

##### Documentation

- **PR [#&#8203;2511](nesquena/hermes-webui#2511 by [@&#8203;franksong2702](https://github.com/franksong2702) (refs [#&#8203;2502](nesquena/hermes-webui#2502) / [#&#8203;2503](nesquena/hermes-webui#2503)) — Update the `docs/ui-ux/` demo appearance controls to initialize as `class="dark" data-skin="slate"` instead of the deprecated `data-theme`-only buttons and legacy theme names. Brings the demo pages in line with the live Theme + Skin contract referenced from the new `docs/CONTRACTS.md` so contributors following the contract-index path don't land on stale demos.
- **PR [#&#8203;2509](nesquena/hermes-webui#2509 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;1925](nesquena/hermes-webui#1925)) — Advance the runtime-adapter RFC after the Slice 3b approval/clarify seam shipped in v0.51.89. The RFC now marks Slice 3b as shipped and defines the next Slice 3c queue/continue + goal control gate: route those controls through `RuntimeAdapter.queue_message(...)` / `update_goal(...)` only after pinning stable response contracts, bounded unavailable-control behavior, replayable lifecycle/status evidence, ordering/idempotency expectations, and explicit non-goals for runner/sidecar ownership or a WebUI-owned queue/goal scheduler. Docs + adapter-seam regression test only — no runtime/control routing changes in this PR.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/556
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
# Conflicts:
#	CHANGELOG.md
bernyforce pushed a commit to bernyforce/hermes-webui that referenced this pull request Jul 29, 2026
# Conflicts:
#	CHANGELOG.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants