refactor(tui): statusbar as 4-mode position (on|off|bottom|top)

Default is back to 'on' (inline, above the input) — bottom was too far
from the input and felt disconnected. Users who want it pinned can
opt in explicitly.

- UiState.statusBar: boolean → 'on' | 'off' | 'bottom' | 'top'
- /statusbar [on|off|bottom|top|toggle]; no-arg still binary-toggles
  between off and on (preserves muscle memory)
- appLayout renders StatusRulePane in three slots (inline inside
  ComposerPane for 'on', above transcript row for 'top', after
  ComposerPane for 'bottom'); only the slot matching ui.statusBar
  actually mounts
- drop the input's marginBottom when 'bottom' so the rule sits tight
  against the input instead of floating a row below
- useConfigSync.normalizeStatusBar coerces legacy bool (true→on,
  false→off) and unknown shapes to 'on' for forward-compat reads
- tui_gateway: split compact from statusbar config handlers; persist
  string enum with _coerce_statusbar helper for legacy bool configs
This commit is contained in:
Brooklyn Nicholson 2026-04-22 13:41:01 -05:00
parent 7027ce42ef
commit d55a17bd82
8 changed files with 118 additions and 25 deletions

View file

@ -10,9 +10,23 @@ import type {
} from '../gatewayTypes.js'
import { asRpcResult } from '../lib/rpc.js'
import type { StatusBarMode } from './interfaces.js'
import { turnController } from './turnController.js'
import { patchUiState } from './uiStore.js'
const STATUSBAR_MODES = new Set<StatusBarMode>(['bottom', 'off', 'on', 'top'])
// Legacy configs stored tui_statusbar as a bool; new configs write a string
// ('on' | 'off' | 'bottom' | 'top'). Coerce both shapes so existing users
// keep their preference without manual migration.
export const normalizeStatusBar = (raw: unknown): StatusBarMode => {
if (raw === false) return 'off'
if (raw === true || raw == null) return 'on'
if (typeof raw === 'string' && STATUSBAR_MODES.has(raw as StatusBarMode)) return raw as StatusBarMode
return 'on'
}
const MTIME_POLL_MS = 5000
const quietRpc = async <T extends Record<string, any> = Record<string, any>>(
@ -37,7 +51,7 @@ export const applyDisplay = (cfg: ConfigFullResponse | null, setBell: (v: boolea
inlineDiffs: d.inline_diffs !== false,
showCost: !!d.show_cost,
showReasoning: !!d.show_reasoning,
statusBar: d.tui_statusbar !== false,
statusBar: normalizeStatusBar(d.tui_statusbar),
streaming: d.streaming !== false
})
}