From 13142e6ae30aa8ef619db52f528bc4bd18fe7158 Mon Sep 17 00:00:00 2001
From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com>
Date: Tue, 30 Jun 2026 20:44:34 +0200
Subject: [PATCH] refactor(lib): extract clean server + navigation infra to
lib/ (decouple-knot files stay in utils/ pending seam)
---
src/api/analysis.ts | 2 +-
src/api/coverCache.ts | 4 ++--
src/app/ConnectionIndicator.tsx | 2 +-
src/app/hooks/useMigrationOrchestrator.ts | 2 +-
src/components/PasteClipboardHandler.tsx | 2 +-
src/components/SongInfoModal.tsx | 2 +-
src/cover/fetchUrl.ts | 2 +-
src/cover/integrations/discord.ts | 2 +-
src/cover/serverScope.ts | 2 +-
src/cover/storageKeys.ts | 4 ++--
src/cover/useLibraryCoverBackfill.ts | 4 ++--
src/cover/useLibraryCoverRef.ts | 2 +-
src/features/album/components/AlbumCard.tsx | 2 +-
src/features/album/hooks/useAlbumDetailData.ts | 2 +-
src/features/album/pages/AlbumDetail.tsx | 2 +-
.../artist/components/ArtistCardLocal.tsx | 2 +-
src/features/artist/hooks/useArtistDetailData.ts | 2 +-
src/features/artist/pages/ArtistDetail.tsx | 2 +-
src/features/auth/pages/Login.test.tsx | 4 ++--
src/features/auth/pages/Login.tsx | 8 ++++----
.../components/FavoritesSongsTracklist.tsx | 2 +-
src/features/genre/pages/Genres.tsx | 2 +-
src/features/offline/pages/OfflineLibrary.tsx | 2 +-
src/features/offline/store/offlineStore.ts | 2 +-
.../offline/utils/favoritesOfflineSync.ts | 2 +-
.../offline/utils/legacyOfflineFileMigration.ts | 4 ++--
.../offline/utils/libraryTierReconcile.ts | 2 +-
.../offline/utils/offlineLibraryHelpers.ts | 6 +++---
src/features/offline/utils/pinnedOfflineSync.ts | 4 ++--
.../offline/utils/resumeIncompleteOfflinePins.ts | 2 +-
.../orbit/components/OrbitSharePopover.tsx | 2 +-
.../orbit/components/OrbitStartModal.tsx | 2 +-
.../playback/store/applyQueueHistorySnapshot.ts | 2 +-
.../store/applyServerPlayQueue.idle.test.ts | 2 +-
.../playback/store/applyServerPlayQueue.ts | 2 +-
.../playback/store/loudnessRefresh.test.ts | 2 +-
src/features/playback/store/loudnessRefresh.ts | 2 +-
.../playback/store/playbackUrlRouting.ts | 2 +-
src/features/playback/store/playerStore.ts | 2 +-
src/features/playback/store/queueItemRef.ts | 2 +-
src/features/playback/store/queueRestore.ts | 2 +-
.../playback/store/queueTrackResolver.ts | 4 ++--
.../playback/utils/playback/playbackServer.ts | 4 ++--
.../utils/playback/resolvePlaybackUrl.ts | 2 +-
.../playback/utils/playback/trackServerScope.ts | 2 +-
src/features/queue/components/QueuePanel.tsx | 4 ++--
src/features/search/components/LiveSearch.tsx | 2 +-
src/features/search/hooks/useShareSearch.ts | 2 +-
src/features/search/pages/SearchBrowsePage.tsx | 4 ++--
.../settings/components/AddServerForm.test.tsx | 2 +-
.../settings/components/AddServerForm.tsx | 8 ++++----
.../components/AnalyticsStrategySection.tsx | 4 ++--
.../components/CoverCacheStrategySection.tsx | 4 ++--
src/features/settings/components/ServersTab.tsx | 16 ++++++++--------
src/features/settings/components/UserForm.tsx | 4 ++--
.../components/userMgmt/MagicStringModal.tsx | 4 ++--
.../settings/hooks/useUserMgmtActions.ts | 4 ++--
src/features/settings/pages/Settings.tsx | 2 +-
.../perfProbe/SidebarPerfProbeConnectionsTab.tsx | 4 ++--
.../perfProbe/SidebarPerfProbeServerSection.tsx | 2 +-
src/hooks/useConnectionStatus.test.ts | 2 +-
src/hooks/useConnectionStatus.ts | 4 ++--
src/hooks/useNavidromeAdminRole.ts | 2 +-
src/hotCachePrefetch/analysisPrune.ts | 2 +-
src/lib/api/library.ts | 4 ++--
src/lib/api/subsonic.scheduleProbe.test.ts | 2 +-
src/lib/api/subsonic.ts | 6 +++---
src/lib/api/subsonicClient.ts | 6 +++---
src/lib/api/subsonicStreamUrl.ts | 4 ++--
src/lib/api/subsonicTypes.ts | 2 +-
src/lib/library/genreCatalogCountsCache.ts | 2 +-
.../library/hooks/useLibraryAnalysisBackfill.ts | 2 +-
src/lib/library/hooks/useLibraryIndexSync.ts | 2 +-
src/lib/library/librarySession.ts | 6 +++---
src/lib/media/trackServerScope.ts | 4 ++--
.../advancedSearchScrollSnapshot.test.ts | 6 +++---
.../navigation/advancedSearchScrollSnapshot.ts | 4 ++--
.../navigation/detailServerScope.test.ts | 4 ++--
.../navigation/detailServerScope.ts | 2 +-
.../navigation/restoreMainViewportScroll.ts | 2 +-
src/{utils => lib}/server/redactSubsonicUrl.ts | 0
src/{utils => lib}/server/serverBaseUrl.ts | 2 +-
.../server/serverDisplayName.test.ts | 4 ++--
src/{utils => lib}/server/serverDisplayName.ts | 2 +-
src/{utils => lib}/server/serverEndpoint.test.ts | 4 ++--
src/{utils => lib}/server/serverEndpoint.ts | 4 ++--
.../server/serverFingerprint.test.ts | 2 +-
src/{utils => lib}/server/serverFingerprint.ts | 6 +++---
.../server/serverHttpHeaders.test.ts | 2 +-
src/{utils => lib}/server/serverHttpHeaders.ts | 6 +++---
src/{utils => lib}/server/serverIndexKey.ts | 6 +++---
.../server/serverIndexMigration.ts | 4 ++--
src/{utils => lib}/server/serverLookup.ts | 6 +++---
.../server/serverMagicString.test.ts | 2 +-
src/{utils => lib}/server/serverMagicString.ts | 4 ++--
.../server/serverUrlRemigration.test.ts | 2 +-
.../server/serverUrlRemigration.ts | 8 ++++----
.../server/subsonicServerIdentity.test.ts | 2 +-
.../server/subsonicServerIdentity.ts | 0
.../server/syncServerHttpContext.test.ts | 2 +-
.../server/syncServerHttpContext.ts | 6 +++---
src/localPlaybackInvalidation.ts | 4 ++--
src/serverCapabilities/context.ts | 2 +-
src/serverCapabilities/storeView.ts | 2 +-
src/serverCapabilities/types.ts | 2 +-
src/store/advancedSearchSessionStore.ts | 2 +-
src/store/analysisStrategyStore.ts | 2 +-
src/store/authPerServerCapabilityActions.ts | 2 +-
src/store/authServerProfileActions.ts | 2 +-
src/store/authStore.ts | 6 +++---
src/store/authStoreTypes.ts | 2 +-
src/store/coverStrategyStore.ts | 2 +-
src/store/localPlaybackMigration.ts | 2 +-
src/store/localPlaybackResolve.ts | 2 +-
src/utils/navigation/albumDetailNavigation.ts | 2 +-
src/utils/network/subsonicNetworkGuard.ts | 2 +-
src/utils/server/rewriteFrontendStoreKeys.ts | 2 +-
src/utils/server/switchActiveServer.ts | 4 ++--
src/utils/share/copyEntityShareLink.ts | 4 ++--
src/utils/share/enqueueShareSearchPayload.ts | 4 ++--
src/utils/share/shareLink.test.ts | 2 +-
src/utils/share/shareSearch.test.ts | 2 +-
src/utils/share/shareServerOriginLabel.ts | 4 ++--
123 files changed, 191 insertions(+), 191 deletions(-)
rename src/{utils => lib}/navigation/advancedSearchScrollSnapshot.test.ts (95%)
rename src/{utils => lib}/navigation/advancedSearchScrollSnapshot.ts (97%)
rename src/{utils => lib}/navigation/detailServerScope.test.ts (88%)
rename src/{utils => lib}/navigation/detailServerScope.ts (94%)
rename src/{utils => lib}/navigation/restoreMainViewportScroll.ts (96%)
rename src/{utils => lib}/server/redactSubsonicUrl.ts (100%)
rename src/{utils => lib}/server/serverBaseUrl.ts (83%)
rename src/{utils => lib}/server/serverDisplayName.test.ts (94%)
rename src/{utils => lib}/server/serverDisplayName.ts (96%)
rename src/{utils => lib}/server/serverEndpoint.test.ts (99%)
rename src/{utils => lib}/server/serverEndpoint.ts (99%)
rename src/{utils => lib}/server/serverFingerprint.test.ts (99%)
rename src/{utils => lib}/server/serverFingerprint.ts (98%)
rename src/{utils => lib}/server/serverHttpHeaders.test.ts (98%)
rename src/{utils => lib}/server/serverHttpHeaders.ts (97%)
rename src/{utils => lib}/server/serverIndexKey.ts (90%)
rename src/{utils => lib}/server/serverIndexMigration.ts (66%)
rename src/{utils => lib}/server/serverLookup.ts (86%)
rename src/{utils => lib}/server/serverMagicString.test.ts (99%)
rename src/{utils => lib}/server/serverMagicString.ts (97%)
rename src/{utils => lib}/server/serverUrlRemigration.test.ts (99%)
rename src/{utils => lib}/server/serverUrlRemigration.ts (95%)
rename src/{utils => lib}/server/subsonicServerIdentity.test.ts (98%)
rename src/{utils => lib}/server/subsonicServerIdentity.ts (100%)
rename src/{utils => lib}/server/syncServerHttpContext.test.ts (92%)
rename src/{utils => lib}/server/syncServerHttpContext.ts (77%)
diff --git a/src/api/analysis.ts b/src/api/analysis.ts
index 90bcdca7..dfeaedbe 100644
--- a/src/api/analysis.ts
+++ b/src/api/analysis.ts
@@ -1,6 +1,6 @@
import { invoke } from '@tauri-apps/api/core';
import { useAuthStore } from '../store/authStore';
-import { serverIndexKeyFromUrl } from '../utils/server/serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
export interface AnalysisBackfillQueueStatsDto {
queued: number;
diff --git a/src/api/coverCache.ts b/src/api/coverCache.ts
index 738044e5..271dae7c 100644
--- a/src/api/coverCache.ts
+++ b/src/api/coverCache.ts
@@ -2,8 +2,8 @@ import { invoke } from '@tauri-apps/api/core';
import { useAuthStore } from '../store/authStore';
import { useThemeStore } from '../store/themeStore';
import { coverIndexKeyFromRef, coverStorageKeyFromRef } from '../cover/storageKeys';
-import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
-import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
+import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
import { restBaseFromUrl } from '@/lib/api/subsonicClient';
import type { CoverArtRef, CoverArtTier } from '../cover/types';
diff --git a/src/app/ConnectionIndicator.tsx b/src/app/ConnectionIndicator.tsx
index a3072c08..cf2ba816 100644
--- a/src/app/ConnectionIndicator.tsx
+++ b/src/app/ConnectionIndicator.tsx
@@ -10,7 +10,7 @@ import type { ServerProfile } from '@/store/authStoreTypes';
import { useAuthStore } from '@/store/authStore';
import { switchActiveServer } from '@/utils/server/switchActiveServer';
import { showToast } from '@/lib/dom/toast';
-import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
+import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
interface Props {
status: ConnectionStatus;
diff --git a/src/app/hooks/useMigrationOrchestrator.ts b/src/app/hooks/useMigrationOrchestrator.ts
index 5841cddd..5a61ae0e 100644
--- a/src/app/hooks/useMigrationOrchestrator.ts
+++ b/src/app/hooks/useMigrationOrchestrator.ts
@@ -4,7 +4,7 @@ import { libraryGenreTagsInspect, libraryGenreTagsRun } from '@/lib/api/library'
import { migrationInspect, migrationRun, type ServerIndexMapping } from '@/api/migration';
import { useAuthStore } from '@/store/authStore';
import { useMigrationStore } from '@/store/migrationStore';
-import { serverIndexKeyFromUrl } from '@/utils/server/serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import { rewriteFrontendStoreKeys } from '@/utils/server/rewriteFrontendStoreKeys';
const MIGRATION_DONE_FLAG = 'psysonic-server-key-migration-v1';
diff --git a/src/components/PasteClipboardHandler.tsx b/src/components/PasteClipboardHandler.tsx
index 0812ac3c..f5a63442 100644
--- a/src/components/PasteClipboardHandler.tsx
+++ b/src/components/PasteClipboardHandler.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import { useAuthStore } from '../store/authStore';
import type { EntitySharePayloadV1 } from '../utils/share/shareLink';
import { decodeSharePayloadFromText } from '../utils/share/shareLink';
-import { decodeServerMagicStringFromText } from '../utils/server/serverMagicString';
+import { decodeServerMagicStringFromText } from '@/lib/server/serverMagicString';
import { applySharePastePayload, applySharePasteQueue } from '../utils/share/applySharePaste';
import { shareQueueServerContext } from '../utils/share/shareServerOriginLabel';
import { showToast } from '@/lib/dom/toast';
diff --git a/src/components/SongInfoModal.tsx b/src/components/SongInfoModal.tsx
index 21c43b06..aa9808b9 100644
--- a/src/components/SongInfoModal.tsx
+++ b/src/components/SongInfoModal.tsx
@@ -10,7 +10,7 @@ import { ndGetSongPath } from '@/lib/api/navidromeAdmin';
import { useAuthStore } from '../store/authStore';
import { useLibraryIndexStore } from '../store/libraryIndexStore';
import { useTranslation } from 'react-i18next';
-import { copyTextToClipboard } from '../utils/server/serverMagicString';
+import { copyTextToClipboard } from '@/lib/server/serverMagicString';
import { showToast } from '@/lib/dom/toast';
import { formatTrackTime } from '@/lib/format/formatDuration';
import { formatLastSeen } from '../utils/componentHelpers/userMgmtHelpers';
diff --git a/src/cover/fetchUrl.ts b/src/cover/fetchUrl.ts
index 0eadbb31..109681d9 100644
--- a/src/cover/fetchUrl.ts
+++ b/src/cover/fetchUrl.ts
@@ -4,7 +4,7 @@ import {
} from '@/lib/api/subsonicStreamUrl';
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
import { useAuthStore } from '../store/authStore';
-import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
+import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
import type { CoverArtRef, CoverArtTier } from './types';
/** Builds ephemeral getCoverArt URL — NOT a cache key */
diff --git a/src/cover/integrations/discord.ts b/src/cover/integrations/discord.ts
index 58661cdc..345adaf5 100644
--- a/src/cover/integrations/discord.ts
+++ b/src/cover/integrations/discord.ts
@@ -1,5 +1,5 @@
import { buildCoverArtUrlForServer } from '@/lib/api/subsonicStreamUrl';
-import { serverShareBaseUrl } from '../../utils/server/serverEndpoint';
+import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
import { useAuthStore } from '../../store/authStore';
import type { CoverArtRef } from '../types';
diff --git a/src/cover/serverScope.ts b/src/cover/serverScope.ts
index 10e6641e..8def61e9 100644
--- a/src/cover/serverScope.ts
+++ b/src/cover/serverScope.ts
@@ -1,4 +1,4 @@
-import { findServerByIdOrIndexKey } from '../utils/server/serverLookup';
+import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
import { COVER_SCOPE_ACTIVE, type CoverServerScope } from './types';
/** Explicit server bucket for cover disk/IDB — use when entity carries `serverId` (e.g. cross-server favorites). */
diff --git a/src/cover/storageKeys.ts b/src/cover/storageKeys.ts
index d7c12745..9b1ad953 100644
--- a/src/cover/storageKeys.ts
+++ b/src/cover/storageKeys.ts
@@ -1,11 +1,11 @@
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
import { useAuthStore } from '../store/authStore';
-import { findServerByIdOrIndexKey } from '../utils/server/serverLookup';
+import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
import {
resolveIndexKey,
serverIndexKeyForProfile,
serverIndexKeyFromUrl,
-} from '../utils/server/serverIndexKey';
+} from '@/lib/server/serverIndexKey';
import type { CoverArtRef, CoverArtTier, CoverServerScope } from './types';
/**
diff --git a/src/cover/useLibraryCoverBackfill.ts b/src/cover/useLibraryCoverBackfill.ts
index ab20bfd5..03076c04 100644
--- a/src/cover/useLibraryCoverBackfill.ts
+++ b/src/cover/useLibraryCoverBackfill.ts
@@ -11,8 +11,8 @@ import { coverStrategyAllowsLibraryBackfill } from '@/lib/library/coverStrategy'
import { useAuthStore } from '@/store/authStore';
import { useCoverStrategyStore } from '@/store/coverStrategyStore';
import { subscribeLibraryCoverBackfillWake } from '@/lib/library/coverBackfillWake';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
-import { subscribeConnectCache } from '@/utils/server/serverEndpoint';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
+import { subscribeConnectCache } from '@/lib/server/serverEndpoint';
/**
* Library cover warm-up — configure session in Rust; full pass runs natively.
diff --git a/src/cover/useLibraryCoverRef.ts b/src/cover/useLibraryCoverRef.ts
index 40c8fab4..983f689d 100644
--- a/src/cover/useLibraryCoverRef.ts
+++ b/src/cover/useLibraryCoverRef.ts
@@ -11,7 +11,7 @@ import {
resolvePlaybackCoverScope,
} from './ref';
import { coverServerScopeForServerId } from './serverScope';
-import { resolveServerIdForIndexKey } from '../utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
import {
resolveAlbumCoverRefFromLibrary,
diff --git a/src/features/album/components/AlbumCard.tsx b/src/features/album/components/AlbumCard.tsx
index 7b03d3c4..34eb3ee4 100644
--- a/src/features/album/components/AlbumCard.tsx
+++ b/src/features/album/components/AlbumCard.tsx
@@ -23,7 +23,7 @@ import { useDragDrop } from '@/lib/dnd/DragDropContext';
import { isAlbumRecentlyAdded } from '@/features/album/utils/albumRecency';
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
import { coverServerScopeForServerId } from '@/cover/serverScope';
-import { appendServerQuery } from '@/utils/navigation/detailServerScope';
+import { appendServerQuery } from '@/lib/navigation/detailServerScope';
interface AlbumCardProps {
album: SubsonicAlbum;
diff --git a/src/features/album/hooks/useAlbumDetailData.ts b/src/features/album/hooks/useAlbumDetailData.ts
index f2b24829..0146182d 100644
--- a/src/features/album/hooks/useAlbumDetailData.ts
+++ b/src/features/album/hooks/useAlbumDetailData.ts
@@ -16,7 +16,7 @@ import {
loadArtistFromLocalPlayback,
offlineLocalBrowseEnabled,
} from '@/features/offline';
-import { readDetailServerId } from '@/utils/navigation/detailServerScope';
+import { readDetailServerId } from '@/lib/navigation/detailServerScope';
import { libraryIsReady } from '@/lib/library/libraryReady';
import {
shouldAttemptSubsonicForActiveServer,
diff --git a/src/features/album/pages/AlbumDetail.tsx b/src/features/album/pages/AlbumDetail.tsx
index 4607e063..96aa9aed 100644
--- a/src/features/album/pages/AlbumDetail.tsx
+++ b/src/features/album/pages/AlbumDetail.tsx
@@ -45,7 +45,7 @@ import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import LosslessModeBanner from '@/ui/LosslessModeBanner';
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
import { isLosslessMode } from '@/lib/library/losslessMode';
-import { readDetailServerId } from '@/utils/navigation/detailServerScope';
+import { readDetailServerId } from '@/lib/navigation/detailServerScope';
import { useOfflineBrowseContext } from '@/features/offline';
import { offlineActionPolicy } from '@/features/offline';
diff --git a/src/features/artist/components/ArtistCardLocal.tsx b/src/features/artist/components/ArtistCardLocal.tsx
index 62a1389c..3beadae0 100644
--- a/src/features/artist/components/ArtistCardLocal.tsx
+++ b/src/features/artist/components/ArtistCardLocal.tsx
@@ -7,7 +7,7 @@ import { useArtistCoverRef } from '@/cover/useLibraryCoverRef';
import { COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '@/cover/layoutSizes';
import { useNavigateToArtist } from '@/features/artist/hooks/useNavigateToArtist';
import { coverServerScopeForServerId } from '@/cover/serverScope';
-import { appendServerQuery } from '@/utils/navigation/detailServerScope';
+import { appendServerQuery } from '@/lib/navigation/detailServerScope';
interface Props {
artist: SubsonicArtist;
diff --git a/src/features/artist/hooks/useArtistDetailData.ts b/src/features/artist/hooks/useArtistDetailData.ts
index 9162023e..4e66a10a 100644
--- a/src/features/artist/hooks/useArtistDetailData.ts
+++ b/src/features/artist/hooks/useArtistDetailData.ts
@@ -10,7 +10,7 @@ import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { loadArtistFromLibraryIndex } from '@/features/offline';
import { useOfflineBrowseContext } from '@/features/offline';
import { loadArtistFromLocalPlayback, offlineLocalBrowseEnabled } from '@/features/offline';
-import { readDetailServerId } from '@/utils/navigation/detailServerScope';
+import { readDetailServerId } from '@/lib/navigation/detailServerScope';
import { runLocalArtistLosslessBrowse } from '@/lib/library/browseTextSearch';
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
diff --git a/src/features/artist/pages/ArtistDetail.tsx b/src/features/artist/pages/ArtistDetail.tsx
index 158e71d4..041dba8b 100644
--- a/src/features/artist/pages/ArtistDetail.tsx
+++ b/src/features/artist/pages/ArtistDetail.tsx
@@ -38,7 +38,7 @@ import { useWarmGridCovers } from '@/cover/useWarmGridCovers';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
import { sortArtistAlbumsByYear } from '@/features/artist/utils/sortArtistAlbums';
-import { readDetailServerId } from '@/utils/navigation/detailServerScope';
+import { readDetailServerId } from '@/lib/navigation/detailServerScope';
export default function ArtistDetail() {
diff --git a/src/features/auth/pages/Login.test.tsx b/src/features/auth/pages/Login.test.tsx
index aae01cf6..a558db5b 100644
--- a/src/features/auth/pages/Login.test.tsx
+++ b/src/features/auth/pages/Login.test.tsx
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
import { resetAuthStore } from '@/test/helpers/storeReset';
import { useAuthStore } from '@/store/authStore';
-import { encodeServerMagicString } from '@/utils/server/serverMagicString';
+import { encodeServerMagicString } from '@/lib/server/serverMagicString';
vi.mock('@/lib/api/subsonic', () => ({
pingWithCredentialsForProfile: vi.fn(async () => ({
@@ -16,7 +16,7 @@ vi.mock('@/lib/api/subsonic', () => ({
scheduleInstantMixProbeForServer: vi.fn(),
}));
-vi.mock('@/utils/server/syncServerHttpContext', () => ({
+vi.mock('@/lib/server/syncServerHttpContext', () => ({
syncServerHttpContextForProfile: vi.fn(async () => undefined),
}));
diff --git a/src/features/auth/pages/Login.tsx b/src/features/auth/pages/Login.tsx
index 842475bc..ef89403e 100644
--- a/src/features/auth/pages/Login.tsx
+++ b/src/features/auth/pages/Login.tsx
@@ -9,8 +9,8 @@ import {
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
serverCustomHeadersFromForm,
validateCustomHeaders,
-} from '@/utils/server/serverHttpHeaders';
-import { syncServerHttpContextForProfile } from '@/utils/server/syncServerHttpContext';
+} from '@/lib/server/serverHttpHeaders';
+import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
import { useTranslation } from 'react-i18next';
import i18n from '@/lib/i18n';
import CustomSelect from '@/ui/CustomSelect';
@@ -19,8 +19,8 @@ import {
DECODED_PASSWORD_VISUAL_MASK,
encodeServerMagicString,
type ServerMagicPayload,
-} from '@/utils/server/serverMagicString';
-import { shortHostFromServerUrl, serverListDisplayLabel } from '@/utils/server/serverDisplayName';
+} from '@/lib/server/serverMagicString';
+import { shortHostFromServerUrl, serverListDisplayLabel } from '@/lib/server/serverDisplayName';
const PsysonicLogo = () => (
diff --git a/src/features/favorites/components/FavoritesSongsTracklist.tsx b/src/features/favorites/components/FavoritesSongsTracklist.tsx
index 6bc77524..70794767 100644
--- a/src/features/favorites/components/FavoritesSongsTracklist.tsx
+++ b/src/features/favorites/components/FavoritesSongsTracklist.tsx
@@ -13,7 +13,7 @@ import { useThemeStore } from '@/store/themeStore';
import { useDragDrop } from '@/lib/dnd/DragDropContext';
import { useOrbitSongRowBehavior } from '@/features/orbit';
import { songToTrack } from '@/lib/media/songToTrack';
-import { appendServerQuery } from '@/utils/navigation/detailServerScope';
+import { appendServerQuery } from '@/lib/navigation/detailServerScope';
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight';
import { SORTABLE_COLUMNS } from '@/features/favorites/hooks/useFavoritesSongFiltering';
diff --git a/src/features/genre/pages/Genres.tsx b/src/features/genre/pages/Genres.tsx
index 4dc13b17..6c23a980 100644
--- a/src/features/genre/pages/Genres.tsx
+++ b/src/features/genre/pages/Genres.tsx
@@ -10,7 +10,7 @@ import { useLibraryIndexStore } from '@/store/libraryIndexStore';
import { fetchGenreCatalog, filterGenresWithContent } from '@/features/playback/utils/playback/genreBrowsePlayback';
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
import { peekGenreCatalogCache } from '@/lib/library/genreCatalogCountsCache';
-import { resolveIndexKey } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey } from '@/lib/server/serverIndexKey';
import { genreColor } from '@/lib/library/genreColor';
const SCROLL_KEY = 'genres-scroll';
diff --git a/src/features/offline/pages/OfflineLibrary.tsx b/src/features/offline/pages/OfflineLibrary.tsx
index 0a458718..3da66973 100644
--- a/src/features/offline/pages/OfflineLibrary.tsx
+++ b/src/features/offline/pages/OfflineLibrary.tsx
@@ -35,7 +35,7 @@ import {
import { showToast } from '@/lib/dom/toast';
import { shuffleArray } from '@/lib/util/shuffleArray';
import { getMediaDir } from '@/lib/media/mediaDir';
-import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
+import { canonicalQueueServerKey, resolveIndexKey } from '@/lib/server/serverIndexKey';
import { reconcileAllLibraryTiersFromDisk } from '@/features/offline/utils/libraryTierReconcile';
import {
inferPinSourcesFromLibraryIndex,
diff --git a/src/features/offline/store/offlineStore.ts b/src/features/offline/store/offlineStore.ts
index a9b47aa8..83d09353 100644
--- a/src/features/offline/store/offlineStore.ts
+++ b/src/features/offline/store/offlineStore.ts
@@ -17,7 +17,7 @@ import {
pendingOfflinePinSongs,
} from '@/features/offline/utils/offlineLibraryHelpers';
import { librarySqlServerId } from '@/api/coverCache';
-import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { isSmartPlaylistName } from '@/utils/componentHelpers/playlistDetailHelpers';
import {
enqueueOfflinePin,
diff --git a/src/features/offline/utils/favoritesOfflineSync.ts b/src/features/offline/utils/favoritesOfflineSync.ts
index 828b0dff..ca0223b9 100644
--- a/src/features/offline/utils/favoritesOfflineSync.ts
+++ b/src/features/offline/utils/favoritesOfflineSync.ts
@@ -12,7 +12,7 @@ import { cancelledDownloads, useOfflineJobStore } from '@/features/offline/store
import { useFavoritesOfflineSyncStore } from '@/features/offline/store/favoritesOfflineSyncStore';
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
import { getMediaDir } from '@/lib/media/mediaDir';
-import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { FAVORITES_OFFLINE_JOB_ID } from '@/features/offline/utils/favoritesOfflineConstants';
import { isActiveServerReachable } from '@/utils/network/activeServerReachability';
import { favoritesServerIds } from '@/features/offline/utils/favoritesOfflineBrowse';
diff --git a/src/features/offline/utils/legacyOfflineFileMigration.ts b/src/features/offline/utils/legacyOfflineFileMigration.ts
index 7819e2b9..21d5f745 100644
--- a/src/features/offline/utils/legacyOfflineFileMigration.ts
+++ b/src/features/offline/utils/legacyOfflineFileMigration.ts
@@ -6,8 +6,8 @@ import { useLocalPlaybackStore, type LocalPlaybackEntry, type PinSource } from '
import { localPlaybackEntryKey } from '@/store/localPlaybackKeys';
import { importLegacyLocalPlayback } from '@/store/localPlaybackMigration';
import { getMediaDir } from '@/lib/media/mediaDir';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
-import { resolveIndexKey } from '@/utils/server/serverIndexKey';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
+import { resolveIndexKey } from '@/lib/server/serverIndexKey';
interface LegacyOfflineMigrationResult {
trackId: string;
diff --git a/src/features/offline/utils/libraryTierReconcile.ts b/src/features/offline/utils/libraryTierReconcile.ts
index 3b8ba469..ae629d00 100644
--- a/src/features/offline/utils/libraryTierReconcile.ts
+++ b/src/features/offline/utils/libraryTierReconcile.ts
@@ -6,7 +6,7 @@ import { useAuthStore } from '@/store/authStore';
import type { LocalPlaybackEntry, PinSource } from '@/store/localPlaybackStore';
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
import { getMediaDir } from '@/lib/media/mediaDir';
-import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import {
entryBelongsToServer,
findLocalPlaybackEntry,
diff --git a/src/features/offline/utils/offlineLibraryHelpers.ts b/src/features/offline/utils/offlineLibraryHelpers.ts
index de73a8ce..3967f03e 100644
--- a/src/features/offline/utils/offlineLibraryHelpers.ts
+++ b/src/features/offline/utils/offlineLibraryHelpers.ts
@@ -8,10 +8,10 @@ import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
import { findLocalPlaybackEntry, hasLocalLibraryBytes } from '@/store/localPlaybackResolve';
import { useOfflineStore, type OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
import { resolveTrackCoverArtId, trackToSong } from '@/lib/library/advancedSearchLocal';
-import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
+import { canonicalQueueServerKey, resolveIndexKey } from '@/lib/server/serverIndexKey';
import type { Track } from '@/lib/media/trackTypes';
-import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
export interface OfflineLibraryCard {
serverIndexKey: string;
diff --git a/src/features/offline/utils/pinnedOfflineSync.ts b/src/features/offline/utils/pinnedOfflineSync.ts
index 1ec322f1..f99b31da 100644
--- a/src/features/offline/utils/pinnedOfflineSync.ts
+++ b/src/features/offline/utils/pinnedOfflineSync.ts
@@ -15,8 +15,8 @@ import {
isActiveServerReachable,
onActiveServerBecameReachable,
} from '@/utils/network/activeServerReachability';
-import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { findLocalPlaybackEntry } from '@/store/localPlaybackResolve';
import { enqueueOfflinePin } from '@/features/offline/utils/offlinePinQueue';
diff --git a/src/features/offline/utils/resumeIncompleteOfflinePins.ts b/src/features/offline/utils/resumeIncompleteOfflinePins.ts
index 13c55ac0..e553f185 100644
--- a/src/features/offline/utils/resumeIncompleteOfflinePins.ts
+++ b/src/features/offline/utils/resumeIncompleteOfflinePins.ts
@@ -7,7 +7,7 @@ import { useOfflineStore } from '@/features/offline/store/offlineStore';
import { trackToSong } from '@/lib/library/advancedSearchLocal';
import { isActiveServerReachable, onActiveServerBecameReachable } from '@/utils/network/activeServerReachability';
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { isOfflinePinComplete } from '@/features/offline/utils/offlineLibraryHelpers';
import { resolveAlbumForServer } from '@/features/offline/utils/offlineMediaResolve';
diff --git a/src/features/orbit/components/OrbitSharePopover.tsx b/src/features/orbit/components/OrbitSharePopover.tsx
index 4bffc5a3..e480f39e 100644
--- a/src/features/orbit/components/OrbitSharePopover.tsx
+++ b/src/features/orbit/components/OrbitSharePopover.tsx
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
import { useAuthStore } from '@/store/authStore';
import { buildOrbitShareLink } from '@/features/orbit/utils/orbit';
-import { serverShareBaseUrl } from '@/utils/server/serverEndpoint';
+import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
interface Props {
anchorRef: React.RefObject;
diff --git a/src/features/orbit/components/OrbitStartModal.tsx b/src/features/orbit/components/OrbitStartModal.tsx
index 354f13f2..2d7c3e3c 100644
--- a/src/features/orbit/components/OrbitStartModal.tsx
+++ b/src/features/orbit/components/OrbitStartModal.tsx
@@ -13,7 +13,7 @@ import {
import { randomOrbitSessionName } from '@/features/orbit/utils/orbitNames';
import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
-import { isLanUrl, serverShareBaseUrl } from '@/utils/server/serverEndpoint';
+import { isLanUrl, serverShareBaseUrl } from '@/lib/server/serverEndpoint';
import { ORBIT_DEFAULT_MAX_USERS } from '@/features/orbit/api/orbit';
interface Props { onClose: () => void; }
diff --git a/src/features/playback/store/applyQueueHistorySnapshot.ts b/src/features/playback/store/applyQueueHistorySnapshot.ts
index 2a611169..7c3ef426 100644
--- a/src/features/playback/store/applyQueueHistorySnapshot.ts
+++ b/src/features/playback/store/applyQueueHistorySnapshot.ts
@@ -13,7 +13,7 @@ import type { QueueItemRef } from '@/lib/media/trackTypes';
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
import { resolveQueueTrack } from '@/features/playback/store/queueTrackView';
import { seedQueueResolver } from '@/features/playback/store/queueTrackResolver';
-import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
+import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
import { queueUndoRestoreAudioEngine } from '@/features/playback/store/queueUndoAudioRestore';
import {
diff --git a/src/features/playback/store/applyServerPlayQueue.idle.test.ts b/src/features/playback/store/applyServerPlayQueue.idle.test.ts
index 83462bc2..e8ff8a96 100644
--- a/src/features/playback/store/applyServerPlayQueue.idle.test.ts
+++ b/src/features/playback/store/applyServerPlayQueue.idle.test.ts
@@ -7,7 +7,7 @@ vi.mock('@/lib/api/subsonicPlayQueue', () => ({
getPlayQueueForServer: (...args: unknown[]) => getPlayQueueForServerMock(...args),
}));
-vi.mock('@/utils/server/serverLookup', () => ({
+vi.mock('@/lib/server/serverLookup', () => ({
resolveServerIdForIndexKey: (id: string) => id,
}));
diff --git a/src/features/playback/store/applyServerPlayQueue.ts b/src/features/playback/store/applyServerPlayQueue.ts
index 8bca47bc..fe68257c 100644
--- a/src/features/playback/store/applyServerPlayQueue.ts
+++ b/src/features/playback/store/applyServerPlayQueue.ts
@@ -1,7 +1,7 @@
import { getPlayQueueForServer, type PlayQueueResult } from '@/lib/api/subsonicPlayQueue';
import { songToTrack } from '@/lib/media/songToTrack';
import { bindQueueServerId } from '@/features/playback/utils/playback/playbackServer';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
import { seedQueueResolver } from '@/features/playback/store/queueTrackResolver';
import type { Track } from '@/lib/media/trackTypes';
diff --git a/src/features/playback/store/loudnessRefresh.test.ts b/src/features/playback/store/loudnessRefresh.test.ts
index e7a25094..f6820a28 100644
--- a/src/features/playback/store/loudnessRefresh.test.ts
+++ b/src/features/playback/store/loudnessRefresh.test.ts
@@ -39,7 +39,7 @@ const hoisted = vi.hoisted(() => {
vi.mock('@tauri-apps/api/core', () => ({ invoke: hoisted.invokeMock }));
vi.mock('@/lib/api/subsonicStreamUrl', () => ({ buildStreamUrl: hoisted.buildStreamUrlMock }));
-vi.mock('@/utils/server/redactSubsonicUrl', () => ({ redactSubsonicUrlForLog: hoisted.redactMock }));
+vi.mock('@/lib/server/redactSubsonicUrl', () => ({ redactSubsonicUrlForLog: hoisted.redactMock }));
vi.mock('@/store/authStore', () => ({ useAuthStore: { getState: () => hoisted.auth } }));
vi.mock('@/features/playback/store/playerStore', () => ({
usePlayerStore: {
diff --git a/src/features/playback/store/loudnessRefresh.ts b/src/features/playback/store/loudnessRefresh.ts
index 575b0768..39db5155 100644
--- a/src/features/playback/store/loudnessRefresh.ts
+++ b/src/features/playback/store/loudnessRefresh.ts
@@ -1,7 +1,7 @@
import { buildStreamUrl } from '@/lib/api/subsonicStreamUrl';
import { invoke } from '@tauri-apps/api/core';
import { getPlaybackIndexKey } from '@/features/playback/utils/playback/playbackServer';
-import { redactSubsonicUrlForLog } from '@/utils/server/redactSubsonicUrl';
+import { redactSubsonicUrlForLog } from '@/lib/server/redactSubsonicUrl';
import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { emitNormalizationDebug } from '@/features/playback/store/normalizationDebug';
diff --git a/src/features/playback/store/playbackUrlRouting.ts b/src/features/playback/store/playbackUrlRouting.ts
index 1dba27f1..588c90d4 100644
--- a/src/features/playback/store/playbackUrlRouting.ts
+++ b/src/features/playback/store/playbackUrlRouting.ts
@@ -1,6 +1,6 @@
import { findLocalPlaybackUrl } from '@/store/localPlaybackResolve';
import { resolvePlaybackUrl, type PlaybackSourceKind } from '@/features/playback/utils/playback/resolvePlaybackUrl';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
/**
diff --git a/src/features/playback/store/playerStore.ts b/src/features/playback/store/playerStore.ts
index 76c0b5c1..aab43117 100644
--- a/src/features/playback/store/playerStore.ts
+++ b/src/features/playback/store/playerStore.ts
@@ -7,7 +7,7 @@ import { emitPlaybackProgress } from '@/features/playback/store/playbackProgress
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
-import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
+import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
import { readInitialQueueVisibility } from '@/features/playback/store/queueVisibilityStorage';
import { createNetworkLoveActions } from '@/features/playback/store/networkLoveActions';
import { createMiscActions } from '@/features/playback/store/miscActions';
diff --git a/src/features/playback/store/queueItemRef.ts b/src/features/playback/store/queueItemRef.ts
index 57f70d18..191cab79 100644
--- a/src/features/playback/store/queueItemRef.ts
+++ b/src/features/playback/store/queueItemRef.ts
@@ -1,6 +1,6 @@
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
import { stampTrackServerId } from '@/lib/media/trackServerScope';
-import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
+import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
/**
* Derive thin `QueueItemRef`s from a `Track[]` queue (thin-state). Per-item
diff --git a/src/features/playback/store/queueRestore.ts b/src/features/playback/store/queueRestore.ts
index 8336262a..0eb9bda9 100644
--- a/src/features/playback/store/queueRestore.ts
+++ b/src/features/playback/store/queueRestore.ts
@@ -1,7 +1,7 @@
import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import type { QueueItemRef } from '@/lib/media/trackTypes';
-import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
+import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
import { resolveBatch } from './queueTrackResolver';
/**
diff --git a/src/features/playback/store/queueTrackResolver.ts b/src/features/playback/store/queueTrackResolver.ts
index 01fa7d62..c855e28d 100644
--- a/src/features/playback/store/queueTrackResolver.ts
+++ b/src/features/playback/store/queueTrackResolver.ts
@@ -3,8 +3,8 @@ import { getSongForServer } from '@/lib/api/subsonicLibrary';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
import { songToTrack } from '@/lib/media/songToTrack';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
-import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
+import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
import { trackToSong } from '@/lib/library/advancedSearchLocal';
import { libraryIsReady } from '@/lib/library/libraryReady';
diff --git a/src/features/playback/utils/playback/playbackServer.ts b/src/features/playback/utils/playback/playbackServer.ts
index 72cf9730..bc03e738 100644
--- a/src/features/playback/utils/playback/playbackServer.ts
+++ b/src/features/playback/utils/playback/playbackServer.ts
@@ -9,13 +9,13 @@ import { usePlayerStore } from '@/features/playback/store/playerStore';
import { switchActiveServer } from '@/utils/server/switchActiveServer';
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
-import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import {
canonicalQueueServerKey,
resolveIndexKey,
serverIndexKeyForProfile,
serverIndexKeyFromUrl,
-} from '@/utils/server/serverIndexKey';
+} from '@/lib/server/serverIndexKey';
import {
activeServerProfileId,
isMultiServerQueue,
diff --git a/src/features/playback/utils/playback/resolvePlaybackUrl.ts b/src/features/playback/utils/playback/resolvePlaybackUrl.ts
index e8eeff8c..98db5d20 100644
--- a/src/features/playback/utils/playback/resolvePlaybackUrl.ts
+++ b/src/features/playback/utils/playback/resolvePlaybackUrl.ts
@@ -1,6 +1,6 @@
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
import { findLocalPlaybackUrl } from '@/store/localPlaybackResolve';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { getPlaybackCacheServerKey, getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
/** Same resolution order as {@link resolvePlaybackUrl} — for UI hints only. */
diff --git a/src/features/playback/utils/playback/trackServerScope.ts b/src/features/playback/utils/playback/trackServerScope.ts
index 3e4ee520..d313bf25 100644
--- a/src/features/playback/utils/playback/trackServerScope.ts
+++ b/src/features/playback/utils/playback/trackServerScope.ts
@@ -1,6 +1,6 @@
import type { QueueItemRef } from '@/lib/media/trackTypes';
import { usePlayerStore } from '@/features/playback/store/playerStore';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { activeServerProfileId, profileIdFromQueueRef } from '@/lib/media/trackServerScope';
/**
diff --git a/src/features/queue/components/QueuePanel.tsx b/src/features/queue/components/QueuePanel.tsx
index 1e2aabce..a7ad1854 100644
--- a/src/features/queue/components/QueuePanel.tsx
+++ b/src/features/queue/components/QueuePanel.tsx
@@ -13,8 +13,8 @@ import { useTranslation } from 'react-i18next';
import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate';
import { useAuthStore } from '@/store/authStore';
import { encodeSharePayload } from '@/utils/share/shareLink';
-import { serverShareBaseUrl } from '@/utils/server/serverEndpoint';
-import { copyTextToClipboard } from '@/utils/server/serverMagicString';
+import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
+import { copyTextToClipboard } from '@/lib/server/serverMagicString';
import { showToast } from '@/lib/dom/toast';
import { useThemeStore } from '@/store/themeStore';
import { useLyricsStore } from '@/store/lyricsStore';
diff --git a/src/features/search/components/LiveSearch.tsx b/src/features/search/components/LiveSearch.tsx
index 28478c1b..c40b4fca 100644
--- a/src/features/search/components/LiveSearch.tsx
+++ b/src/features/search/components/LiveSearch.tsx
@@ -54,7 +54,7 @@ import {
resolveLiveSearchScopeGhost,
} from '@/features/search/components/liveSearchScope';
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
-import { resolveIndexKey } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey } from '@/lib/server/serverIndexKey';
type LiveSearchSource = 'local' | 'network';
diff --git a/src/features/search/hooks/useShareSearch.ts b/src/features/search/hooks/useShareSearch.ts
index bd4221cd..4c25ae42 100644
--- a/src/features/search/hooks/useShareSearch.ts
+++ b/src/features/search/hooks/useShareSearch.ts
@@ -11,7 +11,7 @@ import type { ServerProfile } from '@/store/authStoreTypes';
import { findServerIdForShareUrl } from '@/utils/share/shareLink';
import { shareServerOriginLabel } from '@/utils/share/shareServerOriginLabel';
import { parseShareSearchText } from '@/utils/share/shareSearch';
-import { serverIndexKeyFromUrl } from '@/utils/server/serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import { useShareSearchPreview } from '@/features/search/hooks/useShareSearchPreview';
export function useShareSearch(query: string, onSuccess?: () => void) {
diff --git a/src/features/search/pages/SearchBrowsePage.tsx b/src/features/search/pages/SearchBrowsePage.tsx
index d2fa8861..98bc5ec1 100644
--- a/src/features/search/pages/SearchBrowsePage.tsx
+++ b/src/features/search/pages/SearchBrowsePage.tsx
@@ -35,8 +35,8 @@ import {
registerAdvancedSearchSessionProvider,
resolveAdvancedSearchLeaveSnapshot,
type AdvancedSearchLeaveSnapshot,
-} from '@/utils/navigation/advancedSearchScrollSnapshot';
-import { restoreMainViewportScroll } from '@/utils/navigation/restoreMainViewportScroll';
+} from '@/lib/navigation/advancedSearchScrollSnapshot';
+import { restoreMainViewportScroll } from '@/lib/navigation/restoreMainViewportScroll';
import {
loadMoreLocalSongs,
runNetworkAdvancedTextSearch,
diff --git a/src/features/settings/components/AddServerForm.test.tsx b/src/features/settings/components/AddServerForm.test.tsx
index 6a738475..041b1acc 100644
--- a/src/features/settings/components/AddServerForm.test.tsx
+++ b/src/features/settings/components/AddServerForm.test.tsx
@@ -3,7 +3,7 @@ import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
import { AddServerForm } from '@/features/settings/components/AddServerForm';
-import { encodeServerMagicString } from '@/utils/server/serverMagicString';
+import { encodeServerMagicString } from '@/lib/server/serverMagicString';
// resolve_host_addresses Tauri command — hint-only, must not block save.
vi.mock('@/api/network', () => ({
diff --git a/src/features/settings/components/AddServerForm.tsx b/src/features/settings/components/AddServerForm.tsx
index ff3bd98e..4449cdd6 100644
--- a/src/features/settings/components/AddServerForm.tsx
+++ b/src/features/settings/components/AddServerForm.tsx
@@ -6,16 +6,16 @@ import {
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
serverCustomHeadersFromForm,
validateCustomHeaders,
-} from '@/utils/server/serverHttpHeaders';
+} from '@/lib/server/serverHttpHeaders';
import { CustomHttpHeadersEditor } from '@/features/settings/components/CustomHttpHeadersEditor';
import {
decodeServerMagicString,
encodeServerMagicString,
DECODED_PASSWORD_VISUAL_MASK,
type ServerMagicPayload,
-} from '@/utils/server/serverMagicString';
-import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
-import { isLanUrl } from '@/utils/server/serverEndpoint';
+} from '@/lib/server/serverMagicString';
+import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
+import { isLanUrl } from '@/lib/server/serverEndpoint';
import { resolveHostAddresses } from '@/api/network';
type FormState = {
diff --git a/src/features/settings/components/AnalyticsStrategySection.tsx b/src/features/settings/components/AnalyticsStrategySection.tsx
index e5287a85..aa759a91 100644
--- a/src/features/settings/components/AnalyticsStrategySection.tsx
+++ b/src/features/settings/components/AnalyticsStrategySection.tsx
@@ -21,8 +21,8 @@ import {
} from '@/api/analysis';
import { libraryGetTracksBatch, type LibraryTrackDto, type TrackRefDto } from '@/lib/api/library';
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
-import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { showToast } from '@/lib/dom/toast';
import {
ANALYTICS_STRATEGIES,
diff --git a/src/features/settings/components/CoverCacheStrategySection.tsx b/src/features/settings/components/CoverCacheStrategySection.tsx
index 5095e188..ae0c0d91 100644
--- a/src/features/settings/components/CoverCacheStrategySection.tsx
+++ b/src/features/settings/components/CoverCacheStrategySection.tsx
@@ -14,8 +14,8 @@ import {
libraryCoverProgress,
} from '@/api/coverCache';
import { clearDiskSrcCacheForServer } from '@/cover/diskSrcCache';
-import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { showToast } from '@/lib/dom/toast';
import { formatBytes } from '@/lib/format/formatBytes';
import { clearImageCache, getImageCacheSize } from '@/utils/imageCache';
diff --git a/src/features/settings/components/ServersTab.tsx b/src/features/settings/components/ServersTab.tsx
index a75dba86..ae5890f9 100644
--- a/src/features/settings/components/ServersTab.tsx
+++ b/src/features/settings/components/ServersTab.tsx
@@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { open as openUrl } from '@tauri-apps/plugin-shell';
import { AlertTriangle, CheckCircle2, Info, Lock, LogOut, Pencil, Plus, Power, Server, Sparkles, Wifi, WifiOff } from 'lucide-react';
import { useAuthStore } from '@/store/authStore';
-import { formatServerSoftware, isNavidromeAudiomuseSoftwareEligible, type InstantMixProbeResult, type SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
+import { formatServerSoftware, isNavidromeAudiomuseSoftwareEligible, type InstantMixProbeResult, type SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
import { buildCapabilityContext } from '@/serverCapabilities/context';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
import { libraryDeleteServerData, librarySyncClearSession } from '@/lib/api/library';
@@ -16,24 +16,24 @@ import { pingWithCredentialsForProfile, scheduleInstantMixProbeForServer } from
import {
clearServerHttpContext,
syncServerHttpContextForProfile,
-} from '@/utils/server/syncServerHttpContext';
-import { type ServerMagicPayload } from '@/utils/server/serverMagicString';
-import { ensureConnectUrlResolved, invalidateReachableEndpointCache } from '@/utils/server/serverEndpoint';
+} from '@/lib/server/syncServerHttpContext';
+import { type ServerMagicPayload } from '@/lib/server/serverMagicString';
+import { ensureConnectUrlResolved, invalidateReachableEndpointCache } from '@/lib/server/serverEndpoint';
import {
verifySameServerEndpoints,
type VerifySameServerResult,
-} from '@/utils/server/serverFingerprint';
+} from '@/lib/server/serverFingerprint';
import {
indexKeyRemapForUrlChange,
runIndexKeyRemigration,
-} from '@/utils/server/serverUrlRemigration';
+} from '@/lib/server/serverUrlRemigration';
import { useConfirmModalStore } from '@/store/confirmModalStore';
import { showToast } from '@/lib/dom/toast';
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
import { isFeatureActiveForServer, resolveFeatureForServer } from '@/serverCapabilities/storeView';
import type { ResolvedCapability } from '@/serverCapabilities/types';
-import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle } from '@/utils/server/serverDisplayName';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle } from '@/lib/server/serverDisplayName';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { switchActiveServer } from '@/utils/server/switchActiveServer';
import { AddServerForm } from '@/features/settings/components/AddServerForm';
import { ServerCapabilityHeaderBadge } from '@/features/settings/components/ServerCapabilityHeaderBadge';
diff --git a/src/features/settings/components/UserForm.tsx b/src/features/settings/components/UserForm.tsx
index 86a2708d..b878e97e 100644
--- a/src/features/settings/components/UserForm.tsx
+++ b/src/features/settings/components/UserForm.tsx
@@ -7,8 +7,8 @@ import {
copyTextToClipboard,
encodeServerMagicString,
magicPayloadAddressFields,
-} from '@/utils/server/serverMagicString';
-import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
+} from '@/lib/server/serverMagicString';
+import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
import { useAuthStore } from '@/store/authStore';
export interface UserFormState {
diff --git a/src/features/settings/components/userMgmt/MagicStringModal.tsx b/src/features/settings/components/userMgmt/MagicStringModal.tsx
index 04006fab..6f35b36c 100644
--- a/src/features/settings/components/userMgmt/MagicStringModal.tsx
+++ b/src/features/settings/components/userMgmt/MagicStringModal.tsx
@@ -8,8 +8,8 @@ import {
copyTextToClipboard,
encodeServerMagicString,
magicPayloadAddressFields,
-} from '@/utils/server/serverMagicString';
-import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
+} from '@/lib/server/serverMagicString';
+import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
import { useAuthStore } from '@/store/authStore';
interface Props {
diff --git a/src/features/settings/hooks/useUserMgmtActions.ts b/src/features/settings/hooks/useUserMgmtActions.ts
index dd9ca800..c5a82265 100644
--- a/src/features/settings/hooks/useUserMgmtActions.ts
+++ b/src/features/settings/hooks/useUserMgmtActions.ts
@@ -12,8 +12,8 @@ import { showToast } from '@/lib/dom/toast';
import {
copyTextToClipboard,
encodeServerMagicString,
-} from '@/utils/server/serverMagicString';
-import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
+} from '@/lib/server/serverMagicString';
+import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
import type { UserFormState } from '@/features/settings/components/UserForm';
interface UseUserMgmtActionsArgs {
diff --git a/src/features/settings/pages/Settings.tsx b/src/features/settings/pages/Settings.tsx
index 17e7a2c8..964e12a2 100644
--- a/src/features/settings/pages/Settings.tsx
+++ b/src/features/settings/pages/Settings.tsx
@@ -21,7 +21,7 @@ import { searchSettings, type SettingsSearchHit } from '@/features/settings/comp
import { type Tab, resolveTab } from '@/features/settings/components/settingsTabs';
import { UserManagementSection } from '@/features/settings/components/UserManagementSection';
import { ndLogin } from '@/lib/api/navidromeAdmin';
-import { type ServerMagicPayload } from '@/utils/server/serverMagicString';
+import { type ServerMagicPayload } from '@/lib/server/serverMagicString';
export default function Settings() {
diff --git a/src/features/sidebar/components/perfProbe/SidebarPerfProbeConnectionsTab.tsx b/src/features/sidebar/components/perfProbe/SidebarPerfProbeConnectionsTab.tsx
index b687a643..9d3db27f 100644
--- a/src/features/sidebar/components/perfProbe/SidebarPerfProbeConnectionsTab.tsx
+++ b/src/features/sidebar/components/perfProbe/SidebarPerfProbeConnectionsTab.tsx
@@ -2,8 +2,8 @@ import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useNavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
-import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
-import { findServerByIdOrIndexKey } from '@/utils/server/serverLookup';
+import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
+import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
import { PerfProbeMetricSection } from '@/features/sidebar/components/perfProbe/PerfProbeMetricCard';
import PerfProbeDetailList from '@/features/sidebar/components/perfProbe/PerfProbeDetailList';
import PerfProbeStatusBadge, { type PerfProbeBadgeTone } from '@/features/sidebar/components/perfProbe/PerfProbeStatusBadge';
diff --git a/src/features/sidebar/components/perfProbe/SidebarPerfProbeServerSection.tsx b/src/features/sidebar/components/perfProbe/SidebarPerfProbeServerSection.tsx
index 02b342e8..b2bd4236 100644
--- a/src/features/sidebar/components/perfProbe/SidebarPerfProbeServerSection.tsx
+++ b/src/features/sidebar/components/perfProbe/SidebarPerfProbeServerSection.tsx
@@ -1,6 +1,6 @@
import { useAuthStore } from '@/store/authStore';
import type { NavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
-import { isNavidromeServer, formatServerSoftware } from '@/utils/server/subsonicServerIdentity';
+import { isNavidromeServer, formatServerSoftware } from '@/lib/server/subsonicServerIdentity';
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS, OP_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
import {
isFeatureActiveForServer,
diff --git a/src/hooks/useConnectionStatus.test.ts b/src/hooks/useConnectionStatus.test.ts
index 7f73a146..3fe31f29 100644
--- a/src/hooks/useConnectionStatus.test.ts
+++ b/src/hooks/useConnectionStatus.test.ts
@@ -5,7 +5,7 @@ import { useAuthStore } from '@/store/authStore';
import {
invalidateReachableEndpointCache,
type PickReachableResult,
-} from '@/utils/server/serverEndpoint';
+} from '@/lib/server/serverEndpoint';
vi.mock('@/lib/api/subsonic', () => ({
pingWithCredentials: vi.fn(),
diff --git a/src/hooks/useConnectionStatus.ts b/src/hooks/useConnectionStatus.ts
index 4342c0e1..b6662c25 100644
--- a/src/hooks/useConnectionStatus.ts
+++ b/src/hooks/useConnectionStatus.ts
@@ -1,13 +1,13 @@
import { useState, useEffect, useCallback, useRef, useMemo, useSyncExternalStore } from 'react';
import { useAuthStore } from '../store/authStore';
import { scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
-import { serverListDisplayLabel } from '../utils/server/serverDisplayName';
+import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
import {
ensureConnectUrlResolved,
invalidateReachableEndpointCache,
isLanUrl,
type ServerEndpointKind,
-} from '../utils/server/serverEndpoint';
+} from '@/lib/server/serverEndpoint';
import {
getConnectionStatus,
setActiveServerReachable,
diff --git a/src/hooks/useNavidromeAdminRole.ts b/src/hooks/useNavidromeAdminRole.ts
index fe55e36d..1d55b1e2 100644
--- a/src/hooks/useNavidromeAdminRole.ts
+++ b/src/hooks/useNavidromeAdminRole.ts
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { ndLogin } from '@/lib/api/navidromeAdmin';
import { useAuthStore } from '../store/authStore';
-import { isNavidromeServer } from '../utils/server/subsonicServerIdentity';
+import { isNavidromeServer } from '@/lib/server/subsonicServerIdentity';
export type NavidromeAdminRole = 'idle' | 'checking' | 'admin' | 'user' | 'na' | 'error';
diff --git a/src/hotCachePrefetch/analysisPrune.ts b/src/hotCachePrefetch/analysisPrune.ts
index c0f31992..9f61cecf 100644
--- a/src/hotCachePrefetch/analysisPrune.ts
+++ b/src/hotCachePrefetch/analysisPrune.ts
@@ -4,7 +4,7 @@ import { usePlayerStore } from '@/features/playback/store/playerStore';
import { collectPlaybackMiddlePriorityTrackIds } from '@/features/playback/store/loudnessBackfillWindow';
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
import { analysisSetPlaybackPriorityHints } from '../api/analysis';
-import { serverIndexKeyFromUrl } from '../utils/server/serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import { hotCacheFrontendDebug } from './helpers';
let analysisPruneTimer: ReturnType | null = null;
diff --git a/src/lib/api/library.ts b/src/lib/api/library.ts
index 7333fffa..305e1aff 100644
--- a/src/lib/api/library.ts
+++ b/src/lib/api/library.ts
@@ -9,8 +9,8 @@
import { invoke } from '@tauri-apps/api/core';
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import { useAuthStore } from '@/store/authStore';
-import { serverIndexKeyFromUrl } from '@/utils/server/serverIndexKey';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
// ── DTO mirrors (camelCase, matching the Rust `#[serde(rename_all = "camelCase")]`) ─
diff --git a/src/lib/api/subsonic.scheduleProbe.test.ts b/src/lib/api/subsonic.scheduleProbe.test.ts
index 18e173ea..24d50f98 100644
--- a/src/lib/api/subsonic.scheduleProbe.test.ts
+++ b/src/lib/api/subsonic.scheduleProbe.test.ts
@@ -7,7 +7,7 @@ vi.mock('@/lib/api/subsonicOpenSubsonic', () => ({
import { fetchOpenSubsonicExtensionsWithCredentials } from '@/lib/api/subsonicOpenSubsonic';
import { scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
import { useAuthStore } from '@/store/authStore';
-import type { SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
+import type { SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
const fetchMock = vi.mocked(fetchOpenSubsonicExtensionsWithCredentials);
const SID = 'srv-probe';
diff --git a/src/lib/api/subsonic.ts b/src/lib/api/subsonic.ts
index da45c2b0..a90191a2 100644
--- a/src/lib/api/subsonic.ts
+++ b/src/lib/api/subsonic.ts
@@ -2,12 +2,12 @@ import axios from 'axios';
import md5 from 'md5';
import { useAuthStore } from '@/store/authStore';
import type { ServerProfile } from '@/store/authStoreTypes';
-import { headersForServerRequest } from '@/utils/server/serverHttpHeaders';
-import { findServerByIdOrIndexKey } from '@/utils/server/serverLookup';
+import { headersForServerRequest } from '@/lib/server/serverHttpHeaders';
+import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
import {
type InstantMixProbeResult,
type SubsonicServerIdentity,
-} from '@/utils/server/subsonicServerIdentity';
+} from '@/lib/server/subsonicServerIdentity';
import { fetchOpenSubsonicExtensionsWithCredentials } from '@/lib/api/subsonicOpenSubsonic';
import { buildCapabilityContext } from '@/serverCapabilities/context';
import {
diff --git a/src/lib/api/subsonicClient.ts b/src/lib/api/subsonicClient.ts
index 1d91e9a3..6e57df66 100644
--- a/src/lib/api/subsonicClient.ts
+++ b/src/lib/api/subsonicClient.ts
@@ -3,9 +3,9 @@ import md5 from 'md5';
import { version } from '@/../package.json';
import { useAuthStore } from '@/store/authStore';
import type { ServerProfile } from '@/store/authStoreTypes';
-import { connectBaseUrlForServer } from '@/utils/server/serverEndpoint';
-import { headersForServerRequest } from '@/utils/server/serverHttpHeaders';
-import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
+import { headersForServerRequest } from '@/lib/server/serverHttpHeaders';
+import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
export const SUBSONIC_CLIENT = `psysonic/${version}`;
diff --git a/src/lib/api/subsonicStreamUrl.ts b/src/lib/api/subsonicStreamUrl.ts
index 5a467f13..32ec3dcc 100644
--- a/src/lib/api/subsonicStreamUrl.ts
+++ b/src/lib/api/subsonicStreamUrl.ts
@@ -3,8 +3,8 @@ import { coverStorageKeyFromRef } from '@/cover/storageKeys';
import { coverEntryToRef, resolveAlbumCoverEntry } from '@/cover/resolveEntry';
import type { CoverArtTier } from '@/cover/types';
import { useAuthStore } from '@/store/authStore';
-import { connectBaseUrlForServer } from '@/utils/server/serverEndpoint';
-import { findServerByIdOrIndexKey } from '@/utils/server/serverLookup';
+import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
+import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
import { restBaseFromUrl, SUBSONIC_CLIENT, secureRandomSalt } from '@/lib/api/subsonicClient';
function coverArtQueryParams(username: string, password: string, id: string, size: number): URLSearchParams {
diff --git a/src/lib/api/subsonicTypes.ts b/src/lib/api/subsonicTypes.ts
index 58cf0cbf..3b9337e2 100644
--- a/src/lib/api/subsonicTypes.ts
+++ b/src/lib/api/subsonicTypes.ts
@@ -1,4 +1,4 @@
-import type { SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
+import type { SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
/** OpenSubsonic `ItemGenre` on songs/albums (atomic genres from the server). */
export interface SubsonicItemGenre {
diff --git a/src/lib/library/genreCatalogCountsCache.ts b/src/lib/library/genreCatalogCountsCache.ts
index 2bdc0bd8..826ad99a 100644
--- a/src/lib/library/genreCatalogCountsCache.ts
+++ b/src/lib/library/genreCatalogCountsCache.ts
@@ -1,5 +1,5 @@
import type { SubsonicGenre } from '@/lib/api/subsonicTypes';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
/** Fresh hits skip SQLite entirely. */
const FRESH_TTL_MS = 60 * 60 * 1000;
diff --git a/src/lib/library/hooks/useLibraryAnalysisBackfill.ts b/src/lib/library/hooks/useLibraryAnalysisBackfill.ts
index 536bcd0f..f74b17e2 100644
--- a/src/lib/library/hooks/useLibraryAnalysisBackfill.ts
+++ b/src/lib/library/hooks/useLibraryAnalysisBackfill.ts
@@ -7,7 +7,7 @@ import { librarySqlServerId } from '@/api/coverCache';
import { useAuthStore } from '@/store/authStore';
import { useAnalysisStrategyStore } from '@/store/analysisStrategyStore';
import { DEFAULT_ADVANCED_PARALLELISM } from '@/lib/library/analysisStrategy';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
const DISABLED_CONFIGURE = {
enabled: false,
diff --git a/src/lib/library/hooks/useLibraryIndexSync.ts b/src/lib/library/hooks/useLibraryIndexSync.ts
index 3a4194b4..a91a5c26 100644
--- a/src/lib/library/hooks/useLibraryIndexSync.ts
+++ b/src/lib/library/hooks/useLibraryIndexSync.ts
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useAuthStore } from '@/store/authStore';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
import { showToast } from '@/lib/dom/toast';
-import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import {
libraryGetStatus,
librarySyncCancel,
diff --git a/src/lib/library/librarySession.ts b/src/lib/library/librarySession.ts
index 9de4adc3..eff5e309 100644
--- a/src/lib/library/librarySession.ts
+++ b/src/lib/library/librarySession.ts
@@ -6,9 +6,9 @@ import { enqueueLibrarySync, queueInitialSyncIfNeeded } from './librarySyncQueue
import type { ServerProfile } from '@/store/authStoreTypes';
import { useAuthStore } from '@/store/authStore';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
-import { ensureConnectUrlResolved } from '@/utils/server/serverEndpoint';
-import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
-import { syncServerHttpContextForProfile } from '@/utils/server/syncServerHttpContext';
+import { ensureConnectUrlResolved } from '@/lib/server/serverEndpoint';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
+import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
import { libraryDevEnabled, logLibraryStatus, logLibrarySync, timed } from './libraryDevLog';
export type BindServerResult = 'bound' | 'offline' | 'error';
diff --git a/src/lib/media/trackServerScope.ts b/src/lib/media/trackServerScope.ts
index 81b5d9c8..add91a05 100644
--- a/src/lib/media/trackServerScope.ts
+++ b/src/lib/media/trackServerScope.ts
@@ -1,7 +1,7 @@
import type { Track, QueueItemRef } from '@/lib/media/trackTypes';
import { useAuthStore } from '@/store/authStore';
-import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
-import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
+import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
/**
* Pure server-scope helpers for the shared media model: stamp the owning server
diff --git a/src/utils/navigation/advancedSearchScrollSnapshot.test.ts b/src/lib/navigation/advancedSearchScrollSnapshot.test.ts
similarity index 95%
rename from src/utils/navigation/advancedSearchScrollSnapshot.test.ts
rename to src/lib/navigation/advancedSearchScrollSnapshot.test.ts
index 0d438f26..1ea286cc 100644
--- a/src/utils/navigation/advancedSearchScrollSnapshot.test.ts
+++ b/src/lib/navigation/advancedSearchScrollSnapshot.test.ts
@@ -1,6 +1,6 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it } from 'vitest';
-import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
+import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import {
clearAdvancedSearchLeaveSnapshots,
peekPersistedAdvancedSearchLeaveSnapshot,
@@ -9,8 +9,8 @@ import {
registerAdvancedSearchSessionProvider,
resolveAdvancedSearchLeaveSnapshot,
saveAdvancedSearchLeaveSnapshot,
-} from './advancedSearchScrollSnapshot';
-import { useAdvancedSearchSessionStore } from '../../store/advancedSearchSessionStore';
+} from '@/lib/navigation/advancedSearchScrollSnapshot';
+import { useAdvancedSearchSessionStore } from '@/store/advancedSearchSessionStore';
describe('advancedSearchScrollSnapshot', () => {
afterEach(() => {
diff --git a/src/utils/navigation/advancedSearchScrollSnapshot.ts b/src/lib/navigation/advancedSearchScrollSnapshot.ts
similarity index 97%
rename from src/utils/navigation/advancedSearchScrollSnapshot.ts
rename to src/lib/navigation/advancedSearchScrollSnapshot.ts
index d74b5c83..d6c6021e 100644
--- a/src/utils/navigation/advancedSearchScrollSnapshot.ts
+++ b/src/lib/navigation/advancedSearchScrollSnapshot.ts
@@ -1,8 +1,8 @@
-import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
+import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import {
useAdvancedSearchSessionStore,
type AdvancedSearchSessionStash,
-} from '../../store/advancedSearchSessionStore';
+} from '@/store/advancedSearchSessionStore';
export type AdvancedSearchLeaveSnapshot = {
scrollTop: number;
diff --git a/src/utils/navigation/detailServerScope.test.ts b/src/lib/navigation/detailServerScope.test.ts
similarity index 88%
rename from src/utils/navigation/detailServerScope.test.ts
rename to src/lib/navigation/detailServerScope.test.ts
index 37136be6..4191b7ab 100644
--- a/src/utils/navigation/detailServerScope.test.ts
+++ b/src/lib/navigation/detailServerScope.test.ts
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it } from 'vitest';
-import { useAuthStore } from '../../store/authStore';
-import { appendServerQuery, readDetailServerId } from './detailServerScope';
+import { useAuthStore } from '@/store/authStore';
+import { appendServerQuery, readDetailServerId } from '@/lib/navigation/detailServerScope';
describe('detailServerScope', () => {
beforeEach(() => {
diff --git a/src/utils/navigation/detailServerScope.ts b/src/lib/navigation/detailServerScope.ts
similarity index 94%
rename from src/utils/navigation/detailServerScope.ts
rename to src/lib/navigation/detailServerScope.ts
index 3d2da295..3dd52ef6 100644
--- a/src/utils/navigation/detailServerScope.ts
+++ b/src/lib/navigation/detailServerScope.ts
@@ -1,4 +1,4 @@
-import { useAuthStore } from '../../store/authStore';
+import { useAuthStore } from '@/store/authStore';
/** Resolve `?server=` on album/artist detail routes; falls back when absent or unknown. */
export function readDetailServerId(
diff --git a/src/utils/navigation/restoreMainViewportScroll.ts b/src/lib/navigation/restoreMainViewportScroll.ts
similarity index 96%
rename from src/utils/navigation/restoreMainViewportScroll.ts
rename to src/lib/navigation/restoreMainViewportScroll.ts
index dcdf1f3c..30e66356 100644
--- a/src/utils/navigation/restoreMainViewportScroll.ts
+++ b/src/lib/navigation/restoreMainViewportScroll.ts
@@ -1,4 +1,4 @@
-import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
+import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
const SAFETY_TIMEOUT_MS = 3000;
diff --git a/src/utils/server/redactSubsonicUrl.ts b/src/lib/server/redactSubsonicUrl.ts
similarity index 100%
rename from src/utils/server/redactSubsonicUrl.ts
rename to src/lib/server/redactSubsonicUrl.ts
diff --git a/src/utils/server/serverBaseUrl.ts b/src/lib/server/serverBaseUrl.ts
similarity index 83%
rename from src/utils/server/serverBaseUrl.ts
rename to src/lib/server/serverBaseUrl.ts
index 4505c4fc..95b72337 100644
--- a/src/utils/server/serverBaseUrl.ts
+++ b/src/lib/server/serverBaseUrl.ts
@@ -1,4 +1,4 @@
-import type { ServerProfile } from '../../store/authStoreTypes';
+import type { ServerProfile } from '@/store/authStoreTypes';
/** Normalized Subsonic root URL for a server profile (same shape as `getBaseUrl`). */
export function serverProfileBaseUrl(server: Pick): string {
diff --git a/src/utils/server/serverDisplayName.test.ts b/src/lib/server/serverDisplayName.test.ts
similarity index 94%
rename from src/utils/server/serverDisplayName.test.ts
rename to src/lib/server/serverDisplayName.test.ts
index f136fb71..7ffbd395 100644
--- a/src/utils/server/serverDisplayName.test.ts
+++ b/src/lib/server/serverDisplayName.test.ts
@@ -1,6 +1,6 @@
-import type { ServerProfile } from '../../store/authStoreTypes';
+import type { ServerProfile } from '@/store/authStoreTypes';
import { describe, expect, it } from 'vitest';
-import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle, shortHostFromServerUrl } from './serverDisplayName';
+import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle, shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
function srv(p: Partial & Pick): ServerProfile {
return {
diff --git a/src/utils/server/serverDisplayName.ts b/src/lib/server/serverDisplayName.ts
similarity index 96%
rename from src/utils/server/serverDisplayName.ts
rename to src/lib/server/serverDisplayName.ts
index 2efb3a59..4366c607 100644
--- a/src/utils/server/serverDisplayName.ts
+++ b/src/lib/server/serverDisplayName.ts
@@ -1,4 +1,4 @@
-import type { ServerProfile } from '../../store/authStoreTypes';
+import type { ServerProfile } from '@/store/authStoreTypes';
/** Host (+ port) from a server base URL, e.g. `https://music.one.com/foo` → `music.one.com`. */
export function shortHostFromServerUrl(urlRaw?: string | null): string {
const t = typeof urlRaw === 'string' ? urlRaw.trim() : '';
diff --git a/src/utils/server/serverEndpoint.test.ts b/src/lib/server/serverEndpoint.test.ts
similarity index 99%
rename from src/utils/server/serverEndpoint.test.ts
rename to src/lib/server/serverEndpoint.test.ts
index 426afb70..a810fa5d 100644
--- a/src/utils/server/serverEndpoint.test.ts
+++ b/src/lib/server/serverEndpoint.test.ts
@@ -17,8 +17,8 @@ import {
serverAddressEndpoints,
serverShareBaseUrl,
subscribeConnectCache,
-} from './serverEndpoint';
-import type { ServerProfile } from '../../store/authStoreTypes';
+} from '@/lib/server/serverEndpoint';
+import type { ServerProfile } from '@/store/authStoreTypes';
function makeProfile(overrides: Partial = {}): ServerProfile {
return {
diff --git a/src/utils/server/serverEndpoint.ts b/src/lib/server/serverEndpoint.ts
similarity index 99%
rename from src/utils/server/serverEndpoint.ts
rename to src/lib/server/serverEndpoint.ts
index 384e23b6..10ef80f6 100644
--- a/src/utils/server/serverEndpoint.ts
+++ b/src/lib/server/serverEndpoint.ts
@@ -1,7 +1,7 @@
import { pingWithCredentialsForProfile } from '@/lib/api/subsonic';
import type { PingWithCredentialsResult } from '@/lib/api/subsonicTypes';
-import type { ServerProfile } from '../../store/authStoreTypes';
-import { serverProfileBaseUrl } from './serverBaseUrl';
+import type { ServerProfile } from '@/store/authStoreTypes';
+import { serverProfileBaseUrl } from '@/lib/server/serverBaseUrl';
export type ServerEndpointKind = 'local' | 'public';
diff --git a/src/utils/server/serverFingerprint.test.ts b/src/lib/server/serverFingerprint.test.ts
similarity index 99%
rename from src/utils/server/serverFingerprint.test.ts
rename to src/lib/server/serverFingerprint.test.ts
index a9d3ff5e..70db0f0d 100644
--- a/src/utils/server/serverFingerprint.test.ts
+++ b/src/lib/server/serverFingerprint.test.ts
@@ -16,7 +16,7 @@ import {
fetchServerFingerprint,
verifySameServerEndpoints,
type ServerFingerprint,
-} from './serverFingerprint';
+} from '@/lib/server/serverFingerprint';
function makeFingerprint(overrides: Partial = {}): ServerFingerprint {
return {
diff --git a/src/utils/server/serverFingerprint.ts b/src/lib/server/serverFingerprint.ts
similarity index 98%
rename from src/utils/server/serverFingerprint.ts
rename to src/lib/server/serverFingerprint.ts
index 66888a7b..1ad47b63 100644
--- a/src/utils/server/serverFingerprint.ts
+++ b/src/lib/server/serverFingerprint.ts
@@ -22,9 +22,9 @@ import {
SUBSONIC_CLIENT,
type ServerHttpHeaderProfile,
} from '@/lib/api/subsonicClient';
-import type { ServerProfile } from '../../store/authStoreTypes';
-import { allNormalizedAddresses } from './serverEndpoint';
-import { headersForServerRequest } from './serverHttpHeaders';
+import type { ServerProfile } from '@/store/authStoreTypes';
+import { allNormalizedAddresses } from '@/lib/server/serverEndpoint';
+import { headersForServerRequest } from '@/lib/server/serverHttpHeaders';
export type ServerFingerprint = {
ping: {
diff --git a/src/utils/server/serverHttpHeaders.test.ts b/src/lib/server/serverHttpHeaders.test.ts
similarity index 98%
rename from src/utils/server/serverHttpHeaders.test.ts
rename to src/lib/server/serverHttpHeaders.test.ts
index f7957fac..a8c4d33f 100644
--- a/src/utils/server/serverHttpHeaders.test.ts
+++ b/src/lib/server/serverHttpHeaders.test.ts
@@ -4,7 +4,7 @@ import {
requestBaseUrlFromHttpUrl,
serverCustomHeadersFromForm,
validateCustomHeaders,
-} from './serverHttpHeaders';
+} from '@/lib/server/serverHttpHeaders';
describe('requestBaseUrlFromHttpUrl', () => {
it('strips /rest/ path and query from stream URLs', () => {
diff --git a/src/utils/server/serverHttpHeaders.ts b/src/lib/server/serverHttpHeaders.ts
similarity index 97%
rename from src/utils/server/serverHttpHeaders.ts
rename to src/lib/server/serverHttpHeaders.ts
index b1ec0489..cc89eb77 100644
--- a/src/utils/server/serverHttpHeaders.ts
+++ b/src/lib/server/serverHttpHeaders.ts
@@ -4,9 +4,9 @@ import type {
CustomHeadersFieldError,
CustomHeadersValidationResult,
ServerProfile,
-} from '../../store/authStoreTypes';
-import { serverIndexKeyForProfile } from './serverIndexKey';
-import { normalizeServerBaseUrl, serverAddressEndpoints, type ServerEndpointKind } from './serverEndpoint';
+} from '@/store/authStoreTypes';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
+import { normalizeServerBaseUrl, serverAddressEndpoints, type ServerEndpointKind } from '@/lib/server/serverEndpoint';
export const DEFAULT_CUSTOM_HEADERS_APPLY_TO: CustomHeadersApplyTo = 'public';
diff --git a/src/utils/server/serverIndexKey.ts b/src/lib/server/serverIndexKey.ts
similarity index 90%
rename from src/utils/server/serverIndexKey.ts
rename to src/lib/server/serverIndexKey.ts
index a4d48175..fadd5ecb 100644
--- a/src/utils/server/serverIndexKey.ts
+++ b/src/lib/server/serverIndexKey.ts
@@ -1,6 +1,6 @@
-import type { ServerProfile } from '../../store/authStoreTypes';
-import { useAuthStore } from '../../store/authStore';
-import { serverProfileBaseUrl } from './serverBaseUrl';
+import type { ServerProfile } from '@/store/authStoreTypes';
+import { useAuthStore } from '@/store/authStore';
+import { serverProfileBaseUrl } from '@/lib/server/serverBaseUrl';
/** Stable index key derived from a server URL (host + optional path, no scheme). */
export function serverIndexKeyFromUrl(urlRaw: string): string {
diff --git a/src/utils/server/serverIndexMigration.ts b/src/lib/server/serverIndexMigration.ts
similarity index 66%
rename from src/utils/server/serverIndexMigration.ts
rename to src/lib/server/serverIndexMigration.ts
index 8db7debf..2a956afe 100644
--- a/src/utils/server/serverIndexMigration.ts
+++ b/src/lib/server/serverIndexMigration.ts
@@ -1,5 +1,5 @@
-import { useAuthStore } from '../../store/authStore';
-import { rewriteFrontendStoreKeys } from './rewriteFrontendStoreKeys';
+import { useAuthStore } from '@/store/authStore';
+import { rewriteFrontendStoreKeys } from '@/utils/server/rewriteFrontendStoreKeys';
/**
* Legacy compatibility shim. The migration lifecycle now runs through
diff --git a/src/utils/server/serverLookup.ts b/src/lib/server/serverLookup.ts
similarity index 86%
rename from src/utils/server/serverLookup.ts
rename to src/lib/server/serverLookup.ts
index 22c4da67..3793fa38 100644
--- a/src/utils/server/serverLookup.ts
+++ b/src/lib/server/serverLookup.ts
@@ -1,6 +1,6 @@
-import { useAuthStore } from '../../store/authStore';
-import type { ServerProfile } from '../../store/authStoreTypes';
-import { serverIndexKeyForProfile, serverIndexKeyFromUrl } from './serverIndexKey';
+import { useAuthStore } from '@/store/authStore';
+import type { ServerProfile } from '@/store/authStoreTypes';
+import { serverIndexKeyForProfile, serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
export function findServerByIdOrIndexKey(serverIdOrKey: string): ServerProfile | undefined {
const servers = useAuthStore.getState().servers;
diff --git a/src/utils/server/serverMagicString.test.ts b/src/lib/server/serverMagicString.test.ts
similarity index 99%
rename from src/utils/server/serverMagicString.test.ts
rename to src/lib/server/serverMagicString.test.ts
index 52d34045..0f35c464 100644
--- a/src/utils/server/serverMagicString.test.ts
+++ b/src/lib/server/serverMagicString.test.ts
@@ -6,7 +6,7 @@ import {
decodeServerMagicString,
decodeServerMagicStringFromText,
encodeServerMagicString,
-} from './serverMagicString';
+} from '@/lib/server/serverMagicString';
describe('DECODED_PASSWORD_VISUAL_MASK', () => {
it('has fixed length independent of real passwords', () => {
diff --git a/src/utils/server/serverMagicString.ts b/src/lib/server/serverMagicString.ts
similarity index 97%
rename from src/utils/server/serverMagicString.ts
rename to src/lib/server/serverMagicString.ts
index ca5018f3..3406dbcd 100644
--- a/src/utils/server/serverMagicString.ts
+++ b/src/lib/server/serverMagicString.ts
@@ -1,5 +1,5 @@
-import type { ServerProfile } from '../../store/authStoreTypes';
-import { normalizeServerBaseUrl, serverShareBaseUrl } from './serverEndpoint';
+import type { ServerProfile } from '@/store/authStoreTypes';
+import { normalizeServerBaseUrl, serverShareBaseUrl } from '@/lib/server/serverEndpoint';
/**
* Prefix for server invite strings (Subsonic credentials). Same family as library
diff --git a/src/utils/server/serverUrlRemigration.test.ts b/src/lib/server/serverUrlRemigration.test.ts
similarity index 99%
rename from src/utils/server/serverUrlRemigration.test.ts
rename to src/lib/server/serverUrlRemigration.test.ts
index 7b55f43e..00475477 100644
--- a/src/utils/server/serverUrlRemigration.test.ts
+++ b/src/lib/server/serverUrlRemigration.test.ts
@@ -11,7 +11,7 @@ import { invoke } from '@tauri-apps/api/core';
import {
indexKeyRemapForUrlChange,
runIndexKeyRemigration,
-} from './serverUrlRemigration';
+} from '@/lib/server/serverUrlRemigration';
function inspectStub(needs = true) {
return {
diff --git a/src/utils/server/serverUrlRemigration.ts b/src/lib/server/serverUrlRemigration.ts
similarity index 95%
rename from src/utils/server/serverUrlRemigration.ts
rename to src/lib/server/serverUrlRemigration.ts
index f28ad18d..a4c08281 100644
--- a/src/utils/server/serverUrlRemigration.ts
+++ b/src/lib/server/serverUrlRemigration.ts
@@ -28,15 +28,15 @@
*/
import { invoke } from '@tauri-apps/api/core';
-import type { ServerProfile } from '../../store/authStoreTypes';
+import type { ServerProfile } from '@/store/authStoreTypes';
import {
migrationInspect,
migrationRun,
type MigrationInspectReport,
type MigrationRunResult,
-} from '../../api/migration';
-import { rewriteFrontendStoreKeysForRemap } from './rewriteFrontendStoreKeys';
-import { serverIndexKeyFromUrl } from './serverIndexKey';
+} from '@/api/migration';
+import { rewriteFrontendStoreKeysForRemap } from '@/utils/server/rewriteFrontendStoreKeys';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
export type IndexKeyRemap = { oldKey: string; newKey: string };
diff --git a/src/utils/server/subsonicServerIdentity.test.ts b/src/lib/server/subsonicServerIdentity.test.ts
similarity index 98%
rename from src/utils/server/subsonicServerIdentity.test.ts
rename to src/lib/server/subsonicServerIdentity.test.ts
index be124c84..f714fc0a 100644
--- a/src/utils/server/subsonicServerIdentity.test.ts
+++ b/src/lib/server/subsonicServerIdentity.test.ts
@@ -4,7 +4,7 @@ import {
isNavidromeAudiomuseSoftwareEligible,
isNavidromeServer,
parseLeadingSemver,
-} from './subsonicServerIdentity';
+} from '@/lib/server/subsonicServerIdentity';
describe('parseLeadingSemver', () => {
it('parses Navidrome-style version strings', () => {
diff --git a/src/utils/server/subsonicServerIdentity.ts b/src/lib/server/subsonicServerIdentity.ts
similarity index 100%
rename from src/utils/server/subsonicServerIdentity.ts
rename to src/lib/server/subsonicServerIdentity.ts
diff --git a/src/utils/server/syncServerHttpContext.test.ts b/src/lib/server/syncServerHttpContext.test.ts
similarity index 92%
rename from src/utils/server/syncServerHttpContext.test.ts
rename to src/lib/server/syncServerHttpContext.test.ts
index 576eeac0..764db12e 100644
--- a/src/utils/server/syncServerHttpContext.test.ts
+++ b/src/lib/server/syncServerHttpContext.test.ts
@@ -4,7 +4,7 @@ const invokeMock = vi.hoisted(() => vi.fn(async () => undefined));
vi.mock('@tauri-apps/api/core', () => ({ invoke: invokeMock }));
-import { syncServerHttpContextForProfile } from './syncServerHttpContext';
+import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
const server = {
id: 'app-uuid-1',
diff --git a/src/utils/server/syncServerHttpContext.ts b/src/lib/server/syncServerHttpContext.ts
similarity index 77%
rename from src/utils/server/syncServerHttpContext.ts
rename to src/lib/server/syncServerHttpContext.ts
index 0d0cc7f4..fe37d1b1 100644
--- a/src/utils/server/syncServerHttpContext.ts
+++ b/src/lib/server/syncServerHttpContext.ts
@@ -1,7 +1,7 @@
import { invoke } from '@tauri-apps/api/core';
-import type { ServerProfile } from '../../store/authStoreTypes';
-import { serverHttpContextWireForProfile } from './serverHttpHeaders';
-import { serverIndexKeyForProfile } from './serverIndexKey';
+import type { ServerProfile } from '@/store/authStoreTypes';
+import { serverHttpContextWireForProfile } from '@/lib/server/serverHttpHeaders';
+import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
export async function syncServerHttpContextForProfile(server: ServerProfile): Promise {
const wire = serverHttpContextWireForProfile(server);
diff --git a/src/localPlaybackInvalidation.ts b/src/localPlaybackInvalidation.ts
index 0be6b895..d2a6aa1c 100644
--- a/src/localPlaybackInvalidation.ts
+++ b/src/localPlaybackInvalidation.ts
@@ -7,8 +7,8 @@ import { layoutFingerprintFromLibraryTrack } from '@/lib/media/mediaLayout';
import { getMediaDir } from '@/lib/media/mediaDir';
import { runLegacyOfflineFileMigration } from '@/features/offline';
import { reconcileLibraryTierForServer } from '@/features/offline';
-import { resolveServerIdForIndexKey } from './utils/server/serverLookup';
-import { serverIndexKeyFromUrl } from './utils/server/serverIndexKey';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
async function invalidateEntriesForLibraryServer(libraryServerId: string): Promise {
const store = useLocalPlaybackStore.getState();
diff --git a/src/serverCapabilities/context.ts b/src/serverCapabilities/context.ts
index 0369677e..5908b98e 100644
--- a/src/serverCapabilities/context.ts
+++ b/src/serverCapabilities/context.ts
@@ -2,7 +2,7 @@ import {
isNavidromeServer,
parseLeadingSemver,
type SubsonicServerIdentity,
-} from '../utils/server/subsonicServerIdentity';
+} from '@/lib/server/subsonicServerIdentity';
import type { CapabilityContext, Semver } from './types';
function semverGte(a: Semver, b: Semver): boolean {
diff --git a/src/serverCapabilities/storeView.ts b/src/serverCapabilities/storeView.ts
index 4a9ce57d..e4efecc9 100644
--- a/src/serverCapabilities/storeView.ts
+++ b/src/serverCapabilities/storeView.ts
@@ -1,7 +1,7 @@
import { useAuthStore } from '../store/authStore';
import type {
InstantMixProbeResult,
-} from '../utils/server/subsonicServerIdentity';
+} from '@/lib/server/subsonicServerIdentity';
import { buildCapabilityContext } from './context';
import {
PROBE_LEGACY_INSTANT_MIX,
diff --git a/src/serverCapabilities/types.ts b/src/serverCapabilities/types.ts
index 8e3405fa..a778e565 100644
--- a/src/serverCapabilities/types.ts
+++ b/src/serverCapabilities/types.ts
@@ -1,4 +1,4 @@
-import type { SubsonicServerIdentity } from '../utils/server/subsonicServerIdentity';
+import type { SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
export type Semver = [number, number, number];
diff --git a/src/store/advancedSearchSessionStore.ts b/src/store/advancedSearchSessionStore.ts
index f155359a..b4fe44b2 100644
--- a/src/store/advancedSearchSessionStore.ts
+++ b/src/store/advancedSearchSessionStore.ts
@@ -1,6 +1,6 @@
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/lib/api/subsonicTypes';
import { create } from 'zustand';
-import type { AdvancedSearchLeaveSnapshot } from '../utils/navigation/advancedSearchScrollSnapshot';
+import type { AdvancedSearchLeaveSnapshot } from '@/lib/navigation/advancedSearchScrollSnapshot';
export type AdvancedSearchResultType = 'all' | 'artists' | 'albums' | 'songs';
diff --git a/src/store/analysisStrategyStore.ts b/src/store/analysisStrategyStore.ts
index ef74aafe..200478a8 100644
--- a/src/store/analysisStrategyStore.ts
+++ b/src/store/analysisStrategyStore.ts
@@ -7,7 +7,7 @@ import {
type AnalyticsStrategy,
} from '@/lib/library/analysisStrategy';
import { useAuthStore } from './authStore';
-import { serverIndexKeyFromUrl } from '../utils/server/serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import type { ServerProfile } from './authStoreTypes';
const resolveStrategyKey = (serverId: string): string => {
diff --git a/src/store/authPerServerCapabilityActions.ts b/src/store/authPerServerCapabilityActions.ts
index 16cb1f9e..a479bc06 100644
--- a/src/store/authPerServerCapabilityActions.ts
+++ b/src/store/authPerServerCapabilityActions.ts
@@ -1,4 +1,4 @@
-import { isNavidromeAudiomuseSoftwareEligible } from '../utils/server/subsonicServerIdentity';
+import { isNavidromeAudiomuseSoftwareEligible } from '@/lib/server/subsonicServerIdentity';
import type { AuthState } from './authStoreTypes';
type SetState = (
diff --git a/src/store/authServerProfileActions.ts b/src/store/authServerProfileActions.ts
index 56c6f761..ad9ca06a 100644
--- a/src/store/authServerProfileActions.ts
+++ b/src/store/authServerProfileActions.ts
@@ -1,7 +1,7 @@
import type { AuthState } from './authStoreTypes';
import { generateId } from './authStoreHelpers';
import { getQueueServerId, clearQueueServerForPlayback } from './playbackEngineBridge';
-import { resolveServerIdForIndexKey } from '../utils/server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
type SetState = (
partial: Partial | ((state: AuthState) => Partial),
diff --git a/src/store/authStore.ts b/src/store/authStore.ts
index 0b3853fb..d743f070 100644
--- a/src/store/authStore.ts
+++ b/src/store/authStore.ts
@@ -20,10 +20,10 @@ import {
DEFAULT_LIBRARY_GRID_MAX_COLUMNS,
} from './authStoreDefaults';
import { computeAuthStoreRehydration } from './authStoreRehydrate';
-import { syncAllServerHttpContexts } from '../utils/server/syncServerHttpContext';
+import { syncAllServerHttpContexts } from '@/lib/server/syncServerHttpContext';
import type { AuthState } from './authStoreTypes';
-import { getCachedConnectBaseUrl } from '../utils/server/serverEndpoint';
-import { serverProfileBaseUrl } from '../utils/server/serverBaseUrl';
+import { getCachedConnectBaseUrl } from '@/lib/server/serverEndpoint';
+import { serverProfileBaseUrl } from '@/lib/server/serverBaseUrl';
diff --git a/src/store/authStoreTypes.ts b/src/store/authStoreTypes.ts
index 95aa92d1..66fdcda4 100644
--- a/src/store/authStoreTypes.ts
+++ b/src/store/authStoreTypes.ts
@@ -4,7 +4,7 @@ import type {
AudiomusePluginProbeResult,
InstantMixProbeResult,
SubsonicServerIdentity,
-} from '../utils/server/subsonicServerIdentity';
+} from '@/lib/server/subsonicServerIdentity';
import type { PersistedAccount } from '../music-network';
export type CustomHeaderEntry = {
diff --git a/src/store/coverStrategyStore.ts b/src/store/coverStrategyStore.ts
index 3faa594d..91bca48f 100644
--- a/src/store/coverStrategyStore.ts
+++ b/src/store/coverStrategyStore.ts
@@ -6,7 +6,7 @@ import {
type CoverCacheStrategy,
} from '@/lib/library/coverStrategy';
import { useAuthStore } from './authStore';
-import { serverIndexKeyFromUrl } from '../utils/server/serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import type { ServerProfile } from './authStoreTypes';
const resolveStrategyKey = (serverId: string): string => {
diff --git a/src/store/localPlaybackMigration.ts b/src/store/localPlaybackMigration.ts
index b28d8159..f6820041 100644
--- a/src/store/localPlaybackMigration.ts
+++ b/src/store/localPlaybackMigration.ts
@@ -3,7 +3,7 @@ import type { HotCacheEntry } from '@/features/playback/store/hotCacheStoreTypes
import { localPlaybackEntryKey } from './localPlaybackKeys';
import type { LocalPlaybackEntry, PinSource } from './localPlaybackStore';
import type { OfflineAlbumMeta, OfflineTrackMeta } from '@/features/offline';
-import { resolveIndexKey } from '../utils/server/serverIndexKey';
+import { resolveIndexKey } from '@/lib/server/serverIndexKey';
const MIGRATION_FLAG = 'psysonic-local-playback-migrated-v1';
diff --git a/src/store/localPlaybackResolve.ts b/src/store/localPlaybackResolve.ts
index feb8d7da..95781d3b 100644
--- a/src/store/localPlaybackResolve.ts
+++ b/src/store/localPlaybackResolve.ts
@@ -5,7 +5,7 @@
import { useAuthStore } from '@/store/authStore';
import type { LocalPlaybackEntry } from '@/store/localPlaybackStore';
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
-import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
+import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
function serverIndexKeysForServerId(serverId: string): string[] {
const servers = useAuthStore.getState().servers;
diff --git a/src/utils/navigation/albumDetailNavigation.ts b/src/utils/navigation/albumDetailNavigation.ts
index 9a165ad3..8e9f4b7e 100644
--- a/src/utils/navigation/albumDetailNavigation.ts
+++ b/src/utils/navigation/albumDetailNavigation.ts
@@ -11,7 +11,7 @@ import {
import {
peekPersistedAdvancedSearchLeaveSnapshot,
saveAdvancedSearchLeaveSnapshot,
-} from './advancedSearchScrollSnapshot';
+} from '@/lib/navigation/advancedSearchScrollSnapshot';
export type AlbumDetailLocationState = {
returnTo?: string;
diff --git a/src/utils/network/subsonicNetworkGuard.ts b/src/utils/network/subsonicNetworkGuard.ts
index 5309178f..dcc4ae55 100644
--- a/src/utils/network/subsonicNetworkGuard.ts
+++ b/src/utils/network/subsonicNetworkGuard.ts
@@ -1,7 +1,7 @@
import { useAuthStore } from '../../store/authStore';
import { resolvePlaybackUrl } from '@/features/playback/utils/playback/resolvePlaybackUrl';
import { isDevOfflineBrowseForced } from '@/features/offline';
-import { resolveServerIdForIndexKey } from '../server/serverLookup';
+import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
import { isActiveServerReachable } from './activeServerReachability';
function isSameServerProfile(a: string, b: string): boolean {
diff --git a/src/utils/server/rewriteFrontendStoreKeys.ts b/src/utils/server/rewriteFrontendStoreKeys.ts
index 921ae26e..0f055535 100644
--- a/src/utils/server/rewriteFrontendStoreKeys.ts
+++ b/src/utils/server/rewriteFrontendStoreKeys.ts
@@ -5,7 +5,7 @@ import { useLocalPlaybackStore } from '../../store/localPlaybackStore';
import { useLibraryIndexStore } from '../../store/libraryIndexStore';
import { useOfflineStore } from '@/features/offline';
import { usePlayerStore } from '@/features/playback/store/playerStore';
-import { serverIndexKeyFromUrl } from './serverIndexKey';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
/**
* One `legacyId → indexKey` rewrite step. `legacyId` is whatever the keys
diff --git a/src/utils/server/switchActiveServer.ts b/src/utils/server/switchActiveServer.ts
index d6dd3bee..8cefbefe 100644
--- a/src/utils/server/switchActiveServer.ts
+++ b/src/utils/server/switchActiveServer.ts
@@ -9,8 +9,8 @@ import { useOrbitStore } from '@/features/orbit';
import { flushPlayQueueForServer } from '@/features/playback/store/queueSync';
import { markQueueHandoffPending } from '@/features/playback/store/queueSyncUiState';
import { endOrbitSession, leaveOrbitSession } from '@/features/orbit';
-import { ensureConnectUrlResolved } from './serverEndpoint';
-import { syncServerHttpContextForProfile } from './syncServerHttpContext';
+import { ensureConnectUrlResolved } from '@/lib/server/serverEndpoint';
+import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
export async function switchActiveServer(server: ServerProfile): Promise {
coverTrafficBeginServerSwitch();
diff --git a/src/utils/share/copyEntityShareLink.ts b/src/utils/share/copyEntityShareLink.ts
index f9fc8658..0dc4ef4d 100644
--- a/src/utils/share/copyEntityShareLink.ts
+++ b/src/utils/share/copyEntityShareLink.ts
@@ -1,7 +1,7 @@
import { useAuthStore } from '../../store/authStore';
-import { serverShareBaseUrl } from '../server/serverEndpoint';
+import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
import { encodeSharePayload, type EntityShareKind } from './shareLink';
-import { copyTextToClipboard } from '../server/serverMagicString';
+import { copyTextToClipboard } from '@/lib/server/serverMagicString';
/** Copies a track / album / artist / composer share link (`psysonic2-`) to the clipboard. */
export async function copyEntityShareLink(kind: EntityShareKind, id: string): Promise {
diff --git a/src/utils/share/enqueueShareSearchPayload.ts b/src/utils/share/enqueueShareSearchPayload.ts
index 08835240..8f64c6ec 100644
--- a/src/utils/share/enqueueShareSearchPayload.ts
+++ b/src/utils/share/enqueueShareSearchPayload.ts
@@ -14,8 +14,8 @@ import { songToTrack } from '@/lib/media/songToTrack';
import type { Track } from '@/lib/media/trackTypes';
import { orbitBulkGuard } from '@/features/orbit';
import { findServerIdForShareUrl } from './shareLink';
-import { connectBaseUrlForServer } from '../server/serverEndpoint';
-import { serverIndexKeyFromUrl } from '../server/serverIndexKey';
+import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import type {
AlbumShareSearchPayload,
ArtistShareSearchPayload,
diff --git a/src/utils/share/shareLink.test.ts b/src/utils/share/shareLink.test.ts
index a6f6aa10..ddef0d4f 100644
--- a/src/utils/share/shareLink.test.ts
+++ b/src/utils/share/shareLink.test.ts
@@ -7,7 +7,7 @@ import {
findServerIdForShareUrl,
normalizeShareServerUrl,
} from './shareLink';
-import { decodeServerMagicString, encodeServerMagicString, SERVER_MAGIC_STRING_PREFIX } from '../server/serverMagicString';
+import { decodeServerMagicString, encodeServerMagicString, SERVER_MAGIC_STRING_PREFIX } from '@/lib/server/serverMagicString';
import { makeServer } from '@/test/helpers/factories';
describe('shareLink vs serverMagicString', () => {
diff --git a/src/utils/share/shareSearch.test.ts b/src/utils/share/shareSearch.test.ts
index 18c9401e..642a7f79 100644
--- a/src/utils/share/shareSearch.test.ts
+++ b/src/utils/share/shareSearch.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
-import { encodeServerMagicString } from '../server/serverMagicString';
+import { encodeServerMagicString } from '@/lib/server/serverMagicString';
import { encodeSharePayload } from './shareLink';
import { parseShareSearchText, sharePayloadTotal } from './shareSearch';
diff --git a/src/utils/share/shareServerOriginLabel.ts b/src/utils/share/shareServerOriginLabel.ts
index ea80b239..2258cad5 100644
--- a/src/utils/share/shareServerOriginLabel.ts
+++ b/src/utils/share/shareServerOriginLabel.ts
@@ -1,6 +1,6 @@
import type { ServerProfile } from '../../store/authStoreTypes';
-import { serverListDisplayLabel } from '../server/serverDisplayName';
-import { serverIndexKeyFromUrl } from '../server/serverIndexKey';
+import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
+import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
import { findServerIdForShareUrl } from './shareLink';
import type { ShareSearchMatch } from './shareSearch';