jfrery-zama commited on
Commit
0cdcb6f
·
unverified ·
1 Parent(s): 91e3ad8

improve ux

Browse files
Files changed (2) hide show
  1. index.html +39 -1
  2. wasm-demo.js +73 -2
index.html CHANGED
@@ -71,6 +71,8 @@
71
  font-family: 'Telegraf', sans-serif;
72
  cursor: pointer;
73
  transition: all .2s ease;
 
 
74
  }
75
  .contact-btn:hover {
76
  background: var(--black);
@@ -253,6 +255,38 @@
253
  flex-shrink: 0;
254
  }
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  footer {
257
  font-size: 1.2rem;
258
  text-align: center;
@@ -351,7 +385,7 @@
351
  <!-- Navbar -->
352
  <nav class="navbar">
353
  <div class="logo">ZAMA</div>
354
- <button class="contact-btn">Contact us</button>
355
  </nav>
356
 
357
  <!-- Hero -->
@@ -433,6 +467,10 @@ sequenceDiagram
433
  <p id="srvComputing" class="status" aria-live="polite" hidden>Server computing...</p>
434
  <p id="srvProgress" class="status" aria-live="polite" hidden></p>
435
  </div>
 
 
 
 
436
  <div class="controls" style="margin-top: auto;">
437
  <button id="btnSend" class="btn" disabled>📡 Send</button>
438
  <span id="spin" class="loader" hidden aria-label="Working"></span>
 
71
  font-family: 'Telegraf', sans-serif;
72
  cursor: pointer;
73
  transition: all .2s ease;
74
+ text-decoration: none;
75
+ display: inline-block;
76
  }
77
  .contact-btn:hover {
78
  background: var(--black);
 
255
  flex-shrink: 0;
256
  }
257
 
258
+ /* Progress bar styles */
259
+ .progress-container {
260
+ width: 100%;
261
+ background: var(--grey-200);
262
+ border: 2px solid var(--black);
263
+ height: 24px;
264
+ position: relative;
265
+ overflow: hidden;
266
+ display: none;
267
+ }
268
+
269
+ .progress-bar {
270
+ height: 100%;
271
+ background: var(--black);
272
+ width: 0%;
273
+ transition: width 0.5s ease;
274
+ position: relative;
275
+ }
276
+
277
+ .progress-text {
278
+ position: absolute;
279
+ top: 50%;
280
+ left: 50%;
281
+ transform: translate(-50%, -50%);
282
+ color: var(--black);
283
+ font-size: 0.9rem;
284
+ font-weight: 600;
285
+ z-index: 1;
286
+ mix-blend-mode: difference;
287
+ filter: invert(1);
288
+ }
289
+
290
  footer {
291
  font-size: 1.2rem;
292
  text-align: center;
 
385
  <!-- Navbar -->
386
  <nav class="navbar">
387
  <div class="logo">ZAMA</div>
388
+ <a href="https://www.zama.ai/contact" class="contact-btn" target="_blank" rel="noopener noreferrer">Contact us</a>
389
  </nav>
390
 
391
  <!-- Hero -->
 
467
  <p id="srvComputing" class="status" aria-live="polite" hidden>Server computing...</p>
468
  <p id="srvProgress" class="status" aria-live="polite" hidden></p>
469
  </div>
470
+ <div class="progress-container" id="progressContainer">
471
+ <div class="progress-bar" id="progressBar"></div>
472
+ <div class="progress-text" id="progressText">0%</div>
473
+ </div>
474
  <div class="controls" style="margin-top: auto;">
475
  <button id="btnSend" class="btn" disabled>📡 Send</button>
476
  <span id="spin" class="loader" hidden aria-label="Working"></span>
wasm-demo.js CHANGED
@@ -11,6 +11,7 @@ let keygenWorker;
11
  let encryptWorker;
12
  let sessionUid;
13
  let taskId;
 
14
 
15
  // Memory-efficient base64 encoding for large Uint8Array
16
  function uint8ToBase64(uint8) {
@@ -30,6 +31,60 @@ const $ = id => document.getElementById(id);
30
  const enable = (id, ok=true) => $(id).disabled = !ok;
31
  const show = (id, visible=true) => $(id).hidden = !visible;
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  // Hide all spinners immediately
34
  show('keygenSpin', false);
35
  show('spin', false);
@@ -66,7 +121,7 @@ show('tokenizerSpin', false);
66
  show('encIcon', true);
67
  enable('btnEncrypt', true);
68
  enable('btnSend');
69
- $('encStatus').textContent = 'Your text is encrypted 🔒';
70
  } else if (e.data.type === 'error') {
71
  console.error('[Main] Encryption error:', e.data.error);
72
  show('encryptSpin', false);
@@ -160,6 +215,7 @@ $('btnEncrypt').onclick = async () => {
160
  try {
161
  console.log('[Main] Tokenizing text:', text);
162
  const tokenIds = llama3Tokenizer.encode(text);
 
163
  console.log('[Main] Token IDs:', tokenIds);
164
 
165
  encryptWorker.postMessage({ type: 'encrypt', tokenIds });
@@ -179,6 +235,8 @@ async function pollTaskStatus(currentTaskId, currentUid) {
179
  console.error(`[Poll] Error fetching status: ${statusResponse.status} ${errorText}`);
180
  $('srvStatus').textContent = `Status check error: ${statusResponse.status}`;
181
  show('spin', false);
 
 
182
  return null;
183
  }
184
 
@@ -187,11 +245,14 @@ async function pollTaskStatus(currentTaskId, currentUid) {
187
  $('srvStatus').textContent = `Status: ${statusData.status} - ${statusData.details}`;
188
 
189
  if (statusData.status === 'success' || statusData.status === 'completed') {
 
190
  return statusData;
191
  } else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
192
  console.error('[Poll] Task failed or unrecoverable:', statusData);
193
  $('srvStatus').textContent = `Task failed: ${statusData.status}`;
194
  show('spin', false);
 
 
195
  return null;
196
  } else {
197
  setTimeout(() => pollTaskStatus(currentTaskId, currentUid).then(finalStatus => {
@@ -205,6 +266,8 @@ async function pollTaskStatus(currentTaskId, currentUid) {
205
  console.error('[Poll] Polling exception:', e);
206
  $('srvStatus').textContent = `Polling error: ${e.message}`;
207
  show('spin', false);
 
 
208
  return null;
209
  }
210
  }
@@ -234,6 +297,8 @@ async function getTaskResult(currentTaskId, currentUid, taskName) {
234
  } finally {
235
  show('spin', false);
236
  $('srvComputing').hidden = true;
 
 
237
  }
238
  }
239
 
@@ -252,6 +317,10 @@ $('btnSend').onclick = async () => {
252
  $('srvStatus').textContent = 'Submitting task…';
253
  $('srvComputing').hidden = true;
254
  window.taskStartTime = performance.now();
 
 
 
 
255
 
256
  try {
257
  const formData = new FormData();
@@ -290,6 +359,8 @@ $('btnSend').onclick = async () => {
290
  $('srvStatus').textContent = `Task submission error: ${e.message} (${duration}s)`;
291
  show('spin', false);
292
  $('srvComputing').hidden = true;
 
 
293
  }
294
  };
295
 
@@ -306,4 +377,4 @@ $('btnDecrypt').onclick = () => {
306
  console.error('[Main] Decryption error:', e);
307
  $('decResult').textContent = `Decryption failed: ${e.message}`;
308
  }
309
- };
 
11
  let encryptWorker;
12
  let sessionUid;
13
  let taskId;
14
+ let currentTokenCount = 0; // Track token count for progress estimation
15
 
16
  // Memory-efficient base64 encoding for large Uint8Array
17
  function uint8ToBase64(uint8) {
 
31
  const enable = (id, ok=true) => $(id).disabled = !ok;
32
  const show = (id, visible=true) => $(id).hidden = !visible;
33
 
34
+ // Progress bar functions
35
+ function showProgress() {
36
+ const container = $('progressContainer');
37
+ const bar = $('progressBar');
38
+ const text = $('progressText');
39
+
40
+ container.style.display = 'block';
41
+ bar.style.width = '0%';
42
+ text.textContent = '0%';
43
+ }
44
+
45
+ function hideProgress() {
46
+ const container = $('progressContainer');
47
+ container.style.display = 'none';
48
+ }
49
+
50
+ function updateProgress(percent) {
51
+ const bar = $('progressBar');
52
+ const text = $('progressText');
53
+
54
+ bar.style.width = percent + '%';
55
+ text.textContent = Math.round(percent) + '%';
56
+ }
57
+
58
+ // Progress animation based on token count
59
+ let progressInterval;
60
+ function startProgressAnimation(tokenCount) {
61
+ const estimatedTimePerToken = 30000; // 30 seconds per token in milliseconds
62
+ const totalEstimatedTime = tokenCount * estimatedTimePerToken;
63
+ const startTime = Date.now();
64
+
65
+ progressInterval = setInterval(() => {
66
+ const elapsed = Date.now() - startTime;
67
+ const progress = Math.min((elapsed / totalEstimatedTime) * 100, 95); // Cap at 95% until actual completion
68
+ updateProgress(progress);
69
+
70
+ // Update status message with time estimate
71
+ const remainingTime = Math.max(0, totalEstimatedTime - elapsed);
72
+ const remainingSeconds = Math.ceil(remainingTime / 1000);
73
+ if (remainingSeconds > 0) {
74
+ $('srvProgress').textContent = `Processing ${tokenCount} tokens... (~${remainingSeconds}s remaining)`;
75
+ show('srvProgress', true);
76
+ }
77
+ }, 100); // Update every 100ms for smooth animation
78
+ }
79
+
80
+ function stopProgressAnimation() {
81
+ if (progressInterval) {
82
+ clearInterval(progressInterval);
83
+ progressInterval = null;
84
+ }
85
+ show('srvProgress', false);
86
+ }
87
+
88
  // Hide all spinners immediately
89
  show('keygenSpin', false);
90
  show('spin', false);
 
121
  show('encIcon', true);
122
  enable('btnEncrypt', true);
123
  enable('btnSend');
124
+ $('encStatus').textContent = `Your text is encrypted 🔒 (${currentTokenCount} tokens)`;
125
  } else if (e.data.type === 'error') {
126
  console.error('[Main] Encryption error:', e.data.error);
127
  show('encryptSpin', false);
 
215
  try {
216
  console.log('[Main] Tokenizing text:', text);
217
  const tokenIds = llama3Tokenizer.encode(text);
218
+ currentTokenCount = tokenIds.length; // Store token count
219
  console.log('[Main] Token IDs:', tokenIds);
220
 
221
  encryptWorker.postMessage({ type: 'encrypt', tokenIds });
 
235
  console.error(`[Poll] Error fetching status: ${statusResponse.status} ${errorText}`);
236
  $('srvStatus').textContent = `Status check error: ${statusResponse.status}`;
237
  show('spin', false);
238
+ stopProgressAnimation();
239
+ hideProgress();
240
  return null;
241
  }
242
 
 
245
  $('srvStatus').textContent = `Status: ${statusData.status} - ${statusData.details}`;
246
 
247
  if (statusData.status === 'success' || statusData.status === 'completed') {
248
+ updateProgress(100); // Complete the progress bar
249
  return statusData;
250
  } else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
251
  console.error('[Poll] Task failed or unrecoverable:', statusData);
252
  $('srvStatus').textContent = `Task failed: ${statusData.status}`;
253
  show('spin', false);
254
+ stopProgressAnimation();
255
+ hideProgress();
256
  return null;
257
  } else {
258
  setTimeout(() => pollTaskStatus(currentTaskId, currentUid).then(finalStatus => {
 
266
  console.error('[Poll] Polling exception:', e);
267
  $('srvStatus').textContent = `Polling error: ${e.message}`;
268
  show('spin', false);
269
+ stopProgressAnimation();
270
+ hideProgress();
271
  return null;
272
  }
273
  }
 
297
  } finally {
298
  show('spin', false);
299
  $('srvComputing').hidden = true;
300
+ stopProgressAnimation();
301
+ hideProgress();
302
  }
303
  }
304
 
 
317
  $('srvStatus').textContent = 'Submitting task…';
318
  $('srvComputing').hidden = true;
319
  window.taskStartTime = performance.now();
320
+
321
+ // Show and start progress animation
322
+ showProgress();
323
+ startProgressAnimation(currentTokenCount);
324
 
325
  try {
326
  const formData = new FormData();
 
359
  $('srvStatus').textContent = `Task submission error: ${e.message} (${duration}s)`;
360
  show('spin', false);
361
  $('srvComputing').hidden = true;
362
+ stopProgressAnimation();
363
+ hideProgress();
364
  }
365
  };
366
 
 
377
  console.error('[Main] Decryption error:', e);
378
  $('decResult').textContent = `Decryption failed: ${e.message}`;
379
  }
380
+ };