mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(tui): keep DECSTBM scroll region off bottom row (#26683)
Avoid shifting the terminal's last visible row in the alt-screen DECSTBM fast path, which can leave transient scroll bleed/discoloration artifacts around the status lane until a repaint. Add regression tests to preserve the fast path when safe and skip it when the hint touches the bottom row.
This commit is contained in:
parent
6784c80794
commit
566d8f0d75
2 changed files with 46 additions and 1 deletions
|
|
@ -42,6 +42,8 @@ const stdoutOnly = (diff: ReturnType<LogUpdate['render']>) =>
|
|||
.map(p => (p as { type: 'stdout'; content: string }).content)
|
||||
.join('')
|
||||
|
||||
const hasDecstbm = (text: string) => /\x1b\[\d+;\d+r/.test(text)
|
||||
|
||||
describe('LogUpdate.render diff contract', () => {
|
||||
it('emits only changed cells when most rows match', () => {
|
||||
const w = 20
|
||||
|
|
@ -154,4 +156,44 @@ describe('LogUpdate.render diff contract', () => {
|
|||
expect(diff.some(p => p.type === 'clearTerminal')).toBe(true)
|
||||
expect(stdoutOnly(diff)).toContain('timer2s')
|
||||
})
|
||||
|
||||
it('keeps DECSTBM fast-path when scroll region stays above bottom row', () => {
|
||||
const w = 12
|
||||
const h = 6
|
||||
const prev = mkScreen(w, h)
|
||||
const next = mkScreen(w, h)
|
||||
|
||||
paint(prev, 1, 'row one')
|
||||
paint(next, 1, 'row one')
|
||||
|
||||
const prevFrame = mkFrame(prev, w, h)
|
||||
const nextFrame: Frame = {
|
||||
...mkFrame(next, w, h),
|
||||
scrollHint: { top: 1, bottom: 4, delta: 1 }
|
||||
}
|
||||
const log = new LogUpdate({ isTTY: true, stylePool })
|
||||
const diff = log.render(prevFrame, nextFrame, true, true)
|
||||
|
||||
expect(hasDecstbm(stdoutOnly(diff))).toBe(true)
|
||||
})
|
||||
|
||||
it('skips DECSTBM when scroll region touches the bottom row', () => {
|
||||
const w = 12
|
||||
const h = 6
|
||||
const prev = mkScreen(w, h)
|
||||
const next = mkScreen(w, h)
|
||||
|
||||
paint(prev, 1, 'row one')
|
||||
paint(next, 1, 'row one')
|
||||
|
||||
const prevFrame = mkFrame(prev, w, h)
|
||||
const nextFrame: Frame = {
|
||||
...mkFrame(next, w, h),
|
||||
scrollHint: { top: 1, bottom: 5, delta: 1 }
|
||||
}
|
||||
const log = new LogUpdate({ isTTY: true, stylePool })
|
||||
const diff = log.render(prevFrame, nextFrame, true, true)
|
||||
|
||||
expect(hasDecstbm(stdoutOnly(diff))).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -175,7 +175,10 @@ export class LogUpdate {
|
|||
if (altScreen && next.scrollHint && decstbmSafe) {
|
||||
const { top, bottom, delta } = next.scrollHint
|
||||
|
||||
if (top >= 0 && bottom < prev.screen.height && bottom < next.screen.height) {
|
||||
// Keep DECSTBM away from the terminal's last visible row. In alt-screen
|
||||
// layouts we reserve that lane for status/cursor parking, and scrolling
|
||||
// it can leave transient ghosting/bleed artifacts until a later repaint.
|
||||
if (top >= 0 && bottom < prev.screen.height - 1 && bottom < next.screen.height - 1) {
|
||||
shiftRows(prev.screen, top, bottom, delta)
|
||||
scrollPatch = [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue