diff --git a/frontend/src/AssistantMessage.jsx b/frontend/src/AssistantMessage.jsx
index f1792b8..2ee7d9b 100644
--- a/frontend/src/AssistantMessage.jsx
+++ b/frontend/src/AssistantMessage.jsx
@@ -1,10 +1,11 @@
-import React from "react";
+// src/AssistantMessage.jsx
+import React, { useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
-export default function AssistantMessage({ content, theme }) {
+export default function AssistantMessage({ content, theme, timestamp }) {
return (
(
),
- th: (props) =>
|
+ th: (props) =>
| ,
+ code: CodeWithCopy
}}
>
{content}
+ {timestamp && (
+
+ {formatDateTime(timestamp)}
+
+ )}
);
}
+function CodeWithCopy({ inline, className = "", children, ...props }) {
+ const [copied, setCopied] = useState(false);
+ const codeText = String(children).replace(/\n$/, "");
+
+ // Only show copy button for fenced code with language
+ const isFencedBlock = !inline && /^language-/.test(className);
+
+ if (!isFencedBlock) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(codeText);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1500);
+ } catch (err) {
+ console.error("Copy failed", err);
+ }
+ };
+
+ return (
+
+
+ {codeText}
+
+
+ {copied && (
+
+ Copied!
+
+ )}
+
+ );
+}
+
+// Utility to format timestamp nicely
+function formatDateTime(dateTime) {
+ try {
+ const date = new Date(dateTime);
+ return date.toLocaleString(undefined, {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit"
+ });
+ } catch {
+ return dateTime;
+ }
+}
+
diff --git a/frontend/src/AssistantMessage.jsx.orig b/frontend/src/AssistantMessage.jsx.orig
new file mode 100644
index 0000000..9070abc
--- /dev/null
+++ b/frontend/src/AssistantMessage.jsx.orig
@@ -0,0 +1,32 @@
+// src/AssistantMessage.jsx
+import React from "react";
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+import remarkMath from "remark-math";
+import rehypeKatex from "rehype-katex";
+
+export default function AssistantMessage({ content, theme }) {
+ return (
+
+
+
(
+
+ ),
+ th: (props) => |
+ }}
+ >
+ {content}
+
+
+
+ );
+}
+
+
diff --git a/frontend/src/ChatWindow.jsx b/frontend/src/ChatWindow.jsx
index c36287a..1f7a2ea 100644
--- a/frontend/src/ChatWindow.jsx
+++ b/frontend/src/ChatWindow.jsx
@@ -1,3 +1,4 @@
+// ChatWindow.jsx
import React, { useRef, useEffect } from "react";
import UserMessage from "./UserMessage";
import AssistantMessage from "./AssistantMessage";
@@ -13,9 +14,19 @@ export default function ChatWindow({ messages, loading, theme }) {
{messages.map((m, idx) =>
m.role === "user" ? (
-
+
) : (
-
+
)
)}
{loading && (
diff --git a/frontend/src/UserMessage.jsx b/frontend/src/UserMessage.jsx
index 52a968e..28118cc 100644
--- a/frontend/src/UserMessage.jsx
+++ b/frontend/src/UserMessage.jsx
@@ -1,10 +1,9 @@
-// UserMessage.jsx
+// src/UserMessage.jsx
import React from "react";
-export default function UserMessage({ content, theme }) {
+export default function UserMessage({ content, theme, timestamp }) {
return (
- // This outer div aligns the *bubble* to the right
-
+
+ {timestamp && (
+
+ {formatDateTime(timestamp)}
+
+ )}
);
}
+// Same helper used in AssistantMessage
+function formatDateTime(dateTime) {
+ try {
+ const date = new Date(dateTime);
+ return date.toLocaleString(undefined, {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit"
+ });
+ } catch {
+ return dateTime;
+ }
+}
+