Skip to content

Commit eb4d3c9

Browse files
committed
Breadcrumb: Add "Copy path" context menu
- New `file.copyCurrentDirectoryPath` command (no default shortcut, configurable in Settings) - Right-click breadcrumb header shows native "Copy path" context menu - Copies full absolute path (no `~` shorthand) of the current directory - Shortcut label auto-displays in context menu if user configures one
1 parent 3454656 commit eb4d3c9

8 files changed

Lines changed: 69 additions & 4 deletions

File tree

apps/desktop/src-tauri/src/commands/ui.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::ignore_poison::IgnorePoison;
22
use crate::menu::{
3-
CLOSE_TAB_ID, CommandScope, MenuState, build_context_menu, build_network_host_context_menu, build_tab_context_menu,
4-
menu_id_to_command,
3+
CLOSE_TAB_ID, CommandScope, MenuState, build_breadcrumb_context_menu, build_context_menu,
4+
build_network_host_context_menu, build_tab_context_menu, frontend_shortcut_to_accelerator, menu_id_to_command,
55
};
66
#[cfg(any(target_os = "macos", target_os = "linux"))]
77
use std::process::Command;
@@ -35,6 +35,21 @@ pub fn show_file_context_menu<R: Runtime>(
3535
Ok(())
3636
}
3737

38+
/// Shows a native context menu for the breadcrumb path bar.
39+
/// The `shortcut` is the user's configured shortcut in frontend format (e.g. "⌃⌘C"),
40+
/// or empty string if no shortcut is configured.
41+
#[tauri::command]
42+
pub fn show_breadcrumb_context_menu<R: Runtime>(
43+
window: Window<R>,
44+
shortcut: String,
45+
) -> Result<(), String> {
46+
let app = window.app_handle();
47+
let accelerator = frontend_shortcut_to_accelerator(&shortcut).unwrap_or_default();
48+
let menu = build_breadcrumb_context_menu(app, &accelerator).map_err(|e| e.to_string())?;
49+
menu.popup(window).map_err(|e| e.to_string())?;
50+
Ok(())
51+
}
52+
3853
#[tauri::command]
3954
pub fn show_main_window<R: Runtime>(window: Window<R>) -> Result<(), String> {
4055
window.show().map_err(|e| e.to_string())

apps/desktop/src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ pub fn run() {
704704
commands::icons::clear_extension_icon_cache,
705705
commands::icons::clear_directory_icon_cache,
706706
commands::ui::show_file_context_menu,
707+
commands::ui::show_breadcrumb_context_menu,
707708
commands::ui::show_tab_context_menu,
708709
commands::ui::show_network_host_context_menu,
709710
commands::ui::update_pin_tab_menu,

apps/desktop/src-tauri/src/menu/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub const FILE_DELETE_ID: &str = "file_delete";
2727
pub const FILE_DELETE_PERMANENTLY_ID: &str = "file_delete_permanently";
2828
pub const SHOW_IN_FINDER_ID: &str = "show_in_finder";
2929
pub const COPY_PATH_ID: &str = "copy_path";
30+
pub const COPY_CURRENT_DIR_PATH_ID: &str = "copy_current_dir_path";
3031
pub const COPY_FILENAME_ID: &str = "copy_filename";
3132
pub const GET_INFO_ID: &str = "get_info";
3233
pub const QUICK_LOOK_ID: &str = "quick_look";
@@ -124,6 +125,7 @@ pub fn menu_id_to_command(menu_id: &str) -> Option<(&'static str, CommandScope)>
124125
FILE_DELETE_PERMANENTLY_ID => Some(("file.deletePermanently", CommandScope::FileScoped)),
125126
SHOW_IN_FINDER_ID => Some(("file.showInFinder", CommandScope::FileScoped)),
126127
COPY_PATH_ID => Some(("file.copyPath", CommandScope::FileScoped)),
128+
COPY_CURRENT_DIR_PATH_ID => Some(("file.copyCurrentDirectoryPath", CommandScope::FileScoped)),
127129
COPY_FILENAME_ID => Some(("file.copyFilename", CommandScope::FileScoped)),
128130
GET_INFO_ID => Some(("file.getInfo", CommandScope::FileScoped)),
129131
QUICK_LOOK_ID => Some(("file.quickLook", CommandScope::FileScoped)),
@@ -488,6 +490,20 @@ pub fn build_context_menu<R: Runtime>(
488490
Ok(menu)
489491
}
490492

493+
/// Builds a context menu for the breadcrumb path bar.
494+
/// The `accelerator` parameter is the user's configured shortcut for this command
495+
/// (in Tauri accelerator format, e.g. "Ctrl+Shift+C"), or empty if none is set.
496+
pub fn build_breadcrumb_context_menu<R: Runtime>(
497+
app: &AppHandle<R>,
498+
accelerator: &str,
499+
) -> tauri::Result<Menu<R>> {
500+
let menu = Menu::new(app)?;
501+
let accel: Option<&str> = if accelerator.is_empty() { None } else { Some(accelerator) };
502+
let copy_path_item = MenuItem::with_id(app, COPY_CURRENT_DIR_PATH_ID, "Copy path", true, accel)?;
503+
menu.append(&copy_path_item)?;
504+
Ok(menu)
505+
}
506+
491507
/// Builds a menu for viewer windows (built from scratch on all platforms).
492508
pub fn build_viewer_menu<R: Runtime>(app: &AppHandle<R>) -> tauri::Result<Menu<R>> {
493509
let menu = Menu::new(app)?;

apps/desktop/src/lib/commands/command-registry.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,13 @@ export const commands: Command[] = [
307307
showInPalette: true,
308308
shortcuts: ['⌃⌘C'],
309309
},
310+
{
311+
id: 'file.copyCurrentDirectoryPath',
312+
name: 'Copy current directory path',
313+
scope: 'Main window/File list',
314+
showInPalette: true,
315+
shortcuts: [],
316+
},
310317
{
311318
id: 'file.copyFilename',
312319
name: 'Copy filename',

apps/desktop/src/lib/file-explorer/pane/FilePane.svelte

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@
8282
import { handleNavigationShortcut } from '../navigation/keyboard-shortcuts'
8383
import { resolveValidPath } from '../navigation/path-navigation'
8484
import { homeDir } from '@tauri-apps/api/path'
85-
import { getVolumeSpace, type VolumeSpaceInfo } from '$lib/tauri-commands'
85+
import { getVolumeSpace, showBreadcrumbContextMenu, type VolumeSpaceInfo } from '$lib/tauri-commands'
86+
import { getEffectiveShortcuts } from '$lib/shortcuts/shortcuts-store'
8687
import type { UnreachableState } from '../tabs/tab-types'
8788
import { getDiskUsageLevel, getUsedPercent, formatBarTooltip } from '../disk-space-utils'
8889
import { formatFileSize } from '$lib/settings/reactive-settings.svelte'
@@ -1085,6 +1086,13 @@
10851086
onRequestFocus?.()
10861087
}
10871088
1089+
function handleBreadcrumbContextMenu(e: MouseEvent) {
1090+
e.preventDefault()
1091+
onRequestFocus?.()
1092+
const shortcuts = getEffectiveShortcuts('file.copyCurrentDirectoryPath')
1093+
void showBreadcrumbContextMenu(shortcuts[0] ?? '')
1094+
}
1095+
10881096
function handleVolumeChangeFromBreadcrumb(newVolumeId: string, newVolumePath: string, targetPath: string) {
10891097
// Navigate to the target path (may differ from volume root for favorites)
10901098
// Note: We intentionally don't call onPathChange here - the volume change handler
@@ -1695,7 +1703,8 @@
16951703
role="region"
16961704
aria-label="{paneId === 'left' ? 'Left' : 'Right'} file pane"
16971705
>
1698-
<div class="header">
1706+
<!-- svelte-ignore a11y_no_static_element_interactions -->
1707+
<div class="header" oncontextmenu={handleBreadcrumbContextMenu}>
16991708
<VolumeBreadcrumb
17001709
bind:this={volumeBreadcrumbRef}
17011710
{volumeId}

apps/desktop/src/lib/tauri-commands/file-actions.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ export async function showFileContextMenu(path: string, filename: string, isDire
2929
await invoke('show_file_context_menu', { path, filename, isDirectory })
3030
}
3131

32+
/**
33+
* Shows a native context menu for the breadcrumb path bar.
34+
* @param shortcut - Frontend shortcut string (e.g. "⌃⌘C"), or empty string if no shortcut is configured.
35+
*/
36+
export async function showBreadcrumbContextMenu(shortcut: string): Promise<void> {
37+
await invoke('show_breadcrumb_context_menu', { shortcut })
38+
}
39+
3240
/**
3341
* Show a file in the system file manager (reveal in parent folder).
3442
* On macOS, reveals in Finder. On Linux, uses the default file manager.

apps/desktop/src/lib/tauri-commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export {
5454
openFile,
5555
openExternalUrl,
5656
showFileContextMenu,
57+
showBreadcrumbContextMenu,
5758
showInFinder,
5859
copyToClipboard,
5960
quickLook,

apps/desktop/src/routes/(main)/+page.svelte

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,14 @@
978978
return
979979
}
980980
981+
case 'file.copyCurrentDirectoryPath': {
982+
const currentPath = explorerRef?.getFocusedPanePath()
983+
if (currentPath) {
984+
await copyToClipboard(currentPath)
985+
}
986+
return
987+
}
988+
981989
case 'file.copyFilename': {
982990
const entryUnderCursor = explorerRef?.getFileAndPathUnderCursor()
983991
if (entryUnderCursor) {

0 commit comments

Comments
 (0)