Spaces:
Running
Running
File size: 4,001 Bytes
5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 264f96c 5012205 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import {
ChevronDownIcon,
ChevronUpIcon,
Loader2,
CheckCircle2,
TerminalSquare,
Code,
ArrowRight,
Circle,
} from "lucide-react";
interface ToolInvocationProps {
toolName: string;
state: string;
args: any;
result: any;
isLatestMessage: boolean;
status: string;
}
export function ToolInvocation({
toolName,
state,
args,
result,
isLatestMessage,
status,
}: ToolInvocationProps) {
const [isExpanded, setIsExpanded] = useState(false);
const variants = {
collapsed: {
height: 0,
opacity: 0,
},
expanded: {
height: "auto",
opacity: 1,
},
};
const getStatusIcon = () => {
if (state === "call") {
if (isLatestMessage && status !== "ready") {
return <Loader2 className="animate-spin h-3.5 w-3.5 text-muted-foreground/70" />;
}
return <Circle className="h-3.5 w-3.5 fill-destructive/20 text-destructive/70" />;
}
return <CheckCircle2 size={14} className="text-success/90" />;
};
const formatContent = (content: any): string => {
try {
if (typeof content === "string") {
try {
const parsed = JSON.parse(content);
return JSON.stringify(parsed, null, 2);
} catch {
return content;
}
}
return JSON.stringify(content, null, 2);
} catch {
return String(content);
}
};
return (
<div className="flex flex-col mb-3 bg-muted/30 rounded-lg border border-border/40 overflow-hidden">
<div
className="flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-muted/50 transition-colors"
onClick={() => setIsExpanded(!isExpanded)}
>
<div className="flex items-center justify-center w-5 h-5">
<TerminalSquare className="h-3.5 w-3.5 text-primary/70" />
</div>
<div className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground flex-1">
<span className="text-foreground/80">{toolName}</span>
<ArrowRight className="h-3 w-3 text-muted-foreground/50" />
<span className="text-foreground/60">{state === "call" ? "Running" : "Completed"}</span>
</div>
<div className="flex items-center gap-2">
{getStatusIcon()}
{isExpanded ? (
<ChevronUpIcon className="h-3.5 w-3.5 text-muted-foreground/70" />
) : (
<ChevronDownIcon className="h-3.5 w-3.5 text-muted-foreground/70" />
)}
</div>
</div>
<AnimatePresence initial={false}>
{isExpanded && (
<motion.div
initial="collapsed"
animate="expanded"
exit="collapsed"
variants={variants}
transition={{ duration: 0.2 }}
className="space-y-2 p-3 pt-1 border-t border-border/30"
>
{!!args && (
<div className="space-y-1">
<div className="flex items-center gap-1.5 text-xs text-muted-foreground/70">
<Code className="h-3 w-3" />
<span>Arguments</span>
</div>
<pre className="text-xs font-mono bg-background/50 p-3 rounded-md overflow-x-auto">
{formatContent(args)}
</pre>
</div>
)}
{!!result && (
<div className="space-y-1">
<div className="flex items-center gap-1.5 text-xs text-muted-foreground/70">
<ArrowRight className="h-3 w-3" />
<span>Result</span>
</div>
<pre className="text-xs font-mono bg-background/50 p-3 rounded-md overflow-x-auto max-h-[300px] overflow-y-auto">
{formatContent(result)}
</pre>
</div>
)}
</motion.div>
)}
</AnimatePresence>
</div>
);
} |