From d693dc5a148044922dded467d74daec344a46ea4 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Mon, 22 Sep 2025 02:07:08 +0800 Subject: [PATCH] Translate Chinese comments to English in File Manager components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Complete translation of FileWindow.tsx comments and hardcoded text - Complete translation of DraggableWindow.tsx hardcoded text - Complete translation of FileManagerSidebar.tsx comments - Complete translation of FileManagerGrid.tsx comments and UI text - Complete translation of DiffViewer.tsx hardcoded text with proper i18n - Partial translation of FileManagerModern.tsx comments (major sections done) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../File Manager/FileManagerContextMenu.tsx | 86 ++++---- .../Apps/File Manager/FileManagerGrid.tsx | 202 +++++++++--------- .../Apps/File Manager/FileManagerModern.tsx | 78 +++---- .../Apps/File Manager/FileManagerSidebar.tsx | 64 +++--- .../File Manager/components/DiffViewer.tsx | 56 ++--- .../components/DraggableWindow.tsx | 30 +-- .../File Manager/components/FileViewer.tsx | 102 ++++----- .../File Manager/components/FileWindow.tsx | 66 +++--- .../File Manager/components/WindowManager.tsx | 16 +- src/ui/components/DragIndicator.tsx | 16 +- 10 files changed, 363 insertions(+), 353 deletions(-) diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerContextMenu.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerContextMenu.tsx index 5c7a15b8..844a2ec2 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerContextMenu.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerContextMenu.tsx @@ -107,7 +107,7 @@ export function FileManagerContextMenu({ useEffect(() => { if (!isVisible) return; - // 调整菜单位置避免超出屏幕 + // Adjust menu position to avoid going off screen const adjustPosition = () => { const menuWidth = 200; const menuHeight = 300; @@ -130,13 +130,13 @@ export function FileManagerContextMenu({ adjustPosition(); - // 延迟添加事件监听器,避免捕获到触发菜单的那次点击 + // Delay adding event listeners to avoid capturing the click that triggered the menu let cleanupFn: (() => void) | null = null; const timeoutId = setTimeout(() => { - // 点击外部关闭菜单 + // Click outside to close menu const handleClickOutside = (event: MouseEvent) => { - // 检查点击是否在菜单内部 + // Check if click is inside menu const target = event.target as Element; const menuElement = document.querySelector("[data-context-menu]"); @@ -145,13 +145,13 @@ export function FileManagerContextMenu({ } }; - // 右键点击关闭菜单(Windows行为) + // Right-click to close menu (Windows behavior) const handleRightClick = (event: MouseEvent) => { event.preventDefault(); onClose(); }; - // 键盘支持 + // Keyboard support const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); @@ -159,12 +159,12 @@ export function FileManagerContextMenu({ } }; - // 窗口失焦关闭菜单 + // Close menu on window blur const handleBlur = () => { onClose(); }; - // 滚动时关闭菜单(Windows行为) + // Close menu on scroll (Windows behavior) const handleScroll = () => { onClose(); }; @@ -175,7 +175,7 @@ export function FileManagerContextMenu({ window.addEventListener("blur", handleBlur); window.addEventListener("scroll", handleScroll, true); - // 设置清理函数 + // Set cleanup function cleanupFn = () => { document.removeEventListener("mousedown", handleClickOutside, true); document.removeEventListener("contextmenu", handleRightClick); @@ -183,7 +183,7 @@ export function FileManagerContextMenu({ window.removeEventListener("blur", handleBlur); window.removeEventListener("scroll", handleScroll, true); }; - }, 50); // 50ms延迟,确保不会捕获到触发菜单的点击 + }, 50); // 50ms delay to ensure we don't capture the click that triggered the menu return () => { clearTimeout(timeoutId); @@ -204,13 +204,13 @@ export function FileManagerContextMenu({ (f) => f.type === "file" && f.executable, ); - // 构建菜单项 + // Build menu items const menuItems: MenuItem[] = []; if (isFileContext) { - // 文件/文件夹选中时的菜单 + // Menu when files/folders are selected - // 打开终端功能 - 支持文件和文件夹 + // Open terminal function - supports files and folders if (onOpenTerminal) { const targetPath = isSingleFile ? files[0].type === "directory" @@ -229,7 +229,7 @@ export function FileManagerContextMenu({ }); } - // 运行可执行文件功能 - 仅对单个可执行文件显示 + // Run executable file function - only show for single executable files if (isSingleFile && hasExecutableFiles && onRunExecutable) { menuItems.push({ icon: , @@ -239,7 +239,7 @@ export function FileManagerContextMenu({ }); } - // 添加分隔符(如果有上述功能) + // Add separator (if above functions exist) if ( onOpenTerminal || (isSingleFile && hasExecutableFiles && onRunExecutable) @@ -247,7 +247,7 @@ export function FileManagerContextMenu({ menuItems.push({ separator: true } as MenuItem); } - // 预览功能 + // Preview function if (hasFiles && onPreview) { menuItems.push({ icon: , @@ -257,7 +257,7 @@ export function FileManagerContextMenu({ }); } - // 下载功能 + // Download function if (hasFiles && onDownload) { menuItems.push({ icon: , @@ -269,7 +269,7 @@ export function FileManagerContextMenu({ }); } - // 拖拽到桌面菜单项(支持浏览器和桌面应用) + // Drag to desktop menu item (supports browser and desktop apps) if (hasFiles && onDragToDesktop) { const isModernBrowser = "showSaveFilePicker" in window; menuItems.push({ @@ -284,7 +284,7 @@ export function FileManagerContextMenu({ }); } - // PIN/UNPIN 功能 - 仅对单个文件显示 + // PIN/UNPIN function - only show for single files if (isSingleFile && files[0].type === "file") { const isCurrentlyPinned = isPinned ? isPinned(files[0]) : false; @@ -303,7 +303,7 @@ export function FileManagerContextMenu({ } } - // 添加文件夹快捷方式 - 仅对单个文件夹显示 + // Add folder shortcut - only show for single folders if (isSingleFile && files[0].type === "directory" && onAddShortcut) { menuItems.push({ icon: , @@ -312,7 +312,7 @@ export function FileManagerContextMenu({ }); } - // 添加分隔符(如果有上述功能) + // Add separator (if above functions exist) if ( (hasFiles && (onPreview || onDownload || onDragToDesktop)) || (isSingleFile && @@ -323,7 +323,7 @@ export function FileManagerContextMenu({ menuItems.push({ separator: true } as MenuItem); } - // 重命名功能 + // Rename function if (isSingleFile && onRename) { menuItems.push({ icon: , @@ -333,7 +333,7 @@ export function FileManagerContextMenu({ }); } - // 复制功能 + // Copy function if (onCopy) { menuItems.push({ icon: , @@ -345,7 +345,7 @@ export function FileManagerContextMenu({ }); } - // 剪切功能 + // Cut function if (onCut) { menuItems.push({ icon: , @@ -357,12 +357,12 @@ export function FileManagerContextMenu({ }); } - // 添加分隔符(如果有编辑功能) + // Add separator (if edit functions exist) if ((isSingleFile && onRename) || onCopy || onCut) { menuItems.push({ separator: true } as MenuItem); } - // 删除功能 + // Delete function if (onDelete) { menuItems.push({ icon: , @@ -375,12 +375,12 @@ export function FileManagerContextMenu({ }); } - // 添加分隔符(如果有删除功能) + // Add separator (if delete function exists) if (onDelete) { menuItems.push({ separator: true } as MenuItem); } - // 属性功能 + // Properties function if (isSingleFile && onProperties) { menuItems.push({ icon: , @@ -389,9 +389,9 @@ export function FileManagerContextMenu({ }); } } else { - // 空白区域右键菜单 + // Empty area right-click menu - // 在当前目录打开终端 + // Open terminal in current directory if (onOpenTerminal && currentPath) { menuItems.push({ icon: , @@ -401,7 +401,7 @@ export function FileManagerContextMenu({ }); } - // 上传功能 + // Upload function if (onUpload) { menuItems.push({ icon: , @@ -411,12 +411,12 @@ export function FileManagerContextMenu({ }); } - // 添加分隔符(如果有终端或上传功能) + // Add separator (if terminal or upload functions exist) if ((onOpenTerminal && currentPath) || onUpload) { menuItems.push({ separator: true } as MenuItem); } - // 新建文件夹 + // New folder if (onNewFolder) { menuItems.push({ icon: , @@ -426,7 +426,7 @@ export function FileManagerContextMenu({ }); } - // 新建文件 + // New file if (onNewFile) { menuItems.push({ icon: , @@ -436,12 +436,12 @@ export function FileManagerContextMenu({ }); } - // 添加分隔符(如果有新建功能) + // Add separator (if new functions exist) if (onNewFolder || onNewFile) { menuItems.push({ separator: true } as MenuItem); } - // 刷新功能 + // Refresh function if (onRefresh) { menuItems.push({ icon: , @@ -451,7 +451,7 @@ export function FileManagerContextMenu({ }); } - // 粘贴功能 + // Paste function if (hasClipboard && onPaste) { menuItems.push({ icon: , @@ -462,15 +462,15 @@ export function FileManagerContextMenu({ } } - // 过滤掉连续的分隔符 + // Filter out consecutive separators const filteredMenuItems = menuItems.filter((item, index) => { if (!item.separator) return true; - // 如果是分隔符,检查前一个和后一个是否也是分隔符 + // If it's a separator, check if previous and next are also separators const prevItem = index > 0 ? menuItems[index - 1] : null; const nextItem = index < menuItems.length - 1 ? menuItems[index + 1] : null; - // 如果前一个或后一个是分隔符,则过滤掉当前分隔符 + // If previous or next is a separator, filter out current separator if (prevItem?.separator || nextItem?.separator) { return false; } @@ -478,7 +478,7 @@ export function FileManagerContextMenu({ return true; }); - // 移除开头和结尾的分隔符 + // Remove separators at beginning and end const finalMenuItems = filteredMenuItems.filter((item, index) => { if (!item.separator) return true; return index > 0 && index < filteredMenuItems.length - 1; @@ -486,10 +486,10 @@ export function FileManagerContextMenu({ return ( <> - {/* 透明遮罩层用于捕获点击事件 */} + {/* Transparent overlay to capture click events */}
- {/* 菜单本体 */} + {/* Menu body */}
0 ? size.toFixed(1) : Math.round(size).toString(); @@ -93,7 +93,7 @@ interface FileManagerGridProps { onSystemDragStart?: (files: FileItem[]) => void; onSystemDragEnd?: (e: DragEvent) => void; hasClipboard?: boolean; - // Linus式创建意图props + // Linus-style creation intent props createIntent?: CreateIntent | null; onConfirmCreate?: (name: string) => void; onCancelCreate?: () => void; @@ -204,14 +204,14 @@ export function FileManagerGrid({ const gridRef = useRef(null); const [editingName, setEditingName] = useState(""); - // 统一拖拽状态管理 + // Unified drag state management const [dragState, setDragState] = useState({ type: "none", files: [], counter: 0, }); - // 全局鼠标移动监听 - 用于拖拽tooltip跟随 + // Global mouse move listener - for drag tooltip following useEffect(() => { const handleGlobalMouseMove = (e: MouseEvent) => { if (dragState.type === "internal" && dragState.files.length > 0) { @@ -231,11 +231,11 @@ export function FileManagerGrid({ const editInputRef = useRef(null); - // 开始编辑时设置初始名称 + // Set initial name when starting edit useEffect(() => { if (editingFile) { setEditingName(editingFile.name); - // 延迟聚焦以确保DOM已更新 + // Delay focus to ensure DOM is updated setTimeout(() => { editInputRef.current?.focus(); editInputRef.current?.select(); @@ -243,7 +243,7 @@ export function FileManagerGrid({ } }, [editingFile]); - // 处理编辑确认 + // Handle edit confirmation const handleEditConfirm = () => { if ( editingFile && @@ -256,13 +256,13 @@ export function FileManagerGrid({ onCancelEdit?.(); }; - // 处理编辑取消 + // Handle edit cancellation const handleEditCancel = () => { setEditingName(""); onCancelEdit?.(); }; - // 处理输入框按键 + // Handle input key events const handleEditKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); @@ -273,9 +273,9 @@ export function FileManagerGrid({ } }; - // 文件拖拽处理函数 + // File drag handling function const handleFileDragStart = (e: React.DragEvent, file: FileItem) => { - // 如果拖拽的文件已选中,则拖拽所有选中的文件 + // If dragged file is selected, drag all selected files const filesToDrag = selectedFiles.includes(file) ? selectedFiles : [file]; setDragState({ @@ -285,14 +285,14 @@ export function FileManagerGrid({ mousePosition: { x: e.clientX, y: e.clientY }, }); - // 设置拖拽数据,添加内部拖拽标识 + // Set drag data, add internal drag identifier const dragData = { type: "internal_files", files: filesToDrag.map((f) => f.path), }; e.dataTransfer.setData("text/plain", JSON.stringify(dragData)); - // 触发系统级拖拽开始 + // Trigger system-level drag start onSystemDragStart?.(filesToDrag); e.dataTransfer.effectAllowed = "move"; }; @@ -301,7 +301,7 @@ export function FileManagerGrid({ e.preventDefault(); e.stopPropagation(); - // 只有拖拽到不同文件且不是被拖拽的文件时才设置目标 + // Only set target when dragging to different file and not being dragged file if ( dragState.type === "internal" && !dragState.files.some((f) => f.path === targetFile.path) @@ -315,7 +315,7 @@ export function FileManagerGrid({ e.preventDefault(); e.stopPropagation(); - // 清除拖拽目标高亮 + // Clear drag target highlight if (dragState.target?.path === targetFile.path) { setDragState((prev) => ({ ...prev, target: undefined })); } @@ -330,7 +330,7 @@ export function FileManagerGrid({ return; } - // 检查是否拖拽到自身 + // Check if dragging to self const isDroppingOnSelf = dragState.files.some( (f) => f.path === targetFile.path, ); @@ -340,13 +340,13 @@ export function FileManagerGrid({ return; } - // 判断拖拽行为: - // 1. 文件/文件夹 拖拽到 文件夹 = 移动操作 - // 2. 单个文件 拖拽到 单个文件 = diff对比 - // 3. 其他情况 = 无效操作 + // Determine drag behavior: + // 1. File/folder drag to folder = move operation + // 2. Single file drag to single file = diff comparison + // 3. Other cases = invalid operation if (targetFile.type === "directory") { - // 移动操作 + // Move operation console.log( "Moving files to directory:", dragState.files.map((f) => f.name), @@ -359,7 +359,7 @@ export function FileManagerGrid({ dragState.files.length === 1 && dragState.files[0].type === "file" ) { - // diff对比操作 + // Diff comparison operation console.log( "Comparing files:", dragState.files[0].name, @@ -368,7 +368,7 @@ export function FileManagerGrid({ ); onFileDiff?.(dragState.files[0], targetFile); } else { - // 无效操作,给用户提示 + // Invalid operation, notify user console.log("Invalid drag operation"); } @@ -378,7 +378,7 @@ export function FileManagerGrid({ const handleFileDragEnd = (e: React.DragEvent) => { setDragState({ type: "none", files: [], counter: 0 }); - // 触发系统级拖拽结束检测 + // Trigger system-level drag end detection onSystemDragEnd?.(e.nativeEvent); }; @@ -395,17 +395,17 @@ export function FileManagerGrid({ } | null>(null); const [justFinishedSelecting, setJustFinishedSelecting] = useState(false); - // 导航历史管理 + // Navigation history management const [navigationHistory, setNavigationHistory] = useState([ currentPath, ]); const [historyIndex, setHistoryIndex] = useState(0); - // 路径编辑状态 + // Path editing state const [isEditingPath, setIsEditingPath] = useState(false); const [editPathValue, setEditPathValue] = useState(currentPath); - // 更新导航历史 + // Update navigation history useEffect(() => { const lastPath = navigationHistory[historyIndex]; if (currentPath !== lastPath) { @@ -416,7 +416,7 @@ export function FileManagerGrid({ } }, [currentPath]); - // 导航函数 + // Navigation functions const goBack = () => { if (historyIndex > 0) { const newIndex = historyIndex - 1; @@ -444,7 +444,7 @@ export function FileManagerGrid({ } }; - // 路径导航 + // Path navigation const pathParts = currentPath.split("/").filter(Boolean); const navigateToPath = (index: number) => { if (index === -1) { @@ -455,7 +455,7 @@ export function FileManagerGrid({ } }; - // 路径编辑功能 + // Path editing functionality const startEditingPath = () => { setEditPathValue(currentPath); setIsEditingPath(true); @@ -469,7 +469,7 @@ export function FileManagerGrid({ const confirmEditingPath = () => { const trimmedPath = editPathValue.trim(); if (trimmedPath) { - // 确保路径以 / 开头 + // Ensure path starts with / const normalizedPath = trimmedPath.startsWith("/") ? trimmedPath : "/" + trimmedPath; @@ -488,24 +488,24 @@ export function FileManagerGrid({ } }; - // 同步editPathValue与currentPath + // Sync editPathValue with currentPath useEffect(() => { if (!isEditingPath) { setEditPathValue(currentPath); } }, [currentPath, isEditingPath]); - // 拖放处理 - 区分内部文件拖拽和外部文件上传 + // Drag and drop handling - distinguish internal file drag and external file upload const handleDragEnter = useCallback( (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); - // 检查是否是内部文件拖拽 + // Check if it's internal file drag const isInternalDrag = dragState.type === "internal"; if (!isInternalDrag) { - // 只有外部文件拖拽才显示上传提示 + // Only show upload prompt for external file drag setDragState((prev) => ({ ...prev, type: "external", @@ -524,7 +524,7 @@ export function FileManagerGrid({ e.preventDefault(); e.stopPropagation(); - // 检查是否是内部文件拖拽 + // Check if it's internal file drag const isInternalDrag = dragState.type === "internal"; if (!isInternalDrag && dragState.type === "external") { @@ -546,11 +546,11 @@ export function FileManagerGrid({ e.preventDefault(); e.stopPropagation(); - // 检查是否是内部文件拖拽 + // Check if it's internal file drag const isInternalDrag = dragState.type === "internal"; if (isInternalDrag) { - // 更新鼠标位置 + // Update mouse position setDragState((prev) => ({ ...prev, mousePosition: { x: e.clientX, y: e.clientY }, @@ -563,15 +563,15 @@ export function FileManagerGrid({ [dragState.type], ); - // 滚轮事件处理,确保滚动正常工作 + // Mouse wheel event handling, ensure scrolling works normally const handleWheel = useCallback((e: React.WheelEvent) => { - // 不阻止默认滚动行为,让浏览器自己处理滚动 + // Don't prevent default scroll behavior, let browser handle scrolling e.stopPropagation(); }, []); - // 框选功能实现 + // Box selection functionality implementation const handleMouseDown = useCallback((e: React.MouseEvent) => { - // 只在空白区域开始框选,避免干扰文件点击 + // Only start box selection in empty area, avoid interfering with file clicks if (e.target === e.currentTarget && e.button === 0) { e.preventDefault(); const rect = (e.currentTarget as HTMLElement).getBoundingClientRect(); @@ -582,7 +582,7 @@ export function FileManagerGrid({ setSelectionStart({ x: startX, y: startY }); setSelectionRect({ x: startX, y: startY, width: 0, height: 0 }); - // 重置刚完成框选的标志,准备新的框选 + // Reset flag for just completed selection, prepare for new selection setJustFinishedSelecting(false); } }, []); @@ -601,7 +601,7 @@ export function FileManagerGrid({ setSelectionRect({ x, y, width, height }); - // 检测与文件项的交集,进行实时选择 + // Detect intersection with file items, perform real-time selection if (gridRef.current) { const fileElements = gridRef.current.querySelectorAll("[data-file-path]"); @@ -611,7 +611,7 @@ export function FileManagerGrid({ const elementRect = element.getBoundingClientRect(); const containerRect = gridRef.current!.getBoundingClientRect(); - // 简化坐标计算 - 直接使用相对于容器的坐标 + // Simplify coordinate calculation - directly use coordinates relative to container const relativeElementRect = { left: elementRect.left - containerRect.left, top: elementRect.top - containerRect.top, @@ -619,7 +619,7 @@ export function FileManagerGrid({ bottom: elementRect.bottom - containerRect.top, }; - // 选择框坐标 + // Selection box coordinates const selectionBox = { left: x, top: y, @@ -627,7 +627,7 @@ export function FileManagerGrid({ bottom: y + height, }; - // 检查是否相交 + // Check if intersecting const intersects = !( relativeElementRect.right < selectionBox.left || relativeElementRect.left > selectionBox.right || @@ -646,7 +646,7 @@ export function FileManagerGrid({ console.log("Total selected paths:", selectedPaths.length); - // 更新选中的文件 + // Update selected files const newSelection = files.filter((file) => selectedPaths.includes(file.path), ); @@ -668,7 +668,7 @@ export function FileManagerGrid({ setSelectionStart(null); setSelectionRect(null); - // 只有当移动距离足够大时才认为是框选,否则是点击 + // Only consider as box selection when movement distance is large enough, otherwise it's a click const startPos = selectionStart; if (startPos) { const rect = gridRef.current?.getBoundingClientRect(); @@ -680,13 +680,13 @@ export function FileManagerGrid({ ); if (distance > 5) { - // 真正的框选,设置标志防止立即清空 + // Real box selection, set flag to prevent immediate clearing setJustFinishedSelecting(true); setTimeout(() => { setJustFinishedSelecting(false); }, 50); } else { - // 只是点击,不设置标志,让handleGridClick正常处理 + // Just a click, don't set flag, let handleGridClick handle normally setJustFinishedSelecting(false); } } @@ -696,7 +696,7 @@ export function FileManagerGrid({ [isSelecting, selectionStart], ); - // 全局鼠标事件监听,确保在容器外也能结束框选 + // Global mouse event listener, ensure box selection can end outside container useEffect(() => { const handleGlobalMouseUp = (e: MouseEvent) => { if (isSelecting) { @@ -704,7 +704,7 @@ export function FileManagerGrid({ setSelectionStart(null); setSelectionRect(null); - // 全局mouseup说明是拖拽框选,设置标志 + // Global mouseup indicates drag box selection, set flag setJustFinishedSelecting(true); setTimeout(() => { setJustFinishedSelecting(false); @@ -744,7 +744,7 @@ export function FileManagerGrid({ e.stopPropagation(); if (dragState.type === "internal") { - // 内部拖拽到空白区域:触发下载 + // Internal drag to empty area: trigger download console.log( "Internal drag to empty area detected, triggering download", ); @@ -752,23 +752,23 @@ export function FileManagerGrid({ onDownload(dragState.files); } } else if (dragState.type === "external") { - // 外部拖拽:处理文件上传 + // External drag: handle file upload if (onUpload && e.dataTransfer.files.length > 0) { onUpload(e.dataTransfer.files); } } - // 重置拖拽状态 + // Reset drag state setDragState({ type: "none", files: [], counter: 0 }); }, [onUpload, onDownload, dragState], ); - // 文件选择处理 + // File selection handling const handleFileClick = (file: FileItem, event: React.MouseEvent) => { event.stopPropagation(); - // 确保网格获得焦点以支持键盘事件 + // Ensure grid gets focus to support keyboard events if (gridRef.current) { gridRef.current.focus(); } @@ -781,11 +781,11 @@ export function FileManagerGrid({ ); if (event.detail === 2) { - // 双击打开 + // Double click to open console.log("Double click - opening file"); onFileOpen(file); } else { - // 单击选择 + // Single click to select const multiSelect = event.ctrlKey || event.metaKey; const rangeSelect = event.shiftKey; @@ -797,7 +797,7 @@ export function FileManagerGrid({ ); if (rangeSelect && selectedFiles.length > 0) { - // 范围选择 (Shift+点击) + // Range selection (Shift+click) console.log("Range selection"); const lastSelected = selectedFiles[selectedFiles.length - 1]; const currentIndex = files.findIndex((f) => f.path === file.path); @@ -811,7 +811,7 @@ export function FileManagerGrid({ onSelectionChange(rangeFiles); } } else if (multiSelect) { - // 多选 (Ctrl+点击) + // Multi-selection (Ctrl+click) console.log("Multi selection"); const isSelected = selectedFiles.some((f) => f.path === file.path); if (isSelected) { @@ -822,21 +822,21 @@ export function FileManagerGrid({ onSelectionChange([...selectedFiles, file]); } } else { - // 单选 + // Single selection console.log("Single selection - should select only:", file.name); onSelectionChange([file]); } } }; - // 空白区域点击取消选择 + // Click empty area to cancel selection const handleGridClick = (event: React.MouseEvent) => { - // 确保网格获得焦点以支持键盘事件 + // Ensure grid gets focus to support keyboard events if (gridRef.current) { gridRef.current.focus(); } - // 如果刚完成框选,不要清空选择 + // If just completed box selection, don't clear selection if ( event.target === event.currentTarget && !isSelecting && @@ -846,10 +846,10 @@ export function FileManagerGrid({ } }; - // 键盘支持 + // Keyboard support useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - // 检查是否有输入框或可编辑元素获得焦点,如果有则跳过 + // Check if input box or editable element has focus, skip if so const activeElement = document.activeElement; if ( activeElement && @@ -910,7 +910,7 @@ export function FileManagerGrid({ break; case "Delete": if (selectedFiles.length > 0 && onDelete) { - // 触发删除操作 + // Trigger delete operation onDelete(selectedFiles); } break; @@ -954,9 +954,9 @@ export function FileManagerGrid({ return (
- {/* 工具栏和路径导航 */} + {/* Toolbar and path navigation */}
- {/* 导航按钮 */} + {/* Navigation buttons */}
- {/* 面包屑导航 */} + {/* Breadcrumb navigation */}
{isEditingPath ? ( - // 编辑模式:路径输入框 + // Edit mode: path input box
) : ( - // 查看模式:面包屑导航 + // View mode: breadcrumb navigation <>
- {/* 主文件网格 - 滚动区域 */} + {/* Main file grid - scroll area */}
onContextMenu?.(e)} tabIndex={0} > - {/* 拖拽提示覆盖层 */} + {/* Drag hint overlay */} {dragState.type === "external" && (
@@ -1125,7 +1125,7 @@ export function FileManagerGrid({
) : viewMode === "grid" ? (
- {/* Linus式创建意图UI - 纯粹分离 */} + {/* Linus-style creation intent UI - pure separation */} {createIntent && ( f.path === file.path, ); - // 详细调试路径比较 + // Detailed debug path comparison if (selectedFiles.length > 0) { console.log(`\n=== File: ${file.name} ===`); console.log(`File path: "${file.path}"`); @@ -1184,10 +1184,10 @@ export function FileManagerGrid({ onDragEnd={handleFileDragEnd} >
- {/* 文件图标 */} + {/* File icon */}
{getFileIcon(file, viewMode)}
- {/* 文件名 */} + {/* File name */}
{editingFile?.path === file.path ? ( { - // 阻止文件选择事件 + // Prevent file selection event if (onStartEdit) { e.stopPropagation(); onStartEdit(file); @@ -1241,9 +1241,9 @@ export function FileManagerGrid({ })}
) : ( - /* 列表视图 */ + /* List view */
- {/* Linus式创建意图UI - 列表视图 */} + {/* Linus-style creation intent UI - list view */} {createIntent && ( handleFileDrop(e, file)} onDragEnd={handleFileDragEnd} > - {/* 文件图标 */} + {/* File icon */}
{getFileIcon(file, viewMode)}
- {/* 文件信息 */} + {/* File info */}
{editingFile?.path === file.path ? ( { // 阻止文件选择事件 if (onStartEdit) { @@ -1334,7 +1334,7 @@ export function FileManagerGrid({ )}
- {/* 文件大小 */} + {/* File size */}
{file.type === "file" && file.size !== undefined && @@ -1345,7 +1345,7 @@ export function FileManagerGrid({ )}
- {/* 权限信息 */} + {/* Permission info */}
{file.permissions && (

@@ -1359,7 +1359,7 @@ export function FileManagerGrid({

)} - {/* 框选矩形 */} + {/* Selection rectangle */} {isSelecting && selectionRect && (
- {/* 状态栏 */} + {/* Status bar */}
{t("fileManager.itemCount", { count: files.length })} @@ -1386,7 +1386,7 @@ export function FileManagerGrid({
- {/* 拖拽跟随tooltip */} + {/* Drag following tooltip */} {dragState.type === "internal" && dragState.files.length > 0 && dragState.mousePosition && ( @@ -1403,14 +1403,14 @@ export function FileManagerGrid({ <> - 移动到 {dragState.target.name} + Move to {dragState.target.name} ) : ( <> - 与 {dragState.target.name} 进行diff对比 + Diff compare with {dragState.target.name} ) @@ -1418,7 +1418,7 @@ export function FileManagerGrid({ <> - 拖到窗口外下载 ({dragState.files.length} 个文件) + Drag outside window to download ({dragState.files.length} files) )} @@ -1429,7 +1429,7 @@ export function FileManagerGrid({ ); } -// Linus式创建意图组件:Grid视图 +// Linus-style creation intent component: Grid view function CreateIntentGridItem({ intent, onConfirm, @@ -1482,7 +1482,7 @@ function CreateIntentGridItem({ ); } -// Linus式创建意图组件:List视图 +// Linus-style creation intent component: List view function CreateIntentListItem({ intent, onConfirm, diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx index 9266aca8..998a462f 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx @@ -52,7 +52,7 @@ interface FileManagerModernProps { onClose?: () => void; } -// Linus式数据结构:创建意图与实际文件完全分离 +// Linus-style data structure: creation intent completely separated from actual files interface CreateIntent { id: string; type: 'file' | 'directory'; @@ -60,7 +60,7 @@ interface CreateIntent { currentName: string; } -// 内部组件,使用窗口管理器 +// Internal component, uses window manager function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const { openWindow } = useWindowManager(); const { t } = useTranslation(); @@ -94,13 +94,13 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { files: [], }); - // 操作状态 + // Operation state const [clipboard, setClipboard] = useState<{ files: FileItem[]; operation: "copy" | "cut"; } | null>(null); - // 撤销历史 + // Undo history interface UndoAction { type: "copy" | "cut" | "delete"; description: string; @@ -119,7 +119,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const [undoHistory, setUndoHistory] = useState([]); - // Linus式状态:创建意图与文件编辑分离 + // Linus-style state: creation intent separated from file editing const [createIntent, setCreateIntent] = useState(null); const [editingFile, setEditingFile] = useState(null); @@ -133,43 +133,43 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { maxFileSize: 5120, // 5GB - support large files like SSH tools should }); - // 拖拽到桌面功能 + // Drag to desktop functionality const dragToDesktop = useDragToDesktop({ sshSessionId: sshSessionId || "", sshHost: currentHost!, }); - // 系统级拖拽到桌面功能(新方案) + // System-level drag to desktop functionality (new approach) const systemDrag = useDragToSystemDesktop({ sshSessionId: sshSessionId || "", sshHost: currentHost!, }); - // 初始化SSH连接 + // Initialize SSH connection useEffect(() => { if (currentHost) { initializeSSHConnection(); } }, [currentHost]); - // 文件列表更新 + // File list update useEffect(() => { if (sshSessionId) { handleRefreshDirectory(); } }, [sshSessionId, currentPath]); - // 文件拖拽到外部处理 + // Handle file drag to external const handleFileDragStart = useCallback( (files: FileItem[]) => { - // 记录当前拖拽的文件 + // Record currently dragged files systemDrag.startDragToSystem(files, { enableToast: true, onSuccess: () => { clearSelection(); }, onError: (error) => { - console.error("拖拽失败:", error); + console.error("Drag failed:", error); }, }); }, @@ -178,7 +178,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const handleFileDragEnd = useCallback( (e: DragEvent) => { - // 检查是否拖拽到窗口外 + // Check if dragged outside window const margin = 50; const isOutside = e.clientX < margin || @@ -187,12 +187,12 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { e.clientY > window.innerHeight - margin; if (isOutside) { - // 延迟执行,避免与其他事件冲突 + // Delay execution to avoid conflicts with other events setTimeout(() => { systemDrag.handleDragEnd(e); }, 100); } else { - // 取消拖拽 + // Cancel drag systemDrag.cancelDragToSystem(); } }, @@ -279,10 +279,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } } - // 防抖刷新函数 - 防止疯狂点击 + // Debounced refresh function - prevent excessive clicking function handleRefreshDirectory() { const now = Date.now(); - const DEBOUNCE_MS = 500; // 500ms防抖 + const DEBOUNCE_MS = 500; // 500ms debounce if (now - lastRefreshTime < DEBOUNCE_MS) { console.log("Refresh ignored - too frequent"); @@ -311,12 +311,12 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { // 确保SSH连接有效 await ensureSSHConnection(); - // 读取文件内容 + // Read file content const fileContent = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onerror = () => reject(reader.error); - // 检查文件类型,决定读取方式 + // Check file type to determine reading method const isTextFile = file.type.startsWith("text/") || file.type === "application/json" || @@ -390,7 +390,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const response = await downloadSSHFile(sshSessionId, file.path); if (response?.content) { - // 转换为blob并触发下载 + // Convert to blob and trigger download const byteCharacters = atob(response.content); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { @@ -446,7 +446,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { ); } - // 记录删除历史(虽然无法真正撤销) + // Record deletion history (although cannot truly undo) const deletedFiles = files.map((file) => ({ path: file.path, name: file.name, @@ -454,7 +454,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const undoAction: UndoAction = { type: "delete", - description: `删除了 ${files.length} 个项目`, + description: t("fileManager.deletedItems", { count: files.length }), data: { operation: "cut", // Placeholder deletedFiles, @@ -484,7 +484,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } } - // Linus式创建:纯粹的意图,无副作用 + // Linus-style creation: pure intent, no side effects function handleCreateNewFolder() { const defaultName = generateUniqueName("NewFolder", "directory"); setCreateIntent({ @@ -543,22 +543,22 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const symlinkInfo = await identifySSHSymlink(currentSessionId, file.path); if (symlinkInfo.type === "directory") { - // 如果软链接指向目录,导航到它 + // If symlink points to directory, navigate to it setCurrentPath(symlinkInfo.target); } else if (symlinkInfo.type === "file") { - // 如果软链接指向文件,打开文件 - // 计算窗口位置(稍微错开) + // If symlink points to file, open file + // Calculate window position (slightly offset) const windowCount = Date.now() % 10; const offsetX = 120 + windowCount * 30; const offsetY = 120 + windowCount * 30; - // 创建目标文件对象 + // Create target file object const targetFile: FileItem = { ...file, path: symlinkInfo.target, }; - // 创建窗口组件工厂函数 + // Create window component factory function const createWindowComponent = (windowId: string) => ( ( @@ -635,12 +635,12 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } } - // 专门的文件编辑函数 + // Dedicated file editing function function handleFileEdit(file: FileItem) { handleFileOpen(file, true); } - // 专门的文件查看函数(只读) + // Dedicated file viewing function (read-only) function handleFileView(file: FileItem) { handleFileOpen(file, false); } @@ -648,8 +648,8 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { function handleContextMenu(event: React.MouseEvent, file?: FileItem) { event.preventDefault(); - // 如果右键点击的文件已经在选中列表中,使用所有选中的文件 - // 如果右键点击的文件不在选中列表中,只使用这一个文件 + // If right-clicked file is already in selection list, use all selected files + // If right-clicked file is not in selection list, use only this file let files: FileItem[]; if (file) { const isFileSelected = selectedFiles.some((f) => f.path === file.path); @@ -688,14 +688,14 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const { files, operation } = clipboard; - // 处理复制和剪切操作 + // Handle copy and cut operations let successCount = 0; const copiedItems: string[] = []; for (const file of files) { try { if (operation === "copy") { - // 复制操作:调用复制API + // Copy operation: call copy API const result = await copySSHItem( sshSessionId, file.path, @@ -706,7 +706,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { copiedItems.push(result.uniqueName || file.name); successCount++; } else { - // 剪切操作:移动文件到目标目录 + // Cut operation: move files to target directory const targetPath = currentPath.endsWith("/") ? `${currentPath}${file.name}` : `${currentPath}/${file.name}`; diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerSidebar.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerSidebar.tsx index 3f8c2437..195b4118 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerSidebar.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerSidebar.tsx @@ -38,9 +38,9 @@ interface FileManagerSidebarProps { currentPath: string; onPathChange: (path: string) => void; onLoadDirectory?: (path: string) => void; - onFileOpen?: (file: SidebarItem) => void; // 新增:处理文件打开 + onFileOpen?: (file: SidebarItem) => void; // Added: handle file opening sshSessionId?: string; - refreshTrigger?: number; // 用于触发数据刷新 + refreshTrigger?: number; // Used to trigger data refresh } export function FileManagerSidebar({ @@ -61,7 +61,7 @@ export function FileManagerSidebar({ new Set(["root"]), ); - // 右键菜单状态 + // Right-click menu state const [contextMenu, setContextMenu] = useState<{ x: number; y: number; @@ -74,12 +74,12 @@ export function FileManagerSidebar({ item: null, }); - // 加载快捷功能数据 + // Load quick access data useEffect(() => { loadQuickAccessData(); }, [currentHost, refreshTrigger]); - // 加载目录树(依赖sshSessionId) + // Load directory tree (depends on sshSessionId) useEffect(() => { if (sshSessionId) { loadDirectoryTree(); @@ -90,7 +90,7 @@ export function FileManagerSidebar({ if (!currentHost?.id) return; try { - // 加载最近访问文件(限制5个) + // Load recent files (limit to 5) const recentData = await getRecentFiles(currentHost.id); const recentItems = recentData.slice(0, 5).map((item: any) => ({ id: `recent-${item.id}`, @@ -101,7 +101,7 @@ export function FileManagerSidebar({ })); setRecentItems(recentItems); - // 加载固定文件 + // Load pinned files const pinnedData = await getPinnedFiles(currentHost.id); const pinnedItems = pinnedData.map((item: any) => ({ id: `pinned-${item.id}`, @@ -111,7 +111,7 @@ export function FileManagerSidebar({ })); setPinnedItems(pinnedItems); - // 加载文件夹快捷方式 + // Load folder shortcuts const shortcutData = await getFolderShortcuts(currentHost.id); const shortcutItems = shortcutData.map((item: any) => ({ id: `shortcut-${item.id}`, @@ -122,20 +122,20 @@ export function FileManagerSidebar({ setShortcuts(shortcutItems); } catch (error) { console.error("Failed to load quick access data:", error); - // 如果加载失败,保持空数组 + // If loading fails, keep empty arrays setRecentItems([]); setPinnedItems([]); setShortcuts([]); } }; - // 删除功能实现 + // Delete functionality implementation const handleRemoveRecentFile = async (item: SidebarItem) => { if (!currentHost?.id) return; try { await removeRecentFile(currentHost.id, item.path); - loadQuickAccessData(); // 重新加载数据 + loadQuickAccessData(); // Reload data toast.success( t("fileManager.removedFromRecentFiles", { name: item.name }), ); @@ -150,7 +150,7 @@ export function FileManagerSidebar({ try { await removePinnedFile(currentHost.id, item.path); - loadQuickAccessData(); // 重新加载数据 + loadQuickAccessData(); // Reload data toast.success(t("fileManager.unpinnedSuccessfully", { name: item.name })); } catch (error) { console.error("Failed to unpin file:", error); @@ -163,7 +163,7 @@ export function FileManagerSidebar({ try { await removeFolderShortcut(currentHost.id, item.path); - loadQuickAccessData(); // 重新加载数据 + loadQuickAccessData(); // Reload data toast.success(t("fileManager.removedShortcut", { name: item.name })); } catch (error) { console.error("Failed to remove shortcut:", error); @@ -175,11 +175,11 @@ export function FileManagerSidebar({ if (!currentHost?.id || recentItems.length === 0) return; try { - // 批量删除所有recent文件 + // Batch delete all recent files await Promise.all( recentItems.map((item) => removeRecentFile(currentHost.id, item.path)), ); - loadQuickAccessData(); // 重新加载数据 + loadQuickAccessData(); // Reload data toast.success(t("fileManager.clearedAllRecentFiles")); } catch (error) { console.error("Failed to clear recent files:", error); @@ -187,7 +187,7 @@ export function FileManagerSidebar({ } }; - // 右键菜单处理 + // Right-click menu handling const handleContextMenu = (e: React.MouseEvent, item: SidebarItem) => { e.preventDefault(); e.stopPropagation(); @@ -204,7 +204,7 @@ export function FileManagerSidebar({ setContextMenu((prev) => ({ ...prev, isVisible: false, item: null })); }; - // 点击外部关闭菜单 + // Click outside to close menu useEffect(() => { if (!contextMenu.isVisible) return; @@ -223,7 +223,7 @@ export function FileManagerSidebar({ } }; - // 延迟添加监听器,避免立即触发 + // Delay adding listeners to avoid immediate trigger const timeoutId = setTimeout(() => { document.addEventListener("mousedown", handleClickOutside); document.addEventListener("keydown", handleKeyDown); @@ -240,10 +240,10 @@ export function FileManagerSidebar({ if (!sshSessionId) return; try { - // 加载根目录 + // Load root directory const response = await listSSHFiles(sshSessionId, "/"); - // listSSHFiles 现在总是返回 {files: Array, path: string} 格式 + // listSSHFiles now always returns {files: Array, path: string} format const rootFiles = response.files || []; const rootFolders = rootFiles.filter( (item: any) => item.type === "directory", @@ -255,7 +255,7 @@ export function FileManagerSidebar({ path: folder.path, type: "folder" as const, isExpanded: false, - children: [], // 子目录将按需加载 + children: [], // Subdirectories will be loaded on demand })); setDirectoryTree([ @@ -270,7 +270,7 @@ export function FileManagerSidebar({ ]); } catch (error) { console.error("Failed to load directory tree:", error); - // 如果加载失败,显示简单的根目录 + // If loading fails, show simple root directory setDirectoryTree([ { id: "root", @@ -289,17 +289,17 @@ export function FileManagerSidebar({ toggleFolder(item.id, item.path); onPathChange(item.path); } else if (item.type === "recent" || item.type === "pinned") { - // 对于文件类型,调用文件打开回调 + // For file types, call file open callback if (onFileOpen) { onFileOpen(item); } else { - // 如果没有文件打开回调,切换到文件所在目录 + // If no file open callback, switch to file directory const directory = item.path.substring(0, item.path.lastIndexOf("/")) || "/"; onPathChange(directory); } } else if (item.type === "shortcut") { - // 文件夹快捷方式直接切换到目录 + // Folder shortcuts directly switch to directory onPathChange(item.path); } }; @@ -312,12 +312,12 @@ export function FileManagerSidebar({ } else { newExpanded.add(folderId); - // 按需加载子目录 + // Load subdirectories on demand if (sshSessionId && folderPath && folderPath !== "/") { try { const subResponse = await listSSHFiles(sshSessionId, folderPath); - // listSSHFiles 现在总是返回 {files: Array, path: string} 格式 + // listSSHFiles now always returns {files: Array, path: string} format const subFiles = subResponse.files || []; const subFolders = subFiles.filter( (item: any) => item.type === "directory", @@ -332,7 +332,7 @@ export function FileManagerSidebar({ children: [], })); - // 更新目录树,为当前文件夹添加子目录 + // Update directory tree, add subdirectories for current folder setDirectoryTree((prevTree) => { const updateChildren = (items: SidebarItem[]): SidebarItem[] => { return items.map((item) => { @@ -370,7 +370,7 @@ export function FileManagerSidebar({ style={{ paddingLeft: `${12 + level * 16}px`, paddingRight: "12px" }} onClick={() => handleItemClick(item)} onContextMenu={(e) => { - // 只有快捷功能项才需要右键菜单 + // Only quick access items need right-click menu if ( item.type === "recent" || item.type === "pinned" || @@ -447,7 +447,7 @@ export function FileManagerSidebar({
- {/* 快捷功能区域 */} + {/* Quick access area */} {renderSection( t("fileManager.recent"), , @@ -464,7 +464,7 @@ export function FileManagerSidebar({ shortcuts, )} - {/* 目录树 */} + {/* Directory tree */}
- {/* 右键菜单 */} + {/* Right-click menu */} {contextMenu.isVisible && contextMenu.item && ( <>
diff --git a/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx index 40484cb8..120eab8e 100644 --- a/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import { DiffEditor } from "@monaco-editor/react"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; +import { useTranslation } from "react-i18next"; import { Download, RefreshCw, @@ -35,6 +36,7 @@ export function DiffViewer({ onDownload1, onDownload2, }: DiffViewerProps) { + const { t } = useTranslation(); const [content1, setContent1] = useState(""); const [content2, setContent2] = useState(""); const [isLoading, setIsLoading] = useState(false); @@ -44,7 +46,7 @@ export function DiffViewer({ ); const [showLineNumbers, setShowLineNumbers] = useState(true); - // 确保SSH连接有效 + // Ensure SSH connection is valid const ensureSSHConnection = async () => { try { const status = await getSSHStatus(sshSessionId); @@ -68,10 +70,10 @@ export function DiffViewer({ } }; - // 加载文件内容 + // Load file contents const loadFileContents = async () => { if (file1.type !== "file" || file2.type !== "file") { - setError("只能对比文件类型的项目"); + setError(t("fileManager.canOnlyCompareFiles")); return; } @@ -79,10 +81,10 @@ export function DiffViewer({ setIsLoading(true); setError(null); - // 确保SSH连接有效 + // Ensure SSH connection is valid await ensureSSHConnection(); - // 并行加载两个文件 + // Load both files in parallel const [response1, response2] = await Promise.all([ readSSHFile(sshSessionId, file1.path), readSSHFile(sshSessionId, file2.path), @@ -95,17 +97,23 @@ export function DiffViewer({ const errorData = error?.response?.data; if (errorData?.tooLarge) { - setError(`文件过大: ${errorData.error}`); + setError(t("fileManager.fileTooLarge", { error: errorData.error })); } else if ( error.message?.includes("connection") || error.message?.includes("established") ) { setError( - `SSH连接失败。请检查与 ${sshHost.name} (${sshHost.ip}:${sshHost.port}) 的连接`, + t("fileManager.sshConnectionFailed", { + name: sshHost.name, + ip: sshHost.ip, + port: sshHost.port + }), ); } else { setError( - `加载文件失败: ${error.message || errorData?.error || "未知错误"}`, + t("fileManager.loadFileFailed", { + error: error.message || errorData?.error || t("fileManager.unknownError") + }), ); } } finally { @@ -113,7 +121,7 @@ export function DiffViewer({ } }; - // 下载文件 + // Download file const handleDownloadFile = async (file: FileItem) => { try { await ensureSSHConnection(); @@ -147,7 +155,7 @@ export function DiffViewer({ } }; - // 获取文件语言类型 + // Get file language type const getFileLanguage = (fileName: string): string => { const ext = fileName.split(".").pop()?.toLowerCase(); const languageMap: Record = { @@ -182,7 +190,7 @@ export function DiffViewer({ return languageMap[ext || ""] || "plaintext"; }; - // 初始加载 + // Initial load useEffect(() => { loadFileContents(); }, [file1, file2, sshSessionId]); @@ -192,7 +200,7 @@ export function DiffViewer({
-

正在加载文件对比...

+

{t("fileManager.loadingFileComparison")}

); @@ -206,7 +214,7 @@ export function DiffViewer({

{error}

@@ -215,12 +223,12 @@ export function DiffViewer({ return (
- {/* 工具栏 */} + {/* Toolbar */}
- 对比: + {t("fileManager.compare")}: {file1.name} @@ -230,7 +238,7 @@ export function DiffViewer({
- {/* 视图切换 */} + {/* View toggle */} - {/* 行号切换 */} + {/* Line number toggle */} - {/* 刷新按钮 */} + {/* Refresh button */} @@ -285,7 +293,7 @@ export function DiffViewer({
- {/* Diff编辑器 */} + {/* Diff editor */}
-

初始化编辑器...

+

{t("fileManager.initializingEditor")}

} diff --git a/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx b/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx index 89e42f02..4e9d5d34 100644 --- a/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef, useCallback, useEffect } from "react"; import { cn } from "@/lib/utils"; import { Minus, Square, X, Maximize2, Minimize2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; interface DraggableWindowProps { title: string; @@ -35,7 +36,8 @@ export function DraggableWindow({ zIndex = 1000, onFocus, }: DraggableWindowProps) { - // 窗口状态 + const { t } = useTranslation(); + // Window state const [position, setPosition] = useState({ x: initialX, y: initialY }); const [size, setSize] = useState({ width: initialWidth, @@ -45,19 +47,19 @@ export function DraggableWindow({ const [isResizing, setIsResizing] = useState(false); const [resizeDirection, setResizeDirection] = useState(""); - // 拖拽开始位置 + // Drag start position const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); const [windowStart, setWindowStart] = useState({ x: 0, y: 0 }); const windowRef = useRef(null); const titleBarRef = useRef(null); - // 处理窗口焦点 + // Handle window focus const handleWindowClick = useCallback(() => { onFocus?.(); }, [onFocus]); - // 拖拽处理 + // Drag handling const handleMouseDown = useCallback( (e: React.MouseEvent) => { if (isMaximized) return; @@ -85,7 +87,7 @@ export function DraggableWindow({ y: Math.max( 0, Math.min(window.innerHeight - 40, windowStart.y + deltaY), - ), // 保持标题栏可见 + ), // Keep title bar visible }); } @@ -143,7 +145,7 @@ export function DraggableWindow({ setResizeDirection(""); }, []); - // 调整大小处理 + // Resize handling const handleResizeStart = useCallback( (e: React.MouseEvent, direction: string) => { if (isMaximized) return; @@ -159,7 +161,7 @@ export function DraggableWindow({ [isMaximized, size, onFocus], ); - // 全局事件监听 + // Global event listeners useEffect(() => { if (isDragging || isResizing) { document.addEventListener("mousemove", handleMouseMove); @@ -176,7 +178,7 @@ export function DraggableWindow({ } }, [isDragging, isResizing, handleMouseMove, handleMouseUp]); - // 双击标题栏最大化/还原 + // Double-click title bar to maximize/restore const handleTitleDoubleClick = useCallback(() => { onMaximize?.(); }, [onMaximize]); @@ -198,7 +200,7 @@ export function DraggableWindow({ }} onClick={handleWindowClick} > - {/* 标题栏 */} + {/* Title bar */}
{isMaximized ? ( @@ -257,7 +259,7 @@ export function DraggableWindow({
- {/* 窗口内容 */} + {/* Window content */}
- {/* 调整大小边框 - 只在非最大化时显示 */} + {/* Resize borders - only show when not maximized */} {!isMaximized && ( <> - {/* 边缘调整 */} + {/* Edge resize */}
handleResizeStart(e, "top")} @@ -286,7 +288,7 @@ export function DraggableWindow({ onMouseDown={(e) => handleResizeStart(e, "right")} /> - {/* 角落调整 */} + {/* Corner resize */}
handleResizeStart(e, "top-left")} diff --git a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx index 878d6a91..528536bb 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx @@ -73,12 +73,12 @@ interface FileViewerProps { onDownload?: () => void; } -// 获取编程语言的官方图标 +// Get official icon for programming languages function getLanguageIcon(filename: string): React.ReactNode { const ext = filename.split(".").pop()?.toLowerCase() || ""; const baseName = filename.toLowerCase(); - // 特殊文件名处理 + // Special filename handling if (["dockerfile"].includes(baseName)) { return ; } @@ -124,7 +124,7 @@ function getLanguageIcon(filename: string): React.ReactNode { return iconMap[ext] || ; } -// 获取文件类型和图标 +// Get file type and icon function getFileType(filename: string): { type: string; icon: React.ReactNode; @@ -209,17 +209,17 @@ function getFileType(filename: string): { } } -// 获取CodeMirror语言扩展 +// Get CodeMirror language extension function getLanguageExtension(filename: string) { const ext = filename.split(".").pop()?.toLowerCase() || ""; const baseName = filename.toLowerCase(); - // 特殊文件名处理 + // Special filename handling if (["dockerfile", "makefile", "rakefile", "gemfile"].includes(baseName)) { return loadLanguage(baseName); } - // 根据扩展名映射 + // Map by file extension const langMap: Record = { js: "javascript", jsx: "jsx", @@ -258,7 +258,7 @@ function getLanguageExtension(filename: string) { return language ? loadLanguage(language) : null; } -// 格式化文件大小 +// Format file size function formatFileSize(bytes?: number): string { if (!bytes) return "Unknown size"; const sizes = ["B", "KB", "MB", "GB"]; @@ -294,31 +294,31 @@ export function FileViewer({ const fileTypeInfo = getFileType(file.name); - // 文件大小限制 - 移除硬限制,支持大文件处理 - const WARNING_SIZE = 50 * 1024 * 1024; // 50MB 警告 - const MAX_SIZE = Number.MAX_SAFE_INTEGER; // 移除硬限制 + // File size limits - remove hard limits, support large file handling + const WARNING_SIZE = 50 * 1024 * 1024; // 50MB warning + const MAX_SIZE = Number.MAX_SAFE_INTEGER; // Remove hard limits - // 检查是否应该显示为文本 + // Check if should display as text const shouldShowAsText = fileTypeInfo.type === "text" || fileTypeInfo.type === "code" || (fileTypeInfo.type === "unknown" && (forceShowAsText || !file.size || file.size <= WARNING_SIZE)); - // 检查文件是否过大 + // Check if file is too large const isLargeFile = file.size && file.size > WARNING_SIZE; const isTooLarge = file.size && file.size > MAX_SIZE; - // 同步外部内容更改 + // Sync external content changes useEffect(() => { setEditedContent(content); - // 只有在savedContent更新时才更新originalContent + // Only update originalContent when savedContent is updated if (savedContent) { setOriginalContent(savedContent); } setHasChanges(content !== (savedContent || content)); - // 如果是未知文件类型且文件较大,显示警告 + // If unknown file type and file is large, show warning if (fileTypeInfo.type === "unknown" && isLargeFile && !forceShowAsText) { setShowLargeFileWarning(true); } else { @@ -326,27 +326,27 @@ export function FileViewer({ } }, [content, savedContent, fileTypeInfo.type, isLargeFile, forceShowAsText]); - // 处理内容更改 + // Handle content changes const handleContentChange = (newContent: string) => { setEditedContent(newContent); setHasChanges(newContent !== originalContent); onContentChange?.(newContent); }; - // 保存文件 + // Save file const handleSave = () => { onSave?.(editedContent); - // 注意:不在这里更新originalContent,因为它会通过savedContent prop更新 + // Note: Don't update originalContent here, as it will be updated via savedContent prop }; - // 复原文件 + // Revert file const handleRevert = () => { setEditedContent(originalContent); setHasChanges(false); onContentChange?.(originalContent); }; - // 搜索匹配功能 + // Search matching functionality const findMatches = (text: string) => { if (!text) { setSearchMatches([]); @@ -363,7 +363,7 @@ export function FileViewer({ start: match.index, end: match.index + match[0].length, }); - // 避免无限循环 + // Avoid infinite loop if (match.index === regex.lastIndex) regex.lastIndex++; } @@ -371,7 +371,7 @@ export function FileViewer({ setCurrentMatchIndex(matches.length > 0 ? 0 : -1); }; - // 搜索导航 + // Search navigation const goToNextMatch = () => { if (searchMatches.length === 0) return; setCurrentMatchIndex((prev) => (prev + 1) % searchMatches.length); @@ -384,7 +384,7 @@ export function FileViewer({ ); }; - // 替换功能 + // Replace functionality const handleFindReplace = ( findText: string, replaceWithText: string, @@ -399,7 +399,7 @@ export function FileViewer({ replaceWithText, ); } else if (currentMatchIndex >= 0 && searchMatches[currentMatchIndex]) { - // 替换当前匹配项 + // Replace current match const match = searchMatches[currentMatchIndex]; newContent = editedContent.substring(0, match.start) + @@ -411,7 +411,7 @@ export function FileViewer({ setHasChanges(newContent !== originalContent); onContentChange?.(newContent); - // 重新搜索以更新匹配项 + // Re-search to update matches setTimeout(() => findMatches(findText), 0); }; @@ -425,7 +425,7 @@ export function FileViewer({ setShowReplacePanel(true); }; - // 渲染带高亮的文本 + // Render highlighted text const renderHighlightedText = (text: string) => { if (!searchText || searchMatches.length === 0) { return text; @@ -435,12 +435,12 @@ export function FileViewer({ let lastIndex = 0; searchMatches.forEach((match, index) => { - // 添加匹配前的文本 + // Add text before match if (match.start > lastIndex) { parts.push(text.substring(lastIndex, match.start)); } - // 添加高亮的匹配文本 + // Add highlighted match text const isCurrentMatch = index === currentMatchIndex; parts.push( { setForceShowAsText(true); setShowLargeFileWarning(false); }; - // 处理用户拒绝打开大文件 + // Handle user rejection to open large file const handleCancelOpenAsText = () => { setShowLargeFileWarning(false); }; @@ -491,7 +491,7 @@ export function FileViewer({ return (
- {/* 文件信息头部 */} + {/* File info header */}
@@ -517,7 +517,7 @@ export function FileViewer({
- {/* 编辑工具栏 - 直接显示,无需切换 */} + {/* Edit toolbar - display directly, no toggle needed */} {isEditable && ( <>
- {/* 搜索和替换面板 */} + {/* Search and replace panel */} {showSearchPanel && (
@@ -657,9 +657,9 @@ export function FileViewer({
)} - {/* 文件内容 */} + {/* File content */}
- {/* 大文件警告对话框 */} + {/* Large file warning dialog */} {showLargeFileWarning && (
@@ -722,7 +722,7 @@ export function FileViewer({
)} - {/* 图片预览 */} + {/* Image preview */} {fileTypeInfo.type === "image" && !showLargeFileWarning && (
)} - {/* 文本和代码文件预览 */} + {/* Text and code file preview */} {shouldShowAsText && !showLargeFileWarning && (
{fileTypeInfo.type === "code" ? ( - // 代码文件使用CodeMirror + // Code files use CodeMirror
{searchText && searchMatches.length > 0 ? ( - // 当有搜索结果时,显示只读的高亮文本(带行号) + // When there are search results, show read-only highlighted text (with line numbers)
- {/* 行号列 */} + {/* Line number column */}
{editedContent.split("\n").map((_, index) => (
))}
- {/* 代码内容 */} + {/* Code content */}
{renderHighlightedText(editedContent)}
) : ( - // 没有搜索时显示CodeMirror编辑器 + // Show CodeMirror editor when no search handleContentChange(value)} @@ -790,17 +790,17 @@ export function FileViewer({ )}
) : ( - // 普通文本文件 + // Plain text files
{isEditable ? (
{searchText && searchMatches.length > 0 ? ( - // 当有搜索结果时,显示只读的高亮文本 + // When there are search results, show read-only highlighted text
{renderHighlightedText(editedContent)}
) : ( - // 直接显示可编辑的textarea + // Directly show editable textarea