diff options
| author | Adam Soltys <[email protected]> | 2026-06-26 13:08:11 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-26 13:08:11 -0700 |
| commit | ff0d637911051b45f54d54ece54aa16387c0fd6f (patch) | |
| tree | 505c1ec0d2acf612ac132cd9f4fb1ec581630afa /src/app.js | |
| parent | 8b8e5a7e27fdb491a11771f9b46f159c87e7d3e3 (diff) | |
| download | coinosv3-ff0d637911051b45f54d54ece54aa16387c0fd6f.tar.gz coinosv3-ff0d637911051b45f54d54ece54aa16387c0fd6f.zip | |
Logo returns to the wallet's Receive page; add paste-from-clipboard buttons
- goHome: when a wallet is open, the logo goes to its Receive page (its tabs)
instead of the create/import screen — so it's always a way back to your wallet.
Only with no wallet open does it show create/import.
- Paste buttons: a clipboard 'Paste' icon on the send address field and the
import (seed/key/xpub) and 'load seed to spend' fields, filling them from the
clipboard. Hidden where the Clipboard read API isn't available.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 71 |
1 files changed, 47 insertions, 24 deletions
@@ -299,6 +299,18 @@ function copyBtn(text, label = t('copy')) { return h('button', { class: 'btn-sm', onClick: () => copy(text) }, label); } +// A small "paste from clipboard" button; apply(text) receives the trimmed text. +// Returns null where the Clipboard read API isn't available (the catch keeps it +// silent if a browser blocks the read). +function pasteBtn(apply) { + if (typeof navigator === 'undefined' || !navigator.clipboard || !navigator.clipboard.readText) return null; + return h('button', { + type: 'button', class: 'btn-sm', title: t('paste'), + onClick: async () => { try { const txt = await navigator.clipboard.readText(); if (txt) apply(txt.trim()); } catch {} }, + html: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/></svg>', + }); +} + // ---------------------------------------------------------------- display unit // Global BTC/sats preference, persisted in localStorage across refreshes and // logouts. Every unit label on the site is clickable to toggle it. @@ -537,21 +549,25 @@ function pickConfirm(words) { } function importPane() { + const ta = h('textarea', { + placeholder: t('importPlaceholder'), + autocapitalize: 'none', + autocomplete: 'off', + spellcheck: 'false', + value: ui.importText, + onInput: (e) => (ui.importText = e.target.value), + }); return h( 'div', { class: 'col' }, h( 'label', { class: 'field' }, - h('span', { class: 'lab' }, t('importLabel')), - h('textarea', { - placeholder: t('importPlaceholder'), - autocapitalize: 'none', - autocomplete: 'off', - spellcheck: 'false', - value: ui.importText, - onInput: (e) => (ui.importText = e.target.value), - }) + h('div', { class: 'row between' }, + h('span', { class: 'lab' }, t('importLabel')), + pasteBtn((text) => { ta.value = text; ui.importText = text; }) + ), + ta ), optionsPanel(), h('button', { class: 'btn-primary btn-block', onClick: () => openWallet(ui.importText) }, t('openWallet')) @@ -1681,14 +1697,15 @@ function goHome() { persistAccounts(); activeId = accounts[0] ? accounts[0].id : null; } - if (ui.screen === 'howItWorks') { - ui.screen = ui.returnScreen === 'wallet' ? 'wallet' : 'unlock'; - } - if (ui.screen === 'wallet') { + // A wallet is open → home is its Receive page (not the create/import screen), + // so the logo is always a way back to your wallet. + if (activeAccount()) { + ui.screen = 'wallet'; ui.tab = wallet.offline ? 'settings' : 'receive'; ui.draft = null; ui.sendResult = null; ui.sendError = ''; + ui.fromWallet = false; } else { ui.screen = 'unlock'; ui.unlockTab = 'create'; @@ -2153,16 +2170,20 @@ function receiveTab() { // account's xpub — you can't load the wrong wallet's seed. function loadSeedCard() { const ls = ui.loadSeed; + const seedTa = h('textarea', { + class: 'mono-input', rows: '3', placeholder: t('seedPlaceholder'), + autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', value: ls.value, + onInput: (e) => { ls.value = e.target.value; }, + }); return h( 'div', { class: 'card col' }, - h('h3', {}, t('loadSeedTitle')), + h('div', { class: 'row between' }, + h('h3', { style: 'margin:0' }, t('loadSeedTitle')), + pasteBtn((text) => { seedTa.value = text; ls.value = text; }) + ), h('p', { class: 'small muted', style: 'margin:0' }, t('loadSeedDesc')), - h('textarea', { - class: 'mono-input', rows: '3', placeholder: t('seedPlaceholder'), - autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', value: ls.value, - onInput: (e) => { ls.value = e.target.value; }, - }), + seedTa, h('input', { type: 'password', class: 'mono-input', placeholder: t('bip39PassphraseOpt'), autocapitalize: 'none', autocomplete: 'off', value: ls.passphrase, @@ -2458,15 +2479,17 @@ function recipientRow(s, r, i) { check.style.display = a ? '' : 'none'; }; + const addrInput = h('input', { + type: 'text', class: 'mono-input grow', placeholder: 'bc1q…', + autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', value: r.address, + onInput: (e) => { r.address = e.target.value; syncCheck(); }, + }); const row = h( 'div', { class: 'col gap6' }, h('div', { class: 'input-group' }, - h('input', { - type: 'text', class: 'mono-input grow', placeholder: 'bc1q…', - autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', value: r.address, - onInput: (e) => { r.address = e.target.value; syncCheck(); }, - }), + addrInput, + pasteBtn((text) => { addrInput.value = text; r.address = text; syncCheck(); }), i === 0 && canScan() && h('button', { type: 'button', class: 'btn-sm', title: t('scanQr'), onClick: scanIntoSend, html: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 8V5a1 1 0 0 1 1-1h3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3"/></svg>', |
