Commit graph

13 commits

Author SHA1 Message Date
Brooklyn Nicholson
0d353ca6a8 fix(tui): bound retained state against idle OOM
Guards four unbounded growth paths reachable at idle — the shape matches
reports of the TUI hitting V8's 2GB heap limit after ~1m of idle with 0
tokens used (Mark-Compact freed ~6MB of 2045MB → pure retention).

- `GatewayClient.logs` + `gateway.stderr` events: 200-line cap is bytes-
  uncapped; a chatty Python child emitting multi-MB lines (traceback,
  dumped config, unsplit JSON) retains everything. Truncate at 4KB/line.
- `GatewayClient.bufferedEvents`: unbounded until `drain()` fires. Cap
  at 2000 so a pre-mount event storm can't pin memory indefinitely.
- `useMainApp` gateway `exit` handler: didn't reset `turnController`, so
  a mid-stream crash left `bufRef`/`reasoningText` alive forever.
- `pasteSnips` count-capped (32) but byte-uncapped. Add a 4MB total cap
  and clear snips in `clearIn` so submitted pastes don't linger.
- `StylePool.transitionCache`: uncapped `Map<number,string>`. Full-clear
  at 32k entries (mirrors `charCache` pattern).
2026-04-19 18:43:40 -07:00
Brooklyn Nicholson
39231f29c6 refactor(tui): /clean pass across ui-tui — 49 files, −217 LOC
Full codebase pass using the /clean doctrine (KISS/DRY, no one-off
helpers, no variables-used-once, pure functional where natural,
inlined obvious one-liners, killed dead exports, narrowed types,
spaced JSX). All contracts preserved — no RPC method, event name,
or exported type shape changed.

app/ — 15 files, -134 LOC
- inlined 4 one-off helpers (titleCase, isLong, statusToneFrom,
  focusOutside predicate)
- stores to arrow-const style (buildUiState, buildTurnState,
  buildOverlayState plus get/patch/reset triplets)
- functional slash/registry byName map (flatMap over for-loops)
- dropped dead param `live` in cancelOverlayFromCtrlC
- DRY'd duplicate shift() call in scrollWithSelection
- consolidated sections.push calls in /help

components/ — 12 files, -40 LOC
- extracted inline prop types to interfaces at file bottom (13×)
- inlined 6 one-off vars (pctLabel, logoW, heroW, cwd, title, hint)
- promoted HEART_COLORS + OPTS/LABELS to module scope
- JSX sibling spacing across 9 files
- un-shadowed `raw` in textInput
- components/thinking.tsx + components/markdown.tsx untouched
  (structurally load-bearing / edge-case-heavy)

config content domain protocol/ — 8 files, -77 LOC
- tightened 3 regexes (MOUSE_TRACKING, looksLikeSlashCommand,
  hasInterpolation — dropped stateful lastIndex dance)
- dead export ParsedSlashCommand removed
- MODES narrowed to `as const`, `.find(m => m === s)` replaces
  `.includes() ? (as cast) : null`
- fortunes.ts hash via reduce
- fmtDuration ternary chain
- inlined aboveViewport predicate in viewport.ts

hooks/ + lib/ — 9 files, -38 LOC
- ANSI_RE via String.fromCharCode(27) + WS_RE lifted to module
  scope (no more eslint-disable no-control-regex)
- compactPreview/edgePreview/thinkingPreview → ternary arrows
- useCompletion: hoisted pathReplace, moved stale-ref guard earlier
- useInputHistory: dropped useCallback wrapper (append is stable)
- useVirtualHistory: replaced 4× any with unknown + narrow
  MeasuredNode interface + one cast site

root TS — 3 files, -63 LOC
- banner.ts: parseRichMarkup via matchAll instead of exec/lastIndex,
  artWidth via reduce
- gatewayClient.ts: resolvePython candidate list collapse, inlined
  one-branch guards in dispatch/pushLog/drain/request
- types.ts: alpha-sorted ActiveTool / Msg / SudoReq / SecretReq
  members

eslint config
- disabled react-hooks/exhaustive-deps on packages/hermes-ink/**
  (compiled by react/compiler, deps live in $[N] memo arrays that
  eslint can't introspect) and removed the now-orphan in-file
  disable directive in ScrollBox.tsx

fixes (not from the cleaner pass)
- useComposerState: unlinkSync(file) + try/catch → rmSync(file,
  { force: true }) — kills the no-empty lint error and is more
  idiomatic
- useConfigSync: added setBellOnComplete + setVoiceEnabled to the
  two useEffect dep arrays (they're stable React setState setters;
  adding is safe and silences exhaustive-deps)

verification
- npx eslint src/ packages/ → 0 errors, 0 warnings
- npm run type-check → clean
- npm test → 50/50
- npm run build → 394.8kb ink-bundle.js, 11ms esbuild
- pytest tests/tui_gateway/ tests/test_tui_gateway_server.py
  tests/hermes_cli/test_tui_resume_flow.py
  tests/hermes_cli/test_tui_npm_install.py → 57/57
2026-04-16 22:32:53 -05:00
Brooklyn Nicholson
4b4b4d47bc feat: just more cleaning 2026-04-15 14:14:01 -05:00
Brooklyn Nicholson
9931d1d814 chore: cleanup 2026-04-15 10:35:08 -05:00
Brooklyn Nicholson
33c615504d feat: add inline token count etc and fix venv 2026-04-15 10:20:56 -05:00
Brooklyn Nicholson
6d6b3b03ac feat: add clicky handles 2026-04-13 21:20:55 -05:00
Brooklyn Nicholson
aeb53131f3 fix(ui-tui): harden TUI error handling, model validation, command UX parity, and gateway lifecycle 2026-04-13 18:29:24 -05:00
Brooklyn Nicholson
77b97b810a chore: update how txt pasting ux feels 2026-04-13 14:49:10 -05:00
Ari Lotter
29721fcc58 nix fixes 2026-04-11 19:35:00 -04:00
Ari Lotter
670dcea8f4 ui-tui: add tsc build pipeline
- Switch tsconfig to nodenext module resolution for Node 22 (used by
installer script)
- Add shebang to entry.tsx, preserved into index.js
- Add HERMES_ROOT env var fallback for repo root resolution
2026-04-09 15:35:29 -04:00
Brooklyn Nicholson
afd670a36f feat: small refactors 2026-04-06 18:38:13 -05:00
Brooklyn Nicholson
2818dd8611 feat: add prettier etc for ui-tui 2026-04-02 19:34:30 -05:00
Brooklyn Nicholson
2ea5345a7b feat: new tui based on ink 2026-04-02 19:07:53 -05:00