mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-05 07:41:39 +00:00
* feat(tui): make display.mouse_tracking pick which DEC modes to enable
Previously the boolean flag was all-or-nothing across modes 1000+1002+1003+1006.
Inside tmux, mode 1003 (any-motion) makes every mouse cross of the prompt row
fire a clipboard probe that surfaces as "No image in clipboard" — sometimes
dozens in a row. Disabling tracking entirely killed scroll-wheel scrolling too,
since tmux's own scrollback is preempted by the alt-screen TUI.
`display.mouse_tracking` (and `/mouse <preset>`) now accepts `off | wheel |
buttons | all` in addition to the legacy booleans. `wheel` is 1000+1006:
scroll wheel + click only, no drag, no hover — the tmux-friendly subset.
`buttons` adds 1002 for drag-to-select. `all` (= legacy `true`) keeps the
hover-driven UI (scrollbar paginate-on-hover, link mouseenter, etc.).
* fix(tui): repaint + sync mouse mode when display.mouse_tracking changes
Two interacting bugs left the TUI blank when `display.mouse_tracking`
switched at runtime (config edit, /mouse <preset>):
1. AlternateScreen's effect re-runs on every `mouseTracking` change,
tearing down and re-entering the alt screen. After re-entry, ink's
frame buffers are reset by `resetFramesForAltScreen()` but nothing
schedules the follow-up render — the alt screen sits blank until
some other state change happens to trigger one. Add a
`scheduleRender()` in `setAltScreenActive`'s active=true branch so
the freshly-entered alt screen gets a full repaint immediately.
2. `setAltScreenActive` early-returns when `active` hasn't changed,
which silently drops a `mouseTracking` change if the cleanup→setup
pair somehow leaves `altScreenActive` already true. Call
`setAltScreenMouseTracking` explicitly from the AlternateScreen
effect so the in-memory mode and terminal DECSET sequence stay in
sync regardless of how `setAltScreenActive` resolved (the call is a
no-op when the mode is unchanged).
* fix(tui): address copilot review #4341269705
- tui_gateway/server.py: drop the never-referenced _MOUSE_TRACKING_MODES
frozenset (comment #3284802434). _MOUSE_TRACKING_ALIASES already
centralizes the canonical preset set via its values; the separate
constant added no behavior.
- tests/test_tui_gateway_server.py: update the existing
test_config_mouse_uses_documented_key_with_legacy_fallback to assert
the new preset strings ('all'/'off' instead of 'on'/'off',
display.mouse_tracking persisted as 'all' instead of True) and add
test_config_mouse_accepts_preset_strings_and_aliases covering /mouse
set with wheel/click/unknown (comment #3284802453). The on/off legacy
config.set return shape was an implementation detail of the boolean
flag, not a stable API — the slash command, gateway help text, and
docs all advertise the preset values now.
- ui-tui/packages/hermes-ink/src/ink/ink.tsx: schedule a render at the
end of reenterAltScreen() (comment #3284802461). Mirrors the same fix
in setAltScreenActive() from ece0a2f4c — without it, SIGCONT/resize
self-heal/stdin-gap re-entry leaves the alt screen blank because
every caller returns early after invoking us.
* fix(tui): address copilot review #4341308478 round 2
- ui-tui/src/config/env.ts (comment #3284837577): the precedence
comment was misleading. Actual behavior on origin/main is
HERMES_TUI_MOUSE_TRACKING (explicit override) > Termux default >
HERMES_TUI_DISABLE_MOUSE legacy kill-switch. This is preserved from
main; the only change here was the wrong comment that claimed
DISABLE_MOUSE kept kill-switch semantics. Rewrote the comment block
to document the actual precedence ladder.
- tui_gateway/server.py /mouse set (comment #3284837607): replaced
'str(value or "").strip().lower()' with the explicit None idiom
already used for /indicator, so programmatic callers can pass 0 /
False and have them route through _MOUSE_TRACKING_ALIASES → 'off'
instead of collapsing to '' and triggering the toggle path.
- ui-tui/packages/hermes-ink/src/ink/components/AlternateScreen.tsx
(comment #3284837620): always prepend DISABLE_MOUSE_TRACKING before
enableMouseTrackingFor(...) on mount. Otherwise selecting
'wheel'/'buttons' from a state where DEC 1003 was already asserted
(crash, another app, debugger) would silently leave hover on. Also
unconditionally DISABLE on unmount so a crash mid-mount can't leak
DEC modes back to the host shell.
* chore(release): map nat@nthrow.io to @nthrow for #26681 salvage
* fix(tui): drop redundant setAltScreenMouseTracking in AlternateScreen
Copilot review #4341356637 (comment #3284880417). The explicit
setAltScreenMouseTracking(mouseTracking) after setAltScreenActive(true,
mouseTracking) was defensive paranoia added in the previous fix commit
that's not actually reachable in practice:
- React's cleanup always runs before the next setup, so on any prop
change (mouseTracking or writeRaw) the cleanup sets active=false
first. Setup then sees active was false and applies the new mode
via setAltScreenActive without early-returning.
- On the impossible 'active stayed true' path, the writeRaw above has
already sent DISABLE_MOUSE_TRACKING + enableMouseTrackingFor(newMode)
to the terminal, so the in-memory mode would lag but the visible
state is already correct.
Removing the redundant call means a single DEC sequence per mount.
If the 'active stayed true' path ever manifests in practice, the
right fix is in setAltScreenActive (track mode regardless of the
active early-return), not here.
* fix(tui): always DISABLE before enableMouseTrackingFor in ink.tsx
Copilot review #4341379994 (comments #3284900825, #3284900840,
#3284900852). Three remaining call sites in ink.tsx still re-enabled
mouse tracking without first sending DISABLE_MOUSE_TRACKING:
- handleResize alt-screen recovery (line ~577)
- reassertTerminalModes stdin-gap re-assertion (line ~1351)
- reenterAltScreen SIGCONT/resize/stdin-gap self-heal (line ~1408)
For 'wheel'/'buttons' presets, omitting DISABLE leaves any externally-
asserted DEC 1003 (other apps, prior crash, tmux state) still active
and the hover-free preset silently has hover on. DISABLE_MOUSE_TRACKING
is idempotent and safe to send unconditionally — it resets all four
modes. Matches the pattern already in setAltScreenMouseTracking and
the AlternateScreen mount path.
* fix(tui): always DISABLE before enableMouseTrackingFor in exitAlternateScreen
Copilot review #4341452823 (comment #3284959762). exitAlternateScreen()
was the last call site in ink.tsx still re-enabling mouse tracking
without DISABLE first. Editors (vim/nvim/less) and tmux can leave
DEC 1003 hover asserted across the handoff back; without DISABLE,
'wheel'/'buttons' presets silently kept hover on after the editor
quit. Now all five enableMouseTrackingFor() call sites in ink.tsx
prepend DISABLE_MOUSE_TRACKING — handleResize, reassertTerminalModes,
reenterAltScreen, setAltScreenMouseTracking, exitAlternateScreen.
* fix(tui): add defensive default to enableMouseTrackingFor switch
Copilot review #4341485231 (comment #3284979323). TS exhaustive switch
returns string per the type system, but a JS caller / corrupted config
/ hot-reload-in-dev could reach the function with an unknown value at
runtime. Without a default, that path returns undefined which then
concatenates as the literal string 'undefined' into the terminal byte
stream — visibly garbling output. Treat unknown as 'off' (no DEC
sequences) so the worst case is silent input loss rather than a
wrecked screen.
---------
Co-authored-by: Nat Thrower <nat@nthrow.io>
40 lines
2.6 KiB
TypeScript
40 lines
2.6 KiB
TypeScript
/// <reference path="./ambient.d.ts" />
|
|
export { default as useStderr } from './src/hooks/use-stderr.ts'
|
|
export type { StderrHandle } from './src/hooks/use-stderr.ts'
|
|
export { default as useStdout } from './src/hooks/use-stdout.ts'
|
|
export type { StdoutHandle } from './src/hooks/use-stdout.ts'
|
|
export { Ansi } from './src/ink/Ansi.tsx'
|
|
export { evictInkCaches } from './src/ink/cache-eviction.ts'
|
|
export type { EvictLevel, InkCacheSizes } from './src/ink/cache-eviction.ts'
|
|
export { AlternateScreen } from './src/ink/components/AlternateScreen.tsx'
|
|
export type { MouseTrackingMode } from './src/ink/termio/dec.ts'
|
|
export { default as Box } from './src/ink/components/Box.tsx'
|
|
export type { Props as BoxProps } from './src/ink/components/Box.tsx'
|
|
export { default as Link } from './src/ink/components/Link.tsx'
|
|
export { default as Newline } from './src/ink/components/Newline.tsx'
|
|
export { NoSelect } from './src/ink/components/NoSelect.tsx'
|
|
export { RawAnsi } from './src/ink/components/RawAnsi.tsx'
|
|
export { default as ScrollBox } from './src/ink/components/ScrollBox.tsx'
|
|
export type { ScrollBoxHandle, ScrollBoxProps } from './src/ink/components/ScrollBox.tsx'
|
|
export { default as Spacer } from './src/ink/components/Spacer.tsx'
|
|
export type { Props as StdinProps } from './src/ink/components/StdinContext.ts'
|
|
export { default as Text } from './src/ink/components/Text.tsx'
|
|
export type { Props as TextProps } from './src/ink/components/Text.tsx'
|
|
export type { Key } from './src/ink/events/input-event.ts'
|
|
export { default as useApp } from './src/ink/hooks/use-app.ts'
|
|
export { useCursorAdvance } from './src/ink/hooks/use-cursor-advance.ts'
|
|
export { useDeclaredCursor } from './src/ink/hooks/use-declared-cursor.ts'
|
|
export { default as useInput } from './src/ink/hooks/use-input.ts'
|
|
export { useHasSelection, useSelection } from './src/ink/hooks/use-selection.ts'
|
|
export { default as useStdin } from './src/ink/hooks/use-stdin.ts'
|
|
export { useTabStatus } from './src/ink/hooks/use-tab-status.ts'
|
|
export { useTerminalFocus } from './src/ink/hooks/use-terminal-focus.ts'
|
|
export { useTerminalTitle } from './src/ink/hooks/use-terminal-title.ts'
|
|
export { useTerminalViewport } from './src/ink/hooks/use-terminal-viewport.ts'
|
|
export { default as measureElement } from './src/ink/measure-element.ts'
|
|
export { createRoot, forceRedraw, default as render, renderSync } from './src/ink/root.ts'
|
|
export type { Instance, RenderOptions, Root } from './src/ink/root.ts'
|
|
export { stringWidth } from './src/ink/stringWidth.ts'
|
|
export { wrapAnsi } from './src/ink/wrapAnsi.ts'
|
|
export { default as TextInput, UncontrolledTextInput } from 'ink-text-input'
|
|
export type { Props as TextInputProps } from 'ink-text-input'
|