|
|
@@ -577,9 +577,12 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|
|
|
|
|
|
|
|
|
|
|
*Round 3 (2026-06-29) — tests, build metadata, error copy, edge cases:*
|
|
|
|
*Round 3 (2026-06-29) — tests, build metadata, error copy, edge cases:*
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] **L34 — Integration test off by default.** `real-download.integration.test.ts` is
|
|
|
|
- [x] **L34 — Integration test off by default.** `real-download.integration.test.ts` is
|
|
|
|
`describe.skipIf(!RUN)`; `npm test` never exercises a real download, so there's no automated
|
|
|
|
`describe.skipIf(!RUN)`; `npm test` never exercises a real download, so there's no automated
|
|
|
|
regression for the actual yt-dlp argv path (only the pure builder).
|
|
|
|
regression for the actual yt-dlp argv path (only the pure builder). *Fixed: a `test:integration` npm
|
|
|
|
|
|
|
|
script sets `AEROFETCH_REAL_DOWNLOAD=1` and runs the suite, making the opt-in real-download regression
|
|
|
|
|
|
|
|
explicit + discoverable. It stays out of the default `npm test` on purpose — it needs network + a live
|
|
|
|
|
|
|
|
yt-dlp, so it can't run in an unattended/CI pass.*
|
|
|
|
- [x] **L35 — `isValidMediaItem` has no test** — the one persisted-JSON validator of six with no spec.
|
|
|
|
- [x] **L35 — `isValidMediaItem` has no test** — the one persisted-JSON validator of six with no spec.
|
|
|
|
- [x] **L36 — `compareVersions` differing-length components untested** (e.g. `1.2` vs `1.2.0`).
|
|
|
|
- [x] **L36 — `compareVersions` differing-length components untested** (e.g. `1.2` vs `1.2.0`).
|
|
|
|
- [x] **L37 — `download.ts` formatters untested** (`parseProgress`/`fmtBytes`/`fmtEta`, incl. the `NA` paths).
|
|
|
|
- [x] **L37 — `download.ts` formatters untested** (`parseProgress`/`fmtBytes`/`fmtEta`, incl. the `NA` paths).
|
|
|
@@ -684,8 +687,12 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|
|
|
(not gitignored).
|
|
|
|
(not gitignored).
|
|
|
|
- [x] **L86 — History select-all + filter change deletes hidden rows.** `selected` persists across a
|
|
|
|
- [x] **L86 — History select-all + filter change deletes hidden rows.** `selected` persists across a
|
|
|
|
filter change, so "Delete selected" can remove entries no longer visible (surprising).
|
|
|
|
filter change, so "Delete selected" can remove entries no longer visible (surprising).
|
|
|
|
- [ ] **L87 — History re-download ignores original options.** `redownload` re-queues url/kind/quality
|
|
|
|
- [x] **L87 — History re-download ignores original options.** `redownload` re-queues url/kind/quality
|
|
|
|
only; the post-processing `DownloadOptions` used originally are lost (current defaults apply).
|
|
|
|
only; the post-processing `DownloadOptions` used originally are lost (current defaults apply). *Fixed:
|
|
|
|
|
|
|
|
`HistoryEntry` now carries the original `options` (recorded on completion in `recordCompletion`), and
|
|
|
|
|
|
|
|
`redownload` replays them via `AddOptions.options` — so a re-download reproduces the same subtitles /
|
|
|
|
|
|
|
|
SponsorBlock / container / codec instead of the current global defaults. Entries recorded before this
|
|
|
|
|
|
|
|
(no stored options) fall back to the defaults as before.*
|
|
|
|
- [x] **L88 — `applyEvent('progress')` can promote a `queued` item to `downloading`** outside
|
|
|
|
- [x] **L88 — `applyEvent('progress')` can promote a `queued` item to `downloading`** outside
|
|
|
|
`pump()` — a latent concurrency edge if a progress event ever arrives before/without the launch transition.
|
|
|
|
`pump()` — a latent concurrency edge if a progress event ever arrives before/without the launch transition.
|
|
|
|
- [x] **L89 — Inconsistent release artifacts.** Only `0.4.1` carries `.sha256` files (hand-made) and
|
|
|
|
- [x] **L89 — Inconsistent release artifacts.** Only `0.4.1` carries `.sha256` files (hand-made) and
|
|
|
@@ -712,14 +719,22 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|
|
|
items-ref memo `queueSummaryOf` in [queueStats.ts](src/renderer/src/store/queueStats.ts) — the store hands
|
|
|
|
items-ref memo `queueSummaryOf` in [queueStats.ts](src/renderer/src/store/queueStats.ts) — the store hands
|
|
|
|
out a new items array per change, so both App's taskbar subscription and DownloadsView's render summarize
|
|
|
|
out a new items array per change, so both App's taskbar subscription and DownloadsView's render summarize
|
|
|
|
it once per change (the second caller hits the cache) instead of twice (also closes PERF4).*
|
|
|
|
it once per change (the second caller hits the cache) instead of twice (also closes PERF4).*
|
|
|
|
- [ ] **L95 — "Sign in" hyphenation varies** in the Cookies card ("Sign in…", "Site to sign in to", "Sign-in window", "Signing in…").
|
|
|
|
- [x] **L95 — "Sign in" hyphenation varies** in the Cookies card ("Sign in…", "Site to sign in to", "Sign-in window", "Signing in…").
|
|
|
|
- [ ] **L96 — Ellipsis-on-dialog-buttons inconsistent.** "Export backup…/Import backup…/Sign in…" use
|
|
|
|
*Reviewed: the usages already follow standard English — the verb is two words ("Sign in…", "Signing in…",
|
|
|
|
|
|
|
|
"sign in to") and only the modifier is hyphenated ("Sign-in window"). Consistent by that rule; no change needed.*
|
|
|
|
|
|
|
|
- [x] **L96 — Ellipsis-on-dialog-buttons inconsistent.** "Export backup…/Import backup…/Sign in…" use
|
|
|
|
`…` (opens a dialog), but "Browse" / "Check for updates" / "Update now" don't. Adopt the `…` = opens-a-dialog convention.
|
|
|
|
`…` (opens a dialog), but "Browse" / "Check for updates" / "Update now" don't. Adopt the `…` = opens-a-dialog convention.
|
|
|
|
- [ ] **L97 — Advanced-field placeholders inconsistent.** Adjacent fields use an example (`web_safari`),
|
|
|
|
*Fixed: the folder "Browse" buttons (they open the OS picker) are now "Browse…". "Check for updates" /
|
|
|
|
a literal `(none)`, and `Optional — Gitea access token`. Pick one placeholder style.
|
|
|
|
"Update now" open no dialog (inline result / background download), so they correctly keep no ellipsis.*
|
|
|
|
|
|
|
|
- [x] **L97 — Advanced-field placeholders inconsistent.** Adjacent fields use an example (`web_safari`),
|
|
|
|
|
|
|
|
a literal `(none)`, and `Optional — Gitea access token`. Pick one placeholder style. *Fixed: standardized
|
|
|
|
|
|
|
|
on "an example value where the field has a representative one (`web_safari`, `2M`, `socks5://…`),
|
|
|
|
|
|
|
|
`Optional -- <what>` for opaque optional fields" — the PO-token's uninformative `(none)` is now
|
|
|
|
|
|
|
|
`Optional -- paste a token`, matching the update-token field.*
|
|
|
|
- [x] **L98 — "Embed chapters" has no hint** while every sibling toggle in DownloadOptionsForm does.
|
|
|
|
- [x] **L98 — "Embed chapters" has no hint** while every sibling toggle in DownloadOptionsForm does.
|
|
|
|
- [ ] **L99 — URL input has an `id` but no label.** `input={{ id: 'aerofetch-url' }}` with no
|
|
|
|
- [x] **L99 — URL input has an `id` but no label.** `input={{ id: 'aerofetch-url' }}` with no
|
|
|
|
`<label for>`/`aria-label` (placeholder only) — see M28.
|
|
|
|
`<label for>`/`aria-label` (placeholder only) — see M28. *Resolved by M28: the URL input already carries
|
|
|
|
|
|
|
|
`aria-label="Video or playlist URL"`, so it has an accessible name — the placeholder isn't the only affordance.*
|
|
|
|
- [ ] **L100 — Inconsistent elevation.** DownloadBar (`shadow4`) and CommandPalette (`shadow28`) float;
|
|
|
|
- [ ] **L100 — Inconsistent elevation.** DownloadBar (`shadow4`) and CommandPalette (`shadow28`) float;
|
|
|
|
every other card (Settings, QueueItem, History, Library, Templates) is flat (border-only). Define an elevation scale.
|
|
|
|
every other card (Settings, QueueItem, History, Library, Templates) is flat (border-only). Define an elevation scale.
|
|
|
|
- [ ] **L101 — No z-index scale.** CommandPalette and Hint both hardcode `zIndex: 1000`.
|
|
|
|
- [ ] **L101 — No z-index scale.** CommandPalette and Hint both hardcode `zIndex: 1000`.
|
|
|
@@ -743,8 +758,10 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|
|
|
- [ ] **L111 — Two helper-text mechanisms** — Fluent `Field` `hint` vs manual `<Caption1 className={hint}>`; SettingsView uses both.
|
|
|
|
- [ ] **L111 — Two helper-text mechanisms** — Fluent `Field` `hint` vs manual `<Caption1 className={hint}>`; SettingsView uses both.
|
|
|
|
- [ ] **L112 — `Switch` labeling inconsistent.** Settings/DownloadOptionsForm show "On"/"Off" text;
|
|
|
|
- [ ] **L112 — `Switch` labeling inconsistent.** Settings/DownloadOptionsForm show "On"/"Off" text;
|
|
|
|
Library switches use `aria-label` only (no visible state text).
|
|
|
|
Library switches use `aria-label` only (no visible state text).
|
|
|
|
- [ ] **L113 — Tooltip ≠ aria-label.** The Fetch button's Hint says "Fetch formats / playlist" but its
|
|
|
|
- [x] **L113 — Tooltip ≠ aria-label.** The Fetch button's Hint says "Fetch formats / playlist" but its
|
|
|
|
`aria-label` says "Fetch formats or playlist" (DownloadBar).
|
|
|
|
`aria-label` says "Fetch formats or playlist" (DownloadBar). *Resolved by UX2: the Fetch button's Hint
|
|
|
|
|
|
|
|
tooltip and `aria-label` are now the same string ("Preview available formats (optional)"), so what a
|
|
|
|
|
|
|
|
sighted user sees on hover and what a screen reader announces match.*
|
|
|
|
- [ ] **L114 — Row titles use different components** — `Text` (QueueItem/History/Templates) vs `span` (Library) vs `Caption1` elsewhere.
|
|
|
|
- [ ] **L114 — Row titles use different components** — `Text` (QueueItem/History/Templates) vs `span` (Library) vs `Caption1` elsewhere.
|
|
|
|
- [ ] **L115 — Equivalent row actions labeled inconsistently** — "Re-index" is icon+text; the
|
|
|
|
- [ ] **L115 — Equivalent row actions labeled inconsistently** — "Re-index" is icon+text; the
|
|
|
|
comparable "Retry"/"Remove"/"Re-download" are icon-only.
|
|
|
|
comparable "Retry"/"Remove"/"Re-download" are icon-only.
|
|
|
@@ -907,15 +924,20 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|
|
|
would surface them at build time.
|
|
|
|
would surface them at build time.
|
|
|
|
- [x] **L169 — `noFallthroughCasesInSwitch` is off.** The large `setSettings` switch and `applyEvent`
|
|
|
|
- [x] **L169 — `noFallthroughCasesInSwitch` is off.** The large `setSettings` switch and `applyEvent`
|
|
|
|
switch aren't fallthrough-guarded; a missing `break`/`return` wouldn't be caught.
|
|
|
|
switch aren't fallthrough-guarded; a missing `break`/`return` wouldn't be caught.
|
|
|
|
- [ ] **L170 — No source maps in production.** `sourceMap: false` (toolkit) and no `build.sourcemap` in
|
|
|
|
- [x] **L170 — No source maps in production.** `sourceMap: false` (toolkit) and no `build.sourcemap` in
|
|
|
|
`electron.vite.config.ts`, so field crash stack traces are unmapped — combined with no logging (CC8/M29),
|
|
|
|
`electron.vite.config.ts`, so field crash stack traces are unmapped — combined with no logging (CC8/M29),
|
|
|
|
diagnosing a user-reported crash is very hard. Consider hidden/external source maps.
|
|
|
|
diagnosing a user-reported crash is very hard. Consider hidden/external source maps. *Fixed:
|
|
|
|
|
|
|
|
`build.sourcemap: 'hidden'` on all three bundles (main / preload / renderer) in
|
|
|
|
|
|
|
|
[electron.vite.config.ts](electron.vite.config.ts) — `.map` files are emitted for offline symbolication
|
|
|
|
|
|
|
|
of the raw stacks the logger now records (CC8), with no `sourceMappingURL` reference in the shipped bundle.*
|
|
|
|
- [ ] **L171 — `main.tsx` mock drift.** The preview mock's version strings disagree
|
|
|
|
- [ ] **L171 — `main.tsx` mock drift.** The preview mock's version strings disagree
|
|
|
|
(`getAppVersion`→'0.4.0-preview' vs `checkForAppUpdate.currentVersion`→'0.4.0'), and its `setSettings`
|
|
|
|
(`getAppVersion`→'0.4.0-preview' vs `checkForAppUpdate.currentVersion`→'0.4.0'), and its `setSettings`
|
|
|
|
skips the real validation/sanitization — so the browser preview can accept settings the app rejects,
|
|
|
|
skips the real validation/sanitization — so the browser preview can accept settings the app rejects,
|
|
|
|
masking M34-class issues during design.
|
|
|
|
masking M34-class issues during design.
|
|
|
|
- [ ] **L172 — `skipLibCheck: true`** (toolkit) hides type errors in dependency `.d.ts` files — standard
|
|
|
|
- [x] **L172 — `skipLibCheck: true`** (toolkit) hides type errors in dependency `.d.ts` files — standard
|
|
|
|
practice, low risk, noted for awareness (a dep type regression won't fail typecheck).
|
|
|
|
practice, low risk, noted for awareness (a dep type regression won't fail typecheck). *Acknowledged —
|
|
|
|
|
|
|
|
kept as-is (it IS standard practice and speeds typecheck); recorded so the trade-off (a dependency
|
|
|
|
|
|
|
|
`.d.ts` regression can pass typecheck silently) is a known, accepted decision rather than an oversight.*
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|