import { useCallback } from "react"; import { useRouter } from 'next/navigation'; import Image from "next/image"; import { Github, Mail, Menu as MenuIcon, X } from "lucide-react" import { UserProfile } from "@/app/components/UserProfile"; import { useGlobalSearch } from "./GlobalSearchProvider"; import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarLabel, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, } from "@/components/ui/menubar" import { useState, useEffect } from "react" import { Button } from "@/components/ui/button" import { Separator } from '@/components/ui/separator'; import { useNavidrome } from "./NavidromeContext"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useIsMobile } from "@/hooks/use-mobile" import Link from "next/link" import { Search, Home, List, Radio, Users, Disc, Music, Heart, Grid3X3, Clock, Settings, Circle } from "lucide-react"; interface MenuProps { toggleSidebar: () => void; isSidebarVisible: boolean; toggleStatusBar: () => void; isStatusBarVisible: boolean; } export function Menu({ toggleSidebar, isSidebarVisible, toggleStatusBar, isStatusBarVisible }: MenuProps) { const [isFullScreen, setIsFullScreen] = useState(false) const router = useRouter(); const [open, setOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const { isConnected } = useNavidrome(); const [isClient, setIsClient] = useState(false); const [navidromeUrl, setNavidromeUrl] = useState(null); const isMobile = useIsMobile(); const { openSpotlight } = useGlobalSearch(); // Navigation items for mobile menu const navigationItems = [ { href: '/', label: 'Home', icon: Home }, { href: '/search', label: 'Search', icon: Search }, { href: '/library/albums', label: 'Albums', icon: Disc }, { href: '/library/artists', label: 'Artists', icon: Users }, { href: '/library/songs', label: 'Songs', icon: Circle }, { href: '/library/playlists', label: 'Playlists', icon: Music }, { href: '/favorites', label: 'Favorites', icon: Heart }, { href: '/queue', label: 'Queue', icon: List }, { href: '/radio', label: 'Radio', icon: Radio }, { href: '/browse', label: 'Browse', icon: Grid3X3 }, { href: '/history', label: 'History', icon: Clock }, { href: '/settings', label: 'Settings', icon: Settings }, ]; // For this demo, we'll show connection status instead of user auth const connectionStatus = isConnected ? "Connected to Navidrome" : "Not connected"; const handleFullScreen = useCallback(() => { if (!isFullScreen) { document.documentElement.requestFullscreen() } else { document.exitFullscreen() } setIsFullScreen(!isFullScreen) }, [isFullScreen]) useEffect(() => { setIsClient(true); // Get Navidrome URL from localStorage const config = localStorage.getItem("navidrome-config"); if (config) { try { const { serverUrl } = JSON.parse(config); if (serverUrl) { // Remove protocol (http:// or https://) and trailing slash const prettyUrl = serverUrl.replace(/^https?:\/\//, "").replace(/\/$/, ""); setNavidromeUrl(prettyUrl); } else { setNavidromeUrl(null); } } catch { setNavidromeUrl(null); } } else { setNavidromeUrl(null); } }, []); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if ((event.metaKey || event.ctrlKey) && event.key === ',') { event.preventDefault(); router.push('/settings'); } if ((event.metaKey || event.ctrlKey) && event.key === 's') { event.preventDefault(); toggleSidebar(); } if ((event.metaKey || event.ctrlKey) && event.key === 'f') { event.preventDefault(); handleFullScreen(); } }; if (isClient) { window.addEventListener('keydown', handleKeyDown); } return () => { if (isClient) { window.removeEventListener('keydown', handleKeyDown); } }; }, [router, toggleSidebar, handleFullScreen, isClient]); return ( <>
{/* Mobile Top Bar - Simplified since navigation is now at bottom */} {isMobile ? ( // hey bear! // nothing null ) : ( /* Desktop Navigation */
mice setOpen(true)}>About Music router.push('/settings')}> Preferences ⌘, isClient && window.close()}> Quit Music ⌘Q File router.push('/library/playlists')}> View Playlists Playlist from Selection ⇧⌘N Smart Playlist ⌥⌘N Playlist Folder Genius Playlist Open Stream URL ⌘U Close Window ⌘W Library Update Cloud Library Update Genius Organize Library Export Library Import Playlist Export Playlist Show Duplicate Items Get Album Artwork Get Track Names Import ⌘O Burn Playlist to Disc Show in Finder ⇧⌘R{" "} Convert Page Setup Print ⌘P Edit Undo ⌘Z Redo ⇧⌘Z Cut ⌘X Copy ⌘C Paste ⌘V Select All ⌘A Deselect All ⇧⌘A Smart Dictation{" "} Emoji & Symbols{" "} View Show Playing Next Show Lyrics {isStatusBarVisible ? "Hide Status Bar" : "Show Status Bar"} {isSidebarVisible ? "Hide Sidebar" : "Show Sidebar"} ⌘S {isFullScreen ? "Exit Full Screen" : "Enter Full Screen"}
)} {/* User Profile and Search - Desktop only */} {!isMobile && (
)}
music

mice

{/* Version 1.0.0 */}

A Navidrome client built with Next.js and Shadcn/UI.

Server Status

{isConnected ? "Connected" : "Not connected"}

Navidrome URL {!isClient ? ( Loading... ) : navidromeUrl ? ( navidromeUrl ) : ( Auto-configured )}
Commit: {process.env.NEXT_PUBLIC_COMMIT_SHA || 'unknown'} Copyright © {new Date().getFullYear()} sillyangel
) }