refactor(lib): consolidate generic infra into src/lib

Move domain-agnostic, feature-free helpers out of the flat utils/ and store/
roots into src/lib/ (plan M4, §3 lib/ layer):
- lib/format/: formatBytes, formatClockTime, formatDuration, formatHumanDuration,
  relativeTime (pure format/date/byte/clock helpers)
- lib/i18n.ts: i18next bootstrap (global app infra)
- lib/util/: sanitizeHtml, platform, dedupeById, safeStorage (pure helpers +
  the zustand storage adapter)

playbackScheduleFormat stays in utils/format (runtime usePlayerStore dep =
audio-core coupling, not generic). formatClockTime keeps a type-only
@/store/authStoreTypes import (erased, no runtime inversion — accepted per the
deviceSync precedent).

Pure move via deep @/lib/* specifiers (no barrel → no mock-collapse surface).
tsc 0, lint 0/0, full suite 319 files / 2353 tests green.

Tooling note: lib_move.py regex extended to also rewrite side-effect imports
(import './i18n') and vi.importActual paths — both were silent gaps.
This commit is contained in:
Psychotoxical
2026-06-30 08:07:04 +02:00
parent 8cc022581f
commit 209dd61442
103 changed files with 119 additions and 119 deletions
@@ -7,7 +7,7 @@ import { usePlayerStore } from '@/store/playerStore';
import {
displayPlaylistName, isSmartPlaylistName, type PendingSmartPlaylist,
} from '@/features/playlist/utils/playlistsSmart';
import { formatHumanHoursMinutes } from '@/utils/format/formatHumanDuration';
import { formatHumanHoursMinutes } from '@/lib/format/formatHumanDuration';
import { useDragSource } from '@/contexts/DragDropContext';
import { PlaylistCardMainCover, PlaylistSmartCoverCell } from '@/features/playlist/components/PlaylistCoverImages';
@@ -5,8 +5,8 @@ import type { ColDef } from '@/utils/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
import i18n from '@/i18n';
import { formatTrackTime } from '@/utils/format/formatDuration';
import i18n from '@/lib/i18n';
import { formatTrackTime } from '@/lib/format/formatDuration';
import StarRating from '@/components/StarRating';
import { PlaylistArtistCell } from '@/features/playlist/components/PlaylistArtistCell';
@@ -4,7 +4,7 @@ import { Check, ListPlus, X } from 'lucide-react';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/store/playerStore';
import { songToTrack } from '@/utils/playback/songToTrack';
import { formatTrackTime } from '@/utils/format/formatDuration';
import { formatTrackTime } from '@/lib/format/formatDuration';
import { AlbumCoverArtImage } from '@/cover/AlbumCoverArtImage';
import { COVER_DENSE_SEARCH_CSS_PX } from '@/cover/layoutSizes';
import { AddToPlaylistSubmenu } from '@/components/ContextMenu';
@@ -14,8 +14,8 @@ import { songToTrack } from '@/utils/playback/songToTrack';
import { getQueueTracksView } from '@/utils/library/queueTrackView';
import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
import { formatTrackTime } from '@/utils/format/formatDuration';
import i18n from '@/i18n';
import { formatTrackTime } from '@/lib/format/formatDuration';
import i18n from '@/lib/i18n';
const PL_CENTERED = new Set(['favorite', 'rating', 'duration', 'playCount', 'bpm']);