@medialane/ui 0.170.0 → 0.172.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.
@@ -33,6 +33,8 @@ function ActionDialog({ open, onClose, width = 540, shadow = true, children }) {
33
33
  position: "fixed",
34
34
  inset: 0,
35
35
  zIndex: 1e3,
36
+ // Clickable even while another modal has disabled pointer events on the page.
37
+ pointerEvents: "auto",
36
38
  display: "flex",
37
39
  alignItems: "center",
38
40
  justifyContent: "center",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/action-dialog.tsx"],"sourcesContent":["\"use client\";\n\nexport interface ActionDialogProps {\n open: boolean;\n onClose: () => void;\n\n width?: number;\n shadow?: boolean;\n children: React.ReactNode;\n}\n\nexport function ActionDialog({ open, onClose, width = 540, shadow = true, children }: ActionDialogProps) {\n if (!open) return null;\n return (\n <div\n onClick={onClose}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 24,\n backdropFilter: 'blur(28px) saturate(150%)',\n WebkitBackdropFilter: 'blur(28px) saturate(150%)',\n background: 'rgba(6,7,15,0.55)',\n animation: 'mlFade .18s ease',\n }}\n >\n <div\n onClick={e => e.stopPropagation()}\n className=\"bg-card text-card-foreground border border-border overflow-y-auto\"\n style={{\n width,\n maxWidth: '100%',\n maxHeight: '92vh',\n borderRadius: 24,\n boxShadow: shadow ? '0 50px 120px rgba(0,0,0,0.6)' : 'none',\n animation: 'mlPop .22s cubic-bezier(.2,.8,.3,1)',\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BM;AAnBC,SAAS,aAAa,EAAE,MAAM,SAAS,QAAQ,KAAK,SAAS,MAAM,SAAS,GAAsB;AACvG,MAAI,CAAC,KAAM,QAAO;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,OAAK,EAAE,gBAAgB;AAAA,UAChC,WAAU;AAAA,UACV,OAAO;AAAA,YACL;AAAA,YACA,UAAU;AAAA,YACV,WAAW;AAAA,YACX,cAAc;AAAA,YACd,WAAW,SAAS,iCAAiC;AAAA,YACrD,WAAW;AAAA,UACb;AAAA,UAEC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/action-dialog.tsx"],"sourcesContent":["\"use client\";\n\nexport interface ActionDialogProps {\n open: boolean;\n onClose: () => void;\n\n width?: number;\n shadow?: boolean;\n children: React.ReactNode;\n}\n\nexport function ActionDialog({ open, onClose, width = 540, shadow = true, children }: ActionDialogProps) {\n if (!open) return null;\n return (\n <div\n onClick={onClose}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n // Clickable even while another modal has disabled pointer events on the page.\n pointerEvents: 'auto',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 24,\n backdropFilter: 'blur(28px) saturate(150%)',\n WebkitBackdropFilter: 'blur(28px) saturate(150%)',\n background: 'rgba(6,7,15,0.55)',\n animation: 'mlFade .18s ease',\n }}\n >\n <div\n onClick={e => e.stopPropagation()}\n className=\"bg-card text-card-foreground border border-border overflow-y-auto\"\n style={{\n width,\n maxWidth: '100%',\n maxHeight: '92vh',\n borderRadius: 24,\n boxShadow: shadow ? '0 50px 120px rgba(0,0,0,0.6)' : 'none',\n animation: 'mlPop .22s cubic-bezier(.2,.8,.3,1)',\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCM;AArBC,SAAS,aAAa,EAAE,MAAM,SAAS,QAAQ,KAAK,SAAS,MAAM,SAAS,GAAsB;AACvG,MAAI,CAAC,KAAM,QAAO;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,QAER,eAAe;AAAA,QACf,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,OAAK,EAAE,gBAAgB;AAAA,UAChC,WAAU;AAAA,UACV,OAAO;AAAA,YACL;AAAA,YACA,UAAU;AAAA,YACV,WAAW;AAAA,YACX,cAAc;AAAA,YACd,WAAW,SAAS,iCAAiC;AAAA,YACrD,WAAW;AAAA,UACb;AAAA,UAEC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -10,6 +10,8 @@ function ActionDialog({ open, onClose, width = 540, shadow = true, children }) {
10
10
  position: "fixed",
11
11
  inset: 0,
12
12
  zIndex: 1e3,
13
+ // Clickable even while another modal has disabled pointer events on the page.
14
+ pointerEvents: "auto",
13
15
  display: "flex",
14
16
  alignItems: "center",
15
17
  justifyContent: "center",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/action-dialog.tsx"],"sourcesContent":["\"use client\";\n\nexport interface ActionDialogProps {\n open: boolean;\n onClose: () => void;\n\n width?: number;\n shadow?: boolean;\n children: React.ReactNode;\n}\n\nexport function ActionDialog({ open, onClose, width = 540, shadow = true, children }: ActionDialogProps) {\n if (!open) return null;\n return (\n <div\n onClick={onClose}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 24,\n backdropFilter: 'blur(28px) saturate(150%)',\n WebkitBackdropFilter: 'blur(28px) saturate(150%)',\n background: 'rgba(6,7,15,0.55)',\n animation: 'mlFade .18s ease',\n }}\n >\n <div\n onClick={e => e.stopPropagation()}\n className=\"bg-card text-card-foreground border border-border overflow-y-auto\"\n style={{\n width,\n maxWidth: '100%',\n maxHeight: '92vh',\n borderRadius: 24,\n boxShadow: shadow ? '0 50px 120px rgba(0,0,0,0.6)' : 'none',\n animation: 'mlPop .22s cubic-bezier(.2,.8,.3,1)',\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";AA8BM;AAnBC,SAAS,aAAa,EAAE,MAAM,SAAS,QAAQ,KAAK,SAAS,MAAM,SAAS,GAAsB;AACvG,MAAI,CAAC,KAAM,QAAO;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,OAAK,EAAE,gBAAgB;AAAA,UAChC,WAAU;AAAA,UACV,OAAO;AAAA,YACL;AAAA,YACA,UAAU;AAAA,YACV,WAAW;AAAA,YACX,cAAc;AAAA,YACd,WAAW,SAAS,iCAAiC;AAAA,YACrD,WAAW;AAAA,UACb;AAAA,UAEC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/action-dialog.tsx"],"sourcesContent":["\"use client\";\n\nexport interface ActionDialogProps {\n open: boolean;\n onClose: () => void;\n\n width?: number;\n shadow?: boolean;\n children: React.ReactNode;\n}\n\nexport function ActionDialog({ open, onClose, width = 540, shadow = true, children }: ActionDialogProps) {\n if (!open) return null;\n return (\n <div\n onClick={onClose}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n // Clickable even while another modal has disabled pointer events on the page.\n pointerEvents: 'auto',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 24,\n backdropFilter: 'blur(28px) saturate(150%)',\n WebkitBackdropFilter: 'blur(28px) saturate(150%)',\n background: 'rgba(6,7,15,0.55)',\n animation: 'mlFade .18s ease',\n }}\n >\n <div\n onClick={e => e.stopPropagation()}\n className=\"bg-card text-card-foreground border border-border overflow-y-auto\"\n style={{\n width,\n maxWidth: '100%',\n maxHeight: '92vh',\n borderRadius: 24,\n boxShadow: shadow ? '0 50px 120px rgba(0,0,0,0.6)' : 'none',\n animation: 'mlPop .22s cubic-bezier(.2,.8,.3,1)',\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";AAgCM;AArBC,SAAS,aAAa,EAAE,MAAM,SAAS,QAAQ,KAAK,SAAS,MAAM,SAAS,GAAsB;AACvG,MAAI,CAAC,KAAM,QAAO;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,QAER,eAAe;AAAA,QACf,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,OAAK,EAAE,gBAAgB;AAAA,UAChC,WAAU;AAAA,UACV,OAAO;AAAA,YACL;AAAA,YACA,UAAU;AAAA,YACV,WAAW;AAAA,YACX,cAAc;AAAA,YACd,WAAW,SAAS,iCAAiC;AAAA,YACrD,WAAW;AAAA,UACb;AAAA,UAEC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -25,10 +25,11 @@ module.exports = __toCommonJS(export_key_section_exports);
25
25
  var import_jsx_runtime = require("react/jsx-runtime");
26
26
  var import_react = require("react");
27
27
  var import_lucide_react = require("lucide-react");
28
+ var import_starknet = require("@medialane/sdk/starknet");
28
29
  var import_button = require("../button.js");
29
30
  var import_alert = require("../alert.js");
30
- function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError }) {
31
- const [privateKey, setPrivateKey] = (0, import_react.useState)(null);
31
+ function ExportKeySection({ loadSealed, unlock, describeError }) {
32
+ const [recoveryKey, setRecoveryKey] = (0, import_react.useState)(null);
32
33
  const [busy, setBusy] = (0, import_react.useState)(false);
33
34
  const [error, setError] = (0, import_react.useState)(null);
34
35
  const [copied, setCopied] = (0, import_react.useState)(false);
@@ -41,13 +42,7 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
41
42
  setError("This browser has no wallet key stored.");
42
43
  return;
43
44
  }
44
- if (!isRecoveryKey(sealed)) {
45
- setError(
46
- "This device was approved from another one, so its key cannot restore your account on its own. Export from the device you first signed up on."
47
- );
48
- return;
49
- }
50
- setPrivateKey(await unlock(sealed));
45
+ setRecoveryKey((0, import_starknet.encodeRecoveryKey)({ walletAddress: sealed.address, privateKey: await unlock(sealed) }));
51
46
  } catch (err) {
52
47
  const fallback = "Could not unlock your key. Please try again.";
53
48
  setError(describeError ? describeError(err, fallback) : fallback);
@@ -56,13 +51,13 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
56
51
  }
57
52
  };
58
53
  const copy = async () => {
59
- if (!privateKey) return;
60
- await navigator.clipboard.writeText(privateKey);
54
+ if (!recoveryKey) return;
55
+ await navigator.clipboard.writeText(recoveryKey);
61
56
  setCopied(true);
62
57
  setTimeout(() => setCopied(false), 2e3);
63
58
  };
64
59
  const hide = () => {
65
- setPrivateKey(null);
60
+ setRecoveryKey(null);
66
61
  setCopied(false);
67
62
  };
68
63
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3", children: [
@@ -70,7 +65,7 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
70
65
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-semibold", children: "Recovery key" }),
71
66
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-1 text-xs text-muted-foreground leading-relaxed", children: "Save this somewhere private, such as a password manager. It restores your account if you lose access to every device you use. To start using a new phone or browser, approve it from a device you already have." })
72
67
  ] }),
73
- privateKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
68
+ recoveryKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
74
69
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_alert.Alert, { variant: "destructive", children: [
75
70
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ShieldAlert, { className: "h-4 w-4" }),
76
71
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_alert.AlertDescription, { className: "text-xs leading-relaxed", children: [
@@ -79,7 +74,7 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
79
74
  "Medialane will never ask you for it. Treat any message requesting it as an attempt to take your assets."
80
75
  ] })
81
76
  ] }),
82
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all", children: privateKey }),
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all", children: recoveryKey }),
83
78
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
84
79
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_button.Button, { onClick: copy, variant: "outline", size: "sm", children: [
85
80
  copied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Check, { className: "mr-1.5 h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Copy, { className: "mr-1.5 h-3.5 w-3.5" }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/wallet/export-key-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { KeyRound, Copy, Check, EyeOff, Loader2, ShieldAlert } from \"lucide-react\";\nimport type { SealedOwner } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Alert, AlertDescription } from \"../alert.js\";\n\nexport interface ExportKeySectionProps {\n loadSealed: () => SealedOwner | null;\n unlock: (sealed: SealedOwner) => Promise<string>;\n isRecoveryKey: (sealed: SealedOwner) => boolean;\n describeError?: (err: unknown, fallback: string) => string;\n}\n\nexport function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError }: ExportKeySectionProps) {\n const [privateKey, setPrivateKey] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const reveal = async () => {\n setBusy(true);\n setError(null);\n try {\n const sealed = loadSealed();\n if (!sealed) {\n setError(\"This browser has no wallet key stored.\");\n return;\n }\n if (!isRecoveryKey(sealed)) {\n setError(\n \"This device was approved from another one, so its key cannot restore your account on its own. Export from the device you first signed up on.\",\n );\n return;\n }\n setPrivateKey(await unlock(sealed));\n } catch (err) {\n const fallback = \"Could not unlock your key. Please try again.\";\n setError(describeError ? describeError(err, fallback) : fallback);\n } finally {\n setBusy(false);\n }\n };\n\n const copy = async () => {\n if (!privateKey) return;\n await navigator.clipboard.writeText(privateKey);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const hide = () => {\n setPrivateKey(null);\n setCopied(false);\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div>\n <p className=\"text-sm font-semibold\">Recovery key</p>\n <p className=\"mt-1 text-xs text-muted-foreground leading-relaxed\">\n Save this somewhere private, such as a password manager. It restores your account if you\n lose access to every device you use. To start using a new phone or browser, approve it\n from a device you already have.\n </p>\n </div>\n\n {privateKey ? (\n <>\n <Alert variant=\"destructive\">\n <ShieldAlert className=\"h-4 w-4\" />\n <AlertDescription className=\"text-xs leading-relaxed\">\n <span className=\"font-semibold\">Anyone with this key controls your wallet.</span>{\" \"}\n Medialane will never ask you for it. Treat any message requesting it as an attempt to\n take your assets.\n </AlertDescription>\n </Alert>\n <code className=\"block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all\">\n {privateKey}\n </code>\n <div className=\"flex items-center gap-2\">\n <Button onClick={copy} variant=\"outline\" size=\"sm\">\n {copied ? <Check className=\"mr-1.5 h-3.5 w-3.5\" /> : <Copy className=\"mr-1.5 h-3.5 w-3.5\" />}\n {copied ? \"Copied\" : \"Copy key\"}\n </Button>\n <Button onClick={hide} variant=\"ghost\" size=\"sm\">\n <EyeOff className=\"mr-1.5 h-3.5 w-3.5\" />\n Hide\n </Button>\n </div>\n </>\n ) : (\n <>\n {error && <p className=\"text-sm text-destructive\">{error}</p>}\n <Button onClick={reveal} disabled={busy} variant=\"outline\" size=\"sm\" className=\"self-start\">\n {busy ? <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" /> : <KeyRound className=\"mr-1.5 h-3.5 w-3.5\" />}\n Show recovery key\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2DM;AAzDN,mBAAyB;AACzB,0BAAoE;AAEpE,oBAAuB;AACvB,mBAAwC;AASjC,SAAS,iBAAiB,EAAE,YAAY,QAAQ,eAAe,cAAc,GAA0B;AAC5G,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAwB,IAAI;AAChE,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAE1C,QAAM,SAAS,YAAY;AACzB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,WAAW;AAC1B,UAAI,CAAC,QAAQ;AACX,iBAAS,wCAAwC;AACjD;AAAA,MACF;AACA,UAAI,CAAC,cAAc,MAAM,GAAG;AAC1B;AAAA,UACE;AAAA,QACF;AACA;AAAA,MACF;AACA,oBAAc,MAAM,OAAO,MAAM,CAAC;AAAA,IACpC,SAAS,KAAK;AACZ,YAAM,WAAW;AACjB,eAAS,gBAAgB,cAAc,KAAK,QAAQ,IAAI,QAAQ;AAAA,IAClE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,OAAO,YAAY;AACvB,QAAI,CAAC,WAAY;AACjB,UAAM,UAAU,UAAU,UAAU,UAAU;AAC9C,cAAU,IAAI;AACd,eAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,EACzC;AAEA,QAAM,OAAO,MAAM;AACjB,kBAAc,IAAI;AAClB,cAAU,KAAK;AAAA,EACjB;AAEA,SACE,6CAAC,SAAI,WAAU,uBACb;AAAA,iDAAC,SACC;AAAA,kDAAC,OAAE,WAAU,yBAAwB,0BAAY;AAAA,MACjD,4CAAC,OAAE,WAAU,sDAAqD,6NAIlE;AAAA,OACF;AAAA,IAEC,aACC,4EACE;AAAA,mDAAC,sBAAM,SAAQ,eACb;AAAA,oDAAC,mCAAY,WAAU,WAAU;AAAA,QACjC,6CAAC,iCAAiB,WAAU,2BAC1B;AAAA,sDAAC,UAAK,WAAU,iBAAgB,wDAA0C;AAAA,UAAQ;AAAA,UAAI;AAAA,WAGxF;AAAA,SACF;AAAA,MACA,4CAAC,UAAK,WAAU,gFACb,sBACH;AAAA,MACA,6CAAC,SAAI,WAAU,2BACb;AAAA,qDAAC,wBAAO,SAAS,MAAM,SAAQ,WAAU,MAAK,MAC3C;AAAA,mBAAS,4CAAC,6BAAM,WAAU,sBAAqB,IAAK,4CAAC,4BAAK,WAAU,sBAAqB;AAAA,UACzF,SAAS,WAAW;AAAA,WACvB;AAAA,QACA,6CAAC,wBAAO,SAAS,MAAM,SAAQ,SAAQ,MAAK,MAC1C;AAAA,sDAAC,8BAAO,WAAU,sBAAqB;AAAA,UAAE;AAAA,WAE3C;AAAA,SACF;AAAA,OACF,IAEA,4EACG;AAAA,eAAS,4CAAC,OAAE,WAAU,4BAA4B,iBAAM;AAAA,MACzD,6CAAC,wBAAO,SAAS,QAAQ,UAAU,MAAM,SAAQ,WAAU,MAAK,MAAK,WAAU,cAC5E;AAAA,eAAO,4CAAC,+BAAQ,WAAU,mCAAkC,IAAK,4CAAC,gCAAS,WAAU,sBAAqB;AAAA,QAAG;AAAA,SAEhH;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/wallet/export-key-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { KeyRound, Copy, Check, EyeOff, Loader2, ShieldAlert } from \"lucide-react\";\nimport { encodeRecoveryKey, type SealedOwner } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Alert, AlertDescription } from \"../alert.js\";\n\nexport interface ExportKeySectionProps {\n loadSealed: () => SealedOwner | null;\n unlock: (sealed: SealedOwner) => Promise<string>;\n describeError?: (err: unknown, fallback: string) => string;\n}\n\nexport function ExportKeySection({ loadSealed, unlock, describeError }: ExportKeySectionProps) {\n const [recoveryKey, setRecoveryKey] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const reveal = async () => {\n setBusy(true);\n setError(null);\n try {\n const sealed = loadSealed();\n if (!sealed) {\n setError(\"This browser has no wallet key stored.\");\n return;\n }\n setRecoveryKey(encodeRecoveryKey({ walletAddress: sealed.address, privateKey: await unlock(sealed) }));\n } catch (err) {\n const fallback = \"Could not unlock your key. Please try again.\";\n setError(describeError ? describeError(err, fallback) : fallback);\n } finally {\n setBusy(false);\n }\n };\n\n const copy = async () => {\n if (!recoveryKey) return;\n await navigator.clipboard.writeText(recoveryKey);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const hide = () => {\n setRecoveryKey(null);\n setCopied(false);\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div>\n <p className=\"text-sm font-semibold\">Recovery key</p>\n <p className=\"mt-1 text-xs text-muted-foreground leading-relaxed\">\n Save this somewhere private, such as a password manager. It restores your account if you\n lose access to every device you use. To start using a new phone or browser, approve it\n from a device you already have.\n </p>\n </div>\n\n {recoveryKey ? (\n <>\n <Alert variant=\"destructive\">\n <ShieldAlert className=\"h-4 w-4\" />\n <AlertDescription className=\"text-xs leading-relaxed\">\n <span className=\"font-semibold\">Anyone with this key controls your wallet.</span>{\" \"}\n Medialane will never ask you for it. Treat any message requesting it as an attempt to\n take your assets.\n </AlertDescription>\n </Alert>\n <code className=\"block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all\">\n {recoveryKey}\n </code>\n <div className=\"flex items-center gap-2\">\n <Button onClick={copy} variant=\"outline\" size=\"sm\">\n {copied ? <Check className=\"mr-1.5 h-3.5 w-3.5\" /> : <Copy className=\"mr-1.5 h-3.5 w-3.5\" />}\n {copied ? \"Copied\" : \"Copy key\"}\n </Button>\n <Button onClick={hide} variant=\"ghost\" size=\"sm\">\n <EyeOff className=\"mr-1.5 h-3.5 w-3.5\" />\n Hide\n </Button>\n </div>\n </>\n ) : (\n <>\n {error && <p className=\"text-sm text-destructive\">{error}</p>}\n <Button onClick={reveal} disabled={busy} variant=\"outline\" size=\"sm\" className=\"self-start\">\n {busy ? <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" /> : <KeyRound className=\"mr-1.5 h-3.5 w-3.5\" />}\n Show recovery key\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoDM;AAlDN,mBAAyB;AACzB,0BAAoE;AACpE,sBAAoD;AACpD,oBAAuB;AACvB,mBAAwC;AAQjC,SAAS,iBAAiB,EAAE,YAAY,QAAQ,cAAc,GAA0B;AAC7F,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAwB,IAAI;AAClE,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAE1C,QAAM,SAAS,YAAY;AACzB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,WAAW;AAC1B,UAAI,CAAC,QAAQ;AACX,iBAAS,wCAAwC;AACjD;AAAA,MACF;AACA,yBAAe,mCAAkB,EAAE,eAAe,OAAO,SAAS,YAAY,MAAM,OAAO,MAAM,EAAE,CAAC,CAAC;AAAA,IACvG,SAAS,KAAK;AACZ,YAAM,WAAW;AACjB,eAAS,gBAAgB,cAAc,KAAK,QAAQ,IAAI,QAAQ;AAAA,IAClE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,OAAO,YAAY;AACvB,QAAI,CAAC,YAAa;AAClB,UAAM,UAAU,UAAU,UAAU,WAAW;AAC/C,cAAU,IAAI;AACd,eAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,EACzC;AAEA,QAAM,OAAO,MAAM;AACjB,mBAAe,IAAI;AACnB,cAAU,KAAK;AAAA,EACjB;AAEA,SACE,6CAAC,SAAI,WAAU,uBACb;AAAA,iDAAC,SACC;AAAA,kDAAC,OAAE,WAAU,yBAAwB,0BAAY;AAAA,MACjD,4CAAC,OAAE,WAAU,sDAAqD,6NAIlE;AAAA,OACF;AAAA,IAEC,cACC,4EACE;AAAA,mDAAC,sBAAM,SAAQ,eACb;AAAA,oDAAC,mCAAY,WAAU,WAAU;AAAA,QACjC,6CAAC,iCAAiB,WAAU,2BAC1B;AAAA,sDAAC,UAAK,WAAU,iBAAgB,wDAA0C;AAAA,UAAQ;AAAA,UAAI;AAAA,WAGxF;AAAA,SACF;AAAA,MACA,4CAAC,UAAK,WAAU,gFACb,uBACH;AAAA,MACA,6CAAC,SAAI,WAAU,2BACb;AAAA,qDAAC,wBAAO,SAAS,MAAM,SAAQ,WAAU,MAAK,MAC3C;AAAA,mBAAS,4CAAC,6BAAM,WAAU,sBAAqB,IAAK,4CAAC,4BAAK,WAAU,sBAAqB;AAAA,UACzF,SAAS,WAAW;AAAA,WACvB;AAAA,QACA,6CAAC,wBAAO,SAAS,MAAM,SAAQ,SAAQ,MAAK,MAC1C;AAAA,sDAAC,8BAAO,WAAU,sBAAqB;AAAA,UAAE;AAAA,WAE3C;AAAA,SACF;AAAA,OACF,IAEA,4EACG;AAAA,eAAS,4CAAC,OAAE,WAAU,4BAA4B,iBAAM;AAAA,MACzD,6CAAC,wBAAO,SAAS,QAAQ,UAAU,MAAM,SAAQ,WAAU,MAAK,MAAK,WAAU,cAC5E;AAAA,eAAO,4CAAC,+BAAQ,WAAU,mCAAkC,IAAK,4CAAC,gCAAS,WAAU,sBAAqB;AAAA,QAAG;AAAA,SAEhH;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
@@ -4,9 +4,8 @@ import { SealedOwner } from '@medialane/sdk/starknet';
4
4
  interface ExportKeySectionProps {
5
5
  loadSealed: () => SealedOwner | null;
6
6
  unlock: (sealed: SealedOwner) => Promise<string>;
7
- isRecoveryKey: (sealed: SealedOwner) => boolean;
8
7
  describeError?: (err: unknown, fallback: string) => string;
9
8
  }
10
- declare function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError }: ExportKeySectionProps): react_jsx_runtime.JSX.Element;
9
+ declare function ExportKeySection({ loadSealed, unlock, describeError }: ExportKeySectionProps): react_jsx_runtime.JSX.Element;
11
10
 
12
11
  export { ExportKeySection, type ExportKeySectionProps };
@@ -4,9 +4,8 @@ import { SealedOwner } from '@medialane/sdk/starknet';
4
4
  interface ExportKeySectionProps {
5
5
  loadSealed: () => SealedOwner | null;
6
6
  unlock: (sealed: SealedOwner) => Promise<string>;
7
- isRecoveryKey: (sealed: SealedOwner) => boolean;
8
7
  describeError?: (err: unknown, fallback: string) => string;
9
8
  }
10
- declare function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError }: ExportKeySectionProps): react_jsx_runtime.JSX.Element;
9
+ declare function ExportKeySection({ loadSealed, unlock, describeError }: ExportKeySectionProps): react_jsx_runtime.JSX.Element;
11
10
 
12
11
  export { ExportKeySection, type ExportKeySectionProps };
@@ -2,10 +2,11 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { KeyRound, Copy, Check, EyeOff, Loader2, ShieldAlert } from "lucide-react";
5
+ import { encodeRecoveryKey } from "@medialane/sdk/starknet";
5
6
  import { Button } from "../button.js";
6
7
  import { Alert, AlertDescription } from "../alert.js";
7
- function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError }) {
8
- const [privateKey, setPrivateKey] = useState(null);
8
+ function ExportKeySection({ loadSealed, unlock, describeError }) {
9
+ const [recoveryKey, setRecoveryKey] = useState(null);
9
10
  const [busy, setBusy] = useState(false);
10
11
  const [error, setError] = useState(null);
11
12
  const [copied, setCopied] = useState(false);
@@ -18,13 +19,7 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
18
19
  setError("This browser has no wallet key stored.");
19
20
  return;
20
21
  }
21
- if (!isRecoveryKey(sealed)) {
22
- setError(
23
- "This device was approved from another one, so its key cannot restore your account on its own. Export from the device you first signed up on."
24
- );
25
- return;
26
- }
27
- setPrivateKey(await unlock(sealed));
22
+ setRecoveryKey(encodeRecoveryKey({ walletAddress: sealed.address, privateKey: await unlock(sealed) }));
28
23
  } catch (err) {
29
24
  const fallback = "Could not unlock your key. Please try again.";
30
25
  setError(describeError ? describeError(err, fallback) : fallback);
@@ -33,13 +28,13 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
33
28
  }
34
29
  };
35
30
  const copy = async () => {
36
- if (!privateKey) return;
37
- await navigator.clipboard.writeText(privateKey);
31
+ if (!recoveryKey) return;
32
+ await navigator.clipboard.writeText(recoveryKey);
38
33
  setCopied(true);
39
34
  setTimeout(() => setCopied(false), 2e3);
40
35
  };
41
36
  const hide = () => {
42
- setPrivateKey(null);
37
+ setRecoveryKey(null);
43
38
  setCopied(false);
44
39
  };
45
40
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
@@ -47,7 +42,7 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
47
42
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "Recovery key" }),
48
43
  /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground leading-relaxed", children: "Save this somewhere private, such as a password manager. It restores your account if you lose access to every device you use. To start using a new phone or browser, approve it from a device you already have." })
49
44
  ] }),
50
- privateKey ? /* @__PURE__ */ jsxs(Fragment, { children: [
45
+ recoveryKey ? /* @__PURE__ */ jsxs(Fragment, { children: [
51
46
  /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
52
47
  /* @__PURE__ */ jsx(ShieldAlert, { className: "h-4 w-4" }),
53
48
  /* @__PURE__ */ jsxs(AlertDescription, { className: "text-xs leading-relaxed", children: [
@@ -56,7 +51,7 @@ function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError })
56
51
  "Medialane will never ask you for it. Treat any message requesting it as an attempt to take your assets."
57
52
  ] })
58
53
  ] }),
59
- /* @__PURE__ */ jsx("code", { className: "block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all", children: privateKey }),
54
+ /* @__PURE__ */ jsx("code", { className: "block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all", children: recoveryKey }),
60
55
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
61
56
  /* @__PURE__ */ jsxs(Button, { onClick: copy, variant: "outline", size: "sm", children: [
62
57
  copied ? /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "mr-1.5 h-3.5 w-3.5" }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/wallet/export-key-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { KeyRound, Copy, Check, EyeOff, Loader2, ShieldAlert } from \"lucide-react\";\nimport type { SealedOwner } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Alert, AlertDescription } from \"../alert.js\";\n\nexport interface ExportKeySectionProps {\n loadSealed: () => SealedOwner | null;\n unlock: (sealed: SealedOwner) => Promise<string>;\n isRecoveryKey: (sealed: SealedOwner) => boolean;\n describeError?: (err: unknown, fallback: string) => string;\n}\n\nexport function ExportKeySection({ loadSealed, unlock, isRecoveryKey, describeError }: ExportKeySectionProps) {\n const [privateKey, setPrivateKey] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const reveal = async () => {\n setBusy(true);\n setError(null);\n try {\n const sealed = loadSealed();\n if (!sealed) {\n setError(\"This browser has no wallet key stored.\");\n return;\n }\n if (!isRecoveryKey(sealed)) {\n setError(\n \"This device was approved from another one, so its key cannot restore your account on its own. Export from the device you first signed up on.\",\n );\n return;\n }\n setPrivateKey(await unlock(sealed));\n } catch (err) {\n const fallback = \"Could not unlock your key. Please try again.\";\n setError(describeError ? describeError(err, fallback) : fallback);\n } finally {\n setBusy(false);\n }\n };\n\n const copy = async () => {\n if (!privateKey) return;\n await navigator.clipboard.writeText(privateKey);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const hide = () => {\n setPrivateKey(null);\n setCopied(false);\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div>\n <p className=\"text-sm font-semibold\">Recovery key</p>\n <p className=\"mt-1 text-xs text-muted-foreground leading-relaxed\">\n Save this somewhere private, such as a password manager. It restores your account if you\n lose access to every device you use. To start using a new phone or browser, approve it\n from a device you already have.\n </p>\n </div>\n\n {privateKey ? (\n <>\n <Alert variant=\"destructive\">\n <ShieldAlert className=\"h-4 w-4\" />\n <AlertDescription className=\"text-xs leading-relaxed\">\n <span className=\"font-semibold\">Anyone with this key controls your wallet.</span>{\" \"}\n Medialane will never ask you for it. Treat any message requesting it as an attempt to\n take your assets.\n </AlertDescription>\n </Alert>\n <code className=\"block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all\">\n {privateKey}\n </code>\n <div className=\"flex items-center gap-2\">\n <Button onClick={copy} variant=\"outline\" size=\"sm\">\n {copied ? <Check className=\"mr-1.5 h-3.5 w-3.5\" /> : <Copy className=\"mr-1.5 h-3.5 w-3.5\" />}\n {copied ? \"Copied\" : \"Copy key\"}\n </Button>\n <Button onClick={hide} variant=\"ghost\" size=\"sm\">\n <EyeOff className=\"mr-1.5 h-3.5 w-3.5\" />\n Hide\n </Button>\n </div>\n </>\n ) : (\n <>\n {error && <p className=\"text-sm text-destructive\">{error}</p>}\n <Button onClick={reveal} disabled={busy} variant=\"outline\" size=\"sm\" className=\"self-start\">\n {busy ? <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" /> : <KeyRound className=\"mr-1.5 h-3.5 w-3.5\" />}\n Show recovery key\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";AA2DM,SAUE,UATA,KADF;AAzDN,SAAS,gBAAgB;AACzB,SAAS,UAAU,MAAM,OAAO,QAAQ,SAAS,mBAAmB;AAEpE,SAAS,cAAc;AACvB,SAAS,OAAO,wBAAwB;AASjC,SAAS,iBAAiB,EAAE,YAAY,QAAQ,eAAe,cAAc,GAA0B;AAC5G,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAChE,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,QAAM,SAAS,YAAY;AACzB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,WAAW;AAC1B,UAAI,CAAC,QAAQ;AACX,iBAAS,wCAAwC;AACjD;AAAA,MACF;AACA,UAAI,CAAC,cAAc,MAAM,GAAG;AAC1B;AAAA,UACE;AAAA,QACF;AACA;AAAA,MACF;AACA,oBAAc,MAAM,OAAO,MAAM,CAAC;AAAA,IACpC,SAAS,KAAK;AACZ,YAAM,WAAW;AACjB,eAAS,gBAAgB,cAAc,KAAK,QAAQ,IAAI,QAAQ;AAAA,IAClE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,OAAO,YAAY;AACvB,QAAI,CAAC,WAAY;AACjB,UAAM,UAAU,UAAU,UAAU,UAAU;AAC9C,cAAU,IAAI;AACd,eAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,EACzC;AAEA,QAAM,OAAO,MAAM;AACjB,kBAAc,IAAI;AAClB,cAAU,KAAK;AAAA,EACjB;AAEA,SACE,qBAAC,SAAI,WAAU,uBACb;AAAA,yBAAC,SACC;AAAA,0BAAC,OAAE,WAAU,yBAAwB,0BAAY;AAAA,MACjD,oBAAC,OAAE,WAAU,sDAAqD,6NAIlE;AAAA,OACF;AAAA,IAEC,aACC,iCACE;AAAA,2BAAC,SAAM,SAAQ,eACb;AAAA,4BAAC,eAAY,WAAU,WAAU;AAAA,QACjC,qBAAC,oBAAiB,WAAU,2BAC1B;AAAA,8BAAC,UAAK,WAAU,iBAAgB,wDAA0C;AAAA,UAAQ;AAAA,UAAI;AAAA,WAGxF;AAAA,SACF;AAAA,MACA,oBAAC,UAAK,WAAU,gFACb,sBACH;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,6BAAC,UAAO,SAAS,MAAM,SAAQ,WAAU,MAAK,MAC3C;AAAA,mBAAS,oBAAC,SAAM,WAAU,sBAAqB,IAAK,oBAAC,QAAK,WAAU,sBAAqB;AAAA,UACzF,SAAS,WAAW;AAAA,WACvB;AAAA,QACA,qBAAC,UAAO,SAAS,MAAM,SAAQ,SAAQ,MAAK,MAC1C;AAAA,8BAAC,UAAO,WAAU,sBAAqB;AAAA,UAAE;AAAA,WAE3C;AAAA,SACF;AAAA,OACF,IAEA,iCACG;AAAA,eAAS,oBAAC,OAAE,WAAU,4BAA4B,iBAAM;AAAA,MACzD,qBAAC,UAAO,SAAS,QAAQ,UAAU,MAAM,SAAQ,WAAU,MAAK,MAAK,WAAU,cAC5E;AAAA,eAAO,oBAAC,WAAQ,WAAU,mCAAkC,IAAK,oBAAC,YAAS,WAAU,sBAAqB;AAAA,QAAG;AAAA,SAEhH;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/wallet/export-key-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { KeyRound, Copy, Check, EyeOff, Loader2, ShieldAlert } from \"lucide-react\";\nimport { encodeRecoveryKey, type SealedOwner } from \"@medialane/sdk/starknet\";\nimport { Button } from \"../button.js\";\nimport { Alert, AlertDescription } from \"../alert.js\";\n\nexport interface ExportKeySectionProps {\n loadSealed: () => SealedOwner | null;\n unlock: (sealed: SealedOwner) => Promise<string>;\n describeError?: (err: unknown, fallback: string) => string;\n}\n\nexport function ExportKeySection({ loadSealed, unlock, describeError }: ExportKeySectionProps) {\n const [recoveryKey, setRecoveryKey] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n\n const reveal = async () => {\n setBusy(true);\n setError(null);\n try {\n const sealed = loadSealed();\n if (!sealed) {\n setError(\"This browser has no wallet key stored.\");\n return;\n }\n setRecoveryKey(encodeRecoveryKey({ walletAddress: sealed.address, privateKey: await unlock(sealed) }));\n } catch (err) {\n const fallback = \"Could not unlock your key. Please try again.\";\n setError(describeError ? describeError(err, fallback) : fallback);\n } finally {\n setBusy(false);\n }\n };\n\n const copy = async () => {\n if (!recoveryKey) return;\n await navigator.clipboard.writeText(recoveryKey);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const hide = () => {\n setRecoveryKey(null);\n setCopied(false);\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div>\n <p className=\"text-sm font-semibold\">Recovery key</p>\n <p className=\"mt-1 text-xs text-muted-foreground leading-relaxed\">\n Save this somewhere private, such as a password manager. It restores your account if you\n lose access to every device you use. To start using a new phone or browser, approve it\n from a device you already have.\n </p>\n </div>\n\n {recoveryKey ? (\n <>\n <Alert variant=\"destructive\">\n <ShieldAlert className=\"h-4 w-4\" />\n <AlertDescription className=\"text-xs leading-relaxed\">\n <span className=\"font-semibold\">Anyone with this key controls your wallet.</span>{\" \"}\n Medialane will never ask you for it. Treat any message requesting it as an attempt to\n take your assets.\n </AlertDescription>\n </Alert>\n <code className=\"block break-all rounded-xl bg-muted px-3 py-2.5 font-mono text-xs select-all\">\n {recoveryKey}\n </code>\n <div className=\"flex items-center gap-2\">\n <Button onClick={copy} variant=\"outline\" size=\"sm\">\n {copied ? <Check className=\"mr-1.5 h-3.5 w-3.5\" /> : <Copy className=\"mr-1.5 h-3.5 w-3.5\" />}\n {copied ? \"Copied\" : \"Copy key\"}\n </Button>\n <Button onClick={hide} variant=\"ghost\" size=\"sm\">\n <EyeOff className=\"mr-1.5 h-3.5 w-3.5\" />\n Hide\n </Button>\n </div>\n </>\n ) : (\n <>\n {error && <p className=\"text-sm text-destructive\">{error}</p>}\n <Button onClick={reveal} disabled={busy} variant=\"outline\" size=\"sm\" className=\"self-start\">\n {busy ? <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" /> : <KeyRound className=\"mr-1.5 h-3.5 w-3.5\" />}\n Show recovery key\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";AAoDM,SAUE,UATA,KADF;AAlDN,SAAS,gBAAgB;AACzB,SAAS,UAAU,MAAM,OAAO,QAAQ,SAAS,mBAAmB;AACpE,SAAS,yBAA2C;AACpD,SAAS,cAAc;AACvB,SAAS,OAAO,wBAAwB;AAQjC,SAAS,iBAAiB,EAAE,YAAY,QAAQ,cAAc,GAA0B;AAC7F,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,IAAI;AAClE,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,QAAM,SAAS,YAAY;AACzB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,WAAW;AAC1B,UAAI,CAAC,QAAQ;AACX,iBAAS,wCAAwC;AACjD;AAAA,MACF;AACA,qBAAe,kBAAkB,EAAE,eAAe,OAAO,SAAS,YAAY,MAAM,OAAO,MAAM,EAAE,CAAC,CAAC;AAAA,IACvG,SAAS,KAAK;AACZ,YAAM,WAAW;AACjB,eAAS,gBAAgB,cAAc,KAAK,QAAQ,IAAI,QAAQ;AAAA,IAClE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,OAAO,YAAY;AACvB,QAAI,CAAC,YAAa;AAClB,UAAM,UAAU,UAAU,UAAU,WAAW;AAC/C,cAAU,IAAI;AACd,eAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,EACzC;AAEA,QAAM,OAAO,MAAM;AACjB,mBAAe,IAAI;AACnB,cAAU,KAAK;AAAA,EACjB;AAEA,SACE,qBAAC,SAAI,WAAU,uBACb;AAAA,yBAAC,SACC;AAAA,0BAAC,OAAE,WAAU,yBAAwB,0BAAY;AAAA,MACjD,oBAAC,OAAE,WAAU,sDAAqD,6NAIlE;AAAA,OACF;AAAA,IAEC,cACC,iCACE;AAAA,2BAAC,SAAM,SAAQ,eACb;AAAA,4BAAC,eAAY,WAAU,WAAU;AAAA,QACjC,qBAAC,oBAAiB,WAAU,2BAC1B;AAAA,8BAAC,UAAK,WAAU,iBAAgB,wDAA0C;AAAA,UAAQ;AAAA,UAAI;AAAA,WAGxF;AAAA,SACF;AAAA,MACA,oBAAC,UAAK,WAAU,gFACb,uBACH;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,6BAAC,UAAO,SAAS,MAAM,SAAQ,WAAU,MAAK,MAC3C;AAAA,mBAAS,oBAAC,SAAM,WAAU,sBAAqB,IAAK,oBAAC,QAAK,WAAU,sBAAqB;AAAA,UACzF,SAAS,WAAW;AAAA,WACvB;AAAA,QACA,qBAAC,UAAO,SAAS,MAAM,SAAQ,SAAQ,MAAK,MAC1C;AAAA,8BAAC,UAAO,WAAU,sBAAqB;AAAA,UAAE;AAAA,WAE3C;AAAA,SACF;AAAA,OACF,IAEA,iCACG;AAAA,eAAS,oBAAC,OAAE,WAAU,4BAA4B,iBAAM;AAAA,MACzD,qBAAC,UAAO,SAAS,QAAQ,UAAU,MAAM,SAAQ,WAAU,MAAK,MAAK,WAAU,cAC5E;AAAA,eAAO,oBAAC,WAAQ,WAAU,mCAAkC,IAAK,oBAAC,YAAS,WAAU,sBAAqB;AAAA,QAAG;AAAA,SAEhH;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
@@ -27,12 +27,15 @@ var import_jsx_runtime = require("react/jsx-runtime");
27
27
  var import_react = require("react");
28
28
  var import_sdk = require("@medialane/sdk");
29
29
  var import_action_dialog = require("../action-dialog.js");
30
- function feeLabelFor(estimate) {
31
- if (estimate === "loading" || estimate == null) return null;
32
- const [whole, fraction = ""] = (0, import_sdk.formatAmount)(estimate.feeRaw.toString(), 18).split(".");
30
+ function amountLabel(raw, unit) {
31
+ const [whole, fraction = ""] = (0, import_sdk.formatAmount)(raw.toString(), 18).split(".");
33
32
  const trimmed = fraction.slice(0, 6).replace(/0+$/, "");
34
33
  const amount = trimmed ? `${whole}.${trimmed}` : whole;
35
- return `${amount} ${estimate.unit === "FRI" ? "STRK" : "ETH"}`;
34
+ return `${amount} ${unit === "FRI" ? "STRK" : "ETH"}`;
35
+ }
36
+ function feeLabelFor(estimate) {
37
+ if (estimate === "loading" || estimate == null) return null;
38
+ return amountLabel(estimate.feeRaw, estimate.unit);
36
39
  }
37
40
  function SelfFundConsentDialog({ consent }) {
38
41
  const [pending, setPending] = (0, import_react.useState)(null);
@@ -52,13 +55,21 @@ function SelfFundConsentDialog({ consent }) {
52
55
  setPending(null);
53
56
  };
54
57
  const label = feeLabelFor(pending?.feeEstimate ?? null);
58
+ const estimate = pending?.feeEstimate && pending.feeEstimate !== "loading" ? pending.feeEstimate : null;
59
+ const balanceLabel = estimate?.balanceRaw != null ? amountLabel(estimate.balanceRaw, estimate.unit) : null;
60
+ const notEnough = estimate?.balanceRaw != null && estimate.balanceRaw < estimate.feeRaw;
55
61
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_action_dialog.ActionDialog, { open: pending !== null, onClose: () => respond(false), width: 420, shadow: false, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-6 space-y-4", children: [
56
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-lg font-semibold", children: "Pay this transaction" }),
57
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground", children: "This transaction will cover onchain fees with funds from your account." }),
62
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-lg font-semibold", children: "Network fee needed" }),
63
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground", children: "Medialane usually covers network fees for you, but can't right now. You can pay this one from your wallet, or try again later." }),
58
64
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-lg bg-muted px-4 py-3 flex items-center justify-between", children: [
59
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm text-muted-foreground", children: "Estimated cost" }),
60
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold", children: label ?? "Estimating\u2026" })
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm text-muted-foreground", children: "Network fee" }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold", children: label ? `\u2248 ${label}` : "Estimating\u2026" })
67
+ ] }),
68
+ balanceLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-4", children: [
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm text-muted-foreground", children: "Your balance" }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold", children: balanceLabel })
61
71
  ] }),
72
+ notEnough && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-destructive", children: "You don't have enough to pay this fee. Try again later, when Medialane can cover it." }),
62
73
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-end gap-2", children: [
63
74
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
64
75
  "button",
@@ -66,7 +77,7 @@ function SelfFundConsentDialog({ consent }) {
66
77
  type: "button",
67
78
  onClick: () => respond(false),
68
79
  className: "px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:bg-muted transition-colors",
69
- children: "Cancel"
80
+ children: "Try again later"
70
81
  }
71
82
  ),
72
83
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -74,8 +85,9 @@ function SelfFundConsentDialog({ consent }) {
74
85
  {
75
86
  type: "button",
76
87
  onClick: () => respond(true),
77
- className: "px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity",
78
- children: "Pay with my wallet"
88
+ disabled: notEnough,
89
+ className: "px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed",
90
+ children: label ? `Pay ${label}` : "Pay from my wallet"
79
91
  }
80
92
  )
81
93
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/wallet/self-fund-consent-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { formatAmount } from \"@medialane/sdk\";\nimport type { SelfFundConsent, SelfFundFeeEstimate } from \"@medialane/sdk/starknet\";\nimport { ActionDialog } from \"../action-dialog.js\";\n\ninterface PendingRequest {\n resolve: (consented: boolean) => void;\n feeEstimate: SelfFundFeeEstimate | null | \"loading\";\n}\n\nexport function feeLabelFor(estimate: SelfFundFeeEstimate | null | \"loading\"): string | null {\n if (estimate === \"loading\" || estimate == null) return null;\n const [whole, fraction = \"\"] = formatAmount(estimate.feeRaw.toString(), 18).split(\".\");\n const trimmed = fraction.slice(0, 6).replace(/0+$/, \"\");\n const amount = trimmed ? `${whole}.${trimmed}` : whole;\n return `${amount} ${estimate.unit === \"FRI\" ? \"STRK\" : \"ETH\"}`;\n}\n\nexport function SelfFundConsentDialog({ consent }: { consent: SelfFundConsent }) {\n const [pending, setPending] = useState<PendingRequest | null>(null);\n\n useEffect(() => {\n consent.registerHandler((feeEstimatePromise) => {\n return new Promise<boolean>((resolve) => {\n setPending({ resolve, feeEstimate: \"loading\" });\n void feeEstimatePromise.then((feeEstimate) => {\n setPending((current) => (current ? { ...current, feeEstimate } : current));\n });\n });\n });\n return () => consent.registerHandler(null);\n }, [consent]);\n\n const respond = (consented: boolean) => {\n pending?.resolve(consented);\n setPending(null);\n };\n\n const label = feeLabelFor(pending?.feeEstimate ?? null);\n\n return (\n <ActionDialog open={pending !== null} onClose={() => respond(false)} width={420} shadow={false}>\n <div className=\"p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">Pay this transaction</h2>\n <p className=\"text-sm text-muted-foreground\">\n This transaction will cover onchain fees with funds from your account.\n </p>\n <div className=\"rounded-lg bg-muted px-4 py-3 flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Estimated cost</span>\n <span className=\"text-sm font-semibold\">{label ?? \"Estimating…\"}</span>\n </div>\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => respond(false)}\n className=\"px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:bg-muted transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => respond(true)}\n className=\"px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity\"\n >\n Pay with my wallet\n </button>\n </div>\n </div>\n </ActionDialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6CQ;AA3CR,mBAAoC;AACpC,iBAA6B;AAE7B,2BAA6B;AAOtB,SAAS,YAAY,UAAiE;AAC3F,MAAI,aAAa,aAAa,YAAY,KAAM,QAAO;AACvD,QAAM,CAAC,OAAO,WAAW,EAAE,QAAI,yBAAa,SAAS,OAAO,SAAS,GAAG,EAAE,EAAE,MAAM,GAAG;AACrF,QAAM,UAAU,SAAS,MAAM,GAAG,CAAC,EAAE,QAAQ,OAAO,EAAE;AACtD,QAAM,SAAS,UAAU,GAAG,KAAK,IAAI,OAAO,KAAK;AACjD,SAAO,GAAG,MAAM,IAAI,SAAS,SAAS,QAAQ,SAAS,KAAK;AAC9D;AAEO,SAAS,sBAAsB,EAAE,QAAQ,GAAiC;AAC/E,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAgC,IAAI;AAElE,8BAAU,MAAM;AACd,YAAQ,gBAAgB,CAAC,uBAAuB;AAC9C,aAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,mBAAW,EAAE,SAAS,aAAa,UAAU,CAAC;AAC9C,aAAK,mBAAmB,KAAK,CAAC,gBAAgB;AAC5C,qBAAW,CAAC,YAAa,UAAU,EAAE,GAAG,SAAS,YAAY,IAAI,OAAQ;AAAA,QAC3E,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AACD,WAAO,MAAM,QAAQ,gBAAgB,IAAI;AAAA,EAC3C,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAU,CAAC,cAAuB;AACtC,aAAS,QAAQ,SAAS;AAC1B,eAAW,IAAI;AAAA,EACjB;AAEA,QAAM,QAAQ,YAAY,SAAS,eAAe,IAAI;AAEtD,SACE,4CAAC,qCAAa,MAAM,YAAY,MAAM,SAAS,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK,QAAQ,OACvF,uDAAC,SAAI,WAAU,iBACb;AAAA,gDAAC,QAAG,WAAU,yBAAwB,kCAAoB;AAAA,IAC1D,4CAAC,OAAE,WAAU,iCAAgC,oFAE7C;AAAA,IACA,6CAAC,SAAI,WAAU,mEACb;AAAA,kDAAC,UAAK,WAAU,iCAAgC,4BAAc;AAAA,MAC9D,4CAAC,UAAK,WAAU,yBAAyB,mBAAS,oBAAc;AAAA,OAClE;AAAA,IACA,6CAAC,SAAI,WAAU,0BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC3B,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,OACF;AAAA,KACF,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/wallet/self-fund-consent-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { formatAmount } from \"@medialane/sdk\";\nimport type { SelfFundConsent, SelfFundFeeEstimate } from \"@medialane/sdk/starknet\";\nimport { ActionDialog } from \"../action-dialog.js\";\n\ninterface PendingRequest {\n resolve: (consented: boolean) => void;\n feeEstimate: SelfFundFeeEstimate | null | \"loading\";\n}\n\nfunction amountLabel(raw: bigint, unit: string): string {\n const [whole, fraction = \"\"] = formatAmount(raw.toString(), 18).split(\".\");\n const trimmed = fraction.slice(0, 6).replace(/0+$/, \"\");\n const amount = trimmed ? `${whole}.${trimmed}` : whole;\n return `${amount} ${unit === \"FRI\" ? \"STRK\" : \"ETH\"}`;\n}\n\nexport function feeLabelFor(estimate: SelfFundFeeEstimate | null | \"loading\"): string | null {\n if (estimate === \"loading\" || estimate == null) return null;\n return amountLabel(estimate.feeRaw, estimate.unit);\n}\n\nexport function SelfFundConsentDialog({ consent }: { consent: SelfFundConsent }) {\n const [pending, setPending] = useState<PendingRequest | null>(null);\n\n useEffect(() => {\n consent.registerHandler((feeEstimatePromise) => {\n return new Promise<boolean>((resolve) => {\n setPending({ resolve, feeEstimate: \"loading\" });\n void feeEstimatePromise.then((feeEstimate) => {\n setPending((current) => (current ? { ...current, feeEstimate } : current));\n });\n });\n });\n return () => consent.registerHandler(null);\n }, [consent]);\n\n const respond = (consented: boolean) => {\n pending?.resolve(consented);\n setPending(null);\n };\n\n const label = feeLabelFor(pending?.feeEstimate ?? null);\n const estimate = pending?.feeEstimate && pending.feeEstimate !== \"loading\" ? pending.feeEstimate : null;\n const balanceLabel = estimate?.balanceRaw != null ? amountLabel(estimate.balanceRaw, estimate.unit) : null;\n const notEnough = estimate?.balanceRaw != null && estimate.balanceRaw < estimate.feeRaw;\n\n return (\n <ActionDialog open={pending !== null} onClose={() => respond(false)} width={420} shadow={false}>\n <div className=\"p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">Network fee needed</h2>\n <p className=\"text-sm text-muted-foreground\">\n Medialane usually covers network fees for you, but can&apos;t right now. You can pay this one from your\n wallet, or try again later.\n </p>\n <div className=\"rounded-lg bg-muted px-4 py-3 flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Network fee</span>\n <span className=\"text-sm font-semibold\">{label ? `≈ ${label}` : \"Estimating…\"}</span>\n </div>\n {balanceLabel && (\n <div className=\"flex items-center justify-between px-4\">\n <span className=\"text-sm text-muted-foreground\">Your balance</span>\n <span className=\"text-sm font-semibold\">{balanceLabel}</span>\n </div>\n )}\n {notEnough && (\n <p className=\"text-sm text-destructive\">\n You don&apos;t have enough to pay this fee. Try again later, when Medialane can cover it.\n </p>\n )}\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => respond(false)}\n className=\"px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:bg-muted transition-colors\"\n >\n Try again later\n </button>\n <button\n type=\"button\"\n onClick={() => respond(true)}\n disabled={notEnough}\n className=\"px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {label ? `Pay ${label}` : \"Pay from my wallet\"}\n </button>\n </div>\n </div>\n </ActionDialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoDQ;AAlDR,mBAAoC;AACpC,iBAA6B;AAE7B,2BAA6B;AAO7B,SAAS,YAAY,KAAa,MAAsB;AACtD,QAAM,CAAC,OAAO,WAAW,EAAE,QAAI,yBAAa,IAAI,SAAS,GAAG,EAAE,EAAE,MAAM,GAAG;AACzE,QAAM,UAAU,SAAS,MAAM,GAAG,CAAC,EAAE,QAAQ,OAAO,EAAE;AACtD,QAAM,SAAS,UAAU,GAAG,KAAK,IAAI,OAAO,KAAK;AACjD,SAAO,GAAG,MAAM,IAAI,SAAS,QAAQ,SAAS,KAAK;AACrD;AAEO,SAAS,YAAY,UAAiE;AAC3F,MAAI,aAAa,aAAa,YAAY,KAAM,QAAO;AACvD,SAAO,YAAY,SAAS,QAAQ,SAAS,IAAI;AACnD;AAEO,SAAS,sBAAsB,EAAE,QAAQ,GAAiC;AAC/E,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAgC,IAAI;AAElE,8BAAU,MAAM;AACd,YAAQ,gBAAgB,CAAC,uBAAuB;AAC9C,aAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,mBAAW,EAAE,SAAS,aAAa,UAAU,CAAC;AAC9C,aAAK,mBAAmB,KAAK,CAAC,gBAAgB;AAC5C,qBAAW,CAAC,YAAa,UAAU,EAAE,GAAG,SAAS,YAAY,IAAI,OAAQ;AAAA,QAC3E,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AACD,WAAO,MAAM,QAAQ,gBAAgB,IAAI;AAAA,EAC3C,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAU,CAAC,cAAuB;AACtC,aAAS,QAAQ,SAAS;AAC1B,eAAW,IAAI;AAAA,EACjB;AAEA,QAAM,QAAQ,YAAY,SAAS,eAAe,IAAI;AACtD,QAAM,WAAW,SAAS,eAAe,QAAQ,gBAAgB,YAAY,QAAQ,cAAc;AACnG,QAAM,eAAe,UAAU,cAAc,OAAO,YAAY,SAAS,YAAY,SAAS,IAAI,IAAI;AACtG,QAAM,YAAY,UAAU,cAAc,QAAQ,SAAS,aAAa,SAAS;AAEjF,SACE,4CAAC,qCAAa,MAAM,YAAY,MAAM,SAAS,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK,QAAQ,OACvF,uDAAC,SAAI,WAAU,iBACb;AAAA,gDAAC,QAAG,WAAU,yBAAwB,gCAAkB;AAAA,IACxD,4CAAC,OAAE,WAAU,iCAAgC,4IAG7C;AAAA,IACA,6CAAC,SAAI,WAAU,mEACb;AAAA,kDAAC,UAAK,WAAU,iCAAgC,yBAAW;AAAA,MAC3D,4CAAC,UAAK,WAAU,yBAAyB,kBAAQ,UAAK,KAAK,KAAK,oBAAc;AAAA,OAChF;AAAA,IACC,gBACC,6CAAC,SAAI,WAAU,0CACb;AAAA,kDAAC,UAAK,WAAU,iCAAgC,0BAAY;AAAA,MAC5D,4CAAC,UAAK,WAAU,yBAAyB,wBAAa;AAAA,OACxD;AAAA,IAED,aACC,4CAAC,OAAE,WAAU,4BAA2B,kGAExC;AAAA,IAEF,6CAAC,SAAI,WAAU,0BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC3B,UAAU;AAAA,UACV,WAAU;AAAA,UAET,kBAAQ,OAAO,KAAK,KAAK;AAAA;AAAA,MAC5B;AAAA,OACF;AAAA,KACF,GACF;AAEJ;","names":[]}
@@ -3,12 +3,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
4
  import { formatAmount } from "@medialane/sdk";
5
5
  import { ActionDialog } from "../action-dialog.js";
6
- function feeLabelFor(estimate) {
7
- if (estimate === "loading" || estimate == null) return null;
8
- const [whole, fraction = ""] = formatAmount(estimate.feeRaw.toString(), 18).split(".");
6
+ function amountLabel(raw, unit) {
7
+ const [whole, fraction = ""] = formatAmount(raw.toString(), 18).split(".");
9
8
  const trimmed = fraction.slice(0, 6).replace(/0+$/, "");
10
9
  const amount = trimmed ? `${whole}.${trimmed}` : whole;
11
- return `${amount} ${estimate.unit === "FRI" ? "STRK" : "ETH"}`;
10
+ return `${amount} ${unit === "FRI" ? "STRK" : "ETH"}`;
11
+ }
12
+ function feeLabelFor(estimate) {
13
+ if (estimate === "loading" || estimate == null) return null;
14
+ return amountLabel(estimate.feeRaw, estimate.unit);
12
15
  }
13
16
  function SelfFundConsentDialog({ consent }) {
14
17
  const [pending, setPending] = useState(null);
@@ -28,13 +31,21 @@ function SelfFundConsentDialog({ consent }) {
28
31
  setPending(null);
29
32
  };
30
33
  const label = feeLabelFor(pending?.feeEstimate ?? null);
34
+ const estimate = pending?.feeEstimate && pending.feeEstimate !== "loading" ? pending.feeEstimate : null;
35
+ const balanceLabel = estimate?.balanceRaw != null ? amountLabel(estimate.balanceRaw, estimate.unit) : null;
36
+ const notEnough = estimate?.balanceRaw != null && estimate.balanceRaw < estimate.feeRaw;
31
37
  return /* @__PURE__ */ jsx(ActionDialog, { open: pending !== null, onClose: () => respond(false), width: 420, shadow: false, children: /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-4", children: [
32
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Pay this transaction" }),
33
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "This transaction will cover onchain fees with funds from your account." }),
38
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Network fee needed" }),
39
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Medialane usually covers network fees for you, but can't right now. You can pay this one from your wallet, or try again later." }),
34
40
  /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-muted px-4 py-3 flex items-center justify-between", children: [
35
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Estimated cost" }),
36
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: label ?? "Estimating\u2026" })
41
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Network fee" }),
42
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: label ? `\u2248 ${label}` : "Estimating\u2026" })
43
+ ] }),
44
+ balanceLabel && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4", children: [
45
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Your balance" }),
46
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: balanceLabel })
37
47
  ] }),
48
+ notEnough && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: "You don't have enough to pay this fee. Try again later, when Medialane can cover it." }),
38
49
  /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
39
50
  /* @__PURE__ */ jsx(
40
51
  "button",
@@ -42,7 +53,7 @@ function SelfFundConsentDialog({ consent }) {
42
53
  type: "button",
43
54
  onClick: () => respond(false),
44
55
  className: "px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:bg-muted transition-colors",
45
- children: "Cancel"
56
+ children: "Try again later"
46
57
  }
47
58
  ),
48
59
  /* @__PURE__ */ jsx(
@@ -50,8 +61,9 @@ function SelfFundConsentDialog({ consent }) {
50
61
  {
51
62
  type: "button",
52
63
  onClick: () => respond(true),
53
- className: "px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity",
54
- children: "Pay with my wallet"
64
+ disabled: notEnough,
65
+ className: "px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed",
66
+ children: label ? `Pay ${label}` : "Pay from my wallet"
55
67
  }
56
68
  )
57
69
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/wallet/self-fund-consent-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { formatAmount } from \"@medialane/sdk\";\nimport type { SelfFundConsent, SelfFundFeeEstimate } from \"@medialane/sdk/starknet\";\nimport { ActionDialog } from \"../action-dialog.js\";\n\ninterface PendingRequest {\n resolve: (consented: boolean) => void;\n feeEstimate: SelfFundFeeEstimate | null | \"loading\";\n}\n\nexport function feeLabelFor(estimate: SelfFundFeeEstimate | null | \"loading\"): string | null {\n if (estimate === \"loading\" || estimate == null) return null;\n const [whole, fraction = \"\"] = formatAmount(estimate.feeRaw.toString(), 18).split(\".\");\n const trimmed = fraction.slice(0, 6).replace(/0+$/, \"\");\n const amount = trimmed ? `${whole}.${trimmed}` : whole;\n return `${amount} ${estimate.unit === \"FRI\" ? \"STRK\" : \"ETH\"}`;\n}\n\nexport function SelfFundConsentDialog({ consent }: { consent: SelfFundConsent }) {\n const [pending, setPending] = useState<PendingRequest | null>(null);\n\n useEffect(() => {\n consent.registerHandler((feeEstimatePromise) => {\n return new Promise<boolean>((resolve) => {\n setPending({ resolve, feeEstimate: \"loading\" });\n void feeEstimatePromise.then((feeEstimate) => {\n setPending((current) => (current ? { ...current, feeEstimate } : current));\n });\n });\n });\n return () => consent.registerHandler(null);\n }, [consent]);\n\n const respond = (consented: boolean) => {\n pending?.resolve(consented);\n setPending(null);\n };\n\n const label = feeLabelFor(pending?.feeEstimate ?? null);\n\n return (\n <ActionDialog open={pending !== null} onClose={() => respond(false)} width={420} shadow={false}>\n <div className=\"p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">Pay this transaction</h2>\n <p className=\"text-sm text-muted-foreground\">\n This transaction will cover onchain fees with funds from your account.\n </p>\n <div className=\"rounded-lg bg-muted px-4 py-3 flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Estimated cost</span>\n <span className=\"text-sm font-semibold\">{label ?? \"Estimating…\"}</span>\n </div>\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => respond(false)}\n className=\"px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:bg-muted transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => respond(true)}\n className=\"px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity\"\n >\n Pay with my wallet\n </button>\n </div>\n </div>\n </ActionDialog>\n );\n}\n"],"mappings":";AA6CQ,cAIA,YAJA;AA3CR,SAAS,WAAW,gBAAgB;AACpC,SAAS,oBAAoB;AAE7B,SAAS,oBAAoB;AAOtB,SAAS,YAAY,UAAiE;AAC3F,MAAI,aAAa,aAAa,YAAY,KAAM,QAAO;AACvD,QAAM,CAAC,OAAO,WAAW,EAAE,IAAI,aAAa,SAAS,OAAO,SAAS,GAAG,EAAE,EAAE,MAAM,GAAG;AACrF,QAAM,UAAU,SAAS,MAAM,GAAG,CAAC,EAAE,QAAQ,OAAO,EAAE;AACtD,QAAM,SAAS,UAAU,GAAG,KAAK,IAAI,OAAO,KAAK;AACjD,SAAO,GAAG,MAAM,IAAI,SAAS,SAAS,QAAQ,SAAS,KAAK;AAC9D;AAEO,SAAS,sBAAsB,EAAE,QAAQ,GAAiC;AAC/E,QAAM,CAAC,SAAS,UAAU,IAAI,SAAgC,IAAI;AAElE,YAAU,MAAM;AACd,YAAQ,gBAAgB,CAAC,uBAAuB;AAC9C,aAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,mBAAW,EAAE,SAAS,aAAa,UAAU,CAAC;AAC9C,aAAK,mBAAmB,KAAK,CAAC,gBAAgB;AAC5C,qBAAW,CAAC,YAAa,UAAU,EAAE,GAAG,SAAS,YAAY,IAAI,OAAQ;AAAA,QAC3E,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AACD,WAAO,MAAM,QAAQ,gBAAgB,IAAI;AAAA,EAC3C,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAU,CAAC,cAAuB;AACtC,aAAS,QAAQ,SAAS;AAC1B,eAAW,IAAI;AAAA,EACjB;AAEA,QAAM,QAAQ,YAAY,SAAS,eAAe,IAAI;AAEtD,SACE,oBAAC,gBAAa,MAAM,YAAY,MAAM,SAAS,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK,QAAQ,OACvF,+BAAC,SAAI,WAAU,iBACb;AAAA,wBAAC,QAAG,WAAU,yBAAwB,kCAAoB;AAAA,IAC1D,oBAAC,OAAE,WAAU,iCAAgC,oFAE7C;AAAA,IACA,qBAAC,SAAI,WAAU,mEACb;AAAA,0BAAC,UAAK,WAAU,iCAAgC,4BAAc;AAAA,MAC9D,oBAAC,UAAK,WAAU,yBAAyB,mBAAS,oBAAc;AAAA,OAClE;AAAA,IACA,qBAAC,SAAI,WAAU,0BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC3B,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,OACF;AAAA,KACF,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/wallet/self-fund-consent-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { formatAmount } from \"@medialane/sdk\";\nimport type { SelfFundConsent, SelfFundFeeEstimate } from \"@medialane/sdk/starknet\";\nimport { ActionDialog } from \"../action-dialog.js\";\n\ninterface PendingRequest {\n resolve: (consented: boolean) => void;\n feeEstimate: SelfFundFeeEstimate | null | \"loading\";\n}\n\nfunction amountLabel(raw: bigint, unit: string): string {\n const [whole, fraction = \"\"] = formatAmount(raw.toString(), 18).split(\".\");\n const trimmed = fraction.slice(0, 6).replace(/0+$/, \"\");\n const amount = trimmed ? `${whole}.${trimmed}` : whole;\n return `${amount} ${unit === \"FRI\" ? \"STRK\" : \"ETH\"}`;\n}\n\nexport function feeLabelFor(estimate: SelfFundFeeEstimate | null | \"loading\"): string | null {\n if (estimate === \"loading\" || estimate == null) return null;\n return amountLabel(estimate.feeRaw, estimate.unit);\n}\n\nexport function SelfFundConsentDialog({ consent }: { consent: SelfFundConsent }) {\n const [pending, setPending] = useState<PendingRequest | null>(null);\n\n useEffect(() => {\n consent.registerHandler((feeEstimatePromise) => {\n return new Promise<boolean>((resolve) => {\n setPending({ resolve, feeEstimate: \"loading\" });\n void feeEstimatePromise.then((feeEstimate) => {\n setPending((current) => (current ? { ...current, feeEstimate } : current));\n });\n });\n });\n return () => consent.registerHandler(null);\n }, [consent]);\n\n const respond = (consented: boolean) => {\n pending?.resolve(consented);\n setPending(null);\n };\n\n const label = feeLabelFor(pending?.feeEstimate ?? null);\n const estimate = pending?.feeEstimate && pending.feeEstimate !== \"loading\" ? pending.feeEstimate : null;\n const balanceLabel = estimate?.balanceRaw != null ? amountLabel(estimate.balanceRaw, estimate.unit) : null;\n const notEnough = estimate?.balanceRaw != null && estimate.balanceRaw < estimate.feeRaw;\n\n return (\n <ActionDialog open={pending !== null} onClose={() => respond(false)} width={420} shadow={false}>\n <div className=\"p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">Network fee needed</h2>\n <p className=\"text-sm text-muted-foreground\">\n Medialane usually covers network fees for you, but can&apos;t right now. You can pay this one from your\n wallet, or try again later.\n </p>\n <div className=\"rounded-lg bg-muted px-4 py-3 flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Network fee</span>\n <span className=\"text-sm font-semibold\">{label ? `≈ ${label}` : \"Estimating…\"}</span>\n </div>\n {balanceLabel && (\n <div className=\"flex items-center justify-between px-4\">\n <span className=\"text-sm text-muted-foreground\">Your balance</span>\n <span className=\"text-sm font-semibold\">{balanceLabel}</span>\n </div>\n )}\n {notEnough && (\n <p className=\"text-sm text-destructive\">\n You don&apos;t have enough to pay this fee. Try again later, when Medialane can cover it.\n </p>\n )}\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => respond(false)}\n className=\"px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:bg-muted transition-colors\"\n >\n Try again later\n </button>\n <button\n type=\"button\"\n onClick={() => respond(true)}\n disabled={notEnough}\n className=\"px-4 py-2 rounded-lg text-sm font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {label ? `Pay ${label}` : \"Pay from my wallet\"}\n </button>\n </div>\n </div>\n </ActionDialog>\n );\n}\n"],"mappings":";AAoDQ,cAKA,YALA;AAlDR,SAAS,WAAW,gBAAgB;AACpC,SAAS,oBAAoB;AAE7B,SAAS,oBAAoB;AAO7B,SAAS,YAAY,KAAa,MAAsB;AACtD,QAAM,CAAC,OAAO,WAAW,EAAE,IAAI,aAAa,IAAI,SAAS,GAAG,EAAE,EAAE,MAAM,GAAG;AACzE,QAAM,UAAU,SAAS,MAAM,GAAG,CAAC,EAAE,QAAQ,OAAO,EAAE;AACtD,QAAM,SAAS,UAAU,GAAG,KAAK,IAAI,OAAO,KAAK;AACjD,SAAO,GAAG,MAAM,IAAI,SAAS,QAAQ,SAAS,KAAK;AACrD;AAEO,SAAS,YAAY,UAAiE;AAC3F,MAAI,aAAa,aAAa,YAAY,KAAM,QAAO;AACvD,SAAO,YAAY,SAAS,QAAQ,SAAS,IAAI;AACnD;AAEO,SAAS,sBAAsB,EAAE,QAAQ,GAAiC;AAC/E,QAAM,CAAC,SAAS,UAAU,IAAI,SAAgC,IAAI;AAElE,YAAU,MAAM;AACd,YAAQ,gBAAgB,CAAC,uBAAuB;AAC9C,aAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,mBAAW,EAAE,SAAS,aAAa,UAAU,CAAC;AAC9C,aAAK,mBAAmB,KAAK,CAAC,gBAAgB;AAC5C,qBAAW,CAAC,YAAa,UAAU,EAAE,GAAG,SAAS,YAAY,IAAI,OAAQ;AAAA,QAC3E,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AACD,WAAO,MAAM,QAAQ,gBAAgB,IAAI;AAAA,EAC3C,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAU,CAAC,cAAuB;AACtC,aAAS,QAAQ,SAAS;AAC1B,eAAW,IAAI;AAAA,EACjB;AAEA,QAAM,QAAQ,YAAY,SAAS,eAAe,IAAI;AACtD,QAAM,WAAW,SAAS,eAAe,QAAQ,gBAAgB,YAAY,QAAQ,cAAc;AACnG,QAAM,eAAe,UAAU,cAAc,OAAO,YAAY,SAAS,YAAY,SAAS,IAAI,IAAI;AACtG,QAAM,YAAY,UAAU,cAAc,QAAQ,SAAS,aAAa,SAAS;AAEjF,SACE,oBAAC,gBAAa,MAAM,YAAY,MAAM,SAAS,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK,QAAQ,OACvF,+BAAC,SAAI,WAAU,iBACb;AAAA,wBAAC,QAAG,WAAU,yBAAwB,gCAAkB;AAAA,IACxD,oBAAC,OAAE,WAAU,iCAAgC,4IAG7C;AAAA,IACA,qBAAC,SAAI,WAAU,mEACb;AAAA,0BAAC,UAAK,WAAU,iCAAgC,yBAAW;AAAA,MAC3D,oBAAC,UAAK,WAAU,yBAAyB,kBAAQ,UAAK,KAAK,KAAK,oBAAc;AAAA,OAChF;AAAA,IACC,gBACC,qBAAC,SAAI,WAAU,0CACb;AAAA,0BAAC,UAAK,WAAU,iCAAgC,0BAAY;AAAA,MAC5D,oBAAC,UAAK,WAAU,yBAAyB,wBAAa;AAAA,OACxD;AAAA,IAED,aACC,oBAAC,OAAE,WAAU,4BAA2B,kGAExC;AAAA,IAEF,qBAAC,SAAI,WAAU,0BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC3B,UAAU;AAAA,UACV,WAAU;AAAA,UAET,kBAAQ,OAAO,KAAK,KAAK;AAAA;AAAA,MAC5B;AAAA,OACF;AAAA,KACF,GACF;AAEJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medialane/ui",
3
- "version": "0.170.0",
3
+ "version": "0.172.0",
4
4
  "description": "Shared UI components for Medialane apps",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -51,7 +51,7 @@
51
51
  "preview": "vite --config preview/vite.config.ts"
52
52
  },
53
53
  "peerDependencies": {
54
- "@medialane/sdk": ">=0.138.0",
54
+ "@medialane/sdk": ">=0.144.0",
55
55
  "@radix-ui/react-checkbox": ">=1.1.0",
56
56
  "@radix-ui/react-collapsible": ">=1.1.0",
57
57
  "@radix-ui/react-dialog": ">=1.1.0",
@@ -79,7 +79,7 @@
79
79
  "zod": "^3.25.0"
80
80
  },
81
81
  "devDependencies": {
82
- "@medialane/sdk": "0.138.0",
82
+ "@medialane/sdk": "0.144.0",
83
83
  "@radix-ui/react-checkbox": "^1.3.8",
84
84
  "@radix-ui/react-collapsible": "^1.1.17",
85
85
  "@radix-ui/react-dialog": "^1.1.20",