diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md
index 26ab263..c29bacb 100644
--- a/CODE-AUDIT.md
+++ b/CODE-AUDIT.md
@@ -1,4 +1,4 @@
-# Code Audit
+# Code Audit
Living checklist of audit findings for AeroFetch. Security & correctness were reviewed
2026-06-23 (all fixed — see Completed). The architectural review (2026-06-29) added the
@@ -408,7 +408,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
rings; `box-sizing` is then set ad hoc on the SettingsView swatch.
- [x] **L15 — `MediaThumb` redundant ternary** `kind === 'audio' ? 'audio' : 'video'` (kind is
already that union).
-- [ ] **L16 — `relTime`/`formatWhen` verbosity.** `relTime` returns unbounded "N d ago"; History's
+- [x] **L16 — `relTime`/`formatWhen` verbosity.** `relTime` returns unbounded "N d ago"; History's
`formatWhen` always appends the year, even for the current year.
- [x] **L17 — TemplateManager regex not validated.** An invalid `urlPattern` is accepted with no
feedback and silently never matches (main's `matchesUrl` swallows the error).
@@ -516,11 +516,11 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
re-queued row loses its thumbnail until re-probed.
- [ ] **L73 — Two verbs for the same probe action** — DownloadBar "Fetch" vs LibraryView "Index".
- [ ] **L74 — Accent swatches are triple-labeled** (`aria-pressed` + `aria-label` + `title`).
-- [ ] **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.
- [x] **L76 — Duplicate-warning may show a placeholder title** — `setDup(existing.title)` can be the
`titleFromUrl` placeholder before metadata resolves ('YouTube video (id)').
-- [ ] **L77 — ffmpeg/ffprobe versions have no re-check affordance** (load once; "not found" sticks
+- [x] **L77 — ffmpeg/ffprobe versions have no re-check affordance** (load once; "not found" sticks
with no retry) unlike the yt-dlp "Check version" button.
- [ ] **L78 — `DownloadsView` empty-state copy** ("Paste a URL above to get started") doesn't match
the actual two-step Fetch→Download affordance.
diff --git a/src/renderer/src/components/SettingsView.tsx b/src/renderer/src/components/SettingsView.tsx
index fa0ce24..e4735cb 100644
--- a/src/renderer/src/components/SettingsView.tsx
+++ b/src/renderer/src/components/SettingsView.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef, useState } from 'react'
+import { useEffect, useRef, useState } from 'react'
import {
Field,
Input,
@@ -153,10 +153,10 @@ const useStyles = makeStyles({
}
})
-// Combined "Type — Quality" options for the default-format dropdown.
+// Combined "Type -- Quality" options for the default-format dropdown.
const FORMAT_OPTIONS = [
- ...QUALITY_OPTIONS.video.map((q) => ({ value: `video|${q}`, label: `Video — ${q}` })),
- ...QUALITY_OPTIONS.audio.map((q) => ({ value: `audio|${q}`, label: `Audio — ${q}` }))
+ ...QUALITY_OPTIONS.video.map((q) => ({ value: `video|${q}`, label: `Video -- ${q}` })),
+ ...QUALITY_OPTIONS.audio.map((q) => ({ value: `audio|${q}`, label: `Audio -- ${q}` }))
]
const THEME_MODE_OPTIONS = [
@@ -364,7 +364,7 @@ export function SettingsView(): React.JSX.Element {
const result = await window.api.importBackup()
setImportResult(result)
if (result.ok) {
- // Settings + templates changed underneath the stores — reload both.
+ // Settings + templates changed underneath the stores -- reload both.
const [s, t] = await Promise.all([window.api.getSettings(), window.api.listTemplates()])
useSettings.setState({ ...s, loaded: true })
useTemplates.setState({ templates: t })
@@ -378,7 +378,7 @@ export function SettingsView(): React.JSX.Element {
function copyErrorReport(): void {
// The button is disabled when there are no entries, so `report` is always
- // non-empty here — no need for an unreachable "No errors logged." fallback (L159).
+ // non-empty here -- no need for an unreachable "No errors logged." fallback (L159).
const report = errorEntries
.map((e) =>
[new Date(e.occurredAt).toLocaleString(), e.title ?? e.url, e.url, e.error].join('\n')
@@ -393,8 +393,8 @@ export function SettingsView(): React.JSX.Element {
try {
const result = await window.api.cookiesLogin(loginUrl)
if (result.ok && result.cookieCount === 0) {
- // Window closed without capturing anything — don't imply success (L50).
- setLoginError('No cookies were captured — did you sign in before closing the window?')
+ // Window closed without capturing anything -- don't imply success (L50).
+ setLoginError('No cookies were captured -- did you sign in before closing the window?')
} else if (result.ok) {
setCookiesStatus(await window.api.cookiesStatus())
} else {
@@ -441,7 +441,7 @@ export function SettingsView(): React.JSX.Element {
try {
const result = await window.api.updateYtdlp(ytdlpChannel)
setUpdateResult(result)
- if (result.ok) setVersion(null) // stale — prompt a re-check rather than show a wrong version
+ if (result.ok) setVersion(null) // stale -- prompt a re-check rather than show a wrong version
} catch (e) {
setUpdateResult({ ok: false, error: e instanceof Error ? e.message : String(e) })
} finally {
@@ -468,7 +468,7 @@ export function SettingsView(): React.JSX.Element {
/>
{noResults && (
- No settings match “{search}”.
+ No settings match "{search}".
)}
@@ -577,7 +577,7 @@ export function SettingsView(): React.JSX.Element {
{highContrast
- ? 'A Windows high-contrast theme is active — AeroFetch follows your system colors.'
+ ? 'A Windows high-contrast theme is active -- AeroFetch follows your system colors.'
: 'AeroFetch automatically follows a Windows high-contrast theme if you turn one on.'}
{highContrast && (
@@ -746,7 +746,7 @@ export function SettingsView(): React.JSX.Element {
setPotHint('Token saved.')
} else {
setPotHint(
- 'Token not found — try signing into YouTube first via Cookies above.'
+ 'Token not found -- try signing into YouTube first via Cookies above.'
)
}
} catch {
@@ -799,7 +799,7 @@ export function SettingsView(): React.JSX.Element {
<>
Custom commands
- Named templates of extra yt-dlp flags — your own power-user recipes (e.g.
+ Named templates of extra yt-dlp flags -- your own power-user recipes (e.g.
--write-thumbnail, --no-mtime, anything yt-dlp supports). Appended after every other
option, so a template flag can override a setting above it.
@@ -891,7 +891,7 @@ export function SettingsView(): React.JSX.Element {
Save your settings and custom-command templates to a JSON file, or restore them on another
- machine. Does not include download history or credentials (proxy, API tokens — re-enter
+ machine. Does not include download history or credentials (proxy, API tokens -- re-enter
those after import).
@@ -1034,23 +1034,25 @@ export function SettingsView(): React.JSX.Element {
{appChecking && }
-
- update({ updateToken: d.value })}
- contentBefore={}
- />
-
+ {(updateToken.trim() !== '' || !!appUpdError) && (
+
+ update({ updateToken: d.value })}
+ contentBefore={}
+ />
+
+ )}
{appUpd?.ok && appUpd.available && (
<>
- Version {appUpd.latestVersion} is available — here's what changed:
+ Version {appUpd.latestVersion} is available -- here's what changed:
{appUpd.notes &&
{appUpd.notes}
}
@@ -1073,7 +1075,8 @@ export function SettingsView(): React.JSX.Element {
{!appUpd.downloadUrl && (
- This release has no installer attached — use “View release” to download it manually.
+ This release has no installer attached -- use "View release" to download it
+ manually.
)}
{appDownloading && }
@@ -1081,7 +1084,7 @@ export function SettingsView(): React.JSX.Element {
)}
{appUpd?.ok && !appUpd.available && (
- You're up to date — v{appUpd.currentVersion} is the latest.
+ You're up to date -- v{appUpd.currentVersion} is the latest.
)}
{appUpdError && {appUpdError}}
@@ -1115,14 +1118,24 @@ export function SettingsView(): React.JSX.Element {
{version.error}
)}
{ffmpeg && (
- <>
-
- ffmpeg {ffmpeg.ffmpeg ?? 'not found'}
-
-
- ffprobe {ffmpeg.ffprobe ?? 'not found'}
-
- >
+