+ Kagi
NeoSearch
Xprivo
DuckDuckGo
diff --git a/apps/desktop/extensions/ai-sidebar/newtab.js b/apps/desktop/extensions/ai-sidebar/newtab.js
index a6c4d0c..c031646 100644
--- a/apps/desktop/extensions/ai-sidebar/newtab.js
+++ b/apps/desktop/extensions/ai-sidebar/newtab.js
@@ -11,8 +11,9 @@ const DEFAULT_TICKERS = 'SPY, AAPL, NVDA, BTC-USD';
const DEFAULT_LEAGUES = 'nfl, nba';
const splitList = (s) => String(s || '').split(',').map((x) => x.trim()).filter(Boolean);
-// --- Search: Web (NeoSearch by default) or AI (sidebar) ---
+// --- Search: Web (Kagi by default) or AI (sidebar) ---
const SEARCH_ENGINES = {
+ kagi: { name: 'Kagi', url: 'https://kagi.com/search?q=' },
neosearch: { name: 'NeoSearch', url: 'https://neosearch.org/?q=' },
xprivo: { name: 'Xprivo', url: 'https://www.xprivo.com/search/?q=' },
ddg: { name: 'DuckDuckGo', url: 'https://duckduckgo.com/?q=' },
@@ -36,7 +37,7 @@ const TOR_SEARCH_ENGINES = {
excavator: { name: 'Excavator', url: 'http://2fd6cemt4gmccflhm6imvdfvli3nf7zn6rfrwpsy7uhxrgbypvwf5fad.onion/?q=' },
};
let searchMode = 'web';
-let searchEngine = 'neosearch';
+let searchEngine = 'kagi';
let torSearchEngine = 'ahmia';
let torEnabled = false;
@@ -44,7 +45,7 @@ let torEnabled = false;
// is on, otherwise the clearnet default.
function activeEngine() {
if (torEnabled) return TOR_SEARCH_ENGINES[torSearchEngine] || TOR_SEARCH_ENGINES.ahmia;
- return SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.neosearch;
+ return SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.kagi;
}
function setMode(mode) {
diff --git a/apps/desktop/extensions/ai-sidebar/options.html b/apps/desktop/extensions/ai-sidebar/options.html
index d8d9c95..056fa44 100644
--- a/apps/desktop/extensions/ai-sidebar/options.html
+++ b/apps/desktop/extensions/ai-sidebar/options.html
@@ -130,12 +130,13 @@ bittorrented.com — Live TV, Radio & Podcasts
Search
- Default search engine for the new-tab search box. NeoSearch is the
- private default; Xprivo, DuckDuckGo, and others are available as alternatives. (The omnibox default
+
Default search engine for the new-tab search box. Kagi is the
+ default; NeoSearch, Xprivo, DuckDuckGo, and others are available as alternatives. (The omnibox default
is set separately in chrome://settings/search.)
New-tab search engine (clearnet)
- NeoSearch (private, default)
+ Kagi (default)
+ NeoSearch (private)
Xprivo (private)
DuckDuckGo
Altpower
diff --git a/apps/desktop/extensions/ai-sidebar/settings-sections.js b/apps/desktop/extensions/ai-sidebar/settings-sections.js
index 56722d7..90d1f9b 100644
--- a/apps/desktop/extensions/ai-sidebar/settings-sections.js
+++ b/apps/desktop/extensions/ai-sidebar/settings-sections.js
@@ -42,7 +42,7 @@ export async function mountSettingsSections({ store, el, flash }) {
const cur = await store.get(['feeds', 'tickers', 'leagues', 'searchEngine', 'torSearchEngine']);
// Populate current values (on every mount, e.g. after a cloud pull).
- if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'neosearch';
+ if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'kagi';
if (el('torSearchEngine')) el('torSearchEngine').value = cur.torSearchEngine || 'ahmia';
if (el('tickers')) el('tickers').value = cur.tickers ?? '';
if (el('leagues')) el('leagues').value = cur.leagues ?? '';
diff --git a/apps/desktop/launcher/tronbrowser b/apps/desktop/launcher/tronbrowser
index bd7dc99..933a616 100755
--- a/apps/desktop/launcher/tronbrowser
+++ b/apps/desktop/launcher/tronbrowser
@@ -186,9 +186,10 @@ if flag not in exp:
PY
fi
-# Default the omnibox to NeoSearch once per profile, then respect the user's
-# chrome://settings/search choice.
-if command -v python3 >/dev/null 2>&1 && [ ! -f "$DATA/.tron-search-neosearch" ]; then
+# Default the omnibox to Kagi once per profile, then respect the user's
+# chrome://settings/search choice. The sentinel filename carries the engine so
+# that changing the default here re-applies once on profiles created earlier.
+if command -v python3 >/dev/null 2>&1 && [ ! -f "$DATA/.tron-search-kagi" ]; then
TB_PREFS="$DATA/Default/Preferences" python3 - <<'PY' 2>/dev/null || true
import json, os
p = os.environ["TB_PREFS"]
@@ -198,16 +199,16 @@ try:
except Exception:
d = {}
d.setdefault("default_search_provider_data", {})["template_url_data"] = {
- "short_name": "NeoSearch",
- "keyword": "neosearch.org",
- "url": "https://neosearch.org/?q={searchTerms}",
- "suggestions_url": "https://neosearch.org/suggestions?q={searchTerms}",
- "favicon_url": "https://neosearch.org/favicon-32x32.png",
+ "short_name": "Kagi",
+ "keyword": "kagi.com",
+ "url": "https://kagi.com/search?q={searchTerms}",
+ "suggestions_url": "https://kagi.com/api/autosuggest?q={searchTerms}",
+ "favicon_url": "https://kagi.com/favicon.ico",
"safe_for_autoreplace": False,
}
json.dump(d, open(p, "w"))
PY
- mkdir -p "$DATA"; : > "$DATA/.tron-search-neosearch"
+ mkdir -p "$DATA"; : > "$DATA/.tron-search-kagi"
fi
# Show the feed (ai-sidebar New Tab page) on startup. We can't pass a startup URL —
diff --git a/apps/mobile/src/lib/navigation.test.ts b/apps/mobile/src/lib/navigation.test.ts
index d502601..89eb57d 100644
--- a/apps/mobile/src/lib/navigation.test.ts
+++ b/apps/mobile/src/lib/navigation.test.ts
@@ -23,19 +23,19 @@ describe('normalizeUrl', () => {
it('searches ordinary text', () => {
expect(normalizeUrl('privacy first browser')).toBe(
- 'https://duckduckgo.com/?q=privacy%20first%20browser',
+ 'https://kagi.com/search?q=privacy%20first%20browser',
);
});
it('searches unsupported schemes instead of loading them', () => {
expect(normalizeUrl('javascript:alert(1)')).toBe(
- 'https://duckduckgo.com/?q=javascript%3Aalert(1)',
+ 'https://kagi.com/search?q=javascript%3Aalert(1)',
);
});
it('does not treat domain-looking text with spaces as a URL', () => {
expect(normalizeUrl('example.com malicious suffix')).toBe(
- 'https://duckduckgo.com/?q=example.com%20malicious%20suffix',
+ 'https://kagi.com/search?q=example.com%20malicious%20suffix',
);
});
});
diff --git a/apps/mobile/src/lib/navigation.ts b/apps/mobile/src/lib/navigation.ts
index e746c76..a651d28 100644
--- a/apps/mobile/src/lib/navigation.ts
+++ b/apps/mobile/src/lib/navigation.ts
@@ -4,7 +4,7 @@ const DOMAIN_OR_IP =
/^(?:(?:[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;
function searchUrl(query: string): string {
- return `https://duckduckgo.com/?q=${encodeURIComponent(query)}`;
+ return `https://kagi.com/search?q=${encodeURIComponent(query)}`;
}
/**
diff --git a/apps/web/public/settings-sections.js b/apps/web/public/settings-sections.js
index 56722d7..90d1f9b 100644
--- a/apps/web/public/settings-sections.js
+++ b/apps/web/public/settings-sections.js
@@ -42,7 +42,7 @@ export async function mountSettingsSections({ store, el, flash }) {
const cur = await store.get(['feeds', 'tickers', 'leagues', 'searchEngine', 'torSearchEngine']);
// Populate current values (on every mount, e.g. after a cloud pull).
- if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'neosearch';
+ if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'kagi';
if (el('torSearchEngine')) el('torSearchEngine').value = cur.torSearchEngine || 'ahmia';
if (el('tickers')) el('tickers').value = cur.tickers ?? '';
if (el('leagues')) el('leagues').value = cur.leagues ?? '';
diff --git a/apps/web/public/settings.html b/apps/web/public/settings.html
index 38168a3..abc67c1 100644
--- a/apps/web/public/settings.html
+++ b/apps/web/public/settings.html
@@ -80,7 +80,8 @@ Search
Default search engine for the new-tab search box.
New-tab search engine (clearnet)
- NeoSearch (private, default)
+ Kagi (default)
+ NeoSearch (private)
Xprivo (private)
DuckDuckGo
Altpower