blazing fast link redirects on cloudflare kv hop.dunkirk.sh/u/tacy

feat: add two layer search

dunkirk.sh aa9f37e6 8bc67f9d

verified
+28 -1
+28 -1
src/index.html
··· 545 545 }); 546 546 547 547 searchInput.addEventListener("input", () => { 548 + // Instant search on loaded data 549 + applySearch(); 550 + 551 + // Debounced server search for more results 548 552 clearTimeout(debounceTimer); 549 - debounceTimer = setTimeout(applySearch, 150); 553 + debounceTimer = setTimeout(() => { 554 + const query = searchInput.value.trim(); 555 + if (query) { 556 + searchServer(query); 557 + } 558 + }, 500); 550 559 }); 551 560 552 561 async function loadUrls() { ··· 559 568 } catch (error) { 560 569 urlsTable.innerHTML = '<div style="padding: 2.5rem; text-align: center; color: var(--text-muted);">failed to load urls</div>'; 561 570 document.body.classList.remove("loading"); 571 + } 572 + } 573 + 574 + async function searchServer(query) { 575 + try { 576 + const response = await fetch(`/api/urls?limit=1000&search=${encodeURIComponent(query)}`); 577 + const data = await response.json(); 578 + 579 + // Merge server results with existing data 580 + const existingCodes = new Set(allUrls.map(u => u.shortCode)); 581 + const newUrls = data.urls.filter(u => !existingCodes.has(u.shortCode)); 582 + 583 + if (newUrls.length > 0) { 584 + allUrls.push(...newUrls); 585 + applySearch(); 586 + } 587 + } catch (error) { 588 + console.error('Server search failed:', error); 562 589 } 563 590 } 564 591