@protobase/ui 0.46.8 → 0.46.10

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.
Files changed (37) hide show
  1. package/dist/account/change-email-dialog.js +1 -1
  2. package/dist/account/two-factor-setup.js +1 -1
  3. package/dist/app/confirm-sign-in-messages.js +22 -0
  4. package/dist/app/confirm-sign-in.js +88 -0
  5. package/dist/app/create/create-page.js +2 -2
  6. package/dist/app/create/create-values.js +17 -2
  7. package/dist/app/nav-from-meta.js +7 -3
  8. package/dist/app/sensitive-field.js +10 -2
  9. package/dist/app-shell/sidebar.js +1 -1
  10. package/dist/auth/auth-provider.js +3 -1
  11. package/dist/auth/code-form.js +1 -1
  12. package/dist/auth/two-factor-form.js +1 -1
  13. package/dist/auth/user-to-shell.js +4 -1
  14. package/dist/format/messages.js +2 -2
  15. package/dist/format/number.js +1 -1
  16. package/dist/forms/public-form-page.js +2 -2
  17. package/dist/index.js +1 -1
  18. package/dist/package.js +1 -1
  19. package/dist/shell/assets/app-D_HKIf03.js +9 -0
  20. package/dist/shell/assets/export-worker-IJWS5-Wc.js +3 -0
  21. package/dist/shell/assets/{index-oppnv6lh.js → index-C6JUrzRh.js} +2 -2
  22. package/dist/shell/assets/{records-map-Ck8_EDiN.js → records-map-kVCCf9yO.js} +2 -2
  23. package/dist/shell/assets/shared-protobase-ui-t4TJnncm.js +2 -0
  24. package/dist/shell/index.html +3 -3
  25. package/dist/shell/protobase.shell.json +1 -1
  26. package/dist/shell/protobase.sw.js +2 -2
  27. package/dist/shell-source-maps/assets/app-D_HKIf03.js.map +1 -0
  28. package/dist/shell-source-maps/assets/export-worker-IJWS5-Wc.js.map +1 -0
  29. package/dist/shell-source-maps/assets/{index-oppnv6lh.js.map → index-C6JUrzRh.js.map} +1 -1
  30. package/dist/shell-source-maps/assets/{records-map-Ck8_EDiN.js.map → records-map-kVCCf9yO.js.map} +1 -1
  31. package/dist/shell-source-maps/assets/{shared-protobase-ui-DWI6ok-_.js.map → shared-protobase-ui-t4TJnncm.js.map} +1 -1
  32. package/package.json +4 -4
  33. package/dist/shell/assets/app-BxGBuUKh.js +0 -9
  34. package/dist/shell/assets/export-worker-ClACk00b.js +0 -3
  35. package/dist/shell/assets/shared-protobase-ui-DWI6ok-_.js +0 -2
  36. package/dist/shell-source-maps/assets/app-BxGBuUKh.js.map +0 -1
  37. package/dist/shell-source-maps/assets/export-worker-ClACk00b.js.map +0 -1
@@ -2,9 +2,9 @@ import { text } from "./messages.js";
2
2
  import { Button } from "../primitives/button.js";
3
3
  import { Dialog } from "../primitives/dialog.js";
4
4
  import { Input } from "../primitives/input.js";
5
+ import { OtpInput } from "../primitives/otp-input.js";
5
6
  import { SectionError } from "./account-section.js";
6
7
  import { authMessage } from "../auth/auth-messages.js";
7
- import { OtpInput } from "../primitives/otp-input.js";
8
8
  import { useState } from "react";
9
9
  import { AuthError } from "@protobase/client";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -2,8 +2,8 @@ import { text } from "./messages.js";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "../primitives/button.js";
4
4
  import { Input } from "../primitives/input.js";
5
- import { authMessage } from "../auth/auth-messages.js";
6
5
  import { OtpInput } from "../primitives/otp-input.js";
6
+ import { authMessage } from "../auth/auth-messages.js";
7
7
  import { BackupCodes } from "./backup-codes.js";
8
8
  import { TotpQr, totpKey } from "./totp-qr.js";
9
9
  import { useState } from "react";
@@ -0,0 +1,22 @@
1
+ import { defineMessages } from "../locale/messages.js";
2
+ //#region src/app/confirm-sign-in-messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ confirmTitle: "Confirm it's you",
6
+ confirmWhy: (minutes) => `Showing this needs a passkey or your authenticator app in the last ${minutes} minutes.`,
7
+ usePasskey: "Use a passkey",
8
+ authenticatorCode: "Code from your authenticator app",
9
+ confirm: "Confirm",
10
+ nothingToConfirmWith: "Add a passkey or an authenticator app on your account page first."
11
+ },
12
+ nl: {
13
+ confirmTitle: "Bevestig dat jij het bent",
14
+ confirmWhy: (minutes) => `Dit tonen vraagt in de laatste ${minutes} minuten een passkey of je authenticator-app.`,
15
+ usePasskey: "Passkey gebruiken",
16
+ authenticatorCode: "Code uit je authenticator-app",
17
+ confirm: "Bevestigen",
18
+ nothingToConfirmWith: "Voeg eerst een passkey of een authenticator-app toe op je accountpagina."
19
+ }
20
+ });
21
+ //#endregion
22
+ export { text };
@@ -0,0 +1,88 @@
1
+ import { useOptionalAuth } from "../auth/auth-provider.js";
2
+ import { Button } from "../primitives/button.js";
3
+ import { OtpInput } from "../primitives/otp-input.js";
4
+ import { text } from "./confirm-sign-in-messages.js";
5
+ import { useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { useMutation, useQuery } from "@tanstack/react-query";
8
+ //#region src/app/confirm-sign-in.tsx
9
+ /**
10
+ * Asks the person to confirm it's them, with a passkey or their authenticator app, after the server refused something
11
+ * that needs a recent strong sign-in (`strong-sign-in-required`). It offers only what the account has; a passkey signs
12
+ * the browser in anew, a code confirms the session it has.
13
+ */
14
+ const ConfirmSignIn = ({ minutes, onConfirmed }) => {
15
+ const auth = useOptionalAuth();
16
+ const [code, setCode] = useState("");
17
+ const methods = useQuery({
18
+ queryKey: ["account-sign-in-methods"],
19
+ queryFn: () => auth.session.account.signInMethods(),
20
+ enabled: Boolean(auth)
21
+ });
22
+ const passkey = useMutation({
23
+ mutationFn: () => auth.signInWithPasskey(),
24
+ onSuccess: onConfirmed
25
+ });
26
+ const authenticator = useMutation({
27
+ mutationFn: (entered) => auth.verifyTwoFactor({
28
+ method: "totp",
29
+ code: entered
30
+ }),
31
+ onSuccess: onConfirmed
32
+ });
33
+ const account = methods.data?.account;
34
+ const error = passkey.error ?? authenticator.error ?? methods.error;
35
+ return /* @__PURE__ */ jsxs("section", {
36
+ "aria-label": text("confirmTitle"),
37
+ className: "mt-2 flex flex-col gap-2 rounded-md border border-border bg-surface p-3",
38
+ children: [
39
+ /* @__PURE__ */ jsx("p", {
40
+ className: "font-medium",
41
+ children: text("confirmTitle")
42
+ }),
43
+ /* @__PURE__ */ jsx("p", {
44
+ className: "text-muted-foreground",
45
+ children: text("confirmWhy", minutes)
46
+ }),
47
+ account && account.passkeys > 0 && /* @__PURE__ */ jsx(Button, {
48
+ variant: "primary",
49
+ size: "sm",
50
+ className: "self-start",
51
+ loading: passkey.isPending,
52
+ onClick: () => passkey.mutate(),
53
+ children: text("usePasskey")
54
+ }),
55
+ account?.authenticatorApp && /* @__PURE__ */ jsxs("form", {
56
+ className: "flex flex-wrap items-end gap-2",
57
+ onSubmit: (event) => {
58
+ event.preventDefault();
59
+ authenticator.mutate(code.trim());
60
+ },
61
+ children: [/* @__PURE__ */ jsxs("label", {
62
+ className: "flex flex-col gap-1.5",
63
+ children: [text("authenticatorCode"), /* @__PURE__ */ jsx(OtpInput, {
64
+ name: "code",
65
+ required: true,
66
+ value: code,
67
+ invalid: authenticator.isError,
68
+ onChange: setCode,
69
+ onComplete: (entered) => authenticator.mutate(entered)
70
+ })]
71
+ }), /* @__PURE__ */ jsx(Button, {
72
+ type: "submit",
73
+ size: "sm",
74
+ loading: authenticator.isPending,
75
+ children: text("confirm")
76
+ })]
77
+ }),
78
+ account && account.passkeys === 0 && !account.authenticatorApp && /* @__PURE__ */ jsx("p", { children: text("nothingToConfirmWith") }),
79
+ error && /* @__PURE__ */ jsx("p", {
80
+ role: "alert",
81
+ className: "font-medium text-danger-text",
82
+ children: error.message
83
+ })
84
+ ]
85
+ });
86
+ };
87
+ //#endregion
88
+ export { ConfirmSignIn };
@@ -6,7 +6,7 @@ import { text } from "../messages.js";
6
6
  import { useNavigationGuard } from "../router.js";
7
7
  import { useAppAddress } from "../use-app-address.js";
8
8
  import { text as text$1 } from "./messages.js";
9
- import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired } from "./create-values.js";
9
+ import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired, numberDefault } from "./create-values.js";
10
10
  import { FieldEditor } from "../field-editor.js";
11
11
  import { can } from "../permissions.js";
12
12
  import { fetchLabels } from "../../live/relation-labels.js";
@@ -133,7 +133,7 @@ const LoadedCreate = ({ resource, values }) => {
133
133
  value: isChosen(value) ? value.key : value ?? (field.type === "boolean" ? false : ""),
134
134
  stored: void 0,
135
135
  relationLabel: isChosen(value) ? value.label : void 0,
136
- placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
136
+ placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : numberDefault(field),
137
137
  relationPicker: target ? {
138
138
  target,
139
139
  view: meta.views[target.name]
@@ -59,17 +59,32 @@ const givenValue = (field, value) => {
59
59
  if (field.type === "boolean") return value === "true";
60
60
  return value;
61
61
  };
62
+ const numeric = /* @__PURE__ */ new Set([
63
+ "integer",
64
+ "bigint",
65
+ "decimal"
66
+ ]);
67
+ /**
68
+ * A number field's literal default, which its empty input shows as a placeholder rather than as its value: typing into a
69
+ * prefilled `0` would add to it, and the create applies the default to an empty field.
70
+ */
71
+ const numberDefault = (field) => {
72
+ if (!numeric.has(field.type) || !field.default || !("value" in field.default)) return void 0;
73
+ const value = field.default.value;
74
+ return value === null || value === void 0 ? void 0 : String(value);
75
+ };
62
76
  /**
63
77
  * The form's starting values: the values the address gives (`/notes/new?listing_id=42`), for the fields the form has,
64
- * else literal defaults from `/meta`. Database defaults stay empty so the database fills them.
78
+ * else literal defaults from `/meta`, but for numbers. Database defaults stay empty so the database fills them.
65
79
  */
66
80
  const initialDraft = (model, sections, given = {}) => Object.fromEntries(sections.flatMap((section) => section.fields.flatMap((name) => {
67
81
  const field = model.fields[name];
68
82
  if (Object.hasOwn(given, name)) return [[name, givenValue(field, given[name])]];
69
83
  const fallback = field.default;
84
+ if (numberDefault(field) !== void 0) return [];
70
85
  if (!fallback || !("value" in fallback) || fallback.value === null || fallback.value === void 0) return [];
71
86
  return [[name, typeof fallback.value === "boolean" ? fallback.value : String(fallback.value)]];
72
87
  })));
73
88
  const hasDatabaseDefault = (field) => Boolean(field.default && "db" in field.default);
74
89
  //#endregion
75
- export { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isCreatable, isRequired, keyValue };
90
+ export { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isCreatable, isRequired, keyValue, numberDefault };
@@ -16,8 +16,13 @@ const groupLabels = {
16
16
  core: "admin",
17
17
  hr: "hr"
18
18
  };
19
+ /** The group of a schema's resources: none for the default `public` schema, whose name would read as "visible to anyone". */
20
+ const schemaGroup = (schema = "public") => {
21
+ if (schema === "public") return "";
22
+ return groupLabels[schema] ? text(groupLabels[schema]) : humanize(schema);
23
+ };
19
24
  /**
20
- * One sidebar group per `nav.group` (default: the database schema, and "Pages" for pages); `nav.hidden` and a place in
25
+ * One sidebar group per `nav.group` (default: the database schema, none for `public`, and "Pages" for pages); `nav.hidden` and a place in
21
26
  * the user menu leave an entry out, `nav.order` sorts within a group, and `recent` (from `useNavRecent`) makes an entry
22
27
  * collapsible. Groups with pages come first, so a dashboard leads the sidebar. The resources of a source that cannot be
23
28
  * read are listed under its label, each with the problem. A visitor also gets the resources whose views ask them to sign
@@ -50,8 +55,7 @@ const navFromMeta = (meta, basePath = "", recent = {}) => {
50
55
  for (const model of Object.values(meta.resources)) {
51
56
  const view = meta.views[model.name];
52
57
  if (!view || inMenu.has(model.name)) continue;
53
- const schema = model.table.schema ?? "public";
54
- const defaultGroup = view.nav?.group ?? (groupLabels[schema] ? text(groupLabels[schema]) : humanize(schema));
58
+ const defaultGroup = view.nav?.group ?? schemaGroup(model.table.schema);
55
59
  const add = (groupName, item, order) => {
56
60
  const group = groups.get(groupName) ?? {
57
61
  label: groupName,
@@ -6,15 +6,19 @@ import { displayValue } from "../live/display-value.js";
6
6
  import { text } from "./messages.js";
7
7
  import { toDraft } from "../live/field-values.js";
8
8
  import { ErrorBanner } from "./error-banner.js";
9
+ import { ConfirmSignIn } from "./confirm-sign-in.js";
9
10
  import { useState } from "react";
10
11
  import { Eye, EyeOff } from "lucide-react";
12
+ import { isApiError } from "@protobase/client";
11
13
  import { jsx, jsxs } from "react/jsx-runtime";
12
14
  import { useMutation } from "@tanstack/react-query";
13
15
  //#region src/app/sensitive-field.tsx
14
16
  const mask = "••••••••••";
17
+ const strongSignInMinutes = (error) => isApiError(error) && error.slug === "strong-sign-in-required" ? error.problem.minutes ?? 15 : void 0;
15
18
  /**
16
19
  * A value hidden like a password. Showing a stored one asks the server for it, once per page visit; a write-only one has
17
- * none to show, so its eye only unmasks what was typed.
20
+ * none to show, so its eye only unmasks what was typed. When the server wants a recent strong sign-in first, the field
21
+ * asks the person to confirm it's them, then shows the value.
18
22
  */
19
23
  const SensitiveField = ({ field, hints, value, reveal, locked, invalid, placeholder, onChange }) => {
20
24
  const [shown, setShown] = useState(false);
@@ -38,7 +42,11 @@ const SensitiveField = ({ field, hints, value, reveal, locked, invalid, placehol
38
42
  onClick: toggle,
39
43
  children: !stored.isPending && (shown ? /* @__PURE__ */ jsx(EyeOff, { className: "size-3.5" }) : /* @__PURE__ */ jsx(Eye, { className: "size-3.5" }))
40
44
  });
41
- const error = stored.error && /* @__PURE__ */ jsx("div", {
45
+ const minutes = strongSignInMinutes(stored.error);
46
+ const error = minutes !== void 0 ? /* @__PURE__ */ jsx(ConfirmSignIn, {
47
+ minutes,
48
+ onConfirmed: () => stored.mutate()
49
+ }) : stored.error && /* @__PURE__ */ jsx("div", {
42
50
  className: "mt-2",
43
51
  children: /* @__PURE__ */ jsx(ErrorBanner, {
44
52
  title: text("couldNotShow", label),
@@ -34,7 +34,7 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExp
34
34
  className: cn("flex-1 overflow-y-auto overflow-x-hidden pb-2", mode === "icon-label" ? "px-2" : "px-2"),
35
35
  children: groups.map((group, index) => /* @__PURE__ */ jsxs("div", {
36
36
  className: cn("flex flex-col gap-0.5", index > 0 && "mt-3"),
37
- children: [inline ? /* @__PURE__ */ jsx("div", {
37
+ children: [inline ? group.label && /* @__PURE__ */ jsx("div", {
38
38
  className: cn("px-3 pb-1 pt-1 text-xs font-medium uppercase tracking-wide text-faint-foreground", expanding && "h-4 whitespace-nowrap py-0 opacity-0 transition-opacity group-hover/side:opacity-100 group-focus-within/side:opacity-100", expanded && "opacity-100"),
39
39
  children: group.label
40
40
  }) : index > 0 && /* @__PURE__ */ jsx("div", { className: "mx-auto mb-1 h-px w-6 bg-border" }), group.items.map((item) => item.recent && isTextMode(mode) ? /* @__PURE__ */ jsx(SidebarGroup, {
@@ -12,6 +12,8 @@ const useAuth = () => {
12
12
  if (!api) throw new Error("useAuth must be used inside AuthProvider");
13
13
  return api;
14
14
  };
15
+ /** The auth state and steps inside `AuthProvider`, `null` outside it, for a part that also works without one. */
16
+ const useOptionalAuth = () => useContext(AuthContext);
15
17
  /**
16
18
  * Finds out whether the server has a login and who is signed in, and keeps that state for the app. Without a `session`,
17
19
  * it makes one on `origin`, the page's own by default; in the iOS or Android app, with its `nativeBridge`, the session
@@ -216,4 +218,4 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
216
218
  });
217
219
  };
218
220
  //#endregion
219
- export { AuthProvider, useAuth };
221
+ export { AuthProvider, useAuth, useOptionalAuth };
@@ -1,7 +1,7 @@
1
1
  import { text } from "./messages.js";
2
2
  import { Button } from "../primitives/button.js";
3
- import { AuthLayout } from "./auth-layout.js";
4
3
  import { OtpInput } from "../primitives/otp-input.js";
4
+ import { AuthLayout } from "./auth-layout.js";
5
5
  import { ResendCodeButton } from "./resend-code-button.js";
6
6
  import { useEffect, useRef, useState } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,9 +1,9 @@
1
1
  import { text } from "./messages.js";
2
2
  import { Button } from "../primitives/button.js";
3
3
  import { Input } from "../primitives/input.js";
4
+ import { OtpInput } from "../primitives/otp-input.js";
4
5
  import { Checkbox } from "../primitives/checkbox.js";
5
6
  import { AuthLayout } from "./auth-layout.js";
6
- import { OtpInput } from "../primitives/otp-input.js";
7
7
  import { useState } from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/auth/two-factor-form.tsx
@@ -1,4 +1,5 @@
1
1
  import { text } from "./messages.js";
2
+ import { capitalise } from "../live/naming.js";
2
3
  //#region src/auth/user-to-shell.ts
3
4
  /** The hue of someone's avatar, the same for the same id. */
4
5
  const hueOf = (text) => [...text].reduce((sum, char) => (sum * 31 + char.charCodeAt(0)) % 360, 7);
@@ -7,6 +8,8 @@ const initialsOf = (name) => name.split(/[\s@.]+/).filter(Boolean).slice(0, 2).m
7
8
  const rolesOf = (user) => (user.role ?? "user").split(",").map((role) => role.trim());
8
9
  /** The user holds the `admin` role, among others perhaps. */
9
10
  const isAdmin = (user) => rolesOf(user).includes("admin");
11
+ /** A role's name as words: `full_access` becomes "Full access". */
12
+ const roleLabel = (role) => capitalise(role.replace(/[_-]+/g, " ").trim());
10
13
  /** What the sidebar's profile menu shows for a signed-in user. */
11
14
  const userToShell = (user) => {
12
15
  const roles = rolesOf(user);
@@ -16,7 +19,7 @@ const userToShell = (user) => {
16
19
  email: user.email,
17
20
  initials: initialsOf(user.name || user.email) || "?",
18
21
  hue: hueOf(user.id || user.email),
19
- role: admin ? text("admin") : (roles[0] ?? "user").replace(/^./, (char) => char.toUpperCase()),
22
+ role: admin ? text("admin") : roleLabel(roles[0] ?? "user"),
20
23
  roleTone: admin ? "violet" : "blue"
21
24
  };
22
25
  };
@@ -2,7 +2,7 @@ import { defineMessages } from "../locale/messages.js";
2
2
  //#region src/format/messages.ts
3
3
  const text = defineMessages({
4
4
  en: {
5
- rows: (count) => `${count} rows`,
5
+ rows: (count, one) => `${count} ${one ? "row" : "rows"}`,
6
6
  aboutRows: (count) => `about ${count} rows`,
7
7
  secondsAgo: (count) => `${count} s ago`,
8
8
  minutesAgo: (count) => `${count} min ago`,
@@ -11,7 +11,7 @@ const text = defineMessages({
11
11
  justNow: "just now"
12
12
  },
13
13
  nl: {
14
- rows: (count) => `${count} rijen`,
14
+ rows: (count, one) => `${count} ${one ? "rij" : "rijen"}`,
15
15
  aboutRows: (count) => `ongeveer ${count} rijen`,
16
16
  secondsAgo: (count) => `${count} s geleden`,
17
17
  minutesAgo: (count) => `${count} min geleden`,
@@ -6,7 +6,7 @@ const formatInt = (value) => grouped().format(value);
6
6
  const formatSigned = (value) => value > 0 ? `+${grouped().format(value)}` : grouped().format(value).replace("-", "−");
7
7
  const formatPercent = (value) => `${value}%`;
8
8
  const formatApproxRows = (count) => {
9
- if (count < 1e4) return text("rows", formatInt(count));
9
+ if (count < 1e4) return text("rows", formatInt(count), count === 1);
10
10
  const magnitude = 10 ** (Math.floor(Math.log10(count)) - 3);
11
11
  return text("aboutRows", formatInt(Math.round(count / magnitude) * magnitude));
12
12
  };
@@ -6,7 +6,7 @@ import { Notice } from "../app/meta-gate.js";
6
6
  import { Button } from "../primitives/button.js";
7
7
  import { Field } from "../record-view/field.js";
8
8
  import { humanize } from "../live/naming.js";
9
- import { createBody, hasDatabaseDefault, initialDraft, isRequired } from "../app/create/create-values.js";
9
+ import { createBody, hasDatabaseDefault, initialDraft, isRequired, numberDefault } from "../app/create/create-values.js";
10
10
  import { ErrorBanner } from "../app/error-banner.js";
11
11
  import { FieldEditor } from "../app/field-editor.js";
12
12
  import { fieldErrors, fieldMessage } from "../app/field-errors.js";
@@ -123,7 +123,7 @@ const LoadedForm = ({ form }) => {
123
123
  hints,
124
124
  value: draft[name] ?? (field.type === "boolean" ? false : ""),
125
125
  stored: void 0,
126
- placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
126
+ placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : numberDefault(field),
127
127
  invalid: Boolean(problem),
128
128
  onChange: (next) => change(name, next)
129
129
  })
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import { AppLink, useAppAddress } from "./app/use-app-address.js";
12
12
  import { Segmented } from "./primitives/segmented.js";
13
13
  import { Textarea } from "./primitives/textarea.js";
14
14
  import { useList } from "./data/use-list.js";
15
+ import { OtpInput } from "./primitives/otp-input.js";
15
16
  import { definePlugin, defineUi } from "./app/pages/project-ui.js";
16
17
  import { Markdown } from "./markdown/markdown.js";
17
18
  import { ActionInputError } from "./app/pages/action-inputs/input-errors.js";
@@ -22,7 +23,6 @@ import { SignInPrompt } from "./auth/sign-in-prompt.js";
22
23
  import { useListDisplay } from "./app/displays/display-context.js";
23
24
  import { MenuSelect } from "./primitives/menu-select.js";
24
25
  import { AuthLayout } from "./auth/auth-layout.js";
25
- import { OtpInput } from "./primitives/otp-input.js";
26
26
  import { allowDelayMs, isFramed, useArmedAfterFocus, useGuardedPress } from "./connect/interaction-guard.js";
27
27
  import { ResendCodeButton } from "./auth/resend-code-button.js";
28
28
  import { CodeForm } from "./auth/code-form.js";
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.46.8";
2
+ var version = "0.46.10";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };