import React, { useState, useEffect } from "react"; import ChatSidebar from "./ChatSidebar.tsx"; import ChatNavbar from "./ChatNavbar.tsx"; import ConversationThread from "./ConversationThread.tsx"; import { storeName } from "../consts.ts"; import { type Conversation } from "../types"; import { useIndexedDB } from "../hooks/useIndexedDB"; import { type Model } from "../types/models"; import { getSelectedModel, setSelectedModel as saveSelectedModel } from "../utils/modelPreferences"; import { type IDBPDatabase } from "idb"; import { type Tool } from "use-mcp/react"; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface ChatAppProps {} const ChatApp: React.FC = () => { const [conversations, setConversations] = useState([]); const [conversationId, setConversationId] = useState( undefined ); const [sidebarVisible, setSidebarVisible] = useState(true); const [selectedModel, setSelectedModel] = useState(getSelectedModel()); const [apiKeyUpdateTrigger, setApiKeyUpdateTrigger] = useState(0); const [mcpTools, setMcpTools] = useState([]); const [animationDelay] = useState(() => -Math.random() * 60); const db = useIndexedDB(); const handleApiKeyUpdate = () => { setApiKeyUpdateTrigger(prev => prev + 1); }; const handleModelChange = (model: Model) => { setSelectedModel(model); saveSelectedModel(model); }; // set up conversations on app load useEffect(() => { getConversations(); deleteUnusedConversations(); startNewConversation(); }, [db]); // Initialize sidebar visibility based on screen size useEffect(() => { const isMobile = window.matchMedia("(max-width: 768px)").matches; setSidebarVisible(!isMobile); }, []); const getConversations = async () => { if (!db) return; const conversations = (await db.getAll(storeName)) as Conversation[]; const inverseConversations = conversations.reverse(); setConversations(inverseConversations); }; const deleteConversation = async (id: number, showPromptToUser = true) => { try { if ( showPromptToUser && !window.confirm("Are you sure you want to delete this conversation?") ) { return; } await db?.delete(storeName, id); setConversations((prev) => prev.filter((conv) => conv.id !== id)); setConversationId(conversations[0]?.id); } catch (error) { console.error("Failed to delete conversation:", error); } }; const editConversationTitle = async (id: number, newTitle: string) => { const conversation = (await db!.get(storeName, id)) as Conversation; conversation.title = newTitle; await db!.put(storeName, conversation); setConversations((prev) => prev.map((conv) => (conv.id === id ? { ...conv, title: newTitle } : conv)) ); }; const startNewConversation = async () => { //create unique id for new conversation setConversationId(Date.now() + Math.floor(Math.random() * 1000)); if (window.matchMedia("(max-width: 768px)").matches) { setSidebarVisible(false); } }; // delete conversations with no messages const deleteUnusedConversations = async () => { if (!db) return; const conversations = (await db.getAll(storeName)) as Conversation[]; const unusedConversations = conversations.filter( (conversation) => conversation.messages.length === 0 ); for (const conversation of unusedConversations) { deleteConversation(conversation.id as number, false); } }; return (
{/* Sidebar and Navbar components hidden but kept in codebase */} {false && ( <> )}
); }; export default ChatApp;