mirror of
https://github.com/stablyai/orca.git
synced 2026-10-15 16:02:49 +00:00
Skip hidden title terminal renderer writes
This commit is contained in:
@@ -35,6 +35,32 @@ describe('shouldSkipHiddenRendererOutput', () => {
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('skips complete hidden title OSC chunks when a snapshot restore is available', () => {
|
||||
for (const data of ['\x1b]0;window title\x07', '\x1b]1;icon title\x07', '\x1b]2;both\x1b\\']) {
|
||||
expect(
|
||||
shouldSkipHiddenRendererOutput({
|
||||
foreground: false,
|
||||
canRestoreHiddenOutput: true,
|
||||
startupRendererQueryWindowActive: false,
|
||||
synchronizedOutputActive: false,
|
||||
data
|
||||
})
|
||||
).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('skips hidden title OSC mixed with otherwise restorable plain output', () => {
|
||||
expect(
|
||||
shouldSkipHiddenRendererOutput({
|
||||
foreground: false,
|
||||
canRestoreHiddenOutput: true,
|
||||
startupRendererQueryWindowActive: false,
|
||||
synchronizedOutputActive: false,
|
||||
data: 'line before\r\n\x1b]0;next title\x07line after\r\n'
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps startup query windows and terminal-control chunks live', () => {
|
||||
expect(
|
||||
shouldSkipHiddenRendererOutput({
|
||||
@@ -58,7 +84,7 @@ describe('shouldSkipHiddenRendererOutput', () => {
|
||||
shouldSkipHiddenRendererOutput({
|
||||
foreground: false,
|
||||
canRestoreHiddenOutput: true,
|
||||
startupRendererQueryWindowActive: false,
|
||||
startupRendererQueryWindowActive: true,
|
||||
synchronizedOutputActive: false,
|
||||
data: '\x1b]0;title\x07'
|
||||
})
|
||||
@@ -74,6 +100,20 @@ describe('shouldSkipHiddenRendererOutput', () => {
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps non-title OSC and incomplete title OSC chunks live', () => {
|
||||
for (const data of ['\x1b]52;c;clipboard\x07', '\x1b]9;notify\x07', '\x1b]0;partial-title']) {
|
||||
expect(
|
||||
shouldSkipHiddenRendererOutput({
|
||||
foreground: false,
|
||||
canRestoreHiddenOutput: true,
|
||||
startupRendererQueryWindowActive: false,
|
||||
synchronizedOutputActive: false,
|
||||
data
|
||||
})
|
||||
).toBe(false)
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps rewrite and unicode chunks live', () => {
|
||||
expect(
|
||||
shouldSkipHiddenRendererOutput({
|
||||
|
||||
@@ -13,18 +13,51 @@ function isAllowedPlainHiddenOutputCode(code: number): boolean {
|
||||
return code >= 0x20 && code <= 0x7e
|
||||
}
|
||||
|
||||
function containsOnlyPlainHiddenOutput(data: string): boolean {
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
function findTitleOscEnd(data: string, startIndex: number): number | null {
|
||||
const command = data.charCodeAt(startIndex + 2)
|
||||
if (
|
||||
data.charCodeAt(startIndex) !== 0x1b ||
|
||||
data.charCodeAt(startIndex + 1) !== 0x5d ||
|
||||
(command !== 0x30 && command !== 0x31 && command !== 0x32) ||
|
||||
data.charCodeAt(startIndex + 3) !== 0x3b
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (let index = startIndex + 4; index < data.length; index++) {
|
||||
const code = data.charCodeAt(index)
|
||||
if (code === 0x07) {
|
||||
return index + 1
|
||||
}
|
||||
if (code === 0x1b) {
|
||||
return data.charCodeAt(index + 1) === 0x5c ? index + 2 : null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function containsOnlyRestorableHiddenOutput(data: string): boolean {
|
||||
for (let index = 0; index < data.length; ) {
|
||||
const code = data.charCodeAt(index)
|
||||
if (code === 0x1b) {
|
||||
const nextIndex = findTitleOscEnd(data, index)
|
||||
if (nextIndex === null) {
|
||||
return false
|
||||
}
|
||||
index = nextIndex
|
||||
continue
|
||||
}
|
||||
if (code === 0x0d) {
|
||||
if (data.charCodeAt(index + 1) !== 0x0a) {
|
||||
return false
|
||||
}
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
if (!isAllowedPlainHiddenOutputCode(code)) {
|
||||
return false
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -45,5 +78,5 @@ export function shouldSkipHiddenRendererOutput({
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return containsOnlyPlainHiddenOutput(data)
|
||||
return containsOnlyRestorableHiddenOutput(data)
|
||||
}
|
||||
|
||||
@@ -3358,6 +3358,44 @@ describe('connectPanePty', () => {
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
it('skips hidden title OSC renderer writes while keeping pane title handling wired', async () => {
|
||||
const { connectPanePty } = await import('./pty-connection')
|
||||
const transport = createMockTransport('pty-id')
|
||||
const capturedDataCallback: {
|
||||
current: ((data: string, meta?: { seq?: number; rawLength?: number }) => void) | null
|
||||
} = { current: null }
|
||||
transport.connect.mockImplementation(async ({ callbacks }: { callbacks: ConnectCallbacks }) => {
|
||||
capturedDataCallback.current = callbacks.onData ?? null
|
||||
return 'pty-id'
|
||||
})
|
||||
transportFactoryQueue.push(transport)
|
||||
|
||||
const pane = createPane(1)
|
||||
const manager = createManager(1)
|
||||
const deps = createDeps({
|
||||
isVisibleRef: { current: false }
|
||||
})
|
||||
const disposable = connectPanePty(pane as never, manager as never, deps as never)
|
||||
await flushAsyncTicks(6)
|
||||
|
||||
const hiddenTitle = '\x1b]0;hidden title\x07'
|
||||
capturedDataCallback.current?.(hiddenTitle, {
|
||||
seq: hiddenTitle.length,
|
||||
rawLength: hiddenTitle.length
|
||||
})
|
||||
|
||||
expect(pane.terminal.write).not.toHaveBeenCalledWith(hiddenTitle, expect.any(Function))
|
||||
const titleHandler = createdTransportOptions[0]?.onTitleChange as
|
||||
| ((title: string, rawTitle: string) => void)
|
||||
| undefined
|
||||
if (!titleHandler) {
|
||||
throw new Error('Expected onTitleChange to be registered')
|
||||
}
|
||||
titleHandler('hidden title', 'hidden title')
|
||||
expect(deps.setRuntimePaneTitle).toHaveBeenCalledWith('tab-1', pane.id, 'hidden title')
|
||||
disposable.dispose()
|
||||
})
|
||||
|
||||
it('restores plain hidden remote runtime output from its serialized snapshot', async () => {
|
||||
const { connectPanePty } = await import('./pty-connection')
|
||||
const transport = createMockTransport('remote:env-1@@terminal-1')
|
||||
|
||||
@@ -93,6 +93,25 @@ describe('createIpcPtyTransport', () => {
|
||||
transport.disconnect()
|
||||
})
|
||||
|
||||
it('runs title side effects even when the data callback does not render the chunk', async () => {
|
||||
const { createIpcPtyTransport } = await import('./pty-transport')
|
||||
const onTitleChange = vi.fn()
|
||||
const onDataCallback = vi.fn()
|
||||
const transport = createIpcPtyTransport({ onTitleChange })
|
||||
|
||||
await transport.connect({ url: '', callbacks: { onData: onDataCallback } })
|
||||
|
||||
onData?.({ id: 'pty-1', data: '\u001b]0;hidden-title\u0007' })
|
||||
|
||||
expect(onDataCallback).toHaveBeenCalledWith('\u001b]0;hidden-title\u0007')
|
||||
expect(onTitleChange).not.toHaveBeenCalled()
|
||||
|
||||
await flushPtySideEffects()
|
||||
|
||||
expect(onTitleChange).toHaveBeenCalledWith('hidden-title', 'hidden-title')
|
||||
transport.disconnect()
|
||||
})
|
||||
|
||||
it('does not schedule PTY side-effect drains for ordinary output with no working title', async () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
|
||||
@@ -171,7 +171,7 @@ export async function runHiddenRealPtyPressureScenario<
|
||||
ackGate
|
||||
)
|
||||
|
||||
if (pressureOutputMode === 'plain') {
|
||||
if (pressureOutputMode === 'plain' || pressureOutputMode === 'title') {
|
||||
expect(debug?.hiddenRendererSkipCount ?? 0).toBeGreaterThan(0)
|
||||
expect(debug?.hiddenRendererSkippedChars ?? 0).toBeGreaterThan(0)
|
||||
} else {
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { mkdirSync, writeFileSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
export type HiddenPressureOutputMode = 'tui' | 'plain'
|
||||
export type HiddenPressureOutputMode = 'tui' | 'plain' | 'title'
|
||||
|
||||
export function pressureOutputScript(runId: string, mode: HiddenPressureOutputMode): string {
|
||||
const headerPrefix = mode === 'plain' ? '' : '\\x1b[0m'
|
||||
const donePrefix = mode === 'plain' ? '' : '\\x1b[0m'
|
||||
const headerPrefix = mode === 'tui' ? '\\x1b[0m' : ''
|
||||
const donePrefix = mode === 'tui' ? '\\x1b[0m' : ''
|
||||
const chunkExpression =
|
||||
mode === 'plain'
|
||||
? "'plain pressure pane=' + paneIndex + ' frame=' + frame + ' ' + chunkBody + '\\n'"
|
||||
: "'\\x1b[?2026h\\x1b[1;1Hpressure pane=' + paneIndex + ' frame=' + frame + ' ' + chunkBody + '\\x1b[?2026l\\n'"
|
||||
: mode === 'title'
|
||||
? "'\\x1b]0;title pressure pane=' + paneIndex + ' frame=' + frame + ' ' + chunkBody + '\\x07'"
|
||||
: "'\\x1b[?2026h\\x1b[1;1Hpressure pane=' + paneIndex + ' frame=' + frame + ' ' + chunkBody + '\\x1b[?2026l\\n'"
|
||||
return `
|
||||
const paneIndex = process.argv[2] ?? '0'
|
||||
const targetChars = Number(process.argv[3] ?? '0')
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
waitForTerminalOutput
|
||||
} from './helpers/terminal'
|
||||
import { runHiddenRealPtyPressureScenario } from './artificial-opencode-hidden-pressure-scenario'
|
||||
import type { HiddenPressureOutputMode } from './artificial-opencode-hidden-pressure-script'
|
||||
import { runMainPressureScenario } from './artificial-opencode-main-pressure-scenario'
|
||||
import { startSyntheticOpenCodeInjection } from './artificial-opencode-synthetic-injection'
|
||||
|
||||
@@ -728,7 +729,7 @@ test.describe('Artificial OpenCode terminal load', () => {
|
||||
testInfo: TestInfo,
|
||||
hiddenPaneCount: number,
|
||||
annotationSuffix?: string,
|
||||
pressureOutputMode?: 'tui' | 'plain'
|
||||
pressureOutputMode?: HiddenPressureOutputMode
|
||||
): Promise<void> {
|
||||
await runHiddenRealPtyPressureScenario({
|
||||
orcaPage,
|
||||
@@ -779,6 +780,19 @@ test.describe('Artificial OpenCode terminal load', () => {
|
||||
'plain'
|
||||
)
|
||||
})
|
||||
test('skips renderer writes for title-only hidden PTY output while preserving restore', async ({
|
||||
orcaPage,
|
||||
testRepoPath
|
||||
}, testInfo) => {
|
||||
await runConfiguredHiddenRealPtyPressureScenario(
|
||||
orcaPage,
|
||||
testRepoPath,
|
||||
testInfo,
|
||||
HIDDEN_PRESSURE_PANES,
|
||||
'-title',
|
||||
'title'
|
||||
)
|
||||
})
|
||||
for (const paneCount of SCALE_HIDDEN_PRESSURE_PANES) {
|
||||
test(`keeps hidden restore responsive with ${paneCount} ACK-backpressured real PTYs`, async ({
|
||||
orcaPage,
|
||||
|
||||
Reference in New Issue
Block a user