+
The model is processing...
)}
+
);
diff --git a/frontend/src/MessageContent.jsx b/frontend/src/MessageContent.jsx
index 2f9891f..c835b52 100644
--- a/frontend/src/MessageContent.jsx
+++ b/frontend/src/MessageContent.jsx
@@ -4,7 +4,6 @@ import ReactMarkdown from 'react-markdown';
import remarkMath from 'remark-math';
import remarkGfm from 'remark-gfm';
import rehypeKatex from 'rehype-katex';
-import 'katex/dist/katex.min.css';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import './MessageContent.css';
diff --git a/frontend/src/NoSessionBox.jsx b/frontend/src/NoSessionBox.jsx
new file mode 100644
index 0000000..07a68f9
--- /dev/null
+++ b/frontend/src/NoSessionBox.jsx
@@ -0,0 +1,34 @@
+// srv/NoSessionBox.jsx
+import React from "react";
+
+export default function NoSessionBox({ onCreateSession }) {
+ return (
+
+
Nessuna sessione attiva
+
+
+
+
+ Per iniziare una nuova conversazione, crea una sessione assegnandole un nome.
+ Potrai poi rivedere le sessioni precedendi e riprenderle salvando la cronologia.
+
+
+ NB: il numero massimo di messaggi è limitato dalla finestra di contesto massima operativa e potrebbe portare a chiudere una sessione al raggiungimento del limite stesso.
+
+
+
+
+
+
+ );
+}
+
diff --git a/frontend/src/SessionTable.jsx b/frontend/src/SessionTable.jsx
index 4ce508d..6941c2c 100644
--- a/frontend/src/SessionTable.jsx
+++ b/frontend/src/SessionTable.jsx
@@ -1,5 +1,5 @@
// src/SessionTable.jsx
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState, useRef } from "react";
import { getSessionId } from "./useSessionId";
export default function SessionTable({ userId, onSelectSession, onClosePanel }) {
@@ -8,8 +8,10 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
const [editingSessionId, setEditingSessionId] = useState(null);
const [editName, setEditName] = useState("");
const activeSessionId = getSessionId();
+ const wsRef = useRef(null);
const fetchSessions = async () => {
+ if (!userId) return;
setLoading(true);
try {
const res = await fetch(`/v1/sessions?user_id=${userId}`);
@@ -23,7 +25,51 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
};
useEffect(() => {
- if (userId) fetchSessions();
+ if (!userId) return;
+
+ // Caricamento iniziale
+ fetchSessions();
+
+ // Apri WebSocket
+ const wsUrl = `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/v1/ws/sessions?user_id=${userId}`;
+ const ws = new WebSocket(wsUrl);
+ wsRef.current = ws;
+
+ ws.onopen = () => {
+ console.log("[WS] Connected to sessions stream");
+ };
+
+ ws.onmessage = (event) => {
+ try {
+ const msg = JSON.parse(event.data);
+ // Possibili tipi di evento: full_list, created, updated, deleted
+ if (msg.type === "full_list") {
+ setSessions(msg.sessions);
+ } else if (msg.type === "created") {
+ setSessions((prev) => [msg.session, ...prev]);
+ } else if (msg.type === "updated" && msg.session?.session_id) {
+ setSessions((prev) =>
+ prev.map((s) =>
+ String(s.session_id) === String(msg.session.session_id)
+ ? { ...s, ...msg.session }
+ : s
+ )
+ );
+ } else if (msg.type === "deleted") {
+ setSessions((prev) => prev.filter((s) => s.session_id !== msg.session_id));
+ }
+ } catch (err) {
+ console.error("[WS] Error parsing message", err);
+ }
+ };
+
+ ws.onclose = () => {
+ console.log("[WS] Disconnected from sessions stream");
+ };
+
+ return () => {
+ ws.close();
+ };
}, [userId]);
const startEditing = (session) => {
@@ -38,8 +84,13 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_name: editName }),
});
+ // Aggiornamento ottimistico
+ setSessions((prev) =>
+ prev.map((s) =>
+ s.session_id === sessionId ? { ...s, session_name: editName } : s
+ )
+ );
setEditingSessionId(null);
- fetchSessions();
} catch (err) {
console.error("Failed to rename session", err);
}
@@ -49,7 +100,7 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
if (!window.confirm("Delete this session and its history?")) return;
try {
await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, { method: "DELETE" });
- fetchSessions();
+ // Non serve fetchSessions: il WS notificherà la cancellazione
} catch (err) {
console.error("Failed to delete session", err);
}
@@ -115,7 +166,7 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
{new Date(s.created_at).toLocaleString()} |
{s.message_count}
- |
+
|