diff --git a/apps/web/src/app/(v2)/chat/threads/page.tsx b/apps/web/src/app/(v2)/chat/threads/page.tsx
index 84566c10..4079194b 100644
--- a/apps/web/src/app/(v2)/chat/threads/page.tsx
+++ b/apps/web/src/app/(v2)/chat/threads/page.tsx
@@ -16,6 +16,7 @@ import { MANAGER_GRAPH_ID } from "@open-swe/shared/constants";
import { useThreadsStatus } from "@/hooks/useThreadsStatus";
import { cn } from "@/lib/utils";
import { threadsToMetadata } from "@/lib/thread-utils";
+import { UserPopover } from "@/components/user-popover";
import { OpenSWELogoSVG } from "@/components/icons/openswe";
type FilterStatus =
@@ -122,8 +123,8 @@ function AllThreadsPageContent() {
-
+
diff --git a/apps/web/src/components/github/github-oauth-button.tsx b/apps/web/src/components/github/github-oauth-button.tsx
deleted file mode 100644
index fe1a1947..00000000
--- a/apps/web/src/components/github/github-oauth-button.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import { LogOut } from "lucide-react";
-import { TooltipIconButton } from "../ui/tooltip-icon-button";
-
-const GITHUB_APP_INSTALLED_KEY = "github_app_installed";
-
-export function GitHubLogoutButton() {
- const [isLoading, setIsLoading] = useState(false);
-
- const handleLogout = async () => {
- setIsLoading(true);
- try {
- const response = await fetch("/api/auth/logout", {
- method: "POST",
- });
- if (response.ok) {
- localStorage.removeItem(GITHUB_APP_INSTALLED_KEY);
- window.location.href = "/";
- } else {
- console.error("Logout failed");
- }
- } catch (error) {
- console.error("Error during logout:", error);
- } finally {
- setIsLoading(false);
- }
- };
-
- return (
-
-
-
- );
-}
diff --git a/apps/web/src/components/user-popover.tsx b/apps/web/src/components/user-popover.tsx
new file mode 100644
index 00000000..85f92dd7
--- /dev/null
+++ b/apps/web/src/components/user-popover.tsx
@@ -0,0 +1,194 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+} from "@/components/ui/select";
+import { Separator } from "@/components/ui/separator";
+import { useGitHubAppProvider } from "@/providers/GitHubApp";
+import { Building2, LogOut, User } from "lucide-react";
+import { GitHubSVG } from "@/components/icons/github";
+import { cn } from "@/lib/utils";
+import { useState } from "react";
+
+interface UserPopoverProps {
+ className?: string;
+}
+
+export function UserPopover({ className }: UserPopoverProps) {
+ const {
+ installations,
+ currentInstallation,
+ installationsLoading: isLoading,
+ installationsError: error,
+ switchInstallation,
+ } = useGitHubAppProvider();
+
+ const [isLoggingOut, setIsLoggingOut] = useState(false);
+
+ const GITHUB_APP_INSTALLED_KEY = "github_app_installed";
+
+ const handleLogout = async () => {
+ setIsLoggingOut(true);
+ try {
+ const response = await fetch("/api/auth/logout", {
+ method: "POST",
+ });
+ if (response.ok) {
+ localStorage.removeItem(GITHUB_APP_INSTALLED_KEY);
+ window.location.href = "/";
+ } else {
+ console.error("Logout failed");
+ }
+ } catch (error) {
+ console.error("Error during logout:", error);
+ } finally {
+ setIsLoggingOut(false);
+ }
+ };
+
+ const handleValueChange = async (value: string) => {
+ await switchInstallation(value);
+ };
+
+ const getAccountIcon = (accountType: "User" | "Organization") => {
+ return accountType === "Organization" ? (
+
+ ) : (
+
+ );
+ };
+
+ if (isLoading || !currentInstallation) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+

+
+
+ {currentInstallation.accountName}
+
+
+ {getAccountIcon(currentInstallation.accountType)}
+
+ {currentInstallation.accountType.toLowerCase()}
+
+
+
+
+
+ {installations.length > 1 && (
+ <>
+
+
+
+
+
+ >
+ )}
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/v2/default-view.tsx b/apps/web/src/components/v2/default-view.tsx
index f05f36a1..9942d14d 100644
--- a/apps/web/src/components/v2/default-view.tsx
+++ b/apps/web/src/components/v2/default-view.tsx
@@ -19,10 +19,9 @@ import { GitHubInstallationBanner } from "../github/installation-banner";
import { ApiKeyBanner } from "../api-key-banner";
import { QuickActions } from "./quick-actions";
import { DraftsSection } from "./drafts-section";
-import { GitHubLogoutButton } from "../github/github-oauth-button";
import { MANAGER_GRAPH_ID } from "@open-swe/shared/constants";
import { TooltipIconButton } from "../ui/tooltip-icon-button";
-import { InstallationSelector } from "../github/installation-selector";
+import { UserPopover } from "../user-popover";
import { useThreadsStatus } from "@/hooks/useThreadsStatus";
import { Thread } from "@langchain/langgraph-sdk";
@@ -126,7 +125,6 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
/>
-
ready
@@ -134,7 +132,7 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
-
+