Spaces:
Running
Running
Upload 3 files
Browse files- index.html +1 -1
- main.js +24 -14
- 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
|
| 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 |
-
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 48 |
-
|
| 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 |
-
|
| 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 |
-
//
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 121 |
-
|
| 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 |
-
|
| 232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
|
| 234 |
-
//
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
}
|
|
|
|
|
|
|
|
|
|
| 240 |
}
|
| 241 |
-
|
| 242 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
self.postMessage({ type: 'dataDeleted', payload: 'Cached data deleted successfully.' });
|
| 244 |
} catch (error) {
|
| 245 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 {
|