From e0f491dcdb0ad97827b64a87a75ca627004af01d Mon Sep 17 00:00:00 2001 From: Henry Li Date: Thu, 22 Jan 2026 14:19:54 +0800 Subject: [PATCH] feat: add main menu --- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 45 +++++++++ frontend/src/components/ui/avatar.tsx | 53 ++++++++++ .../workspace/workspace-nav-chat-list.tsx | 32 ++++++ .../workspace/workspace-nav-menu.tsx | 97 ++++++++++++++++--- .../workspace/workspace-sidebar.tsx | 37 +------ frontend/src/core/i18n/locales/en-US.ts | 5 + frontend/src/core/i18n/locales/types.ts | 5 + frontend/src/core/i18n/locales/zh-CN.ts | 5 + 9 files changed, 232 insertions(+), 48 deletions(-) create mode 100644 frontend/src/components/ui/avatar.tsx create mode 100644 frontend/src/components/workspace/workspace-nav-chat-list.tsx diff --git a/frontend/package.json b/frontend/package.json index e1c65e6..af28994 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "@codemirror/language-data": "^6.5.2", "@langchain/core": "^1.1.15", "@langchain/langgraph-sdk": "^1.5.3", + "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 0440797..9869369 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@langchain/langgraph-sdk': specifier: ^1.5.3 version: 1.5.3(@langchain/core@1.1.15(@opentelemetry/api@1.9.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-avatar': + specifier: ^1.1.11 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-collapsible': specifier: ^1.1.12 version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -1011,6 +1014,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-avatar@1.1.11': + resolution: {integrity: sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collapsible@1.1.12': resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} peerDependencies: @@ -1405,6 +1421,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-layout-effect@1.1.1': resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: @@ -5963,6 +5988,19 @@ snapshots: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) + '@radix-ui/react-avatar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-context': 1.1.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6373,6 +6411,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.8 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.8)(react@19.2.3)': dependencies: react: 19.2.3 diff --git a/frontend/src/components/ui/avatar.tsx b/frontend/src/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/frontend/src/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/frontend/src/components/workspace/workspace-nav-chat-list.tsx b/frontend/src/components/workspace/workspace-nav-chat-list.tsx new file mode 100644 index 0000000..c699b81 --- /dev/null +++ b/frontend/src/components/workspace/workspace-nav-chat-list.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { MessagesSquare } from "lucide-react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; + +import { + SidebarGroup, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar"; +import { useI18n } from "@/core/i18n/hooks"; + +export function WorkspaceNavChatList() { + const { t } = useI18n(); + const pathname = usePathname(); + return ( + + + + + + + {t.sidebar.chats} + + + + + + ); +} diff --git a/frontend/src/components/workspace/workspace-nav-menu.tsx b/frontend/src/components/workspace/workspace-nav-menu.tsx index 9c1fdad..390d66d 100644 --- a/frontend/src/components/workspace/workspace-nav-menu.tsx +++ b/frontend/src/components/workspace/workspace-nav-menu.tsx @@ -1,32 +1,101 @@ "use client"; -import { MessagesSquare } from "lucide-react"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; +import { + BugIcon, + ChevronsUpDown, + InfoIcon, + MailIcon, + Settings2Icon, + SettingsIcon, +} from "lucide-react"; +import { useState } from "react"; import { - SidebarGroup, + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from "@/components/ui/sidebar"; import { useI18n } from "@/core/i18n/hooks"; +import { GithubIcon } from "./github-icon"; +import { SettingsDialog } from "./settings"; + export function WorkspaceNavMenu() { + const [settingsOpen, setSettingsOpen] = useState(false); const { t } = useI18n(); - const pathname = usePathname(); return ( - - + <> + + - - - - {t.sidebar.chats} - - + + + +
+
+ + {t.workspace.settingsAndMore} +
+ +
+
+
+ + + setSettingsOpen(true)}> + + {t.common.settings} + + + + + + {t.workspace.visitGithub} + + + + + + {t.workspace.reportIssue} + + + + + {t.workspace.contactUs} + + + + + + {t.workspace.about} + + +
-
+ ); } diff --git a/frontend/src/components/workspace/workspace-sidebar.tsx b/frontend/src/components/workspace/workspace-sidebar.tsx index 092324e..911aee0 100644 --- a/frontend/src/components/workspace/workspace-sidebar.tsx +++ b/frontend/src/components/workspace/workspace-sidebar.tsx @@ -1,33 +1,21 @@ "use client"; -import { SettingsIcon } from "lucide-react"; -import { useState } from "react"; - import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarRail, - SidebarMenu, - SidebarMenuItem, - SidebarMenuButton, - SidebarGroup, - SidebarGroupContent, } from "@/components/ui/sidebar"; -import { SettingsDialog } from "@/components/workspace/settings"; -import { useI18n } from "@/core/i18n/hooks"; import { RecentChatList } from "./recent-chat-list"; import { WorkspaceHeader } from "./workspace-header"; +import { WorkspaceNavChatList } from "./workspace-nav-chat-list"; import { WorkspaceNavMenu } from "./workspace-nav-menu"; export function WorkspaceSidebar({ ...props }: React.ComponentProps) { - const { t } = useI18n(); - const [settingsOpen, setSettingsOpen] = useState(false); - return ( <> @@ -35,33 +23,14 @@ export function WorkspaceSidebar({ - + - - - - - - - - - - - + - - ); } diff --git a/frontend/src/core/i18n/locales/en-US.ts b/frontend/src/core/i18n/locales/en-US.ts index 58ee095..b104c26 100644 --- a/frontend/src/core/i18n/locales/en-US.ts +++ b/frontend/src/core/i18n/locales/en-US.ts @@ -68,6 +68,11 @@ export const enUS: Translations = { // Workspace workspace: { githubTooltip: "DeerFlow on Github", + settingsAndMore: "Settings and more", + visitGithub: "Visit DeerFlow on GitHub", + reportIssue: "Report a issue", + contactUs: "Contact us", + about: "About", }, // Conversation diff --git a/frontend/src/core/i18n/locales/types.ts b/frontend/src/core/i18n/locales/types.ts index 716230f..9e60cde 100644 --- a/frontend/src/core/i18n/locales/types.ts +++ b/frontend/src/core/i18n/locales/types.ts @@ -63,6 +63,11 @@ export interface Translations { // Workspace workspace: { githubTooltip: string; + settingsAndMore: string; + visitGithub: string; + reportIssue: string; + contactUs: string; + about: string; }; // Conversation diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts index 67a8d3b..fe4c2bd 100644 --- a/frontend/src/core/i18n/locales/zh-CN.ts +++ b/frontend/src/core/i18n/locales/zh-CN.ts @@ -66,6 +66,11 @@ export const zhCN: Translations = { // Workspace workspace: { githubTooltip: "DeerFlow 在 Github", + settingsAndMore: "设置和更多", + visitGithub: "在 Github 上查看 DeerFlow", + reportIssue: "报告问题", + contactUs: "联系我们", + about: "关于", }, // Conversation