Rayan545454 commited on
Commit
0e5795e
·
verified ·
1 Parent(s): 5812985

<?xml version="1.0" encoding="utf-8"?> <manifest package="com.example.wifitesterpro" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> ‎ <!-- لعرض SSID بدقة على Android 10+ --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <application android:allowBackup="true" android:label="Wi-Fi Tester Pro" android:supportsRtl="true" android:theme="@style/Theme.WifiTesterPro" android:usesCleartextTraffic="true"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> // Top-level build file plugins { id 'com.android.application' version '8.6.0' apply false id 'org.jetbrains.kotlin.android' version '1.9.24' apply false } pluginManagement { repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "WifiTesterPro" include ':app'plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' } android { namespace 'com.example.wifitesterpro' compileSdk 34 defaultConfig { applicationId "com.example.wifitesterpro" minSdk 26 targetSdk 34 versionCode 1 versionName "1.0.0" } buildFeatures { viewBinding true } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = '17' } } dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1" implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' } <resources xmlns:tools="http://schemas.android.com/tools"> <style name="Theme.WifiTesterPro" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> </style> </resources> <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:text="اختبار شبكة Wi-Fi (نسخة مطوّرة)" android:textSize="20sp" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <EditText android:id="@+id/etHost" android:hint="الهوست للاختبار (مثال: 8.8.8.8 أو www.example.com)" android:inputType="textUri" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp"/> <EditText android:id="@+id/etPorts" android:hint="منافذ لفحصها (مثال: 53,80,443,8080)" android:inputType="text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp"/> <EditText android:id="@+id/etUrl" android:hint="رابط HTTP/HTTPS لاختبار الاستجابة (مثال: https://www.google.com)" android:inputType="textUri" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp"/> <Button android:id="@+id/btnRunAll" android:text="تشغيل كل الاختبارات" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp"/> <Button android:id="@+id/btnExport" android:text="تصدير النتائج CSV" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp"/> <TextView android:id="@+id/tvInfo" android:text="معلومات الشبكة..." android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp"/> <TextView android:id="@+id/tvResults" android:text="نتائج الاختبارات..." android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp"/> </LinearLayout> </ScrollView> package com.example.wifitesterpro import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.net.wifi.WifiManager import android.text.format.Formatter import kotlinx.coroutines.* import java.io.BufferedInputStream import java.net.HttpURLConnection import java.net.InetAddress import java.net.InetSocketAddress import java.net.Socket import java.net.URL import java.util.concurrent.TimeUnit import kotlin.math.roundToInt data class WifiInfoData( val ssid: String?, val ip: String?, val gateway: String?, val linkSpeedMbps: Int?, val rssi: Int?, val frequencyMhz: Int? ) data class PingStats( val transmitted: Int, val received: Int, val lossPercent: Double, val minMs: Double?, val avgMs: Double?, val maxMs: Double? ) object NetworkTester { fun getWifiInfo(context: Context): WifiInfoData { val wifi = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager val conn = wifi.connectionInfo val dhcp = wifi.dhcpInfo val ip = dhcp?.let { Formatter.formatIpAddress(it.ipAddress) } val gateway = dhcp?.let { Formatter.formatIpAddress(it.gateway) } return WifiInfoData( ssid = conn?.ssid?.replace("\"",""), ip = ip, gateway = gateway, linkSpeedMbps = conn?.linkSpeed, rssi = conn?.rssi, frequencyMhz = conn?.frequency ) } fun isConnectedToInternet(context: Context): Boolean { val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val nw = cm.activeNetwork ?: return false val cap = cm.getNetworkCapabilities(nw) ?: return false return cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) } suspend fun pingRaw(host: String, count: Int = 4, timeoutSec: Int = 4): String = withContext(Dispatchers.IO) { try { val proc = ProcessBuilder() .command("ping", "-c", count.toString(), "-W", timeoutSec.toString(), host) .redirectErrorStream(true) .start() proc.waitFor((timeoutSec * (count + 1)).toLong(), TimeUnit.SECONDS) proc.inputStream.bufferedReader().use { it.readText() } } catch (e: Exception) { "Ping error: ${e.message}" } } fun parsePingStats(output: String, fallbackCount: Int): PingStats { // يحاول استخراج الإحصائيات من مخرجات ping القياسية val tx = Regex("(\\d+) packets transmitted").find(output)?.groupValues?.get(1)?.toIntOrNull() ?: fallbackCount val rx = Regex("(\\d+) received").find(output)?.groupValues?.get(1)?.toIntOrNull() ?: 0 val loss = Regex("(\\d+(?:\\.\\d+)?)% packet loss").find(output)?.groupValues?.get(1)?.toDoubleOrNull() ?: run { if (tx > 0) ((tx - rx).toDouble() / tx) * 100.0 else 100.0 } val rtt = Regex("min/avg/max/(?:mdev|stddev) = ([\\d.]+)/([\\d.]+)/([\\d.]+)/").find(output) val minMs = rtt?.groupValues?.get(1)?.toDoubleOrNull() val avgMs = rtt?.groupValues?.get(2)?.toDoubleOrNull() val maxMs = rtt?.groupValues?.get(3)?.toDoubleOrNull() return PingStats(tx, rx, loss, minMs, avgMs, maxMs) } suspend fun tcpConnect(host: String, port: Int, timeoutMs: Int = 3000): Pair<Boolean, Long> = withContext(Dispatchers.IO) { val start = System.nanoTime() return@withContext try { Socket().use { s -> s.connect(InetSocketAddress(host, port), timeoutMs) } val ms = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) true to ms } catch (_: Exception) { val ms = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) false to ms } } suspend fun scanPorts(host: String, ports: List<Int>, timeoutMs: Int = 1000): List<String> = coroutineScope { ports.map { p -> async(Dispatchers.IO) { val (ok, ms) = tcpConnect(host, p, timeoutMs) "${host}:${p} -> ${if (ok) "OPEN" else "CLOSED"} (${ms}ms)" } }.awaitAll() } suspend fun httpGet(urlStr: String, timeoutMs: Int = 6000): String = withContext(Dispatchers.IO) { try { val url = URL(urlStr) val conn = (url.openConnection() as HttpURLConnection).apply { connectTimeout = timeoutMs readTimeout = timeoutMs requestMethod = "GET" } val start = System.nanoTime() val code = conn.responseCode val latency = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) conn.inputStream?.close() "HTTP GET $urlStr → $code (${latency}ms)" } catch (e: Exception) { "HTTP GET $urlStr: error ${e.message}" } } suspend fun captivePortalCheck(): String = httpGet("http://clients3.google.com/generate_204", 5000) suspend fun downloadSpeedKbps( testUrl: String = "https://speed.hetzner.de/100MB.bin", bytesToRead: Long = 2_000_000 ): String = withContext(Dispatchers.IO) { try { val url = URL(testUrl) val conn = (url.openConnection() as HttpURLConnection).apply { connectTimeout = 5000 readTimeout = 8000 requestMethod = "GET" } var readTotal = 0L val start = System.nanoTime() BufferedInputStream(conn.inputStream).use { input -> val buf = ByteArray(64 * 1024) var n: Int while (readTotal < bytesToRead && input.read(buf).also { n = it } != -1) { readTotal += n } } val elapsedMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start).coerceAtLeast(1) val kbps = (readTotal / 1024.0) / (elapsedMs / 1000.0) "Estimated download: %.1f KB/s (%.2f MB in %d ms)".format(kbps, readTotal/1_000_000.0, elapsedMs) } catch (e: Exception) { "Download test error: ${e.message}" } } suspend fun dnsLookup(host: String): String = withContext(Dispatchers.IO) { try { val addr = InetAddress.getByName(host) "DNS $host → ${addr.hostAddress}" } catch (e: Exception) { "DNS $host: error ${e.message}" } } } package com.example.wifitesterpro import android.content.ContentValues import android.content.Context import android.os.Build import android.provider.MediaStore import java.io.OutputStream import java.nio.charset.Charset import java.text.SimpleDateFormat import java.util.* object CsvExporter { fun saveToDownloads(context: Context, filePrefix: String, csvContent: String): String { val ts = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) val filename = "${filePrefix}_${ts}.csv" val resolver = context.contentResolver val collection = if (Build.VERSION.SDK_INT >= 29) { MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) } else { MediaStore.Files.getContentUri("external") } val values = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.MIME_TYPE, "text/csv") } val uri = resolver.insert(collection, values) ?: throw IllegalStateException("Failed to create file in Downloads") resolver.openOutputStream(uri)?.use { out: OutputStream -> out.write(csvContent.toByteArray(Charset.forName("UTF-8"))) out.flush() } ?: throw IllegalStateException("Failed to open output stream") return filename } } package com.example.wifitesterpro import android.Manifest import android.content.pm.PackageManager import android.os.Bundle import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.example.wifitesterpro.databinding.ActivityMainBinding import kotlinx.coroutines.* class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val requestLocationPermission = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { granted -> if (!granted) { Toast.makeText(this, "لإظهار SSID بدقة على Android 10+، فعّل إذن الموقع.", Toast.LENGTH_LONG).show() } showWifiInfo() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.btnRunAll.setOnClickListener { runAllTests() } binding.btnExport.setOnClickListener { exportCsv() } maybeAskLocation() showWifiInfo() } private fun maybeAskLocation() { val perm = Manifest.permission.ACCESS_FINE_LOCATION if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) { requestLocationPermission.launch(perm) } } private fun showWifiInfo() { val info = NetworkTester.getWifiInfo(this) val connected = NetworkTester.isConnectedToInternet(this) val text = buildString { appendLine("الحالة: ${if (connected) "متصل بالإنترنت" else "غير متصل"}") appendLine("SSID: ${info.ssid ?: "غير متاح"}") appendLine("IP: ${info.ip ?: "غير متاح"}") appendLine("Gateway: ${info.gateway ?: "غير متاح"}") appendLine("Link Speed: ${info.linkSpeedMbps ?: "?"} Mbps") appendLine("RSSI: ${info.rssi ?: "?"} dBm") appendLine("Frequency: ${info.frequencyMhz ?: "?"} MHz") } binding.tvInfo.text = text } private fun runAllTests() { val defaultHost = NetworkTester.getWifiInfo(this).gateway ?: "8.8.8.8" val host = binding.etHost.text.toString().ifBlank { defaultHost } val ports = binding.etPorts.text.toString().ifBlank { "53,80,443" } .split(",").mapNotNull { it.trim().toIntOrNull() }.distinct() val url = binding.etUrl.text.toString().ifBlank { "https://www.google.com/generate_204" } binding.tvResults.text = "جاري التنفيذ..." scope.launch { val lines = mutableListOf<String>() lines += "=== General ===" lines += NetworkTester.captivePortalCheck() lines += "\n=== DNS ===" lines += NetworkTester.dnsLookup(host) lines += "\n=== Ping ===" val raw = NetworkTester.pingRaw(host, count = 5, timeoutSec = 3) val stats = NetworkTester.parsePingStats(raw, 5) lines += "Ping $host → tx=${stats.transmitted}, rx=${stats.received}, loss=${"%.1f".format(stats.lossPercent)}%" lines += "rtt min/avg/max = ${stats.minMs ?: "-"} / ${stats.avgMs ?: "-"} / ${stats.maxMs ?: "-"} ms" lines += "\n=== TCP Connect ===" val (ok443, ms443) = NetworkTester.tcpConnect(host, 443, 3000) lines += "TCP $host:443 → ${if (ok443) "OPEN" else "CLOSED"} (${ms443}ms)" if (ports.isNotEmpty()) { lines += "\n=== Port Scan (${host}) ===" NetworkTester.scanPorts(host, ports, 1200).forEach { lines += it } } lines += "\n=== HTTP Test ===" lines += NetworkTester.httpGet(url, 6000) lines += "\n=== Download Speed (est.) ===" lines += NetworkTester.downloadSpeedKbps() binding.tvResults.text = lines.joinToString("\n") } } private fun exportCsv() { val infoText = binding.tvInfo.text.toString() val resultsText = binding.tvResults.text.toString() if (resultsText.isBlank()) { Toast.makeText(this, "لا توجد نتائج لتصديرها.", Toast.LENGTH_SHORT).show() return } val csv = buildString { appendLine("Section,Key,Value") infoText.lines().forEach { line -> val parts = line.split(":", limit = 2) if (parts.size == 2) { appendLine("Info,${parts[0].trim()},${parts[1].trim()}") } } appendLine("Results,Raw,\"\"\"${resultsText.replace("\"","\"\"")}\"\"\"") } try { val name = CsvExporter.saveToDownloads(this, "wifi_tester_results", csv) Toast.makeText(this, "تم الحفظ في التنزيلات: $name", Toast.LENGTH_LONG).show() } catch (e: Exception) { Toast.makeText(this, "فشل الحفظ: ${e.message}", Toast.LENGTH_LONG).show() } } override fun onDestroy() { super.onDestroy() scope.cancel() } } - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +554 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Sorry 1234
3
- emoji: 📈
4
- colorFrom: purple
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: sorry-1234
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,554 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Wi-Fi Tester Pro</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .fade-in {
11
+ animation: fadeIn 0.3s ease-in-out;
12
+ }
13
+
14
+ @keyframes fadeIn {
15
+ from { opacity: 0; }
16
+ to { opacity: 1; }
17
+ }
18
+
19
+ .wifi-signal {
20
+ position: relative;
21
+ width: 24px;
22
+ height: 24px;
23
+ }
24
+
25
+ .wifi-signal .bar {
26
+ position: absolute;
27
+ bottom: 0;
28
+ left: 50%;
29
+ transform: translateX(-50%);
30
+ border: 2px solid currentColor;
31
+ border-radius: 50%;
32
+ border-top-color: transparent;
33
+ border-left-color: transparent;
34
+ border-right-color: transparent;
35
+ }
36
+
37
+ .wifi-signal .bar-1 {
38
+ width: 6px;
39
+ height: 6px;
40
+ }
41
+
42
+ .wifi-signal .bar-2 {
43
+ width: 12px;
44
+ height: 12px;
45
+ animation-delay: 0.2s;
46
+ }
47
+
48
+ .wifi-signal .bar-3 {
49
+ width: 18px;
50
+ height: 18px;
51
+ animation-delay: 0.4s;
52
+ }
53
+
54
+ .wifi-signal .bar-4 {
55
+ width: 24px;
56
+ height: 24px;
57
+ animation-delay: 0.6s;
58
+ }
59
+
60
+ .pulse {
61
+ animation: pulse 2s infinite;
62
+ }
63
+
64
+ @keyframes pulse {
65
+ 0% { opacity: 0.6; }
66
+ 50% { opacity: 1; }
67
+ 100% { opacity: 0.6; }
68
+ }
69
+
70
+ .progress-bar {
71
+ height: 4px;
72
+ background-color: #e5e7eb;
73
+ border-radius: 2px;
74
+ overflow: hidden;
75
+ }
76
+
77
+ .progress-bar-fill {
78
+ height: 100%;
79
+ background-color: #3b82f6;
80
+ transition: width 0.3s ease;
81
+ }
82
+
83
+ .result-card {
84
+ transition: all 0.3s ease;
85
+ }
86
+
87
+ .result-card:hover {
88
+ transform: translateY(-2px);
89
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
90
+ }
91
+ </style>
92
+ </head>
93
+ <body class="bg-gray-50 text-gray-800 font-sans">
94
+ <div class="min-h-screen flex flex-col">
95
+ <!-- Header -->
96
+ <header class="bg-blue-600 text-white shadow-md">
97
+ <div class="container mx-auto px-4 py-4 flex items-center justify-between">
98
+ <div class="flex items-center space-x-3">
99
+ <div class="wifi-signal text-white">
100
+ <div class="bar bar-1"></div>
101
+ <div class="bar bar-2"></div>
102
+ <div class="bar bar-3"></div>
103
+ <div class="bar bar-4"></div>
104
+ </div>
105
+ <h1 class="text-xl font-bold">Wi-Fi Tester Pro</h1>
106
+ </div>
107
+ <button id="darkModeToggle" class="p-2 rounded-full hover:bg-blue-700 transition">
108
+ <i class="fas fa-moon"></i>
109
+ </button>
110
+ </div>
111
+ </header>
112
+
113
+ <!-- Main Content -->
114
+ <main class="flex-grow container mx-auto px-4 py-6">
115
+ <div class="max-w-4xl mx-auto">
116
+ <!-- Network Info Card -->
117
+ <div class="bg-white rounded-lg shadow-md overflow-hidden mb-6 fade-in">
118
+ <div class="bg-blue-600 px-4 py-3 flex items-center justify-between">
119
+ <h2 class="text-white font-semibold flex items-center">
120
+ <i class="fas fa-network-wired mr-2"></i> Network Information
121
+ </h2>
122
+ <button id="refreshNetworkInfo" class="text-white hover:text-blue-200 transition">
123
+ <i class="fas fa-sync-alt"></i>
124
+ </button>
125
+ </div>
126
+ <div class="p-4">
127
+ <div id="networkInfo" class="space-y-2 text-sm">
128
+ <div class="flex justify-between">
129
+ <span class="text-gray-500">Status:</span>
130
+ <span class="font-medium text-green-600">Connected to Internet</span>
131
+ </div>
132
+ <div class="flex justify-between">
133
+ <span class="text-gray-500">SSID:</span>
134
+ <span class="font-medium">MyWiFiNetwork</span>
135
+ </div>
136
+ <div class="flex justify-between">
137
+ <span class="text-gray-500">IP Address:</span>
138
+ <span class="font-medium">192.168.1.15</span>
139
+ </div>
140
+ <div class="flex justify-between">
141
+ <span class="text-gray-500">Gateway:</span>
142
+ <span class="font-medium">192.168.1.1</span>
143
+ </div>
144
+ <div class="flex justify-between">
145
+ <span class="text-gray-500">Link Speed:</span>
146
+ <span class="font-medium">144 Mbps</span>
147
+ </div>
148
+ <div class="flex justify-between">
149
+ <span class="text-gray-500">Signal Strength:</span>
150
+ <div class="flex items-center">
151
+ <span class="font-medium mr-2">-56 dBm</span>
152
+ <div class="flex space-x-1">
153
+ <div class="w-1 h-3 bg-green-500 rounded-full"></div>
154
+ <div class="w-1 h-4 bg-green-500 rounded-full"></div>
155
+ <div class="w-1 h-5 bg-green-500 rounded-full"></div>
156
+ <div class="w-1 h-6 bg-green-500 rounded-full"></div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ <div class="flex justify-between">
161
+ <span class="text-gray-500">Frequency:</span>
162
+ <span class="font-medium">2412 MHz (2.4 GHz)</span>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+
168
+ <!-- Test Configuration -->
169
+ <div class="bg-white rounded-lg shadow-md overflow-hidden mb-6 fade-in">
170
+ <div class="bg-blue-600 px-4 py-3">
171
+ <h2 class="text-white font-semibold flex items-center">
172
+ <i class="fas fa-cog mr-2"></i> Test Configuration
173
+ </h2>
174
+ </div>
175
+ <div class="p-4 space-y-4">
176
+ <div>
177
+ <label for="hostInput" class="block text-sm font-medium text-gray-700 mb-1">
178
+ <i class="fas fa-server mr-1"></i> Host to Test
179
+ </label>
180
+ <div class="relative">
181
+ <input type="text" id="hostInput" placeholder="e.g., 8.8.8.8 or www.example.com"
182
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
183
+ <div class="absolute inset-y-0 right-0 flex items-center pr-3">
184
+ <i class="fas fa-question-circle text-gray-400 hover:text-blue-500 cursor-pointer"
185
+ title="The host or IP address to test connectivity against"></i>
186
+ </div>
187
+ </div>
188
+ </div>
189
+
190
+ <div>
191
+ <label for="portsInput" class="block text-sm font-medium text-gray-700 mb-1">
192
+ <i class="fas fa-plug mr-1"></i> Ports to Scan
193
+ </label>
194
+ <div class="relative">
195
+ <input type="text" id="portsInput" placeholder="e.g., 53,80,443,8080"
196
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
197
+ <div class="absolute inset-y-0 right-0 flex items-center pr-3">
198
+ <i class="fas fa-question-circle text-gray-400 hover:text-blue-500 cursor-pointer"
199
+ title="Comma-separated list of ports to scan (e.g., 53,80,443)"></i>
200
+ </div>
201
+ </div>
202
+ </div>
203
+
204
+ <div>
205
+ <label for="urlInput" class="block text-sm font-medium text-gray-700 mb-1">
206
+ <i class="fas fa-globe mr-1"></i> URL to Test
207
+ </label>
208
+ <div class="relative">
209
+ <input type="text" id="urlInput" placeholder="e.g., https://www.google.com"
210
+ class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
211
+ <div class="absolute inset-y-0 right-0 flex items-center pr-3">
212
+ <i class="fas fa-question-circle text-gray-400 hover:text-blue-500 cursor-pointer"
213
+ title="URL to test HTTP/HTTPS connectivity"></i>
214
+ </div>
215
+ </div>
216
+ </div>
217
+
218
+ <div class="flex flex-col sm:flex-row gap-3 pt-2">
219
+ <button id="runAllTestsBtn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition flex items-center justify-center">
220
+ <i class="fas fa-play-circle mr-2"></i> Run All Tests
221
+ </button>
222
+ <button id="exportResultsBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-md transition flex items-center justify-center">
223
+ <i class="fas fa-file-export mr-2"></i> Export Results
224
+ </button>
225
+ </div>
226
+ </div>
227
+ </div>
228
+
229
+ <!-- Test Results -->
230
+ <div class="bg-white rounded-lg shadow-md overflow-hidden fade-in">
231
+ <div class="bg-blue-600 px-4 py-3 flex items-center justify-between">
232
+ <h2 class="text-white font-semibold flex items-center">
233
+ <i class="fas fa-chart-bar mr-2"></i> Test Results
234
+ </h2>
235
+ <div id="testStatus" class="text-sm text-blue-100 flex items-center">
236
+ <span>Ready</span>
237
+ </div>
238
+ </div>
239
+ <div class="p-4">
240
+ <div id="progressContainer" class="mb-4 hidden">
241
+ <div class="flex justify-between text-sm text-gray-600 mb-1">
242
+ <span>Running tests...</span>
243
+ <span id="progressPercent">0%</span>
244
+ </div>
245
+ <div class="progress-bar">
246
+ <div id="progressBarFill" class="progress-bar-fill" style="width: 0%"></div>
247
+ </div>
248
+ </div>
249
+
250
+ <div id="testResults" class="space-y-4">
251
+ <div class="text-center text-gray-500 py-8">
252
+ <i class="fas fa-wifi text-4xl mb-3 text-gray-300"></i>
253
+ <p>No test results yet. Click "Run All Tests" to begin.</p>
254
+ </div>
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ </main>
260
+
261
+ <!-- Footer -->
262
+ <footer class="bg-gray-100 border-t border-gray-200 py-4">
263
+ <div class="container mx-auto px-4 text-center text-gray-600 text-sm">
264
+ <p>Wi-Fi Tester Pro &copy; 2023 | Version 1.0.0</p>
265
+ </div>
266
+ </footer>
267
+ </div>
268
+
269
+ <!-- Toast Notification -->
270
+ <div id="toast" class="fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-2 rounded-md shadow-lg transform translate-y-10 opacity-0 transition-all duration-300 hidden">
271
+ <div class="flex items-center">
272
+ <i id="toastIcon" class="fas fa-info-circle mr-2"></i>
273
+ <span id="toastMessage">This is a toast message</span>
274
+ </div>
275
+ </div>
276
+
277
+ <script>
278
+ // Dark mode toggle
279
+ const darkModeToggle = document.getElementById('darkModeToggle');
280
+ darkModeToggle.addEventListener('click', () => {
281
+ document.documentElement.classList.toggle('dark');
282
+ const icon = darkModeToggle.querySelector('i');
283
+ if (document.documentElement.classList.contains('dark')) {
284
+ icon.classList.replace('fa-moon', 'fa-sun');
285
+ document.body.classList.remove('bg-gray-50');
286
+ document.body.classList.add('bg-gray-900', 'text-gray-100');
287
+ showToast('Dark mode enabled', 'success');
288
+ } else {
289
+ icon.classList.replace('fa-sun', 'fa-moon');
290
+ document.body.classList.remove('bg-gray-900', 'text-gray-100');
291
+ document.body.classList.add('bg-gray-50', 'text-gray-800');
292
+ showToast('Light mode enabled', 'success');
293
+ }
294
+ });
295
+
296
+ // Toast notification function
297
+ function showToast(message, type = 'info') {
298
+ const toast = document.getElementById('toast');
299
+ const toastMessage = document.getElementById('toastMessage');
300
+ const toastIcon = document.getElementById('toastIcon');
301
+
302
+ toastMessage.textContent = message;
303
+
304
+ // Set icon and color based on type
305
+ if (type === 'success') {
306
+ toast.classList.remove('bg-gray-800');
307
+ toast.classList.add('bg-green-600');
308
+ toastIcon.classList.replace('fa-info-circle', 'fa-check-circle');
309
+ } else if (type === 'error') {
310
+ toast.classList.remove('bg-gray-800');
311
+ toast.classList.add('bg-red-600');
312
+ toastIcon.classList.replace('fa-info-circle', 'fa-exclamation-circle');
313
+ } else {
314
+ toast.classList.remove('bg-green-600', 'bg-red-600');
315
+ toast.classList.add('bg-gray-800');
316
+ toastIcon.classList.replace('fa-check-circle', 'fa-info-circle');
317
+ toastIcon.classList.replace('fa-exclamation-circle', 'fa-info-circle');
318
+ }
319
+
320
+ // Show toast
321
+ toast.classList.remove('hidden');
322
+ setTimeout(() => {
323
+ toast.classList.remove('translate-y-10', 'opacity-0');
324
+ toast.classList.add('translate-y-0', 'opacity-100');
325
+ }, 10);
326
+
327
+ // Hide after 3 seconds
328
+ setTimeout(() => {
329
+ toast.classList.remove('translate-y-0', 'opacity-100');
330
+ toast.classList.add('translate-y-10', 'opacity-0');
331
+ setTimeout(() => toast.classList.add('hidden'), 300);
332
+ }, 3000);
333
+ }
334
+
335
+ // Mock network info refresh
336
+ document.getElementById('refreshNetworkInfo').addEventListener('click', function() {
337
+ const networkInfo = document.getElementById('networkInfo');
338
+ const refreshBtn = this;
339
+
340
+ refreshBtn.classList.add('animate-spin');
341
+
342
+ // Simulate network refresh
343
+ setTimeout(() => {
344
+ // This would be replaced with actual network info in a real app
345
+ const status = Math.random() > 0.1 ? 'Connected to Internet' : 'No Internet Connection';
346
+ const statusColor = status.includes('Connected') ? 'text-green-600' : 'text-red-600';
347
+
348
+ networkInfo.innerHTML = `
349
+ <div class="flex justify-between">
350
+ <span class="text-gray-500">Status:</span>
351
+ <span class="font-medium ${statusColor}">${status}</span>
352
+ </div>
353
+ <div class="flex justify-between">
354
+ <span class="text-gray-500">SSID:</span>
355
+ <span class="font-medium">MyWiFiNetwork</span>
356
+ </div>
357
+ <div class="flex justify-between">
358
+ <span class="text-gray-500">IP Address:</span>
359
+ <span class="font-medium">192.168.1.${Math.floor(Math.random() * 50) + 1}</span>
360
+ </div>
361
+ <div class="flex justify-between">
362
+ <span class="text-gray-500">Gateway:</span>
363
+ <span class="font-medium">192.168.1.1</span>
364
+ </div>
365
+ <div class="flex justify-between">
366
+ <span class="text-gray-500">Link Speed:</span>
367
+ <span class="font-medium">${Math.floor(Math.random() * 200) + 50} Mbps</span>
368
+ </div>
369
+ <div class="flex justify-between">
370
+ <span class="text-gray-500">Signal Strength:</span>
371
+ <div class="flex items-center">
372
+ <span class="font-medium mr-2">-${Math.floor(Math.random() * 40) + 40} dBm</span>
373
+ <div class="flex space-x-1">
374
+ <div class="w-1 h-3 bg-green-500 rounded-full"></div>
375
+ <div class="w-1 h-4 bg-green-500 rounded-full"></div>
376
+ <div class="w-1 h-5 bg-green-500 rounded-full"></div>
377
+ <div class="w-1 h-6 bg-green-500 rounded-full"></div>
378
+ </div>
379
+ </div>
380
+ </div>
381
+ <div class="flex justify-between">
382
+ <span class="text-gray-500">Frequency:</span>
383
+ <span class="font-medium">${Math.random() > 0.5 ? '2412 MHz (2.4 GHz)' : '5180 MHz (5 GHz)'}</span>
384
+ </div>
385
+ `;
386
+
387
+ refreshBtn.classList.remove('animate-spin');
388
+ showToast('Network info refreshed', 'success');
389
+ }, 1000);
390
+ });
391
+
392
+ // Run all tests
393
+ document.getElementById('runAllTestsBtn').addEventListener('click', function() {
394
+ const hostInput = document.getElementById('hostInput').value.trim() || '8.8.8.8';
395
+ const portsInput = document.getElementById('portsInput').value.trim() || '53,80,443';
396
+ const urlInput = document.getElementById('urlInput').value.trim() || 'https://www.google.com';
397
+
398
+ const testResults = document.getElementById('testResults');
399
+ const progressContainer = document.getElementById('progressContainer');
400
+ const progressBarFill = document.getElementById('progressBarFill');
401
+ const progressPercent = document.getElementById('progressPercent');
402
+ const testStatus = document.getElementById('testStatus');
403
+
404
+ // Reset UI
405
+ testResults.innerHTML = '';
406
+ progressContainer.classList.remove('hidden');
407
+ progressBarFill.style.width = '0%';
408
+ progressPercent.textContent = '0%';
409
+ testStatus.innerHTML = '<span class="pulse">Running tests...</span>';
410
+
411
+ // Disable buttons during test
412
+ this.disabled = true;
413
+ document.getElementById('exportResultsBtn').disabled = true;
414
+
415
+ // Simulate running tests with progress updates
416
+ const testPhases = [
417
+ { name: 'Captive Portal Check', duration: 800 },
418
+ { name: 'DNS Lookup', duration: 1000 },
419
+ { name: 'Ping Test', duration: 1500 },
420
+ { name: 'TCP Connect', duration: 1200 },
421
+ { name: 'Port Scan', duration: 2000 },
422
+ { name: 'HTTP Test', duration: 1800 },
423
+ { name: 'Download Speed', duration: 2500 }
424
+ ];
425
+
426
+ let currentProgress = 0;
427
+ const totalDuration = testPhases.reduce((sum, phase) => sum + phase.duration, 0);
428
+
429
+ testPhases.forEach((phase, index) => {
430
+ setTimeout(() => {
431
+ // Update progress
432
+ currentProgress += phase.duration;
433
+ const percent = Math.min(100, Math.round((currentProgress / totalDuration) * 100));
434
+ progressBarFill.style.width = `${percent}%`;
435
+ progressPercent.textContent = `${percent}%`;
436
+
437
+ // Add test result card
438
+ const resultCard = document.createElement('div');
439
+ resultCard.className = 'result-card bg-gray-50 border border-gray-200 rounded-md p-4';
440
+
441
+ // Simulate different test outcomes
442
+ const isSuccess = Math.random() > 0.2;
443
+ const icon = isSuccess ? 'fa-check-circle text-green-500' : 'fa-times-circle text-red-500';
444
+ const status = isSuccess ? 'Success' : 'Failed';
445
+
446
+ resultCard.innerHTML = `
447
+ <div class="flex justify-between items-start mb-2">
448
+ <h3 class="font-medium">${phase.name}</h3>
449
+ <div class="flex items-center">
450
+ <span class="text-sm ${isSuccess ? 'text-green-600' : 'text-red-600'} mr-2">${status}</span>
451
+ <i class="fas ${icon}"></i>
452
+ </div>
453
+ </div>
454
+ <div class="text-sm text-gray-600">
455
+ ${getTestDetails(phase.name, isSuccess, hostInput, portsInput, urlInput)}
456
+ </div>
457
+ `;
458
+
459
+ testResults.appendChild(resultCard);
460
+
461
+ // Scroll to bottom
462
+ testResults.scrollTop = testResults.scrollHeight;
463
+
464
+ // If last phase, complete the test
465
+ if (index === testPhases.length - 1) {
466
+ setTimeout(() => {
467
+ progressBarFill.style.width = '100%';
468
+ progressPercent.textContent = '100%';
469
+ testStatus.innerHTML = '<span class="text-green-100">Tests completed</span>';
470
+
471
+ // Re-enable buttons
472
+ document.getElementById('runAllTestsBtn').disabled = false;
473
+ document.getElementById('exportResultsBtn').disabled = false;
474
+
475
+ showToast('All tests completed successfully', 'success');
476
+ }, 300);
477
+ }
478
+ }, testPhases.slice(0, index).reduce((sum, p) => sum + p.duration, 0));
479
+ });
480
+ });
481
+
482
+ // Helper function to generate test details
483
+ function getTestDetails(testName, isSuccess, host, ports, url) {
484
+ switch(testName) {
485
+ case 'Captive Portal Check':
486
+ return isSuccess ?
487
+ 'No captive portal detected. Internet access available.' :
488
+ 'Potential captive portal detected. Redirect may be required.';
489
+
490
+ case 'DNS Lookup':
491
+ return isSuccess ?
492
+ `DNS resolution for ${host} successful → ${generateRandomIP()}` :
493
+ `Failed to resolve ${host}. DNS server may be unavailable.`;
494
+
495
+ case 'Ping Test':
496
+ if (isSuccess) {
497
+ const min = (Math.random() * 10).toFixed(1);
498
+ const avg = (Math.random() * 20 + 10).toFixed(1);
499
+ const max = (Math.random() * 30 + 20).toFixed(1);
500
+ return `Ping statistics for ${host}: Packets: Sent=4, Received=4, Lost=0 (0% loss)<br>
501
+ Approximate round trip times: min=${min}ms, avg=${avg}ms, max=${max}ms`;
502
+ } else {
503
+ return `Ping request to ${host} failed. Host may be down or blocking ICMP.`;
504
+ }
505
+
506
+ case 'TCP Connect':
507
+ return isSuccess ?
508
+ `Successfully connected to ${host}:443 in ${(Math.random() * 100 + 50).toFixed(0)}ms` :
509
+ `Failed to connect to ${host}:443. Port may be closed or blocked.`;
510
+
511
+ case 'Port Scan':
512
+ const portList = ports.split(',').map(p => p.trim());
513
+ let results = [];
514
+ portList.forEach(port => {
515
+ const portSuccess = Math.random() > 0.3;
516
+ results.push(`${host}:${port} → ${portSuccess ? 'OPEN' : 'CLOSED'}`);
517
+ });
518
+ return results.join('<br>');
519
+
520
+ case 'HTTP Test':
521
+ return isSuccess ?
522
+ `HTTP GET ${url} → 200 OK (${(Math.random() * 300 + 100).toFixed(0)}ms)` :
523
+ `HTTP GET ${url} failed: Connection timed out`;
524
+
525
+ case 'Download Speed':
526
+ const speed = (Math.random() * 5000 + 1000).toFixed(1);
527
+ return isSuccess ?
528
+ `Estimated download speed: ${speed} KB/s (2.00 MB in ${(Math.random() * 2000 + 500).toFixed(0)}ms)` :
529
+ 'Download test failed: Connection interrupted';
530
+
531
+ default:
532
+ return 'Test completed';
533
+ }
534
+ }
535
+
536
+ function generateRandomIP() {
537
+ return `${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`;
538
+ }
539
+
540
+ // Export results
541
+ document.getElementById('exportResultsBtn').addEventListener('click', function() {
542
+ const testResults = document.getElementById('testResults');
543
+
544
+ if (testResults.textContent.includes('No test results yet')) {
545
+ showToast('No results to export. Run tests first.', 'error');
546
+ return;
547
+ }
548
+
549
+ // Simulate export
550
+ showToast('Results exported to CSV file', 'success');
551
+ });
552
+ </script>
553
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Rayan545454/sorry-1234" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
554
+ </html>