hd-wallet-ui 2.0.19 → 2.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-wallet-ui",
3
- "version": "2.0.19",
3
+ "version": "2.0.20",
4
4
  "description": "HD Wallet modal UI — login, keys, identity, trust map, and security bond. Attach to any button in your app.",
5
5
  "type": "module",
6
6
  "main": "src/app.js",
@@ -40,7 +40,7 @@
40
40
  "buffer": "^6.0.3",
41
41
  "flatbuffers": "^25.9.23",
42
42
  "flatc-wasm": "^26.1.32",
43
- "hd-wallet-wasm": "^2.0.19",
43
+ "hd-wallet-wasm": "^2.0.20",
44
44
  "qrcode": "^1.5.3",
45
45
  "spacedatastandards.org": "^1.93.3",
46
46
  "vcard-cryptoperson": "^1.1.11"
package/src/app.js CHANGED
@@ -526,50 +526,6 @@ function deriveAccountPeerId() {
526
526
  }
527
527
  }
528
528
 
529
- function getWalletIdentityPath(wallet = getCurrentWallet()) {
530
- if (!wallet) return "m/44'/0'/0'";
531
- return `m/44'/0'/${wallet.accountIndex}'`;
532
- }
533
-
534
- function getCurrentWalletIdentity(wallet = getCurrentWallet()) {
535
- if (!state.hdRoot || !wallet) return { xpub: '', peerId: '', path: getWalletIdentityPath(wallet) };
536
- const path = getWalletIdentityPath(wallet);
537
- try {
538
- const accountKey = deriveHDKey(path);
539
- return {
540
- xpub: accountKey?.toXpub?.() || '',
541
- peerId: accountKey?.peerIdString?.() || '',
542
- path,
543
- };
544
- } catch (e) {
545
- console.warn('Failed to derive wallet identity keys:', e);
546
- return { xpub: '', peerId: '', path };
547
- }
548
- }
549
-
550
- function getCurrentWalletSigningAccounts(wallet = getCurrentWallet()) {
551
- if (!wallet) return [];
552
- return state.activeAccounts.filter(a => a.active && getAccountWalletId(a) === wallet.id && isSigningAccountForWallet(a, wallet));
553
- }
554
-
555
- function getCurrentWalletSignatureKey(wallet = getCurrentWallet()) {
556
- if (!state.hdRoot || !wallet) return null;
557
- const accountIndex = wallet.accountIndex;
558
- try {
559
- const path = buildSigningPath(501, accountIndex, 0);
560
- const derived = deriveHDKey(path);
561
- return {
562
- privateKey: derived.privateKey(),
563
- accountIndex,
564
- index: 0,
565
- path,
566
- };
567
- } catch (e) {
568
- console.warn('Failed to derive selected wallet signature key:', e);
569
- return null;
570
- }
571
- }
572
-
573
529
  function updatePathDisplay() {
574
530
  const coin = $('hd-coin')?.value;
575
531
  const account = $('hd-account')?.value || '0';
@@ -821,60 +777,30 @@ function updateCustomPathDefault() {
821
777
  input.dataset.autogenerated = 'true';
822
778
  }
823
779
 
824
- function fitWalletSelectorToSelectedLabel(select) {
825
- if (!select) return;
826
- const selected = select.options[select.selectedIndex];
827
- const label = selected?.textContent || '';
828
- const width = Math.max(72, Math.min(260, (label.length * 8) + 46));
829
- select.style.width = `${width}px`;
830
- }
831
-
832
780
  function renderWalletSelector() {
833
- const selects = [$('account-wallet-select'), $('wallet-active-select')].filter(Boolean);
834
- if (selects.length === 0) return;
781
+ const select = $('wallet-active-select');
782
+ if (!select) return;
835
783
  ensureWalletNamesNormalized();
836
784
 
837
785
  const currentWallet = getCurrentWallet();
838
786
  if (!currentWallet) {
839
- selects.forEach((select) => { select.innerHTML = ''; });
787
+ select.innerHTML = '';
840
788
  return;
841
789
  }
842
790
  state.activeWalletId = currentWallet.id;
843
791
 
792
+ select.innerHTML = '';
844
793
  const displayCurrency = state.walletFiatCurrency || getSelectedCurrency();
845
794
  const activeWallets = getActiveWallets();
846
- selects.forEach((select) => {
847
- select.innerHTML = '';
848
- activeWallets.forEach((wallet) => {
849
- const option = document.createElement('option');
850
- option.value = String(wallet.id);
851
- const walletValue = state.walletFiatTotals[wallet.id] ?? 0;
852
- option.textContent = wallet.id === state.activeWalletId
853
- ? wallet.name
854
- : `${wallet.name} (${formatCurrencyValue(walletValue, displayCurrency)})`;
855
- select.appendChild(option);
856
- });
857
- select.value = String(state.activeWalletId);
858
- fitWalletSelectorToSelectedLabel(select);
795
+ activeWallets.forEach((wallet) => {
796
+ const option = document.createElement('option');
797
+ option.value = String(wallet.id);
798
+ const walletValue = state.walletFiatTotals[wallet.id] ?? 0;
799
+ option.textContent = `${wallet.name} (${formatCurrencyValue(walletValue, displayCurrency)})`;
800
+ select.appendChild(option);
859
801
  });
802
+ select.value = String(state.activeWalletId);
860
803
  updateCustomPathWalletLabel();
861
- updateIdentityWalletKeys();
862
- }
863
-
864
- function updateWalletBondDisplay(currency = state.walletFiatCurrency || getSelectedCurrency()) {
865
- const valueEl = $('wallet-bond-value');
866
- const wallet = getCurrentWallet();
867
- const walletValue = wallet ? state.walletFiatTotals?.[wallet.id] ?? 0 : 0;
868
- if (valueEl) valueEl.textContent = formatCurrencyValue(walletValue, currency);
869
- }
870
-
871
- function updateIdentityWalletKeys() {
872
- const identity = getCurrentWalletIdentity();
873
- const xpubEl = $('identity-wallet-xpub');
874
- const peerIdEl = $('identity-wallet-peerid');
875
-
876
- if (xpubEl) setTruncatedValue(xpubEl, identity.xpub || 'N/A');
877
- if (peerIdEl) setTruncatedValue(peerIdEl, identity.peerId || 'N/A');
878
804
  }
879
805
 
880
806
  function sleep(ms) {
@@ -1295,7 +1221,6 @@ function hideWalletOverlays() {
1295
1221
  function showWalletMainView() {
1296
1222
  const main = $('wallet-main-view');
1297
1223
  hideWalletOverlays();
1298
- closeAssetActionOverlay();
1299
1224
  if (main) main.style.display = 'block';
1300
1225
  }
1301
1226
 
@@ -1598,49 +1523,6 @@ function closeWalletActionMenus() {
1598
1523
  $('wallet-receive-menu')?.classList.remove('visible');
1599
1524
  }
1600
1525
 
1601
- function closeAssetActionOverlay() {
1602
- const overlay = $('wallet-asset-action-overlay');
1603
- if (overlay) overlay.style.display = 'none';
1604
- state.selectedWalletAssetIdx = null;
1605
- }
1606
-
1607
- function showAssetActionOverlay(acct, idx) {
1608
- const overlay = $('wallet-asset-action-overlay');
1609
- if (!overlay || !acct) return;
1610
-
1611
- state.selectedWalletAssetIdx = idx;
1612
- const icon = CHAIN_ICONS[acct.name] || { symbol: '?' };
1613
- const fullName = CHAIN_FULL_NAMES[acct.name] || acct.name;
1614
- const pathLabel = acct.path || `m/44'/${acct.coinType}'/${acct.account}'/0/${acct.index}`;
1615
-
1616
- const titleEl = $('wallet-asset-action-title');
1617
- const pathEl = $('wallet-asset-action-path');
1618
- const addressEl = $('wallet-asset-action-address');
1619
- if (titleEl) titleEl.textContent = `${icon.symbol} ${fullName}`;
1620
- if (pathEl) pathEl.textContent = pathLabel;
1621
- if (addressEl) {
1622
- addressEl.textContent = acct.address || '';
1623
- addressEl.title = acct.address || '';
1624
- }
1625
-
1626
- const sendBtn = $('wallet-asset-send');
1627
- const receiveBtn = $('wallet-asset-receive');
1628
- if (sendBtn) {
1629
- sendBtn.onclick = () => {
1630
- closeAssetActionOverlay();
1631
- showSendView(idx);
1632
- };
1633
- }
1634
- if (receiveBtn) {
1635
- receiveBtn.onclick = () => {
1636
- closeAssetActionOverlay();
1637
- showReceiveModal(acct);
1638
- };
1639
- }
1640
-
1641
- overlay.style.display = 'flex';
1642
- }
1643
-
1644
1526
  function renderAccountsList() {
1645
1527
  const listEl = $('wallet-accounts-list');
1646
1528
  const emptyEl = $('wallet-accounts-empty');
@@ -1689,7 +1571,7 @@ function renderAccountsList() {
1689
1571
  '</div>';
1690
1572
 
1691
1573
  row.addEventListener('click', () => {
1692
- showAssetActionOverlay(acct, idx);
1574
+ showReceiveModal(acct);
1693
1575
  });
1694
1576
 
1695
1577
  listEl.appendChild(row);
@@ -2146,6 +2028,7 @@ async function updateWalletBondTotal() {
2146
2028
  const currency = getSelectedCurrency();
2147
2029
  const prices = await fetchCryptoPrices(currency);
2148
2030
 
2031
+ let total = 0;
2149
2032
  const walletTotals = {};
2150
2033
  let hasPositiveBalance = false;
2151
2034
  let missingPriceForFundedAccount = false;
@@ -2162,20 +2045,27 @@ async function updateWalletBondTotal() {
2162
2045
  }
2163
2046
 
2164
2047
  const fiatValue = bal * price;
2048
+ total += fiatValue;
2165
2049
  const walletId = getAccountWalletId(acct);
2166
2050
  walletTotals[walletId] = (walletTotals[walletId] || 0) + fiatValue;
2167
2051
  }
2168
2052
 
2169
- const pricedTotal = Object.values(walletTotals).reduce((sum, v) => sum + (Number.isFinite(v) ? v : 0), 0);
2170
- if (hasPositiveBalance && pricedTotal <= 0 && missingPriceForFundedAccount) {
2053
+ if (hasPositiveBalance && total <= 0 && missingPriceForFundedAccount) {
2171
2054
  throw new Error('Funded accounts found but fiat pricing is unavailable');
2172
2055
  }
2173
2056
 
2174
2057
  state.walletFiatTotals = walletTotals;
2175
2058
  state.walletFiatCurrency = currency;
2176
2059
 
2177
- updateWalletBondDisplay(currency);
2060
+ const formatted = formatCurrencyValue(total, currency);
2061
+ if (valueEl) valueEl.textContent = formatted;
2178
2062
  renderWalletSelector();
2063
+
2064
+ // Also update the header bond total
2065
+ const accountTotalEl = $('account-total-value');
2066
+ if (accountTotalEl) {
2067
+ accountTotalEl.textContent = 'Bond: ' + formatted;
2068
+ }
2179
2069
  } catch (e) {
2180
2070
  console.warn('Bond total calculation failed:', e);
2181
2071
  // Keep last known totals if pricing endpoint is temporarily unavailable.
@@ -2183,7 +2073,10 @@ async function updateWalletBondTotal() {
2183
2073
  const cachedTotal = Object.values(cachedTotals).reduce((sum, v) => sum + (Number.isFinite(v) ? v : 0), 0);
2184
2074
  if (cachedTotal > 0) {
2185
2075
  const displayCurrency = state.walletFiatCurrency || getSelectedCurrency();
2186
- updateWalletBondDisplay(displayCurrency);
2076
+ const formatted = formatCurrencyValue(cachedTotal, displayCurrency);
2077
+ if (valueEl) valueEl.textContent = formatted;
2078
+ const accountTotalEl = $('account-total-value');
2079
+ if (accountTotalEl) accountTotalEl.textContent = 'Bond: ' + formatted;
2187
2080
  } else if (valueEl && !valueEl.textContent) {
2188
2081
  valueEl.textContent = '$0.00';
2189
2082
  }
@@ -2596,36 +2489,6 @@ async function generatePKIKeyPairs() {
2596
2489
  // Login / Logout
2597
2490
  // =============================================================================
2598
2491
 
2599
- function hideStoredWalletLoginUI() {
2600
- const storedTab = $('stored-tab');
2601
- if (storedTab) storedTab.style.display = 'none';
2602
-
2603
- const pinSect = $('stored-pin-section');
2604
- if (pinSect) pinSect.style.display = 'block';
2605
-
2606
- const psSect = $('stored-passkey-section');
2607
- if (psSect) psSect.style.display = 'none';
2608
-
2609
- const divider = $('stored-divider');
2610
- if (divider) divider.style.display = 'none';
2611
-
2612
- const dateEl = $('stored-wallet-date');
2613
- if (dateEl) dateEl.textContent = '';
2614
-
2615
- const unlockPin = $('pin-input-unlock');
2616
- if (unlockPin) unlockPin.value = '';
2617
-
2618
- const unlockBtn = $('unlock-stored-wallet');
2619
- if (unlockBtn) unlockBtn.disabled = true;
2620
-
2621
- $qa('.method-tab').forEach(t => t.classList.remove('active'));
2622
- $qa('.method-content').forEach(c => c.classList.remove('active'));
2623
- const pwMethod = $('password-method');
2624
- if (pwMethod) pwMethod.classList.add('active');
2625
- const pwTab = $q('.method-tab[data-method="password"]');
2626
- if (pwTab) pwTab.classList.add('active');
2627
- }
2628
-
2629
2492
  function login(keys) {
2630
2493
  state.loggedIn = true;
2631
2494
  state.wallet = keys;
@@ -2696,6 +2559,21 @@ function login(keys) {
2696
2559
  if (xpubEl) {
2697
2560
  setTruncatedValue(xpubEl, state.hdRoot.toXpub() || 'N/A');
2698
2561
  }
2562
+ // Populate wallet tab xpub display
2563
+ const walletTabXpubEl = $('wallet-tab-xpub');
2564
+ if (walletTabXpubEl) {
2565
+ setTruncatedValue(walletTabXpubEl, state.hdRoot.toXpub() || 'N/A');
2566
+ }
2567
+ // Populate wallet tab PeerID display
2568
+ const walletTabPeerIdEl = $('wallet-tab-peerid');
2569
+ const peerIdRow = $('ph-portfolio-peerid-row');
2570
+ if (walletTabPeerIdEl && peerIdRow) {
2571
+ const peerIdStr = deriveAccountPeerId();
2572
+ if (peerIdStr) {
2573
+ setTruncatedValue(walletTabPeerIdEl, peerIdStr);
2574
+ peerIdRow.style.display = '';
2575
+ }
2576
+ }
2699
2577
  populateAccountAddressDropdown();
2700
2578
  if (xprvEl) {
2701
2579
  xprvEl.textContent = 'Hidden (click reveal)';
@@ -2816,10 +2694,7 @@ function logout() {
2816
2694
  state.masterSeed = null;
2817
2695
  state.hdRoot = null;
2818
2696
  state.mnemonic = null;
2819
- state.addresses = { btc: null, eth: null, sol: null };
2820
2697
 
2821
- WalletStorage.clearStorage();
2822
- hideStoredWalletLoginUI();
2823
2698
  localStorage.removeItem(PKI_STORAGE_KEY);
2824
2699
 
2825
2700
  // Update hero stats
@@ -2856,9 +2731,6 @@ function logout() {
2856
2731
  // Clear HD wallet UI
2857
2732
  const derivedResult = $('derived-result');
2858
2733
  if (derivedResult) derivedResult.style.display = 'none';
2859
-
2860
- $('login-modal')?.classList.remove('active');
2861
- $('keys-modal')?.classList.remove('active');
2862
2734
  }
2863
2735
 
2864
2736
  // =============================================================================
@@ -2945,9 +2817,43 @@ function downloadData(data, filename, mimeType) {
2945
2817
  // Wallet Address Population & Balance Fetching
2946
2818
  // =============================================================================
2947
2819
 
2820
+ // Account header — show xpub only
2948
2821
  function populateAccountAddressDropdown() {
2949
- renderWalletSelector();
2950
- updateIdentityWalletKeys();
2822
+ const addrEl = $('account-address-display');
2823
+ if (!addrEl) return;
2824
+
2825
+ const xpubStr = state.hdRoot ? state.hdRoot.toXpub() : '';
2826
+ addrEl.textContent = `${xpubStr.slice(0,10)}...${xpubStr.slice(-10)}`;
2827
+ addrEl.title = xpubStr;
2828
+
2829
+ const copyBtn = $('account-address-copy');
2830
+ if (copyBtn) {
2831
+ copyBtn.onclick = async () => {
2832
+ if (xpubStr && await safeCopyText(xpubStr)) {
2833
+ copyBtn.title = 'Copied!';
2834
+ setTimeout(() => { copyBtn.title = 'Copy xpub'; }, 1500);
2835
+ }
2836
+ };
2837
+ }
2838
+
2839
+ // Populate PeerID row (derived from account-level secp256k1 key)
2840
+ const peerIdStr = deriveAccountPeerId();
2841
+ const peerIdRow = $('account-peerid-row');
2842
+ const peerIdEl = $('account-peerid-display');
2843
+ if (peerIdStr && peerIdRow && peerIdEl) {
2844
+ peerIdRow.style.display = '';
2845
+ peerIdEl.textContent = `${peerIdStr.slice(0,8)}...${peerIdStr.slice(-8)}`;
2846
+ peerIdEl.title = peerIdStr;
2847
+ }
2848
+ const peerCopyBtn = $('account-peerid-copy');
2849
+ if (peerCopyBtn) {
2850
+ peerCopyBtn.onclick = async () => {
2851
+ if (peerIdStr && await safeCopyText(peerIdStr)) {
2852
+ peerCopyBtn.title = 'Copied!';
2853
+ setTimeout(() => { peerCopyBtn.title = 'Copy PeerID'; }, 1500);
2854
+ }
2855
+ };
2856
+ }
2951
2857
  }
2952
2858
 
2953
2859
  function populateWalletAddresses() {
@@ -3406,11 +3312,10 @@ function generateVCard(info, { skipPhoto = false } = {}) {
3406
3312
  }
3407
3313
 
3408
3314
  if (info.includeKeys && state.wallet?.x25519) {
3409
- const identity = getCurrentWalletIdentity();
3410
3315
  person.KEY = [
3411
- // Always include the selected wallet xPub.
3412
- ...(identity.xpub ? [{
3413
- XPUB: identity.xpub,
3316
+ // Always include xPub
3317
+ ...(state.hdRoot?.toXpub ? [{
3318
+ XPUB: state.hdRoot.toXpub(),
3414
3319
  LABEL: '',
3415
3320
  }] : []),
3416
3321
  // X25519 encryption key
@@ -3419,7 +3324,8 @@ function generateVCard(info, { skipPhoto = false } = {}) {
3419
3324
  LABEL: 'X25519',
3420
3325
  },
3421
3326
  // Active accounts from wallet scan
3422
- ...getCurrentWalletSigningAccounts()
3327
+ ...state.activeAccounts
3328
+ .filter(a => a.active && isSigningAccount(a))
3423
3329
  .flatMap(a => {
3424
3330
  const entries = [];
3425
3331
  const pathLabel = a.path || `m/44'/${a.coinType}'/${a.account}'/0/${a.index}`;
@@ -3440,9 +3346,8 @@ function generateVCard(info, { skipPhoto = false } = {}) {
3440
3346
  return entries;
3441
3347
  }),
3442
3348
  ];
3443
- } else if (info.xpubOnly) {
3444
- const identity = getCurrentWalletIdentity();
3445
- if (identity.xpub) person.KEY = [{ XPUB: identity.xpub, LABEL: '' }];
3349
+ } else if (info.xpubOnly && state.hdRoot?.toXpub) {
3350
+ person.KEY = [{ XPUB: state.hdRoot.toXpub(), LABEL: '' }];
3446
3351
  }
3447
3352
 
3448
3353
  const note = info.includeKeys
@@ -3494,16 +3399,15 @@ function getSignableBody(vcardText) {
3494
3399
  }
3495
3400
 
3496
3401
  function signVCard(vcardText) {
3497
- const signatureKey = getCurrentWalletSignatureKey();
3498
- if (!signatureKey?.privateKey) return vcardText;
3402
+ if (!state.wallet?.ed25519?.privateKey) return vcardText;
3499
3403
 
3500
3404
  const body = getSignableBody(vcardText);
3501
3405
  const messageBytes = new TextEncoder().encode(body);
3502
- const signature = hdWallet().curves.ed25519.sign(messageBytes, signatureKey.privateKey);
3406
+ const signature = hdWallet().curves.ed25519.sign(messageBytes, state.wallet.ed25519.privateKey);
3503
3407
  const sigB64 = toBase64(signature);
3504
3408
 
3505
- // Encode signature + selected wallet derivation path.
3506
- const sigValue = `${sigB64}:501:${signatureKey.accountIndex}:${signatureKey.index}`;
3409
+ // Encode signature + derivation path (coinType=501, account=0, index=0)
3410
+ const sigValue = `${sigB64}:501:0:0`;
3507
3411
 
3508
3412
  // Find highest itemN and key index
3509
3413
  let maxItem = 0;
@@ -3872,8 +3776,8 @@ function isPasskeySupported() {
3872
3776
 
3873
3777
  // Track selected remember method (pin or passkey) for each login type
3874
3778
  const rememberMethod = {
3875
- password: 'pin',
3876
- seed: 'pin'
3779
+ password: 'passkey',
3780
+ seed: 'passkey'
3877
3781
  };
3878
3782
 
3879
3783
  function setupLoginHandlers() {
@@ -4319,7 +4223,7 @@ function setupMainAppHandlers() {
4319
4223
  // Modal close handlers
4320
4224
  $qa('.modal').forEach(modal => {
4321
4225
  modal.addEventListener('click', (e) => {
4322
- if (e.target === modal || e.target.closest?.('.modal-close')) {
4226
+ if (e.target === modal || e.target.classList.contains('modal-close')) {
4323
4227
  modal.classList.remove('active');
4324
4228
  }
4325
4229
  });
@@ -4328,7 +4232,7 @@ function setupMainAppHandlers() {
4328
4232
  // Account modal tab switching
4329
4233
  $qa('.modal-tab[data-modal-tab]').forEach(tab => {
4330
4234
  tab.addEventListener('click', () => {
4331
- $qa('.modal-tab').forEach(t => t.classList.remove('active'));
4235
+ $qa('.modal-tab[data-modal-tab]').forEach(t => t.classList.remove('active'));
4332
4236
  $qa('.modal-tab-content').forEach(c => c.classList.remove('active'));
4333
4237
  tab.classList.add('active');
4334
4238
  const target = $(tab.dataset.modalTab);
@@ -4746,12 +4650,10 @@ function setupMainAppHandlers() {
4746
4650
  if (targetEl) {
4747
4651
  try {
4748
4652
  let value = '';
4749
- if (targetId === 'wallet-xpub') {
4653
+ if (targetId === 'wallet-xpub' || targetId === 'wallet-tab-xpub') {
4750
4654
  value = state.hdRoot?.toXpub?.() || '';
4751
- } else if (targetId === 'identity-wallet-xpub') {
4752
- value = getCurrentWalletIdentity().xpub || '';
4753
- } else if (targetId === 'identity-wallet-peerid') {
4754
- value = getCurrentWalletIdentity().peerId || '';
4655
+ } else if (targetId === 'wallet-tab-peerid') {
4656
+ value = deriveAccountPeerId() || '';
4755
4657
  } else if (targetId === 'wallet-xprv') {
4756
4658
  if (targetEl.dataset.revealed !== 'true') {
4757
4659
  alert('Reveal the xprv first to copy it.');
@@ -4844,45 +4746,74 @@ function setupMainAppHandlers() {
4844
4746
  });
4845
4747
 
4846
4748
  // Wallet tab controls
4847
- const handleWalletSelectChange = (e) => {
4749
+ $('wallet-active-select')?.addEventListener('change', (e) => {
4848
4750
  const walletId = Number.parseInt(e.target.value, 10);
4849
4751
  if (Number.isNaN(walletId)) return;
4850
4752
  state.activeWalletId = walletId;
4851
4753
  closeWalletActionMenus();
4852
- closeAssetActionOverlay();
4853
4754
  renderWalletSelector();
4854
- updateWalletBondDisplay();
4855
4755
  renderAccountsList();
4856
4756
  updateCustomPathDefault();
4857
- };
4858
- $('account-wallet-select')?.addEventListener('change', handleWalletSelectChange);
4859
- $('wallet-active-select')?.addEventListener('change', handleWalletSelectChange);
4860
- $('wallet-manage-tab')?.addEventListener('click', () => {
4861
- closeWalletActionMenus();
4862
- closeAssetActionOverlay();
4863
- $qa('.modal-tab').forEach(t => t.classList.remove('active'));
4864
- $qa('.modal-tab-content').forEach(c => c.classList.remove('active'));
4865
- $('wallet-manage-tab')?.classList.add('active');
4866
- $('wallet-tab-content')?.classList.add('active');
4867
- showWalletsView();
4868
4757
  });
4869
4758
  $('wallet-manage-btn')?.addEventListener('click', () => {
4870
4759
  closeWalletActionMenus();
4871
- closeAssetActionOverlay();
4872
4760
  showWalletsView();
4873
4761
  });
4874
4762
  $('wallet-scan-btn')?.addEventListener('click', () => {
4875
4763
  scanActiveAccounts();
4876
4764
  });
4877
- $('wallet-asset-action-close')?.addEventListener('click', closeAssetActionOverlay);
4765
+ const sendAction = $('wallet-send-action');
4766
+ const receiveAction = $('wallet-receive-action');
4767
+ $('wallet-send-btn')?.addEventListener('click', (e) => {
4768
+ e.stopPropagation();
4769
+ updateWalletActionMenus();
4770
+ const sendMenu = $('wallet-send-menu');
4771
+ const receiveMenu = $('wallet-receive-menu');
4772
+ if (!sendMenu || !receiveMenu) return;
4773
+ const nextVisible = !sendMenu.classList.contains('visible');
4774
+ receiveMenu.classList.remove('visible');
4775
+ sendMenu.classList.toggle('visible', nextVisible);
4776
+ });
4777
+ $('wallet-receive-btn-main')?.addEventListener('click', (e) => {
4778
+ e.stopPropagation();
4779
+ updateWalletActionMenus();
4780
+ const sendMenu = $('wallet-send-menu');
4781
+ const receiveMenu = $('wallet-receive-menu');
4782
+ if (!sendMenu || !receiveMenu) return;
4783
+ const nextVisible = !receiveMenu.classList.contains('visible');
4784
+ sendMenu.classList.remove('visible');
4785
+ receiveMenu.classList.toggle('visible', nextVisible);
4786
+ });
4787
+ $qa('#wallet-send-menu .ph-action-menu-item').forEach((btn) => {
4788
+ btn.addEventListener('click', (e) => {
4789
+ e.stopPropagation();
4790
+ const chain = btn.dataset.chain;
4791
+ const acct = getWalletAccountForChain(chain);
4792
+ closeWalletActionMenus();
4793
+ if (!acct) return;
4794
+ showSendView(state.activeAccounts.indexOf(acct));
4795
+ });
4796
+ });
4797
+ $qa('#wallet-receive-menu .ph-action-menu-item').forEach((btn) => {
4798
+ btn.addEventListener('click', (e) => {
4799
+ e.stopPropagation();
4800
+ const chain = btn.dataset.chain;
4801
+ const acct = getWalletAccountForChain(chain);
4802
+ closeWalletActionMenus();
4803
+ if (!acct) return;
4804
+ showReceiveModal(acct);
4805
+ });
4806
+ });
4807
+ _root.addEventListener('click', (e) => {
4808
+ if (sendAction?.contains(e.target) || receiveAction?.contains(e.target)) return;
4809
+ closeWalletActionMenus();
4810
+ });
4878
4811
  $('wallet-export-btn-main')?.addEventListener('click', () => {
4879
4812
  closeWalletActionMenus();
4880
- closeAssetActionOverlay();
4881
4813
  showExportView();
4882
4814
  });
4883
4815
  $('wallet-advanced-btn-main')?.addEventListener('click', () => {
4884
4816
  closeWalletActionMenus();
4885
- closeAssetActionOverlay();
4886
4817
  showAdvancedView();
4887
4818
  });
4888
4819
  $('wallet-wallets-back')?.addEventListener('click', () => {
@@ -6060,27 +5991,14 @@ export async function createWalletUI(rootElement, options = {}) {
6060
5991
  return {
6061
5992
  /** Open the login modal */
6062
5993
  openLogin() {
6063
- if (state.loggedIn) {
6064
- const accountModal = document.getElementById('keys-modal');
6065
- if (accountModal) accountModal.classList.add('active');
6066
- return;
6067
- }
6068
- const loginModal = document.getElementById('login-modal');
6069
- if (loginModal) {
6070
- loginModal.classList.add('active');
6071
- const storedTab = loginModal.querySelector('[data-method="stored"]');
6072
- if (storedTab && storedTab.style.display !== 'none') storedTab.click();
6073
- }
5994
+ const modal = document.getElementById('login-modal');
5995
+ if (modal) modal.classList.add('active');
6074
5996
  },
6075
5997
  /** Open the account / keys modal (requires login first) */
6076
5998
  openAccount() {
6077
5999
  const modal = document.getElementById('keys-modal');
6078
6000
  if (modal) modal.classList.add('active');
6079
6001
  },
6080
- /** Clear the wallet session; host applications own any visible logout control */
6081
- logout() {
6082
- logout();
6083
- },
6084
6002
  /** Remove all injected wallet UI elements from the DOM */
6085
6003
  destroy() {
6086
6004
  const container = document.getElementById('hd-wallet-ui-container');