@emblemvault/emblem-auth-react 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/dist/index.js ADDED
@@ -0,0 +1,1134 @@
1
+ import { createContext, useState, useRef, useCallback, useEffect, useContext } from 'react';
2
+ import { EmblemAuthSDK } from 'emblem-auth-sdk';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ var globalSDKInstance = null;
6
+ var isSDKInitializing = false;
7
+ var EmblemAuthContext = createContext(void 0);
8
+ function EmblemAuthProvider({
9
+ children,
10
+ appId,
11
+ apiUrl,
12
+ modalUrl,
13
+ debug = false
14
+ }) {
15
+ const [session, setSession] = useState(null);
16
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
17
+ const [isLoading, setIsLoading] = useState(false);
18
+ const [error, setError] = useState(null);
19
+ const [vaultInfo, setVaultInfo] = useState(null);
20
+ const [authSDK, setAuthSDK] = useState(globalSDKInstance);
21
+ const initialized = useRef(false);
22
+ const log = useCallback(
23
+ (message, ...args) => {
24
+ if (debug) {
25
+ console.log(`[EmblemAuth] ${message}`, ...args);
26
+ }
27
+ },
28
+ [debug]
29
+ );
30
+ const fetchVaultInfo = useCallback(
31
+ async (sdk) => {
32
+ try {
33
+ const info = await sdk.getVaultInfo();
34
+ if (info) {
35
+ setVaultInfo(info);
36
+ log("Vault info loaded:", info);
37
+ }
38
+ } catch (err) {
39
+ log("Failed to fetch vault info:", err);
40
+ }
41
+ },
42
+ [log]
43
+ );
44
+ const handleAuthSuccess = useCallback(
45
+ (newSession, sdk) => {
46
+ log("Auth success - session:", newSession);
47
+ setSession(newSession);
48
+ setIsAuthenticated(true);
49
+ setIsLoading(false);
50
+ setError(null);
51
+ fetchVaultInfo(sdk);
52
+ },
53
+ [log, fetchVaultInfo]
54
+ );
55
+ const handleAuthError = useCallback(
56
+ (err) => {
57
+ log("Auth error:", err);
58
+ setError(err);
59
+ setIsLoading(false);
60
+ setIsAuthenticated(false);
61
+ setSession(null);
62
+ },
63
+ [log]
64
+ );
65
+ const handleSessionExpired = useCallback(() => {
66
+ log("Session expired");
67
+ setSession(null);
68
+ setIsAuthenticated(false);
69
+ setVaultInfo(null);
70
+ }, [log]);
71
+ useEffect(() => {
72
+ if (initialized.current || globalSDKInstance || isSDKInitializing) {
73
+ if (globalSDKInstance && !authSDK) {
74
+ setAuthSDK(globalSDKInstance);
75
+ const existingSession2 = globalSDKInstance.getSession();
76
+ if (existingSession2) {
77
+ handleAuthSuccess(existingSession2, globalSDKInstance);
78
+ }
79
+ }
80
+ return;
81
+ }
82
+ initialized.current = true;
83
+ isSDKInitializing = true;
84
+ log("Initializing SDK with appId:", appId);
85
+ const sdk = new EmblemAuthSDK({
86
+ appId,
87
+ apiUrl,
88
+ modalUrl,
89
+ onSuccess: (newSession) => {
90
+ handleAuthSuccess(newSession, sdk);
91
+ },
92
+ onError: (err) => {
93
+ handleAuthError(err);
94
+ }
95
+ });
96
+ globalSDKInstance = sdk;
97
+ isSDKInitializing = false;
98
+ setAuthSDK(sdk);
99
+ const existingSession = sdk.getSession();
100
+ if (existingSession) {
101
+ log("Found existing session");
102
+ handleAuthSuccess(existingSession, sdk);
103
+ }
104
+ const handleSessionUpdate = (updatedSession) => {
105
+ if (updatedSession) {
106
+ setSession(updatedSession);
107
+ setIsAuthenticated(true);
108
+ } else {
109
+ handleSessionExpired();
110
+ }
111
+ };
112
+ sdk.on("session", handleSessionUpdate);
113
+ sdk.on("sessionExpired", handleSessionExpired);
114
+ return () => {
115
+ sdk.off("session", handleSessionUpdate);
116
+ sdk.off("sessionExpired", handleSessionExpired);
117
+ };
118
+ }, [appId, apiUrl, modalUrl, log, handleAuthSuccess, handleAuthError, handleSessionExpired, authSDK]);
119
+ const openAuthModal = useCallback(async () => {
120
+ if (!authSDK) {
121
+ setError(new Error("Auth SDK not initialized"));
122
+ return;
123
+ }
124
+ log("Opening auth modal");
125
+ setIsLoading(true);
126
+ setError(null);
127
+ try {
128
+ await authSDK.openAuthModal();
129
+ } catch (err) {
130
+ setIsLoading(false);
131
+ setError(err instanceof Error ? err : new Error("Failed to open auth modal"));
132
+ }
133
+ }, [authSDK, log]);
134
+ const logout = useCallback(() => {
135
+ if (!authSDK) return;
136
+ log("Logging out");
137
+ authSDK.logout();
138
+ setSession(null);
139
+ setIsAuthenticated(false);
140
+ setVaultInfo(null);
141
+ setError(null);
142
+ }, [authSDK, log]);
143
+ const refreshSession = useCallback(async () => {
144
+ if (!authSDK) return null;
145
+ log("Refreshing session");
146
+ try {
147
+ const refreshedSession = await authSDK.refreshSession();
148
+ if (refreshedSession) {
149
+ setSession(refreshedSession);
150
+ setIsAuthenticated(true);
151
+ return refreshedSession;
152
+ }
153
+ return null;
154
+ } catch (err) {
155
+ log("Failed to refresh session:", err);
156
+ setError(err instanceof Error ? err : new Error("Failed to refresh session"));
157
+ return null;
158
+ }
159
+ }, [authSDK, log]);
160
+ const vaultId = session?.user?.vaultId ?? null;
161
+ const walletAddress = session?.user?.evmAddress ?? null;
162
+ const value = {
163
+ // State
164
+ session,
165
+ isAuthenticated,
166
+ isLoading,
167
+ error,
168
+ vaultInfo,
169
+ // Derived
170
+ vaultId,
171
+ walletAddress,
172
+ // Actions
173
+ openAuthModal,
174
+ logout,
175
+ refreshSession,
176
+ // For Hustle integration
177
+ authSDK
178
+ };
179
+ return /* @__PURE__ */ jsx(EmblemAuthContext.Provider, { value, children });
180
+ }
181
+ function useEmblemAuth() {
182
+ const context = useContext(EmblemAuthContext);
183
+ if (context === void 0) {
184
+ throw new Error("useEmblemAuth must be used within an EmblemAuthProvider");
185
+ }
186
+ return context;
187
+ }
188
+ function useEmblemAuthOptional() {
189
+ const context = useContext(EmblemAuthContext);
190
+ return context ?? null;
191
+ }
192
+ function resetAuthSDK() {
193
+ globalSDKInstance = null;
194
+ isSDKInitializing = false;
195
+ }
196
+
197
+ // src/styles/tokens.ts
198
+ var defaults = {
199
+ colors: {
200
+ // Backgrounds
201
+ bgPrimary: "#0b0d10",
202
+ bgSecondary: "#12161b",
203
+ bgTertiary: "#1a1f25",
204
+ bgHover: "#252b33",
205
+ bgOverlay: "rgba(0, 0, 0, 0.7)",
206
+ // Borders
207
+ borderPrimary: "#222b35",
208
+ borderSecondary: "#333",
209
+ borderHover: "#444",
210
+ // Text
211
+ textPrimary: "#e6eef8",
212
+ textSecondary: "#8892a4",
213
+ textTertiary: "#6b7280",
214
+ textInverse: "#fff",
215
+ // Accent - Primary (blue)
216
+ accentPrimary: "#4c9aff",
217
+ accentPrimaryHover: "#7bb6ff",
218
+ accentPrimaryBg: "rgba(76, 154, 255, 0.1)",
219
+ // Accent - Success (green)
220
+ accentSuccess: "#10b981",
221
+ accentSuccessHover: "#34d399",
222
+ accentSuccessBg: "rgba(16, 185, 129, 0.1)",
223
+ // Accent - Warning (yellow/orange)
224
+ accentWarning: "#f59e0b",
225
+ accentWarningBg: "rgba(245, 158, 11, 0.1)",
226
+ // Accent - Error (red)
227
+ accentError: "#dc2626",
228
+ accentErrorHover: "#ef4444",
229
+ accentErrorBg: "rgba(239, 68, 68, 0.1)",
230
+ // Messages
231
+ msgUser: "#1e3a5f",
232
+ msgAssistant: "#1a2633"
233
+ },
234
+ typography: {
235
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
236
+ fontFamilyMono: '"SF Mono", Monaco, monospace',
237
+ fontSizeXs: "11px",
238
+ fontSizeSm: "13px",
239
+ fontSizeMd: "14px",
240
+ fontSizeLg: "16px",
241
+ fontSizeXl: "18px",
242
+ fontWeightNormal: "400",
243
+ fontWeightMedium: "500",
244
+ fontWeightSemibold: "600",
245
+ lineHeightTight: "1.3",
246
+ lineHeightNormal: "1.5",
247
+ lineHeightRelaxed: "1.7"
248
+ },
249
+ spacing: {
250
+ xs: "4px",
251
+ sm: "8px",
252
+ md: "12px",
253
+ lg: "16px",
254
+ xl: "20px",
255
+ xxl: "24px"
256
+ },
257
+ radius: {
258
+ sm: "4px",
259
+ md: "6px",
260
+ lg: "8px",
261
+ xl: "12px",
262
+ pill: "20px",
263
+ full: "50%"
264
+ },
265
+ shadows: {
266
+ sm: "0 2px 8px rgba(0,0,0,0.2)",
267
+ md: "0 4px 16px rgba(0,0,0,0.3)",
268
+ lg: "0 8px 32px rgba(0,0,0,0.4)",
269
+ xl: "0 16px 48px rgba(0,0,0,0.5)"
270
+ },
271
+ // Glow effects (for enhanced themes)
272
+ glows: {
273
+ primary: "rgba(76, 154, 255, 0.4)",
274
+ success: "rgba(16, 185, 129, 0.4)",
275
+ error: "rgba(239, 68, 68, 0.4)",
276
+ ambient: "rgba(76, 154, 255, 0.08)"
277
+ },
278
+ transitions: {
279
+ fast: "0.15s ease",
280
+ normal: "0.2s ease",
281
+ slow: "0.3s ease"
282
+ },
283
+ zIndex: {
284
+ dropdown: "100",
285
+ modal: "1000",
286
+ fullscreen: "1000",
287
+ modalOverFullscreen: "10000"
288
+ }
289
+ };
290
+ function toVarName(category, key) {
291
+ const kebab = key.replace(/([A-Z])/g, "-$1").toLowerCase();
292
+ return `--hustle-${category}-${kebab}`;
293
+ }
294
+ function generateCSSVariables() {
295
+ const lines = [":root {"];
296
+ for (const [key, value] of Object.entries(defaults.colors)) {
297
+ lines.push(` ${toVarName("color", key)}: ${value};`);
298
+ }
299
+ for (const [key, value] of Object.entries(defaults.typography)) {
300
+ lines.push(` ${toVarName("font", key)}: ${value};`);
301
+ }
302
+ for (const [key, value] of Object.entries(defaults.spacing)) {
303
+ lines.push(` ${toVarName("space", key)}: ${value};`);
304
+ }
305
+ for (const [key, value] of Object.entries(defaults.radius)) {
306
+ lines.push(` ${toVarName("radius", key)}: ${value};`);
307
+ }
308
+ for (const [key, value] of Object.entries(defaults.shadows)) {
309
+ lines.push(` ${toVarName("shadow", key)}: ${value};`);
310
+ }
311
+ for (const [key, value] of Object.entries(defaults.glows)) {
312
+ lines.push(` ${toVarName("glow", key)}: ${value};`);
313
+ }
314
+ for (const [key, value] of Object.entries(defaults.transitions)) {
315
+ lines.push(` ${toVarName("transition", key)}: ${value};`);
316
+ }
317
+ for (const [key, value] of Object.entries(defaults.zIndex)) {
318
+ lines.push(` ${toVarName("z", key)}: ${value};`);
319
+ }
320
+ lines.push("}");
321
+ return lines.join("\n");
322
+ }
323
+ generateCSSVariables();
324
+ function createColorTokens() {
325
+ const result = {};
326
+ for (const [key, defaultValue] of Object.entries(defaults.colors)) {
327
+ result[key] = `var(${toVarName("color", key)}, ${defaultValue})`;
328
+ }
329
+ return result;
330
+ }
331
+ function createTypographyTokens() {
332
+ const result = {};
333
+ for (const [key, defaultValue] of Object.entries(defaults.typography)) {
334
+ result[key] = `var(${toVarName("font", key)}, ${defaultValue})`;
335
+ }
336
+ return result;
337
+ }
338
+ function createSpacingTokens() {
339
+ const result = {};
340
+ for (const [key, defaultValue] of Object.entries(defaults.spacing)) {
341
+ result[key] = `var(${toVarName("space", key)}, ${defaultValue})`;
342
+ }
343
+ return result;
344
+ }
345
+ function createRadiusTokens() {
346
+ const result = {};
347
+ for (const [key, defaultValue] of Object.entries(defaults.radius)) {
348
+ result[key] = `var(${toVarName("radius", key)}, ${defaultValue})`;
349
+ }
350
+ return result;
351
+ }
352
+ function createShadowTokens() {
353
+ const result = {};
354
+ for (const [key, defaultValue] of Object.entries(defaults.shadows)) {
355
+ result[key] = `var(${toVarName("shadow", key)}, ${defaultValue})`;
356
+ }
357
+ return result;
358
+ }
359
+ function createGlowTokens() {
360
+ const result = {};
361
+ for (const [key, defaultValue] of Object.entries(defaults.glows)) {
362
+ result[key] = `var(${toVarName("glow", key)}, ${defaultValue})`;
363
+ }
364
+ return result;
365
+ }
366
+ function createTransitionTokens() {
367
+ const result = {};
368
+ for (const [key, defaultValue] of Object.entries(defaults.transitions)) {
369
+ result[key] = `var(${toVarName("transition", key)}, ${defaultValue})`;
370
+ }
371
+ return result;
372
+ }
373
+ function createZIndexTokens() {
374
+ const result = {};
375
+ for (const [key, defaultValue] of Object.entries(defaults.zIndex)) {
376
+ result[key] = parseInt(defaultValue, 10);
377
+ }
378
+ return result;
379
+ }
380
+ var tokens = {
381
+ colors: createColorTokens(),
382
+ typography: createTypographyTokens(),
383
+ spacing: createSpacingTokens(),
384
+ radius: createRadiusTokens(),
385
+ shadows: createShadowTokens(),
386
+ glows: createGlowTokens(),
387
+ transitions: createTransitionTokens(),
388
+ zIndex: createZIndexTokens()
389
+ };
390
+ var presets = {
391
+ base: {
392
+ fontFamily: tokens.typography.fontFamily,
393
+ fontSize: tokens.typography.fontSizeMd,
394
+ lineHeight: tokens.typography.lineHeightNormal,
395
+ color: tokens.colors.textPrimary
396
+ },
397
+ card: {
398
+ background: tokens.colors.bgSecondary,
399
+ border: `1px solid ${tokens.colors.borderPrimary}`,
400
+ borderRadius: tokens.radius.xl
401
+ },
402
+ input: {
403
+ background: tokens.colors.bgTertiary,
404
+ border: `1px solid ${tokens.colors.borderSecondary}`,
405
+ borderRadius: tokens.radius.lg,
406
+ color: tokens.colors.textPrimary,
407
+ fontSize: tokens.typography.fontSizeMd,
408
+ padding: `${tokens.spacing.md} ${tokens.spacing.lg}`,
409
+ transition: `border-color ${tokens.transitions.normal}`
410
+ },
411
+ button: {
412
+ display: "inline-flex",
413
+ alignItems: "center",
414
+ justifyContent: "center",
415
+ gap: tokens.spacing.sm,
416
+ padding: `${tokens.spacing.sm} ${tokens.spacing.lg}`,
417
+ borderRadius: tokens.radius.lg,
418
+ fontSize: tokens.typography.fontSizeMd,
419
+ fontWeight: tokens.typography.fontWeightMedium,
420
+ cursor: "pointer",
421
+ transition: `all ${tokens.transitions.normal}`,
422
+ border: `1px solid ${tokens.colors.borderSecondary}`,
423
+ outline: "none"
424
+ },
425
+ buttonPrimary: {
426
+ background: tokens.colors.accentPrimary,
427
+ color: tokens.colors.textInverse,
428
+ borderColor: tokens.colors.accentPrimary
429
+ },
430
+ buttonSecondary: {
431
+ background: tokens.colors.bgTertiary,
432
+ color: tokens.colors.textPrimary,
433
+ borderColor: tokens.colors.borderSecondary
434
+ },
435
+ buttonIcon: {
436
+ width: "36px",
437
+ height: "36px",
438
+ padding: 0,
439
+ // background inherited from global button styles
440
+ color: tokens.colors.textSecondary
441
+ },
442
+ mono: {
443
+ fontFamily: tokens.typography.fontFamilyMono,
444
+ fontSize: tokens.typography.fontSizeSm
445
+ },
446
+ label: {
447
+ fontSize: tokens.typography.fontSizeXs,
448
+ color: tokens.colors.textTertiary,
449
+ marginBottom: tokens.spacing.xs
450
+ }
451
+ };
452
+ var animations = `
453
+ @keyframes hustle-spin {
454
+ to { transform: rotate(360deg); }
455
+ }
456
+ @keyframes hustle-pulse {
457
+ 0%, 100% { opacity: 1; }
458
+ 50% { opacity: 0.5; }
459
+ }
460
+ @keyframes hustle-glow {
461
+ 0%, 100% {
462
+ opacity: 1;
463
+ text-shadow: 0 0 4px ${defaults.colors.accentPrimaryBg};
464
+ }
465
+ 50% {
466
+ opacity: 0.6;
467
+ text-shadow: 0 0 8px ${defaults.colors.accentPrimary};
468
+ }
469
+ }
470
+ `;
471
+ function truncateAddress(address) {
472
+ if (!address || address.length < 10) return address || "";
473
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
474
+ }
475
+ async function copyToClipboard(text) {
476
+ try {
477
+ await navigator.clipboard.writeText(text);
478
+ return true;
479
+ } catch {
480
+ return false;
481
+ }
482
+ }
483
+ var styles = {
484
+ wrapper: {
485
+ position: "relative",
486
+ display: "inline-flex",
487
+ alignItems: "center",
488
+ gap: tokens.spacing.sm
489
+ },
490
+ button: {
491
+ ...presets.button,
492
+ padding: `${tokens.spacing.sm} ${tokens.spacing.xl}`
493
+ },
494
+ disconnected: {
495
+ background: tokens.colors.bgTertiary,
496
+ color: tokens.colors.textPrimary,
497
+ borderColor: tokens.colors.borderSecondary
498
+ },
499
+ disconnectedHover: {
500
+ background: tokens.colors.bgHover,
501
+ borderColor: tokens.colors.borderHover
502
+ },
503
+ connected: {
504
+ background: "transparent",
505
+ color: tokens.colors.accentSuccess,
506
+ borderColor: tokens.colors.accentSuccess,
507
+ borderRadius: tokens.radius.pill
508
+ },
509
+ connectedHover: {
510
+ background: tokens.colors.accentSuccessBg
511
+ },
512
+ loading: {
513
+ background: tokens.colors.borderSecondary,
514
+ color: tokens.colors.textSecondary,
515
+ cursor: "wait"
516
+ },
517
+ disabled: {
518
+ background: tokens.colors.borderSecondary,
519
+ color: tokens.colors.textTertiary,
520
+ cursor: "not-allowed",
521
+ opacity: 0.5
522
+ },
523
+ icon: {
524
+ fontSize: tokens.typography.fontSizeLg
525
+ },
526
+ spinner: {
527
+ display: "inline-block",
528
+ width: "14px",
529
+ height: "14px",
530
+ border: "2px solid currentColor",
531
+ borderTopColor: "transparent",
532
+ borderRadius: tokens.radius.full,
533
+ animation: "hustle-spin 0.8s linear infinite"
534
+ },
535
+ address: {
536
+ ...presets.mono,
537
+ color: tokens.colors.textPrimary
538
+ },
539
+ dot: {
540
+ color: tokens.colors.textSecondary
541
+ },
542
+ check: {
543
+ color: tokens.colors.accentSuccess
544
+ },
545
+ arrow: {
546
+ fontSize: "10px",
547
+ color: tokens.colors.textSecondary,
548
+ marginLeft: tokens.spacing.xs
549
+ },
550
+ // Disconnect button
551
+ disconnectBtn: {
552
+ display: "flex",
553
+ alignItems: "center",
554
+ justifyContent: "center",
555
+ width: "36px",
556
+ height: "36px",
557
+ background: "transparent",
558
+ border: `1px solid ${tokens.colors.borderSecondary}`,
559
+ borderRadius: tokens.radius.lg,
560
+ color: tokens.colors.textSecondary,
561
+ cursor: "pointer",
562
+ fontSize: "16px",
563
+ transition: `all ${tokens.transitions.normal}`
564
+ },
565
+ disconnectBtnHover: {
566
+ borderColor: tokens.colors.accentError,
567
+ color: tokens.colors.accentError
568
+ },
569
+ // Vault info dropdown
570
+ dropdown: {
571
+ position: "absolute",
572
+ top: "100%",
573
+ left: 0,
574
+ marginTop: tokens.spacing.xs,
575
+ background: tokens.colors.bgPrimary,
576
+ border: `1px solid ${tokens.colors.accentSuccess}`,
577
+ borderRadius: tokens.radius.xl,
578
+ padding: tokens.spacing.lg,
579
+ minWidth: "300px",
580
+ zIndex: tokens.zIndex.dropdown,
581
+ boxShadow: `0 8px 32px rgba(0,0,0,0.4), 0 0 0 1px ${tokens.colors.accentSuccessBg}`
582
+ },
583
+ dropdownHeader: {
584
+ fontSize: tokens.typography.fontSizeXs,
585
+ fontWeight: tokens.typography.fontWeightSemibold,
586
+ color: tokens.colors.textSecondary,
587
+ letterSpacing: "0.5px",
588
+ marginBottom: tokens.spacing.lg,
589
+ textTransform: "uppercase"
590
+ },
591
+ dropdownRow: {
592
+ marginBottom: tokens.spacing.md
593
+ },
594
+ dropdownLabel: {
595
+ display: "block",
596
+ fontSize: tokens.typography.fontSizeXs,
597
+ color: tokens.colors.textTertiary,
598
+ marginBottom: tokens.spacing.xs
599
+ },
600
+ dropdownValueRow: {
601
+ display: "flex",
602
+ alignItems: "center",
603
+ justifyContent: "space-between",
604
+ gap: tokens.spacing.sm
605
+ },
606
+ dropdownValue: {
607
+ fontSize: tokens.typography.fontSizeMd,
608
+ color: tokens.colors.textPrimary,
609
+ fontWeight: tokens.typography.fontWeightMedium,
610
+ flex: 1
611
+ },
612
+ dropdownValueMono: {
613
+ ...presets.mono,
614
+ wordBreak: "break-all"
615
+ },
616
+ copyBtn: {
617
+ background: "transparent",
618
+ border: `1px solid ${tokens.colors.borderSecondary}`,
619
+ color: tokens.colors.textSecondary,
620
+ padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,
621
+ borderRadius: tokens.radius.sm,
622
+ cursor: "pointer",
623
+ fontSize: tokens.typography.fontSizeXs,
624
+ transition: `all ${tokens.transitions.normal}`,
625
+ whiteSpace: "nowrap"
626
+ },
627
+ copyBtnHover: {
628
+ background: tokens.colors.bgHover,
629
+ borderColor: tokens.colors.accentPrimary,
630
+ color: tokens.colors.accentPrimary
631
+ },
632
+ copyBtnCopied: {
633
+ background: tokens.colors.accentSuccess,
634
+ borderColor: tokens.colors.accentSuccess,
635
+ color: tokens.colors.textInverse
636
+ }
637
+ };
638
+ function ConnectButton({
639
+ className = "",
640
+ style,
641
+ connectLabel = "Connect",
642
+ loadingLabel = "Connecting...",
643
+ onConnect,
644
+ onDisconnect,
645
+ showVaultInfo = true,
646
+ disabled = false
647
+ }) {
648
+ const {
649
+ isAuthenticated,
650
+ isLoading,
651
+ walletAddress,
652
+ vaultId,
653
+ openAuthModal,
654
+ logout
655
+ } = useEmblemAuth();
656
+ const [isHovered, setIsHovered] = useState(false);
657
+ const [showDropdown, setShowDropdown] = useState(false);
658
+ const [disconnectHovered, setDisconnectHovered] = useState(false);
659
+ const [copiedField, setCopiedField] = useState(null);
660
+ const [copyHovered, setCopyHovered] = useState(null);
661
+ const handleClick = useCallback(async () => {
662
+ if (disabled) return;
663
+ if (!isAuthenticated && !isLoading) {
664
+ await openAuthModal();
665
+ onConnect?.();
666
+ }
667
+ }, [disabled, isAuthenticated, isLoading, openAuthModal, onConnect]);
668
+ const handleDisconnect = useCallback(() => {
669
+ logout();
670
+ onDisconnect?.();
671
+ setShowDropdown(false);
672
+ }, [logout, onDisconnect]);
673
+ const handleCopy = useCallback(async (field, value) => {
674
+ const success = await copyToClipboard(value);
675
+ if (success) {
676
+ setCopiedField(field);
677
+ setTimeout(() => setCopiedField(null), 1500);
678
+ }
679
+ }, []);
680
+ let buttonStyle = { ...styles.button };
681
+ let content = connectLabel;
682
+ if (disabled) {
683
+ buttonStyle = { ...buttonStyle, ...styles.disconnected, ...styles.disabled };
684
+ } else if (isLoading) {
685
+ buttonStyle = { ...buttonStyle, ...styles.disconnected, ...styles.loading };
686
+ content = /* @__PURE__ */ jsxs(Fragment, { children: [
687
+ /* @__PURE__ */ jsx("span", { style: styles.spinner }),
688
+ loadingLabel
689
+ ] });
690
+ } else if (isAuthenticated) {
691
+ buttonStyle = { ...buttonStyle, ...styles.connected };
692
+ if (isHovered || showDropdown) {
693
+ buttonStyle = { ...buttonStyle, ...styles.connectedHover };
694
+ }
695
+ const truncated = truncateAddress(walletAddress || "");
696
+ content = /* @__PURE__ */ jsxs(Fragment, { children: [
697
+ /* @__PURE__ */ jsx("span", { style: styles.check, children: "\u2713" }),
698
+ /* @__PURE__ */ jsx("span", { children: "Connected" }),
699
+ /* @__PURE__ */ jsx("span", { style: styles.dot, children: "\u2022" }),
700
+ /* @__PURE__ */ jsx("span", { style: styles.address, children: truncated }),
701
+ /* @__PURE__ */ jsx("span", { style: styles.arrow, children: "\u25BE" })
702
+ ] });
703
+ } else {
704
+ buttonStyle = { ...buttonStyle, ...styles.disconnected };
705
+ if (isHovered) {
706
+ buttonStyle = { ...buttonStyle, ...styles.disconnectedHover };
707
+ }
708
+ content = /* @__PURE__ */ jsxs(Fragment, { children: [
709
+ /* @__PURE__ */ jsx("span", { style: styles.icon, children: "\u2192" }),
710
+ connectLabel
711
+ ] });
712
+ }
713
+ if (style) {
714
+ buttonStyle = { ...buttonStyle, ...style };
715
+ }
716
+ const renderCopyBtn = (field, value) => {
717
+ const isCopied = copiedField === field;
718
+ const isHover = copyHovered === field;
719
+ return /* @__PURE__ */ jsx(
720
+ "button",
721
+ {
722
+ type: "button",
723
+ onClick: (e) => {
724
+ e.stopPropagation();
725
+ handleCopy(field, value);
726
+ },
727
+ style: {
728
+ ...styles.copyBtn,
729
+ ...isCopied ? styles.copyBtnCopied : isHover ? styles.copyBtnHover : {}
730
+ },
731
+ onMouseEnter: () => setCopyHovered(field),
732
+ onMouseLeave: () => setCopyHovered(null),
733
+ children: isCopied ? "Copied!" : "Copy"
734
+ }
735
+ );
736
+ };
737
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
738
+ /* @__PURE__ */ jsx("style", { children: animations }),
739
+ /* @__PURE__ */ jsxs(
740
+ "div",
741
+ {
742
+ style: styles.wrapper,
743
+ onMouseEnter: () => isAuthenticated && showVaultInfo && setShowDropdown(true),
744
+ onMouseLeave: () => setShowDropdown(false),
745
+ children: [
746
+ /* @__PURE__ */ jsx(
747
+ "button",
748
+ {
749
+ type: "button",
750
+ onClick: handleClick,
751
+ disabled: disabled || isLoading,
752
+ className,
753
+ style: buttonStyle,
754
+ onMouseEnter: () => setIsHovered(true),
755
+ onMouseLeave: () => setIsHovered(false),
756
+ children: content
757
+ }
758
+ ),
759
+ isAuthenticated && /* @__PURE__ */ jsx(
760
+ "button",
761
+ {
762
+ type: "button",
763
+ onClick: handleDisconnect,
764
+ style: {
765
+ ...styles.disconnectBtn,
766
+ ...disconnectHovered ? styles.disconnectBtnHover : {}
767
+ },
768
+ onMouseEnter: () => setDisconnectHovered(true),
769
+ onMouseLeave: () => setDisconnectHovered(false),
770
+ title: "Disconnect",
771
+ children: "\u23FB"
772
+ }
773
+ ),
774
+ isAuthenticated && showVaultInfo && showDropdown && /* @__PURE__ */ jsxs("div", { style: styles.dropdown, children: [
775
+ /* @__PURE__ */ jsx("div", { style: styles.dropdownHeader, children: "Vault Information" }),
776
+ /* @__PURE__ */ jsxs("div", { style: styles.dropdownRow, children: [
777
+ /* @__PURE__ */ jsx("span", { style: styles.dropdownLabel, children: "Vault ID" }),
778
+ /* @__PURE__ */ jsxs("div", { style: styles.dropdownValueRow, children: [
779
+ /* @__PURE__ */ jsxs("span", { style: styles.dropdownValue, children: [
780
+ "#",
781
+ vaultId
782
+ ] }),
783
+ renderCopyBtn("vaultId", vaultId || "")
784
+ ] })
785
+ ] }),
786
+ /* @__PURE__ */ jsxs("div", { style: { ...styles.dropdownRow, marginBottom: 0 }, children: [
787
+ /* @__PURE__ */ jsx("span", { style: styles.dropdownLabel, children: "Connected Wallet" }),
788
+ /* @__PURE__ */ jsxs("div", { style: styles.dropdownValueRow, children: [
789
+ /* @__PURE__ */ jsx("span", { style: { ...styles.dropdownValue, ...styles.dropdownValueMono }, children: walletAddress }),
790
+ renderCopyBtn("wallet", walletAddress || "")
791
+ ] })
792
+ ] })
793
+ ] })
794
+ ]
795
+ }
796
+ )
797
+ ] });
798
+ }
799
+ async function copyToClipboard2(text) {
800
+ try {
801
+ await navigator.clipboard.writeText(text);
802
+ return true;
803
+ } catch {
804
+ return false;
805
+ }
806
+ }
807
+ var s = {
808
+ container: {
809
+ position: "relative",
810
+ display: "inline-flex",
811
+ alignItems: "center",
812
+ gap: tokens.spacing.sm,
813
+ fontFamily: tokens.typography.fontFamily
814
+ },
815
+ disconnected: {
816
+ display: "inline-flex",
817
+ alignItems: "center",
818
+ gap: tokens.spacing.sm,
819
+ color: tokens.colors.textSecondary,
820
+ fontSize: tokens.typography.fontSizeMd
821
+ },
822
+ dot: {
823
+ display: "inline-block",
824
+ width: "8px",
825
+ height: "8px",
826
+ borderRadius: tokens.radius.full,
827
+ backgroundColor: tokens.colors.textTertiary
828
+ },
829
+ dotConnected: {
830
+ backgroundColor: tokens.colors.accentSuccess
831
+ },
832
+ spinner: {
833
+ display: "inline-block",
834
+ width: "12px",
835
+ height: "12px",
836
+ border: `2px solid ${tokens.colors.textSecondary}`,
837
+ borderTopColor: "transparent",
838
+ borderRadius: tokens.radius.full,
839
+ animation: "hustle-spin 0.8s linear infinite"
840
+ },
841
+ logoutBtn: {
842
+ ...presets.buttonIcon,
843
+ border: `1px solid ${tokens.colors.borderSecondary}`,
844
+ borderRadius: tokens.radius.lg,
845
+ transition: `all ${tokens.transitions.normal}`
846
+ },
847
+ logoutBtnHover: {
848
+ borderColor: tokens.colors.accentError,
849
+ color: tokens.colors.accentError
850
+ },
851
+ vaultInfoWrapper: {
852
+ position: "relative"
853
+ },
854
+ vaultInfo: {
855
+ position: "absolute",
856
+ top: "100%",
857
+ right: 0,
858
+ marginTop: tokens.spacing.sm,
859
+ background: tokens.colors.bgSecondary,
860
+ border: `1px solid ${tokens.colors.borderPrimary}`,
861
+ borderRadius: tokens.radius.xl,
862
+ padding: tokens.spacing.lg,
863
+ minWidth: "380px",
864
+ zIndex: tokens.zIndex.dropdown,
865
+ boxShadow: tokens.shadows.lg
866
+ },
867
+ vaultInfoHeader: {
868
+ fontSize: tokens.typography.fontSizeXs,
869
+ fontWeight: tokens.typography.fontWeightSemibold,
870
+ color: tokens.colors.textSecondary,
871
+ letterSpacing: "0.5px",
872
+ marginBottom: tokens.spacing.lg,
873
+ textTransform: "uppercase"
874
+ },
875
+ vaultInfoRow: {
876
+ marginBottom: tokens.spacing.md
877
+ },
878
+ vaultLabel: {
879
+ display: "block",
880
+ fontSize: "12px",
881
+ color: tokens.colors.textTertiary,
882
+ marginBottom: tokens.spacing.xs
883
+ },
884
+ vaultValueRow: {
885
+ display: "flex",
886
+ alignItems: "center",
887
+ justifyContent: "space-between",
888
+ gap: tokens.spacing.sm
889
+ },
890
+ vaultValue: {
891
+ fontSize: tokens.typography.fontSizeMd,
892
+ color: tokens.colors.textPrimary,
893
+ fontWeight: tokens.typography.fontWeightMedium,
894
+ flex: 1
895
+ },
896
+ vaultValueMono: {
897
+ ...presets.mono,
898
+ wordBreak: "break-all"
899
+ },
900
+ copyBtn: {
901
+ background: "transparent",
902
+ border: `1px solid ${tokens.colors.borderSecondary}`,
903
+ color: tokens.colors.textSecondary,
904
+ padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,
905
+ borderRadius: tokens.radius.sm,
906
+ cursor: "pointer",
907
+ fontSize: tokens.typography.fontSizeXs,
908
+ transition: `all ${tokens.transitions.normal}`,
909
+ whiteSpace: "nowrap"
910
+ },
911
+ copyBtnHover: {
912
+ background: tokens.colors.bgHover,
913
+ borderColor: tokens.colors.accentPrimary,
914
+ color: tokens.colors.accentPrimary
915
+ },
916
+ copyBtnCopied: {
917
+ background: tokens.colors.accentSuccess,
918
+ borderColor: tokens.colors.accentSuccess,
919
+ color: tokens.colors.textInverse
920
+ }
921
+ };
922
+ function AuthStatus({
923
+ className = "",
924
+ style,
925
+ showVaultInfo = false,
926
+ showLogout = false
927
+ }) {
928
+ const {
929
+ isAuthenticated,
930
+ isLoading,
931
+ walletAddress,
932
+ vaultId,
933
+ vaultInfo,
934
+ logout
935
+ } = useEmblemAuth();
936
+ const [isHovered, setIsHovered] = useState(false);
937
+ const [logoutHovered, setLogoutHovered] = useState(false);
938
+ const [copiedField, setCopiedField] = useState(null);
939
+ const [copyHovered, setCopyHovered] = useState(null);
940
+ const handleCopy = useCallback(async (field, value) => {
941
+ const success = await copyToClipboard2(value);
942
+ if (success) {
943
+ setCopiedField(field);
944
+ setTimeout(() => setCopiedField(null), 1500);
945
+ }
946
+ }, []);
947
+ if (!isAuthenticated) {
948
+ if (isLoading) {
949
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
950
+ /* @__PURE__ */ jsx("style", { children: animations }),
951
+ /* @__PURE__ */ jsxs("div", { className, style: { ...s.disconnected, ...style }, children: [
952
+ /* @__PURE__ */ jsx("span", { style: s.spinner }),
953
+ /* @__PURE__ */ jsx("span", { children: "Connecting..." })
954
+ ] })
955
+ ] });
956
+ }
957
+ return /* @__PURE__ */ jsxs("div", { className, style: { ...s.disconnected, ...style }, children: [
958
+ /* @__PURE__ */ jsx("span", { style: s.dot }),
959
+ /* @__PURE__ */ jsx("span", { children: "Not connected" })
960
+ ] });
961
+ }
962
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
963
+ /* @__PURE__ */ jsx("style", { children: animations }),
964
+ /* @__PURE__ */ jsxs("div", { className, style: { ...s.container, ...style }, children: [
965
+ /* @__PURE__ */ jsxs(
966
+ "div",
967
+ {
968
+ style: s.vaultInfoWrapper,
969
+ onMouseEnter: () => showVaultInfo && setIsHovered(true),
970
+ onMouseLeave: () => showVaultInfo && setIsHovered(false),
971
+ children: [
972
+ /* @__PURE__ */ jsx("span", { style: { ...s.dot, ...s.dotConnected }, title: "Connected" }),
973
+ showVaultInfo && isHovered && /* @__PURE__ */ jsxs("div", { style: s.vaultInfo, children: [
974
+ /* @__PURE__ */ jsx("div", { style: s.vaultInfoHeader, children: "Vault Information" }),
975
+ /* @__PURE__ */ jsxs("div", { style: s.vaultInfoRow, children: [
976
+ /* @__PURE__ */ jsx("span", { style: s.vaultLabel, children: "Vault ID" }),
977
+ /* @__PURE__ */ jsxs("div", { style: s.vaultValueRow, children: [
978
+ /* @__PURE__ */ jsxs("span", { style: s.vaultValue, children: [
979
+ "#",
980
+ vaultId
981
+ ] }),
982
+ /* @__PURE__ */ jsx(
983
+ CopyButton,
984
+ {
985
+ field: "vaultId",
986
+ value: vaultId || "",
987
+ copiedField,
988
+ copyHovered,
989
+ setCopyHovered,
990
+ onCopy: handleCopy
991
+ }
992
+ )
993
+ ] })
994
+ ] }),
995
+ /* @__PURE__ */ jsxs("div", { style: s.vaultInfoRow, children: [
996
+ /* @__PURE__ */ jsx("span", { style: s.vaultLabel, children: "Connected Wallet" }),
997
+ /* @__PURE__ */ jsxs("div", { style: s.vaultValueRow, children: [
998
+ /* @__PURE__ */ jsx("span", { style: { ...s.vaultValue, ...s.vaultValueMono }, children: walletAddress }),
999
+ /* @__PURE__ */ jsx(
1000
+ CopyButton,
1001
+ {
1002
+ field: "wallet",
1003
+ value: walletAddress || "",
1004
+ copiedField,
1005
+ copyHovered,
1006
+ setCopyHovered,
1007
+ onCopy: handleCopy
1008
+ }
1009
+ )
1010
+ ] })
1011
+ ] }),
1012
+ vaultInfo?.evmAddress && /* @__PURE__ */ jsxs("div", { style: s.vaultInfoRow, children: [
1013
+ /* @__PURE__ */ jsx("span", { style: s.vaultLabel, children: "Vault EVM Address" }),
1014
+ /* @__PURE__ */ jsxs("div", { style: s.vaultValueRow, children: [
1015
+ /* @__PURE__ */ jsx("span", { style: { ...s.vaultValue, ...s.vaultValueMono }, children: vaultInfo.evmAddress }),
1016
+ /* @__PURE__ */ jsx(
1017
+ CopyButton,
1018
+ {
1019
+ field: "evmAddress",
1020
+ value: vaultInfo.evmAddress,
1021
+ copiedField,
1022
+ copyHovered,
1023
+ setCopyHovered,
1024
+ onCopy: handleCopy
1025
+ }
1026
+ )
1027
+ ] })
1028
+ ] }),
1029
+ vaultInfo?.solanaAddress && /* @__PURE__ */ jsxs("div", { style: s.vaultInfoRow, children: [
1030
+ /* @__PURE__ */ jsx("span", { style: s.vaultLabel, children: "Vault Solana Address" }),
1031
+ /* @__PURE__ */ jsxs("div", { style: s.vaultValueRow, children: [
1032
+ /* @__PURE__ */ jsx("span", { style: { ...s.vaultValue, ...s.vaultValueMono }, children: vaultInfo.solanaAddress }),
1033
+ /* @__PURE__ */ jsx(
1034
+ CopyButton,
1035
+ {
1036
+ field: "solAddress",
1037
+ value: vaultInfo.solanaAddress,
1038
+ copiedField,
1039
+ copyHovered,
1040
+ setCopyHovered,
1041
+ onCopy: handleCopy
1042
+ }
1043
+ )
1044
+ ] })
1045
+ ] })
1046
+ ] })
1047
+ ]
1048
+ }
1049
+ ),
1050
+ showLogout && /* @__PURE__ */ jsx(
1051
+ "button",
1052
+ {
1053
+ type: "button",
1054
+ onClick: logout,
1055
+ style: {
1056
+ ...s.logoutBtn,
1057
+ ...logoutHovered ? s.logoutBtnHover : {}
1058
+ },
1059
+ onMouseEnter: () => setLogoutHovered(true),
1060
+ onMouseLeave: () => setLogoutHovered(false),
1061
+ title: "Disconnect",
1062
+ children: "\u23FB"
1063
+ }
1064
+ )
1065
+ ] })
1066
+ ] });
1067
+ }
1068
+ function CopyButton({ field, value, copiedField, copyHovered, setCopyHovered, onCopy }) {
1069
+ const isCopied = copiedField === field;
1070
+ const isHovered = copyHovered === field;
1071
+ return /* @__PURE__ */ jsx(
1072
+ "button",
1073
+ {
1074
+ type: "button",
1075
+ onClick: () => onCopy(field, value),
1076
+ style: {
1077
+ ...s.copyBtn,
1078
+ ...isCopied ? s.copyBtnCopied : isHovered ? s.copyBtnHover : {}
1079
+ },
1080
+ onMouseEnter: () => setCopyHovered(field),
1081
+ onMouseLeave: () => setCopyHovered(null),
1082
+ children: isCopied ? "Copied!" : "Copy"
1083
+ }
1084
+ );
1085
+ }
1086
+
1087
+ // src/utils/index.ts
1088
+ function truncateAddress2(address, startChars = 6, endChars = 4) {
1089
+ if (!address) return "";
1090
+ if (address.length <= startChars + endChars) return address;
1091
+ return `${address.slice(0, startChars)}...${address.slice(-endChars)}`;
1092
+ }
1093
+ async function copyToClipboard3(text) {
1094
+ try {
1095
+ await navigator.clipboard.writeText(text);
1096
+ return true;
1097
+ } catch {
1098
+ try {
1099
+ const textarea = document.createElement("textarea");
1100
+ textarea.value = text;
1101
+ textarea.style.position = "fixed";
1102
+ textarea.style.opacity = "0";
1103
+ document.body.appendChild(textarea);
1104
+ textarea.select();
1105
+ const success = document.execCommand("copy");
1106
+ document.body.removeChild(textarea);
1107
+ return success;
1108
+ } catch {
1109
+ return false;
1110
+ }
1111
+ }
1112
+ }
1113
+ function generateId(prefix = "id") {
1114
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
1115
+ }
1116
+ function decodeJwtPayload(token) {
1117
+ try {
1118
+ const parts = token.split(".");
1119
+ if (parts.length !== 3) return null;
1120
+ const payload = JSON.parse(atob(parts[1]));
1121
+ return payload;
1122
+ } catch {
1123
+ return null;
1124
+ }
1125
+ }
1126
+ function isJwtExpired(token) {
1127
+ const payload = decodeJwtPayload(token);
1128
+ if (!payload || typeof payload.exp !== "number") return true;
1129
+ return Date.now() >= payload.exp * 1e3;
1130
+ }
1131
+
1132
+ export { AuthStatus, ConnectButton, EmblemAuthProvider, copyToClipboard3 as copyToClipboard, decodeJwtPayload, generateId, isJwtExpired, resetAuthSDK, truncateAddress2 as truncateAddress, useEmblemAuth, useEmblemAuthOptional };
1133
+ //# sourceMappingURL=index.js.map
1134
+ //# sourceMappingURL=index.js.map