Skip to content

fix(tui): avoid destructive redraw on focus regain - #88596

Closed
helix4u wants to merge 1 commit into
NousResearch:mainfrom
helix4u:fix/tui-focus-redraw-pr
Closed

fix(tui): avoid destructive redraw on focus regain#88596
helix4u wants to merge 1 commit into
NousResearch:mainfrom
helix4u:fix/tui-focus-redraw-pr

Conversation

@helix4u

@helix4u helix4u commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Makes terminal focus recovery repaint from blank virtual frame buffers without clearing the physical screen or re-asserting terminal modes.

The focus-regain handler previously reused the destructive /redraw path. Every focus change sent ERASE_SCREEN, resent mouse-tracking DECRESET sequences, and then rebuilt the frame. That caused a visible full-screen redraw and introduced unnecessary terminal-mode output on ordinary tab or pane changes.

The replacement keeps the stale-row recovery: it resets the virtual buffers and renders a complete replacement frame. The existing screen remains visible until that frame is ready, and focus regain sends neither the clear-screen sequence nor mouse-mode resets.

Related Issue

Discord support report: https://discord.com/channels/1053877538025386074/1538966610776301719

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Replace focus-in's reassertTerminalModes() plus forceRedraw() calls with a virtual frame reset and direct repaint.
  • Add a behavioral regression test proving focus recovery repaints content without emitting ERASE_SCREEN or DISABLE_MOUSE_TRACKING.

How to Test

  1. Run npm test -- packages/hermes-ink/src/ink/ink-focus-redraw.test.ts from ui-tui.
  2. Run npm run typecheck from ui-tui.
  3. Switch focus away from and back to an active TUI tab or pane and confirm the current content repaints without a visible clear-screen cycle or an inserted lowercase l.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Windows 11

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

Focused Vitest regression, TUI TypeScript check, and targeted ESLint all pass locally. Python tests were not run because this is a TypeScript-only change.

@helix4u
helix4u marked this pull request as ready for review August 17, 2026 19:20
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Aug 17, 2026
@austinpickett

Copy link
Copy Markdown
Collaborator

Superseded by #89623.

Your report is right and the flicker is real — I reproduced it: forceRedraw() writes ERASE_SCREEN + CURSOR_HOME and then the frame, so the terminal can present a blank screen between the two writes. That's the flash.

The one thing I changed is where the fix lands. Dropping the clear also drops the healing this handler was added for (#86332): when an emulator throttles hidden-tab output, Ink's virtual frame can think a row is already blank while the physical screen still shows the old status text, and the cell diff skips blank-over-blank — so the stale row stays. Replaying the emitted ANSI into a terminal model, the stale row survives on both alt and main screen on this branch.

#89623 keeps your commit and your framing, and fixes the flicker the other way: the clear stays but stops being its own write, queued via needsEraseBeforePaint so clear+paint land in a single write. That also got extended to main-screen (INLINE_MODE/Termux), which had no in-band erase path at all — so the flicker is gone there too.

Thanks for finding this — the atomicity gap was real and had been sitting in both screens.

austinpickett added a commit that referenced this pull request Aug 19, 2026
Two defects from the review of the previous commit.

Scrollback erase leaked to focus regain. Reusing needsEraseBeforePaint
routed focus-in through the same erase selection as resize healing,
whose heuristic is TERM_PROGRAM == 'Apple_Terminal' — so on Apple
Terminal an ordinary tab or pane switch emitted CSI 3J and wiped the
user's scrollback. That erase exists to clear alt-screen reflow
artifacts after a resize, which is the only case worth discarding
history for. Track the deep erase behind its own flag, set only by
resize healing; every other requester gets 2J.

Terminal modes were never re-asserted. #88596 dropped
reassertTerminalModes(false) from the focus path and the previous
commit did not restore it, leaving one caller (onStdinResume). An
emulator that cleared the DEC mouse modes while the pane was hidden
then stayed dead until the DECRQM watchdog's next 2s probe. Restore the
non-destructive call — extended keys plus mouse preset, no alt-screen
re-entry, no erase — so it costs a few idempotent bytes and no flicker.

Both are covered: reverting either fix fails its test. The mode
assertion is checked on the alt screen only, since mouse tracking is
alt-screen-scoped and reassertTerminalModes returns early on main.

Reported-by: Copilot
OutThisLife pushed a commit that referenced this pull request Aug 19, 2026
…ic-repaint

fix(tui): heal focus regain without a separate screen clear (supersedes #88596)
lisajlau pushed a commit to lisajlau/hermes-agent that referenced this pull request Aug 20, 2026
…-regain-atomic-repaint

fix(tui): heal focus regain without a separate screen clear (supersedes NousResearch#88596)
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/) P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants