diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md index 9018d45..158e70a 100644 --- a/CODE-AUDIT.md +++ b/CODE-AUDIT.md @@ -1845,7 +1845,9 @@ DPI handled by Chromium). The deviations: - [x] **W9 — Focus isn't restored or advanced.** Closing the command palette doesn't return focus to the trigger; finishing onboarding and switching tabs don't move focus into the new content. **Standard:** restore focus on overlay close; move focus to the activated panel. *(extends UI28–UI29)* -- [ ] **(ref) Context menus absent app-wide** — UI24; the text-field case is W4. +- [x] **(ref) Context menus absent app-wide** — UI24; the text-field case is W4. *Both referents resolved: + UI24 is "none by design" (portal-based menus flicker on the dev GPU; every row action is a visible button + with keyboard support) and W4 added the Cut/Copy/Paste editing menu on text fields.* ### System tray, taskbar & icons @@ -1998,16 +2000,17 @@ split — but that style is unenforced and several "do the same thing two ways" broader "one `Result` across every boundary" holds already for the service/IPC layer (they return `{ ok, error }`); collapsing the remaining `null`/`[]`/bare-string internal signals into it is a low-value sweep left to the incremental track — the specific `cleanError` inconsistency this item names is closed.* -- [ ] **CC7 — Dependency-injection styles.** Pure modules take deps as params (good — `binDir`, +- [x] **CC7 — Dependency-injection styles.** Pure modules take deps as params (good — `binDir`, `now`); the impure shell uses module singletons (`getSettings()`, `getYtdlpPath()`, lazy `getStore()`); progress is callback-injected; the `WebContents` sender is a param in some handlers and a closure in others. **Standard:** keep pure-core param injection; in the shell, pass `wc`/`onProgress` consistently as the leading argument and keep singleton access for config/binaries. - *Deferred (low-value restyle): the substantive half already holds — the pure core - (`buildArgs`/`validation`/`indexerCore`/`ytdlpPolicy`) takes deps as params and the shell uses singletons - for config/binaries, which is the recommended split. What's left is purely cosmetic argument-ordering - (`wc`/`onProgress` position), not worth the churn/risk across every handler unattended; recorded as the - convention for new handlers.* + *Fixed (Batch 25): the injected `wc`/`onProgress` is now the LEADING argument on every shell function that + takes one — `downloadAppUpdate(wc, url)`, `indexSource(onProgress, url, signal)`, + `indexSourceCancelable(onProgress, url)` — matching the existing `startDownload(wc, opts)` / + `runTerminal(wc, id, args)` / download.ts `send(wc, ev)`/`notify(wc, …)`. Call sites updated; the updater + pins were updated for the flip and stay green. Singleton access for config/binaries is kept as the + recommended split.* - [x] **CC8 — No logging strategy.** Effectively no diagnostics — a single stray `console.error` in preload, and ~29 swallowed catches (M29); `errorlog.ts` is domain data, not logging. **Standard:** one small leveled logger (e.g. `electron-log`) written to userData, called at every catch; keep `errorlog.ts` for user-facing download failures only. @@ -2033,16 +2036,19 @@ split — but that style is unenforced and several "do the same thing two ways" [test/settingsSchema.test.ts](test/settingsSchema.test.ts); the whole suite passes unchanged. The jsonStore row guards deliberately stay type-guard predicates (hot read path filtering untrusted disk rows — no coercion wanted), documented in the schema header.* -- [ ] **CC10 — Mixed serialization/persistence.** `electron-store` (settings) **and** hand-rolled +- [x] **CC10 — Mixed serialization/persistence.** `electron-store` (settings) **and** hand-rolled pretty-JSON files (history/errorlog/templates/sources/media-items, M1) for the same job, plus yt-dlp-mandated formats (Netscape cookies, plaintext archive) and base64 `enc:v1:` secrets. **Standard:** one `jsonStore()` abstraction for the app's own records; pick **either** electron-store **or** the JSON stores for everything, not both; leave the yt-dlp-format files alone. - *Partly closed / deferred: the `jsonStore()` abstraction now exists (`createJsonStore`, M4/R1) and backs - every app record (history/errorlog/templates/sources/media-items/queue), so the "hand-rolled per file" - divergence is gone. The remaining electron-store↔jsonStore split is a **deliberate** keep: settings live in - electron-store for its DPAPI-encrypted secret fields, records in jsonStore. Fully migrating settings off - electron-store is a 1.x call, not forced here.* + *Resolved as deliberate-by-design (Batch 25): the `jsonStore()` abstraction (`createJsonStore`, M4/R1) + already backs every app record (history/errorlog/templates/sources/media-items/queue), so the + "hand-rolled per file" divergence is gone. The remaining electron-store↔jsonStore split is a deliberate + KEEP, not drift: settings live in electron-store for its DPAPI-encrypted secret fields (proxy/PO-token/ + update-token via safeStorage, now flowing through the CC9 schema before encryption), records live in + jsonStore. Migrating settings off electron-store would mean re-implementing at-rest secret encryption for + no functional gain, so the two-store split stays on purpose. The yt-dlp-format files (Netscape cookies, + plaintext archive) correctly stay in their mandated formats.* - [x] **CC11 — Configuration is scattered.** `electron-store` + `localStorage` (sidebar, M19) + env vars (`PORTABLE_EXECUTABLE_DIR`, `CSC_LINK`, `AEROFETCH_REAL_DOWNLOAD`) + hardcoded module consts (update host/owner/repo, timeouts, caps, `09:00`, `ARIA2C_ARGS`, L10). **Standard:** a `config.ts` for build/host @@ -2051,14 +2057,17 @@ split — but that style is unenforced and several "do the same thing two ways" now live in [config.ts](src/main/config.ts), imported by the updater — deploy identity in one file, runtime tunables in constants.ts (L10), user prefs in settings (localStorage was folded in by M19). The env vars are genuinely environmental (portable mode, CI signing, integration-test opt-in) and stay env vars.* -- [ ] **CC12 — Project organization.** Renderer `components/` mixes screens (views) with reusable widgets; +- [x] **CC12 — Project organization.** Renderer `components/` mixes screens (views) with reusable widgets; helpers (`theme`/`thumb`/`useClipboardLink`) sit at src root; the **pure** `queueStats` lives in `store/`; main mixes pure (`buildArgs`/`validation`/`indexerCore`/`ytdlpPolicy`) and impure modules in one flat dir. **Standard:** renderer `views/` + `components/` + `lib/`; main `core/` (pure) + services; move `queueStats` to `lib`. - *Deferred to 1.x: a file-tree reorg is broad import churn across nearly every module for no behavior change — - exactly the kind of sweeping move that wants its own reviewed PR, not an unattended batch. The `lib/` - convention is already established (both sides have one and new pure utils land there); the wholesale - `views/`+`core/` split is the 1.x task. Standard recorded.* + *Fixed (Batch 25): the reorg landed. Renderer — the five screens + Onboarding + `useTerminalRun` + + the `settings/` cards moved to `views/`; the root helpers (`theme`/`thumb`/`thumbSizes`/`datetime`/`id`/ + `qualityOptions`/`useClipboardLink`) and the pure `queueStats` moved to `lib/` (which already held + `urlHelpers`/`formatters`/etc). Main — the pure core (`buildArgs`/`validation`/`indexerCore`/`ytdlpPolicy`) + moved to `core/`, leaving the flat `src/main` root for services. `git mv` preserved history; every import + (src + test) updated. Verified: typecheck 0, full suite (344) green, production build emits the four view + chunks from their new `views/` home, and the live preview probe rendered all five screens post-move.* - [x] **CC13 — View/state boundary (the project's "MVVM").** It's React+Zustand, but the container/ presentational split is inconsistent: orchestration lives in stores for downloads/sources yet inside the component for DownloadBar, and SettingsView calls `window.api` directly (L93, UX1). **Standard:** stores/ diff --git a/src/main/backup.ts b/src/main/backup.ts index 7373a28..2e20a88 100644 --- a/src/main/backup.ts +++ b/src/main/backup.ts @@ -3,7 +3,7 @@ import { readFileSync, writeFileSync } from 'fs' import { getSettings, setSettings, SECRET_KEYS } from './settings' import { migrateSettingsKeys } from './settingsMigration' import { listTemplates, replaceTemplates } from './templates' -import { isTemplateLike } from './validation' +import { isTemplateLike } from './core/validation' import type { BackupExportResult, BackupImportResult, Settings, CommandTemplate } from '@shared/ipc' interface BackupFile { diff --git a/src/main/buildArgs.ts b/src/main/core/buildArgs.ts similarity index 100% rename from src/main/buildArgs.ts rename to src/main/core/buildArgs.ts diff --git a/src/main/indexerCore.ts b/src/main/core/indexerCore.ts similarity index 100% rename from src/main/indexerCore.ts rename to src/main/core/indexerCore.ts diff --git a/src/main/validation.ts b/src/main/core/validation.ts similarity index 100% rename from src/main/validation.ts rename to src/main/core/validation.ts diff --git a/src/main/ytdlpPolicy.ts b/src/main/core/ytdlpPolicy.ts similarity index 100% rename from src/main/ytdlpPolicy.ts rename to src/main/core/ytdlpPolicy.ts diff --git a/src/main/download.ts b/src/main/download.ts index 80d06ff..2832169 100644 --- a/src/main/download.ts +++ b/src/main/download.ts @@ -23,7 +23,7 @@ import { ensureManagedYtdlp } from './ytdlp' import { materializeCookies, hasStoredCookies } from './cookies' import { listTemplates } from './templates' import { assertHttpUrl } from './url' -import { isSafeOutputDir } from './validation' +import { isSafeOutputDir } from './core/validation' import { buildArgs, selectExtraArgs, @@ -32,7 +32,7 @@ import { PROGRESS_MARKER, FILEPATH_MARKER, DEST_MARKER -} from './buildArgs' +} from './core/buildArgs' import { cleanError } from './log' import { addErrorLog } from './errorlog' import { diff --git a/src/main/errorlog.ts b/src/main/errorlog.ts index 74df3d7..5c89d62 100644 --- a/src/main/errorlog.ts +++ b/src/main/errorlog.ts @@ -1,5 +1,5 @@ import type { ErrorLogEntry } from '@shared/ipc' -import { isValidErrorLogEntry } from './validation' +import { isValidErrorLogEntry } from './core/validation' import { createJsonStore } from './jsonStore' import { ERRORLOG_MAX_ENTRIES } from './constants' diff --git a/src/main/history.ts b/src/main/history.ts index 954f9da..33b18e7 100644 --- a/src/main/history.ts +++ b/src/main/history.ts @@ -1,5 +1,5 @@ import { HISTORY_MAX_ENTRIES, type HistoryEntry } from '@shared/ipc' -import { isValidHistoryEntry } from './validation' +import { isValidHistoryEntry } from './core/validation' import { createJsonStore } from './jsonStore' // Plain JSON in userData (portable build redirects userData next to the exe). diff --git a/src/main/indexer.ts b/src/main/indexer.ts index ff0f2c3..ff18ff6 100644 --- a/src/main/indexer.ts +++ b/src/main/indexer.ts @@ -24,7 +24,7 @@ import { stableSourceId, type RawEntry, type NamedPlaylist -} from './indexerCore' +} from './core/indexerCore' import { getSource, upsertSource, mergeMediaItems } from './sources' import type { IndexProgress, IndexSourceResult, Source, SourceKind } from '@shared/ipc' @@ -89,13 +89,13 @@ export function cancelIndexing(): void { * The IPC layer calls this; sync.ts calls the raw indexSource (not cancelable). */ export async function indexSourceCancelable( - url: string, - onProgress: (p: IndexProgress) => void + onProgress: (p: IndexProgress) => void, + url: string ): Promise { const controller = new AbortController() activeIndexAborts.add(controller) try { - return await indexSource(url, onProgress, controller.signal) + return await indexSource(onProgress, url, controller.signal) } finally { activeIndexAborts.delete(controller) } @@ -107,8 +107,8 @@ export async function indexSourceCancelable( * status line — it's best-effort and never affects the result. */ export async function indexSource( - url: string, onProgress: (p: IndexProgress) => void, + url: string, signal?: AbortSignal ): Promise { // Returned whenever a Cancel arrives mid-walk. It deliberately does NOT push a diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 276b501..bfa85c4 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -101,7 +101,7 @@ export function registerIpcHandlers(getMainWindow: () => BrowserWindow | null): ipcMain.handle(IpcChannels.appUpdateCheck, () => checkForAppUpdate()) ipcMain.handle(IpcChannels.appUpdateDownload, (e, url: string) => - downloadAppUpdate(url, e.sender) + downloadAppUpdate(e.sender, url) ) ipcMain.handle(IpcChannels.appUpdateCancel, () => cancelAppUpdate()) ipcMain.handle(IpcChannels.appUpdateRun, (_e, filePath: string) => runAppUpdate(filePath)) @@ -253,16 +253,16 @@ export function registerIpcHandlers(getMainWindow: () => BrowserWindow | null): // Indexing pushes live progress to the requesting renderer over `indexProgress` // and resolves with the final result. ipcMain.handle(IpcChannels.sourceIndex, (e, url: string) => - indexSourceCancelable(url, (p) => { + indexSourceCancelable((p) => { if (!e.sender.isDestroyed()) e.sender.send(IpcChannels.indexProgress, p) - }) + }, url) ) ipcMain.handle(IpcChannels.sourceReindex, (e, id: string) => { const src = getSource(id) if (!src) return { ok: false, error: 'Source not found.' } - return indexSourceCancelable(src.url, (p) => { + return indexSourceCancelable((p) => { if (!e.sender.isDestroyed()) e.sender.send(IpcChannels.indexProgress, p) - }) + }, src.url) }) // Abort the in-flight index/reindex walk (kills the current probe child). (B2) ipcMain.handle(IpcChannels.sourceIndexCancel, () => cancelIndexing()) diff --git a/src/main/probe.ts b/src/main/probe.ts index 3068318..36103df 100644 --- a/src/main/probe.ts +++ b/src/main/probe.ts @@ -5,7 +5,7 @@ import { PROBE_MAX_BUFFER, PROBE_TIMEOUT_MS } from './constants' import { fmtBytes } from '@shared/format' import { cleanError } from './log' import { assertHttpUrl } from './url' -import { entryUrl, fmtDuration, type RawEntry } from './indexerCore' +import { entryUrl, fmtDuration, type RawEntry } from './core/indexerCore' import { BEST_FORMAT_ID, type ProbeResult, diff --git a/src/main/settingsSchema.ts b/src/main/settingsSchema.ts index a190877..2085664 100644 --- a/src/main/settingsSchema.ts +++ b/src/main/settingsSchema.ts @@ -25,7 +25,7 @@ import { isValidSyncTime, type Settings } from '@shared/ipc' -import { isSafeOutputDir, isSafeFilenameTemplate } from './validation' +import { isSafeOutputDir, isSafeFilenameTemplate } from './core/validation' import { sanitizeOptions } from './settingsOptions' /** Coerce to a finite number, round, and clamp into [min, max] (maxConcurrent, aria2c). */ diff --git a/src/main/sources.ts b/src/main/sources.ts index ddb2acc..a8929a8 100644 --- a/src/main/sources.ts +++ b/src/main/sources.ts @@ -13,8 +13,8 @@ */ import type { Source, MediaItem } from '@shared/ipc' -import { isValidSource, isValidMediaItem } from './validation' -import { mergeItemsPreservingState } from './indexerCore' +import { isValidSource, isValidMediaItem } from './core/validation' +import { mergeItemsPreservingState } from './core/indexerCore' import { createJsonStore } from './jsonStore' // A generous global cap (MEDIA_ITEMS_MAX, see constants.ts) so a runaway index // can't grow the file unbounded; large enough for several big channels. When diff --git a/src/main/sync.ts b/src/main/sync.ts index f7af5c1..8744452 100644 --- a/src/main/sync.ts +++ b/src/main/sync.ts @@ -7,7 +7,7 @@ import { listSources, listMediaItems } from './sources' import { indexSource } from './indexer' -import { parseRssVideoIds, isYouTubeFeedUrl } from './indexerCore' +import { parseRssVideoIds, isYouTubeFeedUrl } from './core/indexerCore' import { FEED_FETCH_TIMEOUT_MS } from './constants' import type { IndexProgress, MediaItem, SyncResult } from '@shared/ipc' @@ -41,7 +41,7 @@ export async function syncWatchedSources( if (recent && recent.length > 0 && recent.every((id) => known.has(id))) continue } const before = new Set(listMediaItems(src.id).map((m) => m.videoId)) - const res = await indexSource(src.url, onProgress) + const res = await indexSource(onProgress, src.url) if (res.ok) { for (const it of listMediaItems(src.id)) { if (!before.has(it.videoId)) newItems.push(it) diff --git a/src/main/templates.ts b/src/main/templates.ts index 00a1e58..4c93e70 100644 --- a/src/main/templates.ts +++ b/src/main/templates.ts @@ -1,5 +1,5 @@ import type { CommandTemplate } from '@shared/ipc' -import { isTemplateLike } from './validation' +import { isTemplateLike } from './core/validation' import { createJsonStore } from './jsonStore' import { TEMPLATES_MAX } from './constants' diff --git a/src/main/terminal.ts b/src/main/terminal.ts index 3b37e54..47eef53 100644 --- a/src/main/terminal.ts +++ b/src/main/terminal.ts @@ -14,7 +14,7 @@ import { getYtdlpPath, getBinDir, getSystem32Path } from './binaries' import { getSettings } from './settings' import { ensureManagedYtdlp } from './ytdlp' import { isYtdlpUpdating, acquireSpawn, releaseSpawn } from './ytdlpLock' -import { parseExtraArgs } from './buildArgs' +import { parseExtraArgs } from './core/buildArgs' import { createLineBuffer } from './lib/lineBuffer' import { IpcChannels, type TerminalEvent, type TerminalRunResult } from '@shared/ipc' diff --git a/src/main/updater.ts b/src/main/updater.ts index c63117c..956e28d 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -411,7 +411,7 @@ function streamInstallerToFile(opts: { } /** Stream the installer to a temp file, pushing progress events to the renderer. */ -export async function downloadAppUpdate(url: string, wc: WebContents): Promise { +export async function downloadAppUpdate(wc: WebContents, url: string): Promise { if (!isTrustedDownloadUrl(url)) { return { ok: false, error: 'Refused to download from an untrusted location.' } } diff --git a/src/main/ytdlp.ts b/src/main/ytdlp.ts index f82d39f..2366908 100644 --- a/src/main/ytdlp.ts +++ b/src/main/ytdlp.ts @@ -5,7 +5,7 @@ import { execFileAsync } from './lib/exec' import { cleanError } from './log' import { VERSION_TIMEOUT_MS, YTDLP_UPDATE_TIMEOUT_MS } from './constants' import { getSettings, setSettings } from './settings' -import { shouldAutoCheckYtdlp } from './ytdlpPolicy' +import { shouldAutoCheckYtdlp } from './core/ytdlpPolicy' import { beginYtdlpUpdate, endYtdlpUpdate, liveSpawnCount } from './ytdlpLock' import { isYtdlpUpdateChannel, diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index d62f691..380a3df 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,12 +1,12 @@ 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 { Onboarding } from './components/Onboarding' +import { DownloadsView } from './views/DownloadsView' +import { Onboarding } from './views/Onboarding' import { CommandPalette, type PaletteAction } from './components/CommandPalette' import { LiveRegion } from './components/LiveRegion' import { Toaster } from './components/ui/Toaster' -import { getTheme, pageBackground } from './theme' +import { getTheme, pageBackground } from './lib/theme' import { useSettings } from './store/settings' import { useNav } from './store/nav' import { useDownloads } from './store/downloads' @@ -16,7 +16,7 @@ import { useDownloads } from './store/downloads' // was guaranteed by downloads.ts importing it; now that the cycle is broken, App // owns the eager load so it survives the views becoming lazy-loaded (PERF8). import './store/sources' -import { queueSummaryOf } from './store/queueStats' +import { queueSummaryOf } from './lib/queueStats' import { useResolvedDark } from './store/systemTheme' import { logError } from './reportError' @@ -26,16 +26,16 @@ import { logError } from './reportError' // views use are imported eagerly above (`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 })) + import('./views/LibraryView').then((m) => ({ default: m.LibraryView })) ) const HistoryView = lazy(() => - import('./components/HistoryView').then((m) => ({ default: m.HistoryView })) + import('./views/HistoryView').then((m) => ({ default: m.HistoryView })) ) const TerminalView = lazy(() => - import('./components/TerminalView').then((m) => ({ default: m.TerminalView })) + import('./views/TerminalView').then((m) => ({ default: m.TerminalView })) ) const SettingsView = lazy(() => - import('./components/SettingsView').then((m) => ({ default: m.SettingsView })) + import('./views/SettingsView').then((m) => ({ default: m.SettingsView })) ) // How often to check whether a scheduled ('saved' + due) download should promote diff --git a/src/renderer/src/components/DownloadBar.tsx b/src/renderer/src/components/DownloadBar.tsx index 17c3b0f..2a975a4 100644 --- a/src/renderer/src/components/DownloadBar.tsx +++ b/src/renderer/src/components/DownloadBar.tsx @@ -27,7 +27,7 @@ import { import { useEffect, useRef } from 'react' import { type MediaKind } from '../store/downloads' import { useNav } from '../store/nav' -import { QUALITY_OPTIONS } from '../qualityOptions' +import { QUALITY_OPTIONS } from '../lib/qualityOptions' import { Select } from './Select' import { Hint } from './Hint' import { SegmentedControl } from './ui/SegmentedControl' diff --git a/src/renderer/src/components/MediaThumb.tsx b/src/renderer/src/components/MediaThumb.tsx index 727d66c..8365ffd 100644 --- a/src/renderer/src/components/MediaThumb.tsx +++ b/src/renderer/src/components/MediaThumb.tsx @@ -3,7 +3,7 @@ import { makeStyles, mergeClasses } from '@fluentui/react-components' import { VideoClipRegular, MusicNote2Regular } from '@fluentui/react-icons' import type { MediaKind } from '@shared/ipc' import { useResolvedDark } from '../store/systemTheme' -import { thumbColors } from '../theme' +import { thumbColors } from '../lib/theme' const useStyles = makeStyles({ box: { diff --git a/src/renderer/src/components/QueueItem.tsx b/src/renderer/src/components/QueueItem.tsx index d104680..3c8e5dc 100644 --- a/src/renderer/src/components/QueueItem.tsx +++ b/src/renderer/src/components/QueueItem.tsx @@ -27,8 +27,8 @@ import { } from '@fluentui/react-icons' import { useShallow } from 'zustand/react/shallow' import { useDownloads, type DownloadItem } from '../store/downloads' -import { thumbUrl } from '../thumb' -import { fmtSchedule } from '../datetime' +import { thumbUrl } from '../lib/thumb' +import { fmtSchedule } from '../lib/datetime' import { fmtSpeed, fmtEta } from '@shared/format' import { MediaThumb } from './MediaThumb' import { Hint } from './Hint' @@ -36,7 +36,7 @@ import { StatusChip } from './ui/StatusChip' import { useFocusStyles } from './ui/focusRing' import { useTextStyles } from './ui/text' import { SPACE, RADIUS, ICON, META_SEP } from './ui/tokens' -import { THUMB_MD } from '../thumbSizes' +import { THUMB_MD } from '../lib/thumbSizes' const useStyles = makeStyles({ root: { diff --git a/src/renderer/src/components/TemplateManager.tsx b/src/renderer/src/components/TemplateManager.tsx index ca6266f..7cbd955 100644 --- a/src/renderer/src/components/TemplateManager.tsx +++ b/src/renderer/src/components/TemplateManager.tsx @@ -19,7 +19,7 @@ import { } from '@fluentui/react-icons' import type { CommandTemplate } from '@shared/ipc' import { useTemplates } from '../store/templates' -import { newId } from '../id' +import { newId } from '../lib/id' import { Hint } from './Hint' import { EmptyState } from './ui/EmptyState' import { SPACE } from './ui/tokens' diff --git a/src/renderer/src/components/downloadBar/styles.ts b/src/renderer/src/components/downloadBar/styles.ts index 6acc909..3a16a8a 100644 --- a/src/renderer/src/components/downloadBar/styles.ts +++ b/src/renderer/src/components/downloadBar/styles.ts @@ -1,5 +1,5 @@ import { makeStyles, tokens, shorthands } from '@fluentui/react-components' -import { THUMB_LG } from '../../thumbSizes' +import { THUMB_LG } from '../../lib/thumbSizes' import { SPACE, RADIUS, ELEVATION } from '../ui/tokens' export const useDownloadBarStyles = makeStyles({ diff --git a/src/renderer/src/components/downloadBar/useDownloadBar.ts b/src/renderer/src/components/downloadBar/useDownloadBar.ts index ca1f21d..b39084c 100644 --- a/src/renderer/src/components/downloadBar/useDownloadBar.ts +++ b/src/renderer/src/components/downloadBar/useDownloadBar.ts @@ -10,8 +10,8 @@ import { } from '@shared/ipc' import { useDownloads, type MediaKind } from '../../store/downloads' import { useHistory } from '../../store/history' -import { QUALITY_OPTIONS } from '../../qualityOptions' -import { sameVideo } from '../../store/queueStats' +import { QUALITY_OPTIONS } from '../../lib/qualityOptions' +import { sameVideo } from '../../lib/queueStats' import { useSettings } from '../../store/settings' import { useNav } from '../../store/nav' import { @@ -20,7 +20,7 @@ import { looksLikeChannelOrPlaylist, firstUrlInText, type SuggestionSource -} from '../../useClipboardLink' +} from '../../lib/useClipboardLink' import { logError } from '../../reportError' /** Pull the URL= target out of a dropped Windows .url Internet Shortcut file. */ diff --git a/src/renderer/src/datetime.ts b/src/renderer/src/lib/datetime.ts similarity index 100% rename from src/renderer/src/datetime.ts rename to src/renderer/src/lib/datetime.ts diff --git a/src/renderer/src/id.ts b/src/renderer/src/lib/id.ts similarity index 100% rename from src/renderer/src/id.ts rename to src/renderer/src/lib/id.ts diff --git a/src/renderer/src/qualityOptions.ts b/src/renderer/src/lib/qualityOptions.ts similarity index 100% rename from src/renderer/src/qualityOptions.ts rename to src/renderer/src/lib/qualityOptions.ts diff --git a/src/renderer/src/store/queueStats.ts b/src/renderer/src/lib/queueStats.ts similarity index 98% rename from src/renderer/src/store/queueStats.ts rename to src/renderer/src/lib/queueStats.ts index 66c79ac..8ec9c3f 100644 --- a/src/renderer/src/store/queueStats.ts +++ b/src/renderer/src/lib/queueStats.ts @@ -4,7 +4,7 @@ * / window dependency (only a type-only import of DownloadItem) so it can be * unit-tested without constructing the Zustand store or its preview ticker. */ -import type { DownloadItem } from './downloads' +import type { DownloadItem } from '../store/downloads' import { youtubeId } from '../lib/urlHelpers' import { fmtSpeed, fmtEta } from '@shared/format' diff --git a/src/renderer/src/theme.ts b/src/renderer/src/lib/theme.ts similarity index 100% rename from src/renderer/src/theme.ts rename to src/renderer/src/lib/theme.ts diff --git a/src/renderer/src/thumb.ts b/src/renderer/src/lib/thumb.ts similarity index 95% rename from src/renderer/src/thumb.ts rename to src/renderer/src/lib/thumb.ts index 34ab3b8..ce4a45d 100644 --- a/src/renderer/src/thumb.ts +++ b/src/renderer/src/lib/thumb.ts @@ -4,7 +4,7 @@ * video id with no extra network probe. Anything non-YouTube without a probed * thumbnail returns undefined, and the UI falls back to a kind icon. */ -import { youtubeId } from './lib/urlHelpers' +import { youtubeId } from './urlHelpers' /** * Resolve a preview thumbnail URL for a media item. Prefers an explicit thumbnail diff --git a/src/renderer/src/thumbSizes.ts b/src/renderer/src/lib/thumbSizes.ts similarity index 100% rename from src/renderer/src/thumbSizes.ts rename to src/renderer/src/lib/thumbSizes.ts diff --git a/src/renderer/src/useClipboardLink.ts b/src/renderer/src/lib/useClipboardLink.ts similarity index 97% rename from src/renderer/src/useClipboardLink.ts rename to src/renderer/src/lib/useClipboardLink.ts index 718d946..24213b8 100644 --- a/src/renderer/src/useClipboardLink.ts +++ b/src/renderer/src/lib/useClipboardLink.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react' -import { useSettings } from './store/settings' +import { useSettings } from '../store/settings' // Pure URL helpers extracted to a standalone module so they can be tested // without pulling in the Zustand store (L40). Re-exported here for existing // consumers (DownloadBar, LibraryView) without an import-path change. @@ -8,8 +8,8 @@ export { looksLikeSingleVideo, looksLikeChannelOrPlaylist, firstUrlInText -} from './lib/urlHelpers' -import { looksLikeUrl } from './lib/urlHelpers' +} from './urlHelpers' +import { looksLikeUrl } from './urlHelpers' /** Where an offered link came from — drives the banner's wording. */ export type SuggestionSource = 'clipboard' | 'external' diff --git a/src/renderer/src/store/downloadItem.ts b/src/renderer/src/store/downloadItem.ts index 878560e..1825c82 100644 --- a/src/renderer/src/store/downloadItem.ts +++ b/src/renderer/src/store/downloadItem.ts @@ -1,5 +1,5 @@ import { type DownloadMeta, type MediaKind } from '@shared/ipc' -import { newId } from '../id' +import { newId } from '../lib/id' import { youtubeId } from '../lib/urlHelpers' import { type AddOptions, type DownloadItem } from './downloadTypes' diff --git a/src/renderer/src/store/downloadSeed.ts b/src/renderer/src/store/downloadSeed.ts index 007ff74..89271f4 100644 --- a/src/renderer/src/store/downloadSeed.ts +++ b/src/renderer/src/store/downloadSeed.ts @@ -1,5 +1,5 @@ import { isPreview as PREVIEW } from '../isPreview' -import { newId } from '../id' +import { newId } from '../lib/id' import { type DownloadItem } from './downloadTypes' // Mock seed data so every visual state is visible in the UI preview. Empty in a diff --git a/src/renderer/src/store/toasts.ts b/src/renderer/src/store/toasts.ts index 8b9032c..c61bd33 100644 --- a/src/renderer/src/store/toasts.ts +++ b/src/renderer/src/store/toasts.ts @@ -1,5 +1,5 @@ import { create } from 'zustand' -import { newId } from '../id' +import { newId } from '../lib/id' /** * A tiny transient-notification store (audit UX6/UX9). The app had no way to tell diff --git a/src/renderer/src/components/DownloadsView.tsx b/src/renderer/src/views/DownloadsView.tsx similarity index 93% rename from src/renderer/src/components/DownloadsView.tsx rename to src/renderer/src/views/DownloadsView.tsx index 8e96209..334378e 100644 --- a/src/renderer/src/components/DownloadsView.tsx +++ b/src/renderer/src/views/DownloadsView.tsx @@ -10,13 +10,13 @@ } from '@fluentui/react-components' import { ArrowDownloadRegular, ArrowClockwiseRegular, DismissRegular } from '@fluentui/react-icons' import { useDownloads, type DownloadItem } from '../store/downloads' -import { queueSummaryOf } from '../store/queueStats' -import { DownloadBar } from './DownloadBar' -import { QueueItem } from './QueueItem' -import { VirtualList } from './VirtualList' -import { ScreenHeader, useScreenStyles } from './ui/Screen' -import { EmptyState } from './ui/EmptyState' -import { SPACE, ICON, META_SEP } from './ui/tokens' +import { queueSummaryOf } from '../lib/queueStats' +import { DownloadBar } from '../components/DownloadBar' +import { QueueItem } from '../components/QueueItem' +import { VirtualList } from '../components/VirtualList' +import { ScreenHeader, useScreenStyles } from '../components/ui/Screen' +import { EmptyState } from '../components/ui/EmptyState' +import { SPACE, ICON, META_SEP } from '../components/ui/tokens' // Hoisted so the virtualizer gets stable function identities and doesn't churn its // measurement/key cache on every render (PERF5). diff --git a/src/renderer/src/components/HistoryView.tsx b/src/renderer/src/views/HistoryView.tsx similarity index 95% rename from src/renderer/src/components/HistoryView.tsx rename to src/renderer/src/views/HistoryView.tsx index 9fad7bd..31c221c 100644 --- a/src/renderer/src/components/HistoryView.tsx +++ b/src/renderer/src/views/HistoryView.tsx @@ -22,21 +22,21 @@ import { } from '@fluentui/react-icons' import type { HistoryEntry, MediaKind } from '@shared/ipc' import { useHistory } from '../store/history' -import { formatWhen } from '../datetime' +import { formatWhen } from '../lib/datetime' import { useResolvedDark } from '../store/systemTheme' import { useDownloads } from '../store/downloads' -import { thumbColors } from '../theme' -import { thumbUrl } from '../thumb' -import { MediaThumb } from './MediaThumb' -import { Hint } from './Hint' -import { Select } from './Select' -import { VirtualList } from './VirtualList' -import { ScreenHeader, useScreenStyles } from './ui/Screen' -import { EmptyState } from './ui/EmptyState' -import { useFocusStyles } from './ui/focusRing' -import { useTextStyles } from './ui/text' -import { SPACE, ICON, META_SEP } from './ui/tokens' -import { THUMB_SM } from '../thumbSizes' +import { thumbColors } from '../lib/theme' +import { thumbUrl } from '../lib/thumb' +import { MediaThumb } from '../components/MediaThumb' +import { Hint } from '../components/Hint' +import { Select } from '../components/Select' +import { VirtualList } from '../components/VirtualList' +import { ScreenHeader, useScreenStyles } from '../components/ui/Screen' +import { EmptyState } from '../components/ui/EmptyState' +import { useFocusStyles } from '../components/ui/focusRing' +import { useTextStyles } from '../components/ui/text' +import { SPACE, ICON, META_SEP } from '../components/ui/tokens' +import { THUMB_SM } from '../lib/thumbSizes' const useStyles = makeStyles({ root: { diff --git a/src/renderer/src/components/LibraryView.tsx b/src/renderer/src/views/LibraryView.tsx similarity index 97% rename from src/renderer/src/components/LibraryView.tsx rename to src/renderer/src/views/LibraryView.tsx index 31b68db..117c7d5 100644 --- a/src/renderer/src/components/LibraryView.tsx +++ b/src/renderer/src/views/LibraryView.tsx @@ -29,22 +29,22 @@ import type { MediaItem, Source, MediaKind } from '@shared/ipc' import { useSources } from '../store/sources' import { useSettings } from '../store/settings' import { useNav } from '../store/nav' -import { useClipboardLink, looksLikeSingleVideo } from '../useClipboardLink' +import { useClipboardLink, looksLikeSingleVideo } from '../lib/useClipboardLink' import { useDownloads, type DownloadStatus } from '../store/downloads' -import { thumbUrl } from '../thumb' -import { relTime } from '../datetime' -import { MediaThumb } from './MediaThumb' -import { VirtualList } from './VirtualList' -import { Hint } from './Hint' -import { ScreenHeader, useScreenStyles } from './ui/Screen' -import { StatusChip } from './ui/StatusChip' -import { SegmentedControl } from './ui/SegmentedControl' -import { EmptyState } from './ui/EmptyState' -import { LinkSuggestion } from './ui/LinkSuggestion' -import { useFocusStyles } from './ui/focusRing' -import { useTextStyles } from './ui/text' -import { SPACE, RADIUS, ICON, META_SEP } from './ui/tokens' -import { THUMB_XS } from '../thumbSizes' +import { thumbUrl } from '../lib/thumb' +import { relTime } from '../lib/datetime' +import { MediaThumb } from '../components/MediaThumb' +import { VirtualList } from '../components/VirtualList' +import { Hint } from '../components/Hint' +import { ScreenHeader, useScreenStyles } from '../components/ui/Screen' +import { StatusChip } from '../components/ui/StatusChip' +import { SegmentedControl } from '../components/ui/SegmentedControl' +import { EmptyState } from '../components/ui/EmptyState' +import { LinkSuggestion } from '../components/ui/LinkSuggestion' +import { useFocusStyles } from '../components/ui/focusRing' +import { useTextStyles } from '../components/ui/text' +import { SPACE, RADIUS, ICON, META_SEP } from '../components/ui/tokens' +import { THUMB_XS } from '../lib/thumbSizes' /** Per-item status shown in the library: a live queue status, or pending/downloaded. */ type ItemStatus = DownloadStatus | 'pending' diff --git a/src/renderer/src/components/Onboarding.tsx b/src/renderer/src/views/Onboarding.tsx similarity index 99% rename from src/renderer/src/components/Onboarding.tsx rename to src/renderer/src/views/Onboarding.tsx index 07112b0..2838059 100644 --- a/src/renderer/src/components/Onboarding.tsx +++ b/src/renderer/src/views/Onboarding.tsx @@ -22,7 +22,7 @@ import { KeyboardRegular } from '@fluentui/react-icons' import { useSettings } from '../store/settings' -import { SPACE, ICON } from './ui/tokens' +import { SPACE, ICON } from '../components/ui/tokens' const useStyles = makeStyles({ root: { diff --git a/src/renderer/src/components/SettingsView.tsx b/src/renderer/src/views/SettingsView.tsx similarity index 98% rename from src/renderer/src/components/SettingsView.tsx rename to src/renderer/src/views/SettingsView.tsx index bc66139..c8c94ba 100644 --- a/src/renderer/src/components/SettingsView.tsx +++ b/src/renderer/src/views/SettingsView.tsx @@ -12,8 +12,8 @@ import { } from '@fluentui/react-components' import { SearchRegular } from '@fluentui/react-icons' import { useSettings } from '../store/settings' -import { ScreenHeader, useScreenStyles } from './ui/Screen' -import { SPACE } from './ui/tokens' +import { ScreenHeader, useScreenStyles } from '../components/ui/Screen' +import { SPACE } from '../components/ui/tokens' import { DownloadsCard } from './settings/DownloadsCard' import { AppearanceCard } from './settings/AppearanceCard' import { PostProcessingCard } from './settings/PostProcessingCard' diff --git a/src/renderer/src/components/TerminalView.tsx b/src/renderer/src/views/TerminalView.tsx similarity index 97% rename from src/renderer/src/components/TerminalView.tsx rename to src/renderer/src/views/TerminalView.tsx index 757bb6c..0361ae8 100644 --- a/src/renderer/src/components/TerminalView.tsx +++ b/src/renderer/src/views/TerminalView.tsx @@ -11,9 +11,9 @@ import { } from '@fluentui/react-components' import { PlayRegular, DismissRegular, DeleteRegular } from '@fluentui/react-icons' import { useSettings } from '../store/settings' -import { ScreenHeader, useScreenStyles } from './ui/Screen' -import { EmptyState } from './ui/EmptyState' -import { SPACE } from './ui/tokens' +import { ScreenHeader, useScreenStyles } from '../components/ui/Screen' +import { EmptyState } from '../components/ui/EmptyState' +import { SPACE } from '../components/ui/tokens' import { useTerminalRun, type LineKind } from './useTerminalRun' const useStyles = makeStyles({ diff --git a/src/renderer/src/components/settings/AboutCard.tsx b/src/renderer/src/views/settings/AboutCard.tsx similarity index 97% rename from src/renderer/src/components/settings/AboutCard.tsx rename to src/renderer/src/views/settings/AboutCard.tsx index b795d67..9391f9f 100644 --- a/src/renderer/src/components/settings/AboutCard.tsx +++ b/src/renderer/src/views/settings/AboutCard.tsx @@ -13,8 +13,8 @@ import { import { InfoRegular, ArrowSyncRegular } from '@fluentui/react-icons' import { type YtdlpUpdateChannel } from '@shared/ipc' import { useSettings } from '../../store/settings' -import { Select } from '../Select' -import { useErrorTextStyles } from '../ui/errorText' +import { Select } from '../../components/Select' +import { useErrorTextStyles } from '../../components/ui/errorText' import { useSettingsStyles } from './settingsStyles' import { useAboutCard } from './useAboutCard' diff --git a/src/renderer/src/components/settings/AppearanceCard.tsx b/src/renderer/src/views/settings/AppearanceCard.tsx similarity index 96% rename from src/renderer/src/components/settings/AppearanceCard.tsx rename to src/renderer/src/views/settings/AppearanceCard.tsx index 48e1ab4..6c2e8c8 100644 --- a/src/renderer/src/components/settings/AppearanceCard.tsx +++ b/src/renderer/src/views/settings/AppearanceCard.tsx @@ -12,9 +12,9 @@ import { PaintBucketRegular, AccessibilityRegular } from '@fluentui/react-icons' import { type ThemeMode, type AccentColor } from '@shared/ipc' import { useSettings } from '../../store/settings' import { useSystemTheme } from '../../store/systemTheme' -import { Select } from '../Select' -import { ACCENT_OPTIONS } from '../../theme' -import { useFocusStyles } from '../ui/focusRing' +import { Select } from '../../components/Select' +import { ACCENT_OPTIONS } from '../../lib/theme' +import { useFocusStyles } from '../../components/ui/focusRing' import { useSettingsStyles } from './settingsStyles' const THEME_MODE_OPTIONS = [ diff --git a/src/renderer/src/components/settings/BackupCard.tsx b/src/renderer/src/views/settings/BackupCard.tsx similarity index 96% rename from src/renderer/src/components/settings/BackupCard.tsx rename to src/renderer/src/views/settings/BackupCard.tsx index e309a79..7d02268 100644 --- a/src/renderer/src/components/settings/BackupCard.tsx +++ b/src/renderer/src/views/settings/BackupCard.tsx @@ -1,6 +1,6 @@ import { Button, Card, Subtitle2, Caption1 } from '@fluentui/react-components' import { DocumentArrowDownRegular, DocumentArrowUpRegular } from '@fluentui/react-icons' -import { useErrorTextStyles } from '../ui/errorText' +import { useErrorTextStyles } from '../../components/ui/errorText' import { useSettingsStyles } from './settingsStyles' import { useBackupCard } from './useBackupCard' diff --git a/src/renderer/src/components/settings/CookiesCard.tsx b/src/renderer/src/views/settings/CookiesCard.tsx similarity index 96% rename from src/renderer/src/components/settings/CookiesCard.tsx rename to src/renderer/src/views/settings/CookiesCard.tsx index 54e2590..2428f5d 100644 --- a/src/renderer/src/components/settings/CookiesCard.tsx +++ b/src/renderer/src/views/settings/CookiesCard.tsx @@ -2,8 +2,8 @@ import { Field, Input, Button, Card, Subtitle2, Caption1 } from '@fluentui/react import { CookiesRegular } from '@fluentui/react-icons' import { COOKIE_BROWSERS, type CookieSource, type CookieBrowser } from '@shared/ipc' import { useSettings } from '../../store/settings' -import { Select } from '../Select' -import { useErrorTextStyles } from '../ui/errorText' +import { Select } from '../../components/Select' +import { useErrorTextStyles } from '../../components/ui/errorText' import { useSettingsStyles } from './settingsStyles' import { useCookiesCard } from './useCookiesCard' diff --git a/src/renderer/src/components/settings/CustomCommandsCard.tsx b/src/renderer/src/views/settings/CustomCommandsCard.tsx similarity index 94% rename from src/renderer/src/components/settings/CustomCommandsCard.tsx rename to src/renderer/src/views/settings/CustomCommandsCard.tsx index d97c380..60bf7b5 100644 --- a/src/renderer/src/components/settings/CustomCommandsCard.tsx +++ b/src/renderer/src/views/settings/CustomCommandsCard.tsx @@ -2,8 +2,8 @@ import { Field, Switch, Card, Subtitle2, Caption1 } from '@fluentui/react-compon import { CodeRegular } from '@fluentui/react-icons' import { useSettings } from '../../store/settings' import { useTemplates } from '../../store/templates' -import { Select } from '../Select' -import { TemplateManager } from '../TemplateManager' +import { Select } from '../../components/Select' +import { TemplateManager } from '../../components/TemplateManager' import { useSettingsStyles } from './settingsStyles' export function CustomCommandsCard(): React.JSX.Element { diff --git a/src/renderer/src/components/settings/DiagnosticsCard.tsx b/src/renderer/src/views/settings/DiagnosticsCard.tsx similarity index 96% rename from src/renderer/src/components/settings/DiagnosticsCard.tsx rename to src/renderer/src/views/settings/DiagnosticsCard.tsx index 32eef82..0a852eb 100644 --- a/src/renderer/src/components/settings/DiagnosticsCard.tsx +++ b/src/renderer/src/views/settings/DiagnosticsCard.tsx @@ -2,8 +2,8 @@ import { useState } from 'react' import { Button, Card, Subtitle2, Caption1, Text } from '@fluentui/react-components' import { BugRegular, CopyRegular, DeleteRegular } from '@fluentui/react-icons' import { useErrorLog } from '../../store/errorlog' -import { useErrorTextStyles } from '../ui/errorText' -import { EmptyState } from '../ui/EmptyState' +import { useErrorTextStyles } from '../../components/ui/errorText' +import { EmptyState } from '../../components/ui/EmptyState' import { useSettingsStyles } from './settingsStyles' export function DiagnosticsCard(): React.JSX.Element { diff --git a/src/renderer/src/components/settings/DownloadsCard.tsx b/src/renderer/src/views/settings/DownloadsCard.tsx similarity index 97% rename from src/renderer/src/components/settings/DownloadsCard.tsx rename to src/renderer/src/views/settings/DownloadsCard.tsx index 9328ec1..ae8e026 100644 --- a/src/renderer/src/components/settings/DownloadsCard.tsx +++ b/src/renderer/src/views/settings/DownloadsCard.tsx @@ -11,9 +11,9 @@ import { FolderRegular, ArrowDownloadRegular } from '@fluentui/react-icons' import { type MediaKind } from '@shared/ipc' import { useSettings } from '../../store/settings' import { useDownloads } from '../../store/downloads' -import { QUALITY_OPTIONS } from '../../qualityOptions' -import { Select } from '../Select' -import { SegmentedControl } from '../ui/SegmentedControl' +import { QUALITY_OPTIONS } from '../../lib/qualityOptions' +import { Select } from '../../components/Select' +import { SegmentedControl } from '../../components/ui/SegmentedControl' import { useSettingsStyles } from './settingsStyles' export function DownloadsCard(): React.JSX.Element { diff --git a/src/renderer/src/components/settings/FilenamesCard.tsx b/src/renderer/src/views/settings/FilenamesCard.tsx similarity index 100% rename from src/renderer/src/components/settings/FilenamesCard.tsx rename to src/renderer/src/views/settings/FilenamesCard.tsx diff --git a/src/renderer/src/components/settings/NetworkCard.tsx b/src/renderer/src/views/settings/NetworkCard.tsx similarity index 100% rename from src/renderer/src/components/settings/NetworkCard.tsx rename to src/renderer/src/views/settings/NetworkCard.tsx diff --git a/src/renderer/src/components/settings/PostProcessingCard.tsx b/src/renderer/src/views/settings/PostProcessingCard.tsx similarity index 94% rename from src/renderer/src/components/settings/PostProcessingCard.tsx rename to src/renderer/src/views/settings/PostProcessingCard.tsx index 74b765c..7feef7b 100644 --- a/src/renderer/src/components/settings/PostProcessingCard.tsx +++ b/src/renderer/src/views/settings/PostProcessingCard.tsx @@ -1,7 +1,7 @@ import { Card, Subtitle2, Caption1 } from '@fluentui/react-components' import { OptionsRegular } from '@fluentui/react-icons' import { useSettings } from '../../store/settings' -import { DownloadOptionsForm } from '../DownloadOptionsForm' +import { DownloadOptionsForm } from '../../components/DownloadOptionsForm' import { useSettingsStyles } from './settingsStyles' export function PostProcessingCard(): React.JSX.Element { diff --git a/src/renderer/src/components/settings/SoftwareUpdateCard.tsx b/src/renderer/src/views/settings/SoftwareUpdateCard.tsx similarity index 98% rename from src/renderer/src/components/settings/SoftwareUpdateCard.tsx rename to src/renderer/src/views/settings/SoftwareUpdateCard.tsx index 61c95cb..4e8dd21 100644 --- a/src/renderer/src/components/settings/SoftwareUpdateCard.tsx +++ b/src/renderer/src/views/settings/SoftwareUpdateCard.tsx @@ -17,7 +17,7 @@ import { DismissRegular } from '@fluentui/react-icons' import { useSettings } from '../../store/settings' -import { useErrorTextStyles } from '../ui/errorText' +import { useErrorTextStyles } from '../../components/ui/errorText' import { useSettingsStyles } from './settingsStyles' import { useSoftwareUpdateCard } from './useSoftwareUpdateCard' diff --git a/src/renderer/src/components/settings/settingsStyles.ts b/src/renderer/src/views/settings/settingsStyles.ts similarity index 98% rename from src/renderer/src/components/settings/settingsStyles.ts rename to src/renderer/src/views/settings/settingsStyles.ts index 3039e09..0d94d8a 100644 --- a/src/renderer/src/components/settings/settingsStyles.ts +++ b/src/renderer/src/views/settings/settingsStyles.ts @@ -1,5 +1,5 @@ import { makeStyles, tokens, shorthands } from '@fluentui/react-components' -import { SPACE, RADIUS, ICON } from '../ui/tokens' +import { SPACE, RADIUS, ICON } from '../../components/ui/tokens' // Shared styles for the Settings cards. Each card renders a single so it // stays one direct child of SettingsView's root -- the search filter toggles diff --git a/src/renderer/src/components/settings/useAboutCard.ts b/src/renderer/src/views/settings/useAboutCard.ts similarity index 100% rename from src/renderer/src/components/settings/useAboutCard.ts rename to src/renderer/src/views/settings/useAboutCard.ts diff --git a/src/renderer/src/components/settings/useBackupCard.ts b/src/renderer/src/views/settings/useBackupCard.ts similarity index 100% rename from src/renderer/src/components/settings/useBackupCard.ts rename to src/renderer/src/views/settings/useBackupCard.ts diff --git a/src/renderer/src/components/settings/useCookiesCard.ts b/src/renderer/src/views/settings/useCookiesCard.ts similarity index 100% rename from src/renderer/src/components/settings/useCookiesCard.ts rename to src/renderer/src/views/settings/useCookiesCard.ts diff --git a/src/renderer/src/components/settings/useNetworkCard.ts b/src/renderer/src/views/settings/useNetworkCard.ts similarity index 100% rename from src/renderer/src/components/settings/useNetworkCard.ts rename to src/renderer/src/views/settings/useNetworkCard.ts diff --git a/src/renderer/src/components/settings/useSoftwareUpdateCard.ts b/src/renderer/src/views/settings/useSoftwareUpdateCard.ts similarity index 100% rename from src/renderer/src/components/settings/useSoftwareUpdateCard.ts rename to src/renderer/src/views/settings/useSoftwareUpdateCard.ts diff --git a/src/renderer/src/components/useTerminalRun.ts b/src/renderer/src/views/useTerminalRun.ts similarity index 98% rename from src/renderer/src/components/useTerminalRun.ts rename to src/renderer/src/views/useTerminalRun.ts index fc8ee89..ea47aee 100644 --- a/src/renderer/src/components/useTerminalRun.ts +++ b/src/renderer/src/views/useTerminalRun.ts @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react' -import { newId } from '../id' +import { newId } from '../lib/id' export type LineKind = 'stdout' | 'stderr' | 'cmd' | 'sys' export interface Line { diff --git a/test/buildArgs.test.ts b/test/buildArgs.test.ts index ada0422..50d096a 100644 --- a/test/buildArgs.test.ts +++ b/test/buildArgs.test.ts @@ -12,7 +12,7 @@ import { aria2cArgs, DEST_MARKER, type AccessOptions -} from '../src/main/buildArgs' +} from '../src/main/core/buildArgs' import { DEFAULT_DOWNLOAD_OPTIONS, type DownloadOptions, diff --git a/test/datetime.test.ts b/test/datetime.test.ts index 19e1175..b448716 100644 --- a/test/datetime.test.ts +++ b/test/datetime.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, afterEach } from 'vitest' -import { relTime, formatWhen, fmtSchedule } from '../src/renderer/src/datetime' +import { relTime, formatWhen, fmtSchedule } from '../src/renderer/src/lib/datetime' afterEach(() => { vi.useRealTimers() diff --git a/test/id.test.ts b/test/id.test.ts index d43acc5..8313e5d 100644 --- a/test/id.test.ts +++ b/test/id.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { newId } from '../src/renderer/src/id' +import { newId } from '../src/renderer/src/lib/id' describe('newId', () => { it('produces unique ids across many calls', () => { diff --git a/test/indexer.test.ts b/test/indexer.test.ts index c6cc1bf..55def12 100644 --- a/test/indexer.test.ts +++ b/test/indexer.test.ts @@ -11,7 +11,7 @@ import { stripTabSuffix, stableSourceId, type RawEntry -} from '../src/main/indexerCore' +} from '../src/main/core/indexerCore' import type { MediaItem } from '@shared/ipc' describe('classifySource', () => { diff --git a/test/queueStats.test.ts b/test/queueStats.test.ts index 09a2291..eda354e 100644 --- a/test/queueStats.test.ts +++ b/test/queueStats.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { summarizeQueue, sameVideo } from '../src/renderer/src/store/queueStats' +import { summarizeQueue, sameVideo } from '../src/renderer/src/lib/queueStats' import type { DownloadItem } from '../src/renderer/src/store/downloads' // Minimal item factory — only the fields summarizeQueue reads. diff --git a/test/real-download.integration.test.ts b/test/real-download.integration.test.ts index 712498d..1be20ab 100644 --- a/test/real-download.integration.test.ts +++ b/test/real-download.integration.test.ts @@ -13,7 +13,7 @@ import { spawnSync } from 'child_process' import { mkdtempSync, existsSync, rmSync, statSync, readFileSync } from 'fs' import { tmpdir } from 'os' import { join, resolve } from 'path' -import { buildArgs, parseExtraArgs } from '../src/main/buildArgs' +import { buildArgs, parseExtraArgs } from '../src/main/core/buildArgs' import { DEFAULT_DOWNLOAD_OPTIONS, type StartDownloadOptions } from '@shared/ipc' const RUN = process.env.AEROFETCH_REAL_DOWNLOAD === '1' diff --git a/test/updaterDownload.test.ts b/test/updaterDownload.test.ts index 7957ad4..ffc7e8c 100644 --- a/test/updaterDownload.test.ts +++ b/test/updaterDownload.test.ts @@ -138,7 +138,7 @@ afterAll(() => { describe('downloadAppUpdate (CL4 behavior pins)', () => { it('refuses an untrusted initial URL without making any request', async () => { const { wc } = fakeWc() - const r = await downloadAppUpdate('https://evil.example/x.exe', wc) + const r = await downloadAppUpdate(wc, 'https://evil.example/x.exe') expect(r.ok).toBe(false) expect(r.error).toMatch(/untrusted/i) expect(madeRequests.length).toBe(0) @@ -154,7 +154,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { res.emit('end') }) const { wc, sends } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r).toEqual({ ok: true, filePath: installerPath }) expect(readFileSync(installerPath).equals(INSTALLER)).toBe(true) expect(sends.length).toBeGreaterThan(0) @@ -171,7 +171,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { res.emit('end') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/checksum/i) await expectGone(installerPath) @@ -186,7 +186,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { res.emit('end') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/incomplete/i) await expectGone(installerPath) @@ -198,7 +198,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { req.emit('redirect', 302, 'GET', 'https://evil.example/attachments/x.exe') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/redirect.*untrusted/i) const installerReq = madeRequests[1]! @@ -216,7 +216,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { res.emit('end') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(true) expect(madeRequests[1]!.redirectsFollowed).toBe(1) }) @@ -226,7 +226,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { req.emit('redirect', 302, 'GET', 'https://evil.example/x.sha256') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/checksum/i) expect(madeRequests.length).toBe(1) // never reached the installer request @@ -237,7 +237,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { req.emit('response', new FakeResponse(404)) }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/no checksum/i) expect(madeRequests.length).toBe(1) @@ -246,7 +246,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { it('refuses a malformed checksum file (no 64-hex digest)', async () => { requestScripts.push(checksumOk('not a digest at all\n')) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/malformed/i) expect(madeRequests.length).toBe(1) @@ -262,7 +262,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { res.emit('end') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/canceled/i) expect(madeRequests.length).toBe(1) @@ -277,7 +277,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { cancelAppUpdate() }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/canceled/i) expect(madeRequests[1]!.aborted).toBe(true) @@ -294,7 +294,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { // …and then nothing: the stream stalls. }) const { wc } = fakeWc() - const pending = downloadAppUpdate(ASSET, wc) + const pending = downloadAppUpdate(wc, ASSET) await vi.advanceTimersByTimeAsync(61_000) const r = await pending expect(r.ok).toBe(false) @@ -310,7 +310,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { req.emit('response', new FakeResponse(503)) }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(false) expect(r.error).toMatch(/503/) }) @@ -327,7 +327,7 @@ describe('downloadAppUpdate (CL4 behavior pins)', () => { res.emit('end') }) const { wc } = fakeWc() - const r = await downloadAppUpdate(ASSET, wc) + const r = await downloadAppUpdate(wc, ASSET) expect(r.ok).toBe(true) }) }) diff --git a/test/validation.test.ts b/test/validation.test.ts index ce96b35..fa80a4e 100644 --- a/test/validation.test.ts +++ b/test/validation.test.ts @@ -7,7 +7,7 @@ import { isTemplateLike, isValidSource, isValidMediaItem -} from '../src/main/validation' +} from '../src/main/core/validation' import { isValidSyncTime, type HistoryEntry, diff --git a/test/ytdlpPolicy.test.ts b/test/ytdlpPolicy.test.ts index 149b181..8be5fc0 100644 --- a/test/ytdlpPolicy.test.ts +++ b/test/ytdlpPolicy.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { shouldAutoCheckYtdlp, AUTO_UPDATE_INTERVAL_MS } from '../src/main/ytdlpPolicy' +import { shouldAutoCheckYtdlp, AUTO_UPDATE_INTERVAL_MS } from '../src/main/core/ytdlpPolicy' const NOW = 1_782_435_469_486 // arbitrary fixed "now"