Skip to content

Fix Windows ConPTY overlay after cold restore - #7310

Merged
Jinwoo-H merged 2 commits into
mainfrom
Jinwoo-H/fix-conpty-cold-restore-overlay
Jul 4, 2026
Merged

Jinwoo-H merged 2 commits into
mainfrom
Jinwoo-H/fix-conpty-cold-restore-overlay

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • preserve restored scrollback while moving it out of the viewport before fresh-shell output
  • replace daemon cold-restore scrollback clearing with replay-guarded newline scrolling, avoiding ESC[3J
  • cover fresh spawn, daemon cold restore, restored-buffer marking, and metadata-only eager replay behavior

Verification

  • pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts src/renderer/src/components/terminal-pane/layout-serialization.test.ts src/renderer/src/components/terminal-pane/terminal-output-visibility.test.ts
  • pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-transport.test.ts -t "does not clear before replaying|does not write an unterminated title-only|does not let an unterminated OSC 9999|does not emit a clear for empty"
  • pnpm typecheck
  • pnpm lint

Jinwoo-H added 2 commits July 3, 2026 23:11
Fresh Windows ConPTY shells paint prompts at viewport coordinates, so restored rows left visible can be overwritten with stale tails. Mark panes that replay persisted scrollback and, only when a fresh shell wins or daemon cold-restore spawns, replay newline scrolling through the guard to move those rows into scrollback without ESC[3J.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a fresh-shell viewport blanking mechanism for terminal panes during cold restore. A new module provides an ANSI escape-sequence builder and a ref type for tracking panes pending viewport blanking. Scrollback restoration now marks restored panes in that ref. PTY connection logic consumes the marker during fresh spawns to conditionally blank the viewport, replaces a prior unconditional clear, and forces blanking on reattach/reconnect failure paths. Lifecycle wiring creates, passes, and cleans up the tracking ref. Tests cover the helper, restoration flow, and PTY rendering behavior.

Sequence Diagram(s)

Included in the hidden review stack artifact above.

Related PRs: None identified.

Suggested labels: terminal, enhancement

Suggested reviewers: None identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description omits required template sections like Screenshots, AI Review Report, Security Audit, and Notes. Add the missing headings and fill them in, including screenshots or 'No visual change', test checkboxes, AI review report, security audit, and notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main fix: Windows ConPTY overlay issues after cold restore.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/renderer/src/components/terminal-pane/pty-connection.test.ts (1)

5278-5285: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify why the synthetic render is disconnected from written.

This renderHeadlessTerminalState call uses a hand-crafted write sequence ('old TUI row...', blankViewport, 'PS >') instead of the actual written captured from connectPanePty above — the mocked pane.terminal.write never produced "old TUI row" content, so this block only demonstrates that the blanking sequence generically preserves prior xterm content, not that this specific scenario preserved real restored rows. A short comment explaining that intent would prevent future readers from assuming this asserts an end-to-end integration with the written output from lines above.

As per coding guidelines, **/*.{ts,tsx,js,jsx} changes driven by a non-obvious constraint should carry "a comment explaining why the code behaves the way it does."

📝 Suggested clarifying comment
+    // Why: pane.terminal.write is mocked above, so it never renders real
+    // scrollback content — this synthetic render only proves the blanking
+    // sequence generically preserves prior rows in scrollback while
+    // clearing the viewport, independent of the `written` capture above.
     const rendered = await renderHeadlessTerminalState(
       ['old TUI row with a long tail\r\nold TUI row two', blankViewport, 'PS >'],
       20,
       4
     )

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2afb0551-16d2-4e3d-9590-2d7cbf9bd488

📥 Commits

Reviewing files that changed from the base of the PR and between c29ee51 and 2738e71.

📒 Files selected for processing (7)
  • src/renderer/src/components/terminal-pane/layout-serialization.test.ts
  • src/renderer/src/components/terminal-pane/layout-serialization.ts
  • src/renderer/src/components/terminal-pane/pty-connection-types.ts
  • src/renderer/src/components/terminal-pane/pty-connection.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection.ts
  • src/renderer/src/components/terminal-pane/terminal-restored-viewport.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts

Comment on lines +3838 to +3855
const consumeRestoredViewportBlankingMarker = (): boolean => {
return deps.restoredViewportBlankingPanesRef?.current.delete(pane.id) ?? false
}

const writeFreshShellViewportBlanking = (): void => {
writeReplayData(buildFreshShellViewportBlankingSequence(pane.terminal.rows))
}

const prepareFreshShellViewportForSpawn = (options: FreshSpawnOptions): void => {
const hadRestoredViewport = consumeRestoredViewportBlankingMarker()
if (!options.forceBlankRestoredViewport && !hadRestoredViewport) {
return
}
// Why: fresh Windows ConPTY output paints at screen coordinates, so
// restored rows must leave the viewport before the first prompt redraw.
writeFreshShellViewportBlanking()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

printf 'Files:\n'
git ls-files 'src/renderer/src/components/terminal-pane/pty-connection.ts'

printf '\nUsages:\n'
rg -n "consumeRestoredViewportBlankingMarker|restoredViewportBlankingPanesRef|prepareFreshShellViewportForSpawn|handleReattachResult|wakeHibernatedAgentPane|startFreshColdRestoreAgentResume|transport\.attach\(" src/renderer/src/components/terminal-pane/pty-connection.ts

printf '\nOutline around relevant file:\n'
ast-grep outline src/renderer/src/components/terminal-pane/pty-connection.ts --view expanded

Repository: stablyai/orca

Length of output: 9250


🏁 Script executed:

set -euo pipefail

sed -n '5152,5295p' src/renderer/src/components/terminal-pane/pty-connection.ts
printf '\n---\n'
sed -n '5750,5855p' src/renderer/src/components/terminal-pane/pty-connection.ts
printf '\n---\n'
sed -n '3836,3858p' src/renderer/src/components/terminal-pane/pty-connection.ts

Repository: stablyai/orca

Length of output: 12792


🏁 Script executed:

set -euo pipefail

rg -n "restoredViewportBlankingPanesRef\.current|restoredViewportBlankingPanesRef|viewportBlanking" src/renderer/src/components/terminal-pane/pty-connection.ts

printf '\nSet/Add sites nearby:\n'
rg -n -C 3 "add\(pane\.id\)|delete\(pane\.id\)|restoredViewportBlankingPanesRef" src/renderer/src/components/terminal-pane/pty-connection.ts

Repository: stablyai/orca

Length of output: 1190


🏁 Script executed:

set -euo pipefail

rg -n "restoredViewportBlankingPanesRef|viewportBlankingPanesRef" .

printf '\nPotential add sites:\n'
rg -n -C 2 "restoredViewportBlankingPanesRef.*add|viewportBlankingPanesRef.*add|add\(pane\.id\)" .

Repository: stablyai/orca

Length of output: 2426


🏁 Script executed:

set -euo pipefail

sed -n '232,270p' src/renderer/src/components/terminal-pane/layout-serialization.ts
printf '\n---\n'
sed -n '1195,1235p' src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts
printf '\n---\n'
sed -n '1390,1435p' src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts

Repository: stablyai/orca

Length of output: 5877


Clear the restored-viewport marker on live reattach
handleReattachResult only consumes restoredViewportBlankingPanesRef in the cold-restore branch. Snapshot/replay and live transport.attach() success paths leave it set even though the marker is added during layout replay and only cleared on pane close, so a later fresh spawn can blank unrelated on-screen content.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/renderer/src/components/terminal-pane/terminal-restored-viewport.test.ts (1)

19-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the clamping branch.

The helper under test clamps rows via Math.max(1, Math.floor(Number.isFinite(rows) ? rows : 24)), but only the happy-path (rows: 5) is exercised here. Consider adding a case for NaN/0/negative/fractional rows to lock in the clamping behavior, since that logic path currently has no test coverage.

✅ Suggested additional test
it('clamps invalid row counts to a safe minimum', () => {
  expect(buildFreshShellViewportBlankingSequence(0)).toContain('\x1b[1;1H')
  expect(buildFreshShellViewportBlankingSequence(NaN)).toContain('\x1b[24;1H')
})

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6754e404-f0ac-43f5-ab85-caa9ca7ab320

📥 Commits

Reviewing files that changed from the base of the PR and between 2738e71 and 953fb04.

📒 Files selected for processing (2)
  • src/renderer/src/components/terminal-pane/terminal-restored-viewport.test.ts
  • src/renderer/src/components/terminal-pane/terminal-restored-viewport.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/components/terminal-pane/terminal-restored-viewport.ts

@Jinwoo-H
Jinwoo-H merged commit a074ed6 into main Jul 4, 2026
2 checks passed
@Jinwoo-H
Jinwoo-H deleted the Jinwoo-H/fix-conpty-cold-restore-overlay branch July 4, 2026 06:24
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant