mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 16:03:53 +00:00
Reset terminal SGR state around restored output (#14700)
* fix(terminal): reset SGR around restored output * fix(terminal): preserve live replay styling * fix(terminal): ground dead reattach fallback
This commit is contained in:
@@ -2,7 +2,10 @@ import { describe, expect, it } from 'vitest'
|
||||
import { HeadlessEmulator } from './headless-emulator'
|
||||
import { buildRehydrateSequences } from './terminal-mode-rehydrate-sequences'
|
||||
import { getRecoveredHistorySeedSegments } from './terminal-history-seed-segments'
|
||||
import { COLD_RESTORE_SEED_MODE_RESET } from '../../shared/terminal-mode-reset-profiles'
|
||||
import {
|
||||
COLD_RESTORE_SEED_MODE_RESET,
|
||||
RESET_GRAPHIC_RENDITION
|
||||
} from '../../shared/terminal-mode-reset-profiles'
|
||||
import type { ColdRestoreInfo } from './terminal-history-cold-restore-info'
|
||||
import type { TerminalModes } from './types'
|
||||
|
||||
@@ -38,7 +41,7 @@ describe('getRecoveredHistorySeedSegments', () => {
|
||||
restoreInfo({ pendingEscapeTailAnsi: '\x1b[3' })
|
||||
)
|
||||
expect(segments).toEqual([
|
||||
'\x1b[?1003h\x1b[?1006h',
|
||||
`${RESET_GRAPHIC_RENDITION}\x1b[?1003h\x1b[?1006h`,
|
||||
'user@host ~ $ \x1b[?1003h',
|
||||
MOUSE_OFF,
|
||||
'\x1b[3'
|
||||
@@ -50,7 +53,7 @@ describe('getRecoveredHistorySeedSegments', () => {
|
||||
getRecoveredHistorySeedSegments(
|
||||
restoreInfo({ modes: { ...ARMED_MODES, alternateScreen: true } })
|
||||
)
|
||||
).toEqual(['user@host ~ $ ', MOUSE_OFF])
|
||||
).toEqual([`${RESET_GRAPHIC_RENDITION}user@host ~ $ `, MOUSE_OFF])
|
||||
})
|
||||
|
||||
it('stays empty when there is no recovered normal buffer', () => {
|
||||
@@ -116,4 +119,10 @@ describe('getRecoveredHistorySeedSegments', () => {
|
||||
// snapshots must keep re-arming or an alt-screen TUI loses scroll forever.
|
||||
expect(buildRehydrateSequences(ARMED_MODES)).toBe('\x1b[?1003h\x1b[?1006h')
|
||||
})
|
||||
|
||||
it('grounds the pen before re-entering the alternate screen', () => {
|
||||
expect(buildRehydrateSequences({ ...ARMED_MODES, alternateScreen: true })).toBe(
|
||||
`${RESET_GRAPHIC_RENDITION}\x1b[?1049h\x1b[?1003h\x1b[?1006h`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { ColdRestoreInfo } from './terminal-history-cold-restore-info'
|
||||
import { COLD_RESTORE_SEED_MODE_RESET } from '../../shared/terminal-mode-reset-profiles'
|
||||
import {
|
||||
COLD_RESTORE_SEED_MODE_RESET,
|
||||
RESET_GRAPHIC_RENDITION
|
||||
} from '../../shared/terminal-mode-reset-profiles'
|
||||
|
||||
// Why the reset belongs in the seed and not only at replay: the recovered stream
|
||||
// re-arms mouse reporting from two independent sources (rehydrateSequences AND
|
||||
@@ -12,7 +15,9 @@ import { COLD_RESTORE_SEED_MODE_RESET } from '../../shared/terminal-mode-reset-p
|
||||
export function getRecoveredHistorySeedSegments(restoreInfo: ColdRestoreInfo): readonly string[] {
|
||||
if (restoreInfo.modes.alternateScreen) {
|
||||
const normalBuffer = restoreInfo.scrollbackAnsi || restoreInfo.snapshotAnsi
|
||||
return normalBuffer ? [normalBuffer, COLD_RESTORE_SEED_MODE_RESET] : []
|
||||
return normalBuffer
|
||||
? [`${RESET_GRAPHIC_RENDITION}${normalBuffer}`, COLD_RESTORE_SEED_MODE_RESET]
|
||||
: []
|
||||
}
|
||||
const recovered = [restoreInfo.rehydrateSequences, restoreInfo.snapshotAnsi].filter(
|
||||
(segment) => segment.length > 0
|
||||
@@ -25,5 +30,9 @@ export function getRecoveredHistorySeedSegments(restoreInfo: ColdRestoreInfo): r
|
||||
}
|
||||
// Why after the snapshot: it must undo the snapshot's own mode trailer, and
|
||||
// pendingEscapeTailAnsi is a torn escape that has to stay at the very end.
|
||||
return [...recovered, COLD_RESTORE_SEED_MODE_RESET, ...(escapeTail ? [escapeTail] : [])]
|
||||
const [firstRecovered, ...remainingRecovered] = recovered
|
||||
const groundedRecovered = firstRecovered
|
||||
? [`${RESET_GRAPHIC_RENDITION}${firstRecovered}`, ...remainingRecovered]
|
||||
: []
|
||||
return [...groundedRecovered, COLD_RESTORE_SEED_MODE_RESET, ...(escapeTail ? [escapeTail] : [])]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { TerminalModes } from './types'
|
||||
import { RESET_GRAPHIC_RENDITION } from '../../shared/terminal-mode-reset-profiles'
|
||||
|
||||
// Why no kitty flags here: rehydrateSequences feeds renderer xterms, and
|
||||
// POST_REPLAY_REATTACH_RESET's deliberate kitty reset (stale CSI-u Ctrl+C
|
||||
@@ -10,7 +11,7 @@ export function buildRehydrateSequences(modes: TerminalModes): string {
|
||||
if (modes.alternateScreen) {
|
||||
// Why: normal-buffer serialization can leave its pen active, while the
|
||||
// separately serialized alt body assumes it starts from default SGR.
|
||||
seqs.push('\x1b[0m\x1b[?1049h')
|
||||
seqs.push(`${RESET_GRAPHIC_RENDITION}\x1b[?1049h`)
|
||||
}
|
||||
if (modes.bracketedPaste) {
|
||||
seqs.push('\x1b[?2004h')
|
||||
|
||||
Reference in New Issue
Block a user