feat: 添加复制路径菜单项
This commit is contained in:
@@ -20,7 +20,8 @@ import type { FileListItem } from '../types/sftp.types';
|
|||||||
import type { WebSocketMessage } from '../types/websocket.types';
|
import type { WebSocketMessage } from '../types/websocket.types';
|
||||||
import PathHistoryDropdown from './PathHistoryDropdown.vue';
|
import PathHistoryDropdown from './PathHistoryDropdown.vue';
|
||||||
import { usePathHistoryStore } from '../stores/pathHistory.store';
|
import { usePathHistoryStore } from '../stores/pathHistory.store';
|
||||||
import FavoritePathsModal from './FavoritePathsModal.vue'; // +++ Import FavoritePathsModal +++
|
import FavoritePathsModal from './FavoritePathsModal.vue';
|
||||||
|
import { useUiNotificationsStore } from '../stores/uiNotifications.store';
|
||||||
|
|
||||||
|
|
||||||
type SftpManagerInstance = ReturnType<typeof createSftpActionsManager>;
|
type SftpManagerInstance = ReturnType<typeof createSftpActionsManager>;
|
||||||
@@ -102,8 +103,9 @@ const fileEditorStore = useFileEditorStore(); // 实例化 File Editor Store
|
|||||||
const settingsStore = useSettingsStore(); // +++ 实例化 Settings Store +++
|
const settingsStore = useSettingsStore(); // +++ 实例化 Settings Store +++
|
||||||
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
||||||
const pathHistoryStore = usePathHistoryStore(); // +++ 实例化 PathHistoryStore +++
|
const pathHistoryStore = usePathHistoryStore(); // +++ 实例化 PathHistoryStore +++
|
||||||
|
const uiNotificationsStore = useUiNotificationsStore(); // +++ 实例化通知 store +++
|
||||||
|
|
||||||
// 从 Settings Store 获取共享设置
|
// 从 Settings Store 获取共享设置
|
||||||
const {
|
const {
|
||||||
shareFileEditorTabsBoolean,
|
shareFileEditorTabsBoolean,
|
||||||
fileManagerRowSizeMultiplierNumber, // +++ 获取行大小 getter +++
|
fileManagerRowSizeMultiplierNumber, // +++ 获取行大小 getter +++
|
||||||
@@ -118,8 +120,6 @@ const {
|
|||||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||||
const sortKey = ref<keyof FileListItem | 'type' | 'size' | 'mtime'>('filename');
|
const sortKey = ref<keyof FileListItem | 'type' | 'size' | 'mtime'>('filename');
|
||||||
const sortDirection = ref<'asc' | 'desc'>('asc');
|
const sortDirection = ref<'asc' | 'desc'>('asc');
|
||||||
// const initialLoadDone = ref(false); // 状态移至 SFTP Manager
|
|
||||||
// const isFetchingInitialPath = ref(false); // 通过 isLoading 和 !initialLoadDone 推断
|
|
||||||
const isEditingPath = ref(false);
|
const isEditingPath = ref(false);
|
||||||
const searchQuery = ref(''); // 搜索查询 ref
|
const searchQuery = ref(''); // 搜索查询 ref
|
||||||
const isMultiSelectMode = ref(false); // 多选模式状态 (主要用于移动端)
|
const isMultiSelectMode = ref(false); // 多选模式状态 (主要用于移动端)
|
||||||
@@ -129,7 +129,6 @@ const pathInputRef = ref<HTMLInputElement | null>(null);
|
|||||||
const editablePath = ref('');
|
const editablePath = ref('');
|
||||||
const fileListContainerRef = ref<HTMLDivElement | null>(null); // 文件列表容器引用
|
const fileListContainerRef = ref<HTMLDivElement | null>(null); // 文件列表容器引用
|
||||||
const dropOverlayRef = ref<HTMLDivElement | null>(null); // +++ 拖拽蒙版引用 +++
|
const dropOverlayRef = ref<HTMLDivElement | null>(null); // +++ 拖拽蒙版引用 +++
|
||||||
// const scrollIntervalId = ref<number | null>(null); // 已移至 useFileManagerDragAndDrop
|
|
||||||
|
|
||||||
// +++ Favorite Paths Modal State +++
|
// +++ Favorite Paths Modal State +++
|
||||||
const showFavoritePathsModal = ref(false);
|
const showFavoritePathsModal = ref(false);
|
||||||
@@ -172,10 +171,8 @@ const startX = ref(0);
|
|||||||
const startWidth = ref(0);
|
const startWidth = ref(0);
|
||||||
|
|
||||||
// --- 辅助函数 ---
|
// --- 辅助函数 ---
|
||||||
// 重新添加 generateRequestId,因为 watchEffect 中需要它
|
|
||||||
const generateRequestId = (): string => `req-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
const generateRequestId = (): string => `req-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
||||||
// joinPath 由 props.sftpManager 提供
|
|
||||||
// sortFiles 在此组件内部用于排序显示
|
|
||||||
|
|
||||||
// UI 格式化函数保持不变
|
// UI 格式化函数保持不变
|
||||||
const formatSize = (size: number): string => {
|
const formatSize = (size: number): string => {
|
||||||
@@ -896,6 +893,22 @@ const handleDecompress = (item: FileListItem) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// +++ 复制路径到剪贴板 +++
|
||||||
|
const handleCopyPath = async (item: FileListItem) => {
|
||||||
|
if (!currentSftpManager.value) return;
|
||||||
|
const fullPath = currentSftpManager.value.joinPath(currentSftpManager.value.currentPath.value, item.filename);
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(fullPath);
|
||||||
|
// 可选:显示成功通知
|
||||||
|
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Copied path to clipboard: ${fullPath}`);
|
||||||
|
uiNotificationsStore.showSuccess(t('fileManager.notifications.pathCopied', 'Path copied to clipboard'));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[FileManager ${props.sessionId}-${props.instanceId}] Failed to copy path: `, err);
|
||||||
|
// 可选:显示错误通知
|
||||||
|
uiNotificationsStore.showError(t('fileManager.errors.copyPathFailed', 'Failed to copy path'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// --- 上下文菜单逻辑 (使用 Composable, 需要 Selection 和 Action Handlers) ---
|
// --- 上下文菜单逻辑 (使用 Composable, 需要 Selection 和 Action Handlers) ---
|
||||||
const {
|
const {
|
||||||
contextMenuVisible,
|
contextMenuVisible,
|
||||||
@@ -936,6 +949,7 @@ const {
|
|||||||
// +++ 传递压缩/解压回调 +++
|
// +++ 传递压缩/解压回调 +++
|
||||||
onCompressRequest: handleCompress,
|
onCompressRequest: handleCompress,
|
||||||
onDecompressRequest: handleDecompress,
|
onDecompressRequest: handleDecompress,
|
||||||
|
onCopyPath: handleCopyPath, // +++ 传递复制路径回调 +++
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- 目录加载与导航 ---
|
// --- 目录加载与导航 ---
|
||||||
@@ -1198,17 +1212,10 @@ watch(() => props.sessionId, (newSessionId, oldSessionId) => {
|
|||||||
isEditingPath.value = false;
|
isEditingPath.value = false;
|
||||||
sortKey.value = 'filename'; // 重置排序
|
sortKey.value = 'filename'; // 重置排序
|
||||||
sortDirection.value = 'asc';
|
sortDirection.value = 'asc';
|
||||||
// initialLoadDone.value = false; // 移除本地状态重置
|
|
||||||
// isFetchingInitialPath.value = false; // 移除本地状态重置
|
|
||||||
|
|
||||||
// 3. 触发新会话的初始路径加载 (watchEffect 会处理)
|
|
||||||
// watchEffect 会在 currentSftpManager.value 改变后重新运行
|
|
||||||
// 并检查新 manager 的状态来决定是否加载初始路径
|
|
||||||
}
|
}
|
||||||
}, { immediate: false }); // immediate: false 避免初始挂载时触发
|
}, { immediate: false }); // immediate: false 避免初始挂载时触发
|
||||||
|
|
||||||
|
|
||||||
// onBeforeUnmount 中 cleanupSftpHandlers 的调用已移至新的 onBeforeUnmount 逻辑中
|
|
||||||
|
|
||||||
// +++ 注册/注销自定义聚焦动作 +++
|
// +++ 注册/注销自定义聚焦动作 +++
|
||||||
let unregisterSearchFocusAction: (() => void) | null = null; // 搜索框注销函数
|
let unregisterSearchFocusAction: (() => void) | null = null; // 搜索框注销函数
|
||||||
@@ -1234,9 +1241,6 @@ onMounted(() => {
|
|||||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Executing path edit focus action for active session.`);
|
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Executing path edit focus action for active session.`);
|
||||||
// startPathEdit 本身不是 async,但注册时需要包装成 async 以匹配类型
|
// startPathEdit 本身不是 async,但注册时需要包装成 async 以匹配类型
|
||||||
startPathEdit(); // 调用暴露的方法
|
startPathEdit(); // 调用暴露的方法
|
||||||
// 假设 startPathEdit 总是尝试聚焦,这里返回 true 表示已尝试
|
|
||||||
// 注意:startPathEdit 内部没有返回成功与否,这里乐观返回 true
|
|
||||||
// 如果需要更精确,startPathEdit 需要返回 boolean
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Path edit focus action skipped for inactive session.`);
|
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Path edit focus action skipped for inactive session.`);
|
||||||
@@ -1262,9 +1266,6 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
unregisterPathFocusAction = null;
|
unregisterPathFocusAction = null;
|
||||||
document.removeEventListener('click', handleClickOutsidePathInput);
|
document.removeEventListener('click', handleClickOutsidePathInput);
|
||||||
// // 调用注入的 SFTP 管理器提供的清理函数 (移除,由 store 处理)
|
|
||||||
// cleanupSftpHandlers();
|
|
||||||
// 调用 store 的清理方法
|
|
||||||
sessionStore.removeSftpManager(props.sessionId, props.instanceId);
|
sessionStore.removeSftpManager(props.sessionId, props.instanceId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1275,7 +1276,6 @@ watch(showExternalDropOverlay, (isVisible) => {
|
|||||||
if (dropOverlayRef.value && fileListContainerRef.value) {
|
if (dropOverlayRef.value && fileListContainerRef.value) {
|
||||||
const scrollHeight = fileListContainerRef.value.scrollHeight;
|
const scrollHeight = fileListContainerRef.value.scrollHeight;
|
||||||
dropOverlayRef.value.style.height = `${scrollHeight}px`;
|
dropOverlayRef.value.style.height = `${scrollHeight}px`;
|
||||||
// console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Overlay shown. Setting height to scrollHeight: ${scrollHeight}px`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -1458,8 +1458,6 @@ const handlePathInput = async (event?: Event | FocusEvent) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's a blur event, and the dropdown is not the target, close dropdown.
|
|
||||||
// The timeout ensures that a click on the dropdown item can be processed first.
|
|
||||||
if (event && event.type === 'blur') {
|
if (event && event.type === 'blur') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const activeEl = document.activeElement;
|
const activeEl = document.activeElement;
|
||||||
@@ -1468,16 +1466,15 @@ const handlePathInput = async (event?: Event | FocusEvent) => {
|
|||||||
// Focus is within the dropdown, do nothing yet
|
// Focus is within the dropdown, do nothing yet
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pathInputRef.value !== activeEl) { // Focus moved away from input and not into dropdown
|
if (pathInputRef.value !== activeEl) {
|
||||||
isEditingPath.value = false; // Only set to false if focus truly left
|
isEditingPath.value = false;
|
||||||
closePathHistory();
|
closePathHistory();
|
||||||
}
|
}
|
||||||
}, 150); // Slightly longer delay to allow dropdown item click
|
}, 150);
|
||||||
return; // Don't navigate on blur, only close dropdown
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's an Enter key press not handled by keydown (e.g. from a button click if any)
|
|
||||||
// or if the function is called directly without an event.
|
|
||||||
if (!currentSftpManager.value) return;
|
if (!currentSftpManager.value) return;
|
||||||
|
|
||||||
const newPath = editablePath.value.trim();
|
const newPath = editablePath.value.trim();
|
||||||
@@ -1505,19 +1502,12 @@ const cancelPathEdit = () => {
|
|||||||
const handleClickOutsidePathInput = (event: MouseEvent) => {
|
const handleClickOutsidePathInput = (event: MouseEvent) => {
|
||||||
if (pathInputWrapperRef.value && !pathInputWrapperRef.value.contains(event.target as Node)) {
|
if (pathInputWrapperRef.value && !pathInputWrapperRef.value.contains(event.target as Node)) {
|
||||||
if (isEditingPath.value || showPathHistoryDropdown.value) {
|
if (isEditingPath.value || showPathHistoryDropdown.value) {
|
||||||
// editablePath.value might be different from current manager path
|
|
||||||
// if user typed something and then clicked outside.
|
|
||||||
// Decide if we should commit or revert. For now, just close.
|
|
||||||
isEditingPath.value = false;
|
isEditingPath.value = false;
|
||||||
closePathHistory();
|
closePathHistory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 清除错误消息的函数 - 不再需要,错误由 UI 通知处理
|
|
||||||
// const clearError = () => {
|
|
||||||
// clearSftpError();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// --- 搜索框激活/取消逻辑 ---
|
// --- 搜索框激活/取消逻辑 ---
|
||||||
const activateSearch = () => {
|
const activateSearch = () => {
|
||||||
@@ -1528,12 +1518,8 @@ const activateSearch = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deactivateSearch = () => {
|
const deactivateSearch = () => {
|
||||||
// 延迟失活以允许点击内部元素(如果需要)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// if (!searchInputRef.value?.contains(document.activeElement)) { // 检查焦点是否还在输入框内
|
|
||||||
isSearchActive.value = false;
|
isSearchActive.value = false;
|
||||||
// }
|
|
||||||
// }, 100); // 100ms 延迟
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelSearch = () => {
|
const cancelSearch = () => {
|
||||||
@@ -1577,14 +1563,8 @@ const sendCdCommandToTerminal = () => {
|
|||||||
}
|
}
|
||||||
// 使用 terminalManager 的 sendData 方法发送命令
|
// 使用 terminalManager 的 sendData 方法发送命令
|
||||||
activeSession.terminalManager.sendData(command);
|
activeSession.terminalManager.sendData(command);
|
||||||
// 可选:添加 UI 通知
|
|
||||||
// import { useUiNotificationsStore } from '../stores/uiNotifications.store'; // 需要导入
|
|
||||||
// const uiNotificationsStore = useUiNotificationsStore(); // 需要实例化
|
|
||||||
// uiNotificationsStore.addNotification({ message: t('fileManager.notifications.cdCommandSent', 'CD command sent to terminal.'), type: 'success', duration: 3000 });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[FileManager ${props.sessionId}-${props.instanceId}] Failed to send command to terminal:`, error);
|
console.error(`[FileManager ${props.sessionId}-${props.instanceId}] Failed to send command to terminal:`, error);
|
||||||
// 可选:添加 UI 通知
|
|
||||||
// uiNotificationsStore.addNotification({ message: t('fileManager.errors.sendCommandFailed', 'Failed to send command.'), type: 'error' });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1608,8 +1588,6 @@ const handleWheel = (event: WheelEvent) => {
|
|||||||
const newMultiplier = Math.max(0.5, Math.min(2, rowSizeMultiplier.value + delta));
|
const newMultiplier = Math.max(0.5, Math.min(2, rowSizeMultiplier.value + delta));
|
||||||
const oldMultiplier = rowSizeMultiplier.value;
|
const oldMultiplier = rowSizeMultiplier.value;
|
||||||
rowSizeMultiplier.value = parseFloat(newMultiplier.toFixed(2)); // 保留两位小数避免浮点数问题
|
rowSizeMultiplier.value = parseFloat(newMultiplier.toFixed(2)); // 保留两位小数避免浮点数问题
|
||||||
// console.log(`Row size multiplier: ${rowSizeMultiplier.value}`); // 调试日志
|
|
||||||
// +++ 在行大小变化后保存设置 +++
|
|
||||||
if (rowSizeMultiplier.value !== oldMultiplier) {
|
if (rowSizeMultiplier.value !== oldMultiplier) {
|
||||||
// +++ 日志:记录触发保存 +++
|
// +++ 日志:记录触发保存 +++
|
||||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] handleWheel triggered saveLayoutSettings.`);
|
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] handleWheel triggered saveLayoutSettings.`);
|
||||||
@@ -1656,8 +1634,6 @@ const handleOpenEditorClick = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Triggering popup editor directly.`);
|
console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Triggering popup editor directly.`);
|
||||||
// 暂时使用 triggerPopup,传递空字符串表示空编辑器
|
|
||||||
// 后续可能需要 fileEditorStore.triggerEmptyPopup(props.sessionId);
|
|
||||||
fileEditorStore.triggerPopup('', props.sessionId); // 修复:传递空字符串而不是 null
|
fileEditorStore.triggerPopup('', props.sessionId); // 修复:传递空字符串而不是 null
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1670,8 +1646,6 @@ const handleOpenEditorClick = () => {
|
|||||||
const handleNavigateToPathFromFavorites = (path: string) => {
|
const handleNavigateToPathFromFavorites = (path: string) => {
|
||||||
if (currentSftpManager.value) {
|
if (currentSftpManager.value) {
|
||||||
currentSftpManager.value.loadDirectory(path);
|
currentSftpManager.value.loadDirectory(path);
|
||||||
// Optionally, add to local path history if not already handled by the store/modal
|
|
||||||
// pathHistoryStore.addPath(path);
|
|
||||||
}
|
}
|
||||||
showFavoritePathsModal.value = false; // Close modal after navigation
|
showFavoritePathsModal.value = false; // Close modal after navigation
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export interface UseFileManagerContextMenuOptions {
|
|||||||
// --- 压缩/解压回调 ---
|
// --- 压缩/解压回调 ---
|
||||||
onCompressRequest: (items: FileListItem[], format: CompressFormat) => void; // +++ 压缩回调 +++
|
onCompressRequest: (items: FileListItem[], format: CompressFormat) => void; // +++ 压缩回调 +++
|
||||||
onDecompressRequest: (item: FileListItem) => void; // +++ 解压回调 +++
|
onDecompressRequest: (item: FileListItem) => void; // +++ 解压回调 +++
|
||||||
|
onCopyPath?: (item: FileListItem) => void; // +++ 复制路径回调 +++
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:检查文件是否为支持的压缩格式
|
// 辅助函数:检查文件是否为支持的压缩格式
|
||||||
@@ -82,6 +83,7 @@ export function useFileManagerContextMenu(options: UseFileManagerContextMenuOpti
|
|||||||
onDownloadDirectory, // +++ 解构文件夹下载回调 +++
|
onDownloadDirectory, // +++ 解构文件夹下载回调 +++
|
||||||
onCompressRequest, // +++ 解构压缩回调 +++
|
onCompressRequest, // +++ 解构压缩回调 +++
|
||||||
onDecompressRequest, // +++ 解构解压回调 +++
|
onDecompressRequest, // +++ 解构解压回调 +++
|
||||||
|
onCopyPath, // +++ 解构复制路径回调 +++
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const contextMenuVisible = ref(false);
|
const contextMenuVisible = ref(false);
|
||||||
@@ -175,6 +177,10 @@ export function useFileManagerContextMenu(options: UseFileManagerContextMenuOpti
|
|||||||
if (targetItem.attrs.isDirectory) {
|
if (targetItem.attrs.isDirectory) {
|
||||||
menu.push({ label: t('fileManager.actions.paste'), action: onPaste, disabled: !(isConnected.value && isSftpReady.value) || !hasClipboardContent });
|
menu.push({ label: t('fileManager.actions.paste'), action: onPaste, disabled: !(isConnected.value && isSftpReady.value) || !hasClipboardContent });
|
||||||
}
|
}
|
||||||
|
// +++ 添加复制路径菜单项 +++
|
||||||
|
if (onCopyPath) {
|
||||||
|
menu.push({ label: t('fileManager.actions.copyPath', 'Copy Path'), action: () => onCopyPath(targetItem), disabled: !(isConnected.value && isSftpReady.value) });
|
||||||
|
}
|
||||||
|
|
||||||
// --- 分隔符 (视觉) ---
|
// --- 分隔符 (视觉) ---
|
||||||
// The invalid object literal was here and is now removed.
|
// The invalid object literal was here and is now removed.
|
||||||
|
|||||||
@@ -441,7 +441,8 @@
|
|||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"cut": "Cut",
|
"cut": "Cut",
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"openEditor": "Open Editor"
|
"openEditor": "Open Editor",
|
||||||
|
"copyPath": "Copy Path"
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
"compress": "Compress",
|
"compress": "Compress",
|
||||||
@@ -495,16 +496,18 @@
|
|||||||
"decompressErrorDetailed": "Decompression failed: {error}",
|
"decompressErrorDetailed": "Decompression failed: {error}",
|
||||||
"commandNotFoundCompress": "Command '{command}' not found on server, cannot complete compression.",
|
"commandNotFoundCompress": "Command '{command}' not found on server, cannot complete compression.",
|
||||||
"commandNotFoundDecompress": "Command '{command}' not found on server, cannot complete decompression.",
|
"commandNotFoundDecompress": "Command '{command}' not found on server, cannot complete decompression.",
|
||||||
"genericCommandNotFound": "Command '{command}' not found on server, cannot complete '{operation}' operation."
|
"genericCommandNotFound": "Command '{command}' not found on server, cannot complete '{operation}' operation.",
|
||||||
|
"copyPathFailed": "Failed to copy path"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"copySuccess": "Copy successful",
|
"copySuccess": "Copy successful",
|
||||||
"moveSuccess": "Move successful",
|
"moveSuccess": "Move successful",
|
||||||
"cdCommandSent": "CD command sent to terminal",
|
"cdCommandSent": "CD command sent to terminal",
|
||||||
"compressSuccess": "Compressed {name} successfully",
|
"compressSuccess": "Compressed {name} successfully",
|
||||||
"decompressSuccess": "Decompressed {name} successfully"
|
"decompressSuccess": "Decompressed {name} successfully",
|
||||||
|
"pathCopied": "Path copied to clipboard"
|
||||||
},
|
},
|
||||||
"warnings": {
|
"warnings": {
|
||||||
"moveSameDirectory": "Cannot cut and paste in the same directory."
|
"moveSameDirectory": "Cannot cut and paste in the same directory."
|
||||||
},
|
},
|
||||||
"prompts": {
|
"prompts": {
|
||||||
|
|||||||
@@ -379,7 +379,8 @@
|
|||||||
"rename": "名前を変更",
|
"rename": "名前を変更",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"upload": "アップロード",
|
"upload": "アップロード",
|
||||||
"uploadFile": "ファイルをアップロード"
|
"uploadFile": "ファイルをアップロード",
|
||||||
|
"copyPath": "パスをコピー"
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
"compress": "圧縮",
|
"compress": "圧縮",
|
||||||
@@ -426,7 +427,8 @@
|
|||||||
"decompressErrorDetailed": "解凍に失敗しました: {error}",
|
"decompressErrorDetailed": "解凍に失敗しました: {error}",
|
||||||
"commandNotFoundCompress": "サーバーにコマンド '{command}' が見つからないため、圧縮操作を完了できません。",
|
"commandNotFoundCompress": "サーバーにコマンド '{command}' が見つからないため、圧縮操作を完了できません。",
|
||||||
"commandNotFoundDecompress": "サーバーにコマンド '{command}' が見つからないため、解凍操作を完了できません。",
|
"commandNotFoundDecompress": "サーバーにコマンド '{command}' が見つからないため、解凍操作を完了できません。",
|
||||||
"genericCommandNotFound": "サーバーにコマンド '{command}' が見つからないため、'{operation}' 操作を完了できません。"
|
"genericCommandNotFound": "サーバーにコマンド '{command}' が見つからないため、'{operation}' 操作を完了できません。",
|
||||||
|
"copyPathFailed": "パスのコピーに失敗しました"
|
||||||
},
|
},
|
||||||
"headers": {
|
"headers": {
|
||||||
"modified": "変更日",
|
"modified": "変更日",
|
||||||
@@ -443,7 +445,8 @@
|
|||||||
"copySuccess": "コピーに成功しました",
|
"copySuccess": "コピーに成功しました",
|
||||||
"moveSuccess": "移動に成功しました",
|
"moveSuccess": "移動に成功しました",
|
||||||
"compressSuccess": "{name} を正常に圧縮しました",
|
"compressSuccess": "{name} を正常に圧縮しました",
|
||||||
"decompressSuccess": "{name} を正常に解凍しました"
|
"decompressSuccess": "{name} を正常に解凍しました",
|
||||||
|
"pathCopied": "パスがクリップボードにコピーされました"
|
||||||
},
|
},
|
||||||
"prompts": {
|
"prompts": {
|
||||||
"confirmDeleteFile": "ファイル \"{name}\" を削除しますか?この操作は元に戻せません。",
|
"confirmDeleteFile": "ファイル \"{name}\" を削除しますか?この操作は元に戻せません。",
|
||||||
|
|||||||
@@ -441,7 +441,8 @@
|
|||||||
"copy": "复制",
|
"copy": "复制",
|
||||||
"cut": "剪切",
|
"cut": "剪切",
|
||||||
"paste": "粘贴",
|
"paste": "粘贴",
|
||||||
"openEditor": "打开编辑器"
|
"openEditor": "打开编辑器",
|
||||||
|
"copyPath": "复制路径"
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
"compress": "压缩",
|
"compress": "压缩",
|
||||||
@@ -495,14 +496,16 @@
|
|||||||
"decompressErrorDetailed": "解压失败: {error}",
|
"decompressErrorDetailed": "解压失败: {error}",
|
||||||
"commandNotFoundCompress": "服务器上缺少 '{command}' 命令,无法完成压缩操作。",
|
"commandNotFoundCompress": "服务器上缺少 '{command}' 命令,无法完成压缩操作。",
|
||||||
"commandNotFoundDecompress": "服务器上缺少 '{command}' 命令,无法完成解压操作。",
|
"commandNotFoundDecompress": "服务器上缺少 '{command}' 命令,无法完成解压操作。",
|
||||||
"genericCommandNotFound": "服务器上缺少 '{command}' 命令,无法完成 '{operation}' 操作。"
|
"genericCommandNotFound": "服务器上缺少 '{command}' 命令,无法完成 '{operation}' 操作。",
|
||||||
|
"copyPathFailed": "复制路径失败"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"copySuccess": "复制成功",
|
"copySuccess": "复制成功",
|
||||||
"moveSuccess": "移动成功",
|
"moveSuccess": "移动成功",
|
||||||
"cdCommandSent": "CD 命令已发送到终端",
|
"cdCommandSent": "CD 命令已发送到终端",
|
||||||
"compressSuccess": "压缩 {name} 成功",
|
"compressSuccess": "压缩 {name} 成功",
|
||||||
"decompressSuccess": "解压 {name} 成功"
|
"decompressSuccess": "解压 {name} 成功",
|
||||||
|
"pathCopied": "路径已复制到剪贴板"
|
||||||
},
|
},
|
||||||
"warnings": {
|
"warnings": {
|
||||||
"moveSameDirectory": "不能在同一目录下剪切和粘贴。"
|
"moveSameDirectory": "不能在同一目录下剪切和粘贴。"
|
||||||
|
|||||||
Reference in New Issue
Block a user