mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(clipboard): preserve copy order across yielding validation (#35124)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ProcessResult, ProcessSpec } from '@orca/process-host/process-spec'
|
||||
import {
|
||||
assertClipboardTextWriteWithinLimitWithYield,
|
||||
CLIPBOARD_TEXT_MEASURE_YIELD_CODE_UNITS,
|
||||
CLIPBOARD_TEXT_WRITE_MAX_BYTES
|
||||
} from '../../shared/clipboard-text'
|
||||
import { createClipboardTextPublisher } from './clipboard-text-publisher'
|
||||
@@ -127,6 +128,37 @@ describe('terminal clipboard writes', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it.each([false, true])(
|
||||
'keeps a newer copy after yielding validation (Wayland=%s)',
|
||||
async (wayland) => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const copy = writer(wayland)
|
||||
const large = 'x'.repeat(CLIPBOARD_TEXT_MEASURE_YIELD_CODE_UNITS * 2)
|
||||
const first = copy(large, 'terminal')
|
||||
const second = copy('newer', 'ordinary')
|
||||
|
||||
await vi.runAllTimersAsync()
|
||||
await Promise.all([first, second])
|
||||
|
||||
if (wayland) {
|
||||
expect(run.mock.calls.map(([spec]) => spec.input === large)).toEqual([true, false])
|
||||
expect(run.mock.calls[1]?.[0].input).toBe('newer')
|
||||
expect(write).not.toHaveBeenCalled()
|
||||
} else {
|
||||
expect(write.mock.calls.map(([text, kind]) => [text === large, kind])).toEqual([
|
||||
[true, 'terminal'],
|
||||
[false, 'ordinary']
|
||||
])
|
||||
expect(write.mock.calls[1]?.[0]).toBe('newer')
|
||||
expect(run).not.toHaveBeenCalled()
|
||||
}
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it('preserves request order across yielding validation and resumes after failure', async () => {
|
||||
let release: (text: string) => void = () => undefined
|
||||
const validate = vi.fn(async (text: string) => text)
|
||||
|
||||
@@ -40,21 +40,25 @@ export function createClipboardTextPublisher(
|
||||
if (text.length > CLIPBOARD_TEXT_WRITE_MAX_BYTES) {
|
||||
return Promise.reject(new Error(CLIPBOARD_TEXT_WRITE_TOO_LARGE_ERROR))
|
||||
}
|
||||
if (!deps.isWayland()) {
|
||||
return deps.validate(text).then((safeText) => deps.write(safeText, kind))
|
||||
const wayland = deps.isWayland()
|
||||
if (wayland) {
|
||||
if (
|
||||
count >= MAX_PENDING_CLIPBOARD_WRITES ||
|
||||
text.length > CLIPBOARD_TEXT_WRITE_MAX_BYTES - queuedCodeUnits
|
||||
) {
|
||||
return Promise.reject(new Error('Too many pending clipboard writes'))
|
||||
}
|
||||
count += 1
|
||||
queuedCodeUnits += text.length
|
||||
}
|
||||
if (
|
||||
count >= MAX_PENDING_CLIPBOARD_WRITES ||
|
||||
text.length > CLIPBOARD_TEXT_WRITE_MAX_BYTES - queuedCodeUnits
|
||||
) {
|
||||
return Promise.reject(new Error('Too many pending clipboard writes'))
|
||||
}
|
||||
count += 1
|
||||
queuedCodeUnits += text.length
|
||||
// Validate inside the queue: large copies yield and must not overwrite a later request.
|
||||
const next = pending
|
||||
.then(async () => {
|
||||
const safeText = await deps.validate(text)
|
||||
if (!wayland) {
|
||||
deps.write(safeText, kind)
|
||||
return
|
||||
}
|
||||
const result = await deps.run({
|
||||
program: await deps.helperPath(),
|
||||
input: safeText,
|
||||
@@ -68,8 +72,10 @@ export function createClipboardTextPublisher(
|
||||
deps.clearCache()
|
||||
})
|
||||
.finally(() => {
|
||||
count -= 1
|
||||
queuedCodeUnits -= text.length
|
||||
if (wayland) {
|
||||
count -= 1
|
||||
queuedCodeUnits -= text.length
|
||||
}
|
||||
})
|
||||
pending = next.catch(() => undefined)
|
||||
return next
|
||||
|
||||
Reference in New Issue
Block a user