Levi / frontend /src /components /Sidebar.tsx
Manish Kumar
new model fix new
99f9013
Raw
History Blame Contribute Delete
6.58 kB
import type React from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
MessageSquare, Code2, Settings2, ChevronLeft, ChevronRight, LayoutDashboard, Cpu,
History, FileText, Terminal
} from 'lucide-react';
interface SidebarProps {
activeTab: string;
setActiveTab: (tab: string) => void;
collapsed: boolean;
setCollapsed: (c: boolean) => void;
}
export const Sidebar: React.FC<SidebarProps> = ({
activeTab,
setActiveTab,
collapsed,
setCollapsed,
}) => {
const navItems = [
{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ id: 'chat', label: 'AI Chat', icon: MessageSquare },
{ id: 'playground', label: 'Playground', icon: Code2 },
{ id: 'history', label: 'History', icon: History },
{ id: 'documents', label: 'Documents', icon: FileText },
{ id: 'snippets', label: 'Snippets', icon: Terminal },
{ id: 'models', label: 'Models', icon: Cpu },
{ id: 'settings', label: 'Settings', icon: Settings2 },
];
return (
<motion.aside
animate={{ width: collapsed ? 64 : 256 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="bg-[#100d20] border-r border-[#1d1b2e] flex flex-col h-screen relative select-none overflow-hidden shrink-0"
>
{/* Brand Header */}
<div className="p-4 border-b border-[#1d1b2e] flex items-center justify-between overflow-hidden shrink-0 h-16">
<div className="flex items-center gap-2.5 overflow-hidden">
<div className="p-1.5 bg-primary/10 rounded-lg text-primary glow-primary shrink-0">
<Cpu size={16} className="animate-pulse" />
</div>
<AnimatePresence>
{!collapsed && (
<motion.div
initial={{ opacity: 0, x: -8 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -8 }}
transition={{ duration: 0.2 }}
className="flex flex-col whitespace-nowrap"
>
<span className="font-bold text-sm tracking-tight text-foreground">
Antigravity
</span>
<span className="text-[10px] font-medium text-muted-foreground -mt-1">
Coder
</span>
</motion.div>
)}
</AnimatePresence>
</div>
{!collapsed && (
<button
onClick={() => setCollapsed(true)}
className="text-muted-foreground hover:text-foreground p-1 hover:bg-muted/50 rounded-lg transition-colors cursor-pointer shrink-0"
title="Collapse Sidebar"
>
<ChevronLeft size={15} />
</button>
)}
</div>
{/* Navigation Links */}
<nav className="p-4 space-y-1.5 shrink-0 flex-1 overflow-y-auto">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = activeTab === item.id;
return (
<button
key={item.id}
onClick={() => setActiveTab(item.id)}
className={`w-full flex items-center gap-3 p-2.5 rounded-xl text-xs font-semibold transition-all group relative cursor-pointer ${
isActive
? 'bg-[#2b1854] text-white border border-[#6344d5]/30'
: 'text-muted-foreground hover:text-foreground hover:bg-muted/20'
}`}
>
<Icon size={16} className={isActive ? 'text-primary' : 'text-muted-foreground group-hover:text-foreground'} />
<AnimatePresence>
{!collapsed && (
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
className="truncate whitespace-nowrap"
>
{item.label}
</motion.span>
)}
</AnimatePresence>
{collapsed && (
<div className="absolute left-18 bg-popover border border-border text-popover-foreground text-[10px] font-bold px-3 py-2 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md">
{item.label}
</div>
)}
</button>
);
})}
</nav>
{/* Expand trigger when collapsed */}
{collapsed && (
<div className="p-4 flex justify-center shrink-0 border-t border-[#1d1b2e] mt-2">
<button
onClick={() => setCollapsed(false)}
className="text-muted-foreground hover:text-foreground p-2 hover:bg-muted/50 rounded-xl transition-colors cursor-pointer"
title="Expand Sidebar"
>
<ChevronRight size={15} />
</button>
</div>
)}
{/* Mock User Profile Area */}
<div className="border-t border-[#1d1b2e] bg-[#0f0c1d] shrink-0">
<AnimatePresence>
{!collapsed && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="p-4"
>
<div className="flex items-center justify-between p-2 bg-[#131121] border border-[#1d1b2e] rounded-xl shadow-sm hover:border-primary/20 transition-all group">
<div className="flex items-center gap-2.5 min-w-0">
<div className="h-8 w-8 rounded-lg bg-gradient-to-tr from-primary to-[#2b1854] text-white flex items-center justify-center font-bold text-xs select-none shrink-0">
D
</div>
<div className="flex flex-col text-[11px] truncate min-w-0">
<span className="font-semibold text-foreground truncate">Developer</span>
<span className="text-[9px] text-[#9d99b3]">Pro Plan</span>
</div>
</div>
<button
onClick={() => setActiveTab('settings')}
className="text-muted-foreground hover:text-foreground cursor-pointer p-1 rounded-md hover:bg-muted/40 shrink-0"
title="Settings"
>
<Settings2 size={14} />
</button>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</motion.aside>
);
};