@enbox/browser 0.3.45 → 0.3.46
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/dist/browser.mjs +168 -94
- package/dist/browser.mjs.map +3 -3
- package/dist/esm/browser-connect-handler.js +2 -1
- package/dist/esm/browser-connect-handler.js.map +1 -1
- package/dist/esm/ui/connect-modal.js +301 -136
- package/dist/esm/ui/connect-modal.js.map +1 -1
- package/dist/types/browser-connect-handler.d.ts +8 -1
- package/dist/types/browser-connect-handler.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/ui/connect-modal.d.ts +56 -3
- package/dist/types/ui/connect-modal.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/browser-connect-handler.ts +11 -1
- package/src/index.ts +1 -1
- package/src/ui/connect-modal.ts +371 -138
|
@@ -13,9 +13,18 @@
|
|
|
13
13
|
* 2. **Stage** — a height-stable region where states crossfade: the QR
|
|
14
14
|
* (or deep link on phones), the pairing-code input, progress, success,
|
|
15
15
|
* and error states.
|
|
16
|
-
* 3. **Footer** — the alternate-method link and the wallet
|
|
17
|
-
* disclosure (
|
|
18
|
-
* demoted to an option).
|
|
16
|
+
* 3. **Footer** — one row holding the alternate-method link and the wallet
|
|
17
|
+
* switcher disclosure (a tile grid with search + custom URL — the
|
|
18
|
+
* entire legacy selector, demoted to an option).
|
|
19
|
+
*
|
|
20
|
+
* Theming: the stylesheet is static and reads `--ec-*` design tokens.
|
|
21
|
+
* Light/dark follows the visitor's system via `prefers-color-scheme` and
|
|
22
|
+
* updates live; the embedding app can force an appearance or override
|
|
23
|
+
* palette tokens through {@link ConnectModalOptions.theme}. Overrides are
|
|
24
|
+
* applied as inline custom properties — never interpolated into the
|
|
25
|
+
* stylesheet — so a hostile string can't escape its declaration. The QR
|
|
26
|
+
* card alone is theme-invariant (dark modules on white scan most
|
|
27
|
+
* reliably).
|
|
19
28
|
*
|
|
20
29
|
* The phone path drives the `@enbox/connect` relay transport via
|
|
21
30
|
* {@link runRelayConnect}; the browser path drives the popup transport via
|
|
@@ -48,6 +57,20 @@ const REMINT_SAFETY_MS = 30000;
|
|
|
48
57
|
const REMINT_MIN_MS = 10000;
|
|
49
58
|
/** Pairing code length (matches the wallet's generated PIN). */
|
|
50
59
|
const PIN_LENGTH = 4;
|
|
60
|
+
/** Catalog size beyond which the wallet switcher shows its search bar. */
|
|
61
|
+
const WALLET_SEARCH_THRESHOLD = 4; // one full row of tiles
|
|
62
|
+
/** Design-token custom property behind each palette key. */
|
|
63
|
+
const PALETTE_TOKENS = {
|
|
64
|
+
background: '--ec-bg',
|
|
65
|
+
surface: '--ec-surface',
|
|
66
|
+
text: '--ec-text',
|
|
67
|
+
textMuted: '--ec-muted',
|
|
68
|
+
border: '--ec-border',
|
|
69
|
+
accent: '--ec-accent',
|
|
70
|
+
accentContrast: '--ec-accent-contrast',
|
|
71
|
+
danger: '--ec-danger',
|
|
72
|
+
success: '--ec-success',
|
|
73
|
+
};
|
|
51
74
|
/**
|
|
52
75
|
* Resolves a wallet origin to its relay `connectServerUrl` via the wallet's
|
|
53
76
|
* `/.well-known/enbox-connect` document ({@link fetchWalletWellKnown} — the
|
|
@@ -158,14 +181,13 @@ export function runConnectModal(options) {
|
|
|
158
181
|
// Method resolution order: explicit option → remembered → phone.
|
|
159
182
|
let method = (_j = (_h = options.preferredMethod) !== null && _h !== void 0 ? _h : lastChoice === null || lastChoice === void 0 ? void 0 : lastChoice.method) !== null && _j !== void 0 ? _j : 'phone';
|
|
160
183
|
return new Promise((resolve, reject) => {
|
|
161
|
-
var _a, _b;
|
|
184
|
+
var _a, _b, _c, _d;
|
|
162
185
|
// ── Host + shadow root ─────────────────────────────────────
|
|
163
186
|
const host = document.createElement('div');
|
|
164
187
|
host.id = 'enbox-connect-modal';
|
|
165
188
|
const shadow = host.attachShadow({ mode: 'open' });
|
|
166
|
-
const isDark = (_b = (_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, '(prefers-color-scheme: dark)').matches) !== null && _b !== void 0 ? _b : false;
|
|
167
189
|
const style = document.createElement('style');
|
|
168
|
-
style.textContent =
|
|
190
|
+
style.textContent = MODAL_STYLES;
|
|
169
191
|
shadow.appendChild(style);
|
|
170
192
|
const overlay = document.createElement('div');
|
|
171
193
|
overlay.className = 'overlay';
|
|
@@ -174,6 +196,42 @@ export function runConnectModal(options) {
|
|
|
174
196
|
modal.setAttribute('role', 'dialog');
|
|
175
197
|
modal.setAttribute('aria-modal', 'true');
|
|
176
198
|
modal.setAttribute('aria-label', `Connect to ${appName}`);
|
|
199
|
+
// ── Theme ──────────────────────────────────────────────────
|
|
200
|
+
// System light/dark is pure CSS (`prefers-color-scheme`), so it tracks
|
|
201
|
+
// the visitor live. A forced appearance pins the token block instead.
|
|
202
|
+
const theme = (_a = options.theme) !== null && _a !== void 0 ? _a : {};
|
|
203
|
+
const appearance = (_b = theme.appearance) !== null && _b !== void 0 ? _b : 'auto';
|
|
204
|
+
if (appearance !== 'auto') {
|
|
205
|
+
modal.setAttribute('data-appearance', appearance);
|
|
206
|
+
}
|
|
207
|
+
// Dapp palette overrides land as inline custom properties: setProperty
|
|
208
|
+
// parses each value as a lone declaration, so a hostile string cannot
|
|
209
|
+
// escape into the stylesheet.
|
|
210
|
+
const systemDark = (_c = window.matchMedia) === null || _c === void 0 ? void 0 : _c.call(window, '(prefers-color-scheme: dark)');
|
|
211
|
+
const hasPaletteOverrides = theme.accent !== undefined || theme.accentContrast !== undefined
|
|
212
|
+
|| theme.light !== undefined || theme.dark !== undefined;
|
|
213
|
+
const applyPaletteTokens = () => {
|
|
214
|
+
const scheme = appearance === 'auto'
|
|
215
|
+
? ((systemDark === null || systemDark === void 0 ? void 0 : systemDark.matches) === true ? 'dark' : 'light')
|
|
216
|
+
: appearance;
|
|
217
|
+
const palette = Object.assign({ accent: theme.accent, accentContrast: theme.accentContrast }, theme[scheme]);
|
|
218
|
+
for (const key of Object.keys(PALETTE_TOKENS)) {
|
|
219
|
+
const value = palette[key];
|
|
220
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
221
|
+
modal.style.setProperty(PALETTE_TOKENS[key], value);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
modal.style.removeProperty(PALETTE_TOKENS[key]);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const onSchemeChange = () => applyPaletteTokens();
|
|
229
|
+
if (hasPaletteOverrides) {
|
|
230
|
+
applyPaletteTokens();
|
|
231
|
+
if (appearance === 'auto') {
|
|
232
|
+
(_d = systemDark === null || systemDark === void 0 ? void 0 : systemDark.addEventListener) === null || _d === void 0 ? void 0 : _d.call(systemDark, 'change', onSchemeChange);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
177
235
|
// ── Session state ──────────────────────────────────────────
|
|
178
236
|
let settled = false;
|
|
179
237
|
let relaySession;
|
|
@@ -182,13 +240,15 @@ export function runConnectModal(options) {
|
|
|
182
240
|
let pinResolve;
|
|
183
241
|
const discoveryCache = new Map();
|
|
184
242
|
const cleanup = () => {
|
|
243
|
+
var _a;
|
|
185
244
|
relaySession === null || relaySession === void 0 ? void 0 : relaySession.cancel();
|
|
186
245
|
clearTimeout(remintTimer);
|
|
187
246
|
document.removeEventListener('keydown', onKeydown);
|
|
247
|
+
(_a = systemDark === null || systemDark === void 0 ? void 0 : systemDark.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(systemDark, 'change', onSchemeChange);
|
|
188
248
|
try {
|
|
189
249
|
document.body.removeChild(host);
|
|
190
250
|
}
|
|
191
|
-
catch ( /* best effort */
|
|
251
|
+
catch ( /* best effort */_b) { /* best effort */ }
|
|
192
252
|
};
|
|
193
253
|
const settle = (settler) => {
|
|
194
254
|
if (settled) {
|
|
@@ -237,12 +297,14 @@ export function runConnectModal(options) {
|
|
|
237
297
|
}
|
|
238
298
|
const qrBox = document.createElement('div');
|
|
239
299
|
qrBox.className = 'qr-box';
|
|
300
|
+
// Fixed colors on the white card: dark-on-light QRs scan reliably in
|
|
301
|
+
// both appearances (inverted codes trip up some camera apps).
|
|
240
302
|
qrBox.replaceChildren(qrToSvg(encodeQr(handoff.walletUri), {
|
|
241
|
-
dark:
|
|
303
|
+
dark: '#14141f',
|
|
242
304
|
light: 'transparent',
|
|
243
305
|
quietZone: 2,
|
|
244
306
|
}));
|
|
245
|
-
setStage(qrBox, el('p', 'stage-caption', 'Scan with your phone’s camera'), el('p', 'stage-subline', 'Your wallet stays on your phone
|
|
307
|
+
setStage(qrBox, el('p', 'stage-caption', 'Scan with your phone’s camera'), el('p', 'stage-subline', 'Your wallet stays on your phone.'));
|
|
246
308
|
};
|
|
247
309
|
const renderClaimed = () => {
|
|
248
310
|
const pulse = document.createElement('div');
|
|
@@ -499,59 +561,69 @@ export function runConnectModal(options) {
|
|
|
499
561
|
method = next;
|
|
500
562
|
yield startMethod();
|
|
501
563
|
});
|
|
502
|
-
// ── Footer: method link + wallet
|
|
564
|
+
// ── Footer: one row (method link + wallet toggle), panel below ──
|
|
503
565
|
const renderFooter = () => {
|
|
504
566
|
footer.replaceChildren();
|
|
567
|
+
const row = document.createElement('div');
|
|
568
|
+
row.className = 'footer-row';
|
|
505
569
|
if (method === 'phone') {
|
|
506
570
|
const alt = document.createElement('button');
|
|
507
571
|
alt.className = 'footer-link method-link';
|
|
508
|
-
alt.textContent = 'No phone
|
|
572
|
+
alt.textContent = 'No phone? Use this browser →';
|
|
509
573
|
// startPopup runs synchronously in this click handler.
|
|
510
574
|
alt.addEventListener('click', () => { startPopup(); });
|
|
511
|
-
|
|
575
|
+
row.appendChild(alt);
|
|
512
576
|
}
|
|
513
577
|
else if (!isMobile) {
|
|
514
578
|
const alt = document.createElement('button');
|
|
515
579
|
alt.className = 'footer-link method-link';
|
|
516
580
|
alt.textContent = 'Use your phone instead →';
|
|
517
581
|
alt.addEventListener('click', () => { void switchMethod('phone'); });
|
|
518
|
-
|
|
582
|
+
row.appendChild(alt);
|
|
519
583
|
}
|
|
584
|
+
let panel;
|
|
520
585
|
if (!lockedWallet && wallets.length > 0) {
|
|
521
|
-
|
|
586
|
+
const switcher = buildWalletSwitcher();
|
|
587
|
+
row.appendChild(switcher.toggle);
|
|
588
|
+
panel = switcher.panel;
|
|
589
|
+
}
|
|
590
|
+
if (row.childElementCount > 0) {
|
|
591
|
+
footer.appendChild(row);
|
|
592
|
+
}
|
|
593
|
+
if (panel !== undefined) {
|
|
594
|
+
footer.appendChild(panel);
|
|
522
595
|
}
|
|
523
596
|
};
|
|
524
597
|
const buildWalletSwitcher = () => {
|
|
525
598
|
var _a, _b;
|
|
526
|
-
const wrap = document.createElement('div');
|
|
527
|
-
wrap.className = 'wallet-switcher';
|
|
528
599
|
const current = wallets.find((wallet) => wallet.url === walletUrl);
|
|
529
600
|
const toggle = document.createElement('button');
|
|
530
601
|
toggle.className = 'footer-link wallet-toggle';
|
|
531
602
|
toggle.setAttribute('aria-expanded', 'false');
|
|
532
|
-
toggle.textContent = `Wallet: ${(_b = (_a = current === null || current === void 0 ? void 0 : current.name) !== null && _a !== void 0 ? _a : walletUrl) !== null && _b !== void 0 ? _b : 'Choose'} ▾`;
|
|
603
|
+
toggle.textContent = `Wallet: ${(_b = (_a = current === null || current === void 0 ? void 0 : current.name) !== null && _a !== void 0 ? _a : hostnameOf(walletUrl)) !== null && _b !== void 0 ? _b : 'Choose'} ▾`;
|
|
533
604
|
const panel = document.createElement('div');
|
|
534
605
|
panel.className = 'wallet-panel';
|
|
535
606
|
panel.hidden = true;
|
|
536
|
-
// Search filter —
|
|
537
|
-
const
|
|
607
|
+
// Search filter — appears once the catalog outgrows one grid row.
|
|
608
|
+
const tiles = [];
|
|
538
609
|
const empty = document.createElement('div');
|
|
539
610
|
empty.className = 'wallet-empty';
|
|
540
611
|
empty.textContent = 'No wallets match your search.';
|
|
541
612
|
empty.hidden = true;
|
|
542
|
-
if (wallets.length >
|
|
613
|
+
if (wallets.length > WALLET_SEARCH_THRESHOLD) {
|
|
543
614
|
const search = document.createElement('input');
|
|
544
615
|
search.type = 'search';
|
|
545
616
|
search.className = 'wallet-search';
|
|
546
617
|
search.placeholder = 'Search wallets…';
|
|
618
|
+
search.setAttribute('aria-label', 'Search wallets');
|
|
547
619
|
search.addEventListener('input', () => {
|
|
548
620
|
const query = search.value.trim().toLowerCase();
|
|
549
621
|
let visible = 0;
|
|
550
|
-
for (const { wallet, el } of
|
|
622
|
+
for (const { wallet, el: tile } of tiles) {
|
|
551
623
|
const match = query === ''
|
|
552
624
|
|| wallet.name.toLowerCase().includes(query)
|
|
553
625
|
|| wallet.url.toLowerCase().includes(query);
|
|
554
|
-
|
|
626
|
+
tile.hidden = !match;
|
|
555
627
|
if (match) {
|
|
556
628
|
visible += 1;
|
|
557
629
|
}
|
|
@@ -560,26 +632,36 @@ export function runConnectModal(options) {
|
|
|
560
632
|
});
|
|
561
633
|
panel.appendChild(search);
|
|
562
634
|
}
|
|
635
|
+
// Square tiles, three per row; past two rows the grid scrolls in
|
|
636
|
+
// place so a large catalog never stretches the modal.
|
|
637
|
+
const scroll = document.createElement('div');
|
|
638
|
+
scroll.className = 'wallet-scroll';
|
|
639
|
+
const grid = document.createElement('div');
|
|
640
|
+
grid.className = 'wallet-grid';
|
|
563
641
|
for (const wallet of wallets) {
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
642
|
+
const tile = document.createElement('button');
|
|
643
|
+
tile.className = 'wallet-tile';
|
|
644
|
+
tile.title = wallet.name;
|
|
645
|
+
tile.appendChild(buildWalletIcon(wallet));
|
|
646
|
+
const tileName = document.createElement('span');
|
|
647
|
+
tileName.className = 'wallet-tile-name';
|
|
648
|
+
tileName.textContent = wallet.name;
|
|
649
|
+
tile.appendChild(tileName);
|
|
571
650
|
if (wallet.url === walletUrl) {
|
|
572
|
-
|
|
651
|
+
tile.classList.add('selected');
|
|
652
|
+
tile.setAttribute('aria-pressed', 'true');
|
|
573
653
|
}
|
|
574
|
-
|
|
654
|
+
tile.addEventListener('click', () => {
|
|
575
655
|
walletUrl = wallet.url;
|
|
576
656
|
renderFooter();
|
|
577
657
|
void startMethod();
|
|
578
658
|
});
|
|
579
|
-
|
|
580
|
-
|
|
659
|
+
tiles.push({ wallet, el: tile });
|
|
660
|
+
grid.appendChild(tile);
|
|
581
661
|
}
|
|
582
|
-
|
|
662
|
+
scroll.appendChild(grid);
|
|
663
|
+
scroll.appendChild(empty);
|
|
664
|
+
panel.appendChild(scroll);
|
|
583
665
|
// Custom wallet URL (the power-user path, demoted to the disclosure).
|
|
584
666
|
const custom = document.createElement('div');
|
|
585
667
|
custom.className = 'wallet-custom';
|
|
@@ -655,9 +737,7 @@ export function runConnectModal(options) {
|
|
|
655
737
|
panel.hidden = !panel.hidden;
|
|
656
738
|
toggle.setAttribute('aria-expanded', panel.hidden ? 'false' : 'true');
|
|
657
739
|
});
|
|
658
|
-
|
|
659
|
-
wrap.appendChild(panel);
|
|
660
|
-
return wrap;
|
|
740
|
+
return { toggle, panel };
|
|
661
741
|
};
|
|
662
742
|
// ── Global interactions ────────────────────────────────────
|
|
663
743
|
overlay.addEventListener('click', (event) => {
|
|
@@ -686,6 +766,18 @@ export function runConnectModal(options) {
|
|
|
686
766
|
function walletInCatalog(wallets, url) {
|
|
687
767
|
return wallets.some((wallet) => wallet.url === url);
|
|
688
768
|
}
|
|
769
|
+
/** Compact display form of a wallet URL for the switcher toggle. */
|
|
770
|
+
function hostnameOf(url) {
|
|
771
|
+
if (url === undefined) {
|
|
772
|
+
return undefined;
|
|
773
|
+
}
|
|
774
|
+
try {
|
|
775
|
+
return new URL(url).hostname;
|
|
776
|
+
}
|
|
777
|
+
catch (_a) {
|
|
778
|
+
return url;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
689
781
|
function el(tag, className, text) {
|
|
690
782
|
const node = document.createElement(tag);
|
|
691
783
|
node.className = className;
|
|
@@ -753,19 +845,42 @@ function trapFocus(shadow, event) {
|
|
|
753
845
|
}
|
|
754
846
|
}
|
|
755
847
|
// ─── Styles ──────────────────────────────────────────────────────
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
848
|
+
//
|
|
849
|
+
// One static sheet; every color reads a `--ec-*` token. Light values sit on
|
|
850
|
+
// `.modal`, dark values apply under `prefers-color-scheme` (so the system
|
|
851
|
+
// appearance tracks live) and under a forced `data-appearance`. Dapp
|
|
852
|
+
// palette overrides land as inline custom properties, which outrank all of
|
|
853
|
+
// these — nothing user-provided is ever concatenated into this string.
|
|
854
|
+
/** Light-appearance design tokens. */
|
|
855
|
+
const LIGHT_TOKENS = `
|
|
856
|
+
--ec-bg: #ffffff;
|
|
857
|
+
--ec-surface: #f5f6f8;
|
|
858
|
+
--ec-text: #17171f;
|
|
859
|
+
--ec-muted: #63636e;
|
|
860
|
+
--ec-border: #e3e4e9;
|
|
861
|
+
--ec-accent: #0a62d0;
|
|
862
|
+
--ec-accent-contrast: #ffffff;
|
|
863
|
+
--ec-danger: #c0392b;
|
|
864
|
+
--ec-success: #1e9e5a;`;
|
|
865
|
+
/** Dark-appearance design tokens. */
|
|
866
|
+
const DARK_TOKENS = `
|
|
867
|
+
--ec-bg: #181822;
|
|
868
|
+
--ec-surface: #23232f;
|
|
869
|
+
--ec-text: #e9e9f0;
|
|
870
|
+
--ec-muted: #9a9aa6;
|
|
871
|
+
--ec-border: #33333f;
|
|
872
|
+
--ec-accent: #58a6ff;
|
|
873
|
+
--ec-accent-contrast: #0b1526;
|
|
874
|
+
--ec-danger: #ff7a7a;
|
|
875
|
+
--ec-success: #43d17c;`;
|
|
876
|
+
const MODAL_STYLES = `
|
|
767
877
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
768
878
|
|
|
879
|
+
/* Keep the hidden attribute authoritative: author display values (e.g.
|
|
880
|
+
the panel's flex) would otherwise override the UA's [hidden] rule and
|
|
881
|
+
leave "collapsed" sections permanently expanded. */
|
|
882
|
+
[hidden] { display: none !important; }
|
|
883
|
+
|
|
769
884
|
.overlay {
|
|
770
885
|
position: fixed;
|
|
771
886
|
inset: 0;
|
|
@@ -773,6 +888,7 @@ function buildStyles(isDark) {
|
|
|
773
888
|
display: flex;
|
|
774
889
|
align-items: center;
|
|
775
890
|
justify-content: center;
|
|
891
|
+
padding: 16px;
|
|
776
892
|
background: rgba(0, 0, 0, 0.5);
|
|
777
893
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
778
894
|
animation: fadeIn 0.15s ease-out;
|
|
@@ -787,82 +903,96 @@ function buildStyles(isDark) {
|
|
|
787
903
|
40%, 80% { transform: translateX(6px); }
|
|
788
904
|
}
|
|
789
905
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
906
|
+
@keyframes claimedPulse {
|
|
907
|
+
0%, 100% { transform: scale(0.85); opacity: 0.7; }
|
|
908
|
+
50% { transform: scale(1); opacity: 1; }
|
|
909
|
+
}
|
|
790
910
|
|
|
791
|
-
.modal {
|
|
792
|
-
|
|
793
|
-
|
|
911
|
+
.modal {${LIGHT_TOKENS}
|
|
912
|
+
--ec-accent-soft: color-mix(in srgb, var(--ec-accent) 14%, transparent);
|
|
913
|
+
background: var(--ec-bg);
|
|
914
|
+
color: var(--ec-text);
|
|
794
915
|
border-radius: 16px;
|
|
795
916
|
width: 400px;
|
|
796
|
-
max-width:
|
|
797
|
-
max-height:
|
|
917
|
+
max-width: 100%;
|
|
918
|
+
max-height: calc(100vh - 32px);
|
|
798
919
|
overflow-y: auto;
|
|
799
|
-
padding: 20px
|
|
800
|
-
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.
|
|
920
|
+
padding: 16px 20px 12px;
|
|
921
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
|
|
801
922
|
animation: slideUp 0.2s ease-out;
|
|
802
923
|
display: flex;
|
|
803
924
|
flex-direction: column;
|
|
804
925
|
}
|
|
926
|
+
@media (prefers-color-scheme: dark) {
|
|
927
|
+
.modal {${DARK_TOKENS}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
.modal[data-appearance='light'] {${LIGHT_TOKENS}
|
|
931
|
+
}
|
|
932
|
+
.modal[data-appearance='dark'] {${DARK_TOKENS}
|
|
933
|
+
}
|
|
805
934
|
|
|
806
|
-
|
|
935
|
+
button, input { font-family: inherit; }
|
|
936
|
+
button:focus-visible, a:focus-visible {
|
|
937
|
+
outline: 2px solid var(--ec-accent);
|
|
938
|
+
outline-offset: 2px;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
.header { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
|
|
807
942
|
.header-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
|
808
|
-
.header-icon { border-radius: 6px; }
|
|
943
|
+
.header-icon { border-radius: 6px; flex-shrink: 0; }
|
|
809
944
|
h2 { font-size: 16px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
810
945
|
|
|
811
946
|
.close-btn {
|
|
812
947
|
background: none; border: none; font-size: 24px; cursor: pointer;
|
|
813
|
-
color:
|
|
948
|
+
color: var(--ec-muted); padding: 4px 8px; border-radius: 8px; line-height: 1;
|
|
949
|
+
flex-shrink: 0;
|
|
814
950
|
}
|
|
815
|
-
.close-btn:hover { color:
|
|
951
|
+
.close-btn:hover { color: var(--ec-text); background: var(--ec-surface); }
|
|
816
952
|
|
|
817
953
|
.stage {
|
|
818
|
-
min-height:
|
|
954
|
+
min-height: 240px;
|
|
819
955
|
display: flex;
|
|
820
956
|
flex-direction: column;
|
|
821
957
|
align-items: center;
|
|
822
958
|
justify-content: center;
|
|
823
|
-
gap:
|
|
959
|
+
gap: 10px;
|
|
824
960
|
text-align: center;
|
|
825
|
-
padding:
|
|
961
|
+
padding: 12px 0 10px;
|
|
826
962
|
}
|
|
827
963
|
.stage.fade-in > * { animation: stageIn 0.15s ease-out; }
|
|
828
964
|
|
|
829
965
|
.qr-box {
|
|
830
|
-
width:
|
|
831
|
-
height:
|
|
832
|
-
padding:
|
|
966
|
+
width: 200px;
|
|
967
|
+
height: 200px;
|
|
968
|
+
padding: 10px;
|
|
833
969
|
border-radius: 12px;
|
|
834
|
-
background:
|
|
970
|
+
background: #ffffff;
|
|
971
|
+
border: 1px solid #e3e4e9;
|
|
972
|
+
}
|
|
973
|
+
.qr-box svg { width: 100%; height: 100%; display: block; }
|
|
974
|
+
|
|
835
975
|
.claimed-pulse {
|
|
836
976
|
width: 64px; height: 64px; border-radius: 50%;
|
|
837
|
-
background:
|
|
977
|
+
background: var(--ec-accent-soft); position: relative;
|
|
838
978
|
}
|
|
839
979
|
.claimed-pulse::after {
|
|
840
980
|
content: ''; position: absolute; inset: 18px; border-radius: 50%;
|
|
841
|
-
background:
|
|
842
|
-
animation:
|
|
843
|
-
}
|
|
844
|
-
@keyframes enbox-claimed-pulse {
|
|
845
|
-
0%, 100% { transform: scale(0.85); opacity: 0.7; }
|
|
846
|
-
50% { transform: scale(1); opacity: 1; }
|
|
847
|
-
}
|
|
848
|
-
@media (prefers-reduced-motion: reduce) {
|
|
849
|
-
.claimed-pulse::after { animation: none; }
|
|
850
|
-
};
|
|
851
|
-
border: 1px solid ${border};
|
|
981
|
+
background: var(--ec-accent);
|
|
982
|
+
animation: claimedPulse 1.6s ease-in-out infinite;
|
|
852
983
|
}
|
|
853
|
-
.qr-box svg { width: 100%; height: 100%; display: block; }
|
|
854
984
|
|
|
855
985
|
.stage-caption { font-size: 15px; font-weight: 600; }
|
|
856
|
-
.stage-subline { font-size: 13px; color:
|
|
857
|
-
.stage-error { font-size: 13px; color:
|
|
986
|
+
.stage-subline { font-size: 13px; color: var(--ec-muted); max-width: 300px; line-height: 1.45; }
|
|
987
|
+
.stage-error { font-size: 13px; color: var(--ec-danger); }
|
|
858
988
|
|
|
859
989
|
.deep-link {
|
|
860
990
|
display: block;
|
|
861
991
|
width: 100%;
|
|
862
|
-
padding:
|
|
992
|
+
padding: 13px 16px;
|
|
863
993
|
border-radius: 12px;
|
|
864
|
-
background:
|
|
865
|
-
color:
|
|
994
|
+
background: var(--ec-accent);
|
|
995
|
+
color: var(--ec-accent-contrast);
|
|
866
996
|
font-weight: 600;
|
|
867
997
|
font-size: 15px;
|
|
868
998
|
text-decoration: none;
|
|
@@ -871,31 +1001,31 @@ function buildStyles(isDark) {
|
|
|
871
1001
|
.pin-row { display: flex; gap: 10px; }
|
|
872
1002
|
.pin-row.shake { animation: shake 0.4s ease-in-out; }
|
|
873
1003
|
.pin-input {
|
|
874
|
-
width:
|
|
1004
|
+
width: 46px; height: 54px;
|
|
875
1005
|
text-align: center;
|
|
876
1006
|
font-size: 24px; font-weight: 600;
|
|
877
|
-
color:
|
|
878
|
-
background:
|
|
879
|
-
border: 1px solid
|
|
1007
|
+
color: var(--ec-text);
|
|
1008
|
+
background: var(--ec-surface);
|
|
1009
|
+
border: 1px solid var(--ec-border);
|
|
880
1010
|
border-radius: 10px;
|
|
881
1011
|
outline: none;
|
|
882
1012
|
}
|
|
883
|
-
.pin-input:focus { border-color:
|
|
1013
|
+
.pin-input:focus { border-color: var(--ec-accent); }
|
|
884
1014
|
|
|
885
1015
|
.check {
|
|
886
1016
|
width: 56px; height: 56px;
|
|
887
1017
|
display: flex; align-items: center; justify-content: center;
|
|
888
1018
|
border-radius: 50%;
|
|
889
|
-
background:
|
|
890
|
-
color: #
|
|
1019
|
+
background: var(--ec-success);
|
|
1020
|
+
color: #ffffff;
|
|
891
1021
|
font-size: 28px;
|
|
892
1022
|
}
|
|
893
1023
|
|
|
894
1024
|
.spinner {
|
|
895
1025
|
width: 32px; height: 32px;
|
|
896
1026
|
border-radius: 50%;
|
|
897
|
-
border: 3px solid
|
|
898
|
-
border-top-color:
|
|
1027
|
+
border: 3px solid var(--ec-border);
|
|
1028
|
+
border-top-color: var(--ec-accent);
|
|
899
1029
|
animation: spin 0.8s linear infinite;
|
|
900
1030
|
}
|
|
901
1031
|
|
|
@@ -903,88 +1033,123 @@ function buildStyles(isDark) {
|
|
|
903
1033
|
padding: 10px 20px;
|
|
904
1034
|
border: none;
|
|
905
1035
|
border-radius: 10px;
|
|
906
|
-
background:
|
|
907
|
-
color:
|
|
1036
|
+
background: var(--ec-accent);
|
|
1037
|
+
color: var(--ec-accent-contrast);
|
|
908
1038
|
font-size: 14px;
|
|
909
1039
|
font-weight: 500;
|
|
910
1040
|
cursor: pointer;
|
|
911
1041
|
}
|
|
912
|
-
.stage-btn:hover { filter: brightness(1.
|
|
1042
|
+
.stage-btn:hover { filter: brightness(1.08); }
|
|
913
1043
|
|
|
914
1044
|
.footer {
|
|
915
|
-
border-top: 1px solid
|
|
916
|
-
padding-top:
|
|
1045
|
+
border-top: 1px solid var(--ec-border);
|
|
1046
|
+
padding-top: 8px;
|
|
917
1047
|
display: flex;
|
|
918
1048
|
flex-direction: column;
|
|
919
1049
|
gap: 6px;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
.footer-row {
|
|
1053
|
+
display: flex;
|
|
920
1054
|
align-items: center;
|
|
1055
|
+
justify-content: space-between;
|
|
1056
|
+
flex-wrap: wrap;
|
|
1057
|
+
gap: 2px 8px;
|
|
921
1058
|
}
|
|
1059
|
+
.footer-row > * { min-width: 0; }
|
|
1060
|
+
.footer-row > :only-child { margin-inline: auto; }
|
|
922
1061
|
|
|
923
1062
|
.footer-link {
|
|
924
1063
|
background: none;
|
|
925
1064
|
border: none;
|
|
926
|
-
color:
|
|
1065
|
+
color: var(--ec-muted);
|
|
927
1066
|
font-size: 13px;
|
|
928
1067
|
cursor: pointer;
|
|
929
|
-
padding:
|
|
1068
|
+
padding: 5px 8px;
|
|
930
1069
|
border-radius: 8px;
|
|
931
1070
|
}
|
|
932
|
-
.footer-link:hover { color:
|
|
933
|
-
.method-link { color:
|
|
1071
|
+
.footer-link:hover { color: var(--ec-text); background: var(--ec-surface); }
|
|
1072
|
+
.method-link { color: var(--ec-accent); }
|
|
1073
|
+
.wallet-toggle { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
934
1074
|
|
|
935
|
-
.wallet-
|
|
936
|
-
.wallet-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1075
|
+
.wallet-panel { width: 100%; padding: 4px 0 2px; display: flex; flex-direction: column; gap: 8px; }
|
|
1076
|
+
.wallet-search {
|
|
1077
|
+
width: 100%; padding: 8px 12px;
|
|
1078
|
+
border-radius: 10px; border: 1px solid var(--ec-border); background: transparent;
|
|
1079
|
+
color: inherit; font: inherit; font-size: 13px;
|
|
1080
|
+
}
|
|
1081
|
+
.wallet-search:focus { outline: none; border-color: var(--ec-accent); }
|
|
1082
|
+
|
|
1083
|
+
.wallet-scroll { max-height: 260px; overflow-y: auto; }
|
|
1084
|
+
.wallet-grid {
|
|
1085
|
+
display: grid;
|
|
1086
|
+
grid-template-columns: repeat(3, 1fr);
|
|
1087
|
+
gap: 8px;
|
|
1088
|
+
}
|
|
1089
|
+
.wallet-tile {
|
|
1090
|
+
aspect-ratio: 1;
|
|
1091
|
+
display: flex;
|
|
1092
|
+
flex-direction: column;
|
|
1093
|
+
align-items: center;
|
|
1094
|
+
justify-content: center;
|
|
1095
|
+
gap: 8px;
|
|
1096
|
+
padding: 8px;
|
|
1097
|
+
min-width: 0;
|
|
1098
|
+
background: var(--ec-surface);
|
|
1099
|
+
border: 1px solid var(--ec-border);
|
|
1100
|
+
border-radius: 12px;
|
|
1101
|
+
color: var(--ec-text);
|
|
1102
|
+
font-size: 12px;
|
|
946
1103
|
cursor: pointer;
|
|
947
1104
|
}
|
|
948
|
-
.wallet-
|
|
1105
|
+
.wallet-tile:hover { border-color: var(--ec-accent); }
|
|
1106
|
+
.wallet-tile.selected {
|
|
1107
|
+
border-color: var(--ec-accent);
|
|
1108
|
+
background: var(--ec-accent-soft);
|
|
1109
|
+
box-shadow: inset 0 0 0 1px var(--ec-accent);
|
|
1110
|
+
}
|
|
1111
|
+
.wallet-tile-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
949
1112
|
|
|
950
1113
|
.wallet-icon { position: relative; width: 28px; height: 28px; flex-shrink: 0; display: inline-flex; }
|
|
1114
|
+
.wallet-tile .wallet-icon { width: 40px; height: 40px; }
|
|
951
1115
|
.wallet-badge {
|
|
952
|
-
width:
|
|
1116
|
+
width: 100%; height: 100%; border-radius: 8px; display: inline-flex;
|
|
953
1117
|
align-items: center; justify-content: center; font-size: 13px;
|
|
954
|
-
font-weight: 600; background:
|
|
1118
|
+
font-weight: 600; background: var(--ec-accent-soft); color: var(--ec-accent);
|
|
955
1119
|
}
|
|
956
|
-
.wallet-
|
|
957
|
-
.wallet-
|
|
958
|
-
.wallet-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
963
|
-
.wallet-search:focus { outline: none; border-color: ${accent}; }
|
|
964
|
-
.wallet-empty { padding: 8px 10px; font-size: 12px; opacity: 0.7; }
|
|
965
|
-
.url-error { padding: 4px 2px 0; font-size: 12px; color: ${danger}; }
|
|
966
|
-
.wallet-custom { display: flex; gap: 6px; margin-top: 4px; }
|
|
1120
|
+
.wallet-tile .wallet-badge { border-radius: 10px; font-size: 17px; }
|
|
1121
|
+
.wallet-img { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 8px; object-fit: cover; }
|
|
1122
|
+
.wallet-tile .wallet-img { border-radius: 10px; }
|
|
1123
|
+
|
|
1124
|
+
.wallet-empty { padding: 10px 4px; font-size: 12px; color: var(--ec-muted); text-align: center; }
|
|
1125
|
+
|
|
1126
|
+
.wallet-custom { display: flex; gap: 6px; }
|
|
967
1127
|
.url-input {
|
|
968
1128
|
flex: 1;
|
|
969
1129
|
padding: 8px 12px;
|
|
970
|
-
border: 1px solid
|
|
1130
|
+
border: 1px solid var(--ec-border);
|
|
971
1131
|
border-radius: 10px;
|
|
972
1132
|
font-size: 13px;
|
|
973
|
-
background:
|
|
974
|
-
color:
|
|
1133
|
+
background: var(--ec-surface);
|
|
1134
|
+
color: var(--ec-text);
|
|
975
1135
|
outline: none;
|
|
976
1136
|
}
|
|
977
|
-
.url-input:focus { border-color:
|
|
978
|
-
.url-input.invalid { border-color:
|
|
1137
|
+
.url-input:focus { border-color: var(--ec-accent); }
|
|
1138
|
+
.url-input.invalid { border-color: var(--ec-danger); }
|
|
979
1139
|
.url-go {
|
|
980
1140
|
padding: 8px 14px;
|
|
981
1141
|
border: none;
|
|
982
1142
|
border-radius: 10px;
|
|
983
|
-
background:
|
|
984
|
-
color:
|
|
1143
|
+
background: var(--ec-accent);
|
|
1144
|
+
color: var(--ec-accent-contrast);
|
|
985
1145
|
font-size: 13px;
|
|
986
1146
|
cursor: pointer;
|
|
987
1147
|
}
|
|
988
|
-
|
|
989
|
-
|
|
1148
|
+
.url-error { font-size: 12px; color: var(--ec-danger); }
|
|
1149
|
+
|
|
1150
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1151
|
+
.overlay, .modal, .stage.fade-in > *, .pin-row.shake { animation: none; }
|
|
1152
|
+
.claimed-pulse::after { animation: none; }
|
|
1153
|
+
}
|
|
1154
|
+
`;
|
|
990
1155
|
//# sourceMappingURL=connect-modal.js.map
|