diff --git a/src/app/MainApp.tsx b/src/app/MainApp.tsx index da6d6895..7d89a629 100644 --- a/src/app/MainApp.tsx +++ b/src/app/MainApp.tsx @@ -19,7 +19,7 @@ import { initPinnedOfflineSync } from '../utils/offline/pinnedOfflineSync'; import { initResumeIncompleteOfflinePins, scheduleResumeIncompleteOfflinePins } from '../utils/offline/resumeIncompleteOfflinePins'; import { runLegacyOfflineFileMigration } from '../utils/migrations/legacyOfflineFileMigration'; import { reconcileLibraryTierForServer } from '../utils/offline/libraryTierReconcile'; -import { initMiniPlayerBridgeOnMain } from '../utils/miniPlayerBridge'; +import { initMiniPlayerBridgeOnMain } from '@/features/miniPlayer'; import { runAdvancedModeMigration } from '../utils/migrations/advancedModeMigration'; import { bootstrapAllIndexedServers } from '../utils/library/librarySession'; import { hydrateQueueFromIndex } from '../utils/library/queueRestore'; diff --git a/src/app/MiniPlayerApp.test.tsx b/src/app/MiniPlayerApp.test.tsx index 50675d8f..2996cd8a 100644 --- a/src/app/MiniPlayerApp.test.tsx +++ b/src/app/MiniPlayerApp.test.tsx @@ -28,7 +28,7 @@ vi.mock('../utils/perf/perfFlags', () => ({ vi.mock('../i18n', () => ({ default: { changeLanguage: vi.fn() }, })); -vi.mock('../components/MiniPlayer', () => ({ default: () =>
})); +vi.mock('@/features/miniPlayer', () => ({ default: () => })); vi.mock('../components/GlobalConfirmModal', () => ({ default: () => })); vi.mock('@/ui/TooltipPortal', () => ({ default: () => })); vi.mock('../components/FpsOverlay', () => ({ default: () => })); diff --git a/src/app/MiniPlayerApp.tsx b/src/app/MiniPlayerApp.tsx index fa1eb81a..ff8b2031 100644 --- a/src/app/MiniPlayerApp.tsx +++ b/src/app/MiniPlayerApp.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { DragDropProvider } from '../contexts/DragDropContext'; -import MiniPlayer from '../components/MiniPlayer'; +import MiniPlayer from '@/features/miniPlayer'; import GlobalConfirmModal from '../components/GlobalConfirmModal'; import TooltipPortal from '@/ui/TooltipPortal'; import FpsOverlay from '../components/FpsOverlay'; diff --git a/src/components/MiniContextMenu.tsx b/src/features/miniPlayer/components/MiniContextMenu.tsx similarity index 96% rename from src/components/MiniContextMenu.tsx rename to src/features/miniPlayer/components/MiniContextMenu.tsx index 07c8efa6..f3b480aa 100644 --- a/src/components/MiniContextMenu.tsx +++ b/src/features/miniPlayer/components/MiniContextMenu.tsx @@ -1,10 +1,10 @@ -import { star, unstar } from '../api/subsonicStarRating'; +import { star, unstar } from '@/api/subsonicStarRating'; import { useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { emit } from '@tauri-apps/api/event'; import { useTranslation } from 'react-i18next'; import { Play, Trash2, Disc3, User, Heart, Info } from 'lucide-react'; -import type { MiniTrackInfo } from '../utils/miniPlayerBridge'; +import type { MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge'; interface Props { x: number; diff --git a/src/components/miniPlayer/MiniControls.tsx b/src/features/miniPlayer/components/MiniControls.tsx similarity index 90% rename from src/components/miniPlayer/MiniControls.tsx rename to src/features/miniPlayer/components/MiniControls.tsx index 4516310d..5fa75c9e 100644 --- a/src/components/miniPlayer/MiniControls.tsx +++ b/src/features/miniPlayer/components/MiniControls.tsx @@ -1,6 +1,6 @@ import { Pause, Play, SkipBack, SkipForward } from 'lucide-react'; -import { formatTrackTime } from '../../utils/format/formatDuration'; -import type { MiniControlAction } from '../../utils/miniPlayerBridge'; +import { formatTrackTime } from '@/utils/format/formatDuration'; +import type { MiniControlAction } from '@/features/miniPlayer/utils/miniPlayerBridge'; interface Props { isPlaying: boolean; diff --git a/src/components/miniPlayer/MiniMeta.tsx b/src/features/miniPlayer/components/MiniMeta.tsx similarity index 91% rename from src/components/miniPlayer/MiniMeta.tsx rename to src/features/miniPlayer/components/MiniMeta.tsx index 0376ca71..611b2ef2 100644 --- a/src/components/miniPlayer/MiniMeta.tsx +++ b/src/features/miniPlayer/components/MiniMeta.tsx @@ -1,7 +1,7 @@ import { emit } from '@tauri-apps/api/event'; import CachedImage from '@/ui/CachedImage'; -import { OpenArtistRefInline } from '../OpenArtistRefInline'; -import type { MiniTrackInfo } from '../../utils/miniPlayerBridge'; +import { OpenArtistRefInline } from '@/components/OpenArtistRefInline'; +import type { MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge'; interface Props { track: MiniTrackInfo | null; diff --git a/src/components/MiniPlayer.test.tsx b/src/features/miniPlayer/components/MiniPlayer.test.tsx similarity index 100% rename from src/components/MiniPlayer.test.tsx rename to src/features/miniPlayer/components/MiniPlayer.test.tsx diff --git a/src/components/MiniPlayer.tsx b/src/features/miniPlayer/components/MiniPlayer.tsx similarity index 84% rename from src/components/MiniPlayer.tsx rename to src/features/miniPlayer/components/MiniPlayer.tsx index c0a4c984..25a38cab 100644 --- a/src/components/MiniPlayer.tsx +++ b/src/features/miniPlayer/components/MiniPlayer.tsx @@ -1,29 +1,29 @@ -import { usePlaybackCoverArt } from '../hooks/usePlaybackCoverArt'; -import { usePlaybackTrackCoverRef } from '../cover/useLibraryCoverRef'; +import { usePlaybackCoverArt } from '@/hooks/usePlaybackCoverArt'; +import { usePlaybackTrackCoverRef } from '@/cover/useLibraryCoverRef'; import { useEffect, useRef, useState } from 'react'; 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 MiniContextMenu from './MiniContextMenu'; -import type { MiniSyncPayload, MiniControlAction, MiniTrackInfo } from '../utils/miniPlayerBridge'; +import { usePlayerStore } from '@/store/playerStore'; +import { registerQueueDragHitTest } from '@/contexts/DragDropContext'; +import MiniContextMenu from '@/features/miniPlayer/components/MiniContextMenu'; +import type { MiniSyncPayload, MiniControlAction, MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge'; import { COLLAPSED_SIZE, EXPANDED_SIZE, COLLAPSED_MIN, EXPANDED_MIN, EXPANDED_H_KEY, QUEUE_OPEN_KEY, readStoredExpandedHeight, readQueueOpen, initialSnapshot, -} from '../utils/componentHelpers/miniPlayerHelpers'; -import { MiniTitlebar } from './miniPlayer/MiniTitlebar'; -import { MiniMeta } from './miniPlayer/MiniMeta'; -import { MiniControls } from './miniPlayer/MiniControls'; -import { MiniToolbar } from './miniPlayer/MiniToolbar'; -import { MiniQueue } from './miniPlayer/MiniQueue'; -import { useMiniVolumePopover } from '../hooks/useMiniVolumePopover'; -import { useMiniCrossfadePopover } from '../hooks/useMiniCrossfadePopover'; -import { useMiniQueueDrag } from '../hooks/useMiniQueueDrag'; -import { useMiniSync } from '../hooks/useMiniSync'; -import { useMiniWindowSetup } from '../hooks/useMiniWindowSetup'; -import { useMiniKeyboardShortcuts } from '../hooks/useMiniKeyboardShortcuts'; +} from '@/features/miniPlayer/utils/miniPlayerHelpers'; +import { MiniTitlebar } from '@/features/miniPlayer/components/MiniTitlebar'; +import { MiniMeta } from '@/features/miniPlayer/components/MiniMeta'; +import { MiniControls } from '@/features/miniPlayer/components/MiniControls'; +import { MiniToolbar } from '@/features/miniPlayer/components/MiniToolbar'; +import { MiniQueue } from '@/features/miniPlayer/components/MiniQueue'; +import { useMiniVolumePopover } from '@/features/miniPlayer/hooks/useMiniVolumePopover'; +import { useMiniCrossfadePopover } from '@/features/miniPlayer/hooks/useMiniCrossfadePopover'; +import { useMiniQueueDrag } from '@/features/miniPlayer/hooks/useMiniQueueDrag'; +import { useMiniSync } from '@/features/miniPlayer/hooks/useMiniSync'; +import { useMiniWindowSetup } from '@/features/miniPlayer/hooks/useMiniWindowSetup'; +import { useMiniKeyboardShortcuts } from '@/features/miniPlayer/hooks/useMiniKeyboardShortcuts'; export default function MiniPlayer() { const { t } = useTranslation(); diff --git a/src/components/miniPlayer/MiniQueue.tsx b/src/features/miniPlayer/components/MiniQueue.tsx similarity index 98% rename from src/components/miniPlayer/MiniQueue.tsx rename to src/features/miniPlayer/components/MiniQueue.tsx index 1a6c5689..24ad76f9 100644 --- a/src/components/miniPlayer/MiniQueue.tsx +++ b/src/features/miniPlayer/components/MiniQueue.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; import type { TFunction } from 'i18next'; import OverlayScrollArea from '@/ui/OverlayScrollArea'; -import type { MiniSyncPayload, MiniTrackInfo } from '../../utils/miniPlayerBridge'; +import type { MiniSyncPayload, MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge'; // Stable initial rect so the virtualizer never re-initializes on re-render (an // inline literal would be a new ref each render → render loop). Replaced by the diff --git a/src/components/miniPlayer/MiniTitlebar.tsx b/src/features/miniPlayer/components/MiniTitlebar.tsx similarity index 97% rename from src/components/miniPlayer/MiniTitlebar.tsx rename to src/features/miniPlayer/components/MiniTitlebar.tsx index 1f560b2d..a80b991d 100644 --- a/src/components/miniPlayer/MiniTitlebar.tsx +++ b/src/features/miniPlayer/components/MiniTitlebar.tsx @@ -1,6 +1,6 @@ import { Maximize2, Pin, PinOff, X } from 'lucide-react'; import type { TFunction } from 'i18next'; -import { IS_LINUX } from '../../utils/platform'; +import { IS_LINUX } from '@/utils/platform'; interface Props { trackTitle: string | undefined; diff --git a/src/components/miniPlayer/MiniToolbar.tsx b/src/features/miniPlayer/components/MiniToolbar.tsx similarity index 97% rename from src/components/miniPlayer/MiniToolbar.tsx rename to src/features/miniPlayer/components/MiniToolbar.tsx index c451047c..48dde227 100644 --- a/src/components/miniPlayer/MiniToolbar.tsx +++ b/src/features/miniPlayer/components/MiniToolbar.tsx @@ -3,8 +3,8 @@ import { createPortal } from 'react-dom'; import { emit } from '@tauri-apps/api/event'; import { Blend, Infinity as InfinityIcon, ListMusic, MoveRight, Shuffle, Volume2, VolumeX, Waves } from 'lucide-react'; import type { TFunction } from 'i18next'; -import type { MiniSyncPayload } from '../../utils/miniPlayerBridge'; -import { getTransitionMode } from '../../utils/playback/playbackTransition'; +import type { MiniSyncPayload } from '@/features/miniPlayer/utils/miniPlayerBridge'; +import { getTransitionMode } from '@/utils/playback/playbackTransition'; interface Props { state: MiniSyncPayload; diff --git a/src/hooks/useMiniAnchoredPopover.ts b/src/features/miniPlayer/hooks/useMiniAnchoredPopover.ts similarity index 100% rename from src/hooks/useMiniAnchoredPopover.ts rename to src/features/miniPlayer/hooks/useMiniAnchoredPopover.ts diff --git a/src/hooks/useMiniCrossfadePopover.ts b/src/features/miniPlayer/hooks/useMiniCrossfadePopover.ts similarity index 84% rename from src/hooks/useMiniCrossfadePopover.ts rename to src/features/miniPlayer/hooks/useMiniCrossfadePopover.ts index cc8f5f52..b2b5505c 100644 --- a/src/hooks/useMiniCrossfadePopover.ts +++ b/src/features/miniPlayer/hooks/useMiniCrossfadePopover.ts @@ -1,4 +1,4 @@ -import { useMiniAnchoredPopover } from './useMiniAnchoredPopover'; +import { useMiniAnchoredPopover } from '@/features/miniPlayer/hooks/useMiniAnchoredPopover'; /** Open-state, refs, and fixed positioning of the portaled mini-player * crossfade settings popover (seconds slider + trim-silence toggle). Opened by diff --git a/src/hooks/useMiniKeyboardShortcuts.ts b/src/features/miniPlayer/hooks/useMiniKeyboardShortcuts.ts similarity index 96% rename from src/hooks/useMiniKeyboardShortcuts.ts rename to src/features/miniPlayer/hooks/useMiniKeyboardShortcuts.ts index f52a19a1..22f74848 100644 --- a/src/hooks/useMiniKeyboardShortcuts.ts +++ b/src/features/miniPlayer/hooks/useMiniKeyboardShortcuts.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { emit } from '@tauri-apps/api/event'; -import { useKeybindingsStore, matchInAppBinding } from '../store/keybindingsStore'; +import { useKeybindingsStore, matchInAppBinding } from '@/store/keybindingsStore'; /** Mini-window keyboard shortcuts. Space/Arrow{Left,Right} run the standard * play-pause/next/prev shortcut actions (source: 'mini-window' so the bridge diff --git a/src/hooks/useMiniQueueDrag.ts b/src/features/miniPlayer/hooks/useMiniQueueDrag.ts similarity index 97% rename from src/hooks/useMiniQueueDrag.ts rename to src/features/miniPlayer/hooks/useMiniQueueDrag.ts index 0558316e..cd2e99ab 100644 --- a/src/hooks/useMiniQueueDrag.ts +++ b/src/features/miniPlayer/hooks/useMiniQueueDrag.ts @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { emit } from '@tauri-apps/api/event'; -import { useDragDrop } from '../contexts/DragDropContext'; -import { usePlayerStore } from '../store/playerStore'; +import { useDragDrop } from '@/contexts/DragDropContext'; +import { usePlayerStore } from '@/store/playerStore'; interface Args { queueOpen: boolean; diff --git a/src/hooks/useMiniSync.ts b/src/features/miniPlayer/hooks/useMiniSync.ts similarity index 91% rename from src/hooks/useMiniSync.ts rename to src/features/miniPlayer/hooks/useMiniSync.ts index 593b661c..99ceb787 100644 --- a/src/hooks/useMiniSync.ts +++ b/src/features/miniPlayer/hooks/useMiniSync.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; import { emit, listen } from '@tauri-apps/api/event'; -import { useWindowVisibility } from './useWindowVisibility'; -import type { MiniSyncPayload } from '../utils/miniPlayerBridge'; +import { useWindowVisibility } from '@/hooks/useWindowVisibility'; +import type { MiniSyncPayload } from '@/features/miniPlayer/utils/miniPlayerBridge'; interface ProgressPayload { current_time: number; diff --git a/src/hooks/useMiniVolumePopover.ts b/src/features/miniPlayer/hooks/useMiniVolumePopover.ts similarity index 84% rename from src/hooks/useMiniVolumePopover.ts rename to src/features/miniPlayer/hooks/useMiniVolumePopover.ts index 64adf95c..c09f6e0d 100644 --- a/src/hooks/useMiniVolumePopover.ts +++ b/src/features/miniPlayer/hooks/useMiniVolumePopover.ts @@ -1,4 +1,4 @@ -import { useMiniAnchoredPopover } from './useMiniAnchoredPopover'; +import { useMiniAnchoredPopover } from '@/features/miniPlayer/hooks/useMiniAnchoredPopover'; /** Open-state, refs, and fixed positioning of the portaled mini-player volume * popover. Thin wrapper over {@link useMiniAnchoredPopover} that preserves the diff --git a/src/hooks/useMiniWindowSetup.ts b/src/features/miniPlayer/hooks/useMiniWindowSetup.ts similarity index 92% rename from src/hooks/useMiniWindowSetup.ts rename to src/features/miniPlayer/hooks/useMiniWindowSetup.ts index 62988478..2b3b6bb3 100644 --- a/src/hooks/useMiniWindowSetup.ts +++ b/src/features/miniPlayer/hooks/useMiniWindowSetup.ts @@ -1,10 +1,10 @@ import { useEffect } from 'react'; import { invoke } from '@tauri-apps/api/core'; -import { useAuthStore } from '../store/authStore'; -import { IS_LINUX } from '../utils/platform'; +import { useAuthStore } from '@/store/authStore'; +import { IS_LINUX } from '@/utils/platform'; import { EXPANDED_SIZE, EXPANDED_MIN, readStoredExpandedHeight, -} from '../utils/componentHelpers/miniPlayerHelpers'; +} from '@/features/miniPlayer/utils/miniPlayerHelpers'; /** Three window-bound setup effects bundled together: * - Linux WebKitGTK smooth-scroll per-window (re-applies after auth hydrates diff --git a/src/features/miniPlayer/index.ts b/src/features/miniPlayer/index.ts new file mode 100644 index 00000000..56e7dfa5 --- /dev/null +++ b/src/features/miniPlayer/index.ts @@ -0,0 +1,8 @@ +/** + * Mini player feature — the compact always-on-top mini-player window UI + * (controls, meta, queue, titlebar, toolbar, context menu) plus the main↔mini + * IPC bridge. The webview entry shell (`app/MiniPlayerApp`) stays in `app/` and + * renders this feature's default export. + */ +export { default } from './components/MiniPlayer'; +export { initMiniPlayerBridgeOnMain } from './utils/miniPlayerBridge'; diff --git a/src/utils/miniPlayerBridge.ts b/src/features/miniPlayer/utils/miniPlayerBridge.ts similarity index 96% rename from src/utils/miniPlayerBridge.ts rename to src/features/miniPlayer/utils/miniPlayerBridge.ts index d4eed5c9..21e7294a 100644 --- a/src/utils/miniPlayerBridge.ts +++ b/src/features/miniPlayer/utils/miniPlayerBridge.ts @@ -1,11 +1,11 @@ import { getCurrentWindow } from '@tauri-apps/api/window'; import { listen, emitTo } from '@tauri-apps/api/event'; -import { usePlayerStore } from '../store/playerStore'; -import { useAuthStore } from '../store/authStore'; -import { setTransitionMode, type TransitionMode } from './playback/playbackTransition'; -import { resolveQueueTrack } from './library/queueTrackView'; -import type { SubsonicOpenArtistRef } from '../api/subsonicTypes'; -import type { Track } from '../store/playerStoreTypes'; +import { usePlayerStore } from '@/store/playerStore'; +import { useAuthStore } from '@/store/authStore'; +import { setTransitionMode, type TransitionMode } from '@/utils/playback/playbackTransition'; +import { resolveQueueTrack } from '@/utils/library/queueTrackView'; +import type { SubsonicOpenArtistRef } from '@/api/subsonicTypes'; +import type { Track } from '@/store/playerStoreTypes'; export const MINI_WINDOW_LABEL = 'mini'; diff --git a/src/utils/componentHelpers/miniPlayerHelpers.ts b/src/features/miniPlayer/utils/miniPlayerHelpers.ts similarity index 91% rename from src/utils/componentHelpers/miniPlayerHelpers.ts rename to src/features/miniPlayer/utils/miniPlayerHelpers.ts index 1d7dfd1b..cc92d349 100644 --- a/src/utils/componentHelpers/miniPlayerHelpers.ts +++ b/src/features/miniPlayer/utils/miniPlayerHelpers.ts @@ -1,7 +1,7 @@ -import { usePlayerStore } from '../../store/playerStore'; -import { resolveQueueTrack } from '../library/queueTrackView'; -import type { MiniSyncPayload, MiniTrackInfo } from '../miniPlayerBridge'; -import type { Track } from '../../store/playerStoreTypes'; +import { usePlayerStore } from '@/store/playerStore'; +import { resolveQueueTrack } from '@/utils/library/queueTrackView'; +import type { MiniSyncPayload, MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge'; +import type { Track } from '@/store/playerStoreTypes'; /** Half-width of the mini initial-snapshot queue window (matches the bridge). */ const MINI_SNAPSHOT_HALF = 100;