diff --git a/surfsense_web/atoms/agent-tools/agent-tools.atoms.ts b/surfsense_web/atoms/agent-tools/agent-tools.atoms.ts index 3e05fc006..2c9e01fb2 100644 --- a/surfsense_web/atoms/agent-tools/agent-tools.atoms.ts +++ b/surfsense_web/atoms/agent-tools/agent-tools.atoms.ts @@ -1,6 +1,6 @@ import { atom } from "jotai"; import { atomWithQuery } from "jotai-tanstack-query"; -import { agentToolsApiService, type AgentToolInfo } from "@/lib/apis/agent-tools-api.service"; +import { type AgentToolInfo, agentToolsApiService } from "@/lib/apis/agent-tools-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx index ce6845c77..6c2cc4ecb 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx @@ -235,39 +235,39 @@ export const ComposioDriveConfig: FC = ({ )} - {isEditMode ? ( -
- + {isFolderTreeOpen && ( + )} - Change Selection - - {isFolderTreeOpen && ( - - )} -
- ) : ( - - )} + + ) : ( + + )} {/* Indexing Options */} diff --git a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts index 731a55c3f..7d2b3682b 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts +++ b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts @@ -123,11 +123,7 @@ export const useConnectorDialog = () => { }, []); const handleAutoIndex = useCallback( - async ( - connector: SearchSourceConnector, - connectorTitle: string, - connectorType: string - ) => { + async (connector: SearchSourceConnector, connectorTitle: string, connectorType: string) => { if (!searchSpaceId || isAutoIndexingRef.current) return; isAutoIndexingRef.current = true; @@ -159,12 +155,10 @@ export const useConnectorDialog = () => { }, }); - trackIndexWithDateRangeStarted( - Number(searchSpaceId), - connectorType, - connector.id, - { hasStartDate: true, hasEndDate: true } - ); + trackIndexWithDateRangeStarted(Number(searchSpaceId), connectorType, connector.id, { + hasStartDate: true, + hasEndDate: true, + }); toast.success(`${connectorTitle} connected!`, { id: toastId, @@ -316,46 +310,46 @@ export const useConnectorDialog = () => { } } } - } else { - // Do NOT call setIsOpen(false) here. Closing the dialog is handled - // explicitly by handleOpenChange and the individual action handlers. - // Relying on URL params to close the dialog caused a race condition - // where Next.js router updates from tab switches briefly produced - // stale searchParams without the "modal" key, closing the popup. + } else { + // Do NOT call setIsOpen(false) here. Closing the dialog is handled + // explicitly by handleOpenChange and the individual action handlers. + // Relying on URL params to close the dialog caused a race condition + // where Next.js router updates from tab switches briefly produced + // stale searchParams without the "modal" key, closing the popup. - // Still clean up sub-view state when the modal param is gone - // (e.g. after browser back navigation or explicit handler URL cleanup). - if (indexingConfig) { - setIndexingConfig(null); - setIndexingConnector(null); - setIndexingConnectorConfig(null); - setStartDate(undefined); - setEndDate(undefined); - setPeriodicEnabled(false); - setFrequencyMinutes("1440"); - setEnableSummary(false); - setIsScrolled(false); - setSearchQuery(""); + // Still clean up sub-view state when the modal param is gone + // (e.g. after browser back navigation or explicit handler URL cleanup). + if (indexingConfig) { + setIndexingConfig(null); + setIndexingConnector(null); + setIndexingConnectorConfig(null); + setStartDate(undefined); + setEndDate(undefined); + setPeriodicEnabled(false); + setFrequencyMinutes("1440"); + setEnableSummary(false); + setIsScrolled(false); + setSearchQuery(""); + } + if (editingConnector) { + setEditingConnector(null); + setConnectorName(null); + setConnectorConfig(null); + setStartDate(undefined); + setEndDate(undefined); + setPeriodicEnabled(false); + setFrequencyMinutes("1440"); + setEnableSummary(false); + setIsScrolled(false); + setSearchQuery(""); + } + if (connectingConnectorType) { + setConnectingConnectorType(null); + } + if (viewingAccountsType) { + setViewingAccountsType(null); + } } - if (editingConnector) { - setEditingConnector(null); - setConnectorName(null); - setConnectorConfig(null); - setStartDate(undefined); - setEndDate(undefined); - setPeriodicEnabled(false); - setFrequencyMinutes("1440"); - setEnableSummary(false); - setIsScrolled(false); - setSearchQuery(""); - } - if (connectingConnectorType) { - setConnectingConnectorType(null); - } - if (viewingAccountsType) { - setViewingAccountsType(null); - } - } } catch (error) { // Invalid query params - log but don't crash console.warn("Invalid connector popup query params:", error); @@ -412,18 +406,18 @@ export const useConnectorDialog = () => { COMPOSIO_CONNECTORS.find((c) => c.id === params.connector) : null; - if (earlyConnector && AUTO_INDEX_CONNECTOR_TYPES.has(earlyConnector.connectorType)) { - toast.loading(`Setting up ${earlyConnector.title}...`, { id: "auto-index" }); - setIsOpen(false); - const url = new URL(window.location.href); - url.searchParams.delete("success"); - url.searchParams.delete("connector"); - url.searchParams.delete("connectorId"); - url.searchParams.delete("view"); - url.searchParams.delete("modal"); - url.searchParams.delete("tab"); - router.replace(url.pathname + url.search, { scroll: false }); - } + if (earlyConnector && AUTO_INDEX_CONNECTOR_TYPES.has(earlyConnector.connectorType)) { + toast.loading(`Setting up ${earlyConnector.title}...`, { id: "auto-index" }); + setIsOpen(false); + const url = new URL(window.location.href); + url.searchParams.delete("success"); + url.searchParams.delete("connector"); + url.searchParams.delete("connectorId"); + url.searchParams.delete("view"); + url.searchParams.delete("modal"); + url.searchParams.delete("tab"); + router.replace(url.pathname + url.search, { scroll: false }); + } refetchAllConnectors().then(async (result) => { if (!result.data) { @@ -792,22 +786,22 @@ export const useConnectorDialog = () => { }, }); - const successMessage = - currentConnectorType === "MCP_CONNECTOR" - ? `${connector.name} added successfully` - : `${connectorTitle} connected and syncing started!`; - toast.success(successMessage); + const successMessage = + currentConnectorType === "MCP_CONNECTOR" + ? `${connector.name} added successfully` + : `${connectorTitle} connected and syncing started!`; + toast.success(successMessage); - // Close dialog and clean up URL - setIsOpen(false); - const url = new URL(window.location.href); - url.searchParams.delete("modal"); - url.searchParams.delete("tab"); - url.searchParams.delete("view"); - url.searchParams.delete("connectorType"); - router.replace(url.pathname + url.search, { scroll: false }); + // Close dialog and clean up URL + setIsOpen(false); + const url = new URL(window.location.href); + url.searchParams.delete("modal"); + url.searchParams.delete("tab"); + url.searchParams.delete("view"); + url.searchParams.delete("connectorType"); + router.replace(url.pathname + url.search, { scroll: false }); - // Clear indexing config state since we're not showing the view + // Clear indexing config state since we're not showing the view setIndexingConfig(null); setIndexingConnector(null); setIndexingConnectorConfig(null); @@ -855,24 +849,24 @@ export const useConnectorDialog = () => { // Refresh connectors list await refetchAllConnectors(); } else { - // Other non-indexable connectors - just show success message and close - const successMessage = - currentConnectorType === "MCP_CONNECTOR" - ? `${connector.name} added successfully` - : `${connectorTitle} connected successfully!`; - toast.success(successMessage); + // Other non-indexable connectors - just show success message and close + const successMessage = + currentConnectorType === "MCP_CONNECTOR" + ? `${connector.name} added successfully` + : `${connectorTitle} connected successfully!`; + toast.success(successMessage); - // Refresh connectors list before closing modal - await refetchAllConnectors(); + // Refresh connectors list before closing modal + await refetchAllConnectors(); - // Close dialog and clean up URL - setIsOpen(false); - const url = new URL(window.location.href); - url.searchParams.delete("modal"); - url.searchParams.delete("tab"); - url.searchParams.delete("view"); - url.searchParams.delete("connectorType"); - router.replace(url.pathname + url.search, { scroll: false }); + // Close dialog and clean up URL + setIsOpen(false); + const url = new URL(window.location.href); + url.searchParams.delete("modal"); + url.searchParams.delete("tab"); + url.searchParams.delete("view"); + url.searchParams.delete("connectorType"); + router.replace(url.pathname + url.search, { scroll: false }); // Clear indexing config state setIndexingConfig(null); @@ -1129,19 +1123,19 @@ export const useConnectorDialog = () => { ); } - toast.success(`${indexingConfig.connectorTitle} indexing started`); + toast.success(`${indexingConfig.connectorTitle} indexing started`); - // Close dialog and clean up URL - setIsOpen(false); - const url = new URL(window.location.href); - url.searchParams.delete("modal"); - url.searchParams.delete("tab"); - url.searchParams.delete("success"); - url.searchParams.delete("connector"); - url.searchParams.delete("view"); - router.replace(url.pathname + url.search, { scroll: false }); + // Close dialog and clean up URL + setIsOpen(false); + const url = new URL(window.location.href); + url.searchParams.delete("modal"); + url.searchParams.delete("tab"); + url.searchParams.delete("success"); + url.searchParams.delete("connector"); + url.searchParams.delete("view"); + router.replace(url.pathname + url.search, { scroll: false }); - refreshConnectors(); + refreshConnectors(); queryClient.invalidateQueries({ queryKey: cacheKeys.logs.summary(Number(searchSpaceId)), }); @@ -1421,43 +1415,43 @@ export const useConnectorDialog = () => { : indexingDescription, }); - // Close dialog and clean up URL - setIsOpen(false); - const url = new URL(window.location.href); - url.searchParams.delete("modal"); - url.searchParams.delete("tab"); - url.searchParams.delete("view"); - url.searchParams.delete("connectorId"); - router.replace(url.pathname + url.search, { scroll: false }); + // Close dialog and clean up URL + setIsOpen(false); + const url = new URL(window.location.href); + url.searchParams.delete("modal"); + url.searchParams.delete("tab"); + url.searchParams.delete("view"); + url.searchParams.delete("connectorId"); + router.replace(url.pathname + url.search, { scroll: false }); - refreshConnectors(); - queryClient.invalidateQueries({ - queryKey: cacheKeys.logs.summary(Number(searchSpaceId)), - }); - } catch (error) { - console.error("Error saving connector:", error); - toast.error("Failed to save connector changes"); - } finally { - setIsSaving(false); - } - }, - [ - editingConnector, - searchSpaceId, - isSaving, - startDate, - endDate, - indexConnector, - updateConnector, - periodicEnabled, - frequencyMinutes, - enableSummary, - getFrequencyLabel, - router, - connectorConfig, - connectorName, - setIsOpen, - ] + refreshConnectors(); + queryClient.invalidateQueries({ + queryKey: cacheKeys.logs.summary(Number(searchSpaceId)), + }); + } catch (error) { + console.error("Error saving connector:", error); + toast.error("Failed to save connector changes"); + } finally { + setIsSaving(false); + } + }, + [ + editingConnector, + searchSpaceId, + isSaving, + startDate, + endDate, + indexConnector, + updateConnector, + periodicEnabled, + frequencyMinutes, + enableSummary, + getFrequencyLabel, + router, + connectorConfig, + connectorName, + setIsOpen, + ] ); // Handle disconnecting connector @@ -1484,36 +1478,36 @@ export const useConnectorDialog = () => { : `${editingConnector.name} disconnected successfully` ); - // Update URL - for MCP from list view, go back to list; otherwise close modal - const url = new URL(window.location.href); - if (editingConnector.connector_type === "MCP_CONNECTOR" && cameFromMCPList) { - // Go back to MCP list view only if we came from there - setViewingMCPList(true); - url.searchParams.set("modal", "connectors"); - url.searchParams.set("view", "mcp-list"); - url.searchParams.delete("connectorId"); - } else { - // Close dialog for all other cases - setIsOpen(false); - url.searchParams.delete("modal"); - url.searchParams.delete("tab"); - url.searchParams.delete("view"); - url.searchParams.delete("connectorId"); - } - router.replace(url.pathname + url.search, { scroll: false }); + // Update URL - for MCP from list view, go back to list; otherwise close modal + const url = new URL(window.location.href); + if (editingConnector.connector_type === "MCP_CONNECTOR" && cameFromMCPList) { + // Go back to MCP list view only if we came from there + setViewingMCPList(true); + url.searchParams.set("modal", "connectors"); + url.searchParams.set("view", "mcp-list"); + url.searchParams.delete("connectorId"); + } else { + // Close dialog for all other cases + setIsOpen(false); + url.searchParams.delete("modal"); + url.searchParams.delete("tab"); + url.searchParams.delete("view"); + url.searchParams.delete("connectorId"); + } + router.replace(url.pathname + url.search, { scroll: false }); - refreshConnectors(); - queryClient.invalidateQueries({ - queryKey: cacheKeys.logs.summary(Number(searchSpaceId)), - }); - } catch (error) { - console.error("Error disconnecting connector:", error); - toast.error("Failed to disconnect connector"); - } finally { - setIsDisconnecting(false); - } - }, - [editingConnector, searchSpaceId, deleteConnector, router, cameFromMCPList, setIsOpen] + refreshConnectors(); + queryClient.invalidateQueries({ + queryKey: cacheKeys.logs.summary(Number(searchSpaceId)), + }); + } catch (error) { + console.error("Error disconnecting connector:", error); + toast.error("Failed to disconnect connector"); + } finally { + setIsDisconnecting(false); + } + }, + [editingConnector, searchSpaceId, deleteConnector, router, cameFromMCPList, setIsOpen] ); // Handle quick index (index with selected date range, or backend defaults if none selected) diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index b77edb6b7..38426a47b 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -28,6 +28,13 @@ import { import { useParams } from "next/navigation"; import { type FC, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; +import { + agentToolsAtom, + disabledToolsAtom, + enabledToolCountAtom, + hydrateDisabledToolsAtom, + toggleToolAtom, +} from "@/atoms/agent-tools/agent-tools.atoms"; import { chatSessionStateAtom } from "@/atoms/chat/chat-session-state.atom"; import { mentionedDocumentsAtom, @@ -66,21 +73,14 @@ import { import type { ThinkingStep } from "@/components/tool-ui/deepagent-thinking"; import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Switch } from "@/components/ui/switch"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import type { Document } from "@/contracts/types/document.types"; import { useBatchCommentsPreload } from "@/hooks/use-comments"; import { useCommentsElectric } from "@/hooks/use-comments-electric"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { Switch } from "@/components/ui/switch"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { useMediaQuery } from "@/hooks/use-media-query"; -import { - agentToolsAtom, - disabledToolsAtom, - enabledToolCountAtom, - hydrateDisabledToolsAtom, - toggleToolAtom, -} from "@/atoms/agent-tools/agent-tools.atoms"; import { cn } from "@/lib/utils"; /** Placeholder texts that cycle in new chats when input is empty */ @@ -623,13 +623,13 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false - e.preventDefault()} - > + e.preventDefault()} + >
Agent Tools @@ -648,7 +648,9 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false const isDisabled = disabledTools.includes(tool.name); const row = (
{/* Connected tools strip */} -
-
diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx index d13da7e47..20a3b5b7d 100644 --- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx @@ -217,20 +217,20 @@ export function Sidebar({
4 ? "pb-8" : ""}`} > - {sharedChats.slice(0, 20).map((chat) => ( - setOpenDropdownChatId(open ? chat.id : null)} - onClick={() => onChatSelect(chat)} - onRename={() => onChatRename?.(chat)} - onArchive={() => onChatArchive?.(chat)} - onDelete={() => onChatDelete?.(chat)} - /> - ))} + {sharedChats.slice(0, 20).map((chat) => ( + setOpenDropdownChatId(open ? chat.id : null)} + onClick={() => onChatSelect(chat)} + onRename={() => onChatRename?.(chat)} + onArchive={() => onChatArchive?.(chat)} + onDelete={() => onChatDelete?.(chat)} + /> + ))}
{/* Gradient fade indicator when more than 4 items */} {sharedChats.length > 4 && ( @@ -291,20 +291,20 @@ export function Sidebar({
4 ? "pb-8" : ""}`} > - {chats.slice(0, 20).map((chat) => ( - setOpenDropdownChatId(open ? chat.id : null)} - onClick={() => onChatSelect(chat)} - onRename={() => onChatRename?.(chat)} - onArchive={() => onChatArchive?.(chat)} - onDelete={() => onChatDelete?.(chat)} - /> - ))} + {chats.slice(0, 20).map((chat) => ( + setOpenDropdownChatId(open ? chat.id : null)} + onClick={() => onChatSelect(chat)} + onRename={() => onChatRename?.(chat)} + onArchive={() => onChatArchive?.(chat)} + onDelete={() => onChatDelete?.(chat)} + /> + ))}
{/* Gradient fade indicator when more than 4 items */} {chats.length > 4 && ( diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx index 88bad2a27..02541eab6 100644 --- a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx +++ b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx @@ -9,8 +9,8 @@ import { Laptop, LogOut, Moon, - UserCog, Sun, + UserCog, } from "lucide-react"; import Image from "next/image"; import { useTranslations } from "next-intl"; diff --git a/surfsense_web/hooks/use-google-picker.ts b/surfsense_web/hooks/use-google-picker.ts index fa2a159b9..3b125827a 100644 --- a/surfsense_web/hooks/use-google-picker.ts +++ b/surfsense_web/hooks/use-google-picker.ts @@ -154,17 +154,17 @@ export function useGooglePicker({ connectorId, onPicked }: UseGooglePickerOption } } - if (action === google.picker.Action.ERROR) { - setError("Google Drive encountered an error. Please try again."); - } + if (action === google.picker.Action.ERROR) { + setError("Google Drive encountered an error. Please try again."); + } - if ( - action === google.picker.Action.PICKED || - action === google.picker.Action.CANCEL || - action === google.picker.Action.ERROR - ) { - closePicker(); - } + if ( + action === google.picker.Action.PICKED || + action === google.picker.Action.CANCEL || + action === google.picker.Action.ERROR + ) { + closePicker(); + } }) .build();