mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
fix(renderer): cancel copied prompt reset on unmount (#20906)
* fix(renderer): cancel copied prompt reset on unmount * fix: address memory PR review regressions and withdraw false positives --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
This commit is contained in:
@@ -138,6 +138,51 @@ describe('EphemeralVmsPane', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('does not schedule a reset when clipboard completion arrives after unmount', async () => {
|
||||
let finishClipboard!: () => void
|
||||
vi.mocked(window.api.ui.writeClipboardText).mockReturnValueOnce(
|
||||
new Promise<void>((resolve) => {
|
||||
finishClipboard = resolve
|
||||
})
|
||||
)
|
||||
const container = await renderPane()
|
||||
const setTimeout = vi.spyOn(window, 'setTimeout')
|
||||
try {
|
||||
await act(async () => {
|
||||
container.querySelector<HTMLButtonElement>('button[aria-label="Copy"]')?.click()
|
||||
})
|
||||
await act(async () => roots.pop()?.unmount())
|
||||
setTimeout.mockClear()
|
||||
await act(async () => {
|
||||
finishClipboard()
|
||||
await Promise.resolve()
|
||||
})
|
||||
expect(setTimeout.mock.calls.filter(([, delay]) => delay === 1500)).toHaveLength(0)
|
||||
} finally {
|
||||
setTimeout.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('shows copied feedback while mounted and releases its reset on unmount', async () => {
|
||||
const container = await renderPane()
|
||||
const setTimeout = vi.spyOn(window, 'setTimeout')
|
||||
const clearTimeout = vi.spyOn(window, 'clearTimeout')
|
||||
try {
|
||||
await act(async () => {
|
||||
container.querySelector<HTMLButtonElement>('button[aria-label="Copy"]')?.click()
|
||||
})
|
||||
expect(container.querySelector('button[aria-label="Copy"]')?.textContent).toBe('Copied')
|
||||
const timerIndex = setTimeout.mock.calls.findIndex(([, delay]) => delay === 1500)
|
||||
expect(timerIndex).toBeGreaterThanOrEqual(0)
|
||||
const timer = setTimeout.mock.results[timerIndex].value
|
||||
await act(async () => roots.pop()?.unmount())
|
||||
expect(clearTimeout).toHaveBeenCalledWith(timer)
|
||||
} finally {
|
||||
setTimeout.mockRestore()
|
||||
clearTimeout.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('refreshes the catalog when plugin content changes', async () => {
|
||||
const listRecipeCatalog = window.api.ephemeralVm.listRecipeCatalog as ReturnType<typeof vi.fn>
|
||||
const container = await renderPane()
|
||||
|
||||
@@ -45,6 +45,15 @@ export function EphemeralVmsPane(): React.JSX.Element {
|
||||
const [promptCopied, setPromptCopied] = useState(false)
|
||||
const mountedRef = useMountedRef()
|
||||
const refreshGenerationRef = useRef(0)
|
||||
const promptResetTimerRef = useRef<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (promptResetTimerRef.current !== null) {
|
||||
window.clearTimeout(promptResetTimerRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Why: an absent runtime still resolves to the local host, which is what the
|
||||
// seven sibling panes rely on to reach the Windows npx preflight.
|
||||
@@ -126,8 +135,17 @@ export function EphemeralVmsPane(): React.JSX.Element {
|
||||
try {
|
||||
await window.api.ui.writeClipboardText(AGENT_PROMPT)
|
||||
useAppStore.getState().recordFeatureInteraction('ephemeral-vm-setup')
|
||||
if (!mountedRef.current) {
|
||||
return
|
||||
}
|
||||
setPromptCopied(true)
|
||||
setTimeout(() => setPromptCopied(false), 1500)
|
||||
if (promptResetTimerRef.current !== null) {
|
||||
window.clearTimeout(promptResetTimerRef.current)
|
||||
}
|
||||
promptResetTimerRef.current = window.setTimeout(() => {
|
||||
promptResetTimerRef.current = null
|
||||
setPromptCopied(false)
|
||||
}, 1500)
|
||||
} catch {
|
||||
toast.error(
|
||||
translate(
|
||||
|
||||
Reference in New Issue
Block a user