<meta charset="utf-8" />
<meta lang="en" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <meta name="xel-theme" content="https://cdn.jsdelivr.net/npm/xel@0.28.13/themes/adwaita-dark.css" />
- <meta name="xel-accent-color" content="blue" />
- <meta name="xel-icons" content="https://unpkg.com/bootstrap-icons@latest/bootstrap-icons.svg" />
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<script>
- let Account, Bip44Wallet, Blake2bWallet, SendBlock, ReceiveBlock, ChangeBlock, rpc, Rolodex, Xel
+ let libnemo, css
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
}
})
- async function load () {
+ window.addEventListener('load', async (event) => {
try {
document.querySelector(location.hash).classList.add('show')
} catch (err) {
location.hash = '#transact'
}
- await loadUi()
await loadNemo()
await loadData()
+ await loadUi()
console.log('done')
- }
-
- async function loadUi () {
- console.log('loading xel')
- Xel = await import('https://cdn.jsdelivr.net/npm/xel@0.28.13')
- await Xel.whenThemeReady
- setTimeout(() => {
- document.getElementsByTagName('body')[0].style.visibility = 'visible'
- }, 250)
- console.log('loaded xel')
- }
+ })
async function loadNemo () {
console.log('loading libnemo')
- await ({ Account, Bip44Wallet, Blake2bWallet, SendBlock, ReceiveBlock, ChangeBlock, Node: rpc, Rolodex } = await import('https://cdn.jsdelivr.net/npm/libnemo@0.0.12'))
+ libnemo = await import('https://cdn.jsdelivr.net/npm/libnemo@0.0.12')
console.log('loaded libnemo')
}
console.log('loaded data')
}
+ async function loadUi () {
+ console.log('loading ui')
+ setTimeout(() => {
+ document.getElementsByTagName('body')[0].style.visibility = 'visible'
+ }, 250)
+ console.log('loaded ui')
+ }
+
function switchPage (evt) {
console.log(evt)
const oldHash = new URL(evt.oldURL)?.hash
}
}
+ const notify = {
+ _show: (msg, color) => {
+ const container = document.querySelector('#notifications')
+ const notification = document.createElement('dialog')
+
+ notification.innerText = msg
+ notification.style.color = color ?? notification.style.color
+
+ container.appendChild(notification)
+ notification.show()
+ },
+ info: (msg) => notify._show(`ℹ️ ${msg}`),
+ ok: (msg) => notify._show(`☑️ ${msg}`, 'Cyan'),
+ error: (msg) => notify._show(`⚠️ ${msg}`, 'Gold')
+ }
+
function addToStorage (key, value) {
if (typeof value !== 'string') {
throw new TypeError(`Cannot add ${typeof value} to storage`)
function getWalletClass (algorithm) {
switch (algorithm) {
case 'blake2b': {
- return Blake2bWallet
+ return libnemo.Blake2bWallet
}
case 'bip44':
default: {
- return Bip44Wallet
+ return libnemo.Bip44Wallet
}
}
}
return
}
const index = form.querySelector('#account-new-index input')?.value
+ const password = form.querySelector('#account-new-password input')?.value
let account
try {
+ console.log('locking')
+ console.log(await wallet.unlock(password))
account = await wallet.accounts(index)
+ console.log('locking')
+ console.log(await wallet.lock(password))
const accountData = {
id: index,
name: name || index
}
const walletElements = []
for (const wallet of wallets) {
- const walletElement = document.createElement('x-label')
+ const walletElement = document.createElement('label')
await wallet.unlock('password')
walletElement.innerText = `${wallet.name}: ${wallet.mnemonic}`
await wallet.lock('password')
await updateWalletSelect()
notify.info(`Session storage cleared`)
}
-
- const notify = {
- _show: (msg, color) => {
- const container = document.querySelector('#notifications')
- const notification = document.createElement('x-card')
-
- notification.innerText = msg
- notification.style.color = color ?? notification.style.color
-
- notification.addEventListener('click', notification.remove)
- container.appendChild(notification)
- },
- info: (msg) => notify._show(`ℹ️ ${msg}`),
- ok: (msg) => notify._show(`☑️ ${msg}`, 'Cyan'),
- error: (msg) => notify._show(`⚠️ ${msg}`, 'Gold')
- }
</script>
<style>
+ html {
+ --color-blue: #209CE9;
+ --color-dark: #20204C;
+ --color-light: #F4FAFF;
+ --color-gray: #676686;
+ --color-black: #070707;
+ --color-white: #FFFFFF;
+ --text-body: 'Open Sans';
+ --text-heading: 'Montserrat';
+ }
body {
- background-color: slategrey;
- color: #209CE9;
- fill: #209CE9;
+ background-color: var(--color-dark);
+ color: var(--color-light);
+ fill: var(--color-blue);
+ font-family: var(--text-body);
+ margin: 0;
}
+ h1, h2, h3, h4, h5, h6 {
+ font-family: var(--text-heading);
+ }
.flex {
display: flex;
}
.flex.x-center,
.flex.x-right,
.flex.x-full {
+ flex: 1 0 0;
flex-direction: row;
justify-content: center;
}
.flex.y-mid,
.flex.y-bottom,
.flex.full {
+ flex: 1 0 0;
flex-direction: column;
justify-content: center;
}
width: 100%;
z-index: 100;
}
- #notifications > x-card {
+ #notifications > dialog {
align-items: center;
display: flex;
margin: 1px 0px;
dialog {
padding: 1rem !important;
}
- label:has(+ input:required)::after {
+ label:has(+ :required)::after {
color: red;
content: '*';
}
- input:required:invalid {
+ :required:invalid {
border: 1px solid red;
}
- x-button[skin="nav"].menubtn {
- justify-content: flex-start;
- padding: 0 12px 0 23px;
- width: 100%;
- }
nav {
align-items: flex-end;
bottom: 0;
padding: 1rem;
}
#xno {
+ fill: var(--color-blue);
flex: 1.5 0 auto;
}
nav > a > * {
aspect-ratio: 1;
+ fill: var(--color-blue);
height: 100%;
max-height: 10vh;
max-width: 10vh;
</style>
</head>
-<body onload="load()" onhashchange="switchPage(event)" style="visibility:hidden;">
+<body onhashchange="switchPage(event)" style="visibility:hidden;">
<div id="notifications"></div>
<header>
<div id="wallets" class="grid-x">
<label for="wallet" class="grid x-2">Wallet</label>
<select id="wallet" class="grid x-10" onchange="updateWalletSelect()">
- <option disabled value>Choose...</option>
+ <option disabled selected value>Choose...</option>
<hr />
<option value="_new">New</option>
<option value="_import">Import</option>
<div class="flex y-stretch">
<div id="wallet-new-name" class="flex x-left x-mid">
<label for="wallet-new-name-input">Name</label>
- <input id="wallet-new-name-input" name="wallet-new-name" autocomplete="off" minlength="1" maxlength="80" pattern="[^_].*" placeholder="My Wallet" required />
+ <input id="wallet-new-name-input" name="wallet-new-name" autocomplete="off" maxlength="80" pattern="[^_].*" placeholder="My Wallet" />
</div>
<div id="wallet-new-algorithm" class="flex x-left x-mid">
<label for="wallet-new-algorithm-select">Algorithm</label>
<!-- Import Wallet Form -->
<dialog id="wallet-import">
- <div class="flex vertical">
- <x-box id="wallet-import-name">
- <x-label>Name</x-label>
- <x-input type="text"></x-input>
- </x-box>
- <x-box id="wallet-import-algorithm">
- <x-radios required onchange="updateWalletForm('#wallet-import')">
- <x-radio value="bip44"><x-label>BIP-44</x-label></x-radio>
- <x-radio value="blake2b"><x-label>BLAKE2b</x-label></x-radio>
- </x-radios>
- </x-box>
- <x-box id="wallet-import-type" class="hide">
- <x-select onchange="updateWalletForm('#wallet-import')">
- <x-menu>
- <x-menuitem value="mnemonic"><x-label>Import mnemonic</x-label></x-menuitem>
- <x-menuitem value="seed"><x-label>Import seed</x-label></x-menuitem>
- </x-menu>
- </x-select>
- </x-box>
- <x-box id="wallet-import-value" class="hide">
- <x-label style="text-transform:capitalize"></x-label>
- <x-input type="password"></x-input>
- </x-box>
- <x-box id="wallet-import-salt" class="hide">
- <x-label>Salt</x-label>
- <x-input type="password">Optional</x-input>
- </x-box>
- <x-box id="wallet-import-password">
- <x-label>Lock</x-label>
- <x-input type="password" required><x-label>Password</x-label></x-input>
- </x-box>
- <x-box>
- <x-buttons>
- <x-button onclick="formCancel('#wallet-import')">
- Cancel
- </x-button>
- <x-button onclick="importWallet().then(w => { console.log(w);showWallets() })">
- OK
- </x-button>
- </x-buttons>
- </x-box>
+ <div class="flex y-stretch">
+ <div id="wallet-import-name" class="flex x-left x-mid">
+ <label for="wallet-import-name-input">Name</label>
+ <input id="wallet-import-name-input" name="wallet-import-name" autocomplete="off" maxlength="80" pattern="[^_].*" placeholder="My Wallet" />
+ </div>
+ <div id="wallet-import-algorithm" class="flex x-left x-mid">
+ <label for="wallet-import-algorithm-select">Algorithm</label>
+ <select id="wallet-import-algorithm-select" name="wallet-import-algorithm" autocomplete="off" onchange="updateWalletForm('#wallet-import')" required>
+ <option disabled selected value>Choose...</option>
+ <option value="bip44">BIP-44</option>
+ <option value="blake2b">BLAKE2b</option>
+ </select>
+ </div>
+ <div id="wallet-import-type" class="flex x-left x-mid hide">
+ <label for="wallet-import-type-select">Type</label>
+ <select id="wallet-import-type-select" name="wallet-import-type" autocomplete="off" onchange="updateWalletForm('#wallet-import')" required>
+ <option disabled selected value>Choose...</option>
+ <option value="mnemonic">Import mnemonic</option>
+ <option value="seed">Import seed</option>
+ </select>
+ </div>
+ <div id="wallet-import-value" class="flex x-left x-mid hide">
+ <label for="wallet-import-value-input" style="text-transform:capitalize"></label>
+ <input id="wallet-import-value-input" name="wallet-import-value" type="password" autocomplete="off" placeholder="Password" required />
+ </div>
+ <div id="wallet-import-salt" class="flex x-left x-mid hide">
+ <label for="wallet-import-salt-input">Salt</label>
+ <input id="wallet-import-salt-input" name="wallet-import-salt" type="password" autocomplete="off" placeholder="Optional" />
+ </div>
+ <div id="wallet-import-password" class="flex x-left x-mid">
+ <label for="wallet-import-password-input">Lock</label>
+ <input id="wallet-import-password-input" name="wallet-import-password" type="password" autocomplete="off" placeholder="Password" required />
+ </div>
+ <hr />
+ <div class="grid-x">
+ <div class="grid x-4"></div>
+ <button class="grid x-4" onclick="formCancel('#wallet-import')">
+ Cancel
+ </button>
+ <button class="grid x-4" onclick="importWallet().then(w => { console.log(w);showWallets() })">
+ OK
+ </button>
+ </div>
</div>
</dialog>
<!-- Account Select -->
- <x-box id="accounts" class="grid-x">
+ <div id="accounts" class="grid-x">
<label for="account" class="grid x-2">Account</label>
<select id="account" class="grid x-10" disabled onchange="updateWalletSelect()">
- <option value="" disabled>Choose...</option>
+ <option disabled selected value>Choose...</option>
<hr />
<option value="_new">New</option>
<hr />
</select>
- </x-box>
+ </div>
<!-- New Account Form -->
<dialog id="account-new">
- <x-box vertical>
- <x-box id="account-new-name">
- <x-label>Name</x-label>
- <x-input type="text"></x-input>
- </x-box>
- <x-box id="account-new-index">
- <x-label>Index (optional)</x-label>
- <x-numberinput min="0" max="2147483647"></x-numberinput>
- </x-box>
- <x-box id="account-new-password">
- <x-label>Unlock</x-label>
- <x-input type="password" required><x-label>Password</x-label></x-input>
- </x-box>
- <x-buttons>
- <x-button onclick="document.querySelector('#account-new').close()">
+ <div class="flex y-stretch">
+ <div id="account-new-name" class="flex x-left x-mid">
+ <label for="account-new-name-input">Name</label>
+ <input id="account-new-name-input" name="account-new-name" autocomplete="off" maxlength="80" pattern="[^_].*" placeholder="My Account" />
+ </div>
+ <div id="account-new-index" class="flex x-left x-mid">
+ <label for="account-new-index-input">Index</label>
+ <input id="account-new-index-input" name="account-new-index" type="number" autocomplete="off" min="0" max="2147483647" />
+ </div>
+ <div id="account-new-password" class="flex x-left x-mid">
+ <label for="account-new-password-input">Unlock</label>
+ <input id="account-new-password-input" name="account-new-password" autocomplete="off" type="password" placeholder="Password" required />
+ </div>
+ <div class="grid-x">
+ <div class="grid x-4"></div>
+ <button class="grid x-4" onclick="formCancel('#account-new')">
Cancel
- </x-button>
- <x-button onclick="createAccount().then(w => { console.log(w);showWallets() })">
+ </button>
+ <button class="grid x-4" onclick="createAccount().then(w => { console.log(w);showWallets() })">
OK
- </x-button>
- </x-buttons>
- </x-box>
+ </button>
+ </div>
+ </div>
</dialog>
</header>
<!-- Exchange Page -->
- <x-box id="exchange" class="page">
+ <div id="exchange" class="page">
<h1>Exchange</h1>
- </x-box>
+ </div>
<!-- History Page -->
- <x-box id="history" class="page">
+ <div id="history" class="page">
<h1>History</h1>
- </x-box>
+ </div>
<!-- Transact Page (default) -->
- <x-box id="transact" class="page">
+ <div id="transact" class="page">
<h1>Transact</h1>
- <x-card id="walletCard">
- </x-card>
- </x-box>
+ <div id="walletCard">
+ </div>
+ </div>
<!-- Rolodex Page -->
- <x-box id="rolodex" class="page">
+ <div id="rolodex" class="page">
<h1>Rolodex</h1>
- </x-box>
+ </div>
<!-- Settings Page -->
- <x-box id="settings" class="page">
+ <div id="settings" class="page">
<h1>Settings</h1>
- <x-button onclick="clearStorage()">
- <x-label>Clear Storage</x-label>
- </x-button>
- </x-box>
+ <button onclick="clearStorage()">
+ Clear Storage
+ </button>
+ </div>
</main>
<!-- Nav Dock -->
<nav>
<a href="#exchange">
- <x-icon href="#currency-exchange"></x-icon>
+ <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" fill="currentColor" class="bi bi-currency-exchange" viewBox="0 0 16 16">
+ <path d="M0 5a5 5 0 0 0 4.027 4.905 6.5 6.5 0 0 1 .544-2.073C3.695 7.536 3.132 6.864 3 5.91h-.5v-.426h.466V5.05q-.001-.07.004-.135H2.5v-.427h.511C3.236 3.24 4.213 2.5 5.681 2.5c.316 0 .59.031.819.085v.733a3.5 3.5 0 0 0-.815-.082c-.919 0-1.538.466-1.734 1.252h1.917v.427h-1.98q-.004.07-.003.147v.422h1.983v.427H3.93c.118.602.468 1.03 1.005 1.229a6.5 6.5 0 0 1 4.97-3.113A5.002 5.002 0 0 0 0 5m16 5.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0m-7.75 1.322c.069.835.746 1.485 1.964 1.562V14h.54v-.62c1.259-.086 1.996-.74 1.996-1.69 0-.865-.563-1.31-1.57-1.54l-.426-.1V8.374c.54.06.884.347.966.745h.948c-.07-.804-.779-1.433-1.914-1.502V7h-.54v.629c-1.076.103-1.808.732-1.808 1.622 0 .787.544 1.288 1.45 1.493l.358.085v1.78c-.554-.08-.92-.376-1.003-.787zm1.96-1.895c-.532-.12-.82-.364-.82-.732 0-.41.311-.719.824-.809v1.54h-.005zm.622 1.044c.645.145.943.38.943.796 0 .474-.37.8-1.02.86v-1.674z" />
+ </svg>
</a>
<a href="#history">
- <x-icon href="#book-half"></x-icon>
+ <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" fill="currentColor" class="bi bi-book-half" viewBox="0 0 16 16">
+ <path d="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783" />
+ </svg>
</a>
<a href="#transact" id="xno">
- <svg width="0px" height="0px" viewBox="0 0 1080 1080" fill="none">
- <circle cx="540" cy="540" r="540" fill="currentcolor" />
- <path
- d="M792.911 881H740.396L541.099 570.561L338.761 881H286.68L513.452 529.3L306.882 206.222H360.42L541.95 490.393L727.322 206.222H777.555L568.762 528.379L792.911 881Z"
- fill="white" />
- <path
- d="M336.487 508.737H744.807V547.116H336.487V508.737ZM336.487 623.872H744.824V662.251H336.47L336.487 623.872Z"
- fill="white" />
+ <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 1080 1080">
+ <circle cx="540" cy="540" r="540" />
+ <path d="M792.911 881H740.396L541.099 570.561L338.761 881H286.68L513.452 529.3L306.882 206.222H360.42L541.95 490.393L727.322 206.222H777.555L568.762 528.379L792.911 881Z" fill="white" />
+ <path d="M336.487 508.737H744.807V547.116H336.487V508.737ZM336.487 623.872H744.824V662.251H336.47L336.487 623.872Z" fill="white" />
</svg>
</a>
<a href="#rolodex">
- <x-icon href="#person-rolodex"></x-icon>
+ <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" fill="currentColor" class="bi bi-person-rolodex" viewBox="0 0 16 16">
+ <path d="M8 9.05a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5" />
+ <path d="M1 1a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h.5a.5.5 0 0 0 .5-.5.5.5 0 0 1 1 0 .5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5.5.5 0 0 1 1 0 .5.5 0 0 0 .5.5h.5a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H6.707L6 1.293A1 1 0 0 0 5.293 1zm0 1h4.293L6 2.707A1 1 0 0 0 6.707 3H15v10h-.085a1.5 1.5 0 0 0-2.4-.63C11.885 11.223 10.554 10 8 10c-2.555 0-3.886 1.224-4.514 2.37a1.5 1.5 0 0 0-2.4.63H1z" />
+ </svg>
</a>
<a href="#settings">
- <x-icon href="#gear"></x-icon>
+ <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" fill="currentColor" class="bi bi-gear" viewBox="0 0 16 16">
+ <path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0" />
+ <path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115z" />
+ </svg>
</a>
</nav>
</body>