diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx index 808f941d6..1df1560b4 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -257,8 +257,10 @@ export function DashboardClientLayout({
- - +
+ + +
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx index 4fa5e9952..d10a2338c 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx @@ -278,14 +278,17 @@ export default function ConnectorsPage() { initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }} - className="mb-8 flex items-center justify-between" + className="mb-8 flex items-center justify-between gap-2" >
-

{t("title")}

-

{t("subtitle")}

+

{t("title")}

+

{t("subtitle")}

- diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx index 6a7503834..4adb5414c 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx @@ -75,14 +75,14 @@ export function DocumentsFilters({ return ( -
+
onSearch(e.target.value)} placeholder={t("filter_placeholder")} @@ -231,11 +231,11 @@ export function DocumentsFilters({
-
+
{selectedIds.size > 0 && ( - - - - -

Edit Document

-
- + + + + +

Edit Document

+
+ - {/* View Metadata Button */} - - - - + + + +

View Metadata

+
+
+ + + + + + + + +

Delete

+
+
+
+ + {/* Mobile Actions Dropdown */} +
+ + + - - - -

View Metadata

-
- +
+ + + + Edit + + setIsMetadataOpen(true)}> + + Metadata + + setIsDeleteOpen(true)} + className="text-destructive focus:text-destructive" + > + + Delete + + +
+
+ - {/* Delete Button */} - - - - - - - -

Delete

-
-
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx index 78fc1aec0..457a81a6f 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx @@ -225,8 +225,8 @@ export default function DocumentsTable() { transition={{ delay: 0.1 }} >
-

{t("title")}

-

{t("subtitle")}

+

{t("title")}

+

{t("subtitle")}

- - -
-
- -
- - {t("confirm_title")} - - {t("confirm_delete_desc", { count: table.getSelectedRowModel().rows.length })} - - -
- - {t("cancel")} - {t("delete")} - -
-
- - )} - {/* Logs Table */} ; + onBulkDelete: () => Promise; id: string; }) { const t = useTranslations("logs"); return ( -
+
{/* Search Input */} - +
+ +
+ {table.getSelectedRowModel().rows.length > 0 && ( + + + + + +
+
+ +
+ + {t("confirm_title")} + + {t("confirm_delete_desc", { count: table.getSelectedRowModel().rows.length })} + + +
+ + {t("cancel")} + {t("delete")} + +
+
+ )} +
); } @@ -973,6 +970,7 @@ function LogsTable({ style={{ width: `${header.getSize()}px` }} className={cn( "h-12 px-4 py-3", + header.column.id === "select" ? "ps-4 pe-0" : "", // keep Created At header from wrapping and align it header.column.id === "created_at" ? "whitespace-nowrap text-right" : "" )} @@ -1030,7 +1028,8 @@ function LogsTable({ - - + + {languages.find((lang) => lang.code === locale)?.name || "English"} diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 82863f06d..cb01e7605 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -154,8 +154,8 @@ const ThinkingStepsDisplay: FC<{ steps: ThinkingStep[]; isThreadRunning?: boolea "text-muted-foreground hover:text-foreground" )} > - {/* Header text with shimmer if processing or has in-progress step */} - {isProcessing && inProgressStep ? ( + {/* Header text with shimmer if processing (streaming) */} + {isProcessing ? ( ) : ( {getHeaderText()} @@ -398,7 +398,7 @@ const ThreadWelcome: FC = () => {
{/* Greeting positioned above the composer - fixed position */}
-

+

{greeting}

@@ -891,14 +891,17 @@ const ThinkingStepsPart: FC = () => { const messageId = useAssistantState(({ message }) => message?.id); const thinkingSteps = thinkingStepsMap.get(messageId) || []; - // Check if thread is still running (for stopping the spinner when cancelled) + // Check if this specific message is currently streaming + // A message is streaming if: thread is running AND this is the last assistant message const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); + const isLastMessage = useAssistantState(({ message }) => message?.isLast ?? false); + const isMessageStreaming = isThreadRunning && isLastMessage; if (thinkingSteps.length === 0) return null; return (
- +
); }; diff --git a/surfsense_web/components/json-metadata-viewer.tsx b/surfsense_web/components/json-metadata-viewer.tsx index 8fe1b10ae..338c0273b 100644 --- a/surfsense_web/components/json-metadata-viewer.tsx +++ b/surfsense_web/components/json-metadata-viewer.tsx @@ -47,11 +47,11 @@ export function JsonMetadataViewer({ if (open !== undefined && onOpenChange !== undefined) { return ( - + - {title} - Metadata + {title} - Metadata -
+
@@ -70,11 +70,11 @@ export function JsonMetadataViewer({ )} - + - {title} - Metadata + {title} - Metadata -
+
diff --git a/surfsense_web/components/new-chat/model-selector.tsx b/surfsense_web/components/new-chat/model-selector.tsx index 4268d998c..f9ffe267c 100644 --- a/surfsense_web/components/new-chat/model-selector.tsx +++ b/surfsense_web/components/new-chat/model-selector.tsx @@ -175,39 +175,41 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp role="combobox" aria-expanded={open} className={cn( - "h-9 gap-2 px-3 rounded-xl border border-border/80 bg-background/50 backdrop-blur-sm", + "h-7 md:h-9 gap-1 md:gap-2 px-2 md:px-3 rounded-lg md:rounded-xl border border-border/80 bg-background/50 backdrop-blur-sm", "hover:bg-muted/80 hover:border-border/30 transition-all duration-200", - "text-sm font-medium text-foreground", + "text-xs md:text-sm font-medium text-foreground", "focus-visible:ring-0 focus-visible:ring-offset-0", className )} > {isLoading ? ( <> - - Loading... + + Loading... + Load... ) : currentConfig ? ( <> {getProviderIcon(currentConfig.provider)} - {currentConfig.name} - - {currentConfig.model_name.split("/").pop()?.slice(0, 15) || - currentConfig.model_name.slice(0, 15)} + {currentConfig.name} + + {currentConfig.model_name.split("/").pop()?.slice(0, 10) || + currentConfig.model_name.slice(0, 10)} ) : ( <> - - Select Model + + Select Model + Model )} - + @@ -225,17 +227,17 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
)} -
+
- +
diff --git a/surfsense_web/components/sidebar/all-chats-sidebar.tsx b/surfsense_web/components/sidebar/all-chats-sidebar.tsx index ef55142fa..bb1ae2f15 100644 --- a/surfsense_web/components/sidebar/all-chats-sidebar.tsx +++ b/surfsense_web/components/sidebar/all-chats-sidebar.tsx @@ -42,9 +42,10 @@ interface AllChatsSidebarProps { open: boolean; onOpenChange: (open: boolean) => void; searchSpaceId: string; + onCloseMobileSidebar?: () => void; } -export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsSidebarProps) { +export function AllChatsSidebar({ open, onOpenChange, searchSpaceId, onCloseMobileSidebar }: AllChatsSidebarProps) { const t = useTranslations("sidebar"); const router = useRouter(); const params = useParams(); @@ -61,6 +62,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS const [searchQuery, setSearchQuery] = useState(""); const [showArchived, setShowArchived] = useState(false); const [mounted, setMounted] = useState(false); + const [openDropdownId, setOpenDropdownId] = useState(null); const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); const isSearchMode = !!debouncedSearchQuery.trim(); @@ -120,8 +122,10 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS (threadId: number) => { router.push(`/dashboard/${searchSpaceId}/new-chat/${threadId}`); onOpenChange(false); + // Also close the main sidebar on mobile + onCloseMobileSidebar?.(); }, - [router, onOpenChange, searchSpaceId] + [router, onOpenChange, searchSpaceId, onCloseMobileSidebar] ); // Handle thread deletion @@ -209,7 +213,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} - className="fixed inset-0 z-50 bg-black/50" + className="fixed inset-0 z-[70] bg-black/50" onClick={() => onOpenChange(false)} aria-hidden="true" /> @@ -220,7 +224,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS animate={{ x: 0 }} exit={{ x: "-100%" }} transition={{ type: "spring", damping: 25, stiffness: 300 }} - className="fixed inset-y-0 left-0 z-50 w-80 bg-background shadow-xl flex flex-col" + className="fixed inset-y-0 left-0 z-[70] w-80 bg-background shadow-xl flex flex-col pointer-events-auto isolate" role="dialog" aria-modal="true" aria-label={t("all_chats") || "All Chats"} @@ -345,14 +349,17 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS {/* Actions dropdown */} - + setOpenDropdownId(isOpen ? thread.id : null)} + > - + handleToggleArchive(thread.id, thread.archived)} disabled={isArchiving} diff --git a/surfsense_web/components/sidebar/all-notes-sidebar.tsx b/surfsense_web/components/sidebar/all-notes-sidebar.tsx index ff9f07175..67d1b4ba6 100644 --- a/surfsense_web/components/sidebar/all-notes-sidebar.tsx +++ b/surfsense_web/components/sidebar/all-notes-sidebar.tsx @@ -27,6 +27,7 @@ interface AllNotesSidebarProps { onOpenChange: (open: boolean) => void; searchSpaceId: string; onAddNote?: () => void; + onCloseMobileSidebar?: () => void; } export function AllNotesSidebar({ @@ -34,6 +35,7 @@ export function AllNotesSidebar({ onOpenChange, searchSpaceId, onAddNote, + onCloseMobileSidebar, }: AllNotesSidebarProps) { const t = useTranslations("sidebar"); const router = useRouter(); @@ -45,6 +47,7 @@ export function AllNotesSidebar({ const [deletingNoteId, setDeletingNoteId] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [mounted, setMounted] = useState(false); + const [openDropdownId, setOpenDropdownId] = useState(null); const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); // Handle mounting for portal @@ -114,8 +117,10 @@ export function AllNotesSidebar({ (noteId: number, noteSearchSpaceId: number) => { router.push(`/dashboard/${noteSearchSpaceId}/editor/${noteId}`); onOpenChange(false); + // Also close the main sidebar on mobile + onCloseMobileSidebar?.(); }, - [router, onOpenChange] + [router, onOpenChange, onCloseMobileSidebar] ); // Handle note deletion @@ -195,7 +200,7 @@ export function AllNotesSidebar({ animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} - className="fixed inset-0 z-50 bg-black/50" + className="fixed inset-0 z-[70] bg-black/50" onClick={() => onOpenChange(false)} aria-hidden="true" /> @@ -206,7 +211,7 @@ export function AllNotesSidebar({ animate={{ x: 0 }} exit={{ x: "-100%" }} transition={{ type: "spring", damping: 25, stiffness: 300 }} - className="fixed inset-y-0 left-0 z-50 w-80 bg-background shadow-xl flex flex-col" + className="fixed inset-y-0 left-0 z-[70] w-80 bg-background shadow-xl flex flex-col pointer-events-auto isolate" role="dialog" aria-modal="true" aria-label={t("all_notes") || "All Notes"} @@ -307,14 +312,17 @@ export function AllNotesSidebar({ {/* Actions dropdown - separate from main click area */} - + setOpenDropdownId(isOpen ? note.id : null)} + > - + handleDeleteNote(note.id, note.search_space_id)} className="text-destructive focus:text-destructive" diff --git a/surfsense_web/components/sidebar/nav-chats.tsx b/surfsense_web/components/sidebar/nav-chats.tsx index 3bb7167da..5b73b75e9 100644 --- a/surfsense_web/components/sidebar/nav-chats.tsx +++ b/surfsense_web/components/sidebar/nav-chats.tsx @@ -28,6 +28,7 @@ import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, + useSidebar, } from "@/components/ui/sidebar"; import { useIsMobile } from "@/hooks/use-mobile"; import { cn } from "@/lib/utils"; @@ -73,6 +74,7 @@ export function NavChats({ const router = useRouter(); const pathname = usePathname(); const isMobile = useIsMobile(); + const { setOpenMobile } = useSidebar(); const [isDeleting, setIsDeleting] = useState(null); const [isOpen, setIsOpen] = useState(defaultOpen); const [isAllChatsSidebarOpen, setIsAllChatsSidebarOpen] = useState(false); @@ -119,7 +121,7 @@ export function NavChats({ {/* Action buttons - always visible on hover */} -
+
{searchSpaceId && chats.length > 0 && (