From 60002e62c933712611c55f116ef4c80deec9e23e Mon Sep 17 00:00:00 2001
From: YurenHao0426 <blackhao0426@gmail.com>
Date: Fri, 13 Feb 2026 20:10:52 +0000
Subject: Add copy response button to response toolbar

Adds a clipboard copy button next to the response label that copies
the raw response text. Shows a brief green checkmark on success.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 frontend/src/components/Sidebar.tsx | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

(limited to 'frontend/src/components/Sidebar.tsx')

diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index b73c513..ac48c6f 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -17,7 +17,7 @@ const preprocessLaTeX = (content: string): string => {
     .replace(/\\\(([\s\S]*?)\\\)/g, (_, math) => `$${math}$`);
 };
 
-import { Play, Settings, Info, ChevronLeft, ChevronRight, Maximize2, Edit3, X, Check, FileText, MessageCircle, Send, GripVertical, GitMerge, Trash2, AlertCircle, Loader2, Navigation, Upload, Search, Link, Layers, Eye, EyeOff } from 'lucide-react';
+import { Play, Settings, Info, ChevronLeft, ChevronRight, Maximize2, Edit3, X, Check, FileText, MessageCircle, Send, GripVertical, GitMerge, Trash2, AlertCircle, Loader2, Navigation, Upload, Search, Link, Layers, Eye, EyeOff, Copy, ClipboardCheck } from 'lucide-react';
 
 interface SidebarProps {
   isOpen: boolean;
@@ -56,6 +56,7 @@ const Sidebar: React.FC<SidebarProps> = ({ isOpen, onToggle, onInteract }) => {
   const [isEditing, setIsEditing] = useState(false);
   const [editedResponse, setEditedResponse] = useState('');
   const [rawTextMode, setRawTextMode] = useState(false);
+  const [copiedResponse, setCopiedResponse] = useState(false);
   
   // Summary states
   const [showSummaryModal, setShowSummaryModal] = useState(false);
@@ -1855,6 +1856,17 @@ const Sidebar: React.FC<SidebarProps> = ({ isOpen, onToggle, onInteract }) => {
                 <div className="flex gap-1">
                   {selectedNode.data.response && (
                     <>
+                      <button
+                        onClick={() => {
+                          navigator.clipboard.writeText(selectedNode.data.response || '');
+                          setCopiedResponse(true);
+                          setTimeout(() => setCopiedResponse(false), 1500);
+                        }}
+                        className={`p-1 rounded ${isDark ? 'text-gray-400 hover:bg-gray-700' : 'text-gray-500 hover:bg-gray-200'}`}
+                        title="Copy response"
+                      >
+                        {copiedResponse ? <ClipboardCheck size={14} className="text-green-500" /> : <Copy size={14} />}
+                      </button>
                       <button
                         onClick={() => setRawTextMode(!rawTextMode)}
                         className={`p-1 rounded ${rawTextMode
-- 
cgit v1.2.3

