theseus/chrome.html
Local Dev 89c80a0103 Theseus navigator UI overhaul, BNS-only indexer, site restructure
Theseus Navigator:
- Firefox-style toolbar: SVG back/fwd/reload/home buttons
- security padlock badge in the address bar opens a floating site-info
  overlay VIEW (on top of the page, never pushes content down)
- Edge-style favorites bar (shown only on the new-tab/home page)
- search-engine picker (DuckDuckGo/Google/Bing/Brave/Startpage)
- Theseus logo moved right, opens Settings; native menu removed
- address bar doubles as search; bottom resolver status line

Argus:
- BNS-only indexer: an electrum endpoint over a bare BCHN node
  (watches just the beacon, caches txs so no txindex needed) + test
- registry lifecycle / portfolio / name-index modules

Site restructure (nav/apps/store/hermes/choose) + runbook notes.
2026-07-30 08:16:55 +02:00

253 lines
14 KiB
HTML

<!doctype html>
<html>
<head><meta charset="utf-8">
<style>
:root { color-scheme: dark; font-family: system-ui, sans-serif; }
/* `display:flex/grid` otherwise overrides the HTML hidden attribute — force it. */
[hidden] { display: none !important; }
body { margin: 0; height: 100vh; background: #0f1420; color: #e7eaf1; user-select: none; overflow: hidden; }
/* tab strip */
.tabs { display: flex; align-items: flex-end; gap: 4px; padding: 6px 8px 0; height: 34px; }
.tab { display: flex; align-items: center; gap: 8px; max-width: 200px; min-width: 90px; padding: 6px 10px;
background: #1b2330; border: 1px solid #ffffff14; border-bottom: none; border-radius: 8px 8px 0 0;
font-size: 12.5px; cursor: default; color: #b9c2d0; }
.tab.active { background: #26304180; color: #fff; }
.tab .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.tab .x { opacity: .5; cursor: pointer; padding: 0 2px; border-radius: 4px; }
.tab .x:hover { opacity: 1; background: #ffffff1a; }
.newtab { padding: 4px 10px; cursor: pointer; color: #8b98a9; border-radius: 6px; font-size: 16px; }
.newtab:hover { background: #ffffff12; color: #fff; }
/* toolbar */
.bar { display: flex; gap: 6px; align-items: center; padding: 6px 10px; }
.nav { display: flex; gap: 1px; }
.ic { width: 34px; height: 32px; display: grid; place-items: center; border-radius: 8px; cursor: pointer;
color: #c5cdda; background: transparent; border: none; padding: 0; }
.ic svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7;
stroke-linecap: round; stroke-linejoin: round; }
.ic:hover { background: #ffffff14; color: #fff; }
.ic:active { background: #ffffff22; }
.ic:disabled { opacity: .28; cursor: default; background: transparent; }
/* address bar with a security badge on the left + bookmark star on the right */
.urlwrap { flex: 1; display: flex; align-items: center; gap: 4px; background: #1b2330; border: 1px solid #ffffff22;
border-radius: 999px; padding: 0 6px 0 4px; }
.urlwrap:focus-within { border-color: #4b7bec; }
/* Firefox-style padlock badge */
.secbadge { display: grid; place-items: center; width: 28px; height: 26px; border-radius: 6px; border: none;
background: transparent; cursor: pointer; padding: 0; }
.secbadge:hover { background: #ffffff14; }
.secbadge svg { width: 14px; height: 14px; }
.secbadge svg .shk { fill: none; stroke: #a9b3c2; stroke-width: 1.3; }
.secbadge svg .bdy { fill: #a9b3c2; }
.secbadge.warn svg .shk { stroke: #f6ad55; } .secbadge.warn svg .bdy { fill: #f6ad55; }
.star { border: none; background: transparent; cursor: pointer; color: #8b98a9; font-size: 15px; padding: 2px 4px; border-radius: 6px; }
.star:hover { background: #ffffff14; color: #fff; } .star.on { color: #ffd23d; }
input { padding: 7px 6px; border-radius: 999px; border: none; background: transparent; color: inherit; font-size: 13.5px; outline: none; }
#url { flex: 1; }
/* search box + engine picker */
.searchbox { display: flex; align-items: center; gap: 2px; background: #1b2330; border: 1px solid #ffffff22;
border-radius: 999px; padding-left: 8px; }
.searchbox:focus-within { border-color: #4b7bec; }
.searchbox .mag { color: #8b98a9; font-size: 12px; }
#search { width: 150px; }
.engine { background: #10151f; color: #b9c2d0; border: none; border-left: 1px solid #ffffff1a; border-radius: 0 999px 999px 0;
font-size: 11.5px; padding: 6px 6px; outline: none; cursor: pointer; }
.reg { font-size: 10.5px; letter-spacing: .03em; padding: 2px 8px; border-radius: 999px; white-space: nowrap;
background: rgba(214,255,61,.13); color: #d6ff3d; border: 1px solid #d6ff3d33; }
.tor { padding: 7px 10px; border-radius: 8px; border: 1px solid #ffffff22; background: #2b2f3a; color: #fff; cursor: pointer; white-space: nowrap; font-size: 12.5px; }
.tor.connecting { background: #7a5c14; } .tor.on { background: #6b3fa0; }
/* Theseus logo — moved to the right, opens Settings */
.logo { display: flex; align-items: center; gap: 6px; font-weight: 700; color: #d6ff3d; white-space: nowrap;
padding: 6px 12px; border: 1px solid #d6ff3d33; border-radius: 8px; cursor: pointer;
background: rgba(214,255,61,.08); font-size: 13px; }
.logo:hover { background: rgba(214,255,61,.16); }
.logo .gear { font-size: 12px; opacity: .8; }
/* bookmarks / saved-pages bar (Edge-style) */
.bookmarks { display: flex; align-items: center; gap: 4px; padding: 3px 10px 5px; overflow: hidden; }
.bm { display: flex; align-items: center; gap: 6px; max-width: 180px; padding: 3px 9px; border-radius: 6px;
background: #171e2a; border: 1px solid #ffffff10; color: #c5cdda; font-size: 12px; cursor: pointer; }
.bm:hover { background: #202a3a; color: #fff; }
.bm .bt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bm .bx { opacity: 0; cursor: pointer; font-size: 11px; } .bm:hover .bx { opacity: .55; }
.bm .bx:hover { opacity: 1; color: #f6768a; }
.bm-empty { color: #5e6678; font-size: 11.5px; padding: 3px 4px; }
.tordisc { font-size: 11.5px; color: #d9c7f2; background: #2a1c40; border-top: 1px solid #6b3fa055; padding: 5px 14px; }
.tordisc a { color: #d6ff3d; }
.bcnrbar { display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: #dbead0;
background: #1a2417; border-top: 1px solid #d6ff3d33; padding: 6px 14px; }
.bcnrbar .breg { font-size: 10px; letter-spacing: .03em; padding: 1px 7px; border-radius: 999px;
background: rgba(214,255,61,.14); color: #d6ff3d; border: 1px solid #d6ff3d33; }
.bcnrbar b { color: #fff; }
.bcnrbar .bopen { margin-left: auto; padding: 4px 11px; border-radius: 7px; border: 1px solid #d6ff3d55;
background: rgba(214,255,61,.12); color: #eaffb0; cursor: pointer; font-size: 11.5px; }
.bcnrbar .bopen:hover { background: rgba(214,255,61,.22); }
.bcnrbar .bdismiss { color: #8b98a9; text-decoration: none; } .bcnrbar .bdismiss:hover { color: #fff; }
</style></head>
<body>
<div class="tabs" id="tabs"></div>
<div class="bar">
<div class="nav">
<button class="ic" id="back" title="Back"><svg viewBox="0 0 16 16"><path d="M10 3 L5 8 L10 13"/></svg></button>
<button class="ic" id="fwd" title="Forward"><svg viewBox="0 0 16 16"><path d="M6 3 L11 8 L6 13"/></svg></button>
<button class="ic" id="reload" title="Reload"><svg viewBox="0 0 16 16"><path d="M13 8 a5 5 0 1 1 -1.5 -3.6"/><path d="M13 2.5 L13 5 L10.5 5"/></svg></button>
<button class="ic" id="home" title="Home"><svg viewBox="0 0 16 16"><path d="M2.5 7.5 L8 3 L13.5 7.5"/><path d="M4 6.7 L4 13 L12 13 L12 6.7"/></svg></button>
</div>
<div class="urlwrap">
<button class="secbadge" id="secbadge" title="Site information" hidden>
<svg viewBox="0 0 16 16" aria-hidden="true"><path class="shk" d="M5 7 V5.3 a3 3 0 0 1 6 0 V7"/><rect class="bdy" x="3.6" y="6.9" width="8.8" height="6" rx="1.2"/></svg>
</button>
<input id="url" placeholder="Search or enter a .bch / .p2p name or web address" spellcheck="false">
<span class="reg" id="reg" hidden></span>
<button class="star" id="star" title="Save this page"></button>
</div>
<div class="searchbox">
<span class="mag">🔍</span>
<input id="search" placeholder="Search" spellcheck="false">
<select id="engine" class="engine" title="Search engine"></select>
</div>
<button class="tor" id="tor" title="Route traffic through Tor">🧅 Tor: Off</button>
<button class="logo" id="logo" title="Settings"><span>⛓ Theseus</span><span class="gear"></span></button>
</div>
<div class="bookmarks" id="bookmarks" hidden></div>
<div id="tordisc" class="tordisc" hidden>
Onion mode hides your IP from sites and your ISP (including .bch lookups).
It is <b>not full anonymity</b> — this browser can still be fingerprinted; for that use the Tor Browser.
<a href="#" id="tordismiss">got it</a>
</div>
<div id="bcnrbar" class="bcnrbar" hidden>
<span class="breg" id="bcnrreg">BCNR</span>
<span><b id="bcnrhost"></b> also exists on the <b>BCNR</b> decentralized registry.</span>
<button id="bcnropen" class="bopen">Open on BCNR</button>
<a href="#" id="bcnrdismiss" class="bdismiss">dismiss</a>
</div>
<script>
const $ = (id) => document.getElementById(id);
const T = window.theseus;
function syncHeight() { requestAnimationFrame(() => T.setChromeHeight(document.body.scrollHeight)); }
// ---- navigation ----
function goURL() { const v = $("url").value.trim(); if (v) T.navigate(v); }
$("url").addEventListener("keydown", (e) => { if (e.key === "Enter") goURL(); });
$("search").addEventListener("keydown", (e) => { if (e.key === "Enter") { const q = $("search").value.trim(); if (q) { T.search(q); $("search").value = ""; } } });
$("back").onclick = () => T.back();
$("fwd").onclick = () => T.forward();
$("reload").onclick = () => T.reload();
$("home").onclick = () => T.goHome();
$("logo").onclick = () => T.openSettings(); // logo (right) opens Settings
// ---- search engine picker ----
T.getSearchEngines().then((d) => {
const sel = $("engine");
sel.innerHTML = d.engines.map((e) => `<option value="${e.id}">${e.name}</option>`).join("");
sel.value = d.current;
sel.onchange = () => T.setSearchEngine(sel.value);
});
// ---- bookmarks bar ----
let current = { url: "", title: "" };
let bookmarks = [];
function renderBookmarks() {
const box = $("bookmarks");
if (!bookmarks.length) { box.innerHTML = `<span class="bm-empty">No saved pages yet — click ☆ to save the current page</span>`; syncHeight(); return; }
box.innerHTML = bookmarks.map((b, i) =>
`<div class="bm" data-i="${i}" title="${(b.url || "").replace(/"/g, "&quot;")}">
<span class="bt">${(b.title || b.url).replace(/</g, "&lt;")}</span>
<span class="bx" data-x="${i}">✕</span></div>`).join("");
box.querySelectorAll(".bm").forEach((el) => el.onclick = (e) => {
const i = Number(el.dataset.i);
if (e.target.dataset.x !== undefined) { T.removeBookmark(bookmarks[i].url); }
else T.navigate(bookmarks[i].url);
});
syncHeight();
updateStar();
}
function updateStar() {
const saved = bookmarks.some((b) => b.url === current.url && current.url);
const s = $("star"); s.textContent = saved ? "★" : "☆"; s.classList.toggle("on", saved);
s.title = saved ? "Remove from saved pages" : "Save this page";
}
$("star").onclick = () => {
if (!current.url) return;
if (bookmarks.some((b) => b.url === current.url)) T.removeBookmark(current.url);
else T.addBookmark({ title: current.title || current.url, url: current.url });
};
T.getBookmarks().then((b) => { bookmarks = b || []; renderBookmarks(); });
T.onBookmarks((b) => { bookmarks = b || []; renderBookmarks(); });
// ---- security badge (Firefox-style padlock); opens the floating overlay ----
function setBadge(d) {
const b = $("secbadge");
if (!d || d.kind === "home") { b.hidden = true; return; }
b.hidden = false;
let cls = "secbadge", title = "Site information — click for details";
if (d.kind === "ok") title = "Secure · " + (d.source || "BCNR") + " — click for details";
else if (d.kind === "web") title = "Connection — click for details";
else if (d.kind === "resolving") title = "Resolving…";
else if (d.kind === "nxdomain") { cls = "secbadge warn"; title = "Not registered on BCNR"; }
else if (d.kind === "error") { cls = "secbadge warn"; title = "Resolution error"; }
b.className = cls; b.title = title;
}
// The popover is a separate overlay view; hand main the badge's screen position.
$("secbadge").onclick = () => {
const r = $("secbadge").getBoundingClientRect();
T.toggleSiteInfo({ x: Math.round(r.left), y: Math.round(r.bottom + 4) });
};
// ---- Tor ----
let torShown = false;
$("tor").onclick = () => { T.toggleTor(); if (!torShown) { $("tordisc").hidden = false; torShown = true; syncHeight(); } };
$("tordismiss").onclick = (e) => { e.preventDefault(); $("tordisc").hidden = true; syncHeight(); };
// ---- passive BCNR offer ----
T.onBcnrOffer((d) => {
const bar = $("bcnrbar");
if (d && d.host) { $("bcnrhost").textContent = d.host; $("bcnrreg").textContent = d.registry || "BCNR"; bar.hidden = false; }
else bar.hidden = true;
syncHeight();
});
$("bcnropen").onclick = () => { $("bcnrbar").hidden = true; syncHeight(); T.switchToBcnr(); };
$("bcnrdismiss").onclick = (e) => { e.preventDefault(); $("bcnrbar").hidden = true; syncHeight(); };
T.onTor((d) => {
const b = $("tor"); b.className = "tor " + (d.state === "on" ? "on" : d.state === "connecting" ? "connecting" : "");
b.textContent = d.state === "on" ? "🧅 Tor: On" : d.state === "connecting" ? "🧅 connecting…" : "🧅 Tor: Off";
});
// ---- tabs ----
T.onTabs((d) => {
$("back").disabled = !d.canBack; $("fwd").disabled = !d.canForward;
if (document.activeElement !== $("url")) $("url").value = d.url || "";
current.url = d.url || "";
const active = d.tabs.find((t) => t.active);
current.title = active ? active.title : "";
updateStar();
const box = $("tabs");
box.innerHTML = d.tabs.map((t) =>
`<div class="tab ${t.active ? "active" : ""}" data-id="${t.id}">
<span class="t">${(t.title || "New Tab").replace(/</g, "&lt;")}</span>
<span class="x" data-close="${t.id}">✕</span></div>`).join("") +
`<span class="newtab" id="newtab">+</span>`;
box.querySelectorAll(".tab").forEach((el) => el.onclick = (e) => {
if (e.target.dataset.close) T.closeTab(Number(e.target.dataset.close));
else T.switchTab(Number(el.dataset.id));
});
$("newtab").onclick = () => T.newTab();
});
// ---- provenance ----
T.onNav((d) => {
setBadge(d);
const reg = $("reg");
if (d.tld) { reg.hidden = false; reg.textContent = (d.registry || "BCNR") + " · ." + d.tld; } else reg.hidden = true;
if (document.activeElement !== $("url")) {
if (d.kind === "home") $("url").value = "";
else if (d.host && !$("url").value) $("url").value = d.host;
}
// Favorites bar shows only on the new-tab / home page (Edge-style); it hides
// once a real page loads so it never eats space while browsing.
$("bookmarks").hidden = !(!d || d.kind === "home");
syncHeight();
});
syncHeight();
</script>
</body>
</html>