Start building!
` }; function loadTemplate(name) { editor.value = TEMPLATES[name] || ''; editor.dispatchEvent(new Event('input')); } // Connect wallet document.getElementById('connect-btn').onclick = async () => { if (!window.ethereum) { alert('Install MetaMask'); return; } try { await window.ethereum.request({ method: 'eth_requestAccounts' }); provider = new ethers.BrowserProvider(window.ethereum); signer = await provider.getSigner(); const addr = await signer.getAddress(); document.getElementById('connect-btn').textContent = addr.slice(0, 6) + '...' + addr.slice(-4); document.getElementById('connect-btn').disabled = true; nft = new ethers.Contract(NETWORK.pulseBytesAddress, PULSEBYTES_ABI, signer); store = new ethers.Contract(NETWORK.byteStoreAddress, BYTESTORE_ABI, signer); // Get owned tokens const tokens = await nft.tokensOfOwner(addr); const select = document.getElementById('byte-select'); if (tokens.length === 0) { document.getElementById('wallet-label').textContent = 'No Bytes owned. Mint first!'; } else { select.style.display = 'inline'; select.innerHTML = tokens.map(t => '').join(''); selectedByte = Number(tokens[0]); select.onchange = () => { selectedByte = Number(select.value); loadExistingPage(); }; saveBtn.disabled = false; loadExistingPage(); } } catch (e) { alert(e.message); } }; // Load existing page async function loadExistingPage() { if (!store || selectedByte === null) return; try { const pageKey = ethers.keccak256(ethers.toUtf8Bytes('page')); const hasData = await store.hasData(selectedByte, pageKey); if (hasData) { const html = await store.getString(selectedByte, pageKey); editor.value = html; editor.dispatchEvent(new Event('input')); saveStatus.textContent = 'Loaded existing page (' + html.length + ' bytes)'; saveStatus.className = 'save-status ok'; } else { saveStatus.textContent = 'No page yet — pick a template!'; saveStatus.className = 'save-status'; } } catch (e) {} } // Save on-chain saveBtn.onclick = async () => { if (!store || selectedByte === null) return; const html = editor.value; const bytes = new TextEncoder().encode(html); if (bytes.length > 32768) { saveStatus.textContent = 'Too large! Max 32KB.'; saveStatus.className = 'save-status err'; return; } saveBtn.disabled = true; saveStatus.textContent = 'Storing on-chain... confirm in MetaMask'; saveStatus.className = 'save-status'; try { const pageKey = ethers.keccak256(ethers.toUtf8Bytes('page')); // Clear old data const hasOld = await store.hasData(selectedByte, pageKey); if (hasOld) { saveStatus.textContent = 'Clearing old page...'; await (await store.clear(selectedByte, pageKey)).wait(); } // Store in chunks const data = ethers.toUtf8Bytes(html); const CHUNK = 8000; for (let i = 0; i < data.length; i += CHUNK) { const chunk = data.slice(i, Math.min(i + CHUNK, data.length)); const chunkNum = Math.floor(i / CHUNK) + 1; const totalChunks = Math.ceil(data.length / CHUNK); saveStatus.textContent = 'Storing chunk ' + chunkNum + '/' + totalChunks + '... confirm in MetaMask'; if (i === 0) { await (await store.storeWithType(selectedByte, pageKey, chunk, 'text/html')).wait(); } else { await (await store.store(selectedByte, pageKey, chunk)).wait(); } } saveStatus.textContent = 'Saved! ' + html.length + ' bytes on PulseChain. View at /page/' + selectedByte; saveStatus.className = 'save-status ok'; } catch (e) { saveStatus.textContent = 'Failed: ' + (e.reason || e.message); saveStatus.className = 'save-status err'; } saveBtn.disabled = false; }; // Load profile template by default loadTemplate('profile');