81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { isWindows } from "./os";
|
|
|
|
/**
|
|
* Normalize user-provided executable path for the current OS.
|
|
* - Expands ~ on macOS/Linux via PathUtils (when available)
|
|
* - Ensures .exe suffix on Windows when missing
|
|
*/
|
|
export function normalizeExecutablePath(input: string): string {
|
|
const trimmed = (input || "").trim();
|
|
if (!trimmed) return "";
|
|
|
|
const path = trimmed;
|
|
|
|
if (isWindows() && !/\.(exe|cmd|bat)$/i.test(path)) {
|
|
return `${path}.exe`;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/** Join path segments using Zotero PathUtils (cross-platform). */
|
|
export function joinPath(...parts: string[]): string {
|
|
const filtered = parts.filter(Boolean);
|
|
if (!filtered.length) return "";
|
|
try {
|
|
return PathUtils.join(...filtered);
|
|
} catch (e) {
|
|
ztoolkit.log("[ChatPapers] PathUtils.join failed", filtered, e);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/** Normalize user-provided storage directory paths (prefs). */
|
|
export function normalizeStoragePath(input: string): string {
|
|
const trimmed = (input || "").trim();
|
|
if (!trimmed) return "";
|
|
|
|
const unified = trimmed.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
|
|
const winMatch = unified.match(/^([A-Za-z]:)(\/.*)?$/);
|
|
if (winMatch) {
|
|
const drive = winMatch[1];
|
|
const rest = (winMatch[2] || "")
|
|
.split("/")
|
|
.filter((part) => part && part !== ".")
|
|
.join("\\");
|
|
return rest ? `${drive}\\${rest}` : drive;
|
|
}
|
|
|
|
const absolute = unified.startsWith("/");
|
|
const parts = unified.split("/").filter((part) => part && part !== ".");
|
|
let result = parts.join("/");
|
|
if (absolute) result = `/${result}`;
|
|
return result.replace(/\/+$/, "") || (absolute ? "/" : "");
|
|
}
|
|
|
|
/** Safe single path segment for file or directory names (cross-platform). */
|
|
export function sanitizePathSegment(input: string, maxLen = 120): string {
|
|
const cleaned = String(input || "")
|
|
.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_")
|
|
.replace(/[.\s]+$/, "")
|
|
.trim();
|
|
if (!cleaned) return "_";
|
|
if (cleaned.length <= maxLen) return cleaned;
|
|
let hash = 0;
|
|
for (let i = 0; i < input.length; i++) {
|
|
hash = (hash * 31 + input.charCodeAt(i)) | 0;
|
|
}
|
|
const tail = Math.abs(hash).toString(36);
|
|
return `${cleaned.slice(0, Math.max(1, maxLen - tail.length - 1))}_${tail}`;
|
|
}
|
|
|
|
/** Convert local file path to file:// URL for <audio src>. */
|
|
export function pathToFileUrl(filePath: string): string {
|
|
const normalized = filePath.replace(/\\/g, "/");
|
|
if (normalized.startsWith("file://")) return normalized;
|
|
if (/^[A-Za-z]:\//.test(normalized)) {
|
|
return `file:///${encodeURI(normalized)}`;
|
|
}
|
|
return `file://${encodeURI(normalized)}`;
|
|
}
|