Rewild Your Web
web
browser
dweb
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3const DISCOVERY_TIMEOUT = 50; // ms to wait for responses
4const MAX_RESULTS = 10;
5
6export class OpenViewsProvider {
7 constructor() {
8 this.name = "Open Tabs";
9 this.icon = "app-window";
10 this.channel = new BroadcastChannel("servo-search");
11 this.pendingQuery = null;
12 this.webviews = [];
13
14 // Listen for responses from browser windows
15 this.channel.onmessage = (e) => {
16 if (e.data.type === "webviewList" && this.pendingQuery) {
17 // Collect web-views from responding windows
18 for (const wv of e.data.webviews) {
19 this.webviews.push({
20 ...wv,
21 windowId: e.data.windowId,
22 });
23 }
24 }
25 };
26 }
27
28 async query(text) {
29 if (!text || text.trim() === "") {
30 return [];
31 }
32
33 const query = text.toLowerCase().trim();
34
35 // Reset state for new query
36 this.webviews = [];
37 this.pendingQuery = query;
38
39 // Request web-view list from all browser windows
40 this.channel.postMessage({ type: "listWebViews" });
41
42 // Wait for responses
43 await new Promise((resolve) => setTimeout(resolve, DISCOVERY_TIMEOUT));
44
45 this.pendingQuery = null;
46
47 // Filter and score results
48 const results = [];
49
50 for (const wv of this.webviews) {
51 const title = (wv.title || "").toLowerCase();
52 const url = (wv.url || "").toLowerCase();
53
54 // Check for matches in title or URL
55 const titleMatch = title.includes(query);
56 const urlMatch = url.includes(query);
57
58 if (titleMatch || urlMatch) {
59 // Score: title match is worth more than URL match
60 const score = (titleMatch ? 0.6 : 0) + (urlMatch ? 0.3 : 0);
61
62 results.push({
63 score: score,
64 kind: "webview",
65 value: {
66 title: wv.title || wv.url || "Untitled",
67 url: wv.url,
68 webviewId: wv.webviewId,
69 windowId: wv.windowId,
70 },
71 });
72 }
73 }
74
75 // Sort by score and limit
76 results.sort((a, b) => b.score - a.score);
77 return results.slice(0, MAX_RESULTS);
78 }
79}