mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 03:56:55 +03:00
fix(app): bound terminal snapshot serialization on teardown (#47924)
This commit is contained in:
committed by
GitHub
Unverified
parent
22a534a0bb
commit
09c318094c
@@ -63,6 +63,36 @@ describe("SerializeAddon", () => {
|
||||
}
|
||||
})
|
||||
|
||||
describe("scrollback option", () => {
|
||||
test("reads only the requested tail and restores the cursor on its screen row", async () => {
|
||||
const { term, addon } = createTerminal(20, 5)
|
||||
await writeAndWait(term, Array.from({ length: 30 }, (_, i) => `line ${i}`).join("\r\n"))
|
||||
await writeAndWait(term, "\x1b[2A\x1b[3G")
|
||||
expect(term.buffer.normal.length).toBe(30)
|
||||
expect([term.buffer.normal.cursorX, term.buffer.normal.cursorY]).toEqual([2, 2])
|
||||
|
||||
const reads = spyOn(term.buffer.normal, "getLine")
|
||||
const serialized = addon.serialize({ scrollback: 3 })
|
||||
expect(new Set(reads.mock.calls.map((args) => args[0]))).toEqual(new Set([22, 23, 24, 25, 26, 27, 28, 29]))
|
||||
reads.mockRestore()
|
||||
|
||||
const restored = createTerminal(20, 5)
|
||||
await writeAndWait(restored.term, serialized)
|
||||
expect(restored.term.getScrollbackLength()).toBe(3)
|
||||
for (let row = 0; row < 8; row++) {
|
||||
expect(restored.term.buffer.normal.getLine(row)?.translateToString(true)).toBe(`line ${22 + row}`)
|
||||
}
|
||||
expect([restored.term.buffer.normal.cursorX, restored.term.buffer.normal.cursorY]).toEqual([2, 2])
|
||||
})
|
||||
|
||||
test("serializes the whole buffer when it has fewer rows than requested", async () => {
|
||||
const { term, addon } = createTerminal(20, 5)
|
||||
await writeAndWait(term, Array.from({ length: 30 }, (_, i) => `line ${i}`).join("\r\n"))
|
||||
|
||||
expect(addon.serialize({ scrollback: 100 })).toBe(addon.serialize())
|
||||
})
|
||||
})
|
||||
|
||||
test("preserves color scheme reporting mode", async () => {
|
||||
const { term, addon } = createTerminal()
|
||||
await writeAndWait(term, "\x1b[?2031h")
|
||||
|
||||
@@ -481,12 +481,12 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
if (excludeFinalCursorPosition) return content
|
||||
|
||||
const absoluteCursorRow = (this._buffer.baseY ?? 0) + this._buffer.cursorY
|
||||
const cursorRow = constrain(absoluteCursorRow - this._firstRow + 1, 1, Number.MAX_SAFE_INTEGER)
|
||||
const cursorCol = this._buffer.cursorX + 1
|
||||
content += `\u001b[${cursorRow};${cursorCol}H`
|
||||
// CUP addresses the screen and ghostty-web reports cursorY relative to the screen, so the
|
||||
// serialized range start must not shift the row. The cursor line sits in the screen region
|
||||
// at the bottom of the buffer, after any scrollback rows.
|
||||
content += `\u001b[${this._buffer.cursorY + 1};${this._buffer.cursorX + 1}H`
|
||||
|
||||
const line = this._buffer.getLine(absoluteCursorRow)
|
||||
const line = this._buffer.getLine(this._buffer.length - this._terminal.rows + this._buffer.cursorY)
|
||||
const cell = line?.getCell(this._buffer.cursorX)
|
||||
const style = (() => {
|
||||
if (!cell) return this._buffer.getNullCell()
|
||||
|
||||
@@ -20,6 +20,12 @@ import { terminalWriter } from "@/session/terminal/writer"
|
||||
|
||||
const TOGGLE_TERMINAL_ID = "terminal.toggle"
|
||||
const DEFAULT_TOGGLE_TERMINAL_KEYBIND = "ctrl+`"
|
||||
// Serialization on unmount is a synchronous O(rows x cols) walk on the main thread and the
|
||||
// result is written to localStorage or desktop state for every terminal in the workspace.
|
||||
// Persisting the most recent 2k scrollback rows keeps restore fidelity for the history users
|
||||
// actually scroll back through while capping teardown cost and snapshot size; the live
|
||||
// terminal keeps its full 10k scrollback while mounted.
|
||||
const persistedScrollbackRows = 2_000
|
||||
export interface TerminalProps extends ComponentProps<"div"> {
|
||||
pty: LocalPTY
|
||||
autoFocus?: boolean
|
||||
@@ -152,7 +158,7 @@ const persistTerminal = (input: {
|
||||
if (!input.addon || !input.onCleanup || !input.term) return
|
||||
const buffer = (() => {
|
||||
try {
|
||||
return input.addon.serialize()
|
||||
return input.addon.serialize({ scrollback: persistedScrollbackRows })
|
||||
} catch {
|
||||
debugTerminal("failed to serialize terminal buffer")
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user