feat: .sol domain registration app - search, reverse lookup, wallet connect

This commit is contained in:
jae 2026-04-05 19:01:53 +00:00
parent 199a318d8d
commit d0d13c3f30
3 changed files with 946 additions and 0 deletions

448
css/soldomains.css Normal file
View file

@ -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);
}

378
js/soldomains.js Normal file
View file

@ -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 = `
<div class="sol-result-card">
<div class="sol-result-header">
<div class="sol-result-domain">${esc(domain)}<span class="sol-ext">.sol</span></div>
<div class="sol-result-status taken">REGISTERED</div>
</div>
<div class="sol-result-body">
<div>
<div class="sol-result-field">
<span class="sol-result-label">OWNER</span>
<div class="sol-result-value">
<a href="${SOLSCAN}${esc(owner)}" target="_blank" rel="noopener">${truncAddr(owner)}</a>
</div>
</div>
${favourite ? `
<div class="sol-result-field">
<span class="sol-result-label">OWNER'S FAVOURITE</span>
<div class="sol-result-value">${esc(favourite)}.sol</div>
</div>` : ''}
</div>
<div>
<div class="sol-result-field">
<span class="sol-result-label">SOLSCAN</span>
<div class="sol-result-value">
<a href="${SOLSCAN}${esc(owner)}" target="_blank" rel="noopener">View on Solscan </a>
</div>
</div>
<div class="sol-result-field">
<span class="sol-result-label">BONFIDA</span>
<div class="sol-result-value">
<a href="${BONFIDA_REG}${esc(domain)}" target="_blank" rel="noopener">View on Bonfida </a>
</div>
</div>
</div>
</div>
</div>
`;
}
function showAvailableDomain(domain) {
const results = $('#search-results');
const len = domain.length;
let priceEstimate = '';
if (len <= 1) priceEstimate = '~750 USDC';
else if (len === 2) priceEstimate = '~700 USDC';
else if (len === 3) priceEstimate = '~640 USDC';
else if (len === 4) priceEstimate = '~160 USDC';
else priceEstimate = '~20 USDC';
results.innerHTML = `
<div class="sol-result-card">
<div class="sol-result-header">
<div class="sol-result-domain">${esc(domain)}<span class="sol-ext">.sol</span></div>
<div class="sol-result-status available">AVAILABLE</div>
</div>
<div class="sol-result-body">
<div>
<div class="sol-result-field">
<span class="sol-result-label">LENGTH</span>
<div class="sol-result-value">${len} character${len !== 1 ? 's' : ''}</div>
</div>
<div class="sol-result-field">
<span class="sol-result-label">ESTIMATED COST</span>
<div class="sol-price">
<span class="sol-price-amount">${priceEstimate.split(' ')[0].replace('~','')}</span>
<span class="sol-price-currency">${priceEstimate.split(' ')[1] || 'USDC'}</span>
<span class="sol-price-usd">(estimated)</span>
</div>
</div>
</div>
<div>
<div class="sol-result-field">
<span class="sol-result-label">REGISTRATION</span>
<div class="sol-result-value">Register via Bonfida's official portal</div>
</div>
<a href="${BONFIDA_REG}${esc(domain)}" target="_blank" rel="noopener" class="sol-register-btn">REGISTER ${esc(domain.toUpperCase())}.SOL </a>
</div>
</div>
</div>
`;
}
// ─── REVERSE LOOKUP ─────────────────────────────────────────
async function doReverse() {
const addr = $('#sol-reverse').value.trim();
if (!addr) return;
const results = $('#reverse-results');
results.innerHTML = loadingHTML('SCANNING WALLET...');
try {
// Get all domains for this wallet
const res = await fetch(`${SNS_API}/domains/${addr}`);
if (!res.ok) {
results.innerHTML = errorHTML('Could not resolve wallet. Check the address.');
return;
}
const data = await res.json();
const domains = data.result || data;
if (!domains || !domains.length) {
results.innerHTML = `<div class="sol-empty">NO .SOL DOMAINS FOUND FOR THIS WALLET</div>`;
return;
}
// Get favourite
let favourite = null;
try {
const favRes = await fetch(`${SNS_API}/favourite-domain/${addr}`);
if (favRes.ok) {
const favData = await favRes.json();
favourite = favData.result || null;
}
} catch(e) {}
results.innerHTML = `
<div class="sol-stats-bar">
<div class="sol-stat">DOMAINS: <span class="sol-stat-value">${domains.length}</span></div>
${favourite ? `<div class="sol-stat">FAVOURITE: <span class="sol-stat-value">${favourite}.sol</span></div>` : ''}
<div class="sol-stat">WALLET: <span class="sol-stat-value">${truncAddr(addr)}</span></div>
</div>
<div class="sol-domains-grid">
${domains.map(d => {
const name = typeof d === 'string' ? d : (d.domain || d);
const isFav = favourite && name === favourite;
return `
<div class="sol-domain-card">
<div class="sol-domain-name">${esc(name)}<span class="sol-ext">.sol</span></div>
${isFav ? '<div class="sol-domain-fav">★ FAVOURITE</div>' : ''}
</div>
`;
}).join('')}
</div>
`;
} catch(err) {
results.innerHTML = errorHTML(`Network error: ${err.message}`);
}
}
// ─── WALLET ─────────────────────────────────────────────────
function initWallet() {
const btn = $('#wallet-btn');
if (!btn) return;
btn.addEventListener('click', toggleWallet);
}
async function toggleWallet() {
if (walletAddress) {
disconnectWallet();
return;
}
// Check for Phantom
if (!window.solana || !window.solana.isPhantom) {
const bar = $('#wallet-status');
if (bar) bar.innerHTML = '<span style="color:rgba(255,50,50,0.8)">PHANTOM WALLET NOT DETECTED — <a href="https://phantom.app" target="_blank" style="color:#a855f7">INSTALL</a></span>';
return;
}
try {
const resp = await window.solana.connect();
walletAddress = resp.publicKey.toString();
updateWalletUI();
} catch(err) {
console.error('Wallet connection failed:', err);
}
}
function disconnectWallet() {
if (window.solana) window.solana.disconnect();
walletAddress = null;
updateWalletUI();
}
function updateWalletUI() {
const status = $('#wallet-status');
const btn = $('#wallet-btn');
if (walletAddress) {
status.className = 'sol-wallet-status connected';
status.innerHTML = `● CONNECTED <span class="sol-wallet-address">${truncAddr(walletAddress)}</span>`;
btn.className = 'sol-wallet-btn disconnect';
btn.textContent = 'DISCONNECT';
} else {
status.className = 'sol-wallet-status';
status.innerHTML = '○ NOT CONNECTED';
btn.className = 'sol-wallet-btn';
btn.textContent = 'CONNECT WALLET';
// Clear my domains
const panel = $('#mydomains-content');
if (panel) panel.innerHTML = `<div class="sol-empty">CONNECT WALLET TO VIEW YOUR DOMAINS</div>`;
}
}
// ─── MY DOMAINS ─────────────────────────────────────────────
async function loadMyDomains() {
if (!walletAddress) return;
const content = $('#mydomains-content');
content.innerHTML = loadingHTML('LOADING YOUR DOMAINS...');
try {
const res = await fetch(`${SNS_API}/domains/${walletAddress}`);
if (!res.ok) {
content.innerHTML = errorHTML('Failed to load domains.');
return;
}
const data = await res.json();
const domains = data.result || data;
// Get favourite
let favourite = null;
try {
const favRes = await fetch(`${SNS_API}/favourite-domain/${walletAddress}`);
if (favRes.ok) {
const favData = await favRes.json();
favourite = favData.result || null;
}
} catch(e) {}
if (!domains || !domains.length) {
content.innerHTML = `
<div class="sol-empty">
NO .SOL DOMAINS FOUND<br>
<a href="${BONFIDA_REG}" target="_blank" rel="noopener" style="color:#a855f7; font-size:0.6rem">Register one on Bonfida </a>
</div>
`;
return;
}
content.innerHTML = `
<div class="sol-stats-bar">
<div class="sol-stat">YOUR DOMAINS: <span class="sol-stat-value">${domains.length}</span></div>
${favourite ? `<div class="sol-stat">FAVOURITE: <span class="sol-stat-value">${favourite}.sol</span></div>` : ''}
</div>
<div class="sol-domains-grid">
${domains.map(d => {
const name = typeof d === 'string' ? d : (d.domain || d);
const isFav = favourite && name === favourite;
return `
<div class="sol-domain-card">
<div class="sol-domain-name">${esc(name)}<span class="sol-ext">.sol</span></div>
${isFav ? '<div class="sol-domain-fav">★ FAVOURITE</div>' : ''}
</div>
`;
}).join('')}
</div>
`;
} catch(err) {
content.innerHTML = errorHTML(`Network error: ${err.message}`);
}
}
// ─── HELPERS ────────────────────────────────────────────────
function truncAddr(a) {
if (!a || a.length < 12) return a;
return a.slice(0, 6) + '...' + a.slice(-4);
}
function esc(s) {
const d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
function loadingHTML(msg) {
return `<div class="sol-loading"><div class="sol-loading-spinner"></div><div class="sol-loading-text">${msg}</div></div>`;
}
function errorHTML(msg) {
return `<div class="sol-error">ERROR // ${msg}</div>`;
}

120
soldomains/index.html Normal file
View file

@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JAESWIFT // .SOL DOMAINS</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/section.css">
<link rel="stylesheet" href="/css/soldomains.css">
<style>
.hidden { display: none !important; }
</style>
</head>
<body>
<div class="scanline-overlay"></div>
<div class="grid-bg"></div>
<nav class="nav-main" id="navbar">
<div class="nav-container">
<a href="/" class="nav-logo">
<span class="logo-bracket">[</span> JAE <span class="logo-bracket">]</span>
</a>
<button class="nav-toggle" id="navToggle" aria-label="Menu">
<span></span><span></span><span></span>
</button>
<ul class="nav-menu" id="navMenu"></ul>
<div class="nav-status">
<span class="nav-clock" id="navClock"></span>
</div>
</div>
</nav>
<div class="breadcrumb">
<a href="/">HOME</a>
<span class="separator">/</span>
<a href="/armoury">ARMOURY</a>
<span class="separator">/</span>
<a href="/armoury/lab.html">LAB</a>
<span class="separator">/</span>
<span class="current">.SOL DOMAINS</span>
</div>
<section class="section-header" style="padding-top: calc(var(--nav-height) + 1.5rem);">
<div class="section-header-label">LAB // SOLANA NAME SERVICE</div>
<h1 class="section-header-title">.SOL DOMAINS</h1>
<p class="section-header-sub">&gt; Look up, register, and manage Solana .sol domains on-chain via Bonfida SNS.</p>
</section>
<div class="sol-container">
<!-- Wallet Bar -->
<div class="sol-wallet-bar">
<div class="sol-wallet-status" id="wallet-status">○ NOT CONNECTED</div>
<button class="sol-wallet-btn" id="wallet-btn">CONNECT WALLET</button>
</div>
<!-- Tabs -->
<div class="sol-tabs">
<button class="sol-tab active" data-tab="search">SEARCH</button>
<button class="sol-tab" data-tab="reverse">REVERSE LOOKUP</button>
<button class="sol-tab" data-tab="mydomains">MY DOMAINS</button>
</div>
<!-- Panel: Search -->
<div class="sol-panel" id="panel-search">
<div class="sol-search-section">
<div class="sol-search-box">
<span class="sol-search-prefix">QUERY //</span>
<input type="text" class="sol-search-input" id="sol-search" placeholder="enter domain name" autocomplete="off" spellcheck="false">
<span class="sol-search-suffix">.sol</span>
<button class="sol-search-btn" id="sol-search-go">RESOLVE</button>
</div>
</div>
<div class="sol-results" id="search-results">
<div class="sol-empty">ENTER A .SOL DOMAIN NAME TO CHECK AVAILABILITY AND OWNERSHIP</div>
</div>
</div>
<!-- Panel: Reverse Lookup -->
<div class="sol-panel hidden" id="panel-reverse">
<div class="sol-search-section">
<div class="sol-reverse-box">
<span class="sol-search-prefix">WALLET //</span>
<input type="text" class="sol-reverse-input" id="sol-reverse" placeholder="paste Solana wallet address" autocomplete="off" spellcheck="false">
<button class="sol-search-btn" id="sol-reverse-go">SCAN</button>
</div>
</div>
<div class="sol-results" id="reverse-results">
<div class="sol-empty">PASTE A SOLANA WALLET ADDRESS TO FIND ALL ASSOCIATED .SOL DOMAINS</div>
</div>
</div>
<!-- Panel: My Domains -->
<div class="sol-panel hidden" id="panel-mydomains">
<div class="sol-results" id="mydomains-content">
<div class="sol-empty">CONNECT WALLET TO VIEW YOUR DOMAINS</div>
</div>
</div>
</div>
<footer class="footer">
<div class="footer-container">
<div class="footer-left">
<span class="footer-logo">[JAE]</span>
<span class="footer-copy">&copy; 2026 JAESWIFT.XYZ</span>
</div>
<div class="footer-right">
<span class="footer-signal">SIGNAL ████<span class="signal-flicker"></span></span>
</div>
</div>
</footer>
<script src="/js/nav.js"></script>
<script src="/js/clock.js"></script>
<script src="/js/soldomains.js"></script>
</body>
</html>