Make the Ink TUI match macOS keyboard expectations: Command handles copy and common editor/session shortcuts, while Control remains reserved for interrupt/cancel flows. Update the visible hotkey help to show platform-appropriate labels.
Previous fix in 9dbf1ec6 handled Ctrl+C inside textInput but the APP-level
useInputHandlers fires the same keypress in a separate React hook and ran
clearIn() regardless. Net effect: the OSC 52 copy succeeded but the input
wiped right after, so Brooklyn only noticed the wipe.
Lift the selection-aware Ctrl+C to a single place by threading input
selection state through a new nanostore (src/app/inputSelectionStore.ts).
textInput syncs its derived `selected` range + a clear() callback to the
store on every selection change, and the app-level Ctrl+C handler reads
the store before its clear/interrupt/die chain:
- terminal-level selection (scrollback) → copy, existing behavior
- in-input selection present → copy + clear selection, preserve input
- input has text, no selection → clearIn(), existing behavior
- empty + busy → interrupt turn
- empty + idle → die
textInput no longer has its own Ctrl+C block; keypress falls through to
app-level like it did before 9dbf1ec6.
Before: textInput explicitly ignored Ctrl+C so the app-level handler took
over — with no knowledge of the TextInput's own selection — and fell through
to clearIn() whenever input had text. Selecting part of the composer and
pressing Ctrl+C silently nuked everything you typed.
Now: Ctrl+C with an active in-input selection writes the selected substring
to the clipboard via OSC 52 and clears the selection. The original semantics
(Ctrl+C with no selection → app-level interrupt/clear/die chain) are
preserved by still returning early in that case.
- drop inline `import()` type annotation in useSessionLifecycle (import
`PanelSection` at the top like everything else)
- include `panel` and `session.resumeById` in the useMainApp useMemo
deps now that the event handler depends on them
- wrap the derived `selected` range in a useMemo so it has stable
identity and stops invalidating the TextInput `rendered` memo every
render
- prettier re-sorting of a couple of export/import lines
- tui_gateway: route approvals through gateway callback (HERMES_GATEWAY_SESSION/
HERMES_EXEC_ASK) so dangerous commands emit approval.request instead of
silently falling through the CLI input() path and auto-denying
- approval UX: dedicated PromptZone between transcript and composer, safer
defaults (sel=0, numeric quick-picks, no Esc=deny), activity trail line,
outcome footer under the cost row
- text input: Ctrl+A select-all, real forward Delete, Ctrl+W always consumed
(fixes Ctrl+Backspace at cursor 0 inserting literal w)
- hermes-ink selection: swap synchronous onRender() for throttled
scheduleRender() on drag, and only notify React subscribers on presence
change — no more per-cell paint/subscribe spam
- useConfigSync: silence config.get polling failures instead of surfacing
'error: timeout: config.get' in the transcript