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