fix(browser): debounce find-in-page query to stop per-keystroke flash (#836)

* fix(browser): debounce find-in-page query to stop per-keystroke flash

Why: findInPage re-highlights the active match on every call, which flashed
as the user typed. Debounce (200ms) so the highlight only re-runs once typing
settles. Enter still uses the live query for immediate next/previous nav.

* fix(browser): auto-select top suggestion, preserve address-bar typing

- Top history suggestion is auto-selected so Enter navigates to the best
  match without an extra ArrowDown keypress.
- URL syncs from the store and webview navigation events no longer clobber
  the address bar while the user is actively typing in it. Previously,
  opening a fresh tab and starting to type could be overridden when the
  configured default URL (e.g. google.com) finished resolving.
This commit is contained in:
Jinjing 2026-04-19 11:38:50 -07:00 committed by GitHub
parent bd52741efc
commit 7734cf9bef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 6 deletions

View file

@ -162,8 +162,11 @@ export default function BrowserAddressBar({
}
}, [open, suggestions.length])
// Why: auto-select the top suggestion so Enter navigates to the best match
// without an extra ArrowDown. Fall back to clearing selection when nothing
// matches so stale highlights don't persist.
useEffect(() => {
setSelectedValue('')
setSelectedValue(suggestions[0]?.url ?? '')
}, [suggestions])
return (

View file

@ -15,9 +15,19 @@ export default function BrowserFind({
}: BrowserFindProps): React.JSX.Element | null {
const inputRef = useRef<HTMLInputElement>(null)
const [query, setQuery] = useState('')
const [debouncedQuery, setDebouncedQuery] = useState('')
const [activeMatch, setActiveMatch] = useState(0)
const [totalMatches, setTotalMatches] = useState(0)
// Why: findInPage re-highlights the active match on every call, which causes
// a visible flash as the user types. Debounce to only re-run once typing
// settles. Enter (findNext/findPrevious) still uses the live `query` so
// explicit navigation is immediate.
useEffect(() => {
const id = setTimeout(() => setDebouncedQuery(query), 200)
return () => clearTimeout(id)
}, [query])
const safeFindInPage = useCallback(
(text: string, opts?: Electron.FindInPageOptions): void => {
const webview = webviewRef.current
@ -70,16 +80,16 @@ export default function BrowserFind({
}, [isOpen, safeStopFindInPage])
useEffect(() => {
if (!query) {
if (!debouncedQuery) {
safeStopFindInPage()
setActiveMatch(0)
setTotalMatches(0)
return
}
if (isOpen) {
safeFindInPage(query)
safeFindInPage(debouncedQuery)
}
}, [query, isOpen, safeFindInPage, safeStopFindInPage])
}, [debouncedQuery, isOpen, safeFindInPage, safeStopFindInPage])
// Why: this effect captures `webviewRef.current` into a local variable, so
// if the webview element were replaced while `isOpen` stays true the listener

View file

@ -505,6 +505,13 @@ function BrowserPagePane({
}, [browserTab.id, browserTab.url])
useEffect(() => {
// Why: if the user is actively typing in the address bar (focused), do not
// clobber their in-progress query when an async URL update lands (e.g., the
// configured default URL resolving after a new tab opens). Syncing will
// resume on the next legitimate URL change after the input loses focus.
if (document.activeElement === addressBarInputRef.current) {
return
}
setAddressBarValue(toDisplayUrl(browserTab.url))
}, [browserTab.url])
@ -1056,7 +1063,11 @@ function BrowserPagePane({
activeLoadFailureRef.current = null
lastKnownWebviewUrlRef.current = normalizeBrowserNavigationUrl(currentUrl) ?? currentUrl
rememberLiveBrowserUrl(browserTab.id, currentUrl)
setAddressBarValue(toDisplayUrl(currentUrl))
// Why: don't overwrite in-progress typing. See comment on the
// browserTab.url sync effect above.
if (document.activeElement !== addressBarInputRef.current) {
setAddressBarValue(toDisplayUrl(currentUrl))
}
onSetUrlRef.current(browserTab.id, currentUrl)
if (keepAddressBarFocusRef.current && currentUrl === ORCA_BROWSER_BLANK_URL) {
focusAddressBarNow()
@ -1083,7 +1094,10 @@ function BrowserPagePane({
}
lastKnownWebviewUrlRef.current = normalizeBrowserNavigationUrl(currentUrl) ?? currentUrl
rememberLiveBrowserUrl(browserTab.id, currentUrl)
setAddressBarValue(toDisplayUrl(currentUrl))
// Why: don't overwrite in-progress typing (see above).
if (document.activeElement !== addressBarInputRef.current) {
setAddressBarValue(toDisplayUrl(currentUrl))
}
onSetUrlRef.current(browserTab.id, currentUrl)
onUpdatePageStateRef.current(browserTab.id, {
title: webview.getTitle() || currentUrl,