/* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, useEffect } from "react"; import { DiffEditor } from "@monaco-editor/react"; import { Button } from "@/components/button.tsx"; import { toast } from "sonner"; import { useTranslation } from "react-i18next"; import { Download, RefreshCw, Eye, EyeOff, ArrowLeftRight, FileText, } from "lucide-react"; import { readSSHFile, downloadSSHFile, getSSHStatus, connectSSH, } from "@/main-axios.ts"; import type { FileItem, SSHHost } from "@/types"; interface DiffViewerProps { file1: FileItem; file2: FileItem; sshSessionId: string; sshHost: SSHHost; onDownload1?: () => void; onDownload2?: () => void; } export function DiffViewer({ file1, file2, sshSessionId, sshHost, }: DiffViewerProps) { const { t } = useTranslation(); const [content1, setContent1] = useState(""); const [content2, setContent2] = useState(""); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [diffMode, setDiffMode] = useState<"side-by-side" | "inline">( "side-by-side", ); const [showLineNumbers, setShowLineNumbers] = useState(true); const ensureSSHConnection = async () => { try { const status = await getSSHStatus(sshSessionId); if (!status.connected) { await connectSSH(sshSessionId, { hostId: sshHost.id, ip: sshHost.ip, port: sshHost.port, username: sshHost.username, password: sshHost.password, sshKey: sshHost.key, keyPassword: sshHost.keyPassword, authType: sshHost.authType, credentialId: sshHost.credentialId, userId: sshHost.userId, }); } } catch { await connectSSH(sshSessionId, { hostId: sshHost.id, ip: sshHost.ip, port: sshHost.port, username: sshHost.username, password: sshHost.password, sshKey: sshHost.key, keyPassword: sshHost.keyPassword, authType: sshHost.authType, credentialId: sshHost.credentialId, userId: sshHost.userId, }); } }; const loadFileContents = async () => { if (file1.type !== "file" || file2.type !== "file") { setError(t("fileManager.canOnlyCompareFiles")); return; } try { setIsLoading(true); setError(null); await ensureSSHConnection(); const [response1, response2] = await Promise.all([ readSSHFile(sshSessionId, file1.path), readSSHFile(sshSessionId, file2.path), ]); setContent1(response1.content || ""); setContent2(response2.content || ""); } catch (error: unknown) { console.error("Failed to load files for diff:", error); const err = error as { message?: string; response?: { data?: { tooLarge?: boolean; error?: string } }; }; const errorData = err?.response?.data; if (errorData?.tooLarge) { setError(t("fileManager.fileTooLarge", { error: errorData.error })); } else if ( err.message?.includes("connection") || err.message?.includes("established") ) { setError( t("fileManager.sshConnectionFailed", { name: sshHost.name, ip: sshHost.ip, port: sshHost.port, }), ); } else { setError( t("fileManager.loadFileFailed", { error: err.message || errorData?.error || t("fileManager.unknownError"), }), ); } } finally { setIsLoading(false); } }; const handleDownloadFile = async (file: FileItem) => { try { await ensureSSHConnection(); const response = await downloadSSHFile(sshSessionId, file.path); if (response?.content) { const byteCharacters = atob(response.content); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); const blob = new Blob([byteArray], { type: response.mimeType || "application/octet-stream", }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = response.fileName || file.name; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); toast.success( t("fileManager.downloadFileSuccess", { name: file.name }), ); } } catch (error: unknown) { console.error("Failed to download file:", error); const err = error as { message?: string }; toast.error( t("fileManager.downloadFileFailed") + ": " + (err.message || t("fileManager.unknownError")), ); } }; const getFileLanguage = (fileName: string): string => { const ext = fileName.split(".").pop()?.toLowerCase(); const languageMap: Record = { js: "javascript", jsx: "javascript", ts: "typescript", tsx: "typescript", py: "python", java: "java", c: "c", cpp: "cpp", cs: "csharp", php: "php", rb: "ruby", go: "go", rs: "rust", html: "html", css: "css", scss: "scss", less: "less", json: "json", xml: "xml", yaml: "yaml", yml: "yaml", md: "markdown", sql: "sql", sh: "shell", bash: "shell", ps1: "powershell", dockerfile: "dockerfile", }; return languageMap[ext || ""] || "plaintext"; }; useEffect(() => { loadFileContents(); }, [file1, file2, sshSessionId]); if (isLoading) { return (

{t("fileManager.loadingFileComparison")}

); } if (error) { return (

{error}

); } return (
{t("fileManager.compare")}: {file1.name} {file2.name}

{t("fileManager.initializingEditor")}

} />
); }