@@ -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 */
399404function 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