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.
This commit is contained in:
Brooklyn Nicholson 2026-04-16 12:18:56 -05:00
parent 9c71f3a6ea
commit 68ecdb6e26
56 changed files with 3666 additions and 4117 deletions

View file

@ -0,0 +1,29 @@
import type { DetailsMode } from '../types.js'
const DETAILS_MODES: DetailsMode[] = ['hidden', 'collapsed', 'expanded']
const THINKING_FALLBACK: Record<string, DetailsMode> = {
collapsed: 'collapsed',
full: 'expanded',
truncated: 'collapsed'
}
export const parseDetailsMode = (v: unknown): DetailsMode | null => {
const s = typeof v === 'string' ? v.trim().toLowerCase() : ''
return DETAILS_MODES.includes(s as DetailsMode) ? (s as DetailsMode) : null
}
export const resolveDetailsMode = (
d: { details_mode?: unknown; thinking_mode?: unknown } | null | undefined
): DetailsMode =>
parseDetailsMode(d?.details_mode) ??
THINKING_FALLBACK[
String(d?.thinking_mode ?? '')
.trim()
.toLowerCase()
] ??
'collapsed'
export const nextDetailsMode = (m: DetailsMode): DetailsMode =>
DETAILS_MODES[(DETAILS_MODES.indexOf(m) + 1) % DETAILS_MODES.length]!