+
@@ -121,10 +132,10 @@ export function NavChats({ chats, defaultOpen = true, searchSpaceId }: NavChatsP
-
-
- {chats.length > 0 ? (
- chats.map((chat) => {
+ {chats.length > 0 ? (
+
+
+ {chats.map((chat) => {
const isDeletingChat = isDeleting === chat.id;
return (
@@ -163,7 +174,7 @@ export function NavChats({ chats, defaultOpen = true, searchSpaceId }: NavChatsP
) : (
)}
- More options
+ {t("more_options") || "More options"}
@@ -191,7 +202,7 @@ export function NavChats({ chats, defaultOpen = true, searchSpaceId }: NavChatsP
{isDeletingChat && isDeleteAction
- ? "Deleting..."
+ ? t("deleting") || "Deleting..."
: action.name}
@@ -203,17 +214,15 @@ export function NavChats({ chats, defaultOpen = true, searchSpaceId }: NavChatsP
)}
);
- })
- ) : (
-
-
-
- {t("no_recent_chats") || "No recent chats"}
-
-
- )}
-
-
+ })}
+
+
+ ) : (
+
+
+ {t("no_recent_chats") || "No recent chats"}
+
+ )}
diff --git a/surfsense_web/components/sidebar/nav-main.tsx b/surfsense_web/components/sidebar/nav-main.tsx
index 274d77b33..606ab2680 100644
--- a/surfsense_web/components/sidebar/nav-main.tsx
+++ b/surfsense_web/components/sidebar/nav-main.tsx
@@ -2,7 +2,7 @@
import { ChevronRight, type LucideIcon } from "lucide-react";
import { useTranslations } from "next-intl";
-import { useMemo } from "react";
+import { useCallback, useMemo, useState } from "react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
@@ -28,7 +28,12 @@ interface NavItem {
}[];
}
-export function NavMain({ items }: { items: NavItem[] }) {
+interface NavMainProps {
+ items: NavItem[];
+ onSourcesExpandedChange?: (expanded: boolean) => void;
+}
+
+export function NavMain({ items, onSourcesExpandedChange }: NavMainProps) {
const t = useTranslations("nav_menu");
// Translation function that handles both exact matches and fallback to original
@@ -53,6 +58,29 @@ export function NavMain({ items }: { items: NavItem[] }) {
// Memoize items to prevent unnecessary re-renders
const memoizedItems = useMemo(() => items, [items]);
+ // Track expanded state for items with sub-menus (like Sources)
+ const [expandedItems, setExpandedItems] = useState
>(() => {
+ const initial: Record = {};
+ items.forEach((item) => {
+ if (item.items?.length) {
+ initial[item.title] = item.isActive ?? false;
+ }
+ });
+ return initial;
+ });
+
+ // Handle collapsible state change
+ const handleOpenChange = useCallback(
+ (title: string, isOpen: boolean) => {
+ setExpandedItems((prev) => ({ ...prev, [title]: isOpen }));
+ // Notify parent when Sources is expanded/collapsed
+ if (title === "Sources" && onSourcesExpandedChange) {
+ onSourcesExpandedChange(isOpen);
+ }
+ },
+ [onSourcesExpandedChange]
+ );
+
return (
{translateTitle("Platform")}
@@ -60,8 +88,15 @@ export function NavMain({ items }: { items: NavItem[] }) {
{memoizedItems.map((item, index) => {
const translatedTitle = translateTitle(item.title);
const hasSub = !!item.items?.length;
+ const isItemOpen = expandedItems[item.title] ?? item.isActive ?? false;
return (
-
+ handleOpenChange(item.title, open) : undefined}
+ defaultOpen={!hasSub ? item.isActive : undefined}
+ >
{hasSub ? (
// When the item has children, make the whole row a collapsible trigger
diff --git a/surfsense_web/components/sidebar/nav-notes.tsx b/surfsense_web/components/sidebar/nav-notes.tsx
index b14ecea77..383338d77 100644
--- a/surfsense_web/components/sidebar/nav-notes.tsx
+++ b/surfsense_web/components/sidebar/nav-notes.tsx
@@ -12,7 +12,7 @@ import {
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
-import { useCallback, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
@@ -29,6 +29,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar";
+import { useIsMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
import { AllNotesSidebar } from "./all-notes-sidebar";
@@ -52,6 +53,7 @@ interface NavNotesProps {
onAddNote?: () => void;
defaultOpen?: boolean;
searchSpaceId?: string;
+ isSourcesExpanded?: boolean;
}
// Map of icon names to their components
@@ -61,13 +63,21 @@ const actionIconMap: Record = {
MoreHorizontal,
};
-export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }: NavNotesProps) {
+export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId, isSourcesExpanded = false }: NavNotesProps) {
const t = useTranslations("sidebar");
const router = useRouter();
+ const isMobile = useIsMobile();
const [isDeleting, setIsDeleting] = useState(null);
const [isOpen, setIsOpen] = useState(defaultOpen);
const [isAllNotesSidebarOpen, setIsAllNotesSidebarOpen] = useState(false);
+ // Auto-collapse on smaller screens when Sources is expanded
+ useEffect(() => {
+ if (isSourcesExpanded && isMobile) {
+ setIsOpen(false);
+ }
+ }, [isSourcesExpanded, isMobile]);
+
// Handle note deletion with loading state
const handleDeleteNote = useCallback(async (noteId: number, deleteAction: () => void) => {
setIsDeleting(noteId);
@@ -113,7 +123,7 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
e.stopPropagation();
setIsAllNotesSidebarOpen(true);
}}
- aria-label="View all notes"
+ aria-label={t("view_all_notes") || "View all notes"}
>
@@ -127,7 +137,7 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
e.stopPropagation();
onAddNote();
}}
- aria-label="Add note"
+ aria-label={t("add_note") || "Add note"}
>
@@ -178,7 +188,7 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
) : (
)}
- More options
+ {t("more_options") || "More options"}
@@ -206,7 +216,7 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
{isDeletingNote && isDeleteAction
- ? "Deleting..."
+ ? t("deleting") || "Deleting..."
: action.name}
diff --git a/surfsense_web/components/ui/sidebar.tsx b/surfsense_web/components/ui/sidebar.tsx
index edc846ba7..caafa6b6e 100644
--- a/surfsense_web/components/ui/sidebar.tsx
+++ b/surfsense_web/components/ui/sidebar.tsx
@@ -352,7 +352,7 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
data-slot="sidebar-content"
data-sidebar="content"
className={cn(
- "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden group-data-[collapsible=icon]:overflow-hidden",
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
className
)}
{...props}
diff --git a/surfsense_web/components/ui/tooltip.tsx b/surfsense_web/components/ui/tooltip.tsx
index c065426ba..98420c858 100644
--- a/surfsense_web/components/ui/tooltip.tsx
+++ b/surfsense_web/components/ui/tooltip.tsx
@@ -42,13 +42,13 @@ function TooltipContent({
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
- "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
+ "bg-popover text-popover-foreground border shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
className
)}
{...props}
>
{children}
-
+
);
diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json
index 478264889..43ec2b0aa 100644
--- a/surfsense_web/messages/en.json
+++ b/surfsense_web/messages/en.json
@@ -98,6 +98,8 @@
"unknown_search_space": "Unknown Search Space",
"delete_chat": "Delete Chat",
"delete_chat_confirm": "Are you sure you want to delete",
+ "delete_note": "Delete Note",
+ "delete_note_confirm": "Are you sure you want to delete",
"action_cannot_undone": "This action cannot be undone.",
"deleting": "Deleting...",
"surfsense_dashboard": "SurfSense Dashboard",
@@ -641,7 +643,7 @@
"search_chats": "Search chats...",
"no_chats_found": "No chats found",
"no_recent_chats": "No recent chats",
- "view_all_chats": "View All Chats",
+ "view_all_chats": "View all chats",
"all_chats": "All Chats",
"all_chats_description": "Browse and manage all your chats",
"no_chats": "No chats yet",
@@ -659,7 +661,15 @@
"no_notes": "No notes yet",
"create_new_note": "Create a new note",
"error_loading_notes": "Error loading notes",
- "loading": "Loading..."
+ "loading": "Loading...",
+ "deleting": "Deleting...",
+ "delete": "Delete",
+ "created": "Created",
+ "updated": "Updated",
+ "more_options": "More options",
+ "clear_search": "Clear search",
+ "view_all_notes": "View all notes",
+ "add_note": "Add note"
},
"errors": {
"something_went_wrong": "Something went wrong",
diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json
index 24b37c61f..d33354e06 100644
--- a/surfsense_web/messages/zh.json
+++ b/surfsense_web/messages/zh.json
@@ -98,6 +98,8 @@
"unknown_search_space": "未知搜索空间",
"delete_chat": "删除对话",
"delete_chat_confirm": "您确定要删除",
+ "delete_note": "删除笔记",
+ "delete_note_confirm": "您确定要删除",
"action_cannot_undone": "此操作无法撤销。",
"deleting": "删除中...",
"surfsense_dashboard": "SurfSense 仪表盘",
@@ -659,7 +661,15 @@
"no_notes": "暂无笔记",
"create_new_note": "创建新笔记",
"error_loading_notes": "加载笔记时出错",
- "loading": "加载中..."
+ "loading": "加载中...",
+ "deleting": "删除中...",
+ "delete": "删除",
+ "created": "创建时间",
+ "updated": "更新时间",
+ "more_options": "更多选项",
+ "clear_search": "清除搜索",
+ "view_all_notes": "查看所有笔记",
+ "add_note": "添加笔记"
},
"errors": {
"something_went_wrong": "出错了",