Skip to content

Commit 67aac86

Browse files
committed
feat: add Copy Relative Path context menu option
Adds option to copy file/folder relative paths based on project root. Supports all URI types (file, content, sftp, ftp) with proper fallback.
1 parent 255ddeb commit 67aac86

File tree

1 file changed

+66
-3
lines changed

1 file changed

+66
-3
lines changed

src/lib/openFolder.js

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ async function handleContextmenu(type, url, name, $target) {
311311
const cancel = `${strings.cancel}${clipBoard ? ` (${strings[clipBoard.action]})` : ""}`;
312312
const COPY = ["copy", strings.copy, "copy"];
313313
const CUT = ["cut", strings.cut, "cut"];
314+
const COPY_RELATIVE_PATH = [
315+
"copy-relative-path",
316+
strings["copy relative path"],
317+
"attach_file",
318+
];
314319
const REMOVE = ["delete", strings.delete, "delete"];
315320
const RENAME = ["rename", strings.rename, "edit"];
316321
const PASTE = ["paste", strings.paste, "paste", !!clipBoard];
@@ -329,7 +334,7 @@ async function handleContextmenu(type, url, name, $target) {
329334
let options;
330335

331336
if (helpers.isFile(type)) {
332-
options = [COPY, CUT, RENAME, REMOVE];
337+
options = [COPY, CUT, COPY_RELATIVE_PATH, RENAME, REMOVE];
333338
if (
334339
url.toLowerCase().endsWith(".zip") &&
335340
(await fsOperation(
@@ -339,7 +344,7 @@ async function handleContextmenu(type, url, name, $target) {
339344
options.push(INSTALL_PLUGIN);
340345
}
341346
} else if (helpers.isDir(type)) {
342-
options = [COPY, CUT, REMOVE, RENAME];
347+
options = [COPY, CUT, COPY_RELATIVE_PATH, REMOVE, RENAME];
343348

344349
if (clipBoard.url != null) {
345350
options.push(PASTE);
@@ -397,7 +402,7 @@ async function handleContextmenu(type, url, name, $target) {
397402
* @param {string} name Name of file or folder
398403
*/
399404
function execOperation(type, action, url, $target, name) {
400-
const { clipBoard, $node, remove } = openFolder.find(url);
405+
const { clipBoard, $node, remove, url: rootUrl } = openFolder.find(url);
401406
const startLoading = () => $node.$title.classList.add("loading");
402407
const stopLoading = () => $node.$title.classList.remove("loading");
403408

@@ -436,6 +441,9 @@ function execOperation(type, action, url, $target, name) {
436441

437442
case "open-in-terminal":
438443
return openInTerminal();
444+
445+
case "copy-relative-path":
446+
return copyRelativePath();
439447
}
440448

441449
async function installPlugin() {
@@ -454,6 +462,61 @@ function execOperation(type, action, url, $target, name) {
454462
}
455463
}
456464

465+
async function copyRelativePath() {
466+
try {
467+
// Validate inputs
468+
if (!url) {
469+
console.error("File path not available");
470+
return;
471+
}
472+
473+
if (!rootUrl) {
474+
console.error("Root folder not found");
475+
return;
476+
}
477+
478+
let relativePath;
479+
480+
// Try using Url.pathname for protocol-based URLs
481+
const rootPath = Url.pathname(rootUrl);
482+
const targetPath = Url.pathname(url);
483+
484+
if (rootPath && targetPath) {
485+
// Both pathnames extracted successfully
486+
relativePath = Path.convertToRelative(rootPath, targetPath);
487+
} else {
488+
// Fallback: Use simple string comparison for URIs where pathname extraction fails
489+
const cleanRoot = rootUrl.endsWith("/")
490+
? rootUrl.slice(0, -1)
491+
: rootUrl;
492+
const cleanTarget = url.endsWith("/") ? url.slice(0, -1) : url;
493+
494+
// Check if target URL starts with root URL
495+
if (cleanTarget.startsWith(cleanRoot)) {
496+
relativePath = cleanTarget.slice(cleanRoot.length + 1);
497+
} else {
498+
// If not a child path, just use basename
499+
relativePath = Url.basename(url);
500+
}
501+
}
502+
503+
if (!relativePath) {
504+
console.error("Unable to calculate relative path");
505+
return;
506+
}
507+
508+
if (cordova.plugins.clipboard) {
509+
cordova.plugins.clipboard.copy(relativePath);
510+
toast(strings.success || "Relative path copied to clipboard");
511+
} else {
512+
console.error("Clipboard not available");
513+
toast("Clipboard not available");
514+
}
515+
} catch (error) {
516+
console.error("Failed to copy relative path:", error);
517+
}
518+
}
519+
457520
async function openInTerminal() {
458521
try {
459522
const prootPath = convertToProotPath(url);

0 commit comments

Comments
 (0)