From eb0eb26f4cefa4880c895ff017f312e8674f9b73 Mon Sep 17 00:00:00 2001
From: karpathy <andrej.karpathy@gmail.com>
Date: Sat, 22 Nov 2025 14:27:53 -0800
Subject: v0

---
 frontend/src/App.jsx | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 frontend/src/App.jsx

(limited to 'frontend/src/App.jsx')

diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
new file mode 100644
index 0000000..0396e3a
--- /dev/null
+++ b/frontend/src/App.jsx
@@ -0,0 +1,120 @@
+import { useState, useEffect } from 'react';
+import Sidebar from './components/Sidebar';
+import ChatInterface from './components/ChatInterface';
+import { api } from './api';
+import './App.css';
+
+function App() {
+  const [conversations, setConversations] = useState([]);
+  const [currentConversationId, setCurrentConversationId] = useState(null);
+  const [currentConversation, setCurrentConversation] = useState(null);
+  const [isLoading, setIsLoading] = useState(false);
+
+  // Load conversations on mount
+  useEffect(() => {
+    loadConversations();
+  }, []);
+
+  // Load conversation details when selected
+  useEffect(() => {
+    if (currentConversationId) {
+      loadConversation(currentConversationId);
+    }
+  }, [currentConversationId]);
+
+  const loadConversations = async () => {
+    try {
+      const convs = await api.listConversations();
+      setConversations(convs);
+    } catch (error) {
+      console.error('Failed to load conversations:', error);
+    }
+  };
+
+  const loadConversation = async (id) => {
+    try {
+      const conv = await api.getConversation(id);
+      setCurrentConversation(conv);
+    } catch (error) {
+      console.error('Failed to load conversation:', error);
+    }
+  };
+
+  const handleNewConversation = async () => {
+    try {
+      const newConv = await api.createConversation();
+      setConversations([
+        { id: newConv.id, created_at: newConv.created_at, message_count: 0 },
+        ...conversations,
+      ]);
+      setCurrentConversationId(newConv.id);
+    } catch (error) {
+      console.error('Failed to create conversation:', error);
+    }
+  };
+
+  const handleSelectConversation = (id) => {
+    setCurrentConversationId(id);
+  };
+
+  const handleSendMessage = async (content) => {
+    if (!currentConversationId) return;
+
+    setIsLoading(true);
+    try {
+      // Optimistically add user message to UI
+      const userMessage = { role: 'user', content };
+      setCurrentConversation((prev) => ({
+        ...prev,
+        messages: [...prev.messages, userMessage],
+      }));
+
+      // Send message and get council response
+      const response = await api.sendMessage(currentConversationId, content);
+
+      // Add assistant message to UI
+      const assistantMessage = {
+        role: 'assistant',
+        stage1: response.stage1,
+        stage2: response.stage2,
+        stage3: response.stage3,
+        metadata: response.metadata,
+      };
+
+      setCurrentConversation((prev) => ({
+        ...prev,
+        messages: [...prev.messages, assistantMessage],
+      }));
+
+      // Reload conversations list to update message count
+      await loadConversations();
+    } catch (error) {
+      console.error('Failed to send message:', error);
+      // Remove optimistic user message on error
+      setCurrentConversation((prev) => ({
+        ...prev,
+        messages: prev.messages.slice(0, -1),
+      }));
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  return (
+    <div className="app">
+      <Sidebar
+        conversations={conversations}
+        currentConversationId={currentConversationId}
+        onSelectConversation={handleSelectConversation}
+        onNewConversation={handleNewConversation}
+      />
+      <ChatInterface
+        conversation={currentConversation}
+        onSendMessage={handleSendMessage}
+        isLoading={isLoading}
+      />
+    </div>
+  );
+}
+
+export default App;
-- 
cgit v1.2.3

