diff --git a/memento-note/components/sidebar.tsx b/memento-note/components/sidebar.tsx index 75ef6ec..a5cc7b2 100644 --- a/memento-note/components/sidebar.tsx +++ b/memento-note/components/sidebar.tsx @@ -12,7 +12,6 @@ import { BookMarked, Bot, Inbox, - CalendarDays, FlaskConical, ArrowUpDown, Archive, @@ -68,6 +67,12 @@ import { useBrainstormSessions, useDeleteBrainstorm } from '@/hooks/use-brainsto type NavigationView = 'notebooks' | 'agents' | 'reminders' | 'brainstorms' | 'revision' | 'insights' type SortOrder = 'newest' | 'oldest' | 'alpha' | 'manual' +const NOTEBOOKS_PANEL_HEIGHT_KEY = 'memento-sidebar-notebooks-height' +const NOTEBOOKS_PANEL_MIN_PX = 120 +const SIDEBAR_WIDTH_KEY = 'memento-sidebar-width' +const SIDEBAR_WIDTH_MIN_PX = 280 +const SIDEBAR_WIDTH_MAX_PX = 560 + function NoteLink({ title, isActive, @@ -635,6 +640,119 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const [notebookSearchQuery, setNotebookSearchQuery] = useState('') const [trashCount, setTrashCount] = useState(0) + const notebooksContainerRef = useRef(null) + const notebooksPanelRef = useRef(null) + const notebooksPanelHeightRef = useRef(null) + const [notebooksPanelHeight, setNotebooksPanelHeight] = useState(null) + const asideRef = useRef(null) + const sidebarWidthRef = useRef(null) + const [sidebarWidth, setSidebarWidth] = useState(null) + + useEffect(() => { + try { + const stored = localStorage.getItem(NOTEBOOKS_PANEL_HEIGHT_KEY) + if (stored) { + const n = parseInt(stored, 10) + if (!Number.isNaN(n) && n >= NOTEBOOKS_PANEL_MIN_PX) { + setNotebooksPanelHeight(n) + notebooksPanelHeightRef.current = n + } + } + const storedW = localStorage.getItem(SIDEBAR_WIDTH_KEY) + if (storedW) { + const w = parseInt(storedW, 10) + if (!Number.isNaN(w) && w >= SIDEBAR_WIDTH_MIN_PX && w <= SIDEBAR_WIDTH_MAX_PX) { + setSidebarWidth(w) + sidebarWidthRef.current = w + } + } + } catch { + /* ignore */ + } + }, []) + + const handleNotebooksResizeStart = useCallback((e: React.PointerEvent) => { + e.preventDefault() + e.stopPropagation() + const panel = notebooksPanelRef.current + const container = notebooksContainerRef.current + if (!panel || !container) return + + const target = e.currentTarget + target.setPointerCapture(e.pointerId) + + const startY = e.clientY + const startH = panel.getBoundingClientRect().height + const topBlock = container.querySelector('[data-sidebar-notebooks-top]') as HTMLElement | null + const handleH = 12 + const containerH = container.getBoundingClientRect().height + const maxH = Math.max( + NOTEBOOKS_PANEL_MIN_PX, + containerH - (topBlock?.offsetHeight ?? 160) - handleH, + ) + + const onMove = (ev: PointerEvent) => { + const delta = ev.clientY - startY + const newH = Math.min(maxH, Math.max(NOTEBOOKS_PANEL_MIN_PX, startH + delta)) + notebooksPanelHeightRef.current = newH + setNotebooksPanelHeight(newH) + } + const onUp = (ev: PointerEvent) => { + target.releasePointerCapture(ev.pointerId) + document.removeEventListener('pointermove', onMove) + document.removeEventListener('pointerup', onUp) + const h = notebooksPanelHeightRef.current + if (h !== null) { + try { + localStorage.setItem(NOTEBOOKS_PANEL_HEIGHT_KEY, String(h)) + } catch { + /* ignore */ + } + } + } + document.addEventListener('pointermove', onMove) + document.addEventListener('pointerup', onUp) + }, []) + + const handleSidebarWidthResizeStart = useCallback((e: React.PointerEvent) => { + e.preventDefault() + e.stopPropagation() + const aside = asideRef.current + if (!aside) return + + const target = e.currentTarget + target.setPointerCapture(e.pointerId) + document.body.style.cursor = 'col-resize' + document.body.style.userSelect = 'none' + + const startX = e.clientX + const startW = aside.getBoundingClientRect().width + + const onMove = (ev: PointerEvent) => { + const delta = isRtl ? startX - ev.clientX : ev.clientX - startX + const newW = Math.min(SIDEBAR_WIDTH_MAX_PX, Math.max(SIDEBAR_WIDTH_MIN_PX, startW + delta)) + sidebarWidthRef.current = newW + setSidebarWidth(newW) + } + const onUp = (ev: PointerEvent) => { + target.releasePointerCapture(ev.pointerId) + document.body.style.cursor = '' + document.body.style.userSelect = '' + document.removeEventListener('pointermove', onMove) + document.removeEventListener('pointerup', onUp) + const w = sidebarWidthRef.current + if (w !== null) { + try { + localStorage.setItem(SIDEBAR_WIDTH_KEY, String(w)) + } catch { + /* ignore */ + } + } + } + document.addEventListener('pointermove', onMove) + document.addEventListener('pointerup', onUp) + }, [isRtl]) + const [draggedId, setDraggedId] = useState(null) // Ref stable pour éviter les stale closures dans les handlers de drop const draggedIdRef = useRef(null) @@ -855,21 +973,6 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) router.push('/home?forceList=1') } - const handleDailyNoteClick = async () => { - try { - const res = await fetch('/api/notes/daily') - const data = await res.json() - if (data.success && data.note) { - const params = new URLSearchParams() - if (data.note.notebookId) params.set('notebook', data.note.notebookId) - params.set('openNote', data.note.id) - router.push(`/home?${params.toString()}`) - } - } catch { - toast.error(t('sidebar.dailyNoteError') || 'Impossible d\'ouvrir la note du jour') - } - } - const handleNoteClick = (noteId: string, notebookId: string) => { const params = new URLSearchParams(searchParams.toString()) params.set('notebook', notebookId) @@ -1180,11 +1283,14 @@ export function Sidebar({ className, user }: { className?: string; user?: any })