'use client'; import React from 'react'; import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; import { Bell, Bot, ChevronDown, FolderOpen, ListTodo, LogOut, Mail, Menu, MessageSquare, Puzzle, Settings, Store, Wrench, X } from 'lucide-react'; import { logout } from '@/lib/api'; import { LanguageSwitcher } from '@/components/LanguageSwitcher'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { appConnectionStatusLabel } from '@/lib/i18n/common'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; import { useChatStore } from '@/lib/store'; type NavItem = { key: 'chat' | 'tasks' | 'notifications' | 'skills' | 'files' | 'tools' | 'agents' | 'outlook' | 'marketplace' | 'settings'; href: string; icon: React.ComponentType<{ className?: string }>; matchPrefixes?: string[]; }; const NAV_ITEMS: NavItem[] = [ { key: 'chat', href: '/', icon: MessageSquare }, { key: 'tasks', href: '/tasks', icon: ListTodo, matchPrefixes: ['/tasks', '/cron'] }, { key: 'notifications', href: '/notifications', icon: Bell, matchPrefixes: ['/notifications'] }, { key: 'skills', href: '/skills', icon: Puzzle }, { key: 'files', href: '/files', icon: FolderOpen, matchPrefixes: ['/files'] }, { key: 'tools', href: '/mcp', icon: Wrench, matchPrefixes: ['/mcp'] }, { key: 'agents', href: '/agents', icon: Bot, matchPrefixes: ['/agents'] }, { key: 'outlook', href: '/outlook', icon: Mail, matchPrefixes: ['/outlook'] }, { key: 'marketplace', href: '/marketplace', icon: Store, matchPrefixes: ['/marketplace'] }, { key: 'settings', href: '/settings', icon: Settings, matchPrefixes: ['/settings', '/status', '/logs'], }, ]; function ConnectionDot() { const { locale } = useAppI18n(); const wsStatus = useChatStore((s) => s.wsStatus); const beaverReady = useChatStore((s) => s.beaverReady); const isOnline = wsStatus === 'connected' && beaverReady === true; const isChecking = wsStatus === 'connected' && beaverReady === null; const isConnecting = wsStatus === 'connecting' || isChecking; const isOffline = wsStatus === 'disconnected' || (wsStatus === 'connected' && beaverReady === false); const color = isOnline ? 'bg-[#869683]' : isConnecting ? 'bg-[#8B7E77]' : 'bg-[#5F5550]'; const label = appConnectionStatusLabel(wsStatus, beaverReady, locale); return (
{label}
); } const Header = () => { const { locale } = useAppI18n(); const pathname = usePathname(); const router = useRouter(); const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false); const user = useChatStore((s) => s.user); const isAuthLoading = useChatStore((s) => s.isAuthLoading); const setUser = useChatStore((s) => s.setUser); const navLabel = React.useCallback((key: NavItem['key']) => { if (key === 'chat') return pickAppText(locale, '对话', 'Chat'); if (key === 'tasks') return 'Task'; if (key === 'notifications') return pickAppText(locale, '通知', 'Notifications'); if (key === 'skills') return pickAppText(locale, '技能', 'Skills'); if (key === 'files') return pickAppText(locale, '文件', 'Files'); if (key === 'tools') return pickAppText(locale, '工具', 'Tools'); if (key === 'agents') return pickAppText(locale, '智能体', 'Agents'); if (key === 'outlook') return 'Outlook'; if (key === 'marketplace') return pickAppText(locale, '市场', 'Marketplace'); return pickAppText(locale, '配置', 'Settings'); }, [locale]); const handleLogout = async () => { await logout(); setUser(null); router.replace('/login'); router.refresh(); }; React.useEffect(() => { setMobileMenuOpen(false); }, [pathname]); React.useEffect(() => { if (!mobileMenuOpen) return; const previousOverflow = document.body.style.overflow; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { setMobileMenuOpen(false); } }; document.body.style.overflow = 'hidden'; document.addEventListener('keydown', handleKeyDown); return () => { document.body.style.overflow = previousOverflow; document.removeEventListener('keydown', handleKeyDown); }; }, [mobileMenuOpen]); const userInitial = (user?.username || user?.email || '?').trim().charAt(0).toUpperCase(); const renderNavLinks = (compact = false) => NAV_ITEMS.map((item) => { const isActive = item.href === '/' ? pathname === '/' : item.matchPrefixes?.some((prefix) => pathname.startsWith(prefix)) ?? pathname.startsWith(item.href); const Icon = item.icon; return ( setMobileMenuOpen(false) : undefined} className={`flex h-11 shrink-0 items-center gap-2 rounded-full text-sm font-medium transition-colors ${ compact ? 'justify-start rounded-lg border border-transparent bg-background px-4' : 'px-4' } ${ isActive ? 'bg-primary text-primary-foreground' : compact ? 'text-[#4F4642] hover:border-[#E6E1DE] hover:bg-muted hover:text-[#0B0B0B]' : 'text-[#4F4642] hover:bg-[#F7F5F4] hover:text-[#0B0B0B]' }`} > {navLabel(item.key)} ); }); return ( <>
Beaver
{user ? (

{user.email}

{userInitial}

{pickAppText(locale, `${user.username},你好!`, `Hi, ${user.username}`)}

{pickAppText(locale, '当前已登录到你的工作区实例。', 'You are currently signed in to your workspace instance.')}

) : !isAuthLoading ? null : null}
{mobileMenuOpen && ( <>