import React, { useRef, useState, useEffect, useCallback } from 'react'; import { NavLink, Link, useLocation, useNavigate } from 'react-router-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { CaretDoubleDownIcon, CaretDoubleUpIcon, ArrowRightIcon, MagnifyingGlassIcon, GearSixIcon, ShuffleIcon, EnvelopeSimpleIcon, } from '@phosphor-icons/react'; import { version } from '../version'; import usePersistentState from '../hooks/usePersistentState'; import { KEY_SIDEBAR_STATE } from '../utils/LocalStorageManager'; import { useAchievements } from '../context/AchievementContext'; import { appIcons as ICON_MAP } from '../utils/appIcons'; import piml from 'piml'; const LuxeSidebar = ({ isOpen, toggleSidebar, toggleModal, setIsPaletteOpen, }) => { const [sidebarConfig, setSidebarConfig] = useState(null); useEffect(() => { const fetchSidebarConfig = async () => { try { const response = await fetch('/sidebar.piml'); if (response.ok) { const text = await response.text(); const parsed = piml.parse(text); setSidebarConfig(parsed.sidebar); } } catch (error) { console.error('Failed to load sidebar config:', error); } }; fetchSidebarConfig(); }, []); const [sidebarState, setSidebarState] = usePersistentState( KEY_SIDEBAR_STATE, { isMainOpen: true, isContentOpen: true, isSoftwareOpen: true, isAppsOpen: true, isStatusOpen: false, isExtrasOpen: false, }, ); const { unlockAchievement } = useAchievements(); const scrollRef = useRef(null); const location = useLocation(); const navigate = useNavigate(); const [showScrollGradient, setShowScrollGradient] = useState({ top: false, bottom: false, }); const checkScroll = useCallback(() => { if (scrollRef.current) { const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; const atBottom = Math.ceil(scrollTop + clientHeight) >= scrollHeight; const atTop = scrollTop <= 0; const isScrollable = scrollHeight > clientHeight; setShowScrollGradient({ top: isScrollable && !atTop, bottom: isScrollable && !atBottom, }); } }, []); useEffect(() => { checkScroll(); const scrollElement = scrollRef.current; if (scrollElement) { scrollElement.addEventListener('scroll', checkScroll); window.addEventListener('resize', checkScroll); } return () => { if (scrollElement) { scrollElement.removeEventListener('scroll', checkScroll); window.removeEventListener('resize', checkScroll); } }; }, [isOpen, sidebarState, checkScroll]); const toggleSection = (section) => { setSidebarState((prevState) => ({ ...prevState, [section]: !prevState[section], })); }; const getLinkClass = ({ isActive }) => `group flex items-center justify-between px-8 py-3 transition-all duration-500 border-l-2 ${ isActive ? 'border-[#8D4004] bg-[#8D4004]/5 text-[#1A1A1A]' : 'border-transparent text-[#1A1A1A]/60 hover:text-[#1A1A1A] hover:bg-[#1A1A1A]/5' }`; const SectionHeader = ({ id, label, isOpen, active }) => ( ); const sidebarVariants = { open: { x: 0, transition: { type: 'spring', stiffness: 300, damping: 30 } }, closed: { x: '-100%', transition: { type: 'spring', stiffness: 300, damping: 30 }, }, }; return ( <>