authorgravatar for 47249803+jorvarea@users.noreply.github.comjorvarea <47249803+jorvarea@users.noreply.github.com> 2026-08-15 14:10:32+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-15 12:10:32+00:00
logf915320914d1bc446e60cbcfe4cd7d75bad4dc2a
tree1ece212d9f7f22e3068b994beb082039bebed311
parent7c8848ebb054c1f4c1279f634633cddc37cb1fac
signature Signed by PGP key B5690EEEBB952194

fix(web): keep the final segment of directory paths with a trailing separator (#5460)

Co-authored-by: jorvarea <jorvarea@users.noreply.github.com>

2 files changed, 31 insertions(+), 2 deletions(-)

apps/web/src/markdown-links.test.ts+25
...@@ -273,3 +273,28 @@ describe("resolveInlineCodeFileLinkMeta", () => {...@@ -273,3 +273,28 @@ describe("resolveInlineCodeFileLinkMeta", () => {
273 expect(resolveInlineCodeFileLinkMeta(".plans/worktree-management-v1.md")).toBeNull();273 expect(resolveInlineCodeFileLinkMeta(".plans/worktree-management-v1.md")).toBeNull();
274 });274 });
275});275});
276
277describe("directory paths with a trailing separator", () => {
278 it("keeps the final segment for a POSIX directory path", () => {
279 expect(resolveMarkdownFileLinkMeta("/tmp/favicons/", "/repo/project")).toMatchObject({
280 basename: "favicons",
281 });
282 });
283
284 it("keeps the final segment for a Windows directory path", () => {
285 expect(
286 resolveMarkdownFileLinkMeta("C:\\Users\\kelchm\\.claude\\", "/repo/project"),
287 ).toMatchObject({ basename: ".claude" });
288 });
289
290 it("matches the label of the same path without a trailing separator", () => {
291 const withSlash = resolveMarkdownFileLinkMeta("/tmp/favicons/", "/repo/project");
292 const withoutSlash = resolveMarkdownFileLinkMeta("/tmp/favicons", "/repo/project");
293 expect(withSlash?.basename).toBe(withoutSlash?.basename);
294 });
295
296 it("does not produce an empty label for the filesystem root", () => {
297 const meta = resolveMarkdownFileLinkMeta("/tmp/", "/repo/project");
298 expect(meta?.basename).not.toBe("");
299 });
300});
apps/web/src/markdown-links.ts+6-2
...@@ -359,8 +359,12 @@ export function resolveInlineCodeFileLinkMeta(...@@ -359,8 +359,12 @@ export function resolveInlineCodeFileLinkMeta(
359}359}
360360
361function basenameOfPath(path: string): string {361function basenameOfPath(path: string): string {
362 const separatorIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));362 // A trailing separator is a valid way to write a directory, so trim it before
363 return separatorIndex >= 0 ? path.slice(separatorIndex + 1) : path;363 // taking the final segment. Without this the segment reads as empty and the
364 // chip renders with no label at all.
365 const trimmed = path.replace(/[/\\]+$/, "") || path;
366 const separatorIndex = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
367 return separatorIndex >= 0 ? trimmed.slice(separatorIndex + 1) : trimmed;
364}368}
365369
366function workspaceRelativePath(path: string, workspaceRoot: string | undefined): string | null {370function workspaceRelativePath(path: string, workspaceRoot: string | undefined): string | null {