Spaces:
Running
Running
add progress bar
Browse files- index.html +33 -0
- wasm-demo.js +67 -0
index.html
CHANGED
@@ -308,6 +308,33 @@
|
|
308 |
color: #1976d2;
|
309 |
}
|
310 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
/* Responsive adjustments */
|
312 |
@media (max-width: 968px) {
|
313 |
.hero {
|
@@ -403,10 +430,16 @@
|
|
403 |
<section class="card" aria-labelledby="step3">
|
404 |
<h2 id="step3">3. Send to server</h2>
|
405 |
<div class="card-content">
|
|
|
|
|
|
|
406 |
<div>
|
407 |
<p id="srvStatus" class="status" aria-live="polite">Waiting for encrypted data… (server processing can take several minutes)</p>
|
408 |
<p id="srvComputing" class="status" aria-live="polite" hidden>Server computing…</p>
|
409 |
</div>
|
|
|
|
|
|
|
410 |
<div class="controls" style="margin-top: auto;">
|
411 |
<button id="btnSend" class="btn" disabled>📡 Send</button>
|
412 |
<span id="spin" class="loader" hidden aria-label="Working"></span>
|
|
|
308 |
color: #1976d2;
|
309 |
}
|
310 |
|
311 |
+
/* Progress bar styling */
|
312 |
+
.progress-container {
|
313 |
+
width: 100%;
|
314 |
+
background: var(--grey-100);
|
315 |
+
border-radius: 4px;
|
316 |
+
height: 12px;
|
317 |
+
margin: calc(var(--spacing-unit) * 2) 0;
|
318 |
+
overflow: hidden;
|
319 |
+
border: 1px solid var(--grey-200);
|
320 |
+
}
|
321 |
+
|
322 |
+
.progress-bar {
|
323 |
+
height: 100%;
|
324 |
+
background: var(--black);
|
325 |
+
border-radius: 3px;
|
326 |
+
transition: width 0.3s ease;
|
327 |
+
width: 0%;
|
328 |
+
}
|
329 |
+
|
330 |
+
.processing-note {
|
331 |
+
font-size: 0.9rem;
|
332 |
+
color: var(--black);
|
333 |
+
opacity: 0.7;
|
334 |
+
line-height: 1.4;
|
335 |
+
margin-bottom: calc(var(--spacing-unit) * 2);
|
336 |
+
}
|
337 |
+
|
338 |
/* Responsive adjustments */
|
339 |
@media (max-width: 968px) {
|
340 |
.hero {
|
|
|
430 |
<section class="card" aria-labelledby="step3">
|
431 |
<h2 id="step3">3. Send to server</h2>
|
432 |
<div class="card-content">
|
433 |
+
<div class="processing-note" id="processingNote" hidden>
|
434 |
+
<strong>Estimated processing time:</strong> <span id="estimatedTime">0</span> seconds
|
435 |
+
</div>
|
436 |
<div>
|
437 |
<p id="srvStatus" class="status" aria-live="polite">Waiting for encrypted data… (server processing can take several minutes)</p>
|
438 |
<p id="srvComputing" class="status" aria-live="polite" hidden>Server computing…</p>
|
439 |
</div>
|
440 |
+
<div class="progress-container" id="progressContainer" hidden>
|
441 |
+
<div class="progress-bar" id="progressBar"></div>
|
442 |
+
</div>
|
443 |
<div class="controls" style="margin-top: auto;">
|
444 |
<button id="btnSend" class="btn" disabled>📡 Send</button>
|
445 |
<span id="spin" class="loader" hidden aria-label="Working"></span>
|
wasm-demo.js
CHANGED
@@ -11,6 +11,8 @@ let keygenWorker;
|
|
11 |
let encryptWorker;
|
12 |
let sessionUid;
|
13 |
let taskId;
|
|
|
|
|
14 |
|
15 |
// Memory-efficient base64 encoding for large Uint8Array
|
16 |
function uint8ToBase64(uint8) {
|
@@ -188,8 +190,16 @@ $('tokenInput').addEventListener('input', () => {
|
|
188 |
try {
|
189 |
const tokenIds = llama3Tokenizer.encode(text);
|
190 |
const tokenCount = tokenIds.length;
|
|
|
191 |
const TOKEN_LIMIT = 16;
|
192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
if (tokenCount > TOKEN_LIMIT) {
|
194 |
$('encStatus').textContent = `⚠️ ${tokenCount}/${TOKEN_LIMIT} tokens - text too long`;
|
195 |
$('encStatus').style.color = '#d32f2f';
|
@@ -203,10 +213,12 @@ $('tokenInput').addEventListener('input', () => {
|
|
203 |
} catch (e) {
|
204 |
// Tokenizer might not be ready yet
|
205 |
$('encStatus').textContent = '';
|
|
|
206 |
}
|
207 |
} else {
|
208 |
$('encStatus').textContent = '';
|
209 |
$('encStatus').style.color = '';
|
|
|
210 |
}
|
211 |
});
|
212 |
|
@@ -273,8 +285,19 @@ async function pollTaskStatus(currentTaskId, currentUid) {
|
|
273 |
showComputing = true;
|
274 |
} else if (statusData.status === 'success' || statusData.status === 'completed') {
|
275 |
userMessage = 'Processing complete!';
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
} else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
|
277 |
userMessage = 'Task failed. Please try again.';
|
|
|
|
|
|
|
|
|
|
|
278 |
} else {
|
279 |
// Fallback for any other status
|
280 |
userMessage = `Status: ${statusData.status}`;
|
@@ -288,6 +311,11 @@ async function pollTaskStatus(currentTaskId, currentUid) {
|
|
288 |
} else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
|
289 |
console.error('[Poll] Task failed or unrecoverable:', statusData);
|
290 |
show('spin', false);
|
|
|
|
|
|
|
|
|
|
|
291 |
return null;
|
292 |
} else {
|
293 |
setTimeout(() => pollTaskStatus(currentTaskId, currentUid).then(finalStatus => {
|
@@ -301,10 +329,24 @@ async function pollTaskStatus(currentTaskId, currentUid) {
|
|
301 |
console.error('[Poll] Polling exception:', e);
|
302 |
$('srvStatus').textContent = 'Connection error. Please check your network.';
|
303 |
show('spin', false);
|
|
|
|
|
|
|
|
|
|
|
304 |
return null;
|
305 |
}
|
306 |
}
|
307 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
308 |
async function getTaskResult(currentTaskId, currentUid, taskName) {
|
309 |
$('srvStatus').textContent = 'Retrieving results...';
|
310 |
try {
|
@@ -329,7 +371,12 @@ async function getTaskResult(currentTaskId, currentUid, taskName) {
|
|
329 |
$('srvStatus').textContent = 'Failed to retrieve results. Please try again.';
|
330 |
} finally {
|
331 |
show('spin', false);
|
|
|
332 |
$('srvComputing').hidden = true;
|
|
|
|
|
|
|
|
|
333 |
}
|
334 |
}
|
335 |
|
@@ -345,9 +392,19 @@ $('btnSend').onclick = async () => {
|
|
345 |
|
346 |
show('encIcon', false);
|
347 |
show('spin', true);
|
|
|
|
|
|
|
348 |
$('srvStatus').textContent = 'Sending encrypted data...';
|
349 |
$('srvComputing').hidden = true; // Ensure it's hidden initially
|
350 |
window.taskStartTime = performance.now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
351 |
|
352 |
try {
|
353 |
const formData = new FormData();
|
@@ -372,6 +429,11 @@ $('btnSend').onclick = async () => {
|
|
372 |
taskId = newTaskId;
|
373 |
console.log('[Main] Task submitted to server. Task ID:', taskId);
|
374 |
$('srvStatus').textContent = 'Request submitted. Checking status...';
|
|
|
|
|
|
|
|
|
|
|
375 |
|
376 |
pollTaskStatus(taskId, sessionUid).then(finalStatus => {
|
377 |
if (finalStatus && (finalStatus.status === 'success' || finalStatus.status === 'completed')) {
|
@@ -384,7 +446,12 @@ $('btnSend').onclick = async () => {
|
|
384 |
console.error(`[Main] Task submission failed after ${duration}s:`, e);
|
385 |
$('srvStatus').textContent = 'Failed to submit request. Please try again.';
|
386 |
show('spin', false);
|
|
|
387 |
$('srvComputing').hidden = true;
|
|
|
|
|
|
|
|
|
388 |
}
|
389 |
};
|
390 |
|
|
|
11 |
let encryptWorker;
|
12 |
let sessionUid;
|
13 |
let taskId;
|
14 |
+
let currentTokenCount = 0;
|
15 |
+
let progressTimer;
|
16 |
|
17 |
// Memory-efficient base64 encoding for large Uint8Array
|
18 |
function uint8ToBase64(uint8) {
|
|
|
190 |
try {
|
191 |
const tokenIds = llama3Tokenizer.encode(text);
|
192 |
const tokenCount = tokenIds.length;
|
193 |
+
currentTokenCount = tokenCount;
|
194 |
const TOKEN_LIMIT = 16;
|
195 |
|
196 |
+
// Update estimated processing time
|
197 |
+
const estimatedSeconds = tokenCount * 30;
|
198 |
+
const minutes = Math.floor(estimatedSeconds / 60);
|
199 |
+
const seconds = estimatedSeconds % 60;
|
200 |
+
const timeText = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
201 |
+
$('estimatedTime').textContent = timeText;
|
202 |
+
|
203 |
if (tokenCount > TOKEN_LIMIT) {
|
204 |
$('encStatus').textContent = `⚠️ ${tokenCount}/${TOKEN_LIMIT} tokens - text too long`;
|
205 |
$('encStatus').style.color = '#d32f2f';
|
|
|
213 |
} catch (e) {
|
214 |
// Tokenizer might not be ready yet
|
215 |
$('encStatus').textContent = '';
|
216 |
+
currentTokenCount = 0;
|
217 |
}
|
218 |
} else {
|
219 |
$('encStatus').textContent = '';
|
220 |
$('encStatus').style.color = '';
|
221 |
+
currentTokenCount = 0;
|
222 |
}
|
223 |
});
|
224 |
|
|
|
285 |
showComputing = true;
|
286 |
} else if (statusData.status === 'success' || statusData.status === 'completed') {
|
287 |
userMessage = 'Processing complete!';
|
288 |
+
// Set progress to 100% and clear timer
|
289 |
+
$('progressBar').style.width = '100%';
|
290 |
+
if (progressTimer) {
|
291 |
+
clearInterval(progressTimer);
|
292 |
+
progressTimer = null;
|
293 |
+
}
|
294 |
} else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
|
295 |
userMessage = 'Task failed. Please try again.';
|
296 |
+
// Clear timer on failure
|
297 |
+
if (progressTimer) {
|
298 |
+
clearInterval(progressTimer);
|
299 |
+
progressTimer = null;
|
300 |
+
}
|
301 |
} else {
|
302 |
// Fallback for any other status
|
303 |
userMessage = `Status: ${statusData.status}`;
|
|
|
311 |
} else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
|
312 |
console.error('[Poll] Task failed or unrecoverable:', statusData);
|
313 |
show('spin', false);
|
314 |
+
show('progressContainer', false);
|
315 |
+
if (progressTimer) {
|
316 |
+
clearInterval(progressTimer);
|
317 |
+
progressTimer = null;
|
318 |
+
}
|
319 |
return null;
|
320 |
} else {
|
321 |
setTimeout(() => pollTaskStatus(currentTaskId, currentUid).then(finalStatus => {
|
|
|
329 |
console.error('[Poll] Polling exception:', e);
|
330 |
$('srvStatus').textContent = 'Connection error. Please check your network.';
|
331 |
show('spin', false);
|
332 |
+
show('progressContainer', false);
|
333 |
+
if (progressTimer) {
|
334 |
+
clearInterval(progressTimer);
|
335 |
+
progressTimer = null;
|
336 |
+
}
|
337 |
return null;
|
338 |
}
|
339 |
}
|
340 |
|
341 |
+
function updateProgressBar() {
|
342 |
+
if (!window.taskStartTime || !window.expectedDuration) return;
|
343 |
+
|
344 |
+
const elapsed = performance.now() - window.taskStartTime;
|
345 |
+
const progress = Math.min((elapsed / window.expectedDuration) * 100, 95); // Cap at 95% until completion
|
346 |
+
$('progressBar').style.width = `${progress}%`;
|
347 |
+
console.log(`[Progress] ${progress.toFixed(1)}% (${(elapsed/1000).toFixed(1)}s / ${(window.expectedDuration/1000).toFixed(1)}s)`);
|
348 |
+
}
|
349 |
+
|
350 |
async function getTaskResult(currentTaskId, currentUid, taskName) {
|
351 |
$('srvStatus').textContent = 'Retrieving results...';
|
352 |
try {
|
|
|
371 |
$('srvStatus').textContent = 'Failed to retrieve results. Please try again.';
|
372 |
} finally {
|
373 |
show('spin', false);
|
374 |
+
show('progressContainer', false);
|
375 |
$('srvComputing').hidden = true;
|
376 |
+
if (progressTimer) {
|
377 |
+
clearInterval(progressTimer);
|
378 |
+
progressTimer = null;
|
379 |
+
}
|
380 |
}
|
381 |
}
|
382 |
|
|
|
392 |
|
393 |
show('encIcon', false);
|
394 |
show('spin', true);
|
395 |
+
show('processingNote', true);
|
396 |
+
show('progressContainer', true); // Show progress container immediately
|
397 |
+
$('progressBar').style.width = '0%'; // Reset progress bar
|
398 |
$('srvStatus').textContent = 'Sending encrypted data...';
|
399 |
$('srvComputing').hidden = true; // Ensure it's hidden initially
|
400 |
window.taskStartTime = performance.now();
|
401 |
+
window.expectedDuration = currentTokenCount * 30 * 1000; // Convert to milliseconds
|
402 |
+
|
403 |
+
// Clear any existing progress timer
|
404 |
+
if (progressTimer) {
|
405 |
+
clearInterval(progressTimer);
|
406 |
+
progressTimer = null;
|
407 |
+
}
|
408 |
|
409 |
try {
|
410 |
const formData = new FormData();
|
|
|
429 |
taskId = newTaskId;
|
430 |
console.log('[Main] Task submitted to server. Task ID:', taskId);
|
431 |
$('srvStatus').textContent = 'Request submitted. Checking status...';
|
432 |
+
|
433 |
+
// Start progress bar updates
|
434 |
+
if (!progressTimer) {
|
435 |
+
progressTimer = setInterval(updateProgressBar, 1000);
|
436 |
+
}
|
437 |
|
438 |
pollTaskStatus(taskId, sessionUid).then(finalStatus => {
|
439 |
if (finalStatus && (finalStatus.status === 'success' || finalStatus.status === 'completed')) {
|
|
|
446 |
console.error(`[Main] Task submission failed after ${duration}s:`, e);
|
447 |
$('srvStatus').textContent = 'Failed to submit request. Please try again.';
|
448 |
show('spin', false);
|
449 |
+
show('progressContainer', false);
|
450 |
$('srvComputing').hidden = true;
|
451 |
+
if (progressTimer) {
|
452 |
+
clearInterval(progressTimer);
|
453 |
+
progressTimer = null;
|
454 |
+
}
|
455 |
}
|
456 |
};
|
457 |
|