diff --git a/src/main.js b/src/main.js index b84db1d..2aa9155 100644 --- a/src/main.js +++ b/src/main.js @@ -4,7 +4,8 @@ import { encodeMetadata, parseMetadataJSON } from './adapters/metadata.js'; import { MetadataIndex, deriveProtocol } from './adapters/metadata-index.js'; import { LocalOrdAPI } from './adapters/local-ord-api.js'; import { mineLab, setMiner, minerStatus } from './miner.js'; -import { TestWallet } from './adapters/wallet.js'; +import { TestWallet, ExtensionWallet, loadWalletBackup } from './adapters/wallet.js'; +import { inscribeWithProvider } from './adapters/extension-inscription.js'; import { LocalRPC } from './adapters/chain.js'; import { InscriptionAdapter, MAX_BYTES, MIME_TYPES } from './adapters/inscription.js'; import { preview, clearPreviews } from './preview.js'; @@ -16,6 +17,7 @@ const short = id => `${id.slice(0, 12)}…${id.slice(-8)}`; const THEME_KEY = 'super-testnet-theme'; let fileBytes = null, fileLoading = false, fileRevision = 0; let lab, busy = false, reviewed = null, failed = false; +let inscriboorProvider = null; const message = (text, error = false) => { $('status').textContent = text; $('status').classList.toggle('error', error); }; const el = (tag, text, className) => { const node = document.createElement(tag); if (text !== undefined) node.textContent = text; if (className) node.className = className; return node; }; @@ -166,8 +168,9 @@ function render() { const wallet = lab?.wallet, chain = lab?.chain; const disabled = busy || failed; $('empty-wallet').hidden = !!wallet; $('active-wallet').hidden = !wallet; - $('wallet-state').textContent = wallet ? 'LOCAL / TEST ONLY' : 'NOT CREATED'; - $('create-wallet').disabled = disabled; $('load-wallet').disabled = disabled; + $('wallet-state').textContent = wallet ? (wallet.kind === 'extension-wallet' ? 'INSCRIBOOR / TEST' : 'LOCAL / TEST ONLY') : 'NOT CREATED'; + $('create-wallet').disabled = disabled; $('connect-inscriboor').disabled = disabled || !inscriboorProvider; $('load-wallet').disabled = disabled; + $('inscriboor-status').textContent = inscriboorProvider ? 'Inscriboor detected - extension key stays outside this page.' : 'Inscriboor not detected. Reload the extension or use the local test wallet.'; $('mine').disabled = disabled || !wallet; $('miner-interval').disabled = disabled || !wallet; $('miner-interval').value = String(lab?.miner?.interval ?? 0); @@ -184,6 +187,8 @@ function render() { $('balance').textContent = btc(chain.balance(wallet.script)); const reserved = chain.coins(wallet.script).filter(c => c.inscription).reduce((n,c) => n+c.value,0); $('reserved').textContent = `${reserved.toLocaleString()} sats protected in collectibles · balance includes pending outputs`; + $('export-wallet').textContent = wallet.kind === 'extension-wallet' ? 'Save wallet descriptor' : 'Save test wallet'; + $('signing-note').textContent = wallet.kind === 'extension-wallet' ? 'The next button opens Inscriboor for approval. The page never receives the extension key.' : 'The next button signs locally with this browser test wallet and submits commit + reveal.'; } const items = wallet ? new InscriptionAdapter(chain, wallet).list() : []; const selectedParent = $('parent').value; @@ -232,6 +237,17 @@ $('create-wallet').onclick = () => mutate(next => { if (next.wallet) throw new Error('A wallet already exists in this browser. Reload to use it.'); next.wallet = TestWallet.create(); }, 'Test wallet created. Next: mine a block to get 50 fake BTC.'); +$('connect-inscriboor').onclick = async () => { + try { + const provider = inscriboorProvider ?? window.inscriboor; + if (!provider?.request) throw new Error('Inscriboor extension not detected.'); + const account = (await provider.request({ method:'bitcoin_getAccounts' }))?.[0]; + await mutate(next => { + if (next.wallet) throw new Error('A wallet already exists in this browser. Reset or restore a different lab first.'); + next.wallet = new ExtensionWallet(account, provider); + }, 'Inscriboor connected. Mine a block, prepare an inscription, then approve it in the extension.'); + } catch (error) { message(error.message, true); } +}; const mine = automatic => mutate(next => mineLab(next, automatic), 'Block mined. Pending transactions moved from the mempool into your newest block.'); $('mine').onclick = () => mine(false); $('miner-interval').onchange = () => { @@ -289,9 +305,14 @@ $('cancel-review').onclick = closeReview; $('sign').onclick = () => { const consent = reviewed; if (!consent) return; - mutate(next => { + mutate(async next => { if (next.wallet?.address !== consent.address) throw new Error('Wallet changed. Review again before signing.'); - new InscriptionAdapter(next.chain, next.wallet).inscribe(consent.payload, consent.parent, consent.metadata, consent.options); + if (next.wallet.kind === 'extension-wallet') { + const provider = inscriboorProvider ?? window.inscriboor; + if (!provider?.request) throw new Error('Reconnect the Inscriboor extension before signing.'); + next.wallet.attach(provider); + await inscribeWithProvider(next.chain, next.wallet, consent.payload, consent.parent, consent.metadata, consent.options); + } else new InscriptionAdapter(next.chain, next.wallet).inscribe(consent.payload, consent.parent, consent.metadata, consent.options); }, 'Inscription submitted. Its commit + reveal are now visible in the mempool; mine a block to confirm them.', resetComposer); }; $('export-wallet').onclick = () => download('super-testnet-wallet.json', JSON.stringify(lab.wallet.backup(), null, 2)); @@ -306,7 +327,7 @@ $('load-wallet').onchange = async event => { try { const raw = await file.text(), data = JSON.parse(raw); const restore = data.kind === 'test-lab'; - const loaded = restore ? decodeLab(raw) : TestWallet.load(data); + const loaded = restore ? decodeLab(raw) : loadWalletBackup(data); if (lab.wallet && !confirm(restore ? 'Replace this entire local lab with the backup? Save your current lab first.' : 'Switch the active test wallet? The local chain stays here. Save your current wallet first.')) return; await mutate(next => { if (restore) Object.assign(next, loaded); else next.wallet = loaded; @@ -382,9 +403,11 @@ window.addEventListener('storage', event => { closeReview(); load(); if (!failed) message('Local lab changed in another tab; refreshed.'); }); function load() { - try { lab = readLab(); failed = false; if (lab.wallet) message('Test wallet loaded from this browser. Your local chain is ready.'); } + try { lab = readLab(); failed = false; if (lab.wallet?.kind === 'extension-wallet' && inscriboorProvider) lab.wallet.attach(inscriboorProvider); if (lab.wallet) message(lab.wallet.kind === 'extension-wallet' ? 'Inscriboor wallet descriptor loaded. Reconnect the extension to sign.' : 'Test wallet loaded from this browser. Your local chain is ready.'); } catch (error) { failed = true; message(`Cannot load saved lab: ${error.message} Export it for recovery, then reset this local lab.`, true); } render(); } +window.addEventListener('inscriboor#initialized', () => { inscriboorProvider = window.inscriboor; if (lab?.wallet?.kind === 'extension-wallet') lab.wallet.attach(inscriboorProvider); render(); }); +inscriboorProvider = window.inscriboor?.isInscriboor ? window.inscriboor : null; initTheme(); load(); \ No newline at end of file