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