cauthy-auth 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  // src/react.tsx
4
- import { createContext, useCallback, useContext, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
4
+ import { createContext, useCallback as useCallback2, useContext, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
5
5
 
6
6
  // src/passkey.ts
7
7
  function canUsePasskeys() {
@@ -721,7 +721,7 @@ function definePolicy(given) {
721
721
  }
722
722
 
723
723
  // src/components.tsx
724
- import { Fragment, useEffect, useId, useRef, useState } from "react";
724
+ import { Fragment, useCallback, useEffect, useId, useRef, useState } from "react";
725
725
  import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
726
726
  var TEXTS = {
727
727
  en: {
@@ -861,6 +861,7 @@ var TEXTS = {
861
861
  last_manager: "Somebody else must manage the members first: give that role to another member.",
862
862
  last_owner: "An organization keeps at least one owner: make another member owner first.",
863
863
  organization_not_found: "That organization no longer exists.",
864
+ member_exists: "That person already belongs to the organization; their role was not changed. Change it in the list instead.",
864
865
  already_member: "That person already belongs to the organization. Change their role instead.",
865
866
  unknown_role: "Choose one of the roles offered.",
866
867
  user_not_active: "That account is disabled.",
@@ -1041,6 +1042,7 @@ var TEXTS = {
1041
1042
  last_manager: "Primero alguien m\xE1s debe administrar los miembros: dale ese rol a otro miembro.",
1042
1043
  last_owner: "Una organizaci\xF3n conserva al menos un propietario: primero haz propietario a otro miembro.",
1043
1044
  organization_not_found: "Esa organizaci\xF3n ya no existe.",
1045
+ member_exists: "Esa persona ya pertenece a la organizaci\xF3n; su rol no cambi\xF3. C\xE1mbialo en la lista.",
1044
1046
  already_member: "Esa persona ya pertenece a la organizaci\xF3n. Cambia su rol.",
1045
1047
  unknown_role: "Escoge uno de los roles ofrecidos.",
1046
1048
  user_not_active: "Esa cuenta est\xE1 desactivada.",
@@ -1085,7 +1087,7 @@ var TEXTS = {
1085
1087
  }
1086
1088
  }
1087
1089
  };
1088
- var PlainButton = (p) => /* @__PURE__ */ jsx("button", { ...p });
1090
+ var PlainButton = ({ variant: _variant, loading: _loading, ...p }) => /* @__PURE__ */ jsx("button", { ...p });
1089
1091
  var PlainInput = (p) => /* @__PURE__ */ jsx("input", { ...p });
1090
1092
  var looks = /* @__PURE__ */ new Map();
1091
1093
  var loaded = /* @__PURE__ */ new Set();
@@ -1200,8 +1202,24 @@ function useForm(props) {
1200
1202
  const C = props.components ?? {};
1201
1203
  const Button = C.Button ?? PlainButton;
1202
1204
  const Input = C.Input ?? PlainInput;
1203
- const [error, setError] = useState(null);
1204
- const [busy, setBusy] = useState(false);
1205
+ const [error, setOnlyError] = useState(null);
1206
+ const [problems, setProblems] = useState({ list: [], round: 0 });
1207
+ const setError = useCallback((e) => {
1208
+ setOnlyError(e);
1209
+ setProblems((p) => p.list.length === 0 ? p : { list: [], round: p.round });
1210
+ }, []);
1211
+ const check = (...found) => {
1212
+ const list = found.filter((e) => Boolean(e));
1213
+ setOnlyError(null);
1214
+ setProblems((p) => ({ list, round: list.length > 0 ? p.round + 1 : p.round }));
1215
+ return list.length === 0;
1216
+ };
1217
+ const fieldError = (name) => problems.list.find((e) => e.field === name) ?? (error?.field === name ? error : null);
1218
+ const firstWrong = (name) => problems.list[0]?.field === name ? problems.round : 0;
1219
+ const [acting, setActing] = useState(null);
1220
+ const busy = acting !== null;
1221
+ const setBusy = useCallback((on) => setActing(on === false ? null : on === true ? SUBMIT : on), []);
1222
+ const loading = (key = SUBMIT) => acting === key;
1205
1223
  const [policy, setPolicy] = useState(void 0);
1206
1224
  const locale = props.locale ?? "en";
1207
1225
  const say = (e) => {
@@ -1214,7 +1232,28 @@ function useForm(props) {
1214
1232
  else window.location.assign(sitePath(to) ?? sameOrigin(props.returnTo) ?? "/");
1215
1233
  };
1216
1234
  const root = useRoot(props);
1217
- return { t, cx, own, C, Button, Input, error, setError, busy, setBusy, say, done, root, policy, setPolicy, passwordRule };
1235
+ return { t, cx, own, C, Button, Input, error, setError, check, fieldError, firstWrong, busy, setBusy, loading, say, done, root, policy, setPolicy, passwordRule };
1236
+ }
1237
+ var SUBMIT = "submit";
1238
+ function Wrap(p) {
1239
+ const { Form: OwnForm } = p.f.C;
1240
+ if (OwnForm) {
1241
+ const own = /* @__PURE__ */ jsx(
1242
+ OwnForm,
1243
+ {
1244
+ as: p.as === "form" ? "form" : "div",
1245
+ onSubmit: p.onSubmit,
1246
+ noValidate: p.noValidate,
1247
+ "aria-busy": p.busy,
1248
+ className: p.slot ? p.f.own(p.slot) : void 0,
1249
+ section: p.section,
1250
+ children: p.children
1251
+ }
1252
+ );
1253
+ return p.root?.["data-cauthy"] !== void 0 ? /* @__PURE__ */ jsx("div", { ...p.root, children: own }) : own;
1254
+ }
1255
+ const Tag2 = p.as;
1256
+ return /* @__PURE__ */ jsx(Tag2, { className: p.slot ? p.f.cx(p.slot) : void 0, ...p.root, onSubmit: p.onSubmit, noValidate: p.noValidate, "aria-busy": p.busy, children: p.children });
1218
1257
  }
1219
1258
  function Note({ f, children, footer }) {
1220
1259
  const { Text } = f.C;
@@ -1295,6 +1334,11 @@ function isPasswordRule(e) {
1295
1334
  if (e.code === "weak_password" || PASSWORD_RULES.includes(e.code)) return true;
1296
1335
  return (e.code === "too_short" || e.code === "too_long") && (e.field === "password" || e.field === "newPassword");
1297
1336
  }
1337
+ function newPasswordProblem(value, policy, field = "password") {
1338
+ const bad = checkNewPassword(value, policy);
1339
+ if (!bad) return null;
1340
+ return { code: bad.code, message: bad.message, field, ...bad.code === "required" ? {} : { reasons: passwordProblems(value, policy) } };
1341
+ }
1298
1342
  function usePasswordPolicy(f, methods) {
1299
1343
  const { setPolicy } = f;
1300
1344
  useEffect(() => {
@@ -1304,7 +1348,11 @@ function usePasswordPolicy(f, methods) {
1304
1348
  function Field(p) {
1305
1349
  const id = useId();
1306
1350
  const { f } = p;
1307
- const bad = f.error?.field === p.name ? f.error : null;
1351
+ const bad = f.fieldError(p.name);
1352
+ const focus = f.firstWrong(p.name);
1353
+ useEffect(() => {
1354
+ if (focus > 0 && typeof document !== "undefined") document.getElementById(id)?.focus();
1355
+ }, [focus, id]);
1308
1356
  const described = [bad ? `${id}-error` : "", p.hint ? `${id}-hint` : ""].filter(Boolean).join(" ");
1309
1357
  const input = /* @__PURE__ */ jsx(
1310
1358
  f.Input,
@@ -1380,7 +1428,7 @@ function SecondStepForm(p) {
1380
1428
  });
1381
1429
  }, []);
1382
1430
  const sendEmail = async () => {
1383
- f.setBusy(true);
1431
+ f.setBusy("email");
1384
1432
  f.setError(null);
1385
1433
  const r = await auth.signIn.mfaSendEmail();
1386
1434
  f.setBusy(false);
@@ -1391,7 +1439,7 @@ function SecondStepForm(p) {
1391
1439
  const pushWait = useRef(null);
1392
1440
  useEffect(() => () => pushWait.current?.abort(), []);
1393
1441
  const sendPush = async () => {
1394
- f.setBusy(true);
1442
+ f.setBusy("push");
1395
1443
  f.setError(null);
1396
1444
  const r = await auth.signIn.mfaPush();
1397
1445
  f.setBusy(false);
@@ -1407,7 +1455,7 @@ function SecondStepForm(p) {
1407
1455
  f.done();
1408
1456
  };
1409
1457
  const sendCode = async () => {
1410
- f.setBusy(true);
1458
+ f.setBusy("phone");
1411
1459
  f.setError(null);
1412
1460
  const r = await auth.signIn.mfaSendCode();
1413
1461
  f.setBusy(false);
@@ -1425,10 +1473,10 @@ function SecondStepForm(p) {
1425
1473
  };
1426
1474
  const general = f.error && !f.error.field ? f.error : null;
1427
1475
  const to = phoneWay?.phone?.number ?? "";
1428
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1476
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1429
1477
  /* @__PURE__ */ jsx(Note, { f, children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1430
1478
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1431
- phoneWay?.email && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1479
+ phoneWay?.email && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("email"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1432
1480
  pushNumber > 0 ? f.C.Text ? (
1433
1481
  // The application's text: the number to pick drawn large, by its own rules.
1434
1482
  /* @__PURE__ */ jsxs("div", { role: "status", "aria-live": "polite", children: [
@@ -1440,15 +1488,15 @@ function SecondStepForm(p) {
1440
1488
  /* @__PURE__ */ jsx("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1441
1489
  /* @__PURE__ */ jsx("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1442
1490
  /* @__PURE__ */ jsx("p", { children: f.t.pushWaiting })
1443
- ] }) : phoneWay?.push && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1444
- phoneWay?.phone && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1491
+ ] }) : phoneWay?.push && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("push"), disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1492
+ phoneWay?.phone && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("phone"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1445
1493
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1446
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1494
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1447
1495
  ] });
1448
1496
  }
1449
1497
  function ProviderButtons(p) {
1450
1498
  const { f } = p;
1451
- return /* @__PURE__ */ jsx(Fragment2, { children: p.providers.map((provider) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1499
+ return /* @__PURE__ */ jsx(Fragment2, { children: p.providers.map((provider) => /* @__PURE__ */ jsxs(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1452
1500
  provider.iconUrl && /* @__PURE__ */ jsx("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1453
1501
  f.t.continueWith,
1454
1502
  " ",
@@ -1488,7 +1536,7 @@ function SignIn(props) {
1488
1536
  }, [email, methods]);
1489
1537
  const toCompany = () => {
1490
1538
  autofill.current?.abort();
1491
- f.setBusy(true);
1539
+ f.setBusy("sso");
1492
1540
  auth.signIn.sso({ email }, { returnTo: props.returnTo });
1493
1541
  };
1494
1542
  useEffect(() => {
@@ -1530,6 +1578,7 @@ function SignIn(props) {
1530
1578
  if (methods?.magicLink) return sendLink();
1531
1579
  return;
1532
1580
  }
1581
+ if (step === "password" && !f.check(checkEmail(email), checkPassword(password))) return;
1533
1582
  f.setBusy(true);
1534
1583
  f.setError(null);
1535
1584
  const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
@@ -1545,7 +1594,7 @@ function SignIn(props) {
1545
1594
  };
1546
1595
  const usePasskey = async () => {
1547
1596
  autofill.current?.abort();
1548
- f.setBusy(true);
1597
+ f.setBusy("passkey");
1549
1598
  f.setError(null);
1550
1599
  const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale, email });
1551
1600
  if (r.ok && r.status === "redirected") return;
@@ -1556,7 +1605,7 @@ function SignIn(props) {
1556
1605
  };
1557
1606
  const sendLink = async () => {
1558
1607
  autofill.current?.abort();
1559
- f.setBusy(true);
1608
+ f.setBusy("link");
1560
1609
  f.setError(null);
1561
1610
  const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token, create: props.createAccounts, returnTo: props.returnTo });
1562
1611
  f.setBusy(false);
@@ -1567,24 +1616,24 @@ function SignIn(props) {
1567
1616
  };
1568
1617
  const general = f.error && !f.error.field ? f.error : null;
1569
1618
  const asksEmail = offers.email;
1570
- if (step === "opening") return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1619
+ if (step === "opening") return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1571
1620
  if (step === "sent") {
1572
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1621
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1573
1622
  /* @__PURE__ */ jsx(Note, { f, children: f.t.signInLinkSent }),
1574
1623
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1575
1624
  /* @__PURE__ */ jsx(Note, { f, children: f.t.codeFromMail }),
1576
1625
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1577
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1626
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1578
1627
  ] });
1579
1628
  }
1580
1629
  if (step === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1581
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1630
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1582
1631
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1583
1632
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1584
- offers.passkey && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1633
+ offers.passkey && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("passkey"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1585
1634
  asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ jsx(Or, { f, children: f.t.orWithEmail }),
1586
1635
  asksEmail && /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1587
- company && /* @__PURE__ */ jsxs(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1636
+ company && /* @__PURE__ */ jsxs(f.Button, { type: "button", variant: company.enforced ? "primary" : "secondary", loading: f.loading("sso"), disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1588
1637
  f.t.continueWith,
1589
1638
  " ",
1590
1639
  company.name
@@ -1606,10 +1655,10 @@ function SignIn(props) {
1606
1655
  }
1607
1656
  ),
1608
1657
  challenge.node,
1609
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1658
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1610
1659
  ] }),
1611
1660
  !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1612
- methods?.magicLink && !company?.enforced && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1661
+ methods?.magicLink && !company?.enforced && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("link"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1613
1662
  methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ jsxs(Note, { f, footer: true, children: [
1614
1663
  f.t.noAccount,
1615
1664
  " ",
@@ -1649,11 +1698,19 @@ function SignUp(props) {
1649
1698
  const [acceptLegal, setAcceptLegal] = useState(false);
1650
1699
  const [consents, setConsents] = useState({});
1651
1700
  const legal = methods?.legal;
1701
+ const guest = auth.session?.user.isAnonymous === true;
1652
1702
  const submit = async (e) => {
1653
1703
  e.preventDefault();
1704
+ const asksCompany = methods?.organization?.ask === true;
1705
+ if (!f.check(
1706
+ asksCompany && checkOrganization(organization, methods?.organization?.required === true),
1707
+ checkName(name),
1708
+ checkEmail(email),
1709
+ newPasswordProblem(password, f.policy)
1710
+ )) return;
1654
1711
  f.setBusy(true);
1655
1712
  f.setError(null);
1656
- const r = auth.session?.user.isAnonymous === true ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1713
+ const r = guest ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1657
1714
  name,
1658
1715
  email,
1659
1716
  password,
@@ -1669,30 +1726,31 @@ function SignUp(props) {
1669
1726
  };
1670
1727
  const general = f.error && !f.error.field ? f.error : null;
1671
1728
  const passwordOn = methods?.passwordSignIn !== false;
1672
- const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1729
+ const providers = guest || methods?.organization?.required ? [] : methods?.providers ?? [];
1673
1730
  if (sent) {
1674
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1731
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1675
1732
  /* @__PURE__ */ jsx(Note, { f, children: f.t.checkEmail }),
1676
1733
  props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1677
1734
  ] });
1678
1735
  }
1736
+ if (methods === null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1679
1737
  const footer = props.links?.signIn && /* @__PURE__ */ jsxs(Note, { f, footer: true, children: [
1680
1738
  f.t.haveAccount,
1681
1739
  " ",
1682
1740
  /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.signIn })
1683
1741
  ] });
1684
1742
  if (!passwordOn) {
1685
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1743
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1686
1744
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1687
1745
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1688
1746
  footer
1689
1747
  ] });
1690
1748
  }
1691
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1749
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1692
1750
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1693
1751
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1694
1752
  providers.length > 0 && /* @__PURE__ */ jsx(Or, { f, children: f.t.orWithEmail }),
1695
- methods?.organization?.ask && /* @__PURE__ */ jsx(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1753
+ methods.organization?.ask && /* @__PURE__ */ jsx(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1696
1754
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1697
1755
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1698
1756
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
@@ -1703,7 +1761,7 @@ function SignUp(props) {
1703
1761
  ] }),
1704
1762
  legal?.consentPurposes.map((p) => /* @__PURE__ */ jsx(Check, { f, name: `consent_${p.key}`, checked: consents[p.key] === true, required: p.required, onChange: (on) => setConsents({ ...consents, [p.key]: on }), children: p.label }, p.key)),
1705
1763
  challenge.node,
1706
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1764
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1707
1765
  footer
1708
1766
  ] });
1709
1767
  }
@@ -1725,16 +1783,16 @@ function ForgotPassword(props) {
1725
1783
  };
1726
1784
  const general = f.error && !f.error.field ? f.error : null;
1727
1785
  if (sent) {
1728
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1786
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1729
1787
  /* @__PURE__ */ jsx(Note, { f, children: f.t.linkSent }),
1730
1788
  props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1731
1789
  ] });
1732
1790
  }
1733
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1791
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1734
1792
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1735
1793
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1736
1794
  challenge.node,
1737
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1795
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1738
1796
  props.links?.signIn && /* @__PURE__ */ jsx(Note, { f, footer: true, children: /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn }) })
1739
1797
  ] });
1740
1798
  }
@@ -1761,21 +1819,21 @@ function ResetPassword(props) {
1761
1819
  const general = f.error && !f.error.field ? f.error : null;
1762
1820
  const back = props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1763
1821
  if (changed) {
1764
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1822
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1765
1823
  /* @__PURE__ */ jsx(Note, { f, children: f.t.passwordChanged }),
1766
1824
  back
1767
1825
  ] });
1768
1826
  }
1769
1827
  if (token === "") {
1770
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1828
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1771
1829
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1772
1830
  props.links?.forgotPassword && /* @__PURE__ */ jsx(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword })
1773
1831
  ] });
1774
1832
  }
1775
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1833
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1776
1834
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1777
1835
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.newPassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1778
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1836
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1779
1837
  ] });
1780
1838
  }
1781
1839
  function VerifyEmail(props) {
@@ -1794,7 +1852,7 @@ function VerifyEmail(props) {
1794
1852
  setState("failed");
1795
1853
  });
1796
1854
  });
1797
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, "aria-busy": state === "confirming", children: [
1855
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, busy: state === "confirming", children: [
1798
1856
  state === "confirming" && /* @__PURE__ */ jsx(Note, { f, children: f.t.confirming }),
1799
1857
  state === "confirmed" && /* @__PURE__ */ jsx(Note, { f, children: f.t.emailConfirmed }),
1800
1858
  state === "failed" && f.error && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(f.error) }),
@@ -1838,10 +1896,10 @@ function AcceptInvite(props) {
1838
1896
  const general = f.error && !f.error.field ? f.error : null;
1839
1897
  const back = props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1840
1898
  if (state === "opening") {
1841
- return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ jsx(Note, { f, children: f.t.openingInvitation }) });
1899
+ return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true, children: /* @__PURE__ */ jsx(Note, { f, children: f.t.openingInvitation }) });
1842
1900
  }
1843
1901
  if (state === "invalid") {
1844
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1902
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1845
1903
  /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: general ? f.say(general) : f.t.invitationInvalid }),
1846
1904
  back
1847
1905
  ] });
@@ -1849,13 +1907,13 @@ function AcceptInvite(props) {
1849
1907
  if (state === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1850
1908
  const workspace = invitation?.workspace ?? "";
1851
1909
  const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1852
- return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1910
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1853
1911
  /* @__PURE__ */ jsx(Heading, { f, children: heading }),
1854
1912
  /* @__PURE__ */ jsx(Note, { f, children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1855
1913
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1856
1914
  /* @__PURE__ */ jsx("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1857
1915
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.choosePassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1858
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1916
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1859
1917
  ] });
1860
1918
  }
1861
1919
  function PendingTerms(props) {
@@ -1875,7 +1933,7 @@ function PendingTerms(props) {
1875
1933
  setOwed([]);
1876
1934
  props.onSuccess?.();
1877
1935
  };
1878
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1936
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1879
1937
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.termsChanged }),
1880
1938
  /* @__PURE__ */ jsxs(Note, { f, children: [
1881
1939
  f.t.termsChangedHelp,
@@ -1883,15 +1941,15 @@ function PendingTerms(props) {
1883
1941
  /* @__PURE__ */ jsx(Documents, { f, documents: owed })
1884
1942
  ] }),
1885
1943
  f.error && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(f.error) }),
1886
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1944
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1887
1945
  ] });
1888
1946
  }
1889
1947
  var ACCOUNT_SECTIONS = ["profile", "email", "password", "linked", "secondStep", "phone", "passkeys", "agents", "sessions"];
1890
1948
  function useSection(props) {
1891
1949
  const f = useForm({ ...props, unstyled: true });
1892
1950
  const [done, setDone] = useState(null);
1893
- const run = async (action) => {
1894
- f.setBusy(true);
1951
+ const run = async (action, key = SUBMIT) => {
1952
+ f.setBusy(key);
1895
1953
  f.setError(null);
1896
1954
  setDone(null);
1897
1955
  const r = await action();
@@ -1905,13 +1963,12 @@ function useSection(props) {
1905
1963
  function Account(props) {
1906
1964
  const auth = useAuth();
1907
1965
  const { accountUrl } = useSession();
1908
- const { root } = useForm(props);
1909
- const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
1966
+ const f = useForm(props);
1910
1967
  const [account, setAccount] = useState(null);
1911
1968
  useEffect(() => {
1912
1969
  void auth.account.get().then(setAccount);
1913
1970
  }, []);
1914
- if (account === null) return /* @__PURE__ */ jsx("div", { className: cx("account"), ...root, "aria-busy": "true" });
1971
+ if (account === null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root: f.root, busy: true });
1915
1972
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleString());
1916
1973
  const mfa = (mfaEnabled) => setAccount({ ...account, mfaEnabled });
1917
1974
  const part = (section) => {
@@ -1936,7 +1993,7 @@ function Account(props) {
1936
1993
  return /* @__PURE__ */ jsx(SessionsPart, { props, when });
1937
1994
  }
1938
1995
  };
1939
- return /* @__PURE__ */ jsx("div", { className: cx("account"), ...root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ jsx(Fragment, { children: part(section) }, section)) });
1996
+ return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root: f.root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ jsx(Fragment, { children: part(section) }, section)) });
1940
1997
  }
1941
1998
  function AccountProfile(props) {
1942
1999
  return /* @__PURE__ */ jsx(Account, { ...props, sections: ["profile"] });
@@ -2009,15 +2066,20 @@ function OrganizationMembers(props) {
2009
2066
  useEffect(() => {
2010
2067
  void load();
2011
2068
  }, [id]);
2012
- if (gone !== null) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ jsx(Say, { f, tone: "success", children: gone }) });
2013
- if (seat === void 0) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, "aria-busy": "true" });
2014
- if (seat === null) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ jsx(Note, { f, children: f.t.organizationMissing }) });
2069
+ if (gone !== null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ jsx(Say, { f, tone: "success", children: gone }) });
2070
+ if (seat === void 0) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root, busy: true });
2071
+ if (seat === null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ jsx(Note, { f, children: f.t.organizationMissing }) });
2015
2072
  const manage = seat.can.manageMembers;
2016
2073
  const giveable = rolesToGive(seat, roles, props.roles);
2017
- const label = (r) => roles.find((c) => c.role === r)?.name || r;
2074
+ const roleName = (c) => props.roleLabel?.(c) || c.name || c.role;
2075
+ const label = (r) => {
2076
+ const c = roles.find((x) => x.role === r);
2077
+ return c ? roleName(c) : r;
2078
+ };
2018
2079
  const add = async (e) => {
2019
2080
  e.preventDefault();
2020
- const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }));
2081
+ if (!f.check(checkEmail(email))) return;
2082
+ const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }), "add");
2021
2083
  if (!r.ok) return;
2022
2084
  setEmail("");
2023
2085
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
@@ -2025,28 +2087,28 @@ function OrganizationMembers(props) {
2025
2087
  await load();
2026
2088
  };
2027
2089
  const change = async (m, next) => {
2028
- const r = await run(() => auth.organizations.setRole(id, m.userId, next));
2090
+ const r = await run(() => auth.organizations.setRole(id, m.userId, next), `role:${m.userId}`);
2029
2091
  if (!r.ok) return;
2030
2092
  saved({ kind: "member.role_changed", organizationId: id, userId: m.userId, role: next, previousRole: m.role });
2031
2093
  await load();
2032
2094
  };
2033
2095
  const remove = async (m) => {
2034
2096
  setAsking(null);
2035
- const r = await run(() => auth.organizations.remove(id, m.userId));
2097
+ const r = await run(() => auth.organizations.remove(id, m.userId), `remove:${m.userId}`);
2036
2098
  if (!r.ok) return;
2037
2099
  saved({ kind: m.you ? "member.left" : "member.removed", organizationId: id, userId: m.userId });
2038
2100
  if (m.you) setSeat(null);
2039
2101
  else await load();
2040
2102
  };
2041
2103
  const inviteAgain = async (m) => {
2042
- const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
2104
+ const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }), `invite:${m.userId}`);
2043
2105
  if (!r.ok) return;
2044
2106
  setDone(f.t.memberInvitationSent);
2045
2107
  saved({ kind: "member.invited_again", organizationId: id, userId: m.userId });
2046
2108
  };
2047
2109
  const rename = async (e) => {
2048
2110
  e.preventDefault();
2049
- const r = await run(() => auth.organizations.rename(id, name));
2111
+ const r = await run(() => auth.organizations.rename(id, name), "rename");
2050
2112
  if (!r.ok) return;
2051
2113
  setSeat(r.organization);
2052
2114
  setName(r.organization.name);
@@ -2055,7 +2117,7 @@ function OrganizationMembers(props) {
2055
2117
  };
2056
2118
  const close = async (how) => {
2057
2119
  setAsking(null);
2058
- const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id));
2120
+ const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id), how);
2059
2121
  if (!r.ok) return;
2060
2122
  setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2061
2123
  saved({ kind: how === "archive" ? "organization.archived" : "organization.deleted", organizationId: id });
@@ -2070,8 +2132,8 @@ function OrganizationMembers(props) {
2070
2132
  const confirming = (text2, label2, yes) => /* @__PURE__ */ jsxs("span", { role: "group", "aria-label": label2, children: [
2071
2133
  /* @__PURE__ */ jsx("span", { children: text2 }),
2072
2134
  " ",
2073
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
2074
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
2135
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
2136
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
2075
2137
  ] });
2076
2138
  const leaveQuestion = f.t.memberLeaveConfirm.replace("{org}", seat.name);
2077
2139
  const archiveQuestion = f.t.organizationArchiveConfirm.replace("{org}", seat.name);
@@ -2079,8 +2141,8 @@ function OrganizationMembers(props) {
2079
2141
  const archiving = seat.can.archive && !hidden("archive");
2080
2142
  const deleting = seat.can.delete && !hidden("delete");
2081
2143
  const renaming = seat.can.update && !hidden("rename");
2082
- return /* @__PURE__ */ jsxs("div", { className: f.cx("account"), ...root, children: [
2083
- /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2144
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "account", root, children: [
2145
+ /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "members", children: [
2084
2146
  /* @__PURE__ */ jsxs(Heading, { f, children: [
2085
2147
  f.t.members,
2086
2148
  " \xB7 ",
@@ -2093,6 +2155,8 @@ function OrganizationMembers(props) {
2093
2155
  f.Button,
2094
2156
  {
2095
2157
  type: "button",
2158
+ variant: "danger",
2159
+ loading: f.loading(`remove:${auth.session.user.id}`),
2096
2160
  disabled: f.busy,
2097
2161
  className: f.cx("providerButton"),
2098
2162
  onClick: () => ask(auth.session.user.id, f.t.memberLeave, leaveQuestion, () => void remove({ userId: auth.session.user.id, you: true })),
@@ -2127,34 +2191,34 @@ function OrganizationMembers(props) {
2127
2191
  label: `${f.t.memberRole}: ${who}`,
2128
2192
  value: m.role,
2129
2193
  disabled: f.busy,
2130
- options: choices.map((c) => ({ value: c.role, label: c.name || c.role })),
2194
+ options: choices.map((c) => ({ value: c.role, label: roleName(c) })),
2131
2195
  onChange: (next) => void change(m, next)
2132
2196
  }
2133
2197
  ) : f.C.Badge ? /* @__PURE__ */ jsx(f.C.Badge, { tone: "neutral", children: label(m.role) }) : /* @__PURE__ */ jsx("span", { children: label(m.role) }),
2134
2198
  " ",
2135
2199
  asking === m.userId ? confirming(question, action, () => void remove(m)) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2136
- may.inviteAgain && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
2137
- removable && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask(m.userId, action, question, () => void remove(m)), children: action })
2200
+ may.inviteAgain && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading(`invite:${m.userId}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
2201
+ removable && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${m.userId}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask(m.userId, action, question, () => void remove(m)), children: action })
2138
2202
  ] })
2139
2203
  ] }, m.userId);
2140
2204
  }) }),
2141
2205
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2142
2206
  ] }),
2143
- manage && giveable.length > 0 && /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
2207
+ manage && giveable.length > 0 && /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2144
2208
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.memberAdd }),
2145
2209
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
2146
- /* @__PURE__ */ jsx(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: c.name || c.role })), onChange: setRole }),
2147
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2210
+ /* @__PURE__ */ jsx(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: roleName(c) })), onChange: setRole }),
2211
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2148
2212
  ] }),
2149
- (renaming || archiving || deleting) && /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2213
+ (renaming || archiving || deleting) && /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "organization", children: [
2150
2214
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.organization }),
2151
- renaming && /* @__PURE__ */ jsxs("form", { onSubmit: rename, noValidate: true, children: [
2215
+ renaming && /* @__PURE__ */ jsxs(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2152
2216
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
2153
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2217
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2154
2218
  ] }),
2155
2219
  asking === "archive" ? confirming(archiveQuestion, f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(deleteQuestion, f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2156
- archiving && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2157
- deleting && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
2220
+ archiving && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("archive"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2221
+ deleting && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("delete"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
2158
2222
  ] })
2159
2223
  ] })
2160
2224
  ] });
@@ -2179,14 +2243,15 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2179
2243
  }, []);
2180
2244
  if (here === null) return null;
2181
2245
  if (!here) {
2182
- return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2246
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "passkeys", children: [
2183
2247
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2184
2248
  /* @__PURE__ */ jsx(A, { f, href: accountUrl, children: f.t.managePasskeys })
2185
2249
  ] });
2186
2250
  }
2187
2251
  const add = async (e, kind = "device") => {
2188
2252
  e?.preventDefault();
2189
- const r = await run(() => auth.account.addPasskey({ name, password, kind }));
2253
+ if (account.hasPassword && !f.check(checkPassword(password))) return;
2254
+ const r = await run(() => auth.account.addPasskey({ name, password, kind }), kind === "security_key" ? "security_key" : SUBMIT);
2190
2255
  if (!r.ok) {
2191
2256
  if (r.error.code === "cancelled") f.setError(null);
2192
2257
  return;
@@ -2198,12 +2263,12 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2198
2263
  props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2199
2264
  };
2200
2265
  const remove = async (id) => {
2201
- const r = await run(() => auth.account.removePasskey(id));
2266
+ const r = await run(() => auth.account.removePasskey(id), `remove:${id}`);
2202
2267
  if (!r.ok) return;
2203
2268
  setList(list.filter((p) => p.id !== id));
2204
2269
  props.onSaved?.({ kind: "passkey.removed", id });
2205
2270
  };
2206
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
2271
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "passkeys", onSubmit: add, noValidate: true, children: [
2207
2272
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2208
2273
  /* @__PURE__ */ jsx(Note, { f, children: f.t.passkeysHelp }),
2209
2274
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
@@ -2213,12 +2278,12 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2213
2278
  " \xB7 ",
2214
2279
  when(p.createdAt),
2215
2280
  " ",
2216
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2281
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${p.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2217
2282
  ] }, p.id)) }),
2218
2283
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
2219
2284
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2220
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2221
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2285
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2286
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("security_key"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2222
2287
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2223
2288
  ] });
2224
2289
  }
@@ -2234,12 +2299,12 @@ function ProfilePart({ props, account, onSaved }) {
2234
2299
  setDone(f.t.saved);
2235
2300
  props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2236
2301
  };
2237
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2302
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "profile", onSubmit: submit, noValidate: true, children: [
2238
2303
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.profile }),
2239
2304
  /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2240
2305
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2241
2306
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2242
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2307
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2243
2308
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2244
2309
  ] });
2245
2310
  }
@@ -2250,19 +2315,20 @@ function EmailPart({ props, account }) {
2250
2315
  const [password, setPassword] = useState("");
2251
2316
  const submit = async (e) => {
2252
2317
  e.preventDefault();
2318
+ if (!f.check(checkEmail(email), account.hasPassword && checkPassword(password))) return;
2253
2319
  const r = await run(() => auth.account.changeEmail({ email, password }));
2254
2320
  if (!r.ok) return;
2255
2321
  setPassword("");
2256
2322
  setDone(f.t.emailSent.replace("{email}", r.email));
2257
2323
  props.onSaved?.({ kind: "email.change_requested", email: r.email });
2258
2324
  };
2259
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2325
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "email", onSubmit: submit, noValidate: true, children: [
2260
2326
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.emailAddress }),
2261
2327
  /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2262
2328
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2263
2329
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2264
2330
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2265
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2331
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2266
2332
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2267
2333
  ] });
2268
2334
  }
@@ -2285,13 +2351,13 @@ function LinkedPart({ props }) {
2285
2351
  if (linked === null) return null;
2286
2352
  const name = (id) => offered.find((p) => p.id === id)?.name ?? id;
2287
2353
  const unlink = async (provider) => {
2288
- const r = await run(() => auth.account.unlink(provider));
2354
+ const r = await run(() => auth.account.unlink(provider), `unlink:${provider}`);
2289
2355
  if (!r.ok) return;
2290
2356
  setLinked(linked.filter((x) => x.provider !== provider));
2291
2357
  props.onSaved?.({ kind: "identity.unlinked", provider });
2292
2358
  };
2293
2359
  const here = typeof window === "undefined" ? void 0 : window.location.pathname;
2294
- return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2360
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "linked", children: [
2295
2361
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.linkedAccounts }),
2296
2362
  came && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: f.t.linked }),
2297
2363
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
@@ -2299,9 +2365,9 @@ function LinkedPart({ props }) {
2299
2365
  name(x.provider),
2300
2366
  x.email ? ` \xB7 ${x.email}` : "",
2301
2367
  " ",
2302
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2368
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`unlink:${x.provider}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2303
2369
  ] }, x.provider)) }),
2304
- offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2370
+ offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ jsxs(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2305
2371
  f.t.linkWith,
2306
2372
  " ",
2307
2373
  p.name
@@ -2316,6 +2382,7 @@ function PasswordPart({ props }) {
2316
2382
  usePasswordPolicy(f, useMethods(auth));
2317
2383
  const submit = async (e) => {
2318
2384
  e.preventDefault();
2385
+ if (!f.check(checkPassword(password), newPasswordProblem(newPassword, f.policy, "newPassword"))) return;
2319
2386
  const r = await run(() => auth.account.changePassword({ password, newPassword }));
2320
2387
  if (!r.ok) return;
2321
2388
  setPassword("");
@@ -2323,12 +2390,12 @@ function PasswordPart({ props }) {
2323
2390
  setDone(f.t.passwordChangedHere);
2324
2391
  props.onSaved?.({ kind: "password.changed" });
2325
2392
  };
2326
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2393
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "password", onSubmit: submit, noValidate: true, children: [
2327
2394
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.changePassword }),
2328
2395
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2329
2396
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2330
2397
  /* @__PURE__ */ jsx(Field, { f, name: "newPassword", type: "password", label: f.t.newPassword, value: newPassword, autoComplete: "new-password", required: true, onChange: setNewPassword, hint: f.passwordRule }),
2331
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2398
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2332
2399
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2333
2400
  ] });
2334
2401
  }
@@ -2365,7 +2432,7 @@ function SecondStepPart({ props, account, onChanged }) {
2365
2432
  props.onSaved?.({ kind: "mfa.disabled" });
2366
2433
  };
2367
2434
  const renew = async () => {
2368
- const r = await run(() => auth.account.mfa.recoveryCodes({ password }));
2435
+ const r = await run(() => auth.account.mfa.recoveryCodes({ password }), "codes");
2369
2436
  if (!r.ok) return;
2370
2437
  setCodes(r.recoveryCodes);
2371
2438
  props.onSaved?.({ kind: "recovery_codes.regenerated" });
@@ -2376,24 +2443,24 @@ function SecondStepPart({ props, account, onChanged }) {
2376
2443
  /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2377
2444
  ] });
2378
2445
  if (setup) {
2379
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2446
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: confirm, noValidate: true, children: [
2380
2447
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2381
2448
  /* @__PURE__ */ jsx(Note, { f, children: f.t.addToApp }),
2382
2449
  /* @__PURE__ */ jsx("code", { className: f.cx("secret"), children: setup.secret }),
2383
2450
  /* @__PURE__ */ jsx(A, { f, href: setup.otpauthUri, children: f.t.openInApp }),
2384
2451
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2385
2452
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2386
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2453
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2387
2454
  ] });
2388
2455
  }
2389
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2456
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2390
2457
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2391
2458
  /* @__PURE__ */ jsx(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2392
2459
  shown,
2393
2460
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2394
2461
  passwordField,
2395
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2396
- account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2462
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: account.mfaEnabled ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2463
+ account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("codes"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2397
2464
  ] });
2398
2465
  }
2399
2466
  function PhonePart({ props, account, onChanged }) {
@@ -2443,15 +2510,15 @@ function PhonePart({ props, account, onChanged }) {
2443
2510
  };
2444
2511
  const passwordField = account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2445
2512
  if (sentTo) {
2446
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2513
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: confirm, noValidate: true, children: [
2447
2514
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2448
2515
  /* @__PURE__ */ jsx(Note, { f, children: f.t.codeSent.replace("{to}", sentTo) }),
2449
2516
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2450
2517
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2451
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2518
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2452
2519
  ] });
2453
2520
  }
2454
- return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2521
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2455
2522
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2456
2523
  /* @__PURE__ */ jsx(Note, { f, children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2457
2524
  codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
@@ -2467,7 +2534,7 @@ function PhonePart({ props, account, onChanged }) {
2467
2534
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
2468
2535
  ] }, c)) }),
2469
2536
  passwordField,
2470
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2537
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: ways.phone ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2471
2538
  ] });
2472
2539
  }
2473
2540
  function AgentsPart({ props, when }) {
@@ -2479,12 +2546,12 @@ function AgentsPart({ props, when }) {
2479
2546
  }, []);
2480
2547
  if (list.length === 0) return null;
2481
2548
  const disconnect = async (id) => {
2482
- const r = await run(() => auth.account.disconnectAgent(id));
2549
+ const r = await run(() => auth.account.disconnectAgent(id), `disconnect:${id}`);
2483
2550
  if (!r.ok) return;
2484
2551
  setList(list.filter((a) => a.id !== id));
2485
2552
  props.onSaved?.({ kind: "agent.disconnected", id });
2486
2553
  };
2487
- return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2554
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "agents", children: [
2488
2555
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.agents }),
2489
2556
  /* @__PURE__ */ jsx(Note, { f, children: f.t.agentsHelp }),
2490
2557
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
@@ -2497,7 +2564,7 @@ function AgentsPart({ props, when }) {
2497
2564
  " \xB7 ",
2498
2565
  when(a.lastUsedAt),
2499
2566
  " ",
2500
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2567
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`disconnect:${a.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2501
2568
  ] }, a.id)) })
2502
2569
  ] });
2503
2570
  }
@@ -2515,7 +2582,7 @@ function SessionsPart({ props, when }) {
2515
2582
  if (props.onSignedOut) props.onSignedOut();
2516
2583
  else window.location.assign("/");
2517
2584
  };
2518
- return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2585
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "sessions", children: [
2519
2586
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.sessions }),
2520
2587
  /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2521
2588
  s.device || f.t.unknownDevice,
@@ -2525,7 +2592,7 @@ function SessionsPart({ props, when }) {
2525
2592
  when(s.createdAt)
2526
2593
  ] }, i)) }),
2527
2594
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2528
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2595
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2529
2596
  ] });
2530
2597
  }
2531
2598
  function ChannelPick({ f, value, channels, onChange }) {
@@ -2546,15 +2613,22 @@ function ChannelPick({ f, value, channels, onChange }) {
2546
2613
  // src/react.tsx
2547
2614
  import { jsx as jsx2 } from "react/jsx-runtime";
2548
2615
  var Ctx = createContext(null);
2616
+ var LocaleCtx = createContext(() => void 0);
2549
2617
  var AHEAD_MS = 6e4;
2550
- function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2618
+ function CauthyProvider({ children, basePath = "/api/auth", initialSession, locale }) {
2551
2619
  const base = withoutTrailingSlashes(basePath);
2620
+ const said = useRef2(locale);
2621
+ said.current = locale;
2622
+ const readLocale = useCallback2(() => {
2623
+ const l = said.current;
2624
+ return typeof l === "function" ? l() : l;
2625
+ }, []);
2552
2626
  const [session, setSession] = useState2(initialSession ?? null);
2553
2627
  const [status, setStatus] = useState2(
2554
2628
  initialSession === void 0 ? "loading" : initialSession === null ? "unauthenticated" : "authenticated"
2555
2629
  );
2556
2630
  const asking = useRef2(null);
2557
- const refresh = useCallback(() => {
2631
+ const refresh = useCallback2(() => {
2558
2632
  asking.current ??= (async () => {
2559
2633
  try {
2560
2634
  const res = await fetch(`${base}/session`, { credentials: "same-origin", headers: { accept: "application/json" } });
@@ -2612,7 +2686,7 @@ function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2612
2686
  }),
2613
2687
  [session, status, refresh, base]
2614
2688
  );
2615
- return /* @__PURE__ */ jsx2(Ctx.Provider, { value, children });
2689
+ return /* @__PURE__ */ jsx2(Ctx.Provider, { value, children: /* @__PURE__ */ jsx2(LocaleCtx.Provider, { value: readLocale, children }) });
2616
2690
  }
2617
2691
  function useSession() {
2618
2692
  const ctx = useContext(Ctx);
@@ -2621,8 +2695,9 @@ function useSession() {
2621
2695
  }
2622
2696
  function useAuth() {
2623
2697
  const { session, status, refresh, basePath } = useSession();
2698
+ const locale = useContext(LocaleCtx);
2624
2699
  return useMemo(() => {
2625
- const client = createAuthClient({ basePath });
2700
+ const client = createAuthClient({ basePath, locale });
2626
2701
  const then = async (running) => {
2627
2702
  const r = await running;
2628
2703
  await refresh();
@@ -2659,7 +2734,7 @@ function useAuth() {
2659
2734
  status,
2660
2735
  refresh
2661
2736
  };
2662
- }, [basePath, refresh, session, status]);
2737
+ }, [basePath, locale, refresh, session, status]);
2663
2738
  }
2664
2739
  export {
2665
2740
  AcceptInvite,