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 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -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).
|
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
|
- [x] **L18 — Terminal nav item shown when custom commands are off** — leads to a gated dead-end
|
||||||
view; consider hiding/disabling it.
|
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
|
- [x] **L20 — `youtubePlayerClient` is free text** with no allowlist/validation; a typo passes
|
||||||
straight to yt-dlp's `--extractor-args`.
|
straight to yt-dlp's `--extractor-args`.
|
||||||
- [ ] **L21 — Inconsistent in-component error handling.** SettingsView funnels the app-update
|
- [ ] **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.
|
known path.
|
||||||
- [x] **L72 — History re-download drops the thumbnail** (passes only title/channel), so the
|
- [x] **L72 — History re-download drops the thumbnail** (passes only title/channel), so the
|
||||||
re-queued row loses its thumbnail until re-probed.
|
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] **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
|
- [x] **L75 — Update-token field always visible** in the Software-update card, even when no update
|
||||||
is pending — buries an advanced/rarely-needed input.
|
is pending — buries an advanced/rarely-needed input.
|
||||||
|
|||||||
@@ -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 { FluentProvider, makeStyles, tokens } from '@fluentui/react-components'
|
||||||
import { Sidebar, type TabValue } from './components/Sidebar'
|
import { Sidebar, type TabValue } from './components/Sidebar'
|
||||||
import { DownloadsView } from './components/DownloadsView'
|
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
|
// 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.
|
// 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).
|
// fires on every progress tick and this avoids one IPC roundtrip per tick (L25).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let lastFraction = -1
|
let lastFraction = -1
|
||||||
@@ -80,7 +80,7 @@ function App(): React.JSX.Element {
|
|||||||
return useDownloads.subscribe((st) => push(st.items))
|
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).
|
// only rebuilds when the theme label needs to change (L32).
|
||||||
const paletteActions = useMemo<PaletteAction[]>(
|
const paletteActions = useMemo<PaletteAction[]>(
|
||||||
() => [
|
() => [
|
||||||
@@ -110,8 +110,10 @@ function App(): React.JSX.Element {
|
|||||||
hint: 'Focus URL',
|
hint: 'Focus URL',
|
||||||
run: () => {
|
run: () => {
|
||||||
setTab('downloads')
|
setTab('downloads')
|
||||||
// Wait for the download bar to mount after the tab switch, then focus.
|
// Double-rAF waits for React's re-render + the browser's paint before focusing.
|
||||||
setTimeout(() => document.getElementById('aerofetch-url')?.focus(), 60)
|
requestAnimationFrame(() =>
|
||||||
|
requestAnimationFrame(() => document.getElementById('aerofetch-url')?.focus())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -555,17 +555,17 @@ export function DownloadBar(): React.JSX.Element {
|
|||||||
if (info || probeError || playlist) download()
|
if (info || probeError || playlist) download()
|
||||||
else void fetchFormats()
|
else void fetchFormats()
|
||||||
}}
|
}}
|
||||||
placeholder="Paste a video or playlist URL, then fetch…"
|
placeholder="Paste a video or playlist URL, then check…"
|
||||||
size="large"
|
size="large"
|
||||||
contentBefore={<ArrowDownloadRegular />}
|
contentBefore={<ArrowDownloadRegular />}
|
||||||
/>
|
/>
|
||||||
<Hint label="Fetch formats / playlist" placement="bottom" align="end">
|
<Hint label="Check URL" placement="bottom" align="end">
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
icon={probing ? <Spinner size="tiny" /> : <SearchRegular />}
|
icon={probing ? <Spinner size="tiny" /> : <SearchRegular />}
|
||||||
onClick={fetchFormats}
|
onClick={fetchFormats}
|
||||||
disabled={!url.trim() || probing}
|
disabled={!url.trim() || probing}
|
||||||
aria-label="Fetch formats or playlist"
|
aria-label="Check URL for formats or playlist"
|
||||||
/>
|
/>
|
||||||
</Hint>
|
</Hint>
|
||||||
<Hint label="Paste from clipboard" placement="bottom" align="end">
|
<Hint label="Paste from clipboard" placement="bottom" align="end">
|
||||||
|
|||||||
Reference in New Issue
Block a user