Two UX polishes: 1. Register-tab icon flipped from 🪪 (identification-card emoji, renders as a hollow box in system-ui fonts without extended emoji) to ✏️ (pencil), which reads as 'edit/create' and ships in every emoji font worth targeting. Changed across the nav on all 8 pages plus the footer injector's Product column. Kept the Register nav item — six items total in the nav, and from any subpage it's one click to the search on landing. 2. tld.html no longer duplicates the wallet-choice + mint flow inline. The Register button on a search result now hands the label off to the shared register-flow.js modal — same modal that name registration uses — via a new window.siriusRegisterTld(label, {serviceFeeSats}) entry point. register-flow.js grew: - startTldFlow / window.siriusRegisterTld: sets state.tld and walks the modal through wallet-choice → Fund → stepConfirmTld → stepRegisterTld → stepDoneTld - stepConfirmTld: shows label + fee + owner, no per-name quote - stepRegisterTld: dispatches to registerTldWithBuiltInWallet or registerTldWithExternalWallet based on state.wallet/state.session - stepDoneTld: 'X is yours' with txid + certificate id + operator address, no record editor (a TLD certificate has no records to set immediately) - stepWallet / stepExternalConfirm branch on state.tld so the modal heading and the WC-confirm path route correctly tld.html trimmed: removed the entire signin-section + mint-section plus their inline handlers (~250 lines gone). Now just search + registered-TLDs list + a delegator to the shared modal. If a user is already signed in via the wallet dropdown, the modal recognises the saved wallet ('Unlock my browser wallet' button appears) instead of asking them to sign in again on this page.
256 lines
12 KiB
HTML
256 lines
12 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Register a TLD — Sirius.X</title>
|
||
<meta name="description" content="Mint a top-level domain on Bitcoin Cash. Any wallet can register a TLD; second-level names can then be sold under it.">
|
||
<link rel="icon" href="./assets/favicon.svg" type="image/svg+xml">
|
||
<link rel="mask-icon" href="./assets/favicon.svg" color="#d6ff3d">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:site_name" content="Sirius.X">
|
||
<meta property="og:title" content="Register a TLD on Bitcoin Cash">
|
||
<meta property="og:description" content="Mint a TLD certificate. One BCH transaction, no renewal, no registrar.">
|
||
<meta property="og:url" content="https://silentmode.st/sirius-x/tld.html">
|
||
<meta property="og:image" content="https://silentmode.st/sirius-x/brand/banner.svg">
|
||
<style>
|
||
:root{
|
||
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09);
|
||
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
|
||
--ok:#4fd1a5; --taken:#f6768a; --warn:#ffc75f;
|
||
}
|
||
*{box-sizing:border-box}
|
||
html{scroll-behavior:smooth}
|
||
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}
|
||
a{color:var(--acid)}
|
||
.wrap{max-width:820px;margin:0 auto;padding:0 1.2rem}
|
||
.topnav{position:sticky;top:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap;
|
||
padding:10px 1.2rem;background:rgba(11,14,20,.82);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
||
.topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px}
|
||
.topnav .brand .g{color:var(--acid)}
|
||
.topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px}
|
||
.topnav a:hover{color:var(--ink);background:var(--panel)}
|
||
.topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)}
|
||
.topnav a.portal{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500}
|
||
.topnav a.portal:hover{background:rgba(214,255,61,.18)}
|
||
header.hero{text-align:center;padding:3.4rem 1.2rem 1rem}
|
||
.mark{font-size:44px;line-height:1}
|
||
h1{font-size:clamp(1.7rem,4.5vw,2.3rem);margin:.3rem 0 .3rem}
|
||
h1 .g{color:var(--acid)}
|
||
.tag{color:var(--mut);font-size:1rem;max-width:600px;margin:0 auto}
|
||
h2{font-size:1.25rem;margin:0 0 .4rem}
|
||
section{padding:2rem 0;border-top:1px solid var(--line);margin-top:2rem}
|
||
section:first-of-type{border-top:none;margin-top:1rem}
|
||
.btn{display:inline-block;text-decoration:none;padding:10px 18px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;border:none;cursor:pointer;font-family:inherit}
|
||
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
|
||
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
|
||
.btn:hover{filter:brightness(1.12)}
|
||
.btn:disabled{opacity:.45;cursor:not-allowed;filter:none}
|
||
.muted{color:var(--mut);font-size:13px}
|
||
.dim{color:var(--dim);font-size:12.5px}
|
||
code,.mono{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px;word-break:break-all}
|
||
.badge{display:inline-block;font-size:11px;padding:2px 10px;border-radius:999px;font-weight:600}
|
||
.b-ok{background:rgba(79,209,165,.15);color:var(--ok)}
|
||
.b-taken{background:rgba(246,118,138,.15);color:var(--taken)}
|
||
.b-check{background:rgba(139,152,169,.14);color:var(--mut)}
|
||
.status{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:10px 14px;font-size:13px;color:var(--mut);margin-top:12px}
|
||
.status.ok{border-left:3px solid var(--ok)}
|
||
.status.err{border-left:3px solid var(--taken);color:var(--taken)}
|
||
.status.warn{border-left:3px solid var(--warn);color:var(--warn)}
|
||
/* TLD search cell */
|
||
.search{max-width:600px;margin:1.5rem auto 0}
|
||
.searchrow{display:flex;gap:8px}
|
||
.searchrow input{flex:1;padding:14px 18px;font-size:17px;border-radius:12px;border:1px solid #ffffff22;background:#141a24;color:var(--ink);font-family:inherit;outline:none}
|
||
.searchrow input:focus{border-color:#4b7bec}
|
||
.searchhint{font-size:12.5px;color:var(--mut);margin-top:8px;text-align:center}
|
||
.result-card{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;
|
||
background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 18px;margin-top:12px}
|
||
.result-card .lbl{font-family:ui-monospace,monospace;font-size:18px;color:var(--ink)}
|
||
.result-card .lbl .dot{color:var(--acid)}
|
||
/* Registered-TLDs table */
|
||
table.tlds{width:100%;border-collapse:collapse;margin-top:1rem;font-size:13px}
|
||
table.tlds th,table.tlds td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
|
||
table.tlds th{color:var(--mut);font-weight:500;font-size:11.5px;text-transform:uppercase;letter-spacing:.4px}
|
||
table.tlds .tld{font-family:ui-monospace,monospace;color:var(--acid);font-size:13.5px;font-weight:600}
|
||
table.tlds .cat{font-family:ui-monospace,monospace;color:var(--mut);font-size:11.5px;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
footer{border-top:1px solid var(--line);padding:2rem 0 3rem;color:var(--dim);font-size:13px;text-align:center;margin-top:2.5rem}
|
||
</style>
|
||
<script type="importmap">
|
||
{ "imports": { "@bitauth/libauth": "https://silentmode.st/js/libauth.js" } }
|
||
</script>
|
||
</head>
|
||
<body>
|
||
|
||
<nav class="topnav">
|
||
<a class="brand" href="./">⭐ <span class="g">Sirius.X</span></a>
|
||
<a href="./#try">Try it</a>
|
||
<a href="./#search-input">✏️ Register</a>
|
||
<a href="./tld.html" class="here">🌐 TLD</a>
|
||
<a href="./theseus/"><svg width="14" height="14" viewBox="0 0 64 64" style="vertical-align:-2px;margin-right:4px" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/><path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/><circle cx="32" cy="32" r="3" fill="#0b0e14"/></svg>Theseus</a>
|
||
<a href="./portal.html" class="portal">🔑 Sign in</a>
|
||
</nav>
|
||
|
||
<header class="hero">
|
||
<div class="mark">🌐</div>
|
||
<h1>Register a <span class="g">TLD</span></h1>
|
||
<p class="tag">A TLD is a certificate on the Bitcoin Cash chain. You mint it, you own it — any
|
||
second-level name registered under it lives in your TLD's registry. One BCH transaction,
|
||
no renewal, no ICANN.</p>
|
||
</header>
|
||
|
||
<div class="wrap">
|
||
|
||
<section id="search-section">
|
||
<h2>Search a TLD</h2>
|
||
<p class="muted">Type a label (a–z, 0–9, hyphen; up to 16 chars). Available TLDs get a Register
|
||
button that opens the shared wallet-choice modal — the same flow name registration uses.</p>
|
||
<div class="search">
|
||
<div class="searchrow">
|
||
<input id="q" placeholder="your-tld" spellcheck="false" autocomplete="off" autofocus maxlength="16">
|
||
</div>
|
||
<div class="searchhint">Register minting to your token address; you become the registry operator.</div>
|
||
</div>
|
||
<div id="result"></div>
|
||
</section>
|
||
|
||
<section id="listing">
|
||
<h2>Registered TLDs</h2>
|
||
<p class="muted">Fetched from the on-chain TLD registry via the public gateway. Refresh the page for the latest.</p>
|
||
<div id="tlds-status" class="status">Loading…</div>
|
||
<table class="tlds" id="tlds-table" style="display:none">
|
||
<thead><tr><th>TLD</th><th>Category</th></tr></thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
<footer id="site-footer"></footer>
|
||
|
||
<script>
|
||
// Simple oracle stub: static chipnet rate for testing. Same number the
|
||
// portal uses. Mainnet swaps for a real price feed.
|
||
const CHIPNET_SATS_PER_USD = 250_000;
|
||
const DEFAULT_USD = 5;
|
||
|
||
const $ = (id) => document.getElementById(id);
|
||
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
||
const validLabel = (s) => /^[a-z0-9-]{1,16}$/.test(s);
|
||
const normLabel = (s) => String(s).toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 16);
|
||
|
||
// ---------- registered-TLD list ----------
|
||
let registeredTlds = null;
|
||
async function loadTldList() {
|
||
const status = $("tlds-status"), tbody = $("tlds-table").querySelector("tbody");
|
||
try {
|
||
const r = await fetch("https://navigate.st/api/tlds");
|
||
if (!r.ok) throw new Error("api " + r.status);
|
||
const j = await r.json();
|
||
const tlds = (j.tlds || []).map((t) => (typeof t === "string" ? { tld: t } : t));
|
||
tlds.sort((a, b) => a.tld.localeCompare(b.tld));
|
||
tbody.innerHTML = tlds.map((t) =>
|
||
`<tr><td class="tld">.${esc(t.tld)}</td><td class="cat">${esc(t.certificate || t.category || "")}</td></tr>`
|
||
).join("");
|
||
$("tlds-table").style.display = "";
|
||
status.className = "status ok";
|
||
status.textContent = `${tlds.length} TLDs registered on chipnet.`;
|
||
registeredTlds = tlds.map((t) => t.tld);
|
||
refreshSearch();
|
||
} catch (e) {
|
||
status.className = "status err";
|
||
status.textContent = "Could not load TLD list: " + (e.message || e);
|
||
}
|
||
}
|
||
loadTldList();
|
||
|
||
// ---------- search result renderer ----------
|
||
function renderResult(label, state) {
|
||
const res = $("result");
|
||
if (!label) { res.innerHTML = ""; return; }
|
||
if (state === "checking") {
|
||
res.innerHTML = `<div class="result-card">
|
||
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
|
||
<span class="badge b-check">checking…</span><span></span></div>`;
|
||
return;
|
||
}
|
||
if (state === "taken") {
|
||
res.innerHTML = `<div class="result-card">
|
||
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
|
||
<span class="badge b-taken">taken</span><span></span></div>
|
||
<p class="dim" style="margin-top:8px">Someone already minted <code>.${esc(label)}</code>. Try a
|
||
different label — TLDs are first-come, first-served.</p>`;
|
||
return;
|
||
}
|
||
res.innerHTML = `<div class="result-card">
|
||
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
|
||
<span class="badge b-ok">available</span>
|
||
<button class="btn acid" id="go-register">Register .${esc(label)} →</button>
|
||
</div>`;
|
||
$("go-register").onclick = () => beginRegister(label);
|
||
}
|
||
|
||
// ---------- search wiring ----------
|
||
let currentLabel = "";
|
||
function refreshSearch() {
|
||
const raw = normLabel($("q").value);
|
||
currentLabel = raw;
|
||
if (!raw) { renderResult("", null); return; }
|
||
if (!validLabel(raw)) { renderResult(raw, "invalid"); return; }
|
||
if (registeredTlds !== null) {
|
||
renderResult(raw, registeredTlds.includes(raw) ? "taken" : "ok");
|
||
return;
|
||
}
|
||
renderResult(raw, "checking");
|
||
}
|
||
$("q").addEventListener("input", () => {
|
||
$("q").value = normLabel($("q").value);
|
||
refreshSearch();
|
||
});
|
||
$("q").addEventListener("keydown", (e) => {
|
||
if (e.key === "Enter" && currentLabel && registeredTlds && !registeredTlds.includes(currentLabel)) {
|
||
beginRegister(currentLabel);
|
||
}
|
||
});
|
||
|
||
// ---------- mint delegation ----------
|
||
// Register button hands the label off to the shared modal (register-flow.js
|
||
// — window.siriusRegisterTld). That modal owns the wallet-choice, fund,
|
||
// confirm, and mint steps — same UX as name registration.
|
||
function beginRegister(label) {
|
||
const call = () => window.siriusRegisterTld(label, {
|
||
serviceFeeSats: BigInt(DEFAULT_USD * CHIPNET_SATS_PER_USD),
|
||
});
|
||
if (typeof window.siriusRegisterTld === "function") { call(); return; }
|
||
// Module still loading — retry until it lands.
|
||
const btn = $("go-register");
|
||
const orig = btn?.textContent;
|
||
if (btn) { btn.disabled = true; btn.textContent = "loading…"; }
|
||
const t = setInterval(() => {
|
||
if (typeof window.siriusRegisterTld === "function") {
|
||
clearInterval(t);
|
||
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
||
call();
|
||
}
|
||
}, 100);
|
||
}
|
||
|
||
// ---------- ?q= deep-link (from landing links etc.) ----------
|
||
(() => {
|
||
const q = normLabel(new URLSearchParams(location.search).get("q") || "");
|
||
if (!q) return;
|
||
$("q").value = q;
|
||
const wait = setInterval(() => {
|
||
if (registeredTlds === null) return;
|
||
clearInterval(wait);
|
||
refreshSearch();
|
||
setTimeout(() => $("q").scrollIntoView({ behavior: "smooth", block: "start" }), 100);
|
||
}, 150);
|
||
})();
|
||
</script>
|
||
|
||
<script type="module" src="./js/register-flow.js?v=20260908tld"></script>
|
||
<script defer src="./js/site-footer.js?v=20260907rel"></script>
|
||
<script defer src="./js/profile-menu.js?v=20260907inline"></script>
|
||
</body>
|
||
</html>
|