Files
claude-code/src/tools/WebSearchTool/UI.tsx
2026-04-07 16:17:48 +08:00

128 lines
3.0 KiB
TypeScript

import React from 'react'
import { MessageResponse } from '../../components/MessageResponse.js'
import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js'
import { Box, Text } from '@anthropic/ink'
import type { ProgressMessage } from '../../types/message.js'
import { truncate } from '../../utils/format.js'
import type {
Output,
SearchResult,
WebSearchProgress,
} from './WebSearchTool.js'
function getSearchSummary(
results: (SearchResult | string | null | undefined)[],
): {
searchCount: number
totalResultCount: number
} {
let searchCount = 0
let totalResultCount = 0
for (const result of results) {
if (result != null && typeof result !== 'string') {
searchCount++
totalResultCount += result.content?.length ?? 0
}
}
return { searchCount, totalResultCount }
}
export function renderToolUseMessage(
{
query,
allowed_domains,
blocked_domains,
}: Partial<{
query: string
allowed_domains?: string[]
blocked_domains?: string[]
}>,
{ verbose }: { verbose: boolean },
): React.ReactNode {
if (!query) {
return null
}
let message = ''
if (query) {
message += `"${query}"`
}
if (verbose) {
if (allowed_domains && allowed_domains.length > 0) {
message += `, only allowing domains: ${allowed_domains.join(', ')}`
}
if (blocked_domains && blocked_domains.length > 0) {
message += `, blocking domains: ${blocked_domains.join(', ')}`
}
}
return message
}
export function renderToolUseProgressMessage(
progressMessages: ProgressMessage<WebSearchProgress>[],
): React.ReactNode {
if (progressMessages.length === 0) {
return null
}
const lastProgress = progressMessages[progressMessages.length - 1]
if (!lastProgress?.data) {
return null
}
const data = lastProgress.data
switch (data.type) {
case 'query_update':
return (
<MessageResponse>
<Text dimColor>Searching: {data.query}</Text>
</MessageResponse>
)
case 'search_results_received':
return (
<MessageResponse>
<Text dimColor>
Found {data.resultCount} results for &quot;{data.query}&quot;
</Text>
</MessageResponse>
)
default:
return null
}
}
export function renderToolResultMessage(output: Output): React.ReactNode {
const { searchCount } = getSearchSummary(output.results ?? [])
const timeDisplay =
output.durationSeconds >= 1
? `${Math.round(output.durationSeconds)}s`
: `${Math.round(output.durationSeconds * 1000)}ms`
return (
<Box justifyContent="space-between" width="100%">
<MessageResponse height={1}>
<Text>
Did {searchCount} search
{searchCount !== 1 ? 'es' : ''} in {timeDisplay}
</Text>
</MessageResponse>
</Box>
)
}
export function getToolUseSummary(
input: Partial<{ query: string }> | undefined,
): string | null {
if (!input?.query) {
return null
}
return truncate(input.query, TOOL_SUMMARY_MAX_LENGTH)
}