Fix L106/L107/L122: unify refresh icons, spinner size, busy-button feedback

L106: Established the convention — ArrowClockwise = retry/redo a local action
      (retry-all, re-download, retry queue item); ArrowSync = network sync/
      update (sync sources, reindex, check/apply updates). Flipped the two
      misclassified sites (LibraryView "check for new" sync, SettingsView
      "Update yt-dlp") to ArrowSync and dropped the now-unused imports.

L107: QueueItem queued-row Spinner was size="extra-tiny"; every other spinner
      is "tiny". Standardized to "tiny".

L122: The app-update and yt-dlp-update buttons showed "Checking…"/"Updating…"
      in the label AND a separate sibling Spinner. Folded the spinner into the
      button icon (icon swaps to Spinner while busy), matching DownloadBar/
      LibraryView, and removed the redundant sibling Spinner.

typecheck + 242 tests + eslint green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 15:29:19 -04:00
parent d7b5737806
commit 0d2000f307
4 changed files with 11 additions and 11 deletions
+3 -3
View File
@@ -587,9 +587,9 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
[store/downloads.ts](src/renderer/src/store/downloads.ts); components import it from both. Single source it. [store/downloads.ts](src/renderer/src/store/downloads.ts); components import it from both. Single source it.
*Fixed: the store now imports `MediaKind` from `@shared/ipc` and re-exports it, so the duplicate local *Fixed: the store now imports `MediaKind` from `@shared/ipc` and re-exports it, so the duplicate local
`type MediaKind` is gone while existing `import { MediaKind } from '../store/downloads'` sites still resolve.* `type MediaKind` is gone while existing `import { MediaKind } from '../store/downloads'` sites still resolve.*
- [ ] **L106 — Refresh icons used interchangeably.** `ArrowClockwiseRegular` (retry, re-download, - [x] **L106 — Refresh icons used interchangeably.** `ArrowClockwiseRegular` (retry, re-download,
check-for-new, update-yt-dlp) vs `ArrowSyncRegular` (re-index, check-for-updates) for the same "redo/refresh" idea. check-for-new, update-yt-dlp) vs `ArrowSyncRegular` (re-index, check-for-updates) for the same "redo/refresh" idea.
- [ ] **L107 — Spinner sizes mixed**`"tiny"` almost everywhere, `"extra-tiny"` for the QueueItem queued row. - [x] **L107 — Spinner sizes mixed**`"tiny"` almost everywhere, `"extra-tiny"` for the QueueItem queued row.
- [x] **L108 — Main window sets no explicit `title`** (relies on the document `<title>`). - [x] **L108 — Main window sets no explicit `title`** (relies on the document `<title>`).
- [ ] **L109 — Versions auto-load with no indicator.** yt-dlp/ffmpeg versions in About just appear; - [ ] **L109 — Versions auto-load with no indicator.** yt-dlp/ffmpeg versions in About just appear;
only the manual "Check" button shows a spinner. only the manual "Check" button shows a spinner.
@@ -612,7 +612,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
other card surface is a bespoke styled `<div>`. other card surface is a bespoke styled `<div>`.
- [ ] **L121 — Raw scrim literal.** CommandPalette backdrop is `rgba(0,0,0,0.32)` (the only raw rgba in - [ ] **L121 — Raw scrim literal.** CommandPalette backdrop is `rgba(0,0,0,0.32)` (the only raw rgba in
the renderer), identical in light/dark — low contrast over a dark UI. the renderer), identical in light/dark — low contrast over a dark UI.
- [ ] **L122 — "Checking…" indicated twice** — the Settings update button changes its label to "Checking…" *and* a separate Spinner renders beside it. - [x] **L122 — "Checking…" indicated twice** — the Settings update button changes its label to "Checking…" *and* a separate Spinner renders beside it.
- [ ] **L123 — `Caption1` is overloaded** for hints, metadata, counts, and timestamps — one size doing four jobs. - [ ] **L123 — `Caption1` is overloaded** for hints, metadata, counts, and timestamps — one size doing four jobs.
- [ ] **L124 — `Body1` vs `Text` role overlap**`Body1` appears only in empty states/onboarding/gate; `Text` carries titles; the split isn't principled. - [ ] **L124 — `Body1` vs `Text` role overlap**`Body1` appears only in empty states/onboarding/gate; `Text` carries titles; the split isn't principled.
- [ ] **L125 — Icon sizing has two syntaxes** — px strings (`fontSize: '20px'`) on icon *containers* vs numeric `fontSize={28}` on inline icons. - [ ] **L125 — Icon sizing has two syntaxes** — px strings (`fontSize: '20px'`) on icon *containers* vs numeric `fontSize={28}` on inline icons.
+1 -2
View File
@@ -16,7 +16,6 @@ import {
import { import {
SearchRegular, SearchRegular,
ArrowSyncRegular, ArrowSyncRegular,
ArrowClockwiseRegular,
DeleteRegular, DeleteRegular,
ArrowDownloadRegular, ArrowDownloadRegular,
ChevronDownRegular, ChevronDownRegular,
@@ -551,7 +550,7 @@ export function LibraryView(): React.JSX.Element {
<Button <Button
size="small" size="small"
appearance="secondary" appearance="secondary"
icon={syncing ? <Spinner size="tiny" /> : <ArrowClockwiseRegular />} icon={syncing ? <Spinner size="tiny" /> : <ArrowSyncRegular />}
onClick={onCheckNew} onClick={onCheckNew}
disabled={syncing || watchedCount === 0} disabled={syncing || watchedCount === 0}
> >
+1 -1
View File
@@ -204,7 +204,7 @@ export const QueueItem = memo(function QueueItem({
{item.status === 'queued' && ( {item.status === 'queued' && (
<div className={styles.progressRow}> <div className={styles.progressRow}>
<Spinner size="extra-tiny" /> <Spinner size="tiny" />
<Caption1 className={styles.stats}>Waiting to start</Caption1> <Caption1 className={styles.stats}>Waiting to start</Caption1>
</div> </div>
)} )}
+6 -5
View File
@@ -25,7 +25,6 @@ import {
GlobeRegular, GlobeRegular,
CookiesRegular, CookiesRegular,
CodeRegular, CodeRegular,
ArrowClockwiseRegular,
DocumentArrowDownRegular, DocumentArrowDownRegular,
DocumentArrowUpRegular, DocumentArrowUpRegular,
BugRegular, BugRegular,
@@ -1039,13 +1038,12 @@ export function SettingsView(): React.JSX.Element {
<div className={styles.folderRow}> <div className={styles.folderRow}>
<Button <Button
icon={<ArrowSyncRegular />} icon={appChecking ? <Spinner size="tiny" /> : <ArrowSyncRegular />}
onClick={checkAppUpdate} onClick={checkAppUpdate}
disabled={appChecking || appDownloading} disabled={appChecking || appDownloading}
> >
{appChecking ? 'Checking…' : 'Check for updates'} {appChecking ? 'Checking…' : 'Check for updates'}
</Button> </Button>
{appChecking && <Spinner size="tiny" />}
</div> </div>
{(updateToken.trim() !== '' || !!appUpdError) && ( {(updateToken.trim() !== '' || !!appUpdError) && (
@@ -1175,10 +1173,13 @@ export function SettingsView(): React.JSX.Element {
/> />
</Field> </Field>
<div className={styles.folderRow}> <div className={styles.folderRow}>
<Button icon={<ArrowClockwiseRegular />} onClick={runUpdate} disabled={updating}> <Button
icon={updating ? <Spinner size="tiny" /> : <ArrowSyncRegular />}
onClick={runUpdate}
disabled={updating}
>
{updating ? 'Updating…' : 'Update yt-dlp'} {updating ? 'Updating…' : 'Update yt-dlp'}
</Button> </Button>
{updating && <Spinner size="tiny" />}
</div> </div>
{updateResult?.ok && ( {updateResult?.ok && (
<Text style={{ fontFamily: tokens.fontFamilyMonospace, whiteSpace: 'pre-wrap' }}> <Text style={{ fontFamily: tokens.fontFamilyMonospace, whiteSpace: 'pre-wrap' }}>