From a0ce494dd97978bcccb73e40b73952529f47376b Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:31:26 +0530 Subject: [PATCH] feat(composer-action): enhance connector management with grouped rows and improved state handling --- .../components/assistant-ui/thread.tsx | 319 +++++------------- 1 file changed, 91 insertions(+), 228 deletions(-) diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 5c31af139..c3663e3fc 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -12,8 +12,8 @@ import { ArrowUpIcon, Camera, ChevronDown, - ChevronRight, Clipboard, + LayoutGrid, Plus, Settings2, SquareIcon, @@ -43,6 +43,7 @@ import { clearPremiumAlertForThreadAtom, premiumAlertByThreadAtom, } from "@/atoms/chat/premium-alert.atom"; +import { importConnectorRequestAtom } from "@/atoms/connector-dialog/connector-dialog.atoms"; import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms"; import { membersAtom } from "@/atoms/members/members-query.atoms"; import { llmSetupStatusAtomFamily } from "@/atoms/model-connections/model-connections-query.atoms"; @@ -50,6 +51,11 @@ import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { AssistantMessage } from "@/components/assistant-ui/assistant-message"; import { ChatSessionStatus } from "@/components/assistant-ui/chat-session-status"; import { ChatViewport } from "@/components/assistant-ui/chat-viewport"; +import { ComposerAddMenuDrawer } from "@/components/assistant-ui/composer-add-menu-drawer"; +import { + type ConnectorTypeRow, + groupConnectorsByType, +} from "@/components/assistant-ui/connector-popup/constants/connector-constants"; import { useDocumentUploadDialog } from "@/components/assistant-ui/document-upload-popup"; import { InlineMentionEditor, @@ -66,19 +72,12 @@ import { ComposerSuggestionPopoverContent } from "@/components/new-chat/composer import { PromptPicker, type PromptPickerRef } from "@/components/new-chat/prompt-picker"; import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import { - Drawer, - DrawerContent, - DrawerHandle, - DrawerHeader, - DrawerTitle, -} from "@/components/ui/drawer"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, + DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, @@ -88,12 +87,14 @@ import { Popover, PopoverAnchor } from "@/components/ui/popover"; import { Skeleton } from "@/components/ui/skeleton"; import { Switch } from "@/components/ui/switch"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { CONNECTOR_ICON_TO_TYPES, CONNECTOR_TOOL_ICON_PATHS, getToolDisplayName, getToolIcon, } from "@/contracts/enums/toolIcons"; +import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import { useBatchCommentsPreload } from "@/hooks/use-comments"; import { useCommentsSync } from "@/hooks/use-comments-sync"; import { useMediaQuery } from "@/hooks/use-media-query"; @@ -1052,9 +1053,6 @@ const ComposerAction: FC = ({ ); const [toolsPopoverOpen, setToolsPopoverOpen] = useState(false); const [openConnectorSubmenu, setOpenConnectorSubmenu] = useState(null); - const [expandedConnectorGroups, setExpandedConnectorGroups] = useState>( - () => new Set() - ); const isDesktop = useMediaQuery("(min-width: 640px)"); const { openDialog: openUploadDialog } = useDocumentUploadDialog(); const pendingScreenImages = useAtomValue(pendingUserImageDataUrlsAtom); @@ -1089,6 +1087,23 @@ const ComposerAction: FC = ({ () => new Set((connectors ?? []).map((c) => c.connector_type)), [connectors] ); + // "Your connectors" rows for the add-menu (desktop submenu + mobile drawer), + // grouped by type via the same helper the connectors panel rail uses. + const connectorRows = useMemo( + () => groupConnectorsByType((connectors ?? []) as SearchSourceConnector[]), + [connectors] + ); + const setImportRequest = useSetAtom(importConnectorRequestAtom); + const openConnectorManage = useCallback( + (row: ConnectorTypeRow) => { + // Deep-link into the connector's manage view: the panel's + // useConnectorDialog consumes this atom and routes by account count + // (none -> connect, one -> edit, many -> accounts list). + setImportRequest({ connectorType: row.type, mode: "auto" }); + openConnectors(); + }, + [setImportRequest, openConnectors] + ); const toggleToolGroup = useCallback( (toolNames: string[]) => { @@ -1101,18 +1116,6 @@ const ComposerAction: FC = ({ }, [disabledToolsSet, setDisabledTools] ); - const setConnectorGroupExpanded = useCallback((label: string, expanded: boolean) => { - setExpandedConnectorGroups((prev) => { - const next = new Set(prev); - if (expanded) { - next.add(label); - } else { - next.delete(label); - } - return next; - }); - }, []); - const filteredTools = agentTools; const groupedTools = useMemo(() => { if (!filteredTools) return []; @@ -1181,206 +1184,30 @@ const ComposerAction: FC = ({
{!isDesktop ? ( - <> - - - - - - openUploadDialog()}> - - Upload Files - - - - MCP Connectors - - setToolsPopoverOpen(true)}> - - Manage Tools - - - - - - - - - Manage Tools - - -
- {regularToolGroups.map((group) => ( -
-
- {group.label} -
- {group.tools.map((tool) => { - const isDisabled = disabledToolsSet.has(tool.name); - const ToolIcon = getToolIcon(tool.name); - return ( -
- - - {formatToolName(tool.name)} - - toggleTool(tool.name)} - className="shrink-0" - /> -
- ); - })} -
- ))} - {connectorToolGroups.length > 0 && ( -
-
- Connector Actions -
- {connectorToolGroups.map((group) => { - const iconKey = group.connectorIcon ?? ""; - const iconInfo = CONNECTOR_TOOL_ICON_PATHS[iconKey]; - const toolNames = group.tools.map((t) => t.name); - const allDisabled = toolNames.every((n) => disabledToolsSet.has(n)); - const isExpanded = expandedConnectorGroups.has(group.label); - return ( - setConnectorGroupExpanded(group.label, open)} - > -
- - - - toggleToolGroup(toolNames)} - className="shrink-0" - /> -
- - {group.tools.map((tool) => { - const isDisabled = disabledToolsSet.has(tool.name); - return ( -
- - {formatToolName(tool.name)} - - toggleTool(tool.name)} - className="shrink-0" - /> -
- ); - })} -
-
- ); - })} -
- )} - {otherToolGroup && ( -
-
- {otherToolGroup.label} -
- {otherToolGroup.tools.map((tool) => { - const isDisabled = disabledToolsSet.has(tool.name); - const ToolIcon = getToolIcon(tool.name); - return ( -
- - - {formatToolName(tool.name)} - - toggleTool(tool.name)} - className="shrink-0" - /> -
- ); - })} -
- )} - {!filteredTools?.length && ( -
- - {["t1", "t2", "t3", "t4"].map((k) => ( -
- - - -
- ))} - - {["c1", "c2", "c3"].map((k) => ( -
- - - -
- ))} -
- )} -
-
-
- + + + + } + onUploadFiles={() => openUploadDialog()} + connectorRows={connectorRows} + onSelectConnector={openConnectorManage} + onBrowseConnectors={openConnectors} + regularToolGroups={regularToolGroups} + connectorToolGroups={connectorToolGroups} + otherToolGroup={otherToolGroup} + disabledToolsSet={disabledToolsSet} + onToggleTool={toggleTool} + onToggleToolGroup={toggleToolGroup} + toolsLoading={!filteredTools?.length} + /> ) : ( { @@ -1419,10 +1246,46 @@ const ComposerAction: FC = ({ Take a screenshot - - - MCP Connectors - + {connectorRows.length > 0 ? ( + + + + MCP Connectors + + + + {connectorRows.map((row) => ( + openConnectorManage(row)} + className="gap-2" + > + {getConnectorIcon(row.type, "h-4 w-4")} + {row.title} + {row.accountCount > 1 ? ( + + {row.accountCount} + + ) : null} + + ))} + + + + Browse all connectors + + + + + ) : ( + + + MCP Connectors + + )} {