feat(audit): Batch 18 — watched visual pass (UI3, UI12, UI16, UI21, UX18, L109, UX26)

UI3: remaining list-row/control paddings on the SPACE scale (History row,
Downloads summary, Library head/detail, Terminal gate/log, template rows).
UI21: active-nav rail now shows collapsed too (moved into navItemActive).
UX18: stable 'Quality' label + brand '• fetched' tag instead of the
label/control morph after Fetch.
L109/UX26: skeleton lines for the About yt-dlp/ffmpeg boot load; skeleton
cards in Settings until the store's loaded flag flips.
UI12/UI16: deferred eyeball deltas verified via Electron-probe screenshots
(light+dark, collapsed sidebar, fetch flow); UI16 convention codified in
ui/tokens.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 11:55:45 -04:00
parent 747cecd1e0
commit cdb6eea437
13 changed files with 147 additions and 61 deletions
+36 -29
View File
@@ -1007,10 +1007,11 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
check-for-new, update-yt-dlp) vs `ArrowSyncRegular` (re-index, check-for-updates) for the same "redo/refresh" idea.
- [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>`).
- [ ] **L109 — Versions auto-load with no indicator.** yt-dlp/ffmpeg versions in About just appear;
only the manual "Check" button shows a spinner. *Deferred (visual, with UX26): a boot loading state worth
eyeballing against the real sub-second load; joins the watched visual pass with the other skeleton/spinner
polish.*
- [x] **L109 — Versions auto-load with no indicator.** yt-dlp/ffmpeg versions in About just appear;
only the manual "Check" button shows a spinner. *Fixed (Batch 18, with UX26): the About card shows Fluent
`Skeleton` lines for the yt-dlp and ffmpeg versions while the mount loads them (`checking` now starts true
and settles in a `finally`; an ffmpeg bridge failure settles to "not found" rather than a permanent
skeleton). Verified in the preview probe that the card settles to the loaded state cleanly.*
- [x] **L110 — One muted-text token, many class aliases.** `colorNeutralForeground3` is re-declared as
`hint`/`sub`/`meta`/`srcSub`/`stats`/`count`/`emptyHint`/`previewMeta` across components. *Fixed: one
`useTextStyles().muted` in [ui/text.ts](src/renderer/src/components/ui/text.ts). Adopted for the pure-color
@@ -1389,13 +1390,14 @@ are an undefined spacing/radius/type scale and a drift between Fluent components
(Downloads), 18 (Library), 12 (History). **Standard:** a single section-gap (16px) via a shared screen wrapper.
*Fixed: all five view roots now use `SPACE.section` (16) — Downloads 20→16, Library 18→16, History 12→16;
Settings/Terminal were already 16 and are now tokenized.*
- [ ] **UI3 — Card padding has no scale.** 14 (QueueItem) / 16 (DownloadBar) / 20 (Settings card) /
- [x] **UI3 — Card padding has no scale.** 14 (QueueItem) / 16 (DownloadBar) / 20 (Settings card) /
32 (Onboarding) / `10px 12px` (History row) / `12px 14px` (Library head) / `8px 10px` (template row).
**Standard:** a padding scale (e.g. control 810, list-row 12, card 16, hero 24) keyed to surface tier.
*Implemented (SPACE scale defined; the card tier unified to 16 — QueueItem 14→16, Settings cards 20→16;
DownloadBar already 16; Onboarding → `hero`) but **left unchecked for a watched visual pass**: 20→16 tightens
the 11 settings cards and 14→16 loosens queue rows. The smaller list-row/control paddings (History/Library
head/template) are not yet migrated. Eyeball, then check off or retune the card value.*
*Done (Batch 18): the remaining list-row/control paddings migrated to the `SPACE` scale — History row and
Downloads summary → `snug/cozy`, Library head/detail and Terminal gate/log and the template form → `cozy`
(12, the list-row tier; the odd 14px horizontals snapped down), template row → `tight/snug`. The earlier
card-tier deltas (20→16 settings cards, 14→16 queue rows) plus these were eyeballed via the Electron-probe
screenshot pass (light + dark, Downloads/History/Library/Settings): nothing reads cramped or loose; kept.*
- [x] **UI4 — Asymmetric app content padding.** `padding: '24px 28px'` ([App.tsx](src/renderer/src/App.tsx)) —
28px horizontal appears nowhere else. **Standard:** symmetric or scale-based padding. *Fixed: the `<main>`
content padding is now the symmetric `SPACE.page` (24px).*
@@ -1436,13 +1438,13 @@ are an undefined spacing/radius/type scale and a drift between Fluent components
### Icons
- [ ] **UI12 — No icon-size scale.** Inline `fontSize` of 11/14/16/18/20/22/26/28/40 is scattered
- [x] **UI12 — No icon-size scale.** Inline `fontSize` of 11/14/16/18/20/22/26/28/40 is scattered
across components. **Standard:** a small set (16 inline / 20 control / 24 section / 40 empty-state).
*Implemented (`ICON` = 16/20/24/40, all four tiers in use; standalone chrome glyphs migrated) but **left
unchecked for a watched visual pass** because a few glyphs shift: the former-18px chrome icons (sidebar nav +
theme toggles, IconButton md) → 20 (control), the settings card-header icons 20→24 (section), and the private
badge 14→16. Box-proportional glyphs (brand/source tiles, `watchBadge` 11px, thumbnail placeholders 22/28)
are documented exclusions and unchanged. Eyeball the 18→20 / 20→24 / 14→16 deltas.*
*Done (Batch 18): the `ICON` = 16/20/24/40 tiers were already in place; the deferred eyeball of the shifted
glyphs (18→20 sidebar nav/theme toggles, 20→24 settings card headers, 14→16 private badge) happened in the
Electron-probe screenshot pass — the nav icons sit balanced in their 20px tier, the card-header glyphs read
as proper section markers at 24, nothing looks oversized. Box-proportional glyphs (brand/source tiles,
`watchBadge`, thumbnail placeholders) remain documented exclusions.*
- [x] **UI13 — Regular vs Filled weight mixed for one concept.** `ArrowDownloadRegular` (nav, buttons)
vs `ArrowDownloadFilled` (brand mark). **Standard:** one weight per concept (Filled only for brand/emphasis).
*Verified: `ArrowDownloadFilled` appears only on the two brand marks (Sidebar, Onboarding); every other
@@ -1457,11 +1459,13 @@ are an undefined spacing/radius/type scale and a drift between Fluent components
- [x] **UI15 — Raw `<button>`s alongside Fluent `<Button>`.** Sidebar navItem/iconBtn/themeSeg,
DownloadBar segment/plKindBtn, CommandPalette item, and Library `groupHead` (a `role="button"` div)
are bespoke. **Standard:** wrap recurring patterns so hover/focus/disabled are uniform (ties to L5/M15).
- [ ] **UI16 — Button size hierarchy isn't applied uniformly.** DownloadBar `large`; Library "Index"
- [x] **UI16 — Button size hierarchy isn't applied uniformly.** DownloadBar `large`; Library "Index"
`large` but its toolbar `small`; History all `small`; Settings mostly default. **Standard:** size-by-role (screen primary = large; secondary = medium; row actions = small).
*Convention documented (screen-primary large · row/toolbar small · settings default/medium); current usage
already broadly follows it. **Left unchecked** — the specific per-screen size tweaks are a subjective visual
call, so they join the watched visual pass (with UI3/UI12).*
*Done (Batch 18): the usage the item lists already matches the size-by-role rule (each screen's one primary
is `large` — Download/Add-to-queue, Library Index; row/toolbar actions `small`; settings cards default) —
confirmed screen-by-screen in the screenshot pass, no tweaks needed. The convention is now codified in
[ui/tokens.ts](src/renderer/src/components/ui/tokens.ts) alongside the other scales so it's a rule, not an
accident.*
- [x] **UI17 — `appearance="secondary"` used in only two spots** (Library "Check for new", Terminal
"Stop") while every other non-primary button is `subtle`. **Standard:** pick subtle *or* secondary as the standard non-primary appearance.
*Fixed: both switched to `subtle` (the app's standard non-primary appearance — used by every other
@@ -1488,11 +1492,11 @@ are an undefined spacing/radius/type scale and a drift between Fluent components
**brand-tint**. Each class is now internally consistent, and the rule is documented on
[SegmentedControl](src/renderer/src/components/ui/SegmentedControl.tsx). The "Library pill" the item cited is
gone — UI18 replaced it with the shared `StatusChip`, so there's no third treatment left to reconcile.*
- [ ] **UI21 — Sidebar active-nav rail is expanded-only.** The inset brand box-shadow shows only when
- [x] **UI21 — Sidebar active-nav rail is expanded-only.** The inset brand box-shadow shows only when
expanded; collapsed relies on tint alone. **Standard:** a consistent active indicator in both states.
*Deferred (subjective visual): whether the collapsed rail needs its own inset indicator (and how it reads
against the new active-download `CounterBadge` added in Batch 11) is an eyeball call; joins the watched
visual pass. The tint already marks the active item in both states.*
*Fixed (Batch 18): the inset rail moved from a conditional inline style into `navItemActive` itself, so it
shows in both states. Screenshot-verified collapsed: the 3px brand rail hugs the row's left edge and reads
clearly beside the active-download `CounterBadge` pinned at the icon's top-right — no collision.*
- [x] **UI22 — Brand-icon tiles differ.** `mark` (on-brand on solid brand) vs `srcIcon`
(brandForeground2 on brand-tint) vs `sectionIcon` (compoundBrand, no tile). **Standard:** a defined icon-emphasis set.
*Resolved (the set is now defined + documented): three deliberate emphasis levels — **brand mark** (`mark`,
@@ -1731,10 +1735,12 @@ root cause is already filed.
keyboard focus — so screen-reader and keyboard users get the name; the icon isn't the only affordance. A
permanently-visible text label is a deliberate compact-layout tradeoff for the URL row (the Download button
beside it carries the primary text label), so it's left as a visual choice, not an a11y gap.*
- [ ] **UX18 — The quality control morphs after Fetch** (preset list → real formats; label "Quality" →
- [x] **UX18 — The quality control morphs after Fetch** (preset list → real formats; label "Quality" →
"Quality / format") in place — a surprising transform. **Fix:** keep a stable control with a clear "formats loaded" state.
*Deferred (subjective visual): the morph is arguably informative (the label change signals real formats
loaded); whether it "surprises" is a judgment best made watching it, so it joins the watched visual pass.*
*Fixed (Batch 18): the label stays "Quality" in both states; a small brand-colored "• fetched" tag appears
beside it once real formats load, so the state change is signalled without the control or its label
morphing. Verified in the preview probe: Fetch on a URL loads the mock formats and the tag + format list
render as designed (screenshot).*
- [x] **UX19 — Window size/position isn't remembered.** [index.ts](src/main/index.ts) opens a fixed
920×700 every launch (no bounds persistence). **Fix:** persist + restore window bounds. *Fixed with W2 —
[windowState.ts](src/main/windowState.ts) persists + restores bounds/maximized with an off-screen guard.*
@@ -1766,10 +1772,11 @@ root cause is already filed.
- [x] **UX25 — Empty-state copy mismatches the flow** (L78): Downloads says "Paste a URL above to get
started," but the actual path is Fetch/Download. *Resolved by L78 (DownloadsView empty-state copy
rewritten to match the Fetch/Download flow).*
- [ ] **UX26 — yt-dlp/ffmpeg version + initial settings load have no skeleton/spinner** — brief blank
- [x] **UX26 — yt-dlp/ffmpeg version + initial settings load have no skeleton/spinner** — brief blank
states on boot (plus the documented one-frame theme flash). **Fix:** lightweight loading states.
*Deferred (low value, needs eyeballing): the blanks are sub-second on a normal launch; adding skeletons is
a perceived-polish tweak worth doing while watching the actual boot, so it joins the visual pass.*
*Fixed (Batch 18, with L109): the About versions show skeleton lines during the boot load, and SettingsView
renders three skeleton cards until the settings store's `loaded` flag flips — no more flash of fallback
values. (The one-frame theme flash remains separately documented under SR1.)*
---