Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
feat: add bandwidth and latency testing functionality with progress tracking
Browse files- src/app.css +28 -0
- src/lib/index.ts +12 -7
- src/routes/+page.svelte +54 -10
src/app.css
CHANGED
@@ -1,2 +1,30 @@
|
|
1 |
@import 'tailwindcss';
|
2 |
@plugin '@tailwindcss/typography';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
@import 'tailwindcss';
|
2 |
@plugin '@tailwindcss/typography';
|
3 |
+
|
4 |
+
.progress-bar {
|
5 |
+
height: 9px;
|
6 |
+
border-radius: 4px;
|
7 |
+
background: linear-gradient(90deg, #4f46e5 0%, #10b981 50%, #f59e0b 100%);
|
8 |
+
transition: width 0.3s ease;
|
9 |
+
}
|
10 |
+
.wave {
|
11 |
+
position: relative;
|
12 |
+
overflow: hidden;
|
13 |
+
}
|
14 |
+
.wave::after {
|
15 |
+
content: "";
|
16 |
+
position: absolute;
|
17 |
+
top: 0;
|
18 |
+
left: 0;
|
19 |
+
right: 0;
|
20 |
+
bottom: 0;
|
21 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
22 |
+
animation: wave 2s linear infinite;
|
23 |
+
}
|
24 |
+
@keyframes wave {
|
25 |
+
0% { transform: translateX(-100%); }
|
26 |
+
100% { transform: translateX(100%); }
|
27 |
+
}
|
28 |
+
.glow {
|
29 |
+
box-shadow: 0 0 15px rgba(79, 70, 229, 0.5);
|
30 |
+
}
|
src/lib/index.ts
CHANGED
@@ -7,7 +7,7 @@ interface BandwidthCallback {
|
|
7 |
totalBytes: number,
|
8 |
bytesPerSecond: number,
|
9 |
done: boolean
|
10 |
-
):
|
11 |
}
|
12 |
|
13 |
export async function bandwidthTest(
|
@@ -18,8 +18,8 @@ export async function bandwidthTest(
|
|
18 |
performance.clearResourceTimings();
|
19 |
// start timer
|
20 |
const startTime = performance.now();
|
21 |
-
|
22 |
-
const url = 'https://cdn-test-cloudfront.hf.co/15mb.json';
|
23 |
const response = await fetch(url);
|
24 |
if (!response.ok) {
|
25 |
throw new Error(`Network response was not ok: ${response.status}`);
|
@@ -37,15 +37,15 @@ export async function bandwidthTest(
|
|
37 |
// }, 2000);
|
38 |
const latency = performance.now() - startTime;
|
39 |
onLatency(latency);
|
40 |
-
const contentLengthHeader = response.headers.get('
|
41 |
-
const totalBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) :
|
42 |
const reader = response.body.getReader();
|
43 |
let loadedBytes = 0;
|
44 |
let lastTimestamp = performance.now();
|
45 |
let lastLoaded = 0;
|
46 |
const REPORT_INTERVAL_MS = 500;
|
47 |
onProgress(latency, loadedBytes, totalBytes, 0, false);
|
48 |
-
let bytesPerSecond= 0;
|
49 |
while (true) {
|
50 |
const { done, value } = await reader.read();
|
51 |
if (done) {
|
@@ -71,7 +71,12 @@ export async function bandwidthTest(
|
|
71 |
|
72 |
// Invoke callback
|
73 |
const elapsedMs = performance.now() - startTime;
|
74 |
-
onProgress(elapsedMs, loadedBytes, totalBytes, bytesPerSecond, false);
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
// Reset our “last” markers
|
77 |
lastLoaded = loadedBytes;
|
|
|
7 |
totalBytes: number,
|
8 |
bytesPerSecond: number,
|
9 |
done: boolean
|
10 |
+
): boolean;
|
11 |
}
|
12 |
|
13 |
export async function bandwidthTest(
|
|
|
18 |
performance.clearResourceTimings();
|
19 |
// start timer
|
20 |
const startTime = performance.now();
|
21 |
+
const url = 'https://cdn-test-cloudfront.hf.co/5gb.safetensors';
|
22 |
+
// const url = 'https://cdn-test-cloudfront.hf.co/15mb.json';
|
23 |
const response = await fetch(url);
|
24 |
if (!response.ok) {
|
25 |
throw new Error(`Network response was not ok: ${response.status}`);
|
|
|
37 |
// }, 2000);
|
38 |
const latency = performance.now() - startTime;
|
39 |
onLatency(latency);
|
40 |
+
const contentLengthHeader = response.headers.get('content-length');
|
41 |
+
const totalBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 1e99;
|
42 |
const reader = response.body.getReader();
|
43 |
let loadedBytes = 0;
|
44 |
let lastTimestamp = performance.now();
|
45 |
let lastLoaded = 0;
|
46 |
const REPORT_INTERVAL_MS = 500;
|
47 |
onProgress(latency, loadedBytes, totalBytes, 0, false);
|
48 |
+
let bytesPerSecond = 0;
|
49 |
while (true) {
|
50 |
const { done, value } = await reader.read();
|
51 |
if (done) {
|
|
|
71 |
|
72 |
// Invoke callback
|
73 |
const elapsedMs = performance.now() - startTime;
|
74 |
+
const stop = onProgress(elapsedMs, loadedBytes, totalBytes, bytesPerSecond, false);
|
75 |
+
if (stop) {
|
76 |
+
// stop the test
|
77 |
+
console.log(`Stopping bandwidth test at ${loadedBytes} bytes after ${elapsedMs} ms`);
|
78 |
+
break;
|
79 |
+
}
|
80 |
|
81 |
// Reset our “last” markers
|
82 |
lastLoaded = loadedBytes;
|
src/routes/+page.svelte
CHANGED
@@ -5,11 +5,13 @@
|
|
5 |
|
6 |
Chart.register(...registerables);
|
7 |
|
8 |
-
let currentBandwidth = $state(
|
9 |
let currentLatency = $state(0);
|
|
|
10 |
let bandwidthMeasurements: number[] = $state([]);
|
11 |
let timeMeasurements: string[] = $state([]);
|
12 |
-
let
|
|
|
13 |
let mbps = (bw / 1000000 * 8); // convert Bps to Mbps
|
14 |
// update the bandwidth state
|
15 |
currentBandwidth = mbps.toFixed(2);
|
@@ -21,14 +23,38 @@
|
|
21 |
bandwidthMeasurements.shift();
|
22 |
timeMeasurements.shift();
|
23 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
};
|
25 |
let latencyCallback = (latency: number) => {
|
26 |
// update the latency state
|
27 |
currentLatency = latency;
|
28 |
-
console.log('Latency:', latency);
|
29 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
//counter(callback);
|
31 |
-
bandwidthTest(bandwidthCallback, latencyCallback);
|
32 |
|
33 |
let canvas: HTMLCanvasElement;
|
34 |
|
@@ -97,6 +123,22 @@
|
|
97 |
speedChart.destroy();
|
98 |
};
|
99 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
</script>
|
101 |
<!-- Main Card -->
|
102 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8 transition-all duration-300 hover:shadow-xl">
|
@@ -104,8 +146,8 @@
|
|
104 |
<div class="flex items-center justify-between mb-6">
|
105 |
<h2 class="text-xl font-semibold text-gray-800">Connection Statistics</h2>
|
106 |
<div id="connection-status" class="flex items-center">
|
107 |
-
<span class="h-3 w-3 rounded-full
|
108 |
-
<span class="text-sm text-gray-500">
|
109 |
</div>
|
110 |
</div>
|
111 |
|
@@ -132,22 +174,24 @@
|
|
132 |
<div class="mb-6">
|
133 |
<div class="flex justify-between mb-2">
|
134 |
<span class="text-sm font-medium text-gray-700">Test Progress</span>
|
135 |
-
<span id="progress-percent" class="text-sm font-medium text-gray-700">0%</span>
|
136 |
</div>
|
137 |
<div class="w-full bg-gray-200 rounded-full h-2.5">
|
138 |
-
<div id="progress-bar" class="progress-bar h-2.5 rounded-full
|
|
|
139 |
</div>
|
140 |
</div>
|
141 |
<!-- Test Controls -->
|
142 |
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
143 |
<button id="start-test"
|
144 |
-
class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center glow"
|
|
|
145 |
<i class="fas fa-play mr-2"></i>
|
146 |
Start Test
|
147 |
</button>
|
148 |
<button id="stop-test"
|
149 |
class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center"
|
150 |
-
disabled>
|
151 |
<i class="fas fa-stop mr-2"></i>
|
152 |
Stop Test
|
153 |
</button>
|
|
|
5 |
|
6 |
Chart.register(...registerables);
|
7 |
|
8 |
+
let currentBandwidth = $state('0');
|
9 |
let currentLatency = $state(0);
|
10 |
+
let progress = $state(0);
|
11 |
let bandwidthMeasurements: number[] = $state([]);
|
12 |
let timeMeasurements: string[] = $state([]);
|
13 |
+
let testStatus = $state('Idle');
|
14 |
+
let bandwidthCallback = (elapsedMs: number, loadedBytes: number, totalBytes: number, bw: number, done: boolean) => {
|
15 |
let mbps = (bw / 1000000 * 8); // convert Bps to Mbps
|
16 |
// update the bandwidth state
|
17 |
currentBandwidth = mbps.toFixed(2);
|
|
|
23 |
bandwidthMeasurements.shift();
|
24 |
timeMeasurements.shift();
|
25 |
}
|
26 |
+
// update the progress state
|
27 |
+
progress = (loadedBytes / totalBytes) * 100;
|
28 |
+
if (testStatus == 'Stopped') {
|
29 |
+
return true;
|
30 |
+
}
|
31 |
+
if (done) {
|
32 |
+
testStatus = 'Completed';
|
33 |
+
progress = 100;
|
34 |
+
} else {
|
35 |
+
testStatus = 'Running';
|
36 |
+
}
|
37 |
+
return false;
|
38 |
};
|
39 |
let latencyCallback = (latency: number) => {
|
40 |
// update the latency state
|
41 |
currentLatency = latency;
|
|
|
42 |
};
|
43 |
+
|
44 |
+
const startTest = () => {
|
45 |
+
testStatus = 'Running';
|
46 |
+
progress = 0;
|
47 |
+
bandwidthMeasurements = [];
|
48 |
+
timeMeasurements = [];
|
49 |
+
currentBandwidth = '0';
|
50 |
+
currentLatency = 0;
|
51 |
+
bandwidthTest(bandwidthCallback, latencyCallback);
|
52 |
+
};
|
53 |
+
const stopTest = () => {
|
54 |
+
testStatus = 'Stopped';
|
55 |
+
}
|
56 |
//counter(callback);
|
57 |
+
//bandwidthTest(bandwidthCallback, latencyCallback);
|
58 |
|
59 |
let canvas: HTMLCanvasElement;
|
60 |
|
|
|
123 |
speedChart.destroy();
|
124 |
};
|
125 |
};
|
126 |
+
const getStatusClass = () => {
|
127 |
+
switch (testStatus) {
|
128 |
+
case 'Idle':
|
129 |
+
return 'bg-gray-300';
|
130 |
+
case 'Running':
|
131 |
+
return 'bg-blue-500';
|
132 |
+
case 'Completed':
|
133 |
+
return 'bg-green-500';
|
134 |
+
case 'Error':
|
135 |
+
return 'bg-red-500';
|
136 |
+
case 'Stopped':
|
137 |
+
return 'bg-red-500';
|
138 |
+
default:
|
139 |
+
return 'bg-gray-300';
|
140 |
+
}
|
141 |
+
};
|
142 |
</script>
|
143 |
<!-- Main Card -->
|
144 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8 transition-all duration-300 hover:shadow-xl">
|
|
|
146 |
<div class="flex items-center justify-between mb-6">
|
147 |
<h2 class="text-xl font-semibold text-gray-800">Connection Statistics</h2>
|
148 |
<div id="connection-status" class="flex items-center">
|
149 |
+
<span class="h-3 w-3 rounded-full {getStatusClass()} mr-2"></span>
|
150 |
+
<span class="text-sm text-gray-500">{testStatus}</span>
|
151 |
</div>
|
152 |
</div>
|
153 |
|
|
|
174 |
<div class="mb-6">
|
175 |
<div class="flex justify-between mb-2">
|
176 |
<span class="text-sm font-medium text-gray-700">Test Progress</span>
|
177 |
+
<span id="progress-percent" class="text-sm font-medium text-gray-700">{progress.toFixed(0)}%</span>
|
178 |
</div>
|
179 |
<div class="w-full bg-gray-200 rounded-full h-2.5">
|
180 |
+
<div id="progress-bar" class="progress-bar h-2.5 rounded-full {progress<100 && progress >0? 'wave':''}"
|
181 |
+
style="width: {progress}%"></div>
|
182 |
</div>
|
183 |
</div>
|
184 |
<!-- Test Controls -->
|
185 |
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
186 |
<button id="start-test"
|
187 |
+
class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center glow"
|
188 |
+
onclick={startTest}>
|
189 |
<i class="fas fa-play mr-2"></i>
|
190 |
Start Test
|
191 |
</button>
|
192 |
<button id="stop-test"
|
193 |
class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center"
|
194 |
+
disabled={testStatus!=='Running'} onclick={stopTest}>
|
195 |
<i class="fas fa-stop mr-2"></i>
|
196 |
Stop Test
|
197 |
</button>
|