diff --git a/.changeset/mark-sidebar-root-groups.md b/.changeset/mark-sidebar-root-groups.md new file mode 100644 index 00000000..8a753edc --- /dev/null +++ b/.changeset/mark-sidebar-root-groups.md @@ -0,0 +1,5 @@ +--- +"hunkdiff": patch +--- + +Label repo-root file runs with an in-place `./` sidebar header so root files remain visually distinct without changing review order. diff --git a/src/ui/AppHost.interactions.test.tsx b/src/ui/AppHost.interactions.test.tsx index 29a7828f..ddef9003 100644 --- a/src/ui/AppHost.interactions.test.tsx +++ b/src/ui/AppHost.interactions.test.tsx @@ -3299,8 +3299,8 @@ describe("App interactions", () => { } await act(async () => { - // Click inside the second file row in the left sidebar. - await setup.mockMouse.click(6, 4); + // Click inside the second file row below the repo-root group header. + await setup.mockMouse.click(6, 5); }); await flush(setup); diff --git a/src/ui/components/ui-components.test.tsx b/src/ui/components/ui-components.test.tsx index f6f9d092..863dc607 100644 --- a/src/ui/components/ui-components.test.tsx +++ b/src/ui/components/ui-components.test.tsx @@ -441,6 +441,12 @@ describe("UI components", () => { previousPath: "src/ui/Legacy.tsx", stats: { additions: 0, deletions: 0 }, }, + createTestDiffFile( + "root", + "zzz-root.ts", + "export const root = 1;\n", + "export const root = 2;\n", + ), ]; const frame = await captureFrame( { onSelectFile={() => {}} />, 36, - 10, + 12, ); expect(frame).toContain("src/ui/"); expect(frame).toContain("src/core/"); + expect(frame).toContain("./"); + expect(frame).toContain(" zzz-root.ts"); + expect(frame.indexOf("src/ui/")).toBeLessThan(frame.indexOf("./")); expect(frame).toContain(" App.tsx"); expect(frame).toContain(" MenuDropdown.tsx"); expect(frame).toContain(" watch.ts"); diff --git a/src/ui/lib/files.test.ts b/src/ui/lib/files.test.ts index 4e2b653f..766edf52 100644 --- a/src/ui/lib/files.test.ts +++ b/src/ui/lib/files.test.ts @@ -117,6 +117,32 @@ describe("files helpers", () => { }); }); + test("buildSidebarEntries marks each root-file run with an in-place ./ group", () => { + const files = [ + createTestDiffFile({ id: "nested-a", path: "src/a.ts" }), + createTestDiffFile({ id: "root-a", path: "README.md" }), + createTestDiffFile({ id: "root-b", path: "package.json" }), + createTestDiffFile({ id: "nested-b", path: "test/b.ts" }), + createTestDiffFile({ id: "root-c", path: "LICENSE" }), + ]; + + const labels = buildSidebarEntries(files).map((entry) => + entry.kind === "group" ? entry.label : entry.name, + ); + + expect(labels).toEqual([ + "src/", + "a.ts", + "./", + "README.md", + "package.json", + "test/", + "b.ts", + "./", + "LICENSE", + ]); + }); + 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 0fdb247b..eb3a0709 100644 --- a/src/ui/lib/files.ts +++ b/src/ui/lib/files.ts @@ -122,22 +122,19 @@ export function filterReviewFiles(files: DiffFile[], query: string): DiffFile[] /** Build the grouped sidebar entries while preserving the review stream order. */ export function buildSidebarEntries(files: DiffFile[]): SidebarEntry[] { const entries: SidebarEntry[] = []; - let activeGroup: string | null = null; + let activeGroup: string | undefined; files.forEach((file, index) => { const path = sanitizeTerminalLine(normalizeDiffPath(file.path) ?? file.path); const group = dirname(path); - const nextGroup = group === "." ? null : group; - - if (nextGroup !== activeGroup) { - activeGroup = nextGroup; - if (activeGroup) { - entries.push({ - kind: "group", - id: `group:${activeGroup}:${index}`, - label: `${activeGroup}/`, - }); - } + + if (group !== activeGroup) { + activeGroup = group; + entries.push({ + kind: "group", + id: `group:${group}:${index}`, + label: group === "." ? "./" : `${group}/`, + }); } const agentCommentCount = file.agent?.annotations.length ?? 0;