Skip to content

Commit edc12de

Browse files
committed
[chrome] add alternative search engine support
1 parent 287ce50 commit edc12de

File tree

3 files changed

+77
-12
lines changed

3 files changed

+77
-12
lines changed

packages/chrome/src/components/Omnibar/Suggestion.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { css } from "dreamland/core";
2-
import type { OmniboxResult } from "./suggestions";
2+
import { type OmniboxResult, AVAILABLE_SEARCH_ENGINES } from "./suggestions";
33
import { iconSearch, iconTrendingUp } from "../../icons";
4+
import { browser } from "../../Browser";
45
import { Icon } from "../Icon";
56
import { Favicon } from "../Favicon";
67
import { trimUrl } from "./utils";
@@ -72,7 +73,12 @@ export function Suggestion(props: {
7273
{item.title}
7374
<span style="font-weight: normal; opacity: 0.7">
7475
{" "}
75-
- Google Search
76+
-{" "}
77+
{
78+
AVAILABLE_SEARCH_ENGINES[browser.settings.defaultSearchEngine]
79+
.name
80+
}{" "}
81+
Search
7682
</span>
7783
</span>
7884
) : null}

packages/chrome/src/components/Omnibar/suggestions.ts

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,53 @@ export type OmniboxResult = {
1515
relevanceScore?: number;
1616
};
1717

18+
export interface SearchEngine {
19+
name: string;
20+
suggestUrlBuilder: (query: string) => string;
21+
searchUrlBuilder: (query: string) => string;
22+
suggestionParser: (data: any) => string[];
23+
}
24+
25+
/** Available search engines */
26+
export const AVAILABLE_SEARCH_ENGINES: Record<string, SearchEngine> = {
27+
google: {
28+
name: "Google",
29+
searchUrlBuilder: (query) =>
30+
`https://www.google.com/search?q=${encodeURIComponent(query)}`,
31+
suggestUrlBuilder: (query) =>
32+
`https://suggestqueries.google.com/complete/search?client=chrome&q=${encodeURIComponent(query)}`,
33+
suggestionParser: (data) => {
34+
if (Array.isArray(data) && data.length > 1 && Array.isArray(data[1])) {
35+
return data[1].map((item: any) => String(item)).filter(Boolean);
36+
}
37+
return [];
38+
},
39+
},
40+
brave: {
41+
name: "Brave",
42+
searchUrlBuilder: (query) =>
43+
`https://search.brave.com/search?q=${encodeURIComponent(query)}`,
44+
suggestUrlBuilder: (query) =>
45+
`https://search.brave.com/api/suggest?q=${encodeURIComponent(query)}&source=web`,
46+
suggestionParser: (data) => {
47+
// Google format
48+
if (Array.isArray(data) && data.length > 1 && Array.isArray(data[1])) {
49+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
50+
return data[1].map((item: any) => String(item)).filter(Boolean);
51+
}
52+
// Brave Format
53+
if (
54+
Array.isArray(data) &&
55+
data.length > 0 &&
56+
typeof data[0] === "string"
57+
) {
58+
return data.map((item: string) => String(item)).filter(Boolean);
59+
}
60+
return [];
61+
},
62+
},
63+
};
64+
1865
function calculateRelevanceScore(result: OmniboxResult, query: string): number {
1966
if (!query) return 0;
2067

@@ -130,7 +177,9 @@ const addDirectResult = (
130177
{
131178
kind: "directsearch",
132179
url: new URL(
133-
`https://www.google.com/search?q=${encodeURIComponent(query)}`
180+
AVAILABLE_SEARCH_ENGINES[
181+
browser.settings.defaultSearchEngine
182+
].searchUrlBuilder(query)
134183
),
135184
title: query,
136185
favicon: null,
@@ -147,13 +196,20 @@ const fetchGoogleSuggestions = async (
147196

148197
try {
149198
const resp = await bare.fetch(
150-
`http://suggestqueries.google.com/complete/search?client=chrome&q=${encodeURIComponent(query)}`
199+
AVAILABLE_SEARCH_ENGINES[
200+
browser.settings.defaultSearchEngine
201+
].suggestUrlBuilder(query)
151202
);
152203

153204
const json = await resp.json();
205+
let rawSuggestions =
206+
AVAILABLE_SEARCH_ENGINES[
207+
browser.settings.defaultSearchEngine
208+
].suggestionParser(json);
209+
rawSuggestions = rawSuggestions.slice(0, 5);
154210
const suggestions: OmniboxResult[] = [];
155211

156-
for (const item of json[1].slice(0, 5)) {
212+
for (const item of rawSuggestions) {
157213
// it's gonna be stuff like "http //fortnite.com/2fa ps5"
158214
// generally not useful
159215
if (item.startsWith("http")) continue;
@@ -162,7 +218,9 @@ const fetchGoogleSuggestions = async (
162218
kind: "search",
163219
title: item,
164220
url: new URL(
165-
`https://www.google.com/search?q=${encodeURIComponent(item)}`
221+
AVAILABLE_SEARCH_ENGINES[
222+
browser.settings.defaultSearchEngine
223+
].searchUrlBuilder(query)
166224
),
167225
favicon: null,
168226
});
@@ -217,6 +275,7 @@ export type TrendingQuery = {
217275

218276
export let trendingCached: TrendingQuery[] | null = null;
219277
export async function fetchGoogleTrending(geo = "US"): Promise<void> {
278+
// TODO: make this search engine agnostic
220279
try {
221280
if (trendingCached) return;
222281

packages/chrome/src/pages/SettingsPage.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { browser } from "../Browser";
77
import { Checkbox } from "../components/Checkbox";
88
import { Button } from "../components/Button";
99
import { Input } from "../components/Input";
10+
import { AVAILABLE_SEARCH_ENGINES } from "../components/Omnibar/suggestions";
1011

1112
import {
1213
iconSettings,
@@ -203,12 +204,11 @@ export function SettingsPage(
203204
class="select-input"
204205
value={use(browser.settings.defaultSearchEngine)}
205206
>
206-
<option value="google">Google</option>
207-
<option value="bing">Bing</option>
208-
<option value="duckduckgo">DuckDuckGo</option>
209-
<option value="yahoo">Yahoo</option>
210-
<option value="ecosia">Ecosia</option>
211-
<option value="startpage">Startpage</option>
207+
{Object.keys(AVAILABLE_SEARCH_ENGINES).map((key) => (
208+
<option value={key}>
209+
{AVAILABLE_SEARCH_ENGINES[key].name}
210+
</option>
211+
))}
212212
</select>
213213
</div>
214214
</div>

0 commit comments

Comments
 (0)