diff --git a/packages/app/src/assistant-file-links/link.tsx b/packages/app/src/assistant-file-links/link.tsx index d209411ac..2630685e1 100644 --- a/packages/app/src/assistant-file-links/link.tsx +++ b/packages/app/src/assistant-file-links/link.tsx @@ -18,6 +18,7 @@ import { useStableEvent } from "@/hooks/use-stable-event"; import { CODE_SURFACE_DATASET } from "@/styles/code-surface"; import { useAssistantFileLinkResolverContext } from "./provider"; import type { AssistantFileLinkSource } from "./resolver"; +import { formatFileLinkTooltipPath } from "./tooltip-path"; import { useFileLink } from "./use-file-link"; interface AssistantMarkdownLinkProps { @@ -38,7 +39,7 @@ export function AssistantMarkdownLink({ const { configRef } = useAssistantFileLinkResolverContext(); const workspaceRoot = configRef.current.workspaceRoot; const tooltipPath = useMemo( - () => (target ? formatInlinePathTargetForTooltip(target, workspaceRoot) : null), + () => (target ? formatFileLinkTooltipPath({ target, workspaceRoot }) : null), [target, workspaceRoot], ); const handleAnchorClickCapture = useStableEvent((event: MouseEvent) => { @@ -147,38 +148,6 @@ export function AssistantMarkdownCodeLink({ ); } -function formatInlinePathTargetForTooltip( - target: { path: string; lineStart?: number; lineEnd?: number }, - workspaceRoot: string | undefined, -): string { - let result = relativizePathToWorkspace(target.path, workspaceRoot); - if (target.lineStart) { - result += `:${target.lineStart}`; - if (target.lineEnd && target.lineEnd !== target.lineStart) { - result += `-${target.lineEnd}`; - } - } - return result; -} - -function relativizePathToWorkspace(filePath: string, workspaceRoot: string | undefined): string { - if (!workspaceRoot) { - return filePath; - } - const root = workspaceRoot.replace(/\/+$/, ""); - if (!root) { - return filePath; - } - if (filePath === root) { - return "."; - } - const prefix = `${root}/`; - if (filePath.startsWith(prefix)) { - return filePath.slice(prefix.length); - } - return filePath; -} - interface AssistantInlineCodePathLinkProps { content: string; inheritedStyles: TextStyle; diff --git a/packages/app/src/assistant-file-links/tooltip-path.test.ts b/packages/app/src/assistant-file-links/tooltip-path.test.ts new file mode 100644 index 000000000..dc07f5a0d --- /dev/null +++ b/packages/app/src/assistant-file-links/tooltip-path.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import { formatFileLinkTooltipPath } from "./tooltip-path"; + +describe("formatFileLinkTooltipPath", () => { + it("shows a Windows file path relative to its workspace regardless of separators", () => { + expect( + formatFileLinkTooltipPath({ + target: { + path: "C:/Users/me/repo/src/app.ts", + lineStart: 12, + lineEnd: 20, + }, + workspaceRoot: "C:\\Users\\me\\repo", + }), + ).toBe("src/app.ts:12-20"); + }); +}); diff --git a/packages/app/src/assistant-file-links/tooltip-path.ts b/packages/app/src/assistant-file-links/tooltip-path.ts new file mode 100644 index 000000000..16f98c7fe --- /dev/null +++ b/packages/app/src/assistant-file-links/tooltip-path.ts @@ -0,0 +1,23 @@ +import { resolveWorkspaceFilePaths, type WorkspaceFileLocation } from "@/workspace/file-open"; + +interface FormatFileLinkTooltipPathInput { + target: WorkspaceFileLocation; + workspaceRoot?: string; +} + +export function formatFileLinkTooltipPath({ + target, + workspaceRoot, +}: FormatFileLinkTooltipPathInput): string { + const resolvedPaths = workspaceRoot + ? resolveWorkspaceFilePaths({ path: target.path, workspaceRoot }) + : null; + let result = resolvedPaths?.relativePath ?? target.path; + if (target.lineStart) { + result += `:${target.lineStart}`; + if (target.lineEnd && target.lineEnd !== target.lineStart) { + result += `-${target.lineEnd}`; + } + } + return result; +}