diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md index 937e288..78b77bd 100644 --- a/CODE-AUDIT.md +++ b/CODE-AUDIT.md @@ -279,7 +279,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n label contradicts the actual output format; `--audio-quality` is also meaningless for lossless. - [x] **M19 — Sidebar-collapsed pref uses `localStorage`** while every other preference uses electron-store — won't back up/restore and is lost in the portable build across machines. -- [ ] **M20 — ProgressBar / native `` accessibility.** QueueItem/DownloadsView `ProgressBar`s have no accessible name; several `Select`s carry both a `` and an `aria-label` (double announcement). - [x] **M21 — `--audio-quality` always emitted.** `buildArgs` passes `--audio-quality` even for @@ -390,7 +390,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n (~40px) Input/Buttons in DownloadBar. - [x] **L7 — `canceled` and `paused` share the 'warning' badge color** (QueueItem) — visually ambiguous. -- [ ] **L8 — Index/compound list keys.** TerminalView keys log lines by array index; Settings +- [x] **L8 — Index/compound list keys.** TerminalView keys log lines by array index; Settings Diagnostics keys by `id + occurredAt` while every other list keys by `id` alone. - [ ] **L9 — Thumbnail box sizes not shared.** Four hand-tuned 16:9 boxes (120×68, 108×64, 72×44, 60×34) with no shared aspect/size constant. @@ -1393,7 +1393,7 @@ the end so the picture is complete. **Verified-clean first:** card/label/button ### Wording / jargon polish -- [ ] **SR9 — Dev jargon in user-facing option hints** (extends M37): "mux them into the video," "Tiebreaker, +- [x] **SR9 — Dev jargon in user-facing option hints** (extends M37): "mux them into the video," "Tiebreaker, not a hard filter," "Endcards / credits" ("End cards"), "Proof-of-Origin token," "`--extractor-args`." Reads like engineer notes. **Fix:** plain-language hints. - [ ] **SR10 — Inconsistent product self-description / casing** surfaced to users: "yt-dlp frontend" caption diff --git a/src/renderer/src/components/DownloadOptionsForm.tsx b/src/renderer/src/components/DownloadOptionsForm.tsx index 7ae293b..ba90195 100644 --- a/src/renderer/src/components/DownloadOptionsForm.tsx +++ b/src/renderer/src/components/DownloadOptionsForm.tsx @@ -1,4 +1,4 @@ -import { +import { Field, Input, Switch, @@ -115,7 +115,7 @@ export function DownloadOptionsForm({ value, onChange }: Props): React.JSX.Eleme onChange={(v) => setOpt('audioFormat', v as AudioFormat)} /> - + @@ -290,7 +290,7 @@ export function DownloadOptionsForm({ value, onChange }: Props): React.JSX.Eleme
i.status === 'completed' || i.status === 'error' || i.status === 'canceled' ) // The header count is the live queue (work not yet finished), not the whole - // list — completed/canceled/error rows linger until "Clear finished" (L11). + // list -- completed/canceled/error rows linger until "Clear finished" (L11). const queueCount = items.filter( (i) => i.status === 'downloading' || @@ -99,7 +99,12 @@ export function DownloadsView(): React.JSX.Element { {summary.active && (
- + {summary.downloading} downloading {summary.queued ? `, ${summary.queued} queued` : ''} diff --git a/src/renderer/src/components/QueueItem.tsx b/src/renderer/src/components/QueueItem.tsx index faf5f13..07b3137 100644 --- a/src/renderer/src/components/QueueItem.tsx +++ b/src/renderer/src/components/QueueItem.tsx @@ -1,4 +1,4 @@ -import { memo } from 'react' +import { memo } from 'react' import { Text, Caption1, @@ -120,7 +120,7 @@ export const QueueItem = memo(function QueueItem({ const active = item.status === 'downloading' || item.status === 'queued' - // W7: Delete on a focused row removes it — cancelling first if it's still running, + // W7: Delete on a focused row removes it -- cancelling first if it's still running, // since an in-flight download can't just be dropped from the list. Only when the // row itself holds focus, so Delete on one of its action buttons is unaffected. function onKeyDown(e: React.KeyboardEvent): void { @@ -168,7 +168,7 @@ export const QueueItem = memo(function QueueItem({ {item.title} {item.incognito && ( - + )} @@ -185,6 +185,7 @@ export const QueueItem = memo(function QueueItem({ className={styles.progressBar} value={item.finishing || item.sizeUnknown ? undefined : item.progress} thickness="large" + aria-label={`Download progress for ${item.title ?? item.url}`} /> {item.finishing ? ( @@ -209,7 +210,12 @@ export const QueueItem = memo(function QueueItem({ {item.status === 'paused' && (
- + Paused • {pct(item.progress)}
)} diff --git a/src/renderer/src/components/SettingsView.tsx b/src/renderer/src/components/SettingsView.tsx index 072b8f5..dcb006c 100644 --- a/src/renderer/src/components/SettingsView.tsx +++ b/src/renderer/src/components/SettingsView.tsx @@ -1000,7 +1000,7 @@ export function SettingsView(): React.JSX.Element { ) : (
{errorEntries.slice(0, 20).map((e) => ( -
+
{e.title ?? e.url} @@ -1086,7 +1086,7 @@ export function SettingsView(): React.JSX.Element { manually. )} - {appDownloading && } + {appDownloading && } )} diff --git a/src/renderer/src/components/TerminalView.tsx b/src/renderer/src/components/TerminalView.tsx index 4d7ad84..ae275de 100644 --- a/src/renderer/src/components/TerminalView.tsx +++ b/src/renderer/src/components/TerminalView.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { Button, Textarea, @@ -16,9 +16,12 @@ import { ScreenHeader, useScreenStyles } from './ui/Screen' type LineKind = 'stdout' | 'stderr' | 'cmd' | 'sys' interface Line { + id: number text: string kind: LineKind } +let lineSeq = 0 +const mkLine = (text: string, kind: LineKind): Line => ({ id: ++lineSeq, text, kind }) // Verbose yt-dlp runs (--verbose, -F on a large channel) can emit thousands of // lines in seconds. Cap the visible log to prevent unbounded React state growth @@ -62,7 +65,7 @@ const useStyles = makeStyles({ /** * Built-in yt-dlp terminal (Phase N): type raw yt-dlp args, run the bundled * binary, and watch its output stream. Gated on the customCommandEnabled consent - * flag (enforced again in main — see src/main/terminal.ts). + * flag (enforced again in main -- see src/main/terminal.ts). */ export function TerminalView(): React.JSX.Element { const styles = useStyles() @@ -81,20 +84,14 @@ export function TerminalView(): React.JSX.Element { window.api.onTerminalOutput((ev) => { if (ev.id !== runId.current) return if (ev.type === 'output') { - setLines((ls) => - [...ls, { text: ev.line, kind: ev.stream } as Line].slice(-MAX_LOG_LINES) - ) + setLines((ls) => [...ls, mkLine(ev.line, ev.stream)].slice(-MAX_LOG_LINES)) } else if (ev.type === 'error') { - setLines((ls) => - [...ls, { text: ev.error, kind: 'stderr' as const }].slice(-MAX_LOG_LINES) - ) + setLines((ls) => [...ls, mkLine(ev.error, 'stderr')].slice(-MAX_LOG_LINES)) setRunning(false) runId.current = null } else { setLines((ls) => - [...ls, { text: `— exited (code ${ev.code ?? '?'})`, kind: 'sys' as const }].slice( - -MAX_LOG_LINES - ) + [...ls, mkLine(`-- exited (code ${ev.code ?? '?'})`, 'sys')].slice(-MAX_LOG_LINES) ) setRunning(false) runId.current = null @@ -113,19 +110,19 @@ export function TerminalView(): React.JSX.Element { if (!a || running) return const id = newId('t') runId.current = id - setLines((ls) => [...ls, { text: `> yt-dlp ${a}`, kind: 'cmd' }]) + setLines((ls) => [...ls, mkLine(`> yt-dlp ${a}`, 'cmd')]) setRunning(true) window.api .runTerminal(id, a) .then((res) => { if (!res.ok) { - setLines((ls) => [...ls, { text: res.error ?? 'Failed to start.', kind: 'stderr' }]) + setLines((ls) => [...ls, mkLine(res.error ?? 'Failed to start.', 'stderr')]) setRunning(false) runId.current = null } }) .catch((e: unknown) => { - setLines((ls) => [...ls, { text: String(e), kind: 'stderr' }]) + setLines((ls) => [...ls, mkLine(String(e), 'stderr')]) setRunning(false) runId.current = null }) @@ -158,7 +155,7 @@ export function TerminalView(): React.JSX.Element { {!customCommandEnabled && ( - The terminal is part of custom commands. Turn on “Run custom commands” in Settings → + The terminal is part of custom commands. Turn on "Run custom commands" in Settings → Custom commands to use it. )} @@ -210,8 +207,8 @@ export function TerminalView(): React.JSX.Element { {lines.length === 0 ? ( Output will appear here. ) : ( - lines.map((l, i) => ( -
+ lines.map((l) => ( +
{l.text}
))