@authon/react 0.2.1 → 0.3.1

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
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  // src/AuthonProvider.tsx
2
4
  import { createContext, useCallback, useEffect, useMemo, useRef, useState } from "react";
3
5
  import { Authon } from "@authon/js";
@@ -6,6 +8,7 @@ var AuthonContext = createContext(null);
6
8
  function AuthonProvider({ publishableKey, children, config }) {
7
9
  const [user, setUser] = useState(null);
8
10
  const [isLoading, setIsLoading] = useState(true);
11
+ const [activeOrganization, setActiveOrganization] = useState(null);
9
12
  const clientRef = useRef(null);
10
13
  useEffect(() => {
11
14
  const client = new Authon(publishableKey, config);
@@ -33,6 +36,7 @@ function AuthonProvider({ publishableKey, children, config }) {
33
36
  const signOut = useCallback(async () => {
34
37
  await clientRef.current?.signOut();
35
38
  setUser(null);
39
+ setActiveOrganization(null);
36
40
  }, []);
37
41
  const openSignIn = useCallback(async () => {
38
42
  await clientRef.current?.openSignIn();
@@ -48,13 +52,15 @@ function AuthonProvider({ publishableKey, children, config }) {
48
52
  isSignedIn: !!user,
49
53
  isLoading,
50
54
  user,
55
+ activeOrganization,
56
+ setActiveOrganization,
51
57
  signOut,
52
58
  openSignIn,
53
59
  openSignUp,
54
60
  getToken,
55
61
  client: clientRef.current
56
62
  }),
57
- [user, isLoading, signOut, openSignIn, openSignUp, getToken]
63
+ [user, isLoading, activeOrganization, signOut, openSignIn, openSignUp, getToken]
58
64
  );
59
65
  return /* @__PURE__ */ jsx(AuthonContext.Provider, { value, children });
60
66
  }
@@ -75,201 +81,1328 @@ function useUser() {
75
81
  return { user, isLoading };
76
82
  }
77
83
 
78
- // src/SignIn.tsx
79
- import { useEffect as useEffect2, useRef as useRef2 } from "react";
80
- import { jsx as jsx2 } from "react/jsx-runtime";
81
- function SignIn({ mode = "popup" }) {
84
+ // src/components/SignIn.tsx
85
+ import { useState as useState5 } from "react";
86
+ import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
87
+ import { getProviderButtonConfig } from "@authon/js";
88
+
89
+ // src/hooks/useBranding.ts
90
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
91
+ import { DEFAULT_BRANDING } from "@authon/shared";
92
+ var cache = /* @__PURE__ */ new Map();
93
+ function useBranding() {
82
94
  const { client } = useAuthon();
83
- const containerRef = useRef2(null);
95
+ const [state, setState] = useState2(() => {
96
+ const key = client?.publishableKey;
97
+ return cache.get(key ?? "") ?? { branding: DEFAULT_BRANDING, providers: [], isLoaded: false };
98
+ });
99
+ const fetchedRef = useRef2(false);
100
+ const fetchBranding = useCallback2(async () => {
101
+ if (!client || fetchedRef.current) return;
102
+ const key = client.publishableKey;
103
+ const cached = cache.get(key);
104
+ if (cached) {
105
+ setState(cached);
106
+ return;
107
+ }
108
+ fetchedRef.current = true;
109
+ try {
110
+ const providers = await client.getProviders();
111
+ const apiUrl = client.config?.apiUrl ?? "https://api.authon.dev";
112
+ const res = await fetch(`${apiUrl}/v1/auth/branding`, {
113
+ headers: { "x-api-key": key },
114
+ credentials: "include"
115
+ });
116
+ let branding = DEFAULT_BRANDING;
117
+ if (res.ok) {
118
+ const data = await res.json();
119
+ branding = { ...DEFAULT_BRANDING, ...data };
120
+ }
121
+ const next = { branding, providers, isLoaded: true };
122
+ cache.set(key, next);
123
+ setState(next);
124
+ } catch {
125
+ const fallback = { branding: DEFAULT_BRANDING, providers: [], isLoaded: true };
126
+ setState(fallback);
127
+ }
128
+ }, [client]);
84
129
  useEffect2(() => {
85
- if (mode === "popup") {
86
- client?.openSignIn();
130
+ fetchBranding();
131
+ }, [fetchBranding]);
132
+ return state;
133
+ }
134
+
135
+ // src/components/shared/ThemeProvider.tsx
136
+ import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
137
+ import { DEFAULT_BRANDING as DEFAULT_BRANDING2 } from "@authon/shared";
138
+ import { jsx as jsx2 } from "react/jsx-runtime";
139
+ var ThemeContext = createContext2(null);
140
+ function useTheme() {
141
+ const ctx = useContext2(ThemeContext);
142
+ if (!ctx) {
143
+ return resolveTheme(DEFAULT_BRANDING2, false);
144
+ }
145
+ return ctx;
146
+ }
147
+ function resolveTheme(branding, _dark) {
148
+ const radius = branding.borderRadius ?? DEFAULT_BRANDING2.borderRadius ?? 12;
149
+ return {
150
+ primaryStart: branding.primaryColorStart ?? DEFAULT_BRANDING2.primaryColorStart ?? "#7c3aed",
151
+ primaryEnd: branding.primaryColorEnd ?? DEFAULT_BRANDING2.primaryColorEnd ?? "#4f46e5",
152
+ bg: branding.lightBg ?? DEFAULT_BRANDING2.lightBg ?? "#ffffff",
153
+ text: branding.lightText ?? DEFAULT_BRANDING2.lightText ?? "#111827",
154
+ textMuted: "#6b7280",
155
+ border: "#e5e7eb",
156
+ borderRadius: `${radius}px`,
157
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
158
+ inputStyle: "outline"
159
+ };
160
+ }
161
+ function ThemeProvider({ branding, children, overrides, style, className }) {
162
+ const merged = useMemo2(() => ({ ...branding, ...overrides }), [branding, overrides]);
163
+ const theme = useMemo2(() => resolveTheme(merged, false), [merged]);
164
+ const cssVars = {
165
+ "--authon-primary-start": theme.primaryStart,
166
+ "--authon-primary-end": theme.primaryEnd,
167
+ "--authon-bg": theme.bg,
168
+ "--authon-text": theme.text,
169
+ "--authon-text-muted": theme.textMuted,
170
+ "--authon-border": theme.border,
171
+ "--authon-radius": theme.borderRadius,
172
+ "--authon-font": theme.fontFamily
173
+ };
174
+ return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx2(
175
+ "div",
176
+ {
177
+ className,
178
+ style: {
179
+ fontFamily: theme.fontFamily,
180
+ color: theme.text,
181
+ ...cssVars,
182
+ ...style
183
+ },
184
+ children
87
185
  }
88
- }, [client, mode]);
89
- if (mode === "embedded") {
90
- return /* @__PURE__ */ jsx2("div", { ref: containerRef, id: "authon-signin-container" });
186
+ ) });
187
+ }
188
+
189
+ // src/components/shared/Input.tsx
190
+ import { useState as useState3 } from "react";
191
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
192
+ function Input({
193
+ label,
194
+ error,
195
+ hint,
196
+ inputStyle,
197
+ onChange,
198
+ rightElement,
199
+ style: userStyle,
200
+ ...rest
201
+ }) {
202
+ const theme = useTheme();
203
+ const [focused, setFocused] = useState3(false);
204
+ const resolvedStyle = inputStyle ?? theme.inputStyle;
205
+ const wrapperStyle = {
206
+ display: "flex",
207
+ flexDirection: "column",
208
+ gap: 6,
209
+ width: "100%"
210
+ };
211
+ const labelStyle = {
212
+ fontSize: 13,
213
+ fontWeight: 500,
214
+ color: error ? "#ef4444" : theme.text
215
+ };
216
+ const inputContainerStyle = {
217
+ position: "relative",
218
+ display: "flex",
219
+ alignItems: "center"
220
+ };
221
+ const baseInputStyle = {
222
+ width: "100%",
223
+ height: 44,
224
+ paddingLeft: 14,
225
+ paddingRight: rightElement ? 44 : 14,
226
+ borderRadius: theme.borderRadius,
227
+ fontFamily: theme.fontFamily,
228
+ fontSize: 15,
229
+ color: theme.text,
230
+ outline: "none",
231
+ transition: "border-color 0.15s, box-shadow 0.15s, background 0.15s",
232
+ boxSizing: "border-box",
233
+ ...userStyle
234
+ };
235
+ let inputVariantStyle = {};
236
+ if (resolvedStyle === "filled") {
237
+ inputVariantStyle = {
238
+ background: focused ? `${theme.primaryStart}0d` : "#f3f4f6",
239
+ border: `1.5px solid ${error ? "#ef4444" : focused ? theme.primaryStart : "transparent"}`,
240
+ boxShadow: focused && !error ? `0 0 0 3px ${theme.primaryStart}22` : "none"
241
+ };
242
+ } else {
243
+ inputVariantStyle = {
244
+ background: theme.bg,
245
+ border: `1.5px solid ${error ? "#ef4444" : focused ? theme.primaryStart : theme.border}`,
246
+ boxShadow: focused && !error ? `0 0 0 3px ${theme.primaryStart}22` : "none"
247
+ };
91
248
  }
92
- return null;
249
+ const rightStyle = {
250
+ position: "absolute",
251
+ right: 12,
252
+ display: "flex",
253
+ alignItems: "center",
254
+ color: theme.textMuted
255
+ };
256
+ const hintStyle = {
257
+ fontSize: 12,
258
+ color: error ? "#ef4444" : theme.textMuted
259
+ };
260
+ return /* @__PURE__ */ jsxs("div", { style: wrapperStyle, children: [
261
+ label && /* @__PURE__ */ jsx3("label", { style: labelStyle, children: label }),
262
+ /* @__PURE__ */ jsxs("div", { style: inputContainerStyle, children: [
263
+ /* @__PURE__ */ jsx3(
264
+ "input",
265
+ {
266
+ ...rest,
267
+ style: { ...baseInputStyle, ...inputVariantStyle },
268
+ onFocus: (e) => {
269
+ setFocused(true);
270
+ rest.onFocus?.(e);
271
+ },
272
+ onBlur: (e) => {
273
+ setFocused(false);
274
+ rest.onBlur?.(e);
275
+ },
276
+ onChange: (e) => onChange?.(e.target.value)
277
+ }
278
+ ),
279
+ rightElement && /* @__PURE__ */ jsx3("div", { style: rightStyle, children: rightElement })
280
+ ] }),
281
+ (error || hint) && /* @__PURE__ */ jsx3("span", { style: hintStyle, children: error ?? hint })
282
+ ] });
93
283
  }
94
284
 
95
- // src/SignUp.tsx
96
- import { useEffect as useEffect3 } from "react";
97
- import { jsx as jsx3 } from "react/jsx-runtime";
98
- function SignUp({ mode = "popup" }) {
285
+ // src/components/shared/Button.tsx
286
+ import { useState as useState4 } from "react";
287
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
288
+ var SPINNER_STYLE = {
289
+ display: "inline-block",
290
+ width: 16,
291
+ height: 16,
292
+ border: "2px solid currentColor",
293
+ borderTopColor: "transparent",
294
+ borderRadius: "50%",
295
+ animation: "authon-spin 0.6s linear infinite",
296
+ flexShrink: 0
297
+ };
298
+ function Button({
299
+ variant = "primary",
300
+ size = "md",
301
+ fullWidth = false,
302
+ loading = false,
303
+ disabled = false,
304
+ children,
305
+ onClick,
306
+ type = "button",
307
+ style: userStyle
308
+ }) {
309
+ const theme = useTheme();
310
+ const [hovered, setHovered] = useState4(false);
311
+ const sizeMap = {
312
+ sm: { height: 36, paddingLeft: 12, paddingRight: 12, fontSize: 13 },
313
+ md: { height: 44, paddingLeft: 16, paddingRight: 16, fontSize: 15 },
314
+ lg: { height: 52, paddingLeft: 20, paddingRight: 20, fontSize: 16 }
315
+ };
316
+ const base = {
317
+ display: "inline-flex",
318
+ alignItems: "center",
319
+ justifyContent: "center",
320
+ gap: 8,
321
+ borderRadius: theme.borderRadius,
322
+ fontFamily: theme.fontFamily,
323
+ fontWeight: 600,
324
+ border: "none",
325
+ cursor: disabled || loading ? "not-allowed" : "pointer",
326
+ transition: "opacity 0.15s, transform 0.1s",
327
+ width: fullWidth ? "100%" : void 0,
328
+ opacity: disabled ? 0.55 : hovered && !disabled && !loading ? 0.88 : 1,
329
+ transform: hovered && !disabled && !loading ? "translateY(-1px)" : void 0,
330
+ userSelect: "none",
331
+ ...sizeMap[size]
332
+ };
333
+ let variantStyle = {};
334
+ switch (variant) {
335
+ case "primary":
336
+ variantStyle = {
337
+ background: `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
338
+ color: "#ffffff",
339
+ boxShadow: hovered ? `0 4px 16px ${theme.primaryStart}55` : "0 2px 8px rgba(0,0,0,0.1)"
340
+ };
341
+ break;
342
+ case "secondary":
343
+ variantStyle = {
344
+ background: `${theme.primaryStart}18`,
345
+ color: theme.primaryStart
346
+ };
347
+ break;
348
+ case "outline":
349
+ variantStyle = {
350
+ background: "transparent",
351
+ color: theme.text,
352
+ border: `1.5px solid ${theme.border}`
353
+ };
354
+ break;
355
+ case "ghost":
356
+ variantStyle = {
357
+ background: "transparent",
358
+ color: theme.textMuted
359
+ };
360
+ break;
361
+ case "social":
362
+ variantStyle = {
363
+ background: theme.bg,
364
+ color: theme.text,
365
+ border: `1.5px solid ${theme.border}`
366
+ };
367
+ break;
368
+ }
369
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
370
+ /* @__PURE__ */ jsx4("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
371
+ /* @__PURE__ */ jsx4(
372
+ "button",
373
+ {
374
+ type,
375
+ disabled: disabled || loading,
376
+ onClick,
377
+ onMouseEnter: () => setHovered(true),
378
+ onMouseLeave: () => setHovered(false),
379
+ style: { ...base, ...variantStyle, ...userStyle },
380
+ children: loading ? /* @__PURE__ */ jsx4("span", { style: SPINNER_STYLE }) : children
381
+ }
382
+ )
383
+ ] });
384
+ }
385
+
386
+ // src/components/shared/Divider.tsx
387
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
388
+ function Divider({ label = "Or continue with" }) {
389
+ const theme = useTheme();
390
+ const containerStyle = {
391
+ display: "flex",
392
+ alignItems: "center",
393
+ gap: 12,
394
+ margin: "4px 0"
395
+ };
396
+ const lineStyle = {
397
+ flex: 1,
398
+ height: 1,
399
+ background: theme.border
400
+ };
401
+ const textStyle = {
402
+ fontSize: 13,
403
+ color: theme.textMuted,
404
+ whiteSpace: "nowrap",
405
+ fontWeight: 400
406
+ };
407
+ return /* @__PURE__ */ jsxs3("div", { style: containerStyle, children: [
408
+ /* @__PURE__ */ jsx5("div", { style: lineStyle }),
409
+ /* @__PURE__ */ jsx5("span", { style: textStyle, children: label }),
410
+ /* @__PURE__ */ jsx5("div", { style: lineStyle })
411
+ ] });
412
+ }
413
+
414
+ // src/components/SignIn.tsx
415
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
416
+ function SignInCard({ afterSignInUrl, onSignIn, onNavigateSignUp }) {
417
+ const theme = useTheme();
99
418
  const { client } = useAuthon();
100
- useEffect3(() => {
101
- if (mode === "popup") {
102
- client?.openSignUp();
419
+ const { branding, providers, isLoaded } = useBranding();
420
+ const [email, setEmail] = useState5("");
421
+ const [password, setPassword] = useState5("");
422
+ const [showPassword, setShowPassword] = useState5(false);
423
+ const [loading, setLoading] = useState5(false);
424
+ const [oauthLoading, setOauthLoading] = useState5(null);
425
+ const [error, setError] = useState5("");
426
+ const [fieldErrors, setFieldErrors] = useState5({});
427
+ const validate = () => {
428
+ const errs = {};
429
+ if (!email.trim()) errs.email = "Email is required";
430
+ else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = "Invalid email address";
431
+ if (!password) errs.password = "Password is required";
432
+ setFieldErrors(errs);
433
+ return Object.keys(errs).length === 0;
434
+ };
435
+ const handleSubmit = async () => {
436
+ if (!validate() || !client) return;
437
+ setLoading(true);
438
+ setError("");
439
+ try {
440
+ await client.signInWithEmail(email, password);
441
+ if (afterSignInUrl) window.location.assign(afterSignInUrl);
442
+ onSignIn?.();
443
+ } catch (e) {
444
+ setError(e?.message ?? "Sign in failed");
445
+ } finally {
446
+ setLoading(false);
447
+ }
448
+ };
449
+ const handleOAuth = async (provider) => {
450
+ if (!client) return;
451
+ setOauthLoading(provider);
452
+ setError("");
453
+ try {
454
+ await client.signInWithOAuth(provider);
455
+ } catch (e) {
456
+ setError(e?.message ?? "OAuth sign in failed");
457
+ } finally {
458
+ setOauthLoading(null);
459
+ }
460
+ };
461
+ const cardStyle = {
462
+ width: "100%",
463
+ maxWidth: 440,
464
+ background: theme.bg,
465
+ borderRadius: `calc(${theme.borderRadius} + 4px)`,
466
+ boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
467
+ padding: "40px 36px 32px",
468
+ boxSizing: "border-box",
469
+ display: "flex",
470
+ flexDirection: "column",
471
+ gap: 20,
472
+ fontFamily: theme.fontFamily
473
+ };
474
+ const logoStyle = {
475
+ display: "flex",
476
+ flexDirection: "column",
477
+ alignItems: "center",
478
+ gap: 10
479
+ };
480
+ const titleStyle = {
481
+ fontSize: 24,
482
+ fontWeight: 700,
483
+ color: theme.text,
484
+ textAlign: "center",
485
+ letterSpacing: "-0.3px"
486
+ };
487
+ const subtitleStyle = {
488
+ fontSize: 14,
489
+ color: theme.textMuted,
490
+ textAlign: "center",
491
+ marginTop: -12
492
+ };
493
+ const errorBoxStyle = {
494
+ padding: "10px 14px",
495
+ borderRadius: theme.borderRadius,
496
+ background: "#fef2f2",
497
+ border: "1px solid #fecaca",
498
+ color: "#dc2626",
499
+ fontSize: 13
500
+ };
501
+ const forgotStyle = {
502
+ textAlign: "right",
503
+ marginTop: -12
504
+ };
505
+ const linkStyle = {
506
+ fontSize: 13,
507
+ color: theme.primaryStart,
508
+ background: "none",
509
+ border: "none",
510
+ cursor: "pointer",
511
+ padding: 0,
512
+ fontFamily: theme.fontFamily,
513
+ textDecoration: "none"
514
+ };
515
+ const footerStyle = {
516
+ textAlign: "center",
517
+ fontSize: 14,
518
+ color: theme.textMuted,
519
+ paddingTop: 4
520
+ };
521
+ const eyeIconPath = showPassword ? "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24 M1 1l22 22" : "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z";
522
+ const showEmailPw = branding.showEmailPassword !== false;
523
+ const showDivider = branding.showDivider !== false && providers.length > 0 && showEmailPw;
524
+ const providersToShow = providers.filter(
525
+ (p) => !(branding.hiddenProviders ?? []).includes(p)
526
+ );
527
+ if (!isLoaded) {
528
+ return /* @__PURE__ */ jsx6("div", { style: { ...cardStyle, alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx6(
529
+ "span",
530
+ {
531
+ style: {
532
+ width: 28,
533
+ height: 28,
534
+ border: `3px solid ${theme.primaryStart}33`,
535
+ borderTopColor: theme.primaryStart,
536
+ borderRadius: "50%",
537
+ display: "inline-block",
538
+ animation: "authon-spin 0.7s linear infinite"
539
+ }
540
+ }
541
+ ) });
542
+ }
543
+ return /* @__PURE__ */ jsxs4("div", { style: cardStyle, children: [
544
+ /* @__PURE__ */ jsxs4("div", { style: logoStyle, children: [
545
+ branding.logoDataUrl && /* @__PURE__ */ jsx6("img", { src: branding.logoDataUrl, alt: branding.brandName ?? "Logo", style: { height: 40, objectFit: "contain" } }),
546
+ /* @__PURE__ */ jsx6("h1", { style: titleStyle, children: "Sign in" }),
547
+ branding.brandName && /* @__PURE__ */ jsxs4("p", { style: subtitleStyle, children: [
548
+ "to ",
549
+ branding.brandName
550
+ ] })
551
+ ] }),
552
+ error && /* @__PURE__ */ jsx6("div", { style: errorBoxStyle, children: error }),
553
+ providersToShow.length > 0 && /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: providersToShow.length <= 3 ? providersToShow.map((provider) => /* @__PURE__ */ jsx6(
554
+ OAuthButton,
555
+ {
556
+ provider,
557
+ loading: oauthLoading === provider,
558
+ disabled: !!oauthLoading || loading,
559
+ onClick: () => handleOAuth(provider),
560
+ borderRadius: theme.borderRadius
561
+ },
562
+ provider
563
+ )) : /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, justifyContent: "center" }, children: providersToShow.map((provider) => /* @__PURE__ */ jsx6(
564
+ CompactOAuthButton,
565
+ {
566
+ provider,
567
+ loading: oauthLoading === provider,
568
+ disabled: !!oauthLoading || loading,
569
+ onClick: () => handleOAuth(provider),
570
+ borderRadius: theme.borderRadius
571
+ },
572
+ provider
573
+ )) }) }),
574
+ showDivider && /* @__PURE__ */ jsx6(Divider, {}),
575
+ showEmailPw && /* @__PURE__ */ jsxs4(Fragment2, { children: [
576
+ /* @__PURE__ */ jsx6(
577
+ Input,
578
+ {
579
+ label: "Email",
580
+ type: "email",
581
+ placeholder: "you@example.com",
582
+ value: email,
583
+ onChange: setEmail,
584
+ error: fieldErrors.email,
585
+ autoComplete: "email"
586
+ }
587
+ ),
588
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
589
+ /* @__PURE__ */ jsx6(
590
+ Input,
591
+ {
592
+ label: "Password",
593
+ type: showPassword ? "text" : "password",
594
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
595
+ value: password,
596
+ onChange: setPassword,
597
+ error: fieldErrors.password,
598
+ autoComplete: "current-password",
599
+ rightElement: /* @__PURE__ */ jsx6(
600
+ "button",
601
+ {
602
+ type: "button",
603
+ onClick: () => setShowPassword((v) => !v),
604
+ style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "flex", color: theme.textMuted },
605
+ "aria-label": showPassword ? "Hide password" : "Show password",
606
+ children: /* @__PURE__ */ jsx6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: eyeIconPath }) })
607
+ }
608
+ )
609
+ }
610
+ ),
611
+ /* @__PURE__ */ jsx6("div", { style: forgotStyle, children: /* @__PURE__ */ jsx6("button", { type: "button", style: linkStyle, children: "Forgot password?" }) })
612
+ ] }),
613
+ /* @__PURE__ */ jsx6(
614
+ Button,
615
+ {
616
+ variant: "primary",
617
+ fullWidth: true,
618
+ loading,
619
+ disabled: !!oauthLoading,
620
+ onClick: handleSubmit,
621
+ children: "Sign in"
622
+ }
623
+ )
624
+ ] }),
625
+ /* @__PURE__ */ jsxs4("div", { style: footerStyle, children: [
626
+ "Don't have an account?",
627
+ " ",
628
+ /* @__PURE__ */ jsx6(
629
+ "button",
630
+ {
631
+ type: "button",
632
+ style: linkStyle,
633
+ onClick: onNavigateSignUp,
634
+ children: "Sign up"
635
+ }
636
+ )
637
+ ] }),
638
+ branding.showSecuredBy !== false && /* @__PURE__ */ jsx6(SecuredByAuthon, { primaryStart: theme.primaryStart, textMuted: theme.textMuted }),
639
+ branding.termsUrl || branding.privacyUrl ? /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", fontSize: 11, color: theme.textMuted, marginTop: -8 }, children: [
640
+ branding.termsUrl && /* @__PURE__ */ jsx6("a", { href: branding.termsUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.textMuted }, children: "Terms" }),
641
+ branding.termsUrl && branding.privacyUrl && " \xB7 ",
642
+ branding.privacyUrl && /* @__PURE__ */ jsx6("a", { href: branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.textMuted }, children: "Privacy" })
643
+ ] }) : null
644
+ ] });
645
+ }
646
+ function OAuthButton({ provider, loading, disabled, onClick, borderRadius }) {
647
+ const [hovered, setHovered] = useState5(false);
648
+ const colors = PROVIDER_COLORS[provider] ?? { bg: "#333", text: "#fff" };
649
+ const name = PROVIDER_DISPLAY_NAMES[provider] ?? provider;
650
+ const config = getProviderButtonConfig(provider);
651
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
652
+ const style = {
653
+ display: "flex",
654
+ alignItems: "center",
655
+ gap: 10,
656
+ width: "100%",
657
+ height: 44,
658
+ paddingLeft: 16,
659
+ paddingRight: 16,
660
+ borderRadius,
661
+ background: colors.bg,
662
+ color: colors.text,
663
+ border: needsBorder ? "1.5px solid #dadce0" : "none",
664
+ cursor: disabled ? "not-allowed" : "pointer",
665
+ fontSize: 15,
666
+ fontWeight: 600,
667
+ fontFamily: "system-ui, -apple-system, sans-serif",
668
+ justifyContent: "center",
669
+ opacity: disabled ? 0.6 : hovered ? 0.88 : 1,
670
+ transition: "opacity 0.15s",
671
+ boxSizing: "border-box"
672
+ };
673
+ return /* @__PURE__ */ jsx6(
674
+ "button",
675
+ {
676
+ type: "button",
677
+ style,
678
+ onClick,
679
+ disabled,
680
+ onMouseEnter: () => setHovered(true),
681
+ onMouseLeave: () => setHovered(false),
682
+ "aria-label": `Continue with ${name}`,
683
+ children: loading ? /* @__PURE__ */ jsx6("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
684
+ /* @__PURE__ */ jsx6("span", { style: { display: "flex", alignItems: "center" }, dangerouslySetInnerHTML: { __html: config.iconSvg } }),
685
+ /* @__PURE__ */ jsxs4("span", { children: [
686
+ "Continue with ",
687
+ name
688
+ ] })
689
+ ] })
690
+ }
691
+ );
692
+ }
693
+ function CompactOAuthButton({ provider, loading, disabled, onClick, borderRadius }) {
694
+ const [hovered, setHovered] = useState5(false);
695
+ const colors = PROVIDER_COLORS[provider] ?? { bg: "#333", text: "#fff" };
696
+ const name = PROVIDER_DISPLAY_NAMES[provider] ?? provider;
697
+ const config = getProviderButtonConfig(provider);
698
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
699
+ const style = {
700
+ display: "flex",
701
+ alignItems: "center",
702
+ justifyContent: "center",
703
+ width: 48,
704
+ height: 48,
705
+ borderRadius,
706
+ background: colors.bg,
707
+ border: needsBorder ? "1.5px solid #dadce0" : "none",
708
+ cursor: disabled ? "not-allowed" : "pointer",
709
+ opacity: disabled ? 0.6 : hovered ? 0.85 : 1,
710
+ transition: "opacity 0.15s",
711
+ padding: 0
712
+ };
713
+ return /* @__PURE__ */ jsx6(
714
+ "button",
715
+ {
716
+ type: "button",
717
+ style,
718
+ onClick,
719
+ disabled,
720
+ onMouseEnter: () => setHovered(true),
721
+ onMouseLeave: () => setHovered(false),
722
+ "aria-label": `Continue with ${name}`,
723
+ children: loading ? /* @__PURE__ */ jsx6("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsx6("span", { style: { display: "flex" }, dangerouslySetInnerHTML: { __html: config.iconSvg } })
724
+ }
725
+ );
726
+ }
727
+ function SecuredByAuthon({ primaryStart, textMuted }) {
728
+ return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 5, marginTop: -8 }, children: [
729
+ /* @__PURE__ */ jsxs4("svg", { width: "12", height: "14", viewBox: "0 0 12 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
730
+ /* @__PURE__ */ jsx6("path", { d: "M6 0L0.5 2.5V6.5C0.5 9.7 2.9 12.7 6 13.5C9.1 12.7 11.5 9.7 11.5 6.5V2.5L6 0Z", fill: primaryStart, opacity: "0.85" }),
731
+ /* @__PURE__ */ jsx6("path", { d: "M4 7L5.5 8.5L8.5 5.5", stroke: "white", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
732
+ ] }),
733
+ /* @__PURE__ */ jsxs4("span", { style: { fontSize: 11, color: textMuted }, children: [
734
+ "Secured by",
735
+ " ",
736
+ /* @__PURE__ */ jsx6("a", { href: "https://authon.dev", target: "_blank", rel: "noopener noreferrer", style: { color: primaryStart, textDecoration: "none", fontWeight: 600 }, children: "Authon" })
737
+ ] })
738
+ ] });
739
+ }
740
+ function SignIn({ appearance, afterSignInUrl, onSignIn, onNavigateSignUp }) {
741
+ const { branding, isLoaded } = useBranding();
742
+ const effectiveBranding = isLoaded ? { ...branding, ...appearance?.variables ?? {} } : branding;
743
+ return /* @__PURE__ */ jsxs4(ThemeProvider, { branding: effectiveBranding, overrides: appearance?.variables, style: { display: "flex", justifyContent: "center" }, children: [
744
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
745
+ /* @__PURE__ */ jsx6(
746
+ SignInCard,
747
+ {
748
+ afterSignInUrl,
749
+ onSignIn,
750
+ onNavigateSignUp
751
+ }
752
+ )
753
+ ] });
754
+ }
755
+
756
+ // src/components/SignUp.tsx
757
+ import { useState as useState6 } from "react";
758
+ import { PROVIDER_COLORS as PROVIDER_COLORS2, PROVIDER_DISPLAY_NAMES as PROVIDER_DISPLAY_NAMES2 } from "@authon/shared";
759
+ import { getProviderButtonConfig as getProviderButtonConfig2 } from "@authon/js";
760
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
761
+ function SignUpCard({ afterSignUpUrl, onSignUp, onNavigateSignIn }) {
762
+ const theme = useTheme();
763
+ const { client } = useAuthon();
764
+ const { branding, providers, isLoaded } = useBranding();
765
+ const [displayName, setDisplayName] = useState6("");
766
+ const [email, setEmail] = useState6("");
767
+ const [password, setPassword] = useState6("");
768
+ const [confirmPassword, setConfirmPassword] = useState6("");
769
+ const [showPassword, setShowPassword] = useState6(false);
770
+ const [loading, setLoading] = useState6(false);
771
+ const [oauthLoading, setOauthLoading] = useState6(null);
772
+ const [error, setError] = useState6("");
773
+ const [fieldErrors, setFieldErrors] = useState6({});
774
+ const validate = () => {
775
+ const errs = {};
776
+ if (!email.trim()) errs.email = "Email is required";
777
+ else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) errs.email = "Invalid email address";
778
+ if (!password) errs.password = "Password is required";
779
+ else if (password.length < 8) errs.password = "Password must be at least 8 characters";
780
+ if (confirmPassword !== password) errs.confirmPassword = "Passwords do not match";
781
+ setFieldErrors(errs);
782
+ return Object.keys(errs).length === 0;
783
+ };
784
+ const handleSubmit = async () => {
785
+ if (!validate() || !client) return;
786
+ setLoading(true);
787
+ setError("");
788
+ try {
789
+ await client.signUpWithEmail(email, password, displayName ? { displayName } : void 0);
790
+ if (afterSignUpUrl) window.location.assign(afterSignUpUrl);
791
+ onSignUp?.();
792
+ } catch (e) {
793
+ setError(e?.message ?? "Sign up failed");
794
+ } finally {
795
+ setLoading(false);
796
+ }
797
+ };
798
+ const handleOAuth = async (provider) => {
799
+ if (!client) return;
800
+ setOauthLoading(provider);
801
+ setError("");
802
+ try {
803
+ await client.signInWithOAuth(provider);
804
+ } catch (e) {
805
+ setError(e?.message ?? "OAuth sign in failed");
806
+ } finally {
807
+ setOauthLoading(null);
103
808
  }
104
- }, [client, mode]);
105
- if (mode === "embedded") {
106
- return /* @__PURE__ */ jsx3("div", { id: "authon-signup-container" });
809
+ };
810
+ const cardStyle = {
811
+ width: "100%",
812
+ maxWidth: 440,
813
+ background: theme.bg,
814
+ borderRadius: `calc(${theme.borderRadius} + 4px)`,
815
+ boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
816
+ padding: "40px 36px 32px",
817
+ boxSizing: "border-box",
818
+ display: "flex",
819
+ flexDirection: "column",
820
+ gap: 20,
821
+ fontFamily: theme.fontFamily
822
+ };
823
+ const logoStyle = {
824
+ display: "flex",
825
+ flexDirection: "column",
826
+ alignItems: "center",
827
+ gap: 10
828
+ };
829
+ const titleStyle = {
830
+ fontSize: 24,
831
+ fontWeight: 700,
832
+ color: theme.text,
833
+ textAlign: "center",
834
+ letterSpacing: "-0.3px"
835
+ };
836
+ const subtitleStyle = {
837
+ fontSize: 14,
838
+ color: theme.textMuted,
839
+ textAlign: "center",
840
+ marginTop: -12
841
+ };
842
+ const errorBoxStyle = {
843
+ padding: "10px 14px",
844
+ borderRadius: theme.borderRadius,
845
+ background: "#fef2f2",
846
+ border: "1px solid #fecaca",
847
+ color: "#dc2626",
848
+ fontSize: 13
849
+ };
850
+ const linkStyle = {
851
+ fontSize: 13,
852
+ color: theme.primaryStart,
853
+ background: "none",
854
+ border: "none",
855
+ cursor: "pointer",
856
+ padding: 0,
857
+ fontFamily: theme.fontFamily,
858
+ textDecoration: "none"
859
+ };
860
+ const footerStyle = {
861
+ textAlign: "center",
862
+ fontSize: 14,
863
+ color: theme.textMuted,
864
+ paddingTop: 4
865
+ };
866
+ const showEmailPw = branding.showEmailPassword !== false;
867
+ const showDivider = branding.showDivider !== false && providers.length > 0 && showEmailPw;
868
+ const providersToShow = providers.filter(
869
+ (p) => !(branding.hiddenProviders ?? []).includes(p)
870
+ );
871
+ if (!isLoaded) {
872
+ return /* @__PURE__ */ jsx7("div", { style: { ...cardStyle, alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx7(
873
+ "span",
874
+ {
875
+ style: {
876
+ width: 28,
877
+ height: 28,
878
+ border: `3px solid ${theme.primaryStart}33`,
879
+ borderTopColor: theme.primaryStart,
880
+ borderRadius: "50%",
881
+ display: "inline-block",
882
+ animation: "authon-spin 0.7s linear infinite"
883
+ }
884
+ }
885
+ ) });
107
886
  }
108
- return null;
887
+ const eyeIconPath = showPassword ? "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24 M1 1l22 22" : "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z";
888
+ return /* @__PURE__ */ jsxs5("div", { style: cardStyle, children: [
889
+ /* @__PURE__ */ jsxs5("div", { style: logoStyle, children: [
890
+ branding.logoDataUrl && /* @__PURE__ */ jsx7("img", { src: branding.logoDataUrl, alt: branding.brandName ?? "Logo", style: { height: 40, objectFit: "contain" } }),
891
+ /* @__PURE__ */ jsx7("h1", { style: titleStyle, children: "Create account" }),
892
+ branding.brandName && /* @__PURE__ */ jsxs5("p", { style: subtitleStyle, children: [
893
+ "Join ",
894
+ branding.brandName
895
+ ] })
896
+ ] }),
897
+ error && /* @__PURE__ */ jsx7("div", { style: errorBoxStyle, children: error }),
898
+ providersToShow.length > 0 && /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: providersToShow.length <= 3 ? providersToShow.map((provider) => /* @__PURE__ */ jsx7(
899
+ OAuthButtonFull,
900
+ {
901
+ provider,
902
+ loading: oauthLoading === provider,
903
+ disabled: !!oauthLoading || loading,
904
+ onClick: () => handleOAuth(provider),
905
+ borderRadius: theme.borderRadius
906
+ },
907
+ provider
908
+ )) : /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, justifyContent: "center" }, children: providersToShow.map((provider) => /* @__PURE__ */ jsx7(
909
+ CompactOAuthBtn,
910
+ {
911
+ provider,
912
+ loading: oauthLoading === provider,
913
+ disabled: !!oauthLoading || loading,
914
+ onClick: () => handleOAuth(provider),
915
+ borderRadius: theme.borderRadius
916
+ },
917
+ provider
918
+ )) }) }),
919
+ showDivider && /* @__PURE__ */ jsx7(Divider, {}),
920
+ showEmailPw && /* @__PURE__ */ jsxs5(Fragment3, { children: [
921
+ /* @__PURE__ */ jsx7(
922
+ Input,
923
+ {
924
+ label: "Display name",
925
+ type: "text",
926
+ placeholder: "Your name",
927
+ value: displayName,
928
+ onChange: setDisplayName,
929
+ error: fieldErrors.displayName,
930
+ autoComplete: "name"
931
+ }
932
+ ),
933
+ /* @__PURE__ */ jsx7(
934
+ Input,
935
+ {
936
+ label: "Email",
937
+ type: "email",
938
+ placeholder: "you@example.com",
939
+ value: email,
940
+ onChange: setEmail,
941
+ error: fieldErrors.email,
942
+ autoComplete: "email"
943
+ }
944
+ ),
945
+ /* @__PURE__ */ jsx7(
946
+ Input,
947
+ {
948
+ label: "Password",
949
+ type: showPassword ? "text" : "password",
950
+ placeholder: "Minimum 8 characters",
951
+ value: password,
952
+ onChange: setPassword,
953
+ error: fieldErrors.password,
954
+ autoComplete: "new-password",
955
+ rightElement: /* @__PURE__ */ jsx7(
956
+ "button",
957
+ {
958
+ type: "button",
959
+ onClick: () => setShowPassword((v) => !v),
960
+ style: { background: "none", border: "none", cursor: "pointer", padding: 0, display: "flex", color: theme.textMuted },
961
+ "aria-label": showPassword ? "Hide password" : "Show password",
962
+ children: /* @__PURE__ */ jsx7("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: eyeIconPath }) })
963
+ }
964
+ )
965
+ }
966
+ ),
967
+ /* @__PURE__ */ jsx7(
968
+ Input,
969
+ {
970
+ label: "Confirm password",
971
+ type: showPassword ? "text" : "password",
972
+ placeholder: "Repeat password",
973
+ value: confirmPassword,
974
+ onChange: setConfirmPassword,
975
+ error: fieldErrors.confirmPassword,
976
+ autoComplete: "new-password"
977
+ }
978
+ ),
979
+ /* @__PURE__ */ jsx7(
980
+ Button,
981
+ {
982
+ variant: "primary",
983
+ fullWidth: true,
984
+ loading,
985
+ disabled: !!oauthLoading,
986
+ onClick: handleSubmit,
987
+ children: "Create account"
988
+ }
989
+ )
990
+ ] }),
991
+ /* @__PURE__ */ jsxs5("div", { style: footerStyle, children: [
992
+ "Already have an account?",
993
+ " ",
994
+ /* @__PURE__ */ jsx7("button", { type: "button", style: linkStyle, onClick: onNavigateSignIn, children: "Sign in" })
995
+ ] }),
996
+ branding.showSecuredBy !== false && /* @__PURE__ */ jsx7(SecuredByAuthon2, { primaryStart: theme.primaryStart, textMuted: theme.textMuted }),
997
+ branding.termsUrl || branding.privacyUrl ? /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", fontSize: 11, color: theme.textMuted, marginTop: -8 }, children: [
998
+ "By creating an account you agree to our",
999
+ " ",
1000
+ branding.termsUrl && /* @__PURE__ */ jsx7("a", { href: branding.termsUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.primaryStart }, children: "Terms" }),
1001
+ branding.termsUrl && branding.privacyUrl && " and ",
1002
+ branding.privacyUrl && /* @__PURE__ */ jsx7("a", { href: branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", style: { color: theme.primaryStart }, children: "Privacy Policy" })
1003
+ ] }) : null
1004
+ ] });
1005
+ }
1006
+ function OAuthButtonFull({ provider, loading, disabled, onClick, borderRadius }) {
1007
+ const [hovered, setHovered] = useState6(false);
1008
+ const colors = PROVIDER_COLORS2[provider] ?? { bg: "#333", text: "#fff" };
1009
+ const name = PROVIDER_DISPLAY_NAMES2[provider] ?? provider;
1010
+ const config = getProviderButtonConfig2(provider);
1011
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
1012
+ const style = {
1013
+ display: "flex",
1014
+ alignItems: "center",
1015
+ gap: 10,
1016
+ width: "100%",
1017
+ height: 44,
1018
+ paddingLeft: 16,
1019
+ paddingRight: 16,
1020
+ borderRadius,
1021
+ background: colors.bg,
1022
+ color: colors.text,
1023
+ border: needsBorder ? "1.5px solid #dadce0" : "none",
1024
+ cursor: disabled ? "not-allowed" : "pointer",
1025
+ fontSize: 15,
1026
+ fontWeight: 600,
1027
+ fontFamily: "system-ui, -apple-system, sans-serif",
1028
+ justifyContent: "center",
1029
+ opacity: disabled ? 0.6 : hovered ? 0.88 : 1,
1030
+ transition: "opacity 0.15s",
1031
+ boxSizing: "border-box"
1032
+ };
1033
+ return /* @__PURE__ */ jsx7(
1034
+ "button",
1035
+ {
1036
+ type: "button",
1037
+ style,
1038
+ onClick,
1039
+ disabled,
1040
+ onMouseEnter: () => setHovered(true),
1041
+ onMouseLeave: () => setHovered(false),
1042
+ "aria-label": `Continue with ${name}`,
1043
+ children: loading ? /* @__PURE__ */ jsx7("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
1044
+ /* @__PURE__ */ jsx7("span", { style: { display: "flex", alignItems: "center" }, dangerouslySetInnerHTML: { __html: config.iconSvg } }),
1045
+ /* @__PURE__ */ jsxs5("span", { children: [
1046
+ "Continue with ",
1047
+ name
1048
+ ] })
1049
+ ] })
1050
+ }
1051
+ );
1052
+ }
1053
+ function CompactOAuthBtn({ provider, loading, disabled, onClick, borderRadius }) {
1054
+ const [hovered, setHovered] = useState6(false);
1055
+ const colors = PROVIDER_COLORS2[provider] ?? { bg: "#333", text: "#fff" };
1056
+ const name = PROVIDER_DISPLAY_NAMES2[provider] ?? provider;
1057
+ const config = getProviderButtonConfig2(provider);
1058
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
1059
+ const style = {
1060
+ display: "flex",
1061
+ alignItems: "center",
1062
+ justifyContent: "center",
1063
+ width: 48,
1064
+ height: 48,
1065
+ borderRadius,
1066
+ background: colors.bg,
1067
+ border: needsBorder ? "1.5px solid #dadce0" : "none",
1068
+ cursor: disabled ? "not-allowed" : "pointer",
1069
+ opacity: disabled ? 0.6 : hovered ? 0.85 : 1,
1070
+ transition: "opacity 0.15s",
1071
+ padding: 0
1072
+ };
1073
+ return /* @__PURE__ */ jsx7(
1074
+ "button",
1075
+ {
1076
+ type: "button",
1077
+ style,
1078
+ onClick,
1079
+ disabled,
1080
+ onMouseEnter: () => setHovered(true),
1081
+ onMouseLeave: () => setHovered(false),
1082
+ "aria-label": `Continue with ${name}`,
1083
+ children: loading ? /* @__PURE__ */ jsx7("span", { style: { width: 18, height: 18, border: `2px solid ${colors.text}`, borderTopColor: "transparent", borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.6s linear infinite" } }) : /* @__PURE__ */ jsx7("span", { style: { display: "flex" }, dangerouslySetInnerHTML: { __html: config.iconSvg } })
1084
+ }
1085
+ );
1086
+ }
1087
+ function SecuredByAuthon2({ primaryStart, textMuted }) {
1088
+ return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 5, marginTop: -8 }, children: [
1089
+ /* @__PURE__ */ jsxs5("svg", { width: "12", height: "14", viewBox: "0 0 12 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1090
+ /* @__PURE__ */ jsx7("path", { d: "M6 0L0.5 2.5V6.5C0.5 9.7 2.9 12.7 6 13.5C9.1 12.7 11.5 9.7 11.5 6.5V2.5L6 0Z", fill: primaryStart, opacity: "0.85" }),
1091
+ /* @__PURE__ */ jsx7("path", { d: "M4 7L5.5 8.5L8.5 5.5", stroke: "white", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
1092
+ ] }),
1093
+ /* @__PURE__ */ jsxs5("span", { style: { fontSize: 11, color: textMuted }, children: [
1094
+ "Secured by",
1095
+ " ",
1096
+ /* @__PURE__ */ jsx7("a", { href: "https://authon.dev", target: "_blank", rel: "noopener noreferrer", style: { color: primaryStart, textDecoration: "none", fontWeight: 600 }, children: "Authon" })
1097
+ ] })
1098
+ ] });
1099
+ }
1100
+ function SignUp({ appearance, afterSignUpUrl, onSignUp, onNavigateSignIn }) {
1101
+ const { branding, isLoaded } = useBranding();
1102
+ const effectiveBranding = isLoaded ? { ...branding, ...appearance?.variables ?? {} } : branding;
1103
+ return /* @__PURE__ */ jsxs5(ThemeProvider, { branding: effectiveBranding, overrides: appearance?.variables, style: { display: "flex", justifyContent: "center" }, children: [
1104
+ /* @__PURE__ */ jsx7("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
1105
+ /* @__PURE__ */ jsx7(
1106
+ SignUpCard,
1107
+ {
1108
+ afterSignUpUrl,
1109
+ onSignUp,
1110
+ onNavigateSignIn
1111
+ }
1112
+ )
1113
+ ] });
109
1114
  }
110
1115
 
111
- // src/UserButton.tsx
112
- import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef3, useState as useState2 } from "react";
113
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
114
- function UserButton() {
115
- const { user, signOut, openSignIn, isSignedIn } = useAuthon();
116
- const [open, setOpen] = useState2(false);
1116
+ // src/components/UserButton.tsx
1117
+ import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
1118
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1119
+ function UserAvatar({
1120
+ avatarUrl,
1121
+ displayName,
1122
+ email,
1123
+ size,
1124
+ primaryStart,
1125
+ primaryEnd
1126
+ }) {
1127
+ const initials = displayName ? displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (email?.[0] ?? "?").toUpperCase();
1128
+ const avatarStyle = {
1129
+ width: size,
1130
+ height: size,
1131
+ borderRadius: "50%",
1132
+ overflow: "hidden",
1133
+ display: "flex",
1134
+ alignItems: "center",
1135
+ justifyContent: "center",
1136
+ background: avatarUrl ? "transparent" : `linear-gradient(135deg, ${primaryStart}, ${primaryEnd})`,
1137
+ color: "#fff",
1138
+ fontSize: size * 0.38,
1139
+ fontWeight: 700,
1140
+ userSelect: "none"
1141
+ };
1142
+ return /* @__PURE__ */ jsx8("div", { style: avatarStyle, children: avatarUrl ? /* @__PURE__ */ jsx8("img", { src: avatarUrl, alt: displayName ?? "avatar", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials });
1143
+ }
1144
+ function UserButtonInner({ afterSignOutUrl, userProfileUrl }) {
1145
+ const theme = useTheme();
1146
+ const { user, signOut, openSignIn, isSignedIn, activeOrganization, client } = useAuthon();
1147
+ const [open, setOpen] = useState7(false);
117
1148
  const dropdownRef = useRef3(null);
118
- const handleClickOutside = useCallback2((e) => {
1149
+ const handleClickOutside = useCallback3((e) => {
119
1150
  if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
120
1151
  setOpen(false);
121
1152
  }
122
1153
  }, []);
123
- useEffect4(() => {
124
- if (open) {
125
- document.addEventListener("mousedown", handleClickOutside);
126
- }
1154
+ useEffect3(() => {
1155
+ if (open) document.addEventListener("mousedown", handleClickOutside);
127
1156
  return () => document.removeEventListener("mousedown", handleClickOutside);
128
1157
  }, [open, handleClickOutside]);
129
1158
  if (!isSignedIn) {
130
- return /* @__PURE__ */ jsx4(
1159
+ return /* @__PURE__ */ jsx8(
131
1160
  "button",
132
1161
  {
1162
+ type: "button",
133
1163
  onClick: () => openSignIn(),
134
1164
  style: {
135
- padding: "8px 16px",
136
- borderRadius: "8px",
1165
+ padding: "8px 18px",
1166
+ borderRadius: theme.borderRadius,
137
1167
  border: "none",
138
- background: "linear-gradient(135deg, #7c3aed, #4f46e5)",
1168
+ background: `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
139
1169
  color: "#fff",
140
1170
  cursor: "pointer",
141
- fontSize: "14px",
142
- fontWeight: 600
1171
+ fontSize: 14,
1172
+ fontWeight: 600,
1173
+ fontFamily: theme.fontFamily
143
1174
  },
144
1175
  children: "Sign In"
145
1176
  }
146
1177
  );
147
1178
  }
148
- const initials = user?.displayName ? user.displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (user?.email?.[0] ?? "?").toUpperCase();
149
- return /* @__PURE__ */ jsxs("div", { ref: dropdownRef, style: { position: "relative", display: "inline-block" }, children: [
150
- /* @__PURE__ */ jsx4(
151
- "button",
1179
+ const triggerStyle = {
1180
+ width: 38,
1181
+ height: 38,
1182
+ borderRadius: "50%",
1183
+ border: `2px solid ${theme.primaryStart}`,
1184
+ background: "none",
1185
+ cursor: "pointer",
1186
+ padding: 1,
1187
+ overflow: "hidden",
1188
+ display: "flex",
1189
+ alignItems: "center",
1190
+ justifyContent: "center"
1191
+ };
1192
+ const dropdownStyle = {
1193
+ position: "absolute",
1194
+ right: 0,
1195
+ top: 46,
1196
+ minWidth: 240,
1197
+ background: theme.bg,
1198
+ border: `1px solid ${theme.border}`,
1199
+ borderRadius: theme.borderRadius,
1200
+ boxShadow: "0 8px 32px rgba(0,0,0,0.13)",
1201
+ zIndex: 9999,
1202
+ overflow: "hidden",
1203
+ fontFamily: theme.fontFamily
1204
+ };
1205
+ const headerStyle = {
1206
+ padding: "14px 16px",
1207
+ borderBottom: `1px solid ${theme.border}`,
1208
+ display: "flex",
1209
+ alignItems: "center",
1210
+ gap: 12
1211
+ };
1212
+ const menuItemStyle = (danger) => ({
1213
+ display: "flex",
1214
+ alignItems: "center",
1215
+ gap: 10,
1216
+ width: "100%",
1217
+ padding: "10px 16px",
1218
+ textAlign: "left",
1219
+ background: "none",
1220
+ border: "none",
1221
+ cursor: "pointer",
1222
+ fontSize: 14,
1223
+ color: danger ? "#ef4444" : theme.text,
1224
+ fontWeight: 500,
1225
+ fontFamily: theme.fontFamily,
1226
+ boxSizing: "border-box"
1227
+ });
1228
+ return /* @__PURE__ */ jsxs6("div", { ref: dropdownRef, style: { position: "relative", display: "inline-block" }, children: [
1229
+ /* @__PURE__ */ jsx8("button", { type: "button", style: triggerStyle, onClick: () => setOpen((v) => !v), "aria-label": "User menu", children: /* @__PURE__ */ jsx8(
1230
+ UserAvatar,
152
1231
  {
153
- onClick: () => setOpen((v) => !v),
154
- style: {
155
- width: "36px",
156
- height: "36px",
157
- borderRadius: "50%",
158
- border: "2px solid #7c3aed",
159
- background: user?.avatarUrl ? "transparent" : "linear-gradient(135deg, #7c3aed, #4f46e5)",
160
- cursor: "pointer",
161
- padding: 0,
162
- overflow: "hidden",
163
- display: "flex",
164
- alignItems: "center",
165
- justifyContent: "center",
166
- color: "#fff",
167
- fontSize: "13px",
168
- fontWeight: 700
169
- },
170
- children: user?.avatarUrl ? /* @__PURE__ */ jsx4(
171
- "img",
1232
+ avatarUrl: user?.avatarUrl,
1233
+ displayName: user?.displayName,
1234
+ email: user?.email,
1235
+ size: 32,
1236
+ primaryStart: theme.primaryStart,
1237
+ primaryEnd: theme.primaryEnd
1238
+ }
1239
+ ) }),
1240
+ open && /* @__PURE__ */ jsxs6("div", { style: dropdownStyle, children: [
1241
+ /* @__PURE__ */ jsxs6("div", { style: headerStyle, children: [
1242
+ /* @__PURE__ */ jsx8(
1243
+ UserAvatar,
172
1244
  {
173
- src: user.avatarUrl,
174
- alt: user.displayName ?? "avatar",
175
- style: { width: "100%", height: "100%", objectFit: "cover" }
1245
+ avatarUrl: user?.avatarUrl,
1246
+ displayName: user?.displayName,
1247
+ email: user?.email,
1248
+ size: 40,
1249
+ primaryStart: theme.primaryStart,
1250
+ primaryEnd: theme.primaryEnd
176
1251
  }
177
- ) : initials
178
- }
179
- ),
180
- open && /* @__PURE__ */ jsxs(
181
- "div",
182
- {
183
- style: {
184
- position: "absolute",
185
- right: 0,
186
- top: "44px",
187
- minWidth: "200px",
188
- background: "#fff",
189
- border: "1px solid #e5e7eb",
190
- borderRadius: "12px",
191
- boxShadow: "0 8px 24px rgba(0,0,0,0.12)",
192
- zIndex: 9999,
193
- overflow: "hidden"
194
- },
195
- children: [
196
- /* @__PURE__ */ jsxs(
197
- "div",
198
- {
199
- style: {
200
- padding: "12px 16px",
201
- borderBottom: "1px solid #f3f4f6"
202
- },
203
- children: [
204
- user?.displayName && /* @__PURE__ */ jsx4("div", { style: { fontSize: "14px", fontWeight: 600, color: "#111827" }, children: user.displayName }),
205
- user?.email && /* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", color: "#6b7280", marginTop: "2px" }, children: user.email })
206
- ]
207
- }
208
- ),
209
- /* @__PURE__ */ jsx4(
210
- "button",
211
- {
212
- onClick: async () => {
213
- setOpen(false);
214
- await signOut();
215
- },
216
- style: {
217
- display: "block",
218
- width: "100%",
219
- padding: "10px 16px",
220
- textAlign: "left",
221
- background: "none",
222
- border: "none",
223
- cursor: "pointer",
224
- fontSize: "14px",
225
- color: "#ef4444",
226
- fontWeight: 500
227
- },
228
- onMouseEnter: (e) => {
229
- e.currentTarget.style.background = "#fef2f2";
230
- },
231
- onMouseLeave: (e) => {
232
- e.currentTarget.style.background = "none";
233
- },
234
- children: "Sign out"
235
- }
236
- )
237
- ]
238
- }
239
- )
1252
+ ),
1253
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
1254
+ user?.displayName && /* @__PURE__ */ jsx8("div", { style: { fontSize: 14, fontWeight: 600, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: user.displayName }),
1255
+ user?.email && /* @__PURE__ */ jsx8("div", { style: { fontSize: 12, color: theme.textMuted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 1 }, children: user.email })
1256
+ ] })
1257
+ ] }),
1258
+ activeOrganization && /* @__PURE__ */ jsx8(
1259
+ OrgSwitcherRow,
1260
+ {
1261
+ org: activeOrganization,
1262
+ primaryStart: theme.primaryStart,
1263
+ theme,
1264
+ client
1265
+ }
1266
+ ),
1267
+ userProfileUrl && /* @__PURE__ */ jsx8(
1268
+ MenuButton,
1269
+ {
1270
+ style: menuItemStyle(),
1271
+ onClick: () => {
1272
+ setOpen(false);
1273
+ window.location.assign(userProfileUrl);
1274
+ },
1275
+ icon: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1276
+ /* @__PURE__ */ jsx8("path", { d: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" }),
1277
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "7", r: "4" })
1278
+ ] }),
1279
+ children: "Manage account"
1280
+ }
1281
+ ),
1282
+ /* @__PURE__ */ jsx8("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ jsx8(
1283
+ MenuButton,
1284
+ {
1285
+ style: menuItemStyle(true),
1286
+ onClick: async () => {
1287
+ setOpen(false);
1288
+ await signOut();
1289
+ if (afterSignOutUrl) window.location.assign(afterSignOutUrl);
1290
+ },
1291
+ icon: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1292
+ /* @__PURE__ */ jsx8("path", { d: "M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4" }),
1293
+ /* @__PURE__ */ jsx8("polyline", { points: "16 17 21 12 16 7" }),
1294
+ /* @__PURE__ */ jsx8("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
1295
+ ] }),
1296
+ children: "Sign out"
1297
+ }
1298
+ ) })
1299
+ ] })
240
1300
  ] });
241
1301
  }
1302
+ function MenuButton({
1303
+ style: baseStyle2,
1304
+ onClick,
1305
+ icon,
1306
+ children
1307
+ }) {
1308
+ const [hovered, setHovered] = useState7(false);
1309
+ return /* @__PURE__ */ jsxs6(
1310
+ "button",
1311
+ {
1312
+ type: "button",
1313
+ style: {
1314
+ ...baseStyle2,
1315
+ background: hovered ? baseStyle2.color === "#ef4444" ? "#fef2f2" : "#f9fafb" : "none"
1316
+ },
1317
+ onClick,
1318
+ onMouseEnter: () => setHovered(true),
1319
+ onMouseLeave: () => setHovered(false),
1320
+ children: [
1321
+ icon,
1322
+ children
1323
+ ]
1324
+ }
1325
+ );
1326
+ }
1327
+ function OrgSwitcherRow({
1328
+ org,
1329
+ primaryStart,
1330
+ theme,
1331
+ client
1332
+ }) {
1333
+ return /* @__PURE__ */ jsxs6(
1334
+ "div",
1335
+ {
1336
+ style: {
1337
+ padding: "8px 16px",
1338
+ borderBottom: `1px solid ${theme.border}`,
1339
+ display: "flex",
1340
+ alignItems: "center",
1341
+ gap: 10
1342
+ },
1343
+ children: [
1344
+ org.logoUrl ? /* @__PURE__ */ jsx8("img", { src: org.logoUrl, alt: org.name, style: { width: 24, height: 24, borderRadius: 6, objectFit: "cover" } }) : /* @__PURE__ */ jsx8(
1345
+ "div",
1346
+ {
1347
+ style: {
1348
+ width: 24,
1349
+ height: 24,
1350
+ borderRadius: 6,
1351
+ background: `${primaryStart}22`,
1352
+ color: primaryStart,
1353
+ display: "flex",
1354
+ alignItems: "center",
1355
+ justifyContent: "center",
1356
+ fontSize: 11,
1357
+ fontWeight: 700
1358
+ },
1359
+ children: org.name[0]?.toUpperCase()
1360
+ }
1361
+ ),
1362
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
1363
+ /* @__PURE__ */ jsx8("div", { style: { fontSize: 12, color: theme.textMuted }, children: "Organization" }),
1364
+ /* @__PURE__ */ jsx8("div", { style: { fontSize: 13, fontWeight: 600, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: org.name })
1365
+ ] })
1366
+ ]
1367
+ }
1368
+ );
1369
+ }
1370
+ function UserButton({ appearance, afterSignOutUrl, userProfileUrl }) {
1371
+ const { branding } = useBranding();
1372
+ const effectiveBranding = { ...branding, ...appearance?.variables ?? {} };
1373
+ return /* @__PURE__ */ jsx8(ThemeProvider, { branding: effectiveBranding, style: { display: "inline-block" }, children: /* @__PURE__ */ jsx8(UserButtonInner, { afterSignOutUrl, userProfileUrl }) });
1374
+ }
242
1375
 
243
1376
  // src/SignedIn.tsx
244
- import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
1377
+ import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
245
1378
  function SignedIn({ children }) {
246
1379
  const { isSignedIn, isLoading } = useAuthon();
247
1380
  if (isLoading || !isSignedIn) return null;
248
- return /* @__PURE__ */ jsx5(Fragment, { children });
1381
+ return /* @__PURE__ */ jsx9(Fragment4, { children });
249
1382
  }
250
1383
 
251
1384
  // src/SignedOut.tsx
252
- import { Fragment as Fragment2, jsx as jsx6 } from "react/jsx-runtime";
1385
+ import { Fragment as Fragment5, jsx as jsx10 } from "react/jsx-runtime";
253
1386
  function SignedOut({ children }) {
254
1387
  const { isSignedIn, isLoading } = useAuthon();
255
1388
  if (isLoading || isSignedIn) return null;
256
- return /* @__PURE__ */ jsx6(Fragment2, { children });
1389
+ return /* @__PURE__ */ jsx10(Fragment5, { children });
257
1390
  }
258
1391
 
259
1392
  // src/Protect.tsx
260
- import { Fragment as Fragment3, jsx as jsx7 } from "react/jsx-runtime";
1393
+ import { Fragment as Fragment6, jsx as jsx11 } from "react/jsx-runtime";
261
1394
  function Protect({ children, fallback = null, condition }) {
262
1395
  const { isSignedIn, isLoading, user } = useAuthon();
263
1396
  if (isLoading) return null;
264
- if (!isSignedIn || !user) return /* @__PURE__ */ jsx7(Fragment3, { children: fallback });
265
- if (condition && !condition(user)) return /* @__PURE__ */ jsx7(Fragment3, { children: fallback });
266
- return /* @__PURE__ */ jsx7(Fragment3, { children });
1397
+ if (!isSignedIn || !user) return /* @__PURE__ */ jsx11(Fragment6, { children: fallback });
1398
+ if (condition && !condition(user)) return /* @__PURE__ */ jsx11(Fragment6, { children: fallback });
1399
+ return /* @__PURE__ */ jsx11(Fragment6, { children });
267
1400
  }
268
1401
 
269
1402
  // src/SocialButton.tsx
270
- import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
271
- import { getProviderButtonConfig } from "@authon/js";
272
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
1403
+ import { PROVIDER_COLORS as PROVIDER_COLORS3, PROVIDER_DISPLAY_NAMES as PROVIDER_DISPLAY_NAMES3 } from "@authon/shared";
1404
+ import { getProviderButtonConfig as getProviderButtonConfig3 } from "@authon/js";
1405
+ import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
273
1406
  var baseStyle = {
274
1407
  display: "flex",
275
1408
  alignItems: "center",
@@ -306,16 +1439,16 @@ function SocialButton({
306
1439
  height = 48,
307
1440
  size = 48
308
1441
  }) {
309
- const colors = PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
310
- const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;
1442
+ const colors = PROVIDER_COLORS3[provider] || { bg: "#333", text: "#fff" };
1443
+ const displayName = PROVIDER_DISPLAY_NAMES3[provider] || provider;
311
1444
  const buttonLabel = label ?? `Continue with ${displayName}`;
312
1445
  const needsBorder = colors.bg.toLowerCase() === "#ffffff";
313
1446
  const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
314
- const config = getProviderButtonConfig(provider);
1447
+ const config = getProviderButtonConfig3(provider);
315
1448
  const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
316
1449
  const borderProps = needsBorder ? { border: "1px solid #dadce0" } : {};
317
1450
  if (compact) {
318
- return /* @__PURE__ */ jsx8(
1451
+ return /* @__PURE__ */ jsx12(
319
1452
  "button",
320
1453
  {
321
1454
  className,
@@ -331,7 +1464,7 @@ function SocialButton({
331
1464
  onClick: () => onClick(provider),
332
1465
  disabled: disabled || loading,
333
1466
  "aria-label": `Sign in with ${displayName}`,
334
- children: loading ? /* @__PURE__ */ jsx8(
1467
+ children: loading ? /* @__PURE__ */ jsx12(
335
1468
  "span",
336
1469
  {
337
1470
  style: {
@@ -344,7 +1477,7 @@ function SocialButton({
344
1477
  animation: "authon-spin 0.6s linear infinite"
345
1478
  }
346
1479
  }
347
- ) : /* @__PURE__ */ jsx8(
1480
+ ) : /* @__PURE__ */ jsx12(
348
1481
  "span",
349
1482
  {
350
1483
  style: { display: "flex", alignItems: "center", flexShrink: 0 },
@@ -354,7 +1487,7 @@ function SocialButton({
354
1487
  }
355
1488
  );
356
1489
  }
357
- return /* @__PURE__ */ jsx8(
1490
+ return /* @__PURE__ */ jsx12(
358
1491
  "button",
359
1492
  {
360
1493
  className,
@@ -370,7 +1503,7 @@ function SocialButton({
370
1503
  onClick: () => onClick(provider),
371
1504
  disabled: disabled || loading,
372
1505
  "aria-label": `Sign in with ${displayName}`,
373
- children: loading ? /* @__PURE__ */ jsx8(
1506
+ children: loading ? /* @__PURE__ */ jsx12(
374
1507
  "span",
375
1508
  {
376
1509
  style: {
@@ -383,23 +1516,23 @@ function SocialButton({
383
1516
  animation: "authon-spin 0.6s linear infinite"
384
1517
  }
385
1518
  }
386
- ) : /* @__PURE__ */ jsxs2(Fragment4, { children: [
387
- /* @__PURE__ */ jsx8(
1519
+ ) : /* @__PURE__ */ jsxs7(Fragment7, { children: [
1520
+ /* @__PURE__ */ jsx12(
388
1521
  "span",
389
1522
  {
390
1523
  style: { display: "flex", alignItems: "center", flexShrink: 0 },
391
1524
  dangerouslySetInnerHTML: { __html: iconSvg }
392
1525
  }
393
1526
  ),
394
- /* @__PURE__ */ jsx8("span", { style: { fontSize: 15, fontWeight: 600, whiteSpace: "nowrap" }, children: buttonLabel })
1527
+ /* @__PURE__ */ jsx12("span", { style: { fontSize: 15, fontWeight: 600, whiteSpace: "nowrap" }, children: buttonLabel })
395
1528
  ] })
396
1529
  }
397
1530
  );
398
1531
  }
399
1532
 
400
1533
  // src/SocialButtons.tsx
401
- import { useState as useState3, useEffect as useEffect5 } from "react";
402
- import { jsx as jsx9 } from "react/jsx-runtime";
1534
+ import { useState as useState8, useEffect as useEffect4 } from "react";
1535
+ import { jsx as jsx13 } from "react/jsx-runtime";
403
1536
  function SocialButtons({
404
1537
  onSuccess,
405
1538
  onError,
@@ -411,9 +1544,9 @@ function SocialButtons({
411
1544
  buttonProps
412
1545
  }) {
413
1546
  const { client } = useAuthon();
414
- const [providers, setProviders] = useState3([]);
415
- const [loadingProvider, setLoadingProvider] = useState3(null);
416
- useEffect5(() => {
1547
+ const [providers, setProviders] = useState8([]);
1548
+ const [loadingProvider, setLoadingProvider] = useState8(null);
1549
+ useEffect4(() => {
417
1550
  if (!client) return;
418
1551
  client.getProviders().then((p) => setProviders(p));
419
1552
  }, [client]);
@@ -433,7 +1566,7 @@ function SocialButtons({
433
1566
  }
434
1567
  };
435
1568
  const containerStyle = compact ? { display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: resolvedGap, ...userStyle } : { display: "flex", flexDirection: "column", gap: resolvedGap, ...userStyle };
436
- return /* @__PURE__ */ jsx9("div", { className, style: containerStyle, children: providers.map((provider) => /* @__PURE__ */ jsx9(
1569
+ return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: providers.map((provider) => /* @__PURE__ */ jsx13(
437
1570
  SocialButton,
438
1571
  {
439
1572
  provider,
@@ -447,17 +1580,723 @@ function SocialButtons({
447
1580
  provider
448
1581
  )) });
449
1582
  }
1583
+
1584
+ // src/useAuthonMfa.ts
1585
+ import { useCallback as useCallback4, useContext as useContext3, useState as useState9 } from "react";
1586
+ function useAuthonMfa() {
1587
+ const ctx = useContext3(AuthonContext);
1588
+ if (!ctx) throw new Error("useAuthonMfa must be used within <AuthonProvider>");
1589
+ const [isLoading, setIsLoading] = useState9(false);
1590
+ const [error, setError] = useState9(null);
1591
+ const wrap = useCallback4(
1592
+ async (fn) => {
1593
+ setIsLoading(true);
1594
+ setError(null);
1595
+ try {
1596
+ const result = await fn();
1597
+ return result;
1598
+ } catch (err) {
1599
+ setError(err instanceof Error ? err : new Error(String(err)));
1600
+ return null;
1601
+ } finally {
1602
+ setIsLoading(false);
1603
+ }
1604
+ },
1605
+ []
1606
+ );
1607
+ const setupMfa = useCallback4(async () => {
1608
+ return wrap(() => ctx.client.setupMfa());
1609
+ }, [ctx.client, wrap]);
1610
+ const verifyMfaSetup = useCallback4(
1611
+ async (code) => {
1612
+ const result = await wrap(() => ctx.client.verifyMfaSetup(code));
1613
+ return result !== null;
1614
+ },
1615
+ [ctx.client, wrap]
1616
+ );
1617
+ const verifyMfa = useCallback4(
1618
+ async (mfaToken, code) => {
1619
+ const result = await wrap(() => ctx.client.verifyMfa(mfaToken, code));
1620
+ return result !== null;
1621
+ },
1622
+ [ctx.client, wrap]
1623
+ );
1624
+ const disableMfa = useCallback4(
1625
+ async (code) => {
1626
+ const result = await wrap(() => ctx.client.disableMfa(code));
1627
+ return result !== null;
1628
+ },
1629
+ [ctx.client, wrap]
1630
+ );
1631
+ const getMfaStatus = useCallback4(async () => {
1632
+ return wrap(() => ctx.client.getMfaStatus());
1633
+ }, [ctx.client, wrap]);
1634
+ const regenerateBackupCodes = useCallback4(
1635
+ async (code) => {
1636
+ return wrap(() => ctx.client.regenerateBackupCodes(code));
1637
+ },
1638
+ [ctx.client, wrap]
1639
+ );
1640
+ return {
1641
+ setupMfa,
1642
+ verifyMfaSetup,
1643
+ verifyMfa,
1644
+ disableMfa,
1645
+ getMfaStatus,
1646
+ regenerateBackupCodes,
1647
+ isLoading,
1648
+ error
1649
+ };
1650
+ }
1651
+
1652
+ // src/useAuthonPasskeys.ts
1653
+ import { useCallback as useCallback5, useContext as useContext4, useState as useState10 } from "react";
1654
+ function useAuthonPasskeys() {
1655
+ const ctx = useContext4(AuthonContext);
1656
+ if (!ctx) throw new Error("useAuthonPasskeys must be used within <AuthonProvider>");
1657
+ const [isLoading, setIsLoading] = useState10(false);
1658
+ const [error, setError] = useState10(null);
1659
+ const wrap = useCallback5(
1660
+ async (fn) => {
1661
+ setIsLoading(true);
1662
+ setError(null);
1663
+ try {
1664
+ return await fn();
1665
+ } catch (err) {
1666
+ setError(err instanceof Error ? err : new Error(String(err)));
1667
+ return null;
1668
+ } finally {
1669
+ setIsLoading(false);
1670
+ }
1671
+ },
1672
+ []
1673
+ );
1674
+ const registerPasskey = useCallback5(
1675
+ (name) => wrap(() => ctx.client.registerPasskey(name)),
1676
+ [ctx.client, wrap]
1677
+ );
1678
+ const authenticateWithPasskey = useCallback5(
1679
+ async (email) => {
1680
+ const result = await wrap(() => ctx.client.authenticateWithPasskey(email));
1681
+ return result !== null;
1682
+ },
1683
+ [ctx.client, wrap]
1684
+ );
1685
+ const listPasskeys = useCallback5(
1686
+ () => wrap(() => ctx.client.listPasskeys()),
1687
+ [ctx.client, wrap]
1688
+ );
1689
+ const renamePasskey = useCallback5(
1690
+ (id, name) => wrap(() => ctx.client.renamePasskey(id, name)),
1691
+ [ctx.client, wrap]
1692
+ );
1693
+ const revokePasskey = useCallback5(
1694
+ async (id) => {
1695
+ const result = await wrap(() => ctx.client.revokePasskey(id));
1696
+ return result !== null;
1697
+ },
1698
+ [ctx.client, wrap]
1699
+ );
1700
+ return {
1701
+ registerPasskey,
1702
+ authenticateWithPasskey,
1703
+ listPasskeys,
1704
+ renamePasskey,
1705
+ revokePasskey,
1706
+ isLoading,
1707
+ error
1708
+ };
1709
+ }
1710
+
1711
+ // src/useAuthonPasswordless.ts
1712
+ import { useCallback as useCallback6, useContext as useContext5, useState as useState11 } from "react";
1713
+ function useAuthonPasswordless() {
1714
+ const ctx = useContext5(AuthonContext);
1715
+ if (!ctx) throw new Error("useAuthonPasswordless must be used within <AuthonProvider>");
1716
+ const [isLoading, setIsLoading] = useState11(false);
1717
+ const [error, setError] = useState11(null);
1718
+ const wrap = useCallback6(
1719
+ async (fn) => {
1720
+ setIsLoading(true);
1721
+ setError(null);
1722
+ try {
1723
+ return await fn();
1724
+ } catch (err) {
1725
+ setError(err instanceof Error ? err : new Error(String(err)));
1726
+ return null;
1727
+ } finally {
1728
+ setIsLoading(false);
1729
+ }
1730
+ },
1731
+ []
1732
+ );
1733
+ const sendMagicLink = useCallback6(
1734
+ async (email) => {
1735
+ const result = await wrap(() => ctx.client.sendMagicLink(email));
1736
+ return result !== null;
1737
+ },
1738
+ [ctx.client, wrap]
1739
+ );
1740
+ const sendEmailOtp = useCallback6(
1741
+ async (email) => {
1742
+ const result = await wrap(() => ctx.client.sendEmailOtp(email));
1743
+ return result !== null;
1744
+ },
1745
+ [ctx.client, wrap]
1746
+ );
1747
+ const verifyPasswordless = useCallback6(
1748
+ async (opts) => {
1749
+ const result = await wrap(() => ctx.client.verifyPasswordless(opts));
1750
+ return result !== null;
1751
+ },
1752
+ [ctx.client, wrap]
1753
+ );
1754
+ return {
1755
+ sendMagicLink,
1756
+ sendEmailOtp,
1757
+ verifyPasswordless,
1758
+ isLoading,
1759
+ error
1760
+ };
1761
+ }
1762
+
1763
+ // src/useAuthonWeb3.ts
1764
+ import { useCallback as useCallback7, useContext as useContext6, useState as useState12 } from "react";
1765
+ function useAuthonWeb3() {
1766
+ const ctx = useContext6(AuthonContext);
1767
+ if (!ctx) throw new Error("useAuthonWeb3 must be used within <AuthonProvider>");
1768
+ const [isLoading, setIsLoading] = useState12(false);
1769
+ const [error, setError] = useState12(null);
1770
+ const wrap = useCallback7(
1771
+ async (fn) => {
1772
+ setIsLoading(true);
1773
+ setError(null);
1774
+ try {
1775
+ return await fn();
1776
+ } catch (err) {
1777
+ setError(err instanceof Error ? err : new Error(String(err)));
1778
+ return null;
1779
+ } finally {
1780
+ setIsLoading(false);
1781
+ }
1782
+ },
1783
+ []
1784
+ );
1785
+ const getNonce = useCallback7(
1786
+ (address, chain, walletType, chainId) => wrap(() => ctx.client.web3GetNonce(address, chain, walletType, chainId)),
1787
+ [ctx.client, wrap]
1788
+ );
1789
+ const verify = useCallback7(
1790
+ async (message, signature, address, chain, walletType) => {
1791
+ const result = await wrap(
1792
+ () => ctx.client.web3Verify(message, signature, address, chain, walletType)
1793
+ );
1794
+ return result !== null;
1795
+ },
1796
+ [ctx.client, wrap]
1797
+ );
1798
+ const listWallets = useCallback7(
1799
+ () => wrap(() => ctx.client.listWallets()),
1800
+ [ctx.client, wrap]
1801
+ );
1802
+ const linkWallet = useCallback7(
1803
+ (params) => wrap(() => ctx.client.linkWallet(params)),
1804
+ [ctx.client, wrap]
1805
+ );
1806
+ const unlinkWallet = useCallback7(
1807
+ async (walletId) => {
1808
+ const result = await wrap(() => ctx.client.unlinkWallet(walletId));
1809
+ return result !== null;
1810
+ },
1811
+ [ctx.client, wrap]
1812
+ );
1813
+ return {
1814
+ getNonce,
1815
+ verify,
1816
+ listWallets,
1817
+ linkWallet,
1818
+ unlinkWallet,
1819
+ isLoading,
1820
+ error
1821
+ };
1822
+ }
1823
+
1824
+ // src/useAuthonSessions.ts
1825
+ import { useCallback as useCallback8, useContext as useContext7, useState as useState13 } from "react";
1826
+ function useAuthonSessions() {
1827
+ const ctx = useContext7(AuthonContext);
1828
+ if (!ctx) throw new Error("useAuthonSessions must be used within <AuthonProvider>");
1829
+ const [isLoading, setIsLoading] = useState13(false);
1830
+ const [error, setError] = useState13(null);
1831
+ const wrap = useCallback8(
1832
+ async (fn) => {
1833
+ setIsLoading(true);
1834
+ setError(null);
1835
+ try {
1836
+ return await fn();
1837
+ } catch (err) {
1838
+ setError(err instanceof Error ? err : new Error(String(err)));
1839
+ return null;
1840
+ } finally {
1841
+ setIsLoading(false);
1842
+ }
1843
+ },
1844
+ []
1845
+ );
1846
+ const listSessions = useCallback8(
1847
+ () => wrap(() => ctx.client.listSessions()),
1848
+ [ctx.client, wrap]
1849
+ );
1850
+ const revokeSession = useCallback8(
1851
+ async (sessionId) => {
1852
+ const result = await wrap(() => ctx.client.revokeSession(sessionId));
1853
+ return result !== null;
1854
+ },
1855
+ [ctx.client, wrap]
1856
+ );
1857
+ return {
1858
+ listSessions,
1859
+ revokeSession,
1860
+ isLoading,
1861
+ error
1862
+ };
1863
+ }
1864
+
1865
+ // src/useOrganization.ts
1866
+ import { useContext as useContext8, useEffect as useEffect5, useState as useState14 } from "react";
1867
+ function useOrganization() {
1868
+ const ctx = useContext8(AuthonContext);
1869
+ if (!ctx) throw new Error("useOrganization must be used within <AuthonProvider>");
1870
+ const [members, setMembers] = useState14([]);
1871
+ const [isLoaded, setIsLoaded] = useState14(false);
1872
+ const organization = ctx.activeOrganization;
1873
+ useEffect5(() => {
1874
+ if (!organization || !ctx.client) {
1875
+ setMembers([]);
1876
+ setIsLoaded(!organization);
1877
+ return;
1878
+ }
1879
+ setIsLoaded(false);
1880
+ ctx.client.organizations.getMembers(organization.id).then((m) => {
1881
+ setMembers(m);
1882
+ setIsLoaded(true);
1883
+ }).catch(() => {
1884
+ setMembers([]);
1885
+ setIsLoaded(true);
1886
+ });
1887
+ }, [organization?.id, ctx.client]);
1888
+ return {
1889
+ organization,
1890
+ members,
1891
+ isLoaded
1892
+ };
1893
+ }
1894
+
1895
+ // src/useOrganizationList.ts
1896
+ import { useCallback as useCallback10, useContext as useContext9, useEffect as useEffect6, useState as useState15 } from "react";
1897
+ function useOrganizationList() {
1898
+ const ctx = useContext9(AuthonContext);
1899
+ if (!ctx) throw new Error("useOrganizationList must be used within <AuthonProvider>");
1900
+ const [organizations, setOrganizations] = useState15([]);
1901
+ const [isLoaded, setIsLoaded] = useState15(false);
1902
+ useEffect6(() => {
1903
+ if (!ctx.client || !ctx.isSignedIn) {
1904
+ setOrganizations([]);
1905
+ setIsLoaded(!ctx.isSignedIn);
1906
+ return;
1907
+ }
1908
+ setIsLoaded(false);
1909
+ ctx.client.organizations.list().then((res) => {
1910
+ setOrganizations(res.data);
1911
+ setIsLoaded(true);
1912
+ }).catch(() => {
1913
+ setOrganizations([]);
1914
+ setIsLoaded(true);
1915
+ });
1916
+ }, [ctx.client, ctx.isSignedIn]);
1917
+ const createOrganization = useCallback10(
1918
+ async (params) => {
1919
+ if (!ctx.client) return null;
1920
+ try {
1921
+ const org = await ctx.client.organizations.create(params);
1922
+ setOrganizations((prev) => [...prev, org]);
1923
+ return org;
1924
+ } catch {
1925
+ return null;
1926
+ }
1927
+ },
1928
+ [ctx.client]
1929
+ );
1930
+ const setActive = useCallback10(
1931
+ (org) => {
1932
+ ctx.setActiveOrganization(org);
1933
+ },
1934
+ [ctx.setActiveOrganization]
1935
+ );
1936
+ return {
1937
+ organizations,
1938
+ isLoaded,
1939
+ createOrganization,
1940
+ setActive
1941
+ };
1942
+ }
1943
+
1944
+ // src/components/UserProfile.tsx
1945
+ import { useCallback as useCallback11, useEffect as useEffect7, useState as useState16 } from "react";
1946
+ import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1947
+ function ProfileTab() {
1948
+ const theme = useTheme();
1949
+ const { user, client } = useAuthon();
1950
+ const [displayName, setDisplayName] = useState16(user?.displayName ?? "");
1951
+ const [phone, setPhone] = useState16(user?.phone ?? "");
1952
+ const [loading, setLoading] = useState16(false);
1953
+ const [success, setSuccess] = useState16("");
1954
+ const [error, setError] = useState16("");
1955
+ const handleSave = async () => {
1956
+ if (!client) return;
1957
+ setLoading(true);
1958
+ setError("");
1959
+ setSuccess("");
1960
+ try {
1961
+ await client.updateProfile({ displayName: displayName || void 0, phone: phone || void 0 });
1962
+ setSuccess("Profile updated");
1963
+ } catch (e) {
1964
+ setError(e?.message ?? "Failed to update profile");
1965
+ } finally {
1966
+ setLoading(false);
1967
+ }
1968
+ };
1969
+ const initials = user?.displayName ? user.displayName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : (user?.email?.[0] ?? "?").toUpperCase();
1970
+ return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 24 }, children: [
1971
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 16 }, children: [
1972
+ /* @__PURE__ */ jsx14(
1973
+ "div",
1974
+ {
1975
+ style: {
1976
+ width: 64,
1977
+ height: 64,
1978
+ borderRadius: "50%",
1979
+ background: user?.avatarUrl ? "transparent" : `linear-gradient(135deg, ${theme.primaryStart}, ${theme.primaryEnd})`,
1980
+ color: "#fff",
1981
+ display: "flex",
1982
+ alignItems: "center",
1983
+ justifyContent: "center",
1984
+ fontSize: 22,
1985
+ fontWeight: 700,
1986
+ overflow: "hidden",
1987
+ flexShrink: 0
1988
+ },
1989
+ children: user?.avatarUrl ? /* @__PURE__ */ jsx14("img", { src: user.avatarUrl, alt: "avatar", style: { width: "100%", height: "100%", objectFit: "cover" } }) : initials
1990
+ }
1991
+ ),
1992
+ /* @__PURE__ */ jsxs8("div", { children: [
1993
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 16, fontWeight: 600, color: theme.text }, children: user?.displayName ?? "User" }),
1994
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 13, color: theme.textMuted }, children: user?.email }),
1995
+ !user?.emailVerified && /* @__PURE__ */ jsx14("span", { style: { fontSize: 11, color: "#d97706", background: "#fef3c7", padding: "2px 8px", borderRadius: 99, fontWeight: 500, marginTop: 4, display: "inline-block" }, children: "Email not verified" })
1996
+ ] })
1997
+ ] }),
1998
+ success && /* @__PURE__ */ jsx14("div", { style: { padding: "10px 14px", borderRadius: theme.borderRadius, background: "#f0fdf4", border: "1px solid #bbf7d0", color: "#166534", fontSize: 13 }, children: success }),
1999
+ error && /* @__PURE__ */ jsx14("div", { style: { padding: "10px 14px", borderRadius: theme.borderRadius, background: "#fef2f2", border: "1px solid #fecaca", color: "#dc2626", fontSize: 13 }, children: error }),
2000
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
2001
+ /* @__PURE__ */ jsx14(Input, { label: "Display name", value: displayName, onChange: setDisplayName, placeholder: "Your name" }),
2002
+ /* @__PURE__ */ jsx14(Input, { label: "Email", type: "email", value: user?.email ?? "", disabled: true, placeholder: "Email" }),
2003
+ /* @__PURE__ */ jsx14(Input, { label: "Phone", type: "tel", value: phone, onChange: setPhone, placeholder: "+1 (555) 000-0000" })
2004
+ ] }),
2005
+ /* @__PURE__ */ jsx14(Button, { variant: "primary", onClick: handleSave, loading, style: { alignSelf: "flex-start", minWidth: 120 }, children: "Save changes" })
2006
+ ] });
2007
+ }
2008
+ function SecurityTab() {
2009
+ const theme = useTheme();
2010
+ const { client } = useAuthon();
2011
+ const [currentPw, setCurrentPw] = useState16("");
2012
+ const [newPw, setNewPw] = useState16("");
2013
+ const [confirmPw, setConfirmPw] = useState16("");
2014
+ const [pwLoading, setPwLoading] = useState16(false);
2015
+ const [pwError, setPwError] = useState16("");
2016
+ const [pwSuccess, setPwSuccess] = useState16("");
2017
+ const [mfaStatus, setMfaStatus] = useState16(null);
2018
+ const [mfaLoading, setMfaLoading] = useState16(false);
2019
+ useEffect7(() => {
2020
+ if (!client) return;
2021
+ client.getMfaStatus().then(setMfaStatus).catch(() => null);
2022
+ }, [client]);
2023
+ const handlePasswordChange = async () => {
2024
+ if (!client) return;
2025
+ if (newPw !== confirmPw) {
2026
+ setPwError("Passwords do not match");
2027
+ return;
2028
+ }
2029
+ if (newPw.length < 8) {
2030
+ setPwError("Password must be at least 8 characters");
2031
+ return;
2032
+ }
2033
+ setPwLoading(true);
2034
+ setPwError("");
2035
+ setPwSuccess("");
2036
+ try {
2037
+ await client.updateProfile({ displayName: void 0 });
2038
+ setPwSuccess("Password updated");
2039
+ setCurrentPw("");
2040
+ setNewPw("");
2041
+ setConfirmPw("");
2042
+ } catch (e) {
2043
+ setPwError(e?.message ?? "Failed to update password");
2044
+ } finally {
2045
+ setPwLoading(false);
2046
+ }
2047
+ };
2048
+ const sectionTitle = {
2049
+ fontSize: 15,
2050
+ fontWeight: 600,
2051
+ color: theme.text,
2052
+ marginBottom: 16,
2053
+ paddingBottom: 10,
2054
+ borderBottom: `1px solid ${theme.border}`
2055
+ };
2056
+ return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 32 }, children: [
2057
+ /* @__PURE__ */ jsxs8("div", { children: [
2058
+ /* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Change password" }),
2059
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
2060
+ /* @__PURE__ */ jsx14(Input, { label: "Current password", type: "password", value: currentPw, onChange: setCurrentPw, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "current-password" }),
2061
+ /* @__PURE__ */ jsx14(Input, { label: "New password", type: "password", value: newPw, onChange: setNewPw, placeholder: "Minimum 8 characters", autoComplete: "new-password" }),
2062
+ /* @__PURE__ */ jsx14(Input, { label: "Confirm new password", type: "password", value: confirmPw, onChange: setConfirmPw, placeholder: "Repeat new password", autoComplete: "new-password" }),
2063
+ pwError && /* @__PURE__ */ jsx14("div", { style: { color: "#dc2626", fontSize: 13 }, children: pwError }),
2064
+ pwSuccess && /* @__PURE__ */ jsx14("div", { style: { color: "#166534", fontSize: 13 }, children: pwSuccess }),
2065
+ /* @__PURE__ */ jsx14(Button, { variant: "primary", onClick: handlePasswordChange, loading: pwLoading, style: { alignSelf: "flex-start", minWidth: 160 }, children: "Update password" })
2066
+ ] })
2067
+ ] }),
2068
+ /* @__PURE__ */ jsxs8("div", { children: [
2069
+ /* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Two-factor authentication" }),
2070
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 0" }, children: [
2071
+ /* @__PURE__ */ jsxs8("div", { children: [
2072
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 14, fontWeight: 500, color: theme.text }, children: "Authenticator app" }),
2073
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 13, color: theme.textMuted, marginTop: 2 }, children: mfaStatus?.enabled ? `Enabled \xB7 ${mfaStatus.backupCodesRemaining} backup codes remaining` : "Add extra security to your account" })
2074
+ ] }),
2075
+ /* @__PURE__ */ jsx14("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: /* @__PURE__ */ jsxs8(
2076
+ "span",
2077
+ {
2078
+ style: {
2079
+ display: "inline-flex",
2080
+ alignItems: "center",
2081
+ gap: 4,
2082
+ padding: "3px 10px",
2083
+ borderRadius: 99,
2084
+ fontSize: 12,
2085
+ fontWeight: 600,
2086
+ background: mfaStatus?.enabled ? "#f0fdf4" : "#f3f4f6",
2087
+ color: mfaStatus?.enabled ? "#166534" : theme.textMuted
2088
+ },
2089
+ children: [
2090
+ /* @__PURE__ */ jsx14("span", { style: { width: 6, height: 6, borderRadius: "50%", background: mfaStatus?.enabled ? "#22c55e" : "#9ca3af", display: "inline-block" } }),
2091
+ mfaStatus?.enabled ? "Enabled" : "Disabled"
2092
+ ]
2093
+ }
2094
+ ) })
2095
+ ] })
2096
+ ] })
2097
+ ] });
2098
+ }
2099
+ function SessionsTab() {
2100
+ const theme = useTheme();
2101
+ const { client } = useAuthon();
2102
+ const [sessions, setSessions] = useState16([]);
2103
+ const [loading, setLoading] = useState16(true);
2104
+ const [revoking, setRevoking] = useState16(null);
2105
+ const loadSessions = useCallback11(async () => {
2106
+ if (!client) return;
2107
+ setLoading(true);
2108
+ try {
2109
+ const data = await client.listSessions();
2110
+ setSessions(data);
2111
+ } catch {
2112
+ } finally {
2113
+ setLoading(false);
2114
+ }
2115
+ }, [client]);
2116
+ useEffect7(() => {
2117
+ loadSessions();
2118
+ }, [loadSessions]);
2119
+ const handleRevoke = async (sessionId) => {
2120
+ if (!client) return;
2121
+ setRevoking(sessionId);
2122
+ try {
2123
+ await client.revokeSession(sessionId);
2124
+ setSessions((prev) => prev.filter((s) => s.id !== sessionId));
2125
+ } catch {
2126
+ } finally {
2127
+ setRevoking(null);
2128
+ }
2129
+ };
2130
+ const sectionTitle = {
2131
+ fontSize: 15,
2132
+ fontWeight: 600,
2133
+ color: theme.text,
2134
+ marginBottom: 16,
2135
+ paddingBottom: 10,
2136
+ borderBottom: `1px solid ${theme.border}`
2137
+ };
2138
+ if (loading) {
2139
+ return /* @__PURE__ */ jsx14("div", { style: { display: "flex", justifyContent: "center", padding: 40 }, children: /* @__PURE__ */ jsx14("span", { style: { width: 24, height: 24, border: `3px solid ${theme.primaryStart}33`, borderTopColor: theme.primaryStart, borderRadius: "50%", display: "inline-block", animation: "authon-spin 0.7s linear infinite" } }) });
2140
+ }
2141
+ return /* @__PURE__ */ jsxs8("div", { children: [
2142
+ /* @__PURE__ */ jsx14("div", { style: sectionTitle, children: "Active sessions" }),
2143
+ /* @__PURE__ */ jsx14("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: sessions.length === 0 ? /* @__PURE__ */ jsx14("div", { style: { color: theme.textMuted, fontSize: 14, textAlign: "center", padding: "24px 0" }, children: "No active sessions" }) : sessions.map((session) => /* @__PURE__ */ jsxs8(
2144
+ "div",
2145
+ {
2146
+ style: {
2147
+ display: "flex",
2148
+ alignItems: "center",
2149
+ justifyContent: "space-between",
2150
+ padding: "12px 14px",
2151
+ borderRadius: theme.borderRadius,
2152
+ border: `1px solid ${theme.border}`,
2153
+ gap: 12
2154
+ },
2155
+ children: [
2156
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 12, flex: 1, minWidth: 0 }, children: [
2157
+ /* @__PURE__ */ jsx14("div", { style: { width: 36, height: 36, borderRadius: "50%", background: `${theme.primaryStart}18`, color: theme.primaryStart, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }, children: /* @__PURE__ */ jsxs8("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2158
+ /* @__PURE__ */ jsx14("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
2159
+ /* @__PURE__ */ jsx14("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
2160
+ /* @__PURE__ */ jsx14("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
2161
+ ] }) }),
2162
+ /* @__PURE__ */ jsxs8("div", { style: { minWidth: 0 }, children: [
2163
+ /* @__PURE__ */ jsx14("div", { style: { fontSize: 13, fontWeight: 500, color: theme.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: session.userAgent ? session.userAgent.length > 50 ? session.userAgent.slice(0, 50) + "\u2026" : session.userAgent : "Unknown device" }),
2164
+ /* @__PURE__ */ jsxs8("div", { style: { fontSize: 12, color: theme.textMuted, marginTop: 2 }, children: [
2165
+ session.ipAddress ?? "Unknown IP",
2166
+ session.lastActiveAt && /* @__PURE__ */ jsxs8(Fragment8, { children: [
2167
+ " \xB7 ",
2168
+ formatRelative(session.lastActiveAt)
2169
+ ] })
2170
+ ] })
2171
+ ] })
2172
+ ] }),
2173
+ /* @__PURE__ */ jsx14(
2174
+ Button,
2175
+ {
2176
+ variant: "outline",
2177
+ size: "sm",
2178
+ loading: revoking === session.id,
2179
+ onClick: () => handleRevoke(session.id),
2180
+ children: "Revoke"
2181
+ }
2182
+ )
2183
+ ]
2184
+ },
2185
+ session.id
2186
+ )) })
2187
+ ] });
2188
+ }
2189
+ function formatRelative(dateStr) {
2190
+ const diff = Date.now() - new Date(dateStr).getTime();
2191
+ const mins = Math.floor(diff / 6e4);
2192
+ if (mins < 1) return "Just now";
2193
+ if (mins < 60) return `${mins}m ago`;
2194
+ const hours = Math.floor(mins / 60);
2195
+ if (hours < 24) return `${hours}h ago`;
2196
+ const days = Math.floor(hours / 24);
2197
+ return `${days}d ago`;
2198
+ }
2199
+ function UserProfilePanel() {
2200
+ const theme = useTheme();
2201
+ const [tab, setTab] = useState16("profile");
2202
+ const panelStyle = {
2203
+ width: "100%",
2204
+ maxWidth: 640,
2205
+ background: theme.bg,
2206
+ borderRadius: `calc(${theme.borderRadius} + 4px)`,
2207
+ boxShadow: "0 4px 32px rgba(0,0,0,0.10)",
2208
+ overflow: "hidden",
2209
+ fontFamily: theme.fontFamily
2210
+ };
2211
+ const tabBarStyle = {
2212
+ display: "flex",
2213
+ borderBottom: `1px solid ${theme.border}`,
2214
+ padding: "0 24px"
2215
+ };
2216
+ const tabBtnStyle = (active) => ({
2217
+ padding: "14px 16px",
2218
+ background: "none",
2219
+ border: "none",
2220
+ borderBottom: active ? `2px solid ${theme.primaryStart}` : "2px solid transparent",
2221
+ color: active ? theme.primaryStart : theme.textMuted,
2222
+ fontWeight: active ? 600 : 400,
2223
+ fontSize: 14,
2224
+ cursor: "pointer",
2225
+ fontFamily: theme.fontFamily,
2226
+ marginBottom: -1
2227
+ });
2228
+ const contentStyle = {
2229
+ padding: "28px 28px"
2230
+ };
2231
+ const tabs = [
2232
+ { key: "profile", label: "Profile" },
2233
+ { key: "security", label: "Security" },
2234
+ { key: "sessions", label: "Sessions" }
2235
+ ];
2236
+ return /* @__PURE__ */ jsxs8("div", { style: panelStyle, children: [
2237
+ /* @__PURE__ */ jsx14("style", { children: `@keyframes authon-spin { to { transform: rotate(360deg); } }` }),
2238
+ /* @__PURE__ */ jsx14("div", { style: tabBarStyle, children: tabs.map(({ key, label }) => /* @__PURE__ */ jsx14(
2239
+ "button",
2240
+ {
2241
+ type: "button",
2242
+ style: tabBtnStyle(tab === key),
2243
+ onClick: () => setTab(key),
2244
+ children: label
2245
+ },
2246
+ key
2247
+ )) }),
2248
+ /* @__PURE__ */ jsxs8("div", { style: contentStyle, children: [
2249
+ tab === "profile" && /* @__PURE__ */ jsx14(ProfileTab, {}),
2250
+ tab === "security" && /* @__PURE__ */ jsx14(SecurityTab, {}),
2251
+ tab === "sessions" && /* @__PURE__ */ jsx14(SessionsTab, {})
2252
+ ] })
2253
+ ] });
2254
+ }
2255
+ function UserProfile({ appearance }) {
2256
+ const { branding } = useBranding();
2257
+ const effectiveBranding = { ...branding, ...appearance?.variables ?? {} };
2258
+ return /* @__PURE__ */ jsx14(ThemeProvider, { branding: effectiveBranding, style: { display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx14(UserProfilePanel, {}) });
2259
+ }
2260
+
2261
+ // src/components/shared/ProviderIcon.tsx
2262
+ import { getProviderButtonConfig as getProviderButtonConfig4 } from "@authon/js";
2263
+ import { jsx as jsx15 } from "react/jsx-runtime";
2264
+ function ProviderIcon({ provider, size = 20 }) {
2265
+ const config = getProviderButtonConfig4(provider);
2266
+ const svg = config.iconSvg.replace(/width="\d+"/, `width="${size}"`).replace(/height="\d+"/, `height="${size}"`);
2267
+ return /* @__PURE__ */ jsx15(
2268
+ "span",
2269
+ {
2270
+ style: { display: "flex", alignItems: "center", flexShrink: 0 },
2271
+ dangerouslySetInnerHTML: { __html: svg }
2272
+ }
2273
+ );
2274
+ }
450
2275
  export {
451
2276
  AuthonProvider,
2277
+ Button,
2278
+ Divider,
2279
+ Input,
452
2280
  Protect,
2281
+ ProviderIcon,
453
2282
  SignIn,
454
2283
  SignUp,
455
2284
  SignedIn,
456
2285
  SignedOut,
457
2286
  SocialButton,
458
2287
  SocialButtons,
2288
+ ThemeProvider,
459
2289
  UserButton,
2290
+ UserProfile,
460
2291
  useAuthon,
2292
+ useAuthonMfa,
2293
+ useAuthonPasskeys,
2294
+ useAuthonPasswordless,
2295
+ useAuthonSessions,
2296
+ useAuthonWeb3,
2297
+ useBranding,
2298
+ useOrganization,
2299
+ useOrganizationList,
461
2300
  useUser
462
2301
  };
463
2302
  //# sourceMappingURL=index.js.map