refactor(utils): group utils/ files into topic folders (Phase L, part 1) (#689)

111 of 122 top-level src/utils/ files move into 16 topic folders (audio,
cache, cover, share, server, playback, playlist, deviceSync, waveform,
mix, format, export, changelog, ui, perf, componentHelpers). True
singletons with no cluster stay at the utils/ root.

Pure file-move: a path-aware codemod rewrote 539 relative-import
specifiers across 275 files; no logic touched. The hot-path coverage
gate list (.github/frontend-hot-path-files.txt) is updated to the new
paths for the 11 gated utils files — a mechanical consequence of the
move, not a CI change. tsc is green.
This commit is contained in:
Frank Stellmacher
2026-05-14 14:27:44 +02:00
committed by GitHub
parent 2409a1fec8
commit 7a7a9f5e6b
324 changed files with 551 additions and 551 deletions
+3 -3
View File
@@ -2,7 +2,7 @@ import { buildCoverArtUrl, coverArtCacheKey, buildDownloadUrl } from '../api/sub
import { setRating, star, unstar } from '../api/subsonicStarRating';
import { getArtistInfo } from '../api/subsonicArtists';
import type { SubsonicSong } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { invoke } from '@tauri-apps/api/core';
@@ -22,9 +22,9 @@ import AlbumTrackList from '../components/AlbumTrackList';
import { AlbumDetailToolbar } from '../components/albumDetail/AlbumDetailToolbar';
import { useCachedUrl } from '../components/CachedImage';
import { useTranslation } from 'react-i18next';
import { showToast } from '../utils/toast';
import { showToast } from '../utils/ui/toast';
import { useSelectionStore } from '../store/selectionStore';
import { sanitizeFilename } from '../utils/albumDetailHelpers';
import { sanitizeFilename } from '../utils/componentHelpers/albumDetailHelpers';
export default function AlbumDetail() {
const { t } = useTranslation();
+3 -3
View File
@@ -2,7 +2,7 @@ import { buildDownloadUrl } from '../api/subsonicStreamUrl';
import { getAlbumsByGenre } from '../api/subsonicGenres';
import { getAlbumList, getAlbum } from '../api/subsonicLibrary';
import type { SubsonicAlbum } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
import AlbumCard from '../components/AlbumCard';
import GenreFilterBar from '../components/GenreFilterBar';
@@ -16,13 +16,13 @@ import { useDownloadModalStore } from '../store/downloadModalStore';
import { usePlayerStore } from '../store/playerStore';
import { invoke } from '@tauri-apps/api/core';
import { join } from '@tauri-apps/api/path';
import { showToast } from '../utils/toast';
import { showToast } from '../utils/ui/toast';
import { useZipDownloadStore } from '../store/zipDownloadStore';
import { CheckSquare2, Download, HardDriveDownload, ListMusic, Disc3, ListPlus } from 'lucide-react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../constants/appScroll';
import { useElementClientHeightById } from '../hooks/useResizeClientHeight';
import { usePerfProbeFlags } from '../utils/perfFlags';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { useRangeSelection } from '../hooks/useRangeSelection';
const ALBUM_GRID_GAP_PX = 16; // matches --space-4
+7 -7
View File
@@ -3,7 +3,7 @@ import { buildCoverArtUrl, coverArtCacheKey } from '../api/subsonicStreamUrl';
import { setRating, star, unstar } from '../api/subsonicStarRating';
import { getAlbum } from '../api/subsonicLibrary';
import type { SubsonicArtist, SubsonicAlbum, SubsonicSong, SubsonicArtistInfo } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import { useEffect, useState, useRef, Fragment, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import AlbumCard from '../components/AlbumCard';
@@ -22,21 +22,21 @@ import { useTranslation } from 'react-i18next';
import { lastfmIsConfigured } from '../api/lastfm';
import LastfmIcon from '../components/LastfmIcon';
import { invalidateCoverArt } from '../utils/imageCache';
import { showToast } from '../utils/toast';
import { copyEntityShareLink } from '../utils/copyEntityShareLink';
import { extractCoverColors } from '../utils/dynamicColors';
import { showToast } from '../utils/ui/toast';
import { copyEntityShareLink } from '../utils/share/copyEntityShareLink';
import { extractCoverColors } from '../utils/ui/dynamicColors';
import StarRating from '../components/StarRating';
import { useArtistLayoutStore, type ArtistSectionId } from '../store/artistLayoutStore';
import { sanitizeHtml } from '../utils/artistDetailHelpers';
import { sanitizeHtml } from '../utils/componentHelpers/artistDetailHelpers';
import { useArtistDetailData } from '../hooks/useArtistDetailData';
import { useArtistSimilarArtists } from '../hooks/useArtistSimilarArtists';
import {
runArtistDetailPlayAll, runArtistDetailShuffle, runArtistDetailStartRadio,
} from '../utils/runArtistDetailPlay';
} from '../utils/componentHelpers/runArtistDetailPlay';
import {
runArtistEntityRating, runArtistToggleStar, runArtistShare, runArtistImageUpload,
} from '../utils/runArtistDetailActions';
} from '../utils/componentHelpers/runArtistDetailActions';
import ArtistDetailHero from '../components/artistDetail/ArtistDetailHero';
import ArtistDetailTopTracks from '../components/artistDetail/ArtistDetailTopTracks';
import ArtistDetailSimilarArtists from '../components/artistDetail/ArtistDetailSimilarArtists';
+2 -2
View File
@@ -10,14 +10,14 @@ import { useTranslation } from 'react-i18next';
import { useVirtualizer } from '@tanstack/react-virtual';
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../constants/appScroll';
import { useElementClientHeightById } from '../hooks/useResizeClientHeight';
import { usePerfProbeFlags } from '../utils/perfFlags';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import {
ALL_SENTINEL,
ALPHABET,
ARTIST_LIST_LAST_IN_LETTER_EST,
ARTIST_LIST_LETTER_ROW_EST,
ARTIST_LIST_ROW_EST,
} from '../utils/artistsHelpers';
} from '../utils/componentHelpers/artistsHelpers';
import { useArtistsFiltering } from '../hooks/useArtistsFiltering';
import { useArtistsInfiniteScroll } from '../hooks/useArtistsInfiniteScroll';
import { ArtistsGridView } from '../components/artists/ArtistsGridView';
+2 -2
View File
@@ -13,8 +13,8 @@ import { open } from '@tauri-apps/plugin-shell';
import { usePlayerStore } from '../store/playerStore';
import { useAuthStore } from '../store/authStore';
import { useTranslation } from 'react-i18next';
import { copyEntityShareLink } from '../utils/copyEntityShareLink';
import { showToast } from '../utils/toast';
import { copyEntityShareLink } from '../utils/share/copyEntityShareLink';
import { showToast } from '../utils/ui/toast';
/** Strip dangerous tags/attributes from server-provided HTML. Mirrors the
* ArtistDetail sanitiser — kept inline because it's a 10-liner not worth a
+1 -1
View File
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { useVirtualizer } from '@tanstack/react-virtual';
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../constants/appScroll';
import { useElementClientHeightById } from '../hooks/useResizeClientHeight';
import { usePerfProbeFlags } from '../utils/perfFlags';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
const ALL_SENTINEL = 'ALL';
const ALPHABET = [ALL_SENTINEL, '#', ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')];
+5 -5
View File
@@ -10,13 +10,13 @@ import CustomSelect from '../components/CustomSelect';
import { useTranslation } from 'react-i18next';
import { useDeviceSyncStore, DeviceSyncSource } from '../store/deviceSyncStore';
import { useDeviceSyncJobStore } from '../store/deviceSyncJobStore';
import { showToast } from '../utils/toast';
import { showToast } from '../utils/ui/toast';
import { IS_WINDOWS } from '../utils/platform';
import {
formatBytes,
type SourceTab,
} from '../utils/deviceSyncHelpers';
} from '../utils/deviceSync/deviceSyncHelpers';
import { useDeviceSyncDrives } from '../hooks/useDeviceSyncDrives';
import { useDeviceSyncSourceStatuses } from '../hooks/useDeviceSyncSourceStatuses';
import { useDeviceSyncBrowser } from '../hooks/useDeviceSyncBrowser';
@@ -26,13 +26,13 @@ import {
runDeviceSyncMigrationPreview,
runDeviceSyncMigrationExecute,
type MigrationPhase, type MigrationPair, type MigrationResult,
} from '../utils/runDeviceSyncMigration';
} from '../utils/deviceSync/runDeviceSyncMigration';
import {
runDeviceSyncSummaryPrompt,
runDeviceSyncExecute,
type SyncDelta,
} from '../utils/runDeviceSyncExecution';
import { runDeviceSyncChooseFolder } from '../utils/runDeviceSyncChooseFolder';
} from '../utils/deviceSync/runDeviceSyncExecution';
import { runDeviceSyncChooseFolder } from '../utils/deviceSync/runDeviceSyncChooseFolder';
import DeviceSyncHeader from '../components/deviceSync/DeviceSyncHeader';
import DeviceSyncPreSyncModal from '../components/deviceSync/DeviceSyncPreSyncModal';
import DeviceSyncMigrationModal from '../components/deviceSync/DeviceSyncMigrationModal';
+1 -1
View File
@@ -1,6 +1,6 @@
import { setRating, unstar } from '../api/subsonicStarRating';
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong, InternetRadioStation } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTracklistColumns, type ColDef } from '../utils/useTracklistColumns';
import { TopFavoriteArtistsRow } from '../components/favorites/TopFavoriteArtists';
+1 -1
View File
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import {
entryToAlbumIfPresent, entryToTrack,
type Column, type ColumnKind, type NavPos,
} from '../utils/folderBrowserHelpers';
} from '../utils/componentHelpers/folderBrowserHelpers';
import FolderBrowserColumn from '../components/folderBrowser/FolderBrowserColumn';
import { useFolderBrowserNowPlayingPath } from '../hooks/useFolderBrowserNowPlayingPath';
import { useFolderBrowserScrolling } from '../hooks/useFolderBrowserScrolling';
+3 -3
View File
@@ -12,9 +12,9 @@ import { NavLink, useNavigate } from 'react-router-dom';
import { ChevronRight } from 'lucide-react';
import { useHomeStore } from '../store/homeStore';
import { useAuthStore } from '../store/authStore';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mixRatingFilter';
import { usePerfProbeFlags } from '../utils/perfFlags';
import { bumpPerfCounter } from '../utils/perfTelemetry';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mix/mixRatingFilter';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { bumpPerfCounter } from '../utils/perf/perfTelemetry';
import { dedupeById } from '../utils/dedupeById';
/** Match Random Albums overshoot when mix filter uses album/artist axes so hero + discover row can still fill. */
+1 -1
View File
@@ -11,7 +11,7 @@ import { invalidateCoverArt } from '../utils/imageCache';
import CustomSelect from '../components/CustomSelect';
import { useTranslation } from 'react-i18next';
import { open } from '@tauri-apps/plugin-shell';
import { showToast } from '../utils/toast';
import { showToast } from '../utils/ui/toast';
import RadioToolbar, { type RadioSortBy } from '../components/internetRadio/RadioToolbar';
import AlphabetFilterBar from '../components/internetRadio/AlphabetFilterBar';
import RadioCard from '../components/internetRadio/RadioCard';
+2 -2
View File
@@ -11,8 +11,8 @@ import {
DECODED_PASSWORD_VISUAL_MASK,
encodeServerMagicString,
type ServerMagicPayload,
} from '../utils/serverMagicString';
import { shortHostFromServerUrl, serverListDisplayLabel } from '../utils/serverDisplayName';
} from '../utils/server/serverMagicString';
import { shortHostFromServerUrl, serverListDisplayLabel } from '../utils/server/serverDisplayName';
const PsysonicLogo = () => (
<img src="/logo-psysonic.png" width="64" height="64" alt="Psysonic" style={{ borderRadius: 18 }} />
+3 -3
View File
@@ -1,7 +1,7 @@
import { buildDownloadUrl } from '../api/subsonicStreamUrl';
import { getAlbum } from '../api/subsonicLibrary';
import type { SubsonicAlbum } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import AlbumCard from '../components/AlbumCard';
import { ndListLosslessAlbumsPage } from '../api/navidromeBrowse';
@@ -12,8 +12,8 @@ import { useDownloadModalStore } from '../store/downloadModalStore';
import { usePlayerStore } from '../store/playerStore';
import { useZipDownloadStore } from '../store/zipDownloadStore';
import { useRangeSelection } from '../hooks/useRangeSelection';
import { usePerfProbeFlags } from '../utils/perfFlags';
import { showToast } from '../utils/toast';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { showToast } from '../utils/ui/toast';
import { invoke } from '@tauri-apps/api/core';
import { join } from '@tauri-apps/api/path';
import { CheckSquare2, Download, HardDriveDownload, ListPlus } from 'lucide-react';
+1 -1
View File
@@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { buildAndPlayLuckyMix } from '../utils/luckyMix';
import { buildAndPlayLuckyMix } from '../utils/mix/luckyMix';
export default function LuckyMixPage() {
const navigate = useNavigate();
+2 -2
View File
@@ -1,14 +1,14 @@
import { buildCoverArtUrl, coverArtCacheKey } from '../api/subsonicStreamUrl';
import { getAlbumList, getAlbum } from '../api/subsonicLibrary';
import type { SubsonicAlbum } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { ArrowUpDown, ArrowDown, ArrowUp, TrendingUp, UsersRound, Play, ListPlus } from 'lucide-react';
import { useAuthStore } from '../store/authStore';
import { usePlayerStore } from '../store/playerStore';
import CachedImage from '../components/CachedImage';
import { playAlbum } from '../utils/playAlbum';
import { playAlbum } from '../utils/playback/playAlbum';
import { useTranslation } from 'react-i18next';
const PAGE_SIZE = 50;
+1 -1
View File
@@ -13,7 +13,7 @@ import { useDownloadModalStore } from '../store/downloadModalStore';
import { usePlayerStore } from '../store/playerStore';
import { invoke } from '@tauri-apps/api/core';
import { join } from '@tauri-apps/api/path';
import { showToast } from '../utils/toast';
import { showToast } from '../utils/ui/toast';
import { useZipDownloadStore } from '../store/zipDownloadStore';
import { useRangeSelection } from '../hooks/useRangeSelection';
+2 -2
View File
@@ -8,7 +8,7 @@ import { open as shellOpen } from '@tauri-apps/plugin-shell';
import { usePlayerStore } from '../store/playerStore';
import { useAuthStore } from '../store/authStore';
import { useLyricsStore } from '../store/lyricsStore';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import { useCachedUrl } from '../components/CachedImage';
import CachedImage from '../components/CachedImage';
import { useRadioMetadata } from '../hooks/useRadioMetadata';
@@ -25,7 +25,7 @@ import {
formatTime, formatCompact, isoToParts,
buildContributorRows,
type ContributorRow,
} from '../utils/nowPlayingHelpers';
} from '../utils/componentHelpers/nowPlayingHelpers';
import NpCardWrap from '../components/nowPlaying/NpCardWrap';
import NpColumnEl from '../components/nowPlaying/NpColumnEl';
import RadioView from '../components/nowPlaying/RadioView';
+8 -8
View File
@@ -26,9 +26,9 @@ import {
isSmartPlaylistName,
displayPlaylistName,
codecLabel,
} from '../utils/playlistDetailHelpers';
import type { SpotifyCsvTrack } from '../utils/spotifyCsvImport';
import { runPlaylistCsvImport } from '../utils/runPlaylistCsvImport';
} from '../utils/componentHelpers/playlistDetailHelpers';
import type { SpotifyCsvTrack } from '../utils/playlist/spotifyCsvImport';
import { runPlaylistCsvImport } from '../utils/playlist/runPlaylistCsvImport';
import PlaylistEditModal from '../components/playlist/PlaylistEditModal';
import CsvImportReportModal from '../components/playlist/CsvImportReportModal';
import PlaylistSongSearchPanel from '../components/playlist/PlaylistSongSearchPanel';
@@ -36,11 +36,11 @@ import PlaylistSuggestions from '../components/playlist/PlaylistSuggestions';
import PlaylistHero from '../components/playlist/PlaylistHero';
import PlaylistTracklist from '../components/playlist/PlaylistTracklist';
import PlaylistFilterToolbar from '../components/playlist/PlaylistFilterToolbar';
import type { PlaylistSortKey, PlaylistSortDir } from '../utils/playlistDisplayedSongs';
import { runPlaylistZipDownload } from '../utils/runPlaylistZipDownload';
import { runPlaylistSaveMeta } from '../utils/runPlaylistSaveMeta';
import { runPlaylistLoad } from '../utils/runPlaylistLoad';
import { startPlaylistRowDrag } from '../utils/startPlaylistRowDrag';
import type { PlaylistSortKey, PlaylistSortDir } from '../utils/playlist/playlistDisplayedSongs';
import { runPlaylistZipDownload } from '../utils/playlist/runPlaylistZipDownload';
import { runPlaylistSaveMeta } from '../utils/playlist/runPlaylistSaveMeta';
import { runPlaylistLoad } from '../utils/playlist/runPlaylistLoad';
import { startPlaylistRowDrag } from '../utils/playlist/startPlaylistRowDrag';
import { usePlaylistCovers } from '../hooks/usePlaylistCovers';
import { usePlaylistSelection } from '../hooks/usePlaylistSelection';
import { usePlaylistSuggestions } from '../hooks/usePlaylistSuggestions';
+6 -6
View File
@@ -2,7 +2,7 @@ import { getPlaylist } from '../api/subsonicPlaylists';
import { getGenres } from '../api/subsonicGenres';
import { filterSongsToActiveLibrary } from '../api/subsonicLibrary';
import type { SubsonicPlaylist, SubsonicGenre } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { usePlayerStore } from '../store/playerStore';
@@ -11,19 +11,19 @@ import { useAuthStore } from '../store/authStore';
import { useTranslation } from 'react-i18next';
import { useRangeSelection } from '../hooks/useRangeSelection';
import { formatHumanHoursMinutes } from '../utils/formatHumanDuration';
import { formatHumanHoursMinutes } from '../utils/format/formatHumanDuration';
import {
defaultSmartFilters, isSmartPlaylistName,
type SmartFilters, type PendingSmartPlaylist,
} from '../utils/playlistsSmart';
} from '../utils/playlist/playlistsSmart';
import { useSmartCoverCollage } from '../hooks/useSmartCoverCollage';
import { usePlaylistsLibraryScopeCounts } from '../hooks/usePlaylistsLibraryScopeCounts';
import { usePendingSmartPolling } from '../hooks/usePendingSmartPolling';
import { runPlaylistsOpenSmartEditor } from '../utils/runPlaylistsOpenSmartEditor';
import { runPlaylistsSaveSmart } from '../utils/runPlaylistsSaveSmart';
import { runPlaylistsOpenSmartEditor } from '../utils/playlist/runPlaylistsOpenSmartEditor';
import { runPlaylistsSaveSmart } from '../utils/playlist/runPlaylistsSaveSmart';
import {
runPlaylistDelete, runPlaylistDeleteSelected, runPlaylistMergeSelected,
} from '../utils/runPlaylistsActions';
} from '../utils/playlist/runPlaylistsActions';
import PlaylistsSmartEditor from '../components/playlists/PlaylistsSmartEditor';
import PlaylistsHeader from '../components/playlists/PlaylistsHeader';
import PlaylistCard from '../components/playlists/PlaylistCard';
+2 -2
View File
@@ -8,12 +8,12 @@ import AlbumCard from '../components/AlbumCard';
import GenreFilterBar from '../components/GenreFilterBar';
import { useTranslation } from 'react-i18next';
import { useAuthStore } from '../store/authStore';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mixRatingFilter';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mix/mixRatingFilter';
import { useOfflineStore } from '../store/offlineStore';
import { useDownloadModalStore } from '../store/downloadModalStore';
import { invoke } from '@tauri-apps/api/core';
import { join } from '@tauri-apps/api/path';
import { showToast } from '../utils/toast';
import { showToast } from '../utils/ui/toast';
import { useZipDownloadStore } from '../store/zipDownloadStore';
import { useRangeSelection } from '../hooks/useRangeSelection';
+3 -3
View File
@@ -1,7 +1,7 @@
import { star, unstar } from '../api/subsonicStarRating';
import { getGenres } from '../api/subsonicGenres';
import type { SubsonicSong, SubsonicGenre } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useEffect, useMemo, useState } from 'react';
import { usePlayerStore } from '../store/playerStore';
import { usePreviewStore } from '../store/previewStore';
@@ -12,8 +12,8 @@ import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
import {
fetchRandomMixSongsUntilFull,
getMixMinRatingsConfigFromAuth,
} from '../utils/mixRatingFilter';
import { AUDIOBOOK_GENRES, filterRandomMixSongs, formatRandomMixDuration } from '../utils/randomMixHelpers';
} from '../utils/mix/mixRatingFilter';
import { AUDIOBOOK_GENRES, filterRandomMixSongs, formatRandomMixDuration } from '../utils/componentHelpers/randomMixHelpers';
import RandomMixHeader from '../components/randomMix/RandomMixHeader';
import RandomMixFiltersPanel from '../components/randomMix/RandomMixFiltersPanel';
import RandomMixGenrePanel from '../components/randomMix/RandomMixGenrePanel';
+1 -1
View File
@@ -19,7 +19,7 @@ import { SystemTab } from '../components/settings/SystemTab';
import { SETTINGS_INDEX, type Tab, matchScore, resolveTab } from '../components/settings/settingsTabs';
import { UserManagementSection } from '../components/settings/UserManagementSection';
import { ndLogin } from '../api/navidromeAdmin';
import { type ServerMagicPayload } from '../utils/serverMagicString';
import { type ServerMagicPayload } from '../utils/server/serverMagicString';
export default function Settings() {
+1 -1
View File
@@ -3,7 +3,7 @@ import { getAlbumList } from '../api/subsonicLibrary';
import type { SubsonicAlbum, SubsonicGenre } from '../api/subsonicTypes';
import React, { useEffect, useState } from 'react';
import { Share2 } from 'lucide-react';
import { formatHumanHoursMinutes } from '../utils/formatHumanDuration';
import { formatHumanHoursMinutes } from '../utils/format/formatHumanDuration';
import AlbumRow from '../components/AlbumRow';
import StatsExportModal from '../components/StatsExportModal';
import { useTranslation } from 'react-i18next';
+3 -3
View File
@@ -1,7 +1,7 @@
import { buildCoverArtUrl, coverArtCacheKey } from '../api/subsonicStreamUrl';
import { getRandomSongs } from '../api/subsonicLibrary';
import type { SubsonicSong } from '../api/subsonicTypes';
import { songToTrack } from '../utils/songToTrack';
import { songToTrack } from '../utils/playback/songToTrack';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { Play, ListPlus, RefreshCw, Sparkles } from 'lucide-react';
@@ -11,9 +11,9 @@ import { usePlayerStore } from '../store/playerStore';
import CachedImage from '../components/CachedImage';
import SongRail from '../components/SongRail';
import VirtualSongList from '../components/VirtualSongList';
import { playSongNow } from '../utils/playSong';
import { playSongNow } from '../utils/playback/playSong';
import { ndListSongs, ndInvalidateSongsCache } from '../api/navidromeBrowse';
import { usePerfProbeFlags } from '../utils/perfFlags';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
const RANDOM_RAIL_SIZE = 18;
/** Over-fetch buffer so the client-side `userRating > 0` filter still leaves
+2 -2
View File
@@ -4,8 +4,8 @@ import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { version } from '../../package.json';
import changelogRaw from '../../CHANGELOG.md?raw';
import { findChangelogReleaseEntry } from '../utils/changelogReleaseMatch';
import { renderChangelogBody } from '../utils/changelogMarkdown';
import { findChangelogReleaseEntry } from '../utils/changelog/changelogReleaseMatch';
import { renderChangelogBody } from '../utils/changelog/changelogMarkdown';
export default function WhatsNew() {
const { t } = useTranslation();