From 0b40ea62e12ea061bbc8e954cf8dee288e9faf56 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 10 Jun 2026 01:27:32 -0700 Subject: [PATCH] Extract changed files card with compact aligned diff stats (#3023) Co-authored-by: ss Co-authored-by: Claude Opus 4.8 Co-authored-by: codex --- .../src/components/chat/ChangedFilesTree.tsx | 82 ++++++++++++++++++- .../web/src/components/chat/DiffStatLabel.tsx | 33 +++++++- .../src/components/chat/MessagesTimeline.tsx | 56 +++---------- 3 files changed, 118 insertions(+), 53 deletions(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 29bd96ea056f0c3fa84401d70f919ea0ac308f1d..ae5b431738206b97b8e3891bc350f46d752451db 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -1,14 +1,85 @@ import { type TurnId } from "@t3tools/contracts"; import { memo, useCallback, useMemo, useState } from "react"; import { type TurnDiffFileChange } from "../../types"; -import { buildTurnDiffTree, type TurnDiffTreeNode } from "../../lib/turnDiffTree"; +import { + buildTurnDiffTree, + summarizeTurnDiffStats, + type TurnDiffTreeNode, +} from "../../lib/turnDiffTree"; import { ChevronRightIcon, FolderIcon, FolderClosedIcon } from "lucide-react"; import { cn } from "~/lib/utils"; import { DiffStatLabel, hasNonZeroStat } from "./DiffStatLabel"; import { VscodeEntryIcon } from "./VscodeEntryIcon"; +import { Button } from "../ui/button"; const EMPTY_DIRECTORY_OVERRIDES: Record = {}; +export const ChangedFilesCard = memo(function ChangedFilesCard(props: { + turnId: TurnId; + files: ReadonlyArray; + allDirectoriesExpanded: boolean; + resolvedTheme: "light" | "dark"; + onToggleAllDirectories: () => void; + onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void; +}) { + const { + turnId, + files, + allDirectoriesExpanded, + resolvedTheme, + onToggleAllDirectories, + onOpenTurnDiff, + } = props; + const summaryStat = useMemo(() => summarizeTurnDiffStats(files), [files]); + + return ( +
+
+

+ {files.length} changed files + {hasNonZeroStat(summaryStat) && ( + + )} +

+
+ + +
+
+
+ +
+
+ ); +}); + export const ChangedFilesTree = memo(function ChangedFilesTree(props: { turnId: TurnId; files: ReadonlyArray; @@ -22,6 +93,7 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: { () => collectDirectoryPaths(treeNodes).join("\u0000"), [treeNodes], ); + const hasDirectoryNodes = directoryPathsKey.length > 0; const expansionStateKey = `${allDirectoriesExpanded ? "expanded" : "collapsed"}\u0000${directoryPathsKey}`; const [directoryExpansionState, setDirectoryExpansionState] = useState<{ key: string; @@ -60,7 +132,7 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: { - - - - - + + setExpanded(routeThreadKey, turnSummary.turnId, !allDirectoriesExpanded) + } + onOpenTurnDiff={onOpenTurnDiff} + /> ); } -- 2.54.0