diff --git a/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx b/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx
index 35ce80c7e9b..b012032188c 100644
--- a/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx
+++ b/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx
@@ -7,21 +7,15 @@ import { useAppDispatch } from "../../redux/hooks";
import { setSelectedProfile } from "../../redux/slices/profilesSlice";
import { CONFIG_ROUTES } from "../../util/navigation";
import { ToolTip } from "../gui/Tooltip";
-import { Button, ListboxOption, useFontSize } from "../ui";
+import { Button, ListboxOption } from "../ui";
import { AssistantIcon } from "./AssistantIcon";
interface AssistantOptionProps {
profile: ProfileDescription;
selected: boolean;
- onClick: () => void;
}
-export function AssistantOption({
- profile,
- selected,
- onClick,
-}: AssistantOptionProps) {
- const tinyFont = useFontSize(-4);
+export function AssistantOption({ profile, selected }: AssistantOptionProps) {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const ideMessenger = useContext(IdeMessengerContext);
@@ -38,8 +32,6 @@ export function AssistantOption({
ideMessenger.post("didChangeSelectedProfile", {
id: profile.id,
});
-
- onClick();
}
return (
diff --git a/gui/src/components/AssistantAndOrgListbox/AssistantOptions.tsx b/gui/src/components/AssistantAndOrgListbox/AssistantOptions.tsx
index a8baff97164..61c070776dc 100644
--- a/gui/src/components/AssistantAndOrgListbox/AssistantOptions.tsx
+++ b/gui/src/components/AssistantAndOrgListbox/AssistantOptions.tsx
@@ -3,13 +3,9 @@ import { AssistantOption } from "./AssistantOption";
interface AssistantOptionsProps {
selectedProfileId: string | undefined;
- onClose: () => void;
}
-export function AssistantOptions({
- selectedProfileId,
- onClose,
-}: AssistantOptionsProps) {
+export function AssistantOptions({ selectedProfileId }: AssistantOptionsProps) {
const { profiles } = useAuth();
return (
@@ -23,7 +19,6 @@ export function AssistantOptions({
))
diff --git a/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx b/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx
index 80c42583d66..d6693761e05 100644
--- a/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx
+++ b/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx
@@ -12,7 +12,6 @@ import { ListboxOption } from "../ui";
interface OrganizationOptionProps {
organization: { id: string; name: string; iconUrl?: string | null };
- onClose: () => void;
}
function getOrgIcon(
@@ -40,10 +39,7 @@ function getOrgIcon(
return ;
}
-export function OrganizationOption({
- organization,
- onClose,
-}: OrganizationOptionProps) {
+export function OrganizationOption({ organization }: OrganizationOptionProps) {
const dispatch = useAppDispatch();
const ideMessenger = useContext(IdeMessengerContext);
const selectedOrgId = useAppSelector(
@@ -56,7 +52,6 @@ export function OrganizationOption({
ideMessenger.post("didChangeSelectedOrg", {
id: organization.id,
});
- onClose();
}
return (
diff --git a/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx b/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx
index d374870d44f..eb09682c70c 100644
--- a/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx
+++ b/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx
@@ -1,17 +1,13 @@
import { useAuth } from "../../context/Auth";
import { OrganizationOption } from "./OrganizationOption";
-interface OrganizationOptionsProps {
- onClose: () => void;
-}
-
-export function OrganizationOptions({ onClose }: OrganizationOptionsProps) {
+export function OrganizationOptions() {
const { organizations } = useAuth();
return (
{organizations.map((org) => (
-
+
))}
);
diff --git a/gui/src/components/AssistantAndOrgListbox/SelectedAssistantButton.tsx b/gui/src/components/AssistantAndOrgListbox/SelectedAssistantButton.tsx
index 09334a9ec12..eb456e3dfc6 100644
--- a/gui/src/components/AssistantAndOrgListbox/SelectedAssistantButton.tsx
+++ b/gui/src/components/AssistantAndOrgListbox/SelectedAssistantButton.tsx
@@ -10,11 +10,13 @@ import { AssistantIcon } from "./AssistantIcon";
interface SelectedAssistantButtonProps {
selectedProfile: ProfileDescription | null;
variant?: "lump" | "sidebar";
+ setOptionsOpen: (open: boolean) => void;
}
export function SelectedAssistantButton({
selectedProfile,
variant,
+ setOptionsOpen,
}: SelectedAssistantButtonProps) {
const configLoading = useAppSelector((store) => store.config.loading);
@@ -28,6 +30,7 @@ export function SelectedAssistantButton({
data-testid="assistant-select-button"
className={`text-description overflow-hidden border-none bg-transparent hover:brightness-110 ${isSidebar ? "w-full justify-start" : "gap-1.5"} ${buttonPadding}`}
style={buttonStyle}
+ onClick={() => setOptionsOpen(true)}
>
(null);
const dispatch = useAppDispatch();
const navigate = useNavigate();
- const listboxRef = useRef
(null);
const currentOrg = useAppSelector(selectCurrentOrg);
const ideMessenger = useContext(IdeMessengerContext);
const {
@@ -57,12 +53,6 @@ export function AssistantAndOrgListbox({
const shouldRenderOrgInfo =
session && organizations.length > 1 && !isOnPremSession(session);
- function close() {
- // Close the listbox by clicking outside or programmatically
- const event = new KeyboardEvent("keydown", { key: "Escape" });
- document.dispatchEvent(event);
- }
-
function onNewAssistant() {
if (session) {
void ideMessenger.request("controlPlane/openUrl", {
@@ -72,34 +62,34 @@ export function AssistantAndOrgListbox({
} else {
void ideMessenger.request("config/newAssistantFile", undefined);
}
- close();
+ setListboxOpen(false);
}
function onNewOrganization() {
void ideMessenger.request("controlPlane/openUrl", {
path: "/organizations/new",
});
- close();
+ setListboxOpen(false);
}
function onAgentsConfig() {
navigate(CONFIG_ROUTES.AGENTS);
- close();
+ setListboxOpen(false);
}
function onOrganizationsConfig() {
navigate(CONFIG_ROUTES.ORGANIZATIONS);
- close();
+ setListboxOpen(false);
}
function onRulesConfig() {
navigate(CONFIG_ROUTES.RULES);
- close();
+ setListboxOpen(false);
}
function onToolsConfig() {
navigate(CONFIG_ROUTES.TOOLS);
- close();
+ setListboxOpen(false);
}
useEffect(() => {
@@ -143,190 +133,188 @@ export function AssistantAndOrgListbox({
};
}, [currentOrg, selectedProfile]);
+ useClickOutside(listboxOptionsRef, () => {
+ setListboxOpen(false);
+ });
+
return (
-
+
setListboxOpen(val)}
/>
-
-
-
-
- Agents
-
-
-
-
-
+
+
+
Agents
+
+
+
+
-
+
- {shouldRenderOrgInfo && (
- <>
-
-
-
- Organizations
-
-
-
-
-
+ {shouldRenderOrgInfo && (
+ <>
+
+
+
+ Organizations
+
+
+
+
+
-
+
-
- >
- )}
+
+ >
+ )}
- {/* Settings Section */}
- {variant !== "sidebar" && (
-
-
+ {/* Settings Section */}
+ {variant !== "sidebar" && (
+
+
+
+
+ {session ? (
+ ) : (
- {session ? (
-
- ) : (
-
- )}
+ )}
-
-
- )}
+
+
+ )}
- {/* Bottom Actions */}
-
-
-
- {getMetaKeyLabel()} ⇧ '
to toggle agent
-
-
+ {/* Bottom Actions */}
+
+
+
+ {getMetaKeyLabel()} ⇧ '
to toggle agent
+
-
-
+
+
);
diff --git a/gui/src/hooks/useClickOutside.tsx b/gui/src/hooks/useClickOutside.tsx
new file mode 100644
index 00000000000..e5edf456880
--- /dev/null
+++ b/gui/src/hooks/useClickOutside.tsx
@@ -0,0 +1,16 @@
+import { useEffect } from "react";
+
+export const useClickOutside = (
+ ref: React.RefObject
,
+ onClickOutsideCallback: (event: MouseEvent) => void,
+) => {
+ useEffect(() => {
+ const onClickOutside = (event: MouseEvent) => {
+ if (ref.current && !ref.current.contains(event.target as Node)) {
+ onClickOutsideCallback(event);
+ }
+ };
+ document.addEventListener("mousedown", onClickOutside);
+ return () => document.removeEventListener("mousedown", onClickOutside);
+ }, [ref.current]);
+};