claude-code/tools/PowerShellTool/UI.tsx

131 lines
19 KiB
TypeScript
Raw Normal View History

import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs';
import * as React from 'react';
import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js';
import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js';
import { MessageResponse } from '../../components/MessageResponse.js';
import { OutputLine } from '../../components/shell/OutputLine.js';
import { ShellProgressMessage } from '../../components/shell/ShellProgressMessage.js';
import { ShellTimeDisplay } from '../../components/shell/ShellTimeDisplay.js';
import { Box, Text } from '../../ink.js';
import type { Tool } from '../../Tool.js';
import type { ProgressMessage } from '../../types/message.js';
import type { PowerShellProgress } from '../../types/tools.js';
import type { ThemeName } from '../../utils/theme.js';
import type { Out, PowerShellToolInput } from './PowerShellTool.js';
// Constants for command display
const MAX_COMMAND_DISPLAY_LINES = 2;
const MAX_COMMAND_DISPLAY_CHARS = 160;
export function renderToolUseMessage(input: Partial<PowerShellToolInput>, {
verbose,
theme: _theme
}: {
verbose: boolean;
theme: ThemeName;
}): React.ReactNode {
const {
command
} = input;
if (!command) {
return null;
}
const displayCommand = command;
if (!verbose) {
const lines = displayCommand.split('\n');
const needsLineTruncation = lines.length > MAX_COMMAND_DISPLAY_LINES;
const needsCharTruncation = displayCommand.length > MAX_COMMAND_DISPLAY_CHARS;
if (needsLineTruncation || needsCharTruncation) {
let truncated = displayCommand;
if (needsLineTruncation) {
truncated = lines.slice(0, MAX_COMMAND_DISPLAY_LINES).join('\n');
}
if (truncated.length > MAX_COMMAND_DISPLAY_CHARS) {
truncated = truncated.slice(0, MAX_COMMAND_DISPLAY_CHARS);
}
return <Text>{truncated.trim()}</Text>;
}
}
return displayCommand;
}
export function renderToolUseProgressMessage(progressMessagesForMessage: ProgressMessage<PowerShellProgress>[], {
verbose,
tools: _tools,
terminalSize: _terminalSize,
inProgressToolCallCount: _inProgressToolCallCount
}: {
tools: Tool[];
verbose: boolean;
terminalSize?: {
columns: number;
rows: number;
};
inProgressToolCallCount?: number;
}): React.ReactNode {
const lastProgress = progressMessagesForMessage.at(-1);
if (!lastProgress || !lastProgress.data) {
return <MessageResponse height={1}>
<Text dimColor>Running</Text>
</MessageResponse>;
}
const data = lastProgress.data;
return <ShellProgressMessage fullOutput={data.fullOutput} output={data.output} elapsedTimeSeconds={data.elapsedTimeSeconds} totalLines={data.totalLines} totalBytes={data.totalBytes} timeoutMs={data.timeoutMs} taskId={data.taskId} verbose={verbose} />;
}
export function renderToolUseQueuedMessage(): React.ReactNode {
return <MessageResponse height={1}>
<Text dimColor>Waiting</Text>
</MessageResponse>;
}
export function renderToolResultMessage(content: Out, progressMessagesForMessage: ProgressMessage<PowerShellProgress>[], {
verbose,
theme: _theme,
tools: _tools,
style: _style
}: {
verbose: boolean;
theme: ThemeName;
tools: Tool[];
style?: 'condensed';
}): React.ReactNode {
const lastProgress = progressMessagesForMessage.at(-1);
const timeoutMs = lastProgress?.data?.timeoutMs;
const {
stdout,
stderr,
interrupted,
returnCodeInterpretation,
isImage,
backgroundTaskId
} = content;
if (isImage) {
return <MessageResponse height={1}>
<Text dimColor>[Image data detected and sent to Claude]</Text>
</MessageResponse>;
}
return <Box flexDirection="column">
{stdout !== '' ? <OutputLine content={stdout} verbose={verbose} /> : null}
{stderr.trim() !== '' ? <OutputLine content={stderr} verbose={verbose} isError /> : null}
{stdout === '' && stderr.trim() === '' ? <MessageResponse height={1}>
<Text dimColor>
{backgroundTaskId ? <>
Running in the background{' '}
<KeyboardShortcutHint shortcut="↓" action="manage" parens />
</> : interrupted ? 'Interrupted' : returnCodeInterpretation || '(No output)'}
</Text>
</MessageResponse> : null}
{timeoutMs ? <MessageResponse>
<ShellTimeDisplay timeoutMs={timeoutMs} />
</MessageResponse> : null}
</Box>;
}
export function renderToolUseErrorMessage(result: ToolResultBlockParam['content'], {
verbose,
progressMessagesForMessage: _progressMessagesForMessage,
tools: _tools
}: {
verbose: boolean;
progressMessagesForMessage: ProgressMessage<PowerShellProgress>[];
tools: Tool[];
}): React.ReactNode {
return <FallbackToolUseErrorMessage result={result} verbose={verbose} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJUb29sUmVzdWx0QmxvY2tQYXJhbSIsIlJlYWN0IiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJGYWxsYmFja1Rvb2xVc2VFcnJvck1lc3NhZ2UiLCJNZXNzYWdlUmVzcG9uc2UiLCJPdXRwdXRMaW5lIiwiU2hlbGxQcm9ncmVzc01lc3NhZ2UiLCJTaGVsbFRpbWVEaXNwbGF5IiwiQm94IiwiVGV4dCIsIlRvb2wiLCJQcm9ncmVzc01lc3NhZ2UiLCJQb3dlclNoZWxsUHJvZ3Jlc3MiLCJUaGVtZU5hbWUiLCJPdXQiLCJQb3dlclNoZWxsVG9vbElucHV0IiwiTUFYX0NPTU1BTkRfRElTUExBWV9MSU5FUyIsIk1BWF9DT01NQU5EX0RJU1BMQVlfQ0hBUlMiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsImlucHV0IiwiUGFydGlhbCIsInZlcmJvc2UiLCJ0aGVtZSIsIl90aGVtZSIsIlJlYWN0Tm9kZSIsImNvbW1hbmQiLCJkaXNwbGF5Q29tbWFuZCIsImxpbmVzIiwic3BsaXQiLCJuZWVkc0xpbmVUcnVuY2F0aW9uIiwibGVuZ3RoIiwibmVlZHNDaGFyVHJ1bmNhdGlvbiIsInRydW5jYXRlZCIsInNsaWNlIiwiam9pbiIsInRyaW0iLCJyZW5kZXJUb29sVXNlUHJvZ3Jlc3NNZXNzYWdlIiwicHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2UiLCJ0b29scyIsIl90b29scyIsInRlcm1pbmFsU2l6ZSIsIl90ZXJtaW5hbFNpemUiLCJpblByb2dyZXNzVG9vbENhbGxDb3VudCIsIl9pblByb2dyZXNzVG9vbENhbGxDb3VudCIsImNvbHVtbnMiLCJyb3dzIiwibGFzdFByb2dyZXNzIiwiYXQiLCJkYXRhIiwiZnVsbE91dHB1dCIsIm91dHB1dCIsImVsYXBzZWRUaW1lU2Vjb25kcyIsInRvdGFsTGluZXMiLCJ0b3RhbEJ5dGVzIiwidGltZW91dE1zIiwidGFza0lkIiwicmVuZGVyVG9vbFVzZVF1ZXVlZE1lc3NhZ2UiLCJyZW5kZXJUb29sUmVzdWx0TWVzc2FnZSIsImNvbnRlbnQiLCJzdHlsZSIsIl9zdHlsZSIsInN0ZG91dCIsInN0ZGVyciIsImludGVycnVwdGVkIiwicmV0dXJuQ29kZUludGVycHJldGF0aW9uIiwiaXNJbWFnZSIsImJhY2tncm91bmRUYXNrSWQiLCJyZW5kZXJUb29sVXNlRXJyb3JNZXNzYWdlIiwicmVzdWx0IiwiX3Byb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlIl0sInNvdXJjZXMiOlsiVUkudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgVG9vbFJlc3VsdEJsb2NrUGFyYW0gfSBmcm9tICdAYW50aHJvcGljLWFpL3Nkay9yZXNvdXJjZXMvaW5kZXgubWpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IEZhbGxiYWNrVG9vbFVzZUVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvRmFsbGJhY2tUb29sVXNlRXJyb3JNZXNzYWdlLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBPdXRwdXRMaW5lIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9zaGVsbC9PdXRwdXRMaW5lLmpzJ1xuaW1wb3J0IHsgU2hlbGxQcm9ncmVzc01lc3NhZ2UgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL3NoZWxsL1NoZWxsUHJvZ3Jlc3NNZXNzYWdlLmpzJ1xuaW1wb3J0IHsgU2hlbGxUaW1lRGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvc2hlbGwvU2hlbGxUaW1lRGlzcGxheS5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgVG9vbCB9IGZyb20gJy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgdHlwZSB7IFByb2dyZXNzTWVzc2FnZSB9IGZyb20gJy4uLy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQgdHlwZSB7IFBvd2VyU2hlbGxQcm9ncmVzcyB9IGZyb20gJy4uLy4uL3R5cGVzL3Rvb2xzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZU5hbWUgfSBmcm9tICcuLi8uLi91dGlscy90aGVtZS5qcydcbmltcG9ydCB0eXBlIHsgT3V0LCBQb3dlclNoZWxsVG9vbElucHV0IH0gZnJvbSAnLi9Qb3dlclNoZWxsVG9vbC5qcydcblxuLy8gQ29uc3RhbnRzIGZvciBjb21tYW5kIGRpc3BsYXlcbmNvbnN0IE1BWF9DT01NQU5EX0RJU1BMQVlfTElORVMgPSAyXG5jb25zdCBNQVhfQ09NTUFORF9ESVNQTEFZX0NIQVJTID0gMTYwXG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb29sVXNlTWVzc2FnZShcbiAgaW5wdXQ6IFBhcnRpYWw8UG93ZXJTaGVsbFRvb2xJbnB1dD4sXG4gIHsgdmVyYm9zZSwgdGhlbWU6IF90aGVtZSB9OiB7IHZlcmJvc2U6IGJvb2xlYW47IHRoZW1lOiBUaGVtZU5hbWUgfSxcbik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgY29tbWFuZCB9ID0gaW5wdXRcbiAgaWYgKCFjb21tYW5kKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IGRpc3BsYXlDb21tYW5kID0gY29tbWFuZFxuXG4gIGlmICghdmVyYm9zZSkge1xuICAgIGNvbnN0IGxpbmVzID0gZGlzcGxheUNvbW1hbmQuc3BsaXQoJ1xcbicpXG4gICAgY29uc3QgbmVlZHNMaW5lVHJ1bmNhdGlvbiA9IGxpbmVzLmxlbmd0aCA+IE1BWF9DT01NQU5EX0RJU1BMQVlfTElORVNcbiAgICBjb25zdCBuZWVkc0NoYXJUcnVuY2F0aW9uID1cbiAgICAgIGRpc3BsYXlDb21tYW5kLmxlbmd0aCA+IE1BWF9DT01NQU5EX0RJU1BMQVlfQ0hBUlNcblxuICAgIGlmIChuZWVkc0xpbmVUcnVuY2F0aW9uIHx8IG5lZWRzQ2hhclRydW5jYXRpb24pIHtcbiAgICAgIGxldCB0cnVuY2F0ZWQgPSBkaXNwbGF5Q29tbWFuZFxuXG4gICAgICBpZiAobmVlZHNMaW5lVHJ1bmNhdGlvbikge1xuICAgICAgICB0cnVuY2F0ZWQgPSBsaW5lcy5zbGljZSgwLCBNQVhfQ09NTUFORF9ESVNQTEFZX0xJTkVTKS5qb2luKCdcXG4nKVxuICAgICAgfVxuXG4gICAgICBpZiAodHJ1bmNhdGVkLmxlbmd0aCA+IE1BWF9DT01NQU5EX0RJU1BMQVlfQ0hBUlMpIHtcbiAgICAgICAgdHJ1bmNhdGVkID0gdHJ1bmNhdGVkLnNsaWNlKDAsIE1BWF9DT01NQU5EX0RJU1BMQVlfQ0h