sites: scroll-spy tracks scroll position, fires the last section on bottom

The IntersectionObserver-based spy only fired when a section entered a
narrow band 40–45% down the viewport. Short sections at the tail of a
page — Operator key and Roadmap on theseus.x/extensions, whatever came
last on the other pages — never made it into that band because the
page runs out of scroll before their top crosses 40%, so their sidebar
links stayed dim no matter how far the reader scrolled.

Rewrote the spy across every page with a sidebar (theseus.x home /
plug-ins / extensions and sirius.x home / tld / theseus / docs / admin
/ brand): it listens to window scroll, picks the last section whose
top has crossed 35% of the viewport, and — when the window is scrolled
to within 4px of the bottom — snaps to the final section regardless of
where its top is. Every link in the rail can now activate.

theseus.x/extensions also had the spy IIFE bundled with the manifest
loader in one <script> block near the middle of the page, so at run
time the parser hadn't yet reached the last three sections and only
Bundled/Community/Build were ever registered. Moved the spy to its own
<script> just before </body> so it sees every section that ships.
This commit is contained in:
Local Dev 2026-09-20 19:20:30 +02:00
parent 0e8e1949df
commit 172d16e84b
3 changed files with 686 additions and 655 deletions

View file

@ -260,23 +260,6 @@
</div>`).join(""); </div>`).join("");
} catch (e) { box.innerHTML = '<div class="ext" style="color:var(--dim)">Catalog unavailable right now.</div>'; } } catch (e) { box.innerHTML = '<div class="ext" style="color:var(--dim)">Catalog unavailable right now.</div>'; }
})(); })();
// Sidenav scroll-spy: highlight the section currently in view.
(() => {
const links = document.querySelectorAll(".sidenav a[href^='#']");
if (!links.length) return;
const map = new Map();
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) map.set(el, a); });
const io = new IntersectionObserver((entries) => {
entries.forEach((en) => {
if (!en.isIntersecting) return;
const a = map.get(en.target); if (!a) return;
links.forEach((l) => l.classList.remove("active"));
a.classList.add("active");
});
}, { rootMargin: "-40% 0px -55% 0px", threshold: 0 });
map.forEach((_, el) => io.observe(el));
})();
</script> </script>
<section id="updates"> <section id="updates">
@ -318,5 +301,33 @@
</div> </div>
<script>
// Sidenav scroll-spy: highlight the section currently in view.
// Uses scroll-position tracking so short sections at the end of the page
// still activate their link when the reader scrolls to them.
(() => {
const links = document.querySelectorAll(".sidenav a[href^='#']");
if (!links.length) return;
const entries = [];
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
if (!entries.length) return;
const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
const update = () => {
const doc = document.documentElement;
const scrollBottom = window.scrollY + window.innerHeight;
if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
const line = window.scrollY + window.innerHeight * 0.35;
let active = entries[0].link;
for (const e of entries) {
if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
else break;
}
setActive(active);
};
window.addEventListener("scroll", update, { passive: true });
window.addEventListener("resize", update);
update();
})();
</script>
</body> </body>
</html> </html>

View file

@ -1,395 +1,405 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>theseus.x — the browser that speaks Bitcoin Cash names</title> <title>theseus.x — the browser that speaks Bitcoin Cash names</title>
<meta name="description" content="Theseus Navigator: a Chromium-based browser with native Bitcoin Cash name resolution built in. No OS-level daemon, no DNS shim, no root-store install. Windows + Android."> <meta name="description" content="Theseus Navigator: a Chromium-based browser with native Bitcoin Cash name resolution built in. No OS-level daemon, no DNS shim, no root-store install. Windows + Android.">
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml"> <link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
<link rel="mask-icon" href="assets/favicon.svg" color="#d6ff3d"> <link rel="mask-icon" href="assets/favicon.svg" color="#d6ff3d">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:site_name" content="theseus.x"> <meta property="og:site_name" content="theseus.x">
<meta property="og:title" content="theseus.x — the browser that speaks Bitcoin Cash names"> <meta property="og:title" content="theseus.x — the browser that speaks Bitcoin Cash names">
<meta property="og:description" content="Native .bch / BCNR resolution in a Chromium browser. No daemon, no root-store install. Windows + Android."> <meta property="og:description" content="Native .bch / BCNR resolution in a Chromium browser. No daemon, no root-store install. Windows + Android.">
<meta property="og:url" content="https://theseus.x/"> <meta property="og:url" content="https://theseus.x/">
<meta property="og:image" content="https://theseus.x/assets/logo.svg"> <meta property="og:image" content="https://theseus.x/assets/logo.svg">
<meta name="twitter:card" content="summary"> <meta name="twitter:card" content="summary">
<meta name="twitter:title" content="theseus.x — the browser that speaks Bitcoin Cash names"> <meta name="twitter:title" content="theseus.x — the browser that speaks Bitcoin Cash names">
<meta name="twitter:description" content="Native BCNR resolution in a Chromium browser. No daemon, no root-store install."> <meta name="twitter:description" content="Native BCNR resolution in a Chromium browser. No daemon, no root-store install.">
<style> <style>
:root{ :root{
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09); --bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09);
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; --ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
--chain:#d6ff3d; --sia:#b39ddb; --srv:#4fd1a5; --ok:#4fd1a5; --warn:#ffc75f; --chain:#d6ff3d; --sia:#b39ddb; --srv:#4fd1a5; --ok:#4fd1a5; --warn:#ffc75f;
} }
*{box-sizing:border-box} *{box-sizing:border-box}
html{scroll-behavior:smooth} html{scroll-behavior:smooth}
body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg)); body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg));
color:var(--ink);font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh} color:var(--ink);font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh}
a{color:var(--acid)} a{color:var(--acid)}
.wrap{max-width:980px;margin:0 auto;padding:0 1.2rem} .wrap{max-width:980px;margin:0 auto;padding:0 1.2rem}
.topnav{position:fixed;top:0;left:0;right:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap; .topnav{position:fixed;top:0;left:0;right:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap;
padding:10px 1.2rem;background:rgba(11,14,20,.88);backdrop-filter:blur(10px)} padding:10px 1.2rem;background:rgba(11,14,20,.88);backdrop-filter:blur(10px)}
.topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px;display:flex;align-items:center;gap:8px} .topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px;display:flex;align-items:center;gap:8px}
.topnav .brand .g{color:var(--acid)} .topnav .brand .g{color:var(--acid)}
.topnav .brand svg{width:20px;height:20px} .topnav .brand svg{width:20px;height:20px}
.topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px; .topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px;
display:inline-flex;align-items:center;gap:6px} display:inline-flex;align-items:center;gap:6px}
.topnav a:hover{color:var(--ink);background:var(--panel)} .topnav a:hover{color:var(--ink);background:var(--panel)}
.topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)} .topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)}
.topnav a.get{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500} .topnav a.get{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500}
.topnav a.get:hover{background:rgba(214,255,61,.18)} .topnav a.get:hover{background:rgba(214,255,61,.18)}
.topnav a .g{color:var(--acid)} .topnav a .g{color:var(--acid)}
.topnav a svg.tabicon{width:15px;height:15px;flex:none} .topnav a svg.tabicon{width:15px;height:15px;flex:none}
/* Adaptive edge nav: full-width panel at left edge on wide, /* Adaptive edge nav: full-width panel at left edge on wide,
icon rail on medium, bottom bar with icons on mobile. icon rail on medium, bottom bar with icons on mobile.
Topnav is fixed at the top spanning the full width; sidebar starts Topnav is fixed at the top spanning the full width; sidebar starts
just below it and content flows in the right column. */ just below it and content flows in the right column. */
:root{--sn-w:200px;--tn-h:52px} :root{--sn-w:200px;--tn-h:52px}
/* Content stays centered — the sidebar overlays on top of the page. */ /* Content stays centered — the sidebar overlays on top of the page. */
body{padding-top:var(--tn-h)} body{padding-top:var(--tn-h)}
.sidenav{position:fixed;top:var(--tn-h);left:0;bottom:0;width:var(--sn-w);z-index:15; .sidenav{position:fixed;top:var(--tn-h);left:0;bottom:0;width:var(--sn-w);z-index:15;
background:transparent;padding:56px 12px 22px 18px; background:transparent;padding:56px 12px 22px 18px;
overflow-y:auto;transition:width .15s ease;pointer-events:none} overflow-y:auto;transition:width .15s ease;pointer-events:none}
.sidenav a{display:flex;align-items:center;gap:14px;color:var(--mut);text-decoration:none; .sidenav a{display:flex;align-items:center;gap:14px;color:var(--mut);text-decoration:none;
padding:9px 12px;border-radius:10px; padding:9px 12px;border-radius:10px;
font-size:14.5px;font-weight:500;opacity:.35;pointer-events:auto; font-size:14.5px;font-weight:500;opacity:.35;pointer-events:auto;
transition:opacity .12s,color .12s,background .12s,backdrop-filter .12s} transition:opacity .12s,color .12s,background .12s,backdrop-filter .12s}
.sidenav a .ic{flex:none;width:22px;font-size:18px;line-height:1;text-align:center; .sidenav a .ic{flex:none;width:22px;font-size:18px;line-height:1;text-align:center;
display:none;justify-content:center;align-items:center} display:none;justify-content:center;align-items:center}
.sidenav a .lb{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .sidenav a .lb{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidenav a:hover{opacity:1;color:var(--ink);background:rgba(11,14,20,.72);backdrop-filter:blur(8px)} .sidenav a:hover{opacity:1;color:var(--ink);background:rgba(11,14,20,.72);backdrop-filter:blur(8px)}
.sidenav a.active{opacity:.85;color:var(--acid)} .sidenav a.active{opacity:.85;color:var(--acid)}
.sidenav a.active:hover{opacity:1} .sidenav a.active:hover{opacity:1}
/* Medium: shrink to an icon rail — icons only */ /* Medium: shrink to an icon rail — icons only */
@media (max-width:1180px){ @media (max-width:1180px){
:root{--sn-w:64px} :root{--sn-w:64px}
.sidenav{padding:22px 6px} .sidenav{padding:22px 6px}
.sidenav .lbl{display:none} .sidenav .lbl{display:none}
.sidenav a{justify-content:center;padding:12px 4px;gap:0} .sidenav a{justify-content:center;padding:12px 4px;gap:0}
.sidenav a .ic{display:inline-flex} .sidenav a .ic{display:inline-flex}
.sidenav a .lb{display:none} .sidenav a .lb{display:none}
} }
/* Mobile: bottom icon bar */ /* Mobile: bottom icon bar */
@media (max-width:720px){ @media (max-width:720px){
:root{--sn-w:0px} :root{--sn-w:0px}
body{padding-left:0;padding-bottom:64px} body{padding-left:0;padding-bottom:64px}
.sidenav{top:auto;bottom:0;left:0;right:0;width:100%;height:auto; .sidenav{top:auto;bottom:0;left:0;right:0;width:100%;height:auto;
padding:4px 4px;flex-direction:row;background:rgba(11,14,20,.52); padding:4px 4px;flex-direction:row;background:rgba(11,14,20,.52);
display:flex;justify-content:space-around;overflow-x:auto} display:flex;justify-content:space-around;overflow-x:auto}
.sidenav .lbl{display:none} .sidenav .lbl{display:none}
.sidenav a{flex:1;min-width:56px;padding:10px 6px;justify-content:center;gap:0} .sidenav a{flex:1;min-width:56px;padding:10px 6px;justify-content:center;gap:0}
.sidenav a .ic{display:inline-flex} .sidenav a .ic{display:inline-flex}
.sidenav a .lb{display:none} .sidenav a .lb{display:none}
} }
header.hero{text-align:center;padding:5rem 1.2rem 1.5rem} header.hero{text-align:center;padding:5rem 1.2rem 1.5rem}
.mark{display:inline-block} .mark{display:inline-block}
.mark svg{width:80px;height:80px;filter:drop-shadow(0 4px 24px rgba(214,255,61,.18))} .mark svg{width:80px;height:80px;filter:drop-shadow(0 4px 24px rgba(214,255,61,.18))}
h1{font-size:clamp(2rem,5.5vw,2.9rem);margin:.9rem 0 .3rem;letter-spacing:-.5px} h1{font-size:clamp(2rem,5.5vw,2.9rem);margin:.9rem 0 .3rem;letter-spacing:-.5px}
h1 .g{color:var(--acid)} h1 .g{color:var(--acid)}
.tag{color:var(--mut);font-size:1.05rem;max-width:660px;margin:0 auto} .tag{color:var(--mut);font-size:1.05rem;max-width:660px;margin:0 auto}
.cta{margin-top:1.8rem;display:flex;gap:10px;justify-content:center;flex-wrap:wrap} .cta{margin-top:1.8rem;display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
.btn{display:inline-block;text-decoration:none;padding:11px 20px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;border:none;cursor:pointer;font-family:inherit;transition:filter .1s} .btn{display:inline-block;text-decoration:none;padding:11px 20px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;border:none;cursor:pointer;font-family:inherit;transition:filter .1s}
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)} .btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600} .btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
.btn:hover{filter:brightness(1.12)} .btn:hover{filter:brightness(1.12)}
section{padding:2.6rem 0;border-top:1px solid var(--line);margin-top:2.5rem;scroll-margin-top:72px} section{padding:2.6rem 0;border-top:1px solid var(--line);margin-top:2.5rem;scroll-margin-top:72px}
section:first-of-type{border-top:none;margin-top:1.5rem} section:first-of-type{border-top:none;margin-top:1.5rem}
h2{font-size:1.5rem;margin:0 0 .4rem} h2{font-size:1.5rem;margin:0 0 .4rem}
h3{font-size:1.1rem;margin:0 0 .35rem} h3{font-size:1.1rem;margin:0 0 .35rem}
.lede{color:var(--mut);margin:0 0 1.6rem;max-width:720px} .lede{color:var(--mut);margin:0 0 1.6rem;max-width:720px}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px} .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px 20px} .card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px 20px}
.card h3{margin:0 0 .5rem;display:flex;align-items:center;gap:8px} .card h3{margin:0 0 .5rem;display:flex;align-items:center;gap:8px}
.card p{margin:0;color:var(--mut);font-size:14px} .card p{margin:0;color:var(--mut);font-size:14px}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none} .dot{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none}
.d-chain{background:var(--chain)} .d-sia{background:var(--sia)} .d-srv{background:var(--srv)} .d-chain{background:var(--chain)} .d-sia{background:var(--sia)} .d-srv{background:var(--srv)}
.note{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--acid); .note{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--acid);
border-radius:10px;padding:14px 18px;color:var(--mut);font-size:14px;margin-top:1.4rem} border-radius:10px;padding:14px 18px;color:var(--mut);font-size:14px;margin-top:1.4rem}
.note.warn{border-left-color:var(--warn)} .note.warn{border-left-color:var(--warn)}
.note b{color:var(--ink)} .note b{color:var(--ink)}
code,.mono{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px} code,.mono{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px}
pre{background:#0e131b;border:1px solid var(--line);border-radius:10px;padding:12px 14px;overflow-x:auto; pre{background:#0e131b;border:1px solid var(--line);border-radius:10px;padding:12px 14px;overflow-x:auto;
font-family:ui-monospace,monospace;font-size:12.5px;color:var(--ink);margin:.8rem 0} font-family:ui-monospace,monospace;font-size:12.5px;color:var(--ink);margin:.8rem 0}
/* Downloads: platform cards */ /* Downloads: platform cards */
.plats{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:1rem} .plats{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:1rem}
@media (max-width:720px){.plats{grid-template-columns:1fr}} @media (max-width:720px){.plats{grid-template-columns:1fr}}
.plat{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px 22px} .plat{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px 22px}
.plat .lbl{color:var(--acid);font:600 11px/1 system-ui;letter-spacing:.22em;text-transform:uppercase} .plat .lbl{color:var(--acid);font:600 11px/1 system-ui;letter-spacing:.22em;text-transform:uppercase}
.plat h3{margin:.4rem 0 .3rem;font-size:1.15rem} .plat h3{margin:.4rem 0 .3rem;font-size:1.15rem}
.plat p{margin:0;color:var(--mut);font-size:13.5px} .plat p{margin:0;color:var(--mut);font-size:13.5px}
.plat .meta{color:var(--dim);font-size:12px;margin-top:.6rem} .plat .meta{color:var(--dim);font-size:12px;margin-top:.6rem}
.plat .btns{margin-top:.9rem;display:flex;gap:8px;flex-wrap:wrap} .plat .btns{margin-top:.9rem;display:flex;gap:8px;flex-wrap:wrap}
.plat .btns .btn{padding:9px 16px;font-size:13.5px} .plat .btns .btn{padding:9px 16px;font-size:13.5px}
.plat .hashes{margin-top:.9rem;padding-top:.7rem;border-top:1px solid var(--line);font-size:11.5px;color:var(--dim)} .plat .hashes{margin-top:.9rem;padding-top:.7rem;border-top:1px solid var(--line);font-size:11.5px;color:var(--dim)}
.plat .hashes .h{display:flex;gap:8px;flex-wrap:wrap;align-items:baseline;margin-top:4px} .plat .hashes .h{display:flex;gap:8px;flex-wrap:wrap;align-items:baseline;margin-top:4px}
.plat .hashes .h b{color:var(--mut);font-weight:600;min-width:70px} .plat .hashes .h b{color:var(--mut);font-weight:600;min-width:70px}
.plat .hashes code{font-family:ui-monospace,monospace;font-size:10.5px;color:#c9d4e3;word-break:break-all;background:transparent;border:none;padding:0} .plat .hashes code{font-family:ui-monospace,monospace;font-size:10.5px;color:#c9d4e3;word-break:break-all;background:transparent;border:none;padding:0}
footer{color:var(--dim);font-size:12.5px;margin:3rem 0 2rem;text-align:center} footer{color:var(--dim);font-size:12.5px;margin:3rem 0 2rem;text-align:center}
footer a{color:var(--mut)} footer a{color:var(--mut)}
</style> </style>
</head> </head>
<body> <body>
<nav class="topnav"> <nav class="topnav">
<a class="brand" href="./"> <a class="brand" href="./">
<svg viewBox="0 0 64 64" aria-hidden="true"> <svg viewBox="0 0 64 64" aria-hidden="true">
<rect width="64" height="64" rx="12" fill="#0b0e14"/> <rect width="64" height="64" rx="12" fill="#0b0e14"/>
<path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/> <path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/>
<path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/> <path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/>
<path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/> <path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/>
<circle cx="32" cy="32" r="3" fill="#0b0e14"/> <circle cx="32" cy="32" r="3" fill="#0b0e14"/>
</svg> </svg>
<span>Theseus<span class="g">.X</span></span> <span>Theseus<span class="g">.X</span></span>
</a> </a>
<a class="here" href="./">Home</a> <a class="here" href="./">Home</a>
<a href="plug-ins/">Plug-ins</a> <a href="plug-ins/">Plug-ins</a>
<a href="extensions/">Extensions</a> <a href="extensions/">Extensions</a>
<a href="https://sirius.x/" title="Register a BCNR name"><svg class="tabicon" viewBox="0 0 64 64" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 4 L36.4 27.6 L60 32 L36.4 36.4 L32 60 L27.6 36.4 L4 32 L27.6 27.6 Z" fill="#d6ff3d"/></svg><span>Sirius<span class="g">.X</span></span></a> <a href="https://sirius.x/" title="Register a BCNR name"><svg class="tabicon" viewBox="0 0 64 64" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 4 L36.4 27.6 L60 32 L36.4 36.4 L32 60 L27.6 36.4 L4 32 L27.6 27.6 Z" fill="#d6ff3d"/></svg><span>Sirius<span class="g">.X</span></span></a>
<a class="get" href="#download">Get Theseus</a> <a class="get" href="#download">Get Theseus</a>
</nav> </nav>
<aside class="sidenav" aria-label="On this page"> <aside class="sidenav" aria-label="On this page">
<a href="#what" title="What it is"><span class="ic" aria-hidden="true">🌐</span><span class="lb">What it is</span></a> <a href="#what" title="What it is"><span class="ic" aria-hidden="true">🌐</span><span class="lb">What it is</span></a>
<a href="#features" title="Features" ><span class="ic" aria-hidden="true">⚡</span><span class="lb">Features</span></a> <a href="#features" title="Features" ><span class="ic" aria-hidden="true">⚡</span><span class="lb">Features</span></a>
<a href="#download" title="Download" ><span class="ic" aria-hidden="true">⬇</span><span class="lb">Download</span></a> <a href="#download" title="Download" ><span class="ic" aria-hidden="true">⬇</span><span class="lb">Download</span></a>
<a href="#verify" title="Verify" ><span class="ic" aria-hidden="true">🔐</span><span class="lb">Verify</span></a> <a href="#verify" title="Verify" ><span class="ic" aria-hidden="true">🔐</span><span class="lb">Verify</span></a>
</aside> </aside>
<header class="hero"> <header class="hero">
<div class="mark"> <div class="mark">
<svg viewBox="0 0 64 64" aria-hidden="true"> <svg viewBox="0 0 64 64" aria-hidden="true">
<rect width="64" height="64" rx="12" fill="#0b0e14"/> <rect width="64" height="64" rx="12" fill="#0b0e14"/>
<path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/> <path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/>
<path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/> <path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/>
<path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/> <path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/>
<circle cx="32" cy="32" r="3" fill="#0b0e14"/> <circle cx="32" cy="32" r="3" fill="#0b0e14"/>
</svg> </svg>
</div> </div>
<h1>Theseus <span class="g">Navigator</span></h1> <h1>Theseus <span class="g">Navigator</span></h1>
<p class="tag">A Chromium-based browser that resolves Bitcoin&nbsp;Cash names on the <p class="tag">A Chromium-based browser that resolves Bitcoin&nbsp;Cash names on the
device itself. No OS-level daemon, no DNS shim, no root-store install. Type device itself. No OS-level daemon, no DNS shim, no root-store install. Type
<code>silentmode.bch</code>, get the on-chain answer, see which registry &amp; <code>silentmode.bch</code>, get the on-chain answer, see which registry &amp;
which storage served it.</p> which storage served it.</p>
<div class="cta"> <div class="cta">
<a class="btn acid" href="#download">Download for Windows</a> <a class="btn acid" href="#download">Download for Windows</a>
<a class="btn ghost" href="#download">Android APK</a> <a class="btn ghost" href="#download">Android APK</a>
<a class="btn ghost" href="#what">How it works</a> <a class="btn ghost" href="#what">How it works</a>
</div> </div>
</header> </header>
<div class="wrap"> <div class="wrap">
<section id="what"> <section id="what">
<h2>What it is</h2> <h2>What it is</h2>
<p class="lede">Theseus is a browser built around the assumption that names should <p class="lede">Theseus is a browser built around the assumption that names should
be resolved by the person using them, not by an intermediary. It ships an be resolved by the person using them, not by an intermediary. It ships an
embedded resolver (Ariadne) that watches the Bitcoin Cash chain directly and answers embedded resolver (Ariadne) that watches the Bitcoin Cash chain directly and answers
name lookups locally — the app <i>is</i> both the browser and the resolver. name lookups locally — the app <i>is</i> both the browser and the resolver.
Every page carries a badge showing which registry produced the record Every page carries a badge showing which registry produced the record
(BCNR, ICANN) and which backend served the bytes (on-chain, Sia, direct IP, (BCNR, ICANN) and which backend served the bytes (on-chain, Sia, direct IP,
or a plain HTTPS fallback).</p> or a plain HTTPS fallback).</p>
<div class="grid"> <div class="grid">
<div class="card"> <div class="card">
<h3><span class="dot d-chain"></span> On-chain first</h3> <h3><span class="dot d-chain"></span> On-chain first</h3>
<p>Every dotted host is asked of BCNR before anything else. If the chain <p>Every dotted host is asked of BCNR before anything else. If the chain
answers, that answer wins — no matter what any DNS operator says. If answers, that answer wins — no matter what any DNS operator says. If
BCNR has no record, Theseus falls through to the clearnet.</p> BCNR has no record, Theseus falls through to the clearnet.</p>
</div> </div>
<div class="card"> <div class="card">
<h3><span class="dot d-sia"></span> Trust one thing</h3> <h3><span class="dot d-sia"></span> Trust one thing</h3>
<p>The app is the trust boundary. No daemon, no root cert, no proxy in the <p>The app is the trust boundary. No daemon, no root cert, no proxy in the
middle. Uninstall it and BCNR is gone from that machine — nothing lingers middle. Uninstall it and BCNR is gone from that machine — nothing lingers
in the OS.</p> in the OS.</p>
</div> </div>
<div class="card"> <div class="card">
<h3><span class="dot d-srv"></span> Warm from the first click</h3> <h3><span class="dot d-srv"></span> Warm from the first click</h3>
<p>A bundled beacon snapshot loads before the first navigation can fire; a <p>A bundled beacon snapshot loads before the first navigation can fire; a
background delta refresh keeps the index current every 30&nbsp;s; a Sia background delta refresh keeps the index current every 30&nbsp;s; a Sia
snapshot pull on boot catches long-idle installs up in one HTTP call.</p> snapshot pull on boot catches long-idle installs up in one HTTP call.</p>
</div> </div>
</div> </div>
</section> </section>
<section id="features"> <section id="features">
<h2>What's inside</h2> <h2>What's inside</h2>
<p class="lede">Everything a mainstream browser has, plus what the chain enables.</p> <p class="lede">Everything a mainstream browser has, plus what the chain enables.</p>
<div class="grid"> <div class="grid">
<div class="card"> <div class="card">
<h3>Native BCNR</h3> <h3>Native BCNR</h3>
<p>Type a name, get the on-chain record. <code>h</code> serves from the chain, <p>Type a name, get the on-chain record. <code>h</code> serves from the chain,
<code>s3</code> from Sia storage, <code>ip</code> direct-connects, <code>u</code> <code>s3</code> from Sia storage, <code>ip</code> direct-connects, <code>u</code>
redirects. TLS fingerprint pinning against the on-chain <code>tls</code> record redirects. TLS fingerprint pinning against the on-chain <code>tls</code> record
for <code>ip</code> hops.</p> for <code>ip</code> hops.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>window.bcnr API</h3> <h3>window.bcnr API</h3>
<p>Every page can call <code>window.bcnr.resolveName(…)</code>, <p>Every page can call <code>window.bcnr.resolveName(…)</code>,
<code>isRegistered(…)</code>, <code>getBcnrTlds()</code>, and <code>isRegistered(…)</code>, <code>getBcnrTlds()</code>, and
<code>getRecordVersion(…)</code>. dApps don't need to ship their own resolver.</p> <code>getRecordVersion(…)</code>. dApps don't need to ship their own resolver.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>Password manager</h3> <h3>Password manager</h3>
<p>Local vault, deterministic derivation from a BIP-39 seed. Same seed on a <p>Local vault, deterministic derivation from a BIP-39 seed. Same seed on a
different device produces the same passwords. Autofill on demand — never different device produces the same passwords. Autofill on demand — never
watches the page for you.</p> watches the page for you.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>Tor toggle</h3> <h3>Tor toggle</h3>
<p>One click routes everything through a bundled Tor client — pluggable <p>One click routes everything through a bundled Tor client — pluggable
transports included for hostile networks. Off by default; nothing is transports included for hostile networks. Off by default; nothing is
proxied without your say-so.</p> proxied without your say-so.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>Anti-fingerprinting</h3> <h3>Anti-fingerprinting</h3>
<p>Timezone / locale / language / geolocation can be shown, hidden, spoofed <p>Timezone / locale / language / geolocation can be shown, hidden, spoofed
with a plausible decoy, or set manually. Media-device labels blanked by with a plausible decoy, or set manually. Media-device labels blanked by
default so <code>enumerateDevices</code> can't leak an audiooutput ID.</p> default so <code>enumerateDevices</code> can't leak an audiooutput ID.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>Privacy by default</h3> <h3>Privacy by default</h3>
<p>Cookies, cache, storage, and history clear on quit unless you opt in. <p>Cookies, cache, storage, and history clear on quit unless you opt in.
Camera and microphone denied by default. WebRTC IP policy set to Camera and microphone denied by default. WebRTC IP policy set to
public-only.</p> public-only.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>System-wide, optional</h3> <h3>System-wide, optional</h3>
<p>Theseus's resolver is in-process by default — nothing else on the <p>Theseus's resolver is in-process by default — nothing else on the
machine is touched. The Windows installer offers <b>Ariadne's Thread</b> machine is touched. The Windows installer offers <b>Ariadne's Thread</b>
as a recommended-on checkbox: one click also installs a system-wide as a recommended-on checkbox: one click also installs a system-wide
resolver so Chrome, Edge, and every other browser resolve BCNR too. resolver so Chrome, Edge, and every other browser resolve BCNR too.
Uninstalling Theseus asks whether to remove it. Skip the checkbox and Uninstalling Theseus asks whether to remove it. Skip the checkbox and
Theseus is still fully self-contained.</p> Theseus is still fully self-contained.</p>
</div> </div>
<div class="card"> <div class="card">
<h3>Small, auditable</h3> <h3>Small, auditable</h3>
<p>Under 100 MB, Electron + a few kilobytes of BCNR glue. Source at <p>Under 100 MB, Electron + a few kilobytes of BCNR glue. Source at
<a href="https://silentmode.st/">silentmode.st</a>. Reproducible builds <a href="https://silentmode.st/">silentmode.st</a>. Reproducible builds
with <code>SOURCE_DATE_EPOCH</code>.</p> with <code>SOURCE_DATE_EPOCH</code>.</p>
</div> </div>
</div> </div>
</section> </section>
<section id="download"> <section id="download">
<h2>Download</h2> <h2>Download</h2>
<p class="lede">Latest is <b data-live="theseus-version">0.3.49</b>. Windows installer + portable, and an <p class="lede">Latest is <b data-live="theseus-version">0.3.49</b>. Windows installer + portable, and an
Android build (Ariadne mobile — same brand, phone form factor).</p> Android build (Ariadne mobile — same brand, phone form factor).</p>
<div class="plats"> <div class="plats">
<div class="plat"> <div class="plat">
<div class="lbl">Desktop · Windows 64-bit</div> <div class="lbl">Desktop · Windows 64-bit</div>
<h3>Theseus for Windows</h3> <h3>Theseus for Windows</h3>
<p>Full-window desktop browser with Tor toggle and the multi-source BNS <p>Full-window desktop browser with Tor toggle and the multi-source BNS
warm-start.</p> warm-start.</p>
<div class="meta">v<span data-live="theseus-version">0.3.49</span> · installer ~132 MB · portable ~132 MB · unsigned</div> <div class="meta">v<span data-live="theseus-version">0.3.49</span> · installer ~132 MB · portable ~132 MB · unsigned</div>
<div class="btns"> <div class="btns">
<a class="btn acid" href="https://dl.silentmode.st/latest/TheseusNavigator-Setup.exe">Installer</a> <a class="btn acid" href="https://dl.silentmode.st/latest/TheseusNavigator-Setup.exe">Installer</a>
<a class="btn ghost" href="https://dl.silentmode.st/latest/TheseusNavigator-portable.exe">Portable .exe</a> <a class="btn ghost" href="https://dl.silentmode.st/latest/TheseusNavigator-portable.exe">Portable .exe</a>
</div> </div>
<div class="hashes" id="hashes-win"> <div class="hashes" id="hashes-win">
<div class="h"><b>Installer</b><code data-live="theseus-setup-sha">0f1c34063963cd72e051662617aea51642a01c3a3c4a2a0a03f831131fa83cc5</code></div> <div class="h"><b>Installer</b><code data-live="theseus-setup-sha">0f1c34063963cd72e051662617aea51642a01c3a3c4a2a0a03f831131fa83cc5</code></div>
<div class="h"><b>Portable</b><code data-live="theseus-portable-sha">1e44162b3dddb79dff7609374e0f02153a38255d5a94847dd95d99d29c7f582a</code></div> <div class="h"><b>Portable</b><code data-live="theseus-portable-sha">1e44162b3dddb79dff7609374e0f02153a38255d5a94847dd95d99d29c7f582a</code></div>
</div> </div>
</div> </div>
<div class="plat"> <div class="plat">
<div class="lbl">Mobile · Android 5.0+ (API 21)</div> <div class="lbl">Mobile · Android 5.0+ (API 21)</div>
<h3>Ariadne for Android</h3> <h3>Ariadne for Android</h3>
<p>Phone-sized browser with tap-to-verify provenance — the block and <p>Phone-sized browser with tap-to-verify provenance — the block and
transaction behind each name are one tap away.</p> transaction behind each name are one tap away.</p>
<div class="meta">v<span data-live="android-version">0.18</span> · ~146 KB · self-signed release key · in-app auto-update</div> <div class="meta">v<span data-live="android-version">0.18</span> · ~146 KB · self-signed release key · in-app auto-update</div>
<div class="btns"> <div class="btns">
<a class="btn acid" href="https://dl.silentmode.st/latest/ariadne.apk">Download APK</a> <a class="btn acid" href="https://dl.silentmode.st/latest/ariadne.apk">Download APK</a>
</div> </div>
<div class="hashes" id="hashes-android"> <div class="hashes" id="hashes-android">
<div class="h"><b>APK</b><code data-live="android-sha">920272deb1ff3cb163d609882f4fdaf422c163648b96944d16670ba0ba2ac5bf</code></div> <div class="h"><b>APK</b><code data-live="android-sha">920272deb1ff3cb163d609882f4fdaf422c163648b96944d16670ba0ba2ac5bf</code></div>
<div class="h"><b>Signing key</b><code>46ebbbe5dc73148d66410fd0337b1e6683472128c983e8e2578200b9e3ca548c</code></div> <div class="h"><b>Signing key</b><code>46ebbbe5dc73148d66410fd0337b1e6683472128c983e8e2578200b9e3ca548c</code></div>
</div> </div>
</div> </div>
</div> </div>
<div class="note"><b>Nothing is signed by an authority your device trusts</b> <div class="note"><b>Nothing is signed by an authority your device trusts</b>
— by design. Verify the SHA-256 below before running. The download buttons point at — by design. Verify the SHA-256 below before running. The download buttons point at
<code>dl.silentmode.st/latest/…</code>, which always redirects to the current <code>dl.silentmode.st/latest/…</code>, which always redirects to the current
build, so a saved link never goes stale. The machine-readable build, so a saved link never goes stale. The machine-readable
manifest is at manifest is at
<a href="https://dl.silentmode.st/releases-manifest.json">releases-manifest.json</a>.</div> <a href="https://dl.silentmode.st/releases-manifest.json">releases-manifest.json</a>.</div>
</section> </section>
<section id="verify"> <section id="verify">
<h2>Verify what you downloaded</h2> <h2>Verify what you downloaded</h2>
<p class="lede">The hashes above are the same ones published at <p class="lede">The hashes above are the same ones published at
<a href="https://silentmode.bch/releases/">silentmode.bch/releases</a> (Sia <a href="https://silentmode.bch/releases/">silentmode.bch/releases</a> (Sia
mirror, name-anchored) and inside the on-chain mirror, name-anchored) and inside the on-chain
<code>releases.silentmode.bch</code> pointer. Three independent surfaces — <code>releases.silentmode.bch</code> pointer. Three independent surfaces —
they should all agree.</p> they should all agree.</p>
<pre>Windows PowerShell: <pre>Windows PowerShell:
Get-FileHash <span data-live="theseus-setup-file">TheseusNavigator-Setup-0.3.49.exe</span> -Algorithm SHA256 Get-FileHash <span data-live="theseus-setup-file">TheseusNavigator-Setup-0.3.49.exe</span> -Algorithm SHA256
macOS / Linux: macOS / Linux:
sha256sum <span data-live="theseus-setup-file">TheseusNavigator-Setup-0.3.49.exe</span> sha256sum <span data-live="theseus-setup-file">TheseusNavigator-Setup-0.3.49.exe</span>
Expected: Expected:
<span data-live="theseus-setup-sha">0f1c34063963cd72e051662617aea51642a01c3a3c4a2a0a03f831131fa83cc5</span></pre> <span data-live="theseus-setup-sha">0f1c34063963cd72e051662617aea51642a01c3a3c4a2a0a03f831131fa83cc5</span></pre>
</section> </section>
<footer> <footer>
<p>theseus.x is a BCNR name resolving to a Sia-hosted static site. <p>theseus.x is a BCNR name resolving to a Sia-hosted static site.
Source and issues: <a href="https://silentmode.st/">silentmode.st</a>. Source and issues: <a href="https://silentmode.st/">silentmode.st</a>.
Alpha software on a test network.</p> Alpha software on a test network.</p>
</footer> </footer>
</div> </div>
<script> <script>
// Keep versions + hashes current from the releases manifest. The text baked // Keep versions + hashes current from the releases manifest. The text baked
// into the page is the fallback when this fetch can't run (offline, or a // into the page is the fallback when this fetch can't run (offline, or a
// scheme that blocks cross-origin reads). // scheme that blocks cross-origin reads).
(async () => { (async () => {
try { try {
const r = await fetch("https://dl.silentmode.st/releases-manifest.json", { cache: "no-store" }); const r = await fetch("https://dl.silentmode.st/releases-manifest.json", { cache: "no-store" });
if (!r.ok) return; if (!r.ok) return;
const m = await r.json(); const m = await r.json();
const th = (m.releases || []).find((x) => x.id === "theseus-navigator"); const th = (m.releases || []).find((x) => x.id === "theseus-navigator");
const an = (m.releases || []).find((x) => x.id === "ariadne-android"); const an = (m.releases || []).find((x) => x.id === "ariadne-android");
const set = (key, val) => { if (val == null) return; document.querySelectorAll('[data-live="' + key + '"]').forEach((el) => { el.textContent = String(val); }); }; const set = (key, val) => { if (val == null) return; document.querySelectorAll('[data-live="' + key + '"]').forEach((el) => { el.textContent = String(val); }); };
if (th) { if (th) {
const files = Object.keys(th.files || {}); const files = Object.keys(th.files || {});
const setup = files.find((f) => /Setup-.*\.exe$/.test(f)), portable = files.find((f) => /-portable\.exe$/.test(f)); const setup = files.find((f) => /Setup-.*\.exe$/.test(f)), portable = files.find((f) => /-portable\.exe$/.test(f));
set("theseus-version", th.version); set("theseus-version", th.version);
set("theseus-setup-file", setup); set("theseus-setup-file", setup);
set("theseus-setup-sha", setup && th.files[setup]); set("theseus-setup-sha", setup && th.files[setup]);
set("theseus-portable-sha", portable && th.files[portable]); set("theseus-portable-sha", portable && th.files[portable]);
} }
if (an) { if (an) {
const apk = Object.keys(an.files || {})[0]; const apk = Object.keys(an.files || {})[0];
set("android-version", an.version); set("android-version", an.version);
set("android-sha", apk && an.files[apk]); set("android-sha", apk && an.files[apk]);
} }
} catch {} } catch {}
})(); })();
// Sidenav scroll-spy: highlight the section currently in view. // Sidenav scroll-spy: highlight the section currently in view.
(() => { // Uses scroll-position tracking so short sections at the end of the page
const links = document.querySelectorAll(".sidenav a[href^='#']"); // still activate their link when the reader scrolls to them.
if (!links.length) return; (() => {
const map = new Map(); const links = document.querySelectorAll(".sidenav a[href^='#']");
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) map.set(el, a); }); if (!links.length) return;
const io = new IntersectionObserver((entries) => { const entries = [];
entries.forEach((en) => { links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
if (!en.isIntersecting) return; if (!entries.length) return;
const a = map.get(en.target); if (!a) return; const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
links.forEach((l) => l.classList.remove("active")); const update = () => {
a.classList.add("active"); const doc = document.documentElement;
}); const scrollBottom = window.scrollY + window.innerHeight;
}, { rootMargin: "-40% 0px -55% 0px", threshold: 0 }); if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
map.forEach((_, el) => io.observe(el)); const line = window.scrollY + window.innerHeight * 0.35;
})(); let active = entries[0].link;
</script> for (const e of entries) {
</body> if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
</html> else break;
}
setActive(active);
};
window.addEventListener("scroll", update, { passive: true });
window.addEventListener("resize", update);
update();
})();
</script>
</body>
</html>

View file

@ -1,243 +1,253 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>theseus.x — Plug-ins</title> <title>theseus.x — Plug-ins</title>
<meta name="description" content="Silent Mode's first-party plug-ins for Theseus Navigator: the Aegis wallet and the Ariadne's Thread system-wide resolver. Current signed versions, hashes, and how they update."> <meta name="description" content="Silent Mode's first-party plug-ins for Theseus Navigator: the Aegis wallet and the Ariadne's Thread system-wide resolver. Current signed versions, hashes, and how they update.">
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml"> <link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
<link rel="mask-icon" href="../assets/favicon.svg" color="#d6ff3d"> <link rel="mask-icon" href="../assets/favicon.svg" color="#d6ff3d">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:site_name" content="theseus.x"> <meta property="og:site_name" content="theseus.x">
<meta property="og:title" content="theseus.x — Plug-ins"> <meta property="og:title" content="theseus.x — Plug-ins">
<meta property="og:description" content="First-party plug-ins for Theseus: Aegis wallet and Ariadne's Thread. Signed, verified on the device."> <meta property="og:description" content="First-party plug-ins for Theseus: Aegis wallet and Ariadne's Thread. Signed, verified on the device.">
<meta property="og:url" content="https://theseus.x/plug-ins/"> <meta property="og:url" content="https://theseus.x/plug-ins/">
<style> <style>
:root{ :root{
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09); --bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09);
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; --ok:#4fd1a5; --ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; --ok:#4fd1a5;
} }
*{box-sizing:border-box} *{box-sizing:border-box}
body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg)); body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg));
color:var(--ink);font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh} color:var(--ink);font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh}
a{color:var(--acid)} a{color:var(--acid)}
.wrap{max-width:900px;margin:0 auto;padding:0 1.2rem} .wrap{max-width:900px;margin:0 auto;padding:0 1.2rem}
.topnav{position:fixed;top:0;left:0;right:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap; .topnav{position:fixed;top:0;left:0;right:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap;
padding:10px 1.2rem;background:rgba(11,14,20,.88);backdrop-filter:blur(10px)} padding:10px 1.2rem;background:rgba(11,14,20,.88);backdrop-filter:blur(10px)}
.topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px;display:flex;align-items:center;gap:8px} .topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px;display:flex;align-items:center;gap:8px}
.topnav .brand .g{color:var(--acid)} .topnav .brand .g{color:var(--acid)}
.topnav .brand svg{width:20px;height:20px} .topnav .brand svg{width:20px;height:20px}
.topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px; .topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px;
display:inline-flex;align-items:center;gap:6px} display:inline-flex;align-items:center;gap:6px}
.topnav a:hover{color:var(--ink);background:var(--panel)} .topnav a:hover{color:var(--ink);background:var(--panel)}
.topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)} .topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)}
.topnav a .g{color:var(--acid)} .topnav a .g{color:var(--acid)}
.topnav a svg.tabicon{width:15px;height:15px;flex:none} .topnav a svg.tabicon{width:15px;height:15px;flex:none}
.topnav a.get{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500} .topnav a.get{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500}
.topnav a.get:hover{background:rgba(214,255,61,.18)} .topnav a.get:hover{background:rgba(214,255,61,.18)}
/* Adaptive edge nav: full-width panel at left edge on wide, /* Adaptive edge nav: full-width panel at left edge on wide,
icon rail on medium, bottom bar with icons on mobile. */ icon rail on medium, bottom bar with icons on mobile. */
:root{--sn-w:200px;--tn-h:52px} :root{--sn-w:200px;--tn-h:52px}
body{padding-top:var(--tn-h)} body{padding-top:var(--tn-h)}
.sidenav{position:fixed;top:var(--tn-h);left:0;bottom:0;width:var(--sn-w);z-index:15; .sidenav{position:fixed;top:var(--tn-h);left:0;bottom:0;width:var(--sn-w);z-index:15;
background:transparent;padding:56px 12px 22px 18px; background:transparent;padding:56px 12px 22px 18px;
overflow-y:auto;transition:width .15s ease;pointer-events:none} overflow-y:auto;transition:width .15s ease;pointer-events:none}
.sidenav a{display:flex;align-items:center;gap:14px;color:var(--mut);text-decoration:none; .sidenav a{display:flex;align-items:center;gap:14px;color:var(--mut);text-decoration:none;
padding:9px 12px;border-radius:10px; padding:9px 12px;border-radius:10px;
font-size:14.5px;font-weight:500;opacity:.35;pointer-events:auto; font-size:14.5px;font-weight:500;opacity:.35;pointer-events:auto;
transition:opacity .12s,color .12s,background .12s,backdrop-filter .12s} transition:opacity .12s,color .12s,background .12s,backdrop-filter .12s}
.sidenav a .ic{flex:none;width:22px;font-size:18px;line-height:1;text-align:center; .sidenav a .ic{flex:none;width:22px;font-size:18px;line-height:1;text-align:center;
display:none;justify-content:center;align-items:center} display:none;justify-content:center;align-items:center}
.sidenav a .lb{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .sidenav a .lb{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidenav a:hover{opacity:1;color:var(--ink);background:rgba(11,14,20,.72);backdrop-filter:blur(8px)} .sidenav a:hover{opacity:1;color:var(--ink);background:rgba(11,14,20,.72);backdrop-filter:blur(8px)}
.sidenav a.active{opacity:.85;color:var(--acid)} .sidenav a.active{opacity:.85;color:var(--acid)}
.sidenav a.active:hover{opacity:1} .sidenav a.active:hover{opacity:1}
@media (max-width:1180px){ @media (max-width:1180px){
:root{--sn-w:64px} :root{--sn-w:64px}
.sidenav{padding:22px 6px} .sidenav{padding:22px 6px}
.sidenav .lbl{display:none} .sidenav .lbl{display:none}
.sidenav a{justify-content:center;padding:12px 4px;gap:0} .sidenav a{justify-content:center;padding:12px 4px;gap:0}
.sidenav a .ic{display:inline-flex} .sidenav a .ic{display:inline-flex}
.sidenav a .lb{display:none} .sidenav a .lb{display:none}
} }
@media (max-width:720px){ @media (max-width:720px){
:root{--sn-w:0px} :root{--sn-w:0px}
body{padding-left:0;padding-bottom:64px} body{padding-left:0;padding-bottom:64px}
.sidenav{top:auto;bottom:0;left:0;right:0;width:100%;height:auto; .sidenav{top:auto;bottom:0;left:0;right:0;width:100%;height:auto;
padding:4px 4px;flex-direction:row;background:rgba(11,14,20,.52); padding:4px 4px;flex-direction:row;background:rgba(11,14,20,.52);
display:flex;justify-content:space-around;overflow-x:auto} display:flex;justify-content:space-around;overflow-x:auto}
.sidenav .lbl{display:none} .sidenav .lbl{display:none}
.sidenav a{flex:1;min-width:56px;padding:10px 6px;justify-content:center;gap:0} .sidenav a{flex:1;min-width:56px;padding:10px 6px;justify-content:center;gap:0}
.sidenav a .ic{display:inline-flex} .sidenav a .ic{display:inline-flex}
.sidenav a .lb{display:none} .sidenav a .lb{display:none}
} }
header.hero{text-align:center;padding:3.5rem 1.2rem 1rem} header.hero{text-align:center;padding:3.5rem 1.2rem 1rem}
h1{font-size:clamp(1.8rem,4.5vw,2.4rem);margin:.6rem 0 .3rem;letter-spacing:-.3px} h1{font-size:clamp(1.8rem,4.5vw,2.4rem);margin:.6rem 0 .3rem;letter-spacing:-.3px}
h1 .g{color:var(--acid)} h1 .g{color:var(--acid)}
.tag{color:var(--mut);font-size:1.02rem;max-width:640px;margin:0 auto} .tag{color:var(--mut);font-size:1.02rem;max-width:640px;margin:0 auto}
section{padding:2rem 0;border-top:1px solid var(--line);margin-top:1.6rem;scroll-margin-top:72px} section{padding:2rem 0;border-top:1px solid var(--line);margin-top:1.6rem;scroll-margin-top:72px}
section:first-of-type{border-top:none;margin-top:1rem} section:first-of-type{border-top:none;margin-top:1rem}
h2{font-size:1.35rem;margin:0 0 .5rem} h2{font-size:1.35rem;margin:0 0 .5rem}
.lede{color:var(--mut);margin:0 0 1.2rem;max-width:720px} .lede{color:var(--mut);margin:0 0 1.2rem;max-width:720px}
code{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px} code{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px}
.ext{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px 22px;margin:1rem 0} .ext{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px 22px;margin:1rem 0}
.ext h3{margin:0 0 .3rem;font-size:1.15rem;display:flex;align-items:center;gap:10px;flex-wrap:wrap} .ext h3{margin:0 0 .3rem;font-size:1.15rem;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.ext h3 .icon{font-size:1.4rem} .ext h3 .icon{font-size:1.4rem}
.ext .cur{color:var(--acid);font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase} .ext .cur{color:var(--acid);font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.ext .desc{color:var(--mut);font-size:14px;margin:.3rem 0 .8rem} .ext .desc{color:var(--mut);font-size:14px;margin:.3rem 0 .8rem}
.ext .verifybox{margin-top:.9rem;padding-top:.8rem;border-top:1px solid var(--line);font-size:11.5px;color:var(--dim)} .ext .verifybox{margin-top:.9rem;padding-top:.8rem;border-top:1px solid var(--line);font-size:11.5px;color:var(--dim)}
.ext .verifybox .lbl{color:var(--mut);font-size:12px;margin-bottom:6px} .ext .verifybox .lbl{color:var(--mut);font-size:12px;margin-bottom:6px}
.ext .h{display:flex;gap:8px;flex-wrap:wrap;align-items:baseline;margin-top:4px} .ext .h{display:flex;gap:8px;flex-wrap:wrap;align-items:baseline;margin-top:4px}
.ext .h b{color:var(--mut);font-weight:600;min-width:90px} .ext .h b{color:var(--mut);font-weight:600;min-width:90px}
.ext .h code{font-family:ui-monospace,monospace;font-size:10.5px;color:#c9d4e3;word-break:break-all;background:transparent;border:none;padding:0} .ext .h code{font-family:ui-monospace,monospace;font-size:10.5px;color:#c9d4e3;word-break:break-all;background:transparent;border:none;padding:0}
.ext .meta{color:var(--dim);font-size:12.5px;margin-top:.4rem} .ext .meta{color:var(--dim);font-size:12.5px;margin-top:.4rem}
.btns{display:flex;gap:8px;flex-wrap:wrap;margin-top:.8rem} .btns{display:flex;gap:8px;flex-wrap:wrap;margin-top:.8rem}
.btn{display:inline-block;padding:.45rem .9rem;border-radius:9px;background:var(--acid);color:#0b0e14;font-weight:650;text-decoration:none;font-size:14px} .btn{display:inline-block;padding:.45rem .9rem;border-radius:9px;background:var(--acid);color:#0b0e14;font-weight:650;text-decoration:none;font-size:14px}
.btn.ghost{background:transparent;color:var(--ink);border:1px solid var(--line)} .btn.ghost{background:transparent;color:var(--ink);border:1px solid var(--line)}
.note{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--acid);border-radius:10px;padding:14px 18px;color:var(--mut);font-size:14px;margin:1.4rem 0} .note{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--acid);border-radius:10px;padding:14px 18px;color:var(--mut);font-size:14px;margin:1.4rem 0}
.note b{color:var(--ink)} .note b{color:var(--ink)}
footer{color:var(--dim);font-size:12.5px;margin:3rem 0 2rem;text-align:center} footer{color:var(--dim);font-size:12.5px;margin:3rem 0 2rem;text-align:center}
footer a{color:var(--mut)} footer a{color:var(--mut)}
</style> </style>
</head> </head>
<body> <body>
<nav class="topnav"> <nav class="topnav">
<a class="brand" href="../"> <a class="brand" href="../">
<svg viewBox="0 0 64 64" aria-hidden="true"> <svg viewBox="0 0 64 64" aria-hidden="true">
<rect width="64" height="64" rx="12" fill="#0b0e14"/> <rect width="64" height="64" rx="12" fill="#0b0e14"/>
<path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/> <path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/>
<path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/> <path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/>
<path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/> <path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/>
<circle cx="32" cy="32" r="3" fill="#0b0e14"/> <circle cx="32" cy="32" r="3" fill="#0b0e14"/>
</svg> </svg>
<span>Theseus<span class="g">.X</span></span> <span>Theseus<span class="g">.X</span></span>
</a> </a>
<a href="../">Home</a> <a href="../">Home</a>
<a class="here" href="./">Plug-ins</a> <a class="here" href="./">Plug-ins</a>
<a href="../extensions/">Extensions</a> <a href="../extensions/">Extensions</a>
<a href="https://sirius.x/" title="Register a BCNR name"><svg class="tabicon" viewBox="0 0 64 64" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 4 L36.4 27.6 L60 32 L36.4 36.4 L32 60 L27.6 36.4 L4 32 L27.6 27.6 Z" fill="#d6ff3d"/></svg><span>Sirius<span class="g">.X</span></span></a> <a href="https://sirius.x/" title="Register a BCNR name"><svg class="tabicon" viewBox="0 0 64 64" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 4 L36.4 27.6 L60 32 L36.4 36.4 L32 60 L27.6 36.4 L4 32 L27.6 27.6 Z" fill="#d6ff3d"/></svg><span>Sirius<span class="g">.X</span></span></a>
<a class="get" href="../#download">Get Theseus</a> <a class="get" href="../#download">Get Theseus</a>
</nav> </nav>
<aside class="sidenav" aria-label="On this page"> <aside class="sidenav" aria-label="On this page">
<a href="#aegis-section" title="Aegis wallet" ><span class="ic" aria-hidden="true"><svg viewBox="0 0 32 32" fill="none" width="20" height="20"><polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="#0a0a0d" stroke="#D6FF3D" stroke-width="1.6" stroke-linejoin="round"/><circle cx="16" cy="16" r="4.5" fill="none" stroke="#D6FF3D" stroke-width="1.4"/><circle cx="16" cy="16" r="1.6" fill="#D6FF3D"/></svg></span><span class="lb">Aegis wallet</span></a> <a href="#aegis-section" title="Aegis wallet" ><span class="ic" aria-hidden="true"><svg viewBox="0 0 32 32" fill="none" width="20" height="20"><polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="#0a0a0d" stroke="#D6FF3D" stroke-width="1.6" stroke-linejoin="round"/><circle cx="16" cy="16" r="4.5" fill="none" stroke="#D6FF3D" stroke-width="1.4"/><circle cx="16" cy="16" r="1.6" fill="#D6FF3D"/></svg></span><span class="lb">Aegis wallet</span></a>
<a href="#ariadne-section" title="Ariadne's Thread" ><span class="ic" aria-hidden="true">🧵</span><span class="lb">Ariadne's Thread</span></a> <a href="#ariadne-section" title="Ariadne's Thread" ><span class="ic" aria-hidden="true">🧵</span><span class="lb">Ariadne's Thread</span></a>
<a href="#line" title="Plug-ins vs extensions"><span class="ic" aria-hidden="true">🧩</span><span class="lb">Plug-ins vs extensions</span></a> <a href="#line" title="Plug-ins vs extensions"><span class="ic" aria-hidden="true">🧩</span><span class="lb">Plug-ins vs extensions</span></a>
</aside> </aside>
<header class="hero"> <header class="hero">
<h1>Theseus <span class="g">Plug-ins</span></h1> <h1>Theseus <span class="g">Plug-ins</span></h1>
<p class="tag">Silent Mode's own components that ship inside Theseus and appear under <p class="tag">Silent Mode's own components that ship inside Theseus and appear under
<b>Settings › Plug-ins</b>: the wallet and the system-wide resolver. Community-made <b>Settings › Plug-ins</b>: the wallet and the system-wide resolver. Community-made
add-ons live on the separate <a href="../extensions/">Extensions</a> page.</p> add-ons live on the separate <a href="../extensions/">Extensions</a> page.</p>
</header> </header>
<div class="wrap"> <div class="wrap">
<section id="aegis-section"> <section id="aegis-section">
<h2>Aegis — the wallet</h2> <h2>Aegis — the wallet</h2>
<p class="lede">Multi-chain wallet derived from your Theseus vault (BCH, BTC, TRX, ETH, SOL, SC, DGB) with a <p class="lede">Multi-chain wallet derived from your Theseus vault (BCH, BTC, TRX, ETH, SOL, SC, DGB) with a
dApp bridge for the pages you visit. Bundled with every Theseus build and updated over a signed channel dApp bridge for the pages you visit. Bundled with every Theseus build and updated over a signed channel
between releases; the panel's own <i>Stage → Restart</i> flow applies an update without reinstalling Theseus.</p> between releases; the panel's own <i>Stage → Restart</i> flow applies an update without reinstalling Theseus.</p>
<div class="ext" id="aegis" data-channel="../extensions/aegis/updates.json"> <div class="ext" id="aegis" data-channel="../extensions/aegis/updates.json">
<h3><span class="icon" style="font-size:0;line-height:0"><svg viewBox="0 0 32 32" fill="none" width="26" height="26" style="vertical-align:-6px"><polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="#0a0a0d" stroke="#D6FF3D" stroke-width="1.6" stroke-linejoin="round"/><circle cx="16" cy="16" r="4.5" fill="none" stroke="#D6FF3D" stroke-width="1.4"/><circle cx="16" cy="16" r="1.6" fill="#D6FF3D"/></svg></span> Aegis <span class="cur" style="margin-left:auto">current <span data-live="version">0.6.31</span></span></h3> <h3><span class="icon" style="font-size:0;line-height:0"><svg viewBox="0 0 32 32" fill="none" width="26" height="26" style="vertical-align:-6px"><polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="#0a0a0d" stroke="#D6FF3D" stroke-width="1.6" stroke-linejoin="round"/><circle cx="16" cy="16" r="4.5" fill="none" stroke="#D6FF3D" stroke-width="1.4"/><circle cx="16" cy="16" r="1.6" fill="#D6FF3D"/></svg></span> Aegis <span class="cur" style="margin-left:auto">current <span data-live="version">0.6.31</span></span></h3>
<div class="desc">Vault-derived keys, per-chain accounts, imported keys, message signing, dApp permissions per site.</div> <div class="desc">Vault-derived keys, per-chain accounts, imported keys, message signing, dApp permissions per site.</div>
<div class="meta">Plug-in · Settings › Plug-ins · <span data-live="count">signed versions on the channel</span></div> <div class="meta">Plug-in · Settings › Plug-ins · <span data-live="count">signed versions on the channel</span></div>
<div class="verifybox"> <div class="verifybox">
<div class="lbl">Signed update manifest — Ed25519 by the Silent Mode operator, SHA-256 verified before install</div> <div class="lbl">Signed update manifest — Ed25519 by the Silent Mode operator, SHA-256 verified before install</div>
<div class="h"><b>updates.json</b><code><a href="../extensions/aegis/updates.json">theseus.x/extensions/aegis/updates.json</a></code></div> <div class="h"><b>updates.json</b><code><a href="../extensions/aegis/updates.json">theseus.x/extensions/aegis/updates.json</a></code></div>
<div class="h"><b>Tarball</b><code><a data-live="url" href="../extensions/aegis/">…</a></code></div> <div class="h"><b>Tarball</b><code><a data-live="url" href="../extensions/aegis/">…</a></code></div>
<div class="h"><b>sha256</b><code data-live="sha256">—</code></div> <div class="h"><b>sha256</b><code data-live="sha256">—</code></div>
</div> </div>
</div> </div>
</section> </section>
<section id="ariadne-section"> <section id="ariadne-section">
<h2>Ariadne's Thread — the system-wide resolver</h2> <h2>Ariadne's Thread — the system-wide resolver</h2>
<p class="lede">Makes BCDN names resolve for <em>every</em> program on the machine, not only Theseus: a local <p class="lede">Makes BCDN names resolve for <em>every</em> program on the machine, not only Theseus: a local
DNS resolver plus gateway, installed by Theseus's setup by default (opt-out) and switchable from DNS resolver plus gateway, installed by Theseus's setup by default (opt-out) and switchable from
<b>Settings › Plug-ins</b>. The same resolver ships standalone for Windows and as Ariadne for Android.</p> <b>Settings › Plug-ins</b>. The same resolver ships standalone for Windows and as Ariadne for Android.</p>
<div class="ext" id="ariadne"> <div class="ext" id="ariadne">
<h3><span class="icon">🧵</span> Ariadne's Thread <span class="cur" style="margin-left:auto">current <span data-live="resolver-version">0.1.3</span></span></h3> <h3><span class="icon">🧵</span> Ariadne's Thread <span class="cur" style="margin-left:auto">current <span data-live="resolver-version">0.1.3</span></span></h3>
<div class="desc">Windows service: answers A/AAAA for registered names, forwards everything else to your upstream <div class="desc">Windows service: answers A/AAAA for registered names, forwards everything else to your upstream
resolver, serves Sia-hosted content through a local gateway with a machine-local CA. Answers MX/TXT/NS from resolver, serves Sia-hosted content through a local gateway with a machine-local CA. Answers MX/TXT/NS from
owner-signed DNS records.</div> owner-signed DNS records.</div>
<div class="meta">Plug-in · Settings › Plug-ins · Turn on / off · Install / Update / Uninstall from inside Theseus</div> <div class="meta">Plug-in · Settings › Plug-ins · Turn on / off · Install / Update / Uninstall from inside Theseus</div>
<div class="btns"> <div class="btns">
<a class="btn" href="https://dl.silentmode.st/latest/AriadneResolver-Setup.exe">Windows installer</a> <a class="btn" href="https://dl.silentmode.st/latest/AriadneResolver-Setup.exe">Windows installer</a>
<a class="btn ghost" href="https://dl.silentmode.st/latest/ariadne.apk">Ariadne for Android (APK) · v<span data-live="android-version">0.19</span></a> <a class="btn ghost" href="https://dl.silentmode.st/latest/ariadne.apk">Ariadne for Android (APK) · v<span data-live="android-version">0.19</span></a>
</div> </div>
<div class="verifybox"> <div class="verifybox">
<div class="lbl">Hashes from the releases manifest — verify before running</div> <div class="lbl">Hashes from the releases manifest — verify before running</div>
<div class="h"><b>Installer</b><code data-live="resolver-sha">—</code></div> <div class="h"><b>Installer</b><code data-live="resolver-sha">—</code></div>
<div class="h"><b>APK</b><code data-live="android-sha">—</code></div> <div class="h"><b>APK</b><code data-live="android-sha">—</code></div>
<div class="h"><b>Manifest</b><code><a href="https://dl.silentmode.st/releases-manifest.json">dl.silentmode.st/releases-manifest.json</a></code></div> <div class="h"><b>Manifest</b><code><a href="https://dl.silentmode.st/releases-manifest.json">dl.silentmode.st/releases-manifest.json</a></code></div>
</div> </div>
</div> </div>
</section> </section>
<section id="line"> <section id="line">
<h2>Where the line is</h2> <h2>Where the line is</h2>
<p class="lede">Plug-ins are first-party: Silent Mode builds them, signs them with the operator key, and Theseus treats <p class="lede">Plug-ins are first-party: Silent Mode builds them, signs them with the operator key, and Theseus treats
them as part of itself (own Settings section, own update flow). Extensions are everyone else's: published by them as part of itself (own Settings section, own update flow). Extensions are everyone else's: published by
any BCDN name holder, signed with that name's wallet, listed on <a href="../extensions/">theseus.x/extensions</a> any BCDN name holder, signed with that name's wallet, listed on <a href="../extensions/">theseus.x/extensions</a>
and installed from <b>Settings › Extensions</b>. Both are verified on your device before a byte runs.</p> and installed from <b>Settings › Extensions</b>. Both are verified on your device before a byte runs.</p>
<div class="note"><b>Want to build one?</b> Start on the <a href="../extensions/#build">Extensions page</a> — the same add-on <div class="note"><b>Want to build one?</b> Start on the <a href="../extensions/#build">Extensions page</a> — the same add-on
format powers both, and publishing takes a name and a signature, nothing else.</div> format powers both, and publishing takes a name and a signature, nothing else.</div>
</section> </section>
<footer> <footer>
Silent Mode · <a href="https://code.silentmode.st/silentmode/theseus/src/branch/master/TheseusNavigator/docs/ADDON-UPDATES.md">how signed updates work</a> Silent Mode · <a href="https://code.silentmode.st/silentmode/theseus/src/branch/master/TheseusNavigator/docs/ADDON-UPDATES.md">how signed updates work</a>
</footer> </footer>
</div> </div>
<script> <script>
(async () => { (async () => {
const set = (k, f) => document.querySelectorAll('[data-live="' + k + '"]').forEach(f); const set = (k, f) => document.querySelectorAll('[data-live="' + k + '"]').forEach(f);
const cmp = (a, b) => { const x = String(a).split(".").map(Number), y = String(b).split(".").map(Number); for (let i = 0; i < Math.max(x.length, y.length); i++) { const d = (x[i] || 0) - (y[i] || 0); if (d) return d; } return 0; }; const cmp = (a, b) => { const x = String(a).split(".").map(Number), y = String(b).split(".").map(Number); for (let i = 0; i < Math.max(x.length, y.length); i++) { const d = (x[i] || 0) - (y[i] || 0); if (d) return d; } return 0; };
try { try {
const r = await fetch("../extensions/aegis/updates.json", { cache: "no-store" }); const r = await fetch("../extensions/aegis/updates.json", { cache: "no-store" });
if (r.ok) { if (r.ok) {
const m = await r.json(); const list = (m.addons || []).slice().sort((x, y) => cmp(y.version, x.version)); const top = list[0]; const m = await r.json(); const list = (m.addons || []).slice().sort((x, y) => cmp(y.version, x.version)); const top = list[0];
if (top) { if (top) {
set("version", (el) => { el.textContent = top.version; }); set("version", (el) => { el.textContent = top.version; });
set("sha256", (el) => { el.textContent = top.sha256 || "—"; }); set("sha256", (el) => { el.textContent = top.sha256 || "—"; });
set("url", (el) => { el.href = top.url; el.textContent = String(top.url).split("/").pop(); }); set("url", (el) => { el.href = top.url; el.textContent = String(top.url).split("/").pop(); });
set("count", (el) => { el.textContent = list.length + " signed version" + (list.length === 1 ? "" : "s") + " on the channel"; }); set("count", (el) => { el.textContent = list.length + " signed version" + (list.length === 1 ? "" : "s") + " on the channel"; });
} }
} }
} catch {} } catch {}
try { try {
const r = await fetch("https://dl.silentmode.st/releases-manifest.json", { cache: "no-store" }); const r = await fetch("https://dl.silentmode.st/releases-manifest.json", { cache: "no-store" });
if (r.ok) { if (r.ok) {
const m = await r.json(); const g = (id) => (m.releases || []).find((x) => x.id === id); const m = await r.json(); const g = (id) => (m.releases || []).find((x) => x.id === id);
const res = g("ariadne-resolver"), an = g("ariadne-android"); const res = g("ariadne-resolver"), an = g("ariadne-android");
if (res) { set("resolver-version", (el) => { el.textContent = res.version; }); const f = Object.keys(res.files || {})[0]; set("resolver-sha", (el) => { el.textContent = f ? res.files[f] : "—"; }); } if (res) { set("resolver-version", (el) => { el.textContent = res.version; }); const f = Object.keys(res.files || {})[0]; set("resolver-sha", (el) => { el.textContent = f ? res.files[f] : "—"; }); }
if (an) { set("android-version", (el) => { el.textContent = an.version; }); const f = Object.keys(an.files || {})[0]; set("android-sha", (el) => { el.textContent = f ? an.files[f] : "—"; }); } if (an) { set("android-version", (el) => { el.textContent = an.version; }); const f = Object.keys(an.files || {})[0]; set("android-sha", (el) => { el.textContent = f ? an.files[f] : "—"; }); }
} }
} catch {} } catch {}
})(); })();
// Sidenav scroll-spy: highlight the section currently in view. // Sidenav scroll-spy: highlight the section currently in view.
(() => { // Uses scroll-position tracking so short sections at the end of the page
const links = document.querySelectorAll(".sidenav a[href^='#']"); // still activate their link when the reader scrolls to them.
if (!links.length) return; (() => {
const map = new Map(); const links = document.querySelectorAll(".sidenav a[href^='#']");
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) map.set(el, a); }); if (!links.length) return;
const io = new IntersectionObserver((entries) => { const entries = [];
entries.forEach((en) => { links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
if (!en.isIntersecting) return; if (!entries.length) return;
const a = map.get(en.target); if (!a) return; const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
links.forEach((l) => l.classList.remove("active")); const update = () => {
a.classList.add("active"); const doc = document.documentElement;
}); const scrollBottom = window.scrollY + window.innerHeight;
}, { rootMargin: "-40% 0px -55% 0px", threshold: 0 }); if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
map.forEach((_, el) => io.observe(el)); const line = window.scrollY + window.innerHeight * 0.35;
})(); let active = entries[0].link;
</script> for (const e of entries) {
</body> if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
</html> else break;
}
setActive(active);
};
window.addEventListener("scroll", update, { passive: true });
window.addEventListener("resize", update);
update();
})();
</script>
</body>
</html>