@authon/js 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -89,7 +89,13 @@ var translations = {
89
89
  magicLink: "Continue with Magic Link",
90
90
  passkey: "Sign in with Passkey",
91
91
  securedBy: "Secured by",
92
- backToSignIn: "Back to sign in"
92
+ backToSignIn: "Back to sign in",
93
+ profile: "Profile",
94
+ editProfile: "Edit Profile",
95
+ displayName: "Display Name",
96
+ save: "Save",
97
+ signOut: "Sign Out",
98
+ sessions: "Active Sessions"
93
99
  },
94
100
  ko: {
95
101
  welcomeBack: "\uB2E4\uC2DC \uC624\uC2E0 \uAC78 \uD658\uC601\uD569\uB2C8\uB2E4",
@@ -107,7 +113,13 @@ var translations = {
107
113
  magicLink: "\uB9E4\uC9C1 \uB9C1\uD06C\uB85C \uACC4\uC18D\uD558\uAE30",
108
114
  passkey: "\uD328\uC2A4\uD0A4\uB85C \uB85C\uADF8\uC778",
109
115
  securedBy: "\uBCF4\uC548 \uC81C\uACF5",
110
- backToSignIn: "\uB85C\uADF8\uC778\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30"
116
+ backToSignIn: "\uB85C\uADF8\uC778\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30",
117
+ profile: "\uD504\uB85C\uD544",
118
+ editProfile: "\uD504\uB85C\uD544 \uD3B8\uC9D1",
119
+ displayName: "\uD45C\uC2DC \uC774\uB984",
120
+ save: "\uC800\uC7A5",
121
+ signOut: "\uB85C\uADF8\uC544\uC6C3",
122
+ sessions: "\uD65C\uC131 \uC138\uC158"
111
123
  },
112
124
  ja: {
113
125
  welcomeBack: "\u304A\u304B\u3048\u308A\u306A\u3055\u3044",
@@ -125,7 +137,13 @@ var translations = {
125
137
  magicLink: "\u30DE\u30B8\u30C3\u30AF\u30EA\u30F3\u30AF\u3067\u7D9A\u884C",
126
138
  passkey: "\u30D1\u30B9\u30AD\u30FC\u3067\u30ED\u30B0\u30A4\u30F3",
127
139
  securedBy: "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u63D0\u4F9B",
128
- backToSignIn: "\u30ED\u30B0\u30A4\u30F3\u306B\u623B\u308B"
140
+ backToSignIn: "\u30ED\u30B0\u30A4\u30F3\u306B\u623B\u308B",
141
+ profile: "\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB",
142
+ editProfile: "\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u7DE8\u96C6",
143
+ displayName: "\u8868\u793A\u540D",
144
+ save: "\u4FDD\u5B58",
145
+ signOut: "\u30B5\u30A4\u30F3\u30A2\u30A6\u30C8",
146
+ sessions: "\u30A2\u30AF\u30C6\u30A3\u30D6\u30BB\u30C3\u30B7\u30E7\u30F3"
129
147
  },
130
148
  "zh-CN": {
131
149
  welcomeBack: "\u6B22\u8FCE\u56DE\u6765",
@@ -143,7 +161,13 @@ var translations = {
143
161
  magicLink: "\u4F7F\u7528\u9B54\u6CD5\u94FE\u63A5\u7EE7\u7EED",
144
162
  passkey: "\u4F7F\u7528\u901A\u884C\u5BC6\u94A5\u767B\u5F55",
145
163
  securedBy: "\u5B89\u5168\u4FDD\u969C",
146
- backToSignIn: "\u8FD4\u56DE\u767B\u5F55"
164
+ backToSignIn: "\u8FD4\u56DE\u767B\u5F55",
165
+ profile: "\u4E2A\u4EBA\u8D44\u6599",
166
+ editProfile: "\u7F16\u8F91\u8D44\u6599",
167
+ displayName: "\u663E\u793A\u540D\u79F0",
168
+ save: "\u4FDD\u5B58",
169
+ signOut: "\u9000\u51FA\u767B\u5F55",
170
+ sessions: "\u6D3B\u8DC3\u4F1A\u8BDD"
147
171
  },
148
172
  "zh-TW": {
149
173
  welcomeBack: "\u6B61\u8FCE\u56DE\u4F86",
@@ -161,7 +185,13 @@ var translations = {
161
185
  magicLink: "\u4F7F\u7528\u9B54\u6CD5\u9023\u7D50\u7E7C\u7E8C",
162
186
  passkey: "\u4F7F\u7528\u901A\u884C\u91D1\u9470\u767B\u5165",
163
187
  securedBy: "\u5B89\u5168\u4FDD\u969C",
164
- backToSignIn: "\u8FD4\u56DE\u767B\u5165"
188
+ backToSignIn: "\u8FD4\u56DE\u767B\u5165",
189
+ profile: "\u500B\u4EBA\u8CC7\u6599",
190
+ editProfile: "\u7DE8\u8F2F\u8CC7\u6599",
191
+ displayName: "\u986F\u793A\u540D\u7A31",
192
+ save: "\u5132\u5B58",
193
+ signOut: "\u767B\u51FA",
194
+ sessions: "\u6D3B\u8E8D\u5DE5\u4F5C\u968E\u6BB5"
165
195
  },
166
196
  "pt-BR": {
167
197
  welcomeBack: "Bem-vindo de volta",
@@ -179,7 +209,13 @@ var translations = {
179
209
  magicLink: "Continuar com Magic Link",
180
210
  passkey: "Entrar com Passkey",
181
211
  securedBy: "Protegido por",
182
- backToSignIn: "Voltar ao login"
212
+ backToSignIn: "Voltar ao login",
213
+ profile: "Perfil",
214
+ editProfile: "Editar perfil",
215
+ displayName: "Nome de exibicao",
216
+ save: "Salvar",
217
+ signOut: "Sair",
218
+ sessions: "Sessoes ativas"
183
219
  },
184
220
  es: {
185
221
  welcomeBack: "Bienvenido de nuevo",
@@ -197,7 +233,13 @@ var translations = {
197
233
  magicLink: "Continuar con Magic Link",
198
234
  passkey: "Iniciar con Passkey",
199
235
  securedBy: "Protegido por",
200
- backToSignIn: "Volver al inicio de sesion"
236
+ backToSignIn: "Volver al inicio de sesion",
237
+ profile: "Perfil",
238
+ editProfile: "Editar perfil",
239
+ displayName: "Nombre de visualizacion",
240
+ save: "Guardar",
241
+ signOut: "Cerrar sesion",
242
+ sessions: "Sesiones activas"
201
243
  },
202
244
  de: {
203
245
  welcomeBack: "Willkommen zuruck",
@@ -215,7 +257,13 @@ var translations = {
215
257
  magicLink: "Weiter mit Magic Link",
216
258
  passkey: "Mit Passkey anmelden",
217
259
  securedBy: "Gesichert durch",
218
- backToSignIn: "Zuruck zur Anmeldung"
260
+ backToSignIn: "Zuruck zur Anmeldung",
261
+ profile: "Profil",
262
+ editProfile: "Profil bearbeiten",
263
+ displayName: "Anzeigename",
264
+ save: "Speichern",
265
+ signOut: "Abmelden",
266
+ sessions: "Aktive Sitzungen"
219
267
  },
220
268
  fr: {
221
269
  welcomeBack: "Bon retour",
@@ -233,7 +281,13 @@ var translations = {
233
281
  magicLink: "Continuer avec Magic Link",
234
282
  passkey: "Se connecter avec Passkey",
235
283
  securedBy: "Securise par",
236
- backToSignIn: "Retour a la connexion"
284
+ backToSignIn: "Retour a la connexion",
285
+ profile: "Profil",
286
+ editProfile: "Modifier le profil",
287
+ displayName: "Nom d'affichage",
288
+ save: "Enregistrer",
289
+ signOut: "Se deconnecter",
290
+ sessions: "Sessions actives"
237
291
  },
238
292
  hi: {
239
293
  welcomeBack: "\u0935\u093E\u092A\u0938\u0940 \u092A\u0930 \u0938\u094D\u0935\u093E\u0917\u0924 \u0939\u0948",
@@ -251,7 +305,13 @@ var translations = {
251
305
  magicLink: "\u092E\u0948\u091C\u093F\u0915 \u0932\u093F\u0902\u0915 \u0938\u0947 \u091C\u093E\u0930\u0940 \u0930\u0916\u0947\u0902",
252
306
  passkey: "\u092A\u093E\u0938\u0915\u0940 \u0938\u0947 \u0938\u093E\u0907\u0928 \u0907\u0928",
253
307
  securedBy: "\u0938\u0941\u0930\u0915\u094D\u0937\u093E \u092A\u094D\u0930\u0926\u093E\u0924\u093E",
254
- backToSignIn: "\u0938\u093E\u0907\u0928 \u0907\u0928 \u092A\u0930 \u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902"
308
+ backToSignIn: "\u0938\u093E\u0907\u0928 \u0907\u0928 \u092A\u0930 \u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902",
309
+ profile: "\u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932",
310
+ editProfile: "\u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932 \u0938\u0902\u092A\u093E\u0926\u093F\u0924 \u0915\u0930\u0947\u0902",
311
+ displayName: "\u092A\u094D\u0930\u0926\u0930\u094D\u0936\u0928 \u0928\u093E\u092E",
312
+ save: "\u0938\u0939\u0947\u091C\u0947\u0902",
313
+ signOut: "\u0938\u093E\u0907\u0928 \u0906\u0909\u091F",
314
+ sessions: "\u0938\u0915\u094D\u0930\u093F\u092F \u0938\u0924\u094D\u0930"
255
315
  },
256
316
  tr: {
257
317
  welcomeBack: "Tekrar hos geldiniz",
@@ -269,7 +329,13 @@ var translations = {
269
329
  magicLink: "Magic Link ile devam et",
270
330
  passkey: "Passkey ile giris yap",
271
331
  securedBy: "Guvenlik saglayici",
272
- backToSignIn: "Girise don"
332
+ backToSignIn: "Girise don",
333
+ profile: "Profil",
334
+ editProfile: "Profili duzenle",
335
+ displayName: "Goruntu adi",
336
+ save: "Kaydet",
337
+ signOut: "Cikis yap",
338
+ sessions: "Aktif oturumlar"
273
339
  },
274
340
  id: {
275
341
  welcomeBack: "Selamat datang kembali",
@@ -287,7 +353,13 @@ var translations = {
287
353
  magicLink: "Lanjutkan dengan Magic Link",
288
354
  passkey: "Masuk dengan Passkey",
289
355
  securedBy: "Diamankan oleh",
290
- backToSignIn: "Kembali ke login"
356
+ backToSignIn: "Kembali ke login",
357
+ profile: "Profil",
358
+ editProfile: "Edit profil",
359
+ displayName: "Nama tampilan",
360
+ save: "Simpan",
361
+ signOut: "Keluar",
362
+ sessions: "Sesi aktif"
291
363
  },
292
364
  vi: {
293
365
  welcomeBack: "Chao mung tro lai",
@@ -305,7 +377,13 @@ var translations = {
305
377
  magicLink: "Tiep tuc voi Magic Link",
306
378
  passkey: "Dang nhap voi Passkey",
307
379
  securedBy: "Bao mat boi",
308
- backToSignIn: "Quay lai dang nhap"
380
+ backToSignIn: "Quay lai dang nhap",
381
+ profile: "Ho so",
382
+ editProfile: "Chinh sua ho so",
383
+ displayName: "Ten hien thi",
384
+ save: "Luu",
385
+ signOut: "Dang xuat",
386
+ sessions: "Phien dang nhap hoat dong"
309
387
  },
310
388
  th: {
311
389
  welcomeBack: "\u0E22\u0E34\u0E19\u0E14\u0E35\u0E15\u0E49\u0E2D\u0E19\u0E23\u0E31\u0E1A\u0E01\u0E25\u0E31\u0E1A",
@@ -323,7 +401,13 @@ var translations = {
323
401
  magicLink: "\u0E14\u0E33\u0E40\u0E19\u0E34\u0E19\u0E01\u0E32\u0E23\u0E15\u0E48\u0E2D\u0E14\u0E49\u0E27\u0E22 Magic Link",
324
402
  passkey: "\u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A\u0E14\u0E49\u0E27\u0E22 Passkey",
325
403
  securedBy: "\u0E23\u0E31\u0E01\u0E29\u0E32\u0E04\u0E27\u0E32\u0E21\u0E1B\u0E25\u0E2D\u0E14\u0E20\u0E31\u0E22\u0E42\u0E14\u0E22",
326
- backToSignIn: "\u0E01\u0E25\u0E31\u0E1A\u0E44\u0E1B\u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A"
404
+ backToSignIn: "\u0E01\u0E25\u0E31\u0E1A\u0E44\u0E1B\u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A",
405
+ profile: "\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C",
406
+ editProfile: "\u0E41\u0E01\u0E49\u0E44\u0E02\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C",
407
+ displayName: "\u0E0A\u0E37\u0E48\u0E2D\u0E17\u0E35\u0E48\u0E41\u0E2A\u0E14\u0E07",
408
+ save: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01",
409
+ signOut: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A",
410
+ sessions: "\u0E40\u0E0B\u0E2A\u0E0A\u0E31\u0E19\u0E17\u0E35\u0E48\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19\u0E2D\u0E22\u0E39\u0E48"
327
411
  },
328
412
  ru: {
329
413
  welcomeBack: "\u0421 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0438\u0435\u043C",
@@ -341,7 +425,13 @@ var translations = {
341
425
  magicLink: "\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u0441 Magic Link",
342
426
  passkey: "\u0412\u043E\u0439\u0442\u0438 \u0441 Passkey",
343
427
  securedBy: "\u0417\u0430\u0449\u0438\u0449\u0435\u043D\u043E",
344
- backToSignIn: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u043A \u0432\u0445\u043E\u0434\u0443"
428
+ backToSignIn: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u043A \u0432\u0445\u043E\u0434\u0443",
429
+ profile: "\u041F\u0440\u043E\u0444\u0438\u043B\u044C",
430
+ editProfile: "\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C",
431
+ displayName: "\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u0438\u043C\u044F",
432
+ save: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C",
433
+ signOut: "\u0412\u044B\u0439\u0442\u0438",
434
+ sessions: "\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0441\u0435\u0430\u043D\u0441\u044B"
345
435
  },
346
436
  it: {
347
437
  welcomeBack: "Bentornato",
@@ -359,7 +449,13 @@ var translations = {
359
449
  magicLink: "Continua con Magic Link",
360
450
  passkey: "Accedi con Passkey",
361
451
  securedBy: "Protetto da",
362
- backToSignIn: "Torna all'accesso"
452
+ backToSignIn: "Torna all'accesso",
453
+ profile: "Profilo",
454
+ editProfile: "Modifica profilo",
455
+ displayName: "Nome visualizzato",
456
+ save: "Salva",
457
+ signOut: "Esci",
458
+ sessions: "Sessioni attive"
363
459
  },
364
460
  pl: {
365
461
  welcomeBack: "Witaj ponownie",
@@ -377,7 +473,13 @@ var translations = {
377
473
  magicLink: "Kontynuuj z Magic Link",
378
474
  passkey: "Zaloguj sie z Passkey",
379
475
  securedBy: "Zabezpieczone przez",
380
- backToSignIn: "Powrot do logowania"
476
+ backToSignIn: "Powrot do logowania",
477
+ profile: "Profil",
478
+ editProfile: "Edytuj profil",
479
+ displayName: "Nazwa wyswietlana",
480
+ save: "Zapisz",
481
+ signOut: "Wyloguj sie",
482
+ sessions: "Aktywne sesje"
381
483
  },
382
484
  nl: {
383
485
  welcomeBack: "Welkom terug",
@@ -395,7 +497,13 @@ var translations = {
395
497
  magicLink: "Doorgaan met Magic Link",
396
498
  passkey: "Inloggen met Passkey",
397
499
  securedBy: "Beveiligd door",
398
- backToSignIn: "Terug naar inloggen"
500
+ backToSignIn: "Terug naar inloggen",
501
+ profile: "Profiel",
502
+ editProfile: "Profiel bewerken",
503
+ displayName: "Weergavenaam",
504
+ save: "Opslaan",
505
+ signOut: "Uitloggen",
506
+ sessions: "Actieve sessies"
399
507
  },
400
508
  ar: {
401
509
  welcomeBack: "\u0645\u0631\u062D\u0628\u064B\u0627 \u0628\u0639\u0648\u062F\u062A\u0643",
@@ -413,7 +521,13 @@ var translations = {
413
521
  magicLink: "\u0627\u0644\u0645\u062A\u0627\u0628\u0639\u0629 \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 Magic Link",
414
522
  passkey: "\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644 \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 Passkey",
415
523
  securedBy: "\u0645\u062D\u0645\u064A \u0628\u0648\u0627\u0633\u0637\u0629",
416
- backToSignIn: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644"
524
+ backToSignIn: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644",
525
+ profile: "\u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0634\u062E\u0635\u064A",
526
+ editProfile: "\u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0634\u062E\u0635\u064A",
527
+ displayName: "\u0627\u0644\u0627\u0633\u0645 \u0627\u0644\u0645\u0639\u0631\u0648\u0636",
528
+ save: "\u062D\u0641\u0638",
529
+ signOut: "\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062E\u0631\u0648\u062C",
530
+ sessions: "\u0627\u0644\u062C\u0644\u0633\u0627\u062A \u0627\u0644\u0646\u0634\u0637\u0629"
417
531
  },
418
532
  sv: {
419
533
  welcomeBack: "Valkommen tillbaka",
@@ -431,7 +545,13 @@ var translations = {
431
545
  magicLink: "Fortsatt med Magic Link",
432
546
  passkey: "Logga in med Passkey",
433
547
  securedBy: "Sakrad av",
434
- backToSignIn: "Tillbaka till inloggning"
548
+ backToSignIn: "Tillbaka till inloggning",
549
+ profile: "Profil",
550
+ editProfile: "Redigera profil",
551
+ displayName: "Visningsnamn",
552
+ save: "Spara",
553
+ signOut: "Logga ut",
554
+ sessions: "Aktiva sessioner"
435
555
  },
436
556
  uk: {
437
557
  welcomeBack: "\u0417 \u043F\u043E\u0432\u0435\u0440\u043D\u0435\u043D\u043D\u044F\u043C",
@@ -449,7 +569,13 @@ var translations = {
449
569
  magicLink: "\u041F\u0440\u043E\u0434\u043E\u0432\u0436\u0438\u0442\u0438 \u0437 Magic Link",
450
570
  passkey: "\u0423\u0432\u0456\u0439\u0442\u0438 \u0437 Passkey",
451
571
  securedBy: "\u0417\u0430\u0445\u0438\u0449\u0435\u043D\u043E",
452
- backToSignIn: "\u041F\u043E\u0432\u0435\u0440\u043D\u0443\u0442\u0438\u0441\u044F \u0434\u043E \u0432\u0445\u043E\u0434\u0443"
572
+ backToSignIn: "\u041F\u043E\u0432\u0435\u0440\u043D\u0443\u0442\u0438\u0441\u044F \u0434\u043E \u0432\u0445\u043E\u0434\u0443",
573
+ profile: "\u041F\u0440\u043E\u0444\u0456\u043B\u044C",
574
+ editProfile: "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438 \u043F\u0440\u043E\u0444\u0456\u043B\u044C",
575
+ displayName: "\u0412\u0456\u0434\u043E\u0431\u0440\u0430\u0436\u0443\u0432\u0430\u043D\u0435 \u0456\u043C'\u044F",
576
+ save: "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438",
577
+ signOut: "\u0412\u0438\u0439\u0442\u0438",
578
+ sessions: "\u0410\u043A\u0442\u0438\u0432\u043D\u0456 \u0441\u0435\u0430\u043D\u0441\u0438"
453
579
  }
454
580
  };
455
581
  function getStrings(locale) {
@@ -1573,6 +1699,676 @@ var ModalRenderer = class {
1573
1699
  }
1574
1700
  };
1575
1701
 
1702
+ // src/profile.ts
1703
+ function hexToRgba2(hex, alpha) {
1704
+ const h = hex.replace("#", "");
1705
+ const r = parseInt(h.substring(0, 2), 16);
1706
+ const g = parseInt(h.substring(2, 4), 16);
1707
+ const b = parseInt(h.substring(4, 6), 16);
1708
+ return `rgba(${r},${g},${b},${alpha})`;
1709
+ }
1710
+ function formatRelativeTime(dateStr) {
1711
+ if (!dateStr) return "\u2014";
1712
+ const diff = Date.now() - new Date(dateStr).getTime();
1713
+ const minutes = Math.floor(diff / 6e4);
1714
+ if (minutes < 1) return "just now";
1715
+ if (minutes < 60) return `${minutes}m ago`;
1716
+ const hours = Math.floor(minutes / 60);
1717
+ if (hours < 24) return `${hours}h ago`;
1718
+ const days = Math.floor(hours / 24);
1719
+ return `${days}d ago`;
1720
+ }
1721
+ function parseUserAgent(ua) {
1722
+ if (!ua) return "Unknown device";
1723
+ if (/iPhone|iPad/.test(ua)) return "iOS";
1724
+ if (/Android/.test(ua)) return "Android";
1725
+ if (/Windows/.test(ua)) return "Windows";
1726
+ if (/Macintosh|Mac OS/.test(ua)) return "macOS";
1727
+ if (/Linux/.test(ua)) return "Linux";
1728
+ return "Unknown device";
1729
+ }
1730
+ var ProfileRenderer = class {
1731
+ shadowRoot = null;
1732
+ hostElement = null;
1733
+ containerElement = null;
1734
+ containerId = null;
1735
+ mode;
1736
+ theme;
1737
+ branding;
1738
+ themeObserver = null;
1739
+ mediaQueryListener = null;
1740
+ t;
1741
+ user;
1742
+ sessions;
1743
+ isEditMode = false;
1744
+ onSave;
1745
+ onSignOut;
1746
+ onRevokeSession;
1747
+ onClose;
1748
+ escHandler = null;
1749
+ constructor(options) {
1750
+ this.mode = options.mode;
1751
+ this.theme = options.theme || "auto";
1752
+ this.branding = { ...DEFAULT_BRANDING, ...options.branding };
1753
+ this.t = getStrings(options.locale || "en");
1754
+ this.user = options.user;
1755
+ this.sessions = options.sessions || [];
1756
+ this.onSave = options.onSave;
1757
+ this.onSignOut = options.onSignOut;
1758
+ this.onRevokeSession = options.onRevokeSession;
1759
+ this.onClose = options.onClose;
1760
+ if (options.mode === "embedded" && options.containerId) {
1761
+ this.containerId = options.containerId;
1762
+ }
1763
+ }
1764
+ resolveContainerElement() {
1765
+ if (this.mode !== "embedded" || !this.containerId) return null;
1766
+ const next = document.getElementById(this.containerId);
1767
+ if (this.containerElement !== next) {
1768
+ this.hostElement?.remove();
1769
+ this.hostElement = null;
1770
+ this.shadowRoot = null;
1771
+ }
1772
+ this.containerElement = next;
1773
+ return next;
1774
+ }
1775
+ open() {
1776
+ this.resolveContainerElement();
1777
+ if (this.hostElement && !this.hostElement.isConnected) {
1778
+ this.hostElement = null;
1779
+ this.shadowRoot = null;
1780
+ }
1781
+ if (this.shadowRoot && this.hostElement) {
1782
+ this.rerender();
1783
+ } else {
1784
+ this.isEditMode = false;
1785
+ this.render();
1786
+ }
1787
+ }
1788
+ close() {
1789
+ this.stopThemeObserver();
1790
+ if (this.escHandler) {
1791
+ document.removeEventListener("keydown", this.escHandler);
1792
+ this.escHandler = null;
1793
+ }
1794
+ if (this.hostElement) {
1795
+ this.hostElement.remove();
1796
+ this.hostElement = null;
1797
+ this.shadowRoot = null;
1798
+ }
1799
+ const liveContainer = this.resolveContainerElement();
1800
+ if (liveContainer) {
1801
+ liveContainer.replaceChildren();
1802
+ }
1803
+ }
1804
+ updateUser(user) {
1805
+ this.user = user;
1806
+ if (this.shadowRoot) this.rerender();
1807
+ }
1808
+ updateSessions(sessions) {
1809
+ this.sessions = sessions;
1810
+ if (this.shadowRoot) this.rerender();
1811
+ }
1812
+ setTheme(theme) {
1813
+ this.theme = theme;
1814
+ this.updateThemeCSS();
1815
+ if (theme === "auto") {
1816
+ this.startThemeObserver();
1817
+ } else {
1818
+ this.stopThemeObserver();
1819
+ }
1820
+ }
1821
+ showSaving() {
1822
+ if (!this.shadowRoot) return;
1823
+ const btn = this.shadowRoot.getElementById("profile-save-btn");
1824
+ if (btn) {
1825
+ btn.disabled = true;
1826
+ btn.textContent = "...";
1827
+ }
1828
+ }
1829
+ showSaveError(message) {
1830
+ if (!this.shadowRoot) return;
1831
+ const btn = this.shadowRoot.getElementById("profile-save-btn");
1832
+ if (btn) {
1833
+ btn.disabled = false;
1834
+ btn.textContent = this.t.save;
1835
+ }
1836
+ this.showInlineError("profile-error", message);
1837
+ }
1838
+ showInlineError(id, message) {
1839
+ if (!this.shadowRoot) return;
1840
+ this.shadowRoot.getElementById(id)?.remove();
1841
+ const errEl = document.createElement("div");
1842
+ errEl.id = id;
1843
+ errEl.className = "error-msg";
1844
+ errEl.textContent = message;
1845
+ this.shadowRoot.querySelector(".profile-actions")?.appendChild(errEl);
1846
+ }
1847
+ updateThemeCSS() {
1848
+ if (!this.shadowRoot) return;
1849
+ const styleEl = this.shadowRoot.getElementById("authon-profile-theme-style");
1850
+ if (styleEl) styleEl.textContent = this.buildCSS();
1851
+ }
1852
+ startThemeObserver() {
1853
+ this.stopThemeObserver();
1854
+ if (typeof document === "undefined" || typeof window === "undefined") return;
1855
+ this.themeObserver = new MutationObserver(() => this.updateThemeCSS());
1856
+ this.themeObserver.observe(document.documentElement, {
1857
+ attributes: true,
1858
+ attributeFilter: ["data-theme", "class"]
1859
+ });
1860
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
1861
+ this.mediaQueryListener = () => this.updateThemeCSS();
1862
+ mq.addEventListener("change", this.mediaQueryListener);
1863
+ }
1864
+ stopThemeObserver() {
1865
+ if (this.themeObserver) {
1866
+ this.themeObserver.disconnect();
1867
+ this.themeObserver = null;
1868
+ }
1869
+ if (this.mediaQueryListener) {
1870
+ window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", this.mediaQueryListener);
1871
+ this.mediaQueryListener = null;
1872
+ }
1873
+ }
1874
+ isDark() {
1875
+ if (this.theme === "dark") return true;
1876
+ if (this.theme === "light") return false;
1877
+ if (typeof document !== "undefined") {
1878
+ const html = document.documentElement;
1879
+ if (html.classList.contains("dark") || html.getAttribute("data-theme") === "dark") return true;
1880
+ if (html.classList.contains("light") || html.getAttribute("data-theme") === "light") return false;
1881
+ }
1882
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
1883
+ }
1884
+ render() {
1885
+ const host = document.createElement("div");
1886
+ host.setAttribute("data-authon-profile", "");
1887
+ this.hostElement = host;
1888
+ if (this.mode === "popup") {
1889
+ document.body.appendChild(host);
1890
+ } else {
1891
+ const container = this.resolveContainerElement();
1892
+ if (!container) {
1893
+ this.hostElement = null;
1894
+ throw new Error(`Authon profile container "#${this.containerId}" not found`);
1895
+ }
1896
+ container.replaceChildren();
1897
+ container.appendChild(host);
1898
+ }
1899
+ this.shadowRoot = host.attachShadow({ mode: "open" });
1900
+ this.shadowRoot.innerHTML = this.buildShell();
1901
+ this.attachEvents();
1902
+ if (this.mode === "popup") {
1903
+ this.escHandler = (e) => {
1904
+ if (e.key === "Escape") {
1905
+ this.onClose();
1906
+ this.close();
1907
+ }
1908
+ };
1909
+ document.addEventListener("keydown", this.escHandler);
1910
+ }
1911
+ if (this.theme === "auto") this.startThemeObserver();
1912
+ }
1913
+ rerender() {
1914
+ if (!this.shadowRoot) return;
1915
+ const inner = this.shadowRoot.getElementById("profile-inner");
1916
+ if (!inner) return;
1917
+ inner.style.opacity = "0";
1918
+ inner.style.transform = "translateY(-4px)";
1919
+ setTimeout(() => {
1920
+ inner.innerHTML = this.buildInnerContent();
1921
+ this.attachInnerEvents();
1922
+ void inner.offsetHeight;
1923
+ inner.style.opacity = "1";
1924
+ inner.style.transform = "translateY(0)";
1925
+ }, 120);
1926
+ }
1927
+ buildShell() {
1928
+ const popupWrapper = this.mode === "popup" ? `<div class="backdrop" id="profile-backdrop"></div>` : "";
1929
+ return `
1930
+ <style id="authon-profile-theme-style">${this.buildCSS()}</style>
1931
+ ${popupWrapper}
1932
+ <div class="profile-container" role="dialog" aria-modal="true" aria-label="${this.t.profile}">
1933
+ <div id="profile-inner" class="profile-inner">
1934
+ ${this.buildInnerContent()}
1935
+ </div>
1936
+ </div>
1937
+ `;
1938
+ }
1939
+ buildInnerContent() {
1940
+ const u = this.user;
1941
+ const initials = (u.displayName || u.email || "?").split(/\s+/).map((w) => w[0]?.toUpperCase() ?? "").slice(0, 2).join("");
1942
+ const avatarHtml = u.avatarUrl ? `<img class="avatar-img" src="${u.avatarUrl}" alt="${u.displayName || ""}" />` : `<div class="avatar-placeholder">${initials}</div>`;
1943
+ const closeBtn = this.mode === "popup" ? `<button class="close-btn" id="profile-close-btn" aria-label="Close">
1944
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
1945
+ <path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
1946
+ </svg>
1947
+ </button>` : "";
1948
+ if (this.isEditMode) {
1949
+ return this.buildEditContent(avatarHtml, closeBtn);
1950
+ }
1951
+ return this.buildViewContent(avatarHtml, closeBtn, u);
1952
+ }
1953
+ buildViewContent(avatarHtml, closeBtn, u) {
1954
+ const sessionsHtml = this.sessions.length > 0 ? `<div class="section">
1955
+ <div class="section-label">${this.t.sessions}</div>
1956
+ <div class="sessions-list">
1957
+ ${this.sessions.map((s) => `
1958
+ <div class="session-item" data-session-id="${s.id}">
1959
+ <div class="session-info">
1960
+ <span class="session-device">${parseUserAgent(s.userAgent)}</span>
1961
+ <span class="session-meta">${s.ipAddress || ""} &middot; ${formatRelativeTime(s.lastActiveAt)}</span>
1962
+ </div>
1963
+ <button class="session-revoke-btn" data-session-id="${s.id}" aria-label="Revoke session">
1964
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
1965
+ <path d="M10.5 3.5L3.5 10.5M3.5 3.5l7 7" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
1966
+ </svg>
1967
+ </button>
1968
+ </div>
1969
+ `).join("")}
1970
+ </div>
1971
+ </div>` : "";
1972
+ return `
1973
+ <div class="profile-header">
1974
+ <div class="avatar-wrap">${avatarHtml}</div>
1975
+ <div class="header-text">
1976
+ <div class="display-name">${u.displayName || "\u2014"}</div>
1977
+ <div class="email">${u.email || "\u2014"}</div>
1978
+ </div>
1979
+ ${closeBtn}
1980
+ </div>
1981
+
1982
+ <div class="section">
1983
+ <div class="field-row">
1984
+ <span class="field-label">${this.t.emailAddress}</span>
1985
+ <span class="field-value readonly">${u.email || "\u2014"}</span>
1986
+ </div>
1987
+ <div class="field-row">
1988
+ <span class="field-label">${this.t.displayName}</span>
1989
+ <span class="field-value">${u.displayName || "\u2014"}</span>
1990
+ </div>
1991
+ </div>
1992
+
1993
+ ${sessionsHtml}
1994
+
1995
+ <div class="profile-actions">
1996
+ <button class="edit-btn" id="profile-edit-btn">${this.t.editProfile}</button>
1997
+ <button class="signout-btn" id="profile-signout-btn">${this.t.signOut}</button>
1998
+ </div>
1999
+ `;
2000
+ }
2001
+ buildEditContent(avatarHtml, closeBtn) {
2002
+ const u = this.user;
2003
+ return `
2004
+ <div class="profile-header">
2005
+ <div class="avatar-wrap">${avatarHtml}</div>
2006
+ <div class="header-text">
2007
+ <div class="display-name">${this.t.editProfile}</div>
2008
+ <div class="email">${u.email || "\u2014"}</div>
2009
+ </div>
2010
+ ${closeBtn}
2011
+ </div>
2012
+
2013
+ <div class="section">
2014
+ <div class="field-group">
2015
+ <label class="field-label" for="profile-displayname-input">${this.t.displayName}</label>
2016
+ <input
2017
+ id="profile-displayname-input"
2018
+ class="input"
2019
+ type="text"
2020
+ value="${u.displayName || ""}"
2021
+ placeholder="${this.t.displayName}"
2022
+ autocomplete="name"
2023
+ />
2024
+ </div>
2025
+ <div class="field-group">
2026
+ <label class="field-label" for="profile-avatar-input">Avatar URL</label>
2027
+ <input
2028
+ id="profile-avatar-input"
2029
+ class="input"
2030
+ type="url"
2031
+ value="${u.avatarUrl || ""}"
2032
+ placeholder="https://..."
2033
+ autocomplete="off"
2034
+ />
2035
+ </div>
2036
+ <div class="field-group">
2037
+ <label class="field-label">${this.t.emailAddress}</label>
2038
+ <input class="input readonly" type="email" value="${u.email || ""}" disabled />
2039
+ </div>
2040
+ </div>
2041
+
2042
+ <div class="profile-actions">
2043
+ <button class="save-btn" id="profile-save-btn">${this.t.save}</button>
2044
+ <button class="cancel-btn" id="profile-cancel-btn">Cancel</button>
2045
+ </div>
2046
+ `;
2047
+ }
2048
+ buildCSS() {
2049
+ const b = this.branding;
2050
+ const dark = this.isDark();
2051
+ const bg = dark ? b.darkBg || "#0f172a" : b.lightBg || "#ffffff";
2052
+ const text = dark ? b.darkText || "#f1f5f9" : b.lightText || "#111827";
2053
+ const mutedText = dark ? "#94a3b8" : "#6b7280";
2054
+ const dimText = dark ? "#64748b" : "#9ca3af";
2055
+ const borderColor = dark ? "#334155" : "#d1d5db";
2056
+ const dividerColor = dark ? "#334155" : "#e5e7eb";
2057
+ const inputBg = dark ? "#1e293b" : "#ffffff";
2058
+ const sectionBg = dark ? "#1e293b" : "#f9fafb";
2059
+ const sessionItemBg = dark ? "#263148" : "#f3f4f6";
2060
+ return `
2061
+ :host {
2062
+ --authon-primary-start: ${b.primaryColorStart || "#7c3aed"};
2063
+ --authon-primary-end: ${b.primaryColorEnd || "#4f46e5"};
2064
+ --authon-bg: ${bg};
2065
+ --authon-text: ${text};
2066
+ --authon-muted: ${mutedText};
2067
+ --authon-dim: ${dimText};
2068
+ --authon-border: ${borderColor};
2069
+ --authon-divider: ${dividerColor};
2070
+ --authon-input-bg: ${inputBg};
2071
+ --authon-section-bg: ${sectionBg};
2072
+ --authon-session-bg: ${sessionItemBg};
2073
+ --authon-backdrop-bg: rgba(0,0,0,${dark ? "0.7" : "0.5"});
2074
+ --authon-radius: ${b.borderRadius ?? 12}px;
2075
+ --authon-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
2076
+ font-family: var(--authon-font);
2077
+ color: var(--authon-text);
2078
+ }
2079
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2080
+
2081
+ .backdrop {
2082
+ position: fixed; inset: 0; z-index: 99998;
2083
+ background: var(--authon-backdrop-bg); backdrop-filter: blur(4px);
2084
+ animation: fadeIn 0.2s ease;
2085
+ }
2086
+
2087
+ .profile-container {
2088
+ ${this.mode === "popup" ? "position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99999; max-height: 90vh; overflow-y: auto;" : ""}
2089
+ background: var(--authon-bg);
2090
+ color: var(--authon-text);
2091
+ border: 1px solid var(--authon-border);
2092
+ border-radius: var(--authon-radius);
2093
+ padding: 24px;
2094
+ width: 380px; max-width: 100%;
2095
+ ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
2096
+ }
2097
+
2098
+ .profile-inner {
2099
+ transition: opacity 0.12s ease, transform 0.12s ease;
2100
+ }
2101
+
2102
+ .profile-header {
2103
+ display: flex;
2104
+ align-items: center;
2105
+ gap: 14px;
2106
+ margin-bottom: 20px;
2107
+ position: relative;
2108
+ }
2109
+
2110
+ .avatar-wrap { flex-shrink: 0; }
2111
+
2112
+ .avatar-img {
2113
+ width: 52px; height: 52px;
2114
+ border-radius: 50%;
2115
+ object-fit: cover;
2116
+ border: 2px solid var(--authon-border);
2117
+ }
2118
+
2119
+ .avatar-placeholder {
2120
+ width: 52px; height: 52px;
2121
+ border-radius: 50%;
2122
+ background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
2123
+ display: flex; align-items: center; justify-content: center;
2124
+ font-size: 18px; font-weight: 700; color: #fff;
2125
+ flex-shrink: 0;
2126
+ }
2127
+
2128
+ .header-text { flex: 1; min-width: 0; }
2129
+
2130
+ .display-name {
2131
+ font-size: 16px; font-weight: 600;
2132
+ color: var(--authon-text);
2133
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2134
+ }
2135
+
2136
+ .email {
2137
+ font-size: 13px; color: var(--authon-muted);
2138
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2139
+ margin-top: 2px;
2140
+ }
2141
+
2142
+ .close-btn {
2143
+ position: absolute; top: 0; right: 0;
2144
+ background: none; border: none;
2145
+ color: var(--authon-muted); cursor: pointer;
2146
+ padding: 4px; border-radius: 6px;
2147
+ display: flex; align-items: center; justify-content: center;
2148
+ transition: color 0.15s, background 0.15s;
2149
+ }
2150
+ .close-btn:hover { color: var(--authon-text); background: var(--authon-divider); }
2151
+
2152
+ .section {
2153
+ background: var(--authon-section-bg);
2154
+ border: 1px solid var(--authon-border);
2155
+ border-radius: calc(var(--authon-radius) * 0.67);
2156
+ padding: 12px 14px;
2157
+ margin-bottom: 12px;
2158
+ display: flex; flex-direction: column; gap: 10px;
2159
+ }
2160
+
2161
+ .section-label {
2162
+ font-size: 11px; font-weight: 600;
2163
+ text-transform: uppercase; letter-spacing: 0.05em;
2164
+ color: var(--authon-dim); margin-bottom: 2px;
2165
+ }
2166
+
2167
+ .field-row {
2168
+ display: flex; align-items: center; justify-content: space-between; gap: 8px;
2169
+ }
2170
+
2171
+ .field-label {
2172
+ font-size: 12px; color: var(--authon-muted); flex-shrink: 0;
2173
+ }
2174
+
2175
+ .field-value {
2176
+ font-size: 13px; color: var(--authon-text);
2177
+ text-align: right; min-width: 0;
2178
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2179
+ }
2180
+
2181
+ .field-value.readonly { color: var(--authon-dim); }
2182
+
2183
+ .field-group {
2184
+ display: flex; flex-direction: column; gap: 4px;
2185
+ }
2186
+
2187
+ .input {
2188
+ width: 100%; padding: 9px 12px;
2189
+ background: var(--authon-input-bg);
2190
+ color: var(--authon-text);
2191
+ border: 1px solid var(--authon-border);
2192
+ border-radius: calc(var(--authon-radius) * 0.5);
2193
+ font-size: 13px; font-family: var(--authon-font);
2194
+ outline: none; transition: border-color 0.15s;
2195
+ }
2196
+ .input::placeholder { color: var(--authon-dim); }
2197
+ .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px ${hexToRgba2(b.primaryColorStart || "#7c3aed", 0.15)}; }
2198
+ .input.readonly, .input:disabled { color: var(--authon-dim); cursor: not-allowed; background: var(--authon-section-bg); }
2199
+
2200
+ .sessions-list { display: flex; flex-direction: column; gap: 6px; }
2201
+
2202
+ .session-item {
2203
+ display: flex; align-items: center; justify-content: space-between;
2204
+ background: var(--authon-session-bg);
2205
+ border-radius: calc(var(--authon-radius) * 0.5);
2206
+ padding: 8px 10px; gap: 8px;
2207
+ }
2208
+
2209
+ .session-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
2210
+
2211
+ .session-device {
2212
+ font-size: 13px; font-weight: 500; color: var(--authon-text);
2213
+ }
2214
+
2215
+ .session-meta {
2216
+ font-size: 11px; color: var(--authon-dim);
2217
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2218
+ }
2219
+
2220
+ .session-revoke-btn {
2221
+ background: none; border: none;
2222
+ color: var(--authon-dim); cursor: pointer;
2223
+ padding: 4px; border-radius: 4px; flex-shrink: 0;
2224
+ display: flex; align-items: center; justify-content: center;
2225
+ transition: color 0.15s, background 0.15s;
2226
+ }
2227
+ .session-revoke-btn:hover { color: #ef4444; background: rgba(239,68,68,0.1); }
2228
+
2229
+ .profile-actions {
2230
+ display: flex; flex-direction: column; gap: 8px;
2231
+ margin-top: 4px;
2232
+ }
2233
+
2234
+ .edit-btn {
2235
+ width: 100%; padding: 10px;
2236
+ background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
2237
+ color: #fff; border: none;
2238
+ border-radius: calc(var(--authon-radius) * 0.5);
2239
+ font-size: 14px; font-weight: 600; cursor: pointer;
2240
+ font-family: var(--authon-font); transition: opacity 0.15s;
2241
+ }
2242
+ .edit-btn:hover { opacity: 0.9; }
2243
+
2244
+ .save-btn {
2245
+ width: 100%; padding: 10px;
2246
+ background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
2247
+ color: #fff; border: none;
2248
+ border-radius: calc(var(--authon-radius) * 0.5);
2249
+ font-size: 14px; font-weight: 600; cursor: pointer;
2250
+ font-family: var(--authon-font); transition: opacity 0.15s;
2251
+ }
2252
+ .save-btn:hover { opacity: 0.9; }
2253
+ .save-btn:disabled { opacity: 0.6; cursor: not-allowed; }
2254
+
2255
+ .cancel-btn {
2256
+ width: 100%; padding: 10px;
2257
+ background: none;
2258
+ color: var(--authon-muted);
2259
+ border: 1px solid var(--authon-border);
2260
+ border-radius: calc(var(--authon-radius) * 0.5);
2261
+ font-size: 14px; font-weight: 500; cursor: pointer;
2262
+ font-family: var(--authon-font); transition: background 0.15s, color 0.15s;
2263
+ }
2264
+ .cancel-btn:hover { background: var(--authon-divider); color: var(--authon-text); }
2265
+
2266
+ .signout-btn {
2267
+ width: 100%; padding: 10px;
2268
+ background: none;
2269
+ color: #ef4444;
2270
+ border: 1px solid rgba(239,68,68,0.3);
2271
+ border-radius: calc(var(--authon-radius) * 0.5);
2272
+ font-size: 14px; font-weight: 500; cursor: pointer;
2273
+ font-family: var(--authon-font); transition: background 0.15s;
2274
+ }
2275
+ .signout-btn:hover { background: rgba(239,68,68,0.08); }
2276
+
2277
+ .error-msg {
2278
+ padding: 8px 12px;
2279
+ background: rgba(239,68,68,0.1); border: 1px solid rgba(239,68,68,0.3);
2280
+ border-radius: calc(var(--authon-radius) * 0.33);
2281
+ font-size: 13px; color: #ef4444; text-align: center;
2282
+ animation: fadeIn 0.15s ease;
2283
+ }
2284
+
2285
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
2286
+ @keyframes slideIn { from { opacity: 0; transform: translate(-50%, calc(-50% - 8px)); } to { opacity: 1; transform: translate(-50%, -50%); } }
2287
+ `;
2288
+ }
2289
+ attachEvents() {
2290
+ this.attachBackdropEvent();
2291
+ this.attachInnerEvents();
2292
+ }
2293
+ attachBackdropEvent() {
2294
+ if (!this.shadowRoot || this.mode !== "popup") return;
2295
+ const backdrop = this.shadowRoot.getElementById("profile-backdrop");
2296
+ if (backdrop) {
2297
+ backdrop.addEventListener("click", () => {
2298
+ this.onClose();
2299
+ this.close();
2300
+ });
2301
+ }
2302
+ }
2303
+ attachInnerEvents() {
2304
+ if (!this.shadowRoot) return;
2305
+ const closeBtn = this.shadowRoot.getElementById("profile-close-btn");
2306
+ closeBtn?.addEventListener("click", () => {
2307
+ this.onClose();
2308
+ this.close();
2309
+ });
2310
+ const editBtn = this.shadowRoot.getElementById("profile-edit-btn");
2311
+ editBtn?.addEventListener("click", () => {
2312
+ this.isEditMode = true;
2313
+ this.rerender();
2314
+ });
2315
+ const cancelBtn = this.shadowRoot.getElementById("profile-cancel-btn");
2316
+ cancelBtn?.addEventListener("click", () => {
2317
+ this.isEditMode = false;
2318
+ this.rerender();
2319
+ });
2320
+ const saveBtn = this.shadowRoot.getElementById("profile-save-btn");
2321
+ saveBtn?.addEventListener("click", async () => {
2322
+ const nameInput = this.shadowRoot.getElementById("profile-displayname-input");
2323
+ const avatarInput = this.shadowRoot.getElementById("profile-avatar-input");
2324
+ const displayName = nameInput?.value.trim();
2325
+ const avatarUrl = avatarInput?.value.trim();
2326
+ this.showSaving();
2327
+ this.shadowRoot.getElementById("profile-error")?.remove();
2328
+ try {
2329
+ await this.onSave({
2330
+ displayName: displayName || void 0,
2331
+ avatarUrl: avatarUrl || void 0
2332
+ });
2333
+ this.isEditMode = false;
2334
+ this.rerender();
2335
+ } catch (err) {
2336
+ this.showSaveError(err instanceof Error ? err.message : "Save failed");
2337
+ }
2338
+ });
2339
+ const signOutBtn = this.shadowRoot.getElementById("profile-signout-btn");
2340
+ signOutBtn?.addEventListener("click", async () => {
2341
+ if (signOutBtn instanceof HTMLButtonElement) {
2342
+ signOutBtn.disabled = true;
2343
+ signOutBtn.textContent = "...";
2344
+ }
2345
+ try {
2346
+ await this.onSignOut();
2347
+ this.close();
2348
+ } catch (_) {
2349
+ if (signOutBtn instanceof HTMLButtonElement) {
2350
+ signOutBtn.disabled = false;
2351
+ signOutBtn.textContent = this.t.signOut;
2352
+ }
2353
+ }
2354
+ });
2355
+ this.shadowRoot.querySelectorAll(".session-revoke-btn").forEach((btn) => {
2356
+ btn.addEventListener("click", async () => {
2357
+ const sessionId = btn.dataset.sessionId;
2358
+ if (!sessionId) return;
2359
+ btn.disabled = true;
2360
+ try {
2361
+ await this.onRevokeSession(sessionId);
2362
+ this.sessions = this.sessions.filter((s) => s.id !== sessionId);
2363
+ this.rerender();
2364
+ } catch (_) {
2365
+ btn.disabled = false;
2366
+ }
2367
+ });
2368
+ });
2369
+ }
2370
+ };
2371
+
1576
2372
  // src/session.ts
1577
2373
  var SessionManager = class {
1578
2374
  accessToken = null;
@@ -2102,6 +2898,7 @@ var Authon = class {
2102
2898
  config;
2103
2899
  session;
2104
2900
  modal = null;
2901
+ profile = null;
2105
2902
  listeners = /* @__PURE__ */ new Map();
2106
2903
  branding = null;
2107
2904
  providers = [];
@@ -2135,9 +2932,56 @@ var Authon = class {
2135
2932
  await this.ensureInitialized();
2136
2933
  this.getModal().open("signUp");
2137
2934
  }
2935
+ async openProfile() {
2936
+ const user = this.getUser();
2937
+ if (!user) throw new Error("Must be signed in to open profile");
2938
+ const token = this.session.getToken();
2939
+ let sessions = [];
2940
+ if (token) {
2941
+ try {
2942
+ sessions = await this.listSessions();
2943
+ } catch (_) {
2944
+ }
2945
+ }
2946
+ if (!this.profile) {
2947
+ this.profile = new ProfileRenderer({
2948
+ mode: this.config.mode,
2949
+ theme: this.config.theme,
2950
+ locale: this.config.locale,
2951
+ containerId: this.config.containerId,
2952
+ branding: this.branding || void 0,
2953
+ user,
2954
+ sessions,
2955
+ onSave: async (data) => {
2956
+ const updated = await this.updateProfile(data);
2957
+ this.profile?.updateUser(updated);
2958
+ },
2959
+ onSignOut: async () => {
2960
+ await this.signOut();
2961
+ this.profile = null;
2962
+ },
2963
+ onRevokeSession: async (sessionId) => {
2964
+ await this.revokeSession(sessionId);
2965
+ },
2966
+ onClose: () => {
2967
+ this.profile?.close();
2968
+ this.profile = null;
2969
+ }
2970
+ });
2971
+ } else {
2972
+ this.profile.updateUser(user);
2973
+ this.profile.updateSessions(sessions);
2974
+ }
2975
+ this.profile.open();
2976
+ }
2977
+ closeProfile() {
2978
+ this.profile?.close();
2979
+ this.profile = null;
2980
+ }
2138
2981
  /** Update theme at runtime without destroying form state */
2139
2982
  setTheme(theme) {
2140
2983
  this.getModal().setTheme(theme);
2984
+ this.profile?.setTheme(theme);
2141
2985
  }
2142
2986
  async signInWithOAuth(provider, options) {
2143
2987
  await this.ensureInitialized();
@@ -2450,6 +3294,8 @@ var Authon = class {
2450
3294
  }
2451
3295
  destroy() {
2452
3296
  this.modal?.close();
3297
+ this.profile?.close();
3298
+ this.profile = null;
2453
3299
  this.session.destroy();
2454
3300
  this.listeners.clear();
2455
3301
  }
@@ -2953,6 +3799,7 @@ var Authon = class {
2953
3799
  export {
2954
3800
  Authon,
2955
3801
  AuthonMfaRequiredError,
3802
+ ProfileRenderer,
2956
3803
  generateQrSvg,
2957
3804
  getProviderButtonConfig,
2958
3805
  getStrings,