From 25055dc79a3e333df5ae89b7e88c32195a75bba0 Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:31:33 +0200 Subject: [PATCH] refactor(ui): drain src/components into ui + owning features The legacy src/components/ dir held eight shared modals. Audited each: the five generic primitives (Modal, ConfirmModal, GlobalConfirmModal, ExportPickerModal, ThemeMigrationNotice) move to src/ui (the shared-primitive home). The three that import @/features/* are feature-owned, so they move into their feature instead of polluting ui with core->feature edges: SongInfoModal -> features/playback, LicenseTextModal -> features/settings, PasteClipboardHandler -> features/share. Pure move + import updates, no behavior change. The layering baseline is unchanged (every relocated import lands in a legal direction: feature->ui/store/lib, or cross-feature only via the barrel). src/components/ is now removed. --- src/app/AppShell.tsx | 6 +++--- src/app/MainApp.tsx | 4 ++-- src/app/MiniPlayerApp.test.tsx | 2 +- src/app/MiniPlayerApp.tsx | 2 +- src/features/orbit/components/OrbitParticipantsPopover.tsx | 2 +- src/features/orbit/components/OrbitSessionBar.tsx | 2 +- src/{ => features/playback}/components/SongInfoModal.tsx | 4 ++-- src/{ => features/settings}/components/LicenseTextModal.tsx | 0 src/features/settings/components/LicensesPanel.tsx | 2 +- src/features/settings/components/ThemeImportSection.tsx | 2 +- src/features/settings/components/UserManagementSection.tsx | 2 +- .../share}/components/PasteClipboardHandler.tsx | 6 +++--- src/features/updater/components/AppUpdater.tsx | 2 +- src/{components => ui}/ConfirmModal.tsx | 0 src/{components => ui}/ExportPickerModal.tsx | 0 src/{components => ui}/GlobalConfirmModal.tsx | 0 src/{components => ui}/Modal.tsx | 0 src/{components => ui}/ThemeMigrationNotice.tsx | 0 18 files changed, 18 insertions(+), 18 deletions(-) rename src/{ => features/playback}/components/SongInfoModal.tsx (98%) rename src/{ => features/settings}/components/LicenseTextModal.tsx (100%) rename src/{ => features/share}/components/PasteClipboardHandler.tsx (98%) rename src/{components => ui}/ConfirmModal.tsx (100%) rename src/{components => ui}/ExportPickerModal.tsx (100%) rename src/{components => ui}/GlobalConfirmModal.tsx (100%) rename src/{components => ui}/Modal.tsx (100%) rename src/{components => ui}/ThemeMigrationNotice.tsx (100%) diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx index f0fadc73..2a3b33de 100644 --- a/src/app/AppShell.tsx +++ b/src/app/AppShell.tsx @@ -16,10 +16,10 @@ import QueuePanel from '@/features/queue'; import AppRoutes from './AppRoutes'; import FullscreenPlayer from '@/features/fullscreenPlayer'; import ContextMenu from '@/features/contextMenu/components/ContextMenu'; -import SongInfoModal from '../components/SongInfoModal'; +import SongInfoModal from '@/features/playback/components/SongInfoModal'; import { DownloadFolderModal } from '@/features/offline'; -import GlobalConfirmModal from '../components/GlobalConfirmModal'; -import ThemeMigrationNotice from '../components/ThemeMigrationNotice'; +import GlobalConfirmModal from '@/ui/GlobalConfirmModal'; +import ThemeMigrationNotice from '@/ui/ThemeMigrationNotice'; import { OrbitAccountPicker, OrbitHelpModal } from '@/features/orbit'; import TooltipPortal from '@/ui/TooltipPortal'; import OverlayScrollArea from '@/ui/OverlayScrollArea'; diff --git a/src/app/MainApp.tsx b/src/app/MainApp.tsx index 6a8f0447..8ec2fc0b 100644 --- a/src/app/MainApp.tsx +++ b/src/app/MainApp.tsx @@ -6,8 +6,8 @@ import { invoke } from '@tauri-apps/api/core'; import { showToast } from '@/lib/dom/toast'; import { WindowVisibilityProvider } from '@/lib/hooks/useWindowVisibility'; import { DragDropProvider } from '@/lib/dnd/DragDropContext'; -import PasteClipboardHandler from '../components/PasteClipboardHandler'; -import ExportPickerModal from '../components/ExportPickerModal'; +import PasteClipboardHandler from '@/features/share/components/PasteClipboardHandler'; +import ExportPickerModal from '@/ui/ExportPickerModal'; import { ZipDownloadOverlay } from '@/features/offline'; import FpsOverlay from '@/app/FpsOverlay'; import { useAuthStore } from '../store/authStore'; diff --git a/src/app/MiniPlayerApp.test.tsx b/src/app/MiniPlayerApp.test.tsx index 341c7aa4..9c3be5ed 100644 --- a/src/app/MiniPlayerApp.test.tsx +++ b/src/app/MiniPlayerApp.test.tsx @@ -29,7 +29,7 @@ vi.mock('@/lib/i18n', () => ({ default: { changeLanguage: vi.fn() }, })); vi.mock('@/features/miniPlayer', () => ({ default: () =>
})); -vi.mock('../components/GlobalConfirmModal', () => ({ default: () => })); +vi.mock('@/ui/GlobalConfirmModal', () => ({ default: () => })); vi.mock('@/ui/TooltipPortal', () => ({ default: () => })); vi.mock('@/app/FpsOverlay', () => ({ default: () => })); vi.mock('@/lib/dnd/DragDropContext', () => ({ diff --git a/src/app/MiniPlayerApp.tsx b/src/app/MiniPlayerApp.tsx index d97752c3..bc5b6be6 100644 --- a/src/app/MiniPlayerApp.tsx +++ b/src/app/MiniPlayerApp.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { DragDropProvider } from '@/lib/dnd/DragDropContext'; import MiniPlayer from '@/features/miniPlayer'; -import GlobalConfirmModal from '../components/GlobalConfirmModal'; +import GlobalConfirmModal from '@/ui/GlobalConfirmModal'; import TooltipPortal from '@/ui/TooltipPortal'; import FpsOverlay from '@/app/FpsOverlay'; import { useThemeStore } from '../store/themeStore'; diff --git a/src/features/orbit/components/OrbitParticipantsPopover.tsx b/src/features/orbit/components/OrbitParticipantsPopover.tsx index 0ad758e4..3863f492 100644 --- a/src/features/orbit/components/OrbitParticipantsPopover.tsx +++ b/src/features/orbit/components/OrbitParticipantsPopover.tsx @@ -4,7 +4,7 @@ import { Crown, User, UserMinus, ShieldOff, Mic, MicOff } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useOrbitStore } from '@/features/orbit/store/orbitStore'; import { kickOrbitParticipant, removeOrbitParticipant, setOrbitSuggestionBlocked } from '@/features/orbit/utils/orbit'; -import ConfirmModal from '@/components/ConfirmModal'; +import ConfirmModal from '@/ui/ConfirmModal'; interface Props { /** Anchor — we position the popover directly below its bottom-right. */ diff --git a/src/features/orbit/components/OrbitSessionBar.tsx b/src/features/orbit/components/OrbitSessionBar.tsx index 7b245c4d..c4a1481b 100644 --- a/src/features/orbit/components/OrbitSessionBar.tsx +++ b/src/features/orbit/components/OrbitSessionBar.tsx @@ -18,7 +18,7 @@ import OrbitExitModal from '@/features/orbit/components/OrbitExitModal'; import OrbitSettingsPopover from '@/features/orbit/components/OrbitSettingsPopover'; import OrbitSharePopover from '@/features/orbit/components/OrbitSharePopover'; import OrbitDiagnosticsPopover from '@/features/orbit/components/OrbitDiagnosticsPopover'; -import ConfirmModal from '@/components/ConfirmModal'; +import ConfirmModal from '@/ui/ConfirmModal'; import { formatTrackTime } from '@/lib/format/formatDuration'; /** diff --git a/src/components/SongInfoModal.tsx b/src/features/playback/components/SongInfoModal.tsx similarity index 98% rename from src/components/SongInfoModal.tsx rename to src/features/playback/components/SongInfoModal.tsx index 5b0479cc..af47f45f 100644 --- a/src/components/SongInfoModal.tsx +++ b/src/features/playback/components/SongInfoModal.tsx @@ -7,8 +7,8 @@ import { X } from 'lucide-react'; import { usePlayerStore } from '@/features/playback/store/playerStore'; import { useShallow } from 'zustand/react/shallow'; import { ndGetSongPath } from '@/lib/api/navidromeAdmin'; -import { useAuthStore } from '../store/authStore'; -import { useLibraryIndexStore } from '../store/libraryIndexStore'; +import { useAuthStore } from '@/store/authStore'; +import { useLibraryIndexStore } from '@/store/libraryIndexStore'; import { useTranslation } from 'react-i18next'; import { copyTextToClipboard } from '@/lib/server/serverMagicString'; import { showToast } from '@/lib/dom/toast'; diff --git a/src/components/LicenseTextModal.tsx b/src/features/settings/components/LicenseTextModal.tsx similarity index 100% rename from src/components/LicenseTextModal.tsx rename to src/features/settings/components/LicenseTextModal.tsx diff --git a/src/features/settings/components/LicensesPanel.tsx b/src/features/settings/components/LicensesPanel.tsx index b4b6c7a1..717098d9 100644 --- a/src/features/settings/components/LicensesPanel.tsx +++ b/src/features/settings/components/LicensesPanel.tsx @@ -9,7 +9,7 @@ import { type LicenseEntry, type LicensesData, } from '@/features/settings/utils/licensesData'; -import LicenseTextModal from '@/components/LicenseTextModal'; +import LicenseTextModal from './LicenseTextModal'; const ROW_HEIGHT = 56; diff --git a/src/features/settings/components/ThemeImportSection.tsx b/src/features/settings/components/ThemeImportSection.tsx index e75b2dba..ecfdb1c9 100644 --- a/src/features/settings/components/ThemeImportSection.tsx +++ b/src/features/settings/components/ThemeImportSection.tsx @@ -6,7 +6,7 @@ import { open } from '@tauri-apps/plugin-dialog'; import { useInstalledThemesStore } from '@/store/installedThemesStore'; import { validateThemePackage, type ValidatedTheme } from '@/lib/themes/validateThemePackage'; import { showToast } from '@/lib/dom/toast'; -import ConfirmModal from '@/components/ConfirmModal'; +import ConfirmModal from '@/ui/ConfirmModal'; /** * Import a community theme from a local `.zip` (manifest.json + theme.css). diff --git a/src/features/settings/components/UserManagementSection.tsx b/src/features/settings/components/UserManagementSection.tsx index 9ab539ff..d0ca5be8 100644 --- a/src/features/settings/components/UserManagementSection.tsx +++ b/src/features/settings/components/UserManagementSection.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { RotateCcw, UserPlus, Users } from 'lucide-react'; import type { NdUser } from '@/lib/api/navidromeAdmin'; -import ConfirmModal from '@/components/ConfirmModal'; +import ConfirmModal from '@/ui/ConfirmModal'; import { useUserMgmtData } from '@/features/settings/hooks/useUserMgmtData'; import { useUserMgmtActions } from '@/features/settings/hooks/useUserMgmtActions'; import { UserForm } from '@/features/settings/components/UserForm'; diff --git a/src/components/PasteClipboardHandler.tsx b/src/features/share/components/PasteClipboardHandler.tsx similarity index 98% rename from src/components/PasteClipboardHandler.tsx rename to src/features/share/components/PasteClipboardHandler.tsx index ff300671..9ca40e7a 100644 --- a/src/components/PasteClipboardHandler.tsx +++ b/src/features/share/components/PasteClipboardHandler.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useAuthStore } from '../store/authStore'; +import { useAuthStore } from '@/store/authStore'; import type { EntitySharePayloadV1 } from '@/lib/share/shareLink'; import { decodeSharePayloadFromText } from '@/lib/share/shareLink'; import { decodeServerMagicStringFromText } from '@/lib/server/serverMagicString'; @@ -17,9 +17,9 @@ import { readOrbitState, OrbitJoinError, } from '@/features/orbit'; -import { switchActiveServer } from '../utils/server/switchActiveServer'; +import { switchActiveServer } from '@/utils/server/switchActiveServer'; import { useOrbitAccountPickerStore } from '@/features/orbit'; -import ConfirmModal from './ConfirmModal'; +import ConfirmModal from '@/ui/ConfirmModal'; const ORBIT_JOIN_ERROR_KEYS: Record