@@ -15,6 +15,7 @@ import { unescapeHtmlEntities } from "../../utils/text-normalization"
1515import { parseXmlForDiff } from "../../utils/xml"
1616import { EXPERIMENT_IDS , experiments } from "../../shared/experiments"
1717import { applyDiffToolLegacy } from "./applyDiffTool"
18+ import { computeDiffStats , sanitizeUnifiedDiff } from "../diff/stats"
1819
1920interface DiffOperation {
2021 path : string
@@ -282,31 +283,70 @@ Original error: ${errorMessage}`
282283 ( opResult ) => cline . rooProtectedController ?. isWriteProtected ( opResult . path ) || false ,
283284 )
284285
285- // Prepare batch diff data
286- const batchDiffs = operationsToApprove . map ( ( opResult ) => {
286+ // Stream batch diffs progressively for better UX
287+ const batchDiffs : Array < {
288+ path : string
289+ changeCount : number
290+ key : string
291+ content : string
292+ diffStats ?: { added : number ; removed : number }
293+ diffs ?: Array < { content : string ; startLine ?: number } >
294+ } > = [ ]
295+
296+ for ( const opResult of operationsToApprove ) {
287297 const readablePath = getReadablePath ( cline . cwd , opResult . path )
288298 const changeCount = opResult . diffItems ?. length || 0
289299 const changeText = changeCount === 1 ? "1 change" : `${ changeCount } changes`
290300
291- return {
301+ let unified = ""
302+ try {
303+ const original = await fs . readFile ( opResult . absolutePath ! , "utf-8" )
304+ const processed = ! cline . api . getModel ( ) . id . includes ( "claude" )
305+ ? ( opResult . diffItems || [ ] ) . map ( ( item ) => ( {
306+ ...item ,
307+ content : item . content ? unescapeHtmlEntities ( item . content ) : item . content ,
308+ } ) )
309+ : opResult . diffItems || [ ]
310+
311+ const applyRes =
312+ ( await cline . diffStrategy ?. applyDiff ( original , processed ) ) ?? ( { success : false } as any )
313+ const newContent = applyRes . success && applyRes . content ? applyRes . content : original
314+ unified = formatResponse . createPrettyPatch ( opResult . path , original , newContent )
315+ } catch {
316+ unified = ""
317+ }
318+
319+ const unifiedSanitized = sanitizeUnifiedDiff ( unified )
320+ const stats = computeDiffStats ( unifiedSanitized ) || undefined
321+ batchDiffs . push ( {
292322 path : readablePath ,
293323 changeCount,
294324 key : `${ readablePath } (${ changeText } )` ,
295- content : opResult . path , // Full relative path
325+ content : unifiedSanitized ,
326+ diffStats : stats ,
296327 diffs : opResult . diffItems ?. map ( ( item ) => ( {
297328 content : item . content ,
298329 startLine : item . startLine ,
299330 } ) ) ,
300- }
301- } )
331+ } )
332+
333+ // Send a partial update after each file preview is ready
334+ const partialMessage = JSON . stringify ( {
335+ tool : "appliedDiff" ,
336+ batchDiffs,
337+ isProtected : hasProtectedFiles ,
338+ } satisfies ClineSayTool )
339+ await cline . ask ( "tool" , partialMessage , true ) . catch ( ( ) => { } )
340+ }
302341
342+ // Final approval message (non-partial)
303343 const completeMessage = JSON . stringify ( {
304344 tool : "appliedDiff" ,
305345 batchDiffs,
306346 isProtected : hasProtectedFiles ,
307347 } satisfies ClineSayTool )
308348
309- const { response, text, images } = await cline . ask ( "tool" , completeMessage , hasProtectedFiles )
349+ const { response, text, images } = await cline . ask ( "tool" , completeMessage , false )
310350
311351 // Process batch response
312352 if ( response === "yesButtonClicked" ) {
@@ -418,6 +458,7 @@ Original error: ${errorMessage}`
418458
419459 try {
420460 let originalContent : string | null = await fs . readFile ( absolutePath , "utf-8" )
461+ let beforeContent : string | null = originalContent
421462 let successCount = 0
422463 let formattedError = ""
423464
@@ -540,9 +581,13 @@ ${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""}
540581 if ( operationsToApprove . length === 1 ) {
541582 // Prepare common data for single file operation
542583 const diffContents = diffItems . map ( ( item ) => item . content ) . join ( "\n\n" )
584+ const unifiedPatchRaw = formatResponse . createPrettyPatch ( relPath , beforeContent ! , originalContent ! )
585+ const unifiedPatch = sanitizeUnifiedDiff ( unifiedPatchRaw )
543586 const operationMessage = JSON . stringify ( {
544587 ...sharedMessageProps ,
545588 diff : diffContents ,
589+ content : unifiedPatch ,
590+ diffStats : computeDiffStats ( unifiedPatch ) || undefined ,
546591 } satisfies ClineSayTool )
547592
548593 let toolProgressStatus
0 commit comments