fix(cloud): WAKE-SCROLLBACK-SCROLL scroll back with intent enforcement paused (its synchronous follow-output snapped back), then pin

This commit is contained in:
Neil
2026-11-11 11:18:34 -08:00
parent 98cc759063
commit cc79bf1fbe
2 changed files with 25 additions and 11 deletions
@@ -1,5 +1,9 @@
import { RELEASE_SYNCHRONIZED_OUTPUT } from '../../../../../../shared/terminal/protocol/input-modes/terminal-mode-reset-profiles'
import { syncTerminalScrollIntentFromViewport } from '@/lib/pane-manager/terminal-scroll-intent'
import {
beginTerminalScrollIntentBufferRebuild,
endTerminalScrollIntentBufferRebuild
} from '@/lib/pane-manager/terminal-scroll-intent-rebuild'
import {
cancelDeferredScrollRestore,
cancelPendingFitScrollRestore
@@ -199,18 +203,26 @@ export function bindReplayDataDrain(session: ConnectPanePtySession): void {
continue
}
if (preSleep) {
const terminal = session.pane.terminal
await finishWakeScrollbackRestore(
preSleep,
session.pane.terminal,
session.writeReplayDataAsync
terminal,
session.writeReplayDataAsync,
(line) => {
// The person's place wins: drop restores captured before it (a remount's first fit, at
// the bottom), scroll with intent enforcement paused (its synchronous "follow output"
// would snap back), then record the place as a pin for the fits still to come.
cancelDeferredScrollRestore(terminal)
cancelPendingFitScrollRestore(terminal)
beginTerminalScrollIntentBufferRebuild(terminal)
try {
terminal.scrollToLine(line)
} finally {
endTerminalScrollIntentBufferRebuild(terminal)
}
syncTerminalScrollIntentFromViewport(terminal, { allowBufferShrink: true })
}
)
if (preSleep.scrolledTo !== null) {
// The person's place wins: drop restores captured before it (a remount's first fit, at the
// bottom, waiting for a later fit) and record it as a pin for the fits still to come.
cancelDeferredScrollRestore(session.pane.terminal)
cancelPendingFitScrollRestore(session.pane.terminal)
syncTerminalScrollIntentFromViewport(session.pane.terminal, { allowBufferShrink: true })
}
if (!isCurrentPayload()) {
continue
}
@@ -319,7 +319,9 @@ export function composeWakeRestore(block: string, terminal: WokenTerminal): stri
export async function finishWakeScrollbackRestore(
restore: WakeScrollbackRestore,
terminal: WokenTerminal,
write: (data: string) => Promise<void>
write: (data: string) => Promise<void>,
/** Puts the person back on their line; the pane may pass one that keeps its scroll intent in step. */
scrollTo: (line: number) => void = (line) => terminal.scrollToLine(line)
): Promise<boolean> {
const data = composeWakeRestore(restore.block, terminal)
if (data !== null) {
@@ -333,7 +335,7 @@ export async function finishWakeScrollbackRestore(
return false
}
if (restore.scrolledTo !== null) {
terminal.scrollToLine(restore.scrolledTo)
scrollTo(restore.scrolledTo)
}
return true
}