hd-wallet-ui 2.0.5 → 2.0.7

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.5",
3
+ "version": "2.0.7",
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.5",
43
+ "hd-wallet-wasm": "^2.0.7",
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
@@ -28,6 +28,8 @@ window.Buffer = Buffer;
28
28
 
29
29
  import { getModalHTML } from './template.js';
30
30
  import WalletStorage, { StorageMethod } from './wallet-storage.js';
31
+ import { safeCopyText } from './clipboard.js';
32
+ import { normalizeTabHash } from './hash.js';
31
33
 
32
34
  import {
33
35
  cryptoConfig,
@@ -74,14 +76,7 @@ const $ = (id) => {
74
76
  return null;
75
77
  };
76
78
 
77
- export function normalizeTabHash(rawHash) {
78
- return String(rawHash || '')
79
- .replace(/^\/+/g, '')
80
- .split(/[/?#]/)[0]
81
- .replace(/[^a-z0-9_-]/gi, '')
82
- .replace(/-tab$/i, '')
83
- .toLowerCase();
84
- }
79
+ export { normalizeTabHash };
85
80
 
86
81
  const $q = (sel) => _root.querySelector(sel) || (_root !== document ? document.querySelector(sel) : null);
87
82
  const $qa = (sel) => {
@@ -1612,9 +1607,7 @@ async function handleAccountAction(action, idx) {
1612
1607
  showReceiveModal(acct);
1613
1608
  break;
1614
1609
  case 'copy':
1615
- try {
1616
- await navigator.clipboard.writeText(acct.address);
1617
- } catch {}
1610
+ await safeCopyText(acct.address);
1618
1611
  break;
1619
1612
  case 'toggle':
1620
1613
  toggleAccountActive(idx);
@@ -1669,7 +1662,7 @@ async function showReceiveModal(acct) {
1669
1662
  overlay.style.display = 'flex';
1670
1663
 
1671
1664
  overlay.querySelector('#wallet-receive-copy')?.addEventListener('click', () => {
1672
- navigator.clipboard.writeText(acct.address).catch(() => {});
1665
+ void safeCopyText(acct.address);
1673
1666
  }, { once: true });
1674
1667
 
1675
1668
  overlay.querySelector('#wallet-receive-close')?.addEventListener('click', () => {
@@ -2835,12 +2828,10 @@ function populateAccountAddressDropdown() {
2835
2828
 
2836
2829
  const copyBtn = $('account-address-copy');
2837
2830
  if (copyBtn) {
2838
- copyBtn.onclick = () => {
2839
- if (xpubStr) {
2840
- navigator.clipboard.writeText(xpubStr).then(() => {
2841
- copyBtn.title = 'Copied!';
2842
- setTimeout(() => { copyBtn.title = 'Copy xpub'; }, 1500);
2843
- });
2831
+ copyBtn.onclick = async () => {
2832
+ if (xpubStr && await safeCopyText(xpubStr)) {
2833
+ copyBtn.title = 'Copied!';
2834
+ setTimeout(() => { copyBtn.title = 'Copy xpub'; }, 1500);
2844
2835
  }
2845
2836
  };
2846
2837
  }
@@ -2856,12 +2847,10 @@ function populateAccountAddressDropdown() {
2856
2847
  }
2857
2848
  const peerCopyBtn = $('account-peerid-copy');
2858
2849
  if (peerCopyBtn) {
2859
- peerCopyBtn.onclick = () => {
2860
- if (peerIdStr) {
2861
- navigator.clipboard.writeText(peerIdStr).then(() => {
2862
- peerCopyBtn.title = 'Copied!';
2863
- setTimeout(() => { peerCopyBtn.title = 'Copy PeerID'; }, 1500);
2864
- });
2850
+ peerCopyBtn.onclick = async () => {
2851
+ if (peerIdStr && await safeCopyText(peerIdStr)) {
2852
+ peerCopyBtn.title = 'Copied!';
2853
+ setTimeout(() => { peerCopyBtn.title = 'Copy PeerID'; }, 1500);
2865
2854
  }
2866
2855
  };
2867
2856
  }
@@ -4683,9 +4672,12 @@ function setupMainAppHandlers() {
4683
4672
  if (!value) {
4684
4673
  throw new Error('Nothing to copy');
4685
4674
  }
4686
- await navigator.clipboard.writeText(value);
4687
- btn.classList.add('copied');
4688
- setTimeout(() => btn.classList.remove('copied'), 1500);
4675
+ if (await safeCopyText(value)) {
4676
+ btn.classList.add('copied');
4677
+ setTimeout(() => btn.classList.remove('copied'), 1500);
4678
+ } else {
4679
+ throw new Error('Clipboard unavailable');
4680
+ }
4689
4681
  } catch (err) {
4690
4682
  console.error('Copy failed:', err);
4691
4683
  }
@@ -5050,7 +5042,9 @@ function setupMainAppHandlers() {
5050
5042
  $('copy-vcard')?.addEventListener('click', async () => {
5051
5043
  const vcard = state._exportedVCard || '';
5052
5044
  try {
5053
- await navigator.clipboard.writeText(vcard);
5045
+ if (!await safeCopyText(vcard)) {
5046
+ throw new Error('Clipboard unavailable');
5047
+ }
5054
5048
  const btn = $('copy-vcard');
5055
5049
  if (btn) {
5056
5050
  btn.textContent = 'Copied!';
@@ -5697,7 +5691,7 @@ function setupTrustHandlers() {
5697
5691
  $('encrypt-copy-bundle')?.addEventListener('click', () => {
5698
5692
  const bundle = $('encrypt-bundle')?.value;
5699
5693
  if (bundle) {
5700
- navigator.clipboard.writeText(bundle).catch(() => {});
5694
+ void safeCopyText(bundle);
5701
5695
  }
5702
5696
  });
5703
5697
 
@@ -5853,7 +5847,8 @@ function setupHomepageHandlers() {
5853
5847
  btn.addEventListener('click', () => {
5854
5848
  const code = btn.closest('.code-block')?.querySelector('code');
5855
5849
  if (code) {
5856
- navigator.clipboard.writeText(code.textContent).then(() => {
5850
+ safeCopyText(code.textContent).then((copied) => {
5851
+ if (!copied) return;
5857
5852
  btn.title = 'Copied!';
5858
5853
  setTimeout(() => { btn.title = 'Copy code'; }, 2000);
5859
5854
  });
@@ -0,0 +1,44 @@
1
+ export async function safeCopyText(value) {
2
+ const text = String(value ?? '');
3
+ if (!text) return false;
4
+
5
+ try {
6
+ const writeText = globalThis.navigator?.clipboard?.writeText;
7
+ if (typeof writeText === 'function') {
8
+ await writeText.call(globalThis.navigator.clipboard, text);
9
+ return true;
10
+ }
11
+ } catch {
12
+ // Fall through to the legacy DOM copy path below.
13
+ }
14
+
15
+ return fallbackCopyText(text);
16
+ }
17
+
18
+ function fallbackCopyText(text) {
19
+ const doc = globalThis.document;
20
+ if (!doc?.body || typeof doc.createElement !== 'function' || typeof doc.execCommand !== 'function') {
21
+ return false;
22
+ }
23
+
24
+ const textarea = doc.createElement('textarea');
25
+ textarea.value = text;
26
+ textarea.readOnly = true;
27
+ textarea.setAttribute('aria-hidden', 'true');
28
+ textarea.style.position = 'fixed';
29
+ textarea.style.left = '-9999px';
30
+ textarea.style.top = '0';
31
+ textarea.style.opacity = '0';
32
+
33
+ doc.body.appendChild(textarea);
34
+ try {
35
+ textarea.focus();
36
+ textarea.select();
37
+ textarea.setSelectionRange(0, textarea.value.length);
38
+ return Boolean(doc.execCommand('copy'));
39
+ } catch {
40
+ return false;
41
+ } finally {
42
+ textarea.remove();
43
+ }
44
+ }
package/src/hash.js ADDED
@@ -0,0 +1,9 @@
1
+ export function normalizeTabHash(rawHash) {
2
+ return String(rawHash || '')
3
+ .replace(/^#+/g, '')
4
+ .replace(/^\/+/g, '')
5
+ .split(/[/?#]/)[0]
6
+ .replace(/[^a-z0-9_-]/gi, '')
7
+ .replace(/-tab$/i, '')
8
+ .toLowerCase();
9
+ }
package/styles/main.css CHANGED
@@ -1214,6 +1214,11 @@ body:has(.modal.active) .nav-bar {
1214
1214
  align-items: center;
1215
1215
  gap: 10px;
1216
1216
  margin-top: 4px;
1217
+ min-width: 0;
1218
+ }
1219
+
1220
+ .account-address-label {
1221
+ flex: 0 0 auto;
1217
1222
  }
1218
1223
 
1219
1224
  .account-address-select {
@@ -1236,9 +1241,9 @@ body:has(.modal.active) .nav-bar {
1236
1241
  white-space: nowrap;
1237
1242
  overflow: hidden;
1238
1243
  text-overflow: ellipsis;
1239
- max-width: 280px;
1240
- flex: 1;
1241
- min-width: 0;
1244
+ flex: 1 1 auto;
1245
+ min-width: 12ch;
1246
+ width: 100%;
1242
1247
  }
1243
1248
 
1244
1249
  .account-address-copy {
@@ -1246,10 +1251,13 @@ body:has(.modal.active) .nav-bar {
1246
1251
  border: none;
1247
1252
  color: var(--white-30);
1248
1253
  cursor: pointer;
1254
+ height: 24px;
1249
1255
  padding: 2px;
1256
+ width: 24px;
1250
1257
  flex-shrink: 0;
1251
1258
  display: flex;
1252
1259
  align-items: center;
1260
+ justify-content: center;
1253
1261
  }
1254
1262
  .account-address-copy:hover {
1255
1263
  color: var(--white-70);
@@ -1257,7 +1265,7 @@ body:has(.modal.active) .nav-bar {
1257
1265
 
1258
1266
  @media (max-width: 600px) {
1259
1267
  .account-address-display {
1260
- max-width: 140px;
1268
+ min-width: 10ch;
1261
1269
  }
1262
1270
  }
1263
1271
 
package/styles/widget.css CHANGED
@@ -1216,6 +1216,11 @@ body:has(#hd-wallet-ui-container .modal.active) #hd-wallet-ui-container .nav-bar
1216
1216
  align-items: center;
1217
1217
  gap: 10px;
1218
1218
  margin-top: 4px;
1219
+ min-width: 0;
1220
+ }
1221
+
1222
+ #hd-wallet-ui-container .account-address-label {
1223
+ flex: 0 0 auto;
1219
1224
  }
1220
1225
 
1221
1226
  #hd-wallet-ui-container .account-address-select {
@@ -1238,9 +1243,9 @@ body:has(#hd-wallet-ui-container .modal.active) #hd-wallet-ui-container .nav-bar
1238
1243
  white-space: nowrap;
1239
1244
  overflow: hidden;
1240
1245
  text-overflow: ellipsis;
1241
- max-width: 280px;
1242
- flex: 1;
1243
- min-width: 0;
1246
+ flex: 1 1 auto;
1247
+ min-width: 12ch;
1248
+ width: 100%;
1244
1249
  }
1245
1250
 
1246
1251
  #hd-wallet-ui-container .account-address-copy {
@@ -1248,10 +1253,13 @@ body:has(#hd-wallet-ui-container .modal.active) #hd-wallet-ui-container .nav-bar
1248
1253
  border: none;
1249
1254
  color: var(--white-30);
1250
1255
  cursor: pointer;
1256
+ height: 24px;
1251
1257
  padding: 2px;
1258
+ width: 24px;
1252
1259
  flex-shrink: 0;
1253
1260
  display: flex;
1254
1261
  align-items: center;
1262
+ justify-content: center;
1255
1263
  }
1256
1264
  #hd-wallet-ui-container .account-address-copy:hover {
1257
1265
  color: var(--white-70);
@@ -1259,7 +1267,7 @@ body:has(#hd-wallet-ui-container .modal.active) #hd-wallet-ui-container .nav-bar
1259
1267
 
1260
1268
  @media (max-width: 600px) {
1261
1269
  #hd-wallet-ui-container .account-address-display {
1262
- max-width: 140px;
1270
+ min-width: 10ch;
1263
1271
  }
1264
1272
  }
1265
1273