feat: show changed files after completed turns

Add changed-file pills with per-file diff stats
Add a chat setting to disable the feature fully
Avoid changed-file projection work when disabled
This commit is contained in:
Bohdan Triapitsyn
2026-06-03 22:11:37 +03:00
parent 9087950f1a
commit 04b1425c2e
22 changed files with 218 additions and 41 deletions
@@ -1,6 +1,6 @@
import { projectTurnActivity } from './projectTurnActivity';
import { projectTurnIndexes } from './projectTurnIndexes';
import { projectTurnDiffStats, projectTurnSummary } from './projectTurnSummary';
import { projectTurnChangedFiles, projectTurnDiffStats, projectTurnSummary } from './projectTurnSummary';
import type {
ChatMessageEntry,
TurnMessageRecord,
@@ -83,11 +83,13 @@ const buildTurnStreamState = (userMessage: ChatMessageEntry, assistantMessages:
interface ProjectTurnRecordsOptions {
previousProjection?: TurnProjectionResult | null;
showTextJustificationActivity: boolean;
showTurnChangedFiles: boolean;
}
const DEFAULT_OPTIONS: ProjectTurnRecordsOptions = {
previousProjection: null,
showTextJustificationActivity: false,
showTurnChangedFiles: false,
};
const areSameMessageRefs = (left: ChatMessageEntry[], right: ChatMessageEntry[]): boolean => {
@@ -180,6 +182,7 @@ export const projectTurnRecords = (
hasTools: false,
hasReasoning: false,
diffStats: undefined,
changedFiles: undefined,
stream: {
isStreaming: false,
isRetrying: false,
@@ -215,6 +218,9 @@ export const projectTurnRecords = (
turn.summary = projectTurnSummary(turn.assistantMessages);
turn.summaryText = turn.summary.text ?? getUserSummaryBody(turn.userMessage);
turn.diffStats = projectTurnDiffStats(turn.userMessage);
turn.changedFiles = effectiveOptions.showTurnChangedFiles
? projectTurnChangedFiles(turn.userMessage)
: undefined;
const activity = projectTurnActivity({
turnId: turn.turnId,
@@ -1,6 +1,7 @@
import type { ChatMessageEntry, TurnDiffStats, TurnSummaryRecord } from './types';
import type { ChatMessageEntry, TurnChangedFile, TurnDiffStats, TurnSummaryRecord } from './types';
interface SummaryDiff {
file?: string | null;
additions?: number | null;
deletions?: number | null;
}
@@ -102,3 +103,31 @@ export const projectTurnDiffStats = (userMessage: ChatMessageEntry): TurnDiffSta
files,
};
};
export const projectTurnChangedFiles = (userMessage: ChatMessageEntry): TurnChangedFile[] | undefined => {
const summary = (userMessage.info as { summary?: UserSummaryPayload | null }).summary;
const diffs = summary?.diffs;
if (!Array.isArray(diffs) || diffs.length === 0) {
return undefined;
}
const files = diffs
.map((diff) => {
if (!diff || typeof diff.file !== 'string' || diff.file.trim().length === 0) {
return null;
}
const additions = typeof diff.additions === 'number' ? diff.additions : 0;
const deletions = typeof diff.deletions === 'number' ? diff.deletions : 0;
if (additions === 0 && deletions === 0) {
return null;
}
return {
file: diff.file,
additions,
deletions,
};
})
.filter((file): file is TurnChangedFile => file !== null);
return files.length > 0 ? files : undefined;
};
@@ -34,6 +34,12 @@ export interface TurnDiffStats {
files: number;
}
export interface TurnChangedFile {
file: string;
additions: number;
deletions: number;
}
export interface TurnActivityGroup {
id: string;
anchorMessageId: string;
@@ -70,6 +76,7 @@ export interface TurnRecord {
hasTools: boolean;
hasReasoning: boolean;
diffStats?: TurnDiffStats;
changedFiles?: TurnChangedFile[];
stream: TurnStreamState;
startedAt?: number;
completedAt?: number;
@@ -115,6 +122,7 @@ export interface TurnGroupingContext {
hasTools: boolean;
hasReasoning: boolean;
diffStats?: TurnDiffStats;
changedFiles?: TurnChangedFile[];
userMessageCreatedAt?: number;
userMessageVariant?: string;
isWorking: boolean;