From 7ad196711e5e657282458bb6a1731d9c236544db Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Tue, 30 Jun 2026 08:20:11 +0200 Subject: [PATCH] refactor(lib): move generic hooks, DnD engine, shortcut contract to lib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continue M4 lib/ de-flattening with domain-agnostic infra: - lib/hooks/: 9 pure generic hooks (useDebouncedValue, useIsMobile, useLongPressAction, useRangeSelection, useResizeClientHeight, useWindowVisibility, useSystemPrefersDark, useVirtualizerScrollMargin, useRemeasureGridVirtualizer) — all PURE (only react/zustand/tanstack deps). - lib/dnd/DragDropContext.tsx: the generic mouse-event DnD engine (WebKitGTK HTML5-DnD workaround, ~24 importers, self-contained) — empties src/contexts/. - lib/shortcuts/: shortcutActions + shortcutTypes (the action-id + binding contract; registry/dispatch/bindings stay app-level and import the contract, app->lib direction). useWindowFullscreenState deliberately NOT moved — kept in hooks/ as app-shell per the handoff iron-rule list (overrides its pure-helper appearance). Pure move via deep @/lib/* specifiers. tsc 0, lint 0/0, full suite 319 files / 2353 tests green. --- src/app/AppRoutes.tsx | 2 +- src/app/AppShell.tsx | 2 +- src/app/MainApp.tsx | 4 ++-- src/app/MiniPlayerApp.test.tsx | 2 +- src/app/MiniPlayerApp.tsx | 2 +- src/components/BecauseYouLikeRail.tsx | 2 +- src/components/Hero.tsx | 6 +++--- src/components/PlaybackScheduleBadge.tsx | 2 +- src/components/SongCard.tsx | 2 +- src/components/SongRow.tsx | 2 +- src/components/VirtualCardGrid.tsx | 6 +++--- src/components/randomMix/RandomMixTrackRow.tsx | 2 +- src/config/shortcutActionRegistry.ts | 2 +- src/config/shortcutBindings.ts | 2 +- src/config/shortcutDispatch.ts | 2 +- src/features/album/components/AlbumCard.tsx | 4 ++-- src/features/album/components/AlbumHeader.test.tsx | 2 +- src/features/album/components/AlbumHeader.tsx | 2 +- src/features/album/components/AlbumTrackList.tsx | 2 +- src/features/album/hooks/useAlbumBrowseData.ts | 2 +- src/features/album/hooks/useAlbumTrackListSelection.ts | 2 +- src/features/album/pages/Albums.tsx | 2 +- src/features/album/pages/LosslessAlbums.tsx | 2 +- src/features/album/pages/RandomAlbums.tsx | 2 +- src/features/artist/components/ArtistDetailHero.tsx | 2 +- .../artist/components/ArtistDetailSimilarArtists.tsx | 2 +- src/features/artist/pages/Artists.tsx | 4 ++-- .../favorites/components/FavoritesSongsTracklist.tsx | 4 ++-- src/features/miniPlayer/components/MiniPlayer.tsx | 2 +- src/features/miniPlayer/hooks/useMiniQueueDrag.ts | 2 +- src/features/miniPlayer/hooks/useMiniSync.ts | 2 +- src/features/nowPlaying/components/NpCardWrap.tsx | 2 +- src/features/nowPlaying/pages/NowPlaying.tsx | 2 +- src/features/playlist/components/PlaylistCard.tsx | 2 +- .../playlist/components/PlaylistFolderSection.tsx | 2 +- src/features/playlist/components/PlaylistTracklist.tsx | 4 ++-- src/features/playlist/components/PlaylistsFolderView.tsx | 2 +- src/features/playlist/hooks/usePlaylistDnDReorder.ts | 2 +- src/features/playlist/pages/PlaylistDetail.tsx | 2 +- src/features/playlist/pages/Playlists.tsx | 2 +- src/features/radio/components/RadioCard.tsx | 2 +- src/features/settings/components/BackdropSourceList.tsx | 2 +- src/features/settings/components/InputTab.tsx | 2 +- src/features/settings/components/ReorderGripHandle.tsx | 2 +- src/features/settings/components/settingsSearch.ts | 2 +- src/hooks/tauriBridge/useCliBridge.ts | 2 +- src/hooks/tauriBridge/useInAppKeybindings.ts | 2 +- src/hooks/tauriBridge/useMediaAndWindowBridge.ts | 2 +- src/hooks/useListReorderDnd.ts | 2 +- src/hooks/useQueuePanelDrag.ts | 2 +- src/hooks/useThemeScheduler.ts | 2 +- src/{contexts => lib/dnd}/DragDropContext.tsx | 0 src/{ => lib}/hooks/useDebouncedValue.test.ts | 2 +- src/{ => lib}/hooks/useDebouncedValue.ts | 0 src/{ => lib}/hooks/useIsMobile.ts | 0 src/{ => lib}/hooks/useLongPressAction.test.ts | 2 +- src/{ => lib}/hooks/useLongPressAction.ts | 0 src/{ => lib}/hooks/useRangeSelection.ts | 0 src/{ => lib}/hooks/useRemeasureGridVirtualizer.ts | 0 src/{ => lib}/hooks/useResizeClientHeight.ts | 0 src/{ => lib}/hooks/useSystemPrefersDark.ts | 0 src/{ => lib}/hooks/useVirtualizerScrollMargin.ts | 0 src/{ => lib}/hooks/useWindowVisibility.tsx | 0 src/{config => lib/shortcuts}/shortcutActions.ts | 8 ++++---- src/{config => lib/shortcuts}/shortcutTypes.ts | 0 src/pages/Composers.tsx | 4 ++-- src/pages/GenreDetail.tsx | 2 +- src/pages/MostPlayed.tsx | 2 +- src/pages/NewReleases.tsx | 2 +- src/pages/RandomMix.tsx | 2 +- src/shortcuts/runtime.ts | 2 +- src/store/globalShortcutsStore.ts | 4 ++-- src/store/keybindingsStore.ts | 4 ++-- src/utils/format/playbackScheduleFormat.ts | 2 +- 74 files changed, 78 insertions(+), 78 deletions(-) rename src/{contexts => lib/dnd}/DragDropContext.tsx (100%) rename src/{ => lib}/hooks/useDebouncedValue.test.ts (91%) rename src/{ => lib}/hooks/useDebouncedValue.ts (100%) rename src/{ => lib}/hooks/useIsMobile.ts (100%) rename src/{ => lib}/hooks/useLongPressAction.test.ts (97%) rename src/{ => lib}/hooks/useLongPressAction.ts (100%) rename src/{ => lib}/hooks/useRangeSelection.ts (100%) rename src/{ => lib}/hooks/useRemeasureGridVirtualizer.ts (100%) rename src/{ => lib}/hooks/useResizeClientHeight.ts (100%) rename src/{ => lib}/hooks/useSystemPrefersDark.ts (100%) rename src/{ => lib}/hooks/useVirtualizerScrollMargin.ts (100%) rename src/{ => lib}/hooks/useWindowVisibility.tsx (100%) rename src/{config => lib/shortcuts}/shortcutActions.ts (95%) rename src/{config => lib/shortcuts}/shortcutTypes.ts (100%) diff --git a/src/app/AppRoutes.tsx b/src/app/AppRoutes.tsx index 5ca95a9f..0cb73218 100644 --- a/src/app/AppRoutes.tsx +++ b/src/app/AppRoutes.tsx @@ -1,7 +1,7 @@ import { lazy } from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; import MobilePlayerView from '../components/MobilePlayerView'; -import { useIsMobile } from '../hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; import { useSidebarStore } from '@/features/sidebar'; import { useAuthStore } from '../store/authStore'; import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable'; diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx index 66b0df52..420e5bde 100644 --- a/src/app/AppShell.tsx +++ b/src/app/AppShell.tsx @@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next'; import Sidebar from '@/features/sidebar'; import PlayerBar from '../components/PlayerBar'; import BottomNav from '../components/BottomNav'; -import { useIsMobile } from '../hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; import { LiveSearch } from '@/features/search'; import DevNetworkModeToggle from '../components/DevNetworkModeToggle'; import { NowPlayingDropdown } from '@/features/nowPlaying'; diff --git a/src/app/MainApp.tsx b/src/app/MainApp.tsx index 78532e7c..8419a86e 100644 --- a/src/app/MainApp.tsx +++ b/src/app/MainApp.tsx @@ -3,8 +3,8 @@ import { lazy, Suspense, useEffect, useState } from 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { invoke } from '@tauri-apps/api/core'; import { showToast } from '../utils/ui/toast'; -import { WindowVisibilityProvider } from '../hooks/useWindowVisibility'; -import { DragDropProvider } from '../contexts/DragDropContext'; +import { WindowVisibilityProvider } from '@/lib/hooks/useWindowVisibility'; +import { DragDropProvider } from '@/lib/dnd/DragDropContext'; import PasteClipboardHandler from '../components/PasteClipboardHandler'; import ExportPickerModal from '../components/ExportPickerModal'; import { ZipDownloadOverlay } from '@/features/offline'; diff --git a/src/app/MiniPlayerApp.test.tsx b/src/app/MiniPlayerApp.test.tsx index 2747f70b..72ac8649 100644 --- a/src/app/MiniPlayerApp.test.tsx +++ b/src/app/MiniPlayerApp.test.tsx @@ -32,7 +32,7 @@ vi.mock('@/features/miniPlayer', () => ({ default: () =>
})); vi.mock('@/ui/TooltipPortal', () => ({ default: () =>
})); vi.mock('../components/FpsOverlay', () => ({ default: () =>
})); -vi.mock('../contexts/DragDropContext', () => ({ +vi.mock('@/lib/dnd/DragDropContext', () => ({ DragDropProvider: ({ children }: { children: React.ReactNode }) => <>{children}, })); diff --git a/src/app/MiniPlayerApp.tsx b/src/app/MiniPlayerApp.tsx index 8e4d3944..6edc0351 100644 --- a/src/app/MiniPlayerApp.tsx +++ b/src/app/MiniPlayerApp.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { DragDropProvider } from '../contexts/DragDropContext'; +import { DragDropProvider } from '@/lib/dnd/DragDropContext'; import MiniPlayer from '@/features/miniPlayer'; import GlobalConfirmModal from '../components/GlobalConfirmModal'; import TooltipPortal from '@/ui/TooltipPortal'; diff --git a/src/components/BecauseYouLikeRail.tsx b/src/components/BecauseYouLikeRail.tsx index 45c167f0..d7c45e84 100644 --- a/src/components/BecauseYouLikeRail.tsx +++ b/src/components/BecauseYouLikeRail.tsx @@ -21,7 +21,7 @@ import { import { usePlayerStore } from '../store/playerStore'; import { useAuthStore } from '../store/authStore'; import { playAlbum, playAlbumShuffled } from '../utils/playback/playAlbum'; -import { useLongPressAction } from '../hooks/useLongPressAction'; +import { useLongPressAction } from '@/lib/hooks/useLongPressAction'; import { LongPressWaveOverlay } from './LongPressWaveOverlay'; import { formatHumanHoursMinutes } from '@/lib/format/formatHumanDuration'; import { AlbumRow } from '@/features/album'; diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 060667fe..3b018b46 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -13,14 +13,14 @@ import { useHeroBackdrop } from '../cover/useHeroBackdrop'; import { useCachedUrl } from '@/ui/CachedImage'; import { usePlayerStore } from '../store/playerStore'; import { useTranslation } from 'react-i18next'; -import { useIsMobile } from '../hooks/useIsMobile'; -import { useWindowVisibility } from '../hooks/useWindowVisibility'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; +import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility'; import { useAuthStore } from '../store/authStore'; import { useThemeStore } from '../store/themeStore'; import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mix/mixRatingFilter'; import { usePerfProbeFlags } from '../utils/perf/perfFlags'; import { playAlbum, playAlbumShuffled } from '../utils/playback/playAlbum'; -import { useLongPressAction } from '../hooks/useLongPressAction'; +import { useLongPressAction } from '@/lib/hooks/useLongPressAction'; import { LongPressWaveOverlay } from './LongPressWaveOverlay'; import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album'; diff --git a/src/components/PlaybackScheduleBadge.tsx b/src/components/PlaybackScheduleBadge.tsx index 2a461a9c..7275867d 100644 --- a/src/components/PlaybackScheduleBadge.tsx +++ b/src/components/PlaybackScheduleBadge.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '../store/playerStore'; import { useShallow } from 'zustand/react/shallow'; import { formatPlaybackScheduleRemaining } from '../utils/format/playbackScheduleFormat'; -import { useWindowVisibility } from '../hooks/useWindowVisibility'; +import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility'; export interface PlaybackScheduleBadgeProps { /** Anchor element (usually the play/pause button wrapper) — the ring centres on it. */ diff --git a/src/components/SongCard.tsx b/src/components/SongCard.tsx index 59dc1609..10a5bd19 100644 --- a/src/components/SongCard.tsx +++ b/src/components/SongCard.tsx @@ -9,7 +9,7 @@ import { useCoverArt } from '../cover/useCoverArt'; import { useTrackCoverRef } from '../cover/useLibraryCoverRef'; import { COVER_DENSE_RAIL_CELL_CSS_PX } from '../cover/layoutSizes'; import { enqueueAndPlay } from '../utils/playback/playSong'; -import { useDragDrop } from '../contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { useOrbitSongRowBehavior } from '@/features/orbit'; import { useNavigateToAlbum } from '@/features/album'; import { useNavigateToArtist } from '@/features/artist'; diff --git a/src/components/SongRow.tsx b/src/components/SongRow.tsx index 0d4bb1b1..dc305ef6 100644 --- a/src/components/SongRow.tsx +++ b/src/components/SongRow.tsx @@ -7,7 +7,7 @@ import { Play, ListPlus } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '../store/playerStore'; import { enqueueAndPlay } from '../utils/playback/playSong'; -import { useDragDrop } from '../contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { useOrbitSongRowBehavior } from '@/features/orbit'; import { formatTrackTime } from '@/lib/format/formatDuration'; import { resolveTrackArtistRefs } from '../utils/playback/trackArtistRefs'; diff --git a/src/components/VirtualCardGrid.tsx b/src/components/VirtualCardGrid.tsx index aff5ce2a..1dda46d9 100644 --- a/src/components/VirtualCardGrid.tsx +++ b/src/components/VirtualCardGrid.tsx @@ -3,10 +3,10 @@ import { GRID_COVER_WARM_LIMIT } from '../cover/layoutSizes'; import { useWarmGridCovers } from '../hooks/useWarmGridCovers'; import { useVirtualizer } from '@tanstack/react-virtual'; import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../constants/appScroll'; -import { useElementClientHeightById } from '../hooks/useResizeClientHeight'; +import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight'; import { useCardGridMetrics } from '../hooks/useCardGridMetrics'; -import { useRemeasureGridVirtualizer } from '../hooks/useRemeasureGridVirtualizer'; -import { useVirtualizerScrollMargin } from '../hooks/useVirtualizerScrollMargin'; +import { useRemeasureGridVirtualizer } from '@/lib/hooks/useRemeasureGridVirtualizer'; +import { useVirtualizerScrollMargin } from '@/lib/hooks/useVirtualizerScrollMargin'; import type { CardGridRowHeightVariant } from '../utils/cardGridLayout'; export type VirtualCardGridProps = { diff --git a/src/components/randomMix/RandomMixTrackRow.tsx b/src/components/randomMix/RandomMixTrackRow.tsx index 8a6f36de..4a9a509e 100644 --- a/src/components/randomMix/RandomMixTrackRow.tsx +++ b/src/components/randomMix/RandomMixTrackRow.tsx @@ -4,7 +4,7 @@ import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react'; import type { SubsonicSong } from '@/lib/api/subsonicTypes'; import type { Track } from '../../store/playerStoreTypes'; import { previewInputFromSong, usePreviewStore } from '../../store/previewStore'; -import { useDragDrop } from '../../contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { formatRandomMixDuration } from '../../utils/componentHelpers/randomMixHelpers'; interface Props { diff --git a/src/config/shortcutActionRegistry.ts b/src/config/shortcutActionRegistry.ts index a997cb34..85832fe5 100644 --- a/src/config/shortcutActionRegistry.ts +++ b/src/config/shortcutActionRegistry.ts @@ -8,7 +8,7 @@ import { usePlayerStore } from '../store/playerStore'; import { usePreviewStore } from '../store/previewStore'; import { useLyricsStore } from '../store/lyricsStore'; import { showToast } from '../utils/ui/toast'; -import type { ActionContext, ShortcutSlot, ShortcutActionMeta } from './shortcutTypes'; +import type { ActionContext, ShortcutSlot, ShortcutActionMeta } from '@/lib/shortcuts/shortcutTypes'; let cliPremuteVolume: number | null = null; diff --git a/src/config/shortcutBindings.ts b/src/config/shortcutBindings.ts index c2da8494..a939dee5 100644 --- a/src/config/shortcutBindings.ts +++ b/src/config/shortcutBindings.ts @@ -1,4 +1,4 @@ -import type { ShortcutSlot } from './shortcutTypes'; +import type { ShortcutSlot } from '@/lib/shortcuts/shortcutTypes'; import { SHORTCUT_ACTION_REGISTRY, type ShortcutAction, diff --git a/src/config/shortcutDispatch.ts b/src/config/shortcutDispatch.ts index a3061d94..ae785832 100644 --- a/src/config/shortcutDispatch.ts +++ b/src/config/shortcutDispatch.ts @@ -3,7 +3,7 @@ import i18n from '@/lib/i18n'; import { usePlayerStore } from '../store/playerStore'; import { showToast } from '../utils/ui/toast'; import { playByOpaqueId } from '../utils/playback/playByOpaqueId'; -import type { ActionContext, CliContext } from './shortcutTypes'; +import type { ActionContext, CliContext } from '@/lib/shortcuts/shortcutTypes'; import { SHORTCUT_ACTION_REGISTRY, type ShortcutAction, diff --git a/src/features/album/components/AlbumCard.tsx b/src/features/album/components/AlbumCard.tsx index 82fba0a0..6be3c48d 100644 --- a/src/features/album/components/AlbumCard.tsx +++ b/src/features/album/components/AlbumCard.tsx @@ -17,9 +17,9 @@ import { resolveCoverDisplayTier } from '@/cover/tiers'; import { acquireUrl } from '@/utils/imageCache/urlPool'; import { OpenArtistRefInline } from '@/features/artist'; import { fetchAlbumTracks, playAlbum, playAlbumShuffled } from '@/utils/playback/playAlbum'; -import { useLongPressAction } from '@/hooks/useLongPressAction'; +import { useLongPressAction } from '@/lib/hooks/useLongPressAction'; import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay'; -import { useDragDrop } from '@/contexts/DragDropContext'; +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'; diff --git a/src/features/album/components/AlbumHeader.test.tsx b/src/features/album/components/AlbumHeader.test.tsx index 170472ab..fd5572e4 100644 --- a/src/features/album/components/AlbumHeader.test.tsx +++ b/src/features/album/components/AlbumHeader.test.tsx @@ -16,7 +16,7 @@ vi.mock('react-router-dom', async importActual => { vi.mock('@/cover/useLibraryCoverRef', () => ({ useAlbumCoverRef: () => undefined })); vi.mock('@/cover/lightbox', () => ({ useCoverLightboxSrc: () => ({ open: vi.fn(), lightbox: null }) })); vi.mock('@/features/album/hooks/useAlbumDetailBack', () => ({ useAlbumDetailBack: () => vi.fn() })); -vi.mock('@/hooks/useIsMobile', () => ({ useIsMobile: () => false })); +vi.mock('@/lib/hooks/useIsMobile', () => ({ useIsMobile: () => false })); vi.mock('@/store/themeStore', () => ({ useThemeStore: () => false })); vi.mock('@/components/StarRating', () => ({ default: () => null })); vi.mock('@/features/artist', () => ({ OpenArtistRefInline: () => null })); diff --git a/src/features/album/components/AlbumHeader.tsx b/src/features/album/components/AlbumHeader.tsx index 64d069f9..23221d91 100644 --- a/src/features/album/components/AlbumHeader.tsx +++ b/src/features/album/components/AlbumHeader.tsx @@ -7,7 +7,7 @@ import { CoverArtImage } from '@/cover/CoverArtImage'; import { useAlbumCoverRef } from '@/cover/useLibraryCoverRef'; import { useCoverLightboxSrc } from '@/cover/lightbox'; import { useTranslation } from 'react-i18next'; -import { useIsMobile } from '@/hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; import { useAlbumDetailBack } from '@/features/album/hooks/useAlbumDetailBack'; import { useThemeStore } from '@/store/themeStore'; import StarRating from '@/components/StarRating'; diff --git a/src/features/album/components/AlbumTrackList.tsx b/src/features/album/components/AlbumTrackList.tsx index 1055a645..1e806b26 100644 --- a/src/features/album/components/AlbumTrackList.tsx +++ b/src/features/album/components/AlbumTrackList.tsx @@ -4,7 +4,7 @@ import React, { useMemo, useState, useEffect } from 'react'; import { useTracklistColumns } from '@/utils/useTracklistColumns'; import { usePlayerStore } from '@/store/playerStore'; import { useTranslation } from 'react-i18next'; -import { useIsMobile } from '@/hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; import { useSelectionStore } from '@/store/selectionStore'; import { COLUMNS, diff --git a/src/features/album/hooks/useAlbumBrowseData.ts b/src/features/album/hooks/useAlbumBrowseData.ts index 19099b7d..1f05b34e 100644 --- a/src/features/album/hooks/useAlbumBrowseData.ts +++ b/src/features/album/hooks/useAlbumBrowseData.ts @@ -38,7 +38,7 @@ import { } from '@/utils/library/albumBrowseCatalogChunk'; import { useOfflineBrowseContext } from '@/features/offline'; import { useClientSliceInfiniteScroll } from '@/hooks/useClientSliceInfiniteScroll'; -import { useDebouncedValue } from '@/hooks/useDebouncedValue'; +import { useDebouncedValue } from '@/lib/hooks/useDebouncedValue'; import { useInpageScrollSentinel } from '@/hooks/useInpageScrollSentinel'; const PAGE_SIZE = 30; diff --git a/src/features/album/hooks/useAlbumTrackListSelection.ts b/src/features/album/hooks/useAlbumTrackListSelection.ts index 991b49ee..f51cf785 100644 --- a/src/features/album/hooks/useAlbumTrackListSelection.ts +++ b/src/features/album/hooks/useAlbumTrackListSelection.ts @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useRef } from 'react'; import type { SubsonicSong } from '@/lib/api/subsonicTypes'; import { useSelectionStore } from '@/store/selectionStore'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { songToTrack } from '@/utils/playback/songToTrack'; interface UseAlbumTrackListSelectionArgs { diff --git a/src/features/album/pages/Albums.tsx b/src/features/album/pages/Albums.tsx index 52fc2f59..cfd18b36 100644 --- a/src/features/album/pages/Albums.tsx +++ b/src/features/album/pages/Albums.tsx @@ -26,7 +26,7 @@ import { Download, HardDriveDownload, Disc3, ListPlus } from 'lucide-react'; import SelectionToggleButton from '@/components/SelectionToggleButton'; import FilterQuickClear from '@/components/FilterQuickClear'; import { usePerfProbeFlags } from '@/utils/perf/perfFlags'; -import { useRangeSelection } from '@/hooks/useRangeSelection'; +import { useRangeSelection } from '@/lib/hooks/useRangeSelection'; import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight'; import { useInpageScrollViewport } from '@/hooks/useInpageScrollViewport'; import InpageScrollSentinel from '@/components/InpageScrollSentinel'; diff --git a/src/features/album/pages/LosslessAlbums.tsx b/src/features/album/pages/LosslessAlbums.tsx index 372f05c0..2214ae66 100644 --- a/src/features/album/pages/LosslessAlbums.tsx +++ b/src/features/album/pages/LosslessAlbums.tsx @@ -12,7 +12,7 @@ import { useOfflineStore } from '@/features/offline'; import { useDownloadModalStore } from '@/features/offline'; import { usePlayerStore } from '@/store/playerStore'; import { useZipDownloadStore } from '@/features/offline'; -import { useRangeSelection } from '@/hooks/useRangeSelection'; +import { useRangeSelection } from '@/lib/hooks/useRangeSelection'; import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight'; import { usePerfProbeFlags } from '@/utils/perf/perfFlags'; import { showToast } from '@/utils/ui/toast'; diff --git a/src/features/album/pages/RandomAlbums.tsx b/src/features/album/pages/RandomAlbums.tsx index 0a829222..34870caa 100644 --- a/src/features/album/pages/RandomAlbums.tsx +++ b/src/features/album/pages/RandomAlbums.tsx @@ -22,7 +22,7 @@ import { invoke } from '@tauri-apps/api/core'; import { join } from '@tauri-apps/api/path'; import { showToast } from '@/utils/ui/toast'; import { useZipDownloadStore } from '@/features/offline'; -import { useRangeSelection } from '@/hooks/useRangeSelection'; +import { useRangeSelection } from '@/lib/hooks/useRangeSelection'; import { usePerfProbeFlags } from '@/utils/perf/perfFlags'; import { albumGridWarmCovers, COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '@/cover/layoutSizes'; import { diff --git a/src/features/artist/components/ArtistDetailHero.tsx b/src/features/artist/components/ArtistDetailHero.tsx index e68a385d..040d4ca9 100644 --- a/src/features/artist/components/ArtistDetailHero.tsx +++ b/src/features/artist/components/ArtistDetailHero.tsx @@ -10,7 +10,7 @@ import { useOfflineStore } from '@/features/offline'; import { useAuthStore } from '@/store/authStore'; import { useThemeStore } from '@/store/themeStore'; import { useArtistOfflineState } from '@/features/artist/hooks/useArtistOfflineState'; -import { useIsMobile } from '@/hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; import { ArtistHeroCover } from '@/cover/artistHero'; import { useArtistBanner, useArtistFanart } from '@/cover/useArtistFanart'; import { backdropFromConfig } from '@/cover/artistBackdrop'; diff --git a/src/features/artist/components/ArtistDetailSimilarArtists.tsx b/src/features/artist/components/ArtistDetailSimilarArtists.tsx index 7df6a550..bad99440 100644 --- a/src/features/artist/components/ArtistDetailSimilarArtists.tsx +++ b/src/features/artist/components/ArtistDetailSimilarArtists.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { ChevronDown, ChevronUp } from 'lucide-react'; import type { SubsonicArtist } from '@/lib/api/subsonicTypes'; -import { useIsMobile } from '@/hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; interface Props { marginTop: string; diff --git a/src/features/artist/pages/Artists.tsx b/src/features/artist/pages/Artists.tsx index c1c227de..fa86e2f2 100644 --- a/src/features/artist/pages/Artists.tsx +++ b/src/features/artist/pages/Artists.tsx @@ -9,8 +9,8 @@ import { useAuthStore } from '@/store/authStore'; import { useTranslation } from 'react-i18next'; import { useVirtualizer } from '@tanstack/react-virtual'; import { APP_MAIN_SCROLL_VIEWPORT_ID, ARTISTS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll'; -import { useElementClientHeightById, useElementClientHeightForElement } from '@/hooks/useResizeClientHeight'; -import { useVirtualizerScrollMargin } from '@/hooks/useVirtualizerScrollMargin'; +import { useElementClientHeightById, useElementClientHeightForElement } from '@/lib/hooks/useResizeClientHeight'; +import { useVirtualizerScrollMargin } from '@/lib/hooks/useVirtualizerScrollMargin'; import { usePerfProbeFlags } from '@/utils/perf/perfFlags'; import { ALL_SENTINEL, diff --git a/src/features/favorites/components/FavoritesSongsTracklist.tsx b/src/features/favorites/components/FavoritesSongsTracklist.tsx index dfed81c4..2324bd67 100644 --- a/src/features/favorites/components/FavoritesSongsTracklist.tsx +++ b/src/features/favorites/components/FavoritesSongsTracklist.tsx @@ -10,12 +10,12 @@ import { usePlayerStore } from '@/store/playerStore'; import { previewInputFromSong, usePreviewStore } from '@/store/previewStore'; import { useSelectionStore } from '@/store/selectionStore'; import { useThemeStore } from '@/store/themeStore'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { useOrbitSongRowBehavior } from '@/features/orbit'; import { songToTrack } from '@/utils/playback/songToTrack'; import { appendServerQuery } from '@/utils/navigation/detailServerScope'; import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll'; -import { useElementClientHeightById } from '@/hooks/useResizeClientHeight'; +import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight'; import { SORTABLE_COLUMNS } from '@/features/favorites/hooks/useFavoritesSongFiltering'; interface Props { diff --git a/src/features/miniPlayer/components/MiniPlayer.tsx b/src/features/miniPlayer/components/MiniPlayer.tsx index 25a38cab..da484424 100644 --- a/src/features/miniPlayer/components/MiniPlayer.tsx +++ b/src/features/miniPlayer/components/MiniPlayer.tsx @@ -5,7 +5,7 @@ import { emit } from '@tauri-apps/api/event'; import { invoke } from '@tauri-apps/api/core'; import { useTranslation } from 'react-i18next'; import { usePlayerStore } from '@/store/playerStore'; -import { registerQueueDragHitTest } from '@/contexts/DragDropContext'; +import { registerQueueDragHitTest } from '@/lib/dnd/DragDropContext'; import MiniContextMenu from '@/features/miniPlayer/components/MiniContextMenu'; import type { MiniSyncPayload, MiniControlAction, MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge'; import { diff --git a/src/features/miniPlayer/hooks/useMiniQueueDrag.ts b/src/features/miniPlayer/hooks/useMiniQueueDrag.ts index cd2e99ab..4d6d18db 100644 --- a/src/features/miniPlayer/hooks/useMiniQueueDrag.ts +++ b/src/features/miniPlayer/hooks/useMiniQueueDrag.ts @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import { emit } from '@tauri-apps/api/event'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { usePlayerStore } from '@/store/playerStore'; interface Args { diff --git a/src/features/miniPlayer/hooks/useMiniSync.ts b/src/features/miniPlayer/hooks/useMiniSync.ts index 99ceb787..d7c3d2d1 100644 --- a/src/features/miniPlayer/hooks/useMiniSync.ts +++ b/src/features/miniPlayer/hooks/useMiniSync.ts @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react'; import { emit, listen } from '@tauri-apps/api/event'; -import { useWindowVisibility } from '@/hooks/useWindowVisibility'; +import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility'; import type { MiniSyncPayload } from '@/features/miniPlayer/utils/miniPlayerBridge'; interface ProgressPayload { diff --git a/src/features/nowPlaying/components/NpCardWrap.tsx b/src/features/nowPlaying/components/NpCardWrap.tsx index 05900acc..69bf004b 100644 --- a/src/features/nowPlaying/components/NpCardWrap.tsx +++ b/src/features/nowPlaying/components/NpCardWrap.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useDragSource } from '@/contexts/DragDropContext'; +import { useDragSource } from '@/lib/dnd/DragDropContext'; import type { NpCardId } from '@/features/nowPlaying/store/nowPlayingLayoutStore'; interface NpCardWrapProps { diff --git a/src/features/nowPlaying/pages/NowPlaying.tsx b/src/features/nowPlaying/pages/NowPlaying.tsx index 5f0e682d..e84b7b2a 100644 --- a/src/features/nowPlaying/pages/NowPlaying.tsx +++ b/src/features/nowPlaying/pages/NowPlaying.tsx @@ -13,7 +13,7 @@ import { useAuthStore } from '@/store/authStore'; import { useLyricsStore } from '@/store/lyricsStore'; import { songToTrack } from '@/utils/playback/songToTrack'; import { useRadioMetadata } from '@/features/radio'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import OverlayScrollArea from '@/ui/OverlayScrollArea'; import { useNpLayoutStore, NP_CARD_IDS, diff --git a/src/features/playlist/components/PlaylistCard.tsx b/src/features/playlist/components/PlaylistCard.tsx index 4cbec304..81395dab 100644 --- a/src/features/playlist/components/PlaylistCard.tsx +++ b/src/features/playlist/components/PlaylistCard.tsx @@ -8,7 +8,7 @@ import { displayPlaylistName, isSmartPlaylistName, type PendingSmartPlaylist, } from '@/features/playlist/utils/playlistsSmart'; import { formatHumanHoursMinutes } from '@/lib/format/formatHumanDuration'; -import { useDragSource } from '@/contexts/DragDropContext'; +import { useDragSource } from '@/lib/dnd/DragDropContext'; import { PlaylistCardMainCover, PlaylistSmartCoverCell } from '@/features/playlist/components/PlaylistCoverImages'; interface Props { diff --git a/src/features/playlist/components/PlaylistFolderSection.tsx b/src/features/playlist/components/PlaylistFolderSection.tsx index 799d4866..ebf1fbec 100644 --- a/src/features/playlist/components/PlaylistFolderSection.tsx +++ b/src/features/playlist/components/PlaylistFolderSection.tsx @@ -4,7 +4,7 @@ import { ChevronRight, Folder, Pencil, Trash2 } from 'lucide-react'; import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes'; import { usePlaylistFolderStore } from '@/features/playlist/store/playlistFolderStore'; import type { PlaylistFolder } from '@/features/playlist/utils/playlistFolders'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { VirtualCardGrid } from '@/components/VirtualCardGrid'; interface Props { diff --git a/src/features/playlist/components/PlaylistTracklist.tsx b/src/features/playlist/components/PlaylistTracklist.tsx index fbbebb8a..16aa5575 100644 --- a/src/features/playlist/components/PlaylistTracklist.tsx +++ b/src/features/playlist/components/PlaylistTracklist.tsx @@ -4,7 +4,7 @@ import PlaylistRow, { type PlaylistRowCallbacks } from '@/features/playlist/comp import { TracklistColumnPicker } from '@/components/albumTrackList/TracklistColumnPicker'; import { useTranslation } from 'react-i18next'; import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll'; -import { useElementClientHeightById } from '@/hooks/useResizeClientHeight'; +import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight'; import { useNavigate } from 'react-router-dom'; import { ListPlus, Search, Trash2, X, @@ -15,7 +15,7 @@ import type { Track } from '@/store/playerStoreTypes'; import { usePlayerStore } from '@/store/playerStore'; import { previewInputFromSong, usePreviewStore } from '@/store/previewStore'; import { useThemeStore } from '@/store/themeStore'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { useOrbitSongRowBehavior } from '@/features/orbit'; import { songToTrack } from '@/utils/playback/songToTrack'; import type { PlaylistSortKey, PlaylistSortDir } from '@/features/playlist/utils/playlistDisplayedSongs'; diff --git a/src/features/playlist/components/PlaylistsFolderView.tsx b/src/features/playlist/components/PlaylistsFolderView.tsx index b4174803..4180c224 100644 --- a/src/features/playlist/components/PlaylistsFolderView.tsx +++ b/src/features/playlist/components/PlaylistsFolderView.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes'; import { EMPTY_SERVER_FOLDERS, usePlaylistFolderStore } from '@/features/playlist/store/playlistFolderStore'; import { groupPlaylistsByFolder } from '@/features/playlist/utils/playlistFolders'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import PlaylistFolderSection from '@/features/playlist/components/PlaylistFolderSection'; interface Props { diff --git a/src/features/playlist/hooks/usePlaylistDnDReorder.ts b/src/features/playlist/hooks/usePlaylistDnDReorder.ts index 66ffda99..a8431324 100644 --- a/src/features/playlist/hooks/usePlaylistDnDReorder.ts +++ b/src/features/playlist/hooks/usePlaylistDnDReorder.ts @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import type { SubsonicSong } from '@/lib/api/subsonicTypes'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { runPlaylistReorderDrop } from '@/features/playlist/utils/runPlaylistReorderDrop'; export interface PlaylistDnDReorderDeps { diff --git a/src/features/playlist/pages/PlaylistDetail.tsx b/src/features/playlist/pages/PlaylistDetail.tsx index f371d1f2..e9eb2f48 100644 --- a/src/features/playlist/pages/PlaylistDetail.tsx +++ b/src/features/playlist/pages/PlaylistDetail.tsx @@ -12,7 +12,7 @@ import { useAlbumOfflineState } from '@/features/album'; import { useAuthStore } from '@/store/authStore'; import { useDownloadModalStore } from '@/features/offline'; import { useZipDownloadStore } from '@/features/offline'; -import { useDragDrop } from '@/contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import { useTranslation } from 'react-i18next'; import type { SpotifyCsvTrack } from '@/features/playlist/utils/spotifyCsvImport'; import { runPlaylistCsvImport } from '@/features/playlist/utils/runPlaylistCsvImport'; diff --git a/src/features/playlist/pages/Playlists.tsx b/src/features/playlist/pages/Playlists.tsx index ad303aa1..b93b5de4 100644 --- a/src/features/playlist/pages/Playlists.tsx +++ b/src/features/playlist/pages/Playlists.tsx @@ -8,7 +8,7 @@ import { usePlayerStore } from '@/store/playerStore'; import { usePlaylistStore } from '@/features/playlist/store/playlistStore'; import { useAuthStore } from '@/store/authStore'; import { useTranslation } from 'react-i18next'; -import { useRangeSelection } from '@/hooks/useRangeSelection'; +import { useRangeSelection } from '@/lib/hooks/useRangeSelection'; import { defaultSmartFilters, diff --git a/src/features/radio/components/RadioCard.tsx b/src/features/radio/components/RadioCard.tsx index d5938e32..2e887388 100644 --- a/src/features/radio/components/RadioCard.tsx +++ b/src/features/radio/components/RadioCard.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { Cast, Globe, Heart, Square, Trash2, X } from 'lucide-react'; import { open } from '@tauri-apps/plugin-shell'; import type { InternetRadioStation } from '@/lib/api/subsonicTypes'; -import { useDragDrop, useDragSource } from '@/contexts/DragDropContext'; +import { useDragDrop, useDragSource } from '@/lib/dnd/DragDropContext'; import { CoverArtImage } from '@/cover/CoverArtImage'; import { albumCoverRef } from '@/cover/ref'; import { coverArtIdFromRadio } from '@/cover/ids'; diff --git a/src/features/settings/components/BackdropSourceList.tsx b/src/features/settings/components/BackdropSourceList.tsx index 312624b0..b7c894cf 100644 --- a/src/features/settings/components/BackdropSourceList.tsx +++ b/src/features/settings/components/BackdropSourceList.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { ChevronDown, ChevronUp, GripVertical } from 'lucide-react'; -import { useDragDrop, useDragSource } from '@/contexts/DragDropContext'; +import { useDragDrop, useDragSource } from '@/lib/dnd/DragDropContext'; import type { BackdropSource, BackdropSourcePref } from '@/cover/artistBackdrop'; import type { BackdropSurface } from '@/store/themeStore'; import { moveSourceTo, dropSourceBefore } from '@/features/settings/components/backdropReorder'; diff --git a/src/features/settings/components/InputTab.tsx b/src/features/settings/components/InputTab.tsx index 95183664..63ca9554 100644 --- a/src/features/settings/components/InputTab.tsx +++ b/src/features/settings/components/InputTab.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Keyboard, RotateCcw, X } from 'lucide-react'; -import { IN_APP_SHORTCUT_ACTIONS, GLOBAL_SHORTCUT_ACTIONS } from '@/config/shortcutActions'; +import { IN_APP_SHORTCUT_ACTIONS, GLOBAL_SHORTCUT_ACTIONS } from '@/lib/shortcuts/shortcutActions'; import { useGlobalShortcutsStore, type GlobalAction, buildGlobalShortcut, formatGlobalShortcut } from '@/store/globalShortcutsStore'; import { useKeybindingsStore, type KeyAction, buildInAppBinding, formatBinding } from '@/store/keybindingsStore'; import SettingsSubSection from '@/features/settings/components/SettingsSubSection'; diff --git a/src/features/settings/components/ReorderGripHandle.tsx b/src/features/settings/components/ReorderGripHandle.tsx index 83588ecf..65632e4c 100644 --- a/src/features/settings/components/ReorderGripHandle.tsx +++ b/src/features/settings/components/ReorderGripHandle.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; import { GripVertical } from 'lucide-react'; -import { useDragSource } from '@/contexts/DragDropContext'; +import { useDragSource } from '@/lib/dnd/DragDropContext'; /** * Drag handle shared by the reorder customizers. Emits an id-based payload diff --git a/src/features/settings/components/settingsSearch.ts b/src/features/settings/components/settingsSearch.ts index bd4c8884..30d0c5bb 100644 --- a/src/features/settings/components/settingsSearch.ts +++ b/src/features/settings/components/settingsSearch.ts @@ -1,5 +1,5 @@ import type { TFunction } from 'i18next'; -import { GLOBAL_SHORTCUT_ACTIONS, IN_APP_SHORTCUT_ACTIONS } from '@/config/shortcutActions'; +import { GLOBAL_SHORTCUT_ACTIONS, IN_APP_SHORTCUT_ACTIONS } from '@/lib/shortcuts/shortcutActions'; import { SETTINGS_INDEX, matchScore, type Tab } from '@/features/settings/components/settingsTabs'; export type SettingsSearchHit = { diff --git a/src/hooks/tauriBridge/useCliBridge.ts b/src/hooks/tauriBridge/useCliBridge.ts index 449dafa8..9ddc3ea3 100644 --- a/src/hooks/tauriBridge/useCliBridge.ts +++ b/src/hooks/tauriBridge/useCliBridge.ts @@ -13,7 +13,7 @@ import { switchActiveServer } from '../../utils/server/switchActiveServer'; import i18n from '@/lib/i18n'; import { usePlayerStore } from '../../store/playerStore'; import { useAuthStore } from '../../store/authStore'; -import { executeCliPlayerCommand } from '../../config/shortcutActions'; +import { executeCliPlayerCommand } from '@/lib/shortcuts/shortcutActions'; /** The full `cli:*` listener surface forwarded from the Rust single-instance * handler: audio-device, instant-mix, library / server resolution, search and diff --git a/src/hooks/tauriBridge/useInAppKeybindings.ts b/src/hooks/tauriBridge/useInAppKeybindings.ts index ddf33881..4f39cdc7 100644 --- a/src/hooks/tauriBridge/useInAppKeybindings.ts +++ b/src/hooks/tauriBridge/useInAppKeybindings.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import type { NavigateFunction } from 'react-router-dom'; import { useKeybindingsStore, buildInAppBinding } from '../../store/keybindingsStore'; import { useGlobalShortcutsStore } from '../../store/globalShortcutsStore'; -import { DEFAULT_IN_APP_BINDINGS, executeRuntimeAction } from '../../config/shortcutActions'; +import { DEFAULT_IN_APP_BINDINGS, executeRuntimeAction } from '@/lib/shortcuts/shortcutActions'; import { matchInAppShortcutAction } from '../../shortcuts/runtime'; /** Configurable in-app keybindings: matches keydown chords against the user's diff --git a/src/hooks/tauriBridge/useMediaAndWindowBridge.ts b/src/hooks/tauriBridge/useMediaAndWindowBridge.ts index dccbb4e1..a83c307d 100644 --- a/src/hooks/tauriBridge/useMediaAndWindowBridge.ts +++ b/src/hooks/tauriBridge/useMediaAndWindowBridge.ts @@ -16,7 +16,7 @@ import { executeRuntimeAction, isGlobalShortcutActionId, isShortcutAction, -} from '../../config/shortcutActions'; +} from '@/lib/shortcuts/shortcutActions'; /** Media keys, tray actions, global / cross-window shortcut events, relative & * absolute seek + volume, and the window-close / force-quit exit flow. */ diff --git a/src/hooks/useListReorderDnd.ts b/src/hooks/useListReorderDnd.ts index 1272f2bd..8be42c24 100644 --- a/src/hooks/useListReorderDnd.ts +++ b/src/hooks/useListReorderDnd.ts @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { useDragDrop } from '../contexts/DragDropContext'; +import { useDragDrop } from '@/lib/dnd/DragDropContext'; import type { ListReorderDropTarget } from '../utils/componentHelpers/listReorder'; interface Options { diff --git a/src/hooks/useQueuePanelDrag.ts b/src/hooks/useQueuePanelDrag.ts index 0d80bf5a..f4665d83 100644 --- a/src/hooks/useQueuePanelDrag.ts +++ b/src/hooks/useQueuePanelDrag.ts @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { resolveAlbum, resolveMediaServerId } from '@/features/offline'; import { songToTrack } from '../utils/playback/songToTrack'; -import { useDragDrop, registerQueueDragHitTest } from '../contexts/DragDropContext'; +import { useDragDrop, registerQueueDragHitTest } from '@/lib/dnd/DragDropContext'; import { usePlayerStore } from '../store/playerStore'; import type { Track } from '../store/playerStoreTypes'; diff --git a/src/hooks/useThemeScheduler.ts b/src/hooks/useThemeScheduler.ts index 568d6398..8e40a0b1 100644 --- a/src/hooks/useThemeScheduler.ts +++ b/src/hooks/useThemeScheduler.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import { useThemeStore, getScheduledTheme } from '../store/themeStore'; -import { useSystemPrefersDark } from './useSystemPrefersDark'; +import { useSystemPrefersDark } from '@/lib/hooks/useSystemPrefersDark'; /** * Effective theme id for `data-theme` — scheduler-aware when enabled. diff --git a/src/contexts/DragDropContext.tsx b/src/lib/dnd/DragDropContext.tsx similarity index 100% rename from src/contexts/DragDropContext.tsx rename to src/lib/dnd/DragDropContext.tsx diff --git a/src/hooks/useDebouncedValue.test.ts b/src/lib/hooks/useDebouncedValue.test.ts similarity index 91% rename from src/hooks/useDebouncedValue.test.ts rename to src/lib/hooks/useDebouncedValue.test.ts index 53848c25..f5760135 100644 --- a/src/hooks/useDebouncedValue.test.ts +++ b/src/lib/hooks/useDebouncedValue.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; import { renderHook, act } from '@testing-library/react'; -import { useDebouncedValue } from './useDebouncedValue'; +import { useDebouncedValue } from '@/lib/hooks/useDebouncedValue'; describe('useDebouncedValue', () => { beforeEach(() => { diff --git a/src/hooks/useDebouncedValue.ts b/src/lib/hooks/useDebouncedValue.ts similarity index 100% rename from src/hooks/useDebouncedValue.ts rename to src/lib/hooks/useDebouncedValue.ts diff --git a/src/hooks/useIsMobile.ts b/src/lib/hooks/useIsMobile.ts similarity index 100% rename from src/hooks/useIsMobile.ts rename to src/lib/hooks/useIsMobile.ts diff --git a/src/hooks/useLongPressAction.test.ts b/src/lib/hooks/useLongPressAction.test.ts similarity index 97% rename from src/hooks/useLongPressAction.test.ts rename to src/lib/hooks/useLongPressAction.test.ts index c1442c87..906e1607 100644 --- a/src/hooks/useLongPressAction.test.ts +++ b/src/lib/hooks/useLongPressAction.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { renderHook, act } from '@testing-library/react'; import type { MouseEvent, PointerEvent } from 'react'; -import { useLongPressAction } from './useLongPressAction'; +import { useLongPressAction } from '@/lib/hooks/useLongPressAction'; function makePointerDown(overrides: Partial = {}): PointerEvent { return { diff --git a/src/hooks/useLongPressAction.ts b/src/lib/hooks/useLongPressAction.ts similarity index 100% rename from src/hooks/useLongPressAction.ts rename to src/lib/hooks/useLongPressAction.ts diff --git a/src/hooks/useRangeSelection.ts b/src/lib/hooks/useRangeSelection.ts similarity index 100% rename from src/hooks/useRangeSelection.ts rename to src/lib/hooks/useRangeSelection.ts diff --git a/src/hooks/useRemeasureGridVirtualizer.ts b/src/lib/hooks/useRemeasureGridVirtualizer.ts similarity index 100% rename from src/hooks/useRemeasureGridVirtualizer.ts rename to src/lib/hooks/useRemeasureGridVirtualizer.ts diff --git a/src/hooks/useResizeClientHeight.ts b/src/lib/hooks/useResizeClientHeight.ts similarity index 100% rename from src/hooks/useResizeClientHeight.ts rename to src/lib/hooks/useResizeClientHeight.ts diff --git a/src/hooks/useSystemPrefersDark.ts b/src/lib/hooks/useSystemPrefersDark.ts similarity index 100% rename from src/hooks/useSystemPrefersDark.ts rename to src/lib/hooks/useSystemPrefersDark.ts diff --git a/src/hooks/useVirtualizerScrollMargin.ts b/src/lib/hooks/useVirtualizerScrollMargin.ts similarity index 100% rename from src/hooks/useVirtualizerScrollMargin.ts rename to src/lib/hooks/useVirtualizerScrollMargin.ts diff --git a/src/hooks/useWindowVisibility.tsx b/src/lib/hooks/useWindowVisibility.tsx similarity index 100% rename from src/hooks/useWindowVisibility.tsx rename to src/lib/hooks/useWindowVisibility.tsx diff --git a/src/config/shortcutActions.ts b/src/lib/shortcuts/shortcutActions.ts similarity index 95% rename from src/config/shortcutActions.ts rename to src/lib/shortcuts/shortcutActions.ts index 668a7d6e..9040ef38 100644 --- a/src/config/shortcutActions.ts +++ b/src/lib/shortcuts/shortcutActions.ts @@ -48,14 +48,14 @@ export type { ActionContext, CliContext, ShortcutActionMeta, -} from './shortcutTypes'; +} from '@/lib/shortcuts/shortcutTypes'; export { SHORTCUT_ACTION_REGISTRY, type ShortcutAction, type KeyAction, type GlobalAction, -} from './shortcutActionRegistry'; +} from '@/config/shortcutActionRegistry'; export { isShortcutAction, @@ -64,11 +64,11 @@ export { executeRuntimeAction, executeCliPlayerCommand, type RuntimeAction, -} from './shortcutDispatch'; +} from '@/config/shortcutDispatch'; export { IN_APP_SHORTCUT_ACTIONS, GLOBAL_SHORTCUT_ACTIONS, DEFAULT_IN_APP_BINDINGS, DEFAULT_GLOBAL_SHORTCUTS, -} from './shortcutBindings'; +} from '@/config/shortcutBindings'; diff --git a/src/config/shortcutTypes.ts b/src/lib/shortcuts/shortcutTypes.ts similarity index 100% rename from src/config/shortcutTypes.ts rename to src/lib/shortcuts/shortcutTypes.ts diff --git a/src/pages/Composers.tsx b/src/pages/Composers.tsx index f3347ea0..668aec78 100644 --- a/src/pages/Composers.tsx +++ b/src/pages/Composers.tsx @@ -9,7 +9,7 @@ import { useAuthStore } from '../store/authStore'; import { useTranslation } from 'react-i18next'; import { useVirtualizer } from '@tanstack/react-virtual'; import { APP_MAIN_SCROLL_VIEWPORT_ID, COMPOSERS_INPAGE_SCROLL_VIEWPORT_ID } from '../constants/appScroll'; -import { useElementClientHeightById, useElementClientHeightForElement } from '../hooks/useResizeClientHeight'; +import { useElementClientHeightById, useElementClientHeightForElement } from '@/lib/hooks/useResizeClientHeight'; import { useMainstageInpageHeaderTight } from '../hooks/useMainstageInpageHeaderTight'; import { useBrowseArtistTextSearch } from '@/features/artist'; import { useComposersBrowseFilters, type ComposerBrowseScrollSnapshot } from '../hooks/useComposersBrowseFilters'; @@ -25,7 +25,7 @@ import { useLibraryIgnoredArticles } from '../hooks/useLibraryIgnoredArticles'; import { usePerfProbeFlags } from '../utils/perf/perfFlags'; import { VirtualCardGrid } from '../components/VirtualCardGrid'; import OverlayScrollArea from '@/ui/OverlayScrollArea'; -import { useVirtualizerScrollMargin } from '../hooks/useVirtualizerScrollMargin'; +import { useVirtualizerScrollMargin } from '@/lib/hooks/useVirtualizerScrollMargin'; import { useClientSliceInfiniteScroll } from '../hooks/useClientSliceInfiniteScroll'; import { useInpageScrollViewport } from '../hooks/useInpageScrollViewport'; import InpageScrollSentinel from '../components/InpageScrollSentinel'; diff --git a/src/pages/GenreDetail.tsx b/src/pages/GenreDetail.tsx index bbc51922..1e10db0f 100644 --- a/src/pages/GenreDetail.tsx +++ b/src/pages/GenreDetail.tsx @@ -14,7 +14,7 @@ import { useGenreAlbumBrowse } from '@/features/album'; import { useAlbumBrowseScrollRestore } from '@/features/album'; import { useGenreDetailBrowse } from '../hooks/useGenreDetailBrowse'; import { useInpageScrollViewport } from '../hooks/useInpageScrollViewport'; -import { useLongPressAction } from '../hooks/useLongPressAction'; +import { useLongPressAction } from '@/lib/hooks/useLongPressAction'; import { useMainstageInpageHeaderTight } from '../hooks/useMainstageInpageHeaderTight'; import { useAuthStore } from '../store/authStore'; import { useLibraryIndexStore } from '../store/libraryIndexStore'; diff --git a/src/pages/MostPlayed.tsx b/src/pages/MostPlayed.tsx index 1cd90b39..a5ef2d36 100644 --- a/src/pages/MostPlayed.tsx +++ b/src/pages/MostPlayed.tsx @@ -10,7 +10,7 @@ import { usePlayerStore } from '../store/playerStore'; import { AlbumCoverArtImage } from '../cover/AlbumCoverArtImage'; import { ArtistCoverArtImage } from '../cover/ArtistCoverArtImage'; import { playAlbum, playAlbumShuffled } from '../utils/playback/playAlbum'; -import { useLongPressAction } from '../hooks/useLongPressAction'; +import { useLongPressAction } from '@/lib/hooks/useLongPressAction'; import { LongPressWaveOverlay } from '../components/LongPressWaveOverlay'; import { useTranslation } from 'react-i18next'; import { albumArtistDisplayName } from '@/features/album'; diff --git a/src/pages/NewReleases.tsx b/src/pages/NewReleases.tsx index a1575341..1f9ae0f4 100644 --- a/src/pages/NewReleases.tsx +++ b/src/pages/NewReleases.tsx @@ -18,7 +18,7 @@ import { invoke } from '@tauri-apps/api/core'; import { join } from '@tauri-apps/api/path'; import { showToast } from '../utils/ui/toast'; import { useZipDownloadStore } from '@/features/offline'; -import { useRangeSelection } from '../hooks/useRangeSelection'; +import { useRangeSelection } from '@/lib/hooks/useRangeSelection'; import { usePerfProbeFlags } from '../utils/perf/perfFlags'; import { useMainstageInpageHeaderTight } from '../hooks/useMainstageInpageHeaderTight'; import { albumGridWarmCovers } from '../cover/layoutSizes'; diff --git a/src/pages/RandomMix.tsx b/src/pages/RandomMix.tsx index 383930d9..76b5fbd0 100644 --- a/src/pages/RandomMix.tsx +++ b/src/pages/RandomMix.tsx @@ -7,7 +7,7 @@ import { usePreviewStore } from '../store/previewStore'; import { useAuthStore } from '../store/authStore'; import { useLibraryIndexStore } from '../store/libraryIndexStore'; import { useTranslation } from 'react-i18next'; -import { useIsMobile } from '../hooks/useIsMobile'; +import { useIsMobile } from '@/lib/hooks/useIsMobile'; import { useOrbitSongRowBehavior } from '@/features/orbit'; import { fetchRandomMixSongsUntilFull, diff --git a/src/shortcuts/runtime.ts b/src/shortcuts/runtime.ts index ddcb07dc..710d6c99 100644 --- a/src/shortcuts/runtime.ts +++ b/src/shortcuts/runtime.ts @@ -1,5 +1,5 @@ import { matchInAppBinding, type Bindings } from '../store/keybindingsStore'; -import type { KeyAction } from '../config/shortcutActions'; +import type { KeyAction } from '@/lib/shortcuts/shortcutActions'; export function matchInAppShortcutAction( event: KeyboardEvent, diff --git a/src/store/globalShortcutsStore.ts b/src/store/globalShortcutsStore.ts index 57ccf261..aebddedc 100644 --- a/src/store/globalShortcutsStore.ts +++ b/src/store/globalShortcutsStore.ts @@ -2,7 +2,7 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { invoke } from '@tauri-apps/api/core'; import { MODIFIER_KEY_CODES, formatBinding } from './keybindingsStore'; -import { DEFAULT_GLOBAL_SHORTCUTS, isGlobalShortcutActionId, type GlobalAction } from '../config/shortcutActions'; +import { DEFAULT_GLOBAL_SHORTCUTS, isGlobalShortcutActionId, type GlobalAction } from '@/lib/shortcuts/shortcutActions'; /** Dev builds run alongside release — OS-level grabs stay on the release instance. */ const GLOBAL_SHORTCUTS_OS_ENABLED = !import.meta.env.DEV; @@ -101,4 +101,4 @@ export const useGlobalShortcutsStore = create()( ) ); -export type { GlobalAction } from '../config/shortcutActions'; +export type { GlobalAction } from '@/lib/shortcuts/shortcutActions'; diff --git a/src/store/keybindingsStore.ts b/src/store/keybindingsStore.ts index 23ee89da..ed48d68d 100644 --- a/src/store/keybindingsStore.ts +++ b/src/store/keybindingsStore.ts @@ -1,6 +1,6 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; -import { DEFAULT_IN_APP_BINDINGS, type KeyAction } from '../config/shortcutActions'; +import { DEFAULT_IN_APP_BINDINGS, type KeyAction } from '@/lib/shortcuts/shortcutActions'; /** Physical keys only — ignore for binding capture */ export const MODIFIER_KEY_CODES = [ @@ -12,7 +12,7 @@ export const MODIFIER_KEY_CODES = [ export type Bindings = Record; export const DEFAULT_BINDINGS: Bindings = { ...DEFAULT_IN_APP_BINDINGS }; -export type { KeyAction } from '../config/shortcutActions'; +export type { KeyAction } from '@/lib/shortcuts/shortcutActions'; function normalizeBindings( bindings: Partial> | undefined diff --git a/src/utils/format/playbackScheduleFormat.ts b/src/utils/format/playbackScheduleFormat.ts index 055ff7c9..dc850a8d 100644 --- a/src/utils/format/playbackScheduleFormat.ts +++ b/src/utils/format/playbackScheduleFormat.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { usePlayerStore } from '../../store/playerStore'; -import { useWindowVisibility } from '../../hooks/useWindowVisibility'; +import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility'; /** Remaining time until wall-clock `deadlineMs` (m:ss or h:mm:ss). */ export function formatPlaybackScheduleRemaining(deadlineMs: number | null, nowMs: number): string {