Allow previews to open readable files (#1214)

* Allow previews to open readable files

* Fix Windows file preview path test

* Address file preview review comments
This commit is contained in:
Mohamed Boudra
2026-05-29 11:35:38 +08:00
committed by GitHub
parent 2e1b907c63
commit 3585c80266
11 changed files with 361 additions and 132 deletions
@@ -0,0 +1,74 @@
import { describe, expect, it } from "vitest";
import { resolveFilePreviewReadTarget } from "./preview-target";
describe("resolveFilePreviewReadTarget", () => {
it("uses the workspace cwd for relative paths", () => {
expect(
resolveFilePreviewReadTarget({
path: "packages/app/src/message.tsx",
workspaceRoot: "/Users/test/project",
}),
).toEqual({
cwd: "/Users/test/project",
path: "packages/app/src/message.tsx",
});
});
it("uses the workspace cwd for absolute paths inside the workspace", () => {
expect(
resolveFilePreviewReadTarget({
path: "/Users/test/project/packages/app/src/message.tsx",
workspaceRoot: "/Users/test/project",
}),
).toEqual({
cwd: "/Users/test/project",
path: "/Users/test/project/packages/app/src/message.tsx",
});
});
it("uses the filesystem root for absolute paths outside the workspace", () => {
expect(
resolveFilePreviewReadTarget({
path: "/tmp/paseo-preview.txt",
workspaceRoot: "/Users/test/project",
}),
).toEqual({
cwd: "/",
path: "/tmp/paseo-preview.txt",
});
});
it("uses the home root for tilde paths", () => {
expect(
resolveFilePreviewReadTarget({
path: "~/.paseo/plans/file-preview.md",
workspaceRoot: "/Users/test/project",
}),
).toEqual({
cwd: "~",
path: "~/.paseo/plans/file-preview.md",
});
});
it("uses the drive root for Windows absolute paths outside the workspace", () => {
expect(
resolveFilePreviewReadTarget({
path: "C:/Users/test/Desktop/file.txt",
workspaceRoot: "D:/repo",
}),
).toEqual({
cwd: "C:/",
path: "C:/Users/test/Desktop/file.txt",
});
});
it("rejects relative paths without an absolute workspace root", () => {
expect(resolveFilePreviewReadTarget({ path: "src/app.ts" })).toBeNull();
expect(
resolveFilePreviewReadTarget({
path: "src/app.ts",
workspaceRoot: "relative/root",
}),
).toBeNull();
});
});
@@ -0,0 +1,107 @@
import { isAbsolutePath } from "@/utils/path";
export interface FilePreviewReadTarget {
cwd: string;
path: string;
}
function trimTrailingSeparators(value: string): string {
if (value === "/" || /^[A-Za-z]:[\\/]?$/.test(value)) {
return value.replace(/\\/g, "/");
}
return value.replace(/[\\/]+$/, "");
}
function normalizeForPathComparison(value: string): string {
const normalized = trimTrailingSeparators(value.replace(/\\/g, "/"));
if (/^[A-Za-z]:\//.test(normalized)) {
return `${normalized.slice(0, 1).toUpperCase()}${normalized.slice(1)}`;
}
return normalized;
}
function isPathWithinRoot(candidatePath: string, rootPath: string): boolean {
const candidate = normalizeForPathComparison(candidatePath);
const root = normalizeForPathComparison(rootPath);
if (!candidate || !root) {
return false;
}
if (root === "/") {
return candidate.startsWith("/");
}
if (candidate === root) {
return true;
}
return candidate.startsWith(`${root}/`);
}
function deriveFilesystemRootFromAbsolutePath(value: string): string | null {
if (value.startsWith("/")) {
return "/";
}
const driveMatch = /^([A-Za-z]:)[\\/]/.exec(value);
if (driveMatch?.[1]) {
return `${driveMatch[1]}/`;
}
const uncMatch = /^(\\\\[^\\]+\\[^\\]+)/.exec(value);
if (uncMatch?.[1]) {
return uncMatch[1];
}
return null;
}
function isHomeRelativePath(value: string): boolean {
return value === "~" || value.startsWith("~/") || value.startsWith("~\\");
}
export function resolveFilePreviewReadTarget(input: {
path: string;
workspaceRoot?: string;
}): FilePreviewReadTarget | null {
const previewPath = input.path.trim();
if (!previewPath) {
return null;
}
if (isHomeRelativePath(previewPath)) {
return {
cwd: "~",
path: previewPath,
};
}
const workspaceRoot = input.workspaceRoot?.trim();
if (!isAbsolutePath(previewPath)) {
if (!workspaceRoot || !isAbsolutePath(workspaceRoot)) {
return null;
}
return {
cwd: workspaceRoot,
path: previewPath,
};
}
if (
workspaceRoot &&
isAbsolutePath(workspaceRoot) &&
isPathWithinRoot(previewPath, workspaceRoot)
) {
return {
cwd: workspaceRoot,
path: previewPath,
};
}
const filesystemRoot = deriveFilesystemRootFromAbsolutePath(previewPath);
if (!filesystemRoot) {
return null;
}
return {
cwd: filesystemRoot,
path: previewPath,
};
}