document.addEventListener('DOMContentLoaded', () => { const searchInput = document.getElementById('search-input'); const searchForm = document.getElementById('search-form'); if (searchInput && searchForm) { const performSearch = async (e) => { if (e) e.preventDefault(); const query = searchInput.value.trim(); if (!query) return; const qLower = query.toLowerCase(); let publicNotes = []; let privateNotes = []; // 1. Search Public Notes (Backend) try { const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`); if (res.ok) { publicNotes = await res.json(); } else { console.error("Search API failed:", res.status); } } catch (e) { console.error("Search error:", e); } // 2. Search Private Notes (Client-Side Decryption for Zero-Knowledge Privacy) if (window.SpotNotes && window.SpotNotes.isLoggedIn) { try { const mineRes = await fetch('/api/notes/mine'); if (mineRes.ok) { const myRawNotes = await mineRes.json(); for (const note of myRawNotes) { let decryptedNote = note; if (note.is_encrypted && window.SpotCrypto) { let padKey = window.SpotCrypto.getCachedPadKey(note.notepad_id); if (!padKey && note.notepad && note.notepad.encrypted_key) { padKey = await window.SpotCrypto.unwrapNotepadKey(note.notepad_id, note.notepad.encrypted_key); } decryptedNote = await window.SpotCrypto.decryptNote(note, padKey); } const titleMatch = (decryptedNote.title || '').toLowerCase().includes(qLower); const contentMatch = (decryptedNote.content || '').toLowerCase().includes(qLower); if (titleMatch || contentMatch) { privateNotes.push(decryptedNote); } } } } catch (e) { console.warn("Error searching private notes locally:", e); } } // Combine unique notes const seenIds = new Set(); const combinedNotes = []; [...privateNotes, ...publicNotes].forEach(note => { if (!seenIds.has(note.id)) { seenIds.add(note.id); combinedNotes.push(note); } }); // If notes found, display them if (combinedNotes.length > 0) { if (window.SpotNotes && window.SpotNotes.displaySearchResults) { window.SpotNotes.displaySearchResults(combinedNotes, null, query); } return; } // 3. Fallback: Search Location (Nominatim) if no notes found let location = null; try { const nomRes = await fetch(`/api/geocode?q=${encodeURIComponent(query)}`); const nomData = await nomRes.json(); if (nomData && nomData.length > 0) { location = { lat: parseFloat(nomData[0].lat), lng: parseFloat(nomData[0].lon) }; } } catch (e) { console.error("Geocoding error:", e); } // 4. Update UI if (window.SpotNotes && window.SpotNotes.displaySearchResults) { window.SpotNotes.displaySearchResults(combinedNotes, location, query); } }; searchForm.addEventListener('submit', performSearch); } });