Skip to content

fix(tui): v2 blitz-test UX pack - #13589

Closed
OutThisLife wants to merge 7 commits into
mainfrom
bb/tui-v2-blitz-fixes
Closed

fix(tui): v2 blitz-test UX pack#13589
OutThisLife wants to merge 7 commits into
mainfrom
bb/tui-v2-blitz-fixes

Conversation

@OutThisLife

Copy link
Copy Markdown
Collaborator

Addresses the Hermes TUI v2 blitz-test feedback (Confluence).

Summary

Seven surgical fixes, one commit each:

  • @file completion on Enter — path completions now apply on Enter instead of submitting the incomplete draft (was gated to slash commands only). (Update snapshot #5)
  • Pager scroll/history, /toolsets, /help and any paged slash output gain ↑↓ / j / k for line scroll, PgUp / b for page back, g / G for top/bottom; Enter/Space/PgDn still advance-and-auto-close. (Terminal tool #1, Fix terminal interactivity #4)
  • Model picker stable height — every slot (warning, ↑/↓ hints, item rows) renders unconditionally so the popup stops jitter-resizing during keyboard navigation. (Terminal tool #1)
  • Picker contrast — selected rows in model/session/skills pickers and approval/clarify prompts use inverse bold with brand accent instead of a dim→cornsilk color swap. (Fix VM instance sharing across tasks #6)
  • Readline parity on Linux — Ctrl+A jumps to line start (Mac keeps Cmd+A = select-all), wordMod = mod \|\| k.meta so Alt+B/F/Backspace/Delete work on Linux while Ctrl/Cmd chords keep working. Aligns code with the hotkey table already in the README. (Support passing morph snapshot id #2, Update snapshot #5, Fix VM instance sharing across tasks #6)
  • /tools list / info / etc. — unknown /tools subcommands delegate to slash.exec (Python's hermes_cli already implements tools list), with the same page/sys output split used by createSlashHandler. (Terminal tool #1)
  • Ctrl+C preserves prior outputinterruptTurn flushes every accumulated segment message plus the in-flight partial (with its pendingSegmentTools) to the transcript before idle() wipes them. Resolves the "everything disappears when I cancel" regression. (Cluster failure tracking #10)

Out of scope here: process-title indicators (#5), /resume across telegram sessions (#7 — needs Python edit), code-review diff ordering (#8 — Python), multiline-up history flip (#5), word-wrap flicker (#9).

Test plan

  • npm run type-check clean
  • npm test — 152/152 pass
  • Manual: trigger /toolsets, scroll up/down, then q
  • Manual: /tools list
  • Manual: @<file> tab through options, press Enter — expect insert, not submit
  • Manual: open /model, scroll — height should not bounce
  • Manual: Linux Ctrl+A → start of line; Alt+B / Alt+F → word nav
  • Manual: start a long turn, Ctrl+C mid-stream — prior commentary + tool output stay visible

Completion selection on Enter was gated to slash commands only
(value.startsWith('/')), so @file, ./path, and ~/path completions fell
through and submitted the incomplete input instead of inserting the
highlighted row.

Guard on completions.length && compReplace > 0 — useCompletion already
scopes population to slash and path tokens, and the next !== value check
keeps plain-text submits working when the completion is already applied.
The pager overlay backing /history, /toolsets, /help and any paged slash
output only advanced with Enter/Space and closed at the end. Could not
scroll back, scroll line-by-line, or jump to endpoints.

Adds Up/Down (↑↓, j/k), PgUp (b), g/G for top/bottom, keeps existing
Enter/Space/PgDn forward-and-auto-close, and clamps offset so
over-scrolling past the last page is a no-op.
Warning row, "↑ N more" / "↓ N more" hints, and the items list were all
conditionally rendered, so the picker jumped in size as the selection
moved or providers without a warning slid into view.

Render every slot unconditionally: warning falls back to a blank line,
hints render an empty string when at the edge, and the items grid always
emits VISIBLE rows padded with blanks. Height is now constant across
providers, model counts, and scroll position.
Selected rows in the model/session/skills pickers and approval/clarify
prompts only changed from dim gray to cornsilk, which reads as low
contrast on lighter themes and LCDs (reported during TUI v2 blitz).

Switch the selected row to `inverse bold` with the brand accent color
across modelPicker, sessionPicker, skillsHub, and prompts so the
highlight is terminal-portable and unambiguous. Unselected rows stay
dim. Also extends the sessionPicker middle meta column (which was
always dim) to inherit the row's selection state.
textInput treated the platform action-mod (Cmd on macOS, Ctrl on Linux)
as the sole word-boundary modifier. On Linux that meant:

- Ctrl+A selected all instead of jumping to line start (contra standard
  readline and the hotkey doc in README.md which says `Ctrl+A` = Start
  of line).
- Alt+B / Alt+F / Alt+Backspace / Alt+Delete were dropped, because
  `key.meta` was never consulted — the README already documented
  `Meta+B` / `Meta+F` as word nav.

Gate select-all to macOS Cmd+A (`isMac && mod && inp === 'a'`), route
Linux Ctrl+A through `actionHome`, and broaden every word-boundary
predicate (b/f/Backspace/Delete and the modified arrow keys) from `mod`
to `wordMod = mod || k.meta` so Alt chords work on Linux and Mac while
existing Ctrl/Cmd chords keep working.
/tools' local handler silently returned for anything other than enable
or disable, so /tools list and friends looked broken even though the
Python CLI already implements them (hermes_cli/main.py registers
tools_sub for list/enable/disable).

Keep the client-owned enable/disable path (which has to run
session.setSessionStartedAt + resetVisibleHistory locally) and route
every other sub through slash.exec, matching createSlashHandler's
page/sys split for long vs short output.
interruptTurn only flushed the in-flight streaming chunk (bufRef) to
the transcript before calling idle(), which wiped segmentMessages and
pendingSegmentTools. Every tool call and commentary line the agent had
already emitted in the current turn disappeared the moment the user
cancelled, even though that output is exactly what they want to keep
when they hit Ctrl+C (quote from the blitz feedback: "everything was
fine up until the point where you wanted to push to main").

Append each flushed segment message to the transcript first, then
render the in-flight partial with the `*[interrupted]*` marker and its
pendingSegmentTools. Sys-level "interrupted" note still fires when
there is nothing to preserve.
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ npm lockfile hash out of date

Checked against commit 0b5c535 (PR head at check time).

The hash = "sha256-..." line in these nix files no longer matches the committed package-lock.json:

Apply the fix

  • Apply lockfile fix — tick to push a commit with the correct hashes to this PR branch
  • Or run the Nix Lockfile Fix workflow manually (pass PR #13589)
  • Or locally: nix run .#fix-lockfiles -- --apply and commit the diff

@OutThisLife

Copy link
Copy Markdown
Collaborator Author

Splitting into smaller PRs per fix; only the two modelPicker-touching ones (A6+A7) will share a branch.

@OutThisLife
OutThisLife deleted the bb/tui-v2-blitz-fixes branch April 21, 2026 15:59
@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants