hd-wallet-ui 2.0.18 → 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.18",
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.18",
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
  }
@@ -2666,6 +2559,21 @@ function login(keys) {
2666
2559
  if (xpubEl) {
2667
2560
  setTruncatedValue(xpubEl, state.hdRoot.toXpub() || 'N/A');
2668
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
+ }
2669
2577
  populateAccountAddressDropdown();
2670
2578
  if (xprvEl) {
2671
2579
  xprvEl.textContent = 'Hidden (click reveal)';
@@ -2909,9 +2817,43 @@ function downloadData(data, filename, mimeType) {
2909
2817
  // Wallet Address Population & Balance Fetching
2910
2818
  // =============================================================================
2911
2819
 
2820
+ // Account header — show xpub only
2912
2821
  function populateAccountAddressDropdown() {
2913
- renderWalletSelector();
2914
- 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
+ }
2915
2857
  }
2916
2858
 
2917
2859
  function populateWalletAddresses() {
@@ -3370,11 +3312,10 @@ function generateVCard(info, { skipPhoto = false } = {}) {
3370
3312
  }
3371
3313
 
3372
3314
  if (info.includeKeys && state.wallet?.x25519) {
3373
- const identity = getCurrentWalletIdentity();
3374
3315
  person.KEY = [
3375
- // Always include the selected wallet xPub.
3376
- ...(identity.xpub ? [{
3377
- XPUB: identity.xpub,
3316
+ // Always include xPub
3317
+ ...(state.hdRoot?.toXpub ? [{
3318
+ XPUB: state.hdRoot.toXpub(),
3378
3319
  LABEL: '',
3379
3320
  }] : []),
3380
3321
  // X25519 encryption key
@@ -3383,7 +3324,8 @@ function generateVCard(info, { skipPhoto = false } = {}) {
3383
3324
  LABEL: 'X25519',
3384
3325
  },
3385
3326
  // Active accounts from wallet scan
3386
- ...getCurrentWalletSigningAccounts()
3327
+ ...state.activeAccounts
3328
+ .filter(a => a.active && isSigningAccount(a))
3387
3329
  .flatMap(a => {
3388
3330
  const entries = [];
3389
3331
  const pathLabel = a.path || `m/44'/${a.coinType}'/${a.account}'/0/${a.index}`;
@@ -3404,9 +3346,8 @@ function generateVCard(info, { skipPhoto = false } = {}) {
3404
3346
  return entries;
3405
3347
  }),
3406
3348
  ];
3407
- } else if (info.xpubOnly) {
3408
- const identity = getCurrentWalletIdentity();
3409
- 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: '' }];
3410
3351
  }
3411
3352
 
3412
3353
  const note = info.includeKeys
@@ -3458,16 +3399,15 @@ function getSignableBody(vcardText) {
3458
3399
  }
3459
3400
 
3460
3401
  function signVCard(vcardText) {
3461
- const signatureKey = getCurrentWalletSignatureKey();
3462
- if (!signatureKey?.privateKey) return vcardText;
3402
+ if (!state.wallet?.ed25519?.privateKey) return vcardText;
3463
3403
 
3464
3404
  const body = getSignableBody(vcardText);
3465
3405
  const messageBytes = new TextEncoder().encode(body);
3466
- const signature = hdWallet().curves.ed25519.sign(messageBytes, signatureKey.privateKey);
3406
+ const signature = hdWallet().curves.ed25519.sign(messageBytes, state.wallet.ed25519.privateKey);
3467
3407
  const sigB64 = toBase64(signature);
3468
3408
 
3469
- // Encode signature + selected wallet derivation path.
3470
- 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`;
3471
3411
 
3472
3412
  // Find highest itemN and key index
3473
3413
  let maxItem = 0;
@@ -3836,8 +3776,8 @@ function isPasskeySupported() {
3836
3776
 
3837
3777
  // Track selected remember method (pin or passkey) for each login type
3838
3778
  const rememberMethod = {
3839
- password: 'pin',
3840
- seed: 'pin'
3779
+ password: 'passkey',
3780
+ seed: 'passkey'
3841
3781
  };
3842
3782
 
3843
3783
  function setupLoginHandlers() {
@@ -4283,7 +4223,7 @@ function setupMainAppHandlers() {
4283
4223
  // Modal close handlers
4284
4224
  $qa('.modal').forEach(modal => {
4285
4225
  modal.addEventListener('click', (e) => {
4286
- if (e.target === modal || e.target.closest?.('.modal-close')) {
4226
+ if (e.target === modal || e.target.classList.contains('modal-close')) {
4287
4227
  modal.classList.remove('active');
4288
4228
  }
4289
4229
  });
@@ -4292,7 +4232,7 @@ function setupMainAppHandlers() {
4292
4232
  // Account modal tab switching
4293
4233
  $qa('.modal-tab[data-modal-tab]').forEach(tab => {
4294
4234
  tab.addEventListener('click', () => {
4295
- $qa('.modal-tab').forEach(t => t.classList.remove('active'));
4235
+ $qa('.modal-tab[data-modal-tab]').forEach(t => t.classList.remove('active'));
4296
4236
  $qa('.modal-tab-content').forEach(c => c.classList.remove('active'));
4297
4237
  tab.classList.add('active');
4298
4238
  const target = $(tab.dataset.modalTab);
@@ -4710,12 +4650,10 @@ function setupMainAppHandlers() {
4710
4650
  if (targetEl) {
4711
4651
  try {
4712
4652
  let value = '';
4713
- if (targetId === 'wallet-xpub') {
4653
+ if (targetId === 'wallet-xpub' || targetId === 'wallet-tab-xpub') {
4714
4654
  value = state.hdRoot?.toXpub?.() || '';
4715
- } else if (targetId === 'identity-wallet-xpub') {
4716
- value = getCurrentWalletIdentity().xpub || '';
4717
- } else if (targetId === 'identity-wallet-peerid') {
4718
- value = getCurrentWalletIdentity().peerId || '';
4655
+ } else if (targetId === 'wallet-tab-peerid') {
4656
+ value = deriveAccountPeerId() || '';
4719
4657
  } else if (targetId === 'wallet-xprv') {
4720
4658
  if (targetEl.dataset.revealed !== 'true') {
4721
4659
  alert('Reveal the xprv first to copy it.');
@@ -4808,45 +4746,74 @@ function setupMainAppHandlers() {
4808
4746
  });
4809
4747
 
4810
4748
  // Wallet tab controls
4811
- const handleWalletSelectChange = (e) => {
4749
+ $('wallet-active-select')?.addEventListener('change', (e) => {
4812
4750
  const walletId = Number.parseInt(e.target.value, 10);
4813
4751
  if (Number.isNaN(walletId)) return;
4814
4752
  state.activeWalletId = walletId;
4815
4753
  closeWalletActionMenus();
4816
- closeAssetActionOverlay();
4817
4754
  renderWalletSelector();
4818
- updateWalletBondDisplay();
4819
4755
  renderAccountsList();
4820
4756
  updateCustomPathDefault();
4821
- };
4822
- $('account-wallet-select')?.addEventListener('change', handleWalletSelectChange);
4823
- $('wallet-active-select')?.addEventListener('change', handleWalletSelectChange);
4824
- $('wallet-manage-tab')?.addEventListener('click', () => {
4825
- closeWalletActionMenus();
4826
- closeAssetActionOverlay();
4827
- $qa('.modal-tab').forEach(t => t.classList.remove('active'));
4828
- $qa('.modal-tab-content').forEach(c => c.classList.remove('active'));
4829
- $('wallet-manage-tab')?.classList.add('active');
4830
- $('wallet-tab-content')?.classList.add('active');
4831
- showWalletsView();
4832
4757
  });
4833
4758
  $('wallet-manage-btn')?.addEventListener('click', () => {
4834
4759
  closeWalletActionMenus();
4835
- closeAssetActionOverlay();
4836
4760
  showWalletsView();
4837
4761
  });
4838
4762
  $('wallet-scan-btn')?.addEventListener('click', () => {
4839
4763
  scanActiveAccounts();
4840
4764
  });
4841
- $('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
+ });
4842
4811
  $('wallet-export-btn-main')?.addEventListener('click', () => {
4843
4812
  closeWalletActionMenus();
4844
- closeAssetActionOverlay();
4845
4813
  showExportView();
4846
4814
  });
4847
4815
  $('wallet-advanced-btn-main')?.addEventListener('click', () => {
4848
4816
  closeWalletActionMenus();
4849
- closeAssetActionOverlay();
4850
4817
  showAdvancedView();
4851
4818
  });
4852
4819
  $('wallet-wallets-back')?.addEventListener('click', () => {
@@ -6024,27 +5991,14 @@ export async function createWalletUI(rootElement, options = {}) {
6024
5991
  return {
6025
5992
  /** Open the login modal */
6026
5993
  openLogin() {
6027
- if (state.loggedIn) {
6028
- const accountModal = document.getElementById('keys-modal');
6029
- if (accountModal) accountModal.classList.add('active');
6030
- return;
6031
- }
6032
- const loginModal = document.getElementById('login-modal');
6033
- if (loginModal) {
6034
- loginModal.classList.add('active');
6035
- const storedTab = loginModal.querySelector('[data-method="stored"]');
6036
- if (storedTab && storedTab.style.display !== 'none') storedTab.click();
6037
- }
5994
+ const modal = document.getElementById('login-modal');
5995
+ if (modal) modal.classList.add('active');
6038
5996
  },
6039
5997
  /** Open the account / keys modal (requires login first) */
6040
5998
  openAccount() {
6041
5999
  const modal = document.getElementById('keys-modal');
6042
6000
  if (modal) modal.classList.add('active');
6043
6001
  },
6044
- /** Clear the wallet session; host applications own any visible logout control */
6045
- logout() {
6046
- logout();
6047
- },
6048
6002
  /** Remove all injected wallet UI elements from the DOM */
6049
6003
  destroy() {
6050
6004
  const container = document.getElementById('hd-wallet-ui-container');