hd-wallet-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -0
- package/package.json +59 -0
- package/src/address-derivation.js +476 -0
- package/src/app.js +3302 -0
- package/src/blockchain-trust.js +699 -0
- package/src/constants.js +87 -0
- package/src/lib.js +63 -0
- package/src/template.js +348 -0
- package/src/trust-ui.js +745 -0
- package/src/wallet-storage.js +696 -0
- package/styles/main.css +4521 -0
package/src/constants.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet Constants
|
|
3
|
+
*
|
|
4
|
+
* Coin type configurations, explorer URLs, and derivation path helpers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// Crypto Configuration
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
export const cryptoConfig = {
|
|
12
|
+
btc: {
|
|
13
|
+
name: 'Bitcoin',
|
|
14
|
+
symbol: 'BTC',
|
|
15
|
+
coinType: 0,
|
|
16
|
+
explorer: 'https://blockstream.info/address/',
|
|
17
|
+
balanceApi: 'https://blockstream.info/api/address/',
|
|
18
|
+
formatBalance: (satoshis) => `${(satoshis / 100000000).toFixed(8)} BTC`,
|
|
19
|
+
},
|
|
20
|
+
eth: {
|
|
21
|
+
name: 'Ethereum',
|
|
22
|
+
symbol: 'ETH',
|
|
23
|
+
coinType: 60,
|
|
24
|
+
explorer: 'https://etherscan.io/address/',
|
|
25
|
+
balanceApi: null,
|
|
26
|
+
formatBalance: (wei) => `${(parseFloat(wei) / 1e18).toFixed(6)} ETH`,
|
|
27
|
+
},
|
|
28
|
+
sol: {
|
|
29
|
+
name: 'Solana',
|
|
30
|
+
symbol: 'SOL',
|
|
31
|
+
coinType: 501,
|
|
32
|
+
explorer: 'https://solscan.io/account/',
|
|
33
|
+
balanceApi: null,
|
|
34
|
+
formatBalance: (lamports) => `${(lamports / 1e9).toFixed(4)} SOL`,
|
|
35
|
+
},
|
|
36
|
+
/* Commented out — BTC/ETH/SOL only for now
|
|
37
|
+
ltc: { name: 'Litecoin', symbol: 'LTC', coinType: 2, explorer: 'https://blockchair.com/litecoin/address/', balanceApi: null, formatBalance: (lits) => `${(lits / 100000000).toFixed(8)} LTC` },
|
|
38
|
+
bch: { name: 'Bitcoin Cash', symbol: 'BCH', coinType: 145, explorer: 'https://blockchair.com/bitcoin-cash/address/', balanceApi: null, formatBalance: (sats) => `${(sats / 100000000).toFixed(8)} BCH` },
|
|
39
|
+
doge: { name: 'Dogecoin', symbol: 'DOGE', coinType: 3, explorer: 'https://dogechain.info/address/', balanceApi: null, formatBalance: (sats) => `${(sats / 100000000).toFixed(4)} DOGE` },
|
|
40
|
+
atom: { name: 'Cosmos', symbol: 'ATOM', coinType: 118, explorer: 'https://www.mintscan.io/cosmos/address/', balanceApi: null, formatBalance: (uatom) => `${(uatom / 1e6).toFixed(6)} ATOM` },
|
|
41
|
+
algo: { name: 'Algorand', symbol: 'ALGO', coinType: 330, explorer: 'https://algoexplorer.io/address/', balanceApi: null, formatBalance: (microalgos) => `${(microalgos / 1e6).toFixed(6)} ALGO` },
|
|
42
|
+
dot: { name: 'Polkadot', symbol: 'DOT', coinType: 354, explorer: 'https://polkascan.io/polkadot/account/', balanceApi: null, formatBalance: (planks) => `${(planks / 1e10).toFixed(4)} DOT` },
|
|
43
|
+
ada: { name: 'Cardano', symbol: 'ADA', coinType: 1815, explorer: 'https://cardanoscan.io/address/', balanceApi: null, formatBalance: (lovelace) => `${(lovelace / 1e6).toFixed(6)} ADA` },
|
|
44
|
+
xrp: { name: 'Ripple', symbol: 'XRP', coinType: 144, explorer: 'https://xrpscan.com/account/', balanceApi: null, formatBalance: (drops) => `${(drops / 1e6).toFixed(6)} XRP` },
|
|
45
|
+
*/
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Coin type to config mapping
|
|
49
|
+
export const coinTypeToConfig = Object.fromEntries(
|
|
50
|
+
Object.entries(cryptoConfig).map(([key, config]) => [config.coinType, { key, ...config }])
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// =============================================================================
|
|
54
|
+
// Derivation Path Helpers
|
|
55
|
+
// =============================================================================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build a BIP44 signing path: m/44'/coinType'/account'/0/index
|
|
59
|
+
* @param {string|number} coinType - Coin type number
|
|
60
|
+
* @param {string|number} account - Account index
|
|
61
|
+
* @param {string|number} index - Address index
|
|
62
|
+
* @returns {string} BIP44 derivation path
|
|
63
|
+
*/
|
|
64
|
+
export function buildSigningPath(coinType, account = '0', index = '0') {
|
|
65
|
+
return `m/44'/${coinType}'/${account}'/0/${index}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Build an encryption key path: m/44'/coinType'/account'/1/index
|
|
70
|
+
* Uses change=1 to separate encryption keys from signing keys
|
|
71
|
+
* @param {string|number} coinType - Coin type number
|
|
72
|
+
* @param {string|number} account - Account index
|
|
73
|
+
* @param {string|number} index - Address index
|
|
74
|
+
* @returns {string} Derivation path for encryption keys
|
|
75
|
+
*/
|
|
76
|
+
export function buildEncryptionPath(coinType, account = '0', index = '0') {
|
|
77
|
+
return `m/44'/${coinType}'/${account}'/1/${index}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// PKI Storage Key
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
export const PKI_STORAGE_KEY = 'wallet-pki-keys';
|
|
85
|
+
export const STORED_WALLET_KEY = 'encrypted_wallet';
|
|
86
|
+
export const PASSKEY_CREDENTIAL_KEY = 'passkey_credential';
|
|
87
|
+
export const PASSKEY_WALLET_KEY = 'passkey_wallet';
|
package/src/lib.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HD Wallet UI — Pure Library
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all pure (non-DOM) modules for programmatic use.
|
|
5
|
+
* No UI or DOM dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Constants & configuration
|
|
9
|
+
export {
|
|
10
|
+
cryptoConfig,
|
|
11
|
+
coinTypeToConfig,
|
|
12
|
+
buildSigningPath,
|
|
13
|
+
buildEncryptionPath,
|
|
14
|
+
PKI_STORAGE_KEY,
|
|
15
|
+
STORED_WALLET_KEY,
|
|
16
|
+
PASSKEY_CREDENTIAL_KEY,
|
|
17
|
+
PASSKEY_WALLET_KEY,
|
|
18
|
+
} from './constants.js';
|
|
19
|
+
|
|
20
|
+
// Address derivation & utilities
|
|
21
|
+
export {
|
|
22
|
+
toHexCompact,
|
|
23
|
+
toHex,
|
|
24
|
+
hexToBytes,
|
|
25
|
+
ensureUint8Array,
|
|
26
|
+
generateBtcAddress,
|
|
27
|
+
generateEthAddress,
|
|
28
|
+
generateSolAddress,
|
|
29
|
+
generateXrpAddress,
|
|
30
|
+
deriveEthAddress,
|
|
31
|
+
deriveSuiAddress,
|
|
32
|
+
deriveMonadAddress,
|
|
33
|
+
deriveCardanoAddress,
|
|
34
|
+
generateAddresses,
|
|
35
|
+
generateAddressForCoin,
|
|
36
|
+
truncateAddress,
|
|
37
|
+
fetchBtcBalance,
|
|
38
|
+
fetchEthBalance,
|
|
39
|
+
fetchSolBalance,
|
|
40
|
+
fetchSuiBalance,
|
|
41
|
+
fetchMonadBalance,
|
|
42
|
+
fetchAdaBalance,
|
|
43
|
+
fetchXrpBalance,
|
|
44
|
+
} from './address-derivation.js';
|
|
45
|
+
|
|
46
|
+
// Wallet storage & encryption
|
|
47
|
+
export {
|
|
48
|
+
default as WalletStorage,
|
|
49
|
+
StorageMethod,
|
|
50
|
+
isPasskeySupported,
|
|
51
|
+
isPRFLikelySupported,
|
|
52
|
+
registerPasskey,
|
|
53
|
+
authenticatePasskey,
|
|
54
|
+
getStorageMetadata,
|
|
55
|
+
hasStoredWallet,
|
|
56
|
+
getStorageMethod,
|
|
57
|
+
storeWithPIN,
|
|
58
|
+
retrieveWithPIN,
|
|
59
|
+
storeWithPasskey,
|
|
60
|
+
retrieveWithPasskey,
|
|
61
|
+
clearStorage,
|
|
62
|
+
migrateStorage,
|
|
63
|
+
} from './wallet-storage.js';
|
package/src/template.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
export function getModalHTML() {
|
|
2
|
+
return `
|
|
3
|
+
<!-- Keys Modal -->
|
|
4
|
+
<div id="keys-modal" class="modal">
|
|
5
|
+
<div class="modal-glass modal-wide">
|
|
6
|
+
<div class="modal-header"><div class="account-header-info"><div class="account-header-top"><h3>Account</h3><h3 class="account-total-value" id="account-total-value"></h3></div><div class="account-address-row"><select id="account-address-select" class="glass-select compact account-address-select"><option value="xpub">xpub</option></select><code class="account-address-display" id="account-address-display"></code><button class="account-address-copy" id="account-address-copy" title="Copy address"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button><span class="account-address-value" id="account-address-value"></span></div></div><button class="modal-close">×</button></div>
|
|
7
|
+
<div class="modal-body">
|
|
8
|
+
<div class="modal-tabs">
|
|
9
|
+
<button class="modal-tab active" data-modal-tab="vcard-tab-content">Identity</button>
|
|
10
|
+
<button class="modal-tab" data-modal-tab="keys-tab-content">Keys</button>
|
|
11
|
+
<button class="modal-tab" data-modal-tab="trust-tab-content">Trust Map</button>
|
|
12
|
+
<button class="modal-tab" data-modal-tab="bond-tab-content">Security Bond</button>
|
|
13
|
+
</div>
|
|
14
|
+
<div id="keys-tab-content" class="modal-tab-content">
|
|
15
|
+
<div id="memory-info-box" class="memory-info-box" style="display:none"><div class="memory-info-content"><p><strong>Your keys never leave your device.</strong></p></div><button class="wallet-info-close" id="memory-info-close" title="Close">×</button></div>
|
|
16
|
+
<div class="xpub-section">
|
|
17
|
+
<div class="xpub-header">
|
|
18
|
+
<h4 class="section-label">Extended Public Key (xpub)</h4>
|
|
19
|
+
<div class="wallet-info-icon-wrap" id="xpub-info-toggle" title="What is an xpub?"><svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="0.75"/><text x="8" y="8" text-anchor="middle" dominant-baseline="central" font-size="11" fill="currentColor">i</text></svg></div>
|
|
20
|
+
<div class="memory-notice-row"><span class="memory-notice-label">Local Storage Only</span><div class="wallet-info-icon-wrap" id="memory-info-toggle" title="What does this mean?"><svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="0.75"/><text x="8" y="8" text-anchor="middle" dominant-baseline="central" font-size="11" fill="currentColor">i</text></svg></div></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div id="xpub-info-box" class="xpub-info-box" style="display:none"><div class="xpub-info-content"><p><strong>Your xpub is your public identity root.</strong></p></div><button class="wallet-info-close" id="xpub-info-close" title="Close">×</button></div>
|
|
23
|
+
<div class="key-value-row">
|
|
24
|
+
<code id="keys-xpub" class="key-value truncate"></code>
|
|
25
|
+
<button class="copy-key-btn" data-copy="keys-xpub" title="Copy"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="keys-section hd-section">
|
|
29
|
+
<h4 class="section-label">HD Wallet Derivation (BIP44)</h4>
|
|
30
|
+
<div class="hd-controls-row">
|
|
31
|
+
<div class="hd-control-group"><label>Network</label>
|
|
32
|
+
<select id="hd-coin" class="glass-select compact">
|
|
33
|
+
<option value="0" data-symbol="BTC" data-name="Bitcoin">Bitcoin</option>
|
|
34
|
+
<option value="60" data-symbol="ETH" data-name="Ethereum">Ethereum</option>
|
|
35
|
+
<option value="501" data-symbol="SOL" data-name="Solana">Solana</option>
|
|
36
|
+
<!-- Commented out — BTC/ETH/SOL only for now
|
|
37
|
+
<option value="2" data-symbol="LTC" data-name="Litecoin">Litecoin</option>
|
|
38
|
+
<option value="145" data-symbol="BCH" data-name="Bitcoin Cash">Bitcoin Cash</option>
|
|
39
|
+
<option value="3" data-symbol="DOGE" data-name="Dogecoin">Dogecoin</option>
|
|
40
|
+
<option value="118" data-symbol="ATOM" data-name="Cosmos">Cosmos</option>
|
|
41
|
+
<option value="330" data-symbol="ALGO" data-name="Algorand">Algorand</option>
|
|
42
|
+
<option value="354" data-symbol="DOT" data-name="Polkadot">Polkadot</option>
|
|
43
|
+
<option value="1815" data-symbol="ADA" data-name="Cardano">Cardano</option>
|
|
44
|
+
<option value="144" data-symbol="XRP" data-name="Ripple">Ripple (XRP)</option>
|
|
45
|
+
-->
|
|
46
|
+
</select>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="hd-control-group"><label>Account</label><input type="number" id="hd-account" class="glass-input compact hd-num-input" value="0" min="0" step="1"></div>
|
|
49
|
+
<div class="hd-control-group"><label>Index</label><input type="number" id="hd-index" class="glass-input compact hd-num-input" value="0" min="0" step="1"></div>
|
|
50
|
+
</div>
|
|
51
|
+
<div id="hd-not-initialized" class="hd-not-initialized" style="display: none;"><p>HD wallet not initialized. Please log in with a password or seed phrase.</p></div>
|
|
52
|
+
<div id="derived-result" class="derived-result" style="display: none;">
|
|
53
|
+
<div class="hd-key-row"><div class="hd-key-label">Signing</div><div class="hd-key-path"><code id="signing-path">m/44'/0'/0'/0'/0'</code></div><div class="hd-key-value"><code id="signing-pubkey" class="truncate"></code><button class="copy-btn" data-copy="signing-pubkey" title="Copy"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></button></div></div>
|
|
54
|
+
<div class="hd-key-row"><div class="hd-key-label">Encryption</div><div class="hd-key-path"><code id="encryption-path">m/44'/0'/0'/1'/0'</code></div><div class="hd-key-value"><code id="encryption-pubkey" class="truncate"></code><button class="copy-btn" data-copy="encryption-pubkey" title="Copy"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></button></div></div>
|
|
55
|
+
<div class="hd-address-row"><div class="hd-address-info"><span class="crypto-icon" id="derived-icon"></span><span class="crypto-name" id="derived-crypto-name">Bitcoin</span><code id="derived-address" class="hd-address"></code><a id="derived-explorer-link" href="#" target="_blank" class="explorer-link" title="View on Explorer"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg></a></div><div class="qr-section"><canvas id="address-qr" width="64" height="64"></canvas></div></div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
<details class="keys-section collapsible-section">
|
|
59
|
+
<summary class="section-label collapsible-header"><span>HD Wallet Root</span></summary>
|
|
60
|
+
<div class="collapsible-content">
|
|
61
|
+
<div class="key-item"><label>Master Public Key (xpub)</label><div class="key-value-row"><code id="wallet-xpub" class="key-value truncate"></code><button class="copy-key-btn" data-copy="wallet-xpub" title="Copy"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></div></div>
|
|
62
|
+
<div class="key-item sensitive"><label>Master Private Key (xprv)</label><div class="key-value-row"><code id="wallet-xprv" class="key-value truncate blurred" data-revealed="false"></code><button class="reveal-key-btn" data-target="wallet-xprv" title="Reveal"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button><button class="copy-key-btn" data-copy="wallet-xprv" title="Copy"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></div></div>
|
|
63
|
+
<div class="key-item sensitive"><label>Seed Phrase (BIP39)</label><div class="key-value-row"><code id="wallet-seed-phrase" class="key-value seed-phrase blurred" data-revealed="false"></code><button class="reveal-key-btn" data-target="wallet-seed-phrase" title="Reveal"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button><button class="copy-key-btn" data-copy="wallet-seed-phrase" title="Copy"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></div></div>
|
|
64
|
+
<div class="export-section"><div class="export-dropdown"><button class="glass-btn small export-btn" id="export-wallet-btn"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> Export Wallet</button><div class="export-menu" id="export-menu"><button class="export-option" data-format="mnemonic"><span class="export-label">BIP39 Mnemonic</span></button><button class="export-option" data-format="xpub"><span class="export-label">Extended Public Key</span></button><button class="export-option" data-format="xprv"><span class="export-label">Extended Private Key</span></button></div></div></div>
|
|
65
|
+
</div>
|
|
66
|
+
</details>
|
|
67
|
+
</div>
|
|
68
|
+
<div id="vcard-tab-content" class="modal-tab-content active">
|
|
69
|
+
<div id="vcard-form-view">
|
|
70
|
+
<div class="photo-upload-section">
|
|
71
|
+
<div class="photo-preview" id="vcard-photo-preview">
|
|
72
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="photo-placeholder-icon">
|
|
73
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
|
|
74
|
+
</svg>
|
|
75
|
+
<video id="vcard-camera-video" autoplay playsinline style="display:none;width:100%;height:100%;object-fit:cover;"></video>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="photo-actions">
|
|
78
|
+
<label class="glass-btn small" for="vcard-photo-input">Upload</label>
|
|
79
|
+
<input type="file" id="vcard-photo-input" accept="image/*" hidden>
|
|
80
|
+
<button id="vcard-camera-btn" class="glass-btn small" style="display:none;">Take Photo</button>
|
|
81
|
+
<button id="vcard-camera-capture" class="glass-btn small primary" style="display:none;">Capture</button>
|
|
82
|
+
<button id="vcard-camera-cancel" class="glass-btn small" style="display:none;">Cancel</button>
|
|
83
|
+
<button id="vcard-photo-remove" class="glass-btn small" style="display:none;">Remove</button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="identity-form">
|
|
88
|
+
<div class="vcard-form-stack">
|
|
89
|
+
<div class="vcard-form-row name-row">
|
|
90
|
+
<div class="vcard-input-group vcard-input-sm">
|
|
91
|
+
<label>Prefix</label>
|
|
92
|
+
<input type="text" id="vcard-prefix" class="vcard-input" placeholder="Mr.">
|
|
93
|
+
</div>
|
|
94
|
+
<div class="vcard-input-group">
|
|
95
|
+
<label>First Name</label>
|
|
96
|
+
<input type="text" id="vcard-firstname" class="vcard-input" placeholder="John">
|
|
97
|
+
</div>
|
|
98
|
+
<div class="vcard-input-group">
|
|
99
|
+
<label>Middle</label>
|
|
100
|
+
<input type="text" id="vcard-middlename" class="vcard-input" placeholder="A.">
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="vcard-form-row name-row-2">
|
|
104
|
+
<div class="vcard-input-group">
|
|
105
|
+
<label>Last Name</label>
|
|
106
|
+
<input type="text" id="vcard-lastname" class="vcard-input" placeholder="Doe">
|
|
107
|
+
</div>
|
|
108
|
+
<div class="vcard-input-group vcard-input-sm">
|
|
109
|
+
<label>Suffix</label>
|
|
110
|
+
<input type="text" id="vcard-suffix" class="vcard-input" placeholder="Jr.">
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="vcard-form-row">
|
|
114
|
+
<div class="vcard-input-group">
|
|
115
|
+
<label>Email</label>
|
|
116
|
+
<input type="email" id="vcard-email" class="vcard-input" placeholder="john@example.com">
|
|
117
|
+
</div>
|
|
118
|
+
<div class="vcard-input-group">
|
|
119
|
+
<label>Phone</label>
|
|
120
|
+
<input type="tel" id="vcard-phone" class="vcard-input" placeholder="+1 555-0100">
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="vcard-form-row">
|
|
124
|
+
<div class="vcard-input-group">
|
|
125
|
+
<label>Organization</label>
|
|
126
|
+
<input type="text" id="vcard-org" class="vcard-input" placeholder="Company Name">
|
|
127
|
+
</div>
|
|
128
|
+
<div class="vcard-input-group">
|
|
129
|
+
<label>Job Title</label>
|
|
130
|
+
<input type="text" id="vcard-title" class="vcard-input" placeholder="Software Engineer">
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="vcard-input-group">
|
|
134
|
+
<label>Street Address</label>
|
|
135
|
+
<input type="text" id="vcard-street" class="vcard-input" placeholder="123 Main St">
|
|
136
|
+
</div>
|
|
137
|
+
<div class="vcard-form-row">
|
|
138
|
+
<div class="vcard-input-group">
|
|
139
|
+
<label>City</label>
|
|
140
|
+
<input type="text" id="vcard-city" class="vcard-input" placeholder="San Francisco">
|
|
141
|
+
</div>
|
|
142
|
+
<div class="vcard-input-group vcard-input-sm">
|
|
143
|
+
<label>State</label>
|
|
144
|
+
<input type="text" id="vcard-region" class="vcard-input" placeholder="CA">
|
|
145
|
+
</div>
|
|
146
|
+
<div class="vcard-input-group vcard-input-sm">
|
|
147
|
+
<label>ZIP</label>
|
|
148
|
+
<input type="text" id="vcard-postal" class="vcard-input" placeholder="94102">
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="vcard-input-group">
|
|
152
|
+
<label>Country</label>
|
|
153
|
+
<input type="text" id="vcard-country" class="vcard-input" placeholder="United States">
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="keys-display-section">
|
|
159
|
+
<h4 class="section-label">Cryptographic Keys <span class="readonly-badge">Read Only</span></h4>
|
|
160
|
+
<p class="keys-display-info">These keys are derived from your HD wallet master seed and cannot be edited.</p>
|
|
161
|
+
|
|
162
|
+
<div class="keys-display-grid" id="vcard-keys-display">
|
|
163
|
+
<!-- Keys will be populated here -->
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div id="vcf-import-result" class="vcf-import-result" style="display: none;">
|
|
168
|
+
<h4 class="section-label">Imported Contact</h4>
|
|
169
|
+
<div class="vcf-import-card">
|
|
170
|
+
<div class="vcf-import-photo" id="vcf-import-photo"></div>
|
|
171
|
+
<div class="vcf-import-fields" id="vcf-import-fields"></div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div id="vcard-result-view" style="display: none;">
|
|
177
|
+
<div class="qr-container"><canvas id="qr-code"></canvas></div>
|
|
178
|
+
<div class="vcard-result-actions">
|
|
179
|
+
<button id="download-vcard" class="glass-btn primary">Download .vcf</button>
|
|
180
|
+
<button id="copy-vcard" class="glass-btn">Copy</button>
|
|
181
|
+
</div>
|
|
182
|
+
<details class="vcard-details">
|
|
183
|
+
<summary>View Raw vCard</summary>
|
|
184
|
+
<pre id="vcard-preview"></pre>
|
|
185
|
+
</details>
|
|
186
|
+
<button id="vcard-back-btn" class="glass-btn vcard-back-btn">
|
|
187
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
|
|
188
|
+
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
189
|
+
</svg> Back to Editor
|
|
190
|
+
</button>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="vcard-actions-footer">
|
|
193
|
+
<button id="generate-vcard" class="glass-btn vcard-export-btn">Export vCard</button>
|
|
194
|
+
<label class="glass-btn vcard-import-btn" for="vcf-import-input">Import vCard</label>
|
|
195
|
+
<input type="file" id="vcf-import-input" accept=".vcf,text/vcard" hidden>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
<!-- Trust Map Tab -->
|
|
199
|
+
<div id="trust-tab-content" class="modal-tab-content">
|
|
200
|
+
<!-- Collapsible Trust Levels -->
|
|
201
|
+
<details class="trust-levels-box" open>
|
|
202
|
+
<summary class="trust-levels-header">
|
|
203
|
+
<span>Trust Levels</span>
|
|
204
|
+
<svg class="chevron-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
|
205
|
+
</summary>
|
|
206
|
+
<div class="trust-levels-badges">
|
|
207
|
+
<span class="trust-badge trust-ultimate">Ultimate</span>
|
|
208
|
+
<span class="trust-badge trust-full">Full</span>
|
|
209
|
+
<span class="trust-badge trust-marginal">Marginal</span>
|
|
210
|
+
<span class="trust-badge trust-unknown">Unknown</span>
|
|
211
|
+
<span class="trust-badge trust-never">Never</span>
|
|
212
|
+
</div>
|
|
213
|
+
</details>
|
|
214
|
+
|
|
215
|
+
<!-- Actions bar -->
|
|
216
|
+
<div class="trust-actions-bar">
|
|
217
|
+
<button id="establish-trust-btn" class="glass-btn small primary">+ Establish Trust</button>
|
|
218
|
+
<button id="trust-rules-btn" class="glass-btn small"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v1m0 16v1m-8-9H3m18 0h-1m-2.636-6.364l-.707.707M6.343 17.657l-.707.707m12.728 0l-.707-.707M6.343 6.343l-.707-.707"/><circle cx="12" cy="12" r="4"/></svg> Rules</button>
|
|
219
|
+
<div class="trust-actions-right">
|
|
220
|
+
<button id="trust-export-btn" class="glass-btn small" title="Export trust data"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button>
|
|
221
|
+
<label class="glass-btn small" for="trust-import-input" title="Import trust data"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg></label>
|
|
222
|
+
<input type="file" id="trust-import-input" accept=".json,.trust.json" hidden>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- Trust scan status -->
|
|
227
|
+
<div id="trust-scan-status" class="trust-scan-status">
|
|
228
|
+
<span class="trust-scan-dot"></span>
|
|
229
|
+
<span id="trust-scan-label">Scanning...</span>
|
|
230
|
+
<span id="trust-scan-count" class="trust-scan-count"></span>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!-- Scrollable Trust List -->
|
|
234
|
+
<div id="trust-list" class="trust-list">
|
|
235
|
+
<div id="trust-list-empty" class="trust-list-empty">
|
|
236
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" opacity="0.3">
|
|
237
|
+
<circle cx="12" cy="5" r="3"/><circle cx="6" cy="15" r="3"/><circle cx="18" cy="15" r="3"/>
|
|
238
|
+
<line x1="12" y1="8" x2="8" y2="12"/><line x1="12" y1="8" x2="16" y2="12"/>
|
|
239
|
+
</svg>
|
|
240
|
+
<p>No trust relationships found yet.</p>
|
|
241
|
+
<p class="trust-list-subtitle">Relationships will appear here as your addresses are scanned.</p>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<!-- Security Bond Tab -->
|
|
247
|
+
<div id="bond-tab-content" class="modal-tab-content">
|
|
248
|
+
<div class="bond-intro">
|
|
249
|
+
<h4 class="section-label">Trust Proof</h4>
|
|
250
|
+
<p>Holding value on your keys signals commitment and reduces Sybil risk. Your total balance across all networks serves as proof of stake in the trust network.</p>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="bond-networks-grid">
|
|
253
|
+
<div class="bond-network-card" id="bond-btc-card">
|
|
254
|
+
<div class="bond-net-header"><span class="bond-net-name">Bitcoin</span><span class="bond-net-balance" id="bond-btc-balance">--</span></div>
|
|
255
|
+
<a href="#" id="bond-btc-explorer" class="bond-net-address" target="_blank" rel="noopener"><code id="bond-btc-address">--</code></a>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="bond-network-card" id="bond-eth-card">
|
|
258
|
+
<div class="bond-net-header"><span class="bond-net-name">Ethereum</span><span class="bond-net-balance" id="bond-eth-balance">--</span></div>
|
|
259
|
+
<a href="#" id="bond-eth-explorer" class="bond-net-address" target="_blank" rel="noopener"><code id="bond-eth-address">--</code></a>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="bond-network-card" id="bond-sol-card">
|
|
262
|
+
<div class="bond-net-header"><span class="bond-net-name">Solana</span><span class="bond-net-balance" id="bond-sol-balance">--</span></div>
|
|
263
|
+
<a href="#" id="bond-sol-explorer" class="bond-net-address" target="_blank" rel="noopener"><code id="bond-sol-address">--</code></a>
|
|
264
|
+
</div>
|
|
265
|
+
<!-- Commented out — BTC/ETH/SOL only for now
|
|
266
|
+
<div class="bond-network-card" id="bond-sui-card">...</div>
|
|
267
|
+
<div class="bond-network-card" id="bond-monad-card">...</div>
|
|
268
|
+
<div class="bond-network-card" id="bond-ada-card">...</div>
|
|
269
|
+
<div class="bond-network-card" id="bond-xrp-card">...</div>
|
|
270
|
+
-->
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<!-- Login Modal -->
|
|
279
|
+
<div id="login-modal" class="modal">
|
|
280
|
+
<div class="modal-glass login-modal-content">
|
|
281
|
+
<div class="modal-header"><h3>Login</h3><button class="modal-close">×</button></div>
|
|
282
|
+
<div id="wallet-info-box" class="wallet-info-box">
|
|
283
|
+
<div id="wallet-info-expanded" class="wallet-info-expanded"><div class="wallet-info-content"><strong>Decentralized Wallet</strong> — Your credentials never leave your browser.</div><button class="wallet-info-close" id="wallet-info-dismiss" title="Dismiss">×</button></div>
|
|
284
|
+
<div id="wallet-info-collapsed" class="wallet-info-collapsed" style="display:none"><span>Decentralized Wallet</span><div class="wallet-info-icon-wrap"><svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="currentColor" stroke-width="0.75"/><text x="8" y="8" text-anchor="middle" dominant-baseline="central" font-size="11" fill="currentColor">i</text></svg></div></div>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="modal-body">
|
|
287
|
+
<div class="method-tabs">
|
|
288
|
+
<button class="method-tab active" data-method="password">Password</button>
|
|
289
|
+
<button class="method-tab" data-method="seed">Seed Phrase</button>
|
|
290
|
+
<button class="method-tab" data-method="stored" id="stored-tab" style="display: none;">Stored</button>
|
|
291
|
+
</div>
|
|
292
|
+
<form id="password-method" class="method-content active" onsubmit="return false;">
|
|
293
|
+
<div class="glass-input-group"><input type="text" id="wallet-username" class="glass-input" placeholder="Username" autocomplete="username"></div>
|
|
294
|
+
<div class="glass-input-group">
|
|
295
|
+
<input type="password" id="wallet-password" class="glass-input" placeholder="Password (24+ chars)" autocomplete="new-password">
|
|
296
|
+
<div class="entropy-bar"><div class="entropy-fill" id="strength-fill"></div><div class="entropy-threshold"></div></div>
|
|
297
|
+
<span class="entropy-label"><span id="entropy-bits">0</span> bits entropy</span>
|
|
298
|
+
</div>
|
|
299
|
+
<div class="remember-wallet-group">
|
|
300
|
+
<label class="glass-checkbox"><input type="checkbox" id="remember-wallet-password"><span class="checkmark"></span><span>Remember wallet</span></label>
|
|
301
|
+
<div class="remember-options" id="remember-options-password" style="display: none;">
|
|
302
|
+
<div class="remember-method-selector"><button type="button" class="remember-method-btn" data-method="pin" data-target="password">PIN</button><button type="button" class="remember-method-btn active" data-method="passkey" data-target="password" id="passkey-btn-password">Passkey</button></div>
|
|
303
|
+
<div class="pin-input-group" id="pin-group-password" style="display: none;"><input type="password" id="pin-input-password" class="glass-input pin-input" placeholder="6-digit PIN" maxlength="6" inputmode="numeric" pattern="[0-9]*"></div>
|
|
304
|
+
<div class="passkey-info" id="passkey-info-password"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a5 5 0 0 1 5 5v3H7V7a5 5 0 0 1 5-5z"/><rect x="3" y="10" width="18" height="12" rx="2"/><circle cx="12" cy="16" r="1"/></svg><span>Use Face ID, Touch ID, or device PIN</span></div>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
<button id="derive-from-password" class="glass-btn primary full-width" disabled type="button">Login</button>
|
|
308
|
+
</form>
|
|
309
|
+
<div id="seed-method" class="method-content">
|
|
310
|
+
<div class="glass-input-group"><textarea id="seed-phrase" class="glass-input glass-textarea" rows="3" placeholder="Enter 12 or 24 word seed phrase..."></textarea><div class="seed-actions"><button id="generate-seed" class="glass-btn small">Generate</button><button id="validate-seed" class="glass-btn small">Validate</button></div></div>
|
|
311
|
+
<div class="remember-wallet-group">
|
|
312
|
+
<label class="glass-checkbox"><input type="checkbox" id="remember-wallet-seed"><span class="checkmark"></span><span>Remember wallet</span></label>
|
|
313
|
+
<div class="remember-options" id="remember-options-seed" style="display: none;">
|
|
314
|
+
<div class="remember-method-selector"><button type="button" class="remember-method-btn" data-method="pin" data-target="seed">PIN</button><button type="button" class="remember-method-btn active" data-method="passkey" data-target="seed" id="passkey-btn-seed">Passkey</button></div>
|
|
315
|
+
<div class="pin-input-group" id="pin-group-seed" style="display: none;"><input type="password" id="pin-input-seed" class="glass-input pin-input" placeholder="6-digit PIN" maxlength="6" inputmode="numeric" pattern="[0-9]*"></div>
|
|
316
|
+
<div class="passkey-info" id="passkey-info-seed"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a5 5 0 0 1 5 5v3H7V7a5 5 0 0 1 5-5z"/><rect x="3" y="10" width="18" height="12" rx="2"/><circle cx="12" cy="16" r="1"/></svg><span>Use Face ID, Touch ID, or device PIN</span></div>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
<button id="derive-from-seed" class="glass-btn primary full-width" disabled>Login</button>
|
|
320
|
+
</div>
|
|
321
|
+
<div id="stored-method" class="method-content">
|
|
322
|
+
<div class="stored-wallet-info"><svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg><p>Encrypted wallet found</p><span class="stored-wallet-date" id="stored-wallet-date"></span></div>
|
|
323
|
+
<div id="stored-pin-section"><div class="glass-input-group"><input type="password" id="pin-input-unlock" class="glass-input pin-input-large" placeholder="Enter 6-digit PIN" maxlength="6" inputmode="numeric" pattern="[0-9]*"></div><button id="unlock-stored-wallet" class="glass-btn primary full-width" disabled>Unlock with PIN</button></div>
|
|
324
|
+
<div id="stored-passkey-section" style="display: none;"><button id="unlock-with-passkey" class="glass-btn primary full-width passkey-unlock-btn"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a5 5 0 0 1 5 5v3H7V7a5 5 0 0 1 5-5z"/><rect x="3" y="10" width="18" height="12" rx="2"/><circle cx="12" cy="16" r="1"/></svg> Unlock with Passkey</button></div>
|
|
325
|
+
<div class="stored-divider" id="stored-divider" style="display: none;"><span>or</span></div>
|
|
326
|
+
<button id="forget-stored-wallet" class="glass-btn secondary full-width">Forget Wallet</button>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<!-- Loading Overlay -->
|
|
333
|
+
<div id="loading-overlay" class="loading-overlay">
|
|
334
|
+
<div class="loading-spinner"></div>
|
|
335
|
+
<span id="status">Loading WASM modules...</span>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<!-- Photo remove confirmation modal -->
|
|
339
|
+
<div id="photo-remove-confirm-modal" class="modal">
|
|
340
|
+
<div class="modal-glass" style="max-width:340px;padding:24px;text-align:center;">
|
|
341
|
+
<p style="margin:0 0 20px;font-size:1rem;color:var(--white-70);">Are you sure you want to remove this photo?</p>
|
|
342
|
+
<div style="display:flex;gap:12px;justify-content:center;">
|
|
343
|
+
<button id="photo-remove-yes" class="glass-btn small" style="background:rgba(239,68,68,0.2);border-color:rgba(239,68,68,0.4);color:#f87171;">Remove</button>
|
|
344
|
+
<button id="photo-remove-no" class="glass-btn small">Cancel</button>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>`;
|
|
348
|
+
}
|