Skip to content

Commit a5ec90b

Browse files
ralyodioclaude
andcommitted
feat(ext): Media tab (bittorrented.com) + move Torrents out of Chat (v3.3.1)
- New 📺 Media button in the sidebar opens media.html: connect your bittorrented.com account (reuses bittorrented.js connect/verify/disconnect), then browse Favorites / Live TV / Radio / Podcasts via /api/v1/* (defensive rendering of varying item shapes). - Chat page is now IRC + qrypt.chat only (Torrents removed — it didn't belong under Chat). Note: IRC WS needs the Ergo allowed-origins fix deployed (agentbbs deploy/ergo/ircd.yaml: chrome-extension:// origin) or the connection is rejected before SASL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9a5de2b commit a5ec90b

30 files changed

Lines changed: 152 additions & 33 deletions

File tree

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
<img src="icons/favicon.svg" alt="" class="logo" />
1212
<span class="brand">Tron<span class="accent">Browser</span></span>
1313
<nav class="tabs">
14-
<button class="tab active" data-tab="chat">💬 Chat</button>
15-
<button class="tab" data-tab="torrents">🧲 Torrents</button>
14+
<button class="tab active" data-tab="chat">💬 IRC</button>
1615
<button class="tab" data-tab="qrypt">🔒 qrypt.chat</button>
1716
</nav>
1817
</header>
@@ -50,11 +49,6 @@ <h2>Connect to IRC</h2>
5049
</div>
5150
</section>
5251

53-
<!-- ===== Torrents (placeholder) ===== -->
54-
<section id="tab-torrents" class="panel">
55-
<div class="soon">🧲 <strong>Torrents</strong> — DHT search + indexed torrents via bittorrented.com. Coming next.</div>
56-
</section>
57-
5852
<!-- ===== qrypt.chat (placeholder) ===== -->
5953
<section id="tab-qrypt" class="panel">
6054
<div class="soon">🔒 <strong>qrypt.chat</strong> — quantum-resistant E2EE messaging (CoinPay login, contacts, media). Lands once the qrypt.chat backend is live.</div>

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.3.0",
4+
"version": "3.3.1",
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",
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<link rel="stylesheet" href="chat.css" />
7+
<title>TronBrowser · Media</title>
8+
</head>
9+
<body>
10+
<header class="apphdr">
11+
<img src="icons/favicon.svg" alt="" class="logo" />
12+
<span class="brand">Tron<span class="accent">Browser</span></span>
13+
<nav class="tabs" id="media-tabs">
14+
<button class="tab active" data-tab="favorites">★ Favorites</button>
15+
<button class="tab" data-tab="livetv">📺 Live TV</button>
16+
<button class="tab" data-tab="radio">📻 Radio</button>
17+
<button class="tab" data-tab="podcasts">🎙 Podcasts</button>
18+
</nav>
19+
<span style="flex:1"></span>
20+
<span id="acct" class="muted" style="font-size:12px"></span>
21+
<button id="disc" class="tab hidden" title="Disconnect"></button>
22+
</header>
23+
24+
<!-- connect -->
25+
<section id="connect" class="panel active">
26+
<div class="connect">
27+
<h2>Connect bittorrented.com</h2>
28+
<p class="muted">Sign in to <strong>bittorrented.com</strong> to stream your favorites, live TV, radio, and podcasts right here. We open the sign-in in a tab and bring the token back to this device.</p>
29+
<button id="go" class="primary">Connect bittorrented.com</button>
30+
<div id="status" class="status"></div>
31+
</div>
32+
</section>
33+
34+
<!-- content (one panel, swapped by tab) -->
35+
<section id="content" class="panel">
36+
<main class="convo" style="width:100%">
37+
<div id="list" class="msgs" style="gap:8px"></div>
38+
</main>
39+
</section>
40+
41+
<script type="module" src="media.js"></script>
42+
</body>
43+
</html>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { BTR_BASE, getToken, connect, verify, disconnect } from './bittorrented.js';
2+
3+
const $ = (id) => document.getElementById(id);
4+
const el = (t, c, x) => { const e = document.createElement(t); if (c) e.className = c; if (x != null) e.textContent = x; return e; };
5+
6+
let activeTab = 'favorites';
7+
8+
function setStatus(text, kind) { const s = $('status'); s.textContent = text; s.className = 'status ' + (kind || ''); }
9+
10+
function showConnected(email) {
11+
$('connect').classList.remove('active');
12+
$('content').classList.add('active');
13+
$('acct').textContent = email || 'connected';
14+
$('disc').classList.remove('hidden');
15+
loadTab(activeTab);
16+
}
17+
18+
function showDisconnected() {
19+
$('content').classList.remove('active');
20+
$('connect').classList.add('active');
21+
$('acct').textContent = '';
22+
$('disc').classList.add('hidden');
23+
}
24+
25+
// bittorrented item shapes vary by section — render defensively from common fields.
26+
function fieldOf(o, keys) { for (const k of keys) if (o && o[k] != null && o[k] !== '') return o[k]; return ''; }
27+
28+
function renderItems(items) {
29+
const list = $('list'); list.innerHTML = '';
30+
if (!Array.isArray(items) || items.length === 0) { list.appendChild(el('div', 'muted', 'Nothing here yet.')); return; }
31+
for (const it of items) {
32+
const title = fieldOf(it, ['title', 'name', 'label']) || '(untitled)';
33+
const url = fieldOf(it, ['url', 'stream', 'streamUrl', 'link', 'magnet', 'href']);
34+
const sub = fieldOf(it, ['group', 'category', 'genre', 'author', 'description']);
35+
const row = el('div', 'msg');
36+
const n = el('span', 'n', '▶');
37+
const a = url ? el('a', null, title) : el('span', null, title);
38+
if (url) { a.href = url; a.target = '_blank'; a.rel = 'noreferrer'; }
39+
row.append(n, a);
40+
if (sub) { const s = el('span', 't', ' ' + sub); row.append(s); }
41+
list.appendChild(row);
42+
}
43+
}
44+
45+
async function loadTab(tab) {
46+
activeTab = tab;
47+
document.querySelectorAll('#media-tabs .tab').forEach((b) => b.classList.toggle('active', b.dataset.tab === tab));
48+
const list = $('list'); list.innerHTML = ''; list.appendChild(el('div', 'muted', 'Loading…'));
49+
const token = await getToken();
50+
// Map UI tabs → bittorrented.com /api/v1 endpoints.
51+
const path = { favorites: 'favorites', livetv: 'livetv', radio: 'radio', podcasts: 'podcasts' }[tab] || 'favorites';
52+
try {
53+
const r = await fetch(`${BTR_BASE}/api/v1/${path}`, { headers: { authorization: `Bearer ${token}` } });
54+
if (r.status === 401) { showDisconnected(); setStatus('Session expired — connect again.', 'err'); return; }
55+
const data = await r.json().catch(() => ([]));
56+
renderItems(Array.isArray(data) ? data : (data.items || data.results || data[path] || []));
57+
} catch (e) {
58+
list.innerHTML = '';
59+
list.appendChild(el('div', 'muted', 'Could not load ' + path + '. ' + ((e && e.message) || '')));
60+
}
61+
}
62+
63+
document.querySelectorAll('#media-tabs .tab').forEach((b) => b.addEventListener('click', () => loadTab(b.dataset.tab)));
64+
65+
$('go').addEventListener('click', async () => {
66+
setStatus('Opening bittorrented.com… finish signing in & approving in the new tab.');
67+
try {
68+
const res = await connect();
69+
if (res.connected) { setStatus('Connected ✓', 'ok'); showConnected(res.email); }
70+
else setStatus('Connection not completed.', 'err');
71+
} catch (e) { setStatus((e && e.message) || 'connect failed', 'err'); }
72+
});
73+
74+
$('disc').addEventListener('click', async () => { await disconnect(); showDisconnected(); });
75+
76+
// On load: if already connected, jump straight to content.
77+
(async () => {
78+
const res = await verify();
79+
if (res.connected) showConnected(res.email);
80+
})();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
<img src="icons/favicon.svg" alt="TronBrowser" class="brand-logo" />
1212
<span class="brand">Tron<span class="accent">Browser</span></span>
1313
<span id="provider" class="provider"></span>
14-
<button id="chat" class="tor-btn" title="Open Chat (IRC + more)">💬 Chat</button>
14+
<button id="chat" class="tor-btn" title="Open Chat (IRC + qrypt.chat)">💬 Chat</button>
15+
<button id="media" class="tor-btn" title="Open Media (bittorrented.com)">📺 Media</button>
1516
<button id="tor" class="tor-btn" title="Route this session through Tor" aria-pressed="false">🧅 Tor</button>
1617
<button id="settings" title="Settings"></button>
1718
</header>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ inputEl.addEventListener('keydown', (e) => {
136136
});
137137

138138
el('chat').addEventListener('click', () => chrome.tabs.create({ url: chrome.runtime.getURL('chat.html') }));
139+
el('media').addEventListener('click', () => chrome.tabs.create({ url: chrome.runtime.getURL('media.html') }));
139140
el('settings').addEventListener('click', () => chrome.runtime.openOptionsPage());
140141
el('open-options').addEventListener('click', (e) => { e.preventDefault(); chrome.runtime.openOptionsPage(); });
141142
chrome.storage.onChanged.addListener((changes) => { if (changes.aiConfig) loadConfig(); });

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.3.0",
3+
"version": "3.3.1",
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.3.0",
3+
"version": "3.3.1",
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.3.0",
3+
"version": "3.3.1",
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.3.0",
7+
"version": "3.3.1",
88
"orientation": "portrait",
99
"userInterfaceStyle": "dark",
1010
"platforms": [

0 commit comments

Comments
 (0)