Files
hermes-agent/ui-tui-opentui-v2/src/view/theme.tsx
T
alt-glitch 927c902785 feat(opentui-v2): Phase 1 — live tui_gateway transport + Solid store + theming
GatewayService/liveGateway over the real Python tui_gateway: JSON-RPC stdio
framing (Bun.spawn), 16ms event coalescing flushed inside Solid batch(), typed
GatewayError, and a decode-once GatewayEvent Schema (~35-member tagged union;
unknown/malformed events skip via Option.none, never crash the stream).

The Solid sync-v2-style store grows to: streaming text concat (prefer
payload.text), gateway.ready{skin}/skin.changed -> fromSkin reactive re-theme,
LRU id-dedup, and hydrate-while-buffering (resume scaffold). Theming is a 1:1
port of Ink's theme.ts (DARK/LIGHT, detectLightMode, ANSI-256 normalization,
fromSkin) behind a Solid ThemeProvider so existing skins work unchanged and the
view carries NO hardcoded styles. A console-safe diagnostics log (in-memory
ring + NDJSON file) is the single logging path.

Entry gains a live launch path (default; HERMES_TUI_FAKE=1 -> scripted hello)
with an initial-prompt bootstrap (session.create -> prompt.submit) as the
Phase-2-composer stand-in, plus a minimal Ctrl+C graceful quit
(renderer.destroy -> shutdown Deferred -> scope finalizers -> client.stop) so
the engine reaps its own gateway child instead of orphaning it.

Verified: bun run check green (tsc + eslint + 12 tests / 4 files); live tmux
drive connect -> gateway.ready -> prompt -> streamed reply ("pong") -> clean
teardown with no orphan bun/python. Parity matrix + smoke P1 run log updated.
2026-06-08 14:19:21 +00:00

30 lines
1.2 KiB
TypeScript

/**
* ThemeProvider — the Solid context that exposes the current Theme to the view
* (spec v4 §7.5; mirrors opencode `context/theme.tsx`). The view reads
* `useTheme()().color.*` / `.brand.*` and NEVER hardcodes styles.
*
* The theme is a reactive accessor: when the boundary applies a skin
* (gateway.ready{skin} / skin.changed → store updates the theme), Solid
* fine-grained reactivity re-styles only the affected cells.
*/
import { type Accessor, createContext, type JSX, useContext } from 'solid-js'
import { DEFAULT_THEME, type Theme } from '../logic/theme.ts'
const ThemeContext = createContext<Accessor<Theme>>(() => DEFAULT_THEME)
export interface ThemeProviderProps {
/** Reactive theme accessor (from the store). Defaults to DEFAULT_THEME if omitted. */
readonly theme?: Accessor<Theme>
readonly children: JSX.Element
}
export function ThemeProvider(props: ThemeProviderProps) {
return <ThemeContext.Provider value={props.theme ?? (() => DEFAULT_THEME)}>{props.children}</ThemeContext.Provider>
}
/** Read the current theme inside a component. Call it (`useTheme()()`) to get the Theme. */
export function useTheme(): Accessor<Theme> {
return useContext(ThemeContext)
}