[TUI Code Architecture]

High-integrity architecture specifications, Zustand store configurations, and connection state machines of the Hoox Terminal UI.

The Hoox Terminal UI (TUI) is a full-screen, keyboard-driven operations center built with OpenTUI, React 19, and Zustand. This document covers package layout, state, data flows, and crash recovery for engineers.

Package: packages/tui (@jango-blockchained/hoox-tui)
Entry: src/main.tsx · Root: src/app.tsx · CLI launcher: hoox tui


Architectural Blueprint

Directory map

packages/tui/src/
├── main.tsx                 # createCliRenderer (TUI_FPS / TUI_MOUSE env)
├── app.tsx                  # VIEWS registry, shortcuts, session, SSE kickoff
├── components/
│   ├── views/               # 15 views (+ dashboard/, config-editor/ subpanels)
│   ├── layout/              # sidebar, statusbar
│   ├── shared/              # palette, crash screen, error boundary, …
│   └── ui/                  # dialog / toast wrappers
├── services/
│   ├── cli-bridge/          # typed hoox subprocess bridge
│   ├── hoox-path-service.ts # $HOME/.hoox paths
│   └── tui-storage.ts       # file-backed JSON (no localStorage in Bun)
├── hooks/                   # keyboard, polling, renderer-ref
└── stores/*.test.ts         # unit tests (stores live in hoox-shared)

Stores are not under packages/tui/src/stores for production code — they live in @jango-blockchained/hoox-shared:

StorePathResponsibility
UIpackages/shared/src/stores/ui-store.tsactiveView, sidebar, modal, palette
Servicepackages/shared/src/stores/service-store.tsworkers, trades, logs, alerts, connection, SSE
Configpackages/shared/src/stores/config-store.tstheme, refresh interval, notifications, shortcuts

Session persistence: $HOME/.hoox/.tui-state/session.json via shared restoreSession / saveSession.


View registry (15)

Must stay aligned with ViewId in packages/shared/src/types.ts, sidebar items, VIEWS in app.tsx, and command palette entries:

ViewIdComponentPrimary data source
dashboardDashboardViewfetchWorkers, monitorStatus, checkFix, agentHealthCheck, kill-switch
workersWorkersOverviewstore workers + CLI deploy/logs/repair
worker-detailWorkerDetailselectedWorkerId, configShow, workerLogs
trade-monitorTradeMonitortradeStream (SSE streamTrades)
logs-viewerLogsViewerlogs (SSE streamLogs + CLI fetch)
service-managerServiceManagerdeploy/repair/rebuild/kill-switch
config-editorConfigEditorfilesystem + configValidate
setup-wizardSetupWizardwizard steps + deployAll
settingsSettingsViewconfig store + checkSetup / checkFix
queue-depthQueueDepthViewmonitorQueueDepth
kv-viewerKvViewerconfigKvList / configKvGet
secrets-viewerSecretsViewerconfigSecretsList (names only)
ai-chatAiChatViewagentChatStream SSE
db-queryDbQueryViewvalidateReadOnlySql + dbQuery
edge-topologyEdgeTopologymonorepo graph-metadata.json

Dual-channel + SSE

Startup sequence (AppRoot)

  1. Restore session → set view / sidebar
  2. fetchWorkers() (HTTP) → on failure, cliBridge.monitorStatus()
  3. Fire-and-forget streamTrades() + streamLogs() (SSE; silent if API down)

CLI bridge (src/services/cli-bridge)

All mutating / diagnostic operator actions go through the hoox binary with timeout, abort tags, and structured CliErrorDetails sunk to the service store for the status bar expand panel.

Notable methods: deployAll, deployWorker, repairWorker, rebuild, checkHealth, checkFix, checkSetup, monitorStatus, monitorKillSwitch, monitorQueueDepth, workerLogs, configShow, configValidate, configKvList, configKvGet, configSecretsList, dbQuery, agentHealthCheck.

checkHealthFix exists on the bridge but is unused by views (dashboard/settings use checkFix).


Crash protection

Layer 1 — per-view ErrorBoundary

Every primary view is wrapped. Render failures show Retry without killing sidebar/status.

Layer 2 — CrashRecoveryApp

Process uncaughtException / unhandledRejection → CrashScreen:

  • Restart — remount AppRoot
  • Safe Mode — remount with safeMode
  • Report Bug — write $HOME/.hoox/.tui-state/crash.log

Verification

cd packages/tui
bun run typecheck
bun test --preload ./src/test-setup.ts
# from monorepo root:
bun run test:tui

E2E smoke (test/e2e/smoke.test.ts) requires an interactive TTY; otherwise it skips with a clear reason.


Graph integration

  • Runtime topology view reads graph-metadata.json (repo root), resolved from CWD walk-up or package-relative path.
  • Code graph (graph.json / graph.dot) is produced by bun run graph (scripts/extract-graph.ts).
  • All 15 view exports are present as function nodes under packages/tui:…/views/….
  • Regenerate after large refactors: bun run graph (~25s). Metadata should include every worker under workers/* (including pine-worker).

Known limitations (honest)

AreaLimitation
Worker Detail DOsNames inferred from durableObjectCount + worker name; no DO listing API
Worker Detail configFalls back to demo-ish keys when configShow empty
Trade/log live feedRequires HTTP API + SSE endpoints; offline TUI uses CLI snapshots only
usePolling hookExported but unused — views poll via effects / intervals
CLI flags--fps / --no-mouse set TUI_FPS / TUI_MOUSE; renderer reads them in main.tsx
Graph freshnessFull graph.json can lag submodules until bun run graph is re-run

Tip

OpenTUI <text> children must be strings (or text nodes) — never nest <text> inside <text>. Prefer sibling <text> inside a row <box>.

Next steps