mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-03 07:21:54 +00:00
This PR groups the TUI fixes that restore macOS Terminal usability and clean up the theme/composer regressions: - copy transcript selections on macOS drag-release so Terminal.app users can copy while mouse tracking is enabled - copy composer selections on macOS drag-release; composer selection is internal to TextInput and does not use the global Ink selection bus - keep IDE Cmd+C forwarding setup macOS-only, and make keybinding conflict checks respect simple when-clause overlap/negation - force truecolor before chalk initializes (unless NO_COLOR / FORCE_COLOR / HERMES_TUI_TRUECOLOR opt-outs apply) so the default banner keeps its gold/amber/bronze gradient in Terminal.app - move TUI surfaces onto semantic theme tokens and preserve skin prompt symbols as bare tokens with renderer-owned spacing - render focused placeholders as dim hint text in TTY mode instead of inverse/selected-looking synthetic cursor text
63 lines
3 KiB
TypeScript
63 lines
3 KiB
TypeScript
/** Platform-aware keybinding helpers.
|
|
*
|
|
* On macOS the "action" modifier is Cmd. Modern terminals that support kitty
|
|
* keyboard protocol report Cmd as `key.super`; legacy terminals often surface it
|
|
* as `key.meta`. Some macOS terminals also translate Cmd+Left/Right/Backspace
|
|
* into readline-style Ctrl+A/Ctrl+E/Ctrl+U before the app sees them.
|
|
* On other platforms the action modifier is Ctrl.
|
|
* Ctrl+C stays the interrupt key on macOS. On non-mac terminals it can also
|
|
* copy an active TUI selection, matching common terminal selection behavior.
|
|
*/
|
|
|
|
export const isMac = process.platform === 'darwin'
|
|
|
|
/** True when the platform action-modifier is pressed (Cmd on macOS, Ctrl elsewhere). */
|
|
export const isActionMod = (key: { ctrl: boolean; meta: boolean; super?: boolean }): boolean =>
|
|
isMac ? key.meta || key.super === true : key.ctrl
|
|
|
|
/**
|
|
* Accept raw Ctrl+<letter> as an action shortcut on macOS, where `isActionMod`
|
|
* otherwise means Cmd. Two motivations:
|
|
* - Some macOS terminals rewrite Cmd navigation/deletion into readline control
|
|
* keys (Cmd+Left → Ctrl+A, Cmd+Right → Ctrl+E, Cmd+Backspace → Ctrl+U).
|
|
* - Ctrl+K (kill-to-end) and Ctrl+W (delete-word-back) are standard readline
|
|
* bindings that users expect to work regardless of platform, even though
|
|
* no terminal rewrites Cmd into them.
|
|
*/
|
|
export const isMacActionFallback = (
|
|
key: { ctrl: boolean; meta: boolean; super?: boolean },
|
|
ch: string,
|
|
target: 'a' | 'e' | 'u' | 'k' | 'w'
|
|
): boolean => isMac && key.ctrl && !key.meta && key.super !== true && ch.toLowerCase() === target
|
|
|
|
/** Match action-modifier + a single character (case-insensitive). */
|
|
export const isAction = (key: { ctrl: boolean; meta: boolean; super?: boolean }, ch: string, target: string): boolean =>
|
|
isActionMod(key) && ch.toLowerCase() === target
|
|
|
|
export const isRemoteShell = (env: NodeJS.ProcessEnv = process.env): boolean =>
|
|
Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY)
|
|
|
|
export const isCopyShortcut = (
|
|
key: { ctrl: boolean; meta: boolean; super?: boolean },
|
|
ch: string,
|
|
env: NodeJS.ProcessEnv = process.env
|
|
): boolean =>
|
|
ch.toLowerCase() === 'c' &&
|
|
(isAction(key, ch, 'c') ||
|
|
(isRemoteShell(env) && (key.meta || key.super === true)) ||
|
|
// VS Code/Cursor/Windsurf terminal setup forwards Cmd+C as a CSI-u
|
|
// sequence with the super bit plus a benign ctrl bit. Accept that shape
|
|
// even though raw Ctrl+C should remain interrupt on local macOS.
|
|
(isMac && key.ctrl && (key.meta || key.super === true)))
|
|
|
|
/**
|
|
* Voice recording toggle key (Ctrl+B).
|
|
*
|
|
* Documented as "Ctrl+B" everywhere: tips.py, config.yaml's voice.record_key
|
|
* default, and the Python CLI prompt_toolkit handler. We accept raw Ctrl+B on
|
|
* every platform so the TUI matches those docs. On macOS we additionally
|
|
* accept Cmd+B (the platform action modifier) so existing macOS muscle memory
|
|
* keeps working.
|
|
*/
|
|
export const isVoiceToggleKey = (key: { ctrl: boolean; meta: boolean; super?: boolean }, ch: string): boolean =>
|
|
(key.ctrl || isActionMod(key)) && ch.toLowerCase() === 'b'
|