Skip to content

Commit 4092358

Browse files
ralyodioclaude
andauthored
feat: default search to Kagi across omnibox, new tab, and mobile (#69)
Kagi (https://kagi.com/search?q=) becomes the default search engine everywhere TronBrowser picks one: - Omnibox: the launcher's once-per-profile default_search_provider_data now seeds Kagi. The sentinel filename carries the engine name (.tron-search-kagi), so profiles created under the old NeoSearch default get the new default applied once, and a user's own chrome://settings/search choice is still respected afterwards. - New tab (ai-sidebar): Kagi is added to SEARCH_ENGINES, is the initial value of searchEngine, and is the fallback for an unknown stored id. - Settings (extension options + web): Kagi is the first option and is labelled the default; the stored-value fallback now resolves to kagi. - Mobile: the in-app browser's address-bar search falls back to Kagi. NeoSearch, Xprivo, DuckDuckGo, Altpower, and Oxiverse remain available as alternatives, and the Tor engine list is untouched. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 4ea073d commit 4092358

10 files changed

Lines changed: 31 additions & 25 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ Linux phones (Librem 5 / PinePhone / Ubuntu Touch). See
4747
4848
## What you get today
4949

50-
- **De-googled new tab** — TronBrowser page, **NeoSearch** as the default private
51-
search (no Google in the omnibox; DuckDuckGo available as an alternative), quick links.
50+
- **De-googled new tab** — TronBrowser page, **Kagi** as the default search
51+
(no Google in the omnibox; NeoSearch, Xprivo, and DuckDuckGo available as
52+
alternatives), quick links.
5253
- **RSS reader** on the new tab — seeded from your OPML; add/remove feeds and
5354
**import/export OPML** in Settings.
5455
- **AI sidebar (bring your own keys)** — Anthropic, OpenAI, Google/Gemini,

apps/desktop/extensions/ai-sidebar/newtab.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
<button type="button" id="mode-web" class="mode active" aria-selected="true">Web</button>
2222
<button type="button" id="mode-ai" class="mode" aria-selected="false">AI</button>
2323
</div>
24-
<input id="q" type="text" placeholder="Search NeoSearch" autocomplete="off" autofocus />
24+
<input id="q" type="text" placeholder="Search Kagi" autocomplete="off" autofocus />
2525
</form>
2626

2727
<nav class="links">
28+
<a href="https://kagi.com">Kagi</a>
2829
<a href="https://neosearch.org">NeoSearch</a>
2930
<a href="https://www.xprivo.com">Xprivo</a>
3031
<a href="https://duckduckgo.com">DuckDuckGo</a>

apps/desktop/extensions/ai-sidebar/newtab.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ const DEFAULT_TICKERS = 'SPY, AAPL, NVDA, BTC-USD';
1111
const DEFAULT_LEAGUES = 'nfl, nba';
1212
const splitList = (s) => String(s || '').split(',').map((x) => x.trim()).filter(Boolean);
1313

14-
// --- Search: Web (NeoSearch by default) or AI (sidebar) ---
14+
// --- Search: Web (Kagi by default) or AI (sidebar) ---
1515
const SEARCH_ENGINES = {
16+
kagi: { name: 'Kagi', url: 'https://kagi.com/search?q=' },
1617
neosearch: { name: 'NeoSearch', url: 'https://neosearch.org/?q=' },
1718
xprivo: { name: 'Xprivo', url: 'https://www.xprivo.com/search/?q=' },
1819
ddg: { name: 'DuckDuckGo', url: 'https://duckduckgo.com/?q=' },
@@ -36,15 +37,15 @@ const TOR_SEARCH_ENGINES = {
3637
excavator: { name: 'Excavator', url: 'http://2fd6cemt4gmccflhm6imvdfvli3nf7zn6rfrwpsy7uhxrgbypvwf5fad.onion/?q=' },
3738
};
3839
let searchMode = 'web';
39-
let searchEngine = 'neosearch';
40+
let searchEngine = 'kagi';
4041
let torSearchEngine = 'ahmia';
4142
let torEnabled = false;
4243

4344
// Resolve the engine to use right now: the Tor default while the onion toggle
4445
// is on, otherwise the clearnet default.
4546
function activeEngine() {
4647
if (torEnabled) return TOR_SEARCH_ENGINES[torSearchEngine] || TOR_SEARCH_ENGINES.ahmia;
47-
return SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.neosearch;
48+
return SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.kagi;
4849
}
4950

5051
function setMode(mode) {

apps/desktop/extensions/ai-sidebar/options.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,13 @@ <h2>bittorrented.com — Live TV, Radio &amp; Podcasts</h2>
130130
<span id="btrMsg" class="saved"></span>
131131

132132
<h2>Search</h2>
133-
<p class="hint">Default search engine for the new-tab search box. NeoSearch is the
134-
private default; Xprivo, DuckDuckGo, and others are available as alternatives. (The omnibox default
133+
<p class="hint">Default search engine for the new-tab search box. Kagi is the
134+
default; NeoSearch, Xprivo, DuckDuckGo, and others are available as alternatives. (The omnibox default
135135
is set separately in <code>chrome://settings/search</code>.)</p>
136136
<label for="searchEngine">New-tab search engine (clearnet)</label>
137137
<select id="searchEngine">
138-
<option value="neosearch">NeoSearch (private, default)</option>
138+
<option value="kagi">Kagi (default)</option>
139+
<option value="neosearch">NeoSearch (private)</option>
139140
<option value="xprivo">Xprivo (private)</option>
140141
<option value="ddg">DuckDuckGo</option>
141142
<option value="altpower">Altpower</option>

apps/desktop/extensions/ai-sidebar/settings-sections.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export async function mountSettingsSections({ store, el, flash }) {
4242
const cur = await store.get(['feeds', 'tickers', 'leagues', 'searchEngine', 'torSearchEngine']);
4343

4444
// Populate current values (on every mount, e.g. after a cloud pull).
45-
if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'neosearch';
45+
if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'kagi';
4646
if (el('torSearchEngine')) el('torSearchEngine').value = cur.torSearchEngine || 'ahmia';
4747
if (el('tickers')) el('tickers').value = cur.tickers ?? '';
4848
if (el('leagues')) el('leagues').value = cur.leagues ?? '';

apps/desktop/launcher/tronbrowser

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,10 @@ if flag not in exp:
186186
PY
187187
fi
188188

189-
# Default the omnibox to NeoSearch once per profile, then respect the user's
190-
# chrome://settings/search choice.
191-
if command -v python3 >/dev/null 2>&1 && [ ! -f "$DATA/.tron-search-neosearch" ]; then
189+
# Default the omnibox to Kagi once per profile, then respect the user's
190+
# chrome://settings/search choice. The sentinel filename carries the engine so
191+
# that changing the default here re-applies once on profiles created earlier.
192+
if command -v python3 >/dev/null 2>&1 && [ ! -f "$DATA/.tron-search-kagi" ]; then
192193
TB_PREFS="$DATA/Default/Preferences" python3 - <<'PY' 2>/dev/null || true
193194
import json, os
194195
p = os.environ["TB_PREFS"]
@@ -198,16 +199,16 @@ try:
198199
except Exception:
199200
d = {}
200201
d.setdefault("default_search_provider_data", {})["template_url_data"] = {
201-
"short_name": "NeoSearch",
202-
"keyword": "neosearch.org",
203-
"url": "https://neosearch.org/?q={searchTerms}",
204-
"suggestions_url": "https://neosearch.org/suggestions?q={searchTerms}",
205-
"favicon_url": "https://neosearch.org/favicon-32x32.png",
202+
"short_name": "Kagi",
203+
"keyword": "kagi.com",
204+
"url": "https://kagi.com/search?q={searchTerms}",
205+
"suggestions_url": "https://kagi.com/api/autosuggest?q={searchTerms}",
206+
"favicon_url": "https://kagi.com/favicon.ico",
206207
"safe_for_autoreplace": False,
207208
}
208209
json.dump(d, open(p, "w"))
209210
PY
210-
mkdir -p "$DATA"; : > "$DATA/.tron-search-neosearch"
211+
mkdir -p "$DATA"; : > "$DATA/.tron-search-kagi"
211212
fi
212213

213214
# Show the feed (ai-sidebar New Tab page) on startup. We can't pass a startup URL —

apps/mobile/src/lib/navigation.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ describe('normalizeUrl', () => {
2323

2424
it('searches ordinary text', () => {
2525
expect(normalizeUrl('privacy first browser')).toBe(
26-
'https://duckduckgo.com/?q=privacy%20first%20browser',
26+
'https://kagi.com/search?q=privacy%20first%20browser',
2727
);
2828
});
2929

3030
it('searches unsupported schemes instead of loading them', () => {
3131
expect(normalizeUrl('javascript:alert(1)')).toBe(
32-
'https://duckduckgo.com/?q=javascript%3Aalert(1)',
32+
'https://kagi.com/search?q=javascript%3Aalert(1)',
3333
);
3434
});
3535

3636
it('does not treat domain-looking text with spaces as a URL', () => {
3737
expect(normalizeUrl('example.com malicious suffix')).toBe(
38-
'https://duckduckgo.com/?q=example.com%20malicious%20suffix',
38+
'https://kagi.com/search?q=example.com%20malicious%20suffix',
3939
);
4040
});
4141
});

apps/mobile/src/lib/navigation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const DOMAIN_OR_IP =
44
/^(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}|(?:\d{1,3}\.){3}\d{1,3}|localhost)(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
55

66
function searchUrl(query: string): string {
7-
return `https://duckduckgo.com/?q=${encodeURIComponent(query)}`;
7+
return `https://kagi.com/search?q=${encodeURIComponent(query)}`;
88
}
99

1010
/**

apps/web/public/settings-sections.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export async function mountSettingsSections({ store, el, flash }) {
4242
const cur = await store.get(['feeds', 'tickers', 'leagues', 'searchEngine', 'torSearchEngine']);
4343

4444
// Populate current values (on every mount, e.g. after a cloud pull).
45-
if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'neosearch';
45+
if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'kagi';
4646
if (el('torSearchEngine')) el('torSearchEngine').value = cur.torSearchEngine || 'ahmia';
4747
if (el('tickers')) el('tickers').value = cur.tickers ?? '';
4848
if (el('leagues')) el('leagues').value = cur.leagues ?? '';

apps/web/public/settings.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ <h2>Search</h2>
8080
<p class="hint">Default search engine for the new-tab search box.</p>
8181
<label for="searchEngine">New-tab search engine (clearnet)</label>
8282
<select id="searchEngine">
83-
<option value="neosearch">NeoSearch (private, default)</option>
83+
<option value="kagi">Kagi (default)</option>
84+
<option value="neosearch">NeoSearch (private)</option>
8485
<option value="xprivo">Xprivo (private)</option>
8586
<option value="ddg">DuckDuckGo</option>
8687
<option value="altpower">Altpower</option>

0 commit comments

Comments
 (0)