24 Commits

Author SHA1 Message Date
245620d4a7 docs: add CHANGELOG and commit rewriting script 2026-01-25 01:30:56 +00:00
b426cc05ff fix: docker startup issue, add GitHub release workflow and changelog config 2026-01-25 01:29:13 +00:00
699a27b0b9 Use git commit SHA for versioning, fix audio playback resume, remove all streak localStorage code 2026-01-25 01:22:54 +00:00
b5fc05382e Fix menubar, add lazy loading, improve image quality, limit search results, filter browse artists 2026-01-25 01:16:17 +00:00
43a51b165b Add pagination to library/songs and remove listening streaks
Library/Songs improvements:
- Added pagination with 50 songs per page
- Added Previous/Next navigation buttons
- Updated header to show current page range (e.g., 'Showing 1-50 of 247 songs')
- Track numbers now reflect global position across all pages
- Page resets to 1 when search/sort filters change
- Imported ChevronLeft and ChevronRight icons for navigation

Listening Streak removal:
- Removed CompactListeningStreak component from home page
- Removed ListeningStreakCard component from history page
- Removed listening streak imports from both pages
- Cleaned up empty comment sections

The songs page now handles large libraries more efficiently with pagination,
and the UI is cleaner without the listening streak cards.
2026-01-25 00:46:15 +00:00
c64e40d56b Organize documentation: move markdown files to docs/ folder
- Created docs/ directory for documentation
- Moved KEYBOARD_SHORTCUTS.md → docs/
- Moved DOCKER.md → docs/
- Moved SPOTLIGHT_SEARCH.md → docs/
- Moved themes.md → docs/
- Moved OFFLINE_DOWNLOADS.md → docs/
- Kept README.md in root for repository visibility
2026-01-25 00:39:08 +00:00
6d1e4fb063 Simplify service worker: remove offline download functionality
- Removed all audio download and caching logic
- Removed offline-song URL mapping
- Removed metadata cache (META_CACHE)
- Removed audio cache (AUDIO_CACHE)
- Removed message handlers for DOWNLOAD_ALBUM, DOWNLOAD_SONG, DOWNLOAD_QUEUE
- Removed message handlers for offline status checks and deletion
- Updated VERSION to v3 to force cache cleanup on next load
- Kept only app shell and image caching for faster loading
- Simplified to 120 lines (from 681 lines - 82% reduction)
2026-01-25 00:37:42 +00:00
eb56096992 Remove all offline download and caching functionality
- Deleted all offline-related component files:
  - EnhancedOfflineManager.tsx
  - OfflineIndicator.tsx
  - OfflineLibrarySync.tsx
  - OfflineManagement.tsx
  - OfflineNavidromeContext.tsx
  - OfflineNavidromeProvider.tsx
  - OfflineStatusIndicator.tsx
- Deleted all offline-related hooks:
  - use-offline-audio-player.ts
  - use-offline-downloads.ts
  - use-offline-library-sync.ts
  - use-offline-library.ts
- Updated components to remove offline functionality:
  - RootLayoutClient: Removed OfflineNavidromeProvider, using only NavidromeProvider
  - SongRecommendations: Removed offline data fetching logic
  - album-artwork: Removed OfflineIndicator usage
  - WhatsNewPopup: Updated changelog to reflect offline removal
- Updated pages:
  - album/[id]/page: Removed all OfflineIndicator components from album and song displays
  - page.tsx: Removed OfflineStatusIndicator and offline empty state message
  - settings/page: Removed EnhancedOfflineManager and OfflineManagement sections
- Simplified use-progressive-album-loading hook to only use online API
- Fixed resizable component imports for react-resizable-panels 4.5.1 API changes
2026-01-25 00:35:58 +00:00
df248497ae chore: Update version to 2026.01.24 and add changelog for January 2026 release 2026-01-25 00:16:58 +00:00
7b036d8b6c Update pnpm-lock.yaml to match new overrides configuration 2026-01-25 00:14:02 +00:00
a0051576c6 Remove PostHog analytics and update dependencies to latest minor versions
- Remove posthog-js and posthog-node dependencies
- Delete PostHogProvider component and lib/posthog.ts
- Remove PostHog configuration from environment files
- Remove PostHog rewrites from next.config.mjs
- Update all npm subdependencies to latest minor versions
- Update @types/react and @types/react-dom overrides to 19.2.9 and 19.2.3
2026-01-25 00:12:04 +00:00
52a00ca899 feat: Improve SortableQueueItem component with enhanced click handling and styling 2025-08-16 17:07:18 -05:00
7710bf3cc9 feat: Add keyboard shortcuts and queue management features
- Implement global keyboard shortcuts for playback controls, volume adjustments, and navigation.
- Introduce drag-and-drop functionality for queue reordering with visual feedback.
- Add context menus for tracks, albums, and artists with quick action options.
- Develop Spotlight Search feature with Last.fm integration for enhanced music discovery.
- Create GlobalSearchProvider for managing search state and keyboard shortcuts.
- Ensure accessibility and keyboard navigation support across all new features.
2025-08-12 13:09:33 +00:00
9427a2a237 feat: Add ListeningStreakCard component for tracking listening streaks
feat: Implement InfiniteScroll component for loading more items on scroll

feat: Create useListeningStreak hook to manage listening streak data and statistics

feat: Develop useProgressiveAlbumLoading hook for progressive loading of albums

feat: Implement background sync service worker for automatic data synchronization
2025-08-11 14:50:57 +00:00
1f6ebf18a3 feat: Move service worker registration to a dedicated component for improved client-side handling 2025-08-11 12:35:50 +00:00
c999c43288 feat: Refactor service worker registration and enhance offline download manager with client-side checks 2025-08-11 12:31:08 +00:00
a352021dbc feat: Enhance OfflineManagement component with improved card styling and layout 2025-08-11 05:05:00 +00:00
147602ad8c feat: Implement Auto-Tagging Settings and MusicBrainz integration
- Added AutoTaggingSettings component for configuring auto-tagging preferences.
- Integrated localStorage for saving user preferences and options.
- Developed useAutoTagging hook for fetching and applying metadata from MusicBrainz.
- Created MusicBrainz API client for searching and retrieving music metadata.
- Enhanced metadata structure with additional fields for tracks and albums.
- Implemented rate-limiting for MusicBrainz API requests.
- Added UI components for user interaction and feedback during the tagging process.
2025-08-10 15:02:49 +00:00
18f0811787 feat: Enhance audio settings with ReplayGain, crossfade, and equalizer presets; add AudioSettingsDialog component 2025-08-10 02:57:55 +00:00
7a1c7e1eae feat: Update cover art retrieval to use higher resolution images and enhance download manager with new features 2025-08-10 02:06:39 +00:00
7e6a28e4f4 feat: Enhance UI with Framer Motion animations for album artwork and artist icons 2025-08-08 21:38:58 +00:00
36c1edd01e feat: Add page transition animations and notification settings for audio playback 2025-08-08 21:29:01 +00:00
3839a1be2d feat: Implement offline library synchronization with IndexedDB
- Added `useOfflineLibrarySync` hook for managing offline library sync operations.
- Created `OfflineLibrarySync` component for UI integration.
- Developed `offlineLibraryDB` for IndexedDB interactions, including storing and retrieving albums, artists, songs, and playlists.
- Implemented sync operations for starred items, playlists, and scrobbling.
- Added auto-sync functionality when coming back online.
- Included metadata management for sync settings and statistics.
- Enhanced error handling and user feedback through toasts.
2025-08-08 20:04:06 +00:00
0a0feb3748 feat: Implement offline library management with IndexedDB support
- Added `useOfflineLibrary` hook for managing offline library state and synchronization.
- Created `OfflineLibraryManager` class for handling IndexedDB operations and syncing with Navidrome API.
- Implemented methods for retrieving and storing albums, artists, songs, and playlists.
- Added support for offline favorites management (star/unstar).
- Implemented playlist creation, updating, and deletion functionalities.
- Added search functionality for offline data.
- Created a manifest file for PWA support with icons and shortcuts.
- Added service worker file for caching and offline capabilities.
2025-08-07 22:07:53 +00:00
13 changed files with 61 additions and 158 deletions

View File

@@ -22,18 +22,3 @@ build
.turbo .turbo
.github .github
4xnored.png 4xnored.png
# Documentation and non-runtime files
docs/
CHANGELOG.md
cliff.toml
*.md
!README.md
# Docker compose files
docker-compose*.yml
Dockerfile
# Git and backup files
.git*
backup-*

View File

@@ -1 +1 @@
NEXT_PUBLIC_COMMIT_SHA=477b172 NEXT_PUBLIC_COMMIT_SHA=b5fc053

View File

@@ -3,8 +3,7 @@ name: GitHub Release
on: on:
push: push:
tags: tags:
- '[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]' - 'v*'
permissions: permissions:
contents: write contents: write

View File

@@ -1,9 +1,13 @@
name: Docker Image (Nightly) name: Development Docker Image (Nightly)
on: on:
schedule: schedule:
# Run every night at 5:00 UTC # Run every night at 5:00 UTC
- cron: '0 5 * * *' - cron: '0 5 * * *'
push:
branches:
- dev
workflow_dispatch: # Allow manual triggering
env: env:
REGISTRY: docker.io REGISTRY: docker.io
@@ -85,6 +89,7 @@ jobs:
tags: | tags: |
type=raw,value=nightly type=raw,value=nightly
type=raw,value=dev-latest type=raw,value=dev-latest
type=sha,prefix=dev-
labels: | labels: |
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }} org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}
org.opencontainers.image.licenses=MIT org.opencontainers.image.licenses=MIT

View File

@@ -68,7 +68,7 @@ jobs:
with: with:
context: . context: .
push: true push: true
tags: ${{ steps.meta.outputs.tags || 'latest' }} tags: ${{ steps.meta.outputs.tags }}
labels: | labels: |
${{ steps.meta.outputs.labels }} ${{ steps.meta.outputs.labels }}
org.opencontainers.image.description=$(cat README.md | head -20 | tr '\n' ' ') org.opencontainers.image.description=$(cat README.md | head -20 | tr '\n' ' ')
@@ -81,3 +81,10 @@ jobs:
cache-to: | cache-to: |
type=gha,mode=max,scope=deps-only type=gha,mode=max,scope=deps-only
# - name: Docker Hub Description
# uses: peter-evans/dockerhub-description@v4
# with:
# username: ${{ vars.DOCKERHUB_USERNAME }}
# password: ${{ secrets.DOCKERHUB_TOKEN }}
# repository: sillyangel/mice

View File

@@ -1,50 +1,45 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [unreleased]
## [Unreleased]
### Features
- Fix menubar, add lazy loading, improve image quality, limit search results, filter browse artists
- Add pagination to library/songs and remove listening streaks
- Improve SortableQueueItem component with enhanced click handling and styling
- Add keyboard shortcuts and queue management features
- Add ListeningStreakCard component for tracking listening streaks
- Enhance OfflineManagement component with improved card styling and layout
- Implement Auto-Tagging Settings and MusicBrainz integration
- Enhance audio settings with ReplayGain, crossfade, and equalizer presets; add AudioSettingsDialog component
- Update cover art retrieval to use higher resolution images and enhance download manager with new features
- Enhance UI with Framer Motion animations for album artwork and artist icons
- Add page transition animations and notification settings for audio playback
- Implement offline library synchronization with IndexedDB
- Implement offline library management with IndexedDB support
### Bug Fixes ### Bug Fixes
- Use git commit SHA for versioning, fix audio playback resume, remove all streak localStorage code - Use git commit SHA for versioning, fix audio playback resume, remove all streak localStorage code
- Docker startup issue, add GitHub release workflow and changelog config - Docker startup issue, add GitHub release workflow and changelog config
### Documentation ### Refactoring
- Simplify service worker by removing offline download functionality
- Add CHANGELOG and commit rewriting script - Remove all offline download and caching functionality
### Features
- Implement offline library management with IndexedDB support
- Implement offline library synchronization with IndexedDB
- Add page transition animations and notification settings for audio playback
- Enhance UI with Framer Motion animations for album artwork and artist icons
- Update cover art retrieval to use higher resolution images and enhance download manager with new features
- Enhance audio settings with ReplayGain, crossfade, and equalizer presets; add AudioSettingsDialog component
- Implement Auto-Tagging Settings and MusicBrainz integration
- Enhance OfflineManagement component with improved card styling and layout
- Refactor service worker registration and enhance offline download manager with client-side checks
- Move service worker registration to a dedicated component for improved client-side handling - Move service worker registration to a dedicated component for improved client-side handling
- Add ListeningStreakCard component for tracking listening streaks - Refactor service worker registration and enhance offline download manager with client-side checks
- Add keyboard shortcuts and queue management features
- Improve SortableQueueItem component with enhanced click handling and styling
- Add pagination to library/songs and remove listening streaks
- Fix menubar, add lazy loading, improve image quality, limit search results, filter browse artists
### Miscellaneous ### Miscellaneous
- Organize documentation: move markdown files to docs/ folder
- C
- Merge pull request #39 from sillyangel/dependabot/npm_and_yarn/dev-99ea30e4b7
- Remove PostHog analytics and update dependencies to latest minor versions
- Update pnpm-lock.yaml to match new overrides configuration
- Update version to 2026.01.24 and add changelog for January 2026 release - Update version to 2026.01.24 and add changelog for January 2026 release
- Organize documentation - move markdown files to docs/ folder - Update pnpm-lock.yaml to match new overrides configuration
- Remove PostHog analytics and update dependencies to latest minor versions
### Refactoring - Bump the dev group across 1 directory with 2 updates
- Merge pull request #39 from sillyangel/dependabot/npm_and_yarn/dev-99ea30e4b7
- Remove all offline download and caching functionality
- Simplify service worker by removing offline download functionality
### Styling ### Styling
- Update README formatting and improve content clarity - Update README formatting and improve content clarity
## [2026.01.24] - 2026-01-24
Previous release before changelog tracking.

View File

@@ -18,7 +18,7 @@ This is a "Modern" Navidrome (or Subsonic) client built with [Next.js](https://n
- **Search** - Find music across your entire library - **Search** - Find music across your entire library
- **Audio Player** with queue management - **Audio Player** with queue management
- **Scrobbling** - Track your listening history - **Scrobbling** - Track your listening history
<!-- - **Playlist Management** - Create and manage playlists --> - **Playlist Management** - Create and manage playlists
### Preview ### Preview
![preview](https://github.com/sillyangel/mice/blob/main/public/home-preview.png?raw=true) ![preview](https://github.com/sillyangel/mice/blob/main/public/home-preview.png?raw=true)
@@ -34,8 +34,8 @@ This is a "Modern" Navidrome (or Subsonic) client built with [Next.js](https://n
1. **Clone and install the required dependencies** 1. **Clone and install the required dependencies**
```bash ```bash
git clone https://github.com/sillyangel/mice.git git clone https://github.com/sillyangel/project-still.git
cd mice/ cd project-still/
pnpm install pnpm install
# or npm # or npm
@@ -113,7 +113,7 @@ docker run -p 3000:3000 \
sillyangel/mice:latest sillyangel/mice:latest
``` ```
**For detailed Docker configuration, environment variables, troubleshooting, and advanced setups, see [DOCKER.md](./docs/DOCKER.md)** **For detailed Docker configuration, environment variables, troubleshooting, and advanced setups, see [DOCKER.md](./DOCKER.md)**
## Tech Stack ## Tech Stack

View File

@@ -4,7 +4,7 @@ import { useParams } from 'next/navigation';
import Image from 'next/image'; import Image from 'next/image';
import { Album, Song } from '@/lib/navidrome'; import { Album, Song } from '@/lib/navidrome';
import { useNavidrome } from '@/app/components/NavidromeContext'; import { useNavidrome } from '@/app/components/NavidromeContext';
import { Play, Heart, Shuffle } from 'lucide-react'; import { Play, Heart } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import Link from 'next/link'; import Link from 'next/link';
import { useAudioPlayer } from '@/app/components/AudioPlayerContext' import { useAudioPlayer } from '@/app/components/AudioPlayerContext'
@@ -111,19 +111,6 @@ export default function AlbumPage() {
} }
}; };
const handleShuffleAlbum = async (): Promise<void> => {
if (!album || !tracklist.length) return;
try {
// Shuffle the tracklist
const shuffled = [...tracklist].sort(() => Math.random() - 0.5);
// Play the first shuffled track
await playAlbumFromTrack(album.id, shuffled[0].id);
} catch (error) {
console.error('Failed to shuffle album:', error);
}
};
const isCurrentlyPlaying = (song: Song): boolean => { const isCurrentlyPlaying = (song: Song): boolean => {
return currentTrack?.id === song.id; return currentTrack?.id === song.id;
}; };
@@ -137,13 +124,13 @@ export default function AlbumPage() {
// Dynamic cover art URLs based on image size // Dynamic cover art URLs based on image size
const getMobileCoverArtUrl = () => { const getMobileCoverArtUrl = () => {
return album.coverArt && api return album.coverArt && api
? api.getCoverArtUrl(album.coverArt, 300) ? api.getCoverArtUrl(album.coverArt, 600)
: '/default-user.jpg'; : '/default-user.jpg';
}; };
const getDesktopCoverArtUrl = () => { const getDesktopCoverArtUrl = () => {
return album.coverArt && api return album.coverArt && api
? api.getCoverArtUrl(album.coverArt, 300) ? api.getCoverArtUrl(album.coverArt, 600)
: '/default-user.jpg'; : '/default-user.jpg';
}; };
@@ -159,8 +146,8 @@ export default function AlbumPage() {
<Image <Image
src={getMobileCoverArtUrl()} src={getMobileCoverArtUrl()}
alt={album.name} alt={album.name}
width={300} width={600}
height={300} height={600}
className="rounded-md shadow-lg" className="rounded-md shadow-lg"
/> />
</div> </div>
@@ -186,14 +173,6 @@ export default function AlbumPage() {
> >
<Play className="w-6 h-6" /> <Play className="w-6 h-6" />
</Button> </Button>
<Button
variant="outline"
className="w-12 h-12 rounded-full p-0"
onClick={handleShuffleAlbum}
title="Shuffle Album"
>
<Shuffle className="w-5 h-5" />
</Button>
</div> </div>
</div> </div>
</div> </div>
@@ -203,8 +182,8 @@ export default function AlbumPage() {
<Image <Image
src={getDesktopCoverArtUrl()} src={getDesktopCoverArtUrl()}
alt={album.name} alt={album.name}
width={300} width={600}
height={300} height={600}
className="rounded-md" className="rounded-md"
/> />
<div className="space-y-2"> <div className="space-y-2">
@@ -221,13 +200,8 @@ export default function AlbumPage() {
{/* Controls row */} {/* Controls row */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Button className="px-5" onClick={() => playAlbum(album.id)}> <Button className="px-5" onClick={() => playAlbum(album.id)}>
<Play className="w-4 h-4 mr-2" />
Play Play
</Button> </Button>
<Button variant="outline" className="px-5" onClick={handleShuffleAlbum}>
<Shuffle className="w-4 h-4 mr-2" />
Shuffle
</Button>
</div> </div>
{/* Album info */} {/* Album info */}

View File

@@ -388,11 +388,6 @@ export const AudioPlayer: React.FC = () => {
// Auto-play only if the track has the autoPlay flag and audio is initialized // Auto-play only if the track has the autoPlay flag and audio is initialized
if (currentTrack.autoPlay && (!isMobile || audioInitialized)) { if (currentTrack.autoPlay && (!isMobile || audioInitialized)) {
// Start crossfade fade-in if enabled
if (audioSettings.crossfadeDuration > 0 && audioEffects) {
audioEffects.startCrossfade();
}
// Add a small delay for iOS compatibility // Add a small delay for iOS compatibility
const playPromise = isMobile ? const playPromise = isMobile ?
new Promise(resolve => setTimeout(resolve, 100)).then(() => audioCurrent.play()) : new Promise(resolve => setTimeout(resolve, 100)).then(() => audioCurrent.play()) :
@@ -415,7 +410,7 @@ export const AudioPlayer: React.FC = () => {
setIsPlaying(false); setIsPlaying(false);
} }
} }
}, [currentTrack, onTrackStart, onTrackPlay, isMobile, audioInitialized, audioEffects, audioSettings.gaplessPlayback, audioSettings.replayGainEnabled, audioSettings.crossfadeDuration, queue]); }, [currentTrack, onTrackStart, onTrackPlay, isMobile, audioInitialized, audioEffects, audioSettings.gaplessPlayback, audioSettings.replayGainEnabled, queue]);
useEffect(() => { useEffect(() => {
const audioCurrent = audioRef.current; const audioCurrent = audioRef.current;

View File

@@ -18,7 +18,6 @@ import { useNavidromeConfig } from '@/app/components/NavidromeConfigContext';
import { useTheme } from '@/app/components/ThemeProvider'; import { useTheme } from '@/app/components/ThemeProvider';
import { useToast } from '@/hooks/use-toast'; import { useToast } from '@/hooks/use-toast';
import { FaServer, FaUser, FaLock, FaCheck, FaTimes, FaPalette, FaLastfm } from 'react-icons/fa'; import { FaServer, FaUser, FaLock, FaCheck, FaTimes, FaPalette, FaLastfm } from 'react-icons/fa';
import type { SidebarItem, SidebarLayoutSettings } from '@/hooks/use-sidebar-layout';
export function LoginForm({ export function LoginForm({
className, className,
@@ -37,7 +36,6 @@ export function LoginForm({
}); });
const [isTesting, setIsTesting] = useState(false); const [isTesting, setIsTesting] = useState(false);
const [hasError, setHasError] = useState(false);
// Settings for step 2 // Settings for step 2
const [scrobblingEnabled, setScrobblingEnabled] = useState(() => { const [scrobblingEnabled, setScrobblingEnabled] = useState(() => {
@@ -47,8 +45,7 @@ export function LoginForm({
return true; return true;
}); });
// Sidebar settings with new defaults // New settings - removed sidebar and standalone lastfm options
const [sidebarShortcuts, setSidebarShortcuts] = useState<'albums' | 'playlists' | 'both'>('playlists');
// Check if Navidrome is configured via environment variables // Check if Navidrome is configured via environment variables
const hasEnvConfig = React.useMemo(() => { const hasEnvConfig = React.useMemo(() => {
@@ -122,7 +119,6 @@ export function LoginForm({
e.preventDefault(); e.preventDefault();
if (!formData.serverUrl || !formData.username || !formData.password) { if (!formData.serverUrl || !formData.username || !formData.password) {
setHasError(true);
toast({ toast({
title: "Missing Information", title: "Missing Information",
description: "Please fill in all fields before proceeding.", description: "Please fill in all fields before proceeding.",
@@ -132,7 +128,6 @@ export function LoginForm({
} }
setIsTesting(true); setIsTesting(true);
setHasError(false);
try { try {
// Strip trailing slash from server URL before testing // Strip trailing slash from server URL before testing
const cleanServerUrl = formData.serverUrl.replace(/\/+$/, ''); const cleanServerUrl = formData.serverUrl.replace(/\/+$/, '');
@@ -159,7 +154,6 @@ export function LoginForm({
// Move to settings step // Move to settings step
setStep('settings'); setStep('settings');
} else { } else {
setHasError(true);
toast({ toast({
title: "Connection Failed", title: "Connection Failed",
description: "Could not connect to the server. Please check your settings.", description: "Could not connect to the server. Please check your settings.",
@@ -167,7 +161,6 @@ export function LoginForm({
}); });
} }
} catch (error) { } catch (error) {
setHasError(true);
toast({ toast({
title: "Connection Error", title: "Connection Error",
description: "An error occurred while testing the connection.", description: "An error occurred while testing the connection.",
@@ -182,30 +175,6 @@ export function LoginForm({
// Save all settings // Save all settings
localStorage.setItem('lastfm-scrobbling-enabled', scrobblingEnabled.toString()); localStorage.setItem('lastfm-scrobbling-enabled', scrobblingEnabled.toString());
// Save sidebar settings with new defaults
const defaultSidebarItems: SidebarItem[] = [
{ id: 'home', label: 'Home', visible: true, icon: 'home', href: '/' },
{ id: 'queue', label: 'Queue', visible: true, icon: 'queue', href: '/queue' },
{ id: 'artists', label: 'Artists', visible: true, icon: 'artists', href: '/library/artists' },
{ id: 'albums', label: 'Albums', visible: true, icon: 'albums', href: '/library/albums' },
{ id: 'playlists', label: 'Playlists', visible: true, icon: 'playlists', href: '/library/playlists' },
{ id: 'favorites', label: 'Favorites', visible: true, icon: 'favorites', href: '/favorites' },
{ id: 'settings', label: 'Settings', visible: true, icon: 'settings', href: '/settings' },
// Hidden by default
{ id: 'search', label: 'Search', visible: false, icon: 'search', href: '/search' },
{ id: 'radio', label: 'Radio', visible: false, icon: 'radio', href: '/radio' },
{ id: 'browse', label: 'Browse', visible: false, icon: 'browse', href: '/browse' },
{ id: 'songs', label: 'Songs', visible: false, icon: 'songs', href: '/library/songs' },
{ id: 'history', label: 'History', visible: false, icon: 'history', href: '/history' },
];
const sidebarSettings: SidebarLayoutSettings = {
items: defaultSidebarItems,
shortcuts: sidebarShortcuts,
showIcons: true,
};
localStorage.setItem('sidebar-layout-settings', JSON.stringify(sidebarSettings));
// Mark onboarding as complete // Mark onboarding as complete
localStorage.setItem('onboarding-completed', '1.1.0'); localStorage.setItem('onboarding-completed', '1.1.0');
@@ -291,7 +260,6 @@ export function LoginForm({
<SelectValue placeholder="Select a theme" /> <SelectValue placeholder="Select a theme" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="blue">Blue</SelectItem> <SelectItem value="blue">Blue</SelectItem>
<SelectItem value="violet">Violet</SelectItem> <SelectItem value="violet">Violet</SelectItem>
<SelectItem value="red">Red</SelectItem> <SelectItem value="red">Red</SelectItem>
@@ -328,24 +296,6 @@ export function LoginForm({
</p> </p>
</div> </div>
{/* Sidebar Shortcuts */}
<div className="grid gap-3">
<Label>Sidebar Shortcuts</Label>
<Select value={sidebarShortcuts} onValueChange={(value: 'albums' | 'playlists' | 'both') => setSidebarShortcuts(value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="playlists">Playlists Only</SelectItem>
<SelectItem value="albums">Albums Only</SelectItem>
<SelectItem value="both">Both</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
Choose what shortcuts appear in your sidebar
</p>
</div>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Button onClick={handleFinishSetup} className="w-full"> <Button onClick={handleFinishSetup} className="w-full">
<FaCheck className="w-4 h-4 mr-2" /> <FaCheck className="w-4 h-4 mr-2" />
@@ -399,7 +349,6 @@ export function LoginForm({
placeholder="https://your-navidrome-server.com" placeholder="https://your-navidrome-server.com"
value={formData.serverUrl} value={formData.serverUrl}
onChange={(e) => handleInputChange('serverUrl', e.target.value)} onChange={(e) => handleInputChange('serverUrl', e.target.value)}
className={hasError ? "border-destructive focus-visible:ring-destructive" : ""}
required required
/> />
</div> </div>
@@ -414,7 +363,6 @@ export function LoginForm({
placeholder="your-username" placeholder="your-username"
value={formData.username} value={formData.username}
onChange={(e) => handleInputChange('username', e.target.value)} onChange={(e) => handleInputChange('username', e.target.value)}
className={hasError ? "border-destructive focus-visible:ring-destructive" : ""}
required required
/> />
</div> </div>
@@ -428,7 +376,6 @@ export function LoginForm({
type="password" type="password"
value={formData.password} value={formData.password}
onChange={(e) => handleInputChange('password', e.target.value)} onChange={(e) => handleInputChange('password', e.target.value)}
className={hasError ? "border-destructive focus-visible:ring-destructive" : ""}
required required
/> />
</div> </div>

View File

@@ -41,6 +41,6 @@ commit_parsers = [
] ]
protect_breaking_commits = false protect_breaking_commits = false
filter_commits = false filter_commits = false
tag_pattern = "[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]" tag_pattern = "v[0-9].*"
topo_order = false topo_order = false
sort_commits = "oldest" sort_commits = "oldest"

View File

@@ -16,7 +16,7 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport <ToastPrimitives.Viewport
ref={ref} ref={ref}
className={cn( className={cn(
"fixed top-0 z-[9999] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", "fixed top-0 z-100 flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className className
)} )}
{...props} {...props}

View File

@@ -126,7 +126,6 @@ export class AudioEffects {
public setCrossfadeTime(seconds: number) { public setCrossfadeTime(seconds: number) {
if (this.crossfadeGainNode) { if (this.crossfadeGainNode) {
const now = this.context.currentTime; const now = this.context.currentTime;
this.crossfadeGainNode.gain.cancelScheduledValues(now);
this.crossfadeGainNode.gain.setValueAtTime(1, now); this.crossfadeGainNode.gain.setValueAtTime(1, now);
this.crossfadeGainNode.gain.linearRampToValueAtTime(0, now + seconds); this.crossfadeGainNode.gain.linearRampToValueAtTime(0, now + seconds);
} }
@@ -134,10 +133,7 @@ export class AudioEffects {
public startCrossfade() { public startCrossfade() {
if (this.crossfadeGainNode) { if (this.crossfadeGainNode) {
const now = this.context.currentTime; this.crossfadeGainNode.gain.value = 1;
this.crossfadeGainNode.gain.cancelScheduledValues(now);
this.crossfadeGainNode.gain.setValueAtTime(0, now);
this.crossfadeGainNode.gain.linearRampToValueAtTime(1, now + 0.5); // Fast fade in
} }
} }