Spaces:
Paused
Paused
File size: 3,547 Bytes
0b9dc2e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | import {
defaultGetDisplayName,
defaultRenderCallArgs,
defaultRenderGroup,
defaultRenderResult,
} from './DefaultRenderer';
import { DiffPreview } from './DiffPreview';
import type { ToolRenderer } from './types';
import {
countDiffStats,
DiffStats,
getFilePath,
getResultDiff,
tryGetFileName,
} from '@/components/chat/tool-renderers/_shared.tsx';
export const WriteRenderer: ToolRenderer = {
getDisplayName: (call) => call.name,
renderCallArgs: (call) => {
// While the tool-call JSON is still streaming, ``call.input`` is a
// partial dict (e.g. ``{"file_path": "foo", "content": "...``) and
// parsing yields the wrong file name or none at all. Render an empty
// fragment (not ``null``) so the index.ts ``?? defaultRenderCallArgs``
// fallback doesn't kick in and dump the raw JSON string.
const fileName = tryGetFileName(call.input);
if (!fileName) return <></>;
// Pre-execution we only know the new ``content`` (not the previous
// file body), so any ``+N`` count would be misleading on overwrites.
// The post-execution renderGroup override below adds ``+N -M`` once
// ``metadata.diff`` arrives.
return <div className="flex items-center gap-2 font-normal">{fileName}</div>;
},
renderConfirmBody: (call) => (
<div className="w-full max-w-full overflow-hidden text-ellipsis truncate">
<div className="text-secondary-foreground">{getFilePath(call.input)}</div>
</div>
),
renderResult: (call, result, t) => {
if (result.state === 'success') {
// The backend Write tool always attaches a unified diff in
// ``metadata.diff`` (correctly representing both new-file
// creation against /dev/null and overwrites of existing files
// with absolute line numbers). If it's missing we fall through
// rather than render a misleading client-side diff.
const unifiedDiff = getResultDiff(result as { metadata?: Record<string, unknown> });
if (unifiedDiff) {
return <DiffPreview unifiedDiff={unifiedDiff} />;
}
return undefined;
}
if (call.state === 'asking' || !result || result.state === 'running') {
return t('common.running');
}
if (result.state === 'interrupted') {
return t('common.interrupted');
}
return undefined;
},
renderGroup: (calls, t) =>
defaultRenderGroup(calls, t, {
getDisplayName: (call) =>
WriteRenderer.getDisplayName?.(call, t) ?? defaultGetDisplayName(call),
renderCallArgs: (call) => {
// Once the backend has produced the post-execution unified
// diff, compute and show the real ``+N -M`` stats — this
// correctly accounts for overwrites of existing files
// (which the pre-execution renderCallArgs cannot know).
const enriched = calls.find((c) => c.call.id === call.id);
const resultDiff = enriched?.result
? getResultDiff(enriched.result as { metadata?: Record<string, unknown> })
: undefined;
if (resultDiff) {
const fileName = tryGetFileName(call.input);
if (!fileName) return null;
const { insertions, deletions } = countDiffStats(resultDiff);
return (
<div className="flex items-center gap-2 font-normal">
{fileName}
<DiffStats insertions={insertions} deletions={deletions} />
</div>
);
}
return WriteRenderer.renderCallArgs?.(call, t) ?? defaultRenderCallArgs(call);
},
renderResult: (call, result) =>
WriteRenderer.renderResult?.(call, result, t) ??
defaultRenderResult(call, result, t),
}),
};
|