Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/ui/components/panes/FileListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,15 @@ export function FileListItem({
style={{ height: 1, flexDirection: "row", backgroundColor: rowBackground }}
>
{index > 0 && <text fg={selected ? theme.text : theme.muted}> </text>}
<text fg={stat.kind === "addition" ? theme.badgeAdded : theme.badgeRemoved}>
<text
fg={
stat.kind === "agent-comment"
? theme.noteBorder
: stat.kind === "addition"
? theme.badgeAdded
: theme.badgeRemoved
}
>
Comment on lines +119 to +127
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 theme.fileModified is semantically off for the agent-comment badge

fileModified names the color used for the "M" state icon that appears on modified files. Using it for the agent-comment badge means both glyphs ("M" and "*n") render identically on a modified file, which blurs their distinct meanings. theme.noteBorder already carries the "agent/note context" semantic and is already purple across all themes — it's a more precise choice:

Suggested change
<text
fg={
stat.kind === "agent-comment"
? theme.fileModified
: stat.kind === "addition"
? theme.badgeAdded
: theme.badgeRemoved
}
>
fg={
stat.kind === "agent-comment"
? theme.noteBorder
: stat.kind === "addition"
? theme.badgeAdded
: theme.badgeRemoved
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/components/panes/FileListItem.tsx
Line: 119-127

Comment:
**`theme.fileModified` is semantically off for the agent-comment badge**

`fileModified` names the color used for the "M" state icon that appears on modified files. Using it for the agent-comment badge means both glyphs ("M" and "`*n`") render identically on a modified file, which blurs their distinct meanings. `theme.noteBorder` already carries the "agent/note context" semantic and is already purple across all themes — it's a more precise choice:

```suggestion
                  fg={
                    stat.kind === "agent-comment"
                      ? theme.noteBorder
                      : stat.kind === "addition"
                        ? theme.badgeAdded
                        : theme.badgeRemoved
                  }
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — I switched the badge color to theme.noteBorder so the purple *n reads as note context rather than sharing the modified-file icon color.

Responded by Pi using gpt-5.3-codex.

{stat.text}
</text>
</box>
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/ui-components.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
56 changes: 56 additions & 0 deletions src/ui/lib/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
20 changes: 16 additions & 4 deletions src/ui/lib/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down Expand Up @@ -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<FileListEntry, "additionsText" | "deletionsText">) {
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<FileListEntry, "agentCommentsText" | "additionsText" | "deletionsText">,
) {
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 });
Expand All @@ -57,7 +66,7 @@ export function sidebarEntryStats(entry: Pick<FileListEntry, "additionsText" | "

/** Measure the rendered sidebar stats width, including the space between badges. */
export function sidebarEntryStatsWidth(
entry: Pick<FileListEntry, "additionsText" | "deletionsText">,
entry: Pick<FileListEntry, "agentCommentsText" | "additionsText" | "deletionsText">,
) {
return sidebarEntryStats(entry).reduce(
(width, stat, index) => width + stat.text.length + (index > 0 ? 1 : 0),
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion test/pty/ui-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);

Expand Down
Loading