@abstraxn/signer-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.
Files changed (178) hide show
  1. package/README.md +114 -0
  2. package/dist/src/AbstraxnProvider.d.ts +9 -0
  3. package/dist/src/AbstraxnProvider.js +21 -0
  4. package/dist/src/AbstraxnProvider.js.map +1 -0
  5. package/dist/src/ConnectButton.css +217 -0
  6. package/dist/src/ConnectButton.d.ts +71 -0
  7. package/dist/src/ConnectButton.js +102 -0
  8. package/dist/src/ConnectButton.js.map +1 -0
  9. package/dist/src/ExternalWalletButtons.css +319 -0
  10. package/dist/src/ExternalWalletButtons.d.ts +56 -0
  11. package/dist/src/ExternalWalletButtons.js +272 -0
  12. package/dist/src/ExternalWalletButtons.js.map +1 -0
  13. package/dist/src/OnboardingUI.d.ts +63 -0
  14. package/dist/src/OnboardingUI.js +66 -0
  15. package/dist/src/OnboardingUI.js.map +1 -0
  16. package/dist/src/WalletModal.css +2319 -0
  17. package/dist/src/WalletModal.d.ts +7 -0
  18. package/dist/src/WalletModal.js +322 -0
  19. package/dist/src/WalletModal.js.map +1 -0
  20. package/dist/src/chains.d.ts +56 -0
  21. package/dist/src/chains.js +291 -0
  22. package/dist/src/chains.js.map +1 -0
  23. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.d.ts +12 -0
  24. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js +146 -0
  25. package/dist/src/components/AbstraxnProvider/AbstraxnProvider.js.map +1 -0
  26. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.d.ts +25 -0
  27. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +3086 -0
  28. package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js.map +1 -0
  29. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.d.ts +8 -0
  30. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.js +46 -0
  31. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithWagmi.js.map +1 -0
  32. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.d.ts +8 -0
  33. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.js +12 -0
  34. package/dist/src/components/AbstraxnProvider/AbstraxnProviderWithoutWagmi.js.map +1 -0
  35. package/dist/src/components/AbstraxnProvider/context.d.ts +2 -0
  36. package/dist/src/components/AbstraxnProvider/context.js +6 -0
  37. package/dist/src/components/AbstraxnProvider/context.js.map +1 -0
  38. package/dist/src/components/AbstraxnProvider/index.d.ts +6 -0
  39. package/dist/src/components/AbstraxnProvider/index.js +7 -0
  40. package/dist/src/components/AbstraxnProvider/index.js.map +1 -0
  41. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.d.ts +30 -0
  42. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js +49 -0
  43. package/dist/src/components/AbstraxnProvider/useAbstraxnProviderBase.js.map +1 -0
  44. package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.d.ts +2 -0
  45. package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.js +13 -0
  46. package/dist/src/components/AbstraxnProvider/useAbstraxnWallet.js.map +1 -0
  47. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.d.ts +22 -0
  48. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.js +242 -0
  49. package/dist/src/components/AbstraxnProvider/useOAuthCallbacks.js.map +1 -0
  50. package/dist/src/components/AbstraxnProvider/useWalletInitialization.d.ts +25 -0
  51. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +539 -0
  52. package/dist/src/components/AbstraxnProvider/useWalletInitialization.js.map +1 -0
  53. package/dist/src/components/AbstraxnProvider/utils.d.ts +41 -0
  54. package/dist/src/components/AbstraxnProvider/utils.js +139 -0
  55. package/dist/src/components/AbstraxnProvider/utils.js.map +1 -0
  56. package/dist/src/components/OnboardingUI/OnboardingUI.css +1062 -0
  57. package/dist/src/components/OnboardingUI/OnboardingUIReact.d.ts +15 -0
  58. package/dist/src/components/OnboardingUI/OnboardingUIReact.js +318 -0
  59. package/dist/src/components/OnboardingUI/OnboardingUIReact.js.map +1 -0
  60. package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +265 -0
  61. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +3782 -0
  62. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -0
  63. package/dist/src/components/OnboardingUI/components/EmailForm.d.ts +16 -0
  64. package/dist/src/components/OnboardingUI/components/EmailForm.js +27 -0
  65. package/dist/src/components/OnboardingUI/components/EmailForm.js.map +1 -0
  66. package/dist/src/components/OnboardingUI/components/Modal.d.ts +14 -0
  67. package/dist/src/components/OnboardingUI/components/Modal.js +61 -0
  68. package/dist/src/components/OnboardingUI/components/Modal.js.map +1 -0
  69. package/dist/src/components/OnboardingUI/components/OtpForm.d.ts +20 -0
  70. package/dist/src/components/OnboardingUI/components/OtpForm.js +72 -0
  71. package/dist/src/components/OnboardingUI/components/OtpForm.js.map +1 -0
  72. package/dist/src/components/OnboardingUI/components/PasskeyButton.d.ts +14 -0
  73. package/dist/src/components/OnboardingUI/components/PasskeyButton.js +22 -0
  74. package/dist/src/components/OnboardingUI/components/PasskeyButton.js.map +1 -0
  75. package/dist/src/components/OnboardingUI/components/SocialButtons.d.ts +15 -0
  76. package/dist/src/components/OnboardingUI/components/SocialButtons.js +20 -0
  77. package/dist/src/components/OnboardingUI/components/SocialButtons.js.map +1 -0
  78. package/dist/src/components/OnboardingUI/components/index.d.ts +13 -0
  79. package/dist/src/components/OnboardingUI/components/index.js +9 -0
  80. package/dist/src/components/OnboardingUI/components/index.js.map +1 -0
  81. package/dist/src/components/OnboardingUI/hooks/index.d.ts +7 -0
  82. package/dist/src/components/OnboardingUI/hooks/index.js +6 -0
  83. package/dist/src/components/OnboardingUI/hooks/index.js.map +1 -0
  84. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.d.ts +11 -0
  85. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.js +243 -0
  86. package/dist/src/components/OnboardingUI/hooks/useAuthMethods.js.map +1 -0
  87. package/dist/src/components/OnboardingUI/hooks/useOnboarding.d.ts +21 -0
  88. package/dist/src/components/OnboardingUI/hooks/useOnboarding.js +153 -0
  89. package/dist/src/components/OnboardingUI/hooks/useOnboarding.js.map +1 -0
  90. package/dist/src/components/OnboardingUI/index.d.ts +12 -0
  91. package/dist/src/components/OnboardingUI/index.js +15 -0
  92. package/dist/src/components/OnboardingUI/index.js.map +1 -0
  93. package/dist/src/components/QRCode.d.ts +13 -0
  94. package/dist/src/components/QRCode.js +6 -0
  95. package/dist/src/components/QRCode.js.map +1 -0
  96. package/dist/src/components/WalletModal/components/ChainSelector.css +327 -0
  97. package/dist/src/components/WalletModal/components/ChainSelector.d.ts +11 -0
  98. package/dist/src/components/WalletModal/components/ChainSelector.js +75 -0
  99. package/dist/src/components/WalletModal/components/ChainSelector.js.map +1 -0
  100. package/dist/src/components/WalletModal/components/ExportKeyModal.css +134 -0
  101. package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +14 -0
  102. package/dist/src/components/WalletModal/components/ExportKeyModal.js +26 -0
  103. package/dist/src/components/WalletModal/components/ExportKeyModal.js.map +1 -0
  104. package/dist/src/components/WalletModal/components/ExportWarningModal.css +107 -0
  105. package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +17 -0
  106. package/dist/src/components/WalletModal/components/ExportWarningModal.js +20 -0
  107. package/dist/src/components/WalletModal/components/ExportWarningModal.js.map +1 -0
  108. package/dist/src/components/WalletModal/components/ManageWalletModal.css +246 -0
  109. package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +12 -0
  110. package/dist/src/components/WalletModal/components/ManageWalletModal.js +36 -0
  111. package/dist/src/components/WalletModal/components/ManageWalletModal.js.map +1 -0
  112. package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +127 -0
  113. package/dist/src/components/WalletModal/components/PreviewTransactionModal.d.ts +17 -0
  114. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js +10 -0
  115. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js.map +1 -0
  116. package/dist/src/components/WalletModal/components/ReceiveModal.css +136 -0
  117. package/dist/src/components/WalletModal/components/ReceiveModal.d.ts +8 -0
  118. package/dist/src/components/WalletModal/components/ReceiveModal.js +22 -0
  119. package/dist/src/components/WalletModal/components/ReceiveModal.js.map +1 -0
  120. package/dist/src/components/WalletModal/components/SendModal.css +277 -0
  121. package/dist/src/components/WalletModal/components/SendModal.d.ts +16 -0
  122. package/dist/src/components/WalletModal/components/SendModal.js +219 -0
  123. package/dist/src/components/WalletModal/components/SendModal.js.map +1 -0
  124. package/dist/src/components/WalletModal/components/SuccessModal.css +85 -0
  125. package/dist/src/components/WalletModal/components/SuccessModal.d.ts +13 -0
  126. package/dist/src/components/WalletModal/components/SuccessModal.js +8 -0
  127. package/dist/src/components/WalletModal/components/SuccessModal.js.map +1 -0
  128. package/dist/src/components/WalletModal/components/TokenSelectorModal.css +240 -0
  129. package/dist/src/components/WalletModal/components/TokenSelectorModal.d.ts +21 -0
  130. package/dist/src/components/WalletModal/components/TokenSelectorModal.js +44 -0
  131. package/dist/src/components/WalletModal/components/TokenSelectorModal.js.map +1 -0
  132. package/dist/src/components/WalletModal/components/UserAvatar.d.ts +9 -0
  133. package/dist/src/components/WalletModal/components/UserAvatar.js +31 -0
  134. package/dist/src/components/WalletModal/components/UserAvatar.js.map +1 -0
  135. package/dist/src/components/WalletModal/components/index.d.ts +23 -0
  136. package/dist/src/components/WalletModal/components/index.js +14 -0
  137. package/dist/src/components/WalletModal/components/index.js.map +1 -0
  138. package/dist/src/components/WalletModal/hooks/index.d.ts +6 -0
  139. package/dist/src/components/WalletModal/hooks/index.js +7 -0
  140. package/dist/src/components/WalletModal/hooks/index.js.map +1 -0
  141. package/dist/src/components/WalletModal/hooks/useAddressValidation.d.ts +4 -0
  142. package/dist/src/components/WalletModal/hooks/useAddressValidation.js +17 -0
  143. package/dist/src/components/WalletModal/hooks/useAddressValidation.js.map +1 -0
  144. package/dist/src/components/WalletModal/hooks/useAmountValidation.d.ts +4 -0
  145. package/dist/src/components/WalletModal/hooks/useAmountValidation.js +29 -0
  146. package/dist/src/components/WalletModal/hooks/useAmountValidation.js.map +1 -0
  147. package/dist/src/components/WalletModal/hooks/useSendTransaction.d.ts +20 -0
  148. package/dist/src/components/WalletModal/hooks/useSendTransaction.js +55 -0
  149. package/dist/src/components/WalletModal/hooks/useSendTransaction.js.map +1 -0
  150. package/dist/src/components/WalletModal/index.d.ts +5 -0
  151. package/dist/src/components/WalletModal/index.js +7 -0
  152. package/dist/src/components/WalletModal/index.js.map +1 -0
  153. package/dist/src/components/WalletModal/utils/addressUtils.d.ts +19 -0
  154. package/dist/src/components/WalletModal/utils/addressUtils.js +62 -0
  155. package/dist/src/components/WalletModal/utils/addressUtils.js.map +1 -0
  156. package/dist/src/components/WalletModal/utils/formatUtils.d.ts +20 -0
  157. package/dist/src/components/WalletModal/utils/formatUtils.js +47 -0
  158. package/dist/src/components/WalletModal/utils/formatUtils.js.map +1 -0
  159. package/dist/src/components/WalletModal/utils/index.d.ts +5 -0
  160. package/dist/src/components/WalletModal/utils/index.js +6 -0
  161. package/dist/src/components/WalletModal/utils/index.js.map +1 -0
  162. package/dist/src/connectors.d.ts +27 -0
  163. package/dist/src/connectors.js +70 -0
  164. package/dist/src/connectors.js.map +1 -0
  165. package/dist/src/hooks.d.ts +13136 -0
  166. package/dist/src/hooks.js +1358 -0
  167. package/dist/src/hooks.js.map +1 -0
  168. package/dist/src/index.d.ts +17 -0
  169. package/dist/src/index.js +14 -0
  170. package/dist/src/index.js.map +1 -0
  171. package/dist/src/types.d.ts +224 -0
  172. package/dist/src/types.js +2 -0
  173. package/dist/src/types.js.map +1 -0
  174. package/dist/src/wagmiConfig.d.ts +16 -0
  175. package/dist/src/wagmiConfig.js +103 -0
  176. package/dist/src/wagmiConfig.js.map +1 -0
  177. package/dist/tsconfig.tsbuildinfo +1 -0
  178. package/package.json +70 -0
@@ -0,0 +1,3782 @@
1
+ import { isValidEmail } from "@abstraxn/signer-core";
2
+ // CSS styles embedded as string
3
+ const ONBOARDING_UI_STYLES = `
4
+ .onboarding-container {
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: center;
8
+ justify-content: center;
9
+ min-height: 100vh;
10
+ padding: 20px;
11
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
12
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
13
+ sans-serif;
14
+ -webkit-font-smoothing: antialiased;
15
+ -moz-osx-font-smoothing: grayscale;
16
+ }
17
+
18
+ /* Inline mode - no extra height */
19
+ .onboarding-container.onboarding-inline {
20
+ min-height: auto;
21
+ padding: 0;
22
+ }
23
+
24
+ /* Modal Styles */
25
+ .onboarding-modal-overlay {
26
+ position: fixed;
27
+ top: 0;
28
+ left: 0;
29
+ right: 0;
30
+ bottom: 0;
31
+ background-color: rgba(0, 0, 0, 0.6);
32
+ backdrop-filter: blur(8px);
33
+ -webkit-backdrop-filter: blur(8px);
34
+ display: none; /* Start hidden by default to prevent flicker */
35
+ align-items: center;
36
+ justify-content: center;
37
+ z-index: 9999;
38
+ opacity: 0;
39
+ transition: opacity 0.3s ease-in-out;
40
+ padding: 16px;
41
+ overflow: hidden;
42
+ overscroll-behavior: contain;
43
+ }
44
+
45
+ .onboarding-modal-overlay::-webkit-scrollbar {
46
+ display: none;
47
+ }
48
+
49
+ .onboarding-modal-overlay {
50
+ -ms-overflow-style: none;
51
+ scrollbar-width: none;
52
+ }
53
+
54
+ .onboarding-modal-overlay.onboarding-modal-open {
55
+ display: flex; /* Show when explicitly opened */
56
+ opacity: 1;
57
+ }
58
+
59
+ .onboarding-modal-overlay.onboarding-modal-closing {
60
+ opacity: 0;
61
+ }
62
+
63
+ .onboarding-modal-content {
64
+ position: relative;
65
+ width: 420px;
66
+ max-width: 420px;
67
+ min-width: 420px;
68
+ max-height: calc(100vh - 32px);
69
+ overflow: hidden;
70
+ transform: scale(0.95);
71
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), height 0.3s ease-in-out;
72
+ display: flex;
73
+ flex-direction: column;
74
+ box-sizing: border-box;
75
+ }
76
+
77
+ @media (max-width: 480px) {
78
+ .onboarding-modal-content {
79
+ width: 100%;
80
+ max-width: 100%;
81
+ min-width: 100%;
82
+ max-height: 100vh;
83
+ border-radius: 0;
84
+ }
85
+
86
+ .onboarding-card {
87
+ width: 100%;
88
+ max-width: 100%;
89
+ min-width: 100%;
90
+ }
91
+
92
+ .onboarding-modal-overlay {
93
+ padding: 0;
94
+ }
95
+ }
96
+
97
+ .onboarding-modal-overlay.onboarding-modal-open .onboarding-modal-content {
98
+ transform: scale(1);
99
+ }
100
+
101
+ .onboarding-modal-overlay.onboarding-modal-closing .onboarding-modal-content {
102
+ transform: scale(0.95);
103
+ }
104
+
105
+ /* Modal container adjustments */
106
+ .onboarding-modal-content .onboarding-container {
107
+ min-height: auto;
108
+ padding: 0;
109
+ }
110
+
111
+ /* Light Theme */
112
+ .onboarding-theme-light {
113
+ color: #000000;
114
+ }
115
+
116
+ /* Dark Theme */
117
+ .onboarding-theme-dark {
118
+ color: #ffffff;
119
+ }
120
+
121
+ .onboarding-card {
122
+ width: 420px;
123
+ max-width: 420px;
124
+ min-width: 420px;
125
+ padding: 20px;
126
+ border-radius: 16px;
127
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
128
+ background-color: inherit;
129
+ position: relative;
130
+ overflow-y: auto;
131
+ overflow-x: hidden;
132
+ max-height: calc(100vh - 32px);
133
+ overscroll-behavior: contain;
134
+ transition: height 0.3s ease-in-out;
135
+ box-sizing: border-box;
136
+ }
137
+
138
+ .onboarding-card::-webkit-scrollbar {
139
+ width: 6px;
140
+ }
141
+
142
+ .onboarding-card::-webkit-scrollbar-track {
143
+ background: transparent;
144
+ }
145
+
146
+ .onboarding-card::-webkit-scrollbar-thumb {
147
+ background: rgba(0, 0, 0, 0.2);
148
+ border-radius: 3px;
149
+ }
150
+
151
+ .onboarding-theme-dark .onboarding-card::-webkit-scrollbar-thumb {
152
+ background: rgba(255, 255, 255, 0.2);
153
+ }
154
+
155
+ @media (max-width: 480px) {
156
+ .onboarding-card {
157
+ padding: 24px 20px;
158
+ border-radius: 0;
159
+ max-height: 100vh;
160
+ }
161
+ }
162
+
163
+ .onboarding-theme-light .onboarding-card {
164
+ background-color: #ffffff;
165
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
166
+ border: 1px solid rgba(0, 0, 0, 0.06);
167
+ }
168
+
169
+ .onboarding-theme-dark .onboarding-card {
170
+ background-color: #1f2937;
171
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 1px rgba(255, 255, 255, 0.1);
172
+ border: 1px solid rgba(255, 255, 255, 0.1);
173
+ }
174
+
175
+ .onboarding-header {
176
+ text-align: center;
177
+ margin-bottom: 20px;
178
+ }
179
+
180
+ .onboarding-logo-section {
181
+ display: flex;
182
+ flex-direction: column;
183
+ align-items: center;
184
+ margin-bottom: 12px;
185
+ }
186
+
187
+ .onboarding-logo-container {
188
+ display: flex;
189
+ justify-content: center;
190
+ align-items: center;
191
+ margin-bottom: 6px;
192
+ gap: 10px;
193
+ }
194
+
195
+ .onboarding-logo-img {
196
+ max-width: 120px;
197
+ max-height: 60px;
198
+ object-fit: contain;
199
+ }
200
+
201
+ .onboarding-title {
202
+ font-size: 24px;
203
+ font-weight: 600;
204
+ margin: 0;
205
+ color: inherit;
206
+ text-align: center;
207
+ margin-bottom: 4px;
208
+ letter-spacing: -0.01em;
209
+ line-height: 1.2;
210
+ }
211
+
212
+ @media (max-width: 480px) {
213
+ .onboarding-title {
214
+ font-size: 24px;
215
+ margin-bottom: 24px;
216
+ }
217
+ }
218
+
219
+ .onboarding-theme-light .onboarding-title {
220
+ color: #111827;
221
+ }
222
+
223
+ .onboarding-theme-dark .onboarding-title {
224
+ color: #ffffff;
225
+ }
226
+
227
+ .onboarding-form {
228
+ width: 100%;
229
+ }
230
+
231
+ .onboarding-input-group {
232
+ margin-bottom: 8px;
233
+ }
234
+
235
+ .onboarding-input-label {
236
+ display: block;
237
+ font-size: 14px;
238
+ font-weight: 500;
239
+ color: #374151;
240
+ margin-bottom: 4px;
241
+ letter-spacing: -0.01em;
242
+ line-height: 1.5;
243
+ text-align: left;
244
+ width: 100%;
245
+ }
246
+
247
+ .onboarding-theme-dark .onboarding-input-label {
248
+ color: #e5e7eb;
249
+ }
250
+
251
+ .onboarding-input-wrapper {
252
+ position: relative;
253
+ display: flex;
254
+ align-items: center;
255
+ }
256
+
257
+ .onboarding-input-icon {
258
+ position: absolute;
259
+ left: 14px;
260
+ color: #9ca3af;
261
+ pointer-events: none;
262
+ z-index: 1;
263
+ width: 18px;
264
+ height: 18px;
265
+ }
266
+
267
+ .onboarding-theme-dark .onboarding-input-icon {
268
+ color: #9ca3af;
269
+ }
270
+
271
+ .onboarding-input {
272
+ width: 100%;
273
+ padding: 10px 14px 10px 44px;
274
+ font-size: 14px;
275
+ border: 1.5px solid #e5e7eb;
276
+ border-radius: 8px;
277
+ background-color: inherit;
278
+ transition: all 0.15s ease;
279
+ box-sizing: border-box;
280
+ font-weight: 400;
281
+ letter-spacing: 0;
282
+ line-height: 1.5;
283
+ }
284
+
285
+ .onboarding-theme-light .onboarding-input {
286
+ background-color: #ffffff;
287
+ border-color: #e5e7eb;
288
+ color: #000000;
289
+ }
290
+
291
+ .onboarding-theme-light .onboarding-input:-webkit-autofill,
292
+ .onboarding-theme-light .onboarding-input:-webkit-autofill:hover,
293
+ .onboarding-theme-light .onboarding-input:-webkit-autofill:focus,
294
+ .onboarding-theme-light .onboarding-input:-webkit-autofill:active {
295
+ -webkit-box-shadow: 0 0 0 30px #ffffff inset !important;
296
+ -webkit-text-fill-color: #111827 !important;
297
+ transition: background-color 5000s ease-in-out 0s;
298
+ }
299
+
300
+ .onboarding-theme-dark .onboarding-input {
301
+ background-color: #374151;
302
+ color: #ffffff;
303
+ }
304
+
305
+ .onboarding-theme-dark .onboarding-input:-webkit-autofill,
306
+ .onboarding-theme-dark .onboarding-input:-webkit-autofill:hover,
307
+ .onboarding-theme-dark .onboarding-input:-webkit-autofill:focus,
308
+ .onboarding-theme-dark .onboarding-input:-webkit-autofill:active {
309
+ -webkit-box-shadow: 0 0 0 30px #374151 inset !important;
310
+ -webkit-text-fill-color: #ffffff !important;
311
+ color: #ffffff !important;
312
+ transition: background-color 5000s ease-in-out 0s;
313
+ }
314
+
315
+ .onboarding-input:focus {
316
+ outline: none;
317
+ }
318
+
319
+ .onboarding-theme-light .onboarding-input:focus {
320
+ border-color: #111827;
321
+ box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.1);
322
+ background-color: #ffffff;
323
+ color: #000000;
324
+ }
325
+
326
+ .onboarding-theme-dark .onboarding-input {
327
+ border-color: #4b5563;
328
+ background-color: #374151;
329
+ }
330
+
331
+ .onboarding-theme-dark .onboarding-input:focus {
332
+ border-color: #9ca3af;
333
+ box-shadow: 0 0 0 3px rgba(156, 163, 175, 0.2);
334
+ background-color: #374151;
335
+ color: #ffffff;
336
+ }
337
+
338
+ .onboarding-input:disabled {
339
+ opacity: 0.6;
340
+ cursor: not-allowed;
341
+ }
342
+
343
+ /* Arrow button inside email input */
344
+ .onboarding-input-arrow-button {
345
+ position: absolute;
346
+ right: 8px;
347
+ top: 50%;
348
+ transform: translateY(-50%);
349
+ background: none;
350
+ border: none;
351
+ cursor: pointer;
352
+ padding: 4px;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ z-index: 2;
357
+ transition: all 0.2s ease;
358
+ border-radius: 4px;
359
+ }
360
+
361
+ /* Light theme arrow button */
362
+ .onboarding-theme-light .onboarding-input-arrow-button {
363
+ color: #111827;
364
+ }
365
+
366
+ .onboarding-theme-light .onboarding-input-arrow-button:hover {
367
+ background-color: rgba(17, 24, 39, 0.1);
368
+ color: #1f2937;
369
+ }
370
+
371
+ .onboarding-theme-light .onboarding-input-arrow-button:active {
372
+ background-color: rgba(17, 24, 39, 0.15);
373
+ }
374
+
375
+ /* Dark theme arrow button */
376
+ .onboarding-theme-dark .onboarding-input-arrow-button {
377
+ color: #9ca3af;
378
+ }
379
+
380
+ .onboarding-theme-dark .onboarding-input-arrow-button:hover {
381
+ background-color: rgba(156, 163, 175, 0.2);
382
+ color: #d1d5db;
383
+ }
384
+
385
+ .onboarding-theme-dark .onboarding-input-arrow-button:active {
386
+ background-color: rgba(156, 163, 175, 0.25);
387
+ }
388
+
389
+ .onboarding-input-arrow-button:disabled {
390
+ opacity: 0.5;
391
+ cursor: not-allowed;
392
+ }
393
+
394
+ .onboarding-input-arrow-icon {
395
+ width: 20px;
396
+ height: 20px;
397
+ }
398
+
399
+ .onboarding-error {
400
+ padding: 12px;
401
+ margin-bottom: 16px;
402
+ background-color: #fee2e2;
403
+ color: #dc2626;
404
+ border-radius: 8px;
405
+ font-size: 14px;
406
+ }
407
+
408
+ .onboarding-theme-dark .onboarding-error {
409
+ background-color: #7f1d1d;
410
+ color: #fca5a5;
411
+ }
412
+
413
+ .onboarding-button {
414
+ width: 100%;
415
+ padding: 13px 24px;
416
+ font-size: 15px;
417
+ font-weight: 600;
418
+ border: none;
419
+ border-radius: 12px;
420
+ cursor: pointer;
421
+ transition: all 0.2s ease;
422
+ display: flex;
423
+ align-items: center;
424
+ justify-content: center;
425
+ gap: 8px;
426
+ margin-bottom: 6px;
427
+ letter-spacing: -0.01em;
428
+ line-height: 1.5;
429
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
430
+ min-height: 48px;
431
+ }
432
+
433
+ .onboarding-button:disabled {
434
+ opacity: 0.6;
435
+ cursor: not-allowed;
436
+ }
437
+
438
+ .onboarding-button-primary {
439
+ font-weight: 600;
440
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
441
+ }
442
+
443
+ /* Light theme button */
444
+ .onboarding-theme-light .onboarding-button-primary {
445
+ background-color: #111827;
446
+ color: #ffffff;
447
+ }
448
+
449
+ .onboarding-theme-light .onboarding-button-primary:hover:not(:disabled) {
450
+ background-color: #1f2937;
451
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
452
+ transform: translateY(-1px);
453
+ }
454
+
455
+ .onboarding-theme-light .onboarding-button-primary:active:not(:disabled) {
456
+ background-color: #374151;
457
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
458
+ transform: translateY(0);
459
+ }
460
+
461
+ /* Dark theme button */
462
+ .onboarding-theme-dark .onboarding-button-primary {
463
+ background-color: #4b5563;
464
+ color: #ffffff;
465
+ }
466
+
467
+ .onboarding-theme-dark .onboarding-button-primary:hover:not(:disabled) {
468
+ background-color: #6b7280;
469
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
470
+ transform: translateY(-1px);
471
+ }
472
+
473
+ .onboarding-theme-dark .onboarding-button-primary:active:not(:disabled) {
474
+ background-color: #6b7280;
475
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
476
+ transform: translateY(0);
477
+ }
478
+
479
+ .onboarding-button-google {
480
+ background-color: #ffffff;
481
+ color: #1f2937;
482
+ border: 1.5px solid #e5e7eb;
483
+ font-weight: 500;
484
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
485
+ font-size: 14px;
486
+ }
487
+
488
+ .onboarding-button-social {
489
+ background-color: #ffffff;
490
+ color: #1f2937;
491
+ border: 1.5px solid #e5e7eb;
492
+ font-weight: 500;
493
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
494
+ font-size: 14px;
495
+ }
496
+
497
+ .onboarding-theme-dark .onboarding-button-google {
498
+ background-color: #374151;
499
+ color: #ffffff;
500
+ border-color: #4b5563;
501
+ }
502
+
503
+ .onboarding-theme-dark .onboarding-button-social {
504
+ background-color: #2f3239;
505
+ color: #ffffff;
506
+ border-color: #4b5563;
507
+ }
508
+
509
+ .onboarding-button-google:hover:not(:disabled) {
510
+ background-color: #f9fafb;
511
+ border-color: #d1d5db;
512
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
513
+ transform: translateY(-1px);
514
+ }
515
+
516
+ .onboarding-button-social:hover:not(:disabled) {
517
+ background-color: #f9fafb;
518
+ border-color: #d1d5db;
519
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
520
+ transform: translateY(-1px);
521
+ }
522
+
523
+ .onboarding-theme-dark .onboarding-button-google:hover:not(:disabled) {
524
+ background-color: #4b5563;
525
+ border-color: #6b7280;
526
+ }
527
+
528
+ .onboarding-theme-dark .onboarding-button-social:hover:not(:disabled) {
529
+ background-color: #3c404a;
530
+ border-color: #6b7280;
531
+ }
532
+
533
+ .onboarding-button-social-icon {
534
+ width: 100%;
535
+ height: 48px;
536
+ display: inline-flex;
537
+ align-items: center;
538
+ justify-content: center;
539
+ border: 1.5px solid #e5e7eb;
540
+ border-radius: 12px;
541
+ background-color: #ffffff;
542
+ color: #1f2937;
543
+ cursor: pointer;
544
+ transition: all 0.2s ease;
545
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
546
+ flex: 1;
547
+ }
548
+
549
+ .onboarding-button-social-icon:hover:not(:disabled) {
550
+ background-color: #f9fafb;
551
+ border-color: #d1d5db;
552
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
553
+ transform: translateY(-1px);
554
+ }
555
+
556
+ .onboarding-theme-dark .onboarding-button-social-icon {
557
+ background-color: #2f3239;
558
+ border-color: #4b5563;
559
+ color: #ffffff;
560
+ }
561
+
562
+ .onboarding-theme-dark .onboarding-button-social-icon:hover:not(:disabled) {
563
+ background-color: #3c404a;
564
+ border-color: #6b7280;
565
+ }
566
+
567
+ .onboarding-social-grid {
568
+ display: flex;
569
+ gap: 12px;
570
+ flex-wrap: nowrap;
571
+ margin-top: 8px;
572
+ width: 100%;
573
+ }
574
+
575
+ /* Hide social buttons when OTP screen is visible - using class for better browser support */
576
+ .onboarding-card.onboarding-otp-active .onboarding-button-google,
577
+ .onboarding-card.onboarding-otp-active .onboarding-button-social,
578
+ .onboarding-card.onboarding-otp-active .onboarding-social-grid,
579
+ .onboarding-card.onboarding-otp-active .onboarding-divider:not(.onboarding-otp-divider),
580
+ .onboarding-card.onboarding-otp-active .onboarding-button-passkey,
581
+ .onboarding-card.onboarding-otp-active .onboarding-passkey-signup-link,
582
+ .onboarding-card.onboarding-otp-active .onboarding-passkey-divider,
583
+ .onboarding-card.onboarding-otp-active .onboarding-form:not(.onboarding-otp-form),
584
+ .onboarding-card.onboarding-otp-active .onboarding-input-group:not(.onboarding-otp-inputs-container) {
585
+ display: none !important;
586
+ visibility: hidden !important;
587
+ opacity: 0 !important;
588
+ height: 0 !important;
589
+ margin: 0 !important;
590
+ padding: 0 !important;
591
+ overflow: hidden !important;
592
+ pointer-events: none !important;
593
+ }
594
+
595
+ /* Fallback using :has() for modern browsers */
596
+ @supports selector(:has(*)) {
597
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-button-google,
598
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-button-social,
599
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-social-grid,
600
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-divider:not(.onboarding-otp-divider),
601
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-button-passkey,
602
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-passkey-signup-link,
603
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-passkey-divider {
604
+ display: none !important;
605
+ visibility: hidden !important;
606
+ opacity: 0 !important;
607
+ height: 0 !important;
608
+ margin: 0 !important;
609
+ padding: 0 !important;
610
+ overflow: hidden !important;
611
+ pointer-events: none !important;
612
+ }
613
+ }
614
+
615
+ .onboarding-social-icon {
616
+ width: 20px;
617
+ height: 20px;
618
+ }
619
+
620
+ .onboarding-google-icon {
621
+ width: 20px;
622
+ height: 20px;
623
+ }
624
+
625
+ .onboarding-divider {
626
+ display: flex;
627
+ align-items: center;
628
+ margin: 10px 0;
629
+ position: relative;
630
+ }
631
+
632
+ .onboarding-divider::before,
633
+ .onboarding-divider::after {
634
+ content: '';
635
+ flex: 1;
636
+ height: 1px;
637
+ background-color: #e5e7eb;
638
+ }
639
+
640
+ .onboarding-theme-dark .onboarding-divider::before,
641
+ .onboarding-theme-dark .onboarding-divider::after {
642
+ background-color: #4b5563;
643
+ }
644
+
645
+ .onboarding-divider-text {
646
+ padding: 0 16px;
647
+ font-size: 14px;
648
+ color: #6b7280;
649
+ }
650
+
651
+ .onboarding-theme-dark .onboarding-divider-text {
652
+ color: #9ca3af;
653
+ }
654
+
655
+
656
+ .onboarding-footer {
657
+ margin-top: 16px;
658
+ padding: 0;
659
+ text-align: center;
660
+ background: transparent;
661
+ border: none;
662
+ border-radius: 0;
663
+ }
664
+
665
+ .onboarding-footer-text {
666
+ font-size: 11px;
667
+ color: #9ca3af;
668
+ margin: 0;
669
+ line-height: 1.5;
670
+ font-weight: 400;
671
+ letter-spacing: 0.01em;
672
+ }
673
+
674
+ .onboarding-theme-dark .onboarding-footer-text {
675
+ color: #6b7280;
676
+ }
677
+
678
+ .onboarding-footer-brand {
679
+ font-weight: 700;
680
+ text-decoration: none;
681
+ margin-left: 2px;
682
+ cursor: pointer;
683
+ transition: opacity 0.15s ease;
684
+ }
685
+
686
+ .onboarding-theme-light .onboarding-footer-brand {
687
+ color: #111827;
688
+ }
689
+
690
+ .onboarding-theme-dark .onboarding-footer-brand {
691
+ color: #9ca3af;
692
+ }
693
+
694
+ .onboarding-footer-brand:hover {
695
+ text-decoration: none;
696
+ opacity: 0.8;
697
+ }
698
+
699
+ /* Modal container adjustments */
700
+ .onboarding-modal-content .onboarding-container {
701
+ min-height: auto;
702
+ padding: 0;
703
+ }
704
+
705
+ .onboarding-modal-content .onboarding-card {
706
+ margin: 0;
707
+ }
708
+
709
+ /* OTP Verification Screen */
710
+ .onboarding-otp-verification {
711
+ display: flex;
712
+ flex-direction: column;
713
+ align-items: center;
714
+ text-align: center;
715
+ width: 100%;
716
+ }
717
+
718
+ .onboarding-otp-verification .onboarding-button {
719
+ /* Match OTP inputs width: 6 inputs × 44px + 5 gaps × 10px = 314px */
720
+ width: 314px;
721
+ max-width: 314px;
722
+ }
723
+
724
+ .onboarding-otp-icon-container {
725
+ width: 64px;
726
+ height: 64px;
727
+ border-radius: 50%;
728
+ display: flex;
729
+ align-items: center;
730
+ justify-content: center;
731
+ margin-bottom: 24px;
732
+ position: relative;
733
+ background: transparent;
734
+ border: 3px solid #eef2ff;
735
+ }
736
+
737
+ .onboarding-otp-icon-container::after {
738
+ content: '';
739
+ position: absolute;
740
+ top: -3px;
741
+ left: -3px;
742
+ right: -3px;
743
+ bottom: -3px;
744
+ border-radius: 50%;
745
+ border: 3px solid transparent;
746
+ border-top-color: #363ff9;
747
+ animation: onboarding-spin 2s linear infinite;
748
+ }
749
+
750
+ @keyframes onboarding-spin {
751
+ 0% { transform: rotate(0deg); }
752
+ 100% { transform: rotate(360deg); }
753
+ }
754
+
755
+ .onboarding-theme-dark .onboarding-otp-icon-container {
756
+ border-color: rgba(156, 163, 175, 0.2);
757
+ }
758
+
759
+ .onboarding-otp-icon-inner {
760
+ width: 100%;
761
+ height: 100%;
762
+ background-color: transparent;
763
+ display: flex;
764
+ align-items: center;
765
+ justify-content: center;
766
+ z-index: 1;
767
+ }
768
+
769
+ .onboarding-otp-icon {
770
+ width: 28px;
771
+ height: 28px;
772
+ color: #363ff9;
773
+ }
774
+
775
+ .onboarding-otp-title {
776
+ font-size: 22px;
777
+ font-weight: 600;
778
+ margin: 0 0 12px 0;
779
+ color: inherit;
780
+ line-height: 1.4;
781
+ letter-spacing: -0.01em;
782
+ }
783
+
784
+ .onboarding-theme-light .onboarding-otp-title {
785
+ color: #111827;
786
+ }
787
+
788
+ .onboarding-theme-dark .onboarding-otp-title {
789
+ color: #ffffff;
790
+ }
791
+
792
+ .onboarding-otp-instruction {
793
+ font-size: 14px;
794
+ color: #6b7280;
795
+ margin: 0 0 4px 0;
796
+ line-height: 1.5;
797
+ font-weight: 400;
798
+ }
799
+
800
+ .onboarding-theme-dark .onboarding-otp-instruction {
801
+ color: #9ca3af;
802
+ }
803
+
804
+ .onboarding-otp-email {
805
+ font-size: 14px;
806
+ font-weight: 600;
807
+ color: inherit;
808
+ margin: 0 0 24px 0;
809
+ line-height: 1.5;
810
+ }
811
+
812
+ .onboarding-theme-light .onboarding-otp-email {
813
+ color: #111827;
814
+ }
815
+
816
+ .onboarding-theme-dark .onboarding-otp-email {
817
+ color: #ffffff;
818
+ }
819
+
820
+ .onboarding-otp-inputs-container {
821
+ display: flex;
822
+ gap: 10px;
823
+ justify-content: center;
824
+ margin-bottom: 24px;
825
+ width: 100%;
826
+ }
827
+
828
+ .onboarding-otp-input {
829
+ width: 44px;
830
+ height: 52px;
831
+ text-align: center;
832
+ font-size: 20px;
833
+ font-weight: 600;
834
+ border: 1.5px solid #e5e7eb;
835
+ border-radius: 8px;
836
+ background-color: inherit;
837
+ transition: all 0.15s ease;
838
+ box-sizing: border-box;
839
+ line-height: 1;
840
+ }
841
+
842
+ .onboarding-theme-light .onboarding-otp-input {
843
+ background-color: #ffffff;
844
+ border-color: #e5e7eb;
845
+ color: #000000;
846
+ }
847
+
848
+ .onboarding-otp-input:focus {
849
+ outline: none;
850
+ }
851
+
852
+ .onboarding-theme-light .onboarding-otp-input:focus {
853
+ border-color: #111827;
854
+ box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.1);
855
+ background-color: #ffffff;
856
+ color: #000000;
857
+ }
858
+
859
+ .onboarding-theme-dark .onboarding-otp-input {
860
+ border-color: #4b5563;
861
+ background-color: #374151;
862
+ color: #ffffff;
863
+ }
864
+
865
+ .onboarding-theme-dark .onboarding-otp-input:focus {
866
+ border-color: #9ca3af;
867
+ box-shadow: 0 0 0 3px rgba(156, 163, 175, 0.2);
868
+ background-color: #4b5563;
869
+ color: #ffffff;
870
+ }
871
+
872
+ .onboarding-otp-resend {
873
+ text-align: center;
874
+ margin-top: 20px;
875
+ line-height: 1.5;
876
+ }
877
+
878
+ .onboarding-otp-resend-text {
879
+ font-size: 13px;
880
+ color: #6b7280;
881
+ margin: 0;
882
+ line-height: 1.5;
883
+ font-weight: 400;
884
+ }
885
+
886
+ .onboarding-theme-dark .onboarding-otp-resend-text {
887
+ color: #9ca3af;
888
+ }
889
+
890
+ .onboarding-otp-resend-link {
891
+ color: #dc2626;
892
+ text-decoration: underline;
893
+ cursor: pointer;
894
+ font-weight: 500;
895
+ transition: color 0.15s ease;
896
+ font-size: 13px;
897
+ }
898
+
899
+ .onboarding-otp-resend-link:hover:not(:disabled) {
900
+ color: #b91c1c;
901
+ }
902
+
903
+ .onboarding-otp-resend-link:disabled,
904
+ .onboarding-otp-resend-link-disabled {
905
+ color: #9ca3af;
906
+ cursor: not-allowed;
907
+ text-decoration: none;
908
+ pointer-events: none;
909
+ opacity: 0.6;
910
+ }
911
+
912
+ .onboarding-theme-dark .onboarding-otp-resend-link:disabled,
913
+ .onboarding-theme-dark .onboarding-otp-resend-link-disabled {
914
+ color: #6b7280;
915
+ }
916
+
917
+ /* Passkey Button Styles */
918
+ .onboarding-button-passkey {
919
+ background-color: #374151;
920
+ color: #ffffff;
921
+ border: 1.5px solid #4b5563;
922
+ font-weight: 500;
923
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
924
+ font-size: 14px;
925
+ }
926
+
927
+ .onboarding-theme-dark .onboarding-button-passkey {
928
+ background-color: #4b5563;
929
+ color: #ffffff;
930
+ border-color: #6b7280;
931
+ }
932
+
933
+ .onboarding-button-passkey:hover:not(:disabled) {
934
+ background-color: #4b5563;
935
+ border-color: #6b7280;
936
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
937
+ transform: translateY(-1px);
938
+ }
939
+
940
+ .onboarding-theme-dark .onboarding-button-passkey:hover:not(:disabled) {
941
+ background-color: #6b7280;
942
+ border-color: #9ca3af;
943
+ }
944
+
945
+ .onboarding-passkey-icon {
946
+ width: 20px;
947
+ height: 20px;
948
+ }
949
+
950
+ .onboarding-passkey-signup-link {
951
+ text-decoration: underline;
952
+ cursor: pointer;
953
+ font-size: 14px;
954
+ font-weight: 500;
955
+ margin-top: 12px;
956
+ display: block;
957
+ text-align: center;
958
+ transition: color 0.15s ease;
959
+ }
960
+
961
+ .onboarding-theme-light .onboarding-passkey-signup-link {
962
+ color: #2170f5;
963
+ }
964
+
965
+
966
+ .onboarding-theme-dark .onboarding-passkey-signup-link {
967
+ color: #2170f5;
968
+ }
969
+
970
+ `;
971
+ /**
972
+ * OnboardingUI Class
973
+ *
974
+ * A customizable user onboarding component with email-OTP and Google login options.
975
+ * Works with vanilla JavaScript - no framework dependencies required.
976
+ */
977
+ export class OnboardingUIWeb {
978
+ static stylesInjected = false;
979
+ config;
980
+ container = null;
981
+ rootElement = null;
982
+ modalOverlay = null;
983
+ modalContent = null;
984
+ emailInput = null;
985
+ otpInput = null;
986
+ otpInputs = [];
987
+ autoSubmitEnabled = true; // Flag to control auto-submit after failed attempts
988
+ emailForm = null;
989
+ continueButton = null;
990
+ emailArrowButton = null;
991
+ googleButton = null;
992
+ twitterButton = null;
993
+ discordButton = null;
994
+ passkeyLoginButton = null;
995
+ passkeySignupLink = null;
996
+ passkeyDivider = null;
997
+ passkeyErrorElement = null;
998
+ errorElement = null;
999
+ otpGroup = null;
1000
+ otpVerificationScreen = null;
1001
+ resendButton = null;
1002
+ socialGrid = null;
1003
+ externalWalletContainer = null;
1004
+ externalWalletDivider = null;
1005
+ divider = null;
1006
+ footer = null;
1007
+ verifyButton = null;
1008
+ email = "";
1009
+ otp = "";
1010
+ otpSent = false;
1011
+ loading = false;
1012
+ activeButton = null;
1013
+ resendCooldown = 0;
1014
+ resendCooldownTimer = null;
1015
+ externalWalletsEnabled = false;
1016
+ constructor(config, externalWalletsEnabled = false) {
1017
+ this.externalWalletsEnabled = externalWalletsEnabled;
1018
+ const defaultTheme = config.theme || "light";
1019
+ this.config = {
1020
+ theme: defaultTheme,
1021
+ showFooter: config.showFooter !== false,
1022
+ className: config.className || "",
1023
+ modal: config.modal !== false,
1024
+ closeOnBackdropClick: config.closeOnBackdropClick !== false,
1025
+ onboardTitle: config.onboardTitle || "Sign In",
1026
+ handleUrlParams: config.handleUrlParams !== false,
1027
+ ...config,
1028
+ };
1029
+ }
1030
+ /**
1031
+ * Show external wallet container and divider
1032
+ * Call this before init() to prevent flicker when external wallets are enabled
1033
+ */
1034
+ showExternalWallets() {
1035
+ if (this.externalWalletContainer) {
1036
+ this.externalWalletContainer.style.display = "";
1037
+ }
1038
+ if (this.externalWalletDivider) {
1039
+ this.externalWalletDivider.style.display = "";
1040
+ }
1041
+ }
1042
+ /**
1043
+ * Initialize and render the component
1044
+ */
1045
+ init() {
1046
+ this.injectStyles();
1047
+ this.setupContainer();
1048
+ this.render();
1049
+ this.attachEventListeners();
1050
+ this.checkUrlParams();
1051
+ }
1052
+ /**
1053
+ * Check URL parameters for social login callbacks
1054
+ */
1055
+ checkUrlParams() {
1056
+ if (typeof window === "undefined")
1057
+ return;
1058
+ if (!this.config.handleUrlParams)
1059
+ return;
1060
+ const params = new URLSearchParams(window.location.search);
1061
+ const success = params.get("success");
1062
+ const error = params.get("error");
1063
+ const accessToken = params.get("accessToken");
1064
+ const provider = params.get("provider") || params.get("authProvider");
1065
+ // Helper to extract provider from accessToken JWT
1066
+ const getAuthProviderFromToken = (token) => {
1067
+ try {
1068
+ const base64Url = token.split(".")[1];
1069
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
1070
+ const jsonPayload = decodeURIComponent(window
1071
+ .atob(base64)
1072
+ .split("")
1073
+ .map(function (c) {
1074
+ return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
1075
+ })
1076
+ .join(""));
1077
+ const payload = JSON.parse(jsonPayload);
1078
+ return (payload.authProvider || payload.provider || "").toLowerCase();
1079
+ }
1080
+ catch (e) {
1081
+ return null;
1082
+ }
1083
+ };
1084
+ // Detect OAuth callback: check for provider in URL or extract from accessToken
1085
+ let detectedProvider = provider;
1086
+ if (!detectedProvider && accessToken) {
1087
+ detectedProvider = getAuthProviderFromToken(accessToken);
1088
+ }
1089
+ const isOAuthCallback = detectedProvider &&
1090
+ (detectedProvider === "google" ||
1091
+ detectedProvider === "discord" ||
1092
+ detectedProvider === "twitter" ||
1093
+ detectedProvider === "x");
1094
+ const hasOAuthParams = isOAuthCallback || (success === "true" && !!accessToken);
1095
+ // If success=true and it's an OAuth callback (Google, Discord, Twitter), show loading modal with header and footer
1096
+ if (success === "true" && hasOAuthParams) {
1097
+ // Show loading modal immediately - it creates its own overlay
1098
+ // Use requestAnimationFrame + setTimeout to ensure DOM is ready
1099
+ requestAnimationFrame(() => {
1100
+ setTimeout(() => {
1101
+ // Directly show loading modal - it will create its own overlay with header and footer
1102
+ this.showLoadingModal();
1103
+ }, 100);
1104
+ });
1105
+ return;
1106
+ }
1107
+ // Check login source to decide if we should handle this (for errors)
1108
+ const source = localStorage.getItem("abstraxn_login_source");
1109
+ const isModal = this.config.modal;
1110
+ if (source) {
1111
+ if (source === "modal" && !isModal)
1112
+ return; // Modal initiated, I am Inline -> Ignore
1113
+ if (source === "inline" && isModal)
1114
+ return; // Inline initiated, I am Modal -> Ignore
1115
+ }
1116
+ else {
1117
+ // No source (e.g. unknown origin), default to Modal handling it
1118
+ if (!isModal)
1119
+ return;
1120
+ }
1121
+ if (error) {
1122
+ this.showError(decodeURIComponent(error));
1123
+ }
1124
+ }
1125
+ /**
1126
+ * Show error screen or modal (public method)
1127
+ */
1128
+ showError(message) {
1129
+ // Hide loading modal if it exists
1130
+ this.hideLoadingModal();
1131
+ if (this.config.modal) {
1132
+ this.showErrorModal(message);
1133
+ }
1134
+ else {
1135
+ this.showErrorScreen(message);
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Show error screen
1140
+ */
1141
+ showErrorScreen(message) {
1142
+ if (!this.rootElement)
1143
+ return;
1144
+ // Hide loading modal if it exists (for inline components)
1145
+ this.hideLoadingModal();
1146
+ const card = this.rootElement.querySelector(".onboarding-card");
1147
+ if (!card)
1148
+ return;
1149
+ // Add error mode class to card
1150
+ card.classList.add("onboarding-mode-error");
1151
+ card.classList.remove("onboarding-mode-loading");
1152
+ // Create or show error container
1153
+ let errorContainer = this.rootElement.querySelector(".onboarding-error-container");
1154
+ if (!errorContainer) {
1155
+ errorContainer = this.createElement("div", {
1156
+ className: "onboarding-error-container",
1157
+ style: "display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px 0; text-align: center;",
1158
+ });
1159
+ // Error Icon
1160
+ const iconContainer = this.createElement("div", {
1161
+ style: "width: 64px; height: 64px; background-color: #fee2e2; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 20px;",
1162
+ });
1163
+ // Dark mode adjustment for icon container
1164
+ if (this.config.theme === "dark") {
1165
+ iconContainer.style.backgroundColor = "rgba(220, 38, 38, 0.2)";
1166
+ }
1167
+ const iconSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1168
+ iconSvg.setAttribute("width", "32");
1169
+ iconSvg.setAttribute("height", "32");
1170
+ iconSvg.setAttribute("viewBox", "0 0 24 24");
1171
+ iconSvg.setAttribute("fill", "none");
1172
+ iconSvg.setAttribute("stroke", "#dc2626");
1173
+ iconSvg.setAttribute("stroke-width", "2");
1174
+ iconSvg.setAttribute("stroke-linecap", "round");
1175
+ iconSvg.setAttribute("stroke-linejoin", "round");
1176
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1177
+ circle.setAttribute("cx", "12");
1178
+ circle.setAttribute("cy", "12");
1179
+ circle.setAttribute("r", "10");
1180
+ iconSvg.appendChild(circle);
1181
+ const line1 = document.createElementNS("http://www.w3.org/2000/svg", "line");
1182
+ line1.setAttribute("x1", "12");
1183
+ line1.setAttribute("y1", "8");
1184
+ line1.setAttribute("x2", "12");
1185
+ line1.setAttribute("y2", "12");
1186
+ iconSvg.appendChild(line1);
1187
+ const line2 = document.createElementNS("http://www.w3.org/2000/svg", "line");
1188
+ line2.setAttribute("x1", "12");
1189
+ line2.setAttribute("y1", "16");
1190
+ line2.setAttribute("x2", "12.01");
1191
+ line2.setAttribute("y2", "16");
1192
+ iconSvg.appendChild(line2);
1193
+ iconContainer.appendChild(iconSvg);
1194
+ errorContainer.appendChild(iconContainer);
1195
+ // Title
1196
+ const title = this.createElement("h3", {
1197
+ textContent: "Authentication Failed",
1198
+ style: "font-size: 20px; font-weight: 600; margin: 0 0 12px 0; color: inherit;",
1199
+ });
1200
+ errorContainer.appendChild(title);
1201
+ // Message
1202
+ const msg = this.createElement("p", {
1203
+ textContent: message,
1204
+ style: "font-size: 14px; color: #6b7280; margin: 0 0 24px 0; line-height: 1.5; max-width: 100%; word-break: break-word;",
1205
+ });
1206
+ if (this.config.theme === "dark") {
1207
+ msg.style.color = "#9ca3af";
1208
+ }
1209
+ errorContainer.appendChild(msg);
1210
+ // OK Button
1211
+ const okButton = this.createElement("button", {
1212
+ className: "onboarding-button onboarding-button-primary",
1213
+ textContent: "OK",
1214
+ style: "width: 100%; max-width: 200px;",
1215
+ });
1216
+ okButton.addEventListener("click", () => {
1217
+ if (this.config.modal) {
1218
+ this.close();
1219
+ }
1220
+ else {
1221
+ this.resetToLoginForm();
1222
+ }
1223
+ // Also clear URL params to prevent showing error again on refresh
1224
+ const url = new URL(window.location.href);
1225
+ url.searchParams.delete("error");
1226
+ window.history.replaceState({}, document.title, url.toString());
1227
+ });
1228
+ errorContainer.appendChild(okButton);
1229
+ card.appendChild(errorContainer);
1230
+ }
1231
+ else {
1232
+ // Update message if container exists
1233
+ const msgEl = errorContainer.querySelector("p");
1234
+ if (msgEl)
1235
+ msgEl.textContent = message;
1236
+ errorContainer.style.display = "flex";
1237
+ }
1238
+ // Move footer to the bottom
1239
+ if (this.footer) {
1240
+ card.appendChild(this.footer);
1241
+ }
1242
+ }
1243
+ /**
1244
+ * Show loading screen
1245
+ */
1246
+ showLoadingScreen() {
1247
+ if (!this.rootElement)
1248
+ return;
1249
+ // If inline component, show loading as a modal overlay
1250
+ if (!this.config.modal) {
1251
+ this.showLoadingModal();
1252
+ return;
1253
+ }
1254
+ // Hide the main modal overlay to prevent showing wallet options below loading screen
1255
+ if (this.modalOverlay) {
1256
+ this.modalOverlay.style.display = "none";
1257
+ }
1258
+ // Show loading modal overlay instead (with higher z-index)
1259
+ this.showLoadingModal();
1260
+ }
1261
+ /**
1262
+ * Show loading screen as a modal overlay (for inline components)
1263
+ * Public method to allow external access
1264
+ */
1265
+ showLoadingModal() {
1266
+ // Hide any other modals that might be showing (wallet selection, etc.)
1267
+ const otherModals = document.querySelectorAll(".onboarding-modal-overlay, .external-wallet-modal, .wallet-modal-overlay");
1268
+ otherModals.forEach((modal) => {
1269
+ if (modal.id !== "onboarding-loading-modal-overlay") {
1270
+ modal.style.display = "none";
1271
+ }
1272
+ });
1273
+ // Create modal overlay if it doesn't exist
1274
+ let loadingModalOverlay = document.getElementById("onboarding-loading-modal-overlay");
1275
+ if (!loadingModalOverlay) {
1276
+ loadingModalOverlay = this.createElement("div", {
1277
+ id: "onboarding-loading-modal-overlay",
1278
+ style: `
1279
+ position: fixed;
1280
+ top: 0;
1281
+ left: 0;
1282
+ right: 0;
1283
+ bottom: 0;
1284
+ background-color: rgba(0, 0, 0, 0.5);
1285
+ display: flex;
1286
+ align-items: center;
1287
+ justify-content: center;
1288
+ z-index: 9999999;
1289
+ backdrop-filter: blur(4px);
1290
+ `,
1291
+ });
1292
+ const loadingCard = this.createElement("div", {
1293
+ className: `onboarding-card onboarding-theme-${this.config.theme}`,
1294
+ style: `
1295
+ background: ${this.config.theme === "dark" ? "#1f2937" : "white"};
1296
+ border-radius: 16px;
1297
+ padding: 32px 32px 28px;
1298
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
1299
+ display: flex;
1300
+ flex-direction: column;
1301
+ align-items: center;
1302
+ justify-content: center;
1303
+ width: 420px;
1304
+ max-width: 420px;
1305
+ min-width: 420px;
1306
+ gap: 20px;
1307
+ box-sizing: border-box;
1308
+ `,
1309
+ });
1310
+ // Header with logo and title
1311
+ const header = this.createElement("div", {
1312
+ className: "onboarding-header",
1313
+ style: "display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%; margin-bottom: 32px;",
1314
+ });
1315
+ const logoSection = this.createElement("div", {
1316
+ className: "onboarding-logo-section",
1317
+ });
1318
+ if (this.config.logo) {
1319
+ const logoContainer = this.createElement("div", {
1320
+ className: "onboarding-logo-container",
1321
+ });
1322
+ const logoImg = this.createElement("img", {
1323
+ src: this.config.logo,
1324
+ alt: "Abstraxn",
1325
+ className: "onboarding-logo-img",
1326
+ });
1327
+ logoContainer.appendChild(logoImg);
1328
+ logoSection.appendChild(logoContainer);
1329
+ }
1330
+ header.appendChild(logoSection);
1331
+ // Add title
1332
+ const title = this.createElement("h1", {
1333
+ className: "onboarding-title",
1334
+ textContent: this.config.onboardTitle || "Sign In",
1335
+ style: `
1336
+ font-size: 24px;
1337
+ font-weight: 600;
1338
+ margin: 0;
1339
+ color: ${this.config.theme === "dark" ? "#ffffff" : "#111827"};
1340
+ text-align: center;
1341
+ margin-bottom: 0;
1342
+ letter-spacing: -0.01em;
1343
+ line-height: 1.4;
1344
+ `,
1345
+ });
1346
+ header.appendChild(title);
1347
+ loadingCard.appendChild(header);
1348
+ // Spinner
1349
+ const spinnerColor = this.config.theme === "dark" ? "#9ca3af" : "#111827";
1350
+ const spinnerBorderColor = this.config.theme === "dark"
1351
+ ? "rgba(156, 163, 175, 0.2)"
1352
+ : "rgba(17, 24, 39, 0.1)";
1353
+ const spinner = this.createElement("div", {
1354
+ className: "onboarding-loading-spinner",
1355
+ style: `
1356
+ width: 40px;
1357
+ height: 40px;
1358
+ border: 3px solid ${spinnerBorderColor};
1359
+ border-radius: 50%;
1360
+ border-top-color: ${spinnerColor};
1361
+ animation: onboarding-spin 1s ease-in-out infinite;
1362
+ margin-bottom: 16px;
1363
+ `,
1364
+ });
1365
+ // Add keyframes if not exists
1366
+ if (!document.getElementById("onboarding-spin-style")) {
1367
+ const style = document.createElement("style");
1368
+ style.id = "onboarding-spin-style";
1369
+ style.textContent = `
1370
+ @keyframes onboarding-spin {
1371
+ to { transform: rotate(360deg); }
1372
+ }
1373
+ `;
1374
+ document.head.appendChild(style);
1375
+ }
1376
+ // Text
1377
+ const text = this.createElement("p", {
1378
+ textContent: "Verifying login...",
1379
+ style: `
1380
+ font-size: 16px;
1381
+ color: ${this.config.theme === "dark" ? "#e5e7eb" : "#374151"};
1382
+ margin: 0;
1383
+ font-weight: 500;
1384
+ `,
1385
+ });
1386
+ loadingCard.appendChild(spinner);
1387
+ loadingCard.appendChild(text);
1388
+ // Footer "Powered by Abstraxn"
1389
+ if (this.config.showFooter !== false) {
1390
+ const textColor = this.config.colors?.text ||
1391
+ (this.config.theme === "dark" ? "#9ca3af" : "#6b7280");
1392
+ const brandColor = this.config.colors?.text ||
1393
+ (this.config.theme === "dark" ? "#e5e7eb" : "#374151");
1394
+ const footer = this.createElement("div", {
1395
+ className: "onboarding-footer",
1396
+ });
1397
+ const footerText = this.createElement("p", {
1398
+ className: "onboarding-footer-text",
1399
+ style: `color: ${textColor}; display: flex; align-items: center; gap: 6px; justify-content: center; font-size: 11px; margin: 0;`,
1400
+ });
1401
+ const poweredBySpan = this.createElement("span", {
1402
+ textContent: "Powered by",
1403
+ style: `color: ${textColor};`,
1404
+ });
1405
+ footerText.appendChild(poweredBySpan);
1406
+ const brandContainer = this.createElement("a", {
1407
+ href: "https://www.abstraxn.com/",
1408
+ target: "_blank",
1409
+ rel: "noopener noreferrer",
1410
+ style: "display: inline-flex; align-items: center; gap: 6px; text-decoration: none; cursor: pointer;",
1411
+ "aria-label": "Visit Abstraxn website",
1412
+ });
1413
+ const logoSvg = this.createElement("span", {
1414
+ innerHTML: `
1415
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none" style="width: 20px; height: 20px; display: inline-block; vertical-align: middle;">
1416
+ <path d="M47.9082 37.191L23.9541 23.553L0 9.91504L37.7436 -8.13502e-06L47.9082 37.191Z" fill="url(#paint0_linear_1242_781)"/>
1417
+ <path d="M13.5156 46.3594L10.1846 47.2344L8.99219 42.873L12.4365 42.4111L13.5156 46.3594ZM20.4268 44.5566L17.1152 45.4199L16.125 41.8818L19.5566 41.3721L20.4268 44.5566ZM26.9307 42.834L23.6152 43.6982L22.8438 40.9443L26.2744 40.4326L26.9307 42.834ZM33.5078 41.1074L30.1875 41.9727L29.6328 39.9951L33.0635 39.4844L33.5078 41.1074ZM39.998 39.4014L36.6729 40.2686L36.3359 39.0674L39.7656 38.5508L39.998 39.4014ZM10.1406 33.9434L11.6807 39.5762L8.15918 39.7568L6.46094 33.543L10.1406 33.9434ZM17.6582 34.5996L18.8916 39.1113L15.4199 39.3496L14.0078 34.3076L17.6582 34.5996ZM24.8779 35.332L25.8047 38.7227L22.3252 38.959L21.2266 35.0361L24.8779 35.332ZM32.0918 36.0723L32.7119 38.3398L29.2227 38.5723L28.4375 35.7656L32.0918 36.0723ZM43.2373 38.5488L43.1172 38.1172L47.7939 37.3516L43.2373 38.5488ZM39.3047 36.8096L39.6182 37.9561L36.1221 38.1846L35.6484 36.4961L39.3047 36.8096ZM47.7939 37.3516L43.0107 37.7939L42.8516 37.2236L47.7939 37.3516ZM47.8291 37.2988L42.7227 36.7529L42.5547 36.1523L47.8291 37.2988ZM47.8652 37.2412L42.3711 35.3486L42.2031 34.7471L47.8652 37.2412ZM38.748 34.8604L39.0664 36.0254L35.3369 35.4043L34.8594 33.6982L38.748 34.8604ZM31.0576 32.3994L31.6787 34.6709L27.959 34.0566L27.1719 31.2461L31.0576 32.3994ZM38.0811 32.6318L38.4092 33.8301L34.4893 32.3496L34.1543 31.1523C34.1005 30.9591 34.0322 30.7719 33.9531 30.5918L38.0811 32.6318ZM23.3682 29.9385L24.291 33.3164L20.5801 32.71L19.4844 28.7949L23.3682 29.9385ZM15.6641 27.4805L16.8896 31.9629L13.1875 31.3594L11.7812 26.3379L15.6641 27.4805ZM29.9463 28.4678L30.5693 30.748L26.6201 29.2812L25.8633 26.583C25.8534 26.5431 25.8417 26.5028 25.8281 26.4639L29.9463 28.4678ZM7.6875 25.0938L9.21484 30.6816L5.44727 29.959L3.75781 23.7793L7.6875 25.0938ZM21.5312 23.3623L22.4492 26.7227L18.5059 25.2627L17.4141 21.3643L21.5312 23.3623ZM13.6104 20.1602L14.8242 24.6006L10.8887 23.1387L9.49219 18.1553L13.6104 20.1602ZM5.2168 16.0654L6.72559 21.585L2.70703 19.9473L1.06055 13.8701L5.2168 16.0654Z" fill="url(#paint1_linear_1242_781)"/>
1418
+ <defs>
1419
+ <linearGradient id="paint0_linear_1242_781" x1="7.03362" y1="6.09543" x2="47.2003" y2="57.0656" gradientUnits="userSpaceOnUse">
1420
+ <stop stop-color="#F878D2"/>
1421
+ <stop offset="0.393522" stop-color="#FDCC81"/>
1422
+ <stop offset="1" stop-color="#F9A0D9"/>
1423
+ </linearGradient>
1424
+ <linearGradient id="paint1_linear_1242_781" x1="2.53739" y1="21.5136" x2="48.7969" y2="38.5742" gradientUnits="userSpaceOnUse">
1425
+ <stop stop-color="#F878D2"/>
1426
+ <stop offset="0.393522" stop-color="#FDCC81"/>
1427
+ <stop offset="1" stop-color="#F9A0D9"/>
1428
+ </linearGradient>
1429
+ </defs>
1430
+ </svg>
1431
+ `,
1432
+ style: "display: inline-flex; align-items: center;",
1433
+ });
1434
+ brandContainer.appendChild(logoSvg);
1435
+ const brandText = this.createElement("span", {
1436
+ textContent: "abstraxn",
1437
+ style: `color: ${brandColor}; font-weight: 700; letter-spacing: 0.2px;`,
1438
+ });
1439
+ brandContainer.appendChild(brandText);
1440
+ footerText.appendChild(brandContainer);
1441
+ footer.appendChild(footerText);
1442
+ loadingCard.appendChild(footer);
1443
+ }
1444
+ loadingModalOverlay.appendChild(loadingCard);
1445
+ document.body.appendChild(loadingModalOverlay);
1446
+ }
1447
+ else {
1448
+ // Ensure modal is visible
1449
+ loadingModalOverlay.style.display = "flex";
1450
+ loadingModalOverlay.style.visibility = "visible";
1451
+ loadingModalOverlay.style.opacity = "1";
1452
+ loadingModalOverlay.style.zIndex = "9999999";
1453
+ }
1454
+ // Prevent body scroll
1455
+ document.body.style.overflow = "hidden";
1456
+ // Force visibility (defensive check)
1457
+ if (loadingModalOverlay) {
1458
+ loadingModalOverlay.style.display = "flex";
1459
+ loadingModalOverlay.style.visibility = "visible";
1460
+ loadingModalOverlay.style.opacity = "1";
1461
+ loadingModalOverlay.style.zIndex = "9999999";
1462
+ }
1463
+ }
1464
+ /**
1465
+ * Hide loading modal overlay
1466
+ */
1467
+ hideLoadingModal() {
1468
+ const loadingModalOverlay = document.getElementById("onboarding-loading-modal-overlay");
1469
+ if (loadingModalOverlay) {
1470
+ loadingModalOverlay.style.display = "none";
1471
+ loadingModalOverlay.style.visibility = "hidden";
1472
+ loadingModalOverlay.style.opacity = "0";
1473
+ document.body.style.overflow = "";
1474
+ }
1475
+ }
1476
+ /**
1477
+ * Show error screen as a modal overlay (for inline components)
1478
+ */
1479
+ showErrorModal(message) {
1480
+ // Helper function to close error modal
1481
+ const closeErrorModal = () => {
1482
+ const overlay = document.getElementById("onboarding-error-modal-overlay");
1483
+ if (overlay) {
1484
+ overlay.style.display = "none";
1485
+ document.body.style.overflow = "";
1486
+ // Clear URL params
1487
+ const url = new URL(window.location.href);
1488
+ url.searchParams.delete("error");
1489
+ window.history.replaceState({}, document.title, url.toString());
1490
+ }
1491
+ };
1492
+ // Create modal overlay if it doesn't exist
1493
+ let errorModalOverlay = document.getElementById("onboarding-error-modal-overlay");
1494
+ if (!errorModalOverlay) {
1495
+ errorModalOverlay = this.createElement("div", {
1496
+ id: "onboarding-error-modal-overlay",
1497
+ style: `
1498
+ position: fixed;
1499
+ top: 0;
1500
+ left: 0;
1501
+ right: 0;
1502
+ bottom: 0;
1503
+ background-color: rgba(0, 0, 0, 0.5);
1504
+ display: flex;
1505
+ align-items: center;
1506
+ justify-content: center;
1507
+ z-index: 999999;
1508
+ backdrop-filter: blur(4px);
1509
+ `,
1510
+ });
1511
+ // Add backdrop click handler to close modal
1512
+ errorModalOverlay.addEventListener("click", (e) => {
1513
+ if (e.target === errorModalOverlay) {
1514
+ closeErrorModal();
1515
+ }
1516
+ });
1517
+ const errorCard = this.createElement("div", {
1518
+ className: `onboarding-card onboarding-theme-${this.config.theme}`,
1519
+ style: `
1520
+ background: ${this.config.theme === "dark" ? "#1f2937" : "white"};
1521
+ border-radius: 16px;
1522
+ padding: 40px;
1523
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
1524
+ display: flex;
1525
+ flex-direction: column;
1526
+ align-items: center;
1527
+ justify-content: center;
1528
+ width: 420px;
1529
+ max-width: 420px;
1530
+ min-width: 420px;
1531
+ text-align: center;
1532
+ position: relative;
1533
+ box-sizing: border-box;
1534
+ `,
1535
+ });
1536
+ // Prevent clicks on card from closing modal
1537
+ errorCard.addEventListener("click", (e) => {
1538
+ e.stopPropagation();
1539
+ });
1540
+ // Header with logo
1541
+ const header = this.createElement("div", {
1542
+ className: "onboarding-header",
1543
+ style: "display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%; margin-bottom: 24px;",
1544
+ });
1545
+ const logoSection = this.createElement("div", {
1546
+ className: "onboarding-logo-section",
1547
+ });
1548
+ if (this.config.logo) {
1549
+ const logoContainer = this.createElement("div", {
1550
+ className: "onboarding-logo-container",
1551
+ });
1552
+ const logoImg = this.createElement("img", {
1553
+ src: this.config.logo,
1554
+ alt: "Abstraxn",
1555
+ className: "onboarding-logo-img",
1556
+ });
1557
+ logoContainer.appendChild(logoImg);
1558
+ logoSection.appendChild(logoContainer);
1559
+ }
1560
+ header.appendChild(logoSection);
1561
+ errorCard.appendChild(header);
1562
+ // Error Icon
1563
+ const iconContainer = this.createElement("div", {
1564
+ style: "width: 64px; height: 64px; background-color: #fee2e2; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 20px;",
1565
+ });
1566
+ if (this.config.theme === "dark") {
1567
+ iconContainer.style.backgroundColor = "rgba(220, 38, 38, 0.2)";
1568
+ }
1569
+ const iconSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1570
+ iconSvg.setAttribute("width", "32");
1571
+ iconSvg.setAttribute("height", "32");
1572
+ iconSvg.setAttribute("viewBox", "0 0 24 24");
1573
+ iconSvg.setAttribute("fill", "none");
1574
+ iconSvg.setAttribute("stroke", "#dc2626");
1575
+ iconSvg.setAttribute("stroke-width", "2");
1576
+ iconSvg.setAttribute("stroke-linecap", "round");
1577
+ iconSvg.setAttribute("stroke-linejoin", "round");
1578
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1579
+ circle.setAttribute("cx", "12");
1580
+ circle.setAttribute("cy", "12");
1581
+ circle.setAttribute("r", "10");
1582
+ iconSvg.appendChild(circle);
1583
+ const line1 = document.createElementNS("http://www.w3.org/2000/svg", "line");
1584
+ line1.setAttribute("x1", "12");
1585
+ line1.setAttribute("y1", "8");
1586
+ line1.setAttribute("x2", "12");
1587
+ line1.setAttribute("y2", "12");
1588
+ iconSvg.appendChild(line1);
1589
+ const line2 = document.createElementNS("http://www.w3.org/2000/svg", "line");
1590
+ line2.setAttribute("x1", "12");
1591
+ line2.setAttribute("y1", "16");
1592
+ line2.setAttribute("x2", "12.01");
1593
+ line2.setAttribute("y2", "16");
1594
+ iconSvg.appendChild(line2);
1595
+ iconContainer.appendChild(iconSvg);
1596
+ errorCard.appendChild(iconContainer);
1597
+ // Title
1598
+ const title = this.createElement("h3", {
1599
+ textContent: "Authentication Failed",
1600
+ style: `font-size: 20px; font-weight: 600; margin: 0 0 12px 0; color: ${this.config.theme === "dark" ? "#ffffff" : "#111827"};`,
1601
+ });
1602
+ errorCard.appendChild(title);
1603
+ // Message
1604
+ const msg = this.createElement("p", {
1605
+ textContent: message,
1606
+ style: `font-size: 14px; color: ${this.config.theme === "dark" ? "#e5e7eb" : "#374151"}; margin: 0 0 24px 0; line-height: 1.5; max-width: 100%; word-break: break-word;`,
1607
+ });
1608
+ errorCard.appendChild(msg);
1609
+ // OK Button
1610
+ const okButton = this.createElement("button", {
1611
+ className: "onboarding-button onboarding-button-primary",
1612
+ textContent: "OK",
1613
+ style: "width: 100%; max-width: 200px;",
1614
+ });
1615
+ okButton.addEventListener("click", closeErrorModal);
1616
+ errorCard.appendChild(okButton);
1617
+ // Footer "Powered by Abstraxn"
1618
+ if (this.config.showFooter !== false) {
1619
+ const textColor = this.config.colors?.text ||
1620
+ (this.config.theme === "dark" ? "#9ca3af" : "#6b7280");
1621
+ const brandColor = this.config.colors?.text ||
1622
+ (this.config.theme === "dark" ? "#e5e7eb" : "#374151");
1623
+ const footer = this.createElement("div", {
1624
+ className: "onboarding-footer",
1625
+ });
1626
+ const footerText = this.createElement("p", {
1627
+ className: "onboarding-footer-text",
1628
+ style: `color: ${textColor}; display: flex; align-items: center; gap: 6px; justify-content: center; font-size: 11px; margin: 0; margin-top: 24px;`,
1629
+ });
1630
+ const poweredBySpan = this.createElement("span", {
1631
+ textContent: "Powered by",
1632
+ style: `color: ${textColor};`,
1633
+ });
1634
+ footerText.appendChild(poweredBySpan);
1635
+ const brandContainer = this.createElement("a", {
1636
+ href: "https://www.abstraxn.com/",
1637
+ target: "_blank",
1638
+ rel: "noopener noreferrer",
1639
+ style: "display: inline-flex; align-items: center; gap: 6px; text-decoration: none; cursor: pointer;",
1640
+ "aria-label": "Visit Abstraxn website",
1641
+ });
1642
+ const logoSvg = this.createElement("span", {
1643
+ innerHTML: `
1644
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none" style="width: 20px; height: 20px; display: inline-block; vertical-align: middle;">
1645
+ <path d="M47.9082 37.191L23.9541 23.553L0 9.91504L37.7436 -8.13502e-06L47.9082 37.191Z" fill="url(#paint0_linear_error_modal)"/>
1646
+ <path d="M13.5156 46.3594L10.1846 47.2344L8.99219 42.873L12.4365 42.4111L13.5156 46.3594ZM20.4268 44.5566L17.1152 45.4199L16.125 41.8818L19.5566 41.3721L20.4268 44.5566ZM26.9307 42.834L23.6152 43.6982L22.8438 40.9443L26.2744 40.4326L26.9307 42.834ZM33.5078 41.1074L30.1875 41.9727L29.6328 39.9951L33.0635 39.4844L33.5078 41.1074ZM39.998 39.4014L36.6729 40.2686L36.3359 39.0674L39.7656 38.5508L39.998 39.4014ZM10.1406 33.9434L11.6807 39.5762L8.15918 39.7568L6.46094 33.543L10.1406 33.9434ZM17.6582 34.5996L18.8916 39.1113L15.4199 39.3496L14.0078 34.3076L17.6582 34.5996ZM24.8779 35.332L25.8047 38.7227L22.3252 38.959L21.2266 35.0361L24.8779 35.332ZM32.0918 36.0723L32.7119 38.3398L29.2227 38.5723L28.4375 35.7656L32.0918 36.0723ZM43.2373 38.5488L43.1172 38.1172L47.7939 37.3516L43.2373 38.5488ZM39.3047 36.8096L39.6182 37.9561L36.1221 38.1846L35.6484 36.4961L39.3047 36.8096ZM47.7939 37.3516L43.0107 37.7939L42.8516 37.2236L47.7939 37.3516ZM47.8291 37.2988L42.7227 36.7529L42.5547 36.1523L47.8291 37.2988ZM47.8652 37.2412L42.3711 35.3486L42.2031 34.7471L47.8652 37.2412ZM38.748 34.8604L39.0664 36.0254L35.3369 35.4043L34.8594 33.6982L38.748 34.8604ZM31.0576 32.3994L31.6787 34.6709L27.959 34.0566L27.1719 31.2461L31.0576 32.3994ZM38.0811 32.6318L38.4092 33.8301L34.4893 32.3496L34.1543 31.1523C34.1005 30.9591 34.0322 30.7719 33.9531 30.5918L38.0811 32.6318ZM23.3682 29.9385L24.291 33.3164L20.5801 32.71L19.4844 28.7949L23.3682 29.9385ZM15.6641 27.4805L16.8896 31.9629L13.1875 31.3594L11.7812 26.3379L15.6641 27.4805ZM29.9463 28.4678L30.5693 30.748L26.6201 29.2812L25.8633 26.583C25.8534 26.5431 25.8417 26.5028 25.8281 26.4639L29.9463 28.4678ZM7.6875 25.0938L9.21484 30.6816L5.44727 29.959L3.75781 23.7793L7.6875 25.0938ZM21.5312 23.3623L22.4492 26.7227L18.5059 25.2627L17.4141 21.3643L21.5312 23.3623ZM13.6104 20.1602L14.8242 24.6006L10.8887 23.1387L9.49219 18.1553L13.6104 20.1602ZM5.2168 16.0654L6.72559 21.585L2.70703 19.9473L1.06055 13.8701L5.2168 16.0654Z" fill="url(#paint1_linear_error_modal)"/>
1647
+ <defs>
1648
+ <linearGradient id="paint0_linear_error_modal" x1="7.03362" y1="6.09543" x2="47.2003" y2="57.0656" gradientUnits="userSpaceOnUse">
1649
+ <stop stop-color="#F878D2"/>
1650
+ <stop offset="0.393522" stop-color="#FDCC81"/>
1651
+ <stop offset="1" stop-color="#F9A0D9"/>
1652
+ </linearGradient>
1653
+ <linearGradient id="paint1_linear_error_modal" x1="2.53739" y1="21.5136" x2="48.7969" y2="38.5742" gradientUnits="userSpaceOnUse">
1654
+ <stop stop-color="#F878D2"/>
1655
+ <stop offset="0.393522" stop-color="#FDCC81"/>
1656
+ <stop offset="1" stop-color="#F9A0D9"/>
1657
+ </linearGradient>
1658
+ </defs>
1659
+ </svg>
1660
+ `,
1661
+ style: "display: inline-flex; align-items: center;",
1662
+ });
1663
+ brandContainer.appendChild(logoSvg);
1664
+ const brandText = this.createElement("span", {
1665
+ textContent: "abstraxn",
1666
+ style: `color: ${brandColor}; font-weight: 700; letter-spacing: 0.2px;`,
1667
+ });
1668
+ brandContainer.appendChild(brandText);
1669
+ footerText.appendChild(brandContainer);
1670
+ footer.appendChild(footerText);
1671
+ errorCard.appendChild(footer);
1672
+ }
1673
+ errorModalOverlay.appendChild(errorCard);
1674
+ document.body.appendChild(errorModalOverlay);
1675
+ }
1676
+ else {
1677
+ // Update message if overlay exists
1678
+ const msgEl = errorModalOverlay.querySelector("p");
1679
+ if (msgEl)
1680
+ msgEl.textContent = message;
1681
+ errorModalOverlay.style.display = "flex";
1682
+ }
1683
+ // Prevent body scroll
1684
+ document.body.style.overflow = "hidden";
1685
+ }
1686
+ /**
1687
+ * Inject CSS styles into the document head (only once)
1688
+ */
1689
+ injectStyles() {
1690
+ if (OnboardingUIWeb.stylesInjected) {
1691
+ return;
1692
+ }
1693
+ // Check if styles are already injected
1694
+ const existingStyle = document.getElementById("onboarding-ui-styles");
1695
+ if (existingStyle) {
1696
+ OnboardingUIWeb.stylesInjected = true;
1697
+ return;
1698
+ }
1699
+ // Create and inject style element
1700
+ const styleElement = document.createElement("style");
1701
+ styleElement.id = "onboarding-ui-styles";
1702
+ styleElement.textContent = ONBOARDING_UI_STYLES;
1703
+ document.head.appendChild(styleElement);
1704
+ // Inject custom CSS if provided
1705
+ if (this.config.customCSS) {
1706
+ const customStyleElement = document.createElement("style");
1707
+ customStyleElement.id = "onboarding-ui-custom-styles";
1708
+ customStyleElement.textContent = this.config.customCSS;
1709
+ document.head.appendChild(customStyleElement);
1710
+ }
1711
+ // Apply custom colors if provided
1712
+ if (this.config.colors) {
1713
+ this.applyCustomColors();
1714
+ }
1715
+ OnboardingUIWeb.stylesInjected = true;
1716
+ // Inject utility styles for loading/error modes
1717
+ const utilityStyle = document.createElement("style");
1718
+ utilityStyle.id = "onboarding-utility-styles";
1719
+ utilityStyle.textContent = `
1720
+ .onboarding-card.onboarding-mode-loading > *:not(.onboarding-loading-container):not(.onboarding-header):not(.onboarding-footer),
1721
+ .onboarding-card.onboarding-mode-error > *:not(.onboarding-error-container):not(.onboarding-header):not(.onboarding-footer) {
1722
+ display: none !important;
1723
+ }
1724
+ `;
1725
+ document.head.appendChild(utilityStyle);
1726
+ }
1727
+ /**
1728
+ * Apply custom colors from config
1729
+ */
1730
+ applyCustomColors() {
1731
+ if (!this.config.colors)
1732
+ return;
1733
+ const root = this.rootElement || document.documentElement;
1734
+ const style = document.createElement("style");
1735
+ style.id = "onboarding-ui-colors";
1736
+ let css = "";
1737
+ if (this.config.colors.primary) {
1738
+ css += `.onboarding-button-primary { background-color: ${this.config.colors.primary} !important; background: ${this.config.colors.primary} !important; }`;
1739
+ css += `.onboarding-footer-brand { color: ${this.config.colors.primary} !important; }`;
1740
+ css += `.onboarding-passkey-signup-link { color: ${this.config.colors.primary} !important; }`;
1741
+ }
1742
+ if (this.config.colors.primaryHover) {
1743
+ css += `.onboarding-button-primary:hover:not(:disabled) { background-color: ${this.config.colors.primaryHover} !important; background: ${this.config.colors.primaryHover} !important; }`;
1744
+ }
1745
+ if (this.config.colors.background) {
1746
+ css += `.onboarding-card { background-color: ${this.config.colors.background} !important; }`;
1747
+ }
1748
+ if (this.config.colors.text) {
1749
+ css += `.onboarding-card { color: ${this.config.colors.text} !important; }`;
1750
+ }
1751
+ if (this.config.colors.border) {
1752
+ css += `.onboarding-input { border-color: ${this.config.colors.border} !important; }`;
1753
+ }
1754
+ if (this.config.colors.error) {
1755
+ css += `.onboarding-error { color: ${this.config.colors.error} !important; }`;
1756
+ }
1757
+ style.textContent = css;
1758
+ document.head.appendChild(style);
1759
+ }
1760
+ /**
1761
+ * Setup the container element
1762
+ */
1763
+ setupContainer() {
1764
+ // For modal mode, always use body
1765
+ if (this.config.modal) {
1766
+ this.container = document.body;
1767
+ }
1768
+ else {
1769
+ if (this.config.container) {
1770
+ if (typeof this.config.container === "string") {
1771
+ this.container = document.querySelector(this.config.container);
1772
+ if (!this.container) {
1773
+ throw new Error(`Container element not found: ${this.config.container}`);
1774
+ }
1775
+ }
1776
+ else {
1777
+ this.container = this.config.container;
1778
+ }
1779
+ }
1780
+ else {
1781
+ // Create a default container
1782
+ this.container = document.body;
1783
+ }
1784
+ }
1785
+ }
1786
+ /**
1787
+ * Render the component
1788
+ */
1789
+ render() {
1790
+ if (!this.container)
1791
+ return;
1792
+ if (this.config.modal) {
1793
+ this.renderAsModal();
1794
+ }
1795
+ else {
1796
+ this.renderInline();
1797
+ }
1798
+ }
1799
+ /**
1800
+ * Render as modal overlay
1801
+ */
1802
+ renderAsModal() {
1803
+ // Create modal overlay (backdrop) - start hidden to prevent flicker
1804
+ this.modalOverlay = this.createElement("div", {
1805
+ className: "onboarding-modal-overlay",
1806
+ style: "display: none;", // Start hidden by default
1807
+ });
1808
+ // Create modal content wrapper
1809
+ this.modalContent = this.createElement("div", {
1810
+ className: `onboarding-modal-content onboarding-theme-${this.config.theme}`,
1811
+ role: "dialog",
1812
+ "aria-modal": "true",
1813
+ "aria-labelledby": "onboarding-title",
1814
+ tabIndex: -1,
1815
+ });
1816
+ // Create root element for the form
1817
+ this.rootElement = this.createElement("div", {
1818
+ className: `onboarding-container onboarding-theme-${this.config.theme} ${this.config.className}`,
1819
+ style: this.parseStyle(this.config.style),
1820
+ });
1821
+ // Build the form content
1822
+ this.buildFormContent();
1823
+ // Append form to root, root to modal content, modal content to overlay
1824
+ this.modalContent.appendChild(this.rootElement);
1825
+ this.modalOverlay.appendChild(this.modalContent);
1826
+ // Add click handler for backdrop
1827
+ if (this.config.closeOnBackdropClick) {
1828
+ this.modalOverlay.addEventListener("click", (e) => {
1829
+ if (e.target === this.modalOverlay) {
1830
+ this.close();
1831
+ }
1832
+ });
1833
+ }
1834
+ // Prevent body scroll when modal is open
1835
+ document.body.style.overflow = "hidden";
1836
+ // Append to body
1837
+ document.body.appendChild(this.modalOverlay);
1838
+ // Add animation class
1839
+ setTimeout(() => {
1840
+ if (this.modalOverlay) {
1841
+ this.modalOverlay.classList.add("onboarding-modal-open");
1842
+ // Focus management for accessibility
1843
+ if (this.emailInput) {
1844
+ this.emailInput.focus();
1845
+ }
1846
+ }
1847
+ }, 10);
1848
+ // Add keyboard navigation
1849
+ this.modalOverlay.addEventListener("keydown", (e) => {
1850
+ // Trap focus within modal
1851
+ if (e.key === "Tab") {
1852
+ this.trapFocus(e);
1853
+ }
1854
+ });
1855
+ }
1856
+ /**
1857
+ * Trap focus within modal for accessibility
1858
+ */
1859
+ trapFocus(e) {
1860
+ if (!this.modalContent)
1861
+ return;
1862
+ const focusableElements = this.modalContent.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
1863
+ const firstElement = focusableElements[0];
1864
+ const lastElement = focusableElements[focusableElements.length - 1];
1865
+ if (e.shiftKey && document.activeElement === firstElement) {
1866
+ e.preventDefault();
1867
+ lastElement.focus();
1868
+ }
1869
+ else if (!e.shiftKey && document.activeElement === lastElement) {
1870
+ e.preventDefault();
1871
+ firstElement.focus();
1872
+ }
1873
+ }
1874
+ /**
1875
+ * Render inline (non-modal)
1876
+ */
1877
+ renderInline() {
1878
+ if (!this.container)
1879
+ return;
1880
+ // Create root element
1881
+ this.rootElement = this.createElement("div", {
1882
+ className: `onboarding-container onboarding-inline onboarding-theme-${this.config.theme} ${this.config.className}`,
1883
+ style: this.parseStyle(this.config.style),
1884
+ });
1885
+ // Build the form content
1886
+ this.buildFormContent();
1887
+ // Append to container
1888
+ if (this.container === document.body) {
1889
+ this.container.appendChild(this.rootElement);
1890
+ }
1891
+ else {
1892
+ this.container.innerHTML = "";
1893
+ this.container.appendChild(this.rootElement);
1894
+ }
1895
+ }
1896
+ /**
1897
+ * Build form content (shared between modal and inline)
1898
+ */
1899
+ buildFormContent() {
1900
+ if (!this.rootElement)
1901
+ return;
1902
+ // Create card
1903
+ const card = this.createElement("div", { className: "onboarding-card" });
1904
+ // Header with logo at top
1905
+ const header = this.createElement("div", {
1906
+ className: "onboarding-header",
1907
+ });
1908
+ // Logo section (centered at top)
1909
+ if (this.config.logo) {
1910
+ const logoSection = this.createElement("div", {
1911
+ className: "onboarding-logo-section",
1912
+ });
1913
+ const logoContainer = this.createElement("div", {
1914
+ className: "onboarding-logo-container",
1915
+ });
1916
+ const logoImg = this.createElement("img", {
1917
+ src: this.config.logo,
1918
+ alt: "Logo",
1919
+ className: "onboarding-logo-img",
1920
+ });
1921
+ logoContainer.appendChild(logoImg);
1922
+ logoSection.appendChild(logoContainer);
1923
+ header.appendChild(logoSection);
1924
+ }
1925
+ // Onboarding title (always shown, default: "Sign In")
1926
+ const title = this.createElement("h1", {
1927
+ className: "onboarding-title",
1928
+ textContent: this.config.onboardTitle || "Sign In",
1929
+ id: "onboarding-title",
1930
+ });
1931
+ header.appendChild(title);
1932
+ // Form
1933
+ this.emailForm = this.createElement("form", {
1934
+ className: "onboarding-form",
1935
+ "aria-label": "Sign In form",
1936
+ noValidate: true,
1937
+ });
1938
+ // Email input group
1939
+ const emailGroup = this.createElement("div", {
1940
+ className: "onboarding-input-group",
1941
+ });
1942
+ // Email label
1943
+ const emailLabel = this.createElement("label", {
1944
+ className: "onboarding-input-label",
1945
+ textContent: this.config.labels?.emailLabel || "Email Address",
1946
+ htmlFor: "onboarding-email-input",
1947
+ });
1948
+ emailGroup.appendChild(emailLabel);
1949
+ const emailWrapper = this.createElement("div", {
1950
+ className: "onboarding-input-wrapper",
1951
+ });
1952
+ // Email icon SVG
1953
+ const emailIcon = this.createEmailIcon();
1954
+ emailWrapper.appendChild(emailIcon);
1955
+ // Get auth methods to check if all are enabled
1956
+ const authMethods = this.config.authMethods || ["otp", "google"];
1957
+ const showEmail = authMethods.includes("otp");
1958
+ const showGoogle = authMethods.includes("google");
1959
+ const showTwitter = authMethods.includes("twitter");
1960
+ const showDiscord = authMethods.includes("discord");
1961
+ const showPasskey = authMethods.includes("passkey");
1962
+ const socialMethods = [
1963
+ { key: "google", show: showGoogle },
1964
+ { key: "twitter", show: showTwitter },
1965
+ { key: "discord", show: showDiscord },
1966
+ ].filter((m) => m.show);
1967
+ // Treat “all configs” as email + passkey + all socials present
1968
+ const allAuthMethodsEnabled = showEmail && showPasskey && showGoogle && showTwitter && showDiscord;
1969
+ // Email input
1970
+ this.emailInput = this.createElement("input", {
1971
+ type: "email",
1972
+ id: "onboarding-email-input",
1973
+ placeholder: this.config.labels?.emailPlaceholder || "Enter your email",
1974
+ className: "onboarding-input",
1975
+ required: true,
1976
+ autocomplete: "email",
1977
+ "aria-label": this.config.labels?.emailLabel || "Email Address",
1978
+ "aria-required": "true",
1979
+ style: allAuthMethodsEnabled ? "padding-right: 48px;" : "",
1980
+ });
1981
+ emailWrapper.appendChild(this.emailInput);
1982
+ // Add right arrow button inside input when all auth methods are enabled
1983
+ if (allAuthMethodsEnabled) {
1984
+ this.emailArrowButton = this.createElement("button", {
1985
+ type: "button",
1986
+ className: "onboarding-input-arrow-button",
1987
+ "aria-label": this.config.labels?.emailButton || "Continue with email",
1988
+ disabled: true,
1989
+ });
1990
+ const arrowIcon = this.createArrowIcon();
1991
+ arrowIcon.setAttribute("aria-hidden", "true");
1992
+ this.emailArrowButton.appendChild(arrowIcon);
1993
+ // Add click handler to submit form
1994
+ this.emailArrowButton.addEventListener("click", (e) => {
1995
+ e.preventDefault();
1996
+ if (this.emailInput &&
1997
+ this.emailInput.value.trim() &&
1998
+ isValidEmail(this.emailInput.value.trim())) {
1999
+ this.emailForm?.requestSubmit();
2000
+ }
2001
+ });
2002
+ // Also submit on Enter key
2003
+ this.emailInput.addEventListener("keydown", (e) => {
2004
+ if (e.key === "Enter" &&
2005
+ this.emailInput?.value.trim() &&
2006
+ isValidEmail(this.emailInput.value.trim())) {
2007
+ e.preventDefault();
2008
+ this.emailForm?.requestSubmit();
2009
+ }
2010
+ });
2011
+ emailWrapper.appendChild(this.emailArrowButton);
2012
+ }
2013
+ emailGroup.appendChild(emailWrapper);
2014
+ this.emailForm.appendChild(emailGroup);
2015
+ // OTP input group (initially hidden)
2016
+ this.otpGroup = this.createElement("div", {
2017
+ className: "onboarding-input-group",
2018
+ style: "display: none;",
2019
+ });
2020
+ // OTP label
2021
+ const otpLabel = this.createElement("label", {
2022
+ className: "onboarding-input-label",
2023
+ textContent: this.config.labels?.otpLabel || "Verification code",
2024
+ htmlFor: "onboarding-otp-input",
2025
+ });
2026
+ this.otpGroup.appendChild(otpLabel);
2027
+ const otpWrapper = this.createElement("div", {
2028
+ className: "onboarding-input-wrapper",
2029
+ });
2030
+ this.otpInput = this.createElement("input", {
2031
+ type: "text",
2032
+ id: "onboarding-otp-input",
2033
+ placeholder: this.config.labels?.otpPlaceholder || "Enter 6-digit code",
2034
+ className: "onboarding-input",
2035
+ required: false, // Don't require initially since it's hidden
2036
+ maxLength: 6,
2037
+ inputMode: "numeric",
2038
+ pattern: "[0-9]*",
2039
+ autocomplete: "one-time-code",
2040
+ "aria-label": this.config.labels?.otpLabel || "Verification code",
2041
+ });
2042
+ otpWrapper.appendChild(this.otpInput);
2043
+ this.otpGroup.appendChild(otpWrapper);
2044
+ this.emailForm.appendChild(this.otpGroup);
2045
+ // Error element
2046
+ this.errorElement = this.createElement("div", {
2047
+ className: "onboarding-error",
2048
+ style: "display: none;",
2049
+ role: "alert",
2050
+ "aria-live": "polite",
2051
+ });
2052
+ this.emailForm.appendChild(this.errorElement);
2053
+ // Continue button (hide if all auth methods are enabled)
2054
+ this.continueButton = this.createElement("button", {
2055
+ type: "submit",
2056
+ className: "onboarding-button onboarding-button-primary",
2057
+ textContent: this.config.labels?.emailButton || "Continue",
2058
+ "aria-label": this.config.labels?.emailButton || "Continue with email",
2059
+ style: allAuthMethodsEnabled ? "display: none;" : "",
2060
+ disabled: true,
2061
+ });
2062
+ this.emailForm.appendChild(this.continueButton);
2063
+ card.appendChild(header);
2064
+ // Re-check auth methods for other elements (already checked above)
2065
+ // Only append email form if email auth is enabled
2066
+ if (showEmail) {
2067
+ card.appendChild(this.emailForm);
2068
+ }
2069
+ else {
2070
+ // Still create the form but hide it (needed for OTP flow)
2071
+ this.emailForm.style.display = "none";
2072
+ card.appendChild(this.emailForm);
2073
+ }
2074
+ // Divider (show if email plus any social method is enabled)
2075
+ if (showEmail && socialMethods.length > 0) {
2076
+ this.divider = this.createElement("div", {
2077
+ className: "onboarding-divider",
2078
+ });
2079
+ const dividerText = this.createElement("span", {
2080
+ className: "onboarding-divider-text",
2081
+ textContent: "or",
2082
+ });
2083
+ this.divider.appendChild(dividerText);
2084
+ card.appendChild(this.divider);
2085
+ }
2086
+ else {
2087
+ // Create divider element but hide it
2088
+ this.divider = this.createElement("div", {
2089
+ className: "onboarding-divider",
2090
+ style: "display: none;",
2091
+ });
2092
+ card.appendChild(this.divider);
2093
+ }
2094
+ // Social login buttons (Google, Twitter, Discord)
2095
+ if (socialMethods.length > 0) {
2096
+ if (socialMethods.length === 1) {
2097
+ const method = socialMethods[0].key;
2098
+ const isGoogle = method === "google";
2099
+ const isTwitter = method === "twitter";
2100
+ const isDiscord = method === "discord";
2101
+ const button = this.createElement("button", {
2102
+ type: "button",
2103
+ className: isGoogle
2104
+ ? "onboarding-button onboarding-button-google"
2105
+ : "onboarding-button onboarding-button-social",
2106
+ "aria-label": this.config.labels?.[`${method}Button`] ||
2107
+ (method === "google"
2108
+ ? "Continue with Google"
2109
+ : method === "twitter"
2110
+ ? "Continue with X"
2111
+ : `Continue with ${method.charAt(0).toUpperCase() + method.slice(1)}`),
2112
+ });
2113
+ const icon = isGoogle
2114
+ ? this.createGoogleIcon()
2115
+ : isTwitter
2116
+ ? this.createTwitterIcon()
2117
+ : this.createDiscordIcon();
2118
+ icon.setAttribute("aria-hidden", "true");
2119
+ button.appendChild(icon);
2120
+ const label = this.config.labels?.[`${method}Button`] ||
2121
+ (method === "google"
2122
+ ? "Continue with Google"
2123
+ : method === "twitter"
2124
+ ? "Continue with X"
2125
+ : `Continue with ${method.charAt(0).toUpperCase() + method.slice(1)}`);
2126
+ button.appendChild(document.createTextNode(` ${label}`));
2127
+ if (isGoogle) {
2128
+ this.googleButton = button;
2129
+ }
2130
+ else if (isTwitter) {
2131
+ this.twitterButton = button;
2132
+ }
2133
+ else {
2134
+ this.discordButton = button;
2135
+ }
2136
+ card.appendChild(button);
2137
+ }
2138
+ else {
2139
+ this.socialGrid = this.createElement("div", {
2140
+ className: "onboarding-social-grid",
2141
+ });
2142
+ socialMethods.forEach(({ key: method }) => {
2143
+ const isGoogle = method === "google";
2144
+ const isTwitter = method === "twitter";
2145
+ const isDiscord = method === "discord";
2146
+ const button = this.createElement("button", {
2147
+ type: "button",
2148
+ className: "onboarding-button-social-icon",
2149
+ "aria-label": this.config.labels?.[`${method}Button`] ||
2150
+ (method === "google"
2151
+ ? "Google"
2152
+ : method === "twitter"
2153
+ ? "X"
2154
+ : method.charAt(0).toUpperCase() + method.slice(1)),
2155
+ });
2156
+ const icon = isGoogle
2157
+ ? this.createGoogleIcon()
2158
+ : isTwitter
2159
+ ? this.createTwitterIcon()
2160
+ : this.createDiscordIcon();
2161
+ icon.setAttribute("aria-hidden", "true");
2162
+ button.appendChild(icon);
2163
+ if (isGoogle) {
2164
+ this.googleButton = button;
2165
+ }
2166
+ else if (isTwitter) {
2167
+ this.twitterButton = button;
2168
+ }
2169
+ else {
2170
+ this.discordButton = button;
2171
+ }
2172
+ if (this.socialGrid) {
2173
+ this.socialGrid.appendChild(button);
2174
+ }
2175
+ });
2176
+ if (this.socialGrid) {
2177
+ card.appendChild(this.socialGrid);
2178
+ }
2179
+ }
2180
+ }
2181
+ // Passkey divider (only show if passkey is enabled and there are other auth methods)
2182
+ if (showPasskey && (showEmail || socialMethods.length > 0)) {
2183
+ this.passkeyDivider = this.createElement("div", {
2184
+ className: "onboarding-divider",
2185
+ });
2186
+ const passkeyDividerText = this.createElement("span", {
2187
+ className: "onboarding-divider-text",
2188
+ textContent: "or",
2189
+ });
2190
+ this.passkeyDivider.appendChild(passkeyDividerText);
2191
+ card.appendChild(this.passkeyDivider);
2192
+ }
2193
+ else {
2194
+ this.passkeyDivider = null;
2195
+ }
2196
+ // Passkey login button (only show if passkey auth is enabled)
2197
+ if (showPasskey) {
2198
+ const passkeyBg = this.config.colors?.primary ||
2199
+ (this.config.theme === "dark" ? "#4b5563" : "#ffffff");
2200
+ const passkeyBorder = this.config.colors?.border ||
2201
+ (this.config.theme === "dark" ? "#6b7280" : "#e5e7eb");
2202
+ const passkeyTextColor = this.config.colors?.text ||
2203
+ (this.config.theme === "dark" ? "#ffffff" : "#1f2937");
2204
+ this.passkeyLoginButton = this.createElement("button", {
2205
+ type: "button",
2206
+ className: "onboarding-button onboarding-button-passkey",
2207
+ "aria-label": this.config.labels?.passkeyLoginButton || "Log in with passkey",
2208
+ style: `
2209
+ background-color: ${passkeyBg};
2210
+ color: ${passkeyTextColor};
2211
+ border: 1.5px solid ${passkeyBorder};
2212
+ `,
2213
+ });
2214
+ const passkeyIcon = this.createPasskeyIcon();
2215
+ passkeyIcon.setAttribute("aria-hidden", "true");
2216
+ this.passkeyLoginButton.appendChild(passkeyIcon);
2217
+ const passkeyText = this.config.labels?.passkeyLoginButton || "Log in with passkey";
2218
+ this.passkeyLoginButton.appendChild(document.createTextNode(` ${passkeyText}`));
2219
+ card.appendChild(this.passkeyLoginButton);
2220
+ // Passkey signup link
2221
+ const passkeySignupContainer = this.createElement("div", {
2222
+ style: "text-align: center; margin-top: 12px;",
2223
+ });
2224
+ this.passkeySignupLink = this.createElement("a", {
2225
+ className: "onboarding-passkey-signup-link",
2226
+ href: "#",
2227
+ textContent: this.config.labels?.passkeySignupButton || "Sign up with passkey",
2228
+ "aria-label": this.config.labels?.passkeySignupButton || "Sign up with passkey",
2229
+ });
2230
+ passkeySignupContainer.appendChild(this.passkeySignupLink);
2231
+ card.appendChild(passkeySignupContainer);
2232
+ // Passkey error element (local to passkey section)
2233
+ this.passkeyErrorElement = this.createElement("div", {
2234
+ className: "onboarding-error",
2235
+ style: "display: none; margin-top: 12px;",
2236
+ role: "alert",
2237
+ "aria-live": "polite",
2238
+ });
2239
+ card.appendChild(this.passkeyErrorElement);
2240
+ }
2241
+ else {
2242
+ // Create passkey elements but hide them
2243
+ this.passkeyLoginButton = this.createElement("button", {
2244
+ type: "button",
2245
+ className: "onboarding-button onboarding-button-passkey",
2246
+ style: "display: none;",
2247
+ });
2248
+ card.appendChild(this.passkeyLoginButton);
2249
+ this.passkeySignupLink = this.createElement("a", {
2250
+ className: "onboarding-passkey-signup-link",
2251
+ href: "#",
2252
+ style: "display: none;",
2253
+ });
2254
+ card.appendChild(this.passkeySignupLink);
2255
+ this.passkeyErrorElement = this.createElement("div", {
2256
+ className: "onboarding-error",
2257
+ style: "display: none;",
2258
+ });
2259
+ card.appendChild(this.passkeyErrorElement);
2260
+ }
2261
+ // External wallet divider (shown immediately if external wallets are enabled)
2262
+ this.externalWalletDivider = this.createElement("div", {
2263
+ className: "onboarding-divider",
2264
+ style: this.externalWalletsEnabled ? "" : "display: none;",
2265
+ });
2266
+ const externalWalletDividerText = this.createElement("span", {
2267
+ className: "onboarding-divider-text",
2268
+ textContent: "or",
2269
+ });
2270
+ this.externalWalletDivider.appendChild(externalWalletDividerText);
2271
+ card.appendChild(this.externalWalletDivider);
2272
+ // External wallet container (shown immediately if external wallets are enabled)
2273
+ this.externalWalletContainer = this.createElement("div", {
2274
+ id: "onboarding-external-wallet-container",
2275
+ style: this.externalWalletsEnabled ? "" : "display: none;",
2276
+ });
2277
+ card.appendChild(this.externalWalletContainer);
2278
+ // Footer (inside card, so it appears on initial login screen)
2279
+ if (this.config.showFooter) {
2280
+ const textColor = this.config.colors?.text ||
2281
+ (this.config.theme === "dark" ? "#9ca3af" : "#6b7280");
2282
+ const brandColor = this.config.colors?.text ||
2283
+ (this.config.theme === "dark" ? "#e5e7eb" : "#374151");
2284
+ this.footer = this.createElement("div", {
2285
+ className: "onboarding-footer",
2286
+ });
2287
+ const footerText = this.createElement("p", {
2288
+ className: "onboarding-footer-text",
2289
+ style: `color: ${textColor}; display: flex; align-items: center; gap: 6px; justify-content: center;`,
2290
+ });
2291
+ // "Powered by" text
2292
+ const poweredBySpan = this.createElement("span", {
2293
+ textContent: "Powered by",
2294
+ style: `color: ${textColor};`,
2295
+ });
2296
+ footerText.appendChild(poweredBySpan);
2297
+ // Inline brand with logo (clickable link)
2298
+ const brandContainer = this.createElement("a", {
2299
+ href: "https://www.abstraxn.com/",
2300
+ target: "_blank",
2301
+ rel: "noopener noreferrer",
2302
+ style: "display: inline-flex; align-items: center; gap: 6px; text-decoration: none; cursor: pointer;",
2303
+ "aria-label": "Visit Abstraxn website",
2304
+ });
2305
+ // Abstraxn logo SVG
2306
+ const logoSvg = this.createElement("span", {
2307
+ innerHTML: `
2308
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none" style="width: 20px; height: 20px; display: inline-block; vertical-align: middle;">
2309
+ <path d="M47.9082 37.191L23.9541 23.553L0 9.91504L37.7436 -8.13502e-06L47.9082 37.191Z" fill="url(#paint0_linear_1242_781)"/>
2310
+ <path d="M13.5156 46.3594L10.1846 47.2344L8.99219 42.873L12.4365 42.4111L13.5156 46.3594ZM20.4268 44.5566L17.1152 45.4199L16.125 41.8818L19.5566 41.3721L20.4268 44.5566ZM26.9307 42.834L23.6152 43.6982L22.8438 40.9443L26.2744 40.4326L26.9307 42.834ZM33.5078 41.1074L30.1875 41.9727L29.6328 39.9951L33.0635 39.4844L33.5078 41.1074ZM39.998 39.4014L36.6729 40.2686L36.3359 39.0674L39.7656 38.5508L39.998 39.4014ZM10.1406 33.9434L11.6807 39.5762L8.15918 39.7568L6.46094 33.543L10.1406 33.9434ZM17.6582 34.5996L18.8916 39.1113L15.4199 39.3496L14.0078 34.3076L17.6582 34.5996ZM24.8779 35.332L25.8047 38.7227L22.3252 38.959L21.2266 35.0361L24.8779 35.332ZM32.0918 36.0723L32.7119 38.3398L29.2227 38.5723L28.4375 35.7656L32.0918 36.0723ZM43.2373 38.5488L43.1172 38.1172L47.7939 37.3516L43.2373 38.5488ZM39.3047 36.8096L39.6182 37.9561L36.1221 38.1846L35.6484 36.4961L39.3047 36.8096ZM47.7939 37.3516L43.0107 37.7939L42.8516 37.2236L47.7939 37.3516ZM47.8291 37.2988L42.7227 36.7529L42.5547 36.1523L47.8291 37.2988ZM47.8652 37.2412L42.3711 35.3486L42.2031 34.7471L47.8652 37.2412ZM38.748 34.8604L39.0664 36.0254L35.3369 35.4043L34.8594 33.6982L38.748 34.8604ZM31.0576 32.3994L31.6787 34.6709L27.959 34.0566L27.1719 31.2461L31.0576 32.3994ZM38.0811 32.6318L38.4092 33.8301L34.4893 32.3496L34.1543 31.1523C34.1005 30.9591 34.0322 30.7719 33.9531 30.5918L38.0811 32.6318ZM23.3682 29.9385L24.291 33.3164L20.5801 32.71L19.4844 28.7949L23.3682 29.9385ZM15.6641 27.4805L16.8896 31.9629L13.1875 31.3594L11.7812 26.3379L15.6641 27.4805ZM29.9463 28.4678L30.5693 30.748L26.6201 29.2812L25.8633 26.583C25.8534 26.5431 25.8417 26.5028 25.8281 26.4639L29.9463 28.4678ZM7.6875 25.0938L9.21484 30.6816L5.44727 29.959L3.75781 23.7793L7.6875 25.0938ZM21.5312 23.3623L22.4492 26.7227L18.5059 25.2627L17.4141 21.3643L21.5312 23.3623ZM13.6104 20.1602L14.8242 24.6006L10.8887 23.1387L9.49219 18.1553L13.6104 20.1602ZM5.2168 16.0654L6.72559 21.585L2.70703 19.9473L1.06055 13.8701L5.2168 16.0654Z" fill="url(#paint1_linear_1242_781)"/>
2311
+ <defs>
2312
+ <linearGradient id="paint0_linear_1242_781" x1="7.03362" y1="6.09543" x2="47.2003" y2="57.0656" gradientUnits="userSpaceOnUse">
2313
+ <stop stop-color="#F878D2"/>
2314
+ <stop offset="0.393522" stop-color="#FDCC81"/>
2315
+ <stop offset="1" stop-color="#F9A0D9"/>
2316
+ </linearGradient>
2317
+ <linearGradient id="paint1_linear_1242_781" x1="2.53739" y1="21.5136" x2="48.7969" y2="38.5742" gradientUnits="userSpaceOnUse">
2318
+ <stop stop-color="#F878D2"/>
2319
+ <stop offset="0.393522" stop-color="#FDCC81"/>
2320
+ <stop offset="1" stop-color="#F9A0D9"/>
2321
+ </linearGradient>
2322
+ </defs>
2323
+ </svg>
2324
+ `,
2325
+ style: "display: inline-flex; align-items: center;",
2326
+ });
2327
+ brandContainer.appendChild(logoSvg);
2328
+ const brandText = this.createElement("span", {
2329
+ textContent: "abstraxn",
2330
+ style: `color: ${brandColor}; font-weight: 700; letter-spacing: 0.2px;`,
2331
+ });
2332
+ brandContainer.appendChild(brandText);
2333
+ footerText.appendChild(brandContainer);
2334
+ this.footer.appendChild(footerText);
2335
+ card.appendChild(this.footer);
2336
+ }
2337
+ this.rootElement.appendChild(card);
2338
+ }
2339
+ /**
2340
+ * Attach event listeners
2341
+ */
2342
+ attachEventListeners() {
2343
+ if (!this.emailForm ||
2344
+ !this.emailInput ||
2345
+ !this.otpInput ||
2346
+ !this.continueButton) {
2347
+ return;
2348
+ }
2349
+ // Email input change
2350
+ this.emailInput.addEventListener("input", (e) => {
2351
+ this.email = e.target.value;
2352
+ this.updateButtonState();
2353
+ });
2354
+ // OTP input change
2355
+ this.otpInput.addEventListener("input", (e) => {
2356
+ this.otp = e.target.value;
2357
+ });
2358
+ // Form submit
2359
+ this.emailForm.addEventListener("submit", (e) => {
2360
+ e.preventDefault();
2361
+ this.handleEmailSubmit();
2362
+ });
2363
+ // Google button click
2364
+ if (this.googleButton) {
2365
+ this.googleButton.addEventListener("click", (e) => {
2366
+ e.preventDefault();
2367
+ this.handleGoogleLogin();
2368
+ });
2369
+ }
2370
+ // Twitter button click
2371
+ if (this.twitterButton) {
2372
+ this.twitterButton.addEventListener("click", (e) => {
2373
+ e.preventDefault();
2374
+ this.handleTwitterLogin();
2375
+ });
2376
+ }
2377
+ // Discord button click
2378
+ if (this.discordButton) {
2379
+ this.discordButton.addEventListener("click", (e) => {
2380
+ e.preventDefault();
2381
+ this.handleDiscordLogin();
2382
+ });
2383
+ }
2384
+ // Passkey login button click
2385
+ if (this.passkeyLoginButton) {
2386
+ this.passkeyLoginButton.addEventListener("click", (e) => {
2387
+ e.preventDefault();
2388
+ this.handlePasskeyLogin();
2389
+ });
2390
+ }
2391
+ // Passkey signup link click
2392
+ if (this.passkeySignupLink) {
2393
+ this.passkeySignupLink.addEventListener("click", (e) => {
2394
+ e.preventDefault();
2395
+ this.handlePasskeySignup();
2396
+ });
2397
+ }
2398
+ }
2399
+ /**
2400
+ * Handle email form submission
2401
+ */
2402
+ async handleEmailSubmit() {
2403
+ if (!this.emailInput || !this.otpInput)
2404
+ return;
2405
+ // Ensure email is captured from input field
2406
+ this.email = this.emailInput.value.trim();
2407
+ if (!this.email || !this.email.includes("@")) {
2408
+ this.setError("Please enter a valid email address");
2409
+ return;
2410
+ }
2411
+ this.setError(null);
2412
+ this.setLoading(true, "email");
2413
+ try {
2414
+ if (!this.otpSent) {
2415
+ // Initiate OTP
2416
+ if (this.config.onEmailOtpInitiate) {
2417
+ await this.config.onEmailOtpInitiate(this.email);
2418
+ }
2419
+ else if (this.config.emailOtpEndpoint) {
2420
+ const response = await fetch(this.config.emailOtpEndpoint, {
2421
+ method: "POST",
2422
+ headers: {
2423
+ "Content-Type": "application/json",
2424
+ },
2425
+ body: JSON.stringify({ email: this.email }),
2426
+ });
2427
+ if (!response.ok) {
2428
+ throw new Error("Failed to send OTP");
2429
+ }
2430
+ }
2431
+ this.otpSent = true;
2432
+ this.showOtpInput();
2433
+ if (this.continueButton) {
2434
+ this.continueButton.textContent = "Verify OTP";
2435
+ }
2436
+ }
2437
+ else {
2438
+ // Verify OTP
2439
+ if (this.config.onEmailOtpVerify) {
2440
+ const result = await this.config.onEmailOtpVerify(this.email, this.otp);
2441
+ if (result.success) {
2442
+ this.config.onLoginSuccess?.({ token: result.token });
2443
+ }
2444
+ else {
2445
+ throw new Error("Invalid OTP");
2446
+ }
2447
+ }
2448
+ else if (this.config.emailOtpEndpoint) {
2449
+ const response = await fetch(`${this.config.emailOtpEndpoint}/verify`, {
2450
+ method: "POST",
2451
+ headers: {
2452
+ "Content-Type": "application/json",
2453
+ },
2454
+ body: JSON.stringify({ email: this.email, otp: this.otp }),
2455
+ });
2456
+ const data = await response.json();
2457
+ if (data.success) {
2458
+ this.config.onLoginSuccess?.({ token: data.token });
2459
+ }
2460
+ else {
2461
+ throw new Error(data.message || "Invalid OTP");
2462
+ }
2463
+ }
2464
+ }
2465
+ }
2466
+ catch (err) {
2467
+ const errorMessage = err instanceof Error ? err.message : "An error occurred";
2468
+ this.setError(errorMessage);
2469
+ this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
2470
+ }
2471
+ finally {
2472
+ this.setLoading(false);
2473
+ }
2474
+ }
2475
+ /**
2476
+ * Handle Google login
2477
+ */
2478
+ async handleGoogleLogin() {
2479
+ // console.log('🔵 Google login button clicked');
2480
+ // console.log('🔵 Config:', {
2481
+ // hasOnGoogleLogin: !!this.config.onGoogleLogin,
2482
+ // googleAuthEndpoint: this.config.googleAuthEndpoint
2483
+ // });
2484
+ this.setError(null);
2485
+ this.setLoading(true, "google");
2486
+ try {
2487
+ localStorage.setItem("abstraxn_login_source", this.config.modal ? "modal" : "inline");
2488
+ if (this.config.onGoogleLogin) {
2489
+ // console.log('🔄 Calling onGoogleLogin callback');
2490
+ await this.config.onGoogleLogin();
2491
+ }
2492
+ else if (this.config.googleAuthEndpoint) {
2493
+ // console.log('🔄 Redirecting to Google auth endpoint:', this.config.googleAuthEndpoint);
2494
+ window.location.href = this.config.googleAuthEndpoint;
2495
+ }
2496
+ else {
2497
+ throw new Error("Google login endpoint not configured");
2498
+ }
2499
+ }
2500
+ catch (err) {
2501
+ const errorMessage = err instanceof Error ? err.message : "An error occurred";
2502
+ this.setError(errorMessage);
2503
+ this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
2504
+ }
2505
+ finally {
2506
+ this.setLoading(false);
2507
+ }
2508
+ }
2509
+ /**
2510
+ * Handle Twitter login
2511
+ */
2512
+ async handleTwitterLogin() {
2513
+ this.setError(null);
2514
+ this.setLoading(true, "twitter");
2515
+ try {
2516
+ if (this.config.onTwitterLogin) {
2517
+ await this.config.onTwitterLogin();
2518
+ }
2519
+ else if (this.config.twitterAuthEndpoint) {
2520
+ window.location.href = this.config.twitterAuthEndpoint;
2521
+ }
2522
+ else {
2523
+ throw new Error("X (Twitter) login not configured");
2524
+ }
2525
+ }
2526
+ catch (err) {
2527
+ const errorMessage = err instanceof Error ? err.message : "An error occurred";
2528
+ this.setError(errorMessage);
2529
+ this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
2530
+ }
2531
+ finally {
2532
+ this.setLoading(false);
2533
+ }
2534
+ }
2535
+ /**
2536
+ * Handle Discord login
2537
+ */
2538
+ async handleDiscordLogin() {
2539
+ this.setError(null);
2540
+ this.setLoading(true, "discord");
2541
+ try {
2542
+ if (this.config.onDiscordLogin) {
2543
+ await this.config.onDiscordLogin();
2544
+ }
2545
+ else if (this.config.discordAuthEndpoint) {
2546
+ window.location.href = this.config.discordAuthEndpoint;
2547
+ }
2548
+ else {
2549
+ throw new Error("Discord login not configured");
2550
+ }
2551
+ }
2552
+ catch (err) {
2553
+ const errorMessage = err instanceof Error ? err.message : "An error occurred";
2554
+ this.setError(errorMessage);
2555
+ this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
2556
+ }
2557
+ finally {
2558
+ this.setLoading(false);
2559
+ }
2560
+ }
2561
+ /**
2562
+ * Hide all login-related elements (social buttons, passkey, etc.)
2563
+ * This ensures they stay hidden on the OTP screen
2564
+ * Made public so it can be called from AbstraxnProvider
2565
+ */
2566
+ hideLoginElements() {
2567
+ if (this.emailForm) {
2568
+ this.emailForm.style.display = "none";
2569
+ }
2570
+ // Hide the header when showing OTP screen
2571
+ const header = this.rootElement?.querySelector(".onboarding-header");
2572
+ if (header) {
2573
+ header.style.display = "none";
2574
+ }
2575
+ if (this.divider) {
2576
+ this.divider.style.display = "none";
2577
+ }
2578
+ if (this.googleButton) {
2579
+ this.googleButton.style.display = "none";
2580
+ }
2581
+ if (this.twitterButton) {
2582
+ this.twitterButton.style.display = "none";
2583
+ }
2584
+ if (this.discordButton) {
2585
+ this.discordButton.style.display = "none";
2586
+ }
2587
+ if (this.socialGrid) {
2588
+ this.socialGrid.style.display = "none";
2589
+ }
2590
+ if (this.passkeyDivider) {
2591
+ this.passkeyDivider.style.display = "none";
2592
+ }
2593
+ // Hide passkey buttons on OTP screen
2594
+ if (this.passkeyLoginButton) {
2595
+ this.passkeyLoginButton.style.display = "none";
2596
+ }
2597
+ if (this.passkeySignupLink) {
2598
+ this.passkeySignupLink.style.display = "none";
2599
+ }
2600
+ if (this.passkeyErrorElement) {
2601
+ this.passkeyErrorElement.style.display = "none";
2602
+ }
2603
+ // Hide external wallets on OTP screen
2604
+ if (this.externalWalletContainer) {
2605
+ this.externalWalletContainer.style.display = "none";
2606
+ }
2607
+ if (this.externalWalletDivider) {
2608
+ this.externalWalletDivider.style.display = "none";
2609
+ }
2610
+ // Hide footer on login card (OTP screen has its own footer)
2611
+ if (this.footer) {
2612
+ this.footer.style.display = "none";
2613
+ }
2614
+ }
2615
+ /**
2616
+ * Show OTP verification screen
2617
+ */
2618
+ showOtpInput() {
2619
+ if (!this.rootElement)
2620
+ return;
2621
+ // Hide the email form and all login-related elements
2622
+ this.hideLoginElements();
2623
+ // Create OTP verification screen
2624
+ this.createOtpVerificationScreen();
2625
+ }
2626
+ /**
2627
+ * Create OTP verification screen with 6 input fields
2628
+ */
2629
+ createOtpVerificationScreen() {
2630
+ if (!this.rootElement)
2631
+ return;
2632
+ // Remove existing OTP screen if any
2633
+ if (this.otpVerificationScreen) {
2634
+ this.otpVerificationScreen.remove();
2635
+ this.verifyButton = null;
2636
+ }
2637
+ // Get the card element
2638
+ const card = this.rootElement.querySelector(".onboarding-card");
2639
+ if (!card)
2640
+ return;
2641
+ // Add class to card to indicate OTP screen is active (for CSS targeting)
2642
+ card.classList.add("onboarding-otp-active");
2643
+ // Create OTP verification container
2644
+ this.otpVerificationScreen = this.createElement("div", {
2645
+ className: "onboarding-otp-verification",
2646
+ });
2647
+ // Add Back Button
2648
+ const backButton = this.createElement("button", {
2649
+ type: "button",
2650
+ className: "onboarding-otp-back-button",
2651
+ "aria-label": "Go back to email input",
2652
+ });
2653
+ backButton.innerHTML = `
2654
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2655
+ <path d="M19 12H5M12 19l-7-7 7-7" />
2656
+ </svg>
2657
+ `;
2658
+ backButton.addEventListener("click", () => {
2659
+ if (!this.loading) {
2660
+ this.resetToLoginForm();
2661
+ }
2662
+ });
2663
+ this.otpVerificationScreen.appendChild(backButton);
2664
+ // Email icon container
2665
+ const iconContainer = this.createElement("div", {
2666
+ className: "onboarding-otp-icon-container",
2667
+ });
2668
+ const iconInner = this.createElement("div", {
2669
+ className: "onboarding-otp-icon-inner",
2670
+ });
2671
+ const emailIcon = this.createEmailIconSVG();
2672
+ iconInner.appendChild(emailIcon);
2673
+ iconContainer.appendChild(iconInner);
2674
+ this.otpVerificationScreen.appendChild(iconContainer);
2675
+ // Title
2676
+ const title = this.createElement("h1", {
2677
+ className: "onboarding-otp-title",
2678
+ textContent: "Enter verification code",
2679
+ });
2680
+ this.otpVerificationScreen.appendChild(title);
2681
+ // Instruction text
2682
+ const instruction = this.createElement("p", {
2683
+ className: "onboarding-otp-instruction",
2684
+ textContent: "We sent a verification code to",
2685
+ });
2686
+ this.otpVerificationScreen.appendChild(instruction);
2687
+ // Email address
2688
+ const emailDisplay = this.createElement("p", {
2689
+ className: "onboarding-otp-email",
2690
+ textContent: this.email,
2691
+ });
2692
+ this.otpVerificationScreen.appendChild(emailDisplay);
2693
+ // OTP inputs container
2694
+ const inputsContainer = this.createElement("div", {
2695
+ className: "onboarding-otp-inputs-container",
2696
+ });
2697
+ // Create 6 input fields
2698
+ this.otpInputs = [];
2699
+ for (let i = 0; i < 6; i++) {
2700
+ const input = this.createElement("input", {
2701
+ type: "text",
2702
+ className: "onboarding-otp-input",
2703
+ maxLength: 1,
2704
+ inputMode: "numeric",
2705
+ pattern: "[0-9]*",
2706
+ });
2707
+ // Add event listeners for auto-advance
2708
+ input.addEventListener("input", (e) => this.handleOtpInput(e, i));
2709
+ input.addEventListener("keydown", (e) => this.handleOtpKeydown(e, i));
2710
+ input.addEventListener("paste", (e) => this.handleOtpPaste(e));
2711
+ this.otpInputs.push(input);
2712
+ inputsContainer.appendChild(input);
2713
+ }
2714
+ this.otpVerificationScreen.appendChild(inputsContainer);
2715
+ // Error element (for OTP screen)
2716
+ const otpErrorElement = this.createElement("div", {
2717
+ className: "onboarding-error",
2718
+ style: "display: none;",
2719
+ });
2720
+ this.otpVerificationScreen.appendChild(otpErrorElement);
2721
+ // Store reference for OTP screen errors
2722
+ this.otpVerificationScreen.errorElement = otpErrorElement;
2723
+ // Verify button
2724
+ this.verifyButton = this.createElement("button", {
2725
+ type: "button",
2726
+ className: "onboarding-button onboarding-button-primary",
2727
+ textContent: "Verify",
2728
+ });
2729
+ this.verifyButton.addEventListener("click", () => this.handleOtpVerify());
2730
+ this.otpVerificationScreen.appendChild(this.verifyButton);
2731
+ // Resend section
2732
+ const resendSection = this.createElement("div", {
2733
+ className: "onboarding-otp-resend",
2734
+ });
2735
+ const resendText = this.createElement("p", {
2736
+ className: "onboarding-otp-resend-text",
2737
+ });
2738
+ resendText.innerHTML =
2739
+ 'Didn\'t receive the email? <span class="onboarding-otp-resend-link" id="otp-resend-link">Resend</span>';
2740
+ resendSection.appendChild(resendText);
2741
+ this.otpVerificationScreen.appendChild(resendSection);
2742
+ // Add resend click handler
2743
+ const resendLink = resendSection.querySelector("#otp-resend-link");
2744
+ if (resendLink) {
2745
+ this.resendButton = resendLink;
2746
+ resendLink.addEventListener("click", (e) => {
2747
+ e.preventDefault();
2748
+ e.stopPropagation();
2749
+ // Prevent click if cooldown is active
2750
+ if (this.resendCooldown > 0) {
2751
+ return;
2752
+ }
2753
+ this.handleResendOtp();
2754
+ });
2755
+ }
2756
+ // Footer (add to OTP screen as well)
2757
+ if (this.config.showFooter) {
2758
+ const textColor = this.config.colors?.text ||
2759
+ (this.config.theme === "dark" ? "#9ca3af" : "#6b7280");
2760
+ const brandColor = this.config.colors?.text ||
2761
+ (this.config.theme === "dark" ? "#e5e7eb" : "#374151");
2762
+ const otpFooter = this.createElement("div", {
2763
+ className: "onboarding-footer",
2764
+ });
2765
+ const otpFooterText = this.createElement("p", {
2766
+ className: "onboarding-footer-text",
2767
+ style: `color: ${textColor}; display: flex; align-items: center; gap: 6px; justify-content: center;`,
2768
+ });
2769
+ // "Powered by" text
2770
+ const poweredBySpan = this.createElement("span", {
2771
+ textContent: "Powered by",
2772
+ style: `color: ${textColor};`,
2773
+ });
2774
+ otpFooterText.appendChild(poweredBySpan);
2775
+ // Inline brand with logo (clickable link)
2776
+ const brandContainer = this.createElement("a", {
2777
+ href: "https://www.abstraxn.com/",
2778
+ target: "_blank",
2779
+ rel: "noopener noreferrer",
2780
+ style: "display: inline-flex; align-items: center; gap: 6px; text-decoration: none; cursor: pointer;",
2781
+ "aria-label": "Visit Abstraxn website",
2782
+ });
2783
+ // Abstraxn logo SVG
2784
+ const logoSvg = this.createElement("span", {
2785
+ innerHTML: `
2786
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none" style="width: 20px; height: 20px; display: inline-block; vertical-align: middle;">
2787
+ <path d="M47.9082 37.191L23.9541 23.553L0 9.91504L37.7436 -8.13502e-06L47.9082 37.191Z" fill="url(#paint0_linear_1242_781_otp)"/>
2788
+ <path d="M13.5156 46.3594L10.1846 47.2344L8.99219 42.873L12.4365 42.4111L13.5156 46.3594ZM20.4268 44.5566L17.1152 45.4199L16.125 41.8818L19.5566 41.3721L20.4268 44.5566ZM26.9307 42.834L23.6152 43.6982L22.8438 40.9443L26.2744 40.4326L26.9307 42.834ZM33.5078 41.1074L30.1875 41.9727L29.6328 39.9951L33.0635 39.4844L33.5078 41.1074ZM39.998 39.4014L36.6729 40.2686L36.3359 39.0674L39.7656 38.5508L39.998 39.4014ZM10.1406 33.9434L11.6807 39.5762L8.15918 39.7568L6.46094 33.543L10.1406 33.9434ZM17.6582 34.5996L18.8916 39.1113L15.4199 39.3496L14.0078 34.3076L17.6582 34.5996ZM24.8779 35.332L25.8047 38.7227L22.3252 38.959L21.2266 35.0361L24.8779 35.332ZM32.0918 36.0723L32.7119 38.3398L29.2227 38.5723L28.4375 35.7656L32.0918 36.0723ZM43.2373 38.5488L43.1172 38.1172L47.7939 37.3516L43.2373 38.5488ZM39.3047 36.8096L39.6182 37.9561L36.1221 38.1846L35.6484 36.4961L39.3047 36.8096ZM47.7939 37.3516L43.0107 37.7939L42.8516 37.2236L47.7939 37.3516ZM47.8291 37.2988L42.7227 36.7529L42.5547 36.1523L47.8291 37.2988ZM47.8652 37.2412L42.3711 35.3486L42.2031 34.7471L47.8652 37.2412ZM38.748 34.8604L39.0664 36.0254L35.3369 35.4043L34.8594 33.6982L38.748 34.8604ZM31.0576 32.3994L31.6787 34.6709L27.959 34.0566L27.1719 31.2461L31.0576 32.3994ZM38.0811 32.6318L38.4092 33.8301L34.4893 32.3496L34.1543 31.1523C34.1005 30.9591 34.0322 30.7719 33.9531 30.5918L38.0811 32.6318ZM23.3682 29.9385L24.291 33.3164L20.5801 32.71L19.4844 28.7949L23.3682 29.9385ZM15.6641 27.4805L16.8896 31.9629L13.1875 31.3594L11.7812 26.3379L15.6641 27.4805ZM29.9463 28.4678L30.5693 30.748L26.6201 29.2812L25.8633 26.583C25.8534 26.5431 25.8417 26.5028 25.8281 26.4639L29.9463 28.4678ZM7.6875 25.0938L9.21484 30.6816L5.44727 29.959L3.75781 23.7793L7.6875 25.0938ZM21.5312 23.3623L22.4492 26.7227L18.5059 25.2627L17.4141 21.3643L21.5312 23.3623ZM13.6104 20.1602L14.8242 24.6006L10.8887 23.1387L9.49219 18.1553L13.6104 20.1602ZM5.2168 16.0654L6.72559 21.585L2.70703 19.9473L1.06055 13.8701L5.2168 16.0654Z" fill="url(#paint1_linear_1242_781_otp)"/>
2789
+ <defs>
2790
+ <linearGradient id="paint0_linear_1242_781_otp" x1="7.03362" y1="6.09543" x2="47.2003" y2="57.0656" gradientUnits="userSpaceOnUse">
2791
+ <stop stop-color="#F878D2"/>
2792
+ <stop offset="0.393522" stop-color="#FDCC81"/>
2793
+ <stop offset="1" stop-color="#F9A0D9"/>
2794
+ </linearGradient>
2795
+ <linearGradient id="paint1_linear_1242_781_otp" x1="2.53739" y1="21.5136" x2="48.7969" y2="38.5742" gradientUnits="userSpaceOnUse">
2796
+ <stop stop-color="#F878D2"/>
2797
+ <stop offset="0.393522" stop-color="#FDCC81"/>
2798
+ <stop offset="1" stop-color="#F9A0D9"/>
2799
+ </linearGradient>
2800
+ </defs>
2801
+ </svg>
2802
+ `,
2803
+ style: "display: inline-flex; align-items: center;",
2804
+ });
2805
+ brandContainer.appendChild(logoSvg);
2806
+ const brandText = this.createElement("span", {
2807
+ textContent: "abstraxn",
2808
+ style: `color: ${brandColor}; font-weight: 700; letter-spacing: 0.2px;`,
2809
+ });
2810
+ brandContainer.appendChild(brandText);
2811
+ otpFooterText.appendChild(brandContainer);
2812
+ otpFooter.appendChild(otpFooterText);
2813
+ this.otpVerificationScreen.appendChild(otpFooter);
2814
+ }
2815
+ // Append to card
2816
+ card.appendChild(this.otpVerificationScreen);
2817
+ // Ensure login elements are hidden when OTP screen is created
2818
+ // This is critical to prevent social buttons from showing
2819
+ this.hideLoginElements();
2820
+ // Also ensure they stay hidden after DOM updates
2821
+ setTimeout(() => {
2822
+ if (this.otpVerificationScreen &&
2823
+ this.otpVerificationScreen.parentElement) {
2824
+ this.hideLoginElements();
2825
+ }
2826
+ }, 0);
2827
+ // Focus first input
2828
+ setTimeout(() => {
2829
+ if (this.otpInputs[0]) {
2830
+ this.otpInputs[0].focus();
2831
+ }
2832
+ // Final check to ensure social buttons are hidden
2833
+ if (this.otpVerificationScreen &&
2834
+ this.otpVerificationScreen.parentElement) {
2835
+ this.hideLoginElements();
2836
+ }
2837
+ }, 100);
2838
+ // Start resend cooldown
2839
+ this.startResendCooldown();
2840
+ }
2841
+ /**
2842
+ * Create email icon SVG
2843
+ */
2844
+ createEmailIconSVG() {
2845
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
2846
+ svg.setAttribute("class", "onboarding-otp-icon");
2847
+ svg.setAttribute("viewBox", "0 0 17 17");
2848
+ svg.setAttribute("fill", "currentColor");
2849
+ svg.setAttribute("width", "28");
2850
+ svg.setAttribute("height", "28");
2851
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
2852
+ path.setAttribute("d", "M0 2v13h17v-13h-17zM8.494 9.817l-6.896-6.817h13.82l-6.924 6.817zM5.755 8.516l-4.755 4.682v-9.383l4.755 4.701zM6.466 9.219l2.026 2.003 1.996-1.966 4.8 4.744h-13.677l4.855-4.781zM11.201 8.555l4.799-4.725v9.467l-4.799-4.742z");
2853
+ svg.appendChild(path);
2854
+ return svg;
2855
+ }
2856
+ /**
2857
+ * Handle OTP input - auto-advance to next field
2858
+ */
2859
+ handleOtpInput(e, index) {
2860
+ const input = e.target;
2861
+ const value = input.value.replace(/[^0-9]/g, "");
2862
+ if (value) {
2863
+ input.value = value;
2864
+ // Move to next input if available
2865
+ if (index < 5 && this.otpInputs[index + 1]) {
2866
+ this.otpInputs[index + 1].focus();
2867
+ }
2868
+ // Re-enable auto-submit when user starts typing (clears previous error state)
2869
+ if (!this.autoSubmitEnabled) {
2870
+ this.autoSubmitEnabled = true;
2871
+ this.setError(null); // Clear any previous error
2872
+ }
2873
+ }
2874
+ else {
2875
+ input.value = "";
2876
+ }
2877
+ this.updateOtpValue();
2878
+ // Auto-submit when 6 digits are entered (only if auto-submit is enabled)
2879
+ if (this.otp.length === 6 && this.autoSubmitEnabled && !this.loading) {
2880
+ // Small delay to ensure the last digit is properly set
2881
+ setTimeout(() => {
2882
+ this.handleOtpVerify();
2883
+ }, 100);
2884
+ }
2885
+ }
2886
+ /**
2887
+ * Handle OTP keydown - backspace to go to previous field
2888
+ */
2889
+ handleOtpKeydown(e, index) {
2890
+ const input = e.target;
2891
+ if (e.key === "Backspace" && !input.value && index > 0) {
2892
+ this.otpInputs[index - 1].focus();
2893
+ }
2894
+ }
2895
+ /**
2896
+ * Handle OTP paste - fill all fields from clipboard
2897
+ */
2898
+ handleOtpPaste(e) {
2899
+ e.preventDefault();
2900
+ const pastedData = e.clipboardData?.getData("text") || "";
2901
+ const digits = pastedData.replace(/[^0-9]/g, "").slice(0, 6);
2902
+ for (let i = 0; i < 6; i++) {
2903
+ if (this.otpInputs[i]) {
2904
+ this.otpInputs[i].value = digits[i] || "";
2905
+ }
2906
+ }
2907
+ // Focus the last filled input or the last input
2908
+ const lastFilledIndex = Math.min(digits.length - 1, 5);
2909
+ if (this.otpInputs[lastFilledIndex]) {
2910
+ this.otpInputs[lastFilledIndex].focus();
2911
+ }
2912
+ this.updateOtpValue();
2913
+ // Auto-submit when 6 digits are pasted (only if auto-submit is enabled)
2914
+ if (this.otp.length === 6 && this.autoSubmitEnabled && !this.loading) {
2915
+ // Small delay to ensure all digits are properly set
2916
+ setTimeout(() => {
2917
+ this.handleOtpVerify();
2918
+ }, 100);
2919
+ }
2920
+ }
2921
+ /**
2922
+ * Update OTP value from input fields
2923
+ */
2924
+ updateOtpValue() {
2925
+ this.otp = this.otpInputs.map((input) => input.value).join("");
2926
+ }
2927
+ /**
2928
+ * Handle OTP verification
2929
+ */
2930
+ async handleOtpVerify() {
2931
+ this.updateOtpValue();
2932
+ if (this.otp.length !== 6) {
2933
+ this.setError("Please enter the complete 6-digit code");
2934
+ return;
2935
+ }
2936
+ // Disable inputs during verification
2937
+ this.otpInputs.forEach((input) => {
2938
+ input.disabled = true;
2939
+ });
2940
+ this.setLoading(true, "email");
2941
+ this.setError(null);
2942
+ try {
2943
+ if (this.config.onEmailOtpVerify) {
2944
+ const result = await this.config.onEmailOtpVerify(this.email, this.otp);
2945
+ if (result.success) {
2946
+ this.config.onLoginSuccess?.(result);
2947
+ }
2948
+ else {
2949
+ // Use the error message from the API response, fallback to generic message only if not provided
2950
+ const errorMessage = result.error || "Invalid verification code";
2951
+ throw new Error(errorMessage);
2952
+ }
2953
+ }
2954
+ else if (this.config.emailOtpEndpoint) {
2955
+ const response = await fetch(`${this.config.emailOtpEndpoint}/verify`, {
2956
+ method: "POST",
2957
+ headers: {
2958
+ "Content-Type": "application/json",
2959
+ },
2960
+ body: JSON.stringify({ email: this.email, otp: this.otp }),
2961
+ });
2962
+ const data = await response.json();
2963
+ if (data.success) {
2964
+ this.config.onLoginSuccess?.({ token: data.token });
2965
+ }
2966
+ else {
2967
+ // Use the error message from the API response, fallback to generic message only if not provided
2968
+ const errorMessage = data.message || data.error || "Invalid verification code";
2969
+ throw new Error(errorMessage);
2970
+ }
2971
+ }
2972
+ }
2973
+ catch (err) {
2974
+ const errorMessage = err instanceof Error ? err.message : "An error occurred";
2975
+ this.setError(errorMessage);
2976
+ this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
2977
+ // Disable auto-submit after failed attempt
2978
+ this.autoSubmitEnabled = false;
2979
+ // Ensure social buttons and login elements remain hidden on OTP screen when error occurs
2980
+ // This prevents them from appearing when an OTP error is shown
2981
+ if (this.otpVerificationScreen) {
2982
+ // Immediately hide login elements
2983
+ this.hideLoginElements();
2984
+ // Also use setTimeout to ensure they stay hidden after any potential re-renders
2985
+ setTimeout(() => {
2986
+ if (this.otpVerificationScreen &&
2987
+ this.otpVerificationScreen.parentElement) {
2988
+ this.hideLoginElements();
2989
+ }
2990
+ }, 0);
2991
+ // Additional check after a short delay to catch any late re-renders
2992
+ setTimeout(() => {
2993
+ if (this.otpVerificationScreen &&
2994
+ this.otpVerificationScreen.parentElement) {
2995
+ this.hideLoginElements();
2996
+ }
2997
+ }, 100);
2998
+ }
2999
+ // Clear OTP inputs on error
3000
+ this.otpInputs.forEach((input) => {
3001
+ input.value = "";
3002
+ input.disabled = false;
3003
+ });
3004
+ this.otp = "";
3005
+ if (this.otpInputs[0]) {
3006
+ this.otpInputs[0].focus();
3007
+ }
3008
+ }
3009
+ finally {
3010
+ this.setLoading(false);
3011
+ }
3012
+ }
3013
+ /**
3014
+ * Handle resend OTP
3015
+ */
3016
+ async handleResendOtp() {
3017
+ if (this.resendCooldown > 0) {
3018
+ return;
3019
+ }
3020
+ // Re-enable auto-submit when resending OTP
3021
+ this.autoSubmitEnabled = true;
3022
+ this.setError(null);
3023
+ this.setLoading(true, "email");
3024
+ try {
3025
+ if (this.config.onEmailOtpInitiate) {
3026
+ await this.config.onEmailOtpInitiate(this.email);
3027
+ }
3028
+ else if (this.config.emailOtpEndpoint) {
3029
+ const response = await fetch(this.config.emailOtpEndpoint, {
3030
+ method: "POST",
3031
+ headers: {
3032
+ "Content-Type": "application/json",
3033
+ },
3034
+ body: JSON.stringify({ email: this.email }),
3035
+ });
3036
+ if (!response.ok) {
3037
+ throw new Error("Failed to resend OTP");
3038
+ }
3039
+ }
3040
+ // Clear OTP inputs
3041
+ this.otpInputs.forEach((input) => {
3042
+ input.value = "";
3043
+ input.disabled = false;
3044
+ });
3045
+ this.otp = "";
3046
+ if (this.otpInputs[0]) {
3047
+ this.otpInputs[0].focus();
3048
+ }
3049
+ // Start cooldown
3050
+ this.startResendCooldown();
3051
+ }
3052
+ catch (err) {
3053
+ const errorMessage = err instanceof Error ? err.message : "Failed to resend OTP";
3054
+ this.setError(errorMessage);
3055
+ this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
3056
+ }
3057
+ finally {
3058
+ this.setLoading(false);
3059
+ }
3060
+ }
3061
+ /**
3062
+ * Start resend cooldown timer
3063
+ */
3064
+ startResendCooldown() {
3065
+ // Clear any existing cooldown timer
3066
+ if (this.resendCooldownTimer) {
3067
+ clearInterval(this.resendCooldownTimer);
3068
+ }
3069
+ this.resendCooldown = 60; // 60 seconds cooldown
3070
+ // Update UI immediately
3071
+ if (this.resendButton) {
3072
+ this.resendButton.textContent = `Resend (${this.resendCooldown}s)`;
3073
+ this.resendButton.classList.add("onboarding-otp-resend-link-disabled");
3074
+ this.resendButton.style.pointerEvents = "none";
3075
+ this.resendButton.style.cursor = "not-allowed";
3076
+ this.resendButton.style.opacity = "0.6";
3077
+ }
3078
+ // Start countdown timer
3079
+ this.resendCooldownTimer = setInterval(() => {
3080
+ if (this.resendCooldown > 0) {
3081
+ this.resendCooldown--;
3082
+ if (this.resendButton) {
3083
+ this.resendButton.textContent = `Resend (${this.resendCooldown}s)`;
3084
+ }
3085
+ }
3086
+ else {
3087
+ // Cooldown complete, enable resend
3088
+ if (this.resendCooldownTimer) {
3089
+ clearInterval(this.resendCooldownTimer);
3090
+ this.resendCooldownTimer = null;
3091
+ }
3092
+ if (this.resendButton) {
3093
+ this.resendButton.textContent = "Resend";
3094
+ this.resendButton.classList.remove("onboarding-otp-resend-link-disabled");
3095
+ this.resendButton.style.pointerEvents = "auto";
3096
+ this.resendButton.style.cursor = "pointer";
3097
+ this.resendButton.style.opacity = "1";
3098
+ }
3099
+ }
3100
+ }, 1000);
3101
+ }
3102
+ /**
3103
+ * Set error message
3104
+ */
3105
+ setError(message) {
3106
+ const formatMessage = (msg) => {
3107
+ if (msg.startsWith("{") && msg.endsWith("}")) {
3108
+ return msg;
3109
+ }
3110
+ return `{ ${msg} }`;
3111
+ };
3112
+ // Show error in OTP screen if it exists
3113
+ if (this.otpVerificationScreen) {
3114
+ const otpErrorElement = this.otpVerificationScreen
3115
+ .errorElement;
3116
+ if (otpErrorElement) {
3117
+ if (message) {
3118
+ otpErrorElement.textContent = formatMessage(message);
3119
+ otpErrorElement.style.display = "block";
3120
+ }
3121
+ else {
3122
+ otpErrorElement.style.display = "none";
3123
+ }
3124
+ // Ensure social buttons remain hidden when showing error on OTP screen
3125
+ // Call immediately and also with delays to catch any re-renders
3126
+ this.hideLoginElements();
3127
+ setTimeout(() => {
3128
+ if (this.otpVerificationScreen &&
3129
+ this.otpVerificationScreen.parentElement) {
3130
+ this.hideLoginElements();
3131
+ }
3132
+ }, 0);
3133
+ setTimeout(() => {
3134
+ if (this.otpVerificationScreen &&
3135
+ this.otpVerificationScreen.parentElement) {
3136
+ this.hideLoginElements();
3137
+ }
3138
+ }, 50);
3139
+ setTimeout(() => {
3140
+ if (this.otpVerificationScreen &&
3141
+ this.otpVerificationScreen.parentElement) {
3142
+ this.hideLoginElements();
3143
+ }
3144
+ }, 150);
3145
+ return;
3146
+ }
3147
+ }
3148
+ // Fallback to regular error element
3149
+ if (!this.errorElement)
3150
+ return;
3151
+ if (message) {
3152
+ this.errorElement.textContent = formatMessage(message);
3153
+ this.errorElement.style.display = "block";
3154
+ }
3155
+ else {
3156
+ this.errorElement.style.display = "none";
3157
+ }
3158
+ }
3159
+ /**
3160
+ * Set loading state
3161
+ */
3162
+ setLoading(loading, buttonType) {
3163
+ this.loading = loading;
3164
+ // Track which button is active
3165
+ if (loading && buttonType) {
3166
+ this.activeButton = buttonType;
3167
+ }
3168
+ else if (!loading) {
3169
+ this.activeButton = null;
3170
+ }
3171
+ // Email/Continue button
3172
+ if (this.continueButton) {
3173
+ this.continueButton.disabled = loading || !this.email;
3174
+ // Show loading text only if this is the active button
3175
+ if (loading && this.activeButton === "email") {
3176
+ this.continueButton.textContent = this.otpSent
3177
+ ? "Verifying..."
3178
+ : "Loading...";
3179
+ }
3180
+ else if (!loading) {
3181
+ this.continueButton.textContent = this.otpSent
3182
+ ? "Verify OTP"
3183
+ : "Continue";
3184
+ }
3185
+ }
3186
+ // Email arrow button (when all auth methods are enabled)
3187
+ if (this.emailArrowButton) {
3188
+ this.emailArrowButton.disabled = loading || !this.email;
3189
+ }
3190
+ // Google button
3191
+ if (this.googleButton) {
3192
+ this.googleButton.disabled = loading;
3193
+ // Google button doesn't change text, just gets disabled
3194
+ }
3195
+ // Twitter button
3196
+ if (this.twitterButton) {
3197
+ this.twitterButton.disabled = loading;
3198
+ }
3199
+ // Discord button
3200
+ if (this.discordButton) {
3201
+ this.discordButton.disabled = loading;
3202
+ }
3203
+ // Passkey login button
3204
+ if (this.passkeyLoginButton) {
3205
+ this.passkeyLoginButton.disabled = loading;
3206
+ // Show loading text only if this is the active button
3207
+ if (loading && this.activeButton === "passkey") {
3208
+ // Update only the text node, preserve the icon
3209
+ const textNodes = Array.from(this.passkeyLoginButton.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE);
3210
+ if (textNodes.length > 0) {
3211
+ textNodes[0].textContent = " Authenticating...";
3212
+ }
3213
+ else {
3214
+ // If no text node exists, append one (icon should already be there)
3215
+ this.passkeyLoginButton.appendChild(document.createTextNode(" Authenticating..."));
3216
+ }
3217
+ this.passkeyLoginButton.style.opacity = "0.7";
3218
+ this.passkeyLoginButton.style.cursor = "not-allowed";
3219
+ }
3220
+ else if (!loading) {
3221
+ // Update only the text node, preserve the icon
3222
+ const textNodes = Array.from(this.passkeyLoginButton.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE);
3223
+ const passkeyText = ` ${this.config.labels?.passkeyLoginButton || "Log in with passkey"}`;
3224
+ if (textNodes.length > 0) {
3225
+ textNodes[0].textContent = passkeyText;
3226
+ }
3227
+ else {
3228
+ // If no text node exists, append one (icon should already be there)
3229
+ this.passkeyLoginButton.appendChild(document.createTextNode(passkeyText));
3230
+ }
3231
+ this.passkeyLoginButton.style.opacity = "1";
3232
+ this.passkeyLoginButton.style.cursor = "pointer";
3233
+ }
3234
+ }
3235
+ // Passkey signup link
3236
+ if (this.passkeySignupLink) {
3237
+ if (loading) {
3238
+ this.passkeySignupLink.style.opacity = "0.5";
3239
+ this.passkeySignupLink.style.pointerEvents = "none";
3240
+ this.passkeySignupLink.style.cursor = "not-allowed";
3241
+ }
3242
+ else {
3243
+ this.passkeySignupLink.style.opacity = "1";
3244
+ this.passkeySignupLink.style.pointerEvents = "auto";
3245
+ this.passkeySignupLink.style.cursor = "pointer";
3246
+ }
3247
+ }
3248
+ // Email input
3249
+ if (this.emailInput) {
3250
+ this.emailInput.disabled = loading || this.otpSent;
3251
+ }
3252
+ // OTP input
3253
+ if (this.otpInput) {
3254
+ this.otpInput.disabled = loading;
3255
+ }
3256
+ // Verify button
3257
+ if (this.verifyButton) {
3258
+ this.verifyButton.disabled = loading;
3259
+ if (loading) {
3260
+ this.verifyButton.textContent = "Verifying...";
3261
+ this.verifyButton.style.opacity = "0.7";
3262
+ this.verifyButton.style.cursor = "not-allowed";
3263
+ }
3264
+ else {
3265
+ this.verifyButton.textContent = "Verify";
3266
+ this.verifyButton.style.opacity = "1";
3267
+ this.verifyButton.style.cursor = "pointer";
3268
+ }
3269
+ }
3270
+ }
3271
+ /**
3272
+ * Update button state
3273
+ */
3274
+ updateButtonState() {
3275
+ const isValid = isValidEmail(this.email.trim());
3276
+ if (this.continueButton) {
3277
+ this.continueButton.disabled = this.loading || !isValid;
3278
+ }
3279
+ // Update arrow button state (when all auth methods are enabled)
3280
+ if (this.emailArrowButton) {
3281
+ this.emailArrowButton.disabled = this.loading || !isValid;
3282
+ }
3283
+ }
3284
+ /**
3285
+ * Update theme
3286
+ */
3287
+ setTheme(theme) {
3288
+ this.config.theme = theme;
3289
+ if (this.rootElement) {
3290
+ this.rootElement.className = `onboarding-container onboarding-theme-${theme} ${this.config.className}`;
3291
+ }
3292
+ }
3293
+ /**
3294
+ * Show/hide component
3295
+ */
3296
+ setVisible(visible) {
3297
+ if (this.rootElement) {
3298
+ this.rootElement.style.display = visible ? "" : "none";
3299
+ }
3300
+ }
3301
+ /**
3302
+ * Reset OTP screen and show initial login form
3303
+ */
3304
+ resetToLoginForm() {
3305
+ // Clear resend cooldown timer
3306
+ if (this.resendCooldownTimer) {
3307
+ clearInterval(this.resendCooldownTimer);
3308
+ this.resendCooldownTimer = null;
3309
+ }
3310
+ this.resendCooldown = 0;
3311
+ // Clear OTP verification screen
3312
+ if (this.otpVerificationScreen) {
3313
+ this.otpVerificationScreen.remove();
3314
+ this.otpVerificationScreen = null;
3315
+ }
3316
+ // Remove loading/error mode classes and OTP active class
3317
+ const card = this.rootElement?.querySelector(".onboarding-card");
3318
+ if (card) {
3319
+ card.classList.remove("onboarding-mode-loading");
3320
+ card.classList.remove("onboarding-mode-error");
3321
+ card.classList.remove("onboarding-otp-active");
3322
+ }
3323
+ // Hide loading modal if exists (for inline components)
3324
+ this.hideLoadingModal();
3325
+ // Remove error container if exists
3326
+ const errorContainer = this.rootElement?.querySelector(".onboarding-error-container");
3327
+ if (errorContainer) {
3328
+ errorContainer.remove();
3329
+ }
3330
+ // Clear OTP inputs
3331
+ this.otpInputs.forEach((input) => {
3332
+ input.value = "";
3333
+ input.disabled = false;
3334
+ });
3335
+ this.otpInputs = [];
3336
+ this.otp = "";
3337
+ this.otpSent = false;
3338
+ this.loading = false;
3339
+ // Clear timer
3340
+ // Clear resend cooldown timer
3341
+ if (this.resendCooldownTimer) {
3342
+ clearInterval(this.resendCooldownTimer);
3343
+ this.resendCooldownTimer = null;
3344
+ }
3345
+ this.resendCooldown = 0;
3346
+ if (this.resendButton) {
3347
+ this.resendButton.textContent = "Resend";
3348
+ this.resendButton.classList.remove("onboarding-otp-resend-link-disabled");
3349
+ this.resendButton.style.pointerEvents = "auto";
3350
+ this.resendButton.style.cursor = "pointer";
3351
+ this.resendButton.style.opacity = "1";
3352
+ }
3353
+ // Get auth methods
3354
+ const authMethods = this.config.authMethods || ["otp", "google"];
3355
+ const showEmail = authMethods.includes("otp");
3356
+ const showGoogle = authMethods.includes("google");
3357
+ const showTwitter = authMethods.includes("twitter");
3358
+ const showDiscord = authMethods.includes("discord");
3359
+ const showPasskey = authMethods.includes("passkey");
3360
+ const socialMethods = [
3361
+ { key: "google", show: showGoogle },
3362
+ { key: "twitter", show: showTwitter },
3363
+ { key: "discord", show: showDiscord },
3364
+ ].filter((m) => m.show);
3365
+ // Show the email form and all login-related elements
3366
+ if (this.emailForm) {
3367
+ this.emailForm.style.display = showEmail ? "" : "none";
3368
+ }
3369
+ // Show the header again when returning to login form
3370
+ const header = this.rootElement?.querySelector(".onboarding-header");
3371
+ if (header) {
3372
+ header.style.display = "";
3373
+ }
3374
+ if (this.divider) {
3375
+ this.divider.style.display =
3376
+ showEmail && socialMethods.length > 0 ? "" : "none";
3377
+ }
3378
+ if (this.passkeyDivider) {
3379
+ this.passkeyDivider.style.display =
3380
+ showPasskey && (showEmail || socialMethods.length > 0) ? "" : "none";
3381
+ }
3382
+ if (this.googleButton) {
3383
+ this.googleButton.style.display = showGoogle ? "" : "none";
3384
+ this.googleButton.disabled = false;
3385
+ }
3386
+ if (this.twitterButton) {
3387
+ this.twitterButton.style.display = showTwitter ? "" : "none";
3388
+ this.twitterButton.disabled = false;
3389
+ }
3390
+ if (this.discordButton) {
3391
+ this.discordButton.style.display = showDiscord ? "" : "none";
3392
+ this.discordButton.disabled = false;
3393
+ }
3394
+ if (this.socialGrid) {
3395
+ this.socialGrid.style.display = socialMethods.length > 1 ? "" : "none";
3396
+ }
3397
+ // Show passkey buttons again when returning to login form
3398
+ if (this.passkeyLoginButton) {
3399
+ this.passkeyLoginButton.style.display = showPasskey ? "" : "none";
3400
+ }
3401
+ if (this.passkeySignupLink) {
3402
+ this.passkeySignupLink.style.display = showPasskey ? "" : "none";
3403
+ }
3404
+ if (this.passkeyErrorElement) {
3405
+ this.passkeyErrorElement.style.display = "none"; // Hide error when resetting
3406
+ }
3407
+ // Show external wallets again when returning to login form
3408
+ if (this.externalWalletsEnabled) {
3409
+ if (this.externalWalletContainer) {
3410
+ this.externalWalletContainer.style.display = "";
3411
+ }
3412
+ if (this.externalWalletDivider) {
3413
+ // Show divider only if we have email or social methods
3414
+ const hasEmailOrSocial = showEmail || socialMethods.length > 0;
3415
+ this.externalWalletDivider.style.display = hasEmailOrSocial
3416
+ ? ""
3417
+ : "none";
3418
+ }
3419
+ }
3420
+ if (this.footer) {
3421
+ this.footer.style.display = "";
3422
+ }
3423
+ // Clear error messages
3424
+ this.setError(null);
3425
+ if (this.errorElement) {
3426
+ this.errorElement.style.display = "none";
3427
+ }
3428
+ // Reset email input - enable it and clear value
3429
+ if (this.emailInput) {
3430
+ this.emailInput.value = "";
3431
+ this.emailInput.disabled = false;
3432
+ this.email = "";
3433
+ }
3434
+ // Reset OTP group (hidden input)
3435
+ if (this.otpGroup) {
3436
+ this.otpGroup.style.display = "none";
3437
+ if (this.otpInput) {
3438
+ this.otpInput.value = "";
3439
+ this.otpInput.disabled = false;
3440
+ }
3441
+ }
3442
+ // Reset button state - ensure it shows "Continue" and is properly enabled/disabled
3443
+ if (this.continueButton) {
3444
+ this.continueButton.textContent =
3445
+ this.config.labels?.emailButton || "Continue";
3446
+ }
3447
+ // Update button state to ensure everything is in sync
3448
+ this.updateButtonState();
3449
+ }
3450
+ /**
3451
+ * Close the modal
3452
+ */
3453
+ close() {
3454
+ // Clear URL params to prevent showing error again on refresh
3455
+ const url = new URL(window.location.href);
3456
+ url.searchParams.delete("error");
3457
+ url.searchParams.delete("success");
3458
+ window.history.replaceState({}, document.title, url.toString());
3459
+ if (this.modalOverlay) {
3460
+ // Add closing animation
3461
+ this.modalOverlay.classList.remove("onboarding-modal-open");
3462
+ this.modalOverlay.classList.add("onboarding-modal-closing");
3463
+ // Remove after animation
3464
+ setTimeout(() => {
3465
+ this.destroy();
3466
+ }, 200);
3467
+ }
3468
+ else {
3469
+ this.destroy();
3470
+ }
3471
+ // Restore body scroll
3472
+ document.body.style.overflow = "";
3473
+ // Call onClose callback
3474
+ if (this.config.onClose) {
3475
+ this.config.onClose();
3476
+ }
3477
+ }
3478
+ /**
3479
+ * Destroy the component
3480
+ */
3481
+ destroy() {
3482
+ // Reset to login form before destroying to ensure clean state
3483
+ this.resetToLoginForm();
3484
+ // Clean up modal
3485
+ if (this.modalOverlay && this.modalOverlay.parentNode) {
3486
+ this.modalOverlay.parentNode.removeChild(this.modalOverlay);
3487
+ }
3488
+ // Clean up inline
3489
+ if (this.rootElement && this.rootElement.parentNode) {
3490
+ this.rootElement.parentNode.removeChild(this.rootElement);
3491
+ }
3492
+ // Restore body scroll
3493
+ document.body.style.overflow = "";
3494
+ // Clear references
3495
+ this.rootElement = null;
3496
+ this.modalOverlay = null;
3497
+ this.modalContent = null;
3498
+ this.emailInput = null;
3499
+ this.otpInput = null;
3500
+ this.emailForm = null;
3501
+ this.continueButton = null;
3502
+ this.googleButton = null;
3503
+ this.errorElement = null;
3504
+ this.otpGroup = null;
3505
+ this.otpVerificationScreen = null; // Clear OTP screen reference
3506
+ this.externalWalletContainer = null; // Clear external wallet container reference
3507
+ this.externalWalletDivider = null; // Clear external wallet divider reference
3508
+ }
3509
+ /**
3510
+ * Create DOM element helper
3511
+ */
3512
+ createElement(tag, options = {}) {
3513
+ const element = document.createElement(tag);
3514
+ Object.keys(options).forEach((key) => {
3515
+ const value = options[key];
3516
+ if (key === "className" && typeof value === "string") {
3517
+ element.className = value;
3518
+ }
3519
+ else if (key === "textContent" &&
3520
+ (typeof value === "string" || value === null)) {
3521
+ element.textContent = value;
3522
+ }
3523
+ else if (key === "innerHTML" && typeof value === "string") {
3524
+ element.innerHTML = value;
3525
+ }
3526
+ else if (key === "style" && typeof value === "string") {
3527
+ element.setAttribute("style", value);
3528
+ }
3529
+ else if (key === "htmlFor" && typeof value === "string") {
3530
+ // Use setAttribute for htmlFor to ensure it works correctly
3531
+ element.setAttribute("for", value);
3532
+ }
3533
+ else if (key.startsWith("aria-") ||
3534
+ key === "role" ||
3535
+ key === "tabIndex" ||
3536
+ key === "autocomplete" ||
3537
+ key === "noValidate") {
3538
+ // Handle ARIA attributes and other special attributes
3539
+ if (key === "tabIndex") {
3540
+ element.tabIndex = value;
3541
+ }
3542
+ else if (key === "noValidate") {
3543
+ element.noValidate = value;
3544
+ }
3545
+ else {
3546
+ element.setAttribute(key, String(value));
3547
+ }
3548
+ }
3549
+ else if (key !== "style" && value !== undefined) {
3550
+ element[key] = value;
3551
+ }
3552
+ });
3553
+ return element;
3554
+ }
3555
+ /**
3556
+ * Parse style object or string
3557
+ */
3558
+ parseStyle(style) {
3559
+ if (!style)
3560
+ return "";
3561
+ if (typeof style === "string")
3562
+ return style;
3563
+ return Object.entries(style)
3564
+ .filter(([_, value]) => value !== undefined && value !== null)
3565
+ .map(([key, value]) => {
3566
+ const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
3567
+ return `${cssKey}: ${value}`;
3568
+ })
3569
+ .join("; ");
3570
+ }
3571
+ /**
3572
+ * Create email icon SVG
3573
+ */
3574
+ createEmailIcon() {
3575
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3576
+ svg.setAttribute("class", "onboarding-input-icon");
3577
+ svg.setAttribute("width", "18");
3578
+ svg.setAttribute("height", "18");
3579
+ svg.setAttribute("viewBox", "0 0 17 17");
3580
+ svg.setAttribute("fill", "currentColor");
3581
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3582
+ path.setAttribute("d", "M0 2v13h17v-13h-17zM8.494 9.817l-6.896-6.817h13.82l-6.924 6.817zM5.755 8.516l-4.755 4.682v-9.383l4.755 4.701zM6.466 9.219l2.026 2.003 1.996-1.966 4.8 4.744h-13.677l4.855-4.781zM11.201 8.555l4.799-4.725v9.467l-4.799-4.742z");
3583
+ svg.appendChild(path);
3584
+ return svg;
3585
+ }
3586
+ /**
3587
+ * Create Google icon SVG
3588
+ */
3589
+ createGoogleIcon() {
3590
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3591
+ svg.setAttribute("class", "onboarding-google-icon");
3592
+ svg.setAttribute("width", "20");
3593
+ svg.setAttribute("height", "20");
3594
+ svg.setAttribute("viewBox", "0 0 20 20");
3595
+ svg.setAttribute("fill", "none");
3596
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
3597
+ const paths = [
3598
+ {
3599
+ d: "M18.1712 8.36792H17.5V8.33333H10V11.6667H14.7096C14.2225 13.1071 13.2408 14.2854 12.0125 15.0125L14.9696 17.3458C16.7792 15.6958 17.9167 13.3958 17.9167 10.8333C17.9167 10.275 17.8708 9.72917 17.7833 9.20417L18.1712 8.36792Z",
3600
+ fill: "#4285F4",
3601
+ },
3602
+ {
3603
+ d: "M10 18.3333C12.5542 18.3333 14.7208 17.4708 16.3042 16.0125L13.3471 13.6792C12.5042 14.2542 11.4042 14.5833 10 14.5833C7.52083 14.5833 5.39583 12.9042 4.61667 10.6917L1.57083 13.0292C3.13333 16.1 6.32083 18.3333 10 18.3333Z",
3604
+ fill: "#34A853",
3605
+ },
3606
+ {
3607
+ d: "M4.61667 10.6917C4.39583 10.0708 4.27083 9.40417 4.27083 8.70833C4.27083 8.0125 4.39583 7.34583 4.61667 6.72417L1.57083 4.3875C0.9375 5.66667 0.583336 7.10833 0.583336 8.70833C0.583336 10.3083 0.9375 11.75 1.57083 13.0292L4.61667 10.6917Z",
3608
+ fill: "#FBBC05",
3609
+ },
3610
+ {
3611
+ d: "M10 2.91667C11.5125 2.91667 12.8708 3.47083 13.9292 4.42917L16.375 2C14.7208 0.483333 12.5542 -0.000165871 10 -0.000165871C6.32083 -0.000165871 3.13333 2.23333 1.57083 5.30417L4.61667 7.64167C5.39583 5.42917 7.52083 3.75 10 3.75V2.91667Z",
3612
+ fill: "#EA4335",
3613
+ },
3614
+ ];
3615
+ paths.forEach((pathData) => {
3616
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3617
+ path.setAttribute("d", pathData.d);
3618
+ path.setAttribute("fill", pathData.fill);
3619
+ svg.appendChild(path);
3620
+ });
3621
+ return svg;
3622
+ }
3623
+ /**
3624
+ * Create right arrow icon SVG
3625
+ */
3626
+ createArrowIcon() {
3627
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3628
+ svg.setAttribute("class", "onboarding-input-arrow-icon");
3629
+ svg.setAttribute("width", "20");
3630
+ svg.setAttribute("height", "20");
3631
+ svg.setAttribute("viewBox", "0 0 20 20");
3632
+ svg.setAttribute("fill", "none");
3633
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
3634
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3635
+ path.setAttribute("d", "M7.5 15L12.5 10L7.5 5");
3636
+ path.setAttribute("stroke", "currentColor");
3637
+ path.setAttribute("stroke-width", "2");
3638
+ path.setAttribute("stroke-linecap", "round");
3639
+ path.setAttribute("stroke-linejoin", "round");
3640
+ svg.appendChild(path);
3641
+ return svg;
3642
+ }
3643
+ /**
3644
+ * Create passkey icon SVG
3645
+ */
3646
+ createPasskeyIcon() {
3647
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3648
+ svg.setAttribute("class", "onboarding-passkey-icon");
3649
+ svg.setAttribute("width", "20");
3650
+ svg.setAttribute("height", "20");
3651
+ svg.setAttribute("viewBox", "0 0 24 24");
3652
+ svg.setAttribute("fill", "none");
3653
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
3654
+ svg.setAttribute("aria-hidden", "true");
3655
+ // Passkey icon (user with key)
3656
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3657
+ path.setAttribute("d", "M3.682 19.338v-2.102c0-.476.127-.915.382-1.315.255-.4.603-.708 1.042-.926a14.89 14.89 0 0 1 2.939-1.067c.982-.24 1.966-.36 2.953-.36.352 0 .708.018 1.066.054.358.035.712.084 1.062.147-.015.831.164 1.613.537 2.347a4.882 4.882 0 0 0 1.595 1.822v1.4H3.682Zm15.003 2.713-1.303-1.303v-4.029a2.976 2.976 0 0 1-1.564-1.08 2.944 2.944 0 0 1-.608-1.832c0-.841.297-1.557.891-2.147a2.944 2.944 0 0 1 2.154-.886c.837 0 1.552.295 2.145.886.594.59.89 1.306.89 2.148 0 .656-.182 1.238-.546 1.743a3.018 3.018 0 0 1-1.404 1.084l1.073 1.072-1.294 1.31 1.294 1.301-1.728 1.733ZM11 12.006c-.94 0-1.742-.333-2.405-.999a3.28 3.28 0 0 1-.994-2.4c0-.94.331-1.743.994-2.405A3.276 3.276 0 0 1 11 5.208c.94 0 1.742.331 2.405.994.663.662.994 1.463.994 2.4 0 .939-.331 1.74-.994 2.406a3.27 3.27 0 0 1-2.405.998Zm7.252 2.235a.833.833 0 0 0 .612-.255.842.842 0 0 0 .255-.617.834.834 0 0 0-.253-.614.835.835 0 0 0-.612-.252.85.85 0 0 0-.616.251.823.823 0 0 0-.257.612c0 .24.085.447.257.618a.838.838 0 0 0 .614.257Z");
3658
+ // Set icon color based on theme: white for dark theme, black for light theme
3659
+ const iconColor = this.config.theme === "dark" ? "#ffffff" : "#000000";
3660
+ path.setAttribute("fill", iconColor);
3661
+ svg.appendChild(path);
3662
+ return svg;
3663
+ }
3664
+ /**
3665
+ * Create X (Twitter) icon SVG
3666
+ */
3667
+ createTwitterIcon() {
3668
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3669
+ svg.setAttribute("class", "onboarding-social-icon");
3670
+ svg.setAttribute("width", "20");
3671
+ svg.setAttribute("height", "20");
3672
+ svg.setAttribute("viewBox", "0 0 24 24");
3673
+ svg.setAttribute("fill", "none");
3674
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
3675
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3676
+ path.setAttribute("d", "M4 3h4.6l3.09 4.1L14.5 3H20l-5.63 7.08L20.4 21H15.8l-3.5-4.65L8.3 21H2.8l6-7.67L4 3Zm2.04 1.2L9.7 9l.1.12L4.97 19h1.79l4.04-5.39L14.78 19h1.8l-5.02-6.55-.05-.06L17.97 4.2h-1.8l-3.52 4.62L9 4.2H6.04Z");
3677
+ path.setAttribute("fill", "currentColor");
3678
+ svg.appendChild(path);
3679
+ return svg;
3680
+ }
3681
+ /**
3682
+ * Create Discord icon SVG
3683
+ */
3684
+ createDiscordIcon() {
3685
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3686
+ svg.setAttribute("class", "onboarding-social-icon");
3687
+ svg.setAttribute("width", "20");
3688
+ svg.setAttribute("height", "20");
3689
+ svg.setAttribute("viewBox", "0 0 24 24");
3690
+ svg.setAttribute("fill", "none");
3691
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
3692
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3693
+ path.setAttribute("d", "M20.317 4.37a16.75 16.75 0 0 0-4.091-1.272.06.06 0 0 0-.063.03 11.651 11.651 0 0 0-.512 1.054 15.93 15.93 0 0 0-4.813 0 10.69 10.69 0 0 0-.522-1.054.06.06 0 0 0-.063-.03 16.742 16.742 0 0 0-4.092 1.272.055.055 0 0 0-.025.021C2.84 8.167 2.18 11.875 2.46 15.547a.066.066 0 0 0 .025.045 16.86 16.86 0 0 0 4.98 2.52.06.06 0 0 0 .066-.02c.384-.525.726-1.08 1.02-1.66a.06.06 0 0 0-.033-.083 11.117 11.117 0 0 1-1.593-.765.06.06 0 0 1-.006-.1c.107-.08.214-.162.316-.245a.06.06 0 0 1 .061-.008 11.65 11.65 0 0 0 10.104 0 .06.06 0 0 1 .062.007c.103.083.21.166.316.246a.06.06 0 0 1-.005.1 10.859 10.859 0 0 1-1.594.764.06.06 0 0 0-.033.084 9.255 9.255 0 0 0 1.02 1.659.06.06 0 0 0 .065.02 16.824 16.824 0 0 0 4.982-2.52.06.06 0 0 0 .024-.044c.416-5.09-.698-8.76-2.89-11.156a.048.048 0 0 0-.024-.02ZM9.68 13.348c-.996 0-1.812-.91-1.812-2.03 0-1.12.805-2.031 1.812-2.031 1.016 0 1.822.922 1.812 2.031 0 1.12-.805 2.03-1.812 2.03Zm4.64 0c-.996 0-1.812-.91-1.812-2.03 0-1.12.805-2.031 1.812-2.031 1.016 0 1.822.922 1.812 2.031 0 1.12-.796 2.03-1.812 2.03Z");
3694
+ path.setAttribute("fill", "currentColor");
3695
+ svg.appendChild(path);
3696
+ return svg;
3697
+ }
3698
+ /**
3699
+ * Handle passkey login
3700
+ */
3701
+ async handlePasskeyLogin() {
3702
+ if (this.loading)
3703
+ return;
3704
+ try {
3705
+ this.setLoading(true, "passkey");
3706
+ this.hidePasskeyError();
3707
+ if (this.config.onPasskeyLogin) {
3708
+ await this.config.onPasskeyLogin();
3709
+ // Call onLoginSuccess to close modal and update state
3710
+ if (this.config.onLoginSuccess) {
3711
+ this.config.onLoginSuccess({ user: null });
3712
+ }
3713
+ }
3714
+ else {
3715
+ throw new Error("Passkey login handler not configured");
3716
+ }
3717
+ }
3718
+ catch (error) {
3719
+ this.showPasskeyError(error instanceof Error ? error.message : "Failed to login with passkey");
3720
+ if (this.config.onLoginError) {
3721
+ this.config.onLoginError(error instanceof Error
3722
+ ? error
3723
+ : new Error("Failed to login with passkey"));
3724
+ }
3725
+ }
3726
+ finally {
3727
+ this.setLoading(false);
3728
+ }
3729
+ }
3730
+ /**
3731
+ * Handle passkey signup
3732
+ */
3733
+ async handlePasskeySignup() {
3734
+ if (this.loading)
3735
+ return;
3736
+ try {
3737
+ this.setLoading(true, "passkey");
3738
+ this.hidePasskeyError();
3739
+ if (this.config.onPasskeySignup) {
3740
+ await this.config.onPasskeySignup();
3741
+ // Call onLoginSuccess to close modal and update state
3742
+ if (this.config.onLoginSuccess) {
3743
+ this.config.onLoginSuccess({ user: null });
3744
+ }
3745
+ }
3746
+ else {
3747
+ throw new Error("Passkey signup handler not configured");
3748
+ }
3749
+ }
3750
+ catch (error) {
3751
+ this.showPasskeyError(error instanceof Error ? error.message : "Failed to signup with passkey");
3752
+ if (this.config.onLoginError) {
3753
+ this.config.onLoginError(error instanceof Error
3754
+ ? error
3755
+ : new Error("Failed to signup with passkey"));
3756
+ }
3757
+ }
3758
+ finally {
3759
+ this.setLoading(false);
3760
+ }
3761
+ }
3762
+ /**
3763
+ * Show passkey-specific error near the passkey controls
3764
+ */
3765
+ showPasskeyError(message) {
3766
+ if (!this.passkeyErrorElement)
3767
+ return;
3768
+ this.passkeyErrorElement.textContent = message;
3769
+ this.passkeyErrorElement.style.display = "block";
3770
+ }
3771
+ /**
3772
+ * Hide passkey-specific error
3773
+ */
3774
+ hidePasskeyError() {
3775
+ if (!this.passkeyErrorElement)
3776
+ return;
3777
+ this.passkeyErrorElement.textContent = "";
3778
+ this.passkeyErrorElement.style.display = "none";
3779
+ }
3780
+ }
3781
+ export default OnboardingUIWeb;
3782
+ //# sourceMappingURL=OnboardingUIWeb.js.map