diff --git a/apps/web/src/components/v2/thread-view.tsx b/apps/web/src/components/v2/thread-view.tsx index d683dd95..4cc8f573 100644 --- a/apps/web/src/components/v2/thread-view.tsx +++ b/apps/web/src/components/v2/thread-view.tsx @@ -5,8 +5,18 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Textarea } from "@/components/ui/textarea"; -import { ArrowLeft, GitBranch, Send, User, Bot } from "lucide-react"; +import { + ArrowLeft, + GitBranch, + Send, + User, + Bot, + Copy, + CopyCheck, +} from "lucide-react"; +import { TooltipIconButton } from "@/components/ui/tooltip-icon-button"; import { getMessageContentString } from "@open-swe/shared/messages"; +import { AnimatePresence, motion } from "framer-motion"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ThreadSwitcher } from "./thread-switcher"; import { ThreadDisplayInfo } from "./types"; @@ -21,6 +31,53 @@ import { DO_NOT_RENDER_ID_PREFIX } from "@open-swe/shared/constants"; const PROGRAMMER_ASSISTANT_ID = process.env.NEXT_PUBLIC_PROGRAMMER_ASSISTANT_ID; const PLANNER_ASSISTANT_ID = process.env.NEXT_PUBLIC_PLANNER_ASSISTANT_ID; +function MessageCopyButton({ content }: { content: string }) { + const [copied, setCopied] = useState(false); + + const handleCopy = (e: React.MouseEvent) => { + e.stopPropagation(); + navigator.clipboard.writeText(content); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( + handleCopy(e)} + variant="ghost" + tooltip="Copy content" + className="size-6 p-1" + > + + {copied ? ( + + + + ) : ( + + + + )} + + + ); +} + interface ThreadViewProps { stream: ReturnType>; displayThread: ThreadDisplayInfo; @@ -126,7 +183,7 @@ export function ThreadView({ {filteredMessages.map((message) => (
{message.type === "human" ? ( @@ -139,7 +196,7 @@ export function ThreadView({
)}
-
+
{message.type === "human" ? "You" : "Agent"} @@ -148,6 +205,11 @@ export function ThreadView({
{getMessageContentString(message.content)}
+
+ +
))}