From 7ed574c4080e7c971714e1f1eb3408add5a2b762 Mon Sep 17 00:00:00 2001
From: Ben Senescu <44480372+bensenescu@users.noreply.github.com>
Date: Fri, 3 Jul 2026 13:23:18 -0400
Subject: [PATCH] Sidebar: promote AI & MCP to nav, consolidate footer into
account menu (#337)
---
src/client/components/Sidebar.tsx | 127 ++++++++++--------
.../features/projects/ProjectSwitcher.tsx | 7 +-
src/client/lib/dropdown.ts | 7 +
src/client/navigation/items.ts | 8 +-
4 files changed, 85 insertions(+), 64 deletions(-)
create mode 100644 src/client/lib/dropdown.ts
diff --git a/src/client/components/Sidebar.tsx b/src/client/components/Sidebar.tsx
index 9e0213b..453d6fa 100644
--- a/src/client/components/Sidebar.tsx
+++ b/src/client/components/Sidebar.tsx
@@ -9,8 +9,13 @@ import {
User,
X,
} from "lucide-react";
-import { aiNavItem, getProjectNavGroups } from "@/client/navigation/items";
+import {
+ connectNavGroup,
+ getProjectNavGroups,
+} from "@/client/navigation/items";
import { ProjectSwitcher } from "@/client/features/projects/ProjectSwitcher";
+import { ThemePreferenceMenuItems } from "@/client/components/ThemePreferenceMenuItems";
+import { closeDropdown } from "@/client/lib/dropdown";
import { signOutAndRedirect, useSession } from "@/lib/auth-client";
import { isHostedClientAuthMode } from "@/lib/auth-mode";
import { BILLING_ROUTE } from "@/shared/billing";
@@ -63,7 +68,10 @@ function SidebarNavLink({
}
export function Sidebar({ projectId, onNavigate, onClose }: SidebarProps) {
- const navGroups = projectId ? getProjectNavGroups(projectId) : [];
+ const navGroups = [
+ ...(projectId ? getProjectNavGroups(projectId) : []),
+ connectNavGroup,
+ ];
return (
@@ -125,76 +133,81 @@ function SidebarFooter({ onNavigate }: { onNavigate?: () => void }) {
const { data: session } = useSession();
const isHostedMode = isHostedClientAuthMode();
const email = session?.user?.email;
- const { icon: aiIcon, label: aiLabel, ...aiLinkProps } = aiNavItem;
+
+ const closeMenu = () => {
+ closeDropdown();
+ onNavigate?.();
+ };
return (
-
- {isHostedMode ? (
-
- ) : null}
-
{email ? (
- isHostedMode ? (
-
-
-
- -
-
-
-
-
- ) : (
-
+
+
- )
- ) : null}
+
+
+ -
+
+
+ Settings
+
+
+ {isHostedMode ? (
+ -
+
+
+ Billing
+
+
+ ) : null}
+
+ {isHostedMode ? (
+ <>
+
+ -
+
+
+ >
+ ) : null}
+
+
+ ) : (
+
+ )}
);
}
diff --git a/src/client/features/projects/ProjectSwitcher.tsx b/src/client/features/projects/ProjectSwitcher.tsx
index 5412f5e..1da25d2 100644
--- a/src/client/features/projects/ProjectSwitcher.tsx
+++ b/src/client/features/projects/ProjectSwitcher.tsx
@@ -3,14 +3,9 @@ import { useQuery } from "@tanstack/react-query";
import { Check, ChevronsUpDown, FolderCog } from "lucide-react";
import { getProjects } from "@/serverFunctions/projects";
import { setLastProjectId } from "@/client/lib/active-project";
+import { closeDropdown } from "@/client/lib/dropdown";
import type { ProjectSummary } from "./types";
-function closeDropdown() {
- if (document.activeElement instanceof HTMLElement) {
- document.activeElement.blur();
- }
-}
-
export function ProjectSwitcher({
activeProjectId,
onCloseDrawer,
diff --git a/src/client/lib/dropdown.ts b/src/client/lib/dropdown.ts
new file mode 100644
index 0000000..0265f2e
--- /dev/null
+++ b/src/client/lib/dropdown.ts
@@ -0,0 +1,7 @@
+// daisyUI dropdowns stay open until focus leaves, so blur the trigger to
+// close the menu before navigating or acting on a selection.
+export function closeDropdown() {
+ if (document.activeElement instanceof HTMLElement) {
+ document.activeElement.blur();
+ }
+}
diff --git a/src/client/navigation/items.ts b/src/client/navigation/items.ts
index b2ef682..8964635 100644
--- a/src/client/navigation/items.ts
+++ b/src/client/navigation/items.ts
@@ -60,12 +60,18 @@ const projectNavItems = [
},
] as const;
-export const aiNavItem = linkOptions({
+const aiNavItem = linkOptions({
to: "/ai" as const,
label: "AI & MCP",
icon: Bot,
});
+// Always-visible sidebar group (not project-scoped, unlike the groups below).
+export const connectNavGroup = {
+ label: "Connect",
+ items: [aiNavItem],
+};
+
function getProjectNavItems(projectId: string) {
return linkOptions(
projectNavItems.map((item) => ({