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:
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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'
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,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,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,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
|
||||
|
||||
+7
-7
@@ -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).
|
||||
+13
-13
@@ -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: {
|
||||
+15
-15
@@ -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: {
|
||||
+2
-2
@@ -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'
|
||||
+3
-3
@@ -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({
|
||||
+2
-2
@@ -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'
|
||||
|
||||
+3
-3
@@ -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
-1
@@ -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
-2
@@ -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
-2
@@ -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
-2
@@ -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 {
|
||||
+3
-3
@@ -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
-1
@@ -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 {
|
||||
+1
-1
@@ -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
-1
@@ -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
-1
@@ -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 {
|
||||
Reference in New Issue
Block a user