@kerne/react 0.1.2 → 0.1.3
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/{chunk-3PROPZWW.cjs → chunk-7U3QBMA7.cjs} +34 -13
- package/dist/{chunk-AIUIBAYB.js → chunk-F6NV76OR.js} +23 -2
- package/dist/{i18n-Qketn1RF.d.cts → i18n-BWTT1DWD.d.cts} +16 -0
- package/dist/{i18n-Qketn1RF.d.ts → i18n-BWTT1DWD.d.ts} +16 -0
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +1 -1
- package/dist/ui/index.cjs +470 -186
- package/dist/ui/index.d.cts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +418 -134
- package/package.json +3 -3
package/dist/ui/index.js
CHANGED
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
useSubscription,
|
|
17
17
|
useUser,
|
|
18
18
|
useWaitlist
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-F6NV76OR.js";
|
|
20
20
|
|
|
21
21
|
// src/ui/AuthFlow.tsx
|
|
22
|
-
import { useState as
|
|
22
|
+
import { useState as useState10 } from "react";
|
|
23
23
|
|
|
24
24
|
// src/ui/useUrlToken.ts
|
|
25
25
|
import { useState, useEffect } from "react";
|
|
@@ -37,10 +37,10 @@ function useUrlToken(param = "token", override) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// src/ui/LoginForm.tsx
|
|
40
|
-
import { useState as
|
|
40
|
+
import { useState as useState3 } from "react";
|
|
41
41
|
|
|
42
42
|
// src/ui/primitives.tsx
|
|
43
|
-
import { useId, useContext, createContext } from "react";
|
|
43
|
+
import { useId, useState as useState2, useContext, createContext } from "react";
|
|
44
44
|
|
|
45
45
|
// src/ui/styles.ts
|
|
46
46
|
var STYLE_ELEMENT_ID = "kerne-ui-styles";
|
|
@@ -53,6 +53,7 @@ var DARK_TOKENS = `
|
|
|
53
53
|
--kerne-panel: #1b1f26;
|
|
54
54
|
--kerne-surface: #14171c;
|
|
55
55
|
--kerne-surface-raised: #2a2f38;
|
|
56
|
+
--kerne-track: #313743;
|
|
56
57
|
--kerne-primary: #f0f1f3;
|
|
57
58
|
--kerne-primary-fg: #16181d;
|
|
58
59
|
--kerne-primary-hover: #d8dade;
|
|
@@ -81,6 +82,11 @@ var CSS = `
|
|
|
81
82
|
variant, the dropdown floating over host content, a pressed segment. */
|
|
82
83
|
--kerne-surface: #ffffff;
|
|
83
84
|
--kerne-surface-raised: #ffffff;
|
|
85
|
+
/* Unfilled part of a meter, and the ground of a disabled button. Distinct
|
|
86
|
+
from --kerne-panel, which is tuned to sit on the page: on a card the two
|
|
87
|
+
are nearly the same colour, so a meter drawn in panel had invisible empty
|
|
88
|
+
segments and read as having fewer steps than it has. */
|
|
89
|
+
--kerne-track: #e3e5e9;
|
|
84
90
|
--kerne-primary: #16171a;
|
|
85
91
|
--kerne-primary-fg: #ffffff;
|
|
86
92
|
--kerne-primary-hover: #2c2e33;
|
|
@@ -226,6 +232,18 @@ var CSS = `
|
|
|
226
232
|
}
|
|
227
233
|
.kerne-subtitle { margin: 0; font-size: 14px; color: var(--kerne-fg-muted); line-height: 1.5; }
|
|
228
234
|
|
|
235
|
+
/* Moving between steps (sign-in -> sign-up -> forgot password) used to be an
|
|
236
|
+
instant swap: the content and the height both jumped at once, which reads
|
|
237
|
+
as a page reload rather than one flow. */
|
|
238
|
+
@keyframes kerne-step-in {
|
|
239
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
240
|
+
to { opacity: 1; transform: none; }
|
|
241
|
+
}
|
|
242
|
+
.kerne-step { animation: kerne-step-in .18s ease-out; }
|
|
243
|
+
@media (prefers-reduced-motion: reduce) {
|
|
244
|
+
.kerne-step { animation: none; }
|
|
245
|
+
}
|
|
246
|
+
|
|
229
247
|
/* ---------- Stack ---------- */
|
|
230
248
|
.kerne-stack { display: flex; flex-direction: column; gap: 16px; }
|
|
231
249
|
.kerne-form { display: flex; flex-direction: column; gap: 12px; }
|
|
@@ -233,6 +251,9 @@ var CSS = `
|
|
|
233
251
|
.kerne-label { font-size: 14px; font-weight: 500; color: var(--kerne-fg); }
|
|
234
252
|
.kerne-label-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
|
|
235
253
|
|
|
254
|
+
/* Positioning context for the reveal toggle, which sits inside the input box. */
|
|
255
|
+
.kerne-control { position: relative; display: flex; }
|
|
256
|
+
|
|
236
257
|
.kerne-input {
|
|
237
258
|
font: inherit;
|
|
238
259
|
font-size: 14px;
|
|
@@ -245,6 +266,57 @@ var CSS = `
|
|
|
245
266
|
width: 100%;
|
|
246
267
|
transition: border-color .12s ease, box-shadow .12s ease;
|
|
247
268
|
}
|
|
269
|
+
/* Room for the toggle, so a long password never runs underneath it. */
|
|
270
|
+
.kerne-input[data-kerne-reveal="true"] { padding-right: 42px; }
|
|
271
|
+
|
|
272
|
+
.kerne-reveal {
|
|
273
|
+
position: absolute;
|
|
274
|
+
top: 0; right: 0; bottom: 0;
|
|
275
|
+
width: 40px;
|
|
276
|
+
display: flex; align-items: center; justify-content: center;
|
|
277
|
+
background: none; border: none; padding: 0; cursor: pointer;
|
|
278
|
+
color: var(--kerne-fg-subtle);
|
|
279
|
+
border-radius: var(--kerne-radius);
|
|
280
|
+
transition: color .12s ease;
|
|
281
|
+
}
|
|
282
|
+
.kerne-reveal:hover:not(:disabled) { color: var(--kerne-fg); }
|
|
283
|
+
.kerne-reveal:disabled { cursor: not-allowed; opacity: .5; }
|
|
284
|
+
.kerne-reveal:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: -2px; }
|
|
285
|
+
|
|
286
|
+
.kerne-field-note { font-size: 12.5px; line-height: 1.4; color: var(--kerne-fg-muted); margin: 0; }
|
|
287
|
+
.kerne-field-error { font-size: 12.5px; line-height: 1.4; color: var(--kerne-danger); margin: 0; }
|
|
288
|
+
/* Caps Lock is a "you are about to get this wrong" signal, not background
|
|
289
|
+
guidance - in muted grey it sat at the same weight as a hint and got read
|
|
290
|
+
as one. */
|
|
291
|
+
.kerne-field-note[data-kerne-warn="true"] { color: var(--kerne-warning); }
|
|
292
|
+
|
|
293
|
+
/* ---------- Password rules ---------- */
|
|
294
|
+
/* Unmet stays at hint weight rather than turning red: nothing is wrong yet,
|
|
295
|
+
the user is still typing. Only the tick earns a color. */
|
|
296
|
+
.kerne-rules { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
|
|
297
|
+
.kerne-rule {
|
|
298
|
+
display: flex; align-items: center; gap: 6px;
|
|
299
|
+
font-size: 12.5px; line-height: 1.4; color: var(--kerne-fg-muted);
|
|
300
|
+
}
|
|
301
|
+
.kerne-rule[data-met] { color: var(--kerne-success); }
|
|
302
|
+
|
|
303
|
+
/* ---------- Password strength ---------- */
|
|
304
|
+
.kerne-strength { display: flex; flex-direction: column; gap: 5px; }
|
|
305
|
+
.kerne-strength-track { display: flex; gap: 4px; }
|
|
306
|
+
.kerne-strength-step {
|
|
307
|
+
flex: 1; height: 3px; border-radius: 999px;
|
|
308
|
+
background: var(--kerne-track);
|
|
309
|
+
transition: background-color .18s ease;
|
|
310
|
+
}
|
|
311
|
+
.kerne-strength-label { font-size: 12px; color: var(--kerne-fg-muted); }
|
|
312
|
+
.kerne-strength[data-score="0"] .kerne-strength-step[data-on],
|
|
313
|
+
.kerne-strength[data-score="1"] .kerne-strength-step[data-on] { background: var(--kerne-danger); }
|
|
314
|
+
.kerne-strength[data-score="2"] .kerne-strength-step[data-on] { background: var(--kerne-warning); }
|
|
315
|
+
.kerne-strength[data-score="3"] .kerne-strength-step[data-on],
|
|
316
|
+
.kerne-strength[data-score="4"] .kerne-strength-step[data-on] { background: var(--kerne-success); }
|
|
317
|
+
@media (prefers-reduced-motion: reduce) {
|
|
318
|
+
.kerne-strength-step { transition: none; }
|
|
319
|
+
}
|
|
248
320
|
.kerne-input::placeholder { color: var(--kerne-fg-subtle); }
|
|
249
321
|
.kerne-input:hover:not(:disabled):not(:focus) { border-color: var(--kerne-fg-subtle); }
|
|
250
322
|
.kerne-input:focus {
|
|
@@ -279,6 +351,20 @@ var CSS = `
|
|
|
279
351
|
}
|
|
280
352
|
.kerne-button:focus-visible { outline: 2px solid var(--kerne-accent); outline-offset: 2px; }
|
|
281
353
|
.kerne-button:disabled { opacity: .5; cursor: not-allowed; }
|
|
354
|
+
/* A disabled primary drops the brand entirely rather than wearing it at half
|
|
355
|
+
opacity: on a dark theme the light primary faded into an opaque grey slab
|
|
356
|
+
that read as an enabled button, and a brand colour has no business marking
|
|
357
|
+
an action you cannot take. Loading is excluded - it is still the real
|
|
358
|
+
button, just busy. */
|
|
359
|
+
.kerne-button-primary:disabled:not([data-kerne-loading="true"]) {
|
|
360
|
+
opacity: 1;
|
|
361
|
+
background: var(--kerne-track);
|
|
362
|
+
color: var(--kerne-fg-subtle);
|
|
363
|
+
}
|
|
364
|
+
/* A button mid-request is busy, not unavailable - dimming it to the disabled
|
|
365
|
+
opacity reads as "this stopped working". */
|
|
366
|
+
.kerne-button[data-kerne-loading="true"] { opacity: 1; cursor: progress; }
|
|
367
|
+
.kerne-button .kerne-spinner { width: 15px; height: 15px; border-width: 2px; }
|
|
282
368
|
.kerne-button-primary { background: var(--kerne-primary); color: var(--kerne-primary-fg); }
|
|
283
369
|
.kerne-button-primary:hover:not(:disabled) { background: var(--kerne-primary-hover); }
|
|
284
370
|
.kerne-button-outline {
|
|
@@ -417,7 +503,7 @@ var CSS = `
|
|
|
417
503
|
/* Digits line up between rows instead of jittering as counts change. */
|
|
418
504
|
font-variant-numeric: tabular-nums;
|
|
419
505
|
}
|
|
420
|
-
.kerne-meter-track { height: 6px; border-radius: 999px; background: var(--kerne-
|
|
506
|
+
.kerne-meter-track { height: 6px; border-radius: 999px; background: var(--kerne-track); overflow: hidden; }
|
|
421
507
|
.kerne-meter-fill { height: 100%; border-radius: 999px; background: var(--kerne-fg); transition: width .3s ease; }
|
|
422
508
|
.kerne-meter[data-level="warning"] .kerne-meter-fill { background: var(--kerne-warning); }
|
|
423
509
|
.kerne-meter[data-level="critical"] .kerne-meter-fill { background: var(--kerne-danger); }
|
|
@@ -577,6 +663,43 @@ function resolveTheme(appearance) {
|
|
|
577
663
|
return appearance?.theme ?? "light";
|
|
578
664
|
}
|
|
579
665
|
|
|
666
|
+
// src/ui/password.ts
|
|
667
|
+
var MIN_PASSWORD_LENGTH = 8;
|
|
668
|
+
var BASELINE_POLICY = {
|
|
669
|
+
min_length: MIN_PASSWORD_LENGTH,
|
|
670
|
+
require_uppercase: false,
|
|
671
|
+
require_number: false,
|
|
672
|
+
require_symbol: false
|
|
673
|
+
};
|
|
674
|
+
function resolvePolicy(config) {
|
|
675
|
+
return config?.password_policy ?? BASELINE_POLICY;
|
|
676
|
+
}
|
|
677
|
+
function passwordRules(password, policy) {
|
|
678
|
+
const rules = [{ key: "min_length", met: password.length >= policy.min_length }];
|
|
679
|
+
if (policy.require_uppercase) {
|
|
680
|
+
rules.push({ key: "require_uppercase", met: /[A-Z]/.test(password) });
|
|
681
|
+
}
|
|
682
|
+
if (policy.require_number) {
|
|
683
|
+
rules.push({ key: "require_number", met: /\d/.test(password) });
|
|
684
|
+
}
|
|
685
|
+
if (policy.require_symbol) {
|
|
686
|
+
rules.push({ key: "require_symbol", met: /[^A-Za-z0-9]/.test(password) });
|
|
687
|
+
}
|
|
688
|
+
return rules;
|
|
689
|
+
}
|
|
690
|
+
function meetsPolicy(password, policy) {
|
|
691
|
+
return passwordRules(password, policy).every((rule) => rule.met);
|
|
692
|
+
}
|
|
693
|
+
function passwordStrength(password) {
|
|
694
|
+
if (password.length < MIN_PASSWORD_LENGTH) return 0;
|
|
695
|
+
const variety = [/[a-z]/, /[A-Z]/, /\d/, /[^A-Za-z0-9]/].filter((re) => re.test(password)).length;
|
|
696
|
+
let score = 1;
|
|
697
|
+
if (password.length >= 12) score++;
|
|
698
|
+
if (password.length >= 16) score++;
|
|
699
|
+
if (variety >= 3) score++;
|
|
700
|
+
return Math.min(score, 4);
|
|
701
|
+
}
|
|
702
|
+
|
|
580
703
|
// src/ui/primitives.tsx
|
|
581
704
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
582
705
|
var InsideShell = createContext(false);
|
|
@@ -635,6 +758,10 @@ function Masthead({
|
|
|
635
758
|
] })
|
|
636
759
|
] });
|
|
637
760
|
}
|
|
761
|
+
function prefersAutoFocus() {
|
|
762
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
763
|
+
return window.matchMedia("(pointer: fine)").matches;
|
|
764
|
+
}
|
|
638
765
|
function Field({
|
|
639
766
|
label,
|
|
640
767
|
type,
|
|
@@ -645,55 +772,185 @@ function Field({
|
|
|
645
772
|
disabled,
|
|
646
773
|
required,
|
|
647
774
|
invalid,
|
|
775
|
+
error,
|
|
648
776
|
action,
|
|
649
777
|
code,
|
|
650
|
-
autoFocus
|
|
778
|
+
autoFocus,
|
|
779
|
+
children
|
|
651
780
|
}) {
|
|
652
781
|
const id = useId();
|
|
782
|
+
const errorId = `${id}-error`;
|
|
783
|
+
const noteId = `${id}-note`;
|
|
784
|
+
const l10n = useLocalization();
|
|
785
|
+
const isPassword = type === "password";
|
|
786
|
+
const [revealed, setRevealed] = useState2(false);
|
|
787
|
+
const [capsLock, setCapsLock] = useState2(false);
|
|
788
|
+
const trackCapsLock = (e) => {
|
|
789
|
+
if (!isPassword) return;
|
|
790
|
+
setCapsLock(e.getModifierState?.("CapsLock") ?? false);
|
|
791
|
+
};
|
|
792
|
+
const describedBy = [error ? errorId : null, children ? noteId : null].filter(Boolean).join(" ");
|
|
653
793
|
return /* @__PURE__ */ jsxs("div", { className: "kerne-field", children: [
|
|
654
794
|
action ? /* @__PURE__ */ jsxs("div", { className: "kerne-label-row", children: [
|
|
655
795
|
/* @__PURE__ */ jsx("label", { className: "kerne-label", htmlFor: id, children: label }),
|
|
656
796
|
action
|
|
657
797
|
] }) : /* @__PURE__ */ jsx("label", { className: "kerne-label", htmlFor: id, children: label }),
|
|
658
|
-
/* @__PURE__ */
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
798
|
+
/* @__PURE__ */ jsxs("div", { className: "kerne-control", children: [
|
|
799
|
+
/* @__PURE__ */ jsx(
|
|
800
|
+
"input",
|
|
801
|
+
{
|
|
802
|
+
id,
|
|
803
|
+
className: "kerne-input",
|
|
804
|
+
type: isPassword && revealed ? "text" : type,
|
|
805
|
+
value,
|
|
806
|
+
onChange: (e) => onChange(e.target.value),
|
|
807
|
+
onKeyUp: trackCapsLock,
|
|
808
|
+
onBlur: () => setCapsLock(false),
|
|
809
|
+
autoComplete,
|
|
810
|
+
placeholder,
|
|
811
|
+
disabled,
|
|
812
|
+
required,
|
|
813
|
+
"aria-invalid": invalid || !!error || void 0,
|
|
814
|
+
"aria-describedby": describedBy || void 0,
|
|
815
|
+
"data-kerne-code": code ? "true" : void 0,
|
|
816
|
+
"data-kerne-reveal": isPassword ? "true" : void 0,
|
|
817
|
+
autoFocus: autoFocus && prefersAutoFocus()
|
|
818
|
+
}
|
|
819
|
+
),
|
|
820
|
+
isPassword ? /* @__PURE__ */ jsx(
|
|
821
|
+
"button",
|
|
822
|
+
{
|
|
823
|
+
type: "button",
|
|
824
|
+
className: "kerne-reveal",
|
|
825
|
+
onClick: () => setRevealed((v) => !v),
|
|
826
|
+
disabled,
|
|
827
|
+
"aria-label": revealed ? l10n.common.hidePassword : l10n.common.showPassword,
|
|
828
|
+
"aria-pressed": revealed,
|
|
829
|
+
tabIndex: -1,
|
|
830
|
+
children: revealed ? /* @__PURE__ */ jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsx(EyeIcon, {})
|
|
831
|
+
}
|
|
832
|
+
) : null
|
|
833
|
+
] }),
|
|
834
|
+
children ? /* @__PURE__ */ jsx("div", { id: noteId, children }) : null,
|
|
835
|
+
capsLock ? /* @__PURE__ */ jsx("p", { className: "kerne-field-note", "data-kerne-warn": "true", role: "status", children: l10n.common.capsLockOn }) : null,
|
|
836
|
+
error ? /* @__PURE__ */ jsx("p", { className: "kerne-field-error", id: errorId, role: "alert", children: error }) : null
|
|
675
837
|
] });
|
|
676
838
|
}
|
|
839
|
+
function EyeIcon() {
|
|
840
|
+
return /* @__PURE__ */ jsxs(
|
|
841
|
+
"svg",
|
|
842
|
+
{
|
|
843
|
+
width: "16",
|
|
844
|
+
height: "16",
|
|
845
|
+
viewBox: "0 0 24 24",
|
|
846
|
+
fill: "none",
|
|
847
|
+
stroke: "currentColor",
|
|
848
|
+
strokeWidth: "2",
|
|
849
|
+
"aria-hidden": "true",
|
|
850
|
+
children: [
|
|
851
|
+
/* @__PURE__ */ jsx("path", { d: "M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z", strokeLinejoin: "round" }),
|
|
852
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
|
|
853
|
+
]
|
|
854
|
+
}
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
function EyeOffIcon() {
|
|
858
|
+
return /* @__PURE__ */ jsxs(
|
|
859
|
+
"svg",
|
|
860
|
+
{
|
|
861
|
+
width: "16",
|
|
862
|
+
height: "16",
|
|
863
|
+
viewBox: "0 0 24 24",
|
|
864
|
+
fill: "none",
|
|
865
|
+
stroke: "currentColor",
|
|
866
|
+
strokeWidth: "2",
|
|
867
|
+
"aria-hidden": "true",
|
|
868
|
+
children: [
|
|
869
|
+
/* @__PURE__ */ jsx(
|
|
870
|
+
"path",
|
|
871
|
+
{
|
|
872
|
+
d: "M10.6 6.2A9.9 9.9 0 0 1 12 6c6.4 0 10 7 10 7a15.7 15.7 0 0 1-3 3.7M6.2 7.4A15.6 15.6 0 0 0 2 13s3.6 7 10 7a9.7 9.7 0 0 0 4.3-1",
|
|
873
|
+
strokeLinecap: "round",
|
|
874
|
+
strokeLinejoin: "round"
|
|
875
|
+
}
|
|
876
|
+
),
|
|
877
|
+
/* @__PURE__ */ jsx("path", { d: "m3 3 18 18", strokeLinecap: "round" })
|
|
878
|
+
]
|
|
879
|
+
}
|
|
880
|
+
);
|
|
881
|
+
}
|
|
677
882
|
function Button({
|
|
678
883
|
children,
|
|
679
884
|
variant = "primary",
|
|
680
885
|
disabled,
|
|
886
|
+
loading,
|
|
681
887
|
type = "submit",
|
|
682
888
|
onClick,
|
|
683
889
|
ariaLabel
|
|
684
890
|
}) {
|
|
685
|
-
return /* @__PURE__ */
|
|
891
|
+
return /* @__PURE__ */ jsxs(
|
|
686
892
|
"button",
|
|
687
893
|
{
|
|
688
894
|
type,
|
|
689
895
|
className: `kerne-button kerne-button-${variant}`,
|
|
690
|
-
disabled,
|
|
896
|
+
disabled: disabled || loading,
|
|
691
897
|
onClick,
|
|
692
898
|
"aria-label": ariaLabel,
|
|
693
|
-
|
|
899
|
+
"aria-busy": loading || void 0,
|
|
900
|
+
"data-kerne-loading": loading ? "true" : void 0,
|
|
901
|
+
children: [
|
|
902
|
+
loading ? /* @__PURE__ */ jsx("span", { className: "kerne-spinner", "aria-hidden": "true" }) : null,
|
|
903
|
+
children
|
|
904
|
+
]
|
|
694
905
|
}
|
|
695
906
|
);
|
|
696
907
|
}
|
|
908
|
+
function PasswordStrength({
|
|
909
|
+
password,
|
|
910
|
+
policy
|
|
911
|
+
}) {
|
|
912
|
+
const l10n = useLocalization();
|
|
913
|
+
const resolved = policy ?? resolvePolicy(null);
|
|
914
|
+
if (!meetsPolicy(password, resolved)) {
|
|
915
|
+
const labels2 = {
|
|
916
|
+
min_length: fill(l10n.common.passwordHint, { count: resolved.min_length }),
|
|
917
|
+
require_uppercase: l10n.common.passwordRequireUppercase,
|
|
918
|
+
require_number: l10n.common.passwordRequireNumber,
|
|
919
|
+
require_symbol: l10n.common.passwordRequireSymbol
|
|
920
|
+
};
|
|
921
|
+
const rules = passwordRules(password, resolved);
|
|
922
|
+
if (rules.length === 1) {
|
|
923
|
+
return /* @__PURE__ */ jsx("p", { className: "kerne-field-note", children: labels2[rules[0].key] });
|
|
924
|
+
}
|
|
925
|
+
return /* @__PURE__ */ jsx("ul", { className: "kerne-rules", children: rules.map((rule) => /* @__PURE__ */ jsxs("li", { className: "kerne-rule", "data-met": rule.met ? "true" : void 0, children: [
|
|
926
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: rule.met ? "\u2713" : "\xB7" }),
|
|
927
|
+
labels2[rule.key]
|
|
928
|
+
] }, rule.key)) });
|
|
929
|
+
}
|
|
930
|
+
const score = passwordStrength(password);
|
|
931
|
+
const labels = [
|
|
932
|
+
l10n.common.strengthWeak,
|
|
933
|
+
l10n.common.strengthWeak,
|
|
934
|
+
l10n.common.strengthFair,
|
|
935
|
+
l10n.common.strengthGood,
|
|
936
|
+
l10n.common.strengthStrong
|
|
937
|
+
];
|
|
938
|
+
return /* @__PURE__ */ jsxs("div", { className: "kerne-strength", "data-score": score, children: [
|
|
939
|
+
/* @__PURE__ */ jsx("div", { className: "kerne-strength-track", "aria-hidden": "true", children: [1, 2, 3, 4].map((step) => /* @__PURE__ */ jsx(
|
|
940
|
+
"span",
|
|
941
|
+
{
|
|
942
|
+
className: "kerne-strength-step",
|
|
943
|
+
"data-on": score >= step ? "true" : void 0
|
|
944
|
+
},
|
|
945
|
+
step
|
|
946
|
+
)) }),
|
|
947
|
+
/* @__PURE__ */ jsxs("span", { className: "kerne-strength-label", children: [
|
|
948
|
+
l10n.common.strengthLabel,
|
|
949
|
+
": ",
|
|
950
|
+
labels[score]
|
|
951
|
+
] })
|
|
952
|
+
] });
|
|
953
|
+
}
|
|
697
954
|
function MethodSwitch({
|
|
698
955
|
onClick,
|
|
699
956
|
children
|
|
@@ -746,7 +1003,7 @@ function Panel({
|
|
|
746
1003
|
"div",
|
|
747
1004
|
{
|
|
748
1005
|
className: `kerne-panel${danger ? " kerne-panel-danger" : ""}${variant === "success" ? " kerne-panel-success" : ""}`,
|
|
749
|
-
...danger ? { role: "alert" } : {},
|
|
1006
|
+
...danger ? { role: "alert" } : { role: "status", "aria-live": "polite" },
|
|
750
1007
|
children: [
|
|
751
1008
|
icon,
|
|
752
1009
|
/* @__PURE__ */ jsx("span", { children })
|
|
@@ -911,12 +1168,12 @@ function LoginForm({
|
|
|
911
1168
|
const { config, isLoading: configLoading, error: configError } = useAuthConfig();
|
|
912
1169
|
const l10n = useLocalization();
|
|
913
1170
|
const showError = useErrorResolver();
|
|
914
|
-
const [email, setEmail] =
|
|
915
|
-
const [password, setPassword] =
|
|
916
|
-
const [submitting, setSubmitting] =
|
|
917
|
-
const [error, setError] =
|
|
918
|
-
const [linkSentTo, setLinkSentTo] =
|
|
919
|
-
const [passwordMode, setPasswordMode] =
|
|
1171
|
+
const [email, setEmail] = useState3("");
|
|
1172
|
+
const [password, setPassword] = useState3("");
|
|
1173
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
1174
|
+
const [error, setError] = useState3(null);
|
|
1175
|
+
const [linkSentTo, setLinkSentTo] = useState3(null);
|
|
1176
|
+
const [passwordMode, setPasswordMode] = useState3(false);
|
|
920
1177
|
const shell = (children) => /* @__PURE__ */ jsx2(Shell, { appearance, className, children });
|
|
921
1178
|
const hasOAuth = (config?.oauth_providers.length ?? 0) > 0;
|
|
922
1179
|
const hasCredentials = config?.enable_credentials ?? false;
|
|
@@ -1012,7 +1269,7 @@ function LoginForm({
|
|
|
1012
1269
|
action: forgotPasswordUrl || onForgotPasswordClick ? /* @__PURE__ */ jsx2(TextLink, { href: forgotPasswordUrl, onClick: onForgotPasswordClick, children: l10n.signIn.forgotPassword }) : void 0
|
|
1013
1270
|
}
|
|
1014
1271
|
) : null,
|
|
1015
|
-
/* @__PURE__ */ jsx2(Button, {
|
|
1272
|
+
/* @__PURE__ */ jsx2(Button, { loading: submitting, disabled: !email || usePassword && !password, children: submitLabel }),
|
|
1016
1273
|
hasCredentials && hasMagicLink ? /* @__PURE__ */ jsx2(
|
|
1017
1274
|
MethodSwitch,
|
|
1018
1275
|
{
|
|
@@ -1041,7 +1298,7 @@ function LoginForm({
|
|
|
1041
1298
|
}
|
|
1042
1299
|
|
|
1043
1300
|
// src/ui/RegisterForm.tsx
|
|
1044
|
-
import { useState as
|
|
1301
|
+
import { useState as useState4 } from "react";
|
|
1045
1302
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1046
1303
|
function RegisterForm({
|
|
1047
1304
|
onSuccess,
|
|
@@ -1062,17 +1319,18 @@ function RegisterForm({
|
|
|
1062
1319
|
const client = useClient();
|
|
1063
1320
|
const { register, startPasswordless } = useAuth();
|
|
1064
1321
|
const { config, isLoading: configLoading, error: configError } = useAuthConfig();
|
|
1322
|
+
const policy = resolvePolicy(config);
|
|
1065
1323
|
const invitationToken = useUrlToken("token", invitationTokenProp) || void 0;
|
|
1066
1324
|
const l10n = useLocalization();
|
|
1067
1325
|
const showError = useErrorResolver();
|
|
1068
|
-
const [email, setEmail] =
|
|
1069
|
-
const [password, setPassword] =
|
|
1070
|
-
const [name, setName] =
|
|
1071
|
-
const [invitationCode, setInvitationCode] =
|
|
1072
|
-
const [submitting, setSubmitting] =
|
|
1073
|
-
const [error, setError] =
|
|
1074
|
-
const [linkSentTo, setLinkSentTo] =
|
|
1075
|
-
const [passwordMode, setPasswordMode] =
|
|
1326
|
+
const [email, setEmail] = useState4("");
|
|
1327
|
+
const [password, setPassword] = useState4("");
|
|
1328
|
+
const [name, setName] = useState4("");
|
|
1329
|
+
const [invitationCode, setInvitationCode] = useState4("");
|
|
1330
|
+
const [submitting, setSubmitting] = useState4(false);
|
|
1331
|
+
const [error, setError] = useState4(null);
|
|
1332
|
+
const [linkSentTo, setLinkSentTo] = useState4(null);
|
|
1333
|
+
const [passwordMode, setPasswordMode] = useState4(false);
|
|
1076
1334
|
const shell = (children) => /* @__PURE__ */ jsx3(Shell, { appearance, className, children });
|
|
1077
1335
|
const appName = config?.app_name;
|
|
1078
1336
|
const heading = title ?? (appName ? fill(l10n.signUp.titleWithApp, { app: appName }) : l10n.signUp.title);
|
|
@@ -1216,13 +1474,15 @@ function RegisterForm({
|
|
|
1216
1474
|
autoComplete: "new-password",
|
|
1217
1475
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1218
1476
|
disabled: submitting,
|
|
1219
|
-
required: true
|
|
1477
|
+
required: true,
|
|
1478
|
+
children: /* @__PURE__ */ jsx3(PasswordStrength, { password, policy })
|
|
1220
1479
|
}
|
|
1221
1480
|
) : null,
|
|
1222
1481
|
/* @__PURE__ */ jsx3(
|
|
1223
1482
|
Button,
|
|
1224
1483
|
{
|
|
1225
|
-
|
|
1484
|
+
loading: submitting,
|
|
1485
|
+
disabled: !email || needsCode && !invitationCode || usePassword && !meetsPolicy(password, policy),
|
|
1226
1486
|
children: submitLabel
|
|
1227
1487
|
}
|
|
1228
1488
|
),
|
|
@@ -1254,7 +1514,7 @@ function RegisterForm({
|
|
|
1254
1514
|
}
|
|
1255
1515
|
|
|
1256
1516
|
// src/ui/ForgotPasswordForm.tsx
|
|
1257
|
-
import { useState as
|
|
1517
|
+
import { useState as useState5 } from "react";
|
|
1258
1518
|
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1259
1519
|
function ForgotPasswordForm({
|
|
1260
1520
|
callbackUrl,
|
|
@@ -1271,10 +1531,10 @@ function ForgotPasswordForm({
|
|
|
1271
1531
|
const { config } = useAuthConfig();
|
|
1272
1532
|
const l10n = useLocalization();
|
|
1273
1533
|
const showError = useErrorResolver();
|
|
1274
|
-
const [email, setEmail] =
|
|
1275
|
-
const [submitting, setSubmitting] =
|
|
1276
|
-
const [error, setError] =
|
|
1277
|
-
const [sentTo, setSentTo] =
|
|
1534
|
+
const [email, setEmail] = useState5("");
|
|
1535
|
+
const [submitting, setSubmitting] = useState5(false);
|
|
1536
|
+
const [error, setError] = useState5(null);
|
|
1537
|
+
const [sentTo, setSentTo] = useState5(null);
|
|
1278
1538
|
const handleSubmit = async (e) => {
|
|
1279
1539
|
e.preventDefault();
|
|
1280
1540
|
setError(null);
|
|
@@ -1321,7 +1581,7 @@ function ForgotPasswordForm({
|
|
|
1321
1581
|
autoFocus: true
|
|
1322
1582
|
}
|
|
1323
1583
|
),
|
|
1324
|
-
/* @__PURE__ */ jsx4(Button, {
|
|
1584
|
+
/* @__PURE__ */ jsx4(Button, { loading: submitting, disabled: !email, children: submitting ? l10n.resetPassword.requestSubmitting : l10n.resetPassword.requestSubmit }),
|
|
1325
1585
|
backLine ? /* @__PURE__ */ jsx4("p", { className: "kerne-footer", children: backLine }) : null
|
|
1326
1586
|
] })
|
|
1327
1587
|
] }),
|
|
@@ -1330,7 +1590,7 @@ function ForgotPasswordForm({
|
|
|
1330
1590
|
}
|
|
1331
1591
|
|
|
1332
1592
|
// src/ui/ResetPasswordForm.tsx
|
|
1333
|
-
import { useState as
|
|
1593
|
+
import { useState as useState6 } from "react";
|
|
1334
1594
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1335
1595
|
function ResetPasswordForm({
|
|
1336
1596
|
token: tokenProp,
|
|
@@ -1345,14 +1605,15 @@ function ResetPasswordForm({
|
|
|
1345
1605
|
}) {
|
|
1346
1606
|
const { confirmPasswordReset } = useAuth();
|
|
1347
1607
|
const { config } = useAuthConfig();
|
|
1608
|
+
const policy = resolvePolicy(config);
|
|
1348
1609
|
const token = useUrlToken("token", tokenProp);
|
|
1349
1610
|
const l10n = useLocalization();
|
|
1350
1611
|
const showError = useErrorResolver();
|
|
1351
|
-
const [password, setPassword] =
|
|
1352
|
-
const [confirm, setConfirm] =
|
|
1353
|
-
const [submitting, setSubmitting] =
|
|
1354
|
-
const [error, setError] =
|
|
1355
|
-
const [done, setDone] =
|
|
1612
|
+
const [password, setPassword] = useState6("");
|
|
1613
|
+
const [confirm, setConfirm] = useState6("");
|
|
1614
|
+
const [submitting, setSubmitting] = useState6(false);
|
|
1615
|
+
const [error, setError] = useState6(null);
|
|
1616
|
+
const [done, setDone] = useState6(false);
|
|
1356
1617
|
const heading = title ?? l10n.resetPassword.title;
|
|
1357
1618
|
const mismatch = confirm.length > 0 && password !== confirm;
|
|
1358
1619
|
const signInAction = signInUrl || onSignInClick ? /* @__PURE__ */ jsx5(
|
|
@@ -1416,7 +1677,8 @@ function ResetPasswordForm({
|
|
|
1416
1677
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1417
1678
|
disabled: submitting,
|
|
1418
1679
|
required: true,
|
|
1419
|
-
autoFocus: true
|
|
1680
|
+
autoFocus: true,
|
|
1681
|
+
children: /* @__PURE__ */ jsx5(PasswordStrength, { password, policy })
|
|
1420
1682
|
}
|
|
1421
1683
|
),
|
|
1422
1684
|
/* @__PURE__ */ jsx5(
|
|
@@ -1430,11 +1692,17 @@ function ResetPasswordForm({
|
|
|
1430
1692
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1431
1693
|
disabled: submitting,
|
|
1432
1694
|
required: true,
|
|
1433
|
-
|
|
1695
|
+
error: mismatch ? l10n.resetPassword.mismatch : void 0
|
|
1696
|
+
}
|
|
1697
|
+
),
|
|
1698
|
+
/* @__PURE__ */ jsx5(
|
|
1699
|
+
Button,
|
|
1700
|
+
{
|
|
1701
|
+
loading: submitting,
|
|
1702
|
+
disabled: !meetsPolicy(password, policy) || !confirm || mismatch,
|
|
1703
|
+
children: submitting ? l10n.resetPassword.submitting : l10n.resetPassword.submit
|
|
1434
1704
|
}
|
|
1435
1705
|
),
|
|
1436
|
-
mismatch ? /* @__PURE__ */ jsx5("p", { className: "kerne-footer", children: l10n.resetPassword.mismatch }) : null,
|
|
1437
|
-
/* @__PURE__ */ jsx5(Button, { disabled: submitting || !password || !confirm || mismatch, children: submitting ? l10n.resetPassword.submitting : l10n.resetPassword.submit }),
|
|
1438
1706
|
signInUrl || onSignInClick ? /* @__PURE__ */ jsx5("p", { className: "kerne-footer", children: /* @__PURE__ */ jsx5(TextLink, { href: signInUrl, onClick: onSignInClick, children: l10n.common.backToSignIn }) }) : null
|
|
1439
1707
|
] })
|
|
1440
1708
|
] }),
|
|
@@ -1443,7 +1711,7 @@ function ResetPasswordForm({
|
|
|
1443
1711
|
}
|
|
1444
1712
|
|
|
1445
1713
|
// src/ui/MagicLinkCallback.tsx
|
|
1446
|
-
import { useEffect as useEffect2, useRef, useState as
|
|
1714
|
+
import { useEffect as useEffect2, useRef, useState as useState7 } from "react";
|
|
1447
1715
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1448
1716
|
function MagicLinkCallback({
|
|
1449
1717
|
token: tokenProp,
|
|
@@ -1461,8 +1729,8 @@ function MagicLinkCallback({
|
|
|
1461
1729
|
const token = useUrlToken("token", tokenProp);
|
|
1462
1730
|
const l10n = useLocalization();
|
|
1463
1731
|
const showError = useErrorResolver();
|
|
1464
|
-
const [state, setState] =
|
|
1465
|
-
const [message, setMessage] =
|
|
1732
|
+
const [state, setState] = useState7("pending");
|
|
1733
|
+
const [message, setMessage] = useState7(null);
|
|
1466
1734
|
const consumed = useRef(false);
|
|
1467
1735
|
useEffect2(() => {
|
|
1468
1736
|
if (token === null || consumed.current) return;
|
|
@@ -1502,7 +1770,7 @@ function MagicLinkCallback({
|
|
|
1502
1770
|
}
|
|
1503
1771
|
|
|
1504
1772
|
// src/ui/VerifyEmailForm.tsx
|
|
1505
|
-
import { useEffect as useEffect3, useRef as useRef2, useState as
|
|
1773
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState8 } from "react";
|
|
1506
1774
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1507
1775
|
function VerifyEmailForm({
|
|
1508
1776
|
token: tokenProp,
|
|
@@ -1519,12 +1787,12 @@ function VerifyEmailForm({
|
|
|
1519
1787
|
const token = useUrlToken("token", tokenProp);
|
|
1520
1788
|
const l10n = useLocalization();
|
|
1521
1789
|
const showError = useErrorResolver();
|
|
1522
|
-
const [code, setCode] =
|
|
1523
|
-
const [submitting, setSubmitting] =
|
|
1524
|
-
const [error, setError] =
|
|
1525
|
-
const [resent, setResent] =
|
|
1526
|
-
const [linkState, setLinkState] =
|
|
1527
|
-
const [verified, setVerified] =
|
|
1790
|
+
const [code, setCode] = useState8("");
|
|
1791
|
+
const [submitting, setSubmitting] = useState8(false);
|
|
1792
|
+
const [error, setError] = useState8(null);
|
|
1793
|
+
const [resent, setResent] = useState8(false);
|
|
1794
|
+
const [linkState, setLinkState] = useState8("idle");
|
|
1795
|
+
const [verified, setVerified] = useState8(false);
|
|
1528
1796
|
const consumed = useRef2(false);
|
|
1529
1797
|
const heading = title ?? l10n.verifyEmail.title;
|
|
1530
1798
|
const settle = async (needsTokenRefresh) => {
|
|
@@ -1618,7 +1886,7 @@ function VerifyEmailForm({
|
|
|
1618
1886
|
autoFocus: true
|
|
1619
1887
|
}
|
|
1620
1888
|
),
|
|
1621
|
-
/* @__PURE__ */ jsx7(Button, {
|
|
1889
|
+
/* @__PURE__ */ jsx7(Button, { loading: submitting, disabled: !code, children: submitting ? l10n.verifyEmail.submitting : l10n.verifyEmail.submit }),
|
|
1622
1890
|
/* @__PURE__ */ jsx7(MethodSwitch, { onClick: handleResend, children: l10n.verifyEmail.resend })
|
|
1623
1891
|
] })
|
|
1624
1892
|
] }),
|
|
@@ -1627,7 +1895,7 @@ function VerifyEmailForm({
|
|
|
1627
1895
|
}
|
|
1628
1896
|
|
|
1629
1897
|
// src/ui/ActivateAccountForm.tsx
|
|
1630
|
-
import { useEffect as useEffect4, useState as
|
|
1898
|
+
import { useEffect as useEffect4, useState as useState9 } from "react";
|
|
1631
1899
|
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1632
1900
|
function ActivateAccountForm({
|
|
1633
1901
|
token: tokenProp,
|
|
@@ -1643,15 +1911,16 @@ function ActivateAccountForm({
|
|
|
1643
1911
|
const client = useClient();
|
|
1644
1912
|
const { getActivationContext, completeActivation } = useAuth();
|
|
1645
1913
|
const { config } = useAuthConfig();
|
|
1914
|
+
const policy = resolvePolicy(config);
|
|
1646
1915
|
const token = useUrlToken("token", tokenProp);
|
|
1647
1916
|
const l10n = useLocalization();
|
|
1648
1917
|
const showError = useErrorResolver();
|
|
1649
|
-
const [context, setContext] =
|
|
1650
|
-
const [loadError, setLoadError] =
|
|
1651
|
-
const [password, setPassword] =
|
|
1652
|
-
const [confirm, setConfirm] =
|
|
1653
|
-
const [submitting, setSubmitting] =
|
|
1654
|
-
const [error, setError] =
|
|
1918
|
+
const [context, setContext] = useState9(null);
|
|
1919
|
+
const [loadError, setLoadError] = useState9(null);
|
|
1920
|
+
const [password, setPassword] = useState9("");
|
|
1921
|
+
const [confirm, setConfirm] = useState9("");
|
|
1922
|
+
const [submitting, setSubmitting] = useState9(false);
|
|
1923
|
+
const [error, setError] = useState9(null);
|
|
1655
1924
|
const heading = title ?? l10n.activate.title;
|
|
1656
1925
|
const mismatch = confirm.length > 0 && password !== confirm;
|
|
1657
1926
|
useEffect4(() => {
|
|
@@ -1736,7 +2005,8 @@ function ActivateAccountForm({
|
|
|
1736
2005
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1737
2006
|
disabled: submitting,
|
|
1738
2007
|
required: true,
|
|
1739
|
-
autoFocus: true
|
|
2008
|
+
autoFocus: true,
|
|
2009
|
+
children: /* @__PURE__ */ jsx8(PasswordStrength, { password, policy })
|
|
1740
2010
|
}
|
|
1741
2011
|
),
|
|
1742
2012
|
/* @__PURE__ */ jsx8(
|
|
@@ -1750,12 +2020,18 @@ function ActivateAccountForm({
|
|
|
1750
2020
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1751
2021
|
disabled: submitting,
|
|
1752
2022
|
required: true,
|
|
1753
|
-
|
|
2023
|
+
error: mismatch ? l10n.activate.mismatch : void 0
|
|
1754
2024
|
}
|
|
1755
|
-
)
|
|
1756
|
-
mismatch ? /* @__PURE__ */ jsx8("p", { className: "kerne-footer", children: l10n.activate.mismatch }) : null
|
|
2025
|
+
)
|
|
1757
2026
|
] }) : null,
|
|
1758
|
-
/* @__PURE__ */ jsx8(
|
|
2027
|
+
/* @__PURE__ */ jsx8(
|
|
2028
|
+
Button,
|
|
2029
|
+
{
|
|
2030
|
+
loading: submitting,
|
|
2031
|
+
disabled: credentialsMode && (!meetsPolicy(password, policy) || !confirm || mismatch),
|
|
2032
|
+
children: submitting ? l10n.activate.submitting : l10n.activate.submit
|
|
2033
|
+
}
|
|
2034
|
+
)
|
|
1759
2035
|
] })
|
|
1760
2036
|
] }),
|
|
1761
2037
|
/* @__PURE__ */ jsx8(KerneBranding, { show: config?.show_kerne_branding ?? false })
|
|
@@ -1782,7 +2058,7 @@ function AuthFlow({
|
|
|
1782
2058
|
}) {
|
|
1783
2059
|
const { config } = useAuthConfig();
|
|
1784
2060
|
const token = useUrlToken("token");
|
|
1785
|
-
const [internalStep, setInternalStep] =
|
|
2061
|
+
const [internalStep, setInternalStep] = useState10(null);
|
|
1786
2062
|
const go = (next) => {
|
|
1787
2063
|
if (controlledStep === void 0) setInternalStep(next);
|
|
1788
2064
|
onStepChange?.(next);
|
|
@@ -1790,7 +2066,11 @@ function AuthFlow({
|
|
|
1790
2066
|
const tokenStep = token && (initialStep === void 0 || TOKEN_STEPS.includes(initialStep)) ? initialStep ?? "magic-link" : null;
|
|
1791
2067
|
const step = controlledStep ?? internalStep ?? tokenStep ?? initialStep ?? "sign-in";
|
|
1792
2068
|
const canSignUp = allowSignUp && config?.registration_mode !== "CLOSED";
|
|
1793
|
-
const shared = {
|
|
2069
|
+
const shared = {
|
|
2070
|
+
appearance,
|
|
2071
|
+
logo,
|
|
2072
|
+
className: `kerne-step${className ? ` ${className}` : ""}`
|
|
2073
|
+
};
|
|
1794
2074
|
switch (step) {
|
|
1795
2075
|
case "sign-up":
|
|
1796
2076
|
return /* @__PURE__ */ jsx9(
|
|
@@ -1855,7 +2135,7 @@ function AuthFlow({
|
|
|
1855
2135
|
}
|
|
1856
2136
|
|
|
1857
2137
|
// src/ui/ChangePasswordForm.tsx
|
|
1858
|
-
import { useState as
|
|
2138
|
+
import { useState as useState11 } from "react";
|
|
1859
2139
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1860
2140
|
function ChangePasswordForm({
|
|
1861
2141
|
onSuccess,
|
|
@@ -1864,15 +2144,17 @@ function ChangePasswordForm({
|
|
|
1864
2144
|
className
|
|
1865
2145
|
}) {
|
|
1866
2146
|
const { updatePassword } = useAuth();
|
|
2147
|
+
const { config } = useAuthConfig();
|
|
2148
|
+
const policy = resolvePolicy(config);
|
|
1867
2149
|
const l10n = useLocalization();
|
|
1868
2150
|
const showError = useErrorResolver();
|
|
1869
2151
|
const heading = title === null ? null : title ?? l10n.security.submit;
|
|
1870
|
-
const [currentPassword, setCurrentPassword] =
|
|
1871
|
-
const [newPassword, setNewPassword] =
|
|
1872
|
-
const [confirm, setConfirm] =
|
|
1873
|
-
const [submitting, setSubmitting] =
|
|
1874
|
-
const [error, setError] =
|
|
1875
|
-
const [done, setDone] =
|
|
2152
|
+
const [currentPassword, setCurrentPassword] = useState11("");
|
|
2153
|
+
const [newPassword, setNewPassword] = useState11("");
|
|
2154
|
+
const [confirm, setConfirm] = useState11("");
|
|
2155
|
+
const [submitting, setSubmitting] = useState11(false);
|
|
2156
|
+
const [error, setError] = useState11(null);
|
|
2157
|
+
const [done, setDone] = useState11(false);
|
|
1876
2158
|
const mismatch = confirm.length > 0 && newPassword !== confirm;
|
|
1877
2159
|
const unchanged = newPassword.length > 0 && newPassword === currentPassword;
|
|
1878
2160
|
const handleSubmit = async (e) => {
|
|
@@ -1924,7 +2206,8 @@ function ChangePasswordForm({
|
|
|
1924
2206
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1925
2207
|
disabled: submitting,
|
|
1926
2208
|
required: true,
|
|
1927
|
-
|
|
2209
|
+
error: unchanged ? l10n.security.sameAsCurrent : void 0,
|
|
2210
|
+
children: /* @__PURE__ */ jsx10(PasswordStrength, { password: newPassword, policy })
|
|
1928
2211
|
}
|
|
1929
2212
|
),
|
|
1930
2213
|
/* @__PURE__ */ jsx10(
|
|
@@ -1938,14 +2221,14 @@ function ChangePasswordForm({
|
|
|
1938
2221
|
placeholder: l10n.common.passwordPlaceholder,
|
|
1939
2222
|
disabled: submitting,
|
|
1940
2223
|
required: true,
|
|
1941
|
-
|
|
2224
|
+
error: mismatch ? l10n.security.mismatch : void 0
|
|
1942
2225
|
}
|
|
1943
2226
|
),
|
|
1944
|
-
unchanged ? /* @__PURE__ */ jsx10("p", { className: "kerne-footer", children: l10n.security.sameAsCurrent }) : mismatch ? /* @__PURE__ */ jsx10("p", { className: "kerne-footer", children: l10n.security.mismatch }) : null,
|
|
1945
2227
|
/* @__PURE__ */ jsx10(
|
|
1946
2228
|
Button,
|
|
1947
2229
|
{
|
|
1948
|
-
|
|
2230
|
+
loading: submitting,
|
|
2231
|
+
disabled: !currentPassword || !meetsPolicy(newPassword, policy) || !confirm || mismatch || unchanged,
|
|
1949
2232
|
children: submitting ? l10n.security.submitting : l10n.security.submit
|
|
1950
2233
|
}
|
|
1951
2234
|
)
|
|
@@ -1955,7 +2238,7 @@ function ChangePasswordForm({
|
|
|
1955
2238
|
}
|
|
1956
2239
|
|
|
1957
2240
|
// src/ui/WaitlistForm.tsx
|
|
1958
|
-
import { useState as
|
|
2241
|
+
import { useState as useState12 } from "react";
|
|
1959
2242
|
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1960
2243
|
function WaitlistForm({
|
|
1961
2244
|
title,
|
|
@@ -1974,11 +2257,11 @@ function WaitlistForm({
|
|
|
1974
2257
|
const { config } = useAuthConfig();
|
|
1975
2258
|
const l10n = useLocalization();
|
|
1976
2259
|
const showError = useErrorResolver();
|
|
1977
|
-
const [email, setEmail] =
|
|
1978
|
-
const [name, setName] =
|
|
1979
|
-
const [submitting, setSubmitting] =
|
|
1980
|
-
const [error, setError] =
|
|
1981
|
-
const [joined, setJoined] =
|
|
2260
|
+
const [email, setEmail] = useState12("");
|
|
2261
|
+
const [name, setName] = useState12("");
|
|
2262
|
+
const [submitting, setSubmitting] = useState12(false);
|
|
2263
|
+
const [error, setError] = useState12(null);
|
|
2264
|
+
const [joined, setJoined] = useState12(false);
|
|
1982
2265
|
const handleSubmit = async (e) => {
|
|
1983
2266
|
e.preventDefault();
|
|
1984
2267
|
setError(null);
|
|
@@ -2035,7 +2318,7 @@ function WaitlistForm({
|
|
|
2035
2318
|
autoFocus: true
|
|
2036
2319
|
}
|
|
2037
2320
|
),
|
|
2038
|
-
/* @__PURE__ */ jsx11(Button, {
|
|
2321
|
+
/* @__PURE__ */ jsx11(Button, { loading: submitting, disabled: !email, children: submitting ? l10n.waitlist.submitting : submitLabel ?? l10n.waitlist.submit })
|
|
2039
2322
|
] })
|
|
2040
2323
|
] }),
|
|
2041
2324
|
legal ? /* @__PURE__ */ jsx11("p", { className: "kerne-legal", children: legal }) : null,
|
|
@@ -2044,7 +2327,7 @@ function WaitlistForm({
|
|
|
2044
2327
|
}
|
|
2045
2328
|
|
|
2046
2329
|
// src/ui/UserButton.tsx
|
|
2047
|
-
import { useEffect as useEffect5, useRef as useRef3, useState as
|
|
2330
|
+
import { useEffect as useEffect5, useRef as useRef3, useState as useState13 } from "react";
|
|
2048
2331
|
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2049
2332
|
function SignOutButton({
|
|
2050
2333
|
children,
|
|
@@ -2092,7 +2375,7 @@ function UserButton({
|
|
|
2092
2375
|
const { logout, isAuthenticated } = useAuth();
|
|
2093
2376
|
const { user, fullName, initials, email } = useUser();
|
|
2094
2377
|
const l10n = useLocalization();
|
|
2095
|
-
const [open, setOpen] =
|
|
2378
|
+
const [open, setOpen] = useState13(false);
|
|
2096
2379
|
const wrapRef = useRef3(null);
|
|
2097
2380
|
useEffect5(() => {
|
|
2098
2381
|
if (!open) return;
|
|
@@ -2176,24 +2459,24 @@ function UserButton({
|
|
|
2176
2459
|
}
|
|
2177
2460
|
|
|
2178
2461
|
// src/ui/UserProfile.tsx
|
|
2179
|
-
import { useState as
|
|
2462
|
+
import { useState as useState18 } from "react";
|
|
2180
2463
|
|
|
2181
2464
|
// src/ui/profile/ProfileSection.tsx
|
|
2182
|
-
import { useEffect as useEffect6, useState as
|
|
2465
|
+
import { useEffect as useEffect6, useState as useState14 } from "react";
|
|
2183
2466
|
import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2184
2467
|
function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
2185
2468
|
const { updateProfile, sendVerificationEmail, getVerificationStatus } = useAuth();
|
|
2186
2469
|
const { user, fullName, initials, email } = useUser();
|
|
2187
2470
|
const l10n = useLocalization();
|
|
2188
2471
|
const showError = useErrorResolver();
|
|
2189
|
-
const [firstName, setFirstName] =
|
|
2190
|
-
const [lastName, setLastName] =
|
|
2191
|
-
const [saving, setSaving] =
|
|
2192
|
-
const [saved, setSaved] =
|
|
2193
|
-
const [error, setError] =
|
|
2194
|
-
const [verified, setVerified] =
|
|
2195
|
-
const [sending, setSending] =
|
|
2196
|
-
const [sent, setSent] =
|
|
2472
|
+
const [firstName, setFirstName] = useState14("");
|
|
2473
|
+
const [lastName, setLastName] = useState14("");
|
|
2474
|
+
const [saving, setSaving] = useState14(false);
|
|
2475
|
+
const [saved, setSaved] = useState14(false);
|
|
2476
|
+
const [error, setError] = useState14(null);
|
|
2477
|
+
const [verified, setVerified] = useState14(null);
|
|
2478
|
+
const [sending, setSending] = useState14(false);
|
|
2479
|
+
const [sent, setSent] = useState14(false);
|
|
2197
2480
|
useEffect6(() => {
|
|
2198
2481
|
if (!user) return;
|
|
2199
2482
|
setFirstName(user.first_name ?? "");
|
|
@@ -2269,7 +2552,7 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2269
2552
|
disabled: saving
|
|
2270
2553
|
}
|
|
2271
2554
|
),
|
|
2272
|
-
/* @__PURE__ */ jsx13(Button, {
|
|
2555
|
+
/* @__PURE__ */ jsx13(Button, { loading: saving, children: saving ? l10n.profile.saving : l10n.profile.save })
|
|
2273
2556
|
] })
|
|
2274
2557
|
] }),
|
|
2275
2558
|
/* @__PURE__ */ jsxs12(
|
|
@@ -2283,7 +2566,7 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2283
2566
|
email,
|
|
2284
2567
|
verified === true ? /* @__PURE__ */ jsx13("span", { className: "kerne-badge", "data-tone": "success", children: l10n.profile.verified }) : verified === false ? /* @__PURE__ */ jsx13("span", { className: "kerne-badge", "data-tone": "warning", children: l10n.profile.unverified }) : null
|
|
2285
2568
|
] }) }),
|
|
2286
|
-
verified === false ? /* @__PURE__ */ jsx13(Button, { variant: "outline", type: "button", onClick: handleResend,
|
|
2569
|
+
verified === false ? /* @__PURE__ */ jsx13(Button, { variant: "outline", type: "button", onClick: handleResend, loading: sending, children: sending ? l10n.common.sending : l10n.profile.verify }) : null
|
|
2287
2570
|
] }),
|
|
2288
2571
|
sent ? /* @__PURE__ */ jsx13(Panel, { variant: "success", children: l10n.profile.verificationSent }) : null
|
|
2289
2572
|
]
|
|
@@ -2293,16 +2576,16 @@ function ProfileSection({ verificationCallbackUrl, onSaved }) {
|
|
|
2293
2576
|
}
|
|
2294
2577
|
|
|
2295
2578
|
// src/ui/profile/SecuritySection.tsx
|
|
2296
|
-
import { useState as
|
|
2579
|
+
import { useState as useState15 } from "react";
|
|
2297
2580
|
import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2298
2581
|
function SecuritySection({ showDeleteAccount = true, onDeleted }) {
|
|
2299
2582
|
const { requestAccountDeletion } = useAuth();
|
|
2300
2583
|
const { config } = useAuthConfig();
|
|
2301
2584
|
const l10n = useLocalization();
|
|
2302
2585
|
const showError = useErrorResolver();
|
|
2303
|
-
const [error, setError] =
|
|
2304
|
-
const [confirmingDelete, setConfirmingDelete] =
|
|
2305
|
-
const [deleting, setDeleting] =
|
|
2586
|
+
const [error, setError] = useState15(null);
|
|
2587
|
+
const [confirmingDelete, setConfirmingDelete] = useState15(false);
|
|
2588
|
+
const [deleting, setDeleting] = useState15(false);
|
|
2306
2589
|
const hasPassword = config?.enable_credentials ?? true;
|
|
2307
2590
|
const handleDelete = async () => {
|
|
2308
2591
|
setError(null);
|
|
@@ -2319,7 +2602,7 @@ function SecuritySection({ showDeleteAccount = true, onDeleted }) {
|
|
|
2319
2602
|
error ? /* @__PURE__ */ jsx14(Panel, { variant: "danger", children: error }) : null,
|
|
2320
2603
|
hasPassword ? /* @__PURE__ */ jsx14(Subsection, { title: l10n.security.passwordTitle, description: l10n.security.passwordDesc, children: /* @__PURE__ */ jsx14(ChangePasswordForm, { title: null }) }) : null,
|
|
2321
2604
|
showDeleteAccount ? /* @__PURE__ */ jsx14(Subsection, { title: l10n.security.deleteTitle, description: l10n.security.deleteDesc, children: confirmingDelete ? /* @__PURE__ */ jsxs13("div", { style: { display: "flex", gap: 10, flexWrap: "wrap" }, children: [
|
|
2322
|
-
/* @__PURE__ */ jsx14(Button, { variant: "danger", type: "button", onClick: handleDelete,
|
|
2605
|
+
/* @__PURE__ */ jsx14(Button, { variant: "danger", type: "button", onClick: handleDelete, loading: deleting, children: deleting ? l10n.security.deleting : l10n.security.deleteConfirm }),
|
|
2323
2606
|
/* @__PURE__ */ jsx14(
|
|
2324
2607
|
Button,
|
|
2325
2608
|
{
|
|
@@ -2335,7 +2618,7 @@ function SecuritySection({ showDeleteAccount = true, onDeleted }) {
|
|
|
2335
2618
|
}
|
|
2336
2619
|
|
|
2337
2620
|
// src/ui/profile/BillingSection.tsx
|
|
2338
|
-
import { useMemo, useState as
|
|
2621
|
+
import { useMemo, useState as useState17 } from "react";
|
|
2339
2622
|
|
|
2340
2623
|
// src/ui/format.ts
|
|
2341
2624
|
function humanizeFeatureKey(key) {
|
|
@@ -2486,7 +2769,7 @@ function UsageMeters({
|
|
|
2486
2769
|
}
|
|
2487
2770
|
|
|
2488
2771
|
// src/ui/SubscriptionCard.tsx
|
|
2489
|
-
import { useState as
|
|
2772
|
+
import { useState as useState16 } from "react";
|
|
2490
2773
|
import { Fragment as Fragment8, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2491
2774
|
var STATUS_TONES = {
|
|
2492
2775
|
ACTIVE: "success",
|
|
@@ -2519,8 +2802,8 @@ function SubscriptionCard({
|
|
|
2519
2802
|
UNPAID: l10n.billing.statusUnpaid,
|
|
2520
2803
|
PAUSED: l10n.billing.statusPaused
|
|
2521
2804
|
};
|
|
2522
|
-
const [opening, setOpening] =
|
|
2523
|
-
const [portalError, setPortalError] =
|
|
2805
|
+
const [opening, setOpening] = useState16(false);
|
|
2806
|
+
const [portalError, setPortalError] = useState16(null);
|
|
2524
2807
|
const handlePortal = async () => {
|
|
2525
2808
|
setPortalError(null);
|
|
2526
2809
|
setOpening(true);
|
|
@@ -2596,7 +2879,7 @@ function SubscriptionCard({
|
|
|
2596
2879
|
] }) : null
|
|
2597
2880
|
] }),
|
|
2598
2881
|
canceled_at ? /* @__PURE__ */ jsx16(Panel, { children: l10n.billing.cancelNotice }) : null,
|
|
2599
|
-
!hidePortalButton ? /* @__PURE__ */ jsx16(Button, { variant: "outline", type: "button", onClick: handlePortal,
|
|
2882
|
+
!hidePortalButton ? /* @__PURE__ */ jsx16(Button, { variant: "outline", type: "button", onClick: handlePortal, loading: opening, children: opening ? l10n.billing.opening : l10n.billing.managePortal }) : null
|
|
2600
2883
|
] })
|
|
2601
2884
|
);
|
|
2602
2885
|
}
|
|
@@ -2628,10 +2911,10 @@ function BillingSection({
|
|
|
2628
2911
|
const { openCheckout } = useCheckout();
|
|
2629
2912
|
const l10n = useLocalization();
|
|
2630
2913
|
const showError = useErrorResolver();
|
|
2631
|
-
const [busy, setBusy] =
|
|
2632
|
-
const [error, setError] =
|
|
2914
|
+
const [busy, setBusy] = useState17(null);
|
|
2915
|
+
const [error, setError] = useState17(null);
|
|
2633
2916
|
const intervals = useMemo(() => intervalsOf(plans), [plans]);
|
|
2634
|
-
const [interval, setInterval] =
|
|
2917
|
+
const [interval, setInterval] = useState17(null);
|
|
2635
2918
|
const activeInterval = interval ?? subscription?.plan_price.interval ?? intervals[0] ?? null;
|
|
2636
2919
|
const handleSwitch = async (planPriceId) => {
|
|
2637
2920
|
setError(null);
|
|
@@ -2706,6 +2989,7 @@ function BillingSection({
|
|
|
2706
2989
|
variant: "outline",
|
|
2707
2990
|
type: "button",
|
|
2708
2991
|
onClick: () => handleSwitch(price.id),
|
|
2992
|
+
loading: busy === price.id,
|
|
2709
2993
|
disabled: busy !== null,
|
|
2710
2994
|
children: busy === price.id ? l10n.billing.opening : l10n.billing.switchTo
|
|
2711
2995
|
}
|
|
@@ -2770,7 +3054,7 @@ function UserProfile({
|
|
|
2770
3054
|
description: t.description
|
|
2771
3055
|
}))
|
|
2772
3056
|
];
|
|
2773
|
-
const [internalTab, setInternalTab] =
|
|
3057
|
+
const [internalTab, setInternalTab] = useState18(defaultTab ?? allTabs[0]?.id ?? "profile");
|
|
2774
3058
|
const current = controlledTab ?? internalTab;
|
|
2775
3059
|
const select = (id) => {
|
|
2776
3060
|
if (controlledTab === void 0) setInternalTab(id);
|
|
@@ -2825,7 +3109,7 @@ function UserProfile({
|
|
|
2825
3109
|
}
|
|
2826
3110
|
|
|
2827
3111
|
// src/ui/CheckoutResult.tsx
|
|
2828
|
-
import { useEffect as useEffect7, useRef as useRef4, useState as
|
|
3112
|
+
import { useEffect as useEffect7, useRef as useRef4, useState as useState19 } from "react";
|
|
2829
3113
|
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2830
3114
|
var POLL_INTERVAL_MS = 1500;
|
|
2831
3115
|
function CheckoutResult({
|
|
@@ -2844,7 +3128,7 @@ function CheckoutResult({
|
|
|
2844
3128
|
const client = useClient();
|
|
2845
3129
|
const { config } = useAuthConfig();
|
|
2846
3130
|
const l10n = useLocalization();
|
|
2847
|
-
const [state, setState] =
|
|
3131
|
+
const [state, setState] = useState19("checking");
|
|
2848
3132
|
const confirmed = useRef4(false);
|
|
2849
3133
|
useEffect7(() => {
|
|
2850
3134
|
if (outcome !== "success") return;
|