Rewild Your Web
web browser dweb
at main 125 lines 3.4 kB view raw
1// SPDX-License-Identifier: AGPL-3.0-or-later 2 3import { SearchController } from "//shared.localhost:8888/search/controller.js"; 4 5const container = document.getElementById("container"); 6const searchInput = document.getElementById("search-input"); 7const resultsList = document.getElementById("results-list"); 8 9// BroadcastChannel for communicating with browser window 10const searchChannel = new BroadcastChannel("servo-search"); 11let pendingAction = null; 12 13// Listen for acknowledgments 14searchChannel.onmessage = (e) => { 15 if ( 16 e.data.type === "ack" && 17 pendingAction && 18 e.data.id === pendingAction.id 19 ) { 20 clearTimeout(pendingAction.timeout); 21 pendingAction = null; 22 } 23}; 24 25// Navigate to a URL 26function navigateTo(url) { 27 window.location.href = url; 28} 29 30// Select a web-view in a browser window 31function selectWebView(windowId, webviewId) { 32 const id = Date.now(); 33 34 searchChannel.postMessage({ 35 type: "selectWebView", 36 id: id, 37 targetWindowId: windowId, 38 webviewId: webviewId, 39 }); 40 41 pendingAction = { 42 id: id, 43 timeout: setTimeout(() => { 44 pendingAction = null; 45 }, 100), 46 }; 47} 48 49// Initialize search controller 50const controller = new SearchController({ 51 onNavigate: navigateTo, 52 onSelectWebView: selectWebView, 53 onResultsChanged: renderResults, 54}); 55 56// Render results to the DOM 57function renderResults(results, groups) { 58 resultsList.innerHTML = ""; 59 60 if (results.length === 0) { 61 container.classList.remove("has-results"); 62 return; 63 } 64 65 container.classList.add("has-results"); 66 67 for (const group of groups) { 68 const groupDiv = document.createElement("div"); 69 groupDiv.className = "result-group"; 70 71 // Icon column 72 const iconDiv = document.createElement("div"); 73 iconDiv.className = "result-group-icon"; 74 if (group.providerIcon) { 75 const icon = document.createElement("lucide-icon"); 76 icon.setAttribute("name", group.providerIcon); 77 iconDiv.appendChild(icon); 78 } 79 groupDiv.appendChild(iconDiv); 80 81 // Items column 82 const itemsDiv = document.createElement("div"); 83 itemsDiv.className = "result-group-items"; 84 85 for (const result of group.items) { 86 const itemDiv = document.createElement("div"); 87 itemDiv.className = "result-item"; 88 itemDiv.dataset.kind = result.kind; 89 90 if (result.kind === "link" || result.kind === "webview") { 91 const link = document.createElement("a"); 92 link.href = result.kind === "link" ? result.value.url : "#"; 93 link.className = "result-link"; 94 link.textContent = result.value.title; 95 link.addEventListener("click", (e) => { 96 e.preventDefault(); 97 controller.handleResultClick(result); 98 }); 99 itemDiv.appendChild(link); 100 } else if (result.kind === "text") { 101 const text = document.createElement("span"); 102 text.className = "result-text"; 103 text.textContent = result.value; 104 itemDiv.appendChild(text); 105 } 106 107 itemsDiv.appendChild(itemDiv); 108 } 109 110 groupDiv.appendChild(itemsDiv); 111 resultsList.appendChild(groupDiv); 112 } 113} 114 115// Event listeners 116searchInput.addEventListener("input", () => { 117 controller.query(searchInput.value); 118}); 119 120searchInput.addEventListener("keydown", (e) => { 121 if (e.key === "Enter") { 122 e.preventDefault(); 123 controller.handleSubmit(searchInput.value); 124 } 125});