Download src/components/sidebar.tsx from IOE-IFAD/test: direct link, hf CLI and curl.
- Browser
- Download file 7.51 kB
-
https://huggingface.co/spaces/IOE-IFAD/test/resolve/main/src/components/sidebar.tsx
- Command line
-
hf download hf://spaces/IOE-IFAD/test/src/components/sidebar.tsx
-
curl -L -o sidebar.tsx https://huggingface.co/spaces/IOE-IFAD/test/resolve/main/src/components/sidebar.tsx
7.51 kB
| "use client"; | |
| import { useEffect, useState, useCallback } from "react"; | |
| import { Plus, Search, MessageSquare, Bot } from "lucide-react"; // Removed Library, Sparkles, Archive | |
| import { | |
| Sidebar, | |
| SidebarContent, | |
| SidebarGroup, | |
| SidebarGroupContent, | |
| SidebarGroupLabel, | |
| SidebarMenu, | |
| SidebarHeader, | |
| SidebarMenuItem, | |
| SidebarMenuButton, | |
| useSidebar, | |
| } from "./ui/sidebar"; | |
| import { getAllChats, createChat } from "@/lib/storage"; | |
| import type { Chat } from "@/lib/types"; | |
| import ChatButton from "@/components/sidebar/chatButton"; | |
| import { chatEventEmitter, CHAT_RENAMED_EVENT } from "@/lib/chatEventEmitter"; | |
| import { useChatStore } from "@/stores/chatStore"; | |
| import { cn } from "@/lib/utils"; | |
| import SearchModal from "./search-modal"; | |
| const AppSidebar = () => { | |
| const [chats, setChats] = useState<Chat[]>([]); | |
| const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); | |
| const { loadChat, selectedChatId, updateChatName, deleteChat } = | |
| useChatStore(); | |
| const { state } = useSidebar(); | |
| const isCollapsed = state === "collapsed"; | |
| const loadChats = useCallback(async () => { | |
| try { | |
| const loadedChats = await getAllChats(); | |
| setChats(loadedChats.reverse()); | |
| } catch (error) { | |
| console.error("Failed to load chats:", error); | |
| } | |
| }, []); | |
| const handleChatDelete = async (chatId: string) => { | |
| try { | |
| const deleted = await deleteChat(chatId); | |
| if (!deleted) return false; | |
| setChats((prevChats) => prevChats.filter((chat) => chat.id !== chatId)); | |
| return true; | |
| } catch (error) { | |
| console.error("Failed to delete chat:", error); | |
| return false; | |
| } | |
| }; | |
| const handleChatRename = async (chatId: string, newTitle: string) => { | |
| try { | |
| await updateChatName(chatId, newTitle); | |
| setChats((prevChats) => | |
| prevChats.map((chat) => | |
| chat.id === chatId ? { ...chat, title: newTitle } : chat | |
| ) | |
| ); | |
| return true; | |
| } catch (error) { | |
| console.error("Failed to rename chat:", error); | |
| return false; | |
| } | |
| }; | |
| useEffect(() => { | |
| loadChats(); | |
| chatEventEmitter.on(CHAT_RENAMED_EVENT, loadChats); | |
| return () => { | |
| chatEventEmitter.off(CHAT_RENAMED_EVENT, loadChats); | |
| }; | |
| }, [loadChats]); | |
| const handleNewChat = async () => { | |
| try { | |
| const chatName = "New Chat"; | |
| const newChat = await createChat(chatName, []); | |
| loadChat(newChat.id); | |
| setChats((prevChats) => [newChat, ...prevChats]); | |
| } catch (error) { | |
| console.error("Failed to create new chat:", error); | |
| } | |
| }; | |
| const menuItems = [ | |
| { icon: Plus, label: "New chat", action: handleNewChat }, | |
| { | |
| icon: Search, | |
| label: "Search chats", | |
| action: () => setIsSearchModalOpen(true), | |
| }, | |
| ]; | |
| return ( | |
| <> | |
| <Sidebar | |
| className="border-r border-border/50 bg-gradient-to-b from-card/50 to-background" | |
| collapsible="icon" | |
| > | |
| <SidebarHeader | |
| className={cn( | |
| "border-b border-border/50 bg-background/95 backdrop-blur", | |
| isCollapsed && "px-2" | |
| )} | |
| > | |
| {!isCollapsed && ( | |
| <div className="p-4 flex items-center justify-between"> | |
| <div className="flex items-center gap-2 min-w-0"> | |
| <h1 className="font-semibold text-lg bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent truncate"> | |
| Transcriptinator 6000 | |
| </h1> | |
| </div> | |
| </div> | |
| )} | |
| </SidebarHeader> | |
| <SidebarContent className="flex-1 overflow-x-clip custom-scrollbar"> | |
| <SidebarGroup> | |
| {/* Menu Items */} | |
| <SidebarMenu | |
| className={cn("px-2 py-2", isCollapsed && "space-y-1")} | |
| > | |
| {menuItems.map((item, index) => ( | |
| <SidebarMenuItem key={index}> | |
| <SidebarMenuButton | |
| onClick={item.action} | |
| tooltip={isCollapsed ? item.label : undefined} | |
| className={cn( | |
| "transition-all duration-200 hover:bg-accent/50 rounded-lg h-10", | |
| isCollapsed ? "justify-center w-10" : "justify-start", | |
| item.label === "New chat" && | |
| "bg-primary/10 hover:bg-primary/20 text-primary border border-primary/20" | |
| )} | |
| > | |
| <item.icon className="h-4 w-4" /> | |
| {!isCollapsed && ( | |
| <span className="text-sm font-medium">{item.label}</span> | |
| )} | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| ))} | |
| </SidebarMenu> | |
| {!isCollapsed && ( | |
| <SidebarGroupLabel className="px-4 text-xs font-medium text-muted-foreground/80 mt-4 mb-2"> | |
| Recent Chats | |
| </SidebarGroupLabel> | |
| )} | |
| <SidebarGroupContent> | |
| <SidebarMenu className="px-2"> | |
| {!isCollapsed ? ( | |
| chats.length > 0 ? ( | |
| chats.map((chat) => ( | |
| <ChatButton | |
| key={chat.id} | |
| chat={chat} | |
| isSelected={selectedChatId === chat.id} | |
| onClick={loadChat} | |
| onDelete={handleChatDelete} | |
| onRename={handleChatRename} | |
| /> | |
| )) | |
| ) : ( | |
| <div className="px-4 py-8 text-center"> | |
| <MessageSquare className="h-8 w-8 text-muted-foreground/50 mx-auto mb-3" /> | |
| <p className="text-sm text-muted-foreground"> | |
| No conversations yet | |
| </p> | |
| <p className="text-xs text-muted-foreground/60 mt-1"> | |
| Create your first chat to get started | |
| </p> | |
| </div> | |
| ) | |
| ) : ( | |
| // Collapsed state - show recent chats as icons | |
| chats.slice(0, 8).map((chat) => ( | |
| <SidebarMenuItem key={chat.id}> | |
| <SidebarMenuButton | |
| onClick={() => loadChat(chat.id)} | |
| tooltip={chat.title} | |
| className={cn( | |
| "justify-center w-10 h-10 rounded-lg transition-all duration-200", // Explicitly set w-10 h-10 | |
| selectedChatId === chat.id | |
| ? "bg-primary/20 border border-primary/30 text-primary" | |
| : "hover:bg-accent/50" | |
| )} | |
| > | |
| <Bot | |
| className={cn( | |
| "h-4 w-4", | |
| selectedChatId === chat.id | |
| ? "text-primary" | |
| : "text-muted-foreground" | |
| )} | |
| /> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| )) | |
| )} | |
| </SidebarMenu> | |
| </SidebarGroupContent> | |
| </SidebarGroup> | |
| </SidebarContent> | |
| </Sidebar> | |
| <SearchModal | |
| open={isSearchModalOpen} | |
| onOpenChange={setIsSearchModalOpen} | |
| /> | |
| </> | |
| ); | |
| }; | |
| export default AppSidebar; | |