diff --git a/package.json b/package.json
index c7809ef..4022abc 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"test": "vitest run"
},
"dependencies": {
+ "@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-toast": "^1.2.14",
"@radix-ui/react-tooltip": "^1.2.7",
"@tailwindcss/vite": "^4.1.11",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dcd6d8b..f294231 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@radix-ui/react-dropdown-menu':
+ specifier: ^2.1.15
+ version: 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-toast':
specifier: ^1.2.14
version: 1.2.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
diff --git a/src/components/main-menu/chat-cell.tsx b/src/components/main-menu/chat-cell.tsx
new file mode 100644
index 0000000..975b47d
--- /dev/null
+++ b/src/components/main-menu/chat-cell.tsx
@@ -0,0 +1,71 @@
+import { Dropdown } from "../ui/dropdown";
+import { cn } from "@/lib/utils";
+import { Link, useMatchRoute } from "@tanstack/react-router";
+import { useState } from "react";
+import ThreeDots from "@/icons/three-dots.svg?react";
+import Edit from "@/icons/edit.svg?react";
+import Share from "@/icons/share.svg?react";
+import Trash from "@/icons/trash.svg?react";
+
+export const ChatCell = ({ chat }: { chat: { id: string; title: string } }) => {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+
+ const matchRoute = useMatchRoute();
+ const match = matchRoute({ to: "/interaction-panel/$id", fuzzy: true });
+
+ const activeChatId = match ? match.id : undefined;
+
+ const DROPDOWN_OPTIONS = [
+ {
+ name: "Share",
+ icon: ,
+ onClick: () => console.log("1"),
+ },
+ {
+ name: "Rename",
+ icon: ,
+ onClick: () => console.log("2"),
+ },
+ {
+ name: "Delete",
+ icon: ,
+ onClick: () => console.log("3"),
+ className: "text-feedback-negative-900",
+ },
+ ];
+
+ return (
+
+
+
+ {chat.title}
+
+
+
+
setIsDropdownOpen(!isDropdownOpen)}
+ className="cursor-pointer flex items-center pr-3 justify-end w-5 h-full"
+ >
+
+
+
+ );
+};
diff --git a/src/components/main-menu/main-menu.tsx b/src/components/main-menu/main-menu.tsx
index b945932..29042e1 100644
--- a/src/components/main-menu/main-menu.tsx
+++ b/src/components/main-menu/main-menu.tsx
@@ -4,16 +4,19 @@ import Plus from "@/icons/plus.svg?react";
import Books from "@/icons/books.svg?react";
import Discord from "@/icons/discord.svg?react";
import Profile from "@/icons/profile.svg?react";
-import ThreeDots from "@/icons/three-dots.svg?react";
+import Settings from "@/icons/settings.svg?react";
+import SignOut from "@/icons/sign-out.svg?react";
import { LINKS } from "@/lib/constants";
import { useEffect, useRef, useState, type ReactNode } from "react";
import { Button, type ButtonVariantType } from "../ui/button";
-import { Link, useMatchRoute, useParams } from "@tanstack/react-router";
+import { Link } from "@tanstack/react-router";
import Logo from "@/icons/logo.svg?react";
import LogoWithText from "@/icons/logo-with-text.svg?react";
import Sidebar from "@/icons/sidebar.svg?react";
+import { ChatCell } from "./chat-cell";
+import { Dropdown } from "../ui/dropdown";
interface MenuItemType extends ButtonVariantType {
name: string;
@@ -21,27 +24,6 @@ interface MenuItemType extends ButtonVariantType {
path: string;
}
-const MENU_ITEMS: MenuItemType[] = [
- {
- name: "New Chat",
- icon: ,
- path: "/",
- variant: "primary",
- },
- {
- name: "All chats",
- icon: ,
- path: "/all-chats",
- variant: "secondary",
- },
- {
- name: "Support",
- icon: ,
- path: LINKS.discord,
- variant: "secondary",
- },
-];
-
const MainMenu = observer(() => {
const { isMainMenuOpen } = useAccountStore();
@@ -61,6 +43,8 @@ const MOCK_CHATS = Array.from({ length: 20 }, (_, i) => ({
}));
const MainMenuContent = observer(() => {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+
const { isMainMenuOpen, setIsMainMenuOpen } = useAccountStore();
const containerRef = useRef(null);
const [hasScrolled, setHasScrolled] = useState(false);
@@ -81,6 +65,25 @@ const MainMenuContent = observer(() => {
};
}, []);
+ const DROPDOWN_OPTIONS = [
+ {
+ name: "user-mail",
+ icon: ,
+ onClick: () => console.log("1"),
+ className: "text-text-light-500",
+ },
+ {
+ name: "Settings",
+ icon: ,
+ onClick: () => console.log("2"),
+ },
+ {
+ name: "Sigh out",
+ icon: ,
+ onClick: () => console.log("3"),
+ },
+ ];
+
return (
{
-
);
@@ -190,37 +201,25 @@ const MenuItem = ({ item }: { item: MenuItemType }) => {
);
};
-const ChatCell = ({ chat }: { chat: { id: string; title: string } }) => {
- const matchRoute = useMatchRoute();
- const match = matchRoute({ to: "/interaction-panel/$id", fuzzy: true });
-
- const activeChatId = match ? match.id : undefined;
-
- return (
-
-
-
- {chat.title}
-
-
-
-
-
- );
-};
+const MENU_ITEMS: MenuItemType[] = [
+ {
+ name: "New Chat",
+ icon: ,
+ path: "/",
+ variant: "primary",
+ },
+ {
+ name: "All chats",
+ icon: ,
+ path: "/all-chats",
+ variant: "secondary",
+ },
+ {
+ name: "Support",
+ icon: ,
+ path: LINKS.discord,
+ variant: "secondary",
+ },
+];
export { MainMenu, MenuItem, MENU_ITEMS, MainMenuContent };
diff --git a/src/components/ui/dropdown.tsx b/src/components/ui/dropdown.tsx
new file mode 100644
index 0000000..cae1798
--- /dev/null
+++ b/src/components/ui/dropdown.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
+import { FC, type ReactNode } from "react";
+import clsx from "clsx";
+import { cn } from "@/lib/utils";
+
+interface DropdownOption {
+ name: string;
+ icon?: ReactNode;
+ onClick: () => void;
+ className?: string;
+}
+
+interface DropdownProps {
+ children: ReactNode;
+ options: DropdownOption[];
+ isOpen: boolean;
+ onToggle: () => void;
+ classNameContent?: string;
+ classNameTrigger?: string;
+}
+
+const Dropdown: FC = ({
+ children,
+ options = [],
+ isOpen,
+ onToggle,
+ classNameContent = "",
+ classNameTrigger = "",
+}) => {
+ return (
+
+
+
+
+
+
+
+ {options.map((option, index) => (
+ {
+ e.preventDefault();
+ option.onClick();
+ }}
+ className={cn(
+ "focus:outline-none flex items-center gap-2 h-8 px-2 text-sm text-text-light-900 hover:bg-fill-100 cursor-pointer rounded-[8px] transition-colors",
+ option.className,
+ )}
+ >
+ {option.icon && (
+
+ {option.icon}
+
+ )}
+ {option.name}
+
+ ))}
+
+
+
+ );
+};
+
+export { Dropdown };
diff --git a/src/icons/edit.svg b/src/icons/edit.svg
new file mode 100644
index 0000000..3a1259b
--- /dev/null
+++ b/src/icons/edit.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/settings.svg b/src/icons/settings.svg
new file mode 100644
index 0000000..c444733
--- /dev/null
+++ b/src/icons/settings.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/share.svg b/src/icons/share.svg
new file mode 100644
index 0000000..96ba202
--- /dev/null
+++ b/src/icons/share.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/sign-out.svg b/src/icons/sign-out.svg
new file mode 100644
index 0000000..ff09ee6
--- /dev/null
+++ b/src/icons/sign-out.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/icons/trash.svg b/src/icons/trash.svg
new file mode 100644
index 0000000..7969d48
--- /dev/null
+++ b/src/icons/trash.svg
@@ -0,0 +1,3 @@
+