diff --git a/extensions/ui-customization/footer.ts b/extensions/ui-customization/footer.ts index dd19606e..9fd5d377 100644 --- a/extensions/ui-customization/footer.ts +++ b/extensions/ui-customization/footer.ts @@ -1,5 +1,5 @@ import { homedir } from "node:os"; -import { relative } from "node:path"; +import { posix, win32 } from "node:path"; import type { Theme } from "@earendil-works/pi-coding-agent"; import { getCapabilities, @@ -117,10 +117,21 @@ export function formatTokens(tokens: number) { return `${(tokens / 1_000_000).toFixed(1)}m`; } -export function formatDirectory(cwd: string) { - const home = homedir(); - if (cwd === home) return "~"; - const display = cwd.startsWith(`${home}/`) ? `~/${relative(home, cwd)}` : cwd; +export function formatDirectory( + cwd: string, + home = homedir(), + pathModule = process.platform === "win32" ? win32 : posix, +) { + const relativePath = pathModule.relative(home, cwd); + const outsideHome = + relativePath === ".." || + relativePath.startsWith(`..${pathModule.sep}`) || + pathModule.isAbsolute(relativePath); + const display = outsideHome + ? cwd + : relativePath + ? `~/${relativePath.replaceAll(pathModule.sep, "/")}` + : "~"; return sanitizeTerminalLabel(display); } diff --git a/tests/extensions/ui-customization/footer.test.ts b/tests/extensions/ui-customization/footer.test.ts index 9ec257f9..6be0465e 100644 --- a/tests/extensions/ui-customization/footer.test.ts +++ b/tests/extensions/ui-customization/footer.test.ts @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; +import { posix, win32 } from "node:path"; import { visibleWidth } from "@earendil-works/pi-tui"; import { DEFAULT_FOOTER_LINES, @@ -9,6 +10,7 @@ import { import { buildSegmentCatalog, fitSegmentsToWidth, + formatDirectory, renderFooter, resolveLineSegments, type FooterSegment, @@ -43,6 +45,39 @@ const gitInfo: GitInfoState = { pullRequest: { number: 42, url: "https://example.com/pr/42", isDraft: false }, }; +test("formatDirectory shortens Windows Home paths", () => { + assert.equal( + formatDirectory("C:\\Users\\Adam\\project", "C:\\Users\\Adam", win32), + "~/project", + ); + assert.equal( + formatDirectory("C:\\Users\\Adam", "C:\\Users\\Adam", win32), + "~", + ); + assert.equal( + formatDirectory("C:\\Users\\Adam2\\project", "C:\\Users\\Adam", win32), + "C:\\Users\\Adam2\\project", + ); + assert.equal( + formatDirectory("/Users/adam/project", "/Users/adam"), + "~/project", + ); +}); + +test("formatDirectory respects POSIX backslashes as filename characters", () => { + assert.equal( + formatDirectory("/Users/adam\\project", "/Users/adam", posix), + "/Users/adam\\project", + ); +}); + +test("formatDirectory compares Windows paths case-insensitively", () => { + assert.equal( + formatDirectory("c:\\users\\adam\\project", "C:\\Users\\Adam", win32), + "~/project", + ); +}); + test("default one-line layout leads with model context and ends with cwd", () => { const lines = renderFooter({ cwd: "/Users/me/project",