From f27a82a886d404139bb0637a57b66e90ea0e18b1 Mon Sep 17 00:00:00 2001 From: ss Date: Sat, 18 Jul 2026 03:11:16 +0200 Subject: [PATCH 1/4] ui: decodexify changed files --- .../src/components/chat/ChangedFilesTree.tsx | 26 +++++++++---------- .../src/components/chat/MessagesTimeline.tsx | 21 ++++++++------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 3ea0e6315fd..769837566cf 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -33,9 +33,9 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { const summaryStat = useMemo(() => summarizeTurnDiffStats(files), [files]); return ( -
-
-

+

+
+

{files.length} changed files {hasNonZeroStat(summaryStat) && ( @@ -60,22 +61,21 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { type="button" size="xs" variant="outline" + className="px-1.5" onClick={() => onOpenTurnDiff(turnId, files[0]?.path)} > View diff

-
- -
+
); }); diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index 61d7855844d..70514d1b8ca 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -1235,18 +1235,19 @@ function AssistantChangedFilesSectionInner({ ); const setExpanded = useUiStateStore((store) => store.setThreadChangedFilesExpanded); const summaryStat = summarizeTurnDiffStats(checkpointFiles); - const changedFileCountLabel = String(checkpointFiles.length); return ( -
-
-

- Changed files ({changedFileCountLabel}) +

+
+

+ {checkpointFiles.length} changed files {hasNonZeroStat(summaryStat) && ( - <> - - - + )}

@@ -1254,6 +1255,7 @@ function AssistantChangedFilesSectionInner({ type="button" size="xs" variant="outline" + className="px-1.5" data-scroll-anchor-ignore onClick={() => setExpanded(routeThreadKey, turnSummary.turnId, !allDirectoriesExpanded)} > @@ -1263,6 +1265,7 @@ function AssistantChangedFilesSectionInner({ type="button" size="xs" variant="outline" + className="px-1.5" onClick={() => onOpenTurnDiff(turnSummary.turnId, checkpointFiles[0]?.path)} > View diff From d55d1a4bb15bc8c3a756b1065c3eb75ca2e3fedf Mon Sep 17 00:00:00 2001 From: ss Date: Sat, 18 Jul 2026 03:20:38 +0200 Subject: [PATCH 2/4] ui: pluralize changed file labels --- apps/web/src/components/chat/ChangedFilesTree.tsx | 4 +++- apps/web/src/components/chat/MessagesTimeline.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 769837566cf..8ce4d0bb4a5 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -36,7 +36,9 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: {

- {files.length} changed files + + {files.length} changed file{files.length === 1 ? "" : "s"} + {hasNonZeroStat(summaryStat) && (

- {checkpointFiles.length} changed files + + {checkpointFiles.length} changed file{checkpointFiles.length === 1 ? "" : "s"} + {hasNonZeroStat(summaryStat) && ( Date: Mon, 20 Jul 2026 14:18:08 +0200 Subject: [PATCH 3/4] Improve changed-files headers and controls - Keep changed-files headers sticky while scrolling - Replace text actions with accessible icon buttons and tooltips - Add regression coverage for layout and singular labels --- .../components/chat/ChangedFilesTree.test.tsx | 25 ++++++- .../src/components/chat/ChangedFilesTree.tsx | 68 +++++++++++++------ .../components/chat/MessagesTimeline.test.tsx | 52 +++++++++++++- .../src/components/chat/MessagesTimeline.tsx | 63 +++++++++++------ 4 files changed, 163 insertions(+), 45 deletions(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.test.tsx b/apps/web/src/components/chat/ChangedFilesTree.test.tsx index c371acdb362..2d2ced30fb3 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.test.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.test.tsx @@ -2,7 +2,30 @@ import { TurnId } from "@t3tools/contracts"; import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vite-plus/test"; -import { ChangedFilesTree } from "./ChangedFilesTree"; +import { ChangedFilesCard, ChangedFilesTree } from "./ChangedFilesTree"; + +describe("ChangedFilesCard", () => { + it("keeps its compact header sticky while preserving singular labels", () => { + const markup = renderToStaticMarkup( + {}} + onOpenTurnDiff={() => {}} + />, + ); + + expect(markup).toContain('class="sticky top-0 z-10'); + expect(markup).not.toContain("self-start"); + expect(markup).toContain("whitespace-nowrap"); + expect(markup).toContain('aria-label="Collapse all"'); + expect(markup).toContain('aria-label="View diff"'); + expect(markup).toContain("1 changed file"); + expect(markup).not.toContain("1 changed files"); + }); +}); describe("ChangedFilesTree", () => { it.each([ diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 8ce4d0bb4a5..61152ed0601 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -6,11 +6,19 @@ import { summarizeTurnDiffStats, type TurnDiffTreeNode, } from "../../lib/turnDiffTree"; -import { ChevronRightIcon, FolderIcon, FolderClosedIcon } from "lucide-react"; +import { + ChevronsDownUpIcon, + ChevronsUpDownIcon, + ChevronRightIcon, + FileDiffIcon, + FolderIcon, + FolderClosedIcon, +} from "lucide-react"; import { cn } from "~/lib/utils"; import { DiffStatLabel, hasNonZeroStat } from "./DiffStatLabel"; import { PierreEntryIcon } from "./PierreEntryIcon"; import { Button } from "../ui/button"; +import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; const EMPTY_DIRECTORY_OVERRIDES: Record = {}; @@ -34,8 +42,8 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { return (

-
-

+

+

{files.length} changed file{files.length === 1 ? "" : "s"} @@ -49,25 +57,41 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { )}

- - + + + } + > + {allDirectoriesExpanded ? : } + + + {allDirectoriesExpanded ? "Collapse all" : "Expand all"} + + + + onOpenTurnDiff(turnId, files[0]?.path)} + /> + } + > + + + View diff +
{ + it("keeps assistant changed-files headers sticky below the thread header", async () => { + const { MessagesTimeline } = await import("./MessagesTimeline"); + const assistantMessageId = MessageId.make("message-assistant-with-files"); + const turnId = TurnId.make("turn-with-files"); + const markup = renderToStaticMarkup( + , + ); + + expect(markup).toContain('class="sticky top-2 z-10'); + expect(markup).not.toContain("self-start"); + expect(markup).toContain("whitespace-nowrap"); + expect(markup).toContain('aria-label="Collapse all"'); + expect(markup).toContain('aria-label="View diff"'); + expect(markup).toContain("1 changed file"); + }); + it("uses LegendList isNearEnd when deciding whether the live edge is visible", async () => { const { resolveTimelineIsAtEnd, diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index 7f9dcea20a1..aecdf1601be 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -44,8 +44,11 @@ import { CheckIcon, ChevronDownIcon, ChevronRightIcon, + ChevronsDownUpIcon, + ChevronsUpDownIcon, CircleAlertIcon, EyeIcon, + FileDiffIcon, GlobeIcon, HammerIcon, MessageCircleIcon, @@ -1277,8 +1280,8 @@ function AssistantChangedFilesSectionInner({ return (
-
-

+

+

{checkpointFiles.length} changed file{checkpointFiles.length === 1 ? "" : "s"} @@ -1292,25 +1295,43 @@ function AssistantChangedFilesSectionInner({ )}

- - + + + setExpanded(routeThreadKey, turnSummary.turnId, !allDirectoriesExpanded) + } + /> + } + > + {allDirectoriesExpanded ? : } + + + {allDirectoriesExpanded ? "Collapse all" : "Expand all"} + + + + onOpenTurnDiff(turnSummary.turnId, checkpointFiles[0]?.path)} + /> + } + > + + + View diff +
Date: Mon, 20 Jul 2026 14:42:47 +0200 Subject: [PATCH 4/4] Standardize changed-files action button sizing - Set action buttons to 22px with compact 12px icons - Extend markup tests for consistent sizing --- apps/web/src/components/chat/ChangedFilesTree.test.tsx | 2 ++ apps/web/src/components/chat/ChangedFilesTree.tsx | 10 ++++++++-- apps/web/src/components/chat/MessagesTimeline.test.tsx | 2 ++ apps/web/src/components/chat/MessagesTimeline.tsx | 10 ++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.test.tsx b/apps/web/src/components/chat/ChangedFilesTree.test.tsx index 2d2ced30fb3..120827b34b2 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.test.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.test.tsx @@ -20,6 +20,8 @@ describe("ChangedFilesCard", () => { expect(markup).toContain('class="sticky top-0 z-10'); expect(markup).not.toContain("self-start"); expect(markup).toContain("whitespace-nowrap"); + expect(markup).toContain("!size-[22px]"); + expect(markup).toContain("size-3"); expect(markup).toContain('aria-label="Collapse all"'); expect(markup).toContain('aria-label="View diff"'); expect(markup).toContain("1 changed file"); diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 61152ed0601..6fc1462c5e6 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -64,13 +64,18 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { type="button" size="icon-xs" variant="outline" + className="!size-[22px]" aria-label={allDirectoriesExpanded ? "Collapse all" : "Expand all"} data-scroll-anchor-ignore onClick={onToggleAllDirectories} /> } > - {allDirectoriesExpanded ? : } + {allDirectoriesExpanded ? ( + + ) : ( + + )} {allDirectoriesExpanded ? "Collapse all" : "Expand all"} @@ -83,12 +88,13 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { type="button" size="icon-xs" variant="outline" + className="!size-[22px]" aria-label="View diff" onClick={() => onOpenTurnDiff(turnId, files[0]?.path)} /> } > - + View diff diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx index 271f85daafc..b340a248fbe 100644 --- a/apps/web/src/components/chat/MessagesTimeline.test.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx @@ -264,6 +264,8 @@ describe("MessagesTimeline", () => { expect(markup).toContain('class="sticky top-2 z-10'); expect(markup).not.toContain("self-start"); expect(markup).toContain("whitespace-nowrap"); + expect(markup).toContain("!size-[22px]"); + expect(markup).toContain("size-3"); expect(markup).toContain('aria-label="Collapse all"'); expect(markup).toContain('aria-label="View diff"'); expect(markup).toContain("1 changed file"); diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index aecdf1601be..f759aa150be 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -1302,6 +1302,7 @@ function AssistantChangedFilesSectionInner({ type="button" size="icon-xs" variant="outline" + className="!size-[22px]" aria-label={allDirectoriesExpanded ? "Collapse all" : "Expand all"} data-scroll-anchor-ignore onClick={() => @@ -1310,7 +1311,11 @@ function AssistantChangedFilesSectionInner({ /> } > - {allDirectoriesExpanded ? : } + {allDirectoriesExpanded ? ( + + ) : ( + + )} {allDirectoriesExpanded ? "Collapse all" : "Expand all"} @@ -1323,12 +1328,13 @@ function AssistantChangedFilesSectionInner({ type="button" size="icon-xs" variant="outline" + className="!size-[22px]" aria-label="View diff" onClick={() => onOpenTurnDiff(turnSummary.turnId, checkpointFiles[0]?.path)} /> } > - + View diff