mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 16:03:20 +00:00
Fix Windows terminal clear repaint (#6382)
This commit is contained in:
@@ -779,14 +779,37 @@ export default function TerminalPane({
|
||||
const clearPaneScrollback = useCallback(
|
||||
(pane: ManagedPane): void => {
|
||||
clearedScrollbackLeafIdsRef.current.add(pane.leafId)
|
||||
const transport = paneTransportsRef.current.get(pane.id)
|
||||
const ptyId = transport?.getPtyId() ?? null
|
||||
pane.terminal.clear()
|
||||
// Why: also clear the host buffer for remote-server panes, or the next
|
||||
// host snapshot replays the scrollback we just cleared locally.
|
||||
const ptyId = paneTransportsRef.current.get(pane.id)?.getPtyId() ?? null
|
||||
clearWebRuntimeTerminalBuffer(ptyId)
|
||||
persistLayoutSnapshot()
|
||||
const isRemoteRuntimePty = typeof ptyId === 'string' && isRemoteRuntimePtyId(ptyId)
|
||||
const shouldRepairWindowsClear =
|
||||
isWindowsUserAgent() && getConnectionId(worktreeId) === null && !isRemoteRuntimePty
|
||||
const repairWindowsClear = (): void => {
|
||||
if (!shouldRepairWindowsClear) {
|
||||
return
|
||||
}
|
||||
// Why: local Windows xterm can keep wrapped prompt/cursor paint stale
|
||||
// after clear; keep this frontend-only so ConPTY does not repaint it.
|
||||
safeFit(pane)
|
||||
pane.terminal.refresh(0, Math.max(0, pane.terminal.rows - 1))
|
||||
requestAnimationFrame(() => {
|
||||
if (!managerRef.current?.getPanes().some((livePane) => livePane.id === pane.id)) {
|
||||
return
|
||||
}
|
||||
safeFit(pane)
|
||||
pane.terminal.refresh(0, Math.max(0, pane.terminal.rows - 1))
|
||||
})
|
||||
}
|
||||
// Why: provider/session buffers can outlive the visible xterm. Clear
|
||||
// them through the same contract used by remote/mobile terminal state.
|
||||
void Promise.resolve(transport?.clearBuffer?.()).finally(() => {
|
||||
repairWindowsClear()
|
||||
persistLayoutSnapshot()
|
||||
})
|
||||
},
|
||||
[paneTransportsRef, persistLayoutSnapshot]
|
||||
[paneTransportsRef, persistLayoutSnapshot, worktreeId]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -363,6 +363,7 @@ export type PtyTransport = {
|
||||
getPtyId: () => string | null
|
||||
getConnectionId?: () => string | null | undefined
|
||||
getLocalSessionMetadata?: () => LocalPtySessionMetadata | null
|
||||
clearBuffer?: () => Promise<void>
|
||||
serializeBuffer?: (opts?: { scrollbackRows?: number }) => Promise<PtyBufferSnapshot | null>
|
||||
preserve?: () => void
|
||||
/** Unregister PTY handlers without killing the process for pane remounts. */
|
||||
|
||||
@@ -42,6 +42,7 @@ describe('createIpcPtyTransport', () => {
|
||||
writeAccepted: vi.fn().mockResolvedValue(true),
|
||||
resize: vi.fn(),
|
||||
kill: vi.fn(),
|
||||
clearBuffer: vi.fn().mockResolvedValue(undefined),
|
||||
onData: vi.fn((callback: (payload: { id: string; data: string }) => void) => {
|
||||
onData = callback
|
||||
return () => {}
|
||||
@@ -163,6 +164,20 @@ describe('createIpcPtyTransport', () => {
|
||||
expect(sshTransport.getLocalSessionMetadata?.()).toBeNull()
|
||||
})
|
||||
|
||||
it('clears the connected PTY buffer through the preload API', async () => {
|
||||
const { createIpcPtyTransport } = await import('./pty-transport')
|
||||
const transport = createIpcPtyTransport({})
|
||||
|
||||
await transport.clearBuffer?.()
|
||||
expect(window.api.pty.clearBuffer).not.toHaveBeenCalled()
|
||||
|
||||
await transport.connect({ url: '', callbacks: {} })
|
||||
await transport.clearBuffer?.()
|
||||
|
||||
expect(window.api.pty.clearBuffer).toHaveBeenCalledWith('pty-1')
|
||||
transport.disconnect()
|
||||
})
|
||||
|
||||
it('defers title side effects until after terminal data is delivered', async () => {
|
||||
const { createIpcPtyTransport } = await import('./pty-transport')
|
||||
const onTitleChange = vi.fn()
|
||||
|
||||
@@ -967,6 +967,13 @@ export function createIpcPtyTransport(opts: IpcPtyTransportOptions = {}): PtyTra
|
||||
}
|
||||
},
|
||||
|
||||
async clearBuffer() {
|
||||
if (!connected || !ptyId) {
|
||||
return
|
||||
}
|
||||
await window.api.pty.clearBuffer(ptyId)
|
||||
},
|
||||
|
||||
destroy() {
|
||||
destroyed = true
|
||||
this.disconnect()
|
||||
|
||||
@@ -2445,6 +2445,7 @@ function createPtyApi(): NonNullable<Partial<PreloadApi>['pty']> {
|
||||
onSerializeBufferRequest: () => noopUnsubscribe,
|
||||
onClearBufferRequest: () => noopUnsubscribe,
|
||||
sendSerializedBuffer: () => {},
|
||||
clearBuffer: () => Promise.resolve(),
|
||||
declarePendingPaneSerializer: () => Promise.resolve(0),
|
||||
settlePaneSerializer: () => Promise.resolve(),
|
||||
clearPendingPaneSerializer: () => Promise.resolve(),
|
||||
|
||||
Reference in New Issue
Block a user