- L138: gate the clipboard check on the settings `loaded` flag (not just
clipboardWatch), so it never reads using the pre-load fallback; the effect
re-runs when loaded flips true so a pre-launch copied link is still offered.
- L145: lastSeen is module-level, so a dismissed clipboard link stays suppressed
across the hook's remount on a Downloads<->Library tab switch.
- M14: SettingsView renders each card in a React-owned wrapper and drives its
visibility from a `hidden` state array — the search no longer writes display:none
onto a card's own DOM node. Matching still reads textContent (full label coverage,
no per-card keyword upkeep).
typecheck + 268 tests + eslint + prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
firstUrl() in DownloadBar was a local duplicate of looksLikeUrl iteration
(scanning multi-line text for the first valid URL). Promoted to
firstUrlInText() in src/renderer/src/lib/urlHelpers.ts alongside the other
URL utilities, re-exported from useClipboardLink.ts. DownloadBar now imports
firstUrlInText from there instead of maintaining a local copy.
typecheck + 242 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
H5: HistoryEntry now stores formatId+formatHasAudio; redownload passes them
back to addFromUrl so the original yt-dlp format is reused, not guessed.
Compound quality labels stripped to the preset prefix for old history.
L72: thumbnail now passed in redownload so re-queued rows keep their art.
H6: TerminalView log capped at MAX_LOG_LINES=2000 to prevent unbounded growth.
M21: --audio-quality omitted for lossless audioFormats (flac/wav).
L166: fmtEta hour rollover fixed in all 3 locations -- 2h00:00 not 120:00.
L165: queueStats formatSpeed precision aligned with fmtBytes.
L55: QueueItem suppresses sizeLabel when already in probed-format quality label.
L47: probeMeta null sends empty meta event so Resolving clears via SR6.
L68: notifiedBackground resets on window show for subsequent close-while-downloading.
L76: dup warning falls back to URL when title is still a placeholder.
L91: SettingsView onFormatSelect uses indexOf instead of unsafe tuple cast.
L98: Embed chapters field gets a hint text.
L108: BrowserWindow created with explicit title AeroFetch.
L37: pure formatters extracted to src/main/lib/formatters.ts and unit-tested.
L38: buildArgs test covers webm thumbnail suppression.
L39: CROP_SQUARE_PPA test is structural not exact-string.
L40: looksLikeUrl/looksLikeSingleVideo extracted to src/renderer/src/lib/urlHelpers.ts.
typecheck + 242 tests + eslint + prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Encrypt proxy / youtubePoToken / updateToken on disk via safeStorage (DPAPI on Windows), with a one-time launch migration for legacy plaintext. Decrypted before reaching callers/renderer; backup export still writes clear, as documented. Harden updater token handling to refuse cross-origin redirects on the authenticated REST check.
Extract the clipboard-link logic from DownloadBar into the shared useClipboardLink hook: add an optional filter (library skips single-video links), an offer() for external aerofetch:// / .url links, and a source field driving banner wording. Add looksLikeSingleVideo plus its unit tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closing the window no longer kills in-progress downloads, the library gains the
same copied-link suggestion the downloads tab has, and the in-app updater can now
authenticate to a sign-in-required Gitea.
Background / tray:
- The window's close handler now hides to the tray (instead of quitting) whenever
a download is in flight, even if "Keep running in the tray" is off — quitting
was killing the spawned yt-dlp processes. A one-time notification explains the
app is still running. (download.ts exposes hasActiveDownloads().)
- tray.ts now falls back to an embedded icon when no build/icon.ico ships, so the
tray actually appears — previously an empty icon meant the tray was skipped and
a minimized window could be stranded with no way back.
- New "Start with Windows" setting (launchAtStartup) wired to
app.setLoginItemSettings, synced at startup and on toggle. Useful with
auto-download so watched channels stay current in the background.
- The "Keep running in the tray" hint now explains it also enables background
auto-download of new uploads.
Library clipboard detection:
- Extracted the downloads tab's clipboard watcher into a shared useClipboardLink
hook and used it in the library's add-source field, so a copied channel/playlist
link is offered there too.
Updater fix (works on a private / sign-in-required instance):
- Added an optional updateToken setting. When set, the updater sends it as a Gitea
Authorization header on the release check, the checksum fetch, and the installer
download — so "Check for updates" works where anonymous access is blocked (the
previous "could not reach the update server" case). Blank = anonymous, unchanged.
No token is ever shipped; it's only ever sent to the host-pinned update host.
Settings gains a masked "Update access token" field.
Typecheck clean; 187 tests pass; electron-vite build clean. New UI verified in the
browser preview (tray/startup toggles, token field, library copied-link banner).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>