Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/desktop/extensions/ai-sidebar/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@
],
"run_at": "document_start"
},
{
"matches": [
"https://*/*",
"http://localhost/*",
"http://127.0.0.1/*"
],
"js": [
"w3bs-links.js"
],
"run_at": "document_start",
"all_frames": true
},
{
"matches": [
"https://chromewebstore.google.com/detail/*",
Expand Down
7 changes: 7 additions & 0 deletions apps/desktop/extensions/ai-sidebar/newtab.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fetchQuotes, fetchAllScores } from './markets.js';
import { fetchMotd } from './motd.js';
import { getToken as btrToken, BTR_BASE } from './bittorrented.js';
import { fetchWithTimeout, storageGet } from './net.js';
import { w3bsUrl } from './w3bs.js';

const el = (id) => document.getElementById(id);

Expand Down Expand Up @@ -93,6 +94,12 @@ el('search').addEventListener('submit', async (e) => {
e.preventDefault();
const q = el('q').value.trim();
if (!q) return;
// A W3BS URI is an address, not a question, in either mode.
const w3bs = w3bsUrl(q);
if (w3bs) {
location.href = w3bs;
return;
}
if (searchMode === 'ai') {
// Hand the question to the AI sidebar (privacy-first; uses your provider/key).
await chrome.storage.local.set({ pendingAiQuery: { text: q, at: Date.now() } });
Expand Down
35 changes: 35 additions & 0 deletions apps/desktop/extensions/ai-sidebar/w3bs-links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// w3bs:// links. Chromium has no handler for the scheme, so a click on one
// would ask to open xdg-open (or do nothing). Send it to the w3bs.org resolver
// page in the same tab instead; middle and modifier clicks open a new tab, the
// way an ordinary link would. Keep W3BS_GATEWAY in step with w3bs.js.
(() => {
const W3BS_GATEWAY = 'https://w3bs.org/browse?q=';

const w3bsLink = (event) => {
const path = typeof event.composedPath === 'function' ? event.composedPath() : [event.target];
for (const node of path) {
if (node instanceof HTMLAnchorElement || node instanceof SVGAElement) {
const href = node instanceof SVGAElement ? node.href.baseVal : node.getAttribute('href');
return href && /^\s*w3bs:/i.test(href) ? href.trim() : null;
}
}
return null;
};

const open = (event) => {
if (event.defaultPrevented || (event.type === 'auxclick' && event.button !== 1)) return;
const uri = w3bsLink(event);
if (!uri) return;
event.preventDefault();
event.stopImmediatePropagation();
const url = W3BS_GATEWAY + encodeURIComponent(uri);
if (event.type === 'auxclick' || event.ctrlKey || event.metaKey || event.shiftKey) {
window.open(url, '_blank', 'noopener');
} else {
location.assign(url);
}
};

document.addEventListener('click', open, true);
document.addEventListener('auxclick', open, true);
})();
10 changes: 10 additions & 0 deletions apps/desktop/extensions/ai-sidebar/w3bs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// W3BS URIs (w3bs://type/namespace/name@version) open through the w3bs.org
// resolver page: Chromium has no handler for the scheme. The launcher and the
// w3bs-links.js content script use the same gateway.
export const W3BS_GATEWAY = 'https://w3bs.org/browse?q=';

/** The resolver URL for a w3bs: URI, or null when `input` is not one. */
export function w3bsUrl(input) {
const uri = String(input ?? '').trim();
return /^w3bs:/i.test(uri) ? W3BS_GATEWAY + encodeURIComponent(uri) : null;
}
22 changes: 22 additions & 0 deletions apps/desktop/extensions/ai-sidebar/w3bs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest';
import { w3bsUrl } from './w3bs.js';

describe('w3bsUrl', () => {
it('sends a W3BS URI to the w3bs.org resolver', () => {
expect(w3bsUrl('w3bs://prompt/w3bs/research@1')).toBe(
'https://w3bs.org/browse?q=w3bs%3A%2F%2Fprompt%2Fw3bs%2Fresearch%401',
);
});

it('accepts any case and surrounding space', () => {
expect(w3bsUrl(' W3BS://prompt/w3bs/research ')).toBe(
'https://w3bs.org/browse?q=W3BS%3A%2F%2Fprompt%2Fw3bs%2Fresearch',
);
});

it('leaves everything else alone', () => {
expect(w3bsUrl('https://w3bs.org')).toBeNull();
expect(w3bsUrl('what is w3bs://')).toBeNull();
expect(w3bsUrl('')).toBeNull();
});
});
17 changes: 17 additions & 0 deletions apps/desktop/launcher/tronbrowser
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ while [ "$_i" -lt "$_argc" ]; do
set -- "$@" "$_a"
done

# --- w3bs:// URIs ---------------------------------------------------------
# Chromium has no handler for w3bs://, so a W3BS URI handed to us (`tron
# w3bs://prompt/w3bs/research@1`, or the desktop entry's x-scheme-handler/w3bs)
# opens through the w3bs.org resolver page instead. Override the gateway with
# TRONBROWSER_W3BS_GATEWAY; the URI is appended percent-encoded.
W3BS_GATEWAY="${TRONBROWSER_W3BS_GATEWAY:-https://w3bs.org/browse?q=}"
_argc=$#
_i=0
while [ "$_i" -lt "$_argc" ]; do
_a="$1"; shift; _i=$((_i + 1))
case "$_a" in
w3bs:*|W3BS:*)
_a="$W3BS_GATEWAY$(printf '%s' "$_a" | od -An -v -tx1 | tr -d ' \n' | sed 's/\(..\)/%\1/g')" ;;
esac
set -- "$@" "$_a"
done

BROWSER="" # a binary path, or "flatpak"
FLATPAK_APP=""
IS_SNAP=0
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/scripts/build-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ TryExec=tron
Icon=tronbrowser
Terminal=false
Categories=Network;WebBrowser;
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/w3bs;
Keywords=web;browser;privacy;tor;ai;
StartupNotify=true
StartupWMClass=tronbrowser
Expand Down
18 changes: 18 additions & 0 deletions apps/desktop/test/launcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ describe('launcher flags', () => {
expect(argv).toContain('https://tronbrowser.dev');
expect(argv).toContain('--incognito');
});

it('opens a w3bs:// URI through the w3bs.org resolver', () => {
const { argv } = run(['w3bs://prompt/w3bs/research@1', 'https://tronbrowser.dev']);
const opened = argv.filter((a) => !a.startsWith('--'));
expect(opened).toHaveLength(2);
const url = new URL(opened[0]!);
expect(url.origin + url.pathname).toBe('https://w3bs.org/browse');
expect(url.searchParams.get('q')).toBe('w3bs://prompt/w3bs/research@1');
expect(opened[1]).toBe('https://tronbrowser.dev');
});

it('honours TRONBROWSER_W3BS_GATEWAY', () => {
const { argv } = run(['w3bs://prompt/w3bs/research'], {
env: { TRONBROWSER_W3BS_GATEWAY: 'http://127.0.0.1:8080/browse?q=' },
});
const url = new URL(argv.find((a) => a.startsWith('http://127.0.0.1:8080/'))!);
expect(url.searchParams.get('q')).toBe('w3bs://prompt/w3bs/research');
});
});

// --- Omnibox search engine ---------------------------------------------------
Expand Down
10 changes: 9 additions & 1 deletion apps/web/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1182,11 +1182,19 @@ Exec=$TRON_CLI %U
Icon=$icon
Terminal=false
Categories=Network;WebBrowser;
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/w3bs;
StartupWMClass=TronBrowser
StartupNotify=true
DESKTOP
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$apps_dir" 2>/dev/null || true
# Open w3bs:// links through TronBrowser (the launcher hands them to the
# w3bs.org resolver), unless the user already picked another handler.
if command -v xdg-mime >/dev/null 2>&1; then
_w3bs="$(xdg-mime query default x-scheme-handler/w3bs 2>/dev/null || true)"
if [ -z "$_w3bs" ] || [ "$_w3bs" = "tronbrowser.desktop" ]; then
xdg-mime default tronbrowser.desktop x-scheme-handler/w3bs 2>/dev/null || true
fi
fi

ensure_engine || true # TronBrowser's own engine; ensure_browser is the fallback
ensure_browser
Expand Down
2 changes: 1 addition & 1 deletion distribution/appimage/tronbrowser.desktop
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ Exec=tron %u
Icon=tronbrowser
Terminal=false
Categories=Network;WebBrowser;
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/w3bs;
Loading