From 6becd5779856321be1394893b590398acf5e5e71 Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Tue, 30 Jun 2026 20:33:25 +0200 Subject: [PATCH] =?UTF-8?q?refactor(lib,playback):=20fold=20utils/cache+th?= =?UTF-8?q?emes=20into=20lib;=20playbackScheduleFormat=E2=86=92playback=20?= =?UTF-8?q?(drains=20utils/format)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/app/bootstrap.ts | 4 ++-- src/app/startupSplash.test.ts | 4 ++-- src/app/startupSplash.ts | 2 +- src/components/ThemeMigrationNotice.tsx | 2 +- src/features/artist/hooks/useArtistInfoBatch.ts | 2 +- .../fullscreenPlayer/components/FsPlayBtn.tsx | 2 +- .../nowPlaying/components/MobilePlayerView.tsx | 2 +- .../nowPlaying/hooks/useNowPlayingFetchers.ts | 2 +- .../playback/components/PlaybackDelayModal.tsx | 2 +- .../playback/components/PlaybackScheduleBadge.tsx | 2 +- src/features/playback/components/PlayerBar.tsx | 2 +- .../components/playerBar/PlayerTransportControls.tsx | 2 +- src/features/playback/store/audioEventHandlers.ts | 2 +- .../playback/store/engineLoadTrackAtPosition.ts | 2 +- src/features/playback/store/playTrackAction.ts | 2 +- .../playback/store/queueUndoAudioRestore.test.ts | 2 +- src/features/playback/store/resumeAction.ts | 2 +- .../playback/utils}/playbackScheduleFormat.ts | 0 src/features/settings/components/InstalledThemes.tsx | 6 +++--- .../settings/components/ThemeImportSection.tsx | 2 +- .../settings/components/ThemeStoreSection.test.tsx | 6 +++--- .../settings/components/ThemeStoreSection.tsx | 6 +++--- src/features/settings/components/ThemesTab.tsx | 2 +- src/features/settings/hooks/useThemeUpdates.test.ts | 4 ++-- src/features/settings/hooks/useThemeUpdates.ts | 2 +- src/hotCachePrefetch.ts | 4 ++-- .../cache/ephemeralTierReconcile.test.ts | 4 ++-- src/{utils => lib}/cache/ephemeralTierReconcile.ts | 4 ++-- src/{utils => lib}/cache/hotCacheGate.ts | 0 src/{utils => lib}/cache/nowPlayingCache.ts | 0 src/{utils => lib}/themes/fixedThemes.ts | 0 .../themes/installThemeFromRegistry.test.ts | 12 ++++++------ .../themes/installThemeFromRegistry.ts | 6 +++--- .../themes/startupThemeAppearance.test.ts | 4 ++-- src/{utils => lib}/themes/startupThemeAppearance.ts | 2 +- src/{utils => lib}/themes/themeInjection.test.ts | 4 ++-- src/{utils => lib}/themes/themeInjection.ts | 2 +- src/{utils => lib}/themes/themeMigration.test.ts | 4 ++-- src/{utils => lib}/themes/themeMigration.ts | 2 +- src/{utils => lib}/themes/themeRegistry.test.ts | 2 +- src/{utils => lib}/themes/themeRegistry.ts | 0 src/{utils => lib}/themes/uninstallTheme.test.ts | 6 +++--- src/{utils => lib}/themes/uninstallTheme.ts | 4 ++-- .../themes/validateThemePackage.test.ts | 2 +- src/{utils => lib}/themes/validateThemePackage.ts | 4 ++-- src/store/localPlaybackStore.ts | 4 ++-- 47 files changed, 69 insertions(+), 69 deletions(-) rename src/{utils/format => features/playback/utils}/playbackScheduleFormat.ts (100%) rename src/{utils => lib}/cache/ephemeralTierReconcile.test.ts (93%) rename src/{utils => lib}/cache/ephemeralTierReconcile.ts (93%) rename src/{utils => lib}/cache/hotCacheGate.ts (100%) rename src/{utils => lib}/cache/nowPlayingCache.ts (100%) rename src/{utils => lib}/themes/fixedThemes.ts (100%) rename src/{utils => lib}/themes/installThemeFromRegistry.test.ts (77%) rename src/{utils => lib}/themes/installThemeFromRegistry.ts (84%) rename src/{utils => lib}/themes/startupThemeAppearance.test.ts (96%) rename src/{utils => lib}/themes/startupThemeAppearance.ts (99%) rename src/{utils => lib}/themes/themeInjection.test.ts (97%) rename src/{utils => lib}/themes/themeInjection.ts (98%) rename src/{utils => lib}/themes/themeMigration.test.ts (98%) rename src/{utils => lib}/themes/themeMigration.ts (98%) rename src/{utils => lib}/themes/themeRegistry.test.ts (97%) rename src/{utils => lib}/themes/themeRegistry.ts (100%) rename src/{utils => lib}/themes/uninstallTheme.test.ts (92%) rename src/{utils => lib}/themes/uninstallTheme.ts (89%) rename src/{utils => lib}/themes/validateThemePackage.test.ts (97%) rename src/{utils => lib}/themes/validateThemePackage.ts (97%) diff --git a/src/App.tsx b/src/App.tsx index cfd09135..075b2fc3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ import { usePlayerStore } from '@/features/playback/store/playerStore'; import { useLyricsStore } from './store/lyricsStore'; import { useThemeStore } from './store/themeStore'; import { useInstalledThemesStore } from './store/installedThemesStore'; -import { syncInjectedThemes } from './utils/themes/themeInjection'; +import { syncInjectedThemes } from '@/lib/themes/themeInjection'; import { useThemeScheduler } from '@/app/hooks/useThemeScheduler'; import { useFontStore } from './store/fontStore'; import { getWindowKind } from './app/windowKind'; diff --git a/src/app/bootstrap.ts b/src/app/bootstrap.ts index 01f94b6a..d6bf7ce6 100644 --- a/src/app/bootstrap.ts +++ b/src/app/bootstrap.ts @@ -3,9 +3,9 @@ import { configureStartupSplash } from './startupSplash'; import { setupMusicNetworkRuntime } from './musicNetworkBridge'; import { invoke } from '@tauri-apps/api/core'; import { getWindowKind } from './windowKind'; -import { migrateThemeSelection } from '../utils/themes/themeMigration'; +import { migrateThemeSelection } from '@/lib/themes/themeMigration'; import { getScheduledTheme, useThemeStore } from '../store/themeStore'; -import { syncInjectedThemes } from '../utils/themes/themeInjection'; +import { syncInjectedThemes } from '@/lib/themes/themeInjection'; import { useInstalledThemesStore, type InstalledTheme } from '../store/installedThemesStore'; /** Sync backend HTTP User-Agent from the main webview once at startup. */ diff --git a/src/app/startupSplash.test.ts b/src/app/startupSplash.test.ts index 6a66770e..6f85b2a8 100644 --- a/src/app/startupSplash.test.ts +++ b/src/app/startupSplash.test.ts @@ -9,7 +9,7 @@ vi.mock('./windowKind', () => ({ getWindowKind: vi.fn(() => 'main'), })); -vi.mock('../utils/themes/startupThemeAppearance', () => ({ +vi.mock('@/lib/themes/startupThemeAppearance', () => ({ applyStartupSplashThemeFromStorage: vi.fn(() => 'mocha'), })); @@ -18,7 +18,7 @@ vi.mock('@tauri-apps/api/webviewWindow', () => ({ })); import { getWindowKind } from './windowKind'; -import { applyStartupSplashThemeFromStorage } from '../utils/themes/startupThemeAppearance'; +import { applyStartupSplashThemeFromStorage } from '@/lib/themes/startupThemeAppearance'; describe('startupSplash', () => { beforeEach(() => { diff --git a/src/app/startupSplash.ts b/src/app/startupSplash.ts index 495985ad..592319db 100644 --- a/src/app/startupSplash.ts +++ b/src/app/startupSplash.ts @@ -1,5 +1,5 @@ import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; -import { applyStartupSplashThemeFromStorage } from '../utils/themes/startupThemeAppearance'; +import { applyStartupSplashThemeFromStorage } from '@/lib/themes/startupThemeAppearance'; import { getWindowKind } from './windowKind'; export const STARTUP_SPLASH_ID = 'app-startup-splash'; diff --git a/src/components/ThemeMigrationNotice.tsx b/src/components/ThemeMigrationNotice.tsx index c2be8635..e1312381 100644 --- a/src/components/ThemeMigrationNotice.tsx +++ b/src/components/ThemeMigrationNotice.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import ConfirmModal from './ConfirmModal'; -import { readThemeMigrationNotice, clearThemeMigrationNotice } from '../utils/themes/themeMigration'; +import { readThemeMigrationNotice, clearThemeMigrationNotice } from '@/lib/themes/themeMigration'; /** * One-time, dismissible notice shown after the slim-bundle migration reset a diff --git a/src/features/artist/hooks/useArtistInfoBatch.ts b/src/features/artist/hooks/useArtistInfoBatch.ts index 0385626e..499c6e15 100644 --- a/src/features/artist/hooks/useArtistInfoBatch.ts +++ b/src/features/artist/hooks/useArtistInfoBatch.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from 'react'; import { getArtistInfoForServer } from '@/lib/api/subsonicArtists'; import type { SubsonicArtistInfo, SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes'; -import { makeCache } from '@/utils/cache/nowPlayingCache'; +import { makeCache } from '@/lib/cache/nowPlayingCache'; const artistInfoCache = makeCache(); diff --git a/src/features/fullscreenPlayer/components/FsPlayBtn.tsx b/src/features/fullscreenPlayer/components/FsPlayBtn.tsx index 92b2f59c..f7847c6c 100644 --- a/src/features/fullscreenPlayer/components/FsPlayBtn.tsx +++ b/src/features/fullscreenPlayer/components/FsPlayBtn.tsx @@ -3,7 +3,7 @@ import { Moon, Pause, Play, Sunrise } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '@/features/playback/store/playerStore'; import { usePlaybackDelayPress } from '@/features/playback/hooks/usePlaybackDelayPress'; -import { usePlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat'; +import { usePlaybackScheduleRemaining } from '@/features/playback/utils/playbackScheduleFormat'; import PlaybackDelayModal from '@/features/playback/components/PlaybackDelayModal'; import PlaybackScheduleBadge from '@/features/playback/components/PlaybackScheduleBadge'; diff --git a/src/features/nowPlaying/components/MobilePlayerView.tsx b/src/features/nowPlaying/components/MobilePlayerView.tsx index 6b1b5958..2f54d994 100644 --- a/src/features/nowPlaying/components/MobilePlayerView.tsx +++ b/src/features/nowPlaying/components/MobilePlayerView.tsx @@ -26,7 +26,7 @@ import { LyricsPane } from '@/features/lyrics'; import { usePlaybackDelayPress } from '@/features/playback/hooks/usePlaybackDelayPress'; import PlaybackDelayModal from '@/features/playback/components/PlaybackDelayModal'; import PlaybackScheduleBadge from '@/features/playback/components/PlaybackScheduleBadge'; -import { usePlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat'; +import { usePlaybackScheduleRemaining } from '@/features/playback/utils/playbackScheduleFormat'; // ── Color extraction ────────────────────────────────────────────────────────── // Samples a 16×16 canvas to find the most vibrant (highest-saturation, diff --git a/src/features/nowPlaying/hooks/useNowPlayingFetchers.ts b/src/features/nowPlaying/hooks/useNowPlayingFetchers.ts index f7cd1c2d..952212f1 100644 --- a/src/features/nowPlaying/hooks/useNowPlayingFetchers.ts +++ b/src/features/nowPlaying/hooks/useNowPlayingFetchers.ts @@ -5,7 +5,7 @@ import { resolveNpAlbum, resolveNpDiscography, resolveNpSongMeta, resolveNpTopSo import { fetchBandsintownEvents, type BandsintownEvent } from '@/api/bandsintown'; import type { ArtistStats, TrackStats } from '@/music-network'; import { getMusicNetworkRuntimeOrNull } from '@/music-network'; -import { makeCache } from '@/utils/cache/nowPlayingCache'; +import { makeCache } from '@/lib/cache/nowPlayingCache'; import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard'; import { useConnectionStatus } from '@/hooks/useConnectionStatus'; diff --git a/src/features/playback/components/PlaybackDelayModal.tsx b/src/features/playback/components/PlaybackDelayModal.tsx index 06a462d5..93dffb0a 100644 --- a/src/features/playback/components/PlaybackDelayModal.tsx +++ b/src/features/playback/components/PlaybackDelayModal.tsx @@ -7,7 +7,7 @@ import { useAuthStore } from '@/store/authStore'; import { useShallow } from 'zustand/react/shallow'; import type { TFunction } from 'i18next'; -import { formatPlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat'; +import { formatPlaybackScheduleRemaining } from '@/features/playback/utils/playbackScheduleFormat'; import { formatClockTime } from '@/lib/format/formatClockTime'; import { isValidPlaybackSchedulePreviewTimestamp, diff --git a/src/features/playback/components/PlaybackScheduleBadge.tsx b/src/features/playback/components/PlaybackScheduleBadge.tsx index 4362d15d..d1f91b26 100644 --- a/src/features/playback/components/PlaybackScheduleBadge.tsx +++ b/src/features/playback/components/PlaybackScheduleBadge.tsx @@ -3,7 +3,7 @@ import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '@/features/playback/store/playerStore'; import { useShallow } from 'zustand/react/shallow'; -import { formatPlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat'; +import { formatPlaybackScheduleRemaining } from '@/features/playback/utils/playbackScheduleFormat'; import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility'; export interface PlaybackScheduleBadgeProps { diff --git a/src/features/playback/components/PlayerBar.tsx b/src/features/playback/components/PlayerBar.tsx index 9f093980..c5202c34 100644 --- a/src/features/playback/components/PlayerBar.tsx +++ b/src/features/playback/components/PlayerBar.tsx @@ -19,7 +19,7 @@ import { useRadioMetadata } from '@/features/radio'; import { useRadioMprisSync } from '@/features/radio'; import { usePlaybackDelayPress } from '@/features/playback/hooks/usePlaybackDelayPress'; import PlaybackDelayModal from '@/features/playback/components/PlaybackDelayModal'; -import { usePlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat'; +import { usePlaybackScheduleRemaining } from '@/features/playback/utils/playbackScheduleFormat'; import { usePreviewStore } from '@/features/playback/store/previewStore'; import { usePerfProbeFlags } from '@/utils/perf/perfFlags'; import { coerceOpenArtistRefs } from '@/lib/api/openArtistRefs'; diff --git a/src/features/playback/components/playerBar/PlayerTransportControls.tsx b/src/features/playback/components/playerBar/PlayerTransportControls.tsx index 75bbff75..b40017e9 100644 --- a/src/features/playback/components/playerBar/PlayerTransportControls.tsx +++ b/src/features/playback/components/playerBar/PlayerTransportControls.tsx @@ -7,7 +7,7 @@ import { useAutodjTransitionUi } from '@/features/playback/store/autodjTransitio import { usePreviewStore } from '@/features/playback/store/previewStore'; import PlaybackScheduleBadge from '@/features/playback/components/PlaybackScheduleBadge'; import { usePlaybackDelayPress } from '@/features/playback/hooks/usePlaybackDelayPress'; -import { usePlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat'; +import { usePlaybackScheduleRemaining } from '@/features/playback/utils/playbackScheduleFormat'; type RepeatMode = PlayerState['repeatMode']; type PlayPauseBind = ReturnType['playPauseBind']; diff --git a/src/features/playback/store/audioEventHandlers.ts b/src/features/playback/store/audioEventHandlers.ts index 02e42f61..34d3db89 100644 --- a/src/features/playback/store/audioEventHandlers.ts +++ b/src/features/playback/store/audioEventHandlers.ts @@ -8,7 +8,7 @@ import { import { resolveQueueTrack } from '@/features/playback/store/queueTrackView'; import { invoke } from '@tauri-apps/api/core'; import { getMusicNetworkRuntimeOrNull } from '@/music-network'; -import { setDeferHotCachePrefetch } from '@/utils/cache/hotCacheGate'; +import { setDeferHotCachePrefetch } from '@/lib/cache/hotCacheGate'; import { notifyLibraryPlaybackHint } from '@/features/playback/store/libraryPlaybackHint'; import { playListenSessionFinalize, diff --git a/src/features/playback/store/engineLoadTrackAtPosition.ts b/src/features/playback/store/engineLoadTrackAtPosition.ts index 9c49c90b..31171ba8 100644 --- a/src/features/playback/store/engineLoadTrackAtPosition.ts +++ b/src/features/playback/store/engineLoadTrackAtPosition.ts @@ -1,6 +1,6 @@ import type { Track } from '@/lib/media/trackTypes'; import { invoke } from '@tauri-apps/api/core'; -import { setDeferHotCachePrefetch } from '@/utils/cache/hotCacheGate'; +import { setDeferHotCachePrefetch } from '@/lib/cache/hotCacheGate'; import { getPlaybackIndexKey, playbackCacheKeyForTrack, diff --git a/src/features/playback/store/playTrackAction.ts b/src/features/playback/store/playTrackAction.ts index 10892f3f..e1922a76 100644 --- a/src/features/playback/store/playTrackAction.ts +++ b/src/features/playback/store/playTrackAction.ts @@ -1,7 +1,7 @@ import { playbackReportStart, playbackReportStopped } from '@/features/playback/store/playbackReportSession'; import { invoke } from '@tauri-apps/api/core'; import { getMusicNetworkRuntimeOrNull } from '@/music-network'; -import { setDeferHotCachePrefetch } from '@/utils/cache/hotCacheGate'; +import { setDeferHotCachePrefetch } from '@/lib/cache/hotCacheGate'; import { orbitBulkGuard, orbitSnapshot } from '@/store/orbitRuntime'; import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity'; import { diff --git a/src/features/playback/store/queueUndoAudioRestore.test.ts b/src/features/playback/store/queueUndoAudioRestore.test.ts index 1a3fc310..3a0f0b30 100644 --- a/src/features/playback/store/queueUndoAudioRestore.test.ts +++ b/src/features/playback/store/queueUndoAudioRestore.test.ts @@ -38,7 +38,7 @@ const hoisted = vi.hoisted(() => { }); vi.mock('@tauri-apps/api/core', () => ({ invoke: hoisted.invokeMock })); -vi.mock('@/utils/cache/hotCacheGate', () => ({ setDeferHotCachePrefetch: hoisted.setDeferHotCachePrefetchMock })); +vi.mock('@/lib/cache/hotCacheGate', () => ({ setDeferHotCachePrefetch: hoisted.setDeferHotCachePrefetchMock })); vi.mock('@/features/playback/utils/playback/resolvePlaybackUrl', () => ({ resolvePlaybackUrl: hoisted.resolvePlaybackUrlMock })); vi.mock('@/features/playback/utils/audio/resolveReplayGainDb', () => ({ resolveReplayGainDb: hoisted.resolveReplayGainDbMock })); vi.mock('@/store/authStore', () => ({ useAuthStore: { getState: () => hoisted.auth } })); diff --git a/src/features/playback/store/resumeAction.ts b/src/features/playback/store/resumeAction.ts index dc8a429d..622f0e86 100644 --- a/src/features/playback/store/resumeAction.ts +++ b/src/features/playback/store/resumeAction.ts @@ -1,7 +1,7 @@ import { getSong } from '@/lib/api/subsonicLibrary'; import { invoke } from '@tauri-apps/api/core'; import { estimateLivePosition, orbitSnapshot } from '@/store/orbitRuntime'; -import { setDeferHotCachePrefetch } from '@/utils/cache/hotCacheGate'; +import { setDeferHotCachePrefetch } from '@/lib/cache/hotCacheGate'; import { getPlaybackCacheServerKey, getPlaybackIndexKey, diff --git a/src/utils/format/playbackScheduleFormat.ts b/src/features/playback/utils/playbackScheduleFormat.ts similarity index 100% rename from src/utils/format/playbackScheduleFormat.ts rename to src/features/playback/utils/playbackScheduleFormat.ts diff --git a/src/features/settings/components/InstalledThemes.tsx b/src/features/settings/components/InstalledThemes.tsx index 786b035b..c79aafed 100644 --- a/src/features/settings/components/InstalledThemes.tsx +++ b/src/features/settings/components/InstalledThemes.tsx @@ -3,13 +3,13 @@ import { Check, RefreshCw, X } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useThemeStore } from '@/store/themeStore'; import { useInstalledThemesStore } from '@/store/installedThemesStore'; -import { uninstallTheme } from '@/utils/themes/uninstallTheme'; -import { installThemeFromRegistry } from '@/utils/themes/installThemeFromRegistry'; +import { uninstallTheme } from '@/lib/themes/uninstallTheme'; +import { installThemeFromRegistry } from '@/lib/themes/installThemeFromRegistry'; import { useThemeUpdates } from '@/features/settings/hooks/useThemeUpdates'; import { useThemeAnimationRisk } from '@/features/settings/hooks/useThemeAnimationRisk'; import { showToast } from '@/utils/ui/toast'; import { AnimatedThemeBadge } from '@/features/settings/components/AnimatedThemeBadge'; -import { FIXED_THEMES } from '@/utils/themes/fixedThemes'; +import { FIXED_THEMES } from '@/lib/themes/fixedThemes'; /** Pull a 3-band swatch (bg / card / accent) out of an installed theme's CSS. */ function swatch(css: string): { bg: string; card: string; accent: string } { diff --git a/src/features/settings/components/ThemeImportSection.tsx b/src/features/settings/components/ThemeImportSection.tsx index a4521e70..e2603ddb 100644 --- a/src/features/settings/components/ThemeImportSection.tsx +++ b/src/features/settings/components/ThemeImportSection.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { invoke } from '@tauri-apps/api/core'; import { open } from '@tauri-apps/plugin-dialog'; import { useInstalledThemesStore } from '@/store/installedThemesStore'; -import { validateThemePackage, type ValidatedTheme } from '@/utils/themes/validateThemePackage'; +import { validateThemePackage, type ValidatedTheme } from '@/lib/themes/validateThemePackage'; import { showToast } from '@/utils/ui/toast'; import ConfirmModal from '@/components/ConfirmModal'; diff --git a/src/features/settings/components/ThemeStoreSection.test.tsx b/src/features/settings/components/ThemeStoreSection.test.tsx index 29589ea2..6d4cf076 100644 --- a/src/features/settings/components/ThemeStoreSection.test.tsx +++ b/src/features/settings/components/ThemeStoreSection.test.tsx @@ -3,16 +3,16 @@ import { screen, waitFor, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { renderWithProviders } from '@/test/helpers/renderWithProviders'; import { ThemeStoreSection } from '@/features/settings/components/ThemeStoreSection'; -import type { FetchRegistryResult, Registry, RegistryTheme } from '@/utils/themes/themeRegistry'; +import type { FetchRegistryResult, Registry, RegistryTheme } from '@/lib/themes/themeRegistry'; // Control the registry the store browses so pagination/refresh are deterministic. -vi.mock('@/utils/themes/themeRegistry', () => ({ +vi.mock('@/lib/themes/themeRegistry', () => ({ fetchRegistry: vi.fn(), fetchThemeCss: vi.fn(async () => 'css'), assetUrl: (p: string) => `https://raw.example/${p}`, })); -import { fetchRegistry } from '@/utils/themes/themeRegistry'; +import { fetchRegistry } from '@/lib/themes/themeRegistry'; const fetchRegistryMock = vi.mocked(fetchRegistry); diff --git a/src/features/settings/components/ThemeStoreSection.tsx b/src/features/settings/components/ThemeStoreSection.tsx index 7738fdca..c21b6db4 100644 --- a/src/features/settings/components/ThemeStoreSection.tsx +++ b/src/features/settings/components/ThemeStoreSection.tsx @@ -13,9 +13,9 @@ import { assetUrl, fetchRegistry, type RegistryTheme, -} from '@/utils/themes/themeRegistry'; -import { installThemeFromRegistry } from '@/utils/themes/installThemeFromRegistry'; -import { uninstallTheme } from '@/utils/themes/uninstallTheme'; +} from '@/lib/themes/themeRegistry'; +import { installThemeFromRegistry } from '@/lib/themes/installThemeFromRegistry'; +import { uninstallTheme } from '@/lib/themes/uninstallTheme'; import { isNewer } from '@/utils/componentHelpers/appUpdaterHelpers'; type ModeFilter = 'all' | 'dark' | 'light'; diff --git a/src/features/settings/components/ThemesTab.tsx b/src/features/settings/components/ThemesTab.tsx index 70b6cc9d..965853a5 100644 --- a/src/features/settings/components/ThemesTab.tsx +++ b/src/features/settings/components/ThemesTab.tsx @@ -5,7 +5,7 @@ import { useThemeStore } from '@/store/themeStore'; import { useInstalledThemesStore } from '@/store/installedThemesStore'; import CustomSelect from '@/ui/CustomSelect'; import BackToTopButton from '@/ui/BackToTopButton'; -import { FIXED_THEMES } from '@/utils/themes/fixedThemes'; +import { FIXED_THEMES } from '@/lib/themes/fixedThemes'; import { InstalledThemes } from '@/features/settings/components/InstalledThemes'; import { ThemeImportSection } from '@/features/settings/components/ThemeImportSection'; import { ThemeStoreSection } from '@/features/settings/components/ThemeStoreSection'; diff --git a/src/features/settings/hooks/useThemeUpdates.test.ts b/src/features/settings/hooks/useThemeUpdates.test.ts index cd7def54..6952e8a0 100644 --- a/src/features/settings/hooks/useThemeUpdates.test.ts +++ b/src/features/settings/hooks/useThemeUpdates.test.ts @@ -1,12 +1,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { renderHook, waitFor } from '@testing-library/react'; -vi.mock('@/utils/themes/themeRegistry', () => ({ +vi.mock('@/lib/themes/themeRegistry', () => ({ fetchRegistry: vi.fn(), getCachedRegistry: vi.fn(() => null), })); -import { fetchRegistry, type Registry } from '@/utils/themes/themeRegistry'; +import { fetchRegistry, type Registry } from '@/lib/themes/themeRegistry'; import { useInstalledThemesStore, type InstalledTheme } from '@/store/installedThemesStore'; import { useThemeUpdates, themeUpdateSignature } from '@/features/settings/hooks/useThemeUpdates'; diff --git a/src/features/settings/hooks/useThemeUpdates.ts b/src/features/settings/hooks/useThemeUpdates.ts index e249979d..69771669 100644 --- a/src/features/settings/hooks/useThemeUpdates.ts +++ b/src/features/settings/hooks/useThemeUpdates.ts @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from 'react'; import { isNewer } from '@/utils/componentHelpers/appUpdaterHelpers'; -import { fetchRegistry, getCachedRegistry, type Registry, type RegistryTheme } from '@/utils/themes/themeRegistry'; +import { fetchRegistry, getCachedRegistry, type Registry, type RegistryTheme } from '@/lib/themes/themeRegistry'; import { useInstalledThemesStore } from '@/store/installedThemesStore'; // Refresh the registry from source once per app launch (not just from the diff --git a/src/hotCachePrefetch.ts b/src/hotCachePrefetch.ts index b4ef784b..8ba2f405 100644 --- a/src/hotCachePrefetch.ts +++ b/src/hotCachePrefetch.ts @@ -13,7 +13,7 @@ import { bumpHotCachePreviousTrackGrace, clearHotCachePreviousGrace, getDeferHotCachePrefetch, -} from './utils/cache/hotCacheGate'; +} from '@/lib/cache/hotCacheGate'; import { PREFETCH_AHEAD, type PrefetchJob, @@ -27,7 +27,7 @@ import { scheduleAnalysisQueuePruneFromPlaybackQueue, resetAnalysisPruneState, } from './hotCachePrefetch/analysisPrune'; -import { reconcileEphemeralCache } from './utils/cache/ephemeralTierReconcile'; +import { reconcileEphemeralCache } from '@/lib/cache/ephemeralTierReconcile'; import { hasLocalPersistentPlaybackBytes } from '@/store/localPlaybackResolve'; /** Periodic index↔disk sync (stale rows + empty dirs); unindexed files evicted only on budget pressure. */ diff --git a/src/utils/cache/ephemeralTierReconcile.test.ts b/src/lib/cache/ephemeralTierReconcile.test.ts similarity index 93% rename from src/utils/cache/ephemeralTierReconcile.test.ts rename to src/lib/cache/ephemeralTierReconcile.test.ts index 404dfd1a..61ef9fcf 100644 --- a/src/utils/cache/ephemeralTierReconcile.test.ts +++ b/src/lib/cache/ephemeralTierReconcile.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { reconcileEphemeralCache } from './ephemeralTierReconcile'; -import { useLocalPlaybackStore } from '../../store/localPlaybackStore'; +import { reconcileEphemeralCache } from '@/lib/cache/ephemeralTierReconcile'; +import { useLocalPlaybackStore } from '@/store/localPlaybackStore'; vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn(), diff --git a/src/utils/cache/ephemeralTierReconcile.ts b/src/lib/cache/ephemeralTierReconcile.ts similarity index 93% rename from src/utils/cache/ephemeralTierReconcile.ts rename to src/lib/cache/ephemeralTierReconcile.ts index 00c6164d..14ef3aad 100644 --- a/src/utils/cache/ephemeralTierReconcile.ts +++ b/src/lib/cache/ephemeralTierReconcile.ts @@ -1,6 +1,6 @@ import { invoke } from '@tauri-apps/api/core'; -import { useLocalPlaybackStore } from '../../store/localPlaybackStore'; -import { parseLocalPlaybackEntryKey } from '../../store/localPlaybackKeys'; +import { useLocalPlaybackStore } from '@/store/localPlaybackStore'; +import { parseLocalPlaybackEntryKey } from '@/store/localPlaybackKeys'; import { getMediaDir } from '@/lib/media/mediaDir'; export interface EphemeralReconcileResult { diff --git a/src/utils/cache/hotCacheGate.ts b/src/lib/cache/hotCacheGate.ts similarity index 100% rename from src/utils/cache/hotCacheGate.ts rename to src/lib/cache/hotCacheGate.ts diff --git a/src/utils/cache/nowPlayingCache.ts b/src/lib/cache/nowPlayingCache.ts similarity index 100% rename from src/utils/cache/nowPlayingCache.ts rename to src/lib/cache/nowPlayingCache.ts diff --git a/src/utils/themes/fixedThemes.ts b/src/lib/themes/fixedThemes.ts similarity index 100% rename from src/utils/themes/fixedThemes.ts rename to src/lib/themes/fixedThemes.ts diff --git a/src/utils/themes/installThemeFromRegistry.test.ts b/src/lib/themes/installThemeFromRegistry.test.ts similarity index 77% rename from src/utils/themes/installThemeFromRegistry.test.ts rename to src/lib/themes/installThemeFromRegistry.test.ts index 7324ea3d..d87dffbd 100644 --- a/src/utils/themes/installThemeFromRegistry.test.ts +++ b/src/lib/themes/installThemeFromRegistry.test.ts @@ -1,12 +1,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('./themeRegistry', () => ({ fetchThemeCss: vi.fn() })); -vi.mock('./themeInjection', () => ({ validateThemeCss: vi.fn() })); +vi.mock('@/lib/themes/themeRegistry', () => ({ fetchThemeCss: vi.fn() })); +vi.mock('@/lib/themes/themeInjection', () => ({ validateThemeCss: vi.fn() })); -import { fetchThemeCss, type RegistryTheme } from './themeRegistry'; -import { validateThemeCss } from './themeInjection'; -import { useInstalledThemesStore } from '../../store/installedThemesStore'; -import { installThemeFromRegistry } from './installThemeFromRegistry'; +import { fetchThemeCss, type RegistryTheme } from '@/lib/themes/themeRegistry'; +import { validateThemeCss } from '@/lib/themes/themeInjection'; +import { useInstalledThemesStore } from '@/store/installedThemesStore'; +import { installThemeFromRegistry } from '@/lib/themes/installThemeFromRegistry'; const fetchCss = vi.mocked(fetchThemeCss); const validate = vi.mocked(validateThemeCss); diff --git a/src/utils/themes/installThemeFromRegistry.ts b/src/lib/themes/installThemeFromRegistry.ts similarity index 84% rename from src/utils/themes/installThemeFromRegistry.ts rename to src/lib/themes/installThemeFromRegistry.ts index 579553dd..1c88a726 100644 --- a/src/utils/themes/installThemeFromRegistry.ts +++ b/src/lib/themes/installThemeFromRegistry.ts @@ -1,6 +1,6 @@ -import { fetchThemeCss, type RegistryTheme } from './themeRegistry'; -import { validateThemeCss } from './themeInjection'; -import { useInstalledThemesStore } from '../../store/installedThemesStore'; +import { fetchThemeCss, type RegistryTheme } from '@/lib/themes/themeRegistry'; +import { validateThemeCss } from '@/lib/themes/themeInjection'; +import { useInstalledThemesStore } from '@/store/installedThemesStore'; export type InstallResult = 'ok' | 'invalid' | 'error'; diff --git a/src/utils/themes/startupThemeAppearance.test.ts b/src/lib/themes/startupThemeAppearance.test.ts similarity index 96% rename from src/utils/themes/startupThemeAppearance.test.ts rename to src/lib/themes/startupThemeAppearance.test.ts index 3c7ed76f..393bd187 100644 --- a/src/utils/themes/startupThemeAppearance.test.ts +++ b/src/lib/themes/startupThemeAppearance.test.ts @@ -1,13 +1,13 @@ import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { BUILTIN_SPLASH_PALETTES } from '../../config/startupSplashPalettes'; +import { BUILTIN_SPLASH_PALETTES } from '@/config/startupSplashPalettes'; import { applyStartupSplashPalette, resolveEffectiveThemeId, resolveScheduledThemeId, resolveSplashPalette, -} from './startupThemeAppearance'; +} from '@/lib/themes/startupThemeAppearance'; describe('startupThemeAppearance', () => { beforeEach(() => { diff --git a/src/utils/themes/startupThemeAppearance.ts b/src/lib/themes/startupThemeAppearance.ts similarity index 99% rename from src/utils/themes/startupThemeAppearance.ts rename to src/lib/themes/startupThemeAppearance.ts index 679ea2b4..cf3e2e89 100644 --- a/src/utils/themes/startupThemeAppearance.ts +++ b/src/lib/themes/startupThemeAppearance.ts @@ -2,7 +2,7 @@ import { BUILTIN_SPLASH_PALETTES, BUILTIN_THEME_IDS, type StartupSplashPalette, -} from '../../config/startupSplashPalettes'; +} from '@/config/startupSplashPalettes'; export type PersistedThemeState = { enableThemeScheduler: boolean; diff --git a/src/utils/themes/themeInjection.test.ts b/src/lib/themes/themeInjection.test.ts similarity index 97% rename from src/utils/themes/themeInjection.test.ts rename to src/lib/themes/themeInjection.test.ts index 89452b12..7a49739f 100644 --- a/src/utils/themes/themeInjection.test.ts +++ b/src/lib/themes/themeInjection.test.ts @@ -8,8 +8,8 @@ import { validateThemeCss, injectTheme, syncInjectedThemes, -} from './themeInjection'; -import type { InstalledTheme } from '../../store/installedThemesStore'; +} from '@/lib/themes/themeInjection'; +import type { InstalledTheme } from '@/store/installedThemesStore'; const ATTR = 'data-installed-theme'; diff --git a/src/utils/themes/themeInjection.ts b/src/lib/themes/themeInjection.ts similarity index 98% rename from src/utils/themes/themeInjection.ts rename to src/lib/themes/themeInjection.ts index 06d9960f..e118d6f5 100644 --- a/src/utils/themes/themeInjection.ts +++ b/src/lib/themes/themeInjection.ts @@ -1,4 +1,4 @@ -import type { InstalledTheme } from '../../store/installedThemesStore'; +import type { InstalledTheme } from '@/store/installedThemesStore'; /** * Runtime CSS injection for installed community themes. Built-in themes are diff --git a/src/utils/themes/themeMigration.test.ts b/src/lib/themes/themeMigration.test.ts similarity index 98% rename from src/utils/themes/themeMigration.test.ts rename to src/lib/themes/themeMigration.test.ts index 6cfd0238..ec79aac5 100644 --- a/src/utils/themes/themeMigration.test.ts +++ b/src/lib/themes/themeMigration.test.ts @@ -12,8 +12,8 @@ import { migrateThemeSelection, readThemeMigrationNotice, clearThemeMigrationNotice, -} from './themeMigration'; -import { FIXED_THEMES } from '@/utils/themes/fixedThemes'; +} from '@/lib/themes/themeMigration'; +import { FIXED_THEMES } from '@/lib/themes/fixedThemes'; const THEME_KEY = 'psysonic_theme'; const INSTALLED_KEY = 'psysonic_installed_themes'; diff --git a/src/utils/themes/themeMigration.ts b/src/lib/themes/themeMigration.ts similarity index 98% rename from src/utils/themes/themeMigration.ts rename to src/lib/themes/themeMigration.ts index 6d405e3f..2802d9ab 100644 --- a/src/utils/themes/themeMigration.ts +++ b/src/lib/themes/themeMigration.ts @@ -1,4 +1,4 @@ -import { FIXED_THEMES } from '@/utils/themes/fixedThemes'; +import { FIXED_THEMES } from '@/lib/themes/fixedThemes'; /** * Slim-bundle theme migration (C5). Older builds bundled ~80 palettes that now diff --git a/src/utils/themes/themeRegistry.test.ts b/src/lib/themes/themeRegistry.test.ts similarity index 97% rename from src/utils/themes/themeRegistry.test.ts rename to src/lib/themes/themeRegistry.test.ts index 1cf6f9cc..0235e146 100644 --- a/src/utils/themes/themeRegistry.test.ts +++ b/src/lib/themes/themeRegistry.test.ts @@ -3,7 +3,7 @@ * stale-on-error fallback, and malformed-cache tolerance. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { fetchRegistry, getCachedRegistry } from './themeRegistry'; +import { fetchRegistry, getCachedRegistry } from '@/lib/themes/themeRegistry'; const CACHE_KEY = 'psysonic_theme_registry_cache'; const NOW = 1_000_000_000; diff --git a/src/utils/themes/themeRegistry.ts b/src/lib/themes/themeRegistry.ts similarity index 100% rename from src/utils/themes/themeRegistry.ts rename to src/lib/themes/themeRegistry.ts diff --git a/src/utils/themes/uninstallTheme.test.ts b/src/lib/themes/uninstallTheme.test.ts similarity index 92% rename from src/utils/themes/uninstallTheme.test.ts rename to src/lib/themes/uninstallTheme.test.ts index efec3839..9e7a4988 100644 --- a/src/utils/themes/uninstallTheme.test.ts +++ b/src/lib/themes/uninstallTheme.test.ts @@ -3,9 +3,9 @@ * selection slot that referenced it (active + scheduler day/night). */ import { beforeEach, describe, expect, it } from 'vitest'; -import { uninstallTheme } from './uninstallTheme'; -import { useInstalledThemesStore, type InstalledTheme } from '../../store/installedThemesStore'; -import { useThemeStore } from '../../store/themeStore'; +import { uninstallTheme } from '@/lib/themes/uninstallTheme'; +import { useInstalledThemesStore, type InstalledTheme } from '@/store/installedThemesStore'; +import { useThemeStore } from '@/store/themeStore'; function mk(id: string, mode: 'dark' | 'light' = 'dark'): InstalledTheme { return { id, name: id, author: 'a', version: '1.0.0', description: '', mode, css: `[data-theme='${id}']{--accent:#fff;}`, installedAt: 0 }; diff --git a/src/utils/themes/uninstallTheme.ts b/src/lib/themes/uninstallTheme.ts similarity index 89% rename from src/utils/themes/uninstallTheme.ts rename to src/lib/themes/uninstallTheme.ts index d7acd913..527f277b 100644 --- a/src/utils/themes/uninstallTheme.ts +++ b/src/lib/themes/uninstallTheme.ts @@ -1,5 +1,5 @@ -import { useInstalledThemesStore } from '../../store/installedThemesStore'; -import { useThemeStore } from '../../store/themeStore'; +import { useInstalledThemesStore } from '@/store/installedThemesStore'; +import { useThemeStore } from '@/store/themeStore'; /** * Uninstall a community theme and repair any theme selection that pointed at it. diff --git a/src/utils/themes/validateThemePackage.test.ts b/src/lib/themes/validateThemePackage.test.ts similarity index 97% rename from src/utils/themes/validateThemePackage.test.ts rename to src/lib/themes/validateThemePackage.test.ts index d31d4a3b..05438435 100644 --- a/src/utils/themes/validateThemePackage.test.ts +++ b/src/lib/themes/validateThemePackage.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { validateThemePackage } from './validateThemePackage'; +import { validateThemePackage } from '@/lib/themes/validateThemePackage'; /** A minimal floor-passing theme.css for `id`. */ function css(id = 'my-theme'): string { diff --git a/src/utils/themes/validateThemePackage.ts b/src/lib/themes/validateThemePackage.ts similarity index 97% rename from src/utils/themes/validateThemePackage.ts rename to src/lib/themes/validateThemePackage.ts index 5b0661e4..58ff4a69 100644 --- a/src/utils/themes/validateThemePackage.ts +++ b/src/lib/themes/validateThemePackage.ts @@ -1,5 +1,5 @@ -import { validateThemeCss } from './themeInjection'; -import { FIXED_THEMES } from '@/utils/themes/fixedThemes'; +import { validateThemeCss } from '@/lib/themes/themeInjection'; +import { FIXED_THEMES } from '@/lib/themes/fixedThemes'; /** * Validation for a locally imported theme package (a .zip holding manifest.json diff --git a/src/store/localPlaybackStore.ts b/src/store/localPlaybackStore.ts index fb407418..bd6568cf 100644 --- a/src/store/localPlaybackStore.ts +++ b/src/store/localPlaybackStore.ts @@ -2,7 +2,7 @@ import type { QueueItemRef } from '@/lib/media/trackTypes'; import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import { invoke } from '@tauri-apps/api/core'; -import { isHotCachePreviousTrackUnderGrace } from '../utils/cache/hotCacheGate'; +import { isHotCachePreviousTrackUnderGrace } from '@/lib/cache/hotCacheGate'; import { emitAnalysisStorageChanged } from './analysisSync'; import { useAuthStore } from './authStore'; import { localPlaybackEntryKey, parseLocalPlaybackEntryKey } from './localPlaybackKeys'; @@ -15,7 +15,7 @@ import { evictEphemeralOrphansToFit, getEphemeralDiskBytes, reconcileEphemeralCache, -} from '../utils/cache/ephemeralTierReconcile'; +} from '@/lib/cache/ephemeralTierReconcile'; export type LocalPlaybackTier = 'ephemeral' | 'library' | 'favorite-auto';