feat(desktop): persist & restore terminal tabs + scrollback across relaunch - #54585
Conversation
…launch User terminal tabs and their recent scrollback now survive an app restart (VS Code parity). Tabs, active selection, cwd, and a serialized scrollback snapshot are written to localStorage on every change; on launch the tabs reopen with their history replayed above a fresh shell. Processes are NOT revived — a new shell starts one line below the restored block. - Capture: SerializeAddon snapshots the buffer on a 750ms leading-edge throttle, so a `cmd; quit` lands on disk before teardown; the snapshot is trimmed of its trailing idle prompt (no "double prompt" on restore) and capped (200 scrollback lines / 48k chars) to stay under the storage budget. - Teardown guard: app quit/reload kills the PTYs from the main process, firing onExit in the renderer, but React skips effect cleanups on teardown so the per-instance `disposed` flag never flips. A pagehide/beforeunload flag stops onExit from calling closeTerminal() and wiping the persisted tabs right before relaunch restores them. A real `exit`/Ctrl-D still closes. - Agent mirror tabs stay runtime-only — only user tabs persist.
|
Reviewed — this is clean, well-scoped desktop work. Verified against current Premise holds. It follows the exact app persistence convention — Validation:
Nice catch on the teardown bug. The One nit (non-blocking): the new test file has no Approach is right (snapshot + replay, no fake process revival). Authorship will be preserved via rebase-merge when we land it. Thanks @OutThisLife! |
…rminal-history feat(desktop): persist & restore terminal tabs + scrollback across relaunch
…rminal-history feat(desktop): persist & restore terminal tabs + scrollback across relaunch
…rminal-history feat(desktop): persist & restore terminal tabs + scrollback across relaunch
…rminal-history feat(desktop): persist & restore terminal tabs + scrollback across relaunch
…rminal-history feat(desktop): persist & restore terminal tabs + scrollback across relaunch
Summary
The desktop app's user terminal tabs are wiped on every restart. This adds VS Code-style persistence: tabs reopen on launch with their recent scrollback replayed above a fresh shell. Processes are not revived — a new shell starts one line below the restored history (no fake "reconnected" state).
Tabs, the active selection, each tab's cwd, and a serialized scrollback snapshot are persisted to
localStorage(hermes.desktop.terminals.v1) synchronously on every change, so a snapshot is always on disk before the window tears down — no fragile unload-time flush.How it works
use-terminal-session.ts): aSerializeAddonsnapshots the buffer on a 750 ms leading-edge throttle — the first output after an idle gap persists almost immediately (socmd; quitis captured), then at most once per window while output streams.cleanReviveSnapshot): the snapshot's trailing idle prompt is stripped (the short block after the shell's last blank line) so the restore doesn't show the old prompt directly above the fresh one. Only a short tail is dropped — real command output is never trimmed, and prompts without a leading blank just keep the historical prompt.workspace.tsx→instance.tsx→ hook): the snapshot is written once on mount, then a newline, then the fresh shell boots beneath it.persistTerminalsfilters tokind === 'user', andupdateTerminalReviveBufferignores agent tabs.Key correctness fix — no data loss on quit
App quit / reload / window-close kills the PTYs from the main process, which fires
onExitin the renderer. But React skips effect cleanups on teardown, so the per-instancedisposedflag never flips — the oldonExitpath would then callcloseTerminal()for every tab and wipe the persisted list right before relaunch reads it (symptom: "2nd terminal missing after Cmd+Q", intermittent). A module-levelpagehide/beforeunloadguard now suppressesonExit-drivencloseTerminalduring teardown. A genuineexit/ Ctrl-D still closes the tab.Touch points
terminal/terminals.tsupdateTerminalReviveBuffer, capterminal/use-terminal-session.tsterminal/instance.tsx,terminal/workspace.tsxreviveBufferthrough to the hookapps/desktop/package.json,package-lock.json@xterm/addon-serializeterminal/terminals.test.tsTest plan
tsc -p . --noEmitcleanterminals.test.ts): restore tabs/active/history on load; sync persist; agent tabs never persist; oversized buffer tail-trimmed; storage cleared when all tabs closeexit/ Ctrl-D in a tab still closes just that tab