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
{fileName}
; }, renderConfirmBody: (call) => (
{getFilePath(call.input)}
), 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 }); if (unifiedDiff) { return ; } 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 }) : undefined; if (resultDiff) { const fileName = tryGetFileName(call.input); if (!fileName) return null; const { insertions, deletions } = countDiffStats(resultDiff); return (
{fileName}
); } return WriteRenderer.renderCallArgs?.(call, t) ?? defaultRenderCallArgs(call); }, renderResult: (call, result) => WriteRenderer.renderResult?.(call, result, t) ?? defaultRenderResult(call, result, t), }), };