refactor: update network tool components for improved user experience
- Translated UI text in DNS, Ping, TCPing, and Speed Test components from Chinese to English for better accessibility. - Enhanced user prompts and labels for clarity, including error messages and button texts. - Updated comments in the code to reflect the changes in language and improve code readability.
This commit is contained in:
		| @@ -18,16 +18,16 @@ interface DNSResponse { | |||||||
| } | } | ||||||
|  |  | ||||||
| const DNS_RECORD_TYPES = [ | const DNS_RECORD_TYPES = [ | ||||||
|   { value: "1", label: "A", description: "IPv4 地址" }, |   { value: "1", label: "A", description: "IPv4 Address" }, | ||||||
|   { value: "28", label: "AAAA", description: "IPv6 地址" }, |   { value: "28", label: "AAAA", description: "IPv6 Address" }, | ||||||
|   { value: "5", label: "CNAME", description: "规范名称" }, |   { value: "5", label: "CNAME", description: "Canonical Name" }, | ||||||
|   { value: "15", label: "MX", description: "邮件交换" }, |   { value: "15", label: "MX", description: "Mail Exchange" }, | ||||||
|   { value: "2", label: "NS", description: "名称服务器" }, |   { value: "2", label: "NS", description: "Name Server" }, | ||||||
|   { value: "16", label: "TXT", description: "文本记录" }, |   { value: "16", label: "TXT", description: "Text Record" }, | ||||||
|   { value: "6", label: "SOA", description: "授权起始" }, |   { value: "6", label: "SOA", description: "Start of Authority" }, | ||||||
|   { value: "257", label: "CAA", description: "证书颁发机构授权" }, |   { value: "257", label: "CAA", description: "Certification Authority Authorization" }, | ||||||
|   { value: "12", label: "PTR", description: "指针记录" }, |   { value: "12", label: "PTR", description: "Pointer Record" }, | ||||||
|   { value: "33", label: "SRV", description: "服务记录" }, |   { value: "33", label: "SRV", description: "Service Record" }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const getRecordTypeName = (type: number): string => { | const getRecordTypeName = (type: number): string => { | ||||||
| @@ -43,7 +43,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|   const queryDNS = async () => { |   const queryDNS = async () => { | ||||||
|     if (!domain.trim()) { |     if (!domain.trim()) { | ||||||
|       toast.error("请输入域名"); |       toast.error("Please enter a domain name"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -54,7 +54,7 @@ const Tool: FC = () => { | |||||||
|     const startTime = performance.now(); |     const startTime = performance.now(); | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       // 并发查询所有记录类型 |       // Query all record types concurrently | ||||||
|       const queries = DNS_RECORD_TYPES.map((recordType) => |       const queries = DNS_RECORD_TYPES.map((recordType) => | ||||||
|         fetch( |         fetch( | ||||||
|           `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent( |           `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent( | ||||||
| @@ -80,11 +80,11 @@ const Tool: FC = () => { | |||||||
|       const endTime = performance.now(); |       const endTime = performance.now(); | ||||||
|       setQueryTime(endTime - startTime); |       setQueryTime(endTime - startTime); | ||||||
|  |  | ||||||
|       // 合并所有结果并去重 |       // Merge and deduplicate results | ||||||
|       const combinedResults = allResults.flat(); |       const combinedResults = allResults.flat(); | ||||||
|        |        | ||||||
|       if (combinedResults.length > 0) { |       if (combinedResults.length > 0) { | ||||||
|         // 按记录类型分组并去重 |         // Group by record type and deduplicate | ||||||
|         const uniqueResults = Array.from( |         const uniqueResults = Array.from( | ||||||
|           new Map( |           new Map( | ||||||
|             combinedResults.map((record) => [ |             combinedResults.map((record) => [ | ||||||
| @@ -95,16 +95,16 @@ const Tool: FC = () => { | |||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         setResults(uniqueResults); |         setResults(uniqueResults); | ||||||
|         toast.success(`查询成功,找到 ${uniqueResults.length} 条记录`); |         toast.success(`Query successful, found ${uniqueResults.length} record(s)`); | ||||||
|       } else { |       } else { | ||||||
|         setResults([]); |         setResults([]); | ||||||
|         toast.info("未找到记录"); |         toast.info("No records found"); | ||||||
|       } |       } | ||||||
|     } catch (error: unknown) { |     } catch (error: unknown) { | ||||||
|       if (error instanceof Error) { |       if (error instanceof Error) { | ||||||
|         toast.error(`查询失败: ${error.message}`); |         toast.error(`Query failed: ${error.message}`); | ||||||
|       } else { |       } else { | ||||||
|         toast.error("查询失败"); |         toast.error("Query failed"); | ||||||
|       } |       } | ||||||
|       setResults([]); |       setResults([]); | ||||||
|     } finally { |     } finally { | ||||||
| @@ -122,34 +122,34 @@ const Tool: FC = () => { | |||||||
|     <div className="flex flex-col gap-4 h-full"> |     <div className="flex flex-col gap-4 h-full"> | ||||||
|       <div className="flex flex-col gap-4"> |       <div className="flex flex-col gap-4"> | ||||||
|         <div className="flex flex-col gap-2"> |         <div className="flex flex-col gap-2"> | ||||||
|           <label className="text-sm font-medium">域名</label> |           <label className="text-sm font-medium">Domain Name</label> | ||||||
|           <Input |           <Input | ||||||
|             placeholder="例如: example.com" |             placeholder="e.g. example.com" | ||||||
|             value={domain} |             value={domain} | ||||||
|             onChange={(e) => setDomain(e.target.value)} |             onChange={(e) => setDomain(e.target.value)} | ||||||
|             onKeyPress={handleKeyPress} |             onKeyPress={handleKeyPress} | ||||||
|             disabled={loading} |             disabled={loading} | ||||||
|           /> |           /> | ||||||
|           <span className="text-xs text-muted-foreground"> |           <span className="text-xs text-muted-foreground"> | ||||||
|             将自动查询所有类型的 DNS 记录 |             Will automatically query all DNS record types | ||||||
|           </span> |           </span> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <Button onClick={queryDNS} disabled={loading} className="w-full"> |         <Button onClick={queryDNS} disabled={loading} className="w-full"> | ||||||
|           {loading && <Loader2 className="mr-2 size-4 animate-spin" />} |           {loading && <Loader2 className="mr-2 size-4 animate-spin" />} | ||||||
|           {loading ? "查询中..." : "查询所有记录"} |           {loading ? "Querying..." : "Query All Records"} | ||||||
|         </Button> |         </Button> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       {queryTime > 0 && ( |       {queryTime > 0 && ( | ||||||
|         <div className="text-sm text-muted-foreground"> |         <div className="text-sm text-muted-foreground"> | ||||||
|           查询耗时: {queryTime.toFixed(2)} ms |           Query time: {queryTime.toFixed(2)} ms | ||||||
|         </div> |         </div> | ||||||
|       )} |       )} | ||||||
|  |  | ||||||
|       {results.length > 0 && ( |       {results.length > 0 && ( | ||||||
|         <div className="flex flex-col gap-3 flex-1 overflow-auto"> |         <div className="flex flex-col gap-3 flex-1 overflow-auto"> | ||||||
|           <div className="text-sm font-medium">查询结果:</div> |           <div className="text-sm font-medium">Query Results:</div> | ||||||
|           <div className="space-y-2"> |           <div className="space-y-2"> | ||||||
|             {results.map((record, index) => ( |             {results.map((record, index) => ( | ||||||
|               <div |               <div | ||||||
| @@ -157,13 +157,13 @@ const Tool: FC = () => { | |||||||
|                 className="border rounded-md p-3 bg-card text-card-foreground" |                 className="border rounded-md p-3 bg-card text-card-foreground" | ||||||
|               > |               > | ||||||
|                 <div className="grid grid-cols-2 gap-2 text-sm"> |                 <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|                   <div className="text-muted-foreground">名称:</div> |                   <div className="text-muted-foreground">Name:</div> | ||||||
|                   <div className="font-mono break-all">{record.name}</div> |                   <div className="font-mono break-all">{record.name}</div> | ||||||
|                   <div className="text-muted-foreground">类型:</div> |                   <div className="text-muted-foreground">Type:</div> | ||||||
|                   <div>{getRecordTypeName(record.type)}</div> |                   <div>{getRecordTypeName(record.type)}</div> | ||||||
|                   <div className="text-muted-foreground">TTL:</div> |                   <div className="text-muted-foreground">TTL:</div> | ||||||
|                   <div>{record.TTL} 秒</div> |                   <div>{record.TTL} seconds</div> | ||||||
|                   <div className="text-muted-foreground">数据:</div> |                   <div className="text-muted-foreground">Data:</div> | ||||||
|                   <div className="font-mono break-all">{record.data}</div> |                   <div className="font-mono break-all">{record.data}</div> | ||||||
|                 </div> |                 </div> | ||||||
|               </div> |               </div> | ||||||
|   | |||||||
| @@ -38,14 +38,14 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|   const ping = async () => { |   const ping = async () => { | ||||||
|     if (!url.trim()) { |     if (!url.trim()) { | ||||||
|       toast.error("请输入 URL"); |       toast.error("Please enter a URL"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const seq = ++seqRef.current; |     const seq = ++seqRef.current; | ||||||
|     let targetUrl = url.trim(); |     let targetUrl = url.trim(); | ||||||
|  |  | ||||||
|     // 如果没有协议前缀,默认使用 https:// |     // If no protocol prefix, default to https:// | ||||||
|     if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) { |     if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) { | ||||||
|       targetUrl = `https://${targetUrl}`; |       targetUrl = `https://${targetUrl}`; | ||||||
|     } |     } | ||||||
| @@ -75,7 +75,7 @@ const Tool: FC = () => { | |||||||
|       const time = endTime - startTime; |       const time = endTime - startTime; | ||||||
|  |  | ||||||
|       const errorMessage = |       const errorMessage = | ||||||
|         error instanceof Error ? error.message : "请求失败"; |         error instanceof Error ? error.message : "Request failed"; | ||||||
|  |  | ||||||
|       const newResult: PingResult = { |       const newResult: PingResult = { | ||||||
|         seq, |         seq, | ||||||
| @@ -117,7 +117,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|   const startPing = () => { |   const startPing = () => { | ||||||
|     if (!url.trim()) { |     if (!url.trim()) { | ||||||
|       toast.error("请输入 URL"); |       toast.error("Please enter a URL"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -133,10 +133,10 @@ const Tool: FC = () => { | |||||||
|     }); |     }); | ||||||
|     seqRef.current = 0; |     seqRef.current = 0; | ||||||
|  |  | ||||||
|     // 立即执行第一次 ping |     // Execute first ping immediately | ||||||
|     ping(); |     ping(); | ||||||
|  |  | ||||||
|     // 然后每秒执行一次 |     // Then execute every second | ||||||
|     intervalRef.current = window.setInterval(ping, 1000); |     intervalRef.current = window.setInterval(ping, 1000); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -149,7 +149,7 @@ const Tool: FC = () => { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     // 自动滚动到底部 |     // Auto-scroll to bottom | ||||||
|     if (resultsContainerRef.current) { |     if (resultsContainerRef.current) { | ||||||
|       resultsContainerRef.current.scrollTop = |       resultsContainerRef.current.scrollTop = | ||||||
|         resultsContainerRef.current.scrollHeight; |         resultsContainerRef.current.scrollHeight; | ||||||
| @@ -157,7 +157,7 @@ const Tool: FC = () => { | |||||||
|   }, [results]); |   }, [results]); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     // 清理定时器 |     // Cleanup timer | ||||||
|     return () => { |     return () => { | ||||||
|       if (intervalRef.current) { |       if (intervalRef.current) { | ||||||
|         clearInterval(intervalRef.current); |         clearInterval(intervalRef.current); | ||||||
| @@ -172,9 +172,9 @@ const Tool: FC = () => { | |||||||
|     <div className="flex flex-col gap-4 h-full"> |     <div className="flex flex-col gap-4 h-full"> | ||||||
|       <div className="flex flex-col gap-4"> |       <div className="flex flex-col gap-4"> | ||||||
|         <div className="flex flex-col gap-2"> |         <div className="flex flex-col gap-2"> | ||||||
|           <label className="text-sm font-medium">目标 URL 或 IP</label> |           <label className="text-sm font-medium">Target URL or IP</label> | ||||||
|           <Input |           <Input | ||||||
|             placeholder="例如: example.com 或 https://example.com" |             placeholder="e.g. example.com or https://example.com" | ||||||
|             value={url} |             value={url} | ||||||
|             onChange={(e) => setUrl(e.target.value)} |             onChange={(e) => setUrl(e.target.value)} | ||||||
|             disabled={running} |             disabled={running} | ||||||
| @@ -184,11 +184,11 @@ const Tool: FC = () => { | |||||||
|         <div className="flex gap-2"> |         <div className="flex gap-2"> | ||||||
|           {!running ? ( |           {!running ? ( | ||||||
|             <Button onClick={startPing} className="flex-1"> |             <Button onClick={startPing} className="flex-1"> | ||||||
|               开始 Ping |               Start Ping | ||||||
|             </Button> |             </Button> | ||||||
|           ) : ( |           ) : ( | ||||||
|             <Button onClick={stopPing} variant="destructive" className="flex-1"> |             <Button onClick={stopPing} variant="destructive" className="flex-1"> | ||||||
|               停止 |               Stop | ||||||
|             </Button> |             </Button> | ||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
| @@ -196,23 +196,23 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|       {stats.sent > 0 && ( |       {stats.sent > 0 && ( | ||||||
|         <div className="border rounded-md p-3 bg-card text-card-foreground"> |         <div className="border rounded-md p-3 bg-card text-card-foreground"> | ||||||
|           <div className="text-sm font-medium mb-2">统计信息</div> |           <div className="text-sm font-medium mb-2">Statistics</div> | ||||||
|           <div className="grid grid-cols-2 gap-2 text-sm"> |           <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|             <div className="text-muted-foreground">已发送:</div> |             <div className="text-muted-foreground">Sent:</div> | ||||||
|             <div>{stats.sent} 包</div> |             <div>{stats.sent} packets</div> | ||||||
|             <div className="text-muted-foreground">已接收:</div> |             <div className="text-muted-foreground">Received:</div> | ||||||
|             <div>{stats.received} 包</div> |             <div>{stats.received} packets</div> | ||||||
|             <div className="text-muted-foreground">丢失:</div> |             <div className="text-muted-foreground">Lost:</div> | ||||||
|             <div> |             <div> | ||||||
|               {stats.lost} 包 ({lossRate}%) |               {stats.lost} packets ({lossRate}%) | ||||||
|             </div> |             </div> | ||||||
|             {stats.received > 0 && ( |             {stats.received > 0 && ( | ||||||
|               <> |               <> | ||||||
|                 <div className="text-muted-foreground">最小延迟:</div> |                 <div className="text-muted-foreground">Min Latency:</div> | ||||||
|                 <div>{stats.min.toFixed(2)} ms</div> |                 <div>{stats.min.toFixed(2)} ms</div> | ||||||
|                 <div className="text-muted-foreground">最大延迟:</div> |                 <div className="text-muted-foreground">Max Latency:</div> | ||||||
|                 <div>{stats.max.toFixed(2)} ms</div> |                 <div>{stats.max.toFixed(2)} ms</div> | ||||||
|                 <div className="text-muted-foreground">平均延迟:</div> |                 <div className="text-muted-foreground">Avg Latency:</div> | ||||||
|                 <div>{stats.avg.toFixed(2)} ms</div> |                 <div>{stats.avg.toFixed(2)} ms</div> | ||||||
|               </> |               </> | ||||||
|             )} |             )} | ||||||
| @@ -222,7 +222,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|       {results.length > 0 && ( |       {results.length > 0 && ( | ||||||
|         <div className="flex flex-col gap-2 flex-1 overflow-hidden"> |         <div className="flex flex-col gap-2 flex-1 overflow-hidden"> | ||||||
|           <div className="text-sm font-medium">Ping 结果:</div> |           <div className="text-sm font-medium">Ping Results:</div> | ||||||
|           <div |           <div | ||||||
|             ref={resultsContainerRef} |             ref={resultsContainerRef} | ||||||
|             className="flex-1 overflow-auto space-y-1 font-mono text-sm border rounded-md p-3 bg-card" |             className="flex-1 overflow-auto space-y-1 font-mono text-sm border rounded-md p-3 bg-card" | ||||||
| @@ -238,7 +238,7 @@ const Tool: FC = () => { | |||||||
|                   </> |                   </> | ||||||
|                 ) : ( |                 ) : ( | ||||||
|                   <> |                   <> | ||||||
|                     seq={result.seq} 请求超时 |                     seq={result.seq} Request timeout | ||||||
|                     {result.error && ` (${result.error})`} |                     {result.error && ` (${result.error})`} | ||||||
|                   </> |                   </> | ||||||
|                 )} |                 )} | ||||||
| @@ -247,7 +247,7 @@ const Tool: FC = () => { | |||||||
|             {running && ( |             {running && ( | ||||||
|               <div className="flex items-center gap-2 text-muted-foreground"> |               <div className="flex items-center gap-2 text-muted-foreground"> | ||||||
|                 <Loader2 className="size-3 animate-spin" /> |                 <Loader2 className="size-3 animate-spin" /> | ||||||
|                 运行中... |                 Running... | ||||||
|               </div> |               </div> | ||||||
|             )} |             )} | ||||||
|           </div> |           </div> | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|         return { performance: metrics }; |         return { performance: metrics }; | ||||||
|       } else { |       } else { | ||||||
|         // 如果没有详细的性能数据,只返回总时间 |         // If no detailed performance data, only return total time | ||||||
|         return { |         return { | ||||||
|           performance: { |           performance: { | ||||||
|             dns: 0, |             dns: 0, | ||||||
| @@ -144,13 +144,13 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|   const startTest = async () => { |   const startTest = async () => { | ||||||
|     if (!url.trim()) { |     if (!url.trim()) { | ||||||
|       toast.error("请输入 URL"); |       toast.error("Please enter a URL"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let targetUrl = url.trim(); |     let targetUrl = url.trim(); | ||||||
|  |  | ||||||
|     // 如果没有协议前缀,默认使用 https:// |     // If no protocol prefix, default to https:// | ||||||
|     if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) { |     if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) { | ||||||
|       targetUrl = `https://${targetUrl}`; |       targetUrl = `https://${targetUrl}`; | ||||||
|     } |     } | ||||||
| @@ -164,24 +164,24 @@ const Tool: FC = () => { | |||||||
|       switch (testType) { |       switch (testType) { | ||||||
|         case "performance": |         case "performance": | ||||||
|           testResult = await testPerformance(targetUrl); |           testResult = await testPerformance(targetUrl); | ||||||
|           toast.success("性能测试完成"); |           toast.success("Performance test completed"); | ||||||
|           break; |           break; | ||||||
|         case "download": |         case "download": | ||||||
|           testResult = await testDownloadSpeed(targetUrl); |           testResult = await testDownloadSpeed(targetUrl); | ||||||
|           toast.success("下载速度测试完成"); |           toast.success("Download speed test completed"); | ||||||
|           break; |           break; | ||||||
|         case "upload": |         case "upload": | ||||||
|           testResult = await testUploadSpeed(targetUrl); |           testResult = await testUploadSpeed(targetUrl); | ||||||
|           toast.success("上传速度测试完成"); |           toast.success("Upload speed test completed"); | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       setResult(testResult); |       setResult(testResult); | ||||||
|     } catch (error: unknown) { |     } catch (error: unknown) { | ||||||
|       if (error instanceof Error) { |       if (error instanceof Error) { | ||||||
|         toast.error(`测试失败: ${error.message}`); |         toast.error(`Test failed: ${error.message}`); | ||||||
|       } else { |       } else { | ||||||
|         toast.error("测试失败"); |         toast.error("Test failed"); | ||||||
|       } |       } | ||||||
|     } finally { |     } finally { | ||||||
|       setTesting(false); |       setTesting(false); | ||||||
| @@ -198,24 +198,24 @@ const Tool: FC = () => { | |||||||
|     <div className="flex flex-col gap-4 h-full"> |     <div className="flex flex-col gap-4 h-full"> | ||||||
|       <div className="border rounded-md p-3 bg-yellow-500/10 border-yellow-500/50"> |       <div className="border rounded-md p-3 bg-yellow-500/10 border-yellow-500/50"> | ||||||
|         <div className="text-sm font-medium text-yellow-600 dark:text-yellow-400 mb-1"> |         <div className="text-sm font-medium text-yellow-600 dark:text-yellow-400 mb-1"> | ||||||
|           ⚠️ CORS 限制说明 |           ⚠️ CORS Restrictions | ||||||
|         </div> |         </div> | ||||||
|         <div className="text-xs text-muted-foreground space-y-1"> |         <div className="text-xs text-muted-foreground space-y-1"> | ||||||
|           <p>由于浏览器的 CORS 安全策略,部分网站可能无法直接测试。</p> |           <p>Due to browser CORS security policies, some websites cannot be tested directly.</p> | ||||||
|           <p>建议测试以下类型的网站:</p> |           <p>Recommended websites to test:</p> | ||||||
|           <ul className="list-disc list-inside ml-2"> |           <ul className="list-disc list-inside ml-2"> | ||||||
|             <li>支持 CORS 的公共 API</li> |             <li>Public APIs with CORS support</li> | ||||||
|             <li>您自己控制的网站(可配置 CORS 头)</li> |             <li>Your own websites (with configured CORS headers)</li> | ||||||
|             <li>使用 CORS 代理服务</li> |             <li>Using CORS proxy services</li> | ||||||
|           </ul> |           </ul> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <div className="flex flex-col gap-4"> |       <div className="flex flex-col gap-4"> | ||||||
|         <div className="flex flex-col gap-2"> |         <div className="flex flex-col gap-2"> | ||||||
|           <label className="text-sm font-medium">目标 URL</label> |           <label className="text-sm font-medium">Target URL</label> | ||||||
|           <Input |           <Input | ||||||
|             placeholder="例如: https://example.com" |             placeholder="e.g. https://example.com" | ||||||
|             value={url} |             value={url} | ||||||
|             onChange={(e) => setUrl(e.target.value)} |             onChange={(e) => setUrl(e.target.value)} | ||||||
|             onKeyPress={handleKeyPress} |             onKeyPress={handleKeyPress} | ||||||
| @@ -224,7 +224,7 @@ const Tool: FC = () => { | |||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <div className="flex flex-col gap-2"> |         <div className="flex flex-col gap-2"> | ||||||
|           <label className="text-sm font-medium">测试类型</label> |           <label className="text-sm font-medium">Test Type</label> | ||||||
|           <select |           <select | ||||||
|             className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" |             className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" | ||||||
|             value={testType} |             value={testType} | ||||||
| @@ -233,58 +233,58 @@ const Tool: FC = () => { | |||||||
|             } |             } | ||||||
|             disabled={testing} |             disabled={testing} | ||||||
|           > |           > | ||||||
|             <option value="performance">页面加载性能</option> |             <option value="performance">Page Load Performance</option> | ||||||
|             <option value="download">下载速度</option> |             <option value="download">Download Speed</option> | ||||||
|             <option value="upload">上传速度</option> |             <option value="upload">Upload Speed</option> | ||||||
|           </select> |           </select> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <Button onClick={startTest} disabled={testing} className="w-full"> |         <Button onClick={startTest} disabled={testing} className="w-full"> | ||||||
|           {testing && <Loader2 className="mr-2 size-4 animate-spin" />} |           {testing && <Loader2 className="mr-2 size-4 animate-spin" />} | ||||||
|           {testing ? "测试中..." : "开始测试"} |           {testing ? "Testing..." : "Start Test"} | ||||||
|         </Button> |         </Button> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       {result && ( |       {result && ( | ||||||
|         <div className="flex flex-col gap-3 flex-1 overflow-auto"> |         <div className="flex flex-col gap-3 flex-1 overflow-auto"> | ||||||
|           <div className="text-sm font-medium">测试结果:</div> |           <div className="text-sm font-medium">Test Results:</div> | ||||||
|  |  | ||||||
|           {result.performance && ( |           {result.performance && ( | ||||||
|             <div className="border rounded-md p-3 bg-card text-card-foreground"> |             <div className="border rounded-md p-3 bg-card text-card-foreground"> | ||||||
|               <div className="text-sm font-medium mb-3">页面加载性能</div> |               <div className="text-sm font-medium mb-3">Page Load Performance</div> | ||||||
|               <div className="space-y-2"> |               <div className="space-y-2"> | ||||||
|                 {result.performance.dns > 0 && ( |                 {result.performance.dns > 0 && ( | ||||||
|                   <div className="grid grid-cols-2 gap-2 text-sm"> |                   <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|                     <div className="text-muted-foreground">DNS 查询:</div> |                     <div className="text-muted-foreground">DNS Lookup:</div> | ||||||
|                     <div>{result.performance.dns.toFixed(2)} ms</div> |                     <div>{result.performance.dns.toFixed(2)} ms</div> | ||||||
|                   </div> |                   </div> | ||||||
|                 )} |                 )} | ||||||
|                 {result.performance.tcp > 0 && ( |                 {result.performance.tcp > 0 && ( | ||||||
|                   <div className="grid grid-cols-2 gap-2 text-sm"> |                   <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|                     <div className="text-muted-foreground">TCP 连接:</div> |                     <div className="text-muted-foreground">TCP Connection:</div> | ||||||
|                     <div>{result.performance.tcp.toFixed(2)} ms</div> |                     <div>{result.performance.tcp.toFixed(2)} ms</div> | ||||||
|                   </div> |                   </div> | ||||||
|                 )} |                 )} | ||||||
|                 {result.performance.ssl > 0 && ( |                 {result.performance.ssl > 0 && ( | ||||||
|                   <div className="grid grid-cols-2 gap-2 text-sm"> |                   <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|                     <div className="text-muted-foreground">SSL 握手:</div> |                     <div className="text-muted-foreground">SSL Handshake:</div> | ||||||
|                     <div>{result.performance.ssl.toFixed(2)} ms</div> |                     <div>{result.performance.ssl.toFixed(2)} ms</div> | ||||||
|                   </div> |                   </div> | ||||||
|                 )} |                 )} | ||||||
|                 {result.performance.ttfb > 0 && ( |                 {result.performance.ttfb > 0 && ( | ||||||
|                   <div className="grid grid-cols-2 gap-2 text-sm"> |                   <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|                     <div className="text-muted-foreground">首字节时间 (TTFB):</div> |                     <div className="text-muted-foreground">Time to First Byte (TTFB):</div> | ||||||
|                     <div>{result.performance.ttfb.toFixed(2)} ms</div> |                     <div>{result.performance.ttfb.toFixed(2)} ms</div> | ||||||
|                   </div> |                   </div> | ||||||
|                 )} |                 )} | ||||||
|                 {result.performance.download > 0 && ( |                 {result.performance.download > 0 && ( | ||||||
|                   <div className="grid grid-cols-2 gap-2 text-sm"> |                   <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|                     <div className="text-muted-foreground">内容下载:</div> |                     <div className="text-muted-foreground">Content Download:</div> | ||||||
|                     <div>{result.performance.download.toFixed(2)} ms</div> |                     <div>{result.performance.download.toFixed(2)} ms</div> | ||||||
|                   </div> |                   </div> | ||||||
|                 )} |                 )} | ||||||
|                 <div className="grid grid-cols-2 gap-2 text-sm border-t pt-2 mt-2"> |                 <div className="grid grid-cols-2 gap-2 text-sm border-t pt-2 mt-2"> | ||||||
|                   <div className="text-muted-foreground font-medium">总时间:</div> |                   <div className="text-muted-foreground font-medium">Total Time:</div> | ||||||
|                   <div className="font-medium"> |                   <div className="font-medium"> | ||||||
|                     {result.performance.total.toFixed(2)} ms |                     {result.performance.total.toFixed(2)} ms | ||||||
|                   </div> |                   </div> | ||||||
| @@ -295,7 +295,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|           {result.downloadSpeed !== undefined && ( |           {result.downloadSpeed !== undefined && ( | ||||||
|             <div className="border rounded-md p-3 bg-card text-card-foreground"> |             <div className="border rounded-md p-3 bg-card text-card-foreground"> | ||||||
|               <div className="text-sm font-medium mb-3">下载速度</div> |               <div className="text-sm font-medium mb-3">Download Speed</div> | ||||||
|               <div className="text-2xl font-bold text-green-500"> |               <div className="text-2xl font-bold text-green-500"> | ||||||
|                 {result.downloadSpeed.toFixed(2)} Mbps |                 {result.downloadSpeed.toFixed(2)} Mbps | ||||||
|               </div> |               </div> | ||||||
| @@ -307,7 +307,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|           {result.uploadSpeed !== undefined && ( |           {result.uploadSpeed !== undefined && ( | ||||||
|             <div className="border rounded-md p-3 bg-card text-card-foreground"> |             <div className="border rounded-md p-3 bg-card text-card-foreground"> | ||||||
|               <div className="text-sm font-medium mb-3">上传速度</div> |               <div className="text-sm font-medium mb-3">Upload Speed</div> | ||||||
|               <div className="text-2xl font-bold text-blue-500"> |               <div className="text-2xl font-bold text-blue-500"> | ||||||
|                 {result.uploadSpeed.toFixed(2)} Mbps |                 {result.uploadSpeed.toFixed(2)} Mbps | ||||||
|               </div> |               </div> | ||||||
| @@ -321,13 +321,13 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|       {testType === "download" && ( |       {testType === "download" && ( | ||||||
|         <div className="text-xs text-muted-foreground"> |         <div className="text-xs text-muted-foreground"> | ||||||
|           提示: 下载速度测试会下载目标 URL 的内容并计算速度 |           Note: Download speed test will download content from the target URL and calculate speed | ||||||
|         </div> |         </div> | ||||||
|       )} |       )} | ||||||
|  |  | ||||||
|       {testType === "upload" && ( |       {testType === "upload" && ( | ||||||
|         <div className="text-xs text-muted-foreground"> |         <div className="text-xs text-muted-foreground"> | ||||||
|           提示: 上传速度测试会向目标 URL 发送 1MB 测试数据 |           Note: Upload speed test will send 1MB of test data to the target URL | ||||||
|         </div> |         </div> | ||||||
|       )} |       )} | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|   const tcping = async () => { |   const tcping = async () => { | ||||||
|     if (!host.trim()) { |     if (!host.trim()) { | ||||||
|       toast.error("请输入主机名或 IP"); |       toast.error("Please enter a hostname or IP"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -80,7 +80,7 @@ const Tool: FC = () => { | |||||||
|       const time = endTime - startTime; |       const time = endTime - startTime; | ||||||
|  |  | ||||||
|       const errorMessage = |       const errorMessage = | ||||||
|         error instanceof Error ? error.message : "连接失败"; |         error instanceof Error ? error.message : "Connection failed"; | ||||||
|  |  | ||||||
|       const newResult: TCPingResult = { |       const newResult: TCPingResult = { | ||||||
|         seq, |         seq, | ||||||
| @@ -122,7 +122,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|   const startTCPing = () => { |   const startTCPing = () => { | ||||||
|     if (!host.trim()) { |     if (!host.trim()) { | ||||||
|       toast.error("请输入主机名或 IP"); |       toast.error("Please enter a hostname or IP"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -177,9 +177,9 @@ const Tool: FC = () => { | |||||||
|     <div className="flex flex-col gap-4 h-full"> |     <div className="flex flex-col gap-4 h-full"> | ||||||
|       <div className="flex flex-col gap-4"> |       <div className="flex flex-col gap-4"> | ||||||
|         <div className="flex flex-col gap-2"> |         <div className="flex flex-col gap-2"> | ||||||
|           <label className="text-sm font-medium">主机名或 IP</label> |           <label className="text-sm font-medium">Hostname or IP</label> | ||||||
|           <Input |           <Input | ||||||
|             placeholder="例如: example.com 或 192.168.1.1" |             placeholder="e.g. example.com or 192.168.1.1" | ||||||
|             value={host} |             value={host} | ||||||
|             onChange={(e) => setHost(e.target.value)} |             onChange={(e) => setHost(e.target.value)} | ||||||
|             disabled={running} |             disabled={running} | ||||||
| @@ -187,10 +187,10 @@ const Tool: FC = () => { | |||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <div className="flex flex-col gap-2"> |         <div className="flex flex-col gap-2"> | ||||||
|           <label className="text-sm font-medium">端口</label> |           <label className="text-sm font-medium">Port</label> | ||||||
|           <Input |           <Input | ||||||
|             type="number" |             type="number" | ||||||
|             placeholder="例如: 443" |             placeholder="e.g. 443" | ||||||
|             value={port} |             value={port} | ||||||
|             onChange={(e) => setPort(e.target.value)} |             onChange={(e) => setPort(e.target.value)} | ||||||
|             disabled={running} |             disabled={running} | ||||||
| @@ -202,7 +202,7 @@ const Tool: FC = () => { | |||||||
|         <div className="flex gap-2"> |         <div className="flex gap-2"> | ||||||
|           {!running ? ( |           {!running ? ( | ||||||
|             <Button onClick={startTCPing} className="flex-1"> |             <Button onClick={startTCPing} className="flex-1"> | ||||||
|               开始测试 |               Start Test | ||||||
|             </Button> |             </Button> | ||||||
|           ) : ( |           ) : ( | ||||||
|             <Button |             <Button | ||||||
| @@ -210,7 +210,7 @@ const Tool: FC = () => { | |||||||
|               variant="destructive" |               variant="destructive" | ||||||
|               className="flex-1" |               className="flex-1" | ||||||
|             > |             > | ||||||
|               停止 |               Stop | ||||||
|             </Button> |             </Button> | ||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
| @@ -218,26 +218,26 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|       {stats.sent > 0 && ( |       {stats.sent > 0 && ( | ||||||
|         <div className="border rounded-md p-3 bg-card text-card-foreground"> |         <div className="border rounded-md p-3 bg-card text-card-foreground"> | ||||||
|           <div className="text-sm font-medium mb-2">统计信息</div> |           <div className="text-sm font-medium mb-2">Statistics</div> | ||||||
|           <div className="grid grid-cols-2 gap-2 text-sm"> |           <div className="grid grid-cols-2 gap-2 text-sm"> | ||||||
|             <div className="text-muted-foreground">已发送:</div> |             <div className="text-muted-foreground">Sent:</div> | ||||||
|             <div>{stats.sent} 次</div> |             <div>{stats.sent} times</div> | ||||||
|             <div className="text-muted-foreground">成功:</div> |             <div className="text-muted-foreground">Success:</div> | ||||||
|             <div>{stats.received} 次</div> |             <div>{stats.received} times</div> | ||||||
|             <div className="text-muted-foreground">失败:</div> |             <div className="text-muted-foreground">Failed:</div> | ||||||
|             <div> |             <div> | ||||||
|               {stats.lost} 次 ({lossRate}%) |               {stats.lost} times ({lossRate}%) | ||||||
|             </div> |             </div> | ||||||
|             {stats.received > 0 && ( |             {stats.received > 0 && ( | ||||||
|               <> |               <> | ||||||
|                 <div className="text-muted-foreground">最小延迟:</div> |                 <div className="text-muted-foreground">Min Latency:</div> | ||||||
|                 <div>{stats.min.toFixed(2)} ms</div> |                 <div>{stats.min.toFixed(2)} ms</div> | ||||||
|                 <div className="text-muted-foreground">最大延迟:</div> |                 <div className="text-muted-foreground">Max Latency:</div> | ||||||
|                 <div>{stats.max.toFixed(2)} ms</div> |                 <div>{stats.max.toFixed(2)} ms</div> | ||||||
|                 <div className="text-muted-foreground">平均延迟:</div> |                 <div className="text-muted-foreground">Avg Latency:</div> | ||||||
|                 <div>{stats.avg.toFixed(2)} ms</div> |                 <div>{stats.avg.toFixed(2)} ms</div> | ||||||
|                 <div className="text-muted-foreground">端口状态:</div> |                 <div className="text-muted-foreground">Port Status:</div> | ||||||
|                 <div className="text-green-500 font-medium">开放</div> |                 <div className="text-green-500 font-medium">Open</div> | ||||||
|               </> |               </> | ||||||
|             )} |             )} | ||||||
|           </div> |           </div> | ||||||
| @@ -246,7 +246,7 @@ const Tool: FC = () => { | |||||||
|  |  | ||||||
|       {results.length > 0 && ( |       {results.length > 0 && ( | ||||||
|         <div className="flex flex-col gap-2 flex-1 overflow-hidden"> |         <div className="flex flex-col gap-2 flex-1 overflow-hidden"> | ||||||
|           <div className="text-sm font-medium">TCPing 结果:</div> |           <div className="text-sm font-medium">TCPing Results:</div> | ||||||
|           <div |           <div | ||||||
|             ref={resultsContainerRef} |             ref={resultsContainerRef} | ||||||
|             className="flex-1 overflow-auto space-y-1 font-mono text-sm border rounded-md p-3 bg-card" |             className="flex-1 overflow-auto space-y-1 font-mono text-sm border rounded-md p-3 bg-card" | ||||||
| @@ -262,7 +262,7 @@ const Tool: FC = () => { | |||||||
|                   </> |                   </> | ||||||
|                 ) : ( |                 ) : ( | ||||||
|                   <> |                   <> | ||||||
|                     seq={result.seq} port={port} 连接失败 |                     seq={result.seq} port={port} Connection failed | ||||||
|                     {result.error && ` (${result.error})`} |                     {result.error && ` (${result.error})`} | ||||||
|                   </> |                   </> | ||||||
|                 )} |                 )} | ||||||
| @@ -271,7 +271,7 @@ const Tool: FC = () => { | |||||||
|             {running && ( |             {running && ( | ||||||
|               <div className="flex items-center gap-2 text-muted-foreground"> |               <div className="flex items-center gap-2 text-muted-foreground"> | ||||||
|                 <Loader2 className="size-3 animate-spin" /> |                 <Loader2 className="size-3 animate-spin" /> | ||||||
|                 运行中... |                 Running... | ||||||
|               </div> |               </div> | ||||||
|             )} |             )} | ||||||
|           </div> |           </div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 typist
					typist