@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.js CHANGED
@@ -871,9 +871,11 @@ function inView(item, view) {
871
871
  if (view.startsWith("tag:")) return item.tags.includes(view.slice(4));
872
872
  return true;
873
873
  }
874
+ var AUTHENTICATOR_WORDS = ["authenticator", "2fa", "two-factor", "two factor", "totp", "one-time password", "verification code", "2-step"];
874
875
  function matchesQuery(item, query) {
875
876
  const q = query.trim().toLowerCase();
876
877
  if (!q) return true;
878
+ if (item.authenticator && AUTHENTICATOR_WORDS.some((w) => w === q || q.length >= 3 && w.startsWith(q))) return true;
877
879
  return [item.name, item.definitionLabel, ...item.tags, ...item.loginUrls, ...item.custom.map((c) => `${c.label} ${c.value}`)].some((h) => h.toLowerCase().includes(q));
878
880
  }
879
881
  var HIDDEN_ITEM_TAG = "matrx:hidden";
@@ -1499,9 +1501,23 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
1499
1501
  // src/authenticator/AuthenticatorView.tsx
1500
1502
  import { useEffect as useEffect11, useState as useState13 } from "react";
1501
1503
  import { Button as Button9 } from "@ai-matrx/design-system/controls";
1502
- import { cn as cn6, DropdownMenu as DropdownMenu2, DropdownMenuContent as DropdownMenuContent2, DropdownMenuItem as DropdownMenuItem2, DropdownMenuSeparator as DropdownMenuSeparator2, DropdownMenuTrigger as DropdownMenuTrigger2, TextInputDialog } from "@ai-matrx/design-system";
1504
+ import { cn as cn7, DropdownMenu as DropdownMenu2, DropdownMenuContent as DropdownMenuContent2, DropdownMenuItem as DropdownMenuItem2, DropdownMenuSeparator as DropdownMenuSeparator2, DropdownMenuTrigger as DropdownMenuTrigger2, TextInputDialog } from "@ai-matrx/design-system";
1503
1505
  import { CopyIcon as CopyIcon4 } from "@ai-matrx/design-system/icons";
1504
1506
 
1507
+ // src/ItemIcon.tsx
1508
+ import { cn as cn5 } from "@ai-matrx/design-system";
1509
+ import { jsx as jsx8 } from "react/jsx-runtime";
1510
+ function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
1511
+ const box = size === "lg" ? "size-10 rounded-xl text-base" : size === "md" ? "size-8 rounded-lg text-sm" : "size-6 rounded-md text-xs";
1512
+ const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
1513
+ const initial = siteDomainOf(site).charAt(0).toUpperCase();
1514
+ if (initial && /[A-Z0-9]/.test(initial)) {
1515
+ return /* @__PURE__ */ jsx8("span", { "aria-hidden": true, className: cn5("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
1516
+ }
1517
+ const Glyph = definitionGlyph(definitionKey, family);
1518
+ return /* @__PURE__ */ jsx8("span", { "aria-hidden": true, className: cn5("flex shrink-0 items-center justify-center", box, tone), children: /* @__PURE__ */ jsx8(Glyph, { className: size === "lg" ? "size-5" : "size-4" }) });
1519
+ }
1520
+
1505
1521
  // src/authenticator/AddOtpPanel.tsx
1506
1522
  import { useState as useState12 } from "react";
1507
1523
  import { Button as Button8, Field as Field4, Select as Select2 } from "@ai-matrx/design-system/controls";
@@ -1509,7 +1525,7 @@ import { Button as Button8, Field as Field4, Select as Select2 } from "@ai-matrx
1509
1525
  // src/authenticator/CodeParts.tsx
1510
1526
  import { useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
1511
1527
  import { Button as Button5 } from "@ai-matrx/design-system/controls";
1512
- import { cn as cn5 } from "@ai-matrx/design-system";
1528
+ import { cn as cn6 } from "@ai-matrx/design-system";
1513
1529
  import { CopyIcon as CopyIcon3 } from "@ai-matrx/design-system/icons";
1514
1530
  import { groupCode as groupCode2 } from "@ai-matrx/kit/format";
1515
1531
 
@@ -1612,16 +1628,16 @@ function matchIssuerToSite(issuer, candidates, siteOf) {
1612
1628
  }
1613
1629
 
1614
1630
  // src/authenticator/CodeParts.tsx
1615
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1631
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1616
1632
  function CountdownRing({ remaining, period, loading = false, className }) {
1617
1633
  const r = 9.5;
1618
1634
  const c = 2 * Math.PI * r;
1619
1635
  const p = loading ? 0 : Math.max(0, Math.min(1, remaining / period));
1620
1636
  const urgent = !loading && remaining <= 5;
1621
- return /* @__PURE__ */ jsxs8("span", { className: cn5("relative inline-flex size-6 shrink-0 items-center justify-center", className), role: "img", "aria-label": loading ? "Getting code" : `${remaining} seconds left`, children: [
1637
+ return /* @__PURE__ */ jsxs8("span", { className: cn6("relative inline-flex size-6 shrink-0 items-center justify-center", className), role: "img", "aria-label": loading ? "Getting code" : `${remaining} seconds left`, children: [
1622
1638
  /* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 24 24", className: "absolute inset-0 size-full -rotate-90", "aria-hidden": true, children: [
1623
- /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r, fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "text-muted" }),
1624
- /* @__PURE__ */ jsx8(
1639
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r, fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "text-muted" }),
1640
+ /* @__PURE__ */ jsx9(
1625
1641
  "circle",
1626
1642
  {
1627
1643
  cx: "12",
@@ -1638,7 +1654,7 @@ function CountdownRing({ remaining, period, loading = false, className }) {
1638
1654
  }
1639
1655
  )
1640
1656
  ] }),
1641
- /* @__PURE__ */ jsx8("span", { className: cn5("text-[10px] font-semibold leading-none tabular-nums", urgent ? "text-destructive" : "text-muted-foreground"), children: loading ? "" : remaining })
1657
+ /* @__PURE__ */ jsx9("span", { className: cn6("text-[10px] font-semibold leading-none tabular-nums", urgent ? "text-destructive" : "text-muted-foreground"), children: loading ? "" : remaining })
1642
1658
  ] });
1643
1659
  }
1644
1660
  function useCopied() {
@@ -1668,17 +1684,17 @@ function CodeButton({ live, digits, size = "md", label }) {
1668
1684
  onClick: () => live.code && void copy(live.code),
1669
1685
  "aria-label": live.code ? `Copy ${label} code` : `Getting ${label} code`,
1670
1686
  title: live.code ? "Copy" : void 0,
1671
- className: cn5("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"),
1687
+ className: cn6("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"),
1672
1688
  children: [
1673
- /* @__PURE__ */ jsx8("span", { className: cn5("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 }),
1674
- copied ? /* @__PURE__ */ jsx8("span", { className: "inline-flex size-6 items-center justify-center text-success", role: "status", "aria-label": "Copied", children: /* @__PURE__ */ jsx8(CheckGlyph, { className: "size-4" }) }) : /* @__PURE__ */ jsx8(CountdownRing, { remaining: live.remaining, period: live.period, loading: !live.code })
1689
+ /* @__PURE__ */ jsx9("span", { className: cn6("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 }),
1690
+ copied ? /* @__PURE__ */ jsx9("span", { className: "inline-flex size-6 items-center justify-center text-success", role: "status", "aria-label": "Copied", children: /* @__PURE__ */ jsx9(CheckGlyph, { className: "size-4" }) }) : /* @__PURE__ */ jsx9(CountdownRing, { remaining: live.remaining, period: live.period, loading: !live.code })
1675
1691
  ]
1676
1692
  }
1677
1693
  );
1678
1694
  }
1679
1695
  function CopyCodeButton({ live, label }) {
1680
1696
  const { copied, copy } = useCopied();
1681
- return /* @__PURE__ */ jsx8(Button5, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx8(CheckGlyph, {}) : /* @__PURE__ */ jsx8(CopyIcon3, {}), ...copied ? { glyphTone: "success" } : {}, "aria-label": `Copy ${label} code`, disabled: !live.code, onClick: () => live.code && void copy(live.code) });
1697
+ return /* @__PURE__ */ jsx9(Button5, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx9(CheckGlyph, {}) : /* @__PURE__ */ jsx9(CopyIcon3, {}), ...copied ? { glyphTone: "success" } : {}, "aria-label": `Copy ${label} code`, disabled: !live.code, onClick: () => live.code && void copy(live.code) });
1682
1698
  }
1683
1699
 
1684
1700
  // src/authenticator/live-code.ts
@@ -1770,42 +1786,23 @@ import { useEffect as useEffect10, useRef as useRef8, useState as useState10 } f
1770
1786
  import { Button as Button6, Field as Field3 } from "@ai-matrx/design-system/controls";
1771
1787
 
1772
1788
  // src/authenticator/qr.ts
1773
- function detectorCtor() {
1774
- const c = globalThis.BarcodeDetector;
1775
- return typeof c === "function" ? c : null;
1776
- }
1777
- var support = null;
1789
+ import { decodeQrFromElement, decodeQrFromImageFile } from "@ai-matrx/capture/qr";
1778
1790
  function canDecodeQrLocally() {
1779
- if (!support) {
1780
- const C = detectorCtor();
1781
- support = !C ? Promise.resolve(false) : C.getSupportedFormats ? C.getSupportedFormats().then((f) => f.includes("qr_code"), () => false) : Promise.resolve(true);
1791
+ if (typeof document === "undefined") return Promise.resolve(false);
1792
+ try {
1793
+ return Promise.resolve(Boolean(document.createElement("canvas").getContext("2d")));
1794
+ } catch {
1795
+ return Promise.resolve(false);
1782
1796
  }
1783
- return support;
1784
1797
  }
1785
1798
  function hasCamera() {
1786
1799
  return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
1787
1800
  }
1788
- var detector = null;
1789
- function qrDetector() {
1790
- const C = detectorCtor();
1791
- if (!C) return null;
1792
- detector ??= new C({ formats: ["qr_code"] });
1793
- return detector;
1794
- }
1795
1801
  async function decodeQr(source) {
1796
- const d = qrDetector();
1797
- if (!d) return null;
1798
- const found = await d.detect(source);
1799
- return found.find((b) => b.rawValue)?.rawValue ?? null;
1802
+ return decodeQrFromElement(source);
1800
1803
  }
1801
1804
  async function decodeQrFile(file) {
1802
- if (!await canDecodeQrLocally()) return null;
1803
- const bitmap = await createImageBitmap(file);
1804
- try {
1805
- return await decodeQr(bitmap);
1806
- } finally {
1807
- bitmap.close();
1808
- }
1805
+ return decodeQrFromImageFile(file);
1809
1806
  }
1810
1807
  function imageFrom(data) {
1811
1808
  if (!data) return null;
@@ -1820,7 +1817,7 @@ function imageFrom(data) {
1820
1817
  }
1821
1818
 
1822
1819
  // src/authenticator/Setup.tsx
1823
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1820
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1824
1821
  function useCanScan() {
1825
1822
  const [ok, setOk] = useState10(false);
1826
1823
  useEffect10(() => {
@@ -1886,15 +1883,15 @@ function QrScanPanel({ open, onResult, onClose }) {
1886
1883
  stop();
1887
1884
  };
1888
1885
  }, [open]);
1889
- return /* @__PURE__ */ jsxs9(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */ jsx9(Button6, { variant: "quiet", onClick: onClose, children: "Cancel" }), children: [
1886
+ return /* @__PURE__ */ jsxs9(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */ jsx10(Button6, { variant: "quiet", onClick: onClose, children: "Cancel" }), children: [
1890
1887
  /* @__PURE__ */ jsxs9("div", { className: "relative mx-auto aspect-square w-full max-w-sm overflow-hidden rounded-xl bg-muted", children: [
1891
- /* @__PURE__ */ jsx9("video", { ref: video, className: "size-full object-cover", playsInline: true, muted: true, "aria-label": "Camera" }),
1892
- error ? /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-destructive", children: error }) : /* @__PURE__ */ jsx9("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
1888
+ /* @__PURE__ */ jsx10("video", { ref: video, className: "size-full object-cover", playsInline: true, muted: true, "aria-label": "Camera" }),
1889
+ error ? /* @__PURE__ */ jsx10("div", { className: "absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-destructive", children: error }) : /* @__PURE__ */ jsx10("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
1893
1890
  ] }),
1894
- /* @__PURE__ */ jsx9("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
1891
+ /* @__PURE__ */ jsx10("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
1895
1892
  ] });
1896
1893
  }
1897
- function OtpSetupRow({ value, onChange, label = "One-time password", placeholder = "Setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
1894
+ function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
1898
1895
  const file = useRef8(null);
1899
1896
  const canScan = useCanScan();
1900
1897
  const [scanning, setScanning] = useState10(false);
@@ -1927,9 +1924,9 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1927
1924
  setReading(false);
1928
1925
  }
1929
1926
  };
1930
- const end = status ? /* @__PURE__ */ jsx9("span", { className: "max-w-48 truncate text-xs text-muted-foreground", children: status }) : reading ? /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : imageError || error ? /* @__PURE__ */ jsx9("span", { className: "max-w-56 truncate text-xs text-destructive", title: imageError ?? error ?? void 0, role: "alert", children: imageError ?? error }) : parsed ? /* @__PURE__ */ jsxs9("span", { className: "flex max-w-56 items-center gap-1 text-xs text-success", children: [
1931
- /* @__PURE__ */ jsx9(CheckGlyph, { className: "size-3.5 shrink-0" }),
1932
- /* @__PURE__ */ jsx9("span", { className: "truncate", children: describeEnrollment(parsed) })
1927
+ const end = status ? /* @__PURE__ */ jsx10("span", { className: "max-w-48 truncate text-xs text-muted-foreground", children: status }) : reading ? /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : imageError || error ? /* @__PURE__ */ jsx10("span", { className: "max-w-56 truncate text-xs text-destructive", title: imageError ?? error ?? void 0, role: "alert", children: imageError ?? error }) : parsed ? /* @__PURE__ */ jsxs9("span", { className: "flex max-w-56 items-center gap-1 text-xs text-success", children: [
1928
+ /* @__PURE__ */ jsx10(CheckGlyph, { className: "size-3.5 shrink-0" }),
1929
+ /* @__PURE__ */ jsx10("span", { className: "truncate", children: describeEnrollment(parsed) })
1933
1930
  ] }) : null;
1934
1931
  return /* @__PURE__ */ jsxs9(
1935
1932
  "div",
@@ -1954,11 +1951,11 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1954
1951
  {
1955
1952
  label,
1956
1953
  actions: /* @__PURE__ */ jsxs9(Fragment5, { children: [
1957
- /* @__PURE__ */ jsx9(Button6, { variant: "quiet", icon: /* @__PURE__ */ jsx9(QrGlyph, {}), "aria-label": "Choose QR image", title: "Choose or drop a QR image", disabled: disabled || reading, onClick: () => file.current?.click() }),
1958
- canScan ? /* @__PURE__ */ jsx9(Button6, { variant: "quiet", icon: /* @__PURE__ */ jsx9(CameraGlyph, {}), "aria-label": "Scan with camera", title: "Scan with camera", disabled, onClick: () => setScanning(true) }) : null
1954
+ /* @__PURE__ */ jsx10(Button6, { variant: "quiet", icon: /* @__PURE__ */ jsx10(QrGlyph, {}), "aria-label": "Choose QR image", title: "Choose or drop a QR image", disabled: disabled || reading, onClick: () => file.current?.click() }),
1955
+ canScan ? /* @__PURE__ */ jsx10(Button6, { variant: "quiet", icon: /* @__PURE__ */ jsx10(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
1959
1956
  ] }),
1960
1957
  children: [
1961
- /* @__PURE__ */ jsx9(
1958
+ /* @__PURE__ */ jsx10(
1962
1959
  Field3,
1963
1960
  {
1964
1961
  className: "w-full",
@@ -1981,7 +1978,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1981
1978
  end
1982
1979
  }
1983
1980
  ),
1984
- /* @__PURE__ */ jsx9(
1981
+ /* @__PURE__ */ jsx10(
1985
1982
  "input",
1986
1983
  {
1987
1984
  ref: file,
@@ -1998,7 +1995,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1998
1995
  ]
1999
1996
  }
2000
1997
  ),
2001
- canScan ? /* @__PURE__ */ jsx9(
1998
+ canScan ? /* @__PURE__ */ jsx10(
2002
1999
  QrScanPanel,
2003
2000
  {
2004
2001
  open: scanning,
@@ -2029,7 +2026,7 @@ function mfaMethodOf(item) {
2029
2026
  }
2030
2027
 
2031
2028
  // src/authenticator/TotpRows.tsx
2032
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2029
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2033
2030
  function otpActionError(e, what) {
2034
2031
  const status = e?.status;
2035
2032
  if (status === 404) return "There is no one-time password here.";
@@ -2067,7 +2064,7 @@ function useOtpActions(port, item, onChanged) {
2067
2064
  }),
2068
2065
  remove: async () => {
2069
2066
  const ok = await confirm2({
2070
- title: `Remove the one-time password for ${item.name}?`,
2067
+ title: `Remove the authenticator for ${item.name}?`,
2071
2068
  description: "Codes stop here. Two-factor stays on at the site, and your phone app and backup codes still work.",
2072
2069
  confirmLabel: "Remove",
2073
2070
  variant: "destructive"
@@ -2087,37 +2084,37 @@ function TotpRow({ port, item, onChanged }) {
2087
2084
  const canManage = item.capabilities.canManage || item.capabilities.canEdit;
2088
2085
  if (a.removed || !item.authenticator) return null;
2089
2086
  const menu = canManage ? /* @__PURE__ */ jsxs10(DropdownMenu, { children: [
2090
- /* @__PURE__ */ jsx10(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(Button7, { variant: "quiet", icon: /* @__PURE__ */ jsx10(MoreGlyph, {}), "aria-label": "One-time password options", disabled: a.busy }) }),
2087
+ /* @__PURE__ */ jsx11(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx11(Button7, { variant: "quiet", icon: /* @__PURE__ */ jsx11(MoreGlyph, {}), "aria-label": "Authenticator options", disabled: a.busy }) }),
2091
2088
  /* @__PURE__ */ jsxs10(DropdownMenuContent, { align: "end", className: "min-w-44", children: [
2092
2089
  /* @__PURE__ */ jsxs10(DropdownMenuItem, { onSelect: () => void a.setEnabled(!a.enabled), children: [
2093
- /* @__PURE__ */ jsx10(PowerGlyph, { className: "size-4" }),
2090
+ /* @__PURE__ */ jsx11(PowerGlyph, { className: "size-4" }),
2094
2091
  a.enabled ? "Turn off" : "Turn on"
2095
2092
  ] }),
2096
- /* @__PURE__ */ jsx10(DropdownMenuSeparator, {}),
2093
+ /* @__PURE__ */ jsx11(DropdownMenuSeparator, {}),
2097
2094
  /* @__PURE__ */ jsxs10(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
2098
- /* @__PURE__ */ jsx10(TrashGlyph, { className: "size-4" }),
2095
+ /* @__PURE__ */ jsx11(TrashGlyph, { className: "size-4" }),
2099
2096
  "Remove"
2100
2097
  ] })
2101
2098
  ] })
2102
2099
  ] }) : null;
2103
- return /* @__PURE__ */ jsx10(
2100
+ return /* @__PURE__ */ jsx11(
2104
2101
  VaultRow,
2105
2102
  {
2106
- label: "One-time password",
2103
+ label: "Authenticator",
2107
2104
  actions: /* @__PURE__ */ jsxs10(Fragment6, { children: [
2108
- a.enabled ? /* @__PURE__ */ jsx10(CopyCodeButton, { live, label: item.name }) : null,
2105
+ a.enabled ? /* @__PURE__ */ jsx11(CopyCodeButton, { live, label: item.name }) : null,
2109
2106
  menu
2110
2107
  ] }),
2111
2108
  children: a.error ? /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2", children: [
2112
- /* @__PURE__ */ jsx10("span", { className: "truncate text-sm text-destructive", role: "alert", children: a.error }),
2113
- /* @__PURE__ */ jsx10(Button7, { variant: "quiet", onClick: a.clearError, children: "Dismiss" })
2109
+ /* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-destructive", role: "alert", children: a.error }),
2110
+ /* @__PURE__ */ jsx11(Button7, { variant: "quiet", onClick: a.clearError, children: "Dismiss" })
2114
2111
  ] }) : !a.enabled ? /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2", children: [
2115
- /* @__PURE__ */ jsx10("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2116
- canManage ? /* @__PURE__ */ jsx10(Button7, { variant: "quiet", icon: /* @__PURE__ */ jsx10(PowerGlyph, {}), disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2112
+ /* @__PURE__ */ jsx11("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2113
+ canManage ? /* @__PURE__ */ jsx11(Button7, { variant: "quiet", icon: /* @__PURE__ */ jsx11(PowerGlyph, {}), disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2117
2114
  ] }) : live.error ? /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2", children: [
2118
- /* @__PURE__ */ jsx10("span", { className: "truncate text-sm text-destructive", title: live.error, children: live.error }),
2119
- /* @__PURE__ */ jsx10(Button7, { variant: "quiet", onClick: live.retry, children: "Retry" })
2120
- ] }) : /* @__PURE__ */ jsx10(CodeButton, { live, digits: item.authenticator.digits, label: item.name })
2115
+ /* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-destructive", title: live.error, children: live.error }),
2116
+ /* @__PURE__ */ jsx11(Button7, { variant: "quiet", onClick: live.retry, children: "Retry" })
2117
+ ] }) : /* @__PURE__ */ jsx11(CodeButton, { live, digits: item.authenticator.digits, label: item.name })
2121
2118
  }
2122
2119
  );
2123
2120
  }
@@ -2135,7 +2132,7 @@ function TotpEditRow({ value, onChange, hasExisting, item, port, onEnrolled, onR
2135
2132
  setStatus(wasCancelled(e) ? null : otpActionError(e, "read that image"));
2136
2133
  }
2137
2134
  } : void 0;
2138
- return /* @__PURE__ */ jsx10(
2135
+ return /* @__PURE__ */ jsx11(
2139
2136
  OtpSetupRow,
2140
2137
  {
2141
2138
  value,
@@ -2160,7 +2157,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
2160
2157
  const [busy, setBusy] = useState11(false);
2161
2158
  const [error, setError] = useState11(null);
2162
2159
  const canEdit = item.capabilities.canEdit || item.capabilities.canManage;
2163
- return /* @__PURE__ */ jsx10(VaultRow, { label: "Two-factor", hint: error ? /* @__PURE__ */ jsx10("span", { className: "text-destructive", children: error }) : void 0, children: /* @__PURE__ */ jsx10(
2160
+ return /* @__PURE__ */ jsx11(VaultRow, { label: "Two-factor", hint: error ? /* @__PURE__ */ jsx11("span", { className: "text-destructive", children: error }) : void 0, children: /* @__PURE__ */ jsx11(
2164
2161
  Select,
2165
2162
  {
2166
2163
  value: value ?? "none",
@@ -2187,7 +2184,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
2187
2184
  }
2188
2185
 
2189
2186
  // src/authenticator/AddOtpPanel.tsx
2190
- import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2187
+ import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2191
2188
  var NEW = "__new__";
2192
2189
  var LOGIN_KEYS = ["website_login", "username_password"];
2193
2190
  function normalizeWebsite(raw) {
@@ -2263,23 +2260,23 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2263
2260
  setBusy(false);
2264
2261
  }
2265
2262
  };
2266
- if (saved) return /* @__PURE__ */ jsx11(AddedPanel, { port, item: saved, onClose, onDone: () => onDone(saved.id) });
2263
+ if (saved) return /* @__PURE__ */ jsx12(AddedPanel, { port, item: saved, onClose, onDone: () => onDone(saved.id) });
2267
2264
  return /* @__PURE__ */ jsxs11(
2268
2265
  Panel,
2269
2266
  {
2270
- title: "Add one-time password",
2267
+ title: "Add authenticator",
2271
2268
  size: "md",
2272
2269
  onClose,
2273
2270
  footer: /* @__PURE__ */ jsxs11(Fragment7, { children: [
2274
- /* @__PURE__ */ jsx11(Button8, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
2275
- /* @__PURE__ */ jsx11(Button8, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
2271
+ /* @__PURE__ */ jsx12(Button8, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
2272
+ /* @__PURE__ */ jsx12(Button8, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
2276
2273
  ] }),
2277
2274
  children: [
2278
2275
  /* @__PURE__ */ jsxs11(VaultSection, { children: [
2279
- /* @__PURE__ */ jsx11(
2276
+ /* @__PURE__ */ jsx12(
2280
2277
  OtpSetupRow,
2281
2278
  {
2282
- label: "Setup code",
2279
+ label: "Setup key",
2283
2280
  value: input,
2284
2281
  autoFocus: true,
2285
2282
  disabled: busy,
@@ -2297,7 +2294,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2297
2294
  status: image ? `QR image ready \xB7 ${image.name}` : null
2298
2295
  }
2299
2296
  ),
2300
- /* @__PURE__ */ jsx11(VaultRow, { label: "Add to", children: /* @__PURE__ */ jsx11(
2297
+ itemId ? null : /* @__PURE__ */ jsx12(VaultRow, { label: "Add to", children: /* @__PURE__ */ jsx12(
2301
2298
  Select2,
2302
2299
  {
2303
2300
  value: target,
@@ -2311,7 +2308,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2311
2308
  }
2312
2309
  ) }),
2313
2310
  isNew ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
2314
- /* @__PURE__ */ jsx11(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx11(
2311
+ /* @__PURE__ */ jsx12(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx12(
2315
2312
  Field4,
2316
2313
  {
2317
2314
  className: "w-full",
@@ -2325,24 +2322,27 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2325
2322
  "aria-label": "Name"
2326
2323
  }
2327
2324
  ) }),
2328
- /* @__PURE__ */ jsx11(VaultRow, { label: "Website", hint: websiteBad ? /* @__PURE__ */ jsx11("span", { className: "text-destructive", children: "Not a web address" }) : void 0, children: /* @__PURE__ */ jsx11(Field4, { 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 }) })
2325
+ /* @__PURE__ */ jsx12(VaultRow, { label: "Website", hint: websiteBad ? /* @__PURE__ */ jsx12("span", { className: "text-destructive", children: "Not a web address" }) : void 0, children: /* @__PURE__ */ jsx12(Field4, { 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 }) })
2329
2326
  ] }) : null
2330
2327
  ] }),
2331
- error ? /* @__PURE__ */ jsx11("p", { className: "px-1 text-sm text-destructive", role: "alert", children: error }) : null
2328
+ error ? /* @__PURE__ */ jsx12("p", { className: "px-1 text-sm text-destructive", role: "alert", children: error }) : null
2332
2329
  ]
2333
2330
  }
2334
2331
  );
2335
2332
  }
2333
+ function FirstCodePanel(props) {
2334
+ return /* @__PURE__ */ jsx12(AddedPanel, { ...props });
2335
+ }
2336
2336
  function AddedPanel({ port, item, onClose, onDone }) {
2337
2337
  const live = useLiveCode(port, item, true);
2338
- return /* @__PURE__ */ jsx11(Panel, { title: item.name, size: "sm", onClose, footer: /* @__PURE__ */ jsx11(Button8, { variant: "primary", onClick: onDone, children: "Done" }), children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 py-4", children: [
2339
- live.error ? /* @__PURE__ */ jsx11("span", { className: "text-sm text-destructive", children: live.error }) : /* @__PURE__ */ jsx11(CodeButton, { live, digits: item.authenticator?.digits ?? 6, size: "lg", label: item.name }),
2340
- /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: "Enter this code on the site to finish." })
2338
+ return /* @__PURE__ */ jsx12(Panel, { title: item.name, size: "sm", onClose, footer: /* @__PURE__ */ jsx12(Button8, { variant: "primary", onClick: onDone, children: "Done" }), children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 py-4", children: [
2339
+ live.error ? /* @__PURE__ */ jsx12("span", { className: "text-sm text-destructive", children: live.error }) : /* @__PURE__ */ jsx12(CodeButton, { live, digits: item.authenticator?.digits ?? 6, size: "lg", label: item.name }),
2340
+ /* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground", children: "Enter this code on the site to finish." })
2341
2341
  ] }) });
2342
2342
  }
2343
2343
 
2344
2344
  // src/authenticator/AuthenticatorView.tsx
2345
- import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2345
+ import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
2346
2346
  function matchesEntry(entry, q) {
2347
2347
  if (!entry || !q) return false;
2348
2348
  return [entry.issuer, entry.account].some((v) => v?.toLowerCase().includes(q));
@@ -2360,7 +2360,7 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
2360
2360
  const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
2361
2361
  return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
2362
2362
  }
2363
- function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged, header }) {
2363
+ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onChanged, header }) {
2364
2364
  const [entries, setEntries] = useState13(/* @__PURE__ */ new Map());
2365
2365
  const [hidden, setHidden] = useState13(/* @__PURE__ */ new Set());
2366
2366
  const [adding, setAdding] = useState13(false);
@@ -2380,20 +2380,20 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2380
2380
  const groups = otpGroups(items, entries, query, hidden);
2381
2381
  const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
2382
2382
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
2383
- return /* @__PURE__ */ jsxs12("section", { "aria-label": "One-time passwords", className: "flex h-full min-h-0 flex-col bg-background", children: [
2384
- header ? /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
2383
+ return /* @__PURE__ */ jsxs12("section", { "aria-label": "Authenticator", className: "flex h-full min-h-0 flex-col bg-background", children: [
2384
+ header ? /* @__PURE__ */ jsx13("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
2385
2385
  /* @__PURE__ */ jsxs12("div", { className: "flex min-h-12 items-center justify-between gap-2 px-4 pb-1 pt-3", children: [
2386
2386
  /* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 items-baseline gap-2", children: [
2387
- /* @__PURE__ */ jsx12("h2", { className: "truncate text-sm font-semibold text-foreground", children: "One-time passwords" }),
2388
- total > 0 ? /* @__PURE__ */ jsx12("span", { className: "text-xs tabular-nums text-muted-foreground", children: total }) : null
2387
+ /* @__PURE__ */ jsx13("h2", { className: "truncate text-sm font-semibold text-foreground", children: "Authenticator" }),
2388
+ total > 0 ? /* @__PURE__ */ jsx13("span", { className: "text-xs tabular-nums text-muted-foreground", children: total }) : null
2389
2389
  ] }),
2390
- total > 0 ? /* @__PURE__ */ jsx12(Button9, { variant: "outline", icon: /* @__PURE__ */ jsx12(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
2390
+ total > 0 ? /* @__PURE__ */ jsx13(Button9, { variant: "outline", icon: /* @__PURE__ */ jsx13(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
2391
2391
  ] }),
2392
2392
  total === 0 ? /* @__PURE__ */ jsxs12("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
2393
- /* @__PURE__ */ jsx12("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx12(ShieldGlyph, { className: "size-6" }) }),
2394
- /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-foreground", children: "No one-time passwords yet" }),
2395
- /* @__PURE__ */ jsx12(Button9, { variant: "primary", icon: /* @__PURE__ */ jsx12(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add one-time password" })
2396
- ] }) : groups.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: "Nothing matches." }) : /* @__PURE__ */ jsx12("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6 pt-1 sm:px-4", children: /* @__PURE__ */ jsx12("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: groups.map((g) => /* @__PURE__ */ jsx12(VaultSection, { title: g.label, className: "[&>div]:px-1", children: g.items.map((item) => /* @__PURE__ */ jsx12(
2393
+ /* @__PURE__ */ jsx13("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx13(ShieldGlyph, { className: "size-6" }) }),
2394
+ /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium text-foreground", children: "No authenticator codes yet" }),
2395
+ /* @__PURE__ */ jsx13(Button9, { variant: "primary", icon: /* @__PURE__ */ jsx13(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add authenticator" })
2396
+ ] }) : groups.length === 0 ? /* @__PURE__ */ jsx13("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: "Nothing matches." }) : /* @__PURE__ */ jsx13("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6 pt-1 sm:px-4", children: /* @__PURE__ */ jsx13("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: groups.map((g) => /* @__PURE__ */ jsx13(VaultSection, { title: g.label, className: "[&>div]:px-1", children: g.items.map((item) => /* @__PURE__ */ jsx13(
2397
2397
  OtpListRow,
2398
2398
  {
2399
2399
  port,
@@ -2401,6 +2401,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2401
2401
  family: familyOf2(item.definitionKey),
2402
2402
  entry: entries.get(item.id),
2403
2403
  onOpen: () => onOpen(item.id),
2404
+ ...host.openItemInNewTab ? { onOpenInNewTab: () => host.openItemInNewTab?.(item.id) } : {},
2404
2405
  onRename: () => {
2405
2406
  setRenameError(null);
2406
2407
  setRenaming(item);
@@ -2413,7 +2414,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2413
2414
  },
2414
2415
  item.id
2415
2416
  )) }, g.key)) }) }),
2416
- adding ? /* @__PURE__ */ jsx12(
2417
+ adding ? /* @__PURE__ */ jsx13(
2417
2418
  AddOtpPanel,
2418
2419
  {
2419
2420
  port,
@@ -2427,7 +2428,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2427
2428
  }
2428
2429
  }
2429
2430
  ) : null,
2430
- /* @__PURE__ */ jsx12(
2431
+ /* @__PURE__ */ jsx13(
2431
2432
  TextInputDialog,
2432
2433
  {
2433
2434
  open: renaming !== null,
@@ -2435,7 +2436,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2435
2436
  if (!o && !renameBusy) setRenaming(null);
2436
2437
  },
2437
2438
  title: "Rename login",
2438
- ...renameError ? { description: /* @__PURE__ */ jsx12("span", { className: "text-destructive", children: renameError }) } : {},
2439
+ ...renameError ? { description: /* @__PURE__ */ jsx13("span", { className: "text-destructive", children: renameError }) } : {},
2439
2440
  placeholder: "Login name",
2440
2441
  defaultValue: renaming?.name ?? "",
2441
2442
  confirmLabel: "Rename",
@@ -2458,50 +2459,53 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2458
2459
  )
2459
2460
  ] });
2460
2461
  }
2461
- function OtpListRow({ port, item, family, entry, onOpen, onRename, onChanged, onRemoved }) {
2462
+ function OtpListRow({ port, item, family, entry, onOpen, onOpenInNewTab, onRename, onChanged, onRemoved }) {
2462
2463
  const a = useOtpActions(port, item, (next) => next && next.authenticator === null ? onRemoved() : onChanged());
2463
2464
  const live = useLiveCode(port, item, a.enabled && !a.removed);
2464
2465
  const { copy } = useCopied();
2465
- const Icon = definitionGlyph(item.definitionKey, family);
2466
2466
  const canManage = item.capabilities.canManage || item.capabilities.canEdit;
2467
2467
  if (a.removed) return null;
2468
2468
  const sub = a.error ?? [entry?.account, loginSiteOf(item) || entry?.issuer].filter((v, i, all) => Boolean(v) && all.indexOf(v) === i).join(" \xB7 ");
2469
2469
  return /* @__PURE__ */ jsxs12("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: [
2470
- /* @__PURE__ */ jsx12("button", { type: "button", onClick: onOpen, "aria-label": `Open ${item.name}`, tabIndex: -1, className: cn6("flex size-8 items-center justify-center rounded-lg bg-muted", a.enabled ? "text-foreground" : "text-disabled-foreground"), children: /* @__PURE__ */ jsx12(Icon, { className: "size-4" }) }),
2470
+ /* @__PURE__ */ jsx13("button", { type: "button", onClick: onOpen, "aria-label": `Open ${item.name}`, tabIndex: -1, className: "flex size-8 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx13(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item) || (entry?.issuer ?? "") }) }),
2471
2471
  /* @__PURE__ */ jsxs12("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: [
2472
- /* @__PURE__ */ jsx12("span", { className: cn6("max-w-full truncate text-sm font-medium", a.enabled ? "text-foreground" : "text-muted-foreground"), children: item.name }),
2473
- sub ? /* @__PURE__ */ jsx12("span", { className: cn6("max-w-full truncate text-xs", a.error ? "text-destructive" : "text-muted-foreground"), title: sub, children: sub }) : null
2472
+ /* @__PURE__ */ jsx13("span", { className: cn7("max-w-full truncate text-sm font-medium", a.enabled ? "text-foreground" : "text-muted-foreground"), children: item.name }),
2473
+ sub ? /* @__PURE__ */ jsx13("span", { className: cn7("max-w-full truncate text-xs", a.error ? "text-destructive" : "text-muted-foreground"), title: sub, children: sub }) : null
2474
2474
  ] }),
2475
- /* @__PURE__ */ jsx12("div", { className: "flex min-w-0 justify-end", children: !a.enabled ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5", children: [
2476
- /* @__PURE__ */ jsx12("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2477
- canManage ? /* @__PURE__ */ jsx12(Button9, { variant: "quiet", disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2475
+ /* @__PURE__ */ jsx13("div", { className: "flex min-w-0 justify-end", children: !a.enabled ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5", children: [
2476
+ /* @__PURE__ */ jsx13("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
2477
+ canManage ? /* @__PURE__ */ jsx13(Button9, { variant: "quiet", disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
2478
2478
  ] }) : live.error ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5", children: [
2479
- /* @__PURE__ */ jsx12("span", { className: "max-w-40 truncate text-xs text-destructive", title: live.error, children: live.error }),
2480
- /* @__PURE__ */ jsx12(Button9, { variant: "quiet", onClick: live.retry, children: "Retry" })
2481
- ] }) : /* @__PURE__ */ jsx12(CodeButton, { live, digits: item.authenticator?.digits ?? 6, label: item.name }) }),
2479
+ /* @__PURE__ */ jsx13("span", { className: "max-w-40 truncate text-xs text-destructive", title: live.error, children: live.error }),
2480
+ /* @__PURE__ */ jsx13(Button9, { variant: "quiet", onClick: live.retry, children: "Retry" })
2481
+ ] }) : /* @__PURE__ */ jsx13(CodeButton, { live, digits: item.authenticator?.digits ?? 6, label: item.name }) }),
2482
2482
  /* @__PURE__ */ jsxs12(DropdownMenu2, { children: [
2483
- /* @__PURE__ */ jsx12(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx12(Button9, { variant: "quiet", icon: /* @__PURE__ */ jsx12(MoreGlyph, {}), "aria-label": `${item.name} options`, disabled: a.busy }) }),
2483
+ /* @__PURE__ */ jsx13(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx13(Button9, { variant: "quiet", icon: /* @__PURE__ */ jsx13(MoreGlyph, {}), "aria-label": `${item.name} options`, disabled: a.busy }) }),
2484
2484
  /* @__PURE__ */ jsxs12(DropdownMenuContent2, { align: "end", className: "min-w-44", children: [
2485
2485
  /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onOpen, children: [
2486
- /* @__PURE__ */ jsx12(ExternalGlyph, { className: "size-4" }),
2486
+ /* @__PURE__ */ jsx13(ExternalGlyph, { className: "size-4" }),
2487
2487
  "Open login"
2488
2488
  ] }),
2489
+ onOpenInNewTab ? /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onOpenInNewTab, children: [
2490
+ /* @__PURE__ */ jsx13(ExternalGlyph, { className: "size-4" }),
2491
+ "Open in new tab"
2492
+ ] }) : null,
2489
2493
  live.code ? /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: () => live.code && void copy(live.code), children: [
2490
- /* @__PURE__ */ jsx12(CopyIcon4, { className: "size-4" }),
2494
+ /* @__PURE__ */ jsx13(CopyIcon4, { className: "size-4" }),
2491
2495
  "Copy code"
2492
2496
  ] }) : null,
2493
2497
  canManage ? /* @__PURE__ */ jsxs12(Fragment8, { children: [
2494
2498
  /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onRename, children: [
2495
- /* @__PURE__ */ jsx12(PencilGlyph, { className: "size-4" }),
2499
+ /* @__PURE__ */ jsx13(PencilGlyph, { className: "size-4" }),
2496
2500
  "Rename"
2497
2501
  ] }),
2498
2502
  /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: () => void a.setEnabled(!a.enabled), children: [
2499
- /* @__PURE__ */ jsx12(PowerGlyph, { className: "size-4" }),
2503
+ /* @__PURE__ */ jsx13(PowerGlyph, { className: "size-4" }),
2500
2504
  a.enabled ? "Turn off" : "Turn on"
2501
2505
  ] }),
2502
- /* @__PURE__ */ jsx12(DropdownMenuSeparator2, {}),
2506
+ /* @__PURE__ */ jsx13(DropdownMenuSeparator2, {}),
2503
2507
  /* @__PURE__ */ jsxs12(DropdownMenuItem2, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
2504
- /* @__PURE__ */ jsx12(TrashGlyph, { className: "size-4" }),
2508
+ /* @__PURE__ */ jsx13(TrashGlyph, { className: "size-4" }),
2505
2509
  "Remove"
2506
2510
  ] })
2507
2511
  ] }) : null
@@ -2594,7 +2598,7 @@ function withoutCode(codes, index) {
2594
2598
  }
2595
2599
 
2596
2600
  // src/fields/SettingsPanels.tsx
2597
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2601
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
2598
2602
  var say = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
2599
2603
  function useSaver(onChanged) {
2600
2604
  const [busy, setBusy] = useState14(null);
@@ -2616,14 +2620,14 @@ function useSaver(onChanged) {
2616
2620
  return { busy, error, setError, save };
2617
2621
  }
2618
2622
  function Problem({ children }) {
2619
- return /* @__PURE__ */ jsx13("p", { role: "alert", className: "px-1 text-sm text-destructive", children });
2623
+ return /* @__PURE__ */ jsx14("p", { role: "alert", className: "px-1 text-sm text-destructive", children });
2620
2624
  }
2621
- var quietHint = (text, tone = "muted") => /* @__PURE__ */ jsx13("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
2625
+ var quietHint = (text, tone = "muted") => /* @__PURE__ */ jsx14("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
2622
2626
  function FieldSettingsPanel(props) {
2623
2627
  const field = props.item.fields.find((f) => f.id === props.arg);
2624
2628
  if (!field)
2625
- return /* @__PURE__ */ jsx13(Panel, { title: "Field settings", size: "md", onClose: props.onClose, footer: /* @__PURE__ */ jsx13(Button10, { variant: "primary", onClick: props.onClose, children: "Done" }), children: /* @__PURE__ */ jsx13(Empty, { children: "This field is no longer on the item." }) });
2626
- return /* @__PURE__ */ jsx13(FieldSettings, { ...props, field }, field.id);
2629
+ return /* @__PURE__ */ jsx14(Panel, { title: "Field settings", size: "md", onClose: props.onClose, footer: /* @__PURE__ */ jsx14(Button10, { variant: "primary", onClick: props.onClose, children: "Done" }), children: /* @__PURE__ */ jsx14(Empty, { children: "This field is no longer on the item." }) });
2630
+ return /* @__PURE__ */ jsx14(FieldSettings, { ...props, field }, field.id);
2627
2631
  }
2628
2632
  function FieldSettings({ item, port, field, onClose, onChanged }) {
2629
2633
  const { busy, error, setError, save } = useSaver((next) => onChanged(next));
@@ -2662,9 +2666,9 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2662
2666
  };
2663
2667
  const handling = HANDLING_WORDS[field.handling];
2664
2668
  const locked = !canEdit || busy !== null;
2665
- return /* @__PURE__ */ jsxs13(Panel, { title: `${field.label} settings`, size: "md", onClose, footer: /* @__PURE__ */ jsx13(Button10, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2666
- !canEdit ? /* @__PURE__ */ jsx13("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2667
- /* @__PURE__ */ jsx13(VaultSection, { title: "Who can see it", children: /* @__PURE__ */ jsx13(VaultRow, { label: "Value", hint: quietHint(handling.hint), children: field.handling === "sealed" ? /* @__PURE__ */ jsx13(Badge3, { icon: /* @__PURE__ */ jsx13(LockGlyph, {}), children: "Sealed for good" }) : /* @__PURE__ */ jsx13(
2669
+ return /* @__PURE__ */ jsxs13(Panel, { title: `${field.label} settings`, size: "md", onClose, footer: /* @__PURE__ */ jsx14(Button10, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2670
+ !canEdit ? /* @__PURE__ */ jsx14("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2671
+ /* @__PURE__ */ jsx14(VaultSection, { title: "Who can see it", children: /* @__PURE__ */ jsx14(VaultRow, { label: "Value", hint: quietHint(handling.hint), children: field.handling === "sealed" ? /* @__PURE__ */ jsx14(Badge3, { icon: /* @__PURE__ */ jsx14(LockGlyph, {}), children: "Sealed for good" }) : /* @__PURE__ */ jsx14(
2668
2672
  SegmentedControl,
2669
2673
  {
2670
2674
  "aria-label": "Who can see it",
@@ -2674,7 +2678,7 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2674
2678
  }
2675
2679
  ) }) }),
2676
2680
  /* @__PURE__ */ jsxs13(VaultSection, { title: "Automation", children: [
2677
- /* @__PURE__ */ jsx13(VaultRow, { label: "Env key", hint: envProblem ? quietHint(envProblem, "destructive") : quietHint("The name sandboxes and workflows use"), children: /* @__PURE__ */ jsx13(
2681
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Env key", hint: envProblem ? quietHint(envProblem, "destructive") : quietHint("The name sandboxes and workflows use"), children: /* @__PURE__ */ jsx14(
2678
2682
  Field5,
2679
2683
  {
2680
2684
  ref: envRef,
@@ -2694,10 +2698,10 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2694
2698
  }
2695
2699
  }
2696
2700
  ) }),
2697
- /* @__PURE__ */ jsx13(VaultRow, { label: "Use in sandbox", hint: blocker && !field.injectIntoSandbox ? quietHint(blocker) : quietHint("Sandboxes get it as an env variable"), children: /* @__PURE__ */ jsx13(Switch, { "aria-label": "Use in sandbox", checked: field.injectIntoSandbox, disabled: locked, onCheckedChange: (v) => void setSandbox(v) }) })
2701
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Use in sandbox", hint: blocker && !field.injectIntoSandbox ? quietHint(blocker) : quietHint("Sandboxes get it as an env variable"), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Use in sandbox", checked: field.injectIntoSandbox, disabled: locked, onCheckedChange: (v) => void setSandbox(v) }) })
2698
2702
  ] }),
2699
2703
  /* @__PURE__ */ jsxs13(VaultSection, { title: "Field", children: [
2700
- /* @__PURE__ */ jsx13(VaultRow, { label: "Description", children: /* @__PURE__ */ jsx13(
2704
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Description", children: /* @__PURE__ */ jsx14(
2701
2705
  Field5,
2702
2706
  {
2703
2707
  className: "w-full",
@@ -2713,10 +2717,10 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2713
2717
  }
2714
2718
  }
2715
2719
  ) }),
2716
- /* @__PURE__ */ jsx13(VaultRow, { label: "Editable", hint: quietHint(field.editable ? "The value can be changed" : "The value is locked"), children: /* @__PURE__ */ jsx13(Switch, { "aria-label": "Editable", checked: field.editable, disabled: locked, onCheckedChange: (v) => void update("editable", { editable: v }) }) }),
2717
- /* @__PURE__ */ jsx13(VaultRow, { label: "Turned on", hint: quietHint(field.active ? "In use" : "Kept, but nothing uses it"), children: /* @__PURE__ */ jsx13(Switch, { "aria-label": "Turned on", checked: field.active, disabled: locked, onCheckedChange: (v) => void update("active", { active: v }) }) })
2720
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Editable", hint: quietHint(field.editable ? "The value can be changed" : "The value is locked"), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Editable", checked: field.editable, disabled: locked, onCheckedChange: (v) => void update("editable", { editable: v }) }) }),
2721
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Turned on", hint: quietHint(field.active ? "In use" : "Kept, but nothing uses it"), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Turned on", checked: field.active, disabled: locked, onCheckedChange: (v) => void update("active", { active: v }) }) })
2718
2722
  ] }),
2719
- error ? /* @__PURE__ */ jsx13(Problem, { children: error }) : null
2723
+ error ? /* @__PURE__ */ jsx14(Problem, { children: error }) : null
2720
2724
  ] });
2721
2725
  }
2722
2726
  function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
@@ -2743,10 +2747,10 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
2743
2747
  });
2744
2748
  };
2745
2749
  const fillHint = !hasWebsite ? "Add a website first" : item.browserFillEnabled ? "Offered on matching pages" : "Never filled automatically";
2746
- return /* @__PURE__ */ jsxs13(Panel, { title: "Item settings", size: "md", onClose, footer: /* @__PURE__ */ jsx13(Button10, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2747
- !canEdit ? /* @__PURE__ */ jsx13("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2750
+ return /* @__PURE__ */ jsxs13(Panel, { title: "Item settings", size: "md", onClose, footer: /* @__PURE__ */ jsx14(Button10, { variant: "primary", onClick: onClose, children: "Done" }), children: [
2751
+ !canEdit ? /* @__PURE__ */ jsx14("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
2748
2752
  /* @__PURE__ */ jsxs13(VaultSection, { title: "In the browser", children: [
2749
- /* @__PURE__ */ jsx13(VaultRow, { label: "Website matching", hint: quietHint(MATCH_WORDS[item.uriMatchMode].hint), children: /* @__PURE__ */ jsx13(
2753
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Website matching", hint: quietHint(MATCH_WORDS[item.uriMatchMode].hint), children: /* @__PURE__ */ jsx14(
2750
2754
  Select3,
2751
2755
  {
2752
2756
  "aria-label": "Website matching",
@@ -2756,45 +2760,45 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
2756
2760
  onValueChange: (m) => void save("match", () => port.fields.updateItem(item, { uriMatchMode: m }))
2757
2761
  }
2758
2762
  ) }),
2759
- /* @__PURE__ */ jsx13(VaultRow, { label: "Fill in browser", hint: quietHint(fillHint), children: /* @__PURE__ */ jsx13(Switch, { "aria-label": "Fill in browser", checked: hasWebsite && item.browserFillEnabled, disabled: locked || !hasWebsite, onCheckedChange: (v) => void save("fill", () => port.fields.updateItem(item, { browserFillEnabled: v })) }) }),
2760
- promotable.map((f) => /* @__PURE__ */ jsx13(
2763
+ /* @__PURE__ */ jsx14(VaultRow, { label: "Fill in browser", hint: quietHint(fillHint), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Fill in browser", checked: hasWebsite && item.browserFillEnabled, disabled: locked || !hasWebsite, onCheckedChange: (v) => void save("fill", () => port.fields.updateItem(item, { browserFillEnabled: v })) }) }),
2764
+ promotable.map((f) => /* @__PURE__ */ jsx14(
2761
2765
  VaultRow,
2762
2766
  {
2763
2767
  label: f.label,
2764
2768
  hint: quietHint("Encrypted, so the browser can\u2019t match it"),
2765
- actions: /* @__PURE__ */ jsx13(Button10, { variant: "outline", icon: /* @__PURE__ */ jsx13(GlobeIcon2, {}), disabled: locked, onClick: () => void promote(f), children: "Use as website" }),
2766
- children: /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: "Not a website yet" })
2769
+ actions: /* @__PURE__ */ jsx14(Button10, { variant: "outline", icon: /* @__PURE__ */ jsx14(GlobeIcon2, {}), disabled: locked, onClick: () => void promote(f), children: "Use as website" }),
2770
+ children: /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Not a website yet" })
2767
2771
  },
2768
2772
  f.id
2769
2773
  ))
2770
2774
  ] }),
2771
2775
  /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1.5", children: [
2772
- /* @__PURE__ */ jsx13(DisclosureHeader, { open: advanced, title: "Advanced", meta: item.fields.length ? `${item.fields.length} fields` : void 0, onClick: () => setAdvanced((a) => !a) }),
2773
- advanced ? /* @__PURE__ */ jsx13(VaultSection, { children: item.fields.length === 0 ? /* @__PURE__ */ jsx13(Empty, { children: "No encrypted fields." }) : item.fields.map((f) => /* @__PURE__ */ jsx13(
2776
+ /* @__PURE__ */ jsx14(DisclosureHeader, { open: advanced, title: "Advanced", meta: item.fields.length ? `${item.fields.length} fields` : void 0, onClick: () => setAdvanced((a) => !a) }),
2777
+ advanced ? /* @__PURE__ */ jsx14(VaultSection, { children: item.fields.length === 0 ? /* @__PURE__ */ jsx14(Empty, { children: "No encrypted fields." }) : item.fields.map((f) => /* @__PURE__ */ jsx14(
2774
2778
  VaultRow,
2775
2779
  {
2776
2780
  label: f.label,
2777
- actions: /* @__PURE__ */ jsx13(Button10, { variant: "quiet", icon: /* @__PURE__ */ jsx13(SlidersGlyph, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }),
2781
+ actions: /* @__PURE__ */ jsx14(Button10, { variant: "quiet", icon: /* @__PURE__ */ jsx14(SlidersGlyph, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }),
2778
2782
  children: /* @__PURE__ */ jsxs13("span", { className: "block truncate", children: [
2779
- /* @__PURE__ */ jsx13("span", { className: f.handling === "sealed" ? "text-foreground" : "text-muted-foreground", children: HANDLING_WORDS[f.handling].label }),
2783
+ /* @__PURE__ */ jsx14("span", { className: f.handling === "sealed" ? "text-foreground" : "text-muted-foreground", children: HANDLING_WORDS[f.handling].label }),
2780
2784
  f.envKey ? /* @__PURE__ */ jsxs13("span", { className: "font-mono text-muted-foreground", children: [
2781
2785
  " \xB7 ",
2782
2786
  f.envKey
2783
2787
  ] }) : null,
2784
- f.injectIntoSandbox ? /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
2785
- !f.active ? /* @__PURE__ */ jsx13("span", { className: "text-warning", children: " \xB7 Turned off" }) : null
2788
+ f.injectIntoSandbox ? /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
2789
+ !f.active ? /* @__PURE__ */ jsx14("span", { className: "text-warning", children: " \xB7 Turned off" }) : null
2786
2790
  ] })
2787
2791
  },
2788
2792
  f.id
2789
2793
  )) }) : null
2790
2794
  ] }),
2791
- error ? /* @__PURE__ */ jsx13(Problem, { children: error }) : null
2795
+ error ? /* @__PURE__ */ jsx14(Problem, { children: error }) : null
2792
2796
  ] });
2793
2797
  }
2794
2798
 
2795
2799
  // src/generator/PasswordGenerator.tsx
2796
2800
  import { createContext as createContext5, useContext as useContext5, useEffect as useEffect12, useRef as useRef10, useState as useState15 } from "react";
2797
- import { Popover, PopoverContent, PopoverTrigger, cn as cn7 } from "@ai-matrx/design-system";
2801
+ import { Popover, PopoverContent, PopoverTrigger, cn as cn8 } from "@ai-matrx/design-system";
2798
2802
  import { Button as Button11, NumberStepper, SegmentedControl as SegmentedControl2, Select as Select4, Switch as Switch2 } from "@ai-matrx/design-system/controls";
2799
2803
  import { CopyIcon as CopyIcon5 } from "@ai-matrx/design-system/icons";
2800
2804
 
@@ -2854,10 +2858,10 @@ function strengthOf(bits) {
2854
2858
  }
2855
2859
 
2856
2860
  // src/generator/PasswordGenerator.tsx
2857
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2861
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
2858
2862
  var LimitsCtx = createContext5(null);
2859
2863
  function GeneratorLimitsProvider({ resolve: resolve2, children }) {
2860
- return /* @__PURE__ */ jsx14(LimitsCtx.Provider, { value: resolve2 ?? null, children });
2864
+ return /* @__PURE__ */ jsx15(LimitsCtx.Provider, { value: resolve2 ?? null, children });
2861
2865
  }
2862
2866
  function useGeneratorLimitsSource() {
2863
2867
  return useContext5(LimitsCtx);
@@ -2918,11 +2922,11 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2918
2922
  useEffect12(() => () => {
2919
2923
  if (expiry.current) clearTimeout(expiry.current);
2920
2924
  }, []);
2921
- if (limits.state === "loading") return /* @__PURE__ */ jsx14("p", { className: "px-1 py-3 text-sm text-muted-foreground", "aria-live": "polite", children: "Reading limits\u2026" });
2925
+ if (limits.state === "loading") return /* @__PURE__ */ jsx15("p", { className: "px-1 py-3 text-sm text-muted-foreground", "aria-live": "polite", children: "Reading limits\u2026" });
2922
2926
  if (limits.state === "unavailable")
2923
2927
  return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-2 px-1 py-2", children: [
2924
- /* @__PURE__ */ jsx14("p", { role: "alert", className: "text-sm text-destructive", children: limits.message }),
2925
- /* @__PURE__ */ jsx14(Button11, { variant: "outline", icon: /* @__PURE__ */ jsx14(RefreshGlyph, {}), onClick: () => setRound((n) => n + 1), children: "Try again" })
2928
+ /* @__PURE__ */ jsx15("p", { role: "alert", className: "text-sm text-destructive", children: limits.message }),
2929
+ /* @__PURE__ */ jsx15(Button11, { variant: "outline", icon: /* @__PURE__ */ jsx15(RefreshGlyph, {}), onClick: () => setRound((n) => n + 1), children: "Try again" })
2926
2930
  ] });
2927
2931
  const ceilings = limits.limits;
2928
2932
  const change = (next) => make(next, ceilings);
@@ -2940,7 +2944,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2940
2944
  onUse(value);
2941
2945
  };
2942
2946
  return /* @__PURE__ */ jsxs14("div", { className: "flex w-full flex-col gap-3", "aria-label": "Password generator", children: [
2943
- /* @__PURE__ */ jsx14(
2947
+ /* @__PURE__ */ jsx15(
2944
2948
  SegmentedControl2,
2945
2949
  {
2946
2950
  fill: true,
@@ -2952,13 +2956,13 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2952
2956
  ),
2953
2957
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1 rounded-lg bg-muted px-3 py-2", children: [
2954
2958
  /* @__PURE__ */ jsxs14("div", { className: "flex min-h-7 items-start gap-1", children: [
2955
- /* @__PURE__ */ jsx14("code", { className: cn7("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" }),
2956
- /* @__PURE__ */ jsx14(Button11, { variant: "quiet", icon: revealed ? /* @__PURE__ */ jsx14(EyeOffGlyph, {}) : /* @__PURE__ */ jsx14(EyeGlyph, {}), "aria-label": revealed ? "Hide" : "Reveal", disabled: !candidate, onClick: () => setRevealed((r) => !r) }),
2957
- /* @__PURE__ */ jsx14(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx14(CopyIcon5, {}), "aria-label": "Copy", disabled: !candidate, onClick: () => void copy() }),
2958
- /* @__PURE__ */ jsx14(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx14(RefreshGlyph, {}), "aria-label": "Generate another", onClick: () => change(settings) })
2959
+ /* @__PURE__ */ jsx15("code", { className: cn8("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" }),
2960
+ /* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: revealed ? /* @__PURE__ */ jsx15(EyeOffGlyph, {}) : /* @__PURE__ */ jsx15(EyeGlyph, {}), "aria-label": revealed ? "Hide" : "Reveal", disabled: !candidate, onClick: () => setRevealed((r) => !r) }),
2961
+ /* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx15(CopyIcon5, {}), "aria-label": "Copy", disabled: !candidate, onClick: () => void copy() }),
2962
+ /* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx15(RefreshGlyph, {}), "aria-label": "Generate another", onClick: () => change(settings) })
2959
2963
  ] }),
2960
2964
  strength && candidate ? /* @__PURE__ */ jsxs14("p", { className: "text-xs", children: [
2961
- /* @__PURE__ */ jsx14("span", { className: cn7(strength.tone === "success" && "text-success", strength.tone === "primary" && "text-primary", strength.tone === "warning" && "text-warning"), children: strength.label }),
2965
+ /* @__PURE__ */ jsx15("span", { className: cn8(strength.tone === "success" && "text-success", strength.tone === "primary" && "text-primary", strength.tone === "warning" && "text-warning"), children: strength.label }),
2962
2966
  /* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground", children: [
2963
2967
  " \xB7 ",
2964
2968
  Math.floor(candidate.bits),
@@ -2967,7 +2971,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2967
2971
  ] }) : null
2968
2972
  ] }),
2969
2973
  settings.kind === "password" ? /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-0.5", children: [
2970
- /* @__PURE__ */ jsx14(OptionLine, { label: "Length", children: /* @__PURE__ */ jsx14(NumberStepper, { value: settings.password.length, min: minPasswordLength(settings.password), max: ceilings.maxPasswordLength, fallback: 24, onChange: (length) => setPassword({ length }) }) }),
2974
+ /* @__PURE__ */ jsx15(OptionLine, { label: "Length", children: /* @__PURE__ */ jsx15(NumberStepper, { value: settings.password.length, min: minPasswordLength(settings.password), max: ceilings.maxPasswordLength, fallback: 24, onChange: (length) => setPassword({ length }) }) }),
2971
2975
  [
2972
2976
  ["lowercase", "Lowercase"],
2973
2977
  ["uppercase", "Uppercase"],
@@ -2976,26 +2980,26 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2976
2980
  ].map(([key, label]) => {
2977
2981
  const on = settings.password[key];
2978
2982
  const last = on && minPasswordLength(settings.password) === 1;
2979
- return /* @__PURE__ */ jsx14(OptionLine, { label, children: /* @__PURE__ */ jsx14(Switch2, { "aria-label": label, checked: on, disabled: last, onCheckedChange: (v) => setPassword({ [key]: v }) }) }, key);
2983
+ return /* @__PURE__ */ jsx15(OptionLine, { label, children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": label, checked: on, disabled: last, onCheckedChange: (v) => setPassword({ [key]: v }) }) }, key);
2980
2984
  }),
2981
- /* @__PURE__ */ jsx14(OptionLine, { label: "Avoid look-alikes", hint: "No 0/O, 1/l/I", children: /* @__PURE__ */ jsx14(Switch2, { "aria-label": "Avoid look-alikes", checked: settings.password.excludeAmbiguous, onCheckedChange: (v) => setPassword({ excludeAmbiguous: v }) }) })
2985
+ /* @__PURE__ */ jsx15(OptionLine, { label: "Avoid look-alikes", hint: "No 0/O, 1/l/I", children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": "Avoid look-alikes", checked: settings.password.excludeAmbiguous, onCheckedChange: (v) => setPassword({ excludeAmbiguous: v }) }) })
2982
2986
  ] }) : /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-0.5", children: [
2983
- /* @__PURE__ */ jsx14(OptionLine, { label: "Words", children: /* @__PURE__ */ jsx14(NumberStepper, { value: settings.passphrase.wordCount, min: MIN_PASSPHRASE_WORDS, max: ceilings.maxPassphraseWords, fallback: MIN_PASSPHRASE_WORDS, onChange: (wordCount) => change({ ...settings, passphrase: { ...settings.passphrase, wordCount } }) }) }),
2984
- /* @__PURE__ */ jsx14(OptionLine, { label: "Separator", children: /* @__PURE__ */ jsx14(Select4, { "aria-label": "Separator", variant: "inline", align: "end", value: settings.passphrase.separator, options: SEPARATORS, onValueChange: (separator) => change({ ...settings, passphrase: { ...settings.passphrase, separator } }) }) }),
2985
- /* @__PURE__ */ jsx14(OptionLine, { label: "Capitalize", children: /* @__PURE__ */ jsx14(Switch2, { "aria-label": "Capitalize", checked: settings.passphrase.capitalize, onCheckedChange: (capitalize) => change({ ...settings, passphrase: { ...settings.passphrase, capitalize } }) }) }),
2986
- /* @__PURE__ */ jsx14(OptionLine, { label: "Add a digit", children: /* @__PURE__ */ jsx14(Switch2, { "aria-label": "Add a digit", checked: settings.passphrase.appendDigit, onCheckedChange: (appendDigit) => change({ ...settings, passphrase: { ...settings.passphrase, appendDigit } }) }) })
2987
+ /* @__PURE__ */ jsx15(OptionLine, { label: "Words", children: /* @__PURE__ */ jsx15(NumberStepper, { value: settings.passphrase.wordCount, min: MIN_PASSPHRASE_WORDS, max: ceilings.maxPassphraseWords, fallback: MIN_PASSPHRASE_WORDS, onChange: (wordCount) => change({ ...settings, passphrase: { ...settings.passphrase, wordCount } }) }) }),
2988
+ /* @__PURE__ */ jsx15(OptionLine, { label: "Separator", children: /* @__PURE__ */ jsx15(Select4, { "aria-label": "Separator", variant: "inline", align: "end", value: settings.passphrase.separator, options: SEPARATORS, onValueChange: (separator) => change({ ...settings, passphrase: { ...settings.passphrase, separator } }) }) }),
2989
+ /* @__PURE__ */ jsx15(OptionLine, { label: "Capitalize", children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": "Capitalize", checked: settings.passphrase.capitalize, onCheckedChange: (capitalize) => change({ ...settings, passphrase: { ...settings.passphrase, capitalize } }) }) }),
2990
+ /* @__PURE__ */ jsx15(OptionLine, { label: "Add a digit", children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": "Add a digit", checked: settings.passphrase.appendDigit, onCheckedChange: (appendDigit) => change({ ...settings, passphrase: { ...settings.passphrase, appendDigit } }) }) })
2987
2991
  ] }),
2988
- note ? /* @__PURE__ */ jsx14("p", { "aria-live": "polite", className: cn7("text-xs", note.tone === "destructive" ? "text-destructive" : note.tone === "success" ? "text-success" : "text-muted-foreground"), children: note.text }) : null,
2989
- /* @__PURE__ */ jsx14(Button11, { variant: "primary", disabled: !candidate, onClick: use, children: useLabel })
2992
+ note ? /* @__PURE__ */ jsx15("p", { "aria-live": "polite", className: cn8("text-xs", note.tone === "destructive" ? "text-destructive" : note.tone === "success" ? "text-success" : "text-muted-foreground"), children: note.text }) : null,
2993
+ /* @__PURE__ */ jsx15(Button11, { variant: "primary", disabled: !candidate, onClick: use, children: useLabel })
2990
2994
  ] });
2991
2995
  }
2992
2996
  function OptionLine({ label, hint, children }) {
2993
2997
  return /* @__PURE__ */ jsxs14("div", { className: "flex min-h-9 items-center justify-between gap-3", children: [
2994
2998
  /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-col", children: [
2995
- /* @__PURE__ */ jsx14("span", { className: "truncate text-sm text-foreground", children: label }),
2996
- hint ? /* @__PURE__ */ jsx14("span", { className: "truncate text-xs text-muted-foreground", children: hint }) : null
2999
+ /* @__PURE__ */ jsx15("span", { className: "truncate text-sm text-foreground", children: label }),
3000
+ hint ? /* @__PURE__ */ jsx15("span", { className: "truncate text-xs text-muted-foreground", children: hint }) : null
2997
3001
  ] }),
2998
- /* @__PURE__ */ jsx14("div", { className: "flex shrink-0 items-center", children })
3002
+ /* @__PURE__ */ jsx15("div", { className: "flex shrink-0 items-center", children })
2999
3003
  ] });
3000
3004
  }
3001
3005
  function GenerateButton({ onUse, label, limits, disabled }) {
@@ -3005,8 +3009,8 @@ function GenerateButton({ onUse, label, limits, disabled }) {
3005
3009
  if (!source) return null;
3006
3010
  const name = label ?? "Generate password";
3007
3011
  return /* @__PURE__ */ jsxs14(Popover, { open, onOpenChange: setOpen, children: [
3008
- /* @__PURE__ */ jsx14(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx14(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx14(KeyGlyph, {}), "aria-label": name, title: name, disabled }) }),
3009
- /* @__PURE__ */ jsx14(PopoverContent, { sizing: "content", align: "end", children: open ? /* @__PURE__ */ jsx14(
3012
+ /* @__PURE__ */ jsx15(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx15(KeyGlyph, {}), "aria-label": name, title: name, disabled }) }),
3013
+ /* @__PURE__ */ jsx15(PopoverContent, { sizing: "content", align: "end", children: open ? /* @__PURE__ */ jsx15(
3010
3014
  PasswordGenerator,
3011
3015
  {
3012
3016
  limits: source,
@@ -3178,13 +3182,13 @@ function startNewRestoreRun(preview) {
3178
3182
  import { useState as useState16 } from "react";
3179
3183
  import { Checkbox } from "@ai-matrx/design-system";
3180
3184
  import { Badge as Badge4, Button as Button12, DisclosureHeader as DisclosureHeader2 } from "@ai-matrx/design-system/controls";
3181
- import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
3185
+ import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
3182
3186
  function PickLine({ checked, onChange, disabled = false, title, detail, badge, tone }) {
3183
3187
  return /* @__PURE__ */ jsxs15("label", { className: "flex min-h-11 items-center gap-3 px-4 py-1", "data-clickable": disabled ? void 0 : "", children: [
3184
- /* @__PURE__ */ jsx15(Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
3185
- /* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }),
3186
- detail ? /* @__PURE__ */ jsx15("span", { className: "hidden min-w-0 max-w-[45%] truncate text-xs text-muted-foreground sm:block", children: detail }) : null,
3187
- badge ? /* @__PURE__ */ jsx15(Badge4, { tone: tone ?? "neutral", children: badge }) : null
3188
+ /* @__PURE__ */ jsx16(Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
3189
+ /* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }),
3190
+ detail ? /* @__PURE__ */ jsx16("span", { className: "hidden min-w-0 max-w-[45%] truncate text-xs text-muted-foreground sm:block", children: detail }) : null,
3191
+ badge ? /* @__PURE__ */ jsx16(Badge4, { tone: tone ?? "neutral", children: badge }) : null
3188
3192
  ] });
3189
3193
  }
3190
3194
  function Windowed({ items, size = 150, render }) {
@@ -3192,7 +3196,7 @@ function Windowed({ items, size = 150, render }) {
3192
3196
  const shown = all ? items : items.slice(0, size);
3193
3197
  return /* @__PURE__ */ jsxs15(Fragment9, { children: [
3194
3198
  shown.map(render),
3195
- items.length > shown.length ? /* @__PURE__ */ jsx15("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsxs15(Button12, { variant: "quiet", onClick: () => setAll(true), children: [
3199
+ items.length > shown.length ? /* @__PURE__ */ jsx16("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsxs15(Button12, { variant: "quiet", onClick: () => setAll(true), children: [
3196
3200
  "Show all ",
3197
3201
  items.length
3198
3202
  ] }) }) : null
@@ -3201,17 +3205,17 @@ function Windowed({ items, size = 150, render }) {
3201
3205
  function More({ title = "More", children }) {
3202
3206
  const [open, setOpen] = useState16(false);
3203
3207
  return /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-2", children: [
3204
- /* @__PURE__ */ jsx15(DisclosureHeader2, { open, title, onClick: () => setOpen((o) => !o) }),
3208
+ /* @__PURE__ */ jsx16(DisclosureHeader2, { open, title, onClick: () => setOpen((o) => !o) }),
3205
3209
  open ? children : null
3206
3210
  ] });
3207
3211
  }
3208
3212
  function ErrorLine({ message: message2 }) {
3209
- return message2 ? /* @__PURE__ */ jsx15("p", { role: "alert", className: "px-1 text-sm text-destructive", children: message2 }) : null;
3213
+ return message2 ? /* @__PURE__ */ jsx16("p", { role: "alert", className: "px-1 text-sm text-destructive", children: message2 }) : null;
3210
3214
  }
3211
3215
  var plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
3212
3216
 
3213
3217
  // src/io/BackupPanel.tsx
3214
- import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
3218
+ import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
3215
3219
  var RECORD_STATUS = {
3216
3220
  created: "Restored",
3217
3221
  replayed: "Already restored",
@@ -3224,16 +3228,16 @@ function omissionText(o) {
3224
3228
  }
3225
3229
  function Summary({ value }) {
3226
3230
  return /* @__PURE__ */ jsxs16(VaultSection, { children: [
3227
- /* @__PURE__ */ jsx16(VaultRow, { label: "Credentials", children: value.record_count }),
3228
- /* @__PURE__ */ jsx16(VaultRow, { label: "Protected fields", children: value.field_count }),
3229
- /* @__PURE__ */ jsx16(VaultRow, { label: "Files", children: value.attachment_count > 0 ? `${value.attachment_count} \xB7 ${formatFileSize(value.attachment_bytes)}` : "0" })
3231
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Credentials", children: value.record_count }),
3232
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Protected fields", children: value.field_count }),
3233
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Files", children: value.attachment_count > 0 ? `${value.attachment_count} \xB7 ${formatFileSize(value.attachment_bytes)}` : "0" })
3230
3234
  ] });
3231
3235
  }
3232
3236
  function Omissions({ omissions, accepted, onAccepted, verb }) {
3233
3237
  if (omissions.length === 0) return null;
3234
3238
  return /* @__PURE__ */ jsxs16(VaultSection, { title: "Left out", children: [
3235
- omissions.map((o, i) => /* @__PURE__ */ jsx16(VaultRow, { label: o.component.replaceAll("_", " "), children: /* @__PURE__ */ jsx16("span", { className: "block truncate text-muted-foreground", title: omissionText(o), children: omissionText(o) }) }, `${o.component}-${o.source_item_id ?? i}`)),
3236
- /* @__PURE__ */ jsx16(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ jsx16(Switch3, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
3239
+ omissions.map((o, i) => /* @__PURE__ */ jsx17(VaultRow, { label: o.component.replaceAll("_", " "), children: /* @__PURE__ */ jsx17("span", { className: "block truncate text-muted-foreground", title: omissionText(o), children: omissionText(o) }) }, `${o.component}-${o.source_item_id ?? i}`)),
3240
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ jsx17(Switch3, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
3237
3241
  ] });
3238
3242
  }
3239
3243
  function BackupPanel({ port, items, onClose, onChanged }) {
@@ -3340,20 +3344,20 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3340
3344
  const mismatch = repeat !== "" && repeat !== passphrase;
3341
3345
  const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
3342
3346
  const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3343
- /* @__PURE__ */ jsx16(Button13, { variant: "quiet", onClick: onClose, children: "Close" }),
3344
- backup ? /* @__PURE__ */ jsx16(Button13, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3347
+ /* @__PURE__ */ jsx17(Button13, { variant: "quiet", onClick: onClose, children: "Close" }),
3348
+ backup ? /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3345
3349
  "Review ",
3346
3350
  selected.size
3347
3351
  ] })
3348
3352
  ] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
3349
- /* @__PURE__ */ jsx16(Button13, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
3350
- result ? retryable ? /* @__PURE__ */ jsx16(Button13, { variant: "primary", disabled: busy, onClick: () => void restore(), children: "Try again" }) : /* @__PURE__ */ jsx16(Button13, { variant: "outline", disabled: busy, onClick: () => void again(), children: "Restore again" }) : restorePreview ? /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || restorePreview.omissions.length > 0 && !accepted, onClick: () => void restore(), children: [
3353
+ /* @__PURE__ */ jsx17(Button13, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
3354
+ result ? retryable ? /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy, onClick: () => void restore(), children: "Try again" }) : /* @__PURE__ */ jsx17(Button13, { variant: "outline", disabled: busy, onClick: () => void again(), children: "Restore again" }) : restorePreview ? /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || restorePreview.omissions.length > 0 && !accepted, onClick: () => void restore(), children: [
3351
3355
  "Restore ",
3352
3356
  restorePreview.record_count
3353
- ] }) : /* @__PURE__ */ jsx16(Button13, { variant: "primary", disabled: busy || !file || !passphrase, onClick: () => void reviewRestore(), children: "Review" })
3357
+ ] }) : /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy || !file || !passphrase, onClick: () => void reviewRestore(), children: "Review" })
3354
3358
  ] });
3355
3359
  return /* @__PURE__ */ jsxs16(Panel, { title: "Backup", size: "lg", onClose, footer, children: [
3356
- /* @__PURE__ */ jsx16(
3360
+ /* @__PURE__ */ jsx17(
3357
3361
  SegmentedControl3,
3358
3362
  {
3359
3363
  value: mode,
@@ -3367,35 +3371,35 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3367
3371
  }
3368
3372
  ),
3369
3373
  mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3370
- /* @__PURE__ */ jsx16(
3374
+ /* @__PURE__ */ jsx17(
3371
3375
  VaultSection,
3372
3376
  {
3373
3377
  title: `${selected.size} of ${plural(items.length, "credential")}`,
3374
3378
  actions: /* @__PURE__ */ jsxs16(Fragment10, { children: [
3375
- /* @__PURE__ */ jsx16(Button13, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
3379
+ /* @__PURE__ */ jsx17(Button13, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
3376
3380
  setBackup(null);
3377
3381
  setSelected(new Set(items.map((i) => i.id)));
3378
3382
  }, children: "All" }),
3379
- /* @__PURE__ */ jsx16(Button13, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3383
+ /* @__PURE__ */ jsx17(Button13, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3380
3384
  setBackup(null);
3381
3385
  setSelected(/* @__PURE__ */ new Set());
3382
3386
  }, children: "None" })
3383
3387
  ] }),
3384
- children: items.length === 0 ? /* @__PURE__ */ jsx16(Empty, { children: "Nothing to back up yet." }) : /* @__PURE__ */ jsx16(Windowed, { items, render: (i) => /* @__PURE__ */ jsx16(PickLine, { title: i.name, detail: i.definitionLabel, checked: selected.has(i.id), disabled: busy, onChange: (on) => pick(i.id, on) }, i.id) })
3388
+ children: items.length === 0 ? /* @__PURE__ */ jsx17(Empty, { children: "Nothing to back up yet." }) : /* @__PURE__ */ jsx17(Windowed, { items, render: (i) => /* @__PURE__ */ jsx17(PickLine, { title: i.name, detail: i.definitionLabel, checked: selected.has(i.id), disabled: busy, onChange: (on) => pick(i.id, on) }, i.id) })
3385
3389
  }
3386
3390
  ),
3387
3391
  backup ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3388
- /* @__PURE__ */ jsx16(Summary, { value: backup }),
3389
- /* @__PURE__ */ jsx16(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3392
+ /* @__PURE__ */ jsx17(Summary, { value: backup }),
3393
+ /* @__PURE__ */ jsx17(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3390
3394
  /* @__PURE__ */ jsxs16(VaultSection, { children: [
3391
- /* @__PURE__ */ jsx16(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ jsx16(Field6, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
3392
- /* @__PURE__ */ jsx16(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ jsx16(Field6, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
3395
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
3396
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
3393
3397
  ] })
3394
3398
  ] }) : null
3395
- ] }) : /* @__PURE__ */ jsx16(Fragment10, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
3399
+ ] }) : /* @__PURE__ */ jsx17(Fragment10, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
3396
3400
  /* @__PURE__ */ jsxs16(VaultRow, { label: "File", ...file ? { hint: file.name } : {}, children: [
3397
- /* @__PURE__ */ jsx16(Button13, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
3398
- /* @__PURE__ */ jsx16(
3401
+ /* @__PURE__ */ jsx17(Button13, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
3402
+ /* @__PURE__ */ jsx17(
3399
3403
  "input",
3400
3404
  {
3401
3405
  ref: fileInput,
@@ -3409,16 +3413,16 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3409
3413
  }
3410
3414
  )
3411
3415
  ] }),
3412
- /* @__PURE__ */ jsx16(VaultRow, { label: "Passphrase", children: /* @__PURE__ */ jsx16(Field6, { type: "password", value: passphrase, autoComplete: "current-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) })
3416
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Passphrase", children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: passphrase, autoComplete: "current-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) })
3413
3417
  ] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
3414
- /* @__PURE__ */ jsx16(Summary, { value: restorePreview }),
3415
- !result ? /* @__PURE__ */ jsx16(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
3416
- !result ? /* @__PURE__ */ jsx16("p", { className: "px-1 text-xs text-muted-foreground", children: "Restored items start disabled and restricted." }) : /* @__PURE__ */ jsx16(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__ */ jsx16(VaultRow, { label: names.get(r.source_item_id) ?? "Credential", children: /* @__PURE__ */ jsxs16("span", { className: r.status === "refused" || r.status === "retryable_failure" ? "text-destructive" : "text-foreground", children: [
3418
+ /* @__PURE__ */ jsx17(Summary, { value: restorePreview }),
3419
+ !result ? /* @__PURE__ */ jsx17(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
3420
+ !result ? /* @__PURE__ */ jsx17("p", { className: "px-1 text-xs text-muted-foreground", children: "Restored items start disabled and restricted." }) : /* @__PURE__ */ jsx17(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__ */ jsx17(VaultRow, { label: names.get(r.source_item_id) ?? "Credential", children: /* @__PURE__ */ jsxs16("span", { className: r.status === "refused" || r.status === "retryable_failure" ? "text-destructive" : "text-foreground", children: [
3417
3421
  RECORD_STATUS[r.status],
3418
3422
  r.reason === "alias_conflict" ? " \xB7 name in use" : ""
3419
3423
  ] }) }, r.source_item_id)) })
3420
3424
  ] }) }),
3421
- /* @__PURE__ */ jsx16(ErrorLine, { message: error })
3425
+ /* @__PURE__ */ jsx17(ErrorLine, { message: error })
3422
3426
  ] });
3423
3427
  }
3424
3428
 
@@ -3427,7 +3431,7 @@ import { useMemo, useState as useState18 } from "react";
3427
3431
  import { Button as Button14, Select as Select5 } from "@ai-matrx/design-system/controls";
3428
3432
  import { downloadFile as downloadFile2 } from "@ai-matrx/kit/download";
3429
3433
  import { confirm as confirm5 } from "@ai-matrx/kit/confirm-opener";
3430
- import { Fragment as Fragment11, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
3434
+ import { Fragment as Fragment11, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
3431
3435
  var FORMATS = [
3432
3436
  { value: "matrx_login_csv_v1", label: "Matrx", hint: "Website logins, all common fields" },
3433
3437
  { value: "firefox_csv_v1", label: "Firefox", hint: "Address, username and password" },
@@ -3508,8 +3512,8 @@ function ExportPanel({ port, items, onClose }) {
3508
3512
  size: "lg",
3509
3513
  onClose,
3510
3514
  footer: /* @__PURE__ */ jsxs17(Fragment11, { children: [
3511
- /* @__PURE__ */ jsx17(Button14, { variant: "quiet", onClick: onClose, children: "Close" }),
3512
- preview ? /* @__PURE__ */ jsxs17(Button14, { variant: "primary", icon: /* @__PURE__ */ jsx17(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
3515
+ /* @__PURE__ */ jsx18(Button14, { variant: "quiet", onClick: onClose, children: "Close" }),
3516
+ preview ? /* @__PURE__ */ jsxs17(Button14, { variant: "primary", icon: /* @__PURE__ */ jsx18(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
3513
3517
  "Download ",
3514
3518
  eligible
3515
3519
  ] }) : /* @__PURE__ */ jsxs17(Button14, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void review(), children: [
@@ -3518,7 +3522,7 @@ function ExportPanel({ port, items, onClose }) {
3518
3522
  ] })
3519
3523
  ] }),
3520
3524
  children: [
3521
- /* @__PURE__ */ jsx17(VaultSection, { children: /* @__PURE__ */ jsx17(VaultRow, { label: "Format", hint: format?.hint, children: /* @__PURE__ */ jsx17(
3525
+ /* @__PURE__ */ jsx18(VaultSection, { children: /* @__PURE__ */ jsx18(VaultRow, { label: "Format", hint: format?.hint, children: /* @__PURE__ */ jsx18(
3522
3526
  Select5,
3523
3527
  {
3524
3528
  value: profile,
@@ -3530,21 +3534,21 @@ function ExportPanel({ port, items, onClose }) {
3530
3534
  }
3531
3535
  }
3532
3536
  ) }) }),
3533
- /* @__PURE__ */ jsx17(
3537
+ /* @__PURE__ */ jsx18(
3534
3538
  VaultSection,
3535
3539
  {
3536
3540
  title: `${selected.size} of ${plural(logins.length, "login")}`,
3537
3541
  actions: /* @__PURE__ */ jsxs17(Fragment11, { children: [
3538
- /* @__PURE__ */ jsx17(Button14, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
3542
+ /* @__PURE__ */ jsx18(Button14, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
3539
3543
  changed();
3540
3544
  setSelected(new Set(logins.map((l) => l.id)));
3541
3545
  }, children: "All" }),
3542
- /* @__PURE__ */ jsx17(Button14, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3546
+ /* @__PURE__ */ jsx18(Button14, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
3543
3547
  changed();
3544
3548
  setSelected(/* @__PURE__ */ new Set());
3545
3549
  }, children: "None" })
3546
3550
  ] }),
3547
- children: logins.length === 0 ? /* @__PURE__ */ jsx17(Empty, { children: "No website logins to export." }) : /* @__PURE__ */ jsx17(
3551
+ children: logins.length === 0 ? /* @__PURE__ */ jsx18(Empty, { children: "No website logins to export." }) : /* @__PURE__ */ jsx18(
3548
3552
  Windowed,
3549
3553
  {
3550
3554
  items: logins,
@@ -3552,7 +3556,7 @@ function ExportPanel({ port, items, onClose }) {
3552
3556
  const p = reasons.get(item.id);
3553
3557
  const left = p && !p.eligible;
3554
3558
  const omitted = p ? omissionText2(p.omissions) : "";
3555
- return /* @__PURE__ */ jsx17(
3559
+ return /* @__PURE__ */ jsx18(
3556
3560
  PickLine,
3557
3561
  {
3558
3562
  title: item.name,
@@ -3570,7 +3574,7 @@ function ExportPanel({ port, items, onClose }) {
3570
3574
  )
3571
3575
  }
3572
3576
  ),
3573
- /* @__PURE__ */ jsx17(ErrorLine, { message: error })
3577
+ /* @__PURE__ */ jsx18(ErrorLine, { message: error })
3574
3578
  ]
3575
3579
  }
3576
3580
  );
@@ -4088,7 +4092,7 @@ import { BlobReader as BlobReader2, ZipReader as ZipReader2 } from "@zip.js/zip.
4088
4092
  var safe = (value) => Number.isSafeInteger(value) && value >= 0;
4089
4093
  var unsafeName = (name) => /[\\\0-\x1F\x7F-\x9F]/.test(name);
4090
4094
  function stopIfCancelled(signal) {
4091
- if (signal?.aborted) throw new Error("The archive import was cancelled.");
4095
+ if (signal?.aborted) throw new Error("The archive import was canceled.");
4092
4096
  }
4093
4097
  function hasWarnings(reader, entry) {
4094
4098
  return Boolean(reader.warnings?.length || entry.warnings?.length);
@@ -4189,7 +4193,7 @@ async function openBoundedZip(file, limits, signal) {
4189
4193
  };
4190
4194
  } catch (error) {
4191
4195
  await reader.close();
4192
- if (signal?.aborted) throw new Error("The archive import was cancelled.");
4196
+ if (signal?.aborted) throw new Error("The archive import was canceled.");
4193
4197
  throw error;
4194
4198
  }
4195
4199
  }
@@ -4228,7 +4232,7 @@ async function readDashlaneCsvArchive(file, limits, signal) {
4228
4232
  return { credentialsText, omittedCsvCount };
4229
4233
  } catch (error) {
4230
4234
  credentialsText = void 0;
4231
- if (signal?.aborted) throw new Error("The Dashlane import was cancelled.");
4235
+ if (signal?.aborted) throw new Error("The Dashlane import was canceled.");
4232
4236
  throw error;
4233
4237
  } finally {
4234
4238
  credentialsText = void 0;
@@ -4363,7 +4367,7 @@ function hasValidStructuredImportFileNotices(value, maxCount) {
4363
4367
  }
4364
4368
 
4365
4369
  // src/io/parse-source.ts
4366
- var cancelled = () => new Error("The import was cancelled.");
4370
+ var cancelled = () => new Error("The import was canceled.");
4367
4371
  function parseStructured(source, file, limits, signal) {
4368
4372
  const descriptor = structuredImportSource(source);
4369
4373
  if (!descriptor) return Promise.reject(new Error("This kind of file cannot be imported."));
@@ -4416,7 +4420,7 @@ async function parseImportFile(source, file, limits, signal) {
4416
4420
  }
4417
4421
 
4418
4422
  // src/io/ImportPanel.tsx
4419
- import { Fragment as Fragment12, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4423
+ import { Fragment as Fragment12, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
4420
4424
  var ROLE_OPTIONS = [
4421
4425
  { value: "keep", label: "Keep encrypted" },
4422
4426
  { value: "title", label: "Name" },
@@ -4592,25 +4596,25 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4592
4596
  const stage = done ? "done" : draft ? "review" : "choose";
4593
4597
  const busy = running !== null || reading;
4594
4598
  const footer = stage === "choose" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4595
- /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: onClose, children: "Close" }),
4596
- source === "env" ? /* @__PURE__ */ jsx18(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx18(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
4599
+ /* @__PURE__ */ jsx19(Button15, { variant: "quiet", onClick: onClose, children: "Close" }),
4600
+ source === "env" ? /* @__PURE__ */ jsx19(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx19(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
4597
4601
  ] }) : stage === "review" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4598
- running ? /* @__PURE__ */ jsx18(Button15, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: reset, children: "Back" }),
4599
- /* @__PURE__ */ jsx18(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx18(ImportGlyph, { className: "size-4" }), disabled: busy || chosen === 0 || ambiguous === true || needsApproval, onClick: start, children: running ? `Importing ${running.done} of ${running.total}` : `Import ${chosen}` })
4602
+ running ? /* @__PURE__ */ jsx19(Button15, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ jsx19(Button15, { variant: "quiet", onClick: reset, children: "Back" }),
4603
+ /* @__PURE__ */ jsx19(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx19(ImportGlyph, { className: "size-4" }), disabled: busy || chosen === 0 || ambiguous === true || needsApproval, onClick: start, children: running ? `Importing ${running.done} of ${running.total}` : `Import ${chosen}` })
4600
4604
  ] }) : /* @__PURE__ */ jsxs18(Fragment12, { children: [
4601
- done && done.failed.length + done.untried.length > 0 ? /* @__PURE__ */ jsx18(Button15, { variant: "outline", disabled: busy, onClick: retry, children: running ? `Importing ${running.done} of ${running.total}` : `Try ${done.failed.length + done.untried.length} again` }) : null,
4602
- /* @__PURE__ */ jsx18(Button15, { variant: "primary", onClick: onClose, children: "Done" })
4605
+ done && done.failed.length + done.untried.length > 0 ? /* @__PURE__ */ jsx19(Button15, { variant: "outline", disabled: busy, onClick: retry, children: running ? `Importing ${running.done} of ${running.total}` : `Try ${done.failed.length + done.untried.length} again` }) : null,
4606
+ /* @__PURE__ */ jsx19(Button15, { variant: "primary", onClick: onClose, children: "Done" })
4603
4607
  ] });
4604
4608
  return /* @__PURE__ */ jsxs18(Panel, { title: "Import", size: "xl", onClose, footer, children: [
4605
4609
  stage === "choose" ? /* @__PURE__ */ jsxs18(VaultSection, { children: [
4606
- /* @__PURE__ */ jsx18(VaultRow, { label: "Source", actions: help && host.openUrl ? /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: () => host.openUrl?.(help), children: "How to export" }) : void 0, children: /* @__PURE__ */ jsx18(Select6, { value: source, options: SOURCE_OPTIONS, "aria-label": "Source", onValueChange: (s) => {
4610
+ /* @__PURE__ */ jsx19(VaultRow, { label: "Source", actions: help && host.openUrl ? /* @__PURE__ */ jsx19(Button15, { variant: "quiet", onClick: () => host.openUrl?.(help), children: "How to export" }) : void 0, children: /* @__PURE__ */ jsx19(Select6, { value: source, options: SOURCE_OPTIONS, "aria-label": "Source", onValueChange: (s) => {
4607
4611
  setSource(s);
4608
4612
  setError(null);
4609
4613
  } }) }),
4610
- source === "env" ? /* @__PURE__ */ jsx18(VaultRow, { label: ".env text", hint: "Same key updates in place", top: true, children: /* @__PURE__ */ jsx18(Textarea2, { 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,
4614
+ source === "env" ? /* @__PURE__ */ jsx19(VaultRow, { label: ".env text", hint: "Same key updates in place", top: true, children: /* @__PURE__ */ jsx19(Textarea2, { 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,
4611
4615
  /* @__PURE__ */ jsxs18(VaultRow, { label: "File", hint: fileName ?? void 0, children: [
4612
- /* @__PURE__ */ jsx18(Button15, { variant: "outline", disabled: reading, onClick: () => fileInput.current?.click(), children: reading ? "Reading\u2026" : source === "env" ? "Load a file" : "Choose a file" }),
4613
- /* @__PURE__ */ jsx18(
4616
+ /* @__PURE__ */ jsx19(Button15, { variant: "outline", disabled: reading, onClick: () => fileInput.current?.click(), children: reading ? "Reading\u2026" : source === "env" ? "Load a file" : "Choose a file" }),
4617
+ /* @__PURE__ */ jsx19(
4614
4618
  "input",
4615
4619
  {
4616
4620
  ref: fileInput,
@@ -4625,44 +4629,44 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4625
4629
  }
4626
4630
  )
4627
4631
  ] }),
4628
- source === "env" ? /* @__PURE__ */ jsx18(VaultRow, { label: "Sandboxes", hint: "Pass these values to agent sandboxes", children: /* @__PURE__ */ jsx18(Switch4, { checked: injectSandbox, onCheckedChange: setInjectSandbox, "aria-label": "Use in sandboxes" }) }) : null
4632
+ source === "env" ? /* @__PURE__ */ jsx19(VaultRow, { label: "Sandboxes", hint: "Pass these values to agent sandboxes", children: /* @__PURE__ */ jsx19(Switch4, { checked: injectSandbox, onCheckedChange: setInjectSandbox, "aria-label": "Use in sandboxes" }) }) : null
4629
4633
  ] }) : null,
4630
4634
  stage === "review" && draft ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4631
- organizations.length > 0 ? /* @__PURE__ */ jsx18(VaultSection, { children: /* @__PURE__ */ jsx18(VaultRow, { label: "Save to", children: /* @__PURE__ */ jsx18(Select6, { value: destination, options: destinationOptions, "aria-label": "Save to", onValueChange: (v) => draft.kind === "file" ? restart({ destination: v }) : setDestination(v) }) }) }) : null,
4632
- /* @__PURE__ */ jsx18(
4635
+ organizations.length > 0 ? /* @__PURE__ */ jsx19(VaultSection, { children: /* @__PURE__ */ jsx19(VaultRow, { label: "Save to", children: /* @__PURE__ */ jsx19(Select6, { value: destination, options: destinationOptions, "aria-label": "Save to", onValueChange: (v) => draft.kind === "file" ? restart({ destination: v }) : setDestination(v) }) }) }) : null,
4636
+ /* @__PURE__ */ jsx19(
4633
4637
  VaultSection,
4634
4638
  {
4635
4639
  title: draft.kind === "env" ? plural(envKeys.length, "value") : summary(counts, lines.length),
4636
4640
  actions: /* @__PURE__ */ jsxs18(Fragment12, { children: [
4637
- /* @__PURE__ */ jsx18(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(draft.kind === "env" ? new Set(envKeys.map((e) => e.key + e.text)) : defaultSelection(lines)), children: "Reset" }),
4638
- /* @__PURE__ */ jsx18(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
4641
+ /* @__PURE__ */ jsx19(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(draft.kind === "env" ? new Set(envKeys.map((e) => e.key + e.text)) : defaultSelection(lines)), children: "Reset" }),
4642
+ /* @__PURE__ */ jsx19(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
4639
4643
  ] }),
4640
- children: draft.kind === "env" ? /* @__PURE__ */ jsx18(
4644
+ children: draft.kind === "env" ? /* @__PURE__ */ jsx19(
4641
4645
  Windowed,
4642
4646
  {
4643
4647
  items: envKeys,
4644
4648
  render: (e) => {
4645
4649
  const id = e.key + e.text;
4646
- return /* @__PURE__ */ jsx18(PickLine, { title: e.key, detail: "Value hidden", checked: selected.has(id), disabled: busy, onChange: (on) => toggle(setSelected, id, on) }, id);
4650
+ return /* @__PURE__ */ jsx19(PickLine, { title: e.key, detail: "Value hidden", checked: selected.has(id), disabled: busy, onChange: (on) => toggle(setSelected, id, on) }, id);
4647
4651
  }
4648
4652
  }
4649
- ) : lines.length === 0 ? /* @__PURE__ */ jsx18(Empty, { children: "Nothing to import in this file." }) : /* @__PURE__ */ jsx18(
4653
+ ) : lines.length === 0 ? /* @__PURE__ */ jsx19(Empty, { children: "Nothing to import in this file." }) : /* @__PURE__ */ jsx19(
4650
4654
  Windowed,
4651
4655
  {
4652
4656
  items: lines,
4653
4657
  render: (l) => {
4654
4658
  const badge = STATE_BADGE[l.state];
4655
- return /* @__PURE__ */ jsx18(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);
4659
+ return /* @__PURE__ */ jsx19(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);
4656
4660
  }
4657
4661
  }
4658
4662
  )
4659
4663
  }
4660
4664
  ),
4661
- draft.kind === "file" && draft.parsed.kind === "structured" ? /* @__PURE__ */ jsx18(VaultSection, { children: /* @__PURE__ */ jsx18(VaultRow, { label: "Visible details", hint: "Approve saving websites and public keys in the clear", children: /* @__PURE__ */ jsx18(Switch4, { checked: approvedVisible, disabled: busy, onCheckedChange: setApprovedVisible, "aria-label": "Approve visible details" }) }) }) : null,
4662
- draft.kind === "file" ? /* @__PURE__ */ jsx18(Notices, { draft }) : null,
4663
- draft.kind === "file" ? /* @__PURE__ */ jsx18(More, { children: /* @__PURE__ */ jsxs18(VaultSection, { children: [
4664
- /* @__PURE__ */ jsx18(VaultRow, { label: "Browser fill", hint: "Needs username, password and an https address", children: /* @__PURE__ */ jsx18(Switch4, { checked: browserFill, disabled: busy, onCheckedChange: (on) => restart({ browserFill: on }), "aria-label": "Browser fill" }) }),
4665
- draft.parsed.kind === "csv" ? draft.parsed.preview.headers.map((header, index) => /* @__PURE__ */ jsx18(VaultRow, { label: header || `Column ${index + 1}`, children: /* @__PURE__ */ jsx18(
4665
+ draft.kind === "file" && draft.parsed.kind === "structured" ? /* @__PURE__ */ jsx19(VaultSection, { children: /* @__PURE__ */ jsx19(VaultRow, { label: "Visible details", hint: "Approve saving websites and public keys in the clear", children: /* @__PURE__ */ jsx19(Switch4, { checked: approvedVisible, disabled: busy, onCheckedChange: setApprovedVisible, "aria-label": "Approve visible details" }) }) }) : null,
4666
+ draft.kind === "file" ? /* @__PURE__ */ jsx19(Notices, { draft }) : null,
4667
+ draft.kind === "file" ? /* @__PURE__ */ jsx19(More, { children: /* @__PURE__ */ jsxs18(VaultSection, { children: [
4668
+ /* @__PURE__ */ jsx19(VaultRow, { label: "Browser fill", hint: "Needs username, password and an https address", children: /* @__PURE__ */ jsx19(Switch4, { checked: browserFill, disabled: busy, onCheckedChange: (on) => restart({ browserFill: on }), "aria-label": "Browser fill" }) }),
4669
+ draft.parsed.kind === "csv" ? draft.parsed.preview.headers.map((header, index) => /* @__PURE__ */ jsx19(VaultRow, { label: header || `Column ${index + 1}`, children: /* @__PURE__ */ jsx19(
4666
4670
  Select6,
4667
4671
  {
4668
4672
  value: draft.mapping[index] ?? "keep",
@@ -4672,20 +4676,20 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4672
4676
  }
4673
4677
  ) }, `${header}-${index}`)) : null
4674
4678
  ] }) }) : null,
4675
- ambiguous ? /* @__PURE__ */ jsx18(ErrorLine, { message: "Each of name, username, password, notes and one-time password can be used by one column." }) : null
4679
+ ambiguous ? /* @__PURE__ */ jsx19(ErrorLine, { message: "Each of name, username, password, notes and one-time password can be used by one column." }) : null
4676
4680
  ] }) : null,
4677
4681
  stage === "done" && done ? /* @__PURE__ */ jsxs18(VaultSection, { title: "Result", children: [
4678
- /* @__PURE__ */ jsx18(VaultRow, { label: "Created", children: /* @__PURE__ */ jsx18("span", { className: "text-foreground", children: done.created.length }) }),
4679
- /* @__PURE__ */ jsx18(VaultRow, { label: "Skipped", children: /* @__PURE__ */ jsx18("span", { className: "text-foreground", children: done.skipped }) }),
4680
- /* @__PURE__ */ jsx18(VaultRow, { label: "Failed", children: /* @__PURE__ */ jsx18("span", { className: done.failed.length > 0 ? "text-destructive" : "text-foreground", children: done.failed.length }) }),
4681
- done.untried.length > 0 ? /* @__PURE__ */ jsx18(VaultRow, { label: "Not tried", children: /* @__PURE__ */ jsx18("span", { className: "text-foreground", children: done.untried.length }) }) : null,
4682
- done.withOtp > 0 ? /* @__PURE__ */ jsx18(VaultRow, { label: "Codes", children: /* @__PURE__ */ jsxs18("span", { className: "text-muted-foreground", children: [
4682
+ /* @__PURE__ */ jsx19(VaultRow, { label: "Created", children: /* @__PURE__ */ jsx19("span", { className: "text-foreground", children: done.created.length }) }),
4683
+ /* @__PURE__ */ jsx19(VaultRow, { label: "Skipped", children: /* @__PURE__ */ jsx19("span", { className: "text-foreground", children: done.skipped }) }),
4684
+ /* @__PURE__ */ jsx19(VaultRow, { label: "Failed", children: /* @__PURE__ */ jsx19("span", { className: done.failed.length > 0 ? "text-destructive" : "text-foreground", children: done.failed.length }) }),
4685
+ done.untried.length > 0 ? /* @__PURE__ */ jsx19(VaultRow, { label: "Not tried", children: /* @__PURE__ */ jsx19("span", { className: "text-foreground", children: done.untried.length }) }) : null,
4686
+ done.withOtp > 0 ? /* @__PURE__ */ jsx19(VaultRow, { label: "Codes", children: /* @__PURE__ */ jsxs18("span", { className: "text-muted-foreground", children: [
4683
4687
  plural(done.withOtp, "login"),
4684
4688
  " still need the one-time password added."
4685
4689
  ] }) }) : null,
4686
- done.failed.map((f) => /* @__PURE__ */ jsx18(VaultRow, { label: f.title, children: /* @__PURE__ */ jsx18("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
4690
+ done.failed.map((f) => /* @__PURE__ */ jsx19(VaultRow, { label: f.title, children: /* @__PURE__ */ jsx19("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
4687
4691
  ] }) : null,
4688
- /* @__PURE__ */ jsx18(ErrorLine, { message: error })
4692
+ /* @__PURE__ */ jsx19(ErrorLine, { message: error })
4689
4693
  ] });
4690
4694
  }
4691
4695
  function toggle(set, key, on) {
@@ -4712,7 +4716,7 @@ function Notices({ draft }) {
4712
4716
  }
4713
4717
  }
4714
4718
  if (notes.length === 0) return null;
4715
- return /* @__PURE__ */ jsx18("div", { className: "flex flex-col gap-0.5 px-1 text-xs text-muted-foreground", children: notes.map((n) => /* @__PURE__ */ jsx18("span", { children: n }, n)) });
4719
+ return /* @__PURE__ */ jsx19("div", { className: "flex flex-col gap-0.5 px-1 text-xs text-muted-foreground", children: notes.map((n) => /* @__PURE__ */ jsx19("span", { children: n }, n)) });
4716
4720
  }
4717
4721
 
4718
4722
  // src/ItemList.tsx
@@ -4721,20 +4725,6 @@ import { DropdownMenu as DropdownMenu4, DropdownMenuContent as DropdownMenuConte
4721
4725
  import { cn as cn13 } from "@ai-matrx/design-system";
4722
4726
  import { Button as Button22, SearchField } from "@ai-matrx/design-system/controls";
4723
4727
 
4724
- // src/ItemIcon.tsx
4725
- import { cn as cn8 } from "@ai-matrx/design-system";
4726
- import { jsx as jsx19 } from "react/jsx-runtime";
4727
- function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
4728
- const box = size === "lg" ? "size-10 rounded-xl text-base" : size === "md" ? "size-8 rounded-lg text-sm" : "size-6 rounded-md text-xs";
4729
- const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
4730
- const initial = siteDomainOf(site).charAt(0).toUpperCase();
4731
- if (initial && /[A-Z0-9]/.test(initial)) {
4732
- return /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn8("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
4733
- }
4734
- const Glyph = definitionGlyph(definitionKey, family);
4735
- return /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn8("flex shrink-0 items-center justify-center", box, tone), children: /* @__PURE__ */ jsx19(Glyph, { className: size === "lg" ? "size-5" : "size-4" }) });
4736
- }
4737
-
4738
4728
  // src/ItemPane.tsx
4739
4729
  import { useEffect as useEffect16, useMemo as useMemo3, useRef as useRef14, useState as useState26 } from "react";
4740
4730
  import { DropdownMenu as DropdownMenu3, DropdownMenuContent as DropdownMenuContent3, DropdownMenuItem as DropdownMenuItem3, DropdownMenuSeparator as DropdownMenuSeparator3, DropdownMenuTrigger as DropdownMenuTrigger3 } from "@ai-matrx/design-system";
@@ -5657,6 +5647,8 @@ function ItemPane(props) {
5657
5647
  const [tagsText, setTagsText] = useState26(base.tags.join(", "));
5658
5648
  const [showTags, setShowTags] = useState26(false);
5659
5649
  const [showDescription, setShowDescription] = useState26(false);
5650
+ const [showRecovery, setShowRecovery] = useState26(false);
5651
+ const [settingUpOtp, setSettingUpOtp] = useState26(false);
5660
5652
  const [owner, setOwner] = useState26(defaultOwner ?? "me");
5661
5653
  const [recipient, setRecipient] = useState26("");
5662
5654
  const [generatePrivately, setGeneratePrivately] = useState26(false);
@@ -5685,6 +5677,7 @@ function ItemPane(props) {
5685
5677
  setLoaded({});
5686
5678
  setShowTags(false);
5687
5679
  setShowDescription(false);
5680
+ setShowRecovery(false);
5688
5681
  }, [item?.id, mode]);
5689
5682
  useEffect16(() => onDirty(dirty), [dirty, onDirty]);
5690
5683
  const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
@@ -5712,23 +5705,24 @@ function ItemPane(props) {
5712
5705
  setBusy(true);
5713
5706
  setError(null);
5714
5707
  const out = { ...draft, values: Object.fromEntries(changedValues(base, draft)) };
5708
+ if (out.values[RECOVERY_CODES_KEY] !== void 0) out.values[RECOVERY_CODES_KEY] = parseRecoveryCodes(out.values[RECOVERY_CODES_KEY]).join("\n");
5715
5709
  const withOtp = async (saved, notice2) => {
5716
5710
  const setup = out.authenticator.trim();
5717
- if (!setup) return [saved, notice2];
5711
+ if (!setup) return [saved, notice2, false];
5718
5712
  try {
5719
5713
  await confirmed(() => port.authenticator.enrol(saved, setup));
5720
- return [await port.get(saved), notice2];
5714
+ return [await port.get(saved), notice2, true];
5721
5715
  } catch (e) {
5722
5716
  const why = wasCancelled(e) ? "it was not confirmed" : e instanceof Error ? e.message : "it was refused";
5723
- return [saved, `Saved, but the one-time password was not set up: ${why}. Add it with Edit.`];
5717
+ return [saved, `Saved, but the authenticator was not set up: ${why}. Add it with Edit.`, false];
5724
5718
  }
5725
5719
  };
5726
5720
  try {
5727
5721
  if (item) {
5728
5722
  const r = await port.save(item, definition, out);
5729
- const [next, n] = await withOtp(r.item, r.notice);
5723
+ const [next, n, enrolled] = await withOtp(r.item, r.notice);
5730
5724
  dropKeptDraft(keepKey);
5731
- onSaved(next, false, n);
5725
+ onSaved(next, false, n, enrolled);
5732
5726
  } else if (definition && owner === "person") {
5733
5727
  const secretKey = declared.find((f) => f.storage === "encrypted" && f.kind === "secret")?.key ?? null;
5734
5728
  const r = await port.sharing.assign({ recipientEmail: recipient.trim(), definition, draft: out, generateFieldKey: generatePrivately ? secretKey : null });
@@ -5736,9 +5730,9 @@ function ItemPane(props) {
5736
5730
  onCancelNew();
5737
5731
  } else if (definition) {
5738
5732
  const r = await port.create(definition, owner === "me" ? null : owner, out);
5739
- const [next, n] = await withOtp(r.item, r.notice);
5733
+ const [next, n, enrolled] = await withOtp(r.item, r.notice);
5740
5734
  dropKeptDraft(keepKey);
5741
- onSaved(next, true, n);
5735
+ onSaved(next, true, n, enrolled);
5742
5736
  }
5743
5737
  } catch (e) {
5744
5738
  setError(e instanceof Error ? e.message : "Saving failed. Nothing was changed.");
@@ -5797,6 +5791,10 @@ function ItemPane(props) {
5797
5791
  const viewCustom = (item?.custom ?? []).filter((c) => !declaredKeys.has(c.key) && c.key !== MFA_METHOD_KEY && !isAuthenticatorStorage(c.key));
5798
5792
  const isLogin = (item?.definitionKey ?? definition?.key) === "website_login" || (item?.definitionKey ?? definition?.key) === "username_password";
5799
5793
  const typeLabel = definition?.label ?? item?.definitionLabel ?? "";
5794
+ const recoveryDeclared = declaredKeys.has(RECOVERY_CODES_KEY);
5795
+ const recoveryExtra = extraFields.some(isRecoveryCodesField);
5796
+ const offerRecovery = isLogin || Boolean(item?.authenticator) || draft.authenticator.trim() !== "";
5797
+ const recoveryEditing = !recoveryDeclared && !recoveryExtra && (showRecovery || (draft.values[RECOVERY_CODES_KEY] ?? "") !== "");
5800
5798
  const shownOwner = mode === "new" ? owner === "me" ? { label: "Personal", glyph: /* @__PURE__ */ jsx26(PersonGlyph, {}) } : owner === "person" ? { label: "Someone else", glyph: /* @__PURE__ */ jsx26(SendGlyph, {}) } : { label: organizations.find((o) => o.id === owner)?.name ?? "Organization", glyph: /* @__PURE__ */ jsx26(BuildingGlyph, {}) } : item ? { label: item.owner.label, glyph: item.owner.kind === "me" ? /* @__PURE__ */ jsx26(PersonGlyph, {}) : item.owner.kind === "organization" ? /* @__PURE__ */ jsx26(BuildingGlyph, {}) : /* @__PURE__ */ jsx26(UsersIcon2, {}) } : null;
5801
5799
  const sameNamed = (id) => {
5802
5800
  const name = organizations.find((o) => o.id === id)?.name.trim().toLowerCase();
@@ -5944,7 +5942,8 @@ function ItemPane(props) {
5944
5942
  label: f.label,
5945
5943
  value: draft.values[f.key] ?? "",
5946
5944
  placeholder: f.handling === "visible" || f.key in loaded ? "" : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
5947
- secret: f.handling !== "visible",
5945
+ secret: f.handling !== "visible" && !isRecoveryCodesField(f),
5946
+ multiline: isRecoveryCodesField(f),
5948
5947
  onChange: (v) => setValue(f.key, v),
5949
5948
  onRemove: () => patch({ removed: [...draft.removed, f.id] }),
5950
5949
  removeLabel: `Delete ${f.label}`,
@@ -5981,7 +5980,27 @@ function ItemPane(props) {
5981
5980
  )),
5982
5981
  /* @__PURE__ */ jsx26(AddRow, { label: draft.loginUrls.length === 0 ? "Website" : "", onAdd: () => patch({ loginUrls: [...draft.loginUrls, ""] }), children: draft.loginUrls.length === 0 ? "Add website" : "Add another website" }),
5983
5982
  /* @__PURE__ */ jsx26(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }),
5984
- item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
5983
+ item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null,
5984
+ recoveryEditing ? /* @__PURE__ */ jsx26(
5985
+ EditRow,
5986
+ {
5987
+ label: "Recovery codes",
5988
+ value: draft.values[RECOVERY_CODES_KEY] ?? "",
5989
+ multiline: true,
5990
+ autoFocus: showRecovery && (draft.values[RECOVERY_CODES_KEY] ?? "") === "",
5991
+ placeholder: "Backup codes, one per line",
5992
+ onChange: (v) => setValue(RECOVERY_CODES_KEY, v),
5993
+ onRemove: () => {
5994
+ setShowRecovery(false);
5995
+ setDraft((d) => {
5996
+ const values = { ...d.values };
5997
+ delete values[RECOVERY_CODES_KEY];
5998
+ return { ...d, values };
5999
+ });
6000
+ },
6001
+ removeLabel: "Remove recovery codes"
6002
+ }
6003
+ ) : !recoveryDeclared && !recoveryExtra && offerRecovery ? /* @__PURE__ */ jsx26(AddRow, { label: "Recovery codes", onAdd: () => setShowRecovery(true), children: "Add backup codes" }) : null
5985
6004
  ] }),
5986
6005
  /* @__PURE__ */ jsx26(VaultSection, { children: /* @__PURE__ */ jsx26(EditRow, { label: "Notes", value: draft.notes, multiline: true, placeholder: "Anything worth remembering", onChange: (v) => patch({ notes: v }) }) }),
5987
6006
  /* @__PURE__ */ jsxs24(
@@ -5994,7 +6013,8 @@ function ItemPane(props) {
5994
6013
  /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => patch({ secrets: [...draft.secrets, { key: "", label: "", value: "" }] }), children: "Secret field" }),
5995
6014
  /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => patch({ custom: [...draft.custom, { key: `custom_${draft.custom.length + 1}_${Date.now() % 1e5}`, label: "", value: "" }] }), children: "Text field" }),
5996
6015
  !tagsVisible ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowTags(true), children: "Tags" }) : null,
5997
- !descriptionVisible ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowDescription(true), children: "Description" }) : null
6016
+ !descriptionVisible ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowDescription(true), children: "Description" }) : null,
6017
+ !recoveryDeclared && !recoveryExtra && !recoveryEditing ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowRecovery(true), children: "Recovery codes" }) : null
5998
6018
  ] })
5999
6019
  ] }),
6000
6020
  children: [
@@ -6034,8 +6054,9 @@ function ItemPane(props) {
6034
6054
  item.authenticator ? /* @__PURE__ */ jsx26(TotpRow, { port, item, onChanged }) : seedField && !seedField.active ? (
6035
6055
  // The secret is still there, only off: turning it on brings the codes back. Never "Set up",
6036
6056
  // which would invite replacing a working two-factor secret.
6037
- /* @__PURE__ */ jsx26(VaultRow, { label: "One-time password", actions: canTurnOn ? /* @__PURE__ */ jsx26(TurnOnButton, { label: "One-time password", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "Turned off" }) })
6038
- ) : /* @__PURE__ */ jsx26(AddRow, { label: "One-time password", onAdd: canEdit ? () => onMode("edit") : null, children: "Set up" }),
6057
+ /* @__PURE__ */ jsx26(VaultRow, { label: "Authenticator", actions: canTurnOn ? /* @__PURE__ */ jsx26(TurnOnButton, { label: "Authenticator", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "Turned off" }) })
6058
+ ) : /* @__PURE__ */ jsx26(AddRow, { label: "Authenticator", onAdd: canEdit ? () => setSettingUpOtp(true) : null, children: "Set up" }),
6059
+ !recoveryDeclared && !recoveryExtra && offerRecovery ? /* @__PURE__ */ jsx26(RecoveryCodesRow, { item, field: null, port, onChanged }) : null,
6039
6060
  !item.authenticator && item.custom.some((c) => c.key === MFA_METHOD_KEY && c.value) ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
6040
6061
  ] }),
6041
6062
  /* @__PURE__ */ jsx26(VaultSection, { children: item.notes ? /* @__PURE__ */ jsx26(PlainRow, { label: "Notes", value: item.notes, multiline: true }) : /* @__PURE__ */ jsx26(AddRow, { label: "Notes", onAdd: canEdit ? () => onMode("edit") : null, children: "Add notes" }) }),
@@ -6051,7 +6072,22 @@ function ItemPane(props) {
6051
6072
  " \xB7 Created ",
6052
6073
  formatAbsoluteDate3(item.createdAt, DAY_FORMAT, "")
6053
6074
  ] })
6054
- ] }) : null }) })
6075
+ ] }) : null }) }),
6076
+ settingUpOtp && item ? /* @__PURE__ */ jsx26(
6077
+ AddOtpPanel,
6078
+ {
6079
+ port,
6080
+ items: [item],
6081
+ definitions: definition ? [definition] : [],
6082
+ itemId: item.id,
6083
+ onClose: () => setSettingUpOtp(false),
6084
+ onChanged: () => onChanged(),
6085
+ onDone: () => {
6086
+ setSettingUpOtp(false);
6087
+ onChanged();
6088
+ }
6089
+ }
6090
+ ) : null
6055
6091
  ] }) });
6056
6092
  }
6057
6093
 
@@ -6989,7 +7025,7 @@ var plural2 = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
6989
7025
  function terminalMessage(s) {
6990
7026
  if (s.phase === "completed") return `Imported ${plural2(s.committed, "passkey")}.`;
6991
7027
  if (s.phase === "partial") return `${s.committed} imported; ${s.unsupported + s.failed + s.uncertain + s.not_attempted} need attention.`;
6992
- if (s.phase === "cancelled") return "Import cancelled. Nothing else will be imported.";
7028
+ if (s.phase === "cancelled") return "Import canceled. Nothing else will be imported.";
6993
7029
  if (s.phase === "unavailable") return "Passkey import is unavailable here.";
6994
7030
  if (s.phase === "failed") return "The import did not finish. Recover it before using a new file.";
6995
7031
  return s.message;
@@ -7174,7 +7210,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7174
7210
  present(g, { kind: "status", status: result, message: terminalMessage(result) });
7175
7211
  if (!done && ACTIVE_PHASES.has(result.phase)) schedulePoll(active.id);
7176
7212
  } catch (e) {
7177
- keepOperation(g, fail(e, "The import could not be cancelled."));
7213
+ keepOperation(g, fail(e, "The import could not be canceled."));
7178
7214
  } finally {
7179
7215
  if (current(g)) setBusy(false);
7180
7216
  }
@@ -7660,6 +7696,7 @@ import { cn as cn15, useLook as useLook2 } from "@ai-matrx/design-system";
7660
7696
  import { Select as Select10 } from "@ai-matrx/design-system/controls";
7661
7697
  import { GlobeIcon as GlobeIcon5 } from "@ai-matrx/design-system/icons";
7662
7698
  import { jsx as jsx38, jsxs as jsxs36 } from "react/jsx-runtime";
7699
+ var AUTHENTICATOR_LABEL = "Authenticator";
7663
7700
  function vaultChoices(items, organizations, current) {
7664
7701
  const n = (f) => items.filter(f).length;
7665
7702
  const shared = n((i) => i.owner.kind === "shared");
@@ -7699,7 +7736,7 @@ function buildViews(items, definitions, favoriteIds2, recentIds2) {
7699
7736
  function viewLabel(view, views) {
7700
7737
  if (view === "favorites") return "Favorites";
7701
7738
  if (view === "recent") return "Recently opened";
7702
- if (view === "authenticator") return "One-time passwords";
7739
+ if (view === "authenticator") return AUTHENTICATOR_LABEL;
7703
7740
  if (view === "trash") return "Trash";
7704
7741
  if (view === "page") return "This page";
7705
7742
  return [...views.categories, ...views.tags].find((v) => v.view === view)?.label ?? "All items";
@@ -7710,7 +7747,7 @@ function viewOptions(views, allCount, deletedCount, hasFavorites, hasRecents, ha
7710
7747
  { value: "all", label: "Every category", meta: String(allCount) },
7711
7748
  ...hasFavorites ? [{ value: "favorites", label: "Favorites", meta: views.favorites === void 0 ? "" : String(views.favorites) }] : [],
7712
7749
  ...hasRecents ? [{ value: "recent", label: "Recently opened", meta: views.recent === void 0 ? "" : String(views.recent) }] : [],
7713
- { value: "authenticator", label: "One-time passwords", meta: String(views.authenticator) },
7750
+ { value: "authenticator", label: AUTHENTICATOR_LABEL, meta: String(views.authenticator) },
7714
7751
  ...views.categories.map((v) => ({ value: v.view, label: v.label, meta: String(v.count) })),
7715
7752
  ...views.tags.map((v) => ({ value: v.view, label: `#${v.label}`, meta: String(v.count) })),
7716
7753
  { value: "trash", label: "Trash", meta: String(deletedCount) }
@@ -7752,7 +7789,7 @@ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, v
7752
7789
  /* @__PURE__ */ jsx38(Entry, { on: view === "all", glyph: GridGlyph, label: "All items", count: allCount, onClick: () => onView("all") }),
7753
7790
  hasFavorites ? /* @__PURE__ */ jsx38(Entry, { on: view === "favorites", glyph: StarGlyph, label: "Favorites", count: views.favorites, onClick: () => onView("favorites") }) : null,
7754
7791
  hasRecents ? /* @__PURE__ */ jsx38(Entry, { on: view === "recent", glyph: ClockGlyph, label: "Recently opened", count: views.recent, onClick: () => onView("recent") }) : null,
7755
- /* @__PURE__ */ jsx38(Entry, { on: view === "authenticator", glyph: LockGlyph, label: "One-time passwords", count: views.authenticator, onClick: () => onView("authenticator") })
7792
+ /* @__PURE__ */ jsx38(Entry, { on: view === "authenticator", glyph: LockGlyph, label: AUTHENTICATOR_LABEL, count: views.authenticator, onClick: () => onView("authenticator") })
7756
7793
  ] }),
7757
7794
  views.categories.length > 0 ? /* @__PURE__ */ jsx38(Group, { title: "Categories", children: views.categories.map((e) => /* @__PURE__ */ jsx38(Entry, { on: view === e.view, glyph: e.glyph, label: e.label, count: e.count, onClick: () => onView(e.view) }, e.view)) }) : null,
7758
7795
  views.tags.length > 0 ? /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-0.5", children: [
@@ -7859,6 +7896,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7859
7896
  const loc = location ?? inner;
7860
7897
  const dirty = useRef19(false);
7861
7898
  const [notice, setNotice] = useState40(null);
7899
+ const [firstCode, setFirstCode] = useState40(null);
7862
7900
  const move = useCallback5(
7863
7901
  (next, how) => {
7864
7902
  if (!location) setInner(next);
@@ -7998,9 +8036,21 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7998
8036
  }
7999
8037
  const hasFavorites = port.lists.favorites !== void 0;
8000
8038
  const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
8001
- const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ jsxs37("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] gap-2", children: [
8039
+ const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ jsxs37("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_auto] gap-2", children: [
8002
8040
  /* @__PURE__ */ jsx39(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8003
8041
  /* @__PURE__ */ jsx39(Select11, { 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" }),
8042
+ /* @__PURE__ */ jsx39(
8043
+ Button33,
8044
+ {
8045
+ variant: loc.view === "authenticator" ? "outline" : "quiet",
8046
+ icon: /* @__PURE__ */ jsx39(LockGlyph, {}),
8047
+ "aria-label": AUTHENTICATOR_LABEL,
8048
+ "aria-pressed": loc.view === "authenticator",
8049
+ title: AUTHENTICATOR_LABEL,
8050
+ onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null }),
8051
+ children: mode === "split" ? AUTHENTICATOR_LABEL : null
8052
+ }
8053
+ ),
8004
8054
  /* @__PURE__ */ jsxs37(DropdownMenu6, { children: [
8005
8055
  /* @__PURE__ */ jsx39(DropdownMenuTrigger6, { asChild: true, children: /* @__PURE__ */ jsx39(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx39(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
8006
8056
  /* @__PURE__ */ jsxs37(DropdownMenuContent6, { align: "end", children: [
@@ -8101,9 +8151,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8101
8151
  seed: { name: guess?.name ?? "", url: guess?.url ?? loc.url.trim() },
8102
8152
  onBack: () => go({ kind: null }),
8103
8153
  onMode: () => void 0,
8104
- onSaved: (item, _wasNew, n) => {
8154
+ onSaved: (item, _wasNew, n, enrolled) => {
8105
8155
  replaceItem(item);
8106
8156
  dirty.current = false;
8157
+ if (enrolled) setFirstCode(item);
8107
8158
  move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", panel: null }, "replace");
8108
8159
  setNotice(n);
8109
8160
  notify("Saved");
@@ -8132,9 +8183,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8132
8183
  onBack: back,
8133
8184
  onMode: (m) => go({ mode: m, panel: null }),
8134
8185
  notice,
8135
- onSaved: (item, _wasNew, n) => {
8186
+ onSaved: (item, _wasNew, n, enrolled) => {
8136
8187
  replaceItem(item);
8137
8188
  dirty.current = false;
8189
+ if (enrolled) setFirstCode(item);
8138
8190
  move({ ...loc, mode: "view" }, "replace");
8139
8191
  setNotice(n);
8140
8192
  notify("Saved");
@@ -8232,7 +8284,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8232
8284
  ) : null,
8233
8285
  shown.list ? /* @__PURE__ */ jsx39("div", { className: specialView && mode !== "stack" ? "col-span-2 min-h-0" : "min-h-0", children: list }) : null,
8234
8286
  shown.detail ? detail : null,
8235
- panel
8287
+ panel,
8288
+ firstCode ? /* @__PURE__ */ jsx39(FirstCodePanel, { port, item: firstCode, onClose: () => setFirstCode(null), onDone: () => setFirstCode(null) }) : null
8236
8289
  ] })
8237
8290
  ] });
8238
8291
  }
@@ -9003,7 +9056,9 @@ function createApiVaultPort(api, options = {}) {
9003
9056
  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 }))],
9004
9057
  fields: [
9005
9058
  ...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
9006
- ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map((s) => ({ field_key: fieldKeyOf(s.label), value: s.value, handling: "revealable" }))
9059
+ ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map((s) => ({ field_key: fieldKeyOf(s.label), value: s.value, handling: "revealable" })),
9060
+ // Backup codes on a type that does not declare them (a website login): their own encrypted field.
9061
+ ...!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" }] : []
9007
9062
  ]
9008
9063
  }
9009
9064
  }, organizationId ? { organizationId } : void 0);