feat(audit): Batch 14 — perf (code-split + seed DCE) + first-run default

- PERF8: code-split the non-default tabs. Library/History/Terminal/Settings are
  React.lazy chunks behind a Suspense; Downloads (the launch tab) stays eager. The
  production build now emits separate SettingsView (~90kB), LibraryView (~29kB),
  HistoryView (~16kB), TerminalView (~8kB) chunks — ~140kB out of the initial
  bundle. The stores these views use are imported eagerly elsewhere, so lazy-
  loading the views never delays store startup/persistence (per the C2 note).
- L128: gate the three preview seed arrays (downloadSeed / sources seeds / history
  seed) on `import.meta.env.DEV && PREVIEW`. In the packaged build DEV is
  statically false, so Rollup constant-folds and dead-code-eliminates the seed
  literals; the browser dev preview (DEV true) still gets them.
- SR4: clipboardWatch now defaults false for new installs (privacy — no clipboard
  read on focus until opted in). Existing installs keep their saved value
  (electron-store fills only missing keys); the onboarding tip points to the
  setting. NOTE: this flips a signature convenience off-by-default for new users —
  flagged for the maintainer, trivially reversible.

Deferred with rationale (CODE-AUDIT.md): PERF3 (core-queue micro-opt on a bounded
list, risky unattended), PERF6/L162 (bounded by virtualization + conflicts with
PERF5's hoisted renderItem), SR5 (MP3 = deliberate compatibility default; the label
bug was M18), W12 (fallback-of-a-fallback icon), W15 (GPU software-rendering
workaround — external driver dependency), L104 (History virtualization needs the
L161 layout rework + live verification; bounded by the 500-cap).

Verified: typecheck (node+web) + 279 tests + eslint + production build green
(code-split chunks confirmed in output); touched files prettier-clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 17:35:40 -04:00
parent 71f0b2b68f
commit 0f784847a3
7 changed files with 236 additions and 148 deletions
+25 -9
View File
@@ -1,12 +1,26 @@
import { useState, useEffect, useMemo, useRef } from 'react'
import { useState, useEffect, useMemo, useRef, lazy, Suspense } from 'react'
import { FluentProvider, makeStyles, tokens } from '@fluentui/react-components'
import { Sidebar } from './components/Sidebar'
import { DownloadsView } from './components/DownloadsView'
import { LibraryView } from './components/LibraryView'
import { HistoryView } from './components/HistoryView'
import { TerminalView } from './components/TerminalView'
import { SettingsView } from './components/SettingsView'
import { Onboarding } from './components/Onboarding'
// Code-split the non-default tabs (PERF8): Downloads is the launch tab and stays
// eager, but Library/History/Terminal/Settings load their chunks on first visit so
// they (and their slice of Fluent) aren't in the initial bundle. The stores these
// views use are imported eagerly elsewhere (App's `useDownloads` + `./store/sources`,
// and downloads → history), so lazy-loading the *view* never delays store startup.
const LibraryView = lazy(() =>
import('./components/LibraryView').then((m) => ({ default: m.LibraryView }))
)
const HistoryView = lazy(() =>
import('./components/HistoryView').then((m) => ({ default: m.HistoryView }))
)
const TerminalView = lazy(() =>
import('./components/TerminalView').then((m) => ({ default: m.TerminalView }))
)
const SettingsView = lazy(() =>
import('./components/SettingsView').then((m) => ({ default: m.SettingsView }))
)
import { CommandPalette, type PaletteAction } from './components/CommandPalette'
import { LiveRegion } from './components/LiveRegion'
import { Toaster } from './components/ui/Toaster'
@@ -238,10 +252,12 @@ function App(): React.JSX.Element {
<main className={styles.content}>
{tab === 'downloads' && <DownloadsView />}
{tab === 'library' && <LibraryView />}
{tab === 'history' && <HistoryView />}
{tab === 'terminal' && <TerminalView />}
{tab === 'settings' && <SettingsView />}
<Suspense fallback={null}>
{tab === 'library' && <LibraryView />}
{tab === 'history' && <HistoryView />}
{tab === 'terminal' && <TerminalView />}
{tab === 'settings' && <SettingsView />}
</Suspense>
</main>
{paletteOpen && (
+1 -1
View File
@@ -120,7 +120,7 @@ const useStyles = makeStyles({
const TIPS: { icon: React.JSX.Element; text: string }[] = [
{
icon: <ClipboardPasteRegular />,
text: 'Copy a video link and AeroFetch offers to fetch it as soon as you switch back.'
text: 'Turn on clipboard detection in Settings and AeroFetch will offer to fetch links you copy.'
},
{
icon: <HistoryRegular />,
+57 -54
View File
@@ -3,57 +3,60 @@ import { newId } from '../id'
import { type DownloadItem } from './downloadTypes'
// Mock seed data so every visual state is visible in the UI preview. Empty in a
// real run -- the queue starts blank.
export const seed: DownloadItem[] = PREVIEW
? [
{
id: newId('item'),
url: 'https://youtube.com/watch?v=dQw4w9WgXcQ',
title: 'Building a Desktop App with Electron -- Full Course',
channel: 'DevChannel',
durationLabel: '1:42:08',
kind: 'video',
quality: '1080p',
status: 'downloading',
progress: 0.42,
speedBytesPerSec: 4.7 * 1024 * 1024,
etaSeconds: 72,
sizeLabel: '512 MB'
},
{
id: newId('item'),
url: 'https://youtube.com/watch?v=abcd1234',
title: 'Lo-fi beats to code to (1 hour mix)',
channel: 'ChillStudio',
durationLabel: '1:00:21',
kind: 'audio',
quality: 'Best',
status: 'queued',
progress: 0
},
{
id: newId('item'),
url: 'https://youtube.com/watch?v=zzz9999',
title: 'How yt-dlp Works Under the Hood',
channel: 'Open Source Weekly',
durationLabel: '14:03',
kind: 'video',
quality: '720p',
status: 'completed',
progress: 1,
sizeLabel: '184 MB',
filePath: 'C:\\Users\\you\\Downloads\\How yt-dlp Works Under the Hood.mp4'
},
{
id: newId('item'),
url: 'https://youtube.com/watch?v=err0r',
title: 'Private video',
channel: undefined,
kind: 'video',
quality: 'Best available',
status: 'error',
progress: 0,
error: 'Video unavailable: this video is private.'
}
]
: []
// real run -- the queue starts blank. Gated on `import.meta.env.DEV` as well as the
// runtime PREVIEW flag so Rollup dead-code-eliminates this whole array from the
// packaged (production) renderer build, where DEV is statically false (L128).
export const seed: DownloadItem[] =
import.meta.env.DEV && PREVIEW
? [
{
id: newId('item'),
url: 'https://youtube.com/watch?v=dQw4w9WgXcQ',
title: 'Building a Desktop App with Electron -- Full Course',
channel: 'DevChannel',
durationLabel: '1:42:08',
kind: 'video',
quality: '1080p',
status: 'downloading',
progress: 0.42,
speedBytesPerSec: 4.7 * 1024 * 1024,
etaSeconds: 72,
sizeLabel: '512 MB'
},
{
id: newId('item'),
url: 'https://youtube.com/watch?v=abcd1234',
title: 'Lo-fi beats to code to (1 hour mix)',
channel: 'ChillStudio',
durationLabel: '1:00:21',
kind: 'audio',
quality: 'Best',
status: 'queued',
progress: 0
},
{
id: newId('item'),
url: 'https://youtube.com/watch?v=zzz9999',
title: 'How yt-dlp Works Under the Hood',
channel: 'Open Source Weekly',
durationLabel: '14:03',
kind: 'video',
quality: '720p',
status: 'completed',
progress: 1,
sizeLabel: '184 MB',
filePath: 'C:\\Users\\you\\Downloads\\How yt-dlp Works Under the Hood.mp4'
},
{
id: newId('item'),
url: 'https://youtube.com/watch?v=err0r',
title: 'Private video',
channel: undefined,
kind: 'video',
quality: 'Best available',
status: 'error',
progress: 0,
error: 'Video unavailable: this video is private.'
}
]
: []
+40 -38
View File
@@ -4,44 +4,46 @@ import { isPreview as PREVIEW } from '../isPreview'
import { logError } from '../reportError'
import { revealFile } from '../reveal'
// Mock history so the History tab has content during UI design.
const seed: HistoryEntry[] = PREVIEW
? [
{
id: 'h1',
title: 'How yt-dlp Works Under the Hood',
channel: 'Open Source Weekly',
url: 'https://youtube.com/watch?v=zzz9999',
kind: 'video',
quality: '720p · mp4 · 184 MB',
sizeLabel: '184 MB',
filePath: 'C:\\Users\\you\\Downloads\\How yt-dlp Works Under the Hood.mp4',
completedAt: Date.now() - 1000 * 60 * 42
},
{
id: 'h2',
title: 'Deep Focus — Ambient Mix',
channel: 'ChillStudio',
url: 'https://youtube.com/watch?v=aaa1111',
kind: 'audio',
quality: 'Best',
sizeLabel: '142 MB',
filePath: 'C:\\Users\\you\\Downloads\\Deep Focus — Ambient Mix.mp3',
completedAt: Date.now() - 1000 * 60 * 60 * 26
},
{
id: 'h3',
title: 'Conference Keynote 2026 (Full)',
channel: 'TechConf',
url: 'https://youtube.com/watch?v=bbb2222',
kind: 'video',
quality: '1080p · mp4 · 1.2 GB',
sizeLabel: '1.2 GB',
filePath: 'C:\\Users\\you\\Downloads\\Conference Keynote 2026 (Full).mp4',
completedAt: Date.now() - 1000 * 60 * 60 * 24 * 3
}
]
: []
// Mock history so the History tab has content during UI design. Gated on
// `import.meta.env.DEV` too so Rollup drops it from the production build (L128).
const seed: HistoryEntry[] =
import.meta.env.DEV && PREVIEW
? [
{
id: 'h1',
title: 'How yt-dlp Works Under the Hood',
channel: 'Open Source Weekly',
url: 'https://youtube.com/watch?v=zzz9999',
kind: 'video',
quality: '720p · mp4 · 184 MB',
sizeLabel: '184 MB',
filePath: 'C:\\Users\\you\\Downloads\\How yt-dlp Works Under the Hood.mp4',
completedAt: Date.now() - 1000 * 60 * 42
},
{
id: 'h2',
title: 'Deep Focus — Ambient Mix',
channel: 'ChillStudio',
url: 'https://youtube.com/watch?v=aaa1111',
kind: 'audio',
quality: 'Best',
sizeLabel: '142 MB',
filePath: 'C:\\Users\\you\\Downloads\\Deep Focus — Ambient Mix.mp3',
completedAt: Date.now() - 1000 * 60 * 60 * 26
},
{
id: 'h3',
title: 'Conference Keynote 2026 (Full)',
channel: 'TechConf',
url: 'https://youtube.com/watch?v=bbb2222',
kind: 'video',
quality: '1080p · mp4 · 1.2 GB',
sizeLabel: '1.2 GB',
filePath: 'C:\\Users\\you\\Downloads\\Conference Keynote 2026 (Full).mp4',
completedAt: Date.now() - 1000 * 60 * 60 * 24 * 3
}
]
: []
// Cap the optimistic in-memory list at the same shared limit main persists to
// (HISTORY_MAX_ENTRIES) so a long session can't grow it past what a reload would
+37 -33
View File
@@ -21,40 +21,44 @@ function seedItems(sourceId: string, playlist: string, n: number, downloadedUpTo
}))
}
const seedSources: Source[] = PREVIEW
? [
{
id: 'src-dev',
url: 'https://www.youtube.com/@DevChannel',
kind: 'channel',
title: 'DevChannel',
channel: 'DevChannel',
addedAt: Date.now() - 86_400_000,
lastIndexedAt: Date.now() - 3_600_000,
itemCount: 9
},
{
id: 'src-mix',
url: 'https://www.youtube.com/playlist?list=PLmix',
kind: 'playlist',
title: 'Lo-fi Coding Mixes',
channel: 'ChillStudio',
addedAt: Date.now() - 2 * 86_400_000,
lastIndexedAt: Date.now() - 7_200_000,
itemCount: 5
}
]
: []
// Gated on `import.meta.env.DEV` too so Rollup drops these preview seeds from the
// packaged production renderer (DEV is statically false there) (L128).
const seedSources: Source[] =
import.meta.env.DEV && PREVIEW
? [
{
id: 'src-dev',
url: 'https://www.youtube.com/@DevChannel',
kind: 'channel',
title: 'DevChannel',
channel: 'DevChannel',
addedAt: Date.now() - 86_400_000,
lastIndexedAt: Date.now() - 3_600_000,
itemCount: 9
},
{
id: 'src-mix',
url: 'https://www.youtube.com/playlist?list=PLmix',
kind: 'playlist',
title: 'Lo-fi Coding Mixes',
channel: 'ChillStudio',
addedAt: Date.now() - 2 * 86_400_000,
lastIndexedAt: Date.now() - 7_200_000,
itemCount: 5
}
]
: []
const seedItemsBySource: Record<string, MediaItem[]> = PREVIEW
? {
'src-dev': [
...seedItems('src-dev', 'Full Electron Course', 6, 2),
...seedItems('src-dev', 'Uploads', 3)
],
'src-mix': seedItems('src-mix', 'Lo-fi Coding Mixes', 5, 1)
}
: {}
const seedItemsBySource: Record<string, MediaItem[]> =
import.meta.env.DEV && PREVIEW
? {
'src-dev': [
...seedItems('src-dev', 'Full Electron Course', 6, 2),
...seedItems('src-dev', 'Uploads', 3)
],
'src-mix': seedItems('src-mix', 'Lo-fi Coding Mixes', 5, 1)
}
: {}
// --- Store ------------------------------------------------------------------