import { useState } from "react";
import type { ConversationMessage as ConversationMessageType } from "../../lib/api";
interface ConversationMessageProps {
message: ConversationMessageType;
}
// Get role styling
function getRoleStyle(role: string) {
switch (role.toLowerCase()) {
case "user":
return { label: "User", color: "text-[#9bc3ff]", bg: "bg-[rgba(155,195,255,0.1)]" };
case "assistant":
return { label: "Assistant", color: "text-emerald-400", bg: "bg-[rgba(52,211,153,0.1)]" };
case "system":
return { label: "System", color: "text-yellow-400", bg: "bg-[rgba(250,204,21,0.1)]" };
case "tool":
return { label: "Tool", color: "text-purple-400", bg: "bg-[rgba(192,132,252,0.1)]" };
default:
return { label: role, color: "text-[#7788aa]", bg: "bg-[rgba(119,136,170,0.1)]" };
}
}
// Format JSON for display
function formatJson(data: unknown): string {
try {
return JSON.stringify(data, null, 2);
} catch {
return String(data);
}
}
export function ConversationMessage({ message }: ConversationMessageProps) {
const [showToolDetails, setShowToolDetails] = useState(false);
const { label, color, bg } = getRoleStyle(message.role);
const hasToolInfo = message.toolName || message.toolCalls?.length;
return (
<div className={`p-3 ${bg} border-l-2 border-transparent hover:border-[rgba(117,170,252,0.3)]`}>
{/* Header */}
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className={`font-mono text-[10px] uppercase ${color}`}>{label}</span>
{message.toolName && (
<span className="font-mono text-[9px] text-purple-400 px-1.5 py-0.5 border border-[rgba(192,132,252,0.3)]">
{message.toolName}
</span>
)}
</div>
<div className="flex items-center gap-3">
{message.tokenCount && (
<span className="font-mono text-[9px] text-[#556677]">
{message.tokenCount.toLocaleString()} tokens
</span>
)}
{message.costUsd !== undefined && message.costUsd > 0 && (
<span className="font-mono text-[9px] text-[#556677]">
${message.costUsd.toFixed(4)}
</span>
)}
<span className="font-mono text-[9px] text-[#556677]">
{new Date(message.timestamp).toLocaleTimeString()}
</span>
</div>
</div>
{/* Content */}
<div className="font-mono text-xs text-[#dbe7ff] whitespace-pre-wrap break-words">
{message.content}
</div>
{/* Tool calls */}
{hasToolInfo && (
<div className="mt-2">
<button
onClick={() => setShowToolDetails(!showToolDetails)}
className="font-mono text-[9px] text-purple-400 hover:text-purple-300 uppercase flex items-center gap-1"
>
<svg
className={`w-3 h-3 transition-transform ${showToolDetails ? "rotate-90" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
{message.toolCalls?.length
? `${message.toolCalls.length} tool call${message.toolCalls.length > 1 ? "s" : ""}`
: "Tool details"}
</button>
{showToolDetails && (
<div className="mt-2 space-y-2">
{/* Tool input */}
{message.toolInput && (
<div className="p-2 bg-[rgba(0,0,0,0.3)] border border-[rgba(192,132,252,0.2)]">
<div className="font-mono text-[9px] text-purple-400 uppercase mb-1">Input</div>
<pre className="font-mono text-[10px] text-[#9bc3ff] overflow-x-auto">
{formatJson(message.toolInput)}
</pre>
</div>
)}
{/* Tool result */}
{message.toolResult && (
<div
className={`p-2 border ${
message.isError
? "bg-[rgba(239,68,68,0.1)] border-[rgba(239,68,68,0.3)]"
: "bg-[rgba(0,0,0,0.3)] border-[rgba(192,132,252,0.2)]"
}`}
>
<div
className={`font-mono text-[9px] uppercase mb-1 ${
message.isError ? "text-red-400" : "text-purple-400"
}`}
>
{message.isError ? "Error" : "Result"}
</div>
<pre className="font-mono text-[10px] text-[#9bc3ff] overflow-x-auto max-h-48 overflow-y-auto">
{message.toolResult}
</pre>
</div>
)}
{/* Multiple tool calls */}
{message.toolCalls?.map((call, i) => (
<div
key={call.id || i}
className="p-2 bg-[rgba(0,0,0,0.3)] border border-[rgba(192,132,252,0.2)]"
>
<div className="font-mono text-[9px] text-purple-400 uppercase mb-1">
{call.name}
</div>
<pre className="font-mono text-[10px] text-[#9bc3ff] overflow-x-auto">
{formatJson(call.input)}
</pre>
</div>
))}
</div>
)}
</div>
)}
</div>
);
}