claude-code/tools/WebSearchTool/UI.tsx

101 lines
12 KiB
TypeScript
Raw Normal View History

import React from 'react';
import { MessageResponse } from '../../components/MessageResponse.js';
import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js';
import { Box, Text } from '../../ink.js';
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);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk1lc3NhZ2VSZXNwb25zZSIsIlRPT0xfU1VNTUFSWV9NQVhfTEVOR1RIIiwiQm94IiwiVGV4dCIsIlByb2dyZXNzTWVzc2FnZSIsInRydW5jYXRlIiwiT3V0cHV0IiwiU2VhcmNoUmVzdWx0IiwiV2ViU2VhcmNoUHJvZ3Jlc3MiLCJnZXRTZWFyY2hTdW1tYXJ5IiwicmVzdWx0cyIsInNlYXJjaENvdW50IiwidG90YWxSZXN1bHRDb3VudCIsInJlc3VsdCIsImNvbnRlbnQiLCJsZW5ndGgiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsInF1ZXJ5IiwiYWxsb3dlZF9kb21haW5zIiwiYmxvY2tlZF9kb21haW5zIiwiUGFydGlhbCIsInZlcmJvc2UiLCJSZWFjdE5vZGUiLCJtZXNzYWdlIiwiam9pbiIsInJlbmRlclRvb2xVc2VQcm9ncmVzc01lc3NhZ2UiLCJwcm9ncmVzc01lc3NhZ2VzIiwibGFzdFByb2dyZXNzIiwiZGF0YSIsInR5cGUiLCJyZXN1bHRDb3VudCIsInJlbmRlclRvb2xSZXN1bHRNZXNzYWdlIiwib3V0cHV0IiwidGltZURpc3BsYXkiLCJkdXJhdGlvblNlY29uZHMiLCJNYXRoIiwicm91bmQiLCJnZXRUb29sVXNlU3VtbWFyeSIsImlucHV0Il0sInNvdXJjZXMiOlsiVUkudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IE1lc3NhZ2VSZXNwb25zZSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvTWVzc2FnZVJlc3BvbnNlLmpzJ1xuaW1wb3J0IHsgVE9PTF9TVU1NQVJZX01BWF9MRU5HVEggfSBmcm9tICcuLi8uLi9jb25zdGFudHMvdG9vbExpbWl0cy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgUHJvZ3Jlc3NNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IHRydW5jYXRlIH0gZnJvbSAnLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHR5cGUge1xuICBPdXRwdXQsXG4gIFNlYXJjaFJlc3VsdCxcbiAgV2ViU2VhcmNoUHJvZ3Jlc3MsXG59IGZyb20gJy4vV2ViU2VhcmNoVG9vbC5qcydcblxuZnVuY3Rpb24gZ2V0U2VhcmNoU3VtbWFyeShcbiAgcmVzdWx0czogKFNlYXJjaFJlc3VsdCB8IHN0cmluZyB8IG51bGwgfCB1bmRlZmluZWQpW10sXG4pOiB7XG4gIHNlYXJjaENvdW50OiBudW1iZXJcbiAgdG90YWxSZXN1bHRDb3VudDogbnVtYmVyXG59IHtcbiAgbGV0IHNlYXJjaENvdW50ID0gMFxuICBsZXQgdG90YWxSZXN1bHRDb3VudCA9IDBcblxuICBmb3IgKGNvbnN0IHJlc3VsdCBvZiByZXN1bHRzKSB7XG4gICAgaWYgKHJlc3VsdCAhPSBudWxsICYmIHR5cGVvZiByZXN1bHQgIT09ICdzdHJpbmcnKSB7XG4gICAgICBzZWFyY2hDb3VudCsrXG4gICAgICB0b3RhbFJlc3VsdENvdW50ICs9IHJlc3VsdC5jb250ZW50Py5sZW5ndGggPz8gMFxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7IHNlYXJjaENvdW50LCB0b3RhbFJlc3VsdENvdW50IH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRvb2xVc2VNZXNzYWdlKFxuICB7XG4gICAgcXVlcnksXG4gICAgYWxsb3dlZF9kb21haW5zLFxuICAgIGJsb2NrZWRfZG9tYWlucyxcbiAgfTogUGFydGlhbDx7XG4gICAgcXVlcnk6IHN0cmluZ1xuICAgIGFsbG93ZWRfZG9tYWlucz86IHN0cmluZ1tdXG4gICAgYmxvY2tlZF9kb21haW5zPzogc3RyaW5nW11cbiAgfT4sXG4gIHsgdmVyYm9zZSB9OiB7IHZlcmJvc2U6IGJvb2xlYW4gfSxcbik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmICghcXVlcnkpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgbGV0IG1lc3NhZ2UgPSAnJ1xuXG4gIGlmIChxdWVyeSkge1xuICAgIG1lc3NhZ2UgKz0gYFwiJHtxdWVyeX1cImBcbiAgfVxuXG4gIGlmICh2ZXJib3NlKSB7XG4gICAgaWYgKGFsbG93ZWRfZG9tYWlucyAmJiBhbGxvd2VkX2RvbWFpbnMubGVuZ3RoID4gMCkge1xuICAgICAgbWVzc2FnZSArPSBgLCBvbmx5IGFsbG93aW5nIGRvbWFpbnM6ICR7YWxsb3dlZF9kb21haW5zLmpvaW4oJywgJyl9YFxuICAgIH1cblxuICAgIGlmIChibG9ja2VkX2RvbWFpbnMgJiYgYmxvY2tlZF9kb21haW5zLmxlbmd0aCA+IDApIHtcbiAgICAgIG1lc3NhZ2UgKz0gYCwgYmxvY2tpbmcgZG9tYWluczogJHtibG9ja2VkX2RvbWFpbnMuam9pbignLCAnKX1gXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIG1lc3NhZ2Vcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRvb2xVc2VQcm9ncmVzc01lc3NhZ2UoXG4gIHByb2dyZXNzTWVzc2FnZXM6IFByb2dyZXNzTWVzc2FnZTxXZWJTZWFyY2hQcm9ncmVzcz5bXSxcbik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmIChwcm9ncmVzc01lc3NhZ2VzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBsYXN0UHJvZ3Jlc3MgPSBwcm9ncmVzc01lc3NhZ2VzW3Byb2dyZXNzTWVzc2FnZXMubGVuZ3RoIC0gMV1cbiAgaWYgKCFsYXN0UHJvZ3Jlc3M/LmRhdGEpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgY29uc3QgZGF0YSA9IGxhc3RQcm9ncmVzcy5kYXRhXG5cbiAgc3dpdGNoIChkYXRhLnR5cGUpIHtcbiAgICBjYXNlICdxdWVyeV91cGRhdGUnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5TZWFyY2hpbmc6IHtkYXRhLnF1ZXJ5fTwvVGV4dD5cbiAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICApXG4gICAgY2FzZSAnc2VhcmNoX3Jlc3VsdHNfcmVjZWl2ZWQnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIEZvdW5kIHtkYXRhLnJlc3VsdENvdW50fSByZXN1bHRzIGZvciAmcXVvdDt7ZGF0YS5xdWVyeX0mcXVvdDtcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvTWVzc2FnZVJlc3BvbnNlPlxuICAgICAgKVxuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gbnVsbFxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb29sUmVzdWx