@authon/js 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -22,6 +22,7 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Authon: () => Authon,
24
24
  AuthonMfaRequiredError: () => AuthonMfaRequiredError,
25
+ ProfileRenderer: () => ProfileRenderer,
25
26
  generateQrSvg: () => generateQrSvg,
26
27
  getProviderButtonConfig: () => getProviderButtonConfig,
27
28
  getStrings: () => getStrings,
@@ -120,7 +121,13 @@ var translations = {
120
121
  magicLink: "Continue with Magic Link",
121
122
  passkey: "Sign in with Passkey",
122
123
  securedBy: "Secured by",
123
- backToSignIn: "Back to sign in"
124
+ backToSignIn: "Back to sign in",
125
+ profile: "Profile",
126
+ editProfile: "Edit Profile",
127
+ displayName: "Display Name",
128
+ save: "Save",
129
+ signOut: "Sign Out",
130
+ sessions: "Active Sessions"
124
131
  },
125
132
  ko: {
126
133
  welcomeBack: "\uB2E4\uC2DC \uC624\uC2E0 \uAC78 \uD658\uC601\uD569\uB2C8\uB2E4",
@@ -138,7 +145,13 @@ var translations = {
138
145
  magicLink: "\uB9E4\uC9C1 \uB9C1\uD06C\uB85C \uACC4\uC18D\uD558\uAE30",
139
146
  passkey: "\uD328\uC2A4\uD0A4\uB85C \uB85C\uADF8\uC778",
140
147
  securedBy: "\uBCF4\uC548 \uC81C\uACF5",
141
- backToSignIn: "\uB85C\uADF8\uC778\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30"
148
+ backToSignIn: "\uB85C\uADF8\uC778\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30",
149
+ profile: "\uD504\uB85C\uD544",
150
+ editProfile: "\uD504\uB85C\uD544 \uD3B8\uC9D1",
151
+ displayName: "\uD45C\uC2DC \uC774\uB984",
152
+ save: "\uC800\uC7A5",
153
+ signOut: "\uB85C\uADF8\uC544\uC6C3",
154
+ sessions: "\uD65C\uC131 \uC138\uC158"
142
155
  },
143
156
  ja: {
144
157
  welcomeBack: "\u304A\u304B\u3048\u308A\u306A\u3055\u3044",
@@ -156,7 +169,13 @@ var translations = {
156
169
  magicLink: "\u30DE\u30B8\u30C3\u30AF\u30EA\u30F3\u30AF\u3067\u7D9A\u884C",
157
170
  passkey: "\u30D1\u30B9\u30AD\u30FC\u3067\u30ED\u30B0\u30A4\u30F3",
158
171
  securedBy: "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u63D0\u4F9B",
159
- backToSignIn: "\u30ED\u30B0\u30A4\u30F3\u306B\u623B\u308B"
172
+ backToSignIn: "\u30ED\u30B0\u30A4\u30F3\u306B\u623B\u308B",
173
+ profile: "\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB",
174
+ editProfile: "\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u7DE8\u96C6",
175
+ displayName: "\u8868\u793A\u540D",
176
+ save: "\u4FDD\u5B58",
177
+ signOut: "\u30B5\u30A4\u30F3\u30A2\u30A6\u30C8",
178
+ sessions: "\u30A2\u30AF\u30C6\u30A3\u30D6\u30BB\u30C3\u30B7\u30E7\u30F3"
160
179
  },
161
180
  "zh-CN": {
162
181
  welcomeBack: "\u6B22\u8FCE\u56DE\u6765",
@@ -174,7 +193,13 @@ var translations = {
174
193
  magicLink: "\u4F7F\u7528\u9B54\u6CD5\u94FE\u63A5\u7EE7\u7EED",
175
194
  passkey: "\u4F7F\u7528\u901A\u884C\u5BC6\u94A5\u767B\u5F55",
176
195
  securedBy: "\u5B89\u5168\u4FDD\u969C",
177
- backToSignIn: "\u8FD4\u56DE\u767B\u5F55"
196
+ backToSignIn: "\u8FD4\u56DE\u767B\u5F55",
197
+ profile: "\u4E2A\u4EBA\u8D44\u6599",
198
+ editProfile: "\u7F16\u8F91\u8D44\u6599",
199
+ displayName: "\u663E\u793A\u540D\u79F0",
200
+ save: "\u4FDD\u5B58",
201
+ signOut: "\u9000\u51FA\u767B\u5F55",
202
+ sessions: "\u6D3B\u8DC3\u4F1A\u8BDD"
178
203
  },
179
204
  "zh-TW": {
180
205
  welcomeBack: "\u6B61\u8FCE\u56DE\u4F86",
@@ -192,7 +217,13 @@ var translations = {
192
217
  magicLink: "\u4F7F\u7528\u9B54\u6CD5\u9023\u7D50\u7E7C\u7E8C",
193
218
  passkey: "\u4F7F\u7528\u901A\u884C\u91D1\u9470\u767B\u5165",
194
219
  securedBy: "\u5B89\u5168\u4FDD\u969C",
195
- backToSignIn: "\u8FD4\u56DE\u767B\u5165"
220
+ backToSignIn: "\u8FD4\u56DE\u767B\u5165",
221
+ profile: "\u500B\u4EBA\u8CC7\u6599",
222
+ editProfile: "\u7DE8\u8F2F\u8CC7\u6599",
223
+ displayName: "\u986F\u793A\u540D\u7A31",
224
+ save: "\u5132\u5B58",
225
+ signOut: "\u767B\u51FA",
226
+ sessions: "\u6D3B\u8E8D\u5DE5\u4F5C\u968E\u6BB5"
196
227
  },
197
228
  "pt-BR": {
198
229
  welcomeBack: "Bem-vindo de volta",
@@ -210,7 +241,13 @@ var translations = {
210
241
  magicLink: "Continuar com Magic Link",
211
242
  passkey: "Entrar com Passkey",
212
243
  securedBy: "Protegido por",
213
- backToSignIn: "Voltar ao login"
244
+ backToSignIn: "Voltar ao login",
245
+ profile: "Perfil",
246
+ editProfile: "Editar perfil",
247
+ displayName: "Nome de exibicao",
248
+ save: "Salvar",
249
+ signOut: "Sair",
250
+ sessions: "Sessoes ativas"
214
251
  },
215
252
  es: {
216
253
  welcomeBack: "Bienvenido de nuevo",
@@ -228,7 +265,13 @@ var translations = {
228
265
  magicLink: "Continuar con Magic Link",
229
266
  passkey: "Iniciar con Passkey",
230
267
  securedBy: "Protegido por",
231
- backToSignIn: "Volver al inicio de sesion"
268
+ backToSignIn: "Volver al inicio de sesion",
269
+ profile: "Perfil",
270
+ editProfile: "Editar perfil",
271
+ displayName: "Nombre de visualizacion",
272
+ save: "Guardar",
273
+ signOut: "Cerrar sesion",
274
+ sessions: "Sesiones activas"
232
275
  },
233
276
  de: {
234
277
  welcomeBack: "Willkommen zuruck",
@@ -246,7 +289,13 @@ var translations = {
246
289
  magicLink: "Weiter mit Magic Link",
247
290
  passkey: "Mit Passkey anmelden",
248
291
  securedBy: "Gesichert durch",
249
- backToSignIn: "Zuruck zur Anmeldung"
292
+ backToSignIn: "Zuruck zur Anmeldung",
293
+ profile: "Profil",
294
+ editProfile: "Profil bearbeiten",
295
+ displayName: "Anzeigename",
296
+ save: "Speichern",
297
+ signOut: "Abmelden",
298
+ sessions: "Aktive Sitzungen"
250
299
  },
251
300
  fr: {
252
301
  welcomeBack: "Bon retour",
@@ -264,7 +313,13 @@ var translations = {
264
313
  magicLink: "Continuer avec Magic Link",
265
314
  passkey: "Se connecter avec Passkey",
266
315
  securedBy: "Securise par",
267
- backToSignIn: "Retour a la connexion"
316
+ backToSignIn: "Retour a la connexion",
317
+ profile: "Profil",
318
+ editProfile: "Modifier le profil",
319
+ displayName: "Nom d'affichage",
320
+ save: "Enregistrer",
321
+ signOut: "Se deconnecter",
322
+ sessions: "Sessions actives"
268
323
  },
269
324
  hi: {
270
325
  welcomeBack: "\u0935\u093E\u092A\u0938\u0940 \u092A\u0930 \u0938\u094D\u0935\u093E\u0917\u0924 \u0939\u0948",
@@ -282,7 +337,13 @@ var translations = {
282
337
  magicLink: "\u092E\u0948\u091C\u093F\u0915 \u0932\u093F\u0902\u0915 \u0938\u0947 \u091C\u093E\u0930\u0940 \u0930\u0916\u0947\u0902",
283
338
  passkey: "\u092A\u093E\u0938\u0915\u0940 \u0938\u0947 \u0938\u093E\u0907\u0928 \u0907\u0928",
284
339
  securedBy: "\u0938\u0941\u0930\u0915\u094D\u0937\u093E \u092A\u094D\u0930\u0926\u093E\u0924\u093E",
285
- backToSignIn: "\u0938\u093E\u0907\u0928 \u0907\u0928 \u092A\u0930 \u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902"
340
+ backToSignIn: "\u0938\u093E\u0907\u0928 \u0907\u0928 \u092A\u0930 \u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902",
341
+ profile: "\u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932",
342
+ editProfile: "\u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932 \u0938\u0902\u092A\u093E\u0926\u093F\u0924 \u0915\u0930\u0947\u0902",
343
+ displayName: "\u092A\u094D\u0930\u0926\u0930\u094D\u0936\u0928 \u0928\u093E\u092E",
344
+ save: "\u0938\u0939\u0947\u091C\u0947\u0902",
345
+ signOut: "\u0938\u093E\u0907\u0928 \u0906\u0909\u091F",
346
+ sessions: "\u0938\u0915\u094D\u0930\u093F\u092F \u0938\u0924\u094D\u0930"
286
347
  },
287
348
  tr: {
288
349
  welcomeBack: "Tekrar hos geldiniz",
@@ -300,7 +361,13 @@ var translations = {
300
361
  magicLink: "Magic Link ile devam et",
301
362
  passkey: "Passkey ile giris yap",
302
363
  securedBy: "Guvenlik saglayici",
303
- backToSignIn: "Girise don"
364
+ backToSignIn: "Girise don",
365
+ profile: "Profil",
366
+ editProfile: "Profili duzenle",
367
+ displayName: "Goruntu adi",
368
+ save: "Kaydet",
369
+ signOut: "Cikis yap",
370
+ sessions: "Aktif oturumlar"
304
371
  },
305
372
  id: {
306
373
  welcomeBack: "Selamat datang kembali",
@@ -318,7 +385,13 @@ var translations = {
318
385
  magicLink: "Lanjutkan dengan Magic Link",
319
386
  passkey: "Masuk dengan Passkey",
320
387
  securedBy: "Diamankan oleh",
321
- backToSignIn: "Kembali ke login"
388
+ backToSignIn: "Kembali ke login",
389
+ profile: "Profil",
390
+ editProfile: "Edit profil",
391
+ displayName: "Nama tampilan",
392
+ save: "Simpan",
393
+ signOut: "Keluar",
394
+ sessions: "Sesi aktif"
322
395
  },
323
396
  vi: {
324
397
  welcomeBack: "Chao mung tro lai",
@@ -336,7 +409,13 @@ var translations = {
336
409
  magicLink: "Tiep tuc voi Magic Link",
337
410
  passkey: "Dang nhap voi Passkey",
338
411
  securedBy: "Bao mat boi",
339
- backToSignIn: "Quay lai dang nhap"
412
+ backToSignIn: "Quay lai dang nhap",
413
+ profile: "Ho so",
414
+ editProfile: "Chinh sua ho so",
415
+ displayName: "Ten hien thi",
416
+ save: "Luu",
417
+ signOut: "Dang xuat",
418
+ sessions: "Phien dang nhap hoat dong"
340
419
  },
341
420
  th: {
342
421
  welcomeBack: "\u0E22\u0E34\u0E19\u0E14\u0E35\u0E15\u0E49\u0E2D\u0E19\u0E23\u0E31\u0E1A\u0E01\u0E25\u0E31\u0E1A",
@@ -354,7 +433,13 @@ var translations = {
354
433
  magicLink: "\u0E14\u0E33\u0E40\u0E19\u0E34\u0E19\u0E01\u0E32\u0E23\u0E15\u0E48\u0E2D\u0E14\u0E49\u0E27\u0E22 Magic Link",
355
434
  passkey: "\u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A\u0E14\u0E49\u0E27\u0E22 Passkey",
356
435
  securedBy: "\u0E23\u0E31\u0E01\u0E29\u0E32\u0E04\u0E27\u0E32\u0E21\u0E1B\u0E25\u0E2D\u0E14\u0E20\u0E31\u0E22\u0E42\u0E14\u0E22",
357
- backToSignIn: "\u0E01\u0E25\u0E31\u0E1A\u0E44\u0E1B\u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A"
436
+ backToSignIn: "\u0E01\u0E25\u0E31\u0E1A\u0E44\u0E1B\u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A",
437
+ profile: "\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C",
438
+ editProfile: "\u0E41\u0E01\u0E49\u0E44\u0E02\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C",
439
+ displayName: "\u0E0A\u0E37\u0E48\u0E2D\u0E17\u0E35\u0E48\u0E41\u0E2A\u0E14\u0E07",
440
+ save: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01",
441
+ signOut: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A",
442
+ sessions: "\u0E40\u0E0B\u0E2A\u0E0A\u0E31\u0E19\u0E17\u0E35\u0E48\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19\u0E2D\u0E22\u0E39\u0E48"
358
443
  },
359
444
  ru: {
360
445
  welcomeBack: "\u0421 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0438\u0435\u043C",
@@ -372,7 +457,13 @@ var translations = {
372
457
  magicLink: "\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u0441 Magic Link",
373
458
  passkey: "\u0412\u043E\u0439\u0442\u0438 \u0441 Passkey",
374
459
  securedBy: "\u0417\u0430\u0449\u0438\u0449\u0435\u043D\u043E",
375
- backToSignIn: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u043A \u0432\u0445\u043E\u0434\u0443"
460
+ backToSignIn: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u043A \u0432\u0445\u043E\u0434\u0443",
461
+ profile: "\u041F\u0440\u043E\u0444\u0438\u043B\u044C",
462
+ editProfile: "\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C",
463
+ displayName: "\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u0438\u043C\u044F",
464
+ save: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C",
465
+ signOut: "\u0412\u044B\u0439\u0442\u0438",
466
+ sessions: "\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0441\u0435\u0430\u043D\u0441\u044B"
376
467
  },
377
468
  it: {
378
469
  welcomeBack: "Bentornato",
@@ -390,7 +481,13 @@ var translations = {
390
481
  magicLink: "Continua con Magic Link",
391
482
  passkey: "Accedi con Passkey",
392
483
  securedBy: "Protetto da",
393
- backToSignIn: "Torna all'accesso"
484
+ backToSignIn: "Torna all'accesso",
485
+ profile: "Profilo",
486
+ editProfile: "Modifica profilo",
487
+ displayName: "Nome visualizzato",
488
+ save: "Salva",
489
+ signOut: "Esci",
490
+ sessions: "Sessioni attive"
394
491
  },
395
492
  pl: {
396
493
  welcomeBack: "Witaj ponownie",
@@ -408,7 +505,13 @@ var translations = {
408
505
  magicLink: "Kontynuuj z Magic Link",
409
506
  passkey: "Zaloguj sie z Passkey",
410
507
  securedBy: "Zabezpieczone przez",
411
- backToSignIn: "Powrot do logowania"
508
+ backToSignIn: "Powrot do logowania",
509
+ profile: "Profil",
510
+ editProfile: "Edytuj profil",
511
+ displayName: "Nazwa wyswietlana",
512
+ save: "Zapisz",
513
+ signOut: "Wyloguj sie",
514
+ sessions: "Aktywne sesje"
412
515
  },
413
516
  nl: {
414
517
  welcomeBack: "Welkom terug",
@@ -426,7 +529,13 @@ var translations = {
426
529
  magicLink: "Doorgaan met Magic Link",
427
530
  passkey: "Inloggen met Passkey",
428
531
  securedBy: "Beveiligd door",
429
- backToSignIn: "Terug naar inloggen"
532
+ backToSignIn: "Terug naar inloggen",
533
+ profile: "Profiel",
534
+ editProfile: "Profiel bewerken",
535
+ displayName: "Weergavenaam",
536
+ save: "Opslaan",
537
+ signOut: "Uitloggen",
538
+ sessions: "Actieve sessies"
430
539
  },
431
540
  ar: {
432
541
  welcomeBack: "\u0645\u0631\u062D\u0628\u064B\u0627 \u0628\u0639\u0648\u062F\u062A\u0643",
@@ -444,7 +553,13 @@ var translations = {
444
553
  magicLink: "\u0627\u0644\u0645\u062A\u0627\u0628\u0639\u0629 \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 Magic Link",
445
554
  passkey: "\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644 \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 Passkey",
446
555
  securedBy: "\u0645\u062D\u0645\u064A \u0628\u0648\u0627\u0633\u0637\u0629",
447
- backToSignIn: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644"
556
+ backToSignIn: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644",
557
+ profile: "\u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0634\u062E\u0635\u064A",
558
+ editProfile: "\u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0634\u062E\u0635\u064A",
559
+ displayName: "\u0627\u0644\u0627\u0633\u0645 \u0627\u0644\u0645\u0639\u0631\u0648\u0636",
560
+ save: "\u062D\u0641\u0638",
561
+ signOut: "\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062E\u0631\u0648\u062C",
562
+ sessions: "\u0627\u0644\u062C\u0644\u0633\u0627\u062A \u0627\u0644\u0646\u0634\u0637\u0629"
448
563
  },
449
564
  sv: {
450
565
  welcomeBack: "Valkommen tillbaka",
@@ -462,7 +577,13 @@ var translations = {
462
577
  magicLink: "Fortsatt med Magic Link",
463
578
  passkey: "Logga in med Passkey",
464
579
  securedBy: "Sakrad av",
465
- backToSignIn: "Tillbaka till inloggning"
580
+ backToSignIn: "Tillbaka till inloggning",
581
+ profile: "Profil",
582
+ editProfile: "Redigera profil",
583
+ displayName: "Visningsnamn",
584
+ save: "Spara",
585
+ signOut: "Logga ut",
586
+ sessions: "Aktiva sessioner"
466
587
  },
467
588
  uk: {
468
589
  welcomeBack: "\u0417 \u043F\u043E\u0432\u0435\u0440\u043D\u0435\u043D\u043D\u044F\u043C",
@@ -480,7 +601,13 @@ var translations = {
480
601
  magicLink: "\u041F\u0440\u043E\u0434\u043E\u0432\u0436\u0438\u0442\u0438 \u0437 Magic Link",
481
602
  passkey: "\u0423\u0432\u0456\u0439\u0442\u0438 \u0437 Passkey",
482
603
  securedBy: "\u0417\u0430\u0445\u0438\u0449\u0435\u043D\u043E",
483
- backToSignIn: "\u041F\u043E\u0432\u0435\u0440\u043D\u0443\u0442\u0438\u0441\u044F \u0434\u043E \u0432\u0445\u043E\u0434\u0443"
604
+ backToSignIn: "\u041F\u043E\u0432\u0435\u0440\u043D\u0443\u0442\u0438\u0441\u044F \u0434\u043E \u0432\u0445\u043E\u0434\u0443",
605
+ profile: "\u041F\u0440\u043E\u0444\u0456\u043B\u044C",
606
+ editProfile: "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u043F\u0440\u043E\u0444\u0456\u043B\u044C",
607
+ displayName: "\u0412\u0456\u0434\u043E\u0431\u0440\u0430\u0436\u0443\u0432\u0430\u043D\u0435 \u0456\u043C'\u044F",
608
+ save: "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438",
609
+ signOut: "\u0412\u0438\u0439\u0442\u0438",
610
+ sessions: "\u0410\u043A\u0442\u0438\u0432\u043D\u0456 \u0441\u0435\u0430\u043D\u0441\u0438"
484
611
  }
485
612
  };
486
613
  function getStrings(locale) {
@@ -710,6 +837,53 @@ var ModalRenderer = class {
710
837
  if (!this.shadowRoot) return;
711
838
  this.shadowRoot.getElementById("authon-error-msg")?.remove();
712
839
  }
840
+ showVerificationInput(email, onVerify, onResend) {
841
+ if (!this.shadowRoot) return;
842
+ const inner = this.shadowRoot.querySelector(".modal-inner");
843
+ if (!inner) return;
844
+ inner.innerHTML = `
845
+ <div style="text-align:center;padding:8px 0">
846
+ <div style="width:48px;height:48px;border-radius:12px;background:color-mix(in srgb, var(--authon-primary-start) 15%, transparent);display:flex;align-items:center;justify-content:center;margin:0 auto 16px">
847
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--authon-primary-start)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
848
+ </div>
849
+ <h2 class="title" style="font-size:20px;margin-bottom:4px">${this.t.welcomeBack.includes("Welcome") ? "Check your email" : this.t.welcomeBack}</h2>
850
+ <p style="font-size:13px;color:var(--authon-muted);margin-bottom:20px">${email}</p>
851
+ </div>
852
+ <div class="email-form" id="verify-form">
853
+ <input type="text" id="verify-code" class="input" placeholder="000000" maxlength="6" inputmode="numeric" autocomplete="one-time-code" style="text-align:center;font-size:20px;letter-spacing:0.2em;font-family:ui-monospace,monospace" />
854
+ <button type="button" id="verify-submit" class="submit-btn">${this.t.signIn}</button>
855
+ </div>
856
+ <p style="text-align:center;margin-top:12px;font-size:12px;color:var(--authon-dim)">
857
+ <a href="#" id="resend-link" style="color:var(--authon-primary-start);text-decoration:none;font-weight:500">${this.t.backToSignIn.includes("Back") ? "Resend code" : this.t.backToSignIn}</a>
858
+ </p>
859
+ `;
860
+ const codeInput = this.shadowRoot.getElementById("verify-code");
861
+ const submitBtn = this.shadowRoot.getElementById("verify-submit");
862
+ const resendLink = this.shadowRoot.getElementById("resend-link");
863
+ codeInput?.focus();
864
+ codeInput?.addEventListener("input", () => {
865
+ codeInput.value = codeInput.value.replace(/\D/g, "").slice(0, 6);
866
+ });
867
+ submitBtn?.addEventListener("click", async () => {
868
+ const code = codeInput?.value?.trim();
869
+ if (!code || code.length < 6) return;
870
+ submitBtn.textContent = "...";
871
+ submitBtn.disabled = true;
872
+ await onVerify(code);
873
+ });
874
+ codeInput?.addEventListener("keydown", (e) => {
875
+ if (e.key === "Enter") submitBtn?.click();
876
+ });
877
+ resendLink?.addEventListener("click", async (e) => {
878
+ e.preventDefault();
879
+ resendLink.textContent = "...";
880
+ await onResend();
881
+ resendLink.textContent = "Sent!";
882
+ setTimeout(() => {
883
+ resendLink.textContent = "Resend code";
884
+ }, 2e3);
885
+ });
886
+ }
713
887
  showLoading() {
714
888
  if (!this.shadowRoot) return;
715
889
  this.hideLoading();
@@ -1604,6 +1778,676 @@ var ModalRenderer = class {
1604
1778
  }
1605
1779
  };
1606
1780
 
1781
+ // src/profile.ts
1782
+ function hexToRgba2(hex, alpha) {
1783
+ const h = hex.replace("#", "");
1784
+ const r = parseInt(h.substring(0, 2), 16);
1785
+ const g = parseInt(h.substring(2, 4), 16);
1786
+ const b = parseInt(h.substring(4, 6), 16);
1787
+ return `rgba(${r},${g},${b},${alpha})`;
1788
+ }
1789
+ function formatRelativeTime(dateStr) {
1790
+ if (!dateStr) return "\u2014";
1791
+ const diff = Date.now() - new Date(dateStr).getTime();
1792
+ const minutes = Math.floor(diff / 6e4);
1793
+ if (minutes < 1) return "just now";
1794
+ if (minutes < 60) return `${minutes}m ago`;
1795
+ const hours = Math.floor(minutes / 60);
1796
+ if (hours < 24) return `${hours}h ago`;
1797
+ const days = Math.floor(hours / 24);
1798
+ return `${days}d ago`;
1799
+ }
1800
+ function parseUserAgent(ua) {
1801
+ if (!ua) return "Unknown device";
1802
+ if (/iPhone|iPad/.test(ua)) return "iOS";
1803
+ if (/Android/.test(ua)) return "Android";
1804
+ if (/Windows/.test(ua)) return "Windows";
1805
+ if (/Macintosh|Mac OS/.test(ua)) return "macOS";
1806
+ if (/Linux/.test(ua)) return "Linux";
1807
+ return "Unknown device";
1808
+ }
1809
+ var ProfileRenderer = class {
1810
+ shadowRoot = null;
1811
+ hostElement = null;
1812
+ containerElement = null;
1813
+ containerId = null;
1814
+ mode;
1815
+ theme;
1816
+ branding;
1817
+ themeObserver = null;
1818
+ mediaQueryListener = null;
1819
+ t;
1820
+ user;
1821
+ sessions;
1822
+ isEditMode = false;
1823
+ onSave;
1824
+ onSignOut;
1825
+ onRevokeSession;
1826
+ onClose;
1827
+ escHandler = null;
1828
+ constructor(options) {
1829
+ this.mode = options.mode;
1830
+ this.theme = options.theme || "auto";
1831
+ this.branding = { ...DEFAULT_BRANDING, ...options.branding };
1832
+ this.t = getStrings(options.locale || "en");
1833
+ this.user = options.user;
1834
+ this.sessions = options.sessions || [];
1835
+ this.onSave = options.onSave;
1836
+ this.onSignOut = options.onSignOut;
1837
+ this.onRevokeSession = options.onRevokeSession;
1838
+ this.onClose = options.onClose;
1839
+ if (options.mode === "embedded" && options.containerId) {
1840
+ this.containerId = options.containerId;
1841
+ }
1842
+ }
1843
+ resolveContainerElement() {
1844
+ if (this.mode !== "embedded" || !this.containerId) return null;
1845
+ const next = document.getElementById(this.containerId);
1846
+ if (this.containerElement !== next) {
1847
+ this.hostElement?.remove();
1848
+ this.hostElement = null;
1849
+ this.shadowRoot = null;
1850
+ }
1851
+ this.containerElement = next;
1852
+ return next;
1853
+ }
1854
+ open() {
1855
+ this.resolveContainerElement();
1856
+ if (this.hostElement && !this.hostElement.isConnected) {
1857
+ this.hostElement = null;
1858
+ this.shadowRoot = null;
1859
+ }
1860
+ if (this.shadowRoot && this.hostElement) {
1861
+ this.rerender();
1862
+ } else {
1863
+ this.isEditMode = false;
1864
+ this.render();
1865
+ }
1866
+ }
1867
+ close() {
1868
+ this.stopThemeObserver();
1869
+ if (this.escHandler) {
1870
+ document.removeEventListener("keydown", this.escHandler);
1871
+ this.escHandler = null;
1872
+ }
1873
+ if (this.hostElement) {
1874
+ this.hostElement.remove();
1875
+ this.hostElement = null;
1876
+ this.shadowRoot = null;
1877
+ }
1878
+ const liveContainer = this.resolveContainerElement();
1879
+ if (liveContainer) {
1880
+ liveContainer.replaceChildren();
1881
+ }
1882
+ }
1883
+ updateUser(user) {
1884
+ this.user = user;
1885
+ if (this.shadowRoot) this.rerender();
1886
+ }
1887
+ updateSessions(sessions) {
1888
+ this.sessions = sessions;
1889
+ if (this.shadowRoot) this.rerender();
1890
+ }
1891
+ setTheme(theme) {
1892
+ this.theme = theme;
1893
+ this.updateThemeCSS();
1894
+ if (theme === "auto") {
1895
+ this.startThemeObserver();
1896
+ } else {
1897
+ this.stopThemeObserver();
1898
+ }
1899
+ }
1900
+ showSaving() {
1901
+ if (!this.shadowRoot) return;
1902
+ const btn = this.shadowRoot.getElementById("profile-save-btn");
1903
+ if (btn) {
1904
+ btn.disabled = true;
1905
+ btn.textContent = "...";
1906
+ }
1907
+ }
1908
+ showSaveError(message) {
1909
+ if (!this.shadowRoot) return;
1910
+ const btn = this.shadowRoot.getElementById("profile-save-btn");
1911
+ if (btn) {
1912
+ btn.disabled = false;
1913
+ btn.textContent = this.t.save;
1914
+ }
1915
+ this.showInlineError("profile-error", message);
1916
+ }
1917
+ showInlineError(id, message) {
1918
+ if (!this.shadowRoot) return;
1919
+ this.shadowRoot.getElementById(id)?.remove();
1920
+ const errEl = document.createElement("div");
1921
+ errEl.id = id;
1922
+ errEl.className = "error-msg";
1923
+ errEl.textContent = message;
1924
+ this.shadowRoot.querySelector(".profile-actions")?.appendChild(errEl);
1925
+ }
1926
+ updateThemeCSS() {
1927
+ if (!this.shadowRoot) return;
1928
+ const styleEl = this.shadowRoot.getElementById("authon-profile-theme-style");
1929
+ if (styleEl) styleEl.textContent = this.buildCSS();
1930
+ }
1931
+ startThemeObserver() {
1932
+ this.stopThemeObserver();
1933
+ if (typeof document === "undefined" || typeof window === "undefined") return;
1934
+ this.themeObserver = new MutationObserver(() => this.updateThemeCSS());
1935
+ this.themeObserver.observe(document.documentElement, {
1936
+ attributes: true,
1937
+ attributeFilter: ["data-theme", "class"]
1938
+ });
1939
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
1940
+ this.mediaQueryListener = () => this.updateThemeCSS();
1941
+ mq.addEventListener("change", this.mediaQueryListener);
1942
+ }
1943
+ stopThemeObserver() {
1944
+ if (this.themeObserver) {
1945
+ this.themeObserver.disconnect();
1946
+ this.themeObserver = null;
1947
+ }
1948
+ if (this.mediaQueryListener) {
1949
+ window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", this.mediaQueryListener);
1950
+ this.mediaQueryListener = null;
1951
+ }
1952
+ }
1953
+ isDark() {
1954
+ if (this.theme === "dark") return true;
1955
+ if (this.theme === "light") return false;
1956
+ if (typeof document !== "undefined") {
1957
+ const html = document.documentElement;
1958
+ if (html.classList.contains("dark") || html.getAttribute("data-theme") === "dark") return true;
1959
+ if (html.classList.contains("light") || html.getAttribute("data-theme") === "light") return false;
1960
+ }
1961
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
1962
+ }
1963
+ render() {
1964
+ const host = document.createElement("div");
1965
+ host.setAttribute("data-authon-profile", "");
1966
+ this.hostElement = host;
1967
+ if (this.mode === "popup") {
1968
+ document.body.appendChild(host);
1969
+ } else {
1970
+ const container = this.resolveContainerElement();
1971
+ if (!container) {
1972
+ this.hostElement = null;
1973
+ throw new Error(`Authon profile container "#${this.containerId}" not found`);
1974
+ }
1975
+ container.replaceChildren();
1976
+ container.appendChild(host);
1977
+ }
1978
+ this.shadowRoot = host.attachShadow({ mode: "open" });
1979
+ this.shadowRoot.innerHTML = this.buildShell();
1980
+ this.attachEvents();
1981
+ if (this.mode === "popup") {
1982
+ this.escHandler = (e) => {
1983
+ if (e.key === "Escape") {
1984
+ this.onClose();
1985
+ this.close();
1986
+ }
1987
+ };
1988
+ document.addEventListener("keydown", this.escHandler);
1989
+ }
1990
+ if (this.theme === "auto") this.startThemeObserver();
1991
+ }
1992
+ rerender() {
1993
+ if (!this.shadowRoot) return;
1994
+ const inner = this.shadowRoot.getElementById("profile-inner");
1995
+ if (!inner) return;
1996
+ inner.style.opacity = "0";
1997
+ inner.style.transform = "translateY(-4px)";
1998
+ setTimeout(() => {
1999
+ inner.innerHTML = this.buildInnerContent();
2000
+ this.attachInnerEvents();
2001
+ void inner.offsetHeight;
2002
+ inner.style.opacity = "1";
2003
+ inner.style.transform = "translateY(0)";
2004
+ }, 120);
2005
+ }
2006
+ buildShell() {
2007
+ const popupWrapper = this.mode === "popup" ? `<div class="backdrop" id="profile-backdrop"></div>` : "";
2008
+ return `
2009
+ <style id="authon-profile-theme-style">${this.buildCSS()}</style>
2010
+ ${popupWrapper}
2011
+ <div class="profile-container" role="dialog" aria-modal="true" aria-label="${this.t.profile}">
2012
+ <div id="profile-inner" class="profile-inner">
2013
+ ${this.buildInnerContent()}
2014
+ </div>
2015
+ </div>
2016
+ `;
2017
+ }
2018
+ buildInnerContent() {
2019
+ const u = this.user;
2020
+ const initials = (u.displayName || u.email || "?").split(/\s+/).map((w) => w[0]?.toUpperCase() ?? "").slice(0, 2).join("");
2021
+ const avatarHtml = u.avatarUrl ? `<img class="avatar-img" src="${u.avatarUrl}" alt="${u.displayName || ""}" />` : `<div class="avatar-placeholder">${initials}</div>`;
2022
+ const closeBtn = this.mode === "popup" ? `<button class="close-btn" id="profile-close-btn" aria-label="Close">
2023
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
2024
+ <path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
2025
+ </svg>
2026
+ </button>` : "";
2027
+ if (this.isEditMode) {
2028
+ return this.buildEditContent(avatarHtml, closeBtn);
2029
+ }
2030
+ return this.buildViewContent(avatarHtml, closeBtn, u);
2031
+ }
2032
+ buildViewContent(avatarHtml, closeBtn, u) {
2033
+ const sessionsHtml = this.sessions.length > 0 ? `<div class="section">
2034
+ <div class="section-label">${this.t.sessions}</div>
2035
+ <div class="sessions-list">
2036
+ ${this.sessions.map((s) => `
2037
+ <div class="session-item" data-session-id="${s.id}">
2038
+ <div class="session-info">
2039
+ <span class="session-device">${parseUserAgent(s.userAgent)}</span>
2040
+ <span class="session-meta">${s.ipAddress || ""} &middot; ${formatRelativeTime(s.lastActiveAt)}</span>
2041
+ </div>
2042
+ <button class="session-revoke-btn" data-session-id="${s.id}" aria-label="Revoke session">
2043
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
2044
+ <path d="M10.5 3.5L3.5 10.5M3.5 3.5l7 7" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
2045
+ </svg>
2046
+ </button>
2047
+ </div>
2048
+ `).join("")}
2049
+ </div>
2050
+ </div>` : "";
2051
+ return `
2052
+ <div class="profile-header">
2053
+ <div class="avatar-wrap">${avatarHtml}</div>
2054
+ <div class="header-text">
2055
+ <div class="display-name">${u.displayName || "\u2014"}</div>
2056
+ <div class="email">${u.email || "\u2014"}</div>
2057
+ </div>
2058
+ ${closeBtn}
2059
+ </div>
2060
+
2061
+ <div class="section">
2062
+ <div class="field-row">
2063
+ <span class="field-label">${this.t.emailAddress}</span>
2064
+ <span class="field-value readonly">${u.email || "\u2014"}</span>
2065
+ </div>
2066
+ <div class="field-row">
2067
+ <span class="field-label">${this.t.displayName}</span>
2068
+ <span class="field-value">${u.displayName || "\u2014"}</span>
2069
+ </div>
2070
+ </div>
2071
+
2072
+ ${sessionsHtml}
2073
+
2074
+ <div class="profile-actions">
2075
+ <button class="edit-btn" id="profile-edit-btn">${this.t.editProfile}</button>
2076
+ <button class="signout-btn" id="profile-signout-btn">${this.t.signOut}</button>
2077
+ </div>
2078
+ `;
2079
+ }
2080
+ buildEditContent(avatarHtml, closeBtn) {
2081
+ const u = this.user;
2082
+ return `
2083
+ <div class="profile-header">
2084
+ <div class="avatar-wrap">${avatarHtml}</div>
2085
+ <div class="header-text">
2086
+ <div class="display-name">${this.t.editProfile}</div>
2087
+ <div class="email">${u.email || "\u2014"}</div>
2088
+ </div>
2089
+ ${closeBtn}
2090
+ </div>
2091
+
2092
+ <div class="section">
2093
+ <div class="field-group">
2094
+ <label class="field-label" for="profile-displayname-input">${this.t.displayName}</label>
2095
+ <input
2096
+ id="profile-displayname-input"
2097
+ class="input"
2098
+ type="text"
2099
+ value="${u.displayName || ""}"
2100
+ placeholder="${this.t.displayName}"
2101
+ autocomplete="name"
2102
+ />
2103
+ </div>
2104
+ <div class="field-group">
2105
+ <label class="field-label" for="profile-avatar-input">Avatar URL</label>
2106
+ <input
2107
+ id="profile-avatar-input"
2108
+ class="input"
2109
+ type="url"
2110
+ value="${u.avatarUrl || ""}"
2111
+ placeholder="https://..."
2112
+ autocomplete="off"
2113
+ />
2114
+ </div>
2115
+ <div class="field-group">
2116
+ <label class="field-label">${this.t.emailAddress}</label>
2117
+ <input class="input readonly" type="email" value="${u.email || ""}" disabled />
2118
+ </div>
2119
+ </div>
2120
+
2121
+ <div class="profile-actions">
2122
+ <button class="save-btn" id="profile-save-btn">${this.t.save}</button>
2123
+ <button class="cancel-btn" id="profile-cancel-btn">Cancel</button>
2124
+ </div>
2125
+ `;
2126
+ }
2127
+ buildCSS() {
2128
+ const b = this.branding;
2129
+ const dark = this.isDark();
2130
+ const bg = dark ? b.darkBg || "#0f172a" : b.lightBg || "#ffffff";
2131
+ const text = dark ? b.darkText || "#f1f5f9" : b.lightText || "#111827";
2132
+ const mutedText = dark ? "#94a3b8" : "#6b7280";
2133
+ const dimText = dark ? "#64748b" : "#9ca3af";
2134
+ const borderColor = dark ? "#334155" : "#d1d5db";
2135
+ const dividerColor = dark ? "#334155" : "#e5e7eb";
2136
+ const inputBg = dark ? "#1e293b" : "#ffffff";
2137
+ const sectionBg = dark ? "#1e293b" : "#f9fafb";
2138
+ const sessionItemBg = dark ? "#263148" : "#f3f4f6";
2139
+ return `
2140
+ :host {
2141
+ --authon-primary-start: ${b.primaryColorStart || "#7c3aed"};
2142
+ --authon-primary-end: ${b.primaryColorEnd || "#4f46e5"};
2143
+ --authon-bg: ${bg};
2144
+ --authon-text: ${text};
2145
+ --authon-muted: ${mutedText};
2146
+ --authon-dim: ${dimText};
2147
+ --authon-border: ${borderColor};
2148
+ --authon-divider: ${dividerColor};
2149
+ --authon-input-bg: ${inputBg};
2150
+ --authon-section-bg: ${sectionBg};
2151
+ --authon-session-bg: ${sessionItemBg};
2152
+ --authon-backdrop-bg: rgba(0,0,0,${dark ? "0.7" : "0.5"});
2153
+ --authon-radius: ${b.borderRadius ?? 12}px;
2154
+ --authon-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
2155
+ font-family: var(--authon-font);
2156
+ color: var(--authon-text);
2157
+ }
2158
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2159
+
2160
+ .backdrop {
2161
+ position: fixed; inset: 0; z-index: 99998;
2162
+ background: var(--authon-backdrop-bg); backdrop-filter: blur(4px);
2163
+ animation: fadeIn 0.2s ease;
2164
+ }
2165
+
2166
+ .profile-container {
2167
+ ${this.mode === "popup" ? "position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99999; max-height: 90vh; overflow-y: auto;" : ""}
2168
+ background: var(--authon-bg);
2169
+ color: var(--authon-text);
2170
+ border: 1px solid var(--authon-border);
2171
+ border-radius: var(--authon-radius);
2172
+ padding: 24px;
2173
+ width: 380px; max-width: 100%;
2174
+ ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
2175
+ }
2176
+
2177
+ .profile-inner {
2178
+ transition: opacity 0.12s ease, transform 0.12s ease;
2179
+ }
2180
+
2181
+ .profile-header {
2182
+ display: flex;
2183
+ align-items: center;
2184
+ gap: 14px;
2185
+ margin-bottom: 20px;
2186
+ position: relative;
2187
+ }
2188
+
2189
+ .avatar-wrap { flex-shrink: 0; }
2190
+
2191
+ .avatar-img {
2192
+ width: 52px; height: 52px;
2193
+ border-radius: 50%;
2194
+ object-fit: cover;
2195
+ border: 2px solid var(--authon-border);
2196
+ }
2197
+
2198
+ .avatar-placeholder {
2199
+ width: 52px; height: 52px;
2200
+ border-radius: 50%;
2201
+ background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
2202
+ display: flex; align-items: center; justify-content: center;
2203
+ font-size: 18px; font-weight: 700; color: #fff;
2204
+ flex-shrink: 0;
2205
+ }
2206
+
2207
+ .header-text { flex: 1; min-width: 0; }
2208
+
2209
+ .display-name {
2210
+ font-size: 16px; font-weight: 600;
2211
+ color: var(--authon-text);
2212
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2213
+ }
2214
+
2215
+ .email {
2216
+ font-size: 13px; color: var(--authon-muted);
2217
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2218
+ margin-top: 2px;
2219
+ }
2220
+
2221
+ .close-btn {
2222
+ position: absolute; top: 0; right: 0;
2223
+ background: none; border: none;
2224
+ color: var(--authon-muted); cursor: pointer;
2225
+ padding: 4px; border-radius: 6px;
2226
+ display: flex; align-items: center; justify-content: center;
2227
+ transition: color 0.15s, background 0.15s;
2228
+ }
2229
+ .close-btn:hover { color: var(--authon-text); background: var(--authon-divider); }
2230
+
2231
+ .section {
2232
+ background: var(--authon-section-bg);
2233
+ border: 1px solid var(--authon-border);
2234
+ border-radius: calc(var(--authon-radius) * 0.67);
2235
+ padding: 12px 14px;
2236
+ margin-bottom: 12px;
2237
+ display: flex; flex-direction: column; gap: 10px;
2238
+ }
2239
+
2240
+ .section-label {
2241
+ font-size: 11px; font-weight: 600;
2242
+ text-transform: uppercase; letter-spacing: 0.05em;
2243
+ color: var(--authon-dim); margin-bottom: 2px;
2244
+ }
2245
+
2246
+ .field-row {
2247
+ display: flex; align-items: center; justify-content: space-between; gap: 8px;
2248
+ }
2249
+
2250
+ .field-label {
2251
+ font-size: 12px; color: var(--authon-muted); flex-shrink: 0;
2252
+ }
2253
+
2254
+ .field-value {
2255
+ font-size: 13px; color: var(--authon-text);
2256
+ text-align: right; min-width: 0;
2257
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2258
+ }
2259
+
2260
+ .field-value.readonly { color: var(--authon-dim); }
2261
+
2262
+ .field-group {
2263
+ display: flex; flex-direction: column; gap: 4px;
2264
+ }
2265
+
2266
+ .input {
2267
+ width: 100%; padding: 9px 12px;
2268
+ background: var(--authon-input-bg);
2269
+ color: var(--authon-text);
2270
+ border: 1px solid var(--authon-border);
2271
+ border-radius: calc(var(--authon-radius) * 0.5);
2272
+ font-size: 13px; font-family: var(--authon-font);
2273
+ outline: none; transition: border-color 0.15s;
2274
+ }
2275
+ .input::placeholder { color: var(--authon-dim); }
2276
+ .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px ${hexToRgba2(b.primaryColorStart || "#7c3aed", 0.15)}; }
2277
+ .input.readonly, .input:disabled { color: var(--authon-dim); cursor: not-allowed; background: var(--authon-section-bg); }
2278
+
2279
+ .sessions-list { display: flex; flex-direction: column; gap: 6px; }
2280
+
2281
+ .session-item {
2282
+ display: flex; align-items: center; justify-content: space-between;
2283
+ background: var(--authon-session-bg);
2284
+ border-radius: calc(var(--authon-radius) * 0.5);
2285
+ padding: 8px 10px; gap: 8px;
2286
+ }
2287
+
2288
+ .session-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
2289
+
2290
+ .session-device {
2291
+ font-size: 13px; font-weight: 500; color: var(--authon-text);
2292
+ }
2293
+
2294
+ .session-meta {
2295
+ font-size: 11px; color: var(--authon-dim);
2296
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2297
+ }
2298
+
2299
+ .session-revoke-btn {
2300
+ background: none; border: none;
2301
+ color: var(--authon-dim); cursor: pointer;
2302
+ padding: 4px; border-radius: 4px; flex-shrink: 0;
2303
+ display: flex; align-items: center; justify-content: center;
2304
+ transition: color 0.15s, background 0.15s;
2305
+ }
2306
+ .session-revoke-btn:hover { color: #ef4444; background: rgba(239,68,68,0.1); }
2307
+
2308
+ .profile-actions {
2309
+ display: flex; flex-direction: column; gap: 8px;
2310
+ margin-top: 4px;
2311
+ }
2312
+
2313
+ .edit-btn {
2314
+ width: 100%; padding: 10px;
2315
+ background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
2316
+ color: #fff; border: none;
2317
+ border-radius: calc(var(--authon-radius) * 0.5);
2318
+ font-size: 14px; font-weight: 600; cursor: pointer;
2319
+ font-family: var(--authon-font); transition: opacity 0.15s;
2320
+ }
2321
+ .edit-btn:hover { opacity: 0.9; }
2322
+
2323
+ .save-btn {
2324
+ width: 100%; padding: 10px;
2325
+ background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
2326
+ color: #fff; border: none;
2327
+ border-radius: calc(var(--authon-radius) * 0.5);
2328
+ font-size: 14px; font-weight: 600; cursor: pointer;
2329
+ font-family: var(--authon-font); transition: opacity 0.15s;
2330
+ }
2331
+ .save-btn:hover { opacity: 0.9; }
2332
+ .save-btn:disabled { opacity: 0.6; cursor: not-allowed; }
2333
+
2334
+ .cancel-btn {
2335
+ width: 100%; padding: 10px;
2336
+ background: none;
2337
+ color: var(--authon-muted);
2338
+ border: 1px solid var(--authon-border);
2339
+ border-radius: calc(var(--authon-radius) * 0.5);
2340
+ font-size: 14px; font-weight: 500; cursor: pointer;
2341
+ font-family: var(--authon-font); transition: background 0.15s, color 0.15s;
2342
+ }
2343
+ .cancel-btn:hover { background: var(--authon-divider); color: var(--authon-text); }
2344
+
2345
+ .signout-btn {
2346
+ width: 100%; padding: 10px;
2347
+ background: none;
2348
+ color: #ef4444;
2349
+ border: 1px solid rgba(239,68,68,0.3);
2350
+ border-radius: calc(var(--authon-radius) * 0.5);
2351
+ font-size: 14px; font-weight: 500; cursor: pointer;
2352
+ font-family: var(--authon-font); transition: background 0.15s;
2353
+ }
2354
+ .signout-btn:hover { background: rgba(239,68,68,0.08); }
2355
+
2356
+ .error-msg {
2357
+ padding: 8px 12px;
2358
+ background: rgba(239,68,68,0.1); border: 1px solid rgba(239,68,68,0.3);
2359
+ border-radius: calc(var(--authon-radius) * 0.33);
2360
+ font-size: 13px; color: #ef4444; text-align: center;
2361
+ animation: fadeIn 0.15s ease;
2362
+ }
2363
+
2364
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
2365
+ @keyframes slideIn { from { opacity: 0; transform: translate(-50%, calc(-50% - 8px)); } to { opacity: 1; transform: translate(-50%, -50%); } }
2366
+ `;
2367
+ }
2368
+ attachEvents() {
2369
+ this.attachBackdropEvent();
2370
+ this.attachInnerEvents();
2371
+ }
2372
+ attachBackdropEvent() {
2373
+ if (!this.shadowRoot || this.mode !== "popup") return;
2374
+ const backdrop = this.shadowRoot.getElementById("profile-backdrop");
2375
+ if (backdrop) {
2376
+ backdrop.addEventListener("click", () => {
2377
+ this.onClose();
2378
+ this.close();
2379
+ });
2380
+ }
2381
+ }
2382
+ attachInnerEvents() {
2383
+ if (!this.shadowRoot) return;
2384
+ const closeBtn = this.shadowRoot.getElementById("profile-close-btn");
2385
+ closeBtn?.addEventListener("click", () => {
2386
+ this.onClose();
2387
+ this.close();
2388
+ });
2389
+ const editBtn = this.shadowRoot.getElementById("profile-edit-btn");
2390
+ editBtn?.addEventListener("click", () => {
2391
+ this.isEditMode = true;
2392
+ this.rerender();
2393
+ });
2394
+ const cancelBtn = this.shadowRoot.getElementById("profile-cancel-btn");
2395
+ cancelBtn?.addEventListener("click", () => {
2396
+ this.isEditMode = false;
2397
+ this.rerender();
2398
+ });
2399
+ const saveBtn = this.shadowRoot.getElementById("profile-save-btn");
2400
+ saveBtn?.addEventListener("click", async () => {
2401
+ const nameInput = this.shadowRoot.getElementById("profile-displayname-input");
2402
+ const avatarInput = this.shadowRoot.getElementById("profile-avatar-input");
2403
+ const displayName = nameInput?.value.trim();
2404
+ const avatarUrl = avatarInput?.value.trim();
2405
+ this.showSaving();
2406
+ this.shadowRoot.getElementById("profile-error")?.remove();
2407
+ try {
2408
+ await this.onSave({
2409
+ displayName: displayName || void 0,
2410
+ avatarUrl: avatarUrl || void 0
2411
+ });
2412
+ this.isEditMode = false;
2413
+ this.rerender();
2414
+ } catch (err) {
2415
+ this.showSaveError(err instanceof Error ? err.message : "Save failed");
2416
+ }
2417
+ });
2418
+ const signOutBtn = this.shadowRoot.getElementById("profile-signout-btn");
2419
+ signOutBtn?.addEventListener("click", async () => {
2420
+ if (signOutBtn instanceof HTMLButtonElement) {
2421
+ signOutBtn.disabled = true;
2422
+ signOutBtn.textContent = "...";
2423
+ }
2424
+ try {
2425
+ await this.onSignOut();
2426
+ this.close();
2427
+ } catch (_) {
2428
+ if (signOutBtn instanceof HTMLButtonElement) {
2429
+ signOutBtn.disabled = false;
2430
+ signOutBtn.textContent = this.t.signOut;
2431
+ }
2432
+ }
2433
+ });
2434
+ this.shadowRoot.querySelectorAll(".session-revoke-btn").forEach((btn) => {
2435
+ btn.addEventListener("click", async () => {
2436
+ const sessionId = btn.dataset.sessionId;
2437
+ if (!sessionId) return;
2438
+ btn.disabled = true;
2439
+ try {
2440
+ await this.onRevokeSession(sessionId);
2441
+ this.sessions = this.sessions.filter((s) => s.id !== sessionId);
2442
+ this.rerender();
2443
+ } catch (_) {
2444
+ btn.disabled = false;
2445
+ }
2446
+ });
2447
+ });
2448
+ }
2449
+ };
2450
+
1607
2451
  // src/session.ts
1608
2452
  var SessionManager = class {
1609
2453
  accessToken = null;
@@ -2133,6 +2977,7 @@ var Authon = class {
2133
2977
  config;
2134
2978
  session;
2135
2979
  modal = null;
2980
+ profile = null;
2136
2981
  listeners = /* @__PURE__ */ new Map();
2137
2982
  branding = null;
2138
2983
  providers = [];
@@ -2166,9 +3011,56 @@ var Authon = class {
2166
3011
  await this.ensureInitialized();
2167
3012
  this.getModal().open("signUp");
2168
3013
  }
3014
+ async openProfile() {
3015
+ const user = this.getUser();
3016
+ if (!user) throw new Error("Must be signed in to open profile");
3017
+ const token = this.session.getToken();
3018
+ let sessions = [];
3019
+ if (token) {
3020
+ try {
3021
+ sessions = await this.listSessions();
3022
+ } catch (_) {
3023
+ }
3024
+ }
3025
+ if (!this.profile) {
3026
+ this.profile = new ProfileRenderer({
3027
+ mode: this.config.mode,
3028
+ theme: this.config.theme,
3029
+ locale: this.config.locale,
3030
+ containerId: this.config.containerId,
3031
+ branding: this.branding || void 0,
3032
+ user,
3033
+ sessions,
3034
+ onSave: async (data) => {
3035
+ const updated = await this.updateProfile(data);
3036
+ this.profile?.updateUser(updated);
3037
+ },
3038
+ onSignOut: async () => {
3039
+ await this.signOut();
3040
+ this.profile = null;
3041
+ },
3042
+ onRevokeSession: async (sessionId) => {
3043
+ await this.revokeSession(sessionId);
3044
+ },
3045
+ onClose: () => {
3046
+ this.profile?.close();
3047
+ this.profile = null;
3048
+ }
3049
+ });
3050
+ } else {
3051
+ this.profile.updateUser(user);
3052
+ this.profile.updateSessions(sessions);
3053
+ }
3054
+ this.profile.open();
3055
+ }
3056
+ closeProfile() {
3057
+ this.profile?.close();
3058
+ this.profile = null;
3059
+ }
2169
3060
  /** Update theme at runtime without destroying form state */
2170
3061
  setTheme(theme) {
2171
3062
  this.getModal().setTheme(theme);
3063
+ this.profile?.setTheme(theme);
2172
3064
  }
2173
3065
  async signInWithOAuth(provider, options) {
2174
3066
  await this.ensureInitialized();
@@ -2190,14 +3082,27 @@ var Authon = class {
2190
3082
  return res.user;
2191
3083
  }
2192
3084
  async signUpWithEmail(email, password, meta) {
2193
- const tokens = await this.apiPost("/v1/auth/signup", {
3085
+ const res = await this.apiPost("/v1/auth/signup", {
2194
3086
  email,
2195
3087
  password,
2196
3088
  ...meta
2197
3089
  });
2198
- this.session.setSession(tokens);
2199
- this.emit("signedIn", tokens.user);
2200
- return tokens.user;
3090
+ if (res.needsVerification) {
3091
+ this.emit("verificationRequired", res.email);
3092
+ return { needsVerification: true, email: res.email };
3093
+ }
3094
+ this.session.setSession(res);
3095
+ this.emit("signedIn", res.user);
3096
+ return res.user;
3097
+ }
3098
+ async verifyEmail(email, code) {
3099
+ const res = await this.apiPost("/v1/auth/verify-email", { email, code });
3100
+ this.session.setSession(res);
3101
+ this.emit("signedIn", res.user);
3102
+ return res.user;
3103
+ }
3104
+ async resendVerificationCode(email) {
3105
+ await this.apiPost("/v1/auth/resend-code", { email });
2201
3106
  }
2202
3107
  async signOut() {
2203
3108
  await this.session.signOut();
@@ -2481,6 +3386,8 @@ var Authon = class {
2481
3386
  }
2482
3387
  destroy() {
2483
3388
  this.modal?.close();
3389
+ this.profile?.close();
3390
+ this.profile = null;
2484
3391
  this.session.destroy();
2485
3392
  this.listeners.clear();
2486
3393
  }
@@ -2546,13 +3453,37 @@ var Authon = class {
2546
3453
  onEmailSubmit: (email, password, isSignUp) => {
2547
3454
  this.modal?.clearError();
2548
3455
  const turnstileToken = this.modal?.getTurnstileToken?.() || void 0;
2549
- const promise = isSignUp ? this.signUpWithEmail(email, password, { turnstileToken }) : this.signInWithEmail(email, password, turnstileToken);
2550
- promise.then(() => this.modal?.close()).catch((err) => {
2551
- this.modal?.resetTurnstile?.();
2552
- const msg = err instanceof Error ? err.message : String(err);
2553
- this.modal?.showError(msg || "Authentication failed");
2554
- this.emit("error", err instanceof Error ? err : new Error(msg));
2555
- });
3456
+ if (isSignUp) {
3457
+ this.signUpWithEmail(email, password, { turnstileToken }).then((result) => {
3458
+ if ("needsVerification" in result && result.needsVerification) {
3459
+ this.modal?.showVerificationInput(email, async (code) => {
3460
+ try {
3461
+ await this.verifyEmail(email, code);
3462
+ this.modal?.close();
3463
+ } catch (err) {
3464
+ const msg = err instanceof Error ? err.message : String(err);
3465
+ this.modal?.showError(msg || "Verification failed");
3466
+ }
3467
+ }, async () => {
3468
+ await this.resendVerificationCode(email);
3469
+ });
3470
+ } else {
3471
+ this.modal?.close();
3472
+ }
3473
+ }).catch((err) => {
3474
+ this.modal?.resetTurnstile?.();
3475
+ const msg = err instanceof Error ? err.message : String(err);
3476
+ this.modal?.showError(msg || "Authentication failed");
3477
+ this.emit("error", err instanceof Error ? err : new Error(msg));
3478
+ });
3479
+ } else {
3480
+ this.signInWithEmail(email, password, turnstileToken).then(() => this.modal?.close()).catch((err) => {
3481
+ this.modal?.resetTurnstile?.();
3482
+ const msg = err instanceof Error ? err.message : String(err);
3483
+ this.modal?.showError(msg || "Authentication failed");
3484
+ this.emit("error", err instanceof Error ? err : new Error(msg));
3485
+ });
3486
+ }
2556
3487
  },
2557
3488
  onClose: () => this.modal?.close(),
2558
3489
  onWeb3WalletSelect: async (walletId) => {
@@ -2985,6 +3916,7 @@ var Authon = class {
2985
3916
  0 && (module.exports = {
2986
3917
  Authon,
2987
3918
  AuthonMfaRequiredError,
3919
+ ProfileRenderer,
2988
3920
  generateQrSvg,
2989
3921
  getProviderButtonConfig,
2990
3922
  getStrings,