From 892a697cb904b4ef866638648fd59b411d4d9fdc Mon Sep 17 00:00:00 2001 From: Antigravity Date: Sun, 10 May 2026 19:04:31 +0000 Subject: [PATCH] feat: add right-click context menu with freeze/unfreeze notebook state in sidebar --- memento-note/components/sidebar.tsx | 102 +++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/memento-note/components/sidebar.tsx b/memento-note/components/sidebar.tsx index 7896b5b..824cc0d 100644 --- a/memento-note/components/sidebar.tsx +++ b/memento-note/components/sidebar.tsx @@ -25,6 +25,8 @@ import { Clock, Moon, Sun, + Pin, + PinOff, } from 'lucide-react' import { useLanguage } from '@/lib/i18n' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -88,6 +90,8 @@ function SidebarCarnetItem({ onAddSubNotebook, onRename, onDelete, + onTogglePin, + isPinned, children, isDragging, dragHandleProps, @@ -104,6 +108,8 @@ function SidebarCarnetItem({ onAddSubNotebook: () => void onRename: () => void onDelete: () => void + onTogglePin: () => void + isPinned: boolean children?: React.ReactNode isDragging?: boolean dragHandleProps?: React.HTMLAttributes @@ -113,12 +119,26 @@ function SidebarCarnetItem({ }) { const { t } = useLanguage() const hasChildren = React.Children.count(children) > 0 + const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null) + + // Close context menu on outside click + useEffect(() => { + if (!contextMenu) return + const handler = () => setContextMenu(null) + window.addEventListener('click', handler) + return () => window.removeEventListener('click', handler) + }, [contextMenu]) return (
{ + e.preventDefault() + e.stopPropagation() + setContextMenu({ x: e.clientX, y: e.clientY }) + }} > {level > 0 && (
@@ -184,6 +204,11 @@ function SidebarCarnetItem({
+ {isPinned && ( + + + + )}
+ {/* Right-click context menu */} + + {contextMenu && ( + e.stopPropagation()} + > + +
+ + +
+ + + )} + + {isExpanded && ( >(new Set()) + const [pinnedIds, setPinnedIds] = useState>(new Set()) const [notebookNotes, setNotebookNotes] = useState>({}) const [activeView, setActiveView] = useState('notebooks') const [sortOrder, setSortOrder] = useState('newest') @@ -460,6 +534,29 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) }) }, []) + // Load pinned notebooks from localStorage on mount + useEffect(() => { + try { + const stored = localStorage.getItem('momento-pinned-notebooks') + if (stored) setPinnedIds(new Set(JSON.parse(stored))) + } catch {} + }, []) + + const togglePin = useCallback((id: string) => { + setPinnedIds(prev => { + const next = new Set(prev) + if (next.has(id)) { + next.delete(id) + } else { + next.add(id) + // Ensure it's also expanded when pinned + setExpandedIds(e => { const ne = new Set(e); ne.add(id); return ne }) + } + try { localStorage.setItem('momento-pinned-notebooks', JSON.stringify([...next])) } catch {} + return next + }) + }, []) + const handleStartRename = useCallback((notebook: Notebook) => { setRenamingNotebook(notebook) setRenameValue(notebook.name) @@ -524,7 +621,8 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const hasActiveDescendant = children.some(c => currentNotebookId === c.id || (childNotebooks.get(c.id) || []).some(gc => currentNotebookId === gc.id) ) - const isExpanded = expandedIds.has(notebook.id) || hasActiveDescendant + // A notebook stays expanded if: manually expanded, has active descendant, OR is pinned + const isExpanded = expandedIds.has(notebook.id) || hasActiveDescendant || pinnedIds.has(notebook.id) return ( handleStartRename(notebook)} onDelete={() => setDeletingNotebook(notebook)} + onTogglePin={() => togglePin(notebook.id)} + isPinned={pinnedIds.has(notebook.id)} isDragging={isDragging} level={level} isExpanded={isExpanded}