import React from 'react'; import { Plus, Archive, Settings, ChevronRight, BookOpen, Bot, Microscope, Activity, Pin, Moon, Sun, Bell, Lock, Edit3, Trash2, Users, Clock, GripVertical, Wind, Network, Home, Sparkles, LogOut, ChevronDown, Folder, FolderOpen, FileText, Search, BookMarked, User, ExternalLink, ChevronUp, HelpCircle, EyeOff, Layers, Scissors, Chrome, Crown, ArrowRight, GraduationCap } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { NavigationView, Carnet, Note, SettingsTab, Flashcard } from '../types'; interface NoteLinkProps { note: Note; isActive: boolean; onClick: () => void; } const NoteLink: React.FC = ({ note, isActive, onClick }) => (
{note.title || "Note sans titre"}
{note.isPinned && }
); interface SidebarItemProps { carnet: Carnet; isActive: boolean; notes: Note[]; activeNoteId: string | null; onCarnetClick: () => void; onNoteClick: (noteId: string) => void; onAddSubCarnet: () => void; onRename: () => void; onDelete: () => void; children?: React.ReactNode; level: number; isExpanded: boolean; toggleExpand: () => void; onMove?: (draggedId: string, targetId?: string) => void; } const SidebarItem: React.FC = ({ carnet, isActive, notes, activeNoteId, onCarnetClick, onNoteClick, onAddSubCarnet, onRename, onDelete, children, level, isExpanded, toggleExpand, onMove }) => { const hasChildren = React.Children.count(children) > 0 || notes.length > 0; return (
{/* Subtle Drag Handle */}
{/* Hierarchy Guide Line */} {level > 0 && (
)} {level > 0 && (
)}
{hasChildren ? ( ) : (
// Spacer for alignment )} { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; e.currentTarget.classList.add('bg-accent/5', 'ring-1', 'ring-accent/20'); }} onDragLeave={(e) => { e.currentTarget.classList.remove('bg-accent/5', 'ring-1', 'ring-accent/20'); }} onDrop={(e) => { e.preventDefault(); e.currentTarget.classList.remove('bg-accent/5', 'ring-1', 'ring-accent/20'); const draggedId = e.dataTransfer.getData('carnetId'); if (draggedId && draggedId !== carnet.id) { onMove?.(draggedId, carnet.id); } }} draggable onDragStart={(e) => { e.dataTransfer.setData('carnetId', carnet.id); e.dataTransfer.effectAllowed = 'move'; const ghost = e.currentTarget.cloneNode(true) as HTMLElement; ghost.style.position = 'absolute'; ghost.style.top = '-1000px'; ghost.style.opacity = '0.5'; document.body.appendChild(ghost); e.dataTransfer.setDragImage(ghost, 0, 0); setTimeout(() => document.body.removeChild(ghost), 0); }} > {isActive && ( )}
{isExpanded ? ( ) : ( )}
{carnet.name} {carnet.isPrivate && }
{notes.length > 0 && ( {notes.length} )}
{isExpanded && (
{/* Vertical line for nested content path */}
{children} {notes.map(note => ( onNoteClick(note.id)} /> ))} {notes.length === 0 && !React.Children.count(children) && (

Vide

)}
)}
); }; interface SidebarProps { activeView: NavigationView; isSidebarOpen: boolean; setIsSidebarOpen: (val: boolean) => void; isDarkMode: boolean; setIsDarkMode: (val: boolean) => void; setActiveView: (view: NavigationView) => void; setActiveSettingsTab?: (tab: SettingsTab) => void; carnets: Carnet[]; notes: Note[]; activeCarnetId: string; activeNoteId: string | null; setActiveCarnetId: (id: string) => void; setActiveNoteId: (id: string | null) => void; setShowNewCarnetModal: (show: boolean, parentId?: string, isRenaming?: boolean, carnetId?: string) => void; onDeleteCarnet: (id: string) => void; onMoveCarnet: (draggedId: string, targetId?: string) => void; onGoHome: () => void; onLogout: () => void; flashcards?: Flashcard[]; onSelectReviewDeck?: (noteId: string) => void; } export const Sidebar: React.FC = ({ activeView, isSidebarOpen, setIsSidebarOpen, isDarkMode, setIsDarkMode, setActiveView, setActiveSettingsTab, carnets, notes, activeCarnetId, activeNoteId, setActiveCarnetId, setActiveNoteId, setShowNewCarnetModal, onDeleteCarnet, onMoveCarnet, onGoHome, onLogout, flashcards, onSelectReviewDeck }) => { const [expandedIds, setExpandedIds] = React.useState>(new Set(['4', '1', '2', '3'])); // Default expand key guides const [collapsedSections, setCollapsedSections] = React.useState>(new Set()); const [searchQuery, setSearchQuery] = React.useState(''); const toggleSection = (id: string) => { const newSet = new Set(collapsedSections); if (newSet.has(id)) newSet.delete(id); else newSet.add(id); setCollapsedSections(newSet); }; const toggleExpand = (id: string) => { const newSet = new Set(expandedIds); if (newSet.has(id)) newSet.delete(id); else newSet.add(id); setExpandedIds(newSet); }; // Safe filtration based on searches const filteredCarnets = React.useMemo(() => { if (!searchQuery) return carnets; const q = searchQuery.toLowerCase(); return carnets.filter(c => c.name.toLowerCase().includes(q) || notes.some(n => n.carnetId === c.id && n.title.toLowerCase().includes(q)) ); }, [carnets, searchQuery, notes]); const activeNote = React.useMemo(() => { if (!activeNoteId) return null; return notes.find(n => n.id === activeNoteId); }, [notes, activeNoteId]); // Extract outline markdown headings dynamically for the currently active note const headings = React.useMemo(() => { if (!activeNote || !activeNote.content) return []; const lines = activeNote.content.split('\n'); const hs: { text: string; level: number }[] = []; lines.forEach(line => { const trimmed = line.trim(); const hMatch = trimmed.match(/^(#{1,6})\s+(.+)$/); if (hMatch) { hs.push({ level: hMatch[1].length, text: hMatch[2].replace(/\[.*?\]\(.*?\)/g, '').replace(/[*_`]/g, '').trim() }); } }); return hs.slice(0, 10); }, [activeNote]); const renderCarnetTree = (parentId: string | undefined = undefined, level: number = 0) => { return filteredCarnets .filter(c => c.parentId === parentId && !c.isDeleted) .map(carnet => { const carnetNotes = notes.filter(n => n.carnetId === carnet.id && !n.isDeleted); return ( toggleExpand(carnet.id)} onAddSubCarnet={() => { if (!expandedIds.has(carnet.id)) toggleExpand(carnet.id); setShowNewCarnetModal(true, carnet.id); }} onRename={() => { setShowNewCarnetModal(true, undefined, true, carnet.id); }} onDelete={() => { onDeleteCarnet(carnet.id); }} onCarnetClick={() => { setActiveCarnetId(carnet.id); setActiveNoteId(null); // Auto expand when clicking if (!expandedIds.has(carnet.id)) toggleExpand(carnet.id); }} onNoteClick={(id) => { setActiveCarnetId(carnet.id); setActiveNoteId(id); }} onMove={onMoveCarnet} > {renderCarnetTree(carnet.id, level + 1)} ); }); }; return ( <> {isSidebarOpen && ( setIsSidebarOpen(false)} className="fixed inset-0 bg-black/40 backdrop-blur-sm z-[60] lg:hidden" /> )} ); };