106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
/**
|
|
* ReadToolViewer
|
|
*
|
|
* Renders the Read tool result using CodeBlockViewer.
|
|
*/
|
|
|
|
import React, { memo } from 'react';
|
|
|
|
import { useAppTranslation } from '@features/localization/renderer';
|
|
import { CodeBlockViewer, MarkdownViewer } from '@renderer/components/chat/viewers';
|
|
|
|
import type { LinkedToolItem } from '@renderer/types/groups';
|
|
|
|
interface ReadToolViewerProps {
|
|
linkedTool: LinkedToolItem;
|
|
}
|
|
|
|
export const ReadToolViewer = memo(function ReadToolViewer({ linkedTool }: ReadToolViewerProps) {
|
|
const { t } = useAppTranslation('common');
|
|
const filePath = linkedTool.input.file_path as string;
|
|
|
|
// Prefer enriched toolUseResult data
|
|
const toolUseResult = linkedTool.result?.toolUseResult as Record<string, unknown> | undefined;
|
|
const fileData = toolUseResult?.file as
|
|
| {
|
|
content?: string;
|
|
startLine?: number;
|
|
totalLines?: number;
|
|
numLines?: number;
|
|
}
|
|
| undefined;
|
|
|
|
// Get content: prefer enriched file data, fall back to raw result content
|
|
let content: string;
|
|
if (fileData?.content) {
|
|
content = fileData.content;
|
|
} else {
|
|
const resultContent = linkedTool.result?.content;
|
|
content =
|
|
typeof resultContent === 'string'
|
|
? resultContent
|
|
: Array.isArray(resultContent)
|
|
? resultContent
|
|
.map((item: unknown) => (typeof item === 'string' ? item : JSON.stringify(item)))
|
|
.join('\n')
|
|
: JSON.stringify(resultContent, null, 2);
|
|
}
|
|
|
|
// Get line range
|
|
const startLine = fileData?.startLine ?? (linkedTool.input.offset as number | undefined) ?? 1;
|
|
const numLinesRead = fileData?.numLines;
|
|
const limit = linkedTool.input.limit as number | undefined;
|
|
const endLine = numLinesRead
|
|
? startLine + numLinesRead - 1
|
|
: limit
|
|
? startLine + limit - 1
|
|
: undefined;
|
|
|
|
const isMarkdownFile = /\.mdx?$/i.test(filePath);
|
|
const [viewMode, setViewMode] = React.useState<'code' | 'preview'>(
|
|
isMarkdownFile ? 'preview' : 'code'
|
|
);
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
{isMarkdownFile && (
|
|
<div className="flex items-center justify-end gap-1">
|
|
<button
|
|
type="button"
|
|
onClick={() => setViewMode('code')}
|
|
className="rounded px-2 py-1 text-xs transition-colors"
|
|
style={{
|
|
backgroundColor: viewMode === 'code' ? 'var(--tag-bg)' : 'transparent',
|
|
color: viewMode === 'code' ? 'var(--tag-text)' : 'var(--color-text-muted)',
|
|
border: '1px solid var(--tag-border)',
|
|
}}
|
|
>
|
|
{t('code.code')}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setViewMode('preview')}
|
|
className="rounded px-2 py-1 text-xs transition-colors"
|
|
style={{
|
|
backgroundColor: viewMode === 'preview' ? 'var(--tag-bg)' : 'transparent',
|
|
color: viewMode === 'preview' ? 'var(--tag-text)' : 'var(--color-text-muted)',
|
|
border: '1px solid var(--tag-border)',
|
|
}}
|
|
>
|
|
{t('code.preview')}
|
|
</button>
|
|
</div>
|
|
)}
|
|
{isMarkdownFile && viewMode === 'preview' ? (
|
|
<MarkdownViewer content={content} label={t('code.markdownPreview')} copyable />
|
|
) : (
|
|
<CodeBlockViewer
|
|
fileName={filePath}
|
|
content={content}
|
|
startLine={startLine}
|
|
endLine={endLine}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
});
|