Rewild Your Web
web
browser
dweb
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});