Use the new shadcn sidebar for khoj nav and actions

- Use the sidebar across all pages to quickly navigate through the app, access settings, and go to past chats
- Pending: mobile friendliness
This commit is contained in:
sabaimran
2024-12-19 20:10:03 -08:00
parent 7eb15bf0a9
commit 68af10c805
16 changed files with 1270 additions and 1353 deletions

View File

@@ -60,6 +60,7 @@ import {
NotePencil,
FunnelSimple,
MagnifyingGlass,
ChatsCircle,
} from "@phosphor-icons/react";
interface ChatHistory {
@@ -71,7 +72,6 @@ interface ChatHistory {
compressed: boolean;
created: string;
updated: string;
showSidePanel: (isEnabled: boolean) => void;
}
import {
@@ -106,6 +106,15 @@ import { KhojLogoType } from "@/app/components/logo/khojLogo";
import NavMenu from "@/app/components/navMenu/navMenu";
import { getIconFromIconName } from "@/app/common/iconUtils";
import LoginPrompt from "../loginPrompt/loginPrompt";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
} from "@/components/ui/sidebar";
// Define a fetcher function
const fetcher = (url: string) =>
@@ -403,7 +412,6 @@ function FilesMenu(props: FilesMenuProps) {
}
interface SessionsAndFilesProps {
setEnabled: (enabled: boolean) => void;
subsetOrganizedData: GroupedChatHistory | null;
organizedData: GroupedChatHistory | null;
data: ChatHistory[] | null;
@@ -411,32 +419,28 @@ interface SessionsAndFilesProps {
conversationId: string | null;
uploadedFiles: string[];
isMobileWidth: boolean;
sideBarOpen: boolean;
}
function SessionsAndFiles(props: SessionsAndFilesProps) {
return (
<>
<div>
{props.data && props.data.length > 5 && (
<ChatSessionsModal
data={props.organizedData}
showSidePanel={props.setEnabled}
/>
)}
<div>
{props.data && props.data.length > 5 && (
<ChatSessionsModal data={props.organizedData} sideBarOpen={props.sideBarOpen} />
)}
{props.sideBarOpen && (
<ScrollArea>
<ScrollAreaScrollbar
orientation="vertical"
className="h-full w-2.5 border-l border-l-transparent p-[1px]"
/>
<div className={styles.sessionsList}>
<div className="p-0 m-0">
{props.subsetOrganizedData != null &&
Object.keys(props.subsetOrganizedData)
.filter((tg) => tg !== "All Time")
.map((timeGrouping) => (
<div key={timeGrouping} className={`my-4`}>
<div
className={`text-muted-foreground text-sm font-bold p-[0.5rem]`}
>
<div key={timeGrouping} className={`my-1`}>
<div className={`text-muted-foreground text-xs p-[0.5rem]`}>
{timeGrouping}
</div>
{props.subsetOrganizedData &&
@@ -454,7 +458,6 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
agent_name={chatHistory.agent_name}
agent_color={chatHistory.agent_color}
agent_icon={chatHistory.agent_icon}
showSidePanel={props.setEnabled}
/>
),
)}
@@ -462,13 +465,8 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
))}
</div>
</ScrollArea>
</div>
<FilesMenu
conversationId={props.conversationId}
uploadedFiles={props.uploadedFiles}
isMobileWidth={props.isMobileWidth}
/>
</>
)}
</div>
);
}
@@ -638,41 +636,31 @@ export function ChatSessionActionMenu(props: ChatSessionActionMenuProps) {
</Button>
)}
<DropdownMenu onOpenChange={(open) => setIsOpen(open)} open={isOpen}>
<DropdownMenuTrigger>
<DotsThreeVertical className={`${size}`} />
<DropdownMenuTrigger asChild>
<SidebarMenuAction>
<DotsThreeVertical className={`${size}`} />
</SidebarMenuAction>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsRenaming(true)}
>
<DropdownMenuContent side="right" align="start">
<DropdownMenuItem onClick={() => setIsRenaming(true)}>
<span className="flex items-center">
<Pencil className={`mr-2 ${size}`} />
Rename
</Button>
</span>
</DropdownMenuItem>
{props.sizing === "sm" && (
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsSharing(true)}
>
<DropdownMenuItem onClick={() => setIsSharing(true)}>
<span className="flex items-center">
<Share className={`mr-2 ${size}`} />
Share
</Button>
</span>
</DropdownMenuItem>
)}
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto text-rose-300 hover:text-rose-400"
variant={"ghost"}
onClick={() => setIsDeleting(true)}
>
<DropdownMenuItem onClick={() => setIsDeleting(true)}>
<span className="flex items-center">
<Trash className={`mr-2 ${size}`} />
Delete
</Button>
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -686,30 +674,36 @@ function ChatSession(props: ChatHistory) {
var currConversationId =
new URLSearchParams(window.location.search).get("conversationId") || "-1";
return (
<div
<SidebarMenuItem
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
key={props.conversation_id}
className={`${styles.session} ${props.compressed ? styles.compressed : "!max-w-full"} ${isHovered ? `${styles.sessionHover}` : ""} ${currConversationId === props.conversation_id && currConversationId != "-1" ? "dark:bg-neutral-800 bg-white" : ""}`}
>
<Link
href={`/chat?conversationId=${props.conversation_id}`}
onClick={() => props.showSidePanel(false)}
>
<p className={styles.session}>{title}</p>
</Link>
<SidebarMenuButton asChild>
<Link
href={`/chat?conversationId=${props.conversation_id}`}
className="flex items-center gap-2 no-underline"
>
<p
className={`${styles.session} ${props.compressed ? styles.compressed : styles.expanded}`}
>
{title}
</p>
</Link>
</SidebarMenuButton>
<ChatSessionActionMenu
conversationId={props.conversation_id}
setTitle={setTitle}
sizing="sm"
/>
</div>
</SidebarMenuItem>
);
}
interface ChatSessionsModalProps {
data: GroupedChatHistory | null;
showSidePanel: (isEnabled: boolean) => void;
sideBarOpen: boolean;
}
interface AgentStyle {
@@ -717,7 +711,7 @@ interface AgentStyle {
icon: string;
}
function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
function ChatSessionsModal({ data, sideBarOpen }: ChatSessionsModalProps) {
const [agentsFilter, setAgentsFilter] = useState<string[]>([]);
const [agentOptions, setAgentOptions] = useState<string[]>([]);
const [searchQuery, setSearchQuery] = useState<string>("");
@@ -786,10 +780,12 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
return (
<Dialog>
<DialogTrigger className="flex text-left text-medium text-gray-500 hover:text-gray-300 cursor-pointer my-1 text-sm p-[0.1rem]">
<DialogTrigger
className={`flex text-left text-medium text-gray-500 hover:text-gray-300 cursor-pointer my-1 text-sm ${sideBarOpen ? "p-[0.5rem] " : "p-[0.1rem]"}`}
>
<span className="flex items-center gap-1">
<MagnifyingGlass className="inline h-4 w-4 mr-1" weight="bold" /> Find
Conversation
<ChatsCircle className="inline h-4 w-4 mr-1" />
{sideBarOpen ? "Find Conversations" : ""}
</span>
</DialogTrigger>
<DialogContent>
@@ -814,7 +810,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{/* <ScrollArea className="h-[200px]"> */}
<DropdownMenuLabel>Agents</DropdownMenuLabel>
<DropdownMenuSeparator />
{agentOptions.map((agent) => (
@@ -841,7 +836,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
</div>
</DropdownMenuCheckboxItem>
))}
{/* </ScrollArea> */}
</DropdownMenuContent>
</DropdownMenu>
</div>
@@ -865,7 +859,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
agent_name={chatHistory.agent_name}
agent_color={chatHistory.agent_color}
agent_icon={chatHistory.agent_icon}
showSidePanel={showSidePanel}
/>
))}
</div>
@@ -902,17 +895,17 @@ interface SidePanelProps {
conversationId: string | null;
uploadedFiles: string[];
isMobileWidth: boolean;
sideBarOpen: boolean;
}
export default function SidePanel(props: SidePanelProps) {
export default function AllConversations(props: SidePanelProps) {
const [data, setData] = useState<ChatHistory[] | null>(null);
const [organizedData, setOrganizedData] = useState<GroupedChatHistory | null>(null);
const [subsetOrganizedData, setSubsetOrganizedData] = useState<GroupedChatHistory | null>(null);
const [enabled, setEnabled] = useState(false);
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
const authenticatedData = useAuthenticatedData();
const { data: chatSessions } = useChatSessionsFetchRequest(
const { data: chatSessions, isLoading } = useChatSessionsFetchRequest(
authenticatedData ? `/api/chat/sessions` : "",
);
@@ -953,135 +946,49 @@ export default function SidePanel(props: SidePanelProps) {
}
}, [chatSessions]);
if (isLoading) {
return (
<SidebarGroup className="p-0 m-0">
<SidebarGroupLabel className="!p-0 m-0 px-0">Conversations</SidebarGroupLabel>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index} className="p-0 list-none m-0">
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarGroup>
);
}
return (
<div
className={`${styles.panel} ${enabled ? styles.expanded : styles.collapsed} ${props.isMobileWidth ? "mt-0" : "mt-1"}`}
>
{showLoginPrompt && (
<LoginPrompt
loginRedirectMessage="Sign in to start chatting"
onOpenChange={setShowLoginPrompt}
isMobileWidth={props.isMobileWidth}
/>
)}
<div className={`flex justify-between flex-row`}>
{props.isMobileWidth ? (
<Drawer
open={enabled}
onOpenChange={(open) => {
if (!enabled) setEnabled(false);
setEnabled(open);
}}
>
<DrawerTrigger>
<Sidebar className="h-6 w-6 mx-2" weight="thin" />
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Sessions and Files</DrawerTitle>
<DrawerDescription>
View all conversation sessions and manage conversation file
filters
</DrawerDescription>
</DrawerHeader>
{authenticatedData ? (
<div className={`${styles.panelWrapper}`}>
<SessionsAndFiles
setEnabled={setEnabled}
subsetOrganizedData={subsetOrganizedData}
organizedData={organizedData}
data={data}
uploadedFiles={props.uploadedFiles}
userProfile={authenticatedData}
conversationId={props.conversationId}
isMobileWidth={props.isMobileWidth}
/>
</div>
) : (
<div className={`${styles.panelWrapper}`}>
{" "}
{/* Redirect to login page */}
<Button
variant="default"
onClick={() => setShowLoginPrompt(true)}
>
<UserCirclePlus className="h-4 w-4 mr-1" />
Sign Up
</Button>
</div>
)}
<DrawerFooter>
<DrawerClose>
<Button variant="outline">Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
) : (
<div className={`grid grid-flow-col gap-4 w-fit`}>
<Link href="/" className="content-center">
<KhojLogoType />
</Link>
<div className="grid grid-flow-col gap-2 items-center">
<Link className="mx-4" href="/">
{enabled ? (
<NotePencil className="h-6 w-6" weight="fill" />
) : (
<NotePencil className="h-6 w-6" color="gray" />
)}
</Link>
<button className={styles.button} onClick={() => setEnabled(!enabled)}>
{enabled ? (
<Sidebar className="h-6 w-6" weight="fill" />
) : (
<Sidebar className="h-6 w-6" color="gray" />
)}
</button>
<SidebarGroup>
<SidebarGroupLabel className="!p-0 m-0 px-0">Conversations</SidebarGroupLabel>
<div className={`flex justify-between flex-col`}>
{authenticatedData && (
<>
<div
className={`${props.sideBarOpen ? "border-l-2 border-light-blue-500 border-opacity-25 " : ""}`}
>
<SessionsAndFiles
subsetOrganizedData={subsetOrganizedData}
organizedData={organizedData}
data={data}
uploadedFiles={props.uploadedFiles}
userProfile={authenticatedData}
conversationId={props.conversationId}
isMobileWidth={props.isMobileWidth}
sideBarOpen={props.sideBarOpen}
/>
</div>
<div className="fixed right-0 top-[0.9rem] w-fit h-fit">
<NavMenu />
</div>
</div>
{props.sideBarOpen && (
<FilesMenu
conversationId={props.conversationId}
uploadedFiles={props.uploadedFiles}
isMobileWidth={props.isMobileWidth}
/>
)}
</>
)}
{props.isMobileWidth && (
<Link href="/" className="content-center h-fit self-center">
<KhojLogoType />
</Link>
)}
{props.isMobileWidth && <NavMenu />}
</div>
{authenticatedData && !props.isMobileWidth && enabled && (
<div className={`${styles.panelWrapper}`}>
<SessionsAndFiles
setEnabled={setEnabled}
subsetOrganizedData={subsetOrganizedData}
organizedData={organizedData}
data={data}
uploadedFiles={props.uploadedFiles}
userProfile={authenticatedData}
conversationId={props.conversationId}
isMobileWidth={props.isMobileWidth}
/>
</div>
)}
{!authenticatedData && enabled && !props.isMobileWidth && (
<div className={`${styles.panelWrapper}`}>
<Link href="/" className="flex flex-col content-start items-start no-underline">
<Button variant="ghost">
<House className="h-4 w-4 mr-1" />
Home
</Button>
<Button variant="ghost">
<StackPlus className="h-4 w-4 mr-1" />
New Conversation
</Button>
</Link>{" "}
<Button variant="default" onClick={() => setShowLoginPrompt(true)}>
<UserCirclePlus className="h-4 w-4 mr-1" />
Sign Up
</Button>
</div>
)}
</div>
</SidebarGroup>
);
}

View File

@@ -74,6 +74,14 @@ p.session {
font-size: small;
}
p.compressed {
width: 12rem;
}
p.expanded {
max-width: 20rem;
}
div.header {
display: grid;
grid-template-columns: 1fr auto;

View File

@@ -0,0 +1,130 @@
import { Calendar, Home, Inbox, Search, Settings } from "lucide-react";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar";
import Link from "next/link";
import {
KhojAgentLogo,
KhojAutomationLogo,
KhojLogo,
KhojLogoType,
KhojSearchLogo,
} from "../logo/khojLogo";
import { Gear } from "@phosphor-icons/react/dist/ssr";
import { House, Plus } from "@phosphor-icons/react";
import { useEffect, useState } from "react";
import { useAuthenticatedData } from "@/app/common/auth";
import AllConversations from "../allConversations/allConversations";
import NavMenu from "../navMenu/navMenu";
import { useSidebar } from "@/components/ui/sidebar";
// Menu items.
const items = [
{
title: "New",
url: "/",
icon: Plus,
},
{
title: "Agents",
url: "/agents",
icon: KhojAgentLogo,
},
{
title: "Automations",
url: "/automations",
icon: KhojAutomationLogo,
},
{
title: "Search",
url: "/search",
icon: KhojSearchLogo,
},
{
title: "Settings",
url: "/settings",
icon: Gear,
},
];
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
const SIDEBAR_WIDTH = "18rem";
const SIDEBAR_WIDTH_MOBILE = "20rem";
interface AppSidebarProps {
conversationId: string | null;
}
export function AppSidebar(props: AppSidebarProps) {
const [isMobileWidth, setIsMobileWidth] = useState(false);
const { state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar } =
useSidebar();
useEffect(() => {
const handleResize = () => {
setIsMobileWidth(window.innerWidth < 768);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return (
<Sidebar collapsible={"icon"}>
<SidebarHeader>
<SidebarMenu className="p-0 m-0">
<SidebarMenuItem className="p-0 m-0">
<SidebarMenuButton asChild>
<a className="flex items-center gap-2 no-underline" href="/">
<KhojLogo className="w-14 h-auto" />
<span className="text-2xl">Khoj</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu className="p-0 m-0">
{items.map((item) => (
<SidebarMenuItem key={item.title} className="p-0 list-none m-0">
<SidebarMenuButton asChild>
<a
href={item.url}
className="flex items-center gap-2 no-underline"
>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
<AllConversations
isMobileWidth={isMobileWidth}
conversationId={props.conversationId}
uploadedFiles={[]}
sideBarOpen={open}
/>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<NavMenu sideBarIsOpen={open} />
</SidebarFooter>
</Sidebar>
);
}

View File

@@ -27,6 +27,8 @@ import { KhojAgentLogo, KhojAutomationLogo, KhojSearchLogo } from "../logo/khojL
import { useIsMobileWidth } from "@/app/common/utils";
import LoginPrompt from "../loginPrompt/loginPrompt";
import { Button } from "@/components/ui/button";
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
import { ChevronUp } from "lucide-react";
function SubscriptionBadge({ is_active }: { is_active: boolean }) {
return (
@@ -48,7 +50,11 @@ function VersionBadge({ version }: { version: string }) {
);
}
export default function NavMenu() {
interface NavMenuProps {
sideBarIsOpen: boolean;
}
export default function NavMenu({ sideBarIsOpen }: NavMenuProps) {
const userData = useAuthenticatedData();
const [darkMode, setDarkMode] = useState(false);
const [initialLoadDone, setInitialLoadDone] = useState(false);
@@ -89,31 +95,34 @@ export default function NavMenu() {
}
return (
<div className={styles.titleBar}>
{showLoginPrompt && (
<LoginPrompt
onOpenChange={setShowLoginPrompt}
isMobileWidth={isMobileWidth}
loginRedirectMessage={"Login to your second brain"}
/>
)}
{isMobileWidth ? (
<SidebarMenu className="border-none p-0 m-0">
<SidebarMenuItem className="p-0 m-0">
<DropdownMenu>
<DropdownMenuTrigger>
{userData ? (
<Avatar
className={`h-10 w-10 border-2 ${userData.is_active ? "border-yellow-500" : "border-stone-700 dark:border-stone-300"}`}
>
<AvatarImage src={userData?.photo} alt="user profile" />
<AvatarFallback className="bg-transparent hover:bg-muted">
{userData?.username[0].toUpperCase()}
</AvatarFallback>
</Avatar>
) : (
<UserCircle className="h-10 w-10" />
)}
<DropdownMenuTrigger asChild>
<SidebarMenuButton className="p-0 m-0 rounded-lg" asChild>
{userData ? (
<span className="flex items-center gap-2">
<Avatar
className={`${sideBarIsOpen ? "h-8 w-8" : "h-6 w-6"} border-2 ${userData.is_active ? "border-yellow-500" : "border-stone-700 dark:border-stone-300"}`}
>
<AvatarImage src={userData?.photo} alt="user profile" />
<AvatarFallback className="bg-transparent hover:bg-muted">
{userData?.username[0].toUpperCase()}
</AvatarFallback>
</Avatar>
{sideBarIsOpen && (
<>
<p>{userData?.username}</p>
<ChevronUp className="w-6 h-6" />
</>
)}
</span>
) : (
<UserCircle className="w-10 h-10" />
)}
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent className="gap-2">
<DropdownMenuContent align="end" className="rounded-xl gap-2">
<DropdownMenuItem className="w-full">
<div className="flex flex-col">
<p className="font-semibold">{userData?.email}</p>
@@ -123,10 +132,10 @@ export default function NavMenu() {
)}
</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSeparator className="dark:bg-white height-[2px] bg-black" />
<DropdownMenuItem
onClick={() => setDarkMode(!darkMode)}
className="w-full cursor-pointer"
className="w-full hover:cursor-pointer"
>
<div className="flex flex-rows">
{darkMode ? (
@@ -140,220 +149,51 @@ export default function NavMenu() {
</div>
</DropdownMenuItem>
<DropdownMenuItem>
<Link href="/agents" className="no-underline w-full">
<Link href="https://docs.khoj.dev" className="no-underline w-full">
<div className="flex flex-rows">
<KhojAgentLogo className="w-6 h-6" />
<p className="ml-3 font-semibold">Agents</p>
<Question className="w-6 h-6" />
<p className="ml-3 font-semibold">Help</p>
</div>
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link href="/automations" className="no-underline w-full">
<div className="flex flex-rows">
<KhojAutomationLogo className="w-6 h-6" />
<p className="ml-3 font-semibold">Automations</p>
</div>
</Link>
</DropdownMenuItem>
{userData && (
<DropdownMenuItem>
<Link href="/search" className="no-underline w-full">
<div className="flex flex-rows">
<KhojSearchLogo className="w-6 h-6" />
<p className="ml-3 font-semibold">Search</p>
</div>
</Link>
</DropdownMenuItem>
)}
{userData && (
<DropdownMenuItem>
<Link href="/settings" className="no-underline w-full">
<div className="flex flex-rows">
<GearFine className="w-6 h-6" />
<p className="ml-3 font-semibold">Settings</p>
</div>
</Link>
</DropdownMenuItem>
)}
<>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Link href="https://docs.khoj.dev" className="no-underline w-full">
<div className="flex flex-rows">
<Question className="w-6 h-6" />
<p className="ml-3 font-semibold">Help</p>
</div>
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link
href="https://github.com/khoj-ai/khoj/releases"
className="no-underline w-full"
>
<div className="flex flex-rows">
<Code className="w-6 h-6" />
<p className="ml-3 font-semibold">Releases</p>
</div>
</Link>
</DropdownMenuItem>
{userData ? (
<DropdownMenuItem>
<Link href="/auth/logout" className="no-underline w-full">
<div className="flex flex-rows">
<ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Logout</p>
</div>
</Link>
</DropdownMenuItem>
) : (
<DropdownMenuItem>
<Button
variant={"ghost"}
onClick={() => setShowLoginPrompt(true)}
className="no-underline w-full text-left p-0 content-start justify-start items-start h-fit"
>
<div className="flex flex-rows text-left content-start justify-start items-start p-0">
<ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Login</p>
</div>
</Button>
</DropdownMenuItem>
)}
</>
</DropdownMenuContent>
</DropdownMenu>
) : (
<Menubar className="border-none">
<MenubarMenu>
<MenubarTrigger>
{userData ? (
<Avatar
className={`h-10 w-10 border-2 ${userData.is_active ? "border-yellow-500" : "border-stone-700 dark:border-stone-300"}`}
>
<AvatarImage src={userData?.photo} alt="user profile" />
<AvatarFallback className="bg-transparent hover:bg-muted">
{userData?.username[0].toUpperCase()}
</AvatarFallback>
</Avatar>
) : (
<UserCircle className="w-10 h-10" />
)}
</MenubarTrigger>
<MenubarContent align="end" className="rounded-xl gap-2">
<MenubarItem className="w-full">
<div className="flex flex-col">
<p className="font-semibold">{userData?.email}</p>
<SubscriptionBadge is_active={userData?.is_active ?? false} />
{userData?.khoj_version && (
<VersionBadge version={userData?.khoj_version} />
)}
</div>
</MenubarItem>
<MenubarSeparator className="dark:bg-white height-[2px] bg-black" />
<MenubarItem
onClick={() => setDarkMode(!darkMode)}
className="w-full hover:cursor-pointer"
<Link
href="https://github.com/khoj-ai/khoj/releases"
className="no-underline w-full"
>
<div className="flex flex-rows">
{darkMode ? (
<Sun className="w-6 h-6" />
) : (
<Moon className="w-6 h-6" />
)}
<p className="ml-3 font-semibold">
{darkMode ? "Light Mode" : "Dark Mode"}
</p>
<Code className="w-6 h-6" />
<p className="ml-3 font-semibold">Releases</p>
</div>
</MenubarItem>
<MenubarItem>
<Link href="/agents" className="no-underline w-full">
</Link>
</DropdownMenuItem>
{userData ? (
<DropdownMenuItem>
<Link href="/auth/logout" className="no-underline w-full">
<div className="flex flex-rows">
<KhojAgentLogo className="w-6 h-6" />
<p className="ml-3 font-semibold">Agents</p>
<ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Logout</p>
</div>
</Link>
</MenubarItem>
<MenubarItem>
<Link href="/automations" className="no-underline w-full">
<div className="flex flex-rows">
<KhojAutomationLogo className="w-6 h-6" />
<p className="ml-3 font-semibold">Automations</p>
</DropdownMenuItem>
) : (
<DropdownMenuItem>
<Button
variant={"ghost"}
onClick={() => setShowLoginPrompt(true)}
className="no-underline w-full text-left p-0 content-start justify-start items-start h-fit"
>
<div className="flex flex-rows text-left content-start justify-start items-start p-0">
<ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Login</p>
</div>
</Link>
</MenubarItem>
{userData && (
<MenubarItem>
<Link href="/search" className="no-underline w-full">
<div className="flex flex-rows">
<KhojSearchLogo className="w-6 h-6" />
<p className="ml-3 font-semibold">Search</p>
</div>
</Link>
</MenubarItem>
)}
{userData && (
<MenubarItem>
<Link href="/settings" className="no-underline w-full">
<div className="flex flex-rows">
<GearFine className="w-6 h-6" />
<p className="ml-3 font-semibold">Settings</p>
</div>
</Link>
</MenubarItem>
)}
<>
<MenubarSeparator className="dark:bg-white height-[2px] bg-black" />
<MenubarItem>
<Link
href="https://docs.khoj.dev"
className="no-underline w-full"
>
<div className="flex flex-rows">
<Question className="w-6 h-6" />
<p className="ml-3 font-semibold">Help</p>
</div>
</Link>
</MenubarItem>
<MenubarItem>
<Link
href="https://github.com/khoj-ai/khoj/releases"
className="no-underline w-full"
>
<div className="flex flex-rows">
<Code className="w-6 h-6" />
<p className="ml-3 font-semibold">Releases</p>
</div>
</Link>
</MenubarItem>
{userData ? (
<MenubarItem>
<Link href="/auth/logout" className="no-underline w-full">
<div className="flex flex-rows">
<ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Logout</p>
</div>
</Link>
</MenubarItem>
) : (
<MenubarItem>
<Button
variant={"ghost"}
onClick={() => setShowLoginPrompt(true)}
className="no-underline w-full text-left p-0 content-start justify-start items-start h-fit"
>
<div className="flex flex-rows text-left content-start justify-start items-start p-0">
<ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Login</p>
</div>
</Button>
</MenubarItem>
)}
</>
</MenubarContent>
</MenubarMenu>
</Menubar>
)}
</div>
</Button>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}