refactor: wrap tool call request args as labeled key-value rows instead of raw JSON

This commit is contained in:
carry
2026-06-07 13:46:55 +08:00
parent a9881eac26
commit 34b3f44856
@@ -1,23 +1,99 @@
import type { ToolCallRequestSegment } from '../../types/protocol' import type { ToolCallRequestSegment } from '../../types/protocol'
import { Play } from 'lucide-react' import { Play, ChevronDown, ChevronRight } from 'lucide-react'
import { useState } from 'react'
/** Format a value for inline display — booleans, numbers, short strings stay inline; long strings/objects get special treatment */
function formatArg(value: unknown): { display: string; isLong: boolean } {
if (value === null || value === undefined) {
return { display: String(value), isLong: false }
}
if (typeof value === 'boolean') {
return { display: value ? 'true' : 'false', isLong: false }
}
if (typeof value === 'number') {
return { display: String(value), isLong: false }
}
if (typeof value === 'string') {
const trimmed = value.trim()
const hasNewlines = trimmed.includes('\n')
const isLong = trimmed.length > 80 || hasNewlines
if (hasNewlines || trimmed.length > 200) {
return { display: trimmed.slice(0, 200) + (trimmed.length > 200 ? '…' : ''), isLong: true }
}
return { display: trimmed, isLong }
}
if (Array.isArray(value)) {
const s = JSON.stringify(value)
return { display: s, isLong: s.length > 80 }
}
if (typeof value === 'object') {
const s = JSON.stringify(value, null, 2)
return { display: s, isLong: s.length > 80 || s.includes('\n') }
}
return { display: String(value), isLong: false }
}
function ArgRow({ name, value }: { name: string; value: unknown }) {
const [expanded, setExpanded] = useState(false)
const { display, isLong } = formatArg(value)
const needsExpansion = isLong
return (
<div className="flex items-start gap-3 px-3 py-1.5 hover:bg-gray-800/40 group">
<span className="text-xs font-mono text-yellow-400 shrink-0 min-w-[80px] pt-px">
{name}
</span>
{needsExpansion ? (
<button
onClick={() => setExpanded(!expanded)}
className="flex-1 flex items-start gap-1.5 text-left min-w-0"
>
{expanded ? (
<ChevronDown size={12} className="text-gray-500 shrink-0 mt-0.5" />
) : (
<ChevronRight size={12} className="text-gray-500 shrink-0 mt-0.5" />
)}
<span className="text-xs font-mono text-gray-400 break-all">
{expanded ? display : display.slice(0, 80).replace(/\n/g, ' ') + '…'}
</span>
</button>
) : (
<span className="text-xs font-mono text-blue-300 break-all flex-1">{display}</span>
)}
</div>
)
}
export default function ToolCallRequestView({ export default function ToolCallRequestView({
segment, segment,
}: { }: {
segment: ToolCallRequestSegment segment: ToolCallRequestSegment
}) { }) {
const entries = Object.entries(segment.arguments)
return ( return (
<div className="my-2 rounded-lg bg-gray-900 text-gray-100 overflow-hidden border border-gray-700"> <div className="my-2 rounded-lg bg-gray-900 text-gray-100 overflow-hidden border border-gray-700 shadow-sm">
<div className="flex items-center gap-2 px-3 py-1.5 bg-gray-800"> {/* Header */}
<div className="flex items-center gap-2 px-3 py-1.5 bg-gray-800 border-b border-gray-700">
<Play size={14} className="text-green-400" /> <Play size={14} className="text-green-400" />
<span className="text-xs font-mono font-semibold text-green-400"> <span className="text-xs font-mono font-semibold text-green-400">
{segment.toolName} {segment.toolName}
</span> </span>
<span className="text-xs text-gray-500">request</span> <span className="text-[10px] text-gray-500 bg-gray-700/50 px-1.5 py-0.5 rounded">
request
</span>
</div> </div>
<pre className="px-3 py-2 text-xs font-mono text-gray-300 overflow-x-auto">
{JSON.stringify(segment.arguments, null, 2)} {/* Arguments as labeled rows */}
</pre> {entries.length > 0 ? (
<div className="py-1">
{entries.map(([key, value]) => (
<ArgRow key={key} name={key} value={value} />
))}
</div>
) : (
<div className="px-3 py-2 text-xs text-gray-600 italic">(no arguments)</div>
)}
</div> </div>
) )
} }