浏览代码

refactor: modularize

BRama10 1 周之前
父节点
当前提交
f4a91874e9

+ 7 - 273
agenthub/app/agentchat/page.tsx

@@ -4,21 +4,13 @@ import React, { useState, useRef, useEffect } from 'react';
 import { ChatEditor } from '@/components/chat/editor/Editor';
 import { useMounted } from '@/lib/mounted';
 
-import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton, Loader, Tooltip } from '@mantine/core';
-import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check, Lock, Edit2, X } from 'lucide-react';
-import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat';
-import ReactMarkdown from 'react-markdown';
-import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
-import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
-import rehypeRaw from 'rehype-raw';
+import { Message, Chat } from '@/interfaces/agentchat';
+import { Sidebar } from '@/components/agentchat/Sidebar';
+import { Header } from '@/components/agentchat/Header';
+import { MessageList } from '@/components/agentchat/MessageList';
+
 
 
-const AgentLoader = () => {
-    return <div className="flex items-center space-x-2 animate-pulse">
-        <Loader size="sm" />
-        <span>Agent is thinking...</span>
-    </div>
-}
 
 const updateChatName = (chatId: number, newName: string) => {
     // setChats(prevChats => 
@@ -28,270 +20,12 @@ const updateChatName = (chatId: number, newName: string) => {
     // );
   };
 
-const Sidebar: React.FC<SidebarProps> = ({ chats, activeChat, setActiveChat, addChat, updateChatName, darkMode }) => {
-    const [editingId, setEditingId] = useState<number | null>(null);
-    const [editingName, setEditingName] = useState('');
-  
-    const categoryStyle = "text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2 mt-4 px-2 flex justify-between items-center";
-    const channelStyle = `flex items-center justify-between rounded px-2 py-1.5 text-sm font-medium transition-colors duration-200 ease-in-out cursor-pointer`;
-    const activeChannelStyle = darkMode ? 'bg-gray-700 text-white' : 'bg-gray-300 text-gray-900';
-    const inactiveChannelStyle = darkMode ? 'text-gray-400 hover:bg-gray-700 hover:text-gray-200' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900';
-  
-    const startEditing = (chat: Chat) => {
-      setEditingId(chat.id);
-      setEditingName(chat.name);
-    };
-  
-    const cancelEditing = () => {
-      setEditingId(null);
-      setEditingName('');
-    };
-  
-    const saveEditing = () => {
-      if (editingId !== null && editingName.trim() !== '') {
-        updateChatName(editingId, editingName.trim());
-        setEditingId(null);
-      }
-    };
-  
-    return (
-      <div className={`w-60 flex-shrink-0 ${darkMode ? 'bg-gray-800' : 'bg-gray-100'} p-3 flex flex-col`}>
-        <div className={`p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-200'} rounded-lg mb-4`}>
-          <h2 className={`font-bold ${darkMode ? 'text-white' : 'text-gray-800'}`}>Your Workspace</h2>
-        </div>
-        
-        <div className="flex-grow overflow-y-auto">
-          <div className={categoryStyle}>
-            Channels
-            <Tooltip label="Add Channel" position="right">
-              <ActionIcon 
-                onClick={addChat} 
-                variant="subtle" 
-                color={darkMode ? "gray" : "dark"}
-                className="hover:bg-gray-600"
-              >
-                <Plus size={16} />
-              </ActionIcon>
-            </Tooltip>
-          </div>
-          {chats.map((chat) => (
-            <div
-              key={chat.id}
-              className={`${channelStyle} ${chat.id === activeChat ? activeChannelStyle : inactiveChannelStyle}`}
-            >
-              {editingId === chat.id ? (
-                <div className="flex items-center w-full">
-                  <Hash size={16} className="mr-2 flex-shrink-0" />
-                  <TextInput
-                    value={editingName}
-                    onChange={(e) => setEditingName(e.target.value)}
-                    className="flex-grow"
-                    size="xs"
-                    autoFocus
-                    onKeyPress={(e) => e.key === 'Enter' && saveEditing()}
-                  />
-                  <ActionIcon size="sm" onClick={saveEditing} className="ml-1">
-                    <Check size={14} />
-                  </ActionIcon>
-                  <ActionIcon size="sm" onClick={cancelEditing} className="ml-1">
-                    <X size={14} />
-                  </ActionIcon>
-                </div>
-              ) : (
-                <>
-                  <div className="flex items-center flex-grow" onClick={() => setActiveChat(chat.id)}>
-                    <Hash size={16} className="mr-2 flex-shrink-0" />
-                    <span className="truncate">{chat.name}</span>
-                  </div>
-                  <Tooltip label="Rename Channel" position="right">
-                    <ActionIcon 
-                      onClick={() => startEditing(chat)} 
-                      variant="subtle" 
-                      color={darkMode ? "gray" : "dark"}
-                      className="opacity-0 group-hover:opacity-100 transition-opacity duration-200"
-                    >
-                      <Edit2 size={14} />
-                    </ActionIcon>
-                  </Tooltip>
-                </>
-              )}
-            </div>
-          ))}
-        </div>
-      </div>
-    );
-  };
-
-  const Header: React.FC<HeaderProps> = ({ darkMode, setDarkMode }) => {
-    const theme = useMantineTheme();
-    return (
-      <div className={`flex justify-between items-center p-3 border-b ${darkMode ? 'bg-gray-900 text-white border-gray-800' : 'bg-white text-black border-gray-200'}`}>
-        <div className="flex items-center space-x-2">
-          <Hash size={20} className="text-gray-500" />
-          <h1 className="text-lg font-medium">General</h1>
-        </div>
-        <div className="flex items-center space-x-2">
-          <Sun size={16} className={darkMode ? 'text-gray-400' : 'text-yellow-500'} />
-          <Switch
-            checked={darkMode}
-            onChange={(event) => setDarkMode(event.currentTarget.checked)}
-            size="sm"
-            color={theme.primaryColor}
-          />
-          <Moon size={16} className={darkMode ? 'text-blue-400' : 'text-gray-400'} />
-        </div>
-      </div>
-    );
-  };
-
-
-
-// Improved Markdown component
-interface MarkdownProps {
-    content: string;
-    darkMode: boolean;
-}
-
-const Markdown: React.FC<MarkdownProps> = ({ content, darkMode }) => {
-    const [displayedText, setDisplayedText] = useState('');
-    const animationRef = useRef<number | null>(null);
-    const currentIndexRef = useRef(0);
-
-    useEffect(() => {
-        let lastTimestamp: number | null = null;
-    
-        const streamText = (timestamp: number) => {
-            if (lastTimestamp === null) {
-                lastTimestamp = timestamp;
-            }
-    
-            const elapsed = timestamp - lastTimestamp;
-    
-            if (elapsed >= 30) { // Minimum 30ms between updates
-                if (currentIndexRef.current < content.length) {
-                    const chunkSize = Math.floor(Math.random() * 3) + 1;
-                    const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize);
-    
-                    setDisplayedText(prevText => prevText + nextChunk);
-                    currentIndexRef.current += chunkSize;
-    
-                    // Determine the next delay
-                    let delay = Math.floor(Math.random() * 50) + 30;
-    
-                    if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) {
-                        delay += Math.floor(Math.random() * 300) + 200;
-                    } else if (nextChunk.includes(',') || nextChunk.includes(';')) {
-                        delay += Math.floor(Math.random() * 150) + 100;
-                    }
-    
-                    if (nextChunk.length > 5) {
-                        delay += nextChunk.length * 10;
-                    }
-    
-                    lastTimestamp = timestamp + delay;
-                }
-            }
-    
-            animationRef.current = requestAnimationFrame(streamText);
-        };
-    
-        animationRef.current = requestAnimationFrame(streamText);
-
-        return () => {
-            if (animationRef.current !== null) {
-                cancelAnimationFrame(animationRef.current);
-            }
-        };
-    }, [content]);
 
+  
 
-    return (
-        <ReactMarkdown
-            rehypePlugins={[rehypeRaw]}
-            components={{
-                code({ node, className, children, ...props }) {
-                    const match = /language-(\w+)/.exec(className || '');
-                    const isInline = !match && (props as any).inline;
-                    return isInline ? (
-                        <code className={`${className} ${darkMode ? '!bg-gray-700' : '!bg-gray-200'} rounded px-1 py-0.5`} {...props}>
-                            {children}
-                        </code>
-                    ) : (
-                        <div className="relative">
-                            <div className="flex justify-between items-center px-4 py-2 bg-gray-800 rounded-t-md">
-                                <span className="text-sm !text-[#e06c75]">{match ? match[1] : 'text'}</span>
-                                <CopyButton value={String(children).replace(/\n$/, '')}>
-                                    {({ copied, copy }) => (
-                                        <ActionIcon color={copied ? 'teal' : 'gray'} onClick={copy}>
-                                            {copied ? <Check size={16} /> : <Clipboard size={16} />}
-                                        </ActionIcon>
-                                    )}
-                                </CopyButton>
-                            </div>
-                            <SyntaxHighlighter
-                                language={match ? match[1] : 'text'}
-                                style={atomDark as any}
-                                PreTag="div"
-                                className="rounded-b-md"
-                            >
-                                {String(children).replace(/\n$/, '')}
-                            </SyntaxHighlighter>
-                        </div>
-                    );
-                },
-                p: ({ children, ...props }) => <p className="mb-4 last:mb-0" {...props}>{children}</p>,
-                br: ({ ...props }) => <br {...props} />,
-                ul: ({ children }) => <ul className="list-disc pl-6 mb-4">{children}</ul>,
-                ol: ({ children }) => <ol className="list-decimal pl-6 mb-4">{children}</ol>,
-                li: ({ children }) => <li className="mb-2">{children}</li>,
-                h1: ({ children }) => <h1 className="text-2xl font-bold mb-4">{children}</h1>,
-                h2: ({ children }) => <h2 className="text-xl font-bold mb-3">{children}</h2>,
-                h3: ({ children }) => <h3 className="text-lg font-bold mb-2">{children}</h3>,
-                blockquote: ({ children }) => (
-                    <blockquote className="border-l-4 border-gray-300 pl-4 italic my-4">{children}</blockquote>
-                ),
-            }}
-        >
-            {displayedText}
-        </ReactMarkdown>
-    );
-};
 
-// Updated MessageBubble component
-const MessageBubble: React.FC<MessageBubbleProps> = ({ message, darkMode, index, isThinking = false }) => (
-    <div
-      className={`flex items-start space-x-3 py-3 px-4 ${darkMode ? 'hover:bg-gray-800/50' : 'hover:bg-gray-100/50'
-        } transition-colors duration-200 animate-slideIn opacity-0`}
-      style={{ animationDelay: `${index * 0.05}s`, animationFillMode: 'forwards' }}
-    >
-      <div className={`w-8 h-8 rounded-full flex items-center justify-center text-white text-sm flex-shrink-0 ${message.sender === 'user' ? 'bg-blue-500' : 'bg-green-500'}`}>
-        {message.sender === 'user' ? 'U' : 'B'}
-      </div>
-      <div className="flex-1 min-w-0">
-        <div className="flex items-baseline space-x-2">
-          <span className={`font-medium text-sm truncate ${darkMode ? 'text-gray-200' : 'text-gray-900'}`}>
-            {message.sender === 'user' ? 'User' : 'Bot'}
-          </span>
-          <span className={`text-xs ${darkMode ? 'text-gray-500' : 'text-gray-400'}`}>
-            {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
-          </span>
-        </div>
-        <div className={`mt-1 text-sm break-words ${darkMode ? 'text-gray-300' : 'text-gray-700'}`}>
-          {isThinking ? <AgentLoader /> : <Markdown content={message.text} darkMode={darkMode} />}
-        </div>
-      </div>
-    </div>
-  );
 
-  const MessageList: React.FC<MessageListProps> = ({ messages, darkMode }) => (
-    <div className={`flex-grow overflow-y-auto ${darkMode ? 'bg-gray-900' : 'bg-gray-50'}`}>
-      <div className="py-4 space-y-1">
-        {messages.map((message, index) => (
-          <MessageBubble key={message.id} message={message} darkMode={darkMode} index={index} isThinking={message.thinking} />
-        ))}
-      </div>
-    </div>
-  );
+  
 
 const ChatInterface: React.FC = () => {
     const [messages, setMessages] = useState<Message[]>([]);

+ 31 - 0
agenthub/components/agentchat/Header.tsx

@@ -0,0 +1,31 @@
+import React from 'react';
+
+import { Switch, useMantineTheme } from '@mantine/core';
+import { Sun, Moon, Hash } from 'lucide-react';
+
+export interface HeaderProps {
+    darkMode: boolean;
+    setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
+}
+
+export const Header: React.FC<HeaderProps> = ({ darkMode, setDarkMode }) => {
+    const theme = useMantineTheme();
+    return (
+      <div className={`flex justify-between items-center p-3 border-b ${darkMode ? 'bg-gray-900 text-white border-gray-800' : 'bg-white text-black border-gray-200'}`}>
+        <div className="flex items-center space-x-2">
+          <Hash size={20} className="text-gray-500" />
+          <h1 className="text-lg font-medium">General</h1>
+        </div>
+        <div className="flex items-center space-x-2">
+          <Sun size={16} className={darkMode ? 'text-gray-400' : 'text-yellow-500'} />
+          <Switch
+            checked={darkMode}
+            onChange={(event) => setDarkMode(event.currentTarget.checked)}
+            size="sm"
+            color={theme.primaryColor}
+          />
+          <Moon size={16} className={darkMode ? 'text-blue-400' : 'text-gray-400'} />
+        </div>
+      </div>
+    );
+  };

+ 122 - 0
agenthub/components/agentchat/Markdown.tsx

@@ -0,0 +1,122 @@
+'use client'
+
+import React, { useState, useRef, useEffect } from 'react';
+
+import { ActionIcon, CopyButton } from '@mantine/core';
+import { Clipboard, Check} from 'lucide-react';
+
+import ReactMarkdown from 'react-markdown';
+import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
+import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
+import rehypeRaw from 'rehype-raw';
+
+// Improved Markdown component
+interface MarkdownProps {
+    content: string;
+    darkMode: boolean;
+}
+
+export const Markdown: React.FC<MarkdownProps> = ({ content, darkMode }) => {
+    const [displayedText, setDisplayedText] = useState('');
+    const animationRef = useRef<number | null>(null);
+    const currentIndexRef = useRef(0);
+
+    useEffect(() => {
+        let lastTimestamp: number | null = null;
+    
+        const streamText = (timestamp: number) => {
+            if (lastTimestamp === null) {
+                lastTimestamp = timestamp;
+            }
+    
+            const elapsed = timestamp - lastTimestamp;
+    
+            if (elapsed >= 30) { // Minimum 30ms between updates
+                if (currentIndexRef.current < content.length) {
+                    const chunkSize = Math.floor(Math.random() * 3) + 1;
+                    const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize);
+    
+                    setDisplayedText(prevText => prevText + nextChunk);
+                    currentIndexRef.current += chunkSize;
+    
+                    // Determine the next delay
+                    let delay = Math.floor(Math.random() * 50) + 30;
+    
+                    if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) {
+                        delay += Math.floor(Math.random() * 300) + 200;
+                    } else if (nextChunk.includes(',') || nextChunk.includes(';')) {
+                        delay += Math.floor(Math.random() * 150) + 100;
+                    }
+    
+                    if (nextChunk.length > 5) {
+                        delay += nextChunk.length * 10;
+                    }
+    
+                    lastTimestamp = timestamp + delay;
+                }
+            }
+    
+            animationRef.current = requestAnimationFrame(streamText);
+        };
+    
+        animationRef.current = requestAnimationFrame(streamText);
+
+        return () => {
+            if (animationRef.current !== null) {
+                cancelAnimationFrame(animationRef.current);
+            }
+        };
+    }, [content]);
+
+
+    return (
+        <ReactMarkdown
+            rehypePlugins={[rehypeRaw]}
+            components={{
+                code({ node, className, children, ...props }) {
+                    const match = /language-(\w+)/.exec(className || '');
+                    const isInline = !match && (props as any).inline;
+                    return isInline ? (
+                        <code className={`${className} ${darkMode ? '!bg-gray-700' : '!bg-gray-200'} rounded px-1 py-0.5`} {...props}>
+                            {children}
+                        </code>
+                    ) : (
+                        <div className="relative">
+                            <div className="flex justify-between items-center px-4 py-2 bg-gray-800 rounded-t-md">
+                                <span className="text-sm !text-[#e06c75]">{match ? match[1] : 'text'}</span>
+                                <CopyButton value={String(children).replace(/\n$/, '')}>
+                                    {({ copied, copy }) => (
+                                        <ActionIcon color={copied ? 'teal' : 'gray'} onClick={copy}>
+                                            {copied ? <Check size={16} /> : <Clipboard size={16} />}
+                                        </ActionIcon>
+                                    )}
+                                </CopyButton>
+                            </div>
+                            <SyntaxHighlighter
+                                language={match ? match[1] : 'text'}
+                                style={atomDark as any}
+                                PreTag="div"
+                                className="rounded-b-md"
+                            >
+                                {String(children).replace(/\n$/, '')}
+                            </SyntaxHighlighter>
+                        </div>
+                    );
+                },
+                p: ({ children, ...props }) => <p className="mb-4 last:mb-0" {...props}>{children}</p>,
+                br: ({ ...props }) => <br {...props} />,
+                ul: ({ children }) => <ul className="list-disc pl-6 mb-4">{children}</ul>,
+                ol: ({ children }) => <ol className="list-decimal pl-6 mb-4">{children}</ol>,
+                li: ({ children }) => <li className="mb-2">{children}</li>,
+                h1: ({ children }) => <h1 className="text-2xl font-bold mb-4">{children}</h1>,
+                h2: ({ children }) => <h2 className="text-xl font-bold mb-3">{children}</h2>,
+                h3: ({ children }) => <h3 className="text-lg font-bold mb-2">{children}</h3>,
+                blockquote: ({ children }) => (
+                    <blockquote className="border-l-4 border-gray-300 pl-4 italic my-4">{children}</blockquote>
+                ),
+            }}
+        >
+            {displayedText}
+        </ReactMarkdown>
+    );
+};

+ 43 - 0
agenthub/components/agentchat/MessageBubble.tsx

@@ -0,0 +1,43 @@
+import { Message } from "@/interfaces/agentchat";
+import { Loader } from "@mantine/core";
+import { Markdown } from "./Markdown";
+
+export interface MessageBubbleProps {
+  message: Message;
+  darkMode: boolean;
+  index: number;
+  isThinking?: boolean;
+}
+
+const AgentLoader = () => {
+  return <div className="flex items-center space-x-2 animate-pulse">
+      <Loader size="sm" />
+      <span>Agent is thinking...</span>
+  </div>
+}
+
+// Updated MessageBubble component
+export const MessageBubble: React.FC<MessageBubbleProps> = ({ message, darkMode, index, isThinking = false }) => (
+    <div
+      className={`flex items-start space-x-3 py-3 px-4 ${darkMode ? 'hover:bg-gray-800/50' : 'hover:bg-gray-100/50'
+        } transition-colors duration-200 animate-slideIn opacity-0`}
+      style={{ animationDelay: `${index * 0.05}s`, animationFillMode: 'forwards' }}
+    >
+      <div className={`w-8 h-8 rounded-full flex items-center justify-center text-white text-sm flex-shrink-0 ${message.sender === 'user' ? 'bg-blue-500' : 'bg-green-500'}`}>
+        {message.sender === 'user' ? 'U' : 'B'}
+      </div>
+      <div className="flex-1 min-w-0">
+        <div className="flex items-baseline space-x-2">
+          <span className={`font-medium text-sm truncate ${darkMode ? 'text-gray-200' : 'text-gray-900'}`}>
+            {message.sender === 'user' ? 'User' : 'Bot'}
+          </span>
+          <span className={`text-xs ${darkMode ? 'text-gray-500' : 'text-gray-400'}`}>
+            {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
+          </span>
+        </div>
+        <div className={`mt-1 text-sm break-words ${darkMode ? 'text-gray-300' : 'text-gray-700'}`}>
+          {isThinking ? <AgentLoader /> : <Markdown content={message.text} darkMode={darkMode} />}
+        </div>
+      </div>
+    </div>
+  );

+ 18 - 0
agenthub/components/agentchat/MessageList.tsx

@@ -0,0 +1,18 @@
+import { Message } from "@/interfaces/agentchat";
+import { MessageBubble } from "./MessageBubble";
+
+export interface MessageListProps {
+    messages: Message[];
+    darkMode: boolean;
+}
+
+
+export const MessageList: React.FC<MessageListProps> = ({ messages, darkMode }) => (
+    <div className={`flex-grow overflow-y-auto ${darkMode ? 'bg-gray-900' : 'bg-gray-50'}`}>
+      <div className="py-4 space-y-1">
+        {messages.map((message, index) => (
+          <MessageBubble key={message.id} message={message} darkMode={darkMode} index={index} isThinking={message.thinking} />
+        ))}
+      </div>
+    </div>
+  );

+ 109 - 0
agenthub/components/agentchat/Sidebar.tsx

@@ -0,0 +1,109 @@
+import React, { useState } from 'react';
+
+import { TextInput, ActionIcon, Tooltip } from '@mantine/core';
+import { Plus, Hash, Check, Edit2, X } from 'lucide-react';
+import { Chat } from '@/interfaces/agentchat';
+
+
+export interface SidebarProps {
+    chats: Chat[];
+    activeChat: number;
+    setActiveChat: (id: number) => void;
+    addChat: () => void;
+    updateChatName: (chatId: number, newName: string) => void;
+    darkMode: boolean;
+}
+
+export const Sidebar: React.FC<SidebarProps> = ({ chats, activeChat, setActiveChat, addChat, updateChatName, darkMode }) => {
+    const [editingId, setEditingId] = useState<number | null>(null);
+    const [editingName, setEditingName] = useState('');
+  
+    const categoryStyle = "text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2 mt-4 px-2 flex justify-between items-center";
+    const channelStyle = `flex items-center justify-between rounded px-2 py-1.5 text-sm font-medium transition-colors duration-200 ease-in-out cursor-pointer`;
+    const activeChannelStyle = darkMode ? 'bg-gray-700 text-white' : 'bg-gray-300 text-gray-900';
+    const inactiveChannelStyle = darkMode ? 'text-gray-400 hover:bg-gray-700 hover:text-gray-200' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900';
+  
+    const startEditing = (chat: Chat) => {
+      setEditingId(chat.id);
+      setEditingName(chat.name);
+    };
+  
+    const cancelEditing = () => {
+      setEditingId(null);
+      setEditingName('');
+    };
+  
+    const saveEditing = () => {
+      if (editingId !== null && editingName.trim() !== '') {
+        updateChatName(editingId, editingName.trim());
+        setEditingId(null);
+      }
+    };
+  
+    return (
+      <div className={`w-60 flex-shrink-0 ${darkMode ? 'bg-gray-800' : 'bg-gray-100'} p-3 flex flex-col`}>
+        <div className={`p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-200'} rounded-lg mb-4`}>
+          <h2 className={`font-bold ${darkMode ? 'text-white' : 'text-gray-800'}`}>Your Workspace</h2>
+        </div>
+        
+        <div className="flex-grow overflow-y-auto">
+          <div className={categoryStyle}>
+            Channels
+            <Tooltip label="Add Channel" position="right">
+              <ActionIcon 
+                onClick={addChat} 
+                variant="subtle" 
+                color={darkMode ? "gray" : "dark"}
+                className="hover:bg-gray-600"
+              >
+                <Plus size={16} />
+              </ActionIcon>
+            </Tooltip>
+          </div>
+          {chats.map((chat) => (
+            <div
+              key={chat.id}
+              className={`${channelStyle} ${chat.id === activeChat ? activeChannelStyle : inactiveChannelStyle}`}
+            >
+              {editingId === chat.id ? (
+                <div className="flex items-center w-full">
+                  <Hash size={16} className="mr-2 flex-shrink-0" />
+                  <TextInput
+                    value={editingName}
+                    onChange={(e) => setEditingName(e.target.value)}
+                    className="flex-grow"
+                    size="xs"
+                    autoFocus
+                    onKeyPress={(e) => e.key === 'Enter' && saveEditing()}
+                  />
+                  <ActionIcon size="sm" onClick={saveEditing} className="ml-1">
+                    <Check size={14} />
+                  </ActionIcon>
+                  <ActionIcon size="sm" onClick={cancelEditing} className="ml-1">
+                    <X size={14} />
+                  </ActionIcon>
+                </div>
+              ) : (
+                <>
+                  <div className="flex items-center flex-grow" onClick={() => setActiveChat(chat.id)}>
+                    <Hash size={16} className="mr-2 flex-shrink-0" />
+                    <span className="truncate">{chat.name}</span>
+                  </div>
+                  <Tooltip label="Rename Channel" position="right">
+                    <ActionIcon 
+                      onClick={() => startEditing(chat)} 
+                      variant="subtle" 
+                      color={darkMode ? "gray" : "dark"}
+                      className="opacity-0 group-hover:opacity-100 transition-opacity duration-200"
+                    >
+                      <Edit2 size={14} />
+                    </ActionIcon>
+                  </Tooltip>
+                </>
+              )}
+            </div>
+          ))}
+        </div>
+      </div>
+    );
+  };

+ 0 - 31
agenthub/interfaces/agentchat/index.ts

@@ -5,29 +5,6 @@ export interface Message {
     sender: 'user' | 'bot';
     timestamp: Date;
     attachments?: string[]; // Array of file names or URLs
-  }
-export interface HeaderProps {
-    darkMode: boolean;
-    setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
-}
-
-export interface MessageBubbleProps {
-    message: Message;
-    darkMode: boolean;
-    index: number;
-    isThinking?: boolean;
-  }
-
-export interface MessageListProps {
-    messages: Message[];
-    darkMode: boolean;
-}
-
-export interface InputAreaProps {
-    input: string;
-    setInput: React.Dispatch<React.SetStateAction<string>>;
-    handleSend: () => void;
-    darkMode: boolean;
 }
 
 export interface Chat {
@@ -35,11 +12,3 @@ export interface Chat {
     name: string;
 }
 
-export interface SidebarProps {
-    chats: Chat[];
-    activeChat: number;
-    setActiveChat: (id: number) => void;
-    addChat: () => void;
-    updateChatName: (chatId: number, newName: string) => void;
-    darkMode: boolean;
-}