theo-agent-dashboard

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

ToolCall.tsx (3380B)


      1 import { useState } from "react";
      2 import type { ToolCall as ToolCallType } from "../../store/chat";
      3 import { ToolResultRenderer, getOperationCategory } from "../ToolResult/ToolResultRenderer";
      4 
      5 interface ToolCallProps {
      6   toolCall: ToolCallType;
      7 }
      8 
      9 const STATUS_STYLES: Record<ToolCallType["status"], string> = {
     10   pending: "text-gray-400",
     11   running: "text-yellow-400",
     12   complete: "text-green-400",
     13   error: "text-red-400",
     14 };
     15 
     16 const STATUS_LABELS: Record<ToolCallType["status"], string> = {
     17   pending: "Pending",
     18   running: "Running…",
     19   complete: "Done",
     20   error: "Error",
     21 };
     22 
     23 const OP_BADGE_COLORS: Record<string, string> = {
     24   blue: "bg-blue-900/50 text-blue-300",
     25   green: "bg-green-900/50 text-green-300",
     26   yellow: "bg-yellow-900/50 text-yellow-300",
     27   purple: "bg-purple-900/50 text-purple-300",
     28   orange: "bg-orange-900/50 text-orange-300",
     29   gray: "bg-gray-800 text-gray-400",
     30 };
     31 
     32 /** Render a tool call result or arguments as JSON. */
     33 function formatJson(str: string): string {
     34   try {
     35     return JSON.stringify(JSON.parse(str), null, 2);
     36   } catch {
     37     return str;
     38   }
     39 }
     40 
     41 export function ToolCall({ toolCall }: ToolCallProps) {
     42   const [expanded, setExpanded] = useState(false);
     43   const opCategory = getOperationCategory(toolCall.name);
     44 
     45   return (
     46     <div className="my-1 rounded-lg border border-gray-700 bg-gray-900/50 text-sm">
     47       <button
     48         type="button"
     49         onClick={() => setExpanded(!expanded)}
     50         className="flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-gray-800/50 transition-colors"
     51       >
     52         <svg
     53           className={`h-4 w-4 text-gray-400 transition-transform ${expanded ? "rotate-90" : ""}`}
     54           fill="none"
     55           viewBox="0 0 24 24"
     56           stroke="currentColor"
     57           strokeWidth={2}
     58         >
     59           <path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
     60         </svg>
     61         <span
     62           className={`inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-semibold uppercase ${OP_BADGE_COLORS[opCategory.color] ?? OP_BADGE_COLORS.gray}`}
     63         >
     64           {opCategory.label}
     65         </span>
     66         <span className="font-mono text-gray-300">{toolCall.name}</span>
     67         <span className={`ml-auto text-xs ${STATUS_STYLES[toolCall.status]}`}>
     68           {toolCall.status === "running" && (
     69             <span className="mr-1 inline-block h-2 w-2 animate-pulse rounded-full bg-yellow-400" />
     70           )}
     71           {STATUS_LABELS[toolCall.status]}
     72         </span>
     73       </button>
     74 
     75       {expanded && (
     76         <div className="border-t border-gray-700 px-3 py-2">
     77           <div className="mb-2">
     78             <span className="text-xs font-semibold uppercase text-gray-500">
     79               Arguments
     80             </span>
     81             <pre className="mt-1 overflow-x-auto rounded bg-gray-950 p-2 text-xs text-gray-300">
     82               {formatJson(toolCall.args)}
     83             </pre>
     84           </div>
     85           {toolCall.result !== null && (
     86             <div>
     87               <span className="text-xs font-semibold uppercase text-gray-500">
     88                 Result
     89               </span>
     90               <div className="mt-1 overflow-x-auto rounded bg-gray-950 p-2 text-xs text-gray-300">
     91                 <ToolResultRenderer content={toolCall.result} toolName={toolCall.name} />
     92               </div>
     93             </div>
     94           )}
     95         </div>
     96       )}
     97     </div>
     98   );
     99 }