Complete Phase E theming, onboarding, and Windows share integration

Adds theme presets (Toffee/Slate/Evergreen/Lavender) with a "follow system"
mode and high-contrast awareness, a first-run welcome screen, and the
Windows analog of Android's share sheet for a Win32 app: an aerofetch://
protocol handler plus an Explorer "Send to AeroFetch" entry, both routed
through a single-instance lock so a second launch hands its link to the
already-running window instead of opening a duplicate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 08:45:09 -04:00
parent a822a2bb52
commit fa78b13cac
17 changed files with 883 additions and 77 deletions
+11 -2
View File
@@ -7,6 +7,7 @@ import {
VIDEO_CODECS,
SPONSORBLOCK_CATEGORIES,
COOKIE_BROWSERS,
ACCENT_COLORS,
DEFAULT_DOWNLOAD_OPTIONS,
type Settings,
type DownloadOptions,
@@ -21,6 +22,7 @@ const DEFAULTS: Settings = {
maxConcurrent: 2,
filenameTemplate: '%(title)s.%(ext)s',
theme: 'light',
accentColor: 'toffee',
clipboardWatch: true,
downloadOptions: DEFAULT_DOWNLOAD_OPTIONS,
proxy: '',
@@ -32,7 +34,8 @@ const DEFAULTS: Settings = {
downloadArchive: false,
customCommandEnabled: false,
defaultTemplateId: null,
notifyOnComplete: true
notifyOnComplete: true,
hasCompletedOnboarding: false
}
/** Fixed path for the --download-archive file; not user-configurable. */
@@ -104,7 +107,12 @@ export function setSettings(partial: Partial<Settings>): Settings {
if (value === undefined) continue
switch (key) {
case 'theme':
if (value === 'light' || value === 'dark') s.set('theme', value)
if (value === 'light' || value === 'dark' || value === 'system') s.set('theme', value)
break
case 'accentColor':
if ((ACCENT_COLORS as readonly string[]).includes(value as string)) {
s.set('accentColor', value as Settings['accentColor'])
}
break
case 'defaultKind':
if (value === 'video' || value === 'audio') s.set('defaultKind', value)
@@ -120,6 +128,7 @@ export function setSettings(partial: Partial<Settings>): Settings {
case 'downloadArchive':
case 'customCommandEnabled':
case 'notifyOnComplete':
case 'hasCompletedOnboarding':
if (typeof value === 'boolean') s.set(key, value)
break
case 'defaultTemplateId':