+
toggleOne(doc.id, !!v)}
aria-label="Select row"
/>
-
-
-
{
- await onRefresh();
- }}
- searchSpaceId={searchSpaceId as string}
- />
+
@@ -371,6 +361,14 @@ export function DocumentsTableShell({
)}
+
{
+ await onRefresh();
+ }}
+ searchSpaceId={searchSpaceId as string}
+ />
);
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx
index 1c4d440e7..d6da0fb6a 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/RowActions.tsx
@@ -1,6 +1,6 @@
"use client";
-import { FileText, Pencil, Trash2 } from "lucide-react";
+import { FileText, MoreHorizontal, Pencil, Trash2 } from "lucide-react";
import { motion } from "motion/react";
import { useRouter } from "next/navigation";
import { useState } from "react";
@@ -16,6 +16,12 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import type { Document } from "./types";
@@ -57,53 +63,108 @@ export function RowActions({
return (
- {/* Edit Button */}
-
-
-
-
-
-
-
- Edit Document
-
-
+
+
+
+
+ Edit Document
+
+
- {/* View Metadata Button */}
-
-
-
-
+
+ {/* Mobile Actions Dropdown */}
+
+
+
+
+
+ Open menu
-
-
-
- View Metadata
-
-
+
+
+
+
+ Edit
+
+ setIsMetadataOpen(true)}>
+
+ Metadata
+
+ setIsDeleteOpen(true)}
+ className="text-destructive focus:text-destructive"
+ >
+
+ Delete
+
+
+
+
+
- {/* Delete Button */}
-
-
-
- setIsDeleteOpen(true)}
- disabled={isDeleting}
- >
-
- Delete
-
-
-
-
- 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")}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
index c070dde08..bfdf1c403 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
@@ -506,8 +506,8 @@ export default function LogsManagePage() {
transition={{ delay: 0.1 }}
>
-
{t("title")}
-
{t("subtitle")}
+
{t("title")}
+
{t("subtitle")}
@@ -521,48 +521,10 @@ export default function LogsManagePage() {
uniqueLevels={uniqueLevels}
uniqueStatuses={uniqueStatuses}
inputRef={inputRef}
+ onBulkDelete={handleDeleteRows}
id={id}
/>
- {/* Delete Button */}
- {table.getSelectedRowModel().rows.length > 0 && (
-
-
-
-
-
- {t("delete_selected")}
-
- {table.getSelectedRowModel().rows.length}
-
-
-
-
-
-
-
-
-
- {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("delete_selected")}
+
+ {table.getSelectedRowModel().rows.length}
+
+
+
+
+
+
+
+
+
+ {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 (