diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 9541539..c8c2589 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -4,12 +4,13 @@ import "./App.css";
import { themes } from "./themes";
import ChatLayout from "./ChatLayout";
import { useChatStream } from "./useChatStream";
-import { getSessionId, getUserId, resetSessionId } from "./useSessionId";
+import { getSessionId, getUserId, resetSessionId, setSessionId } from "./useSessionId";
export default function App() {
const { messages, loading, sendMessage, stopGenerating, setMessages } = useChatStream();
const [themeName, setThemeName] = useState("light");
const theme = themes[themeName];
+
const sessionId = getSessionId();
const userId = getUserId();
@@ -29,16 +30,14 @@ export default function App() {
const reloadHistory = async () => {
const res = await fetch(`/v1/history?user_id=${userId}&session_id=${sessionId}`);
const history = await res.json();
- setMessages(history); // from useChatStream
+ setMessages(history);
};
const freshStart = async () => {
await fetch(`/v1/history?user_id=${userId}&session_id=${sessionId}`, { method: "DELETE" });
setMessages([]);
- resetSessionId();
- // or start a brand new sessionId:
- //localStorage.removeItem("sessionId");
- //window.location.reload();
+ const newId = resetSessionId();
+ setSessionId(newId);
};
const createSession = async () => {
@@ -50,7 +49,7 @@ export default function App() {
});
const meta = await res.json();
setSessionId(meta.session_id);
- setMessages([]); // clear chat window
+ setMessages([]);
};
const editSession = async () => {
@@ -74,8 +73,9 @@ export default function App() {
onReloadHistory={reloadHistory}
onFreshStart={freshStart}
onCreateSession={createSession}
+ onEditSession={editSession}
+ userId={userId}
/>
);
}
-
diff --git a/frontend/src/App.jsx.old b/frontend/src/App.jsx.old
new file mode 100644
index 0000000..9541539
--- /dev/null
+++ b/frontend/src/App.jsx.old
@@ -0,0 +1,81 @@
+//App.jsx
+import React, { useState, useEffect } from "react";
+import "./App.css";
+import { themes } from "./themes";
+import ChatLayout from "./ChatLayout";
+import { useChatStream } from "./useChatStream";
+import { getSessionId, getUserId, resetSessionId } from "./useSessionId";
+
+export default function App() {
+ const { messages, loading, sendMessage, stopGenerating, setMessages } = useChatStream();
+ const [themeName, setThemeName] = useState("light");
+ const theme = themes[themeName];
+ const sessionId = getSessionId();
+ const userId = getUserId();
+
+ useEffect(() => {
+ const saved = localStorage.getItem("preferredTheme");
+ if (saved && themes[saved]) setThemeName(saved);
+ }, []);
+
+ useEffect(() => {
+ localStorage.setItem("preferredTheme", themeName);
+ }, [themeName]);
+
+ const toggleTheme = () => {
+ setThemeName((t) => (t === "light" ? "dark" : "light"));
+ };
+
+ const reloadHistory = async () => {
+ const res = await fetch(`/v1/history?user_id=${userId}&session_id=${sessionId}`);
+ const history = await res.json();
+ setMessages(history); // from useChatStream
+ };
+
+ const freshStart = async () => {
+ await fetch(`/v1/history?user_id=${userId}&session_id=${sessionId}`, { method: "DELETE" });
+ setMessages([]);
+ resetSessionId();
+ // or start a brand new sessionId:
+ //localStorage.removeItem("sessionId");
+ //window.location.reload();
+ };
+
+ const createSession = async () => {
+ const firstMessage = prompt("Enter a name or first message for the new session:") || "";
+ const res = await fetch(`/v1/sessions?user_id=${userId}`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ first_message: firstMessage }),
+ });
+ const meta = await res.json();
+ setSessionId(meta.session_id);
+ setMessages([]); // clear chat window
+ };
+
+ const editSession = async () => {
+ const newName = prompt("Enter a new name for this session:");
+ if (!newName) return;
+ await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, {
+ method: "PATCH",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ session_name: newName }),
+ });
+ };
+
+ return (
+
+ );
+}
+
+
diff --git a/frontend/src/App.jsx.orig b/frontend/src/App.jsx.orig
deleted file mode 100644
index d3d874b..0000000
--- a/frontend/src/App.jsx.orig
+++ /dev/null
@@ -1,177 +0,0 @@
-// src/App.jsx
-import React, { useState, useRef, useEffect } from 'react';
-import ReactMarkdown from 'react-markdown';
-import remarkMath from 'remark-math';
-import rehypeKatex from 'rehype-katex';
-import MessageContent from './MessageContent';
-import { useStreamBuffer } from './hooks/useStreamBuffer'
-import './App.css';
-import 'katex/dist/katex.min.css';
-
-
-function App() {
- const [messages, setMessages] = useState([]);
- const [input, setInput] = useState("");
- const [loading, setLoading] = useState(false);
- const messagesEndRef = useRef(null);
- const { buffered, pushChunk, reset } = useStreamBuffer(80);
- const sendMessage = async () => {
- if (!input.trim() || loading) return;
-
- const userMessage = { role: "user", content: input };
- const userId = "user1";
-
- // Calculate where the assistant placeholder will land
- const startIndex = messages.length;
- const assistantIndex = startIndex + 1;
-
- // Optimistic UI: user + empty assistant
- setMessages(prev => [...prev, userMessage, { role: "assistant", content: "" }]);
- setInput("");
- setLoading(true);
- reset(); // clear the buffer for the new response
-
- try {
- const res = await fetch("/v1/chat-stream", {
- method: "POST",
- headers: { "Content-Type": "application/json", "Accept": "text/event-stream" },
- body: JSON.stringify({ user_id: userId, message: userMessage.content })
- });
-
- // Non-streaming fallback
- if (!res.ok || !res.body) {
- const json = await res.json().catch(() => null);
- const text = json?.response ?? "Error: streaming not available.";
- setMessages(prev => {
- const next = [...prev];
- next[assistantIndex] = { role: "assistant", content: text };
- return next;
- });
- setLoading(false);
- return;
- }
-
- const reader = res.body.getReader();
- const decoder = new TextDecoder("utf-8");
- let acc = ""; // final committed text
- let sseBuffer = ""; // raw SSE buffer
-
- while (true) {
- const { value, done } = await reader.read();
- if (done) break;
-
- sseBuffer += decoder.decode(value, { stream: true });
-
- // Split on SSE event boundaries
- const events = sseBuffer.split("\n\n");
- sseBuffer = events.pop() || "";
-
- for (const evt of events) {
- const lines = evt.split("\n").map(l => l.trim()).filter(Boolean);
- for (const line of lines) {
- if (!line.startsWith("data:")) continue;
- const data = line.slice(5).trim();
- if (data === "[DONE]") {
- // Commit final text and finish
- setMessages(prev => {
- const next = [...prev];
- next[assistantIndex] = { role: "assistant", content: acc };
- return next;
- });
- setLoading(false);
- return;
- }
- try {
- const obj = JSON.parse(data);
- const choice = obj?.choices?.[0] ?? {};
- const delta = choice.delta ?? {};
- const piece = delta.content ?? choice.text ?? "";
- if (piece) {
- acc += piece; // reliable final copy
- pushChunk(piece); // smooth UI copy
- }
- } catch {
- // ignore non-JSON control lines
- }
- }
- }
- }
-
- // Stream ended without an explicit [DONE]
- setMessages(prev => {
- const next = [...prev];
- next[assistantIndex] = { role: "assistant", content: acc };
- return next;
- });
- } catch (err) {
- setMessages(prev => {
- const next = [...prev];
- next[assistantIndex] = { role: "assistant", content: `Error: ${String(err)}` };
- return next;
- });
- } finally {
- setLoading(false);
- }
- };
-
- useEffect(() => {
- if (loading) {
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
- }
- }, [buffered, loading]);
-
- return (
-
-
-
-
- {messages.map((msg, i) => {
- const isLastAssistant = i === messages.length - 1 && msg.role === "assistant" && loading;
- return (
-
- );
- })}
-
- {loading && (
-
-
-
- The model is processing...
-
-
- )}
-
-
-
-
-
-
- setInput(e.target.value)}
- onKeyDown={e => e.key === "Enter" && sendMessage()}
- placeholder="Type your message..."
- disabled={loading}
- autoFocus
- />
-
-
-
-
- );
-}
-
-export default App;
diff --git a/frontend/src/ChatHeader.jsx b/frontend/src/ChatHeader.jsx
index d723524..3537950 100644
--- a/frontend/src/ChatHeader.jsx
+++ b/frontend/src/ChatHeader.jsx
@@ -14,7 +14,7 @@ export default function ChatHeader({
className={`${theme.headerBg} p-2 sticky-top shadow row align-items-center`}
>
{/* Left column: control buttons */}
-
+