diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 873631a83..06a064840 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -12,7 +12,7 @@ import { ArrowUpIcon, Camera, ChevronDown, - ChevronUp, + ChevronRight, Clipboard, Globe, Plus, @@ -72,6 +72,11 @@ import { 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, @@ -378,7 +383,7 @@ const ClipboardChip: FC<{ text: string; onDismiss: () => void }> = ({ text, onDi size="icon" className="size-5 text-muted-foreground hover:bg-transparent hover:text-accent-foreground" > - {expanded ? : } + )} - setToolsPopoverOpen(true)}> - - Manage Tools - openUploadDialog()}> Upload Files @@ -952,6 +971,10 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false Manage Connectors + setToolsPopoverOpen(true)}> + + Manage Tools + = ({ isBlockedByOtherUser = false
- {groupedTools - .filter((g) => !g.connectorIcon) - .map((group) => ( + {regularToolGroups.map((group) => (
{group.label} @@ -996,48 +1017,115 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false })}
))} - {groupedTools.some((g) => g.connectorIcon) && ( + {connectorToolGroups.length > 0 && (
Connector Actions
- {groupedTools - .filter((g) => g.connectorIcon) - .map((group) => { + {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) + } > - {iconInfo ? ( - {iconInfo.alt} + + + + toggleToolGroup(toolNames)} + className="shrink-0" /> - ) : ( - - )} - - {group.label} - - 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 && (
@@ -1063,16 +1151,23 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false ) : ( - !open && setToolsPopoverOpen(false)}> + { + if (!open) { + setToolsPopoverOpen(false); + setOpenConnectorSubmenu(null); + } + }} + > @@ -1113,7 +1208,13 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false /> )} - + { + setToolsPopoverOpen(open); + if (!open) setOpenConnectorSubmenu(null); + }} + > Manage Tools @@ -1123,10 +1224,9 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false alignOffset={-192} collisionPadding={8} className="w-60 h-56 gap-1 overflow-y-auto overscroll-none" + onScroll={() => setOpenConnectorSubmenu(null)} > - {groupedTools - .filter((g) => !g.connectorIcon) - .map((group) => ( + {regularToolGroups.map((group) => (
{group.label} @@ -1161,54 +1261,127 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false })}
))} - {groupedTools.some((g) => g.connectorIcon) && ( + {connectorToolGroups.length > 0 && (
Connector Actions
- {groupedTools - .filter((g) => g.connectorIcon) - .map((group) => { + {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)); return ( - { - e.preventDefault(); - toggleToolGroup(toolNames); - }} - className={cn( - "mb-1 last:mb-0 transition-all", - "hover:bg-accent hover:text-accent-foreground", - !allDisabled && "text-primary" - )} + open={openConnectorSubmenu === group.label} + onOpenChange={(open) => + setOpenConnectorSubmenu(open ? group.label : null) + } > - {iconInfo ? ( - {iconInfo.alt}svg:last-child]:ml-0", + !allDisabled && "text-primary" + )} + > + {iconInfo ? ( + {iconInfo.alt} + ) : ( + + )} + {group.label} + event.stopPropagation()} + onClick={(event) => event.stopPropagation()} + onCheckedChange={() => toggleToolGroup(toolNames)} + className="shrink-0 scale-[0.6]" /> - ) : ( - - )} - {group.label} - - + + + + {group.tools.map((tool) => { + const isDisabled = disabledToolsSet.has(tool.name); + return ( + { + e.preventDefault(); + toggleTool(tool.name); + }} + className={cn( + "mb-1 last:mb-0 transition-all", + "hover:bg-accent hover:text-accent-foreground", + !isDisabled && "text-primary" + )} + > + + {formatToolName(tool.name)} + + + + ); + })} + + + ); })}
)} + {otherToolGroup && ( +
+
+ {otherToolGroup.label} +
+ {otherToolGroup.tools.map((tool) => { + const isDisabled = disabledToolsSet.has(tool.name); + const ToolIcon = getToolIcon(tool.name); + return ( + { + e.preventDefault(); + toggleTool(tool.name); + }} + className={cn( + "mb-1 last:mb-0 transition-all", + "hover:bg-accent hover:text-accent-foreground", + !isDisabled && "text-primary" + )} + > + + + {formatToolName(tool.name)} + + + + ); + })} +
+ )} {!filteredTools?.length && (
@@ -1300,7 +1473,13 @@ const TOOL_GROUPS: ToolGroup[] = [ }, { label: "Generate", - tools: ["generate_podcast", "generate_video_presentation", "generate_report", "generate_image"], + tools: [ + "generate_podcast", + "generate_video_presentation", + "generate_report", + "generate_resume", + "generate_image", + ], }, { label: "Memory", @@ -1308,15 +1487,27 @@ const TOOL_GROUPS: ToolGroup[] = [ }, { label: "Gmail", - tools: ["create_gmail_draft", "update_gmail_draft", "send_gmail_email", "trash_gmail_email"], + tools: [ + "search_gmail", + "read_gmail_email", + "create_gmail_draft", + "update_gmail_draft", + "send_gmail_email", + "trash_gmail_email", + ], connectorIcon: "gmail", - tooltip: "Create drafts, update drafts, send emails, and trash emails in Gmail", + tooltip: "Search, read, draft, update, send, and trash emails in Gmail", }, { label: "Google Calendar", - tools: ["create_calendar_event", "update_calendar_event", "delete_calendar_event"], + tools: [ + "search_calendar_events", + "create_calendar_event", + "update_calendar_event", + "delete_calendar_event", + ], connectorIcon: "google_calendar", - tooltip: "Create, update, and delete events in Google Calendar", + tooltip: "Search, create, update, and delete events in Google Calendar", }, { label: "Google Drive", @@ -1360,6 +1551,24 @@ const TOOL_GROUPS: ToolGroup[] = [ connectorIcon: "confluence", tooltip: "Create, update, and delete pages in Confluence", }, + { + label: "Discord", + tools: ["list_discord_channels", "read_discord_messages", "send_discord_message"], + connectorIcon: "discord", + tooltip: "List channels, read messages, and send messages in Discord", + }, + { + label: "Microsoft Teams", + tools: ["list_teams_channels", "read_teams_messages", "send_teams_message"], + connectorIcon: "teams", + tooltip: "List channels, read messages, and send messages in Microsoft Teams", + }, + { + label: "Luma", + tools: ["list_luma_events", "read_luma_event", "create_luma_event"], + connectorIcon: "luma", + tooltip: "List, read, and create events in Luma", + }, ]; const EditComposer: FC = () => { diff --git a/surfsense_web/components/ui/dropdown-menu.tsx b/surfsense_web/components/ui/dropdown-menu.tsx index fdbdc90d8..421bcfcd2 100644 --- a/surfsense_web/components/ui/dropdown-menu.tsx +++ b/surfsense_web/components/ui/dropdown-menu.tsx @@ -33,7 +33,7 @@ function DropdownMenuContent({ data-slot="dropdown-menu-content" sideOffset={sideOffset} className={cn( - "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 shadow-md", + "bg-popover text-popover-foreground border border-popover-border data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 shadow-md", className )} {...props} @@ -201,7 +201,7 @@ function DropdownMenuSubContent({ = { read_luma_event: Calendar, create_luma_event: Calendar, // Misc - get_connected_accounts: Check, + get_connected_accounts: SearchCheck, execute: Wrench, execute_code: Wrench, }; @@ -200,7 +200,7 @@ const TOOL_DISPLAY_NAMES: Record = { read_luma_event: "Read Luma event", create_luma_event: "Create Luma event", // Misc - get_connected_accounts: "Check connected accounts", + get_connected_accounts: "Discover connected accounts", execute: "Run command", execute_code: "Run code", }; @@ -228,6 +228,9 @@ export const CONNECTOR_TOOL_ICON_PATHS: Record = { @@ -240,4 +243,7 @@ export const CONNECTOR_ICON_TO_TYPES: Record = { linear: ["LINEAR_CONNECTOR"], jira: ["JIRA_CONNECTOR"], confluence: ["CONFLUENCE_CONNECTOR"], + discord: ["DISCORD_CONNECTOR"], + teams: ["TEAMS_CONNECTOR"], + luma: ["LUMA_CONNECTOR"], };