Files
hermes/ui-tui/src/app/uiStore.ts
Brooklyn Nicholson 68ecdb6e26 refactor(tui): store-driven turn state + slash registry + module split
Hoist turn state from a 286-line hook into $turnState atom + turnController
singleton. createGatewayEventHandler becomes a typed dispatch over the
controller; its ctx shrinks from 30 fields to 5. Event-handler refs and 16
threaded actions are gone.

Fold three createSlash*Handler factories into a data-driven SlashCommand[]
registry under slash/commands/{core,session,ops}.ts. Aliases are data;
findSlashCommand does name+alias lookup. Shared guarded/guardedErr combinator
in slash/guarded.ts.

Split constants.ts + app/helpers.ts into config/ (timing/limits/env),
content/ (faces/placeholders/hotkeys/verbs/charms/fortunes), domain/ (roles/
details/messages/paths/slash/viewport/usage), protocol/ (interpolation/paste).

Type every RPC response in gatewayTypes.ts (26 new interfaces); drop all
`(r: any)` across slash + main app.

Shrink useMainApp from 1216 -> 646 lines by extracting useSessionLifecycle,
useSubmission, useConfigSync. Add <Fg> themed primitive and strip ~50
`as any` color casts.

Tests: 50 passing. Build + type-check clean.
2026-04-16 12:34:45 -05:00

42 lines
851 B
TypeScript

import { atom } from 'nanostores'
import { ZERO } from '../domain/usage.js'
import { DEFAULT_THEME } from '../theme.js'
import type { UiState } from './interfaces.js'
function buildUiState(): UiState {
return {
bgTasks: new Set(),
busy: false,
compact: false,
detailsMode: 'collapsed',
info: null,
sid: null,
status: 'summoning hermes…',
statusBar: true,
theme: DEFAULT_THEME,
usage: ZERO
}
}
export const $uiState = atom<UiState>(buildUiState())
export function getUiState() {
return $uiState.get()
}
export function patchUiState(next: Partial<UiState> | ((state: UiState) => UiState)) {
if (typeof next === 'function') {
$uiState.set(next($uiState.get()))
return
}
$uiState.set({ ...$uiState.get(), ...next })
}
export function resetUiState() {
$uiState.set(buildUiState())
}