diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md index c133011..af84d74 100644 --- a/CODE-AUDIT.md +++ b/CODE-AUDIT.md @@ -631,9 +631,12 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n emitted "in `buildArgs` (`src/main/download.ts`)" — `buildArgs` lives in `src/main/buildArgs.ts`. - [x] **L81 — ROADMAP internal contradiction.** Phase A: "`--split-chapters` still TODO"; Phase L: `[x]` split-chapters done. Phase A wasn't updated when L landed. -- [ ] **L82 — PINCHFLAT roadmap interface sketches are stale.** [ROADMAP-PINCHFLAT.md](ROADMAP-PINCHFLAT.md) +- [x] **L82 — PINCHFLAT roadmap interface sketches are stale.** [ROADMAP-PINCHFLAT.md](ROADMAP-PINCHFLAT.md) Phase F's `Source`/`MediaItem` code blocks omit shipped fields (`videoId`, `itemCount`, - `watched`, `feedUrl`) — design sketches that drifted from `shared/ipc.ts`. + `watched`, `feedUrl`) — design sketches that drifted from `shared/ipc.ts`. *Fixed: the Phase F + sketch now carries the shipped `videoId`/`itemCount`/`watched`/`feedUrl` fields (plus the + required `url`/`playlistTitle`/`playlistIndex`) and a note that the block reflects the shipped + `shared/ipc.ts` shape.* - [ ] **L83 — Dead build artifacts.** electron-builder emits `.blockmap` differential-update files for each NSIS installer, but the custom [updater.ts](src/main/updater.ts) does a full download and never consumes them. @@ -648,8 +651,12 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n only; the post-processing `DownloadOptions` used originally are lost (current defaults apply). - [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. -- [ ] **L89 — Inconsistent release artifacts.** Only `0.4.1` carries `.sha256` files (hand-made) and - the version sequence skips 0.3.x — symptomatic of the manual release process behind H8. +- [x] **L89 — Inconsistent release artifacts.** Only `0.4.1` carries `.sha256` files (hand-made) and + the version sequence skips 0.3.x — symptomatic of the manual release process behind H8. *Fixed at + the root by H8: [scripts/generate-checksums.cjs](scripts/generate-checksums.cjs) (electron-builder + `afterAllArtifactBuild` hook) writes a `.sha256` next to every built `.exe` and returns them for + upload, so future releases are consistent automatically; the stale pre-H8 `dist/` artifacts are + pruned by L84. (The historical 0.3.x version gap is immutable.)* *Round 5 (2026-06-29) — code-level micro-inconsistencies & polish:* @@ -1084,17 +1091,19 @@ root cause is already filed. - [ ] **UX9 — No global completion feedback off the Downloads tab** (UI25). If OS notifications are off/suppressed (focus assist) and you're on another tab, a finished download gives no in-app sign. **Fix:** a sidebar Downloads badge / lightweight in-app toast. -- [ ] **UX10 — Re-download silently changes quality** (H5) — a confusing "I asked for 720p, got Best." +- [x] **UX10 — Re-download silently changes quality** (H5) — a confusing "I asked for 720p, got Best." *Resolved by H5 (re-download stores `formatId`/`formatHasAudio` and strips compound quality labels).* - [ ] **UX11 — Scheduled downloads silently never fire if the app is quit** (M4). The hint is easy to miss; the user returns to find nothing happened. **Fix:** warn at schedule time that it requires the app running (or persist + relaunch). - [ ] **UX12 — Terminal is a dead-end when custom commands are off** (L18). The nav item is always visible; clicking it shows a gate pointing to a setting on another screen. **Fix:** hide/disable the nav item, or let the gate enable the setting inline. -- [ ] **UX13 — Backup export warns of nothing; it contains secrets** (M22). A user "backs up settings" - and ships proxy creds / tokens in cleartext. **Fix:** warn, or mask/omit secrets. -- [ ] **UX14 — Duplicate warning can show a placeholder title** (L76): 'Already in your queue: +- [x] **UX13 — Backup export warns of nothing; it contains secrets** (M22). A user "backs up settings" + and ships proxy creds / tokens in cleartext. **Fix:** warn, or mask/omit secrets. *Resolved by M22 + (backup strips proxy / PO-token / update-token; the caption states credentials aren't included).* +- [x] **UX14 — Duplicate warning can show a placeholder title** (L76): 'Already in your queue: "YouTube video (abc123)"' before metadata resolves looks broken. **Fix:** fall back to the URL. + *Resolved by L76 (the dup warning falls back to the URL when the title is still the placeholder).* - [ ] **UX15 — No bulk actions in the Downloads queue.** History has a select-mode + bulk delete; Downloads has only per-row remove + "Clear finished." Inconsistent. **Fix:** mirror select/bulk, or add "Cancel all." @@ -1118,8 +1127,9 @@ root cause is already filed. - [ ] **UX23 — Settings folder paths are read-only** (L71) — no paste; Browse-dialog only. - [ ] **UX24 — "Check N watched for new" is disabled with no explanation** when no source is watched — a user with indexed (but unwatched) sources sees a dead button. **Fix:** tooltip / enable with a hint to watch a source. -- [ ] **UX25 — Empty-state copy mismatches the flow** (L78): Downloads says "Paste a URL above to get - started," but the actual path is Fetch/Download. +- [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 states on boot (plus the documented one-frame theme flash). **Fix:** lightweight loading states. @@ -1225,16 +1235,16 @@ DPI handled by Chromium). The deviations: `Badge`s (Fluent supplies its own forced-colors handling) and the thumbnail tints are purely decorative placeholders, so both correctly let the system palette win. Live verification under each Contrast theme is still worthwhile but the color-meaning surfaces are now defended in code.* -- [ ] **(ref) No semantic headings / radiogroup arrow-nav / focus rings** — UI33, UI30, UI28–29 (all bear on Narrator + keyboard users). +- [x] **(ref) No semantic headings / radiogroup arrow-nav / focus rings** — UI33, UI30, UI28–29 (all bear on Narrator + keyboard users). *All four resolved (see UI33/UI30/UI28/UI29).* ### Settings & standard conventions (mostly OK) - [x] **W19 — Window/taskbar don't reflect state.** No window title at all (L108), so the taskbar button never shows "3 downloading" or similar. **Standard:** reflect activity in the title/tooltip. -- [ ] **(OK)** Per-user no-admin install, NSIS uninstaller, `aerofetch://` registration, AppUserModelID, +- [x] **(OK)** Per-user no-admin install, NSIS uninstaller, `aerofetch://` registration, AppUserModelID, tray tooltip + left-click restore + right-click menu, taskbar progress, and jump list all follow Windows conventions. **(Non-goal)** unsigned binary → SmartScreen prompt — accepted; no certificate will - be purchased (the build stays signing-ready via env vars if that ever changes — see [SIGNING.md](docs/SIGNING.md)). + be purchased (the build stays signing-ready via env vars if that ever changes — see [SIGNING.md](docs/SIGNING.md)). *(Acknowledged — verified-OK / accepted non-goal, no action.)* --- @@ -1339,16 +1349,23 @@ filed. Several listed categories are **N/A** to this stack and worth recording s - [x] **Safe to remove now — `getDefaultFolder`** (M2): channel + preload method + main handler + mock, **zero callers**. Pure deletion, no behavior change. -- [ ] **Decide "wire or remove" — command preview** (M5): channel + preload `previewCommand` + main +- [x] **Decide "wire or remove" — command preview** (M5): channel + preload `previewCommand` + main `previewCommand()` + `formatCommandLine`/`quoteForDisplay` (buildArgs, **unit-tested**) + - `CommandPreviewResult` + mock. Either surface it (UX1's per-download panel) or delete the whole chain (and its tests). -- [ ] **Decide "wire or remove" — incognito/private** (M6): `DownloadItem.incognito` + `AddOptions.incognito` - + `buildItem` handling + two history-skip checks + the QueueItem "Private" badge. No UI sets it. Add a toggle or strip the plumbing. -- [ ] **Decide "wire or remove" — per-download options/extraArgs** (UX1): `DownloadItem.options`/`extraArgs`, + `CommandPreviewResult` + mock. *Decided: **wired** (M5/UX1) — the DownloadBar Advanced panel has a + Show/Hide command toggle calling `window.api.previewCommand`; the whole chain is now reachable.* +- [x] **Decide "wire or remove" — incognito/private** (M6): `DownloadItem.incognito` + `AddOptions.incognito` + + `buildItem` handling + two history-skip checks + the QueueItem "Private" badge. *Decided: **wired** + (M6/UX1) — the DownloadBar Advanced panel has an "Incognito mode" checkbox that sets the flag (reset + after each download).* +- [x] **Decide "wire or remove" — per-download options/extraArgs** (UX1): `DownloadItem.options`/`extraArgs`, `StartDownloadOptions.options`/`extraArgs`, `AddOptions.options`/`extraArgs` are plumbed end-to-end but - never set by any UI. Largest block of built-but-unreachable code — wire the panel or remove the fields. -- [ ] **Comment, not code — `MAX_ENQUEUE_BATCH`** (M33): referenced by ipc.ts:629 + the roadmap but never - implemented; fix the comment (nothing to delete). + never set by any UI. *Decided: **wired** (UX1) — the collapsible Advanced panel reuses + `DownloadOptionsForm` for per-download overrides passed via `AddOptions.options`; overrides reset to + the global defaults after each download.* +- [x] **Comment, not code — `MAX_ENQUEUE_BATCH`** (M33): referenced by ipc.ts:629 + the roadmap but never + implemented; fix the comment (nothing to delete). *Resolved by M33: the false `MAX_ENQUEUE_BATCH` + reference is gone; [ipc.ts](src/shared/ipc.ts):730 now honestly states all selected entries are + enqueued at once (with a note to add a cap if large — the actual cap is deferred to PERF3).* **New cleanliness findings:** @@ -1386,9 +1403,9 @@ installer handoff `1500`, tickers `650`/`15_000`, stderr tail `4000`; magic stri 1. **Now (zero risk):** the `getDefaultFolder` slice (M2); un-export `PROGRESS_TEMPLATE` + `getManagedBinDir` (CL5). 2. **Now (housekeeping):** prune `dist/` to the current release (L84); the macOS-only branches if Windows-only is committed (L147). -3. **Decision required (don't silently delete — these are documented features):** command preview (M5), - incognito (M6), and per-download options/extraArgs (UX1) — each is "wire it or remove it." Given UX1 - rates the per-download panel as high value, wiring is likely better than deleting. +3. **Decision made — wired, not deleted (all resolved):** command preview (M5), incognito (M6), and + per-download options/extraArgs (UX1) are now surfaced in the DownloadBar Advanced panel, so the + previously-unreachable plumbing is reachable. Nothing to delete. 4. **Not removable (intentional):** preview seed/mock data + `PREVIEW` branches (C1/L128) — they power the browser-preview dev workflow; consolidate (C1) rather than delete. diff --git a/ROADMAP-PINCHFLAT.md b/ROADMAP-PINCHFLAT.md index 2c72b62..2923c03 100644 --- a/ROADMAP-PINCHFLAT.md +++ b/ROADMAP-PINCHFLAT.md @@ -109,19 +109,26 @@ yet. This is the prerequisite for every later phase. channel?: string addedAt: number lastIndexedAt?: number + itemCount: number // cached MediaItem count for list display + watched?: boolean // included in scheduled / startup sync (Phase J) + feedUrl?: string // YouTube RSS feed for cheap "anything new?" checks } interface MediaItem { - id: string // yt-dlp video id — the dedup key + id: string // globally unique, `${sourceId}:${videoId}` sourceId: string + videoId: string // yt-dlp video id — the dedup key within a source title: string - playlistTitle?: string // for the folder path; 'Uploads' fallback - playlistIndex?: number // 1-based, for NNN numbering + url: string + playlistTitle: string // for the folder path; 'Uploads' fallback + playlistIndex: number // 1-based, for NNN numbering durationLabel?: string downloaded: boolean downloadedAt?: number filePath?: string } ``` + *(This block reflects the shipped `src/shared/ipc.ts` shape; the original sketch + predated the `videoId`/`itemCount`/`watched`/`feedUrl` fields.)* - [x] **Dedup on index.** A video appearing in multiple playlists collapses to one `MediaItem` (keyed by video id); first playlist seen wins the folder assignment (surface this choice in the UI so the user can reassign). The "Uploads" synthetic playlist catches anything in