@ai-matrx/vault 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -936,9 +936,11 @@ function inView(item, view) {
936
936
  if (view.startsWith("tag:")) return item.tags.includes(view.slice(4));
937
937
  return true;
938
938
  }
939
+ var AUTHENTICATOR_WORDS = ["authenticator", "2fa", "two-factor", "two factor", "totp", "one-time password", "verification code", "2-step"];
939
940
  function matchesQuery(item, query) {
940
941
  const q = query.trim().toLowerCase();
941
942
  if (!q) return true;
943
+ if (item.authenticator && AUTHENTICATOR_WORDS.some((w) => w === q || q.length >= 3 && w.startsWith(q))) return true;
942
944
  return [item.name, item.definitionLabel, ...item.tags, ...item.loginUrls, ...item.custom.map((c) => `${c.label} ${c.value}`)].some((h) => h.toLowerCase().includes(q));
943
945
  }
944
946
  var HIDDEN_ITEM_TAG = "matrx:hidden";
@@ -1564,9 +1566,23 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
1564
1566
  // src/authenticator/AuthenticatorView.tsx
1565
1567
  var import_react13 = require("react");
1566
1568
  var import_controls9 = require("@ai-matrx/design-system/controls");
1567
- var import_design_system9 = require("@ai-matrx/design-system");
1569
+ var import_design_system10 = require("@ai-matrx/design-system");
1568
1570
  var import_icons5 = require("@ai-matrx/design-system/icons");
1569
1571
 
1572
+ // src/ItemIcon.tsx
1573
+ var import_design_system7 = require("@ai-matrx/design-system");
1574
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1575
+ function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
1576
+ const box = size === "lg" ? "size-10 rounded-xl text-base" : size === "md" ? "size-8 rounded-lg text-sm" : "size-6 rounded-md text-xs";
1577
+ const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
1578
+ const initial = siteDomainOf(site).charAt(0).toUpperCase();
1579
+ if (initial && /[A-Z0-9]/.test(initial)) {
1580
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": true, className: (0, import_design_system7.cn)("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
1581
+ }
1582
+ const Glyph = definitionGlyph(definitionKey, family);
1583
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": true, className: (0, import_design_system7.cn)("flex shrink-0 items-center justify-center", box, tone), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Glyph, { className: size === "lg" ? "size-5" : "size-4" }) });
1584
+ }
1585
+
1570
1586
  // src/authenticator/AddOtpPanel.tsx
1571
1587
  var import_react12 = require("react");
1572
1588
  var import_controls8 = require("@ai-matrx/design-system/controls");
@@ -1574,7 +1590,7 @@ var import_controls8 = require("@ai-matrx/design-system/controls");
1574
1590
  // src/authenticator/CodeParts.tsx
1575
1591
  var import_react8 = require("react");
1576
1592
  var import_controls5 = require("@ai-matrx/design-system/controls");
1577
- var import_design_system7 = require("@ai-matrx/design-system");
1593
+ var import_design_system8 = require("@ai-matrx/design-system");
1578
1594
  var import_icons4 = require("@ai-matrx/design-system/icons");
1579
1595
  var import_format5 = require("@ai-matrx/kit/format");
1580
1596
 
@@ -1677,16 +1693,16 @@ function matchIssuerToSite(issuer, candidates, siteOf) {
1677
1693
  }
1678
1694
 
1679
1695
  // src/authenticator/CodeParts.tsx
1680
- var import_jsx_runtime8 = require("react/jsx-runtime");
1696
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1681
1697
  function CountdownRing({ remaining, period, loading = false, className }) {
1682
1698
  const r = 9.5;
1683
1699
  const c = 2 * Math.PI * r;
1684
1700
  const p = loading ? 0 : Math.max(0, Math.min(1, remaining / period));
1685
1701
  const urgent = !loading && remaining <= 5;
1686
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: (0, import_design_system7.cn)("relative inline-flex size-6 shrink-0 items-center justify-center", className), role: "img", "aria-label": loading ? "Getting code" : `${remaining} seconds left`, children: [
1687
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { viewBox: "0 0 24 24", className: "absolute inset-0 size-full -rotate-90", "aria-hidden": true, children: [
1688
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "12", r, fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "text-muted" }),
1689
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1702
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: (0, import_design_system8.cn)("relative inline-flex size-6 shrink-0 items-center justify-center", className), role: "img", "aria-label": loading ? "Getting code" : `${remaining} seconds left`, children: [
1703
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { viewBox: "0 0 24 24", className: "absolute inset-0 size-full -rotate-90", "aria-hidden": true, children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "12", cy: "12", r, fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "text-muted" }),
1705
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1690
1706
  "circle",
1691
1707
  {
1692
1708
  cx: "12",
@@ -1703,7 +1719,7 @@ function CountdownRing({ remaining, period, loading = false, className }) {
1703
1719
  }
1704
1720
  )
1705
1721
  ] }),
1706
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: (0, import_design_system7.cn)("text-[10px] font-semibold leading-none tabular-nums", urgent ? "text-destructive" : "text-muted-foreground"), children: loading ? "" : remaining })
1722
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: (0, import_design_system8.cn)("text-[10px] font-semibold leading-none tabular-nums", urgent ? "text-destructive" : "text-muted-foreground"), children: loading ? "" : remaining })
1707
1723
  ] });
1708
1724
  }
1709
1725
  function useCopied() {
@@ -1725,7 +1741,7 @@ function useCopied() {
1725
1741
  function CodeButton({ live, digits, size = "md", label }) {
1726
1742
  const { copied, copy } = useCopied();
1727
1743
  const shown = live.code ? (0, import_format5.groupCode)(live.code) : maskedCode(digits);
1728
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1744
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1729
1745
  "button",
1730
1746
  {
1731
1747
  type: "button",
@@ -1733,17 +1749,17 @@ function CodeButton({ live, digits, size = "md", label }) {
1733
1749
  onClick: () => live.code && void copy(live.code),
1734
1750
  "aria-label": live.code ? `Copy ${label} code` : `Getting ${label} code`,
1735
1751
  title: live.code ? "Copy" : void 0,
1736
- className: (0, import_design_system7.cn)("group -mx-1.5 inline-flex max-w-full items-center gap-2.5 rounded-md px-1.5 py-1 text-left transition-colors hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", size === "lg" && "gap-3"),
1752
+ className: (0, import_design_system8.cn)("group -mx-1.5 inline-flex max-w-full items-center gap-2.5 rounded-md px-1.5 py-1 text-left transition-colors hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", size === "lg" && "gap-3"),
1737
1753
  children: [
1738
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: (0, import_design_system7.cn)("whitespace-nowrap font-mono font-medium tabular-nums tracking-[0.08em]", size === "lg" ? "text-2xl" : "text-[1.0625rem]", live.code ? "text-foreground" : "text-disabled-foreground"), children: shown }),
1739
- copied ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-flex size-6 items-center justify-center text-success", role: "status", "aria-label": "Copied", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckGlyph, { className: "size-4" }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CountdownRing, { remaining: live.remaining, period: live.period, loading: !live.code })
1754
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: (0, import_design_system8.cn)("whitespace-nowrap font-mono font-medium tabular-nums tracking-[0.08em]", size === "lg" ? "text-2xl" : "text-[1.0625rem]", live.code ? "text-foreground" : "text-disabled-foreground"), children: shown }),
1755
+ copied ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "inline-flex size-6 items-center justify-center text-success", role: "status", "aria-label": "Copied", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CheckGlyph, { className: "size-4" }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CountdownRing, { remaining: live.remaining, period: live.period, loading: !live.code })
1740
1756
  ]
1741
1757
  }
1742
1758
  );
1743
1759
  }
1744
1760
  function CopyCodeButton({ live, label }) {
1745
1761
  const { copied, copy } = useCopied();
1746
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_controls5.Button, { variant: "quiet", icon: copied ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons4.CopyIcon, {}), ...copied ? { glyphTone: "success" } : {}, "aria-label": `Copy ${label} code`, disabled: !live.code, onClick: () => live.code && void copy(live.code) });
1762
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_controls5.Button, { variant: "quiet", icon: copied ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CheckGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_icons4.CopyIcon, {}), ...copied ? { glyphTone: "success" } : {}, "aria-label": `Copy ${label} code`, disabled: !live.code, onClick: () => live.code && void copy(live.code) });
1747
1763
  }
1748
1764
 
1749
1765
  // src/authenticator/live-code.ts
@@ -1835,42 +1851,23 @@ var import_react10 = require("react");
1835
1851
  var import_controls6 = require("@ai-matrx/design-system/controls");
1836
1852
 
1837
1853
  // src/authenticator/qr.ts
1838
- function detectorCtor() {
1839
- const c = globalThis.BarcodeDetector;
1840
- return typeof c === "function" ? c : null;
1841
- }
1842
- var support = null;
1854
+ var import_qr = require("@ai-matrx/capture/qr");
1843
1855
  function canDecodeQrLocally() {
1844
- if (!support) {
1845
- const C = detectorCtor();
1846
- support = !C ? Promise.resolve(false) : C.getSupportedFormats ? C.getSupportedFormats().then((f) => f.includes("qr_code"), () => false) : Promise.resolve(true);
1856
+ if (typeof document === "undefined") return Promise.resolve(false);
1857
+ try {
1858
+ return Promise.resolve(Boolean(document.createElement("canvas").getContext("2d")));
1859
+ } catch {
1860
+ return Promise.resolve(false);
1847
1861
  }
1848
- return support;
1849
1862
  }
1850
1863
  function hasCamera() {
1851
1864
  return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
1852
1865
  }
1853
- var detector = null;
1854
- function qrDetector() {
1855
- const C = detectorCtor();
1856
- if (!C) return null;
1857
- detector ??= new C({ formats: ["qr_code"] });
1858
- return detector;
1859
- }
1860
1866
  async function decodeQr(source) {
1861
- const d = qrDetector();
1862
- if (!d) return null;
1863
- const found = await d.detect(source);
1864
- return found.find((b) => b.rawValue)?.rawValue ?? null;
1867
+ return (0, import_qr.decodeQrFromElement)(source);
1865
1868
  }
1866
1869
  async function decodeQrFile(file) {
1867
- if (!await canDecodeQrLocally()) return null;
1868
- const bitmap = await createImageBitmap(file);
1869
- try {
1870
- return await decodeQr(bitmap);
1871
- } finally {
1872
- bitmap.close();
1873
- }
1870
+ return (0, import_qr.decodeQrFromImageFile)(file);
1874
1871
  }
1875
1872
  function imageFrom(data) {
1876
1873
  if (!data) return null;
@@ -1885,7 +1882,7 @@ function imageFrom(data) {
1885
1882
  }
1886
1883
 
1887
1884
  // src/authenticator/Setup.tsx
1888
- var import_jsx_runtime9 = require("react/jsx-runtime");
1885
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1889
1886
  function useCanScan() {
1890
1887
  const [ok, setOk] = (0, import_react10.useState)(false);
1891
1888
  (0, import_react10.useEffect)(() => {
@@ -1951,15 +1948,15 @@ function QrScanPanel({ open, onResult, onClose }) {
1951
1948
  stop();
1952
1949
  };
1953
1950
  }, [open]);
1954
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_controls6.Button, { variant: "quiet", onClick: onClose, children: "Cancel" }), children: [
1955
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative mx-auto aspect-square w-full max-w-sm overflow-hidden rounded-xl bg-muted", children: [
1956
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("video", { ref: video, className: "size-full object-cover", playsInline: true, muted: true, "aria-label": "Camera" }),
1957
- error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-destructive", children: error }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
1951
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls6.Button, { variant: "quiet", onClick: onClose, children: "Cancel" }), children: [
1952
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "relative mx-auto aspect-square w-full max-w-sm overflow-hidden rounded-xl bg-muted", children: [
1953
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("video", { ref: video, className: "size-full object-cover", playsInline: true, muted: true, "aria-label": "Camera" }),
1954
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-destructive", children: error }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
1958
1955
  ] }),
1959
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
1956
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
1960
1957
  ] });
1961
1958
  }
1962
- function OtpSetupRow({ value, onChange, label = "One-time password", placeholder = "Setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
1959
+ function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
1963
1960
  const file = (0, import_react10.useRef)(null);
1964
1961
  const canScan = useCanScan();
1965
1962
  const [scanning, setScanning] = (0, import_react10.useState)(false);
@@ -1992,11 +1989,11 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1992
1989
  setReading(false);
1993
1990
  }
1994
1991
  };
1995
- const end = status ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "max-w-48 truncate text-xs text-muted-foreground", children: status }) : reading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : imageError || error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "max-w-56 truncate text-xs text-destructive", title: imageError ?? error ?? void 0, role: "alert", children: imageError ?? error }) : parsed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex max-w-56 items-center gap-1 text-xs text-success", children: [
1996
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CheckGlyph, { className: "size-3.5 shrink-0" }),
1997
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "truncate", children: describeEnrollment(parsed) })
1992
+ const end = status ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "max-w-48 truncate text-xs text-muted-foreground", children: status }) : reading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : imageError || error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "max-w-56 truncate text-xs text-destructive", title: imageError ?? error ?? void 0, role: "alert", children: imageError ?? error }) : parsed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex max-w-56 items-center gap-1 text-xs text-success", children: [
1993
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CheckGlyph, { className: "size-3.5 shrink-0" }),
1994
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate", children: describeEnrollment(parsed) })
1998
1995
  ] }) : null;
1999
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1996
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2000
1997
  "div",
2001
1998
  {
2002
1999
  onDragOver: (e) => {
@@ -2014,16 +2011,16 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
2014
2011
  },
2015
2012
  className: dragging ? "rounded-lg bg-accent" : void 0,
2016
2013
  children: [
2017
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2014
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2018
2015
  VaultRow,
2019
2016
  {
2020
2017
  label,
2021
- actions: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2022
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_controls6.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(QrGlyph, {}), "aria-label": "Choose QR image", title: "Choose or drop a QR image", disabled: disabled || reading, onClick: () => file.current?.click() }),
2023
- canScan ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_controls6.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CameraGlyph, {}), "aria-label": "Scan with camera", title: "Scan with camera", disabled, onClick: () => setScanning(true) }) : null
2018
+ actions: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2019
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls6.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(QrGlyph, {}), "aria-label": "Choose QR image", title: "Choose or drop a QR image", disabled: disabled || reading, onClick: () => file.current?.click() }),
2020
+ canScan ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls6.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CameraGlyph, {}), "aria-label": "Scan QR code with camera", title: "Scan the site's QR code with the camera", disabled, onClick: () => setScanning(true), children: "Scan" }) : null
2024
2021
  ] }),
2025
2022
  children: [
2026
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2023
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2027
2024
  import_controls6.Field,
2028
2025
  {
2029
2026
  className: "w-full",
@@ -2046,7 +2043,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
2046
2043
  end
2047
2044
  }
2048
2045
  ),
2049
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2046
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2050
2047
  "input",
2051
2048
  {
2052
2049
  ref: file,
@@ -2063,7 +2060,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
2063
2060
  ]
2064
2061
  }
2065
2062
  ),
2066
- canScan ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2063
+ canScan ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2067
2064
  QrScanPanel,
2068
2065
  {
2069
2066
  open: scanning,
@@ -2082,7 +2079,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
2082
2079
  // src/authenticator/TotpRows.tsx
2083
2080
  var import_react11 = require("react");
2084
2081
  var import_controls7 = require("@ai-matrx/design-system/controls");
2085
- var import_design_system8 = require("@ai-matrx/design-system");
2082
+ var import_design_system9 = require("@ai-matrx/design-system");
2086
2083
  var import_confirm_opener2 = require("@ai-matrx/kit/confirm-opener");
2087
2084
 
2088
2085
  // src/authenticator/port.ts
@@ -2094,7 +2091,7 @@ function mfaMethodOf(item) {
2094
2091
  }
2095
2092
 
2096
2093
  // src/authenticator/TotpRows.tsx
2097
- var import_jsx_runtime10 = require("react/jsx-runtime");
2094
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2098
2095
  function otpActionError(e, what) {
2099
2096
  const status = e?.status;
2100
2097
  if (status === 404) return "There is no one-time password here.";
@@ -2132,7 +2129,7 @@ function useOtpActions(port, item, onChanged) {
2132
2129
  }),
2133
2130
  remove: async () => {
2134
2131
  const ok = await (0, import_confirm_opener2.confirm)({
2135
- title: `Remove the one-time password for ${item.name}?`,
2132
+ title: `Remove the authenticator for ${item.name}?`,
2136
2133
  description: "Codes stop here. Two-factor stays on at the site, and your phone app and backup codes still work.",
2137
2134
  confirmLabel: "Remove",
2138
2135
  variant: "destructive"
@@ -2151,38 +2148,38 @@ function TotpRow({ port, item, onChanged }) {
2151
2148
  const live = useLiveCode(port, item, a.enabled && !a.removed);
2152
2149
  const canManage = item.capabilities.canManage || item.capabilities.canEdit;
2153
2150
  if (a.removed || !item.authenticator) return null;
2154
- const menu = canManage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_design_system8.DropdownMenu, { children: [
2155
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system8.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls7.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MoreGlyph, {}), "aria-label": "One-time password options", disabled: a.busy }) }),
2156
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_design_system8.DropdownMenuContent, { align: "end", className: "min-w-44", children: [
2157
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_design_system8.DropdownMenuItem, { onSelect: () => void a.setEnabled(!a.enabled), children: [
2158
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PowerGlyph, { className: "size-4" }),
2151
+ const menu = canManage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_design_system9.DropdownMenu, { children: [
2152
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system9.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls7.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MoreGlyph, {}), "aria-label": "Authenticator options", disabled: a.busy }) }),
2153
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_design_system9.DropdownMenuContent, { align: "end", className: "min-w-44", children: [
2154
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_design_system9.DropdownMenuItem, { onSelect: () => void a.setEnabled(!a.enabled), children: [
2155
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PowerGlyph, { className: "size-4" }),
2159
2156
  a.enabled ? "Turn off" : "Turn on"
2160
2157
  ] }),
2161
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_design_system8.DropdownMenuSeparator, {}),
2162
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_design_system8.DropdownMenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
2163
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrashGlyph, { className: "size-4" }),
2158
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system9.DropdownMenuSeparator, {}),
2159
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_design_system9.DropdownMenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
2160
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TrashGlyph, { className: "size-4" }),
2164
2161
  "Remove"
2165
2162
  ] })
2166
2163
  ] })
2167
2164
  ] }) : null;
2168
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2165
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2169
2166
  VaultRow,
2170
2167
  {
2171
- label: "One-time password",
2172
- actions: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2173
- a.enabled ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CopyCodeButton, { live, label: item.name }) : null,
2168
+ label: "Authenticator",
2169
+ actions: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2170
+ a.enabled ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CopyCodeButton, { live, label: item.name }) : null,
2174
2171
  menu
2175
2172
  ] }),
2176
- children: a.error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex items-center gap-2", children: [
2177
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate text-sm text-destructive", role: "alert", children: a.error }),
2178
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls7.Button, { variant: "quiet", onClick: a.clearError, children: "Dismiss" })
2179
- ] }) : !a.enabled ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex items-center gap-2", children: [
2180
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2181
- canManage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls7.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PowerGlyph, {}), disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2182
- ] }) : live.error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "flex items-center gap-2", children: [
2183
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate text-sm text-destructive", title: live.error, children: live.error }),
2184
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls7.Button, { variant: "quiet", onClick: live.retry, children: "Retry" })
2185
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CodeButton, { live, digits: item.authenticator.digits, label: item.name })
2173
+ children: a.error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex items-center gap-2", children: [
2174
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "truncate text-sm text-destructive", role: "alert", children: a.error }),
2175
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls7.Button, { variant: "quiet", onClick: a.clearError, children: "Dismiss" })
2176
+ ] }) : !a.enabled ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex items-center gap-2", children: [
2177
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2178
+ canManage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls7.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PowerGlyph, {}), disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2179
+ ] }) : live.error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex items-center gap-2", children: [
2180
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "truncate text-sm text-destructive", title: live.error, children: live.error }),
2181
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls7.Button, { variant: "quiet", onClick: live.retry, children: "Retry" })
2182
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CodeButton, { live, digits: item.authenticator.digits, label: item.name })
2186
2183
  }
2187
2184
  );
2188
2185
  }
@@ -2200,7 +2197,7 @@ function TotpEditRow({ value, onChange, hasExisting, item, port, onEnrolled, onR
2200
2197
  setStatus(wasCancelled(e) ? null : otpActionError(e, "read that image"));
2201
2198
  }
2202
2199
  } : void 0;
2203
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2200
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2204
2201
  OtpSetupRow,
2205
2202
  {
2206
2203
  value,
@@ -2225,7 +2222,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
2225
2222
  const [busy, setBusy] = (0, import_react11.useState)(false);
2226
2223
  const [error, setError] = (0, import_react11.useState)(null);
2227
2224
  const canEdit = item.capabilities.canEdit || item.capabilities.canManage;
2228
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(VaultRow, { label: "Two-factor", hint: error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-destructive", children: error }) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2225
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(VaultRow, { label: "Two-factor", hint: error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-destructive", children: error }) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2229
2226
  import_controls7.Select,
2230
2227
  {
2231
2228
  value: value ?? "none",
@@ -2252,7 +2249,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
2252
2249
  }
2253
2250
 
2254
2251
  // src/authenticator/AddOtpPanel.tsx
2255
- var import_jsx_runtime11 = require("react/jsx-runtime");
2252
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2256
2253
  var NEW = "__new__";
2257
2254
  var LOGIN_KEYS = ["website_login", "username_password"];
2258
2255
  function normalizeWebsite(raw) {
@@ -2328,23 +2325,23 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2328
2325
  setBusy(false);
2329
2326
  }
2330
2327
  };
2331
- if (saved) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AddedPanel, { port, item: saved, onClose, onDone: () => onDone(saved.id) });
2332
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2328
+ if (saved) return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AddedPanel, { port, item: saved, onClose, onDone: () => onDone(saved.id) });
2329
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2333
2330
  Panel,
2334
2331
  {
2335
- title: "Add one-time password",
2332
+ title: "Add authenticator",
2336
2333
  size: "md",
2337
2334
  onClose,
2338
- footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2339
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls8.Button, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
2340
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls8.Button, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
2335
+ footer: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2336
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls8.Button, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
2337
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls8.Button, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
2341
2338
  ] }),
2342
2339
  children: [
2343
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(VaultSection, { children: [
2344
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2340
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(VaultSection, { children: [
2341
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2345
2342
  OtpSetupRow,
2346
2343
  {
2347
- label: "Setup code",
2344
+ label: "Setup key",
2348
2345
  value: input,
2349
2346
  autoFocus: true,
2350
2347
  disabled: busy,
@@ -2362,7 +2359,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2362
2359
  status: image ? `QR image ready \xB7 ${image.name}` : null
2363
2360
  }
2364
2361
  ),
2365
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(VaultRow, { label: "Add to", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2362
+ itemId ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(VaultRow, { label: "Add to", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2366
2363
  import_controls8.Select,
2367
2364
  {
2368
2365
  value: target,
@@ -2375,8 +2372,8 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2375
2372
  }
2376
2373
  }
2377
2374
  ) }),
2378
- isNew ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2379
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(VaultRow, { label: "Name", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2375
+ isNew ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2376
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(VaultRow, { label: "Name", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2380
2377
  import_controls8.Field,
2381
2378
  {
2382
2379
  className: "w-full",
@@ -2390,24 +2387,27 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2390
2387
  "aria-label": "Name"
2391
2388
  }
2392
2389
  ) }),
2393
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(VaultRow, { label: "Website", hint: websiteBad ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-destructive", children: "Not a web address" }) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls8.Field, { className: "w-full", type: "url", value: website, disabled: busy || created !== null, onChange: (e) => setWebsite(e.target.value), placeholder: "example.com", "aria-label": "Website", "aria-invalid": websiteBad || void 0 }) })
2390
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(VaultRow, { label: "Website", hint: websiteBad ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-destructive", children: "Not a web address" }) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls8.Field, { className: "w-full", type: "url", value: website, disabled: busy || created !== null, onChange: (e) => setWebsite(e.target.value), placeholder: "example.com", "aria-label": "Website", "aria-invalid": websiteBad || void 0 }) })
2394
2391
  ] }) : null
2395
2392
  ] }),
2396
- error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "px-1 text-sm text-destructive", role: "alert", children: error }) : null
2393
+ error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "px-1 text-sm text-destructive", role: "alert", children: error }) : null
2397
2394
  ]
2398
2395
  }
2399
2396
  );
2400
2397
  }
2398
+ function FirstCodePanel(props) {
2399
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AddedPanel, { ...props });
2400
+ }
2401
2401
  function AddedPanel({ port, item, onClose, onDone }) {
2402
2402
  const live = useLiveCode(port, item, true);
2403
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Panel, { title: item.name, size: "sm", onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_controls8.Button, { variant: "primary", onClick: onDone, children: "Done" }), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center gap-2 py-4", children: [
2404
- live.error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm text-destructive", children: live.error }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CodeButton, { live, digits: item.authenticator?.digits ?? 6, size: "lg", label: item.name }),
2405
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: "Enter this code on the site to finish." })
2403
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Panel, { title: item.name, size: "sm", onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls8.Button, { variant: "primary", onClick: onDone, children: "Done" }), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col items-center gap-2 py-4", children: [
2404
+ live.error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm text-destructive", children: live.error }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CodeButton, { live, digits: item.authenticator?.digits ?? 6, size: "lg", label: item.name }),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-muted-foreground", children: "Enter this code on the site to finish." })
2406
2406
  ] }) });
2407
2407
  }
2408
2408
 
2409
2409
  // src/authenticator/AuthenticatorView.tsx
2410
- var import_jsx_runtime12 = require("react/jsx-runtime");
2410
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2411
2411
  function matchesEntry(entry, q) {
2412
2412
  if (!entry || !q) return false;
2413
2413
  return [entry.issuer, entry.account].some((v) => v?.toLowerCase().includes(q));
@@ -2425,7 +2425,7 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
2425
2425
  const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
2426
2426
  return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
2427
2427
  }
2428
- function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged, header }) {
2428
+ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onChanged, header }) {
2429
2429
  const [entries, setEntries] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
2430
2430
  const [hidden, setHidden] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
2431
2431
  const [adding, setAdding] = (0, import_react13.useState)(false);
@@ -2445,20 +2445,20 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2445
2445
  const groups = otpGroups(items, entries, query, hidden);
2446
2446
  const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
2447
2447
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
2448
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { "aria-label": "One-time passwords", className: "flex h-full min-h-0 flex-col bg-background", children: [
2449
- header ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
2450
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex min-h-12 items-center justify-between gap-2 px-4 pb-1 pt-3", children: [
2451
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex min-w-0 items-baseline gap-2", children: [
2452
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "truncate text-sm font-semibold text-foreground", children: "One-time passwords" }),
2453
- total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-xs tabular-nums text-muted-foreground", children: total }) : null
2448
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("section", { "aria-label": "Authenticator", className: "flex h-full min-h-0 flex-col bg-background", children: [
2449
+ header ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
2450
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-h-12 items-center justify-between gap-2 px-4 pb-1 pt-3", children: [
2451
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 items-baseline gap-2", children: [
2452
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "truncate text-sm font-semibold text-foreground", children: "Authenticator" }),
2453
+ total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-xs tabular-nums text-muted-foreground", children: total }) : null
2454
2454
  ] }),
2455
- total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls9.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
2455
+ total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls9.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
2456
2456
  ] }),
2457
- total === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
2458
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ShieldGlyph, { className: "size-6" }) }),
2459
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-foreground", children: "No one-time passwords yet" }),
2460
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls9.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add one-time password" })
2461
- ] }) : groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: "Nothing matches." }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6 pt-1 sm:px-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: groups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(VaultSection, { title: g.label, className: "[&>div]:px-1", children: g.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2457
+ total === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
2458
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ShieldGlyph, { className: "size-6" }) }),
2459
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-foreground", children: "No authenticator codes yet" }),
2460
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls9.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add authenticator" })
2461
+ ] }) : groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: "Nothing matches." }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6 pt-1 sm:px-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: groups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultSection, { title: g.label, className: "[&>div]:px-1", children: g.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2462
2462
  OtpListRow,
2463
2463
  {
2464
2464
  port,
@@ -2466,6 +2466,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2466
2466
  family: familyOf2(item.definitionKey),
2467
2467
  entry: entries.get(item.id),
2468
2468
  onOpen: () => onOpen(item.id),
2469
+ ...host.openItemInNewTab ? { onOpenInNewTab: () => host.openItemInNewTab?.(item.id) } : {},
2469
2470
  onRename: () => {
2470
2471
  setRenameError(null);
2471
2472
  setRenaming(item);
@@ -2478,7 +2479,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2478
2479
  },
2479
2480
  item.id
2480
2481
  )) }, g.key)) }) }),
2481
- adding ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2482
+ adding ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2482
2483
  AddOtpPanel,
2483
2484
  {
2484
2485
  port,
@@ -2492,15 +2493,15 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2492
2493
  }
2493
2494
  }
2494
2495
  ) : null,
2495
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2496
- import_design_system9.TextInputDialog,
2496
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2497
+ import_design_system10.TextInputDialog,
2497
2498
  {
2498
2499
  open: renaming !== null,
2499
2500
  onOpenChange: (o) => {
2500
2501
  if (!o && !renameBusy) setRenaming(null);
2501
2502
  },
2502
2503
  title: "Rename login",
2503
- ...renameError ? { description: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-destructive", children: renameError }) } : {},
2504
+ ...renameError ? { description: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-destructive", children: renameError }) } : {},
2504
2505
  placeholder: "Login name",
2505
2506
  defaultValue: renaming?.name ?? "",
2506
2507
  confirmLabel: "Rename",
@@ -2523,50 +2524,53 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2523
2524
  )
2524
2525
  ] });
2525
2526
  }
2526
- function OtpListRow({ port, item, family, entry, onOpen, onRename, onChanged, onRemoved }) {
2527
+ function OtpListRow({ port, item, family, entry, onOpen, onOpenInNewTab, onRename, onChanged, onRemoved }) {
2527
2528
  const a = useOtpActions(port, item, (next) => next && next.authenticator === null ? onRemoved() : onChanged());
2528
2529
  const live = useLiveCode(port, item, a.enabled && !a.removed);
2529
2530
  const { copy } = useCopied();
2530
- const Icon = definitionGlyph(item.definitionKey, family);
2531
2531
  const canManage = item.capabilities.canManage || item.capabilities.canEdit;
2532
2532
  if (a.removed) return null;
2533
2533
  const sub = a.error ?? [entry?.account, loginSiteOf(item) || entry?.issuer].filter((v, i, all) => Boolean(v) && all.indexOf(v) === i).join(" \xB7 ");
2534
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid min-h-14 grid-cols-[2rem_minmax(0,1fr)_auto_auto] items-center gap-x-3 rounded-lg px-2 py-1.5 transition-colors hover:bg-accent/50", children: [
2535
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", onClick: onOpen, "aria-label": `Open ${item.name}`, tabIndex: -1, className: (0, import_design_system9.cn)("flex size-8 items-center justify-center rounded-lg bg-muted", a.enabled ? "text-foreground" : "text-disabled-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { className: "size-4" }) }),
2536
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", onClick: onOpen, className: "flex min-w-0 flex-col items-start rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [
2537
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: (0, import_design_system9.cn)("max-w-full truncate text-sm font-medium", a.enabled ? "text-foreground" : "text-muted-foreground"), children: item.name }),
2538
- sub ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: (0, import_design_system9.cn)("max-w-full truncate text-xs", a.error ? "text-destructive" : "text-muted-foreground"), title: sub, children: sub }) : null
2534
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "grid min-h-14 grid-cols-[2rem_minmax(0,1fr)_auto_auto] items-center gap-x-3 rounded-lg px-2 py-1.5 transition-colors hover:bg-accent/50", children: [
2535
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", onClick: onOpen, "aria-label": `Open ${item.name}`, tabIndex: -1, className: "flex size-8 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item) || (entry?.issuer ?? "") }) }),
2536
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { type: "button", onClick: onOpen, className: "flex min-w-0 flex-col items-start rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [
2537
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: (0, import_design_system10.cn)("max-w-full truncate text-sm font-medium", a.enabled ? "text-foreground" : "text-muted-foreground"), children: item.name }),
2538
+ sub ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: (0, import_design_system10.cn)("max-w-full truncate text-xs", a.error ? "text-destructive" : "text-muted-foreground"), title: sub, children: sub }) : null
2539
2539
  ] }),
2540
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex min-w-0 justify-end", children: !a.enabled ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "flex items-center gap-1.5", children: [
2541
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2542
- canManage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls9.Button, { variant: "quiet", disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2543
- ] }) : live.error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "flex items-center gap-1.5", children: [
2544
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "max-w-40 truncate text-xs text-destructive", title: live.error, children: live.error }),
2545
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls9.Button, { variant: "quiet", onClick: live.retry, children: "Retry" })
2546
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CodeButton, { live, digits: item.authenticator?.digits ?? 6, label: item.name }) }),
2547
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenu, { children: [
2548
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system9.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_controls9.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MoreGlyph, {}), "aria-label": `${item.name} options`, disabled: a.busy }) }),
2549
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenuContent, { align: "end", className: "min-w-44", children: [
2550
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenuItem, { onSelect: onOpen, children: [
2551
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ExternalGlyph, { className: "size-4" }),
2540
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex min-w-0 justify-end", children: !a.enabled ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1.5", children: [
2541
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2542
+ canManage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls9.Button, { variant: "quiet", disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2543
+ ] }) : live.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1.5", children: [
2544
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "max-w-40 truncate text-xs text-destructive", title: live.error, children: live.error }),
2545
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls9.Button, { variant: "quiet", onClick: live.retry, children: "Retry" })
2546
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CodeButton, { live, digits: item.authenticator?.digits ?? 6, label: item.name }) }),
2547
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenu, { children: [
2548
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls9.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MoreGlyph, {}), "aria-label": `${item.name} options`, disabled: a.busy }) }),
2549
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuContent, { align: "end", className: "min-w-44", children: [
2550
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuItem, { onSelect: onOpen, children: [
2551
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExternalGlyph, { className: "size-4" }),
2552
2552
  "Open login"
2553
2553
  ] }),
2554
- live.code ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenuItem, { onSelect: () => live.code && void copy(live.code), children: [
2555
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons5.CopyIcon, { className: "size-4" }),
2554
+ onOpenInNewTab ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuItem, { onSelect: onOpenInNewTab, children: [
2555
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ExternalGlyph, { className: "size-4" }),
2556
+ "Open in new tab"
2557
+ ] }) : null,
2558
+ live.code ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuItem, { onSelect: () => live.code && void copy(live.code), children: [
2559
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_icons5.CopyIcon, { className: "size-4" }),
2556
2560
  "Copy code"
2557
2561
  ] }) : null,
2558
- canManage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2559
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenuItem, { onSelect: onRename, children: [
2560
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PencilGlyph, { className: "size-4" }),
2562
+ canManage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2563
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuItem, { onSelect: onRename, children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PencilGlyph, { className: "size-4" }),
2561
2565
  "Rename"
2562
2566
  ] }),
2563
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenuItem, { onSelect: () => void a.setEnabled(!a.enabled), children: [
2564
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PowerGlyph, { className: "size-4" }),
2567
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuItem, { onSelect: () => void a.setEnabled(!a.enabled), children: [
2568
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PowerGlyph, { className: "size-4" }),
2565
2569
  a.enabled ? "Turn off" : "Turn on"
2566
2570
  ] }),
2567
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system9.DropdownMenuSeparator, {}),
2568
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_design_system9.DropdownMenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
2569
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TrashGlyph, { className: "size-4" }),
2571
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.DropdownMenuSeparator, {}),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.DropdownMenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TrashGlyph, { className: "size-4" }),
2570
2574
  "Remove"
2571
2575
  ] })
2572
2576
  ] }) : null
@@ -2659,7 +2663,7 @@ function withoutCode(codes, index) {
2659
2663
  }
2660
2664
 
2661
2665
  // src/fields/SettingsPanels.tsx
2662
- var import_jsx_runtime13 = require("react/jsx-runtime");
2666
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2663
2667
  var say = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
2664
2668
  function useSaver(onChanged) {
2665
2669
  const [busy, setBusy] = (0, import_react14.useState)(null);
@@ -2681,14 +2685,14 @@ function useSaver(onChanged) {
2681
2685
  return { busy, error, setError, save };
2682
2686
  }
2683
2687
  function Problem({ children }) {
2684
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { role: "alert", className: "px-1 text-sm text-destructive", children });
2688
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { role: "alert", className: "px-1 text-sm text-destructive", children });
2685
2689
  }
2686
- var quietHint = (text, tone = "muted") => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
2690
+ var quietHint = (text, tone = "muted") => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
2687
2691
  function FieldSettingsPanel(props) {
2688
2692
  const field = props.item.fields.find((f) => f.id === props.arg);
2689
2693
  if (!field)
2690
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Panel, { title: "Field settings", size: "md", onClose: props.onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Button, { variant: "primary", onClick: props.onClose, children: "Done" }), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Empty, { children: "This field is no longer on the item." }) });
2691
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FieldSettings, { ...props, field }, field.id);
2694
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Panel, { title: "Field settings", size: "md", onClose: props.onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "primary", onClick: props.onClose, children: "Done" }), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Empty, { children: "This field is no longer on the item." }) });
2695
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FieldSettings, { ...props, field }, field.id);
2692
2696
  }
2693
2697
  function FieldSettings({ item, port, field, onClose, onChanged }) {
2694
2698
  const { busy, error, setError, save } = useSaver((next) => onChanged(next));
@@ -2727,9 +2731,9 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2727
2731
  };
2728
2732
  const handling = HANDLING_WORDS[field.handling];
2729
2733
  const locked = !canEdit || busy !== null;
2730
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Panel, { title: `${field.label} settings`, size: "md", onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Button, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2731
- !canEdit ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2732
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultSection, { title: "Who can see it", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Value", hint: quietHint(handling.hint), children: field.handling === "sealed" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Badge, { icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LockGlyph, {}), children: "Sealed for good" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2734
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Panel, { title: `${field.label} settings`, size: "md", onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2735
+ !canEdit ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2736
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultSection, { title: "Who can see it", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Value", hint: quietHint(handling.hint), children: field.handling === "sealed" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Badge, { icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LockGlyph, {}), children: "Sealed for good" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2733
2737
  import_controls10.SegmentedControl,
2734
2738
  {
2735
2739
  "aria-label": "Who can see it",
@@ -2738,8 +2742,8 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2738
2742
  data: handlingChoices(field.handling).map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint, disabled: locked }))
2739
2743
  }
2740
2744
  ) }) }),
2741
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(VaultSection, { title: "Automation", children: [
2742
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Env key", hint: envProblem ? quietHint(envProblem, "destructive") : quietHint("The name sandboxes and workflows use"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2745
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(VaultSection, { title: "Automation", children: [
2746
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Env key", hint: envProblem ? quietHint(envProblem, "destructive") : quietHint("The name sandboxes and workflows use"), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2743
2747
  import_controls10.Field,
2744
2748
  {
2745
2749
  ref: envRef,
@@ -2759,10 +2763,10 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2759
2763
  }
2760
2764
  }
2761
2765
  ) }),
2762
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Use in sandbox", hint: blocker && !field.injectIntoSandbox ? quietHint(blocker) : quietHint("Sandboxes get it as an env variable"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Switch, { "aria-label": "Use in sandbox", checked: field.injectIntoSandbox, disabled: locked, onCheckedChange: (v) => void setSandbox(v) }) })
2766
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Use in sandbox", hint: blocker && !field.injectIntoSandbox ? quietHint(blocker) : quietHint("Sandboxes get it as an env variable"), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Switch, { "aria-label": "Use in sandbox", checked: field.injectIntoSandbox, disabled: locked, onCheckedChange: (v) => void setSandbox(v) }) })
2763
2767
  ] }),
2764
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(VaultSection, { title: "Field", children: [
2765
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Description", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2768
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(VaultSection, { title: "Field", children: [
2769
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Description", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2766
2770
  import_controls10.Field,
2767
2771
  {
2768
2772
  className: "w-full",
@@ -2778,10 +2782,10 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2778
2782
  }
2779
2783
  }
2780
2784
  ) }),
2781
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Editable", hint: quietHint(field.editable ? "The value can be changed" : "The value is locked"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Switch, { "aria-label": "Editable", checked: field.editable, disabled: locked, onCheckedChange: (v) => void update("editable", { editable: v }) }) }),
2782
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Turned on", hint: quietHint(field.active ? "In use" : "Kept, but nothing uses it"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Switch, { "aria-label": "Turned on", checked: field.active, disabled: locked, onCheckedChange: (v) => void update("active", { active: v }) }) })
2785
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Editable", hint: quietHint(field.editable ? "The value can be changed" : "The value is locked"), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Switch, { "aria-label": "Editable", checked: field.editable, disabled: locked, onCheckedChange: (v) => void update("editable", { editable: v }) }) }),
2786
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Turned on", hint: quietHint(field.active ? "In use" : "Kept, but nothing uses it"), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Switch, { "aria-label": "Turned on", checked: field.active, disabled: locked, onCheckedChange: (v) => void update("active", { active: v }) }) })
2783
2787
  ] }),
2784
- error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Problem, { children: error }) : null
2788
+ error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Problem, { children: error }) : null
2785
2789
  ] });
2786
2790
  }
2787
2791
  function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
@@ -2808,10 +2812,10 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
2808
2812
  });
2809
2813
  };
2810
2814
  const fillHint = !hasWebsite ? "Add a website first" : item.browserFillEnabled ? "Offered on matching pages" : "Never filled automatically";
2811
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Panel, { title: "Item settings", size: "md", onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Button, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2812
- !canEdit ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2813
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(VaultSection, { title: "In the browser", children: [
2814
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Website matching", hint: quietHint(MATCH_WORDS[item.uriMatchMode].hint), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2815
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Panel, { title: "Item settings", size: "md", onClose, footer: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2816
+ !canEdit ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2817
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(VaultSection, { title: "In the browser", children: [
2818
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Website matching", hint: quietHint(MATCH_WORDS[item.uriMatchMode].hint), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2815
2819
  import_controls10.Select,
2816
2820
  {
2817
2821
  "aria-label": "Website matching",
@@ -2821,45 +2825,45 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
2821
2825
  onValueChange: (m) => void save("match", () => port.fields.updateItem(item, { uriMatchMode: m }))
2822
2826
  }
2823
2827
  ) }),
2824
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultRow, { label: "Fill in browser", hint: quietHint(fillHint), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Switch, { "aria-label": "Fill in browser", checked: hasWebsite && item.browserFillEnabled, disabled: locked || !hasWebsite, onCheckedChange: (v) => void save("fill", () => port.fields.updateItem(item, { browserFillEnabled: v })) }) }),
2825
- promotable.map((f) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2828
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultRow, { label: "Fill in browser", hint: quietHint(fillHint), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Switch, { "aria-label": "Fill in browser", checked: hasWebsite && item.browserFillEnabled, disabled: locked || !hasWebsite, onCheckedChange: (v) => void save("fill", () => port.fields.updateItem(item, { browserFillEnabled: v })) }) }),
2829
+ promotable.map((f) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2826
2830
  VaultRow,
2827
2831
  {
2828
2832
  label: f.label,
2829
2833
  hint: quietHint("Encrypted, so the browser can\u2019t match it"),
2830
- actions: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_icons6.GlobeIcon, {}), disabled: locked, onClick: () => void promote(f), children: "Use as website" }),
2831
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-muted-foreground", children: "Not a website yet" })
2834
+ actions: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons6.GlobeIcon, {}), disabled: locked, onClick: () => void promote(f), children: "Use as website" }),
2835
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-muted-foreground", children: "Not a website yet" })
2832
2836
  },
2833
2837
  f.id
2834
2838
  ))
2835
2839
  ] }),
2836
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
2837
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.DisclosureHeader, { open: advanced, title: "Advanced", meta: item.fields.length ? `${item.fields.length} fields` : void 0, onClick: () => setAdvanced((a) => !a) }),
2838
- advanced ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VaultSection, { children: item.fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Empty, { children: "No encrypted fields." }) : item.fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2840
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
2841
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.DisclosureHeader, { open: advanced, title: "Advanced", meta: item.fields.length ? `${item.fields.length} fields` : void 0, onClick: () => setAdvanced((a) => !a) }),
2842
+ advanced ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VaultSection, { children: item.fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Empty, { children: "No encrypted fields." }) : item.fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2839
2843
  VaultRow,
2840
2844
  {
2841
2845
  label: f.label,
2842
- actions: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_controls10.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SlidersGlyph, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }),
2843
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "block truncate", children: [
2844
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: f.handling === "sealed" ? "text-foreground" : "text-muted-foreground", children: HANDLING_WORDS[f.handling].label }),
2845
- f.envKey ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "font-mono text-muted-foreground", children: [
2846
+ actions: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SlidersGlyph, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }),
2847
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "block truncate", children: [
2848
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: f.handling === "sealed" ? "text-foreground" : "text-muted-foreground", children: HANDLING_WORDS[f.handling].label }),
2849
+ f.envKey ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "font-mono text-muted-foreground", children: [
2846
2850
  " \xB7 ",
2847
2851
  f.envKey
2848
2852
  ] }) : null,
2849
- f.injectIntoSandbox ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
2850
- !f.active ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-warning", children: " \xB7 Turned off" }) : null
2853
+ f.injectIntoSandbox ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
2854
+ !f.active ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-warning", children: " \xB7 Turned off" }) : null
2851
2855
  ] })
2852
2856
  },
2853
2857
  f.id
2854
2858
  )) }) : null
2855
2859
  ] }),
2856
- error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Problem, { children: error }) : null
2860
+ error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Problem, { children: error }) : null
2857
2861
  ] });
2858
2862
  }
2859
2863
 
2860
2864
  // src/generator/PasswordGenerator.tsx
2861
2865
  var import_react15 = require("react");
2862
- var import_design_system10 = require("@ai-matrx/design-system");
2866
+ var import_design_system11 = require("@ai-matrx/design-system");
2863
2867
  var import_controls11 = require("@ai-matrx/design-system/controls");
2864
2868
  var import_icons7 = require("@ai-matrx/design-system/icons");
2865
2869
 
@@ -2916,10 +2920,10 @@ function strengthOf(bits) {
2916
2920
  }
2917
2921
 
2918
2922
  // src/generator/PasswordGenerator.tsx
2919
- var import_jsx_runtime14 = require("react/jsx-runtime");
2923
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2920
2924
  var LimitsCtx = (0, import_react15.createContext)(null);
2921
2925
  function GeneratorLimitsProvider({ resolve: resolve2, children }) {
2922
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LimitsCtx.Provider, { value: resolve2 ?? null, children });
2926
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(LimitsCtx.Provider, { value: resolve2 ?? null, children });
2923
2927
  }
2924
2928
  function useGeneratorLimitsSource() {
2925
2929
  return (0, import_react15.useContext)(LimitsCtx);
@@ -2980,11 +2984,11 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2980
2984
  (0, import_react15.useEffect)(() => () => {
2981
2985
  if (expiry.current) clearTimeout(expiry.current);
2982
2986
  }, []);
2983
- if (limits.state === "loading") return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "px-1 py-3 text-sm text-muted-foreground", "aria-live": "polite", children: "Reading limits\u2026" });
2987
+ if (limits.state === "loading") return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "px-1 py-3 text-sm text-muted-foreground", "aria-live": "polite", children: "Reading limits\u2026" });
2984
2988
  if (limits.state === "unavailable")
2985
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2 px-1 py-2", children: [
2986
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { role: "alert", className: "text-sm text-destructive", children: limits.message }),
2987
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RefreshGlyph, {}), onClick: () => setRound((n) => n + 1), children: "Try again" })
2989
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-2 px-1 py-2", children: [
2990
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { role: "alert", className: "text-sm text-destructive", children: limits.message }),
2991
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RefreshGlyph, {}), onClick: () => setRound((n) => n + 1), children: "Try again" })
2988
2992
  ] });
2989
2993
  const ceilings = limits.limits;
2990
2994
  const change = (next) => make(next, ceilings);
@@ -3001,8 +3005,8 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
3001
3005
  drop();
3002
3006
  onUse(value);
3003
3007
  };
3004
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-3", "aria-label": "Password generator", children: [
3005
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3008
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex w-full flex-col gap-3", "aria-label": "Password generator", children: [
3009
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3006
3010
  import_controls11.SegmentedControl,
3007
3011
  {
3008
3012
  fill: true,
@@ -3012,24 +3016,24 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
3012
3016
  data: [{ value: "password", label: "Password" }, { value: "passphrase", label: "Passphrase" }]
3013
3017
  }
3014
3018
  ),
3015
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1 rounded-lg bg-muted px-3 py-2", children: [
3016
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-h-7 items-start gap-1", children: [
3017
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { className: (0, import_design_system10.cn)("min-w-0 flex-1 break-all py-1 font-mono text-sm", candidate ? "text-foreground" : "text-muted-foreground"), "data-testid": "generated-value", children: candidate ? revealed ? candidate.value : MASK3 : "\u2014" }),
3018
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Button, { variant: "quiet", icon: revealed ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(EyeGlyph, {}), "aria-label": revealed ? "Hide" : "Reveal", disabled: !candidate, onClick: () => setRevealed((r) => !r) }),
3019
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons7.CopyIcon, {}), "aria-label": "Copy", disabled: !candidate, onClick: () => void copy() }),
3020
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RefreshGlyph, {}), "aria-label": "Generate another", onClick: () => change(settings) })
3019
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1 rounded-lg bg-muted px-3 py-2", children: [
3020
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex min-h-7 items-start gap-1", children: [
3021
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: (0, import_design_system11.cn)("min-w-0 flex-1 break-all py-1 font-mono text-sm", candidate ? "text-foreground" : "text-muted-foreground"), "data-testid": "generated-value", children: candidate ? revealed ? candidate.value : MASK3 : "\u2014" }),
3022
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Button, { variant: "quiet", icon: revealed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(EyeGlyph, {}), "aria-label": revealed ? "Hide" : "Reveal", disabled: !candidate, onClick: () => setRevealed((r) => !r) }),
3023
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons7.CopyIcon, {}), "aria-label": "Copy", disabled: !candidate, onClick: () => void copy() }),
3024
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RefreshGlyph, {}), "aria-label": "Generate another", onClick: () => change(settings) })
3021
3025
  ] }),
3022
- strength && candidate ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-xs", children: [
3023
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: (0, import_design_system10.cn)(strength.tone === "success" && "text-success", strength.tone === "primary" && "text-primary", strength.tone === "warning" && "text-warning"), children: strength.label }),
3024
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-muted-foreground", children: [
3026
+ strength && candidate ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-xs", children: [
3027
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: (0, import_design_system11.cn)(strength.tone === "success" && "text-success", strength.tone === "primary" && "text-primary", strength.tone === "warning" && "text-warning"), children: strength.label }),
3028
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "text-muted-foreground", children: [
3025
3029
  " \xB7 ",
3026
3030
  Math.floor(candidate.bits),
3027
3031
  "-bit"
3028
3032
  ] })
3029
3033
  ] }) : null
3030
3034
  ] }),
3031
- settings.kind === "password" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
3032
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label: "Length", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.NumberStepper, { value: settings.password.length, min: minPasswordLength(settings.password), max: ceilings.maxPasswordLength, fallback: 24, onChange: (length) => setPassword({ length }) }) }),
3035
+ settings.kind === "password" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
3036
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label: "Length", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.NumberStepper, { value: settings.password.length, min: minPasswordLength(settings.password), max: ceilings.maxPasswordLength, fallback: 24, onChange: (length) => setPassword({ length }) }) }),
3033
3037
  [
3034
3038
  ["lowercase", "Lowercase"],
3035
3039
  ["uppercase", "Uppercase"],
@@ -3038,26 +3042,26 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
3038
3042
  ].map(([key, label]) => {
3039
3043
  const on = settings.password[key];
3040
3044
  const last = on && minPasswordLength(settings.password) === 1;
3041
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Switch, { "aria-label": label, checked: on, disabled: last, onCheckedChange: (v) => setPassword({ [key]: v }) }) }, key);
3045
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Switch, { "aria-label": label, checked: on, disabled: last, onCheckedChange: (v) => setPassword({ [key]: v }) }) }, key);
3042
3046
  }),
3043
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label: "Avoid look-alikes", hint: "No 0/O, 1/l/I", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Switch, { "aria-label": "Avoid look-alikes", checked: settings.password.excludeAmbiguous, onCheckedChange: (v) => setPassword({ excludeAmbiguous: v }) }) })
3044
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
3045
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label: "Words", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.NumberStepper, { value: settings.passphrase.wordCount, min: MIN_PASSPHRASE_WORDS, max: ceilings.maxPassphraseWords, fallback: MIN_PASSPHRASE_WORDS, onChange: (wordCount) => change({ ...settings, passphrase: { ...settings.passphrase, wordCount } }) }) }),
3046
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label: "Separator", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Select, { "aria-label": "Separator", variant: "inline", align: "end", value: settings.passphrase.separator, options: SEPARATORS, onValueChange: (separator) => change({ ...settings, passphrase: { ...settings.passphrase, separator } }) }) }),
3047
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label: "Capitalize", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Switch, { "aria-label": "Capitalize", checked: settings.passphrase.capitalize, onCheckedChange: (capitalize) => change({ ...settings, passphrase: { ...settings.passphrase, capitalize } }) }) }),
3048
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OptionLine, { label: "Add a digit", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Switch, { "aria-label": "Add a digit", checked: settings.passphrase.appendDigit, onCheckedChange: (appendDigit) => change({ ...settings, passphrase: { ...settings.passphrase, appendDigit } }) }) })
3047
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label: "Avoid look-alikes", hint: "No 0/O, 1/l/I", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Switch, { "aria-label": "Avoid look-alikes", checked: settings.password.excludeAmbiguous, onCheckedChange: (v) => setPassword({ excludeAmbiguous: v }) }) })
3048
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
3049
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label: "Words", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.NumberStepper, { value: settings.passphrase.wordCount, min: MIN_PASSPHRASE_WORDS, max: ceilings.maxPassphraseWords, fallback: MIN_PASSPHRASE_WORDS, onChange: (wordCount) => change({ ...settings, passphrase: { ...settings.passphrase, wordCount } }) }) }),
3050
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label: "Separator", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Select, { "aria-label": "Separator", variant: "inline", align: "end", value: settings.passphrase.separator, options: SEPARATORS, onValueChange: (separator) => change({ ...settings, passphrase: { ...settings.passphrase, separator } }) }) }),
3051
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label: "Capitalize", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Switch, { "aria-label": "Capitalize", checked: settings.passphrase.capitalize, onCheckedChange: (capitalize) => change({ ...settings, passphrase: { ...settings.passphrase, capitalize } }) }) }),
3052
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OptionLine, { label: "Add a digit", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Switch, { "aria-label": "Add a digit", checked: settings.passphrase.appendDigit, onCheckedChange: (appendDigit) => change({ ...settings, passphrase: { ...settings.passphrase, appendDigit } }) }) })
3049
3053
  ] }),
3050
- note ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { "aria-live": "polite", className: (0, import_design_system10.cn)("text-xs", note.tone === "destructive" ? "text-destructive" : note.tone === "success" ? "text-success" : "text-muted-foreground"), children: note.text }) : null,
3051
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Button, { variant: "primary", disabled: !candidate, onClick: use, children: useLabel })
3054
+ note ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { "aria-live": "polite", className: (0, import_design_system11.cn)("text-xs", note.tone === "destructive" ? "text-destructive" : note.tone === "success" ? "text-success" : "text-muted-foreground"), children: note.text }) : null,
3055
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Button, { variant: "primary", disabled: !candidate, onClick: use, children: useLabel })
3052
3056
  ] });
3053
3057
  }
3054
3058
  function OptionLine({ label, hint, children }) {
3055
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-h-9 items-center justify-between gap-3", children: [
3056
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-w-0 flex-col", children: [
3057
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm text-foreground", children: label }),
3058
- hint ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: hint }) : null
3059
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex min-h-9 items-center justify-between gap-3", children: [
3060
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex min-w-0 flex-col", children: [
3061
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm text-foreground", children: label }),
3062
+ hint ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: hint }) : null
3059
3063
  ] }),
3060
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex shrink-0 items-center", children })
3064
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex shrink-0 items-center", children })
3061
3065
  ] });
3062
3066
  }
3063
3067
  function GenerateButton({ onUse, label, limits, disabled }) {
@@ -3066,9 +3070,9 @@ function GenerateButton({ onUse, label, limits, disabled }) {
3066
3070
  const [open, setOpen] = (0, import_react15.useState)(false);
3067
3071
  if (!source) return null;
3068
3072
  const name = label ?? "Generate password";
3069
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_design_system10.Popover, { open, onOpenChange: setOpen, children: [
3070
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system10.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls11.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KeyGlyph, {}), "aria-label": name, title: name, disabled }) }),
3071
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system10.PopoverContent, { sizing: "content", align: "end", children: open ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3073
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system11.Popover, { open, onOpenChange: setOpen, children: [
3074
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system11.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls11.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(KeyGlyph, {}), "aria-label": name, title: name, disabled }) }),
3075
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system11.PopoverContent, { sizing: "content", align: "end", children: open ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3072
3076
  PasswordGenerator,
3073
3077
  {
3074
3078
  limits: source,
@@ -3238,23 +3242,23 @@ function startNewRestoreRun(preview) {
3238
3242
 
3239
3243
  // src/io/lines.tsx
3240
3244
  var import_react17 = require("react");
3241
- var import_design_system11 = require("@ai-matrx/design-system");
3245
+ var import_design_system12 = require("@ai-matrx/design-system");
3242
3246
  var import_controls12 = require("@ai-matrx/design-system/controls");
3243
- var import_jsx_runtime15 = require("react/jsx-runtime");
3247
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3244
3248
  function PickLine({ checked, onChange, disabled = false, title, detail, badge, tone }) {
3245
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex min-h-11 items-center gap-3 px-4 py-1", "data-clickable": disabled ? void 0 : "", children: [
3246
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system11.Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
3247
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }),
3248
- detail ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "hidden min-w-0 max-w-[45%] truncate text-xs text-muted-foreground sm:block", children: detail }) : null,
3249
- badge ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls12.Badge, { tone: tone ?? "neutral", children: badge }) : null
3249
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex min-h-11 items-center gap-3 px-4 py-1", "data-clickable": disabled ? void 0 : "", children: [
3250
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system12.Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
3251
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }),
3252
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "hidden min-w-0 max-w-[45%] truncate text-xs text-muted-foreground sm:block", children: detail }) : null,
3253
+ badge ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls12.Badge, { tone: tone ?? "neutral", children: badge }) : null
3250
3254
  ] });
3251
3255
  }
3252
3256
  function Windowed({ items, size = 150, render }) {
3253
3257
  const [all, setAll] = (0, import_react17.useState)(false);
3254
3258
  const shown = all ? items : items.slice(0, size);
3255
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
3259
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3256
3260
  shown.map(render),
3257
- items.length > shown.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "px-4 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_controls12.Button, { variant: "quiet", onClick: () => setAll(true), children: [
3261
+ items.length > shown.length ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "px-4 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_controls12.Button, { variant: "quiet", onClick: () => setAll(true), children: [
3258
3262
  "Show all ",
3259
3263
  items.length
3260
3264
  ] }) }) : null
@@ -3262,18 +3266,18 @@ function Windowed({ items, size = 150, render }) {
3262
3266
  }
3263
3267
  function More({ title = "More", children }) {
3264
3268
  const [open, setOpen] = (0, import_react17.useState)(false);
3265
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-2", children: [
3266
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls12.DisclosureHeader, { open, title, onClick: () => setOpen((o) => !o) }),
3269
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-2", children: [
3270
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls12.DisclosureHeader, { open, title, onClick: () => setOpen((o) => !o) }),
3267
3271
  open ? children : null
3268
3272
  ] });
3269
3273
  }
3270
3274
  function ErrorLine({ message: message2 }) {
3271
- return message2 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { role: "alert", className: "px-1 text-sm text-destructive", children: message2 }) : null;
3275
+ return message2 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { role: "alert", className: "px-1 text-sm text-destructive", children: message2 }) : null;
3272
3276
  }
3273
3277
  var plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
3274
3278
 
3275
3279
  // src/io/BackupPanel.tsx
3276
- var import_jsx_runtime16 = require("react/jsx-runtime");
3280
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3277
3281
  var RECORD_STATUS = {
3278
3282
  created: "Restored",
3279
3283
  replayed: "Already restored",
@@ -3285,17 +3289,17 @@ function omissionText(o) {
3285
3289
  return `${o.count ?? "Some"} ${what}: ${o.reason.replaceAll("_", " ")}`;
3286
3290
  }
3287
3291
  function Summary({ value }) {
3288
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(VaultSection, { children: [
3289
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Credentials", children: value.record_count }),
3290
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Protected fields", children: value.field_count }),
3291
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Files", children: value.attachment_count > 0 ? `${value.attachment_count} \xB7 ${(0, import_format6.formatFileSize)(value.attachment_bytes)}` : "0" })
3292
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(VaultSection, { children: [
3293
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Credentials", children: value.record_count }),
3294
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Protected fields", children: value.field_count }),
3295
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Files", children: value.attachment_count > 0 ? `${value.attachment_count} \xB7 ${(0, import_format6.formatFileSize)(value.attachment_bytes)}` : "0" })
3292
3296
  ] });
3293
3297
  }
3294
3298
  function Omissions({ omissions, accepted, onAccepted, verb }) {
3295
3299
  if (omissions.length === 0) return null;
3296
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(VaultSection, { title: "Left out", children: [
3297
- omissions.map((o, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: o.component.replaceAll("_", " "), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "block truncate text-muted-foreground", title: omissionText(o), children: omissionText(o) }) }, `${o.component}-${o.source_item_id ?? i}`)),
3298
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Switch, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
3300
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(VaultSection, { title: "Left out", children: [
3301
+ omissions.map((o, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: o.component.replaceAll("_", " "), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "block truncate text-muted-foreground", title: omissionText(o), children: omissionText(o) }) }, `${o.component}-${o.source_item_id ?? i}`)),
3302
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Switch, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
3299
3303
  ] });
3300
3304
  }
3301
3305
  function BackupPanel({ port, items, onClose, onChanged }) {
@@ -3401,21 +3405,21 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3401
3405
  const retryable = (result?.retryable_failure ?? 0) > 0;
3402
3406
  const mismatch = repeat !== "" && repeat !== passphrase;
3403
3407
  const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
3404
- const footer = mode === "backup" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3405
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
3406
- backup ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_controls13.Button, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3408
+ const footer = mode === "backup" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3409
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
3410
+ backup ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_controls13.Button, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3407
3411
  "Review ",
3408
3412
  selected.size
3409
3413
  ] })
3410
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3411
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
3412
- result ? retryable ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "primary", disabled: busy, onClick: () => void restore(), children: "Try again" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "outline", disabled: busy, onClick: () => void again(), children: "Restore again" }) : restorePreview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_controls13.Button, { variant: "primary", disabled: busy || restorePreview.omissions.length > 0 && !accepted, onClick: () => void restore(), children: [
3414
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3415
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
3416
+ result ? retryable ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "primary", disabled: busy, onClick: () => void restore(), children: "Try again" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "outline", disabled: busy, onClick: () => void again(), children: "Restore again" }) : restorePreview ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_controls13.Button, { variant: "primary", disabled: busy || restorePreview.omissions.length > 0 && !accepted, onClick: () => void restore(), children: [
3413
3417
  "Restore ",
3414
3418
  restorePreview.record_count
3415
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "primary", disabled: busy || !file || !passphrase, onClick: () => void reviewRestore(), children: "Review" })
3419
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "primary", disabled: busy || !file || !passphrase, onClick: () => void reviewRestore(), children: "Review" })
3416
3420
  ] });
3417
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Panel, { title: "Backup", size: "lg", onClose, footer, children: [
3418
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3421
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Panel, { title: "Backup", size: "lg", onClose, footer, children: [
3422
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3419
3423
  import_controls13.SegmentedControl,
3420
3424
  {
3421
3425
  value: mode,
@@ -3428,36 +3432,36 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3428
3432
  ]
3429
3433
  }
3430
3434
  ),
3431
- mode === "backup" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3432
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3435
+ mode === "backup" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3436
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3433
3437
  VaultSection,
3434
3438
  {
3435
3439
  title: `${selected.size} of ${plural(items.length, "credential")}`,
3436
- actions: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3437
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
3440
+ actions: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3441
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
3438
3442
  setBackup(null);
3439
3443
  setSelected(new Set(items.map((i) => i.id)));
3440
3444
  }, children: "All" }),
3441
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3445
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3442
3446
  setBackup(null);
3443
3447
  setSelected(/* @__PURE__ */ new Set());
3444
3448
  }, children: "None" })
3445
3449
  ] }),
3446
- children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Empty, { children: "Nothing to back up yet." }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Windowed, { items, render: (i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PickLine, { title: i.name, detail: i.definitionLabel, checked: selected.has(i.id), disabled: busy, onChange: (on) => pick(i.id, on) }, i.id) })
3450
+ children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Empty, { children: "Nothing to back up yet." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Windowed, { items, render: (i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PickLine, { title: i.name, detail: i.definitionLabel, checked: selected.has(i.id), disabled: busy, onChange: (on) => pick(i.id, on) }, i.id) })
3447
3451
  }
3448
3452
  ),
3449
- backup ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3450
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Summary, { value: backup }),
3451
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3452
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(VaultSection, { children: [
3453
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Field, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
3454
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Field, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
3453
+ backup ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3454
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Summary, { value: backup }),
3455
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3456
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(VaultSection, { children: [
3457
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Field, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
3458
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Field, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
3455
3459
  ] })
3456
3460
  ] }) : null
3457
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: !restorePreview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(VaultSection, { children: [
3458
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(VaultRow, { label: "File", ...file ? { hint: file.name } : {}, children: [
3459
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Button, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
3460
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3461
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children: !restorePreview ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(VaultSection, { children: [
3462
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(VaultRow, { label: "File", ...file ? { hint: file.name } : {}, children: [
3463
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
3464
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3461
3465
  "input",
3462
3466
  {
3463
3467
  ref: fileInput,
@@ -3471,16 +3475,16 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3471
3475
  }
3472
3476
  )
3473
3477
  ] }),
3474
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: "Passphrase", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_controls13.Field, { type: "password", value: passphrase, autoComplete: "current-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) })
3475
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3476
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Summary, { value: restorePreview }),
3477
- !result ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
3478
- !result ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "px-1 text-xs text-muted-foreground", children: "Restored items start disabled and restricted." }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultSection, { title: `${result.created} restored \xB7 ${result.replayed} already \xB7 ${result.refused} refused \xB7 ${result.retryable_failure} to retry`, children: result.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VaultRow, { label: names.get(r.source_item_id) ?? "Credential", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: r.status === "refused" || r.status === "retryable_failure" ? "text-destructive" : "text-foreground", children: [
3478
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Passphrase", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Field, { type: "password", value: passphrase, autoComplete: "current-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) })
3479
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3480
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Summary, { value: restorePreview }),
3481
+ !result ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
3482
+ !result ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "px-1 text-xs text-muted-foreground", children: "Restored items start disabled and restricted." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultSection, { title: `${result.created} restored \xB7 ${result.replayed} already \xB7 ${result.refused} refused \xB7 ${result.retryable_failure} to retry`, children: result.results.map((r) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: names.get(r.source_item_id) ?? "Credential", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: r.status === "refused" || r.status === "retryable_failure" ? "text-destructive" : "text-foreground", children: [
3479
3483
  RECORD_STATUS[r.status],
3480
3484
  r.reason === "alias_conflict" ? " \xB7 name in use" : ""
3481
3485
  ] }) }, r.source_item_id)) })
3482
3486
  ] }) }),
3483
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ErrorLine, { message: error })
3487
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorLine, { message: error })
3484
3488
  ] });
3485
3489
  }
3486
3490
 
@@ -3489,7 +3493,7 @@ var import_react19 = require("react");
3489
3493
  var import_controls14 = require("@ai-matrx/design-system/controls");
3490
3494
  var import_download2 = require("@ai-matrx/kit/download");
3491
3495
  var import_confirm_opener5 = require("@ai-matrx/kit/confirm-opener");
3492
- var import_jsx_runtime17 = require("react/jsx-runtime");
3496
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3493
3497
  var FORMATS = [
3494
3498
  { value: "matrx_login_csv_v1", label: "Matrx", hint: "Website logins, all common fields" },
3495
3499
  { value: "firefox_csv_v1", label: "Firefox", hint: "Address, username and password" },
@@ -3563,24 +3567,24 @@ function ExportPanel({ port, items, onClose }) {
3563
3567
  setBusy(false);
3564
3568
  }
3565
3569
  };
3566
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3570
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3567
3571
  Panel,
3568
3572
  {
3569
3573
  title: "Export logins",
3570
3574
  size: "lg",
3571
3575
  onClose,
3572
- footer: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3573
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls14.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
3574
- preview ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_controls14.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
3576
+ footer: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3577
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls14.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
3578
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_controls14.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
3575
3579
  "Download ",
3576
3580
  eligible
3577
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_controls14.Button, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void review(), children: [
3581
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_controls14.Button, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void review(), children: [
3578
3582
  "Review ",
3579
3583
  selected.size
3580
3584
  ] })
3581
3585
  ] }),
3582
3586
  children: [
3583
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Format", hint: format?.hint, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3587
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Format", hint: format?.hint, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3584
3588
  import_controls14.Select,
3585
3589
  {
3586
3590
  value: profile,
@@ -3592,21 +3596,21 @@ function ExportPanel({ port, items, onClose }) {
3592
3596
  }
3593
3597
  }
3594
3598
  ) }) }),
3595
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3599
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3596
3600
  VaultSection,
3597
3601
  {
3598
3602
  title: `${selected.size} of ${plural(logins.length, "login")}`,
3599
- actions: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3600
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls14.Button, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
3603
+ actions: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3604
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls14.Button, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
3601
3605
  changed();
3602
3606
  setSelected(new Set(logins.map((l) => l.id)));
3603
3607
  }, children: "All" }),
3604
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls14.Button, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3608
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls14.Button, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3605
3609
  changed();
3606
3610
  setSelected(/* @__PURE__ */ new Set());
3607
3611
  }, children: "None" })
3608
3612
  ] }),
3609
- children: logins.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Empty, { children: "No website logins to export." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3613
+ children: logins.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Empty, { children: "No website logins to export." }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3610
3614
  Windowed,
3611
3615
  {
3612
3616
  items: logins,
@@ -3614,7 +3618,7 @@ function ExportPanel({ port, items, onClose }) {
3614
3618
  const p = reasons.get(item.id);
3615
3619
  const left = p && !p.eligible;
3616
3620
  const omitted = p ? omissionText2(p.omissions) : "";
3617
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3621
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3618
3622
  PickLine,
3619
3623
  {
3620
3624
  title: item.name,
@@ -3632,7 +3636,7 @@ function ExportPanel({ port, items, onClose }) {
3632
3636
  )
3633
3637
  }
3634
3638
  ),
3635
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorLine, { message: error })
3639
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorLine, { message: error })
3636
3640
  ]
3637
3641
  }
3638
3642
  );
@@ -4150,7 +4154,7 @@ var import_zip2 = require("@zip.js/zip.js");
4150
4154
  var safe = (value) => Number.isSafeInteger(value) && value >= 0;
4151
4155
  var unsafeName = (name) => /[\\\0-\x1F\x7F-\x9F]/.test(name);
4152
4156
  function stopIfCancelled(signal) {
4153
- if (signal?.aborted) throw new Error("The archive import was cancelled.");
4157
+ if (signal?.aborted) throw new Error("The archive import was canceled.");
4154
4158
  }
4155
4159
  function hasWarnings(reader, entry) {
4156
4160
  return Boolean(reader.warnings?.length || entry.warnings?.length);
@@ -4251,7 +4255,7 @@ async function openBoundedZip(file, limits, signal) {
4251
4255
  };
4252
4256
  } catch (error) {
4253
4257
  await reader.close();
4254
- if (signal?.aborted) throw new Error("The archive import was cancelled.");
4258
+ if (signal?.aborted) throw new Error("The archive import was canceled.");
4255
4259
  throw error;
4256
4260
  }
4257
4261
  }
@@ -4290,7 +4294,7 @@ async function readDashlaneCsvArchive(file, limits, signal) {
4290
4294
  return { credentialsText, omittedCsvCount };
4291
4295
  } catch (error) {
4292
4296
  credentialsText = void 0;
4293
- if (signal?.aborted) throw new Error("The Dashlane import was cancelled.");
4297
+ if (signal?.aborted) throw new Error("The Dashlane import was canceled.");
4294
4298
  throw error;
4295
4299
  } finally {
4296
4300
  credentialsText = void 0;
@@ -4425,7 +4429,7 @@ function hasValidStructuredImportFileNotices(value, maxCount) {
4425
4429
  }
4426
4430
 
4427
4431
  // src/io/parse-source.ts
4428
- var cancelled = () => new Error("The import was cancelled.");
4432
+ var cancelled = () => new Error("The import was canceled.");
4429
4433
  function parseStructured(source, file, limits, signal) {
4430
4434
  const descriptor = structuredImportSource(source);
4431
4435
  if (!descriptor) return Promise.reject(new Error("This kind of file cannot be imported."));
@@ -4478,7 +4482,7 @@ async function parseImportFile(source, file, limits, signal) {
4478
4482
  }
4479
4483
 
4480
4484
  // src/io/ImportPanel.tsx
4481
- var import_jsx_runtime18 = require("react/jsx-runtime");
4485
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4482
4486
  var ROLE_OPTIONS = [
4483
4487
  { value: "keep", label: "Keep encrypted" },
4484
4488
  { value: "title", label: "Name" },
@@ -4653,26 +4657,26 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4653
4657
  const help = EXPORT_HELP[source];
4654
4658
  const stage = done ? "done" : draft ? "review" : "choose";
4655
4659
  const busy = running !== null || reading;
4656
- const footer = stage === "choose" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4657
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
4658
- source === "env" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
4659
- ] }) : stage === "review" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4660
- running ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "quiet", onClick: reset, children: "Back" }),
4661
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ImportGlyph, { className: "size-4" }), disabled: busy || chosen === 0 || ambiguous === true || needsApproval, onClick: start, children: running ? `Importing ${running.done} of ${running.total}` : `Import ${chosen}` })
4662
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4663
- done && done.failed.length + done.untried.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "outline", disabled: busy, onClick: retry, children: running ? `Importing ${running.done} of ${running.total}` : `Try ${done.failed.length + done.untried.length} again` }) : null,
4664
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "primary", onClick: onClose, children: "Done" })
4660
+ const footer = stage === "choose" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4661
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
4662
+ source === "env" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
4663
+ ] }) : stage === "review" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4664
+ running ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "quiet", onClick: reset, children: "Back" }),
4665
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ImportGlyph, { className: "size-4" }), disabled: busy || chosen === 0 || ambiguous === true || needsApproval, onClick: start, children: running ? `Importing ${running.done} of ${running.total}` : `Import ${chosen}` })
4666
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4667
+ done && done.failed.length + done.untried.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "outline", disabled: busy, onClick: retry, children: running ? `Importing ${running.done} of ${running.total}` : `Try ${done.failed.length + done.untried.length} again` }) : null,
4668
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "primary", onClick: onClose, children: "Done" })
4665
4669
  ] });
4666
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Panel, { title: "Import", size: "xl", onClose, footer, children: [
4667
- stage === "choose" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(VaultSection, { children: [
4668
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Source", actions: help && host.openUrl ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "quiet", onClick: () => host.openUrl?.(help), children: "How to export" }) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Select, { value: source, options: SOURCE_OPTIONS, "aria-label": "Source", onValueChange: (s) => {
4670
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Panel, { title: "Import", size: "xl", onClose, footer, children: [
4671
+ stage === "choose" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(VaultSection, { children: [
4672
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Source", actions: help && host.openUrl ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "quiet", onClick: () => host.openUrl?.(help), children: "How to export" }) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Select, { value: source, options: SOURCE_OPTIONS, "aria-label": "Source", onValueChange: (s) => {
4669
4673
  setSource(s);
4670
4674
  setError(null);
4671
4675
  } }) }),
4672
- source === "env" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: ".env text", hint: "Same key updates in place", top: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Textarea, { mono: true, rows: 7, value: envText, autoComplete: "off", spellCheck: false, "aria-label": ".env text", placeholder: "OPENAI_API_KEY=sk-...\nDATABASE_URL=postgres://...", onChange: (e) => setEnvText(e.target.value) }) }) : null,
4673
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(VaultRow, { label: "File", hint: fileName ?? void 0, children: [
4674
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "outline", disabled: reading, onClick: () => fileInput.current?.click(), children: reading ? "Reading\u2026" : source === "env" ? "Load a file" : "Choose a file" }),
4675
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4676
+ source === "env" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: ".env text", hint: "Same key updates in place", top: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Textarea, { mono: true, rows: 7, value: envText, autoComplete: "off", spellCheck: false, "aria-label": ".env text", placeholder: "OPENAI_API_KEY=sk-...\nDATABASE_URL=postgres://...", onChange: (e) => setEnvText(e.target.value) }) }) : null,
4677
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(VaultRow, { label: "File", hint: fileName ?? void 0, children: [
4678
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "outline", disabled: reading, onClick: () => fileInput.current?.click(), children: reading ? "Reading\u2026" : source === "env" ? "Load a file" : "Choose a file" }),
4679
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4676
4680
  "input",
4677
4681
  {
4678
4682
  ref: fileInput,
@@ -4687,44 +4691,44 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4687
4691
  }
4688
4692
  )
4689
4693
  ] }),
4690
- source === "env" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Sandboxes", hint: "Pass these values to agent sandboxes", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Switch, { checked: injectSandbox, onCheckedChange: setInjectSandbox, "aria-label": "Use in sandboxes" }) }) : null
4694
+ source === "env" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Sandboxes", hint: "Pass these values to agent sandboxes", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Switch, { checked: injectSandbox, onCheckedChange: setInjectSandbox, "aria-label": "Use in sandboxes" }) }) : null
4691
4695
  ] }) : null,
4692
- stage === "review" && draft ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4693
- organizations.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Save to", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Select, { value: destination, options: destinationOptions, "aria-label": "Save to", onValueChange: (v) => draft.kind === "file" ? restart({ destination: v }) : setDestination(v) }) }) }) : null,
4694
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4696
+ stage === "review" && draft ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4697
+ organizations.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Save to", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Select, { value: destination, options: destinationOptions, "aria-label": "Save to", onValueChange: (v) => draft.kind === "file" ? restart({ destination: v }) : setDestination(v) }) }) }) : null,
4698
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4695
4699
  VaultSection,
4696
4700
  {
4697
4701
  title: draft.kind === "env" ? plural(envKeys.length, "value") : summary(counts, lines.length),
4698
- actions: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4699
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "quiet", disabled: busy, onClick: () => setSelected(draft.kind === "env" ? new Set(envKeys.map((e) => e.key + e.text)) : defaultSelection(lines)), children: "Reset" }),
4700
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Button, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
4702
+ actions: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4703
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "quiet", disabled: busy, onClick: () => setSelected(draft.kind === "env" ? new Set(envKeys.map((e) => e.key + e.text)) : defaultSelection(lines)), children: "Reset" }),
4704
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Button, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
4701
4705
  ] }),
4702
- children: draft.kind === "env" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4706
+ children: draft.kind === "env" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4703
4707
  Windowed,
4704
4708
  {
4705
4709
  items: envKeys,
4706
4710
  render: (e) => {
4707
4711
  const id = e.key + e.text;
4708
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PickLine, { title: e.key, detail: "Value hidden", checked: selected.has(id), disabled: busy, onChange: (on) => toggle(setSelected, id, on) }, id);
4712
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PickLine, { title: e.key, detail: "Value hidden", checked: selected.has(id), disabled: busy, onChange: (on) => toggle(setSelected, id, on) }, id);
4709
4713
  }
4710
4714
  }
4711
- ) : lines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Empty, { children: "Nothing to import in this file." }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4715
+ ) : lines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Empty, { children: "Nothing to import in this file." }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4712
4716
  Windowed,
4713
4717
  {
4714
4718
  items: lines,
4715
4719
  render: (l) => {
4716
4720
  const badge = STATE_BADGE[l.state];
4717
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PickLine, { title: l.title, detail: l.reason ?? l.detail, checked: selected.has(l.key), disabled: busy || !selectable(l), badge: badge?.text, tone: badge?.tone, onChange: (on) => toggle(setSelected, l.key, on) }, l.key);
4721
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PickLine, { title: l.title, detail: l.reason ?? l.detail, checked: selected.has(l.key), disabled: busy || !selectable(l), badge: badge?.text, tone: badge?.tone, onChange: (on) => toggle(setSelected, l.key, on) }, l.key);
4718
4722
  }
4719
4723
  }
4720
4724
  )
4721
4725
  }
4722
4726
  ),
4723
- draft.kind === "file" && draft.parsed.kind === "structured" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Visible details", hint: "Approve saving websites and public keys in the clear", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Switch, { checked: approvedVisible, disabled: busy, onCheckedChange: setApprovedVisible, "aria-label": "Approve visible details" }) }) }) : null,
4724
- draft.kind === "file" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Notices, { draft }) : null,
4725
- draft.kind === "file" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(More, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(VaultSection, { children: [
4726
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Browser fill", hint: "Needs username, password and an https address", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_controls15.Switch, { checked: browserFill, disabled: busy, onCheckedChange: (on) => restart({ browserFill: on }), "aria-label": "Browser fill" }) }),
4727
- draft.parsed.kind === "csv" ? draft.parsed.preview.headers.map((header, index) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: header || `Column ${index + 1}`, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4727
+ draft.kind === "file" && draft.parsed.kind === "structured" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Visible details", hint: "Approve saving websites and public keys in the clear", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Switch, { checked: approvedVisible, disabled: busy, onCheckedChange: setApprovedVisible, "aria-label": "Approve visible details" }) }) }) : null,
4728
+ draft.kind === "file" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Notices, { draft }) : null,
4729
+ draft.kind === "file" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(More, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(VaultSection, { children: [
4730
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Browser fill", hint: "Needs username, password and an https address", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_controls15.Switch, { checked: browserFill, disabled: busy, onCheckedChange: (on) => restart({ browserFill: on }), "aria-label": "Browser fill" }) }),
4731
+ draft.parsed.kind === "csv" ? draft.parsed.preview.headers.map((header, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: header || `Column ${index + 1}`, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4728
4732
  import_controls15.Select,
4729
4733
  {
4730
4734
  value: draft.mapping[index] ?? "keep",
@@ -4734,20 +4738,20 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4734
4738
  }
4735
4739
  ) }, `${header}-${index}`)) : null
4736
4740
  ] }) }) : null,
4737
- ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorLine, { message: "Each of name, username, password, notes and one-time password can be used by one column." }) : null
4741
+ ambiguous ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: "Each of name, username, password, notes and one-time password can be used by one column." }) : null
4738
4742
  ] }) : null,
4739
- stage === "done" && done ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(VaultSection, { title: "Result", children: [
4740
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Created", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-foreground", children: done.created.length }) }),
4741
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Skipped", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-foreground", children: done.skipped }) }),
4742
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Failed", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: done.failed.length > 0 ? "text-destructive" : "text-foreground", children: done.failed.length }) }),
4743
- done.untried.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Not tried", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-foreground", children: done.untried.length }) }) : null,
4744
- done.withOtp > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: "Codes", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-muted-foreground", children: [
4743
+ stage === "done" && done ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(VaultSection, { title: "Result", children: [
4744
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Created", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-foreground", children: done.created.length }) }),
4745
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Skipped", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-foreground", children: done.skipped }) }),
4746
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Failed", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: done.failed.length > 0 ? "text-destructive" : "text-foreground", children: done.failed.length }) }),
4747
+ done.untried.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Not tried", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-foreground", children: done.untried.length }) }) : null,
4748
+ done.withOtp > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Codes", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-muted-foreground", children: [
4745
4749
  plural(done.withOtp, "login"),
4746
4750
  " still need the one-time password added."
4747
4751
  ] }) }) : null,
4748
- done.failed.map((f) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VaultRow, { label: f.title, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
4752
+ done.failed.map((f) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: f.title, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
4749
4753
  ] }) : null,
4750
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorLine, { message: error })
4754
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error })
4751
4755
  ] });
4752
4756
  }
4753
4757
  function toggle(set, key, on) {
@@ -4774,7 +4778,7 @@ function Notices({ draft }) {
4774
4778
  }
4775
4779
  }
4776
4780
  if (notes.length === 0) return null;
4777
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex flex-col gap-0.5 px-1 text-xs text-muted-foreground", children: notes.map((n) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: n }, n)) });
4781
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex flex-col gap-0.5 px-1 text-xs text-muted-foreground", children: notes.map((n) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: n }, n)) });
4778
4782
  }
4779
4783
 
4780
4784
  // src/ItemList.tsx
@@ -4783,20 +4787,6 @@ var import_design_system18 = require("@ai-matrx/design-system");
4783
4787
  var import_design_system19 = require("@ai-matrx/design-system");
4784
4788
  var import_controls22 = require("@ai-matrx/design-system/controls");
4785
4789
 
4786
- // src/ItemIcon.tsx
4787
- var import_design_system12 = require("@ai-matrx/design-system");
4788
- var import_jsx_runtime19 = require("react/jsx-runtime");
4789
- function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
4790
- const box = size === "lg" ? "size-10 rounded-xl text-base" : size === "md" ? "size-8 rounded-lg text-sm" : "size-6 rounded-md text-xs";
4791
- const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
4792
- const initial = siteDomainOf(site).charAt(0).toUpperCase();
4793
- if (initial && /[A-Z0-9]/.test(initial)) {
4794
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: (0, import_design_system12.cn)("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
4795
- }
4796
- const Glyph = definitionGlyph(definitionKey, family);
4797
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: (0, import_design_system12.cn)("flex shrink-0 items-center justify-center", box, tone), children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Glyph, { className: size === "lg" ? "size-5" : "size-4" }) });
4798
- }
4799
-
4800
4790
  // src/ItemPane.tsx
4801
4791
  var import_react27 = require("react");
4802
4792
  var import_design_system16 = require("@ai-matrx/design-system");
@@ -5719,6 +5709,8 @@ function ItemPane(props) {
5719
5709
  const [tagsText, setTagsText] = (0, import_react27.useState)(base.tags.join(", "));
5720
5710
  const [showTags, setShowTags] = (0, import_react27.useState)(false);
5721
5711
  const [showDescription, setShowDescription] = (0, import_react27.useState)(false);
5712
+ const [showRecovery, setShowRecovery] = (0, import_react27.useState)(false);
5713
+ const [settingUpOtp, setSettingUpOtp] = (0, import_react27.useState)(false);
5722
5714
  const [owner, setOwner] = (0, import_react27.useState)(defaultOwner ?? "me");
5723
5715
  const [recipient, setRecipient] = (0, import_react27.useState)("");
5724
5716
  const [generatePrivately, setGeneratePrivately] = (0, import_react27.useState)(false);
@@ -5747,6 +5739,7 @@ function ItemPane(props) {
5747
5739
  setLoaded({});
5748
5740
  setShowTags(false);
5749
5741
  setShowDescription(false);
5742
+ setShowRecovery(false);
5750
5743
  }, [item?.id, mode]);
5751
5744
  (0, import_react27.useEffect)(() => onDirty(dirty), [dirty, onDirty]);
5752
5745
  const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
@@ -5774,23 +5767,24 @@ function ItemPane(props) {
5774
5767
  setBusy(true);
5775
5768
  setError(null);
5776
5769
  const out = { ...draft, values: Object.fromEntries(changedValues(base, draft)) };
5770
+ if (out.values[RECOVERY_CODES_KEY] !== void 0) out.values[RECOVERY_CODES_KEY] = parseRecoveryCodes(out.values[RECOVERY_CODES_KEY]).join("\n");
5777
5771
  const withOtp = async (saved, notice2) => {
5778
5772
  const setup = out.authenticator.trim();
5779
- if (!setup) return [saved, notice2];
5773
+ if (!setup) return [saved, notice2, false];
5780
5774
  try {
5781
5775
  await confirmed(() => port.authenticator.enrol(saved, setup));
5782
- return [await port.get(saved), notice2];
5776
+ return [await port.get(saved), notice2, true];
5783
5777
  } catch (e) {
5784
5778
  const why = wasCancelled(e) ? "it was not confirmed" : e instanceof Error ? e.message : "it was refused";
5785
- return [saved, `Saved, but the one-time password was not set up: ${why}. Add it with Edit.`];
5779
+ return [saved, `Saved, but the authenticator was not set up: ${why}. Add it with Edit.`, false];
5786
5780
  }
5787
5781
  };
5788
5782
  try {
5789
5783
  if (item) {
5790
5784
  const r = await port.save(item, definition, out);
5791
- const [next, n] = await withOtp(r.item, r.notice);
5785
+ const [next, n, enrolled] = await withOtp(r.item, r.notice);
5792
5786
  (0, import_drafts.dropKeptDraft)(keepKey);
5793
- onSaved(next, false, n);
5787
+ onSaved(next, false, n, enrolled);
5794
5788
  } else if (definition && owner === "person") {
5795
5789
  const secretKey = declared.find((f) => f.storage === "encrypted" && f.kind === "secret")?.key ?? null;
5796
5790
  const r = await port.sharing.assign({ recipientEmail: recipient.trim(), definition, draft: out, generateFieldKey: generatePrivately ? secretKey : null });
@@ -5798,9 +5792,9 @@ function ItemPane(props) {
5798
5792
  onCancelNew();
5799
5793
  } else if (definition) {
5800
5794
  const r = await port.create(definition, owner === "me" ? null : owner, out);
5801
- const [next, n] = await withOtp(r.item, r.notice);
5795
+ const [next, n, enrolled] = await withOtp(r.item, r.notice);
5802
5796
  (0, import_drafts.dropKeptDraft)(keepKey);
5803
- onSaved(next, true, n);
5797
+ onSaved(next, true, n, enrolled);
5804
5798
  }
5805
5799
  } catch (e) {
5806
5800
  setError(e instanceof Error ? e.message : "Saving failed. Nothing was changed.");
@@ -5859,6 +5853,10 @@ function ItemPane(props) {
5859
5853
  const viewCustom = (item?.custom ?? []).filter((c) => !declaredKeys.has(c.key) && c.key !== MFA_METHOD_KEY && !isAuthenticatorStorage(c.key));
5860
5854
  const isLogin = (item?.definitionKey ?? definition?.key) === "website_login" || (item?.definitionKey ?? definition?.key) === "username_password";
5861
5855
  const typeLabel = definition?.label ?? item?.definitionLabel ?? "";
5856
+ const recoveryDeclared = declaredKeys.has(RECOVERY_CODES_KEY);
5857
+ const recoveryExtra = extraFields.some(isRecoveryCodesField);
5858
+ const offerRecovery = isLogin || Boolean(item?.authenticator) || draft.authenticator.trim() !== "";
5859
+ const recoveryEditing = !recoveryDeclared && !recoveryExtra && (showRecovery || (draft.values[RECOVERY_CODES_KEY] ?? "") !== "");
5862
5860
  const shownOwner = mode === "new" ? owner === "me" ? { label: "Personal", glyph: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PersonGlyph, {}) } : owner === "person" ? { label: "Someone else", glyph: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SendGlyph, {}) } : { label: organizations.find((o) => o.id === owner)?.name ?? "Organization", glyph: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(BuildingGlyph, {}) } : item ? { label: item.owner.label, glyph: item.owner.kind === "me" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PersonGlyph, {}) : item.owner.kind === "organization" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(BuildingGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_icons11.UsersIcon, {}) } : null;
5863
5861
  const sameNamed = (id) => {
5864
5862
  const name = organizations.find((o) => o.id === id)?.name.trim().toLowerCase();
@@ -6006,7 +6004,8 @@ function ItemPane(props) {
6006
6004
  label: f.label,
6007
6005
  value: draft.values[f.key] ?? "",
6008
6006
  placeholder: f.handling === "visible" || f.key in loaded ? "" : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
6009
- secret: f.handling !== "visible",
6007
+ secret: f.handling !== "visible" && !isRecoveryCodesField(f),
6008
+ multiline: isRecoveryCodesField(f),
6010
6009
  onChange: (v) => setValue(f.key, v),
6011
6010
  onRemove: () => patch({ removed: [...draft.removed, f.id] }),
6012
6011
  removeLabel: `Delete ${f.label}`,
@@ -6043,7 +6042,27 @@ function ItemPane(props) {
6043
6042
  )),
6044
6043
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AddRow, { label: draft.loginUrls.length === 0 ? "Website" : "", onAdd: () => patch({ loginUrls: [...draft.loginUrls, ""] }), children: draft.loginUrls.length === 0 ? "Add website" : "Add another website" }),
6045
6044
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }),
6046
- item && isLogin && !item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TwoFactorMethodRow, { port, item, onChanged }) : null
6045
+ item && isLogin && !item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TwoFactorMethodRow, { port, item, onChanged }) : null,
6046
+ recoveryEditing ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6047
+ EditRow,
6048
+ {
6049
+ label: "Recovery codes",
6050
+ value: draft.values[RECOVERY_CODES_KEY] ?? "",
6051
+ multiline: true,
6052
+ autoFocus: showRecovery && (draft.values[RECOVERY_CODES_KEY] ?? "") === "",
6053
+ placeholder: "Backup codes, one per line",
6054
+ onChange: (v) => setValue(RECOVERY_CODES_KEY, v),
6055
+ onRemove: () => {
6056
+ setShowRecovery(false);
6057
+ setDraft((d) => {
6058
+ const values = { ...d.values };
6059
+ delete values[RECOVERY_CODES_KEY];
6060
+ return { ...d, values };
6061
+ });
6062
+ },
6063
+ removeLabel: "Remove recovery codes"
6064
+ }
6065
+ ) : !recoveryDeclared && !recoveryExtra && offerRecovery ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AddRow, { label: "Recovery codes", onAdd: () => setShowRecovery(true), children: "Add backup codes" }) : null
6047
6066
  ] }),
6048
6067
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(VaultSection, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(EditRow, { label: "Notes", value: draft.notes, multiline: true, placeholder: "Anything worth remembering", onChange: (v) => patch({ notes: v }) }) }),
6049
6068
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
@@ -6056,7 +6075,8 @@ function ItemPane(props) {
6056
6075
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system16.DropdownMenuItem, { onSelect: () => patch({ secrets: [...draft.secrets, { key: "", label: "", value: "" }] }), children: "Secret field" }),
6057
6076
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system16.DropdownMenuItem, { onSelect: () => patch({ custom: [...draft.custom, { key: `custom_${draft.custom.length + 1}_${Date.now() % 1e5}`, label: "", value: "" }] }), children: "Text field" }),
6058
6077
  !tagsVisible ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system16.DropdownMenuItem, { onSelect: () => setShowTags(true), children: "Tags" }) : null,
6059
- !descriptionVisible ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system16.DropdownMenuItem, { onSelect: () => setShowDescription(true), children: "Description" }) : null
6078
+ !descriptionVisible ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system16.DropdownMenuItem, { onSelect: () => setShowDescription(true), children: "Description" }) : null,
6079
+ !recoveryDeclared && !recoveryExtra && !recoveryEditing ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_design_system16.DropdownMenuItem, { onSelect: () => setShowRecovery(true), children: "Recovery codes" }) : null
6060
6080
  ] })
6061
6081
  ] }),
6062
6082
  children: [
@@ -6096,8 +6116,9 @@ function ItemPane(props) {
6096
6116
  item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TotpRow, { port, item, onChanged }) : seedField && !seedField.active ? (
6097
6117
  // The secret is still there, only off: turning it on brings the codes back. Never "Set up",
6098
6118
  // which would invite replacing a working two-factor secret.
6099
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(VaultRow, { label: "One-time password", actions: canTurnOn ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TurnOnButton, { label: "One-time password", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-muted-foreground", children: "Turned off" }) })
6100
- ) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AddRow, { label: "One-time password", onAdd: canEdit ? () => onMode("edit") : null, children: "Set up" }),
6119
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(VaultRow, { label: "Authenticator", actions: canTurnOn ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TurnOnButton, { label: "Authenticator", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-muted-foreground", children: "Turned off" }) })
6120
+ ) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AddRow, { label: "Authenticator", onAdd: canEdit ? () => setSettingUpOtp(true) : null, children: "Set up" }),
6121
+ !recoveryDeclared && !recoveryExtra && offerRecovery ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(RecoveryCodesRow, { item, field: null, port, onChanged }) : null,
6101
6122
  !item.authenticator && item.custom.some((c) => c.key === MFA_METHOD_KEY && c.value) ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TwoFactorMethodRow, { port, item, onChanged }) : null
6102
6123
  ] }),
6103
6124
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(VaultSection, { children: item.notes ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PlainRow, { label: "Notes", value: item.notes, multiline: true }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AddRow, { label: "Notes", onAdd: canEdit ? () => onMode("edit") : null, children: "Add notes" }) }),
@@ -6113,7 +6134,22 @@ function ItemPane(props) {
6113
6134
  " \xB7 Created ",
6114
6135
  (0, import_format8.formatAbsoluteDate)(item.createdAt, DAY_FORMAT, "")
6115
6136
  ] })
6116
- ] }) : null }) })
6137
+ ] }) : null }) }),
6138
+ settingUpOtp && item ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6139
+ AddOtpPanel,
6140
+ {
6141
+ port,
6142
+ items: [item],
6143
+ definitions: definition ? [definition] : [],
6144
+ itemId: item.id,
6145
+ onClose: () => setSettingUpOtp(false),
6146
+ onChanged: () => onChanged(),
6147
+ onDone: () => {
6148
+ setSettingUpOtp(false);
6149
+ onChanged();
6150
+ }
6151
+ }
6152
+ ) : null
6117
6153
  ] }) });
6118
6154
  }
6119
6155
 
@@ -7051,7 +7087,7 @@ var plural2 = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
7051
7087
  function terminalMessage(s) {
7052
7088
  if (s.phase === "completed") return `Imported ${plural2(s.committed, "passkey")}.`;
7053
7089
  if (s.phase === "partial") return `${s.committed} imported; ${s.unsupported + s.failed + s.uncertain + s.not_attempted} need attention.`;
7054
- if (s.phase === "cancelled") return "Import cancelled. Nothing else will be imported.";
7090
+ if (s.phase === "cancelled") return "Import canceled. Nothing else will be imported.";
7055
7091
  if (s.phase === "unavailable") return "Passkey import is unavailable here.";
7056
7092
  if (s.phase === "failed") return "The import did not finish. Recover it before using a new file.";
7057
7093
  return s.message;
@@ -7236,7 +7272,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7236
7272
  present(g, { kind: "status", status: result, message: terminalMessage(result) });
7237
7273
  if (!done && ACTIVE_PHASES.has(result.phase)) schedulePoll(active.id);
7238
7274
  } catch (e) {
7239
- keepOperation(g, fail(e, "The import could not be cancelled."));
7275
+ keepOperation(g, fail(e, "The import could not be canceled."));
7240
7276
  } finally {
7241
7277
  if (current(g)) setBusy(false);
7242
7278
  }
@@ -7722,6 +7758,7 @@ var import_design_system24 = require("@ai-matrx/design-system");
7722
7758
  var import_controls33 = require("@ai-matrx/design-system/controls");
7723
7759
  var import_icons15 = require("@ai-matrx/design-system/icons");
7724
7760
  var import_jsx_runtime38 = require("react/jsx-runtime");
7761
+ var AUTHENTICATOR_LABEL = "Authenticator";
7725
7762
  function vaultChoices(items, organizations, current) {
7726
7763
  const n = (f) => items.filter(f).length;
7727
7764
  const shared = n((i) => i.owner.kind === "shared");
@@ -7761,7 +7798,7 @@ function buildViews(items, definitions, favoriteIds2, recentIds2) {
7761
7798
  function viewLabel(view, views) {
7762
7799
  if (view === "favorites") return "Favorites";
7763
7800
  if (view === "recent") return "Recently opened";
7764
- if (view === "authenticator") return "One-time passwords";
7801
+ if (view === "authenticator") return AUTHENTICATOR_LABEL;
7765
7802
  if (view === "trash") return "Trash";
7766
7803
  if (view === "page") return "This page";
7767
7804
  return [...views.categories, ...views.tags].find((v) => v.view === view)?.label ?? "All items";
@@ -7772,7 +7809,7 @@ function viewOptions(views, allCount, deletedCount, hasFavorites, hasRecents, ha
7772
7809
  { value: "all", label: "Every category", meta: String(allCount) },
7773
7810
  ...hasFavorites ? [{ value: "favorites", label: "Favorites", meta: views.favorites === void 0 ? "" : String(views.favorites) }] : [],
7774
7811
  ...hasRecents ? [{ value: "recent", label: "Recently opened", meta: views.recent === void 0 ? "" : String(views.recent) }] : [],
7775
- { value: "authenticator", label: "One-time passwords", meta: String(views.authenticator) },
7812
+ { value: "authenticator", label: AUTHENTICATOR_LABEL, meta: String(views.authenticator) },
7776
7813
  ...views.categories.map((v) => ({ value: v.view, label: v.label, meta: String(v.count) })),
7777
7814
  ...views.tags.map((v) => ({ value: v.view, label: `#${v.label}`, meta: String(v.count) })),
7778
7815
  { value: "trash", label: "Trash", meta: String(deletedCount) }
@@ -7814,7 +7851,7 @@ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, v
7814
7851
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Entry, { on: view === "all", glyph: GridGlyph, label: "All items", count: allCount, onClick: () => onView("all") }),
7815
7852
  hasFavorites ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Entry, { on: view === "favorites", glyph: StarGlyph, label: "Favorites", count: views.favorites, onClick: () => onView("favorites") }) : null,
7816
7853
  hasRecents ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Entry, { on: view === "recent", glyph: ClockGlyph, label: "Recently opened", count: views.recent, onClick: () => onView("recent") }) : null,
7817
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Entry, { on: view === "authenticator", glyph: LockGlyph, label: "One-time passwords", count: views.authenticator, onClick: () => onView("authenticator") })
7854
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Entry, { on: view === "authenticator", glyph: LockGlyph, label: AUTHENTICATOR_LABEL, count: views.authenticator, onClick: () => onView("authenticator") })
7818
7855
  ] }),
7819
7856
  views.categories.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Group, { title: "Categories", children: views.categories.map((e) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Entry, { on: view === e.view, glyph: e.glyph, label: e.label, count: e.count, onClick: () => onView(e.view) }, e.view)) }) : null,
7820
7857
  views.tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
@@ -7921,6 +7958,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7921
7958
  const loc = location ?? inner;
7922
7959
  const dirty = (0, import_react41.useRef)(false);
7923
7960
  const [notice, setNotice] = (0, import_react41.useState)(null);
7961
+ const [firstCode, setFirstCode] = (0, import_react41.useState)(null);
7924
7962
  const move = (0, import_react41.useCallback)(
7925
7963
  (next, how) => {
7926
7964
  if (!location) setInner(next);
@@ -8060,9 +8098,21 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8060
8098
  }
8061
8099
  const hasFavorites = port.lists.favorites !== void 0;
8062
8100
  const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
8063
- const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] gap-2", children: [
8101
+ const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_auto] gap-2", children: [
8064
8102
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8065
8103
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_controls34.Select, { value: loc.view, onValueChange: (view) => go({ view, itemId: null, panel: null }), options: viewOptions(views, inThisVault.length, deleted.length, hasFavorites, port.lists.recents !== void 0, host.page !== void 0), "aria-label": "Show" }),
8104
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
8105
+ import_controls34.Button,
8106
+ {
8107
+ variant: loc.view === "authenticator" ? "outline" : "quiet",
8108
+ icon: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(LockGlyph, {}),
8109
+ "aria-label": AUTHENTICATOR_LABEL,
8110
+ "aria-pressed": loc.view === "authenticator",
8111
+ title: AUTHENTICATOR_LABEL,
8112
+ onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null }),
8113
+ children: mode === "split" ? AUTHENTICATOR_LABEL : null
8114
+ }
8115
+ ),
8066
8116
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_design_system25.DropdownMenu, { children: [
8067
8117
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_design_system25.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_controls34.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
8068
8118
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_design_system25.DropdownMenuContent, { align: "end", children: [
@@ -8163,9 +8213,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8163
8213
  seed: { name: guess?.name ?? "", url: guess?.url ?? loc.url.trim() },
8164
8214
  onBack: () => go({ kind: null }),
8165
8215
  onMode: () => void 0,
8166
- onSaved: (item, _wasNew, n) => {
8216
+ onSaved: (item, _wasNew, n, enrolled) => {
8167
8217
  replaceItem(item);
8168
8218
  dirty.current = false;
8219
+ if (enrolled) setFirstCode(item);
8169
8220
  move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", panel: null }, "replace");
8170
8221
  setNotice(n);
8171
8222
  notify("Saved");
@@ -8194,9 +8245,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8194
8245
  onBack: back,
8195
8246
  onMode: (m) => go({ mode: m, panel: null }),
8196
8247
  notice,
8197
- onSaved: (item, _wasNew, n) => {
8248
+ onSaved: (item, _wasNew, n, enrolled) => {
8198
8249
  replaceItem(item);
8199
8250
  dirty.current = false;
8251
+ if (enrolled) setFirstCode(item);
8200
8252
  move({ ...loc, mode: "view" }, "replace");
8201
8253
  setNotice(n);
8202
8254
  notify("Saved");
@@ -8294,7 +8346,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8294
8346
  ) : null,
8295
8347
  shown.list ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: specialView && mode !== "stack" ? "col-span-2 min-h-0" : "min-h-0", children: list }) : null,
8296
8348
  shown.detail ? detail : null,
8297
- panel
8349
+ panel,
8350
+ firstCode ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(FirstCodePanel, { port, item: firstCode, onClose: () => setFirstCode(null), onDone: () => setFirstCode(null) }) : null
8298
8351
  ] })
8299
8352
  ] });
8300
8353
  }
@@ -9065,7 +9118,9 @@ function createApiVaultPort(api, options = {}) {
9065
9118
  non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
9066
9119
  fields: [
9067
9120
  ...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
9068
- ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map((s) => ({ field_key: fieldKeyOf(s.label), value: s.value, handling: "revealable" }))
9121
+ ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map((s) => ({ field_key: fieldKeyOf(s.label), value: s.value, handling: "revealable" })),
9122
+ // Backup codes on a type that does not declare them (a website login): their own encrypted field.
9123
+ ...!declared.some((f) => f.key === RECOVERY_CODES_KEY) && (draft.values[RECOVERY_CODES_KEY] ?? "").trim() !== "" ? [{ field_key: RECOVERY_CODES_KEY, value: parseRecoveryCodes(draft.values[RECOVERY_CODES_KEY] ?? "").join("\n"), handling: "revealable", description: "One-time account recovery codes" }] : []
9069
9124
  ]
9070
9125
  }
9071
9126
  }, organizationId ? { organizationId } : void 0);