feat(audit): Batch 25 — project reorg (CC12), leading DI args (CC7), CC10 by-design

CC12: renderer views/ (5 screens + Onboarding + settings cards) +
lib/ (theme/thumb/datetime/id/qualityOptions/useClipboardLink/queueStats);
main core/ (buildArgs/validation/indexerCore/ytdlpPolicy). git mv preserved
history; all src+test imports updated. Build emits view chunks from views/,
344 tests + typecheck green, live probe rendered all screens.
CC7: wc/onProgress is the leading arg everywhere — downloadAppUpdate(wc,url),
indexSource(onProgress,url,signal), indexSourceCancelable(onProgress,url).
CC10: closed by-design — jsonStore backs all records; settings stay in
electron-store for DPAPI secret encryption (a deliberate two-store split).
Also closes the UI24/W4 context-menu cross-reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 15:37:28 -04:00
parent f45acb645b
commit 9134e7d216
70 changed files with 160 additions and 151 deletions
+1 -1
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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).
+5 -5
View File
@@ -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<IndexSourceResult> {
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<IndexSourceResult> {
// Returned whenever a Cancel arrives mid-walk. It deliberately does NOT push a
+5 -5
View File
@@ -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())
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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). */
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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<AppUpdateDownload> {
export async function downloadAppUpdate(wc: WebContents, url: string): Promise<AppUpdateDownload> {
if (!isTrustedDownloadUrl(url)) {
return { ok: false, error: 'Refused to download from an untrusted location.' }
}
+1 -1
View File
@@ -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,
+8 -8
View File
@@ -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
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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: {
+3 -3
View File
@@ -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: {
@@ -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'
@@ -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({
@@ -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. */
@@ -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'
@@ -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
@@ -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'
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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).
@@ -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: {
@@ -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'
@@ -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: {
@@ -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'
@@ -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({
@@ -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'
@@ -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 = [
@@ -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'
@@ -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'
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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'
@@ -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 <Card> so it
// stays one direct child of SettingsView's root -- the search filter toggles
@@ -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 {