diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index b877de0f..5c61009d 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -46,7 +46,7 @@ const storage = multer.diskStorage({ const upload = multer({ storage: storage, limits: { - fileSize: 100 * 1024 * 1024, // 100MB limit + fileSize: 1024 * 1024 * 1024, // 1GB limit for database operations }, fileFilter: (req, file, cb) => { // Allow SQLite files diff --git a/src/backend/ssh/file-manager.ts b/src/backend/ssh/file-manager.ts index f96611a3..f9a5175b 100644 --- a/src/backend/ssh/file-manager.ts +++ b/src/backend/ssh/file-manager.ts @@ -58,9 +58,9 @@ app.use( ], }), ); -app.use(express.json({ limit: "100mb" })); -app.use(express.urlencoded({ limit: "100mb", extended: true })); -app.use(express.raw({ limit: "200mb", type: "application/octet-stream" })); +app.use(express.json({ limit: "1gb" })); +app.use(express.urlencoded({ limit: "1gb", extended: true })); +app.use(express.raw({ limit: "5gb", type: "application/octet-stream" })); interface SSHSession { client: SSHClient; @@ -492,8 +492,8 @@ app.get("/ssh/file_manager/ssh/readFile", (req, res) => { sshConn.lastActive = Date.now(); - // First check file size to prevent loading huge files - const MAX_READ_SIZE = 10 * 1024 * 1024; // 10MB - same as frontend limit + // Support large file reading - increased limit for better compatibility + const MAX_READ_SIZE = 500 * 1024 * 1024; // 500MB - much more reasonable limit const escapedPath = filePath.replace(/'/g, "'\"'\"'"); // Get file size first @@ -1641,8 +1641,8 @@ app.post("/ssh/file_manager/ssh/downloadFile", async (req, res) => { .json({ error: "Cannot download directories or special files" }); } - // Check file size (limit to 100MB for safety) - const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB + // Support large file downloads - increased limit for better compatibility + const MAX_FILE_SIZE = 5 * 1024 * 1024 * 1024; // 5GB - reasonable for SSH file operations if (stats.size > MAX_FILE_SIZE) { fileLogger.warn("File too large for download", { operation: "file_download", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 5c6387a0..4068b963 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -191,6 +191,7 @@ }, "common": { "close": "Close", + "minimize": "Minimize", "online": "Online", "offline": "Offline", "maintenance": "Maintenance", @@ -661,7 +662,7 @@ "deleteItem": "Delete Item", "currentPath": "Current Path", "uploadFileTitle": "Upload File", - "maxFileSize": "Max: 100MB (JSON) / 200MB (Binary)", + "maxFileSize": "Max: 1GB (JSON) / 5GB (Binary) - Large files supported", "removeFile": "Remove File", "clickToSelectFile": "Click to select a file", "chooseFile": "Choose File", @@ -819,7 +820,46 @@ "pinFile": "Pin file", "addToShortcuts": "Add to shortcuts", "selectLocationToSave": "Select location to save", - "downloadToDefaultLocation": "Download to default location" + "downloadToDefaultLocation": "Download to default location", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "enterPath": "Enter path...", + "editPath": "Edit path", + "confirm": "Confirm", + "cancel": "Cancel", + "folderName": "Folder name", + "find": "Find...", + "replaceWith": "Replace with...", + "startTyping": "Start typing...", + "fileSavedSuccessfully": "File saved successfully", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "fileDownloadedSuccessfully": "File downloaded successfully", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed" }, "tunnels": { "title": "SSH Tunnels", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 5cf1dcad..b2860f90 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -190,6 +190,7 @@ }, "common": { "close": "关闭", + "minimize": "最小化", "online": "在线", "offline": "离线", "maintenance": "维护中", @@ -676,7 +677,7 @@ "deleteItem": "删除项目", "currentPath": "当前路径", "uploadFileTitle": "上传文件", - "maxFileSize": "最大:100MB(JSON)/ 200MB(二进制)", + "maxFileSize": "最大:1GB(JSON)/ 5GB(二进制)- 支持大文件", "removeFile": "移除文件", "clickToSelectFile": "点击选择文件", "chooseFile": "选择文件", @@ -826,7 +827,46 @@ "pinFile": "固定文件", "addToShortcuts": "添加到快捷方式", "selectLocationToSave": "选择位置保存", - "downloadToDefaultLocation": "下载到默认位置" + "downloadToDefaultLocation": "下载到默认位置", + "pasteFailed": "粘贴失败", + "noUndoableActions": "没有可撤销的操作", + "undoCopySuccess": "已撤销复制操作:删除了 {{count}} 个复制的文件", + "undoCopyFailedDelete": "撤销失败:无法删除任何复制的文件", + "undoCopyFailedNoInfo": "撤销失败:找不到复制的文件信息", + "undoMoveSuccess": "已撤销移动操作:移回了 {{count}} 个文件到原位置", + "undoMoveFailedMove": "撤销失败:无法移回任何文件", + "undoMoveFailedNoInfo": "撤销失败:找不到移动的文件信息", + "undoDeleteNotSupported": "删除操作无法撤销:文件已从服务器永久删除", + "undoTypeNotSupported": "不支持撤销此类操作", + "undoOperationFailed": "撤销操作失败", + "unknownError": "未知错误", + "enterPath": "输入路径...", + "editPath": "编辑路径", + "confirm": "确认", + "cancel": "取消", + "folderName": "文件夹名", + "find": "查找...", + "replaceWith": "替换为...", + "startTyping": "开始输入...", + "fileSavedSuccessfully": "文件保存成功", + "autoSaveFailed": "自动保存失败", + "fileAutoSaved": "文件已自动保存", + "fileDownloadedSuccessfully": "文件下载成功", + "moveFileFailed": "移动 {{name}} 失败", + "moveOperationFailed": "移动操作失败", + "canOnlyCompareFiles": "只能对比两个文件", + "comparingFiles": "正在对比文件:{{file1}} 与 {{file2}}", + "dragFailed": "拖拽失败", + "filePinnedSuccessfully": "文件\"{{name}}\"已固定", + "pinFileFailed": "固定文件失败", + "fileUnpinnedSuccessfully": "文件\"{{name}}\"已取消固定", + "unpinFileFailed": "取消固定失败", + "shortcutAddedSuccessfully": "文件夹快捷方式\"{{name}}\"已添加", + "addShortcutFailed": "添加快捷方式失败", + "operationCompletedSuccessfully": "已{{operation}} {{count}} 个项目", + "operationCompleted": "已{{operation}} {{count}} 个项目", + "downloadFileSuccess": "文件 {{name}} 下载成功", + "downloadFileFailed": "下载失败" }, "tunnels": { "title": "SSH 隧道", diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx index 8e9e1de8..06148b98 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx @@ -92,6 +92,7 @@ interface FileManagerGridProps { onFileDiff?: (file1: FileItem, file2: FileItem) => void; onSystemDragStart?: (files: FileItem[]) => void; onSystemDragEnd?: (e: DragEvent) => void; + hasClipboard?: boolean; // Linus式创建意图props createIntent?: CreateIntent | null; onConfirmCreate?: (name: string) => void; @@ -194,6 +195,7 @@ export function FileManagerGrid({ onFileDiff, onSystemDragStart, onSystemDragEnd, + hasClipboard, createIntent, onConfirmCreate, onCancelCreate, @@ -894,7 +896,7 @@ export function FileManagerGrid({ break; case "v": case "V": - if ((event.ctrlKey || event.metaKey) && onPaste) { + if ((event.ctrlKey || event.metaKey) && onPaste && hasClipboard) { event.preventDefault(); onPaste(); } @@ -1016,20 +1018,20 @@ export function FileManagerGrid({ } }} className="flex-1 px-2 py-1 bg-dark-hover border border-dark-border rounded text-sm focus:outline-none focus:ring-1 focus:ring-primary" - placeholder="输入路径..." + placeholder={t("fileManager.enterPath")} autoFocus /> ) : ( @@ -1057,7 +1059,7 @@ export function FileManagerGrid({ @@ -1473,7 +1475,7 @@ function CreateIntentGridItem({ onKeyDown={handleKeyDown} onBlur={() => onConfirm?.(inputName.trim())} className="w-full max-w-[120px] rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 text-xs text-center text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[2px] outline-none" - placeholder={intent.type === 'directory' ? 'Folder name' : 'File name'} + placeholder={intent.type === 'directory' ? t('fileManager.folderName') : t('fileManager.fileName')} /> diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx index 587f110d..9266aca8 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx @@ -130,7 +130,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const { isDragging, dragHandlers } = useDragAndDrop({ onFilesDropped: handleFilesDropped, onError: (error) => toast.error(error), - maxFileSize: 100, // 100MB + maxFileSize: 5120, // 5GB - support large files like SSH tools should }); // 拖拽到桌面功能 @@ -792,13 +792,13 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { if (hasRenamed) { toast.success( - `已${operationText} ${successCount} 个项目,部分文件已自动重命名避免冲突`, + t("fileManager.operationCompletedSuccessfully", { operation: operationText, count: successCount }), ); } else { - toast.success(`已${operationText} ${successCount} 个项目`); + toast.success(t("fileManager.operationCompleted", { operation: operationText, count: successCount })); } } else { - toast.success(`已${operationText} ${successCount} 个项目`); + toast.success(t("fileManager.operationCompleted", { operation: operationText, count: successCount })); } } @@ -811,13 +811,13 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { setClipboard(null); } } catch (error: any) { - toast.error(`粘贴失败: ${error.message || "Unknown error"}`); + toast.error(`${t("fileManager.pasteFailed")}: ${error.message || t("fileManager.unknownError")}`); } } async function handleUndo() { if (undoHistory.length === 0) { - toast.info("没有可撤销的操作"); + toast.info(t("fileManager.noUndoableActions")); return; } @@ -860,14 +860,14 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { // 移除最后一个撤销记录 setUndoHistory((prev) => prev.slice(0, -1)); toast.success( - `已撤销复制操作:删除了 ${successCount} 个复制的文件`, + t("fileManager.undoCopySuccess", { count: successCount }), ); } else { - toast.error("撤销失败:无法删除任何复制的文件"); + toast.error(t("fileManager.undoCopyFailedDelete")); return; } } else { - toast.error("撤销失败:找不到复制的文件信息"); + toast.error(t("fileManager.undoCopyFailedNoInfo")); return; } break; @@ -902,34 +902,34 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { // 移除最后一个撤销记录 setUndoHistory((prev) => prev.slice(0, -1)); toast.success( - `已撤销移动操作:移回了 ${successCount} 个文件到原位置`, + t("fileManager.undoMoveSuccess", { count: successCount }), ); } else { - toast.error("撤销失败:无法移回任何文件"); + toast.error(t("fileManager.undoMoveFailedMove")); return; } } else { - toast.error("撤销失败:找不到移动的文件信息"); + toast.error(t("fileManager.undoMoveFailedNoInfo")); return; } break; case "delete": // 删除操作无法真正撤销(文件已从服务器删除) - toast.info("删除操作无法撤销:文件已从服务器永久删除"); + toast.info(t("fileManager.undoDeleteNotSupported")); // 仍然移除历史记录,因为用户已经知道了这个限制 setUndoHistory((prev) => prev.slice(0, -1)); return; default: - toast.error("不支持撤销此类操作"); + toast.error(t("fileManager.undoTypeNotSupported")); return; } // 刷新文件列表 handleRefreshDirectory(); } catch (error: any) { - toast.error(`撤销操作失败: ${error.message || "Unknown error"}`); + toast.error(`${t("fileManager.undoOperationFailed")}: ${error.message || t("fileManager.unknownError")}`); console.error("Undo failed:", error); } } @@ -1117,7 +1117,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } } catch (error: any) { console.error(`Failed to move file ${file.name}:`, error); - toast.error(`移动 ${file.name} 失败: ${error.message}`); + toast.error(t("fileManager.moveFileFailed", { name: file.name }) + ": " + error.message); } } @@ -1156,14 +1156,14 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } } catch (error: any) { console.error("Drag move operation failed:", error); - toast.error(`移动操作失败: ${error.message}`); + toast.error(t("fileManager.moveOperationFailed") + ": " + error.message); } } // 拖拽处理:文件拖到文件 = diff对比操作 function handleFileDiff(file1: FileItem, file2: FileItem) { if (file1.type !== "file" || file2.type !== "file") { - toast.error("只能对比两个文件"); + toast.error(t("fileManager.canOnlyCompareFiles")); return; } @@ -1202,7 +1202,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { zIndex: Date.now(), }); - toast.success(`正在对比文件: ${file1.name} 与 ${file2.name}`); + toast.success(t("fileManager.comparingFiles", { file1: file1.name, file2: file2.name })); } // 拖拽到桌面处理函数 @@ -1234,7 +1234,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } } catch (error: any) { console.error("拖拽到桌面失败:", error); - toast.error(`拖拽失败: ${error.message || "未知错误"}`); + toast.error(t("fileManager.dragFailed") + ": " + (error.message || t("fileManager.unknownError"))); } } @@ -1344,10 +1344,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { await addPinnedFile(currentHost.id, file.path, file.name); setPinnedFiles((prev) => new Set([...prev, file.path])); setSidebarRefreshTrigger((prev) => prev + 1); // 触发侧边栏刷新 - toast.success(`文件"${file.name}"已固定`); + toast.success(t("fileManager.filePinnedSuccessfully", { name: file.name })); } catch (error) { console.error("Failed to pin file:", error); - toast.error("固定文件失败"); + toast.error(t("fileManager.pinFileFailed")); } } @@ -1363,10 +1363,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { return newSet; }); setSidebarRefreshTrigger((prev) => prev + 1); // 触发侧边栏刷新 - toast.success(`文件"${file.name}"已取消固定`); + toast.success(t("fileManager.fileUnpinnedSuccessfully", { name: file.name })); } catch (error) { console.error("Failed to unpin file:", error); - toast.error("取消固定失败"); + toast.error(t("fileManager.unpinFileFailed")); } } @@ -1378,10 +1378,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { const folderName = path.split("/").pop() || path; await addFolderShortcut(currentHost.id, path, folderName); setSidebarRefreshTrigger((prev) => prev + 1); // 触发侧边栏刷新 - toast.success(`文件夹快捷方式"${folderName}"已添加`); + toast.success(t("fileManager.shortcutAddedSuccessfully", { name: folderName })); } catch (error) { console.error("Failed to add shortcut:", error); - toast.error("添加快捷方式失败"); + toast.error(t("fileManager.addShortcutFailed")); } } @@ -1613,6 +1613,7 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { onCut={handleCutFiles} onPaste={handlePasteFiles} onUndo={handleUndo} + hasClipboard={!!clipboard} onFileDrop={handleFileDrop} onFileDiff={handleFileDiff} onSystemDragStart={handleFileDragStart} diff --git a/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx index 800ea87b..40484cb8 100644 --- a/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/DiffViewer.tsx @@ -139,11 +139,11 @@ export function DiffViewer({ document.body.removeChild(link); URL.revokeObjectURL(url); - toast.success(`文件下载成功: ${file.name}`); + toast.success(t("fileManager.downloadFileSuccess", { name: file.name })); } } catch (error: any) { console.error("Failed to download file:", error); - toast.error(`下载失败: ${error.message || "未知错误"}`); + toast.error(t("fileManager.downloadFileFailed") + ": " + (error.message || t("fileManager.unknownError"))); } }; diff --git a/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx b/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx index 4ad2bc24..89e42f02 100644 --- a/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/DraggableWindow.tsx @@ -221,7 +221,7 @@ export function DraggableWindow({ e.stopPropagation(); onMinimize(); }} - title="最小化" + title={t("common.minimize")} > @@ -250,7 +250,7 @@ export function DraggableWindow({ e.stopPropagation(); onClose(); }} - title="关闭" + title={t("common.close")} > diff --git a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx index 48147558..878d6a91 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx @@ -294,9 +294,9 @@ export function FileViewer({ const fileTypeInfo = getFileType(file.name); - // 文件大小限制 (1MB for warning, 10MB for hard limit) - const WARNING_SIZE = 1024 * 1024; // 1MB - const MAX_SIZE = 10 * 1024 * 1024; // 10MB + // 文件大小限制 - 移除硬限制,支持大文件处理 + const WARNING_SIZE = 50 * 1024 * 1024; // 50MB 警告 + const MAX_SIZE = Number.MAX_SAFE_INTEGER; // 移除硬限制 // 检查是否应该显示为文本 const shouldShowAsText = @@ -580,7 +580,7 @@ export function FileViewer({
{ setSearchText(e.target.value); @@ -629,7 +629,7 @@ export function FileViewer({ {showReplacePanel && (
setReplaceText(e.target.value)} className="w-48 h-8" @@ -805,7 +805,7 @@ export function FileViewer({ value={editedContent} onChange={(e) => handleContentChange(e.target.value)} className="w-full h-full p-4 border-none resize-none outline-none font-mono text-sm overflow-auto bg-background text-foreground" - placeholder="Start typing..." + placeholder={t("fileManager.startTyping")} spellCheck={false} /> )} diff --git a/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx b/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx index 521f4c2d..e06eeba6 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileWindow.tsx @@ -223,7 +223,7 @@ export function FileWindow({ autoSaveTimerRef.current = null; } - toast.success("File saved successfully"); + toast.success(t("fileManager.fileSavedSuccessfully")); } catch (error: any) { console.error("Failed to save file:", error); @@ -236,7 +236,7 @@ export function FileWindow({ `SSH connection failed. Please check your connection to ${sshHost.name} (${sshHost.ip}:${sshHost.port})`, ); } else { - toast.error(`Failed to save file: ${error.message || "Unknown error"}`); + toast.error(`${t("fileManager.failedToSaveFile")}: ${error.message || t("fileManager.unknownError")}`); } } finally { setIsLoading(false); @@ -257,10 +257,10 @@ export function FileWindow({ try { console.log("Auto-saving file..."); await handleSave(newContent); - toast.success("File auto-saved"); + toast.success(t("fileManager.fileAutoSaved")); } catch (error) { console.error("Auto-save failed:", error); - toast.error("Auto-save failed"); + toast.error(t("fileManager.autoSaveFailed")); } }, 60000); // 1分钟 = 60000毫秒 }; @@ -303,7 +303,7 @@ export function FileWindow({ document.body.removeChild(link); URL.revokeObjectURL(url); - toast.success("File downloaded successfully"); + toast.success(t("fileManager.fileDownloadedSuccessfully")); } } catch (error: any) { console.error("Failed to download file:", error); diff --git a/src/ui/Desktop/Apps/File Manager/hooks/useDragAndDrop.ts b/src/ui/Desktop/Apps/File Manager/hooks/useDragAndDrop.ts index 31267ca2..77122eb6 100644 --- a/src/ui/Desktop/Apps/File Manager/hooks/useDragAndDrop.ts +++ b/src/ui/Desktop/Apps/File Manager/hooks/useDragAndDrop.ts @@ -16,7 +16,7 @@ interface UseDragAndDropProps { export function useDragAndDrop({ onFilesDropped, onError, - maxFileSize = 100, // 100MB default + maxFileSize = 5120, // 5GB default - much more reasonable allowedTypes = [], // empty means all types allowed }: UseDragAndDropProps) { const [state, setState] = useState({ diff --git a/src/ui/Desktop/Apps/Terminal/Terminal.tsx b/src/ui/Desktop/Apps/Terminal/Terminal.tsx index 42fc6880..b8e3fb1c 100644 --- a/src/ui/Desktop/Apps/Terminal/Terminal.tsx +++ b/src/ui/Desktop/Apps/Terminal/Terminal.tsx @@ -139,10 +139,7 @@ export const Terminal = forwardRef(function SSHTerminal( [terminal], ); - useEffect(() => { - window.addEventListener("resize", handleWindowResize); - return () => window.removeEventListener("resize", handleWindowResize); - }, []); + // Resize handling moved to AppView to avoid conflicts - Linus principle: eliminate duplicate complexity function handleWindowResize() { if (!isVisibleRef.current) return; @@ -515,33 +512,35 @@ export const Terminal = forwardRef(function SSHTerminal( fitAddonRef.current?.fit(); if (terminal) scheduleNotify(terminal.cols, terminal.rows); hardRefresh(); - }, 100); + }, 150); // Increased debounce for better stability }); resizeObserver.observe(xtermRef.current); + // Show terminal immediately - better UX, no unnecessary delays + setVisible(true); + const readyFonts = (document as any).fonts?.ready instanceof Promise ? (document as any).fonts.ready : Promise.resolve(); + readyFonts.then(() => { + // Reduced delay - Linus principle: eliminate unnecessary waiting setTimeout(() => { fitAddon.fit(); - setTimeout(() => { - fitAddon.fit(); - if (terminal) scheduleNotify(terminal.cols, terminal.rows); - hardRefresh(); - setVisible(true); - if (terminal && !splitScreen) { - terminal.focus(); - } - }, 0); + if (terminal) scheduleNotify(terminal.cols, terminal.rows); + hardRefresh(); + + if (terminal && !splitScreen) { + terminal.focus(); + } const cols = terminal.cols; const rows = terminal.rows; connectToHost(cols, rows); - }, 300); + }, 100); // Reduced from 300ms to 100ms }); return () => { diff --git a/src/ui/Mobile/Apps/Terminal/Terminal.tsx b/src/ui/Mobile/Apps/Terminal/Terminal.tsx index ce7bf874..b69a8f1f 100644 --- a/src/ui/Mobile/Apps/Terminal/Terminal.tsx +++ b/src/ui/Mobile/Apps/Terminal/Terminal.tsx @@ -103,10 +103,7 @@ export const Terminal = forwardRef(function SSHTerminal( [terminal], ); - useEffect(() => { - window.addEventListener("resize", handleWindowResize); - return () => window.removeEventListener("resize", handleWindowResize); - }, []); + // Resize handling optimized to avoid conflicts - Linus principle: eliminate duplicate complexity function handleWindowResize() { if (!isVisibleRef.current) return; @@ -215,7 +212,7 @@ export const Terminal = forwardRef(function SSHTerminal( fitAddonRef.current?.fit(); if (terminal) scheduleNotify(terminal.cols, terminal.rows); hardRefresh(); - }, 100); + }, 150); // Increased debounce for better stability }); resizeObserver.observe(xtermRef.current); @@ -224,15 +221,15 @@ export const Terminal = forwardRef(function SSHTerminal( (document as any).fonts?.ready instanceof Promise ? (document as any).fonts.ready : Promise.resolve(); + // Show terminal immediately - better UX for mobile + setVisible(true); + readyFonts.then(() => { + // Reduced delay - Linus principle: eliminate unnecessary waiting setTimeout(() => { fitAddon.fit(); - setTimeout(() => { - fitAddon.fit(); - if (terminal) scheduleNotify(terminal.cols, terminal.rows); - hardRefresh(); - setVisible(true); - }, 0); + if (terminal) scheduleNotify(terminal.cols, terminal.rows); + hardRefresh(); const cols = terminal.cols; const rows = terminal.rows; @@ -263,7 +260,7 @@ export const Terminal = forwardRef(function SSHTerminal( wasDisconnectedBySSH.current = false; setupWebSocketListeners(ws, cols, rows); - }, 300); + }, 100); // Reduced from 300ms to 100ms }); return () => {