diff --git a/apps/web/src/components/DiffFilePathCopyButton.browser.test.tsx b/apps/web/src/components/DiffFilePathCopyButton.browser.test.tsx
index 1a0b4238f5f6e..0c5d46395f3ae 100644
--- a/apps/web/src/components/DiffFilePathCopyButton.browser.test.tsx
+++ b/apps/web/src/components/DiffFilePathCopyButton.browser.test.tsx
@@ -8,7 +8,7 @@ import { AnchoredToastProvider } from "./ui/toast";
it("explains a denied clipboard write and lets the same path be copied again", async () => {
await using screen = await render(
-
+
,
);
using write = vi
@@ -28,3 +28,16 @@ it("explains a denied clipboard write and lets the same path be copied again", a
expect(write).toHaveBeenCalledTimes(2);
expect(write).toHaveBeenLastCalledWith("apps/web/src/index.ts");
});
+
+it("copies a Windows workspace's file path with Windows separators", async () => {
+ await using screen = await render(
+
+
+ ,
+ );
+ using write = vi.spyOn(navigator.clipboard, "writeText").mockResolvedValue(undefined);
+
+ await screen.getByRole("button", { name: "Copy file path" }).click();
+
+ await vi.waitFor(() => expect(write).toHaveBeenCalledWith("apps\\web\\src\\index.ts"));
+});
diff --git a/apps/web/src/components/DiffFilePathCopyButton.tsx b/apps/web/src/components/DiffFilePathCopyButton.tsx
index 59aa256294d85..c2beef232e8ac 100644
--- a/apps/web/src/components/DiffFilePathCopyButton.tsx
+++ b/apps/web/src/components/DiffFilePathCopyButton.tsx
@@ -1,3 +1,4 @@
+import { formatFilePathForClipboard } from "@t3tools/client-runtime/media-actions";
import { CheckIcon, CopyIcon } from "lucide-react";
import { useRef } from "react";
import { useCopyToClipboard } from "../hooks/useCopyToClipboard";
@@ -5,7 +6,13 @@ import { ANCHORED_COPY_TOAST_TIMEOUT_MS, showAnchoredCopyErrorToast } from "./ui
import { Button } from "./ui/button";
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
-export function DiffFilePathCopyButton({ filePath }: { filePath: string }) {
+export function DiffFilePathCopyButton({
+ filePath,
+ workspaceRoot,
+}: {
+ filePath: string;
+ workspaceRoot: string | undefined;
+}) {
const ref = useRef(null);
const { copyToClipboard, isCopied } = useCopyToClipboard({
onError: (error) => showAnchoredCopyErrorToast(ref, error),
@@ -21,7 +28,9 @@ export function DiffFilePathCopyButton({ filePath }: { filePath: string }) {
size="icon-micro"
variant="ghost-muted"
aria-label="Copy file path"
- onClick={() => copyToClipboard(filePath, undefined)}
+ onClick={() =>
+ copyToClipboard(formatFilePathForClipboard(filePath, workspaceRoot), undefined)
+ }
/>
}
>
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx
index 99e0b07c1845e..71c197b6ce363 100644
--- a/apps/web/src/components/DiffPanel.tsx
+++ b/apps/web/src/components/DiffPanel.tsx
@@ -177,12 +177,14 @@ function DiffFileCollapseToggle({
function DiffFileHeaderSuffix({
filePath,
+ workspaceRoot,
hasStat,
error,
truncated,
onRetry,
}: {
filePath: string;
+ workspaceRoot: string | undefined;
hasStat: boolean;
error: boolean;
truncated: boolean;
@@ -191,7 +193,7 @@ function DiffFileHeaderSuffix({
return (
<>
-
+
{hasStat ? (
onRetry(filePath)} />
@@ -1436,6 +1438,7 @@ export default function DiffPanel({
return (