feat: enhance input handling for network tools
- Added domain and URL normalization on blur for DNS, Ping, Speedtest, and TCPing components. - Improved user experience by ensuring valid input formats and extracting ports where applicable.
This commit is contained in:
		| @@ -41,6 +41,26 @@ const Tool: FC = () => { | ||||
|   const [results, setResults] = useState<DNSRecord[]>([]); | ||||
|   const [queryTime, setQueryTime] = useState<number>(0); | ||||
|  | ||||
|   const handleDomainBlur = () => { | ||||
|     if (!domain.trim()) return; | ||||
|      | ||||
|     let input = domain.trim(); | ||||
|     let cleanDomain = input; | ||||
|      | ||||
|     try { | ||||
|       // Try to parse as URL | ||||
|       const url = new URL(input.startsWith('http') ? input : `https://${input}`); | ||||
|       cleanDomain = url.hostname; | ||||
|     } catch { | ||||
|       // If parsing fails, fallback to manual cleanup | ||||
|       cleanDomain = input.replace(/^https?:\/\//, "").split("/")[0].split(":")[0]; | ||||
|     } | ||||
|      | ||||
|     if (cleanDomain !== input) { | ||||
|       setDomain(cleanDomain); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const queryDNS = async () => { | ||||
|     if (!domain.trim()) { | ||||
|       toast.error("Please enter a domain name"); | ||||
| @@ -58,7 +78,7 @@ const Tool: FC = () => { | ||||
|       const queries = DNS_RECORD_TYPES.map((recordType) => | ||||
|         fetch( | ||||
|           `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent( | ||||
|             domain | ||||
|             domain.trim() | ||||
|           )}&type=${recordType.value}`, | ||||
|           { | ||||
|             headers: { | ||||
| @@ -127,6 +147,7 @@ const Tool: FC = () => { | ||||
|             placeholder="e.g. example.com" | ||||
|             value={domain} | ||||
|             onChange={(e) => setDomain(e.target.value)} | ||||
|             onBlur={handleDomainBlur} | ||||
|             onKeyPress={handleKeyPress} | ||||
|             disabled={loading} | ||||
|           /> | ||||
|   | ||||
| @@ -36,6 +36,27 @@ const Tool: FC = () => { | ||||
|   const seqRef = useRef<number>(0); | ||||
|   const resultsContainerRef = useRef<HTMLDivElement>(null); | ||||
|  | ||||
|   const handleUrlBlur = () => { | ||||
|     if (!url.trim()) return; | ||||
|      | ||||
|     let input = url.trim(); | ||||
|      | ||||
|     try { | ||||
|       // Try to parse as URL | ||||
|       const parsedUrl = new URL(input.startsWith('http') ? input : `https://${input}`); | ||||
|       const normalizedUrl = parsedUrl.toString(); | ||||
|        | ||||
|       if (normalizedUrl !== input) { | ||||
|         setUrl(normalizedUrl); | ||||
|       } | ||||
|     } catch { | ||||
|       // If parsing fails, add https:// prefix | ||||
|       if (!input.startsWith("http://") && !input.startsWith("https://")) { | ||||
|         setUrl(`https://${input}`); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const ping = async () => { | ||||
|     if (!url.trim()) { | ||||
|       toast.error("Please enter a URL"); | ||||
| @@ -43,12 +64,7 @@ const Tool: FC = () => { | ||||
|     } | ||||
|  | ||||
|     const seq = ++seqRef.current; | ||||
|     let targetUrl = url.trim(); | ||||
|  | ||||
|     // If no protocol prefix, default to https:// | ||||
|     if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) { | ||||
|       targetUrl = `https://${targetUrl}`; | ||||
|     } | ||||
|     const targetUrl = url.trim(); | ||||
|  | ||||
|     const startTime = performance.now(); | ||||
|  | ||||
| @@ -177,6 +193,7 @@ const Tool: FC = () => { | ||||
|             placeholder="e.g. example.com or https://example.com" | ||||
|             value={url} | ||||
|             onChange={(e) => setUrl(e.target.value)} | ||||
|             onBlur={handleUrlBlur} | ||||
|             disabled={running} | ||||
|           /> | ||||
|         </div> | ||||
|   | ||||
| @@ -142,18 +142,34 @@ const Tool: FC = () => { | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleUrlBlur = () => { | ||||
|     if (!url.trim()) return; | ||||
|      | ||||
|     let input = url.trim(); | ||||
|      | ||||
|     try { | ||||
|       // Try to parse as URL | ||||
|       const parsedUrl = new URL(input.startsWith('http') ? input : `https://${input}`); | ||||
|       const normalizedUrl = parsedUrl.toString(); | ||||
|        | ||||
|       if (normalizedUrl !== input) { | ||||
|         setUrl(normalizedUrl); | ||||
|       } | ||||
|     } catch { | ||||
|       // If parsing fails, add https:// prefix | ||||
|       if (!input.startsWith("http://") && !input.startsWith("https://")) { | ||||
|         setUrl(`https://${input}`); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const startTest = async () => { | ||||
|     if (!url.trim()) { | ||||
|       toast.error("Please enter a URL"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     let targetUrl = url.trim(); | ||||
|  | ||||
|     // If no protocol prefix, default to https:// | ||||
|     if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) { | ||||
|       targetUrl = `https://${targetUrl}`; | ||||
|     } | ||||
|     const targetUrl = url.trim(); | ||||
|  | ||||
|     setTesting(true); | ||||
|     setResult(null); | ||||
| @@ -218,6 +234,7 @@ const Tool: FC = () => { | ||||
|             placeholder="e.g. https://example.com" | ||||
|             value={url} | ||||
|             onChange={(e) => setUrl(e.target.value)} | ||||
|             onBlur={handleUrlBlur} | ||||
|             onKeyPress={handleKeyPress} | ||||
|             disabled={testing} | ||||
|           /> | ||||
|   | ||||
| @@ -37,6 +37,46 @@ const Tool: FC = () => { | ||||
|   const seqRef = useRef<number>(0); | ||||
|   const resultsContainerRef = useRef<HTMLDivElement>(null); | ||||
|  | ||||
|   const handleHostBlur = () => { | ||||
|     if (!host.trim()) return; | ||||
|      | ||||
|     let input = host.trim(); | ||||
|     let cleanHost = input; | ||||
|     let extractedPort: string | null = null; | ||||
|      | ||||
|     try { | ||||
|       // Try to parse as URL | ||||
|       const url = new URL(input.startsWith('http') ? input : `https://${input}`); | ||||
|       cleanHost = url.hostname; | ||||
|        | ||||
|       // Extract port if specified in URL | ||||
|       if (url.port) { | ||||
|         extractedPort = url.port; | ||||
|       } | ||||
|     } catch { | ||||
|       // If parsing fails, fallback to manual cleanup | ||||
|       const withoutProtocol = input.replace(/^https?:\/\//, ""); | ||||
|       const withoutPath = withoutProtocol.split("/")[0]; | ||||
|        | ||||
|       // Check for port in the format hostname:port | ||||
|       const portMatch = withoutPath.match(/^(.+):(\d+)$/); | ||||
|       if (portMatch) { | ||||
|         cleanHost = portMatch[1]; | ||||
|         extractedPort = portMatch[2]; | ||||
|       } else { | ||||
|         cleanHost = withoutPath; | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     if (cleanHost !== input) { | ||||
|       setHost(cleanHost); | ||||
|     } | ||||
|      | ||||
|     if (extractedPort) { | ||||
|       setPort(extractedPort); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const tcping = async () => { | ||||
|     if (!host.trim()) { | ||||
|       toast.error("Please enter a hostname or IP"); | ||||
| @@ -45,14 +85,11 @@ const Tool: FC = () => { | ||||
|  | ||||
|     const seq = ++seqRef.current; | ||||
|     const portNum = parseInt(port) || 443; | ||||
|     let targetUrl = host.trim(); | ||||
|     const targetHost = host.trim(); | ||||
|  | ||||
|     // 移除协议前缀 | ||||
|     targetUrl = targetUrl.replace(/^https?:\/\//, ""); | ||||
|  | ||||
|     // 构建测试 URL | ||||
|     // Build test URL | ||||
|     const protocol = portNum === 443 ? "https" : "http"; | ||||
|     const url = `${protocol}://${targetUrl}:${portNum}`; | ||||
|     const url = `${protocol}://${targetHost}:${portNum}`; | ||||
|  | ||||
|     const startTime = performance.now(); | ||||
|  | ||||
| @@ -182,6 +219,7 @@ const Tool: FC = () => { | ||||
|             placeholder="e.g. example.com or 192.168.1.1" | ||||
|             value={host} | ||||
|             onChange={(e) => setHost(e.target.value)} | ||||
|             onBlur={handleHostBlur} | ||||
|             disabled={running} | ||||
|           /> | ||||
|         </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 typist
					typist