mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 16:02:35 +00:00
fix: cancel transient picker focus frames (#3495)
This commit is contained in:
@@ -154,10 +154,17 @@ export default function AgentCombobox({
|
||||
return 'blank terminal'.includes(q) || 'terminal'.startsWith(q)
|
||||
}, [query])
|
||||
|
||||
const focusSearchInput = useCallback(() => {
|
||||
const cancelFocusFrame = useCallback((): void => {
|
||||
if (focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => cancelFocusFrame, [cancelFocusFrame])
|
||||
|
||||
const focusSearchInput = useCallback(() => {
|
||||
cancelFocusFrame()
|
||||
focusFrameRef.current = requestAnimationFrame(() => {
|
||||
focusFrameRef.current = null
|
||||
const searchInput = inputRef.current
|
||||
@@ -171,7 +178,7 @@ export default function AgentCombobox({
|
||||
const end = searchInput.value.length
|
||||
searchInput.setSelectionRange(end, end)
|
||||
})
|
||||
}, [])
|
||||
}, [cancelFocusFrame])
|
||||
|
||||
const handleOpenChange = useCallback(
|
||||
(nextOpen: boolean) => {
|
||||
@@ -180,13 +187,10 @@ export default function AgentCombobox({
|
||||
setCommandValue(value ?? BLANK_VALUE)
|
||||
return
|
||||
}
|
||||
if (focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
cancelFocusFrame()
|
||||
setQuery('')
|
||||
},
|
||||
[value]
|
||||
[cancelFocusFrame, value]
|
||||
)
|
||||
|
||||
const handleSelect = useCallback(
|
||||
|
||||
@@ -70,23 +70,32 @@ export function CreateFromPicker({
|
||||
return Array.from(options).sort((left, right) => left.localeCompare(right))
|
||||
}, [effectiveDefault, searchResults, worktrees])
|
||||
|
||||
const focusSearchInput = React.useCallback(() => {
|
||||
const cancelFocusFrame = React.useCallback((): void => {
|
||||
if (focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => cancelFocusFrame, [cancelFocusFrame])
|
||||
|
||||
const focusSearchInput = React.useCallback(() => {
|
||||
cancelFocusFrame()
|
||||
focusFrameRef.current = requestAnimationFrame(() => {
|
||||
focusFrameRef.current = null
|
||||
inputRef.current?.focus()
|
||||
})
|
||||
}, [])
|
||||
}, [cancelFocusFrame])
|
||||
|
||||
const handleOpenChange = React.useCallback((nextOpen: boolean) => {
|
||||
setOpen(nextOpen)
|
||||
if (!nextOpen && focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
}, [])
|
||||
const handleOpenChange = React.useCallback(
|
||||
(nextOpen: boolean) => {
|
||||
setOpen(nextOpen)
|
||||
if (!nextOpen) {
|
||||
cancelFocusFrame()
|
||||
}
|
||||
},
|
||||
[cancelFocusFrame]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!repoId) {
|
||||
|
||||
@@ -28,23 +28,32 @@ export function WorkspaceCombobox({
|
||||
const focusFrameRef = React.useRef<number | null>(null)
|
||||
const selected = worktrees.find((worktree) => worktree.id === value) ?? null
|
||||
|
||||
const focusSearchInput = React.useCallback(() => {
|
||||
const cancelFocusFrame = React.useCallback((): void => {
|
||||
if (focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => cancelFocusFrame, [cancelFocusFrame])
|
||||
|
||||
const focusSearchInput = React.useCallback(() => {
|
||||
cancelFocusFrame()
|
||||
focusFrameRef.current = requestAnimationFrame(() => {
|
||||
focusFrameRef.current = null
|
||||
inputRef.current?.focus()
|
||||
})
|
||||
}, [])
|
||||
}, [cancelFocusFrame])
|
||||
|
||||
const handleOpenChange = React.useCallback((nextOpen: boolean) => {
|
||||
setOpen(nextOpen)
|
||||
if (!nextOpen && focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
}, [])
|
||||
const handleOpenChange = React.useCallback(
|
||||
(nextOpen: boolean) => {
|
||||
setOpen(nextOpen)
|
||||
if (!nextOpen) {
|
||||
cancelFocusFrame()
|
||||
}
|
||||
},
|
||||
[cancelFocusFrame]
|
||||
)
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={handleOpenChange}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { FolderOpen } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
@@ -48,6 +48,8 @@ export function UntitledFileRenameDialog({
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => cancelFocusFrame, [cancelFocusFrame])
|
||||
|
||||
const setNameInputNode = useCallback(
|
||||
(node: HTMLInputElement | null): void => {
|
||||
if (!node) {
|
||||
|
||||
@@ -56,10 +56,17 @@ export default function RepoCombobox({
|
||||
)
|
||||
const filteredRepos = useMemo(() => searchRepos(repos, query), [repos, query])
|
||||
|
||||
const focusSearchInput = useCallback(() => {
|
||||
const cancelFocusFrame = useCallback((): void => {
|
||||
if (focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => cancelFocusFrame, [cancelFocusFrame])
|
||||
|
||||
const focusSearchInput = useCallback(() => {
|
||||
cancelFocusFrame()
|
||||
focusFrameRef.current = requestAnimationFrame(() => {
|
||||
focusFrameRef.current = null
|
||||
const repoSearchInput = inputRef.current
|
||||
@@ -73,7 +80,7 @@ export default function RepoCombobox({
|
||||
const end = repoSearchInput.value.length
|
||||
repoSearchInput.setSelectionRange(end, end)
|
||||
})
|
||||
}, [])
|
||||
}, [cancelFocusFrame])
|
||||
|
||||
const handleOpenChange = useCallback(
|
||||
(nextOpen: boolean) => {
|
||||
@@ -82,16 +89,13 @@ export default function RepoCombobox({
|
||||
setCommandValue(value)
|
||||
return
|
||||
}
|
||||
if (focusFrameRef.current !== null) {
|
||||
cancelAnimationFrame(focusFrameRef.current)
|
||||
focusFrameRef.current = null
|
||||
}
|
||||
cancelFocusFrame()
|
||||
// Why: the create-worktree dialog delays its own field reset until after
|
||||
// close animation, so the repo picker must clear its local filter here or a
|
||||
// stale query can reopen to an apparently missing repo list.
|
||||
setQuery('')
|
||||
},
|
||||
[value]
|
||||
[cancelFocusFrame, value]
|
||||
)
|
||||
|
||||
const handleSelect = useCallback(
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* oxlint limit, following the same pattern as useRemoteRepo.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { Folder, GitBranch, Home, Pencil } from 'lucide-react'
|
||||
import { useAppStore } from '@/store'
|
||||
@@ -301,6 +301,8 @@ export function CreateStep({
|
||||
radioFocusFrameRef.current = null
|
||||
}, [])
|
||||
|
||||
useEffect(() => cancelRadioFocusFrame, [cancelRadioFocusFrame])
|
||||
|
||||
const setRadioGroupNode = useCallback(
|
||||
(node: HTMLDivElement | null): void => {
|
||||
// Why: the queued arrow-key focus is only valid while this radiogroup is mounted.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { GitBranch, GitBranchPlus, Settings } from 'lucide-react'
|
||||
import { DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -158,6 +158,8 @@ export function ProjectAddedContent({
|
||||
radioFocusFrameRef.current = null
|
||||
}, [])
|
||||
|
||||
useEffect(() => cancelRadioFocusFrame, [cancelRadioFocusFrame])
|
||||
|
||||
const setRadioGroupNode = useCallback(
|
||||
(node: HTMLDivElement | null): void => {
|
||||
// Why: the queued arrow-key focus is only valid while this radiogroup is mounted.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Check, ChevronsUpDown, LoaderCircle, Pencil, Plus, RefreshCcw } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
@@ -84,6 +84,8 @@ export default function SparseCheckoutPresetSelect({
|
||||
nameInputFocusFrameRef.current = null
|
||||
}, [])
|
||||
|
||||
useEffect(() => cancelNameInputFocusFrame, [cancelNameInputFocusFrame])
|
||||
|
||||
const setNameInputNode = useCallback(
|
||||
(node: HTMLInputElement | null): void => {
|
||||
// Why: the queued draft focus is only valid while this input is mounted.
|
||||
|
||||
@@ -704,6 +704,8 @@ export function ResourceUsageStatusSegment({
|
||||
popoverBodyFocusFrameRef.current = null
|
||||
}, [])
|
||||
|
||||
useEffect(() => cancelPopoverBodyFocusFrame, [cancelPopoverBodyFocusFrame])
|
||||
|
||||
const setPopoverBodyNode = useCallback(
|
||||
(node: HTMLDivElement | null): void => {
|
||||
// Why: the queued post-kill focus is only valid while the popover body exists.
|
||||
|
||||
Reference in New Issue
Block a user