Skip to content

Commit 9b60d8b

Browse files
ralyodioclaude
andcommitted
feat(search): Tor-network default search engine + chore(release): v3.8.3
New-tab search now has a separate default for when the onion toggle is on: Ahmia (default), Torch, Haystak, OnionLand, Excavator. Auto-switches on torEnabled via storage.onChanged; clearnet default (NeoSearch) unchanged. Editable in Settings on both the extension options page and the web app. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d503ab5 commit 9b60d8b

31 files changed

Lines changed: 100 additions & 37 deletions

File tree

apps/desktop/extensions/ai-sidebar/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "TronBrowser",
4-
"version": "3.8.2",
4+
"version": "3.8.3",
55
"description": "TronBrowser — privacy-first, AI-native. Branded new tab, private search, CoinPay login, and a bring-your-own-keys AI sidebar.",
66
"icons": {
77
"16": "icons/icon-16.png",

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,64 @@ const SEARCH_ENGINES = {
2020
altpower: { name: 'Altpower', url: 'https://altpower.app/#open-source&gsc.tab=0&gsc.q=', suffix: '&gsc.sort=' },
2121
oxiverse: { name: 'Oxiverse', url: 'https://search.oxiverse.com/?q=', suffix: '&tab=web' },
2222
};
23+
// Tor-network search engines — used automatically when the 🧅 toggle is on
24+
// (torEnabled in chrome.storage.local, set by background.js). Ahmia is the
25+
// default: ahmia.fi is a stable clearnet domain reachable over the Tor SOCKS
26+
// proxy that still indexes/returns .onion results, so it keeps working even
27+
// when the onion-only mirrors rotate their addresses. The .onion URLs below
28+
// resolve inside the SOCKS proxy (Chromium does DNS at the proxy for .onion).
29+
// NOTE: onion addresses drift over time — verify against each engine's current
30+
// mirror if a result page stops loading.
31+
const TOR_SEARCH_ENGINES = {
32+
ahmia: { name: 'Ahmia', url: 'https://ahmia.fi/search/?q=' },
33+
torch: { name: 'Torch', url: 'http://torchdeedp3i2jigzjdmfpn5ttjhthh5wbmda2rr3jvqjg5p77c54dqd.onion/search?query=' },
34+
haystak: { name: 'Haystak', url: 'http://haystak5njsmn2hqkewecpaxetahtwhsbsa64jom2k22z5afxhnpxfid.onion/?q=' },
35+
onionland: { name: 'OnionLand', url: 'http://3bbad7fauom4d6sgppalyqddsqbf5u5p56b5k5uk2zxsy3d6ey2jobad.onion/search?q=' },
36+
excavator: { name: 'Excavator', url: 'http://2fd6cemt4gmccflhm6imvdfvli3nf7zn6rfrwpsy7uhxrgbypvwf5fad.onion/?q=' },
37+
};
2338
let searchMode = 'web';
2439
let searchEngine = 'neosearch';
40+
let torSearchEngine = 'ahmia';
41+
let torEnabled = false;
42+
43+
// Resolve the engine to use right now: the Tor default while the onion toggle
44+
// is on, otherwise the clearnet default.
45+
function activeEngine() {
46+
if (torEnabled) return TOR_SEARCH_ENGINES[torSearchEngine] || TOR_SEARCH_ENGINES.ahmia;
47+
return SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.neosearch;
48+
}
2549

2650
function setMode(mode) {
2751
searchMode = mode;
2852
el('mode-web').classList.toggle('active', mode === 'web');
2953
el('mode-ai').classList.toggle('active', mode === 'ai');
3054
el('mode-web').setAttribute('aria-selected', mode === 'web');
3155
el('mode-ai').setAttribute('aria-selected', mode === 'ai');
32-
const eng = SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.neosearch;
56+
const eng = activeEngine();
3357
el('q').placeholder = mode === 'ai' ? 'Ask AI anything…' : `Search ${eng.name}…`;
3458
el('q').focus();
3559
}
3660
el('mode-web').addEventListener('click', () => setMode('web'));
3761
el('mode-ai').addEventListener('click', () => setMode('ai'));
3862

39-
// Load the chosen web search engine (default NeoSearch).
40-
chrome.storage.local.get('searchEngine').then(({ searchEngine: se }) => {
41-
if (se && SEARCH_ENGINES[se]) searchEngine = se;
63+
// Load the chosen clearnet + Tor search engines and current onion-toggle state.
64+
chrome.storage.local.get(['searchEngine', 'torSearchEngine', 'torEnabled']).then((v) => {
65+
if (v.searchEngine && SEARCH_ENGINES[v.searchEngine]) searchEngine = v.searchEngine;
66+
if (v.torSearchEngine && TOR_SEARCH_ENGINES[v.torSearchEngine]) torSearchEngine = v.torSearchEngine;
67+
torEnabled = !!v.torEnabled;
68+
if (searchMode === 'web') setMode('web');
69+
});
70+
71+
// Keep the active engine in sync if the 🧅 toggle flips while the tab is open.
72+
chrome.storage.onChanged.addListener((changes, area) => {
73+
if (area !== 'local') return;
74+
if ('torEnabled' in changes) torEnabled = !!changes.torEnabled.newValue;
75+
if ('torSearchEngine' in changes && TOR_SEARCH_ENGINES[changes.torSearchEngine.newValue]) {
76+
torSearchEngine = changes.torSearchEngine.newValue;
77+
}
78+
if ('searchEngine' in changes && SEARCH_ENGINES[changes.searchEngine.newValue]) {
79+
searchEngine = changes.searchEngine.newValue;
80+
}
4281
if (searchMode === 'web') setMode('web');
4382
});
4483

@@ -52,7 +91,7 @@ el('search').addEventListener('submit', async (e) => {
5291
chrome.runtime.sendMessage({ type: 'open-sidepanel' });
5392
el('q').value = '';
5493
} else {
55-
const eng = SEARCH_ENGINES[searchEngine] || SEARCH_ENGINES.neosearch;
94+
const eng = activeEngine();
5695
location.href = eng.url + encodeURIComponent(q) + (eng.suffix || '');
5796
}
5897
});

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,23 @@ <h2>Search</h2>
108108
<p class="hint">Default search engine for the new-tab search box. NeoSearch is the
109109
private default; Xprivo, DuckDuckGo, and others are available as alternatives. (The omnibox default
110110
is set separately in <code>chrome://settings/search</code>.)</p>
111-
<label for="searchEngine">New-tab search engine</label>
111+
<label for="searchEngine">New-tab search engine (clearnet)</label>
112112
<select id="searchEngine">
113113
<option value="neosearch">NeoSearch (private, default)</option>
114114
<option value="xprivo">Xprivo (private)</option>
115115
<option value="ddg">DuckDuckGo</option>
116116
<option value="altpower">Altpower</option>
117117
<option value="oxiverse">Oxiverse</option>
118118
</select>
119+
<label for="torSearchEngine">New-tab search engine (Tor 🧅)</label>
120+
<select id="torSearchEngine">
121+
<option value="ahmia">Ahmia (indexes .onion, default)</option>
122+
<option value="torch">Torch (.onion)</option>
123+
<option value="haystak">Haystak (.onion)</option>
124+
<option value="onionland">OnionLand (.onion)</option>
125+
<option value="excavator">Excavator (.onion)</option>
126+
</select>
127+
<p class="hint">Used automatically on new tabs while the onion toggle is on.</p>
119128
<div><button id="saveSearch">Save search</button><span id="savedSearch" class="saved"></span></div>
120129

121130
<h2>Markets &amp; Sports</h2>

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ async function persist() { await ctx.store.set({ feeds }); renderFeeds(); }
3939
*/
4040
export async function mountSettingsSections({ store, el, flash }) {
4141
ctx = { store, el, flash };
42-
const cur = await store.get(['feeds', 'tickers', 'leagues', 'searchEngine']);
42+
const cur = await store.get(['feeds', 'tickers', 'leagues', 'searchEngine', 'torSearchEngine']);
4343

4444
// Populate current values (on every mount, e.g. after a cloud pull).
4545
if (el('searchEngine')) el('searchEngine').value = cur.searchEngine || 'neosearch';
46+
if (el('torSearchEngine')) el('torSearchEngine').value = cur.torSearchEngine || 'ahmia';
4647
if (el('tickers')) el('tickers').value = cur.tickers ?? '';
4748
if (el('leagues')) el('leagues').value = cur.leagues ?? '';
4849
feeds = Array.isArray(cur.feeds) && cur.feeds.length ? cur.feeds.slice() : DEFAULT_FEEDS.slice();
@@ -52,7 +53,9 @@ export async function mountSettingsSections({ store, el, flash }) {
5253
wired = true;
5354

5455
el('saveSearch')?.addEventListener('click', async () => {
55-
await ctx.store.set({ searchEngine: el('searchEngine').value });
56+
const patch = { searchEngine: el('searchEngine').value };
57+
if (el('torSearchEngine')) patch.torSearchEngine = el('torSearchEngine').value;
58+
await ctx.store.set(patch);
5659
ctx.flash('savedSearch', 'saved ✓');
5760
});
5861
el('saveMarkets')?.addEventListener('click', async () => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const CLOUD_BASE = 'https://tronbrowser.dev';
1111

1212
// Keys that sync. NOTE: plaintext API keys (aiConfig/aiProviders) are NEVER
1313
// synced — only the E2E-encrypted vault (aiVault) plus non-sensitive prefs.
14-
const KEYS = ['aiVault', 'aiDefault', 'aiModel', 'feeds', 'coinpayConfig', 'tickers', 'leagues', 'searchEngine'];
14+
const KEYS = ['aiVault', 'aiDefault', 'aiModel', 'feeds', 'coinpayConfig', 'tickers', 'leagues', 'searchEngine', 'torSearchEngine'];
1515

1616
async function endpoint() {
1717
const { syncConfig } = await chrome.storage.local.get('syncConfig');

apps/desktop/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/desktop",
3-
"version": "3.8.2",
3+
"version": "3.8.3",
44
"private": true,
55
"description": "Desktop shell for the TronBrowser Chromium fork",
66
"type": "module",

apps/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/docs",
3-
"version": "3.8.2",
3+
"version": "3.8.3",
44
"private": true,
55
"description": "Documentation site",
66
"type": "module",

apps/extensions/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/extensions",
3-
"version": "3.8.2",
3+
"version": "3.8.3",
44
"private": true,
55
"description": "TronBrowser extension store — pay $1, list your MV3 extension (tronbrowser.dev/store)",
66
"type": "module",

apps/mobile/app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"slug": "tronbrowserdev",
55
"owner": "profullstack",
66
"scheme": "tronbrowser",
7-
"version": "3.8.2",
7+
"version": "3.8.3",
88
"orientation": "portrait",
99
"userInterfaceStyle": "dark",
1010
"platforms": [

apps/mobile/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/mobile",
3-
"version": "3.8.2",
3+
"version": "3.8.3",
44
"private": true,
55
"description": "TronBrowser mobile companion (Expo / React Native) — browser · AI chat · agents",
66
"main": "index.js",

0 commit comments

Comments
 (0)