mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
refactor(settings): co-locate settings feature into features/settings
Theme infra (fixedThemes, theme utils), music-network presetIcon, audio-device probe, library-index sync, and the auth*SettingsActions slices stay in the core/global layer (consumed via @/ alias) to avoid inverting core->feature.
This commit is contained in:
@@ -27,7 +27,7 @@ const LuckyMixPage = lazy(() => import('../pages/LuckyMix'));
|
||||
const Playlists = lazy(() => import('../pages/Playlists'));
|
||||
const PlaylistDetail = lazy(() => import('../pages/PlaylistDetail'));
|
||||
const NowPlayingPage = lazy(() => import('@/features/nowPlaying/pages/NowPlaying'));
|
||||
const Settings = lazy(() => import('../pages/Settings'));
|
||||
const Settings = lazy(() => import('@/features/settings/pages/Settings'));
|
||||
const Statistics = lazy(() => import('@/features/stats/pages/Statistics'));
|
||||
const Help = lazy(() => import('../pages/Help'));
|
||||
const WhatsNew = lazy(() => import('../pages/WhatsNew'));
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHelpModalStore } from '../store/helpModalStore';
|
||||
import SettingsSubSection from './SettingsSubSection';
|
||||
import { SettingsSubSection } from '@/features/settings';
|
||||
|
||||
/**
|
||||
* Orbit help modal. Rendered once at the app root; triggered from the
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { PlaybackRateControls } from '../settings/audio/PlaybackRateBlock';
|
||||
import { PlaybackRateControls } from '@/features/settings';
|
||||
import { usePlaybackRateStore } from '../../store/playbackRateStore';
|
||||
import { useOrbitStore } from '../../store/orbitStore';
|
||||
import {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||
import { AddServerForm } from './AddServerForm';
|
||||
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
||||
import { encodeServerMagicString } from '@/utils/server/serverMagicString';
|
||||
|
||||
// resolve_host_addresses Tauri command — hint-only, must not block save.
|
||||
+8
-8
@@ -1,22 +1,22 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { CustomHeaderEntry, CustomHeadersApplyTo, ServerProfile } from '../../store/authStoreTypes';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import type { CustomHeaderEntry, CustomHeadersApplyTo, ServerProfile } from '@/store/authStoreTypes';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import {
|
||||
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
|
||||
serverCustomHeadersFromForm,
|
||||
validateCustomHeaders,
|
||||
} from '../../utils/server/serverHttpHeaders';
|
||||
import { CustomHttpHeadersEditor } from './CustomHttpHeadersEditor';
|
||||
} from '@/utils/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';
|
||||
import { resolveHostAddresses } from '../../api/network';
|
||||
} from '@/utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
||||
import { isLanUrl } from '@/utils/server/serverEndpoint';
|
||||
import { resolveHostAddresses } from '@/api/network';
|
||||
|
||||
type FormState = {
|
||||
name: string;
|
||||
+12
-12
@@ -4,11 +4,11 @@ import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { save as saveDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { writeFile } from '@tauri-apps/plugin-fs';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsSubCard } from './SettingsSubCard';
|
||||
import { useAnalysisStrategyStore } from '../../store/analysisStrategyStore';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsSubCard } from '@/features/settings/components/SettingsSubCard';
|
||||
import { useAnalysisStrategyStore } from '@/store/analysisStrategyStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import {
|
||||
analysisClearFailedTracks,
|
||||
analysisDeleteAllForServer,
|
||||
@@ -18,18 +18,18 @@ import {
|
||||
type AnalysisFailedTrackDto,
|
||||
libraryAnalysisProgress,
|
||||
type LibraryAnalysisProgressDto,
|
||||
} from '../../api/analysis';
|
||||
import { libraryGetTracksBatch, type LibraryTrackDto, type TrackRefDto } from '../../api/library';
|
||||
import { buildStreamUrlForServer } from '../../api/subsonicStreamUrl';
|
||||
import { serverListDisplayLabel } from '../../utils/server/serverDisplayName';
|
||||
import { serverIndexKeyForProfile } from '../../utils/server/serverIndexKey';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
} from '@/api/analysis';
|
||||
import { libraryGetTracksBatch, type LibraryTrackDto, type TrackRefDto } from '@/api/library';
|
||||
import { buildStreamUrlForServer } from '@/api/subsonicStreamUrl';
|
||||
import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import {
|
||||
ANALYTICS_STRATEGIES,
|
||||
ADVANCED_PARALLELISM_MAX,
|
||||
ADVANCED_PARALLELISM_MIN,
|
||||
type AnalyticsStrategy,
|
||||
} from '../../utils/library/analysisStrategy';
|
||||
} from '@/utils/library/analysisStrategy';
|
||||
|
||||
type ClearTarget = {
|
||||
serverId: string;
|
||||
+12
-12
@@ -2,22 +2,22 @@ import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { LayoutGrid, Maximize2, Palette, Sliders, Type, ZoomIn } from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import {
|
||||
LIBRARY_GRID_MAX_COLUMNS_MAX,
|
||||
LIBRARY_GRID_MAX_COLUMNS_MIN,
|
||||
} from '../../store/authStoreDefaults';
|
||||
import type { SeekbarStyle, WindowButtonStyle } from '../../store/authStoreTypes';
|
||||
import { useFontStore, FontId } from '../../store/fontStore';
|
||||
import { useThemeStore } from '../../store/themeStore';
|
||||
import { IS_LINUX, IS_WINDOWS } from '../../utils/platform';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue } from './SettingsSubCard';
|
||||
import { SettingsSegmented, type SegmentedOption } from './SettingsSegmented';
|
||||
} from '@/store/authStoreDefaults';
|
||||
import type { SeekbarStyle, WindowButtonStyle } from '@/store/authStoreTypes';
|
||||
import { useFontStore, FontId } from '@/store/fontStore';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { IS_LINUX, IS_WINDOWS } from '@/utils/platform';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue } from '@/features/settings/components/SettingsSubCard';
|
||||
import { SettingsSegmented, type SegmentedOption } from '@/features/settings/components/SettingsSegmented';
|
||||
import { SeekbarPreview } from '@/features/waveform';
|
||||
import WindowButtonPreview from '../WindowButtonPreview';
|
||||
import WindowButtonPreview from '@/components/WindowButtonPreview';
|
||||
|
||||
export function AppearanceTab() {
|
||||
const { t } = useTranslation();
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useArtistLayoutStore, type ArtistSectionConfig, type ArtistSectionId } from '../../store/artistLayoutStore';
|
||||
import { useListReorderDnd } from '../../hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '../../utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from './ReorderGripHandle';
|
||||
import { useArtistLayoutStore, type ArtistSectionConfig, type ArtistSectionId } from '@/store/artistLayoutStore';
|
||||
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
|
||||
const ARTIST_SECTION_LABEL_KEYS: Record<ArtistSectionId, string> = {
|
||||
bio: 'settings.artistLayoutBio',
|
||||
+14
-14
@@ -1,20 +1,20 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Blend, Gauge, Sliders, Volume2, Waves } from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import Equalizer from '../Equalizer';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { effectiveLoudnessPreAnalysisAttenuationDb } from '../../utils/audio/loudnessPreAnalysisSlider';
|
||||
import { useAudioDevicesProbe } from '../../hooks/useAudioDevicesProbe';
|
||||
import { IS_MACOS } from '../../utils/platform';
|
||||
import { AudioOutputDeviceSection } from './audio/AudioOutputDeviceSection';
|
||||
import { NormalizationBlock } from './audio/NormalizationBlock';
|
||||
import { PlaybackRateBlock } from './audio/PlaybackRateBlock';
|
||||
import { TrackTransitionsBlock } from './audio/TrackTransitionsBlock';
|
||||
import { TrackPreviewsSection } from './audio/TrackPreviewsSection';
|
||||
import { HiResCrossfadeResampleBlock } from './audio/HiResCrossfadeResampleBlock';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import Equalizer from '@/components/Equalizer';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { effectiveLoudnessPreAnalysisAttenuationDb } from '@/utils/audio/loudnessPreAnalysisSlider';
|
||||
import { useAudioDevicesProbe } from '@/hooks/useAudioDevicesProbe';
|
||||
import { IS_MACOS } from '@/utils/platform';
|
||||
import { AudioOutputDeviceSection } from '@/features/settings/components/audio/AudioOutputDeviceSection';
|
||||
import { NormalizationBlock } from '@/features/settings/components/audio/NormalizationBlock';
|
||||
import { PlaybackRateBlock } from '@/features/settings/components/audio/PlaybackRateBlock';
|
||||
import { TrackTransitionsBlock } from '@/features/settings/components/audio/TrackTransitionsBlock';
|
||||
import { TrackPreviewsSection } from '@/features/settings/components/audio/TrackPreviewsSection';
|
||||
import { HiResCrossfadeResampleBlock } from '@/features/settings/components/audio/HiResCrossfadeResampleBlock';
|
||||
|
||||
export function AudioTab() {
|
||||
const { t } = useTranslation();
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { ChevronDown, ChevronUp, GripVertical } from 'lucide-react';
|
||||
import { useDragDrop, useDragSource } from '../../contexts/DragDropContext';
|
||||
import type { BackdropSource, BackdropSourcePref } from '../../cover/artistBackdrop';
|
||||
import type { BackdropSurface } from '../../store/themeStore';
|
||||
import { moveSourceTo, dropSourceBefore } from './backdropReorder';
|
||||
import { useDragDrop, useDragSource } from '@/contexts/DragDropContext';
|
||||
import type { BackdropSource, BackdropSourcePref } from '@/cover/artistBackdrop';
|
||||
import type { BackdropSurface } from '@/store/themeStore';
|
||||
import { moveSourceTo, dropSourceBefore } from '@/features/settings/components/backdropReorder';
|
||||
|
||||
const DRAG_TYPE = 'backdrop-source';
|
||||
|
||||
+4
-4
@@ -2,15 +2,15 @@ import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Clock3, Download, Upload } from 'lucide-react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
import {
|
||||
exportBackupToPath,
|
||||
importAnyBackupFromPath,
|
||||
pickBackupExportPath,
|
||||
pickBackupImportPath,
|
||||
} from '../../utils/export/backup';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
} from '@/features/settings/utils/backup';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
type BackupMode = 'full' | 'library' | 'config';
|
||||
type BackupAction = 'export' | 'import';
|
||||
+14
-14
@@ -2,28 +2,28 @@ import { Image, Trash2 } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useState, type CSSProperties } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsSubCard } from './SettingsSubCard';
|
||||
import { useCoverStrategyStore } from '../../store/coverStrategyStore';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsSubCard } from '@/features/settings/components/SettingsSubCard';
|
||||
import { useCoverStrategyStore } from '@/store/coverStrategyStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import {
|
||||
coverCacheClearServer,
|
||||
coverCacheStatsServer,
|
||||
libraryCoverCatalogSize,
|
||||
libraryCoverProgress,
|
||||
} from '../../api/coverCache';
|
||||
import { clearDiskSrcCacheForServer } from '../../cover/diskSrcCache';
|
||||
import { serverListDisplayLabel } from '../../utils/server/serverDisplayName';
|
||||
import { serverIndexKeyForProfile } from '../../utils/server/serverIndexKey';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import { formatBytes } from '../../utils/format/formatBytes';
|
||||
import { clearImageCache, getImageCacheSize } from '../../utils/imageCache';
|
||||
import { wakeLibraryCoverBackfill } from '../../utils/library/coverBackfillWake';
|
||||
} from '@/api/coverCache';
|
||||
import { clearDiskSrcCacheForServer } from '@/cover/diskSrcCache';
|
||||
import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { formatBytes } from '@/utils/format/formatBytes';
|
||||
import { clearImageCache, getImageCacheSize } from '@/utils/imageCache';
|
||||
import { wakeLibraryCoverBackfill } from '@/utils/library/coverBackfillWake';
|
||||
import {
|
||||
COVER_CACHE_STRATEGIES,
|
||||
type CoverCacheStrategy,
|
||||
} from '../../utils/library/coverStrategy';
|
||||
} from '@/utils/library/coverStrategy';
|
||||
|
||||
type ClearTarget =
|
||||
| { kind: 'image' }
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { CustomHeaderEntry, CustomHeadersApplyTo } from '../../store/authStoreTypes';
|
||||
import type { CustomHeaderEntry, CustomHeadersApplyTo } from '@/store/authStoreTypes';
|
||||
|
||||
export type CustomHttpHeadersEditorProps = {
|
||||
headers: CustomHeaderEntry[];
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHomeStore, HomeSectionId } from '../../store/homeStore';
|
||||
import { useHomeStore, HomeSectionId } from '@/store/homeStore';
|
||||
|
||||
export function HomeCustomizer() {
|
||||
const { t } = useTranslation();
|
||||
+6
-6
@@ -1,12 +1,12 @@
|
||||
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 { useGlobalShortcutsStore, type GlobalAction, buildGlobalShortcut, formatGlobalShortcut } from '../../store/globalShortcutsStore';
|
||||
import { useKeybindingsStore, type KeyAction, buildInAppBinding, formatBinding } from '../../store/keybindingsStore';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsSubCard } from './SettingsSubCard';
|
||||
import { IN_APP_SHORTCUT_ACTIONS, GLOBAL_SHORTCUT_ACTIONS } from '@/config/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';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsSubCard } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
export function InputTab() {
|
||||
const { t } = useTranslation();
|
||||
+9
-9
@@ -1,15 +1,15 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Check, RefreshCw, X } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useThemeStore } from '../../store/themeStore';
|
||||
import { useInstalledThemesStore } from '../../store/installedThemesStore';
|
||||
import { uninstallTheme } from '../../utils/themes/uninstallTheme';
|
||||
import { installThemeFromRegistry } from '../../utils/themes/installThemeFromRegistry';
|
||||
import { useThemeUpdates } from '../../hooks/useThemeUpdates';
|
||||
import { useThemeAnimationRisk } from '../../hooks/useThemeAnimationRisk';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import { AnimatedThemeBadge } from './AnimatedThemeBadge';
|
||||
import { FIXED_THEMES } from './fixedThemes';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useInstalledThemesStore } from '@/store/installedThemesStore';
|
||||
import { uninstallTheme } from '@/utils/themes/uninstallTheme';
|
||||
import { installThemeFromRegistry } from '@/utils/themes/installThemeFromRegistry';
|
||||
import { useThemeUpdates } from '@/hooks/useThemeUpdates';
|
||||
import { useThemeAnimationRisk } from '@/hooks/useThemeAnimationRisk';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { AnimatedThemeBadge } from '@/features/settings/components/AnimatedThemeBadge';
|
||||
import { FIXED_THEMES } from '@/components/settings/fixedThemes';
|
||||
|
||||
/** Pull a 3-band swatch (bg / card / accent) out of an installed theme's CSS. */
|
||||
function swatch(css: string): { bg: string; card: string; accent: string } {
|
||||
+11
-11
@@ -1,16 +1,16 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AlertTriangle, Check, Image as ImageIcon, Info, Sparkles, Wifi } from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useThemeStore } from '../../store/themeStore';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import { BackdropSourceList } from './BackdropSourceList';
|
||||
import type { BackdropSurface } from '../../store/themeStore';
|
||||
import type { BackdropSource } from '../../cover/artistBackdrop';
|
||||
import { MusicNetworkSection } from './musicNetwork/MusicNetworkSection';
|
||||
import { purgeExternalArtworkAllServers } from '../../api/coverCache';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
import { BackdropSourceList } from '@/features/settings/components/BackdropSourceList';
|
||||
import type { BackdropSurface } from '@/store/themeStore';
|
||||
import type { BackdropSource } from '@/cover/artistBackdrop';
|
||||
import { MusicNetworkSection } from '@/features/settings/components/musicNetwork/MusicNetworkSection';
|
||||
import { purgeExternalArtworkAllServers } from '@/api/coverCache';
|
||||
|
||||
export function IntegrationsTab() {
|
||||
const { t } = useTranslation();
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Shuffle, Star } from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { MIX_MIN_RATING_FILTER_MAX_STARS } from '../../store/authStoreDefaults';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import StarRating from '../StarRating';
|
||||
import AnalyticsStrategySection from './AnalyticsStrategySection';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { MIX_MIN_RATING_FILTER_MAX_STARS } from '@/store/authStoreDefaults';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
import StarRating from '@/components/StarRating';
|
||||
import AnalyticsStrategySection from '@/features/settings/components/AnalyticsStrategySection';
|
||||
const AUDIOBOOK_GENRES_DISPLAY = ['Hörbuch', 'Hoerbuch', 'Hörspiel', 'Hoerspiel', 'Audiobook', 'Audio Book', 'Spoken Word', 'Spokenword', 'Podcast', 'Kapitel', 'Thriller', 'Krimi', 'Speech', 'Fantasy', 'Comedy', 'Literature'];
|
||||
|
||||
export function LibraryTab() {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { LoudnessLufsPreset } from '../../store/authStoreTypes';
|
||||
import type { LoudnessLufsPreset } from '@/store/authStoreTypes';
|
||||
|
||||
const LOUDNESS_LUFS_BUTTON_ORDER: LoudnessLufsPreset[] = [-10, -12, -14, -16];
|
||||
|
||||
+6
-6
@@ -1,12 +1,12 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import type { LyricsSourceId } from '../../store/authStoreTypes';
|
||||
import { useListReorderDnd } from '../../hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '../../utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from './ReorderGripHandle';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { LyricsSourceId } from '@/store/authStoreTypes';
|
||||
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
|
||||
const LYRICS_SOURCE_LABEL_KEYS: Record<LyricsSourceId, string> = {
|
||||
server: 'settings.lyricsSourceServer',
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AudioLines, Music2 } from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { LyricsSourcesCustomizer } from './LyricsSourcesCustomizer';
|
||||
import { SettingsSegmented, type SegmentedOption } from './SettingsSegmented';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { LyricsSourcesCustomizer } from '@/features/settings/components/LyricsSourcesCustomizer';
|
||||
import { SettingsSegmented, type SegmentedOption } from '@/features/settings/components/SettingsSegmented';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
export function LyricsTab() {
|
||||
const { t } = useTranslation();
|
||||
+18
-18
@@ -1,24 +1,24 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Disc3, LayoutGrid, ListOrdered, ListTodo, PanelLeft, RotateCcw, Users } from 'lucide-react';
|
||||
import { useArtistLayoutStore } from '../../store/artistLayoutStore';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import type { QueueDisplayMode } from '../../store/authStoreTypes';
|
||||
import { useHomeStore } from '../../store/homeStore';
|
||||
import { usePlayerBarLayoutStore } from '../../store/playerBarLayoutStore';
|
||||
import { usePlaylistLayoutStore } from '../../store/playlistLayoutStore';
|
||||
import { useQueueToolbarStore } from '../../store/queueToolbarStore';
|
||||
import { useArtistLayoutStore } from '@/store/artistLayoutStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { QueueDisplayMode } from '@/store/authStoreTypes';
|
||||
import { useHomeStore } from '@/store/homeStore';
|
||||
import { usePlayerBarLayoutStore } from '@/store/playerBarLayoutStore';
|
||||
import { usePlaylistLayoutStore } from '@/store/playlistLayoutStore';
|
||||
import { useQueueToolbarStore } from '@/store/queueToolbarStore';
|
||||
import { useSidebarStore } from '@/features/sidebar';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { SettingsSegmented, type SegmentedOption } from './SettingsSegmented';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import { ArtistLayoutCustomizer } from './ArtistLayoutCustomizer';
|
||||
import { HomeCustomizer } from './HomeCustomizer';
|
||||
import { PlayerBarLayoutCustomizer } from './PlayerBarLayoutCustomizer';
|
||||
import { PlaylistLayoutCustomizer } from './PlaylistLayoutCustomizer';
|
||||
import { QueueToolbarCustomizer } from './QueueToolbarCustomizer';
|
||||
import { SidebarCustomizer } from './SidebarCustomizer';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSegmented, type SegmentedOption } from '@/features/settings/components/SettingsSegmented';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
import { ArtistLayoutCustomizer } from '@/features/settings/components/ArtistLayoutCustomizer';
|
||||
import { HomeCustomizer } from '@/features/settings/components/HomeCustomizer';
|
||||
import { PlayerBarLayoutCustomizer } from '@/features/settings/components/PlayerBarLayoutCustomizer';
|
||||
import { PlaylistLayoutCustomizer } from '@/features/settings/components/PlaylistLayoutCustomizer';
|
||||
import { QueueToolbarCustomizer } from '@/features/settings/components/QueueToolbarCustomizer';
|
||||
import { SidebarCustomizer } from '@/features/settings/components/SidebarCustomizer';
|
||||
|
||||
export function PersonalisationTab() {
|
||||
const { t } = useTranslation();
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Gauge, Heart, PictureInPicture2, SlidersVertical, Star } from 'lucide-react';
|
||||
import LastfmIcon from '../LastfmIcon';
|
||||
import LastfmIcon from '@/components/LastfmIcon';
|
||||
import {
|
||||
usePlayerBarLayoutStore,
|
||||
type PlayerBarLayoutItemId,
|
||||
} from '../../store/playerBarLayoutStore';
|
||||
} from '@/store/playerBarLayoutStore';
|
||||
|
||||
const PLAYER_BAR_LAYOUT_LABEL_KEYS: Record<PlayerBarLayoutItemId, string> = {
|
||||
starRating: 'settings.playerBarStarRating',
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Download, FileUp, HardDrive, Lightbulb, Search } from 'lucide-react';
|
||||
import { usePlaylistLayoutStore, type PlaylistLayoutItemId } from '../../store/playlistLayoutStore';
|
||||
import { usePlaylistLayoutStore, type PlaylistLayoutItemId } from '@/store/playlistLayoutStore';
|
||||
|
||||
const PLAYLIST_LAYOUT_ICONS: Record<PlaylistLayoutItemId, typeof Search> = {
|
||||
addSongs: Search,
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Blend, Infinity as InfinityIcon, ListMusic, MoveRight, Share2, Shuffle, Trash2, Waves } from 'lucide-react';
|
||||
import { useQueueToolbarStore, QueueToolbarButtonId } from '../../store/queueToolbarStore';
|
||||
import { useListReorderDnd } from '../../hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '../../utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from './ReorderGripHandle';
|
||||
import { useQueueToolbarStore, QueueToolbarButtonId } from '@/store/queueToolbarStore';
|
||||
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
|
||||
const QUEUE_TOOLBAR_BUTTON_ICONS: Record<QueueToolbarButtonId, typeof Shuffle | null> = {
|
||||
shuffle: Shuffle,
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { GripVertical } from 'lucide-react';
|
||||
import { useDragSource } from '../../contexts/DragDropContext';
|
||||
import { useDragSource } from '@/contexts/DragDropContext';
|
||||
|
||||
/**
|
||||
* Drag handle shared by the reorder customizers. Emits an id-based payload
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getCapabilityDefinition } from '../../serverCapabilities/catalog';
|
||||
import { isFeatureActiveForServer, resolveFeatureForServer } from '../../serverCapabilities/storeView';
|
||||
import { getCapabilityDefinition } from '@/serverCapabilities/catalog';
|
||||
import { isFeatureActiveForServer, resolveFeatureForServer } from '@/serverCapabilities/storeView';
|
||||
|
||||
/** Inline badge for auto-managed server capabilities (Settings → Servers header row). */
|
||||
export function ServerCapabilityHeaderBadge({
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
import { RefreshCw, ShieldCheck, WifiOff, Zap } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { SyncStateDto } from '../../api/library';
|
||||
import type { SyncStateDto } from '@/api/library';
|
||||
import {
|
||||
libraryStatusDisplayTrackCount,
|
||||
libraryStatusIsReady,
|
||||
} from '../../utils/library/libraryReady';
|
||||
import type { LibraryServerConnection } from '../../hooks/useLibraryIndexSync';
|
||||
} from '@/utils/library/libraryReady';
|
||||
import type { LibraryServerConnection } from '@/hooks/useLibraryIndexSync';
|
||||
|
||||
interface ServerLibraryIndexControlsProps {
|
||||
status: SyncStateDto | null;
|
||||
+28
-28
@@ -3,43 +3,43 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
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 { buildCapabilityContext } from '../../serverCapabilities/context';
|
||||
import { useLibraryIndexStore } from '../../store/libraryIndexStore';
|
||||
import { libraryDeleteServerData, librarySyncClearSession } from '../../api/library';
|
||||
import { bootstrapIndexedServer } from '../../utils/library/librarySession';
|
||||
import { useLibraryIndexSync } from '../../hooks/useLibraryIndexSync';
|
||||
import ServerLibraryIndexControls from './ServerLibraryIndexControls';
|
||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
||||
import { pingWithCredentialsForProfile, scheduleInstantMixProbeForServer } from '../../api/subsonic';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { formatServerSoftware, isNavidromeAudiomuseSoftwareEligible, type InstantMixProbeResult, type SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
|
||||
import { buildCapabilityContext } from '@/serverCapabilities/context';
|
||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||
import { libraryDeleteServerData, librarySyncClearSession } from '@/api/library';
|
||||
import { bootstrapIndexedServer } from '@/utils/library/librarySession';
|
||||
import { useLibraryIndexSync } from '@/hooks/useLibraryIndexSync';
|
||||
import ServerLibraryIndexControls from '@/features/settings/components/ServerLibraryIndexControls';
|
||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||
import { pingWithCredentialsForProfile, scheduleInstantMixProbeForServer } from '@/api/subsonic';
|
||||
import {
|
||||
clearServerHttpContext,
|
||||
syncServerHttpContextForProfile,
|
||||
} from '../../utils/server/syncServerHttpContext';
|
||||
import { type ServerMagicPayload } from '../../utils/server/serverMagicString';
|
||||
import { ensureConnectUrlResolved, invalidateReachableEndpointCache } from '../../utils/server/serverEndpoint';
|
||||
} from '@/utils/server/syncServerHttpContext';
|
||||
import { type ServerMagicPayload } from '@/utils/server/serverMagicString';
|
||||
import { ensureConnectUrlResolved, invalidateReachableEndpointCache } from '@/utils/server/serverEndpoint';
|
||||
import {
|
||||
verifySameServerEndpoints,
|
||||
type VerifySameServerResult,
|
||||
} from '../../utils/server/serverFingerprint';
|
||||
} from '@/utils/server/serverFingerprint';
|
||||
import {
|
||||
indexKeyRemapForUrlChange,
|
||||
runIndexKeyRemigration,
|
||||
} from '../../utils/server/serverUrlRemigration';
|
||||
import { useConfirmModalStore } from '../../store/confirmModalStore';
|
||||
import { showToast } from '../../utils/ui/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 { switchActiveServer } from '../../utils/server/switchActiveServer';
|
||||
import { AddServerForm } from './AddServerForm';
|
||||
import { ServerCapabilityHeaderBadge } from './ServerCapabilityHeaderBadge';
|
||||
import { useListReorderDnd } from '../../hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '../../utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from './ReorderGripHandle';
|
||||
} from '@/utils/server/serverUrlRemigration';
|
||||
import { useConfirmModalStore } from '@/store/confirmModalStore';
|
||||
import { showToast } from '@/utils/ui/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 { switchActiveServer } from '@/utils/server/switchActiveServer';
|
||||
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
||||
import { ServerCapabilityHeaderBadge } from '@/features/settings/components/ServerCapabilityHeaderBadge';
|
||||
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
import { tooltipAttrs } from '@/ui/tooltipAttrs';
|
||||
|
||||
const AUDIOMUSE_NV_PLUGIN_URL = 'https://github.com/NeptuneHub/AudioMuse-AI-NV-plugin';
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React, { useId } from 'react';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
interface SettingsSubSectionProps {
|
||||
title: string;
|
||||
+8
-8
@@ -1,15 +1,15 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useSidebarStore, SidebarItemConfig, CONSERVED_SIDEBAR_NAV_IDS } from '@/features/sidebar';
|
||||
import { useLuckyMixAvailable } from '../../hooks/useLuckyMixAvailable';
|
||||
import { ALL_NAV_ITEMS } from '../../config/navItems';
|
||||
import { useLuckyMixAvailable } from '@/hooks/useLuckyMixAvailable';
|
||||
import { ALL_NAV_ITEMS } from '@/config/navItems';
|
||||
import { applySidebarReorderById } from '@/features/sidebar';
|
||||
import { useListReorderDnd } from '../../hooks/useListReorderDnd';
|
||||
import type { ListReorderDropTarget } from '../../utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from './ReorderGripHandle';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
||||
import type { ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
|
||||
const REORDER_TYPE = 'sidebar_reorder';
|
||||
|
||||
+9
-9
@@ -3,15 +3,15 @@ import { useTranslation } from 'react-i18next';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { Download, FolderOpen, Trash2, X } from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { countHotCacheTracks } from '../../store/hotCacheStore';
|
||||
import { useLocalPlaybackStore } from '../../store/localPlaybackStore';
|
||||
import { formatBytes, snapHotCacheMb } from '../../utils/format/formatBytes';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue } from './SettingsSubCard';
|
||||
import CoverCacheStrategySection from './CoverCacheStrategySection';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { countHotCacheTracks } from '@/store/hotCacheStore';
|
||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||
import { formatBytes, snapHotCacheMb } from '@/utils/format/formatBytes';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue } from '@/features/settings/components/SettingsSubCard';
|
||||
import CoverCacheStrategySection from '@/features/settings/components/CoverCacheStrategySection';
|
||||
|
||||
export function StorageTab() {
|
||||
const { t } = useTranslation();
|
||||
+14
-14
@@ -5,21 +5,21 @@ import { invoke } from '@tauri-apps/api/core';
|
||||
import { save as saveDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
||||
import { AppWindow, ChevronDown, Download, ExternalLink, Globe, HardDrive, Info, Scale, Sliders, Users } from 'lucide-react';
|
||||
import { version as appVersion } from '../../../package.json';
|
||||
import i18n from '../../i18n';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import type { ClockFormat, LinuxWaylandTextRenderProfile, LoggingMode } from '../../store/authStoreTypes';
|
||||
import { IS_LINUX } from '../../utils/platform';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import { AboutPsysonicBrandHeader } from '../AboutPsysonicLol';
|
||||
import { version as appVersion } from '@/../package.json';
|
||||
import i18n from '@/i18n';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { ClockFormat, LinuxWaylandTextRenderProfile, LoggingMode } from '@/store/authStoreTypes';
|
||||
import { IS_LINUX } from '@/utils/platform';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { AboutPsysonicBrandHeader } from '@/components/AboutPsysonicLol';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import LicensesPanel from '../LicensesPanel';
|
||||
import SettingsSubSection from '../SettingsSubSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsToggle } from './SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import { BackupSection } from './BackupSection';
|
||||
import { CONTRIBUTORS, MAINTAINERS } from '../../config/settingsCredits';
|
||||
import LicensesPanel from '@/components/LicensesPanel';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
import { BackupSection } from '@/features/settings/components/BackupSection';
|
||||
import { CONTRIBUTORS, MAINTAINERS } from '@/config/settingsCredits';
|
||||
|
||||
export function SystemTab() {
|
||||
const { t } = useTranslation();
|
||||
+4
-4
@@ -3,10 +3,10 @@ import { Upload, X } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import { useInstalledThemesStore } from '../../store/installedThemesStore';
|
||||
import { validateThemePackage, type ValidatedTheme } from '../../utils/themes/validateThemePackage';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import ConfirmModal from '../ConfirmModal';
|
||||
import { useInstalledThemesStore } from '@/store/installedThemesStore';
|
||||
import { validateThemePackage, type ValidatedTheme } from '@/utils/themes/validateThemePackage';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
|
||||
/**
|
||||
* Import a community theme from a local `.zip` (manifest.json + theme.css).
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||
import { ThemeStoreSection } from './ThemeStoreSection';
|
||||
import { ThemeStoreSection } from '@/features/settings/components/ThemeStoreSection';
|
||||
import type { FetchRegistryResult, Registry, RegistryTheme } from '@/utils/themes/themeRegistry';
|
||||
|
||||
// Control the registry the store browses so pagination/refresh are deterministic.
|
||||
+10
-10
@@ -2,21 +2,21 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Check, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, WifiOff } from 'lucide-react';
|
||||
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
||||
import CoverLightbox from '../CoverLightbox';
|
||||
import { useThemeAnimationRisk } from '../../hooks/useThemeAnimationRisk';
|
||||
import { AnimatedThemeBadge } from './AnimatedThemeBadge';
|
||||
import CoverLightbox from '@/components/CoverLightbox';
|
||||
import { useThemeAnimationRisk } from '@/hooks/useThemeAnimationRisk';
|
||||
import { AnimatedThemeBadge } from '@/features/settings/components/AnimatedThemeBadge';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import { formatRelativeTime } from '../../utils/format/relativeTime';
|
||||
import { useThemeStore } from '../../store/themeStore';
|
||||
import { useInstalledThemesStore, type InstalledTheme } from '../../store/installedThemesStore';
|
||||
import { formatRelativeTime } from '@/utils/format/relativeTime';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useInstalledThemesStore, type InstalledTheme } from '@/store/installedThemesStore';
|
||||
import {
|
||||
assetUrl,
|
||||
fetchRegistry,
|
||||
type RegistryTheme,
|
||||
} from '../../utils/themes/themeRegistry';
|
||||
import { installThemeFromRegistry } from '../../utils/themes/installThemeFromRegistry';
|
||||
import { uninstallTheme } from '../../utils/themes/uninstallTheme';
|
||||
import { isNewer } from '../../utils/componentHelpers/appUpdaterHelpers';
|
||||
} from '@/utils/themes/themeRegistry';
|
||||
import { installThemeFromRegistry } from '@/utils/themes/installThemeFromRegistry';
|
||||
import { uninstallTheme } from '@/utils/themes/uninstallTheme';
|
||||
import { isNewer } from '@/utils/componentHelpers/appUpdaterHelpers';
|
||||
|
||||
type ModeFilter = 'all' | 'dark' | 'light';
|
||||
type SortMode = 'newest' | 'name';
|
||||
+8
-8
@@ -1,16 +1,16 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Clock, Palette, Store, Upload } from 'lucide-react';
|
||||
import { useThemeStore } from '../../store/themeStore';
|
||||
import { useInstalledThemesStore } from '../../store/installedThemesStore';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useInstalledThemesStore } from '@/store/installedThemesStore';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import BackToTopButton from '@/ui/BackToTopButton';
|
||||
import { FIXED_THEMES } from './fixedThemes';
|
||||
import { InstalledThemes } from './InstalledThemes';
|
||||
import { ThemeImportSection } from './ThemeImportSection';
|
||||
import { ThemeStoreSection } from './ThemeStoreSection';
|
||||
import { SettingsGroup } from './SettingsGroup';
|
||||
import { SettingsSubCard, SettingsField } from './SettingsSubCard';
|
||||
import { FIXED_THEMES } from '@/components/settings/fixedThemes';
|
||||
import { InstalledThemes } from '@/features/settings/components/InstalledThemes';
|
||||
import { ThemeImportSection } from '@/features/settings/components/ThemeImportSection';
|
||||
import { ThemeStoreSection } from '@/features/settings/components/ThemeStoreSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
/**
|
||||
* A flat, always-visible section. The Themes tab has a single purpose, so its
|
||||
+5
-5
@@ -1,15 +1,15 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Shield, Wand2 } from 'lucide-react';
|
||||
import { ndUpdateUser, type NdLibrary, type NdUser } from '../../api/navidromeAdmin';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import { ndUpdateUser, type NdLibrary, type NdUser } from '@/api/navidromeAdmin';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import {
|
||||
copyTextToClipboard,
|
||||
encodeServerMagicString,
|
||||
magicPayloadAddressFields,
|
||||
} from '../../utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '../../utils/server/serverDisplayName';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
} from '@/utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
export interface UserFormState {
|
||||
userName: string;
|
||||
+7
-7
@@ -1,13 +1,13 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RotateCcw, UserPlus, Users } from 'lucide-react';
|
||||
import type { NdUser } from '../../api/navidromeAdmin';
|
||||
import ConfirmModal from '../ConfirmModal';
|
||||
import { useUserMgmtData } from '../../hooks/useUserMgmtData';
|
||||
import { useUserMgmtActions } from '../../hooks/useUserMgmtActions';
|
||||
import { UserForm } from './UserForm';
|
||||
import { UserMgmtRow } from './userMgmt/UserMgmtRow';
|
||||
import { MagicStringModal } from './userMgmt/MagicStringModal';
|
||||
import type { NdUser } from '@/api/navidromeAdmin';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
import { useUserMgmtData } from '@/features/settings/hooks/useUserMgmtData';
|
||||
import { useUserMgmtActions } from '@/features/settings/hooks/useUserMgmtActions';
|
||||
import { UserForm } from '@/features/settings/components/UserForm';
|
||||
import { UserMgmtRow } from '@/features/settings/components/userMgmt/UserMgmtRow';
|
||||
import { MagicStringModal } from '@/features/settings/components/userMgmt/MagicStringModal';
|
||||
|
||||
export function UserManagementSection({
|
||||
serverUrl,
|
||||
+6
-6
@@ -3,12 +3,12 @@ import { invoke } from '@tauri-apps/api/core';
|
||||
import { AudioLines, RotateCcw } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import SettingsSubSection from '../../SettingsSubSection';
|
||||
import { SettingsGroup } from '../SettingsGroup';
|
||||
import { SettingsToggle } from '../SettingsToggle';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { useEqStore } from '../../../store/eqStore';
|
||||
import { buildAudioDeviceSelectOptions } from '../../../utils/audio/audioDeviceLabels';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useEqStore } from '@/store/eqStore';
|
||||
import { buildAudioDeviceSelectOptions } from '@/utils/audio/audioDeviceLabels';
|
||||
|
||||
interface Props {
|
||||
audioDevices: string[];
|
||||
+2
-2
@@ -2,9 +2,9 @@ import {
|
||||
HI_RES_CROSSFADE_RESAMPLE_OPTIONS,
|
||||
type HiResCrossfadeResampleHz,
|
||||
sanitizeHiResCrossfadeResampleHz,
|
||||
} from '../../../utils/audio/hiResCrossfadeResample';
|
||||
} from '@/utils/audio/hiResCrossfadeResample';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { SettingsSubCard, SettingsField } from '../SettingsSubCard';
|
||||
import { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
interface Props {
|
||||
enabled: boolean;
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { RotateCcw } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB } from '../../../store/authStoreDefaults';
|
||||
import { LoudnessLufsButtonGroup } from '../LoudnessLufsButtonGroup';
|
||||
import { SettingsGroup } from '../SettingsGroup';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue, SettingsCallout } from '../SettingsSubCard';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB } from '@/store/authStoreDefaults';
|
||||
import { LoudnessLufsButtonGroup } from '@/features/settings/components/LoudnessLufsButtonGroup';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue, SettingsCallout } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
interface Props {
|
||||
preAnalysisEffectiveDb: number;
|
||||
+7
-7
@@ -17,13 +17,13 @@ import {
|
||||
playbackSpeedStep,
|
||||
varispeedSpeedFromSemitones,
|
||||
type PlaybackStrategy,
|
||||
} from '../../../utils/audio/playbackRateHelpers';
|
||||
import { usePlaybackRateStore } from '../../../store/playbackRateStore';
|
||||
import { useOrbitStore } from '../../../store/orbitStore';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { isOrbitPlaybackSyncActive } from '../../../utils/orbit';
|
||||
import { SettingsToggle } from '../SettingsToggle';
|
||||
import { SettingsSubCard } from '../SettingsSubCard';
|
||||
} from '@/utils/audio/playbackRateHelpers';
|
||||
import { usePlaybackRateStore } from '@/store/playbackRateStore';
|
||||
import { useOrbitStore } from '@/store/orbitStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { isOrbitPlaybackSyncActive } from '@/utils/orbit';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
interface Props {
|
||||
t: TFunction;
|
||||
+7
-7
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Play } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { TRACK_PREVIEW_LOCATIONS } from '../../../store/authStoreDefaults';
|
||||
import type { TrackPreviewLocation } from '../../../store/authStoreTypes';
|
||||
import SettingsSubSection from '../../SettingsSubSection';
|
||||
import { SettingsGroup } from '../SettingsGroup';
|
||||
import { SettingsToggle } from '../SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue } from '../SettingsSubCard';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { TRACK_PREVIEW_LOCATIONS } from '@/store/authStoreDefaults';
|
||||
import type { TrackPreviewLocation } from '@/store/authStoreTypes';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsValue } from '@/features/settings/components/SettingsSubCard';
|
||||
|
||||
interface Props {
|
||||
t: TFunction;
|
||||
+8
-8
@@ -1,20 +1,20 @@
|
||||
import React from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { useOrbitStore } from '../../../store/orbitStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/store/orbitStore';
|
||||
import {
|
||||
AUTODJ_OVERLAP_CAP_MAX_SEC,
|
||||
AUTODJ_OVERLAP_CAP_MIN_SEC,
|
||||
} from '../../../utils/playback/autodjOverlapCap';
|
||||
} from '@/utils/playback/autodjOverlapCap';
|
||||
import {
|
||||
getTransitionMode,
|
||||
setTransitionMode,
|
||||
type TransitionMode,
|
||||
} from '../../../utils/playback/playbackTransition';
|
||||
import { SettingsGroup } from '../SettingsGroup';
|
||||
import { SettingsToggle } from '../SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsRow, SettingsValue } from '../SettingsSubCard';
|
||||
import { SettingsSegmented, type SegmentedOption } from '../SettingsSegmented';
|
||||
} from '@/utils/playback/playbackTransition';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
import { SettingsSubCard, SettingsField, SettingsRow, SettingsValue } from '@/features/settings/components/SettingsSubCard';
|
||||
import { SettingsSegmented, type SegmentedOption } from '@/features/settings/components/SettingsSegmented';
|
||||
|
||||
interface Props {
|
||||
t: TFunction;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { moveSourceTo, dropSourceBefore } from './backdropReorder';
|
||||
import type { BackdropSource, BackdropSourcePref } from '../../cover/artistBackdrop';
|
||||
import { moveSourceTo, dropSourceBefore } from '@/features/settings/components/backdropReorder';
|
||||
import type { BackdropSource, BackdropSourcePref } from '@/cover/artistBackdrop';
|
||||
|
||||
const S = (...names: BackdropSource[]): BackdropSourcePref[] =>
|
||||
names.map((source) => ({ source, enabled: true }));
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { BackdropSourcePref } from '../../cover/artistBackdrop';
|
||||
import type { BackdropSourcePref } from '@/cover/artistBackdrop';
|
||||
|
||||
/**
|
||||
* Move the source at `from` so it lands at exactly index `to` — the ↑/↓ buttons,
|
||||
+2
-2
@@ -6,8 +6,8 @@ import {
|
||||
listPresets,
|
||||
type BuiltinPreset,
|
||||
type PresetId,
|
||||
} from '../../../music-network';
|
||||
import { renderPresetIcon } from './presetIcon';
|
||||
} from '@/music-network';
|
||||
import { renderPresetIcon } from '@/components/settings/musicNetwork/presetIcon';
|
||||
|
||||
/**
|
||||
* "Add a service" list, driven entirely by the preset registry. Token-poll
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import { SettingsGroup } from '../SettingsGroup';
|
||||
import type { Account } from '../../../music-network';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import type { Account } from '@/music-network';
|
||||
|
||||
/**
|
||||
* Picks the single enrichment primary (love / similar / stats source). Only
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import type { Account } from '../../../music-network';
|
||||
import type { Account } from '@/music-network';
|
||||
|
||||
/**
|
||||
* Shown when a Maloja account is connected AND Last.fm scrobbling is enabled —
|
||||
+10
-10
@@ -1,22 +1,22 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Share2 } from 'lucide-react';
|
||||
import SettingsSubSection from '../../SettingsSubSection';
|
||||
import { SettingsGroup } from '../SettingsGroup';
|
||||
import { showToast } from '../../../utils/ui/toast';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import SettingsSubSection from '@/features/settings/components/SettingsSubSection';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import {
|
||||
errorI18nKey,
|
||||
getMusicNetworkRuntime,
|
||||
isMusicNetworkError,
|
||||
type PresetId,
|
||||
type UserProfile,
|
||||
} from '../../../music-network';
|
||||
import { useMusicNetworkState } from './useMusicNetworkState';
|
||||
import { ScrobbleDestinationCard } from './ScrobbleDestinationCard';
|
||||
import { EnrichmentPrimarySelect } from './EnrichmentPrimarySelect';
|
||||
import { ConnectProviderForm } from './ConnectProviderForm';
|
||||
import { MalojaProxyWarning } from './MalojaProxyWarning';
|
||||
} from '@/music-network';
|
||||
import { useMusicNetworkState } from '@/features/settings/components/musicNetwork/useMusicNetworkState';
|
||||
import { ScrobbleDestinationCard } from '@/features/settings/components/musicNetwork/ScrobbleDestinationCard';
|
||||
import { EnrichmentPrimarySelect } from '@/features/settings/components/musicNetwork/EnrichmentPrimarySelect';
|
||||
import { ConnectProviderForm } from '@/features/settings/components/musicNetwork/ConnectProviderForm';
|
||||
import { MalojaProxyWarning } from '@/features/settings/components/musicNetwork/MalojaProxyWarning';
|
||||
|
||||
/**
|
||||
* Integrations UI for the Music Network framework — replaces the old Last.fm
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getPreset, type Account, type UserProfile } from '../../../music-network';
|
||||
import { renderPresetIcon } from './presetIcon';
|
||||
import { getPreset, type Account, type UserProfile } from '@/music-network';
|
||||
import { renderPresetIcon } from '@/components/settings/musicNetwork/presetIcon';
|
||||
|
||||
/**
|
||||
* One connected account as a single self-contained block: header (icon, label,
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { getPreset, type Account } from '../../../music-network';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { getPreset, type Account } from '@/music-network';
|
||||
|
||||
/**
|
||||
* Reactive view of the persisted Music Network state for the Integrations UI.
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { matchScore } from './settingsTabs';
|
||||
import { searchSettings } from './settingsSearch';
|
||||
import { matchScore } from '@/features/settings/components/settingsTabs';
|
||||
import { searchSettings } from '@/features/settings/components/settingsSearch';
|
||||
|
||||
const t = vi.fn((key: string) => {
|
||||
const labels: Record<string, string> = {
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import type { TFunction } from 'i18next';
|
||||
import { GLOBAL_SHORTCUT_ACTIONS, IN_APP_SHORTCUT_ACTIONS } from '../../config/shortcutActions';
|
||||
import { SETTINGS_INDEX, matchScore, type Tab } from './settingsTabs';
|
||||
import { GLOBAL_SHORTCUT_ACTIONS, IN_APP_SHORTCUT_ACTIONS } from '@/config/shortcutActions';
|
||||
import { SETTINGS_INDEX, matchScore, type Tab } from '@/features/settings/components/settingsTabs';
|
||||
|
||||
export type SettingsSearchHit = {
|
||||
tab: Tab;
|
||||
+5
-5
@@ -2,15 +2,15 @@ import React, { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { ndUpdateUser, type NdUser } from '../../../api/navidromeAdmin';
|
||||
import { showToast } from '../../../utils/ui/toast';
|
||||
import { ndUpdateUser, type NdUser } from '@/api/navidromeAdmin';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import {
|
||||
copyTextToClipboard,
|
||||
encodeServerMagicString,
|
||||
magicPayloadAddressFields,
|
||||
} from '../../../utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '../../../utils/server/serverDisplayName';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
} from '@/utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
interface Props {
|
||||
user: NdUser;
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Shield, Trash2, User, Wand2 } from 'lucide-react';
|
||||
import type { i18n as I18nType, TFunction } from 'i18next';
|
||||
import type { NdLibrary, NdUser } from '../../../api/navidromeAdmin';
|
||||
import { formatLastSeen } from '../../../utils/componentHelpers/userMgmtHelpers';
|
||||
import type { NdLibrary, NdUser } from '@/api/navidromeAdmin';
|
||||
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
||||
|
||||
interface Props {
|
||||
user: NdUser;
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
ndUpdateUser,
|
||||
type NdLibrary,
|
||||
type NdUser,
|
||||
} from '../api/navidromeAdmin';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
} from '@/api/navidromeAdmin';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import {
|
||||
copyTextToClipboard,
|
||||
encodeServerMagicString,
|
||||
} from '../utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '../utils/server/serverDisplayName';
|
||||
import type { UserFormState } from '../components/settings/UserForm';
|
||||
} from '@/utils/server/serverMagicString';
|
||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
||||
import type { UserFormState } from '@/features/settings/components/UserForm';
|
||||
|
||||
interface UseUserMgmtActionsArgs {
|
||||
serverUrl: string;
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ndListUsers,
|
||||
type NdLibrary,
|
||||
type NdUser,
|
||||
} from '../api/navidromeAdmin';
|
||||
} from '@/api/navidromeAdmin';
|
||||
|
||||
interface UseUserMgmtDataResult {
|
||||
users: NdUser[];
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Settings feature — the Settings page (lazy via the deep path `pages/Settings`)
|
||||
* and all its tabs/sections (servers, audio, input, library, lyrics, storage,
|
||||
* personalisation, integrations, system, the theme management UI, music-network
|
||||
* config, user management), plus the settings layout primitives.
|
||||
*
|
||||
* Cross-cutting state this feature consumes but does NOT own: `authStore` and its
|
||||
* `auth*SettingsActions` slices, the theme stores + theme infra utils, the
|
||||
* library-index sync hook, and `settingsCredits` — all live in the core/global
|
||||
* layer.
|
||||
*
|
||||
* Public surface (consumed by other features / app shell):
|
||||
*/
|
||||
export { default as SettingsSubSection } from './components/SettingsSubSection';
|
||||
export { PlaybackRateControls } from './components/audio/PlaybackRateBlock';
|
||||
export { CustomHttpHeadersEditor } from './components/CustomHttpHeadersEditor';
|
||||
@@ -4,24 +4,24 @@ import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
AudioLines, HardDrive, Info, Keyboard, LayoutGrid, Music2, Paintbrush, Palette, Search, Server, Sparkles, Users, X,
|
||||
} from 'lucide-react';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { IS_MACOS } from '../utils/platform';
|
||||
import { AppearanceTab } from '../components/settings/AppearanceTab';
|
||||
import { ThemesTab } from '../components/settings/ThemesTab';
|
||||
import { AudioTab } from '../components/settings/AudioTab';
|
||||
import { InputTab } from '../components/settings/InputTab';
|
||||
import { IntegrationsTab } from '../components/settings/IntegrationsTab';
|
||||
import { LibraryTab } from '../components/settings/LibraryTab';
|
||||
import { LyricsTab } from '../components/settings/LyricsTab';
|
||||
import { PersonalisationTab } from '../components/settings/PersonalisationTab';
|
||||
import { ServersTab } from '../components/settings/ServersTab';
|
||||
import { StorageTab } from '../components/settings/StorageTab';
|
||||
import { SystemTab } from '../components/settings/SystemTab';
|
||||
import { searchSettings, type SettingsSearchHit } from '../components/settings/settingsSearch';
|
||||
import { type Tab, resolveTab } from '../components/settings/settingsTabs';
|
||||
import { UserManagementSection } from '../components/settings/UserManagementSection';
|
||||
import { ndLogin } from '../api/navidromeAdmin';
|
||||
import { type ServerMagicPayload } from '../utils/server/serverMagicString';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { IS_MACOS } from '@/utils/platform';
|
||||
import { AppearanceTab } from '@/features/settings/components/AppearanceTab';
|
||||
import { ThemesTab } from '@/features/settings/components/ThemesTab';
|
||||
import { AudioTab } from '@/features/settings/components/AudioTab';
|
||||
import { InputTab } from '@/features/settings/components/InputTab';
|
||||
import { IntegrationsTab } from '@/features/settings/components/IntegrationsTab';
|
||||
import { LibraryTab } from '@/features/settings/components/LibraryTab';
|
||||
import { LyricsTab } from '@/features/settings/components/LyricsTab';
|
||||
import { PersonalisationTab } from '@/features/settings/components/PersonalisationTab';
|
||||
import { ServersTab } from '@/features/settings/components/ServersTab';
|
||||
import { StorageTab } from '@/features/settings/components/StorageTab';
|
||||
import { SystemTab } from '@/features/settings/components/SystemTab';
|
||||
import { searchSettings, type SettingsSearchHit } from '@/features/settings/components/settingsSearch';
|
||||
import { type Tab, resolveTab } from '@/features/settings/components/settingsTabs';
|
||||
import { UserManagementSection } from '@/features/settings/components/UserManagementSection';
|
||||
import { ndLogin } from '@/api/navidromeAdmin';
|
||||
import { type ServerMagicPayload } from '@/utils/server/serverMagicString';
|
||||
|
||||
|
||||
export default function Settings() {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { save, open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { writeFile, readTextFile } from '@tauri-apps/plugin-fs';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { version as appVersion } from '../../../package.json';
|
||||
import { version as appVersion } from '@/../package.json';
|
||||
|
||||
const BACKUP_VERSION = 1;
|
||||
export type ImportedBackupKind = 'config' | 'databases' | 'full';
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { Wifi, WifiOff, Eye, EyeOff, Server, Globe } from 'lucide-react';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import type { CustomHeaderEntry, CustomHeadersApplyTo, ServerProfile } from '../store/authStoreTypes';
|
||||
import { pingWithCredentialsForProfile, scheduleInstantMixProbeForServer } from '../api/subsonic';
|
||||
import { CustomHttpHeadersEditor } from '../components/settings/CustomHttpHeadersEditor';
|
||||
import { CustomHttpHeadersEditor } from '@/features/settings';
|
||||
import {
|
||||
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
|
||||
serverCustomHeadersFromForm,
|
||||
|
||||
Reference in New Issue
Block a user