diff --git a/src/ui/components/panes/FileListItem.tsx b/src/ui/components/panes/FileListItem.tsx index 36276e9..f01f409 100644 --- a/src/ui/components/panes/FileListItem.tsx +++ b/src/ui/components/panes/FileListItem.tsx @@ -116,7 +116,15 @@ export function FileListItem({ style={{ height: 1, flexDirection: "row", backgroundColor: rowBackground }} > {index > 0 && } - + {stat.text} diff --git a/src/ui/components/ui-components.test.tsx b/src/ui/components/ui-components.test.tsx index 4eb55cf..0cd5dc0 100644 --- a/src/ui/components/ui-components.test.tsx +++ b/src/ui/components/ui-components.test.tsx @@ -416,7 +416,7 @@ describe("UI components", () => { expect(frame).toContain(" App.tsx"); expect(frame).toContain(" MenuDropdown.tsx"); expect(frame).toContain(" watch.ts"); - expect(frame).toContain("+2 -1"); + expect(frame).toContain("*1 +2 -1"); expect(frame).toContain("+5"); expect(frame).toContain("-3"); expect(frame).not.toContain("+0"); diff --git a/src/ui/lib/files.test.ts b/src/ui/lib/files.test.ts index 63adaa4..4e2b653 100644 --- a/src/ui/lib/files.test.ts +++ b/src/ui/lib/files.test.ts @@ -46,21 +46,77 @@ describe("files helpers", () => { expect(entries).toHaveLength(3); expect(entries[0]).toMatchObject({ name: "only-add.ts", + agentCommentsText: null, additionsText: "+5", deletionsText: null, }); expect(entries[1]).toMatchObject({ name: "only-remove.ts", + agentCommentsText: null, additionsText: null, deletionsText: "-3", }); expect(entries[2]).toMatchObject({ name: "Legacy.tsx -> Renamed.tsx", + agentCommentsText: null, additionsText: null, deletionsText: null, }); }); + test("buildSidebarEntries includes compact per-file comment counts before diff stats", () => { + const withComments = createTestDiffFile({ + id: "with-comments", + path: "src/ui/commented.ts", + before: lines("const alpha = 1;", "const beta = 2;", "const gamma = 3;"), + after: lines("const alpha = 10;", "const beta = 2;", "const gamma = 30;"), + agent: { + path: "src/ui/commented.ts", + annotations: [ + { summary: "Note on first hunk", newRange: [1, 1] }, + { summary: "Another note on first hunk", newRange: [1, 1] }, + { summary: "Note on second hunk", newRange: [3, 3] }, + ], + }, + }); + + const [entry] = buildSidebarEntries([withComments]).filter((item) => item.kind === "file"); + + expect(entry).toMatchObject({ + name: "commented.ts", + agentCommentsText: "*3", + additionsText: "+2", + deletionsText: "-2", + }); + }); + + test("buildSidebarEntries counts all comments attached to a file, even off-range ones", () => { + const withComments = createTestDiffFile({ + id: "all-comments", + path: "src/ui/all-comments.ts", + before: lines("const alpha = 1;", "const beta = 2;", "const gamma = 3;"), + after: lines("const alpha = 10;", "const beta = 2;", "const gamma = 30;"), + agent: { + path: "src/ui/all-comments.ts", + annotations: [ + { summary: "First note", newRange: [1, 1] }, + { summary: "Second note", newRange: [1, 1] }, + // The sidebar count is per-file, so even comments outside a visible hunk still count. + { summary: "Third note", newRange: [20, 20] }, + ], + }, + }); + + const [entry] = buildSidebarEntries([withComments]).filter((item) => item.kind === "file"); + + expect(entry).toMatchObject({ + name: "all-comments.ts", + agentCommentsText: "*3", + additionsText: "+2", + deletionsText: "-2", + }); + }); + test("fileLabelParts strips parser-added line endings from rename labels", () => { const renamedAcrossDirectories = { ...createTestDiffFile({ diff --git a/src/ui/lib/files.ts b/src/ui/lib/files.ts index eed9314..7729c68 100644 --- a/src/ui/lib/files.ts +++ b/src/ui/lib/files.ts @@ -7,6 +7,7 @@ export interface FileListEntry { kind: "file"; id: string; name: string; + agentCommentsText: string | null; additionsText: string | null; deletionsText: string | null; changeType: FileDiffMetadata["type"]; @@ -40,9 +41,17 @@ function formatSidebarStat(prefix: "+" | "-", value: number) { return value > 0 ? `${prefix}${value}` : null; } -/** Build the visible stats badges for one sidebar row. */ -export function sidebarEntryStats(entry: Pick) { - const stats: Array<{ kind: "addition" | "deletion"; text: string }> = []; +/** Build the visible stats badges for one sidebar row. + * Keep the agent-note badge first so it reads as review context before line churn. + */ +export function sidebarEntryStats( + entry: Pick, +) { + const stats: Array<{ kind: "agent-comment" | "addition" | "deletion"; text: string }> = []; + + if (entry.agentCommentsText) { + stats.push({ kind: "agent-comment", text: entry.agentCommentsText }); + } if (entry.additionsText) { stats.push({ kind: "addition", text: entry.additionsText }); @@ -57,7 +66,7 @@ export function sidebarEntryStats(entry: Pick, + entry: Pick, ) { return sidebarEntryStats(entry).reduce( (width, stat, index) => width + stat.text.length + (index > 0 ? 1 : 0), @@ -128,10 +137,13 @@ export function buildSidebarEntries(files: DiffFile[]): SidebarEntry[] { } } + const agentCommentCount = file.agent?.annotations.length ?? 0; + entries.push({ kind: "file", id: file.id, name: sidebarFileName(file), + agentCommentsText: agentCommentCount > 0 ? `*${agentCommentCount}` : null, additionsText: formatSidebarStat("+", file.stats.additions), deletionsText: formatSidebarStat("-", file.stats.deletions), changeType: file.metadata.type, diff --git a/test/pty/ui-integration.test.ts b/test/pty/ui-integration.test.ts index b7d7679..ba8b72b 100644 --- a/test/pty/ui-integration.test.ts +++ b/test/pty/ui-integration.test.ts @@ -429,7 +429,8 @@ describe("live UI integration", () => { await session.type("beta"); const filtered = await harness.waitForSnapshot( session, - (text) => text.includes("betaValue") && !text.includes("add = true"), + (text) => + text.includes("betaValue") && !text.includes("alpha.ts") && !text.includes("add = true"), 5_000, );