ruidiao commited on
Commit
d635592
·
1 Parent(s): 609c06b

Upload 3 files

Browse files
Files changed (3) hide show
  1. index.html +1 -1
  2. main.js +24 -14
  3. worker.js +73 -10
index.html CHANGED
@@ -22,7 +22,7 @@
22
  <img src="icons/x.svg" alt="X" class="h-6 w-6" />
23
  </a>
24
  </div>
25
- <div id="delete-button-container" class="flex justify-end hidden">
26
  <button id="delete-data-button" class="bg-red-500 text-white px-3 py-1 rounded-lg text-sm hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500">Delete Cached Data</button>
27
  </div>
28
  </div>
 
22
  <img src="icons/x.svg" alt="X" class="h-6 w-6" />
23
  </a>
24
  </div>
25
+ <div id="delete-button-container" class="flex justify-end">
26
  <button id="delete-data-button" class="bg-red-500 text-white px-3 py-1 rounded-lg text-sm hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500">Delete Cached Data</button>
27
  </div>
28
  </div>
main.js CHANGED
@@ -10,10 +10,12 @@ const deleteButtonContainer = document.getElementById('delete-button-container')
10
 
11
  const worker = new Worker('worker.js', { type: 'module' });
12
 
 
13
  // Initial state
14
  // searchInput.disabled = true; // Removed to enable during initialization
15
  // searchButton.disabled = true; // Removed to enable during initialization
16
- deleteButtonContainer.classList.add('hidden');
 
17
 
18
  // Helper to format bytes to MB
19
  function formatBytesToMB(bytes) {
@@ -36,7 +38,7 @@ worker.onmessage = (event) => {
36
  progressContainer.classList.add('hidden');
37
  searchInput.disabled = false;
38
  searchButton.disabled = false;
39
- deleteButtonContainer.classList.remove('hidden'); // Ensure button is shown when index is ready
40
  } else if (type === 'results') {
41
  // statusDiv.textContent = 'Search complete.'; // Removed as results themselves convey completion
42
  progressContainer.classList.add('hidden'); // Hide progress bar
@@ -44,9 +46,8 @@ worker.onmessage = (event) => {
44
  statusDiv.textContent = ''; // Clear status after search is complete
45
  deleteButtonContainer.classList.remove('hidden'); // Ensure delete button is visible
46
  } else if (type === 'indexSize') {
47
- if (payload.indexCached || payload.modelCached) {
48
- deleteButtonContainer.classList.remove('hidden'); // Ensure button is shown if index or model is cached
49
- }
50
 
51
  if (payload.indexCached) {
52
  dataInfoDiv.textContent = ''; // Clear data info if cached
@@ -59,7 +60,7 @@ worker.onmessage = (event) => {
59
  }
60
  } else {
61
  dataInfoDiv.textContent = ''; // Clear data info if not cached
62
- deleteButtonContainer.classList.add('hidden'); // Hide delete button if not cached
63
  searchInput.disabled = false; // Ensure input is enabled
64
  searchButton.disabled = false; // Ensure button is enabled
65
  // Render message with an inline info icon that has an accessible tooltip
@@ -75,14 +76,20 @@ worker.onmessage = (event) => {
75
  </span>`;
76
  }
77
  } else if (type === 'dataDeleted') {
 
78
  statusDiv.textContent = payload;
79
  dataInfoDiv.textContent = ''; // Clear data info
80
- deleteButtonContainer.classList.add('hidden');
81
- // searchInput.disabled = true; // Removed: state will be set by getIndexSize
82
- // searchButton.disabled = true; // Removed: state will be set by getIndexSize
83
  progressContainer.classList.add('hidden'); // Hide progress bar
84
- // Re-request size info after deletion
85
- worker.postMessage({ type: 'getIndexSize' });
 
 
 
 
 
 
 
 
86
  } else if (type === 'error') {
87
  statusDiv.textContent = `Error: ${payload}`;
88
  progressContainer.classList.add('hidden');
@@ -116,10 +123,13 @@ searchInput.addEventListener('keydown', (e) => {
116
  }
117
  });
118
 
 
119
  deleteDataButton.addEventListener('click', () => {
120
- if (confirm('Are you sure you want to delete the cached data?')) {
121
- worker.postMessage({ type: 'deleteData' });
122
- }
 
 
123
  });
124
 
125
  function renderResults(results) {
 
10
 
11
  const worker = new Worker('worker.js', { type: 'module' });
12
 
13
+ deleteButtonContainer.classList.add('hidden');
14
  // Initial state
15
  // searchInput.disabled = true; // Removed to enable during initialization
16
  // searchButton.disabled = true; // Removed to enable during initialization
17
+ // Ensure delete button is always visible (UI-level control). Worker will decide if data exists.
18
+ deleteButtonContainer.classList.remove('hidden');
19
 
20
  // Helper to format bytes to MB
21
  function formatBytesToMB(bytes) {
 
38
  progressContainer.classList.add('hidden');
39
  searchInput.disabled = false;
40
  searchButton.disabled = false;
41
+ // Delete button remains visible at all times per UI requirement
42
  } else if (type === 'results') {
43
  // statusDiv.textContent = 'Search complete.'; // Removed as results themselves convey completion
44
  progressContainer.classList.add('hidden'); // Hide progress bar
 
46
  statusDiv.textContent = ''; // Clear status after search is complete
47
  deleteButtonContainer.classList.remove('hidden'); // Ensure delete button is visible
48
  } else if (type === 'indexSize') {
49
+ // Keep delete button visible always. Worker tells us whether data is cached.
50
+ // Visual enable/disable could be added later if desired.
 
51
 
52
  if (payload.indexCached) {
53
  dataInfoDiv.textContent = ''; // Clear data info if cached
 
60
  }
61
  } else {
62
  dataInfoDiv.textContent = ''; // Clear data info if not cached
63
+ // Keep delete button visible even if no cached data
64
  searchInput.disabled = false; // Ensure input is enabled
65
  searchButton.disabled = false; // Ensure button is enabled
66
  // Render message with an inline info icon that has an accessible tooltip
 
76
  </span>`;
77
  }
78
  } else if (type === 'dataDeleted') {
79
+ // Show confirmation message briefly and then reload the page so the UI and worker state fully reset
80
  statusDiv.textContent = payload;
81
  dataInfoDiv.textContent = ''; // Clear data info
 
 
 
82
  progressContainer.classList.add('hidden'); // Hide progress bar
83
+ // Give the user a moment to see the confirmation, then reload to clear caches and restart the worker
84
+ setTimeout(() => {
85
+ // A hard reload ensures service worker/worker gets a clean start in some environments
86
+ try {
87
+ location.reload();
88
+ } catch (e) {
89
+ // If reload fails for any reason, fall back to re-requesting index size
90
+ worker.postMessage({ type: 'getIndexSize' });
91
+ }
92
+ }, 700);
93
  } else if (type === 'error') {
94
  statusDiv.textContent = `Error: ${payload}`;
95
  progressContainer.classList.add('hidden');
 
123
  }
124
  });
125
 
126
+ // Make confirm non-blocking so the click handler returns quickly (avoids long task warnings)
127
  deleteDataButton.addEventListener('click', () => {
128
+ setTimeout(() => {
129
+ if (confirm('Are you sure you want to delete the cached data?')) {
130
+ worker.postMessage({ type: 'deleteData' });
131
+ }
132
+ }, 0);
133
  });
134
 
135
  function renderResults(results) {
worker.js CHANGED
@@ -228,21 +228,84 @@ self.onmessage = async (event) => {
228
  self.postMessage({ type: 'results', payload: results });
229
  } else if (type === 'deleteData') {
230
  self.postMessage({ type: 'loading', payload: 'Deleting cached data...' });
231
- try {
232
- await deleteFromDB('quoteIndexData');
 
 
 
 
 
 
233
 
234
- // Attempt to clear transformers.js model cache
235
- const cacheNames = await caches.keys();
236
- for (const cacheName of cacheNames) {
237
- if (cacheName.startsWith('transformers-cache') || cacheName.includes(MODEL_NAME.replace(/\//g, '-'))) {
238
- await caches.delete(cacheName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  }
 
 
 
240
  }
241
- indexData = null; // Clear in-memory data
242
- isReady = false; // Mark as not ready until reloaded
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  self.postMessage({ type: 'dataDeleted', payload: 'Cached data deleted successfully.' });
244
  } catch (error) {
245
- self.postMessage({ type: 'error', payload: 'Failed to delete cached data.' });
 
 
 
 
 
 
 
246
  }
247
  } else if (type === 'getIndexSize') {
248
  try {
 
228
  self.postMessage({ type: 'results', payload: results });
229
  } else if (type === 'deleteData') {
230
  self.postMessage({ type: 'loading', payload: 'Deleting cached data...' });
231
+ // Run cleanup with a timeout so UI doesn't get stuck if some API call blocks or takes too long
232
+ const cleanup = async () => {
233
+ // 1) Delete the specific object from the DB if present
234
+ try {
235
+ await deleteFromDB('quoteIndexData');
236
+ } catch (e) {
237
+ // ignore individual delete error and proceed to full DB deletion
238
+ }
239
 
240
+ // 2) Delete the entire IndexedDB database to ensure all stored artifacts are removed
241
+ await new Promise((resolve, reject) => {
242
+ const deleteRequest = indexedDB.deleteDatabase(DB_NAME);
243
+ deleteRequest.onsuccess = () => resolve();
244
+ deleteRequest.onerror = () => reject(deleteRequest.error || new Error('Failed to delete IndexedDB'));
245
+ deleteRequest.onblocked = () => {
246
+ // If deletion is blocked, still try to continue with cache cleanup
247
+ resolve();
248
+ };
249
+ });
250
+
251
+ // 3) Clear Cache Storage entries that may contain model or transformers resources
252
+ try {
253
+ if (typeof caches !== 'undefined' && caches.keys) {
254
+ const cacheNames = await caches.keys();
255
+ for (const cacheName of cacheNames) {
256
+ // Delete transformers related caches and any caches that contain the model name
257
+ if (cacheName.startsWith('transformers-cache') || cacheName.includes(MODEL_NAME.replace(/\//g, '-')) || cacheName.includes('nomic')) {
258
+ await caches.delete(cacheName);
259
+ }
260
+ }
261
  }
262
+ } catch (e) {
263
+ // Non-fatal
264
+ console.warn('Cache cleanup error', e);
265
  }
266
+
267
+ // 4) Clear localStorage keys commonly used by transformers.js or model caches (best-effort)
268
+ try {
269
+ if (typeof localStorage !== 'undefined') {
270
+ const keysToClear = [];
271
+ for (let i = 0; i < localStorage.length; i++) {
272
+ const key = localStorage.key(i);
273
+ if (!key) continue;
274
+ if (key.startsWith('transformers') || key.includes('transformers') || key.includes('nomic') || key.includes('hf_')) {
275
+ keysToClear.push(key);
276
+ }
277
+ }
278
+ for (const k of keysToClear) localStorage.removeItem(k);
279
+ }
280
+ } catch (e) {
281
+ // localStorage may not be available in worker-like contexts; ignore
282
+ }
283
+
284
+ // 5) Clear in-memory references
285
+ indexData = null;
286
+ model = null;
287
+ isReady = false;
288
+ };
289
+
290
+ // Timeout in ms
291
+ const TIMEOUT_MS = 8000;
292
+
293
+ try {
294
+ await Promise.race([
295
+ cleanup(),
296
+ new Promise((_, reject) => setTimeout(() => reject(new Error('cleanup-timeout')), TIMEOUT_MS))
297
+ ]);
298
+ // If cleanup completed within timeout
299
  self.postMessage({ type: 'dataDeleted', payload: 'Cached data deleted successfully.' });
300
  } catch (error) {
301
+ if (error && error.message === 'cleanup-timeout') {
302
+ console.warn('deleteData: cleanup timed out');
303
+ // Post a success-like message so UI doesn't stay stuck; note that some cleanup may still be pending
304
+ self.postMessage({ type: 'dataDeleted', payload: 'Cached data deletion attempted (timed out). Some cleanup may remain.' });
305
+ } else {
306
+ console.error('deleteData error:', error);
307
+ self.postMessage({ type: 'error', payload: 'Failed to delete cached data: ' + (error && error.message ? error.message : String(error)) });
308
+ }
309
  }
310
  } else if (type === 'getIndexSize') {
311
  try {