mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 02:31:47 +00:00
fix(tui): clear Apple Terminal resize artifacts
Use a deeper alt-screen clear for Apple Terminal resize repaints so host reflow artifacts do not survive the recovery frame.
This commit is contained in:
parent
e527240b27
commit
279b656adc
3 changed files with 40 additions and 9 deletions
|
|
@ -73,7 +73,13 @@ import {
|
|||
startSelection,
|
||||
updateSelection
|
||||
} from './selection.js'
|
||||
import { supportsExtendedKeys, SYNC_OUTPUT_SUPPORTED, type Terminal, writeDiffToTerminal } from './terminal.js'
|
||||
import {
|
||||
needsAltScreenResizeScrollbackClear,
|
||||
supportsExtendedKeys,
|
||||
SYNC_OUTPUT_SUPPORTED,
|
||||
type Terminal,
|
||||
writeDiffToTerminal
|
||||
} from './terminal.js'
|
||||
import {
|
||||
CURSOR_HOME,
|
||||
cursorMove,
|
||||
|
|
@ -82,7 +88,8 @@ import {
|
|||
DISABLE_MODIFY_OTHER_KEYS,
|
||||
ENABLE_KITTY_KEYBOARD,
|
||||
ENABLE_MODIFY_OTHER_KEYS,
|
||||
ERASE_SCREEN
|
||||
ERASE_SCREEN,
|
||||
ERASE_SCROLLBACK
|
||||
} from './termio/csi.js'
|
||||
import {
|
||||
DBP,
|
||||
|
|
@ -121,6 +128,11 @@ const ERASE_THEN_HOME_PATCH = Object.freeze({
|
|||
content: ERASE_SCREEN + CURSOR_HOME
|
||||
})
|
||||
|
||||
const DEEP_ERASE_THEN_HOME_PATCH = Object.freeze({
|
||||
type: 'stdout' as const,
|
||||
content: ERASE_SCREEN + ERASE_SCROLLBACK + CURSOR_HOME
|
||||
})
|
||||
|
||||
// Cached per-Ink-instance, invalidated on resize. frame.cursor.y for
|
||||
// alt-screen is always terminalRows - 1 (renderer.ts).
|
||||
function makeAltScreenParkPatch(terminalRows: number) {
|
||||
|
|
@ -863,17 +875,17 @@ export default class Ink {
|
|||
// position independently. Parking at bottom (not 0,0) keeps the guide
|
||||
// where the user's attention is.
|
||||
//
|
||||
// After resize, prepend ERASE_SCREEN too. The diff only writes cells
|
||||
// After resize, prepend a clear too. The diff only writes cells
|
||||
// that changed; cells where new=blank and prev-buffer=blank get skipped
|
||||
// — but the physical terminal still has stale content there (shorter
|
||||
// lines at new width leave old-width text tails visible). ERASE inside
|
||||
// BSU/ESU is atomic: old content stays visible until the whole
|
||||
// erase+paint lands, then swaps in one go. Writing ERASE_SCREEN
|
||||
// synchronously in handleResize would blank the screen for the ~80ms
|
||||
// render() takes.
|
||||
// lines at new width leave old-width text tails visible). Apple Terminal
|
||||
// can also preserve alt-screen reflow artifacts in scrollback during
|
||||
// resize, so it gets CSI 3J in this one recovery path. When BSU/ESU is
|
||||
// supported, the clear+paint lands atomically; otherwise the final state
|
||||
// is still healed even if the repaint is visible.
|
||||
if (this.needsEraseBeforePaint) {
|
||||
this.needsEraseBeforePaint = false
|
||||
optimized.unshift(ERASE_THEN_HOME_PATCH)
|
||||
optimized.unshift(needsAltScreenResizeScrollbackClear() ? DEEP_ERASE_THEN_HOME_PATCH : ERASE_THEN_HOME_PATCH)
|
||||
} else {
|
||||
optimized.unshift(CURSOR_HOME_PATCH)
|
||||
}
|
||||
|
|
|
|||
15
ui-tui/packages/hermes-ink/src/ink/terminal.test.ts
Normal file
15
ui-tui/packages/hermes-ink/src/ink/terminal.test.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { needsAltScreenResizeScrollbackClear } from './terminal.js'
|
||||
|
||||
describe('terminal resize quirks', () => {
|
||||
it('uses a deeper alt-screen resize clear for Apple Terminal', () => {
|
||||
expect(needsAltScreenResizeScrollbackClear({ TERM_PROGRAM: 'Apple_Terminal' })).toBe(true)
|
||||
expect(needsAltScreenResizeScrollbackClear({ TERM_PROGRAM: ' Apple_Terminal ' })).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps the normal resize repaint path for modern terminals', () => {
|
||||
expect(needsAltScreenResizeScrollbackClear({ TERM_PROGRAM: 'vscode' })).toBe(false)
|
||||
expect(needsAltScreenResizeScrollbackClear({ TERM_PROGRAM: 'iTerm.app' })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
@ -168,6 +168,10 @@ export function isXtermJs(): boolean {
|
|||
return xtversionName?.startsWith('xterm.js') ?? false
|
||||
}
|
||||
|
||||
export function needsAltScreenResizeScrollbackClear(env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
return (env.TERM_PROGRAM ?? '').trim() === 'Apple_Terminal'
|
||||
}
|
||||
|
||||
// Terminals known to correctly implement the Kitty keyboard protocol
|
||||
// (CSI >1u) and/or xterm modifyOtherKeys (CSI >4;2m) for ctrl+shift+<letter>
|
||||
// disambiguation. We previously enabled unconditionally (#23350), assuming
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue