theseus: drop Community section from Settings > Extensions

Discovery of new community extensions belongs on theseus.x/extensions,
which has room to describe each one and lists them by publisher. Settings
just shows what's installed on this machine — one card per extension,
whether bundled or community. The Install button on the web page invokes
the same addons-install-community IPC, and Theseus refreshes the
installed list after each install, so the two lists were mirroring the
same state with a docx-editor card appearing on both sides.

Removes the Community h2, its list container, the Refresh catalog
button, loadCommunity() and its wiring — kept only the trust warning as
a note directly under the installed list.
This commit is contained in:
Local Dev 2026-09-21 03:28:04 +02:00
parent 0684906f13
commit 9fbba50999

View file

@ -606,14 +606,12 @@
<div id="addonsUpdStatus" class="pmuted" style="font-size:12.5px;margin-top:6px;text-align:right"></div> <div id="addonsUpdStatus" class="pmuted" style="font-size:12.5px;margin-top:6px;text-align:right"></div>
<h2 class="sub" style="border-top:0;padding-top:0;margin-top:1.5rem">Installed</h2> <h2 class="sub" style="border-top:0;padding-top:0;margin-top:1.5rem">Installed</h2>
<div id="addonsList"><div class="d" style="color:var(--dim)">Loading…</div></div> <div id="addonsList"><div class="d" style="color:var(--dim)">Loading…</div></div>
<h2 class="sub" style="margin-top:1.5rem">Community</h2> <div class="note" style="margin-top:1.5rem">To install a new extension, browse the catalogue at
<p class="lede" style="margin-top:0">Extensions anyone can publish from <a href="https://theseus.x/extensions/" target="_blank" rel="noopener">theseus.x/extensions</a> <a href="https://theseus.x/extensions/" target="_blank" rel="noopener">theseus.x/extensions</a>
by signing the package with the wallet that holds a BCDN name. Before installing, Theseus checks that signature and use the Install button there. Theseus checks each publisher's signature against the name's current
against the name's current owner in its own chain index — the relay can't substitute code under a trusted name.</p> owner on chain before installing — the relay can't substitute code under a trusted name. Extensions run
<div class="row" style="justify-content:flex-end;gap:8px"><button id="communityRefresh" class="btn" type="button">Refresh catalog</button></div> with full app access — treat installing one like installing an unsigned executable, and only load
<div id="communityList"><div class="d" style="color:var(--dim)">Loading…</div></div> extensions whose source you trust.</div>
<div class="note">Extensions run with full app access — treat installing one like installing an unsigned executable.
Only load extensions whose source you trust.</div>
</section> </section>
</div> </div>
</div> </div>
@ -1349,44 +1347,10 @@
btn.addEventListener("click", () => C.revealAddon(btn.dataset.reveal)); btn.addEventListener("click", () => C.revealAddon(btn.dataset.reveal));
}); });
} }
// ---- community catalog (theseus.x/extensions) ---- // Discovery of new community extensions happens at theseus.x/extensions —
const communityList = document.getElementById("communityList"); // Settings only shows what's installed on this machine. The install button
async function loadCommunity() { // on that page invokes the same "addons-install-community" IPC and Theseus
communityList.innerHTML = '<div class="d" style="color:var(--dim)">Loading…</div>'; // refreshes the installed list on its own.
let r;
try { r = await C.communityCatalog(); } catch (e) { r = { ok: false, error: e?.message || String(e), extensions: [] }; }
if (!r.ok) { communityList.innerHTML = '<div class="d" style="color:#f6768a">Catalog unavailable: ' + escapeHtml(r.error) + '</div>'; return; }
// Hide extensions already shown in the Installed list above — the
// Community list's job is to surface things you DON'T have. Updates to
// installed extensions come through the poll + "Check for updates" flow
// in the Installed list, not through a second card for the same id here.
const catalog = r.extensions.filter((e) => !e.installedVersion);
if (!catalog.length) { communityList.innerHTML = '<div class="d" style="color:var(--dim)">' + (r.extensions.length ? "All catalog extensions are already installed." : "Nothing published yet. Be the first — see theseus.x/extensions.") + '</div>'; return; }
communityList.innerHTML = catalog.map((e) => {
const state = e.installedVersion
? (e.canUpdate ? '<button class="btn" data-install="' + escapeAttr(e.id) + '">Update to ' + escapeHtml(e.latest) + '</button>'
: '<span style="color:var(--acid);font-size:12.5px">Installed v' + escapeHtml(e.installedVersion) + '</span>')
: '<button class="btn" data-install="' + escapeAttr(e.id) + '">Install</button>';
return '<div class="row"><div class="txt"><div class="t">' + escapeHtml(e.icon || "🧩") + ' ' + escapeHtml(e.name)
+ ' <span style="color:var(--dim);font-weight:400">v' + escapeHtml(e.latest) + '</span></div>'
+ '<div class="d">' + escapeHtml(e.description || "") + '</div>'
+ '<div class="d" style="color:var(--dim);font-size:12px">Published by <b>' + escapeHtml(e.publisher) + '</b> · ' + escapeHtml(String(e.versions || 1)) + ' version' + (e.versions === 1 ? "" : "s")
+ (e.capabilities && e.capabilities.length ? ' · ' + escapeHtml(e.capabilities.join(", ")) : "") + '</div>'
+ '<div class="d" data-status="' + escapeAttr(e.id) + '"></div></div><div>' + state + '</div></div>';
}).join("");
communityList.querySelectorAll("button[data-install]").forEach((btn) => {
btn.addEventListener("click", async () => {
const id = btn.dataset.install;
const status = communityList.querySelector('[data-status="' + CSS.escape(id) + '"]');
btn.disabled = true; const orig = btn.textContent; btn.textContent = "Verifying…";
let res;
try { res = await C.installCommunity(id); } catch (e) { res = { ok: false, error: e?.message || String(e) }; }
if (res && res.ok) { status.style.color = "var(--acid)"; status.textContent = "✓ Installed v" + res.version + " (signed by " + res.publisher + ")"; loadAddons(); loadCommunity(); }
else { status.style.color = "#f6768a"; status.textContent = "✗ " + (res?.error || "install failed"); btn.disabled = false; btn.textContent = orig; }
});
});
}
document.getElementById("communityRefresh").addEventListener("click", loadCommunity);
function escapeHtml(s) { return String(s || "").replace(/[&<>"']/g, (c) => ({ "&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;" })[c]); } function escapeHtml(s) { return String(s || "").replace(/[&<>"']/g, (c) => ({ "&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;" })[c]); }
function escapeAttr(s) { return escapeHtml(s); } function escapeAttr(s) { return escapeHtml(s); }
async function loadAddons() { async function loadAddons() {
@ -1407,7 +1371,7 @@
}); });
document.getElementById("addonsOpenDir").addEventListener("click", () => C.openAddonsDir()); document.getElementById("addonsOpenDir").addEventListener("click", () => C.openAddonsDir());
document.querySelector('.side a[data-sec="addons"]').addEventListener("click", async () => { document.querySelector('.side a[data-sec="addons"]').addEventListener("click", async () => {
await loadAddonUpdates(); loadAddons(); loadCommunity(); await loadAddonUpdates(); loadAddons();
}); });
// Plug-ins tab: refresh Ariadne's live daemon state on every visit so it // Plug-ins tab: refresh Ariadne's live daemon state on every visit so it
// doesn't display stale "checking…" text if the background poll finished // doesn't display stale "checking…" text if the background poll finished
@ -1463,7 +1427,7 @@
} }
}); });
// Populate on first paint so the tab is ready when the user clicks in. // Populate on first paint so the tab is ready when the user clicks in.
(async () => { await loadAddonUpdates(); loadAddons(); loadCommunity(); })(); (async () => { await loadAddonUpdates(); loadAddons(); })();
</script> </script>
</body> </body>
</html> </html>