diff --git a/css/soldomains.css b/css/soldomains.css new file mode 100644 index 0000000..8ef4758 --- /dev/null +++ b/css/soldomains.css @@ -0,0 +1,448 @@ +/* .SOL DOMAINS - Solana Name Service */ + +.sol-container { + max-width: clamp(1000px, 85vw, 1800px); + margin: 0 auto; + padding: 0 2rem 3rem; +} + +/* Search Section */ +.sol-search-section { + margin-bottom: 2rem; +} + +.sol-search-box { + display: flex; + gap: 0.5rem; + background: rgba(16, 16, 16, 0.85); + border: 1px solid var(--border); + border-left: 3px solid rgba(138, 43, 226, 0.6); + padding: 1rem 1.25rem; + align-items: center; +} + +.sol-search-box:focus-within { + border-left-color: #a855f7; + box-shadow: 0 0 20px rgba(138, 43, 226, 0.1); +} + +.sol-search-prefix { + font-family: 'Orbitron', monospace; + font-size: 0.75rem; + color: #a855f7; + letter-spacing: 2px; + white-space: nowrap; +} + +.sol-search-input { + flex: 1; + background: transparent; + border: none; + outline: none; + font-family: 'JetBrains Mono', monospace; + font-size: 0.8rem; + color: #ffffff; + letter-spacing: 1px; +} + +.sol-search-input::placeholder { + color: rgba(255, 255, 255, 0.2); +} + +.sol-search-suffix { + font-family: 'JetBrains Mono', monospace; + font-size: 0.75rem; + color: rgba(138, 43, 226, 0.6); + letter-spacing: 1px; +} + +.sol-search-btn { + background: rgba(138, 43, 226, 0.15); + border: 1px solid rgba(138, 43, 226, 0.3); + color: #a855f7; + font-family: 'Orbitron', monospace; + font-size: 0.65rem; + letter-spacing: 2px; + padding: 0.6rem 1.2rem; + cursor: pointer; + transition: all 0.25s ease; +} + +.sol-search-btn:hover { + background: rgba(138, 43, 226, 0.25); + border-color: #a855f7; + box-shadow: 0 0 15px rgba(138, 43, 226, 0.2); +} + +/* Wallet Connection */ +.sol-wallet-bar { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + padding: 0.75rem 1.25rem; + background: rgba(16, 16, 16, 0.6); + border: 1px solid rgba(138, 43, 226, 0.15); +} + +.sol-wallet-status { + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + color: rgba(255, 255, 255, 0.4); + letter-spacing: 1px; +} + +.sol-wallet-status.connected { + color: #00cc44; +} + +.sol-wallet-address { + font-family: 'JetBrains Mono', monospace; + font-size: 0.6rem; + color: rgba(138, 43, 226, 0.7); + letter-spacing: 1px; + margin-left: 0.5rem; +} + +.sol-wallet-btn { + background: rgba(138, 43, 226, 0.12); + border: 1px solid rgba(138, 43, 226, 0.3); + color: #a855f7; + font-family: 'JetBrains Mono', monospace; + font-size: 0.6rem; + letter-spacing: 2px; + padding: 0.5rem 1rem; + cursor: pointer; + transition: all 0.25s ease; +} + +.sol-wallet-btn:hover { + background: rgba(138, 43, 226, 0.25); + border-color: #a855f7; +} + +.sol-wallet-btn.disconnect { + border-color: rgba(255, 50, 50, 0.3); + color: rgba(255, 50, 50, 0.7); + background: rgba(255, 50, 50, 0.08); +} + +.sol-wallet-btn.disconnect:hover { + border-color: rgba(255, 50, 50, 0.6); + background: rgba(255, 50, 50, 0.15); +} + +/* Tabs */ +.sol-tabs { + display: flex; + gap: 0; + margin-bottom: 1.5rem; + border-bottom: 1px solid var(--border); +} + +.sol-tab { + font-family: 'Orbitron', monospace; + font-size: 0.6rem; + letter-spacing: 2px; + color: rgba(255, 255, 255, 0.35); + background: transparent; + border: none; + border-bottom: 2px solid transparent; + padding: 0.75rem 1.5rem; + cursor: pointer; + transition: all 0.25s ease; +} + +.sol-tab:hover { + color: rgba(255, 255, 255, 0.6); +} + +.sol-tab.active { + color: #a855f7; + border-bottom-color: #a855f7; +} + +/* Results Panel */ +.sol-results { + min-height: 200px; +} + +.sol-result-card { + background: rgba(16, 16, 16, 0.85); + border: 1px solid var(--border); + border-left: 3px solid rgba(138, 43, 226, 0.4); + padding: 1.5rem; + margin-bottom: 1rem; +} + +.sol-result-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.sol-result-domain { + font-family: 'Orbitron', monospace; + font-size: 1rem; + color: #ffffff; + letter-spacing: 2px; +} + +.sol-result-domain .sol-ext { + color: #a855f7; +} + +.sol-result-status { + font-family: 'JetBrains Mono', monospace; + font-size: 0.6rem; + letter-spacing: 2px; + padding: 0.3rem 0.75rem; + border: 1px solid; +} + +.sol-result-status.available { + color: #00cc44; + border-color: rgba(0, 204, 68, 0.3); + background: rgba(0, 204, 68, 0.08); +} + +.sol-result-status.taken { + color: rgba(255, 170, 0, 0.8); + border-color: rgba(255, 170, 0, 0.3); + background: rgba(255, 170, 0, 0.08); +} + +.sol-result-body { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +@media (max-width: 700px) { + .sol-result-body { grid-template-columns: 1fr; } +} + +.sol-result-field { + margin-bottom: 0.5rem; +} + +.sol-result-label { + font-family: 'JetBrains Mono', monospace; + font-size: 0.55rem; + color: rgba(138, 43, 226, 0.6); + letter-spacing: 2px; + margin-bottom: 0.25rem; + display: block; +} + +.sol-result-value { + font-family: 'JetBrains Mono', monospace; + font-size: 0.7rem; + color: rgba(255, 255, 255, 0.75); + word-break: break-all; +} + +.sol-result-value a { + color: #a855f7; + text-decoration: none; +} + +.sol-result-value a:hover { + text-decoration: underline; +} + +/* Register Button */ +.sol-register-btn { + display: inline-block; + background: linear-gradient(135deg, rgba(138, 43, 226, 0.25), rgba(168, 85, 247, 0.15)); + border: 1px solid rgba(138, 43, 226, 0.5); + color: #ffffff; + font-family: 'Orbitron', monospace; + font-size: 0.7rem; + letter-spacing: 3px; + padding: 0.75rem 2rem; + cursor: pointer; + transition: all 0.3s ease; + margin-top: 1rem; + text-decoration: none; +} + +.sol-register-btn:hover { + background: linear-gradient(135deg, rgba(138, 43, 226, 0.4), rgba(168, 85, 247, 0.3)); + border-color: #a855f7; + box-shadow: 0 0 25px rgba(138, 43, 226, 0.25); + transform: translateY(-1px); +} + +.sol-register-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +/* My Domains List */ +.sol-domains-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +@media (max-width: 900px) { .sol-domains-grid { grid-template-columns: repeat(2, 1fr); } } +@media (max-width: 600px) { .sol-domains-grid { grid-template-columns: 1fr; } } + +.sol-domain-card { + background: rgba(16, 16, 16, 0.85); + border: 1px solid var(--border); + border-left: 3px solid rgba(138, 43, 226, 0.4); + padding: 1rem 1.25rem; + transition: all 0.25s ease; +} + +.sol-domain-card:hover { + border-left-color: #a855f7; + background: rgba(20, 20, 20, 0.95); +} + +.sol-domain-name { + font-family: 'Orbitron', monospace; + font-size: 0.75rem; + color: #ffffff; + letter-spacing: 2px; + margin-bottom: 0.25rem; +} + +.sol-domain-name .sol-ext { + color: #a855f7; +} + +.sol-domain-fav { + font-family: 'JetBrains Mono', monospace; + font-size: 0.5rem; + color: #00cc44; + letter-spacing: 1px; +} + +/* Loading / Empty States */ +.sol-loading { + text-align: center; + padding: 3rem; +} + +.sol-loading-spinner { + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid rgba(138, 43, 226, 0.2); + border-top-color: #a855f7; + border-radius: 50%; + animation: sol-spin 0.8s linear infinite; +} + +@keyframes sol-spin { + to { transform: rotate(360deg); } +} + +.sol-loading-text { + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + color: rgba(255, 255, 255, 0.35); + letter-spacing: 2px; + margin-top: 0.75rem; +} + +.sol-empty { + text-align: center; + padding: 3rem; + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + color: rgba(255, 255, 255, 0.25); + letter-spacing: 1px; +} + +.sol-error { + background: rgba(255, 50, 50, 0.08); + border: 1px solid rgba(255, 50, 50, 0.2); + border-left: 3px solid rgba(255, 50, 50, 0.5); + padding: 1rem 1.25rem; + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + color: rgba(255, 50, 50, 0.8); + letter-spacing: 1px; + margin-bottom: 1rem; +} + +/* Reverse Lookup */ +.sol-reverse-box { + display: flex; + gap: 0.5rem; + background: rgba(16, 16, 16, 0.85); + border: 1px solid var(--border); + border-left: 3px solid rgba(138, 43, 226, 0.4); + padding: 1rem 1.25rem; + align-items: center; + margin-bottom: 1.5rem; +} + +.sol-reverse-input { + flex: 1; + background: transparent; + border: none; + outline: none; + font-family: 'JetBrains Mono', monospace; + font-size: 0.7rem; + color: #ffffff; + letter-spacing: 0.5px; +} + +.sol-reverse-input::placeholder { + color: rgba(255, 255, 255, 0.2); +} + +/* Stats Bar */ +.sol-stats-bar { + display: flex; + gap: 2rem; + padding: 0.5rem 0; + margin-bottom: 1.5rem; + border-bottom: 1px solid rgba(138, 43, 226, 0.1); +} + +.sol-stat { + font-family: 'JetBrains Mono', monospace; + font-size: 0.55rem; + color: rgba(255, 255, 255, 0.3); + letter-spacing: 1px; +} + +.sol-stat-value { + color: #a855f7; +} + +/* Price Display */ +.sol-price { + display: flex; + align-items: baseline; + gap: 0.5rem; + margin-top: 0.75rem; +} + +.sol-price-amount { + font-family: 'Orbitron', monospace; + font-size: 1.1rem; + color: #ffffff; +} + +.sol-price-currency { + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + color: rgba(138, 43, 226, 0.7); + letter-spacing: 2px; +} + +.sol-price-usd { + font-family: 'JetBrains Mono', monospace; + font-size: 0.6rem; + color: rgba(255, 255, 255, 0.3); +} diff --git a/js/soldomains.js b/js/soldomains.js new file mode 100644 index 0000000..e678b64 --- /dev/null +++ b/js/soldomains.js @@ -0,0 +1,378 @@ +/* .SOL DOMAINS — Solana Name Service Lookup & Registration */ + +const SNS_API = 'https://sns-sdk-proxy.bonfida.workers.dev'; +const BONFIDA_REG = 'https://naming.bonfida.org/#/domain/'; +const SOLSCAN = 'https://solscan.io/account/'; + +let walletAddress = null; +let currentTab = 'search'; + +// ─── DOM ──────────────────────────────────────────────────── +const $ = s => document.querySelector(s); +const $$ = s => document.querySelectorAll(s); + +document.addEventListener('DOMContentLoaded', () => { + initTabs(); + initSearch(); + initWallet(); +}); + +// ─── TABS ─────────────────────────────────────────────────── +function initTabs() { + $$('.sol-tab').forEach(tab => { + tab.addEventListener('click', () => { + const t = tab.dataset.tab; + switchTab(t); + }); + }); +} + +function switchTab(t) { + currentTab = t; + $$('.sol-tab').forEach(tab => tab.classList.toggle('active', tab.dataset.tab === t)); + $$('.sol-panel').forEach(p => p.classList.toggle('hidden', p.id !== `panel-${t}`)); + + if (t === 'mydomains' && walletAddress) loadMyDomains(); +} + +// ─── SEARCH / LOOKUP ──────────────────────────────────────── +function initSearch() { + const input = $('#sol-search'); + const btn = $('#sol-search-go'); + if (!input || !btn) return; + + btn.addEventListener('click', () => doSearch()); + input.addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); }); + + // Reverse lookup + const revInput = $('#sol-reverse'); + const revBtn = $('#sol-reverse-go'); + if (revInput && revBtn) { + revBtn.addEventListener('click', () => doReverse()); + revInput.addEventListener('keydown', e => { if (e.key === 'Enter') doReverse(); }); + } +} + +async function doSearch() { + const raw = $('#sol-search').value.trim().toLowerCase(); + if (!raw) return; + + // Strip .sol if user typed it + const domain = raw.replace(/\.sol$/i, ''); + const results = $('#search-results'); + results.innerHTML = loadingHTML('RESOLVING DOMAIN...'); + + try { + // Try to resolve — if it resolves, domain is taken + const res = await fetch(`${SNS_API}/resolve/${domain}`); + if (res.ok) { + const data = await res.json(); + const owner = data.result || data; + // Domain is taken — show info + await showTakenDomain(domain, typeof owner === 'string' ? owner : owner.result || JSON.stringify(owner)); + } else if (res.status === 404) { + // Domain is available + showAvailableDomain(domain); + } else { + results.innerHTML = errorHTML('Failed to query domain. Try again.'); + } + } catch (err) { + results.innerHTML = errorHTML(`Network error: ${err.message}`); + } +} + +async function showTakenDomain(domain, owner) { + const results = $('#search-results'); + + // Try to get more data + let records = {}; + try { + const recRes = await fetch(`${SNS_API}/record-v2/${domain}/SOL`); + if (recRes.ok) { + const recData = await recRes.json(); + records.sol = recData.result || null; + } + } catch(e) {} + + // Try favourite domain of owner + let favourite = null; + try { + const favRes = await fetch(`${SNS_API}/favourite-domain/${owner}`); + if (favRes.ok) { + const favData = await favRes.json(); + favourite = favData.result || null; + } + } catch(e) {} + + results.innerHTML = ` +
> Look up, register, and manage Solana .sol domains on-chain via Bonfida SNS.
+