({
input,
result,
isError = false,
isRunning = false,
startedAt,
completedAt,
}: ToolWebFetchProps)
| 44 | const MAX_VISIBLE = 80; |
| 45 | |
| 46 | export function ToolWebFetch({ |
| 47 | input, |
| 48 | result, |
| 49 | isError = false, |
| 50 | isRunning = false, |
| 51 | startedAt, |
| 52 | completedAt, |
| 53 | }: ToolWebFetchProps) { |
| 54 | const [showFull, setShowFull] = useState(false); |
| 55 | |
| 56 | const status = result ? parseStatus(result) : null; |
| 57 | const isTruncated = !showFull && result && result.length > MAX_VISIBLE * 10; |
| 58 | |
| 59 | return ( |
| 60 | <ToolUseBlock |
| 61 | toolName="webfetch" |
| 62 | toolInput={input} |
| 63 | toolResult={result} |
| 64 | isError={isError} |
| 65 | isRunning={isRunning} |
| 66 | startedAt={startedAt} |
| 67 | completedAt={completedAt} |
| 68 | > |
| 69 | {/* URL header */} |
| 70 | <div className="flex items-center gap-2 px-3 py-2 bg-surface-850 border-b border-surface-700/50"> |
| 71 | <a |
| 72 | href={input.url} |
| 73 | target="_blank" |
| 74 | rel="noopener noreferrer" |
| 75 | onClick={(e) => e.stopPropagation()} |
| 76 | className="font-mono text-xs text-brand-400 hover:text-brand-300 hover:underline truncate flex-1 flex items-center gap-1" |
| 77 | > |
| 78 | {input.url} |
| 79 | <ExternalLink className="w-3 h-3 flex-shrink-0" /> |
| 80 | </a> |
| 81 | {status && <StatusBadge code={status} />} |
| 82 | </div> |
| 83 | |
| 84 | {/* Prompt if any */} |
| 85 | {input.prompt && ( |
| 86 | <div className="px-3 py-2 border-b border-surface-700/50 text-xs text-surface-400 italic"> |
| 87 | Prompt: {input.prompt} |
| 88 | </div> |
| 89 | )} |
| 90 | |
| 91 | {/* Response body */} |
| 92 | {isRunning ? ( |
| 93 | <div className="px-3 py-4 text-surface-500 text-xs animate-pulse"> |
| 94 | Fetching… |
| 95 | </div> |
| 96 | ) : isError ? ( |
| 97 | <div className="px-3 py-3 text-red-400 text-xs font-mono">{result}</div> |
| 98 | ) : result ? ( |
| 99 | <div> |
| 100 | <div className="overflow-auto max-h-[400px] px-3 py-3 text-xs text-surface-300 leading-relaxed whitespace-pre-wrap font-mono"> |
| 101 | {isTruncated ? result.slice(0, MAX_VISIBLE * 10) : result} |
| 102 | </div> |
| 103 | {isTruncated && ( |
nothing calls this directly
no test coverage detected