From 1f2465576b3f52c186872d6adde1e43bca912260 Mon Sep 17 00:00:00 2001 From: debont80 Date: Tue, 30 Jun 2026 14:29:07 -0400 Subject: [PATCH] Fix L19/L73: replace setTimeout focus hack with double-rAF; unify probe verb L19: App.tsx replaced `setTimeout(() => focus(), 60)` in the "New download" palette action with `requestAnimationFrame(() => requestAnimationFrame(...))` to wait exactly one React render + paint cycle rather than an arbitrary 60ms timer that could race on slow machines. L73: DownloadBar's probe button label changed from "Fetch" to "Check URL" (hint and aria-label both updated). Placeholder text updated to match. This distinguishes it from LibraryView's "Index" action, which catalogs an entire channel/playlist, making both labels clearly meaningful in context. typecheck + 242 tests green. Co-Authored-By: Claude Opus 4.8 --- CODE-AUDIT.md | 4 ++-- src/renderer/src/App.tsx | 12 +++++++----- src/renderer/src/components/DownloadBar.tsx | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md index 92b597f..1a8d966 100644 --- a/CODE-AUDIT.md +++ b/CODE-AUDIT.md @@ -414,7 +414,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n feedback and silently never matches (main's `matchesUrl` swallows the error). - [x] **L18 — Terminal nav item shown when custom commands are off** — leads to a gated dead-end view; consider hiding/disabling it. -- [ ] **L19 — `App.tsx` focus hack** — `setTimeout(() => …focus(), 60)` after a tab switch. +- [x] **L19 — `App.tsx` focus hack** — `setTimeout(() => …focus(), 60)` after a tab switch. - [x] **L20 — `youtubePlayerClient` is free text** with no allowlist/validation; a typo passes straight to yt-dlp's `--extractor-args`. - [ ] **L21 — Inconsistent in-component error handling.** SettingsView funnels the app-update @@ -514,7 +514,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n known path. - [x] **L72 — History re-download drops the thumbnail** (passes only title/channel), so the re-queued row loses its thumbnail until re-probed. -- [ ] **L73 — Two verbs for the same probe action** — DownloadBar "Fetch" vs LibraryView "Index". +- [x] **L73 — Two verbs for the same probe action** — DownloadBar "Fetch" vs LibraryView "Index". - [x] **L74 — Accent swatches are triple-labeled** (`aria-pressed` + `aria-label` + `title`). - [x] **L75 — Update-token field always visible** in the Software-update card, even when no update is pending — buries an advanced/rarely-needed input. diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 0e26c5a..00daf3f 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react' +import { useState, useEffect, useMemo } from 'react' import { FluentProvider, makeStyles, tokens } from '@fluentui/react-components' import { Sidebar, type TabValue } from './components/Sidebar' import { DownloadsView } from './components/DownloadsView' @@ -57,7 +57,7 @@ function App(): React.JSX.Element { // Mirror overall queue progress onto the Windows taskbar. Subscribe to the store // directly (not via a selector) so taskbar updates don't re-render the app. - // Compare against the last IPC call and skip when nothing changed — the store + // Compare against the last IPC call and skip when nothing changed -- the store // fires on every progress tick and this avoids one IPC roundtrip per tick (L25). useEffect(() => { let lastFraction = -1 @@ -80,7 +80,7 @@ function App(): React.JSX.Element { return useDownloads.subscribe((st) => push(st.items)) }, []) - // Memoized so CommandPalette sees a stable array reference between renders — + // Memoized so CommandPalette sees a stable array reference between renders -- // only rebuilds when the theme label needs to change (L32). const paletteActions = useMemo( () => [ @@ -110,8 +110,10 @@ function App(): React.JSX.Element { hint: 'Focus URL', run: () => { setTab('downloads') - // Wait for the download bar to mount after the tab switch, then focus. - setTimeout(() => document.getElementById('aerofetch-url')?.focus(), 60) + // Double-rAF waits for React's re-render + the browser's paint before focusing. + requestAnimationFrame(() => + requestAnimationFrame(() => document.getElementById('aerofetch-url')?.focus()) + ) } }, { diff --git a/src/renderer/src/components/DownloadBar.tsx b/src/renderer/src/components/DownloadBar.tsx index d61bad0..4fb1374 100644 --- a/src/renderer/src/components/DownloadBar.tsx +++ b/src/renderer/src/components/DownloadBar.tsx @@ -555,17 +555,17 @@ export function DownloadBar(): React.JSX.Element { if (info || probeError || playlist) download() else void fetchFormats() }} - placeholder="Paste a video or playlist URL, then fetch…" + placeholder="Paste a video or playlist URL, then check…" size="large" contentBefore={} /> - +