mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 09:13:14 +00:00
[open-swe] feat: Add copy button to chat messages in v2 thread view (#286)
* Apply patch * Apply patch * Apply patch * Apply patch * cr * Apply patch * Apply patch * Apply patch * cr * small styling changes --------- Co-authored-by: open-swe-dev[bot] <open-swe-dev@users.noreply.github.com> Co-authored-by: bracesproul <braceasproul@gmail.com>
This commit is contained in:
parent
91d5b0b087
commit
095c7fb58a
1 changed files with 65 additions and 3 deletions
|
|
@ -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<HTMLButtonElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(content);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<TooltipIconButton
|
||||
onClick={(e) => handleCopy(e)}
|
||||
variant="ghost"
|
||||
tooltip="Copy content"
|
||||
className="size-6 p-1"
|
||||
>
|
||||
<AnimatePresence
|
||||
mode="wait"
|
||||
initial={false}
|
||||
>
|
||||
{copied ? (
|
||||
<motion.div
|
||||
key="check"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
<CopyCheck className="h-3 w-3 text-green-500" />
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="copy"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
<Copy className="h-3 w-3" />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</TooltipIconButton>
|
||||
);
|
||||
}
|
||||
|
||||
interface ThreadViewProps {
|
||||
stream: ReturnType<typeof useStream<ManagerGraphState>>;
|
||||
displayThread: ThreadDisplayInfo;
|
||||
|
|
@ -126,7 +183,7 @@ export function ThreadView({
|
|||
{filteredMessages.map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className="flex gap-3"
|
||||
className="group flex gap-3"
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
{message.type === "human" ? (
|
||||
|
|
@ -139,7 +196,7 @@ export function ThreadView({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 space-y-1">
|
||||
<div className="relative flex-1 space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-xs font-medium">
|
||||
{message.type === "human" ? "You" : "Agent"}
|
||||
|
|
@ -148,6 +205,11 @@ export function ThreadView({
|
|||
<div className="text-foreground text-sm leading-relaxed">
|
||||
{getMessageContentString(message.content)}
|
||||
</div>
|
||||
<div className="absolute right-0 -bottom-5 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<MessageCopyButton
|
||||
content={getMessageContentString(message.content)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue