mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
110 lines
2.6 KiB
TypeScript
110 lines
2.6 KiB
TypeScript
import { fileUriToPath } from "@/attachments/utils";
|
|
import { isAbsolutePath } from "@/utils/path";
|
|
|
|
export type AssistantImageSourceResolution =
|
|
| { kind: "direct"; uri: string }
|
|
| { kind: "file_rpc"; 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 deriveFallbackRootFromAbsolutePath(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;
|
|
}
|
|
|
|
export function resolveAssistantImageSource(input: {
|
|
source: string;
|
|
workspaceRoot?: string;
|
|
}): AssistantImageSourceResolution | null {
|
|
const source = input.source.trim();
|
|
if (!source) {
|
|
return null;
|
|
}
|
|
|
|
if (/^(https?:|data:|blob:)/i.test(source)) {
|
|
return { kind: "direct", uri: source };
|
|
}
|
|
|
|
const sourcePath = source.startsWith("file://") ? fileUriToPath(source) : source;
|
|
if (!sourcePath) {
|
|
return null;
|
|
}
|
|
|
|
if (!isAbsolutePath(sourcePath)) {
|
|
const workspaceRoot = input.workspaceRoot?.trim();
|
|
if (!workspaceRoot || !isAbsolutePath(workspaceRoot)) {
|
|
return null;
|
|
}
|
|
return {
|
|
kind: "file_rpc",
|
|
cwd: workspaceRoot,
|
|
path: sourcePath,
|
|
};
|
|
}
|
|
|
|
const workspaceRoot = input.workspaceRoot?.trim();
|
|
if (
|
|
workspaceRoot &&
|
|
isAbsolutePath(workspaceRoot) &&
|
|
isPathWithinRoot(sourcePath, workspaceRoot)
|
|
) {
|
|
return {
|
|
kind: "file_rpc",
|
|
cwd: workspaceRoot,
|
|
path: sourcePath,
|
|
};
|
|
}
|
|
|
|
const fallbackRoot = deriveFallbackRootFromAbsolutePath(sourcePath);
|
|
if (!fallbackRoot) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
kind: "file_rpc",
|
|
cwd: fallbackRoot,
|
|
path: sourcePath,
|
|
};
|
|
}
|