diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md index ec70dba..c0f4129 100644 --- a/CODE-AUDIT.md +++ b/CODE-AUDIT.md @@ -501,7 +501,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n - [ ] **L64 — `ARIA2C_ARGS` connection params hardcoded** (`-x16 -s16 -k1M`), no user control. - [ ] **L65 — Pause uses `taskkill /F`** like cancel — a forced kill risks an unflushed `.part` tail vs a graceful stop. - [x] **L66 — Sidebar caption flips** "yt-dlp frontend" → "v" once the version loads (text shift on boot). -- [x] **L67 — Onboarding has no Skip and can't be revisited** (no "show tips again"). +- [ ] **L67 — Onboarding has no Skip and can't be revisited** (no "show tips again"). - [x] **L68 — Background-running notification fires once per process** (`notifiedBackground` never resets) — won't remind on later window closes. - [ ] **L69 — `settings.ts` mixes path helpers with persistence** (`getDefaultMediaDir`/ @@ -1042,7 +1042,7 @@ DPI handled by Chromium). The deviations: - [x] **W17 — No `aria-live` for status changes.** Narrator doesn't announce download progress, completion, or errors — there are no live regions. **Standard:** polite live regions for queue/status updates. -- [x] **W18 — Custom colors unverified under High Contrast.** The status pills, segmented controls, accent +- [ ] **W18 — Custom colors unverified under High Contrast.** The status pills, segmented controls, accent swatches, and thumbnail tints use explicit background colors that Windows forced-colors mode may not adapt (nothing sets `forced-color-adjust`). **Standard:** test under each Contrast theme; let system colors win. - [ ] **(ref) No semantic headings / radiogroup arrow-nav / focus rings** — UI33, UI30, UI28–29 (all bear on Narrator + keyboard users). diff --git a/src/main/binaries.ts b/src/main/binaries.ts index 81be1fb..8b3cde5 100644 --- a/src/main/binaries.ts +++ b/src/main/binaries.ts @@ -80,7 +80,7 @@ export function getFfprobePath(): string { return join(getBinDir(), 'ffprobe.exe') } -/** User-facing error shown whenever yt-dlp.exe is not found at startup. */ +/** User-facing error shown by download/probe/index/update when yt-dlp.exe is missing. */ export const YTDLP_MISSING_MSG = 'yt-dlp.exe is missing. Open Settings → Software update to re-download it.' diff --git a/src/main/updater.ts b/src/main/updater.ts index 79d1bb2..4e54665 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -1,5 +1,4 @@ -import { app, net, type WebContents } from 'electron' -import { spawn } from 'child_process' +import { app, net, shell, type WebContents } from 'electron' import { createWriteStream, type WriteStream } from 'fs' import { stat, unlink } from 'fs/promises' import { join, normalize, dirname } from 'path' @@ -426,12 +425,14 @@ export async function runAppUpdate(filePath: string): Promise<{ ok: boolean; err return { ok: false, error: 'Refused to run an unexpected file.' } } await stat(target) // throws if the file is missing - // Spawn the installer detached + unref'd so it outlives our process even on a - // slow machine. shell.openPath goes through ShellExecute but gives us no child - // PID to wait on; spawning directly lets us call unref() before we quit. - const child = spawn(target, [], { detached: true, stdio: 'ignore', shell: false }) - child.unref() - // Let the IPC response reach the renderer before we exit. + // Hand the installer to the OS via ShellExecute, which (unlike a raw + // CreateProcess/spawn) honors the NSIS elevation manifest — needed if the + // build ever flips to perMachine. openPath resolves once the launch is + // initiated, so the installer process already exists when we quit. + const err = await shell.openPath(target) + if (err) return { ok: false, error: err } + // Quit on the next tick so this IPC response reaches the renderer first; the + // launched installer is independent of our process and survives the quit. setImmediate(() => app.quit()) return { ok: true } } catch (e) { diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index c3fbc19..ecc44b1 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo, useRef } from 'react' +import { useState, useEffect, useMemo, useRef } from 'react' import { FluentProvider, makeStyles, tokens } from '@fluentui/react-components' import { Sidebar, type TabValue } from './components/Sidebar' import { DownloadsView } from './components/DownloadsView' diff --git a/src/renderer/src/assets/base.css b/src/renderer/src/assets/base.css index 89b5870..c0ee042 100644 --- a/src/renderer/src/assets/base.css +++ b/src/renderer/src/assets/base.css @@ -2,17 +2,6 @@ *::before, *::after { box-sizing: border-box; - /* Let Windows High Contrast mode override all colors by default (W18). */ - forced-color-adjust: auto; -} - -/* CommandPalette backdrop uses a hardcoded rgba — replace it with the standard HC - overlay so the backdrop remains perceptible under forced-colors. */ -@media (forced-colors: active) { - [class*="backdrop"] { - background-color: Canvas; - border: 2px solid ButtonText; - } } :focus-visible { diff --git a/src/renderer/src/components/CommandPalette.tsx b/src/renderer/src/components/CommandPalette.tsx index ba0e525..e844b8e 100644 --- a/src/renderer/src/components/CommandPalette.tsx +++ b/src/renderer/src/components/CommandPalette.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { makeStyles, mergeClasses, tokens, shorthands } from '@fluentui/react-components' import { useFocusStyles } from './ui/focusRing' diff --git a/src/renderer/src/components/DownloadBar.tsx b/src/renderer/src/components/DownloadBar.tsx index 022c702..5a0e0be 100644 --- a/src/renderer/src/components/DownloadBar.tsx +++ b/src/renderer/src/components/DownloadBar.tsx @@ -27,7 +27,12 @@ import { CalendarClockRegular, WarningRegular } from '@fluentui/react-icons' -import type { MediaInfo, FormatOption, PlaylistInfo } from '@shared/ipc' +import { + parseUrlShortcutContent, + type MediaInfo, + type FormatOption, + type PlaylistInfo +} from '@shared/ipc' import { useDownloads, type MediaKind } from '../store/downloads' import { QUALITY_OPTIONS } from '../qualityOptions' import { sameVideo } from '../store/queueStats' @@ -36,12 +41,10 @@ import { Select } from './Select' import { Hint } from './Hint' import { SegmentedControl } from './ui/SegmentedControl' import { IconButton } from './ui/IconButton' -import { parseUrlShortcutContent } from '@shared/ipc' import { useClipboardLink, looksLikeUrl, firstUrlInText } from '../useClipboardLink' import { logError } from '../reportError' import { THUMB_LG } from '../thumbSizes' - /** Pull the URL= target out of a dropped Windows .url Internet Shortcut file. */ function parseUrlFile(content: string): string | null { const url = parseUrlShortcutContent(content) diff --git a/src/renderer/src/components/LibraryView.tsx b/src/renderer/src/components/LibraryView.tsx index f770d59..23b8a80 100644 --- a/src/renderer/src/components/LibraryView.tsx +++ b/src/renderer/src/components/LibraryView.tsx @@ -127,6 +127,9 @@ const useStyles = makeStyles({ width: '100%', border: 'none', backgroundColor: 'transparent', + // A native - - + ) diff --git a/src/renderer/src/components/Select.tsx b/src/renderer/src/components/Select.tsx index 7b29c2a..6b175b1 100644 --- a/src/renderer/src/components/Select.tsx +++ b/src/renderer/src/components/Select.tsx @@ -1,4 +1,4 @@ -import { makeStyles, mergeClasses, tokens, shorthands } from '@fluentui/react-components' +import { makeStyles, mergeClasses, tokens, shorthands } from '@fluentui/react-components' // A native