@medialane/ui 0.174.0 → 0.176.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.
Files changed (93) hide show
  1. package/dist/components/account/account-section.cjs +50 -0
  2. package/dist/components/account/account-section.cjs.map +1 -0
  3. package/dist/components/account/account-section.d.cts +14 -0
  4. package/dist/components/account/account-section.d.ts +14 -0
  5. package/dist/components/account/account-section.js +26 -0
  6. package/dist/components/account/account-section.js.map +1 -0
  7. package/dist/components/account/account.test.cjs +86 -0
  8. package/dist/components/account/account.test.cjs.map +1 -0
  9. package/dist/components/account/account.test.d.cts +2 -0
  10. package/dist/components/account/account.test.d.ts +2 -0
  11. package/dist/components/account/account.test.js +85 -0
  12. package/dist/components/account/account.test.js.map +1 -0
  13. package/dist/components/account/add-guardian-dialog.cjs +90 -0
  14. package/dist/components/account/add-guardian-dialog.cjs.map +1 -0
  15. package/dist/components/account/add-guardian-dialog.d.cts +14 -0
  16. package/dist/components/account/add-guardian-dialog.d.ts +14 -0
  17. package/dist/components/account/add-guardian-dialog.js +66 -0
  18. package/dist/components/account/add-guardian-dialog.js.map +1 -0
  19. package/dist/components/account/device-approval-dialog.cjs +120 -0
  20. package/dist/components/account/device-approval-dialog.cjs.map +1 -0
  21. package/dist/components/account/device-approval-dialog.d.cts +14 -0
  22. package/dist/components/account/device-approval-dialog.d.ts +14 -0
  23. package/dist/components/account/device-approval-dialog.js +96 -0
  24. package/dist/components/account/device-approval-dialog.js.map +1 -0
  25. package/dist/components/account/devices-section.cjs +117 -0
  26. package/dist/components/account/devices-section.cjs.map +1 -0
  27. package/dist/components/account/devices-section.d.cts +12 -0
  28. package/dist/components/account/devices-section.d.ts +12 -0
  29. package/dist/components/account/devices-section.js +93 -0
  30. package/dist/components/account/devices-section.js.map +1 -0
  31. package/dist/components/account/email-code-entry.cjs +85 -0
  32. package/dist/components/account/email-code-entry.cjs.map +1 -0
  33. package/dist/components/account/email-code-entry.d.cts +11 -0
  34. package/dist/components/account/email-code-entry.d.ts +11 -0
  35. package/dist/components/account/email-code-entry.js +61 -0
  36. package/dist/components/account/email-code-entry.js.map +1 -0
  37. package/dist/components/account/guardian-recovery-section.cjs +161 -0
  38. package/dist/components/account/guardian-recovery-section.cjs.map +1 -0
  39. package/dist/components/account/guardian-recovery-section.d.cts +14 -0
  40. package/dist/components/account/guardian-recovery-section.d.ts +14 -0
  41. package/dist/components/account/guardian-recovery-section.js +130 -0
  42. package/dist/components/account/guardian-recovery-section.js.map +1 -0
  43. package/dist/components/account/input-otp.cjs +89 -0
  44. package/dist/components/account/input-otp.cjs.map +1 -0
  45. package/dist/components/account/input-otp.d.cts +37 -0
  46. package/dist/components/account/input-otp.d.ts +37 -0
  47. package/dist/components/account/input-otp.js +52 -0
  48. package/dist/components/account/input-otp.js.map +1 -0
  49. package/dist/components/account/wallet-types.cjs +17 -0
  50. package/dist/components/account/wallet-types.cjs.map +1 -0
  51. package/dist/components/account/wallet-types.d.cts +7 -0
  52. package/dist/components/account/wallet-types.d.ts +7 -0
  53. package/dist/components/account/wallet-types.js +1 -0
  54. package/dist/components/account/wallet-types.js.map +1 -0
  55. package/dist/components/button.d.cts +1 -1
  56. package/dist/components/button.d.ts +1 -1
  57. package/dist/index.cjs +56 -0
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.d.cts +15 -1
  60. package/dist/index.d.ts +15 -1
  61. package/dist/index.js +40 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/utils/app-wallet.cjs +84 -0
  64. package/dist/utils/app-wallet.cjs.map +1 -0
  65. package/dist/utils/app-wallet.d.cts +26 -0
  66. package/dist/utils/app-wallet.d.ts +26 -0
  67. package/dist/utils/app-wallet.js +65 -0
  68. package/dist/utils/app-wallet.js.map +1 -0
  69. package/dist/utils/email-code.cjs +58 -0
  70. package/dist/utils/email-code.cjs.map +1 -0
  71. package/dist/utils/email-code.d.cts +32 -0
  72. package/dist/utils/email-code.d.ts +32 -0
  73. package/dist/utils/email-code.js +30 -0
  74. package/dist/utils/email-code.js.map +1 -0
  75. package/dist/utils/passkey-support.cjs +37 -0
  76. package/dist/utils/passkey-support.cjs.map +1 -0
  77. package/dist/utils/passkey-support.d.cts +7 -0
  78. package/dist/utils/passkey-support.d.ts +7 -0
  79. package/dist/utils/passkey-support.js +13 -0
  80. package/dist/utils/passkey-support.js.map +1 -0
  81. package/dist/utils/use-email-code.cjs +75 -0
  82. package/dist/utils/use-email-code.cjs.map +1 -0
  83. package/dist/utils/use-email-code.d.cts +20 -0
  84. package/dist/utils/use-email-code.d.ts +20 -0
  85. package/dist/utils/use-email-code.js +51 -0
  86. package/dist/utils/use-email-code.js.map +1 -0
  87. package/dist/utils/wallet-failure.cjs +63 -0
  88. package/dist/utils/wallet-failure.cjs.map +1 -0
  89. package/dist/utils/wallet-failure.d.cts +12 -0
  90. package/dist/utils/wallet-failure.d.ts +12 -0
  91. package/dist/utils/wallet-failure.js +38 -0
  92. package/dist/utils/wallet-failure.js.map +1 -0
  93. package/package.json +3 -1
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var device_approval_dialog_exports = {};
21
+ __export(device_approval_dialog_exports, {
22
+ DeviceApprovalDialog: () => DeviceApprovalDialog
23
+ });
24
+ module.exports = __toCommonJS(device_approval_dialog_exports);
25
+ var import_jsx_runtime = require("react/jsx-runtime");
26
+ var import_react = require("react");
27
+ var import_lucide_react = require("lucide-react");
28
+ var import_starknet = require("@medialane/sdk/starknet");
29
+ var import_button = require("../button.js");
30
+ var import_textarea = require("../textarea.js");
31
+ var import_alert = require("../alert.js");
32
+ var import_dialog = require("../dialog.js");
33
+ var import_describe_error = require("../../utils/describe-error.js");
34
+ function DeviceApprovalDialog({ open, onOpenChange, onApproved, wallet, loadSealed }) {
35
+ const [raw, setRaw] = (0, import_react.useState)("");
36
+ const [pending, setPending] = (0, import_react.useState)(null);
37
+ const [busy, setBusy] = (0, import_react.useState)(false);
38
+ const [error, setError] = (0, import_react.useState)(null);
39
+ const reset = () => {
40
+ setRaw("");
41
+ setPending(null);
42
+ setError(null);
43
+ };
44
+ const review = () => {
45
+ setError(null);
46
+ try {
47
+ setPending((0, import_starknet.parsePairingPayload)(raw.trim()));
48
+ } catch (e) {
49
+ setError((0, import_describe_error.describeError)(e, "That device code isn't valid. Check it and try again.").message);
50
+ }
51
+ };
52
+ const approve = async () => {
53
+ const sealed = loadSealed();
54
+ if (!sealed || !pending) return;
55
+ setBusy(true);
56
+ setError(null);
57
+ try {
58
+ await wallet.addDevice(sealed, pending.publicKey);
59
+ reset();
60
+ onOpenChange(false);
61
+ onApproved();
62
+ } catch (e) {
63
+ setError((0, import_describe_error.describeError)(e, "We couldn't approve that device. Please try again.").message);
64
+ } finally {
65
+ setBusy(false);
66
+ }
67
+ };
68
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
69
+ import_dialog.Dialog,
70
+ {
71
+ open,
72
+ onOpenChange: (next) => {
73
+ if (!next) reset();
74
+ onOpenChange(next);
75
+ },
76
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dialog.DialogContent, { children: [
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dialog.DialogHeader, { children: [
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dialog.DialogTitle, { children: "Add a device" }),
79
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dialog.DialogDescription, { children: "On the new device, sign in and choose \u201CAdd this device\u201D. Paste the code it shows you here." })
80
+ ] }),
81
+ pending ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4", children: [
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_alert.Alert, { children: [
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.TriangleAlert, { className: "h-4 w-4" }),
84
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_alert.AlertDescription, { children: "This gives the device full control of your account and everything in it. Only continue if you are holding it yourself." })
85
+ ] }),
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-xl bg-muted/50 p-4 space-y-2", children: [
87
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Smartphone, { className: "h-4 w-4 text-muted-foreground" }),
89
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-medium", children: pending.label || "Unnamed device" })
90
+ ] }),
91
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "break-all font-mono text-xs text-muted-foreground", children: pending.publicKey })
92
+ ] }),
93
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-destructive", children: error }) : null,
94
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { variant: "outline", className: "flex-1", onClick: reset, disabled: busy, children: "Back" }),
96
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { className: "flex-1", onClick: approve, disabled: busy, children: busy ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }) : "Approve device" })
97
+ ] })
98
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4", children: [
99
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
100
+ import_textarea.Textarea,
101
+ {
102
+ value: raw,
103
+ onChange: (e) => setRaw(e.target.value),
104
+ placeholder: "Paste the code from your other device",
105
+ rows: 4,
106
+ className: "font-mono text-xs"
107
+ }
108
+ ),
109
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-destructive", children: error }) : null,
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { className: "w-full", onClick: review, disabled: !raw.trim(), children: "Review device" })
111
+ ] })
112
+ ] })
113
+ }
114
+ );
115
+ }
116
+ // Annotate the CommonJS export names for ESM import in node:
117
+ 0 && (module.exports = {
118
+ DeviceApprovalDialog
119
+ });
120
+ //# sourceMappingURL=device-approval-dialog.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/account/device-approval-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { Loader2, Smartphone, TriangleAlert } from \"lucide-react\";\nimport { parsePairingPayload, type PairingPayload } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Textarea } from \"../textarea.js\";\nimport { Alert, AlertDescription } from \"../alert.js\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"../dialog.js\";\nimport { describeError } from \"../../utils/describe-error.js\";\nimport type { DeviceWallet, LoadSealedOwner } from \"./wallet-types.js\";\n\nexport interface DeviceApprovalDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onApproved: () => void;\n wallet: Pick<DeviceWallet, \"addDevice\">;\n loadSealed: LoadSealedOwner;\n}\n\nexport function DeviceApprovalDialog({ open, onOpenChange, onApproved, wallet, loadSealed }: DeviceApprovalDialogProps) {\n const [raw, setRaw] = useState(\"\");\n const [pending, setPending] = useState<PairingPayload | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const reset = () => {\n setRaw(\"\");\n setPending(null);\n setError(null);\n };\n\n const review = () => {\n setError(null);\n try {\n setPending(parsePairingPayload(raw.trim()));\n } catch (e) {\n setError(describeError(e, \"That device code isn't valid. Check it and try again.\").message);\n }\n };\n\n const approve = async () => {\n const sealed = loadSealed();\n if (!sealed || !pending) return;\n setBusy(true);\n setError(null);\n try {\n await wallet.addDevice(sealed, pending.publicKey);\n reset();\n onOpenChange(false);\n onApproved();\n } catch (e) {\n setError(describeError(e, \"We couldn't approve that device. Please try again.\").message);\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) reset();\n onOpenChange(next);\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Add a device</DialogTitle>\n <DialogDescription>\n On the new device, sign in and choose &ldquo;Add this device&rdquo;. Paste the code it\n shows you here.\n </DialogDescription>\n </DialogHeader>\n\n {pending ? (\n <div className=\"space-y-4\">\n <Alert>\n <TriangleAlert className=\"h-4 w-4\" />\n <AlertDescription>\n This gives the device full control of your account and everything in it. Only\n continue if you are holding it yourself.\n </AlertDescription>\n </Alert>\n\n <div className=\"rounded-xl bg-muted/50 p-4 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Smartphone className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"font-medium\">{pending.label || \"Unnamed device\"}</span>\n </div>\n <p className=\"break-all font-mono text-xs text-muted-foreground\">\n {pending.publicKey}\n </p>\n </div>\n\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" className=\"flex-1\" onClick={reset} disabled={busy}>\n Back\n </Button>\n <Button className=\"flex-1\" onClick={approve} disabled={busy}>\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : \"Approve device\"}\n </Button>\n </div>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <Textarea\n value={raw}\n onChange={(e) => setRaw(e.target.value)}\n placeholder=\"Paste the code from your other device\"\n rows={4}\n className=\"font-mono text-xs\"\n />\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n <Button className=\"w-full\" onClick={review} disabled={!raw.trim()}>\n Review device\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DeviceWallet, LoadSealedOwner } from './wallet-types.cjs';
3
+ import '@medialane/sdk/starknet';
4
+
5
+ interface DeviceApprovalDialogProps {
6
+ open: boolean;
7
+ onOpenChange: (open: boolean) => void;
8
+ onApproved: () => void;
9
+ wallet: Pick<DeviceWallet, "addDevice">;
10
+ loadSealed: LoadSealedOwner;
11
+ }
12
+ declare function DeviceApprovalDialog({ open, onOpenChange, onApproved, wallet, loadSealed }: DeviceApprovalDialogProps): react_jsx_runtime.JSX.Element;
13
+
14
+ export { DeviceApprovalDialog, type DeviceApprovalDialogProps };
@@ -0,0 +1,14 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DeviceWallet, LoadSealedOwner } from './wallet-types.js';
3
+ import '@medialane/sdk/starknet';
4
+
5
+ interface DeviceApprovalDialogProps {
6
+ open: boolean;
7
+ onOpenChange: (open: boolean) => void;
8
+ onApproved: () => void;
9
+ wallet: Pick<DeviceWallet, "addDevice">;
10
+ loadSealed: LoadSealedOwner;
11
+ }
12
+ declare function DeviceApprovalDialog({ open, onOpenChange, onApproved, wallet, loadSealed }: DeviceApprovalDialogProps): react_jsx_runtime.JSX.Element;
13
+
14
+ export { DeviceApprovalDialog, type DeviceApprovalDialogProps };
@@ -0,0 +1,96 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Loader2, Smartphone, TriangleAlert } from "lucide-react";
5
+ import { parsePairingPayload } from "@medialane/sdk/starknet";
6
+ import { Button } from "../button.js";
7
+ import { Textarea } from "../textarea.js";
8
+ import { Alert, AlertDescription } from "../alert.js";
9
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../dialog.js";
10
+ import { describeError } from "../../utils/describe-error.js";
11
+ function DeviceApprovalDialog({ open, onOpenChange, onApproved, wallet, loadSealed }) {
12
+ const [raw, setRaw] = useState("");
13
+ const [pending, setPending] = useState(null);
14
+ const [busy, setBusy] = useState(false);
15
+ const [error, setError] = useState(null);
16
+ const reset = () => {
17
+ setRaw("");
18
+ setPending(null);
19
+ setError(null);
20
+ };
21
+ const review = () => {
22
+ setError(null);
23
+ try {
24
+ setPending(parsePairingPayload(raw.trim()));
25
+ } catch (e) {
26
+ setError(describeError(e, "That device code isn't valid. Check it and try again.").message);
27
+ }
28
+ };
29
+ const approve = async () => {
30
+ const sealed = loadSealed();
31
+ if (!sealed || !pending) return;
32
+ setBusy(true);
33
+ setError(null);
34
+ try {
35
+ await wallet.addDevice(sealed, pending.publicKey);
36
+ reset();
37
+ onOpenChange(false);
38
+ onApproved();
39
+ } catch (e) {
40
+ setError(describeError(e, "We couldn't approve that device. Please try again.").message);
41
+ } finally {
42
+ setBusy(false);
43
+ }
44
+ };
45
+ return /* @__PURE__ */ jsx(
46
+ Dialog,
47
+ {
48
+ open,
49
+ onOpenChange: (next) => {
50
+ if (!next) reset();
51
+ onOpenChange(next);
52
+ },
53
+ children: /* @__PURE__ */ jsxs(DialogContent, { children: [
54
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
55
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Add a device" }),
56
+ /* @__PURE__ */ jsx(DialogDescription, { children: "On the new device, sign in and choose \u201CAdd this device\u201D. Paste the code it shows you here." })
57
+ ] }),
58
+ pending ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
59
+ /* @__PURE__ */ jsxs(Alert, { children: [
60
+ /* @__PURE__ */ jsx(TriangleAlert, { className: "h-4 w-4" }),
61
+ /* @__PURE__ */ jsx(AlertDescription, { children: "This gives the device full control of your account and everything in it. Only continue if you are holding it yourself." })
62
+ ] }),
63
+ /* @__PURE__ */ jsxs("div", { className: "rounded-xl bg-muted/50 p-4 space-y-2", children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
65
+ /* @__PURE__ */ jsx(Smartphone, { className: "h-4 w-4 text-muted-foreground" }),
66
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: pending.label || "Unnamed device" })
67
+ ] }),
68
+ /* @__PURE__ */ jsx("p", { className: "break-all font-mono text-xs text-muted-foreground", children: pending.publicKey })
69
+ ] }),
70
+ error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }) : null,
71
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
72
+ /* @__PURE__ */ jsx(Button, { variant: "outline", className: "flex-1", onClick: reset, disabled: busy, children: "Back" }),
73
+ /* @__PURE__ */ jsx(Button, { className: "flex-1", onClick: approve, disabled: busy, children: busy ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : "Approve device" })
74
+ ] })
75
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
76
+ /* @__PURE__ */ jsx(
77
+ Textarea,
78
+ {
79
+ value: raw,
80
+ onChange: (e) => setRaw(e.target.value),
81
+ placeholder: "Paste the code from your other device",
82
+ rows: 4,
83
+ className: "font-mono text-xs"
84
+ }
85
+ ),
86
+ error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }) : null,
87
+ /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: review, disabled: !raw.trim(), children: "Review device" })
88
+ ] })
89
+ ] })
90
+ }
91
+ );
92
+ }
93
+ export {
94
+ DeviceApprovalDialog
95
+ };
96
+ //# sourceMappingURL=device-approval-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/account/device-approval-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { Loader2, Smartphone, TriangleAlert } from \"lucide-react\";\nimport { parsePairingPayload, type PairingPayload } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Textarea } from \"../textarea.js\";\nimport { Alert, AlertDescription } from \"../alert.js\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"../dialog.js\";\nimport { describeError } from \"../../utils/describe-error.js\";\nimport type { DeviceWallet, LoadSealedOwner } from \"./wallet-types.js\";\n\nexport interface DeviceApprovalDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onApproved: () => void;\n wallet: Pick<DeviceWallet, \"addDevice\">;\n loadSealed: LoadSealedOwner;\n}\n\nexport function DeviceApprovalDialog({ open, onOpenChange, onApproved, wallet, loadSealed }: DeviceApprovalDialogProps) {\n const [raw, setRaw] = useState(\"\");\n const [pending, setPending] = useState<PairingPayload | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const reset = () => {\n setRaw(\"\");\n setPending(null);\n setError(null);\n };\n\n const review = () => {\n setError(null);\n try {\n setPending(parsePairingPayload(raw.trim()));\n } catch (e) {\n setError(describeError(e, \"That device code isn't valid. Check it and try again.\").message);\n }\n };\n\n const approve = async () => {\n const sealed = loadSealed();\n if (!sealed || !pending) return;\n setBusy(true);\n setError(null);\n try {\n await wallet.addDevice(sealed, pending.publicKey);\n reset();\n onOpenChange(false);\n onApproved();\n } catch (e) {\n setError(describeError(e, \"We couldn't approve that device. Please try again.\").message);\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) reset();\n onOpenChange(next);\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Add a device</DialogTitle>\n <DialogDescription>\n On the new device, sign in and choose &ldquo;Add this device&rdquo;. Paste the code it\n shows you here.\n </DialogDescription>\n </DialogHeader>\n\n {pending ? (\n <div className=\"space-y-4\">\n <Alert>\n <TriangleAlert className=\"h-4 w-4\" />\n <AlertDescription>\n This gives the device full control of your account and everything in it. Only\n continue if you are holding it yourself.\n </AlertDescription>\n </Alert>\n\n <div className=\"rounded-xl bg-muted/50 p-4 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Smartphone className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"font-medium\">{pending.label || \"Unnamed device\"}</span>\n </div>\n <p className=\"break-all font-mono text-xs text-muted-foreground\">\n {pending.publicKey}\n </p>\n </div>\n\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" className=\"flex-1\" onClick={reset} disabled={busy}>\n Back\n </Button>\n <Button className=\"flex-1\" onClick={approve} disabled={busy}>\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : \"Approve device\"}\n </Button>\n </div>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <Textarea\n value={raw}\n onChange={(e) => setRaw(e.target.value)}\n placeholder=\"Paste the code from your other device\"\n rows={4}\n className=\"font-mono text-xs\"\n />\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n <Button className=\"w-full\" onClick={review} disabled={!raw.trim()}>\n Review device\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var devices_section_exports = {};
21
+ __export(devices_section_exports, {
22
+ DevicesSection: () => DevicesSection
23
+ });
24
+ module.exports = __toCommonJS(devices_section_exports);
25
+ var import_jsx_runtime = require("react/jsx-runtime");
26
+ var import_react = require("react");
27
+ var import_lucide_react = require("lucide-react");
28
+ var import_starknet = require("@medialane/sdk/starknet");
29
+ var import_button = require("../button.js");
30
+ var import_badge = require("../badge.js");
31
+ var import_describe_error = require("../../utils/describe-error.js");
32
+ var import_device_approval_dialog = require("./device-approval-dialog.js");
33
+ const short = (v) => `${v.slice(0, 10)}\u2026${v.slice(-6)}`;
34
+ function DevicesSection({ walletAddress, wallet, loadSealed }) {
35
+ const [devices, setDevices] = (0, import_react.useState)(null);
36
+ const [addOpen, setAddOpen] = (0, import_react.useState)(false);
37
+ const [busyGuid, setBusyGuid] = (0, import_react.useState)(null);
38
+ const [error, setError] = (0, import_react.useState)(null);
39
+ const refresh = (0, import_react.useCallback)(() => {
40
+ const sealed = loadSealed();
41
+ if (!sealed) return;
42
+ wallet.getOwners(walletAddress).then((owners) => setDevices((0, import_starknet.describeDevices)(owners, sealed.ownerPubKey))).catch(() => setDevices([]));
43
+ }, [walletAddress, wallet, loadSealed]);
44
+ (0, import_react.useEffect)(() => {
45
+ refresh();
46
+ }, [refresh]);
47
+ const handleRemove = async (device) => {
48
+ const sealed = loadSealed();
49
+ if (!sealed || !devices) return;
50
+ const confirmed = window.confirm(
51
+ "Remove this device? It will no longer be able to sign for your account. Adding it back needs a device you still have."
52
+ );
53
+ if (!confirmed) return;
54
+ setBusyGuid(device.guid);
55
+ setError(null);
56
+ try {
57
+ await wallet.removeDevice(sealed, device.guid);
58
+ refresh();
59
+ } catch (e) {
60
+ setError((0, import_describe_error.describeError)(e, "We couldn't remove that device. Please try again.").message);
61
+ } finally {
62
+ setBusyGuid(null);
63
+ }
64
+ };
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-2xl border border-border/60 bg-card p-5 sm:p-6 space-y-5", children: [
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
67
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
68
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-base font-bold text-foreground", children: "Your devices" }),
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground mt-0.5", children: "Each device signs for itself. Add one to use this account somewhere else." })
70
+ ] }),
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_button.Button, { size: "sm", variant: "outline", onClick: () => setAddOpen(true), children: [
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plus, { className: "h-4 w-4 mr-1" }),
73
+ "Add"
74
+ ] })
75
+ ] }),
76
+ devices === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) : devices.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground", children: "No devices found for this account." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "space-y-2", children: devices.map((device) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
77
+ "li",
78
+ {
79
+ className: "flex items-center justify-between gap-3 rounded-xl bg-muted/40 px-4 py-3",
80
+ children: [
81
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3 min-w-0", children: [
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Monitor, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono text-xs text-muted-foreground truncate", children: short(device.guid) }),
84
+ device.isThisDevice ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_badge.Badge, { variant: "secondary", children: "This device" }) : null
85
+ ] }),
86
+ device.isThisDevice ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
87
+ import_button.Button,
88
+ {
89
+ size: "sm",
90
+ variant: "ghost",
91
+ disabled: !(0, import_starknet.canRemoveDevice)(devices, device.guid) || busyGuid !== null,
92
+ onClick: () => handleRemove(device),
93
+ children: busyGuid === device.guid ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }) : "Remove"
94
+ }
95
+ )
96
+ ]
97
+ },
98
+ device.guid
99
+ )) }),
100
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-destructive", children: error }) : null,
101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
102
+ import_device_approval_dialog.DeviceApprovalDialog,
103
+ {
104
+ open: addOpen,
105
+ onOpenChange: setAddOpen,
106
+ onApproved: refresh,
107
+ wallet,
108
+ loadSealed
109
+ }
110
+ )
111
+ ] });
112
+ }
113
+ // Annotate the CommonJS export names for ESM import in node:
114
+ 0 && (module.exports = {
115
+ DevicesSection
116
+ });
117
+ //# sourceMappingURL=devices-section.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/account/devices-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { Loader2, Monitor, Plus } from \"lucide-react\";\nimport { canRemoveDevice, describeDevices, type DeviceEntry } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Badge } from \"../badge.js\";\nimport { describeError } from \"../../utils/describe-error.js\";\nimport { DeviceApprovalDialog } from \"./device-approval-dialog.js\";\nimport type { DeviceWallet, LoadSealedOwner } from \"./wallet-types.js\";\n\nconst short = (v: string) => `${v.slice(0, 10)}…${v.slice(-6)}`;\n\nexport interface DevicesSectionProps {\n walletAddress: string;\n wallet: DeviceWallet;\n loadSealed: LoadSealedOwner;\n}\n\nexport function DevicesSection({ walletAddress, wallet, loadSealed }: DevicesSectionProps) {\n const [devices, setDevices] = useState<DeviceEntry[] | null>(null);\n const [addOpen, setAddOpen] = useState(false);\n const [busyGuid, setBusyGuid] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const refresh = useCallback(() => {\n const sealed = loadSealed();\n if (!sealed) return;\n wallet\n .getOwners(walletAddress)\n .then((owners) => setDevices(describeDevices(owners, sealed.ownerPubKey)))\n .catch(() => setDevices([]));\n }, [walletAddress, wallet, loadSealed]);\n\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n const handleRemove = async (device: DeviceEntry) => {\n const sealed = loadSealed();\n if (!sealed || !devices) return;\n\n const confirmed = window.confirm(\n \"Remove this device? It will no longer be able to sign for your account. Adding it back needs a device you still have.\",\n );\n if (!confirmed) return;\n\n setBusyGuid(device.guid);\n setError(null);\n try {\n await wallet.removeDevice(sealed, device.guid);\n refresh();\n } catch (e) {\n setError(describeError(e, \"We couldn't remove that device. Please try again.\").message);\n } finally {\n setBusyGuid(null);\n }\n };\n\n return (\n <div className=\"rounded-2xl border border-border/60 bg-card p-5 sm:p-6 space-y-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h3 className=\"text-base font-bold text-foreground\">Your devices</h3>\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n Each device signs for itself. Add one to use this account somewhere else.\n </p>\n </div>\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAddOpen(true)}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add\n </Button>\n </div>\n\n {devices === null ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-muted-foreground\" />\n ) : devices.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">No devices found for this account.</p>\n ) : (\n <ul className=\"space-y-2\">\n {devices.map((device) => (\n <li\n key={device.guid}\n className=\"flex items-center justify-between gap-3 rounded-xl bg-muted/40 px-4 py-3\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Monitor className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"font-mono text-xs text-muted-foreground truncate\">\n {short(device.guid)}\n </span>\n {device.isThisDevice ? <Badge variant=\"secondary\">This device</Badge> : null}\n </div>\n {device.isThisDevice ? null : (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={!canRemoveDevice(devices, device.guid) || busyGuid !== null}\n onClick={() => handleRemove(device)}\n >\n {busyGuid === device.guid ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : \"Remove\"}\n </Button>\n )}\n </li>\n ))}\n </ul>\n )}\n\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n\n <DeviceApprovalDialog\n open={addOpen}\n onOpenChange={setAddOpen}\n onApproved={refresh}\n wallet={wallet}\n loadSealed={loadSealed}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,12 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DeviceWallet, LoadSealedOwner } from './wallet-types.cjs';
3
+ import '@medialane/sdk/starknet';
4
+
5
+ interface DevicesSectionProps {
6
+ walletAddress: string;
7
+ wallet: DeviceWallet;
8
+ loadSealed: LoadSealedOwner;
9
+ }
10
+ declare function DevicesSection({ walletAddress, wallet, loadSealed }: DevicesSectionProps): react_jsx_runtime.JSX.Element;
11
+
12
+ export { DevicesSection, type DevicesSectionProps };
@@ -0,0 +1,12 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DeviceWallet, LoadSealedOwner } from './wallet-types.js';
3
+ import '@medialane/sdk/starknet';
4
+
5
+ interface DevicesSectionProps {
6
+ walletAddress: string;
7
+ wallet: DeviceWallet;
8
+ loadSealed: LoadSealedOwner;
9
+ }
10
+ declare function DevicesSection({ walletAddress, wallet, loadSealed }: DevicesSectionProps): react_jsx_runtime.JSX.Element;
11
+
12
+ export { DevicesSection, type DevicesSectionProps };
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import { Loader2, Monitor, Plus } from "lucide-react";
5
+ import { canRemoveDevice, describeDevices } from "@medialane/sdk/starknet";
6
+ import { Button } from "../button.js";
7
+ import { Badge } from "../badge.js";
8
+ import { describeError } from "../../utils/describe-error.js";
9
+ import { DeviceApprovalDialog } from "./device-approval-dialog.js";
10
+ const short = (v) => `${v.slice(0, 10)}\u2026${v.slice(-6)}`;
11
+ function DevicesSection({ walletAddress, wallet, loadSealed }) {
12
+ const [devices, setDevices] = useState(null);
13
+ const [addOpen, setAddOpen] = useState(false);
14
+ const [busyGuid, setBusyGuid] = useState(null);
15
+ const [error, setError] = useState(null);
16
+ const refresh = useCallback(() => {
17
+ const sealed = loadSealed();
18
+ if (!sealed) return;
19
+ wallet.getOwners(walletAddress).then((owners) => setDevices(describeDevices(owners, sealed.ownerPubKey))).catch(() => setDevices([]));
20
+ }, [walletAddress, wallet, loadSealed]);
21
+ useEffect(() => {
22
+ refresh();
23
+ }, [refresh]);
24
+ const handleRemove = async (device) => {
25
+ const sealed = loadSealed();
26
+ if (!sealed || !devices) return;
27
+ const confirmed = window.confirm(
28
+ "Remove this device? It will no longer be able to sign for your account. Adding it back needs a device you still have."
29
+ );
30
+ if (!confirmed) return;
31
+ setBusyGuid(device.guid);
32
+ setError(null);
33
+ try {
34
+ await wallet.removeDevice(sealed, device.guid);
35
+ refresh();
36
+ } catch (e) {
37
+ setError(describeError(e, "We couldn't remove that device. Please try again.").message);
38
+ } finally {
39
+ setBusyGuid(null);
40
+ }
41
+ };
42
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-border/60 bg-card p-5 sm:p-6 space-y-5", children: [
43
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
44
+ /* @__PURE__ */ jsxs("div", { children: [
45
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-bold text-foreground", children: "Your devices" }),
46
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-0.5", children: "Each device signs for itself. Add one to use this account somewhere else." })
47
+ ] }),
48
+ /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setAddOpen(true), children: [
49
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-1" }),
50
+ "Add"
51
+ ] })
52
+ ] }),
53
+ devices === null ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) : devices.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No devices found for this account." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: devices.map((device) => /* @__PURE__ */ jsxs(
54
+ "li",
55
+ {
56
+ className: "flex items-center justify-between gap-3 rounded-xl bg-muted/40 px-4 py-3",
57
+ children: [
58
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
59
+ /* @__PURE__ */ jsx(Monitor, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
60
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground truncate", children: short(device.guid) }),
61
+ device.isThisDevice ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: "This device" }) : null
62
+ ] }),
63
+ device.isThisDevice ? null : /* @__PURE__ */ jsx(
64
+ Button,
65
+ {
66
+ size: "sm",
67
+ variant: "ghost",
68
+ disabled: !canRemoveDevice(devices, device.guid) || busyGuid !== null,
69
+ onClick: () => handleRemove(device),
70
+ children: busyGuid === device.guid ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : "Remove"
71
+ }
72
+ )
73
+ ]
74
+ },
75
+ device.guid
76
+ )) }),
77
+ error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }) : null,
78
+ /* @__PURE__ */ jsx(
79
+ DeviceApprovalDialog,
80
+ {
81
+ open: addOpen,
82
+ onOpenChange: setAddOpen,
83
+ onApproved: refresh,
84
+ wallet,
85
+ loadSealed
86
+ }
87
+ )
88
+ ] });
89
+ }
90
+ export {
91
+ DevicesSection
92
+ };
93
+ //# sourceMappingURL=devices-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/account/devices-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { Loader2, Monitor, Plus } from \"lucide-react\";\nimport { canRemoveDevice, describeDevices, type DeviceEntry } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Badge } from \"../badge.js\";\nimport { describeError } from \"../../utils/describe-error.js\";\nimport { DeviceApprovalDialog } from \"./device-approval-dialog.js\";\nimport type { DeviceWallet, LoadSealedOwner } from \"./wallet-types.js\";\n\nconst short = (v: string) => `${v.slice(0, 10)}…${v.slice(-6)}`;\n\nexport interface DevicesSectionProps {\n walletAddress: string;\n wallet: DeviceWallet;\n loadSealed: LoadSealedOwner;\n}\n\nexport function DevicesSection({ walletAddress, wallet, loadSealed }: DevicesSectionProps) {\n const [devices, setDevices] = useState<DeviceEntry[] | null>(null);\n const [addOpen, setAddOpen] = useState(false);\n const [busyGuid, setBusyGuid] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const refresh = useCallback(() => {\n const sealed = loadSealed();\n if (!sealed) return;\n wallet\n .getOwners(walletAddress)\n .then((owners) => setDevices(describeDevices(owners, sealed.ownerPubKey)))\n .catch(() => setDevices([]));\n }, [walletAddress, wallet, loadSealed]);\n\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n const handleRemove = async (device: DeviceEntry) => {\n const sealed = loadSealed();\n if (!sealed || !devices) return;\n\n const confirmed = window.confirm(\n \"Remove this device? It will no longer be able to sign for your account. Adding it back needs a device you still have.\",\n );\n if (!confirmed) return;\n\n setBusyGuid(device.guid);\n setError(null);\n try {\n await wallet.removeDevice(sealed, device.guid);\n refresh();\n } catch (e) {\n setError(describeError(e, \"We couldn't remove that device. Please try again.\").message);\n } finally {\n setBusyGuid(null);\n }\n };\n\n return (\n <div className=\"rounded-2xl border border-border/60 bg-card p-5 sm:p-6 space-y-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h3 className=\"text-base font-bold text-foreground\">Your devices</h3>\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n Each device signs for itself. Add one to use this account somewhere else.\n </p>\n </div>\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAddOpen(true)}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add\n </Button>\n </div>\n\n {devices === null ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-muted-foreground\" />\n ) : devices.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">No devices found for this account.</p>\n ) : (\n <ul className=\"space-y-2\">\n {devices.map((device) => (\n <li\n key={device.guid}\n className=\"flex items-center justify-between gap-3 rounded-xl bg-muted/40 px-4 py-3\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Monitor className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span className=\"font-mono text-xs text-muted-foreground truncate\">\n {short(device.guid)}\n </span>\n {device.isThisDevice ? <Badge variant=\"secondary\">This device</Badge> : null}\n </div>\n {device.isThisDevice ? null : (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={!canRemoveDevice(devices, device.guid) || busyGuid !== null}\n onClick={() => handleRemove(device)}\n >\n {busyGuid === device.guid ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : \"Remove\"}\n </Button>\n )}\n </li>\n ))}\n </ul>\n )}\n\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n\n <DeviceApprovalDialog\n open={addOpen}\n onOpenChange={setAddOpen}\n onApproved={refresh}\n wallet={wallet}\n loadSealed={loadSealed}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var email_code_entry_exports = {};
21
+ __export(email_code_entry_exports, {
22
+ EmailCodeEntry: () => EmailCodeEntry
23
+ });
24
+ module.exports = __toCommonJS(email_code_entry_exports);
25
+ var import_jsx_runtime = require("react/jsx-runtime");
26
+ var import_lucide_react = require("lucide-react");
27
+ var import_button = require("../button.js");
28
+ var import_alert = require("../alert.js");
29
+ var import_input_otp = require("./input-otp.js");
30
+ var import_email_code = require("../../utils/email-code.js");
31
+ function EmailCodeEntry({ emailCode, onVerify }) {
32
+ const { state, setCode, send } = emailCode;
33
+ const verifying = state.status === "verifying";
34
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex w-full flex-col items-center gap-4", children: [
35
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_alert.Alert, { variant: "destructive", className: "w-full", children: [
36
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { className: "h-4 w-4" }),
37
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_alert.AlertDescription, { children: state.error })
38
+ ] }) : null,
39
+ state.status === "sending" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2 py-2.5 text-sm text-muted-foreground", children: [
40
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }),
41
+ "Sending code\u2026"
42
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
43
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
44
+ import_input_otp.InputOTP,
45
+ {
46
+ maxLength: 6,
47
+ value: state.code,
48
+ onChange: setCode,
49
+ onComplete: (value) => onVerify(value),
50
+ disabled: verifying,
51
+ autoFocus: true,
52
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_input_otp.InputOTPGroup, { children: [0, 1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_input_otp.InputOTPSlot, { index: i, className: "h-12 w-11 text-lg font-semibold" }, i)) })
53
+ }
54
+ ),
55
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_button.Button, { size: "lg", className: "w-full gap-2", onClick: () => onVerify(), disabled: !(0, import_email_code.canVerifyCode)(state), children: [
56
+ verifying ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }) : null,
57
+ "Verify"
58
+ ] }),
59
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-center text-xs leading-relaxed text-muted-foreground", children: [
60
+ "Didn't receive it? Check your spam, or",
61
+ " ",
62
+ state.cooldown > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
63
+ "resend in ",
64
+ state.cooldown,
65
+ "s"
66
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
+ "button",
68
+ {
69
+ type: "button",
70
+ onClick: () => void send(),
71
+ disabled: !(0, import_email_code.canResendCode)(state),
72
+ className: "underline underline-offset-2 hover:text-foreground disabled:opacity-50",
73
+ children: "resend the code"
74
+ }
75
+ ),
76
+ "."
77
+ ] })
78
+ ] })
79
+ ] });
80
+ }
81
+ // Annotate the CommonJS export names for ESM import in node:
82
+ 0 && (module.exports = {
83
+ EmailCodeEntry
84
+ });
85
+ //# sourceMappingURL=email-code-entry.cjs.map