Skip to content

feat(theme-forge): desktop theme editor plugin - #8

Open
criptogus wants to merge 8 commits into
NousResearch:mainfrom
criptogus:add-theme-forge
Open

feat(theme-forge): desktop theme editor plugin#8
criptogus wants to merge 8 commits into
NousResearch:mainfrom
criptogus:add-theme-forge

Conversation

@criptogus

Copy link
Copy Markdown

What

Adds theme-forge, a complete desktop-plugin + dashboard-backend example: a visual theme editor for the Hermes Desktop that does what the app's theme model can't natively — wallpapers, extended --ui-* palette, 3-level bold, conversation font size, markdown accent colors, and animated backdrops (matrix rain, CRT scanlines).

Highlights

  • 13 presets including a film-inspired line: Matrix (digital rain), Terminator (HUD red + scanlines), Hacker, Spider-Man, Wolverine, Deadpool, Iron Man, Batman, Dune, Sith, plus Cyber/Glass/Paper and an editable Custom.
  • 1-click apply through Hermes' native skin path: the dashboard backend writes skins/<name>.yaml + sets display.skin; the gateway watcher broadcasts skin.changed and every surface (CLI/TUI/desktop) repaints — no restart.
  • Live authoring: a MutationObserver on data-hermes-theme + theme registry re-registration repaint the active theme as you edit.
  • Self-contained: pure-ESM plugin.js (no build), FastAPI backend under dashboard/, MIT, full README (install/architecture/limitations).

Notes for reviewers

  • This is intentionally a complete plugin, not a bare reference example — it demonstrates the desktop plugin SDK end-to-end (THEMES_AREA, ROUTES_AREA, SIDEBAR_NAV_AREA, ctx.storage, ctx.rest backend) with real production shape (validation, error toasts, persistence).
  • Install differs from other examples: plugin.js goes to ~/.hermes/desktop-plugins/; only dashboard/ goes to ~/.hermes/plugins/. Documented in the README.
  • Known limitation documented: wallpapers > ~2.5MB as data-URI exceed localStorage quota (works for the session, warns, doesn't persist).

criptogus and others added 8 commits August 2, 2026 13:21
Full theme editor for the Hermes Desktop:
- 13 presets incl. film-inspired line (Matrix rain, Terminator scanlines,
  Wolverine, Deadpool, Iron Man, Batman, Dune, Sith) + editable custom
- Wallpapers (local file/URL), extended --ui-* palette, 3-level bold,
  conversation font size, markdown accent colors (headings/links/code)
- Live authoring via data-hermes-theme observer + registry re-register
- 1-click apply through Hermes' native skin path (dashboard backend writes
  skins/<name>.yaml + display.skin; gateway broadcasts skin.changed)
- MIT, README with install/architecture/limitations
The desktop applies a skin only when the gateway broadcasts skin.changed,
which fires on display.skin/YAML mtime changes. After an app update the
desktop falls back to its local-storage theme even though config still says
forge-*. Fix: on plugin boot, read the configured skin (config.get 'skin')
and re-assert it via the backend /reassert endpoint, which re-writes
display.skin → gateway watcher re-broadcasts → desktop re-applies.
Retries 3x while the gateway comes up.
- New 'Fundo' tab: image or animated video backdrop (URL or local file;
  objectURL first, best-effort data-URI persistence up to 3.5MB)
- Media is a dedicated FX-container layer (<video> or <div>), z-index stack
  media 0 -> overlay 1 -> rain 2 -> scanlines 3
- Blur applies to the media layer only (body filter blurred the whole UI)
- Wallpaper now REPLACES the default chrome: glass surfaces drop to
  chrome 14% / sidebar 22% / editor 7% / elevated 12% when media is set
- Scanlines-only themes get the glass shell too (were invisible behind
  opaque chrome)
- Video pauses on visibilitychange + prefers-reduced-motion; stale blob:
  URLs from dead sessions are cleaned on load
The backdrop blur set `filter: blur(Npx) scale(...)` — `scale()` only
exists on `transform`, so Chromium dropped the entire declaration and the
blur never rendered (silent no-op on both the old body paint and the new
media layer). Now `filter: blur(Npx)` (valid) + `transform: scale(...)`
(edge-reveal compensation) — verified in real Chromium: the red/blue
boundary of a test image smears with blur 8px, and overlay+blur stack.

Also verified the overlay (z-index 1) darkens the media exactly 50% at
opacity 0.5 in real Chromium.
…y wallpaper

Two visible regressions after the media-layer refactor:

1. The sidebar mix with a wallpaper (22%) was so transparent that the
   sidebar and content merged into one continuous image. Restore the
   classic glass hierarchy: sidebar 60% / chrome 16% / editor 10% /
   elevated 18% (dark) — wallpaper dominates the content area while the
   sidebar stays a distinct darker panel.

2. Hot-reload left the pre-refactor plugin's inline body wallpaper behind
   (the old paintBodyBackdrop was removed without a cleanup). That stale
   image propagates to the canvas, so overlay/blur acted on the (invisible)
   media layer while the stale image kept showing. clearBodyLegacy() now
   strips any inline backdrop styles the plugin may have left on <body>
   (the app never sets body backgrounds inline — safe).

Also adds a TEMP diagState() log (console.error → desktop.log) to confirm
the live runtime state after reload; remove before final merge.
A large wallpaper data URI could overflow the localStorage quota and fail
the ENTIRE theme save silently — slider tweaks (blur/overlay/colors)
rolled back on every reload. The wallpaper now lives in its own storage
key (hermes.plugin.theme-forge.forge-custom-img-v1): the theme JSON always
saves, and only the image can be lost if IT overflows (with a clear warn).
loadCustom() migrates old inline data URIs and prefers the dedicated key.
diagState() served its purpose — it confirmed blur/overlay were applying and
the chrome mix was live. Removing before merge (console.error probes are
dev-only; desktop.log forwarding is the intended channel during debugging).
…tems)

New 'Sidebar (menu lateral)' section in the Cores tab with three
reset-to-default color pickers that emit scoped token overrides against the
app's stable data-slot hooks:

- Nav (New Session, Capabilities, Messaging, Artifacts): overrides
  --ui-text-secondary inside the nav group via
  [data-slot=sidebar-group]:has([data-slot=sidebar-menu-button]) — precise,
  session/project rows never match sidebar-menu-button
- Section titles (Pinned, Recents, ...): [data-slot=sidebar-group-label]
- Items (sessions + projects): --ui-text-secondary scoped to
  [data-slot=sidebar-content] (nav group's more specific rule wins for nav)

Hover/active states keep working: they use text-foreground, a different
token, so only the resting color changes. Pinned rows follow the item color
(no stable hook distinguishes them — the pin lives in the row's actions
menu). Verified: buildCss emits the exact scoped rules, defaults merge via
loadCustom, paint engine unchanged.
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.

1 participant