From b52e5780211521d49bfe8b2d82a5c671ae0cd644 Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Sun, 17 May 2026 00:57:35 +0530 Subject: [PATCH] refactor: replace Popover with DropdownMenu in ComposerAction and clean up unused state management --- .../components/assistant-ui/thread.tsx | 299 ++++++++---------- surfsense_web/components/ui/tooltip.tsx | 2 +- 2 files changed, 129 insertions(+), 172 deletions(-) diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 7994af8ac..92bcc31e9 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -14,7 +14,6 @@ import { ChevronDown, ChevronUp, Clipboard, - Dot, Globe, Plus, Settings2, @@ -83,12 +82,14 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { Popover, PopoverContent, PopoverTrigger } 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, @@ -776,24 +777,6 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false const [toolsPopoverOpen, setToolsPopoverOpen] = useState(false); const isDesktop = useMediaQuery("(min-width: 640px)"); const { openDialog: openUploadDialog } = useDocumentUploadDialog(); - const [toolsScrollPos, setToolsScrollPos] = useState<"top" | "middle" | "bottom">("top"); - const toolsRafRef = useRef(undefined); - const handleToolsScroll = useCallback((e: React.UIEvent) => { - const el = e.currentTarget; - if (toolsRafRef.current) return; - toolsRafRef.current = requestAnimationFrame(() => { - const atTop = el.scrollTop <= 2; - const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= 2; - setToolsScrollPos(atTop ? "top" : atBottom ? "bottom" : "middle"); - toolsRafRef.current = undefined; - }); - }, []); - useEffect( - () => () => { - if (toolsRafRef.current) cancelAnimationFrame(toolsRafRef.current); - }, - [] - ); const pendingScreenImages = useAtomValue(pendingUserImageDataUrlsAtom); const setPendingScreenImages = useSetAtom(pendingUserImageDataUrlsAtom); const electronAPI = useElectronAPI(); @@ -943,10 +926,7 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false Manage Tools -
+
{groupedTools .filter((g) => !g.connectorIcon) .map((group) => ( @@ -1043,153 +1023,143 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false ) : ( - - + !open && setToolsPopoverOpen(false)}> + - + - - e.preventDefault()} - > -
Manage Tools
-
- {groupedTools - .filter((g) => !g.connectorIcon) - .map((group) => ( -
-
- {group.label} -
- {group.tools.map((tool) => { - const isDisabled = disabledToolsSet.has(tool.name); - const ToolIcon = getToolIcon(tool.name); - const row = ( -
- - - {formatToolName(tool.name)} - - toggleTool(tool.name)} - className="shrink-0 scale-50 sm:scale-[0.6]" - /> -
- ); - return ( - - {row} - - {tool.description} - - - ); - })} -
- ))} - {groupedTools.some((g) => g.connectorIcon) && ( -
-
- Connector Actions -
+ + + openUploadDialog()}> + + Upload Files + + void handleScreenCapture()}> + + Take a screenshot + + + + + Manage Tools + + + {groupedTools - .filter((g) => g.connectorIcon) - .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 groupDef = TOOL_GROUPS.find((g) => g.label === group.label); - const row = ( -
- {iconInfo ? ( - {iconInfo.alt} - ) : ( - - )} - - {group.label} - - toggleToolGroup(toolNames)} - className="shrink-0 scale-50 sm:scale-[0.6]" - /> + .filter((g) => !g.connectorIcon) + .map((group) => ( +
+
+ {group.label}
- ); - return ( - - {row} - - {groupDef?.tooltip ?? - group.tools.flatMap((t, i) => - i === 0 - ? [t.description] - : [ - , - t.description, - ] + {group.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" )} - - - ); - })} -
- )} - {!filteredTools?.length && ( -
- - {["dt1", "dt2", "dt3", "dt4"].map((k) => ( -
- - - + > + + + {formatToolName(tool.name)} + + + + ); + })} +
+ ))} + {groupedTools.some((g) => g.connectorIcon) && ( +
+
+ Connector Actions +
+ {groupedTools + .filter((g) => g.connectorIcon) + .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" + )} + > + {iconInfo ? ( + {iconInfo.alt} + ) : ( + + )} + {group.label} + + + ); + })}
- ))} - - {["dc1", "dc2", "dc3"].map((k) => ( -
- - - + )} + {!filteredTools?.length && ( +
+ + {["dt1", "dt2", "dt3", "dt4"].map((k) => ( +
+ + + +
+ ))}
- ))} -
- )} -
- - + )} + + + + + )} {hasWebSearchTool && (
)}
- {isDesktop && ( - void handleScreenCapture()} - > - - - )} !thread.isRunning}>