@ai-matrx/vault 0.1.11 → 0.1.12

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
@@ -324,9 +324,20 @@ var FingerprintGlyph = glyph(/* @__PURE__ */ jsxs2(Fragment, { children: [
324
324
  var SendGlyph = glyph(/* @__PURE__ */ jsx2("path", { d: "M14.5 21.7a.5.5 0 0 0 .9 0L22 3a.5.5 0 0 0-.6-.6L2.3 9.1a.5.5 0 0 0 0 .9l7.9 3.2a2 2 0 0 1 1.1 1.1zM21.9 2.1 10.9 13.1" }), [1, 1]);
325
325
 
326
326
  // src/auth/identity.ts
327
+ var CONFIRM_RETURN_KEY = "matrx-vault:confirming-as";
328
+ var RETURN_WINDOW_MS = 15 * 6e4;
327
329
  var SAME_PERSON = "That signed in a different account. Use the account this vault belongs to.";
328
330
  function createSupabaseIdentity(auth, options = {}) {
329
331
  let me = null;
332
+ const now = options.now ?? Date.now;
333
+ const store = () => {
334
+ if (options.store) return options.store;
335
+ try {
336
+ return globalThis.sessionStorage ?? null;
337
+ } catch {
338
+ return null;
339
+ }
340
+ };
330
341
  const who = async () => {
331
342
  const { data } = await auth.getUser();
332
343
  if (!data.user) throw new Error("You are signed out. Sign in again.");
@@ -343,10 +354,35 @@ function createSupabaseIdentity(auth, options = {}) {
343
354
  return { email: u.email ?? null, providers: [...names].filter((p) => p !== "email" && p !== "phone"), hasPassword: names.has("email") };
344
355
  },
345
356
  async withProvider(provider) {
346
- await who();
357
+ const u = await who();
358
+ store()?.setItem(CONFIRM_RETURN_KEY, JSON.stringify({ id: u.id, email: u.email ?? null, at: now() }));
347
359
  const redirectTo = options.redirectTo?.() ?? globalThis.location?.href;
348
360
  const { error } = await auth.signInWithOAuth({ provider, ...redirectTo ? { options: { redirectTo } } : {} });
349
- if (error) throw new Error(error.message);
361
+ if (error) {
362
+ store()?.removeItem(CONFIRM_RETURN_KEY);
363
+ throw new Error(error.message);
364
+ }
365
+ },
366
+ async checkReturn() {
367
+ const s = store();
368
+ const raw = s?.getItem(CONFIRM_RETURN_KEY) ?? null;
369
+ if (!s || raw === null) return null;
370
+ s.removeItem(CONFIRM_RETURN_KEY);
371
+ let expected;
372
+ try {
373
+ expected = JSON.parse(raw);
374
+ } catch {
375
+ return null;
376
+ }
377
+ if (typeof expected.id !== "string" || typeof expected.at !== "number" || now() - expected.at > RETURN_WINDOW_MS) return null;
378
+ const { data } = await auth.getUser();
379
+ if (!data.user || data.user.id === expected.id) return null;
380
+ const email = typeof expected.email === "string" && expected.email ? expected.email : null;
381
+ const message2 = `That signed in a different account, so it was signed out here.${email ? ` Sign in again as ${email}.` : " Sign in again."}`;
382
+ me = null;
383
+ if (options.onDifferentPerson) await options.onDifferentPerson(message2);
384
+ else await auth.signOut?.({ scope: "local" });
385
+ return message2;
350
386
  },
351
387
  async withPassword(password) {
352
388
  const u = await who();
@@ -392,6 +428,18 @@ var providerLabel = (p) => PROVIDER_LABEL[p] ?? p.charAt(0).toUpperCase() + p.sl
392
428
  function IdentityGate({ identity, children }) {
393
429
  const [asking, setAsking] = useState2(false);
394
430
  const waiters = useRef2([]);
431
+ const [returnProblem, setReturnProblem] = useState2(null);
432
+ useEffect2(() => {
433
+ if (!identity?.checkReturn) return;
434
+ let live = true;
435
+ identity.checkReturn().then(
436
+ (problem) => live && problem && setReturnProblem(problem),
437
+ (e) => live && setReturnProblem(e instanceof Error ? e.message : "The account that came back could not be checked.")
438
+ );
439
+ return () => {
440
+ live = false;
441
+ };
442
+ }, [identity]);
395
443
  const confirmed = async (run) => {
396
444
  try {
397
445
  return await run();
@@ -412,7 +460,14 @@ function IdentityGate({ identity, children }) {
412
460
  };
413
461
  return /* @__PURE__ */ jsxs3(Ctx2.Provider, { value: confirmed, children: [
414
462
  children,
415
- identity ? /* @__PURE__ */ jsx3(ConfirmIdentityDialog, { open: asking, identity, onDone: () => settle(true), onCancel: () => settle(false) }) : null
463
+ identity ? /* @__PURE__ */ jsx3(ConfirmIdentityDialog, { open: asking, identity, onDone: () => settle(true), onCancel: () => settle(false) }) : null,
464
+ /* @__PURE__ */ jsx3(Dialog, { open: returnProblem !== null, onOpenChange: (o) => o ? void 0 : setReturnProblem(null), children: /* @__PURE__ */ jsxs3(DialogContent, { size: "sm", children: [
465
+ /* @__PURE__ */ jsxs3(DialogHeader, { children: [
466
+ /* @__PURE__ */ jsx3(DialogTitle, { children: "Different account" }),
467
+ /* @__PURE__ */ jsx3(DialogDescription, { children: returnProblem })
468
+ ] }),
469
+ /* @__PURE__ */ jsx3("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx3(Button, { variant: "primary", onClick: () => setReturnProblem(null), children: "OK" }) })
470
+ ] }) })
416
471
  ] });
417
472
  }
418
473
  var wasCancelled = (e) => e instanceof Error && e.message === CANCELLED;
@@ -476,7 +531,7 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
476
531
  e.preventDefault();
477
532
  if (password) void attempt(() => identity.withPassword(password));
478
533
  }, children: [
479
- /* @__PURE__ */ jsx3(Field, { className: "min-w-0 flex-1", type: "password", autoComplete: "current-password", placeholder: "Your password", "aria-label": "Your password", value: password, onChange: (e) => setPassword(e.target.value) }),
534
+ /* @__PURE__ */ jsx3(Field, { className: "min-w-0 flex-1", type: "password", autoComplete: "current-password", placeholder: "Your password", "aria-label": "Your password", value: password, onChange: (e) => setPassword(e.target.value), autoFocus: true }),
480
535
  /* @__PURE__ */ jsx3(Button, { variant: "outline", type: "submit", disabled: busy || !password, children: "Confirm" })
481
536
  ] }) : null,
482
537
  account10?.email ? codeSent ? /* @__PURE__ */ jsxs3("form", { className: "flex gap-2", onSubmit: (e) => {
@@ -604,6 +659,12 @@ function Panel({ title, open = true, onClose, children, footer, size = "lg" }) {
604
659
  footer ? /* @__PURE__ */ jsx4(DialogFooter, { children: footer }) : null
605
660
  ] }) });
606
661
  }
662
+ function PanelFooter({ status, children }) {
663
+ return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-wrap items-center justify-end gap-2", children: [
664
+ status ? /* @__PURE__ */ jsx4("div", { className: "min-w-0 flex-1 basis-full sm:basis-0", children: status }) : null,
665
+ children
666
+ ] });
667
+ }
607
668
  var DAY_FORMAT = { year: "numeric", month: "short", day: "numeric" };
608
669
 
609
670
  // src/FieldRows.tsx
@@ -614,7 +675,7 @@ function SecretText({ value, multiline }) {
614
675
  return /* @__PURE__ */ jsx5("span", { className: "break-all font-mono text-sm tracking-wide", children: [...value].map((ch, i) => /* @__PURE__ */ jsx5("span", { className: /[0-9]/.test(ch) ? "text-primary" : /[^A-Za-z0-9]/.test(ch) ? "text-warning" : void 0, children: ch }, i)) });
615
676
  }
616
677
  var quietError = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
617
- function SecretRow({ port, item, field, extra }) {
678
+ function SecretRow({ port, item, field, extra, onTurnOn }) {
618
679
  const confirmed = useConfirmed();
619
680
  const copyValue2 = useCopy();
620
681
  const secret = useTransientSecret();
@@ -668,10 +729,27 @@ function SecretRow({ port, item, field, extra }) {
668
729
  top: multiline && shown !== null,
669
730
  actions: /* @__PURE__ */ jsxs5(Fragment2, { children: [
670
731
  extra,
732
+ off && onTurnOn ? /* @__PURE__ */ jsx5(TurnOnButton, { label: field.label, onTurnOn }) : null,
671
733
  canSee && !visible ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: secret.value !== null ? /* @__PURE__ */ jsx5(EyeOffGlyph, {}) : /* @__PURE__ */ jsx5(EyeGlyph, {}), "aria-label": secret.value !== null ? `Hide ${field.label}` : `Show ${field.label}`, disabled: busy, onClick: () => void toggle2() }) : null,
672
734
  canSee ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx5(CopyIcon, {}), "aria-label": `Copy ${field.label}`, disabled: busy, onClick: () => void copy() }) : null
673
735
  ] }),
674
- children: sealed ? /* @__PURE__ */ jsx5(Badge, { icon: /* @__PURE__ */ jsx5(LockGlyph, {}), children: "Sealed" }) : off ? /* @__PURE__ */ jsx5("span", { className: "text-muted-foreground", children: "Turned off" }) : error ? /* @__PURE__ */ jsx5("span", { className: "truncate text-destructive", title: error, children: error }) : shown !== null ? visible ? /* @__PURE__ */ jsx5("span", { className: cn3("block", multiline ? "whitespace-pre-wrap break-words" : "truncate"), children: shown || "\u2014" }) : /* @__PURE__ */ jsx5(SecretText, { value: shown, multiline }) : /* @__PURE__ */ jsx5("span", { className: cn3("block truncate text-muted-foreground", !visible && "tracking-widest"), children: visible ? "\u2026" : MASK })
736
+ children: sealed ? /* @__PURE__ */ jsx5(Badge, { icon: /* @__PURE__ */ jsx5(LockGlyph, {}), children: "Never shown" }) : off ? /* @__PURE__ */ jsx5("span", { className: "text-muted-foreground", children: "Turned off" }) : error ? /* @__PURE__ */ jsx5("span", { className: "truncate text-destructive", title: error, children: error }) : shown !== null ? visible ? /* @__PURE__ */ jsx5("span", { className: cn3("block", multiline ? "whitespace-pre-wrap break-words" : "truncate"), children: shown || "\u2014" }) : /* @__PURE__ */ jsx5(SecretText, { value: shown, multiline }) : /* @__PURE__ */ jsx5("span", { className: cn3("block truncate text-muted-foreground", !visible && "tracking-widest"), children: visible ? "\u2026" : MASK })
737
+ }
738
+ );
739
+ }
740
+ function TurnOnButton({ label, onTurnOn }) {
741
+ const [busy, setBusy] = useState5(false);
742
+ return /* @__PURE__ */ jsx5(
743
+ Button2,
744
+ {
745
+ variant: "outline",
746
+ "aria-label": `Turn on ${label}`,
747
+ disabled: busy,
748
+ onClick: () => {
749
+ setBusy(true);
750
+ void onTurnOn().finally(() => setBusy(false));
751
+ },
752
+ children: "Turn on"
675
753
  }
676
754
  );
677
755
  }
@@ -810,14 +888,27 @@ function loginSiteOf(item) {
810
888
  return raw.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0] ?? "";
811
889
  }
812
890
  }
891
+ var TWO_PART_SUFFIX = /* @__PURE__ */ new Set(["co", "com", "org", "net", "gov", "edu", "ac", "ne", "or"]);
892
+ function siteDomainOf(host) {
893
+ const h = host.toLowerCase().replace(/\.$/, "");
894
+ if (!h || /^[\d.]+$/.test(h) || h.includes(":")) return h;
895
+ const labels = h.split(".").filter(Boolean);
896
+ if (labels.length <= 2) return labels.join(".");
897
+ const sld = labels[labels.length - 2] ?? "";
898
+ const tld = labels[labels.length - 1] ?? "";
899
+ const keep = TWO_PART_SUFFIX.has(sld) && tld.length <= 3 ? 3 : 2;
900
+ return labels.slice(-keep).join(".");
901
+ }
813
902
  function sortItems(items, sort) {
814
903
  const out = [...items];
815
904
  const byName = (a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
816
905
  if (sort === "name") return out.sort(byName);
817
- if (sort === "website") return out.sort((a, b) => {
818
- const x = loginSiteOf(a), y = loginSiteOf(b);
819
- return x && y ? x.localeCompare(y) || byName(a, b) : x ? -1 : y ? 1 : byName(a, b);
820
- });
906
+ if (sort === "website")
907
+ return out.sort((a, b) => {
908
+ const x = loginSiteOf(a), y = loginSiteOf(b);
909
+ if (!x || !y) return x ? -1 : y ? 1 : byName(a, b);
910
+ return siteDomainOf(x).localeCompare(siteDomainOf(y)) || x.localeCompare(y) || byName(a, b);
911
+ });
821
912
  if (sort === "updated") return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
822
913
  return out.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
823
914
  }
@@ -1208,7 +1299,7 @@ import { useEffect as useEffect6, useRef as useRef4, useState as useState6 } fro
1208
1299
  import { Button as Button3 } from "@ai-matrx/design-system/controls";
1209
1300
  import { cn as cn4 } from "@ai-matrx/design-system";
1210
1301
  import { formatAbsoluteDate } from "@ai-matrx/kit/format";
1211
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1302
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1212
1303
  var PAGE = 50;
1213
1304
  var MAX = 500;
1214
1305
  function ActivityPanel({ item, port, onClose }) {
@@ -1237,7 +1328,10 @@ function ActivityPanel({ item, port, onClose }) {
1237
1328
  {
1238
1329
  title: "Activity",
1239
1330
  onClose,
1240
- footer: hasMore ? /* @__PURE__ */ jsx6(Button3, { variant: "outline", disabled: loading, onClick: () => setLimit((n) => Math.min(MAX, n * 2)), children: loading ? "Loading\u2026" : "Show more" }) : void 0,
1331
+ footer: /* @__PURE__ */ jsxs6(Fragment3, { children: [
1332
+ hasMore ? /* @__PURE__ */ jsx6(Button3, { variant: "outline", disabled: loading, onClick: () => setLimit((n) => Math.min(MAX, n * 2)), children: loading ? "Loading\u2026" : "Show more" }) : null,
1333
+ /* @__PURE__ */ jsx6(Button3, { variant: "primary", onClick: onClose, children: "Done" })
1334
+ ] }),
1241
1335
  children: [
1242
1336
  error ? /* @__PURE__ */ jsx6("p", { role: "alert", className: "px-1 text-sm text-destructive", children: error }) : null,
1243
1337
  entries === null ? loading ? /* @__PURE__ */ jsx6(Empty, { children: "Reading activity\u2026" }) : null : entries.length === 0 ? /* @__PURE__ */ jsx6(Empty, { children: "Nothing has happened yet." }) : groupByDay(entries, now).map((day) => /* @__PURE__ */ jsx6(VaultSection, { title: day.label, children: collapseRuns(
@@ -1260,7 +1354,7 @@ function ActivityPanel({ item, port, onClose }) {
1260
1354
  }
1261
1355
 
1262
1356
  // src/activity/HistoryPanel.tsx
1263
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1357
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1264
1358
  var MASK2 = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
1265
1359
  var messageOf = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
1266
1360
  function ItemHistoryPanel({ item, port, arg, onClose, onChanged }) {
@@ -1320,8 +1414,11 @@ function ItemHistoryPanel({ item, port, arg, onClose, onChanged }) {
1320
1414
  {
1321
1415
  title: "Password history",
1322
1416
  onClose,
1323
- footer: page?.nextBefore ? /* @__PURE__ */ jsx7(Button4, { variant: "outline", disabled: more, onClick: () => void loadMore(), children: more ? "Loading\u2026" : "Show earlier" }) : void 0,
1324
- children: !eligible ? /* @__PURE__ */ jsx7(Empty, { children: "History is kept only for your own items." }) : loading && !page ? /* @__PURE__ */ jsx7(Empty, { children: "Reading history\u2026" }) : /* @__PURE__ */ jsxs7(Fragment3, { children: [
1417
+ footer: /* @__PURE__ */ jsxs7(Fragment4, { children: [
1418
+ page?.nextBefore ? /* @__PURE__ */ jsx7(Button4, { variant: "outline", disabled: more, onClick: () => void loadMore(), children: more ? "Loading\u2026" : "Show earlier" }) : null,
1419
+ /* @__PURE__ */ jsx7(Button4, { variant: "primary", onClick: onClose, children: "Done" })
1420
+ ] }),
1421
+ children: !eligible ? /* @__PURE__ */ jsx7(Empty, { children: "History is kept only for your own items." }) : loading && !page ? /* @__PURE__ */ jsx7(Empty, { children: "Reading history\u2026" }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
1325
1422
  error ? /* @__PURE__ */ jsx7("p", { role: "alert", className: "px-1 text-sm text-destructive", children: error }) : null,
1326
1423
  notice ? /* @__PURE__ */ jsx7("p", { role: "status", className: "px-1 text-sm text-success", children: notice }) : null,
1327
1424
  page && groups.length === 0 ? /* @__PURE__ */ jsx7(Empty, { children: "No earlier values yet." }) : null,
@@ -1389,7 +1486,7 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
1389
1486
  VaultRow,
1390
1487
  {
1391
1488
  label: when,
1392
- actions: entry.available ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
1489
+ actions: entry.available ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
1393
1490
  /* @__PURE__ */ jsx7(Button4, { variant: "quiet", icon: secret.value !== null ? /* @__PURE__ */ jsx7(EyeOffGlyph, {}) : /* @__PURE__ */ jsx7(EyeGlyph, {}), "aria-label": secret.value !== null ? "Hide" : "Show", disabled: busy, onClick: () => void toggle2() }),
1394
1491
  /* @__PURE__ */ jsx7(Button4, { variant: "quiet", icon: /* @__PURE__ */ jsx7(CopyIcon2, {}), "aria-label": "Copy", disabled: busy, onClick: () => void copy() }),
1395
1492
  canRestore(page, entry) ? /* @__PURE__ */ jsx7(Button4, { variant: "quiet", icon: /* @__PURE__ */ jsx7(RestoreGlyph, {}), "aria-label": "Restore", disabled: busy, onClick: () => void restore() }) : null
@@ -1723,7 +1820,7 @@ function imageFrom(data) {
1723
1820
  }
1724
1821
 
1725
1822
  // src/authenticator/Setup.tsx
1726
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1823
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1727
1824
  function useCanScan() {
1728
1825
  const [ok, setOk] = useState10(false);
1729
1826
  useEffect10(() => {
@@ -1856,7 +1953,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1856
1953
  VaultRow,
1857
1954
  {
1858
1955
  label,
1859
- actions: /* @__PURE__ */ jsxs9(Fragment4, { children: [
1956
+ actions: /* @__PURE__ */ jsxs9(Fragment5, { children: [
1860
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() }),
1861
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
1862
1959
  ] }),
@@ -1925,13 +2022,14 @@ import { confirm as confirm2 } from "@ai-matrx/kit/confirm-opener";
1925
2022
 
1926
2023
  // src/authenticator/port.ts
1927
2024
  var MFA_METHOD_KEY = "mfa_method";
2025
+ var OTP_SEED_KEY = "totp_seed";
1928
2026
  function mfaMethodOf(item) {
1929
2027
  const v = item.custom.find((c) => c.key === MFA_METHOD_KEY)?.value;
1930
2028
  return v === "sms" ? "sms" : v ? "push_or_other" : null;
1931
2029
  }
1932
2030
 
1933
2031
  // src/authenticator/TotpRows.tsx
1934
- import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2032
+ import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1935
2033
  function otpActionError(e, what) {
1936
2034
  const status = e?.status;
1937
2035
  if (status === 404) return "There is no one-time password here.";
@@ -2006,7 +2104,7 @@ function TotpRow({ port, item, onChanged }) {
2006
2104
  VaultRow,
2007
2105
  {
2008
2106
  label: "One-time password",
2009
- actions: /* @__PURE__ */ jsxs10(Fragment5, { children: [
2107
+ actions: /* @__PURE__ */ jsxs10(Fragment6, { children: [
2010
2108
  a.enabled ? /* @__PURE__ */ jsx10(CopyCodeButton, { live, label: item.name }) : null,
2011
2109
  menu
2012
2110
  ] }),
@@ -2089,7 +2187,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
2089
2187
  }
2090
2188
 
2091
2189
  // src/authenticator/AddOtpPanel.tsx
2092
- import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2190
+ import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2093
2191
  var NEW = "__new__";
2094
2192
  var LOGIN_KEYS = ["website_login", "username_password"];
2095
2193
  function normalizeWebsite(raw) {
@@ -2172,7 +2270,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2172
2270
  title: "Add one-time password",
2173
2271
  size: "md",
2174
2272
  onClose,
2175
- footer: /* @__PURE__ */ jsxs11(Fragment6, { children: [
2273
+ footer: /* @__PURE__ */ jsxs11(Fragment7, { children: [
2176
2274
  /* @__PURE__ */ jsx11(Button8, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
2177
2275
  /* @__PURE__ */ jsx11(Button8, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
2178
2276
  ] }),
@@ -2212,7 +2310,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2212
2310
  }
2213
2311
  }
2214
2312
  ) }),
2215
- isNew ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
2313
+ isNew ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
2216
2314
  /* @__PURE__ */ jsx11(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx11(
2217
2315
  Field4,
2218
2316
  {
@@ -2244,7 +2342,7 @@ function AddedPanel({ port, item, onClose, onDone }) {
2244
2342
  }
2245
2343
 
2246
2344
  // src/authenticator/AuthenticatorView.tsx
2247
- import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2345
+ import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2248
2346
  function matchesEntry(entry, q) {
2249
2347
  if (!entry || !q) return false;
2250
2348
  return [entry.issuer, entry.account].some((v) => v?.toLowerCase().includes(q));
@@ -2283,7 +2381,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2283
2381
  const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
2284
2382
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
2285
2383
  return /* @__PURE__ */ jsxs12("section", { "aria-label": "One-time passwords", className: "flex h-full min-h-0 flex-col bg-background", children: [
2286
- header,
2384
+ header ? /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
2287
2385
  /* @__PURE__ */ jsxs12("div", { className: "flex min-h-12 items-center justify-between gap-2 px-4 pb-1 pt-3", children: [
2288
2386
  /* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 items-baseline gap-2", children: [
2289
2387
  /* @__PURE__ */ jsx12("h2", { className: "truncate text-sm font-semibold text-foreground", children: "One-time passwords" }),
@@ -2295,7 +2393,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2295
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" }) }),
2296
2394
  /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-foreground", children: "No one-time passwords yet" }),
2297
2395
  /* @__PURE__ */ jsx12(Button9, { variant: "primary", icon: /* @__PURE__ */ jsx12(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add one-time password" })
2298
- ] }) : 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, { ...groups.length > 1 ? { title: g.label } : {}, className: "[&>div]:px-1", children: g.items.map((item) => /* @__PURE__ */ jsx12(
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(
2299
2397
  OtpListRow,
2300
2398
  {
2301
2399
  port,
@@ -2392,7 +2490,7 @@ function OtpListRow({ port, item, family, entry, onOpen, onRename, onChanged, on
2392
2490
  /* @__PURE__ */ jsx12(CopyIcon4, { className: "size-4" }),
2393
2491
  "Copy code"
2394
2492
  ] }) : null,
2395
- canManage ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
2493
+ canManage ? /* @__PURE__ */ jsxs12(Fragment8, { children: [
2396
2494
  /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onRename, children: [
2397
2495
  /* @__PURE__ */ jsx12(PencilGlyph, { className: "size-4" }),
2398
2496
  "Rename"
@@ -2432,9 +2530,9 @@ function sandboxBlocker(envKeyDraft, savedEnvKey) {
2432
2530
  return null;
2433
2531
  }
2434
2532
  var HANDLING_WORDS = {
2435
- visible: { label: "Anyone", hint: "Anyone who can use it can show or copy it" },
2436
- revealable: { label: "On request", hint: "Showing or copying it needs reveal access" },
2437
- sealed: { label: "Sealed", hint: "No one can see it. Automation can still use it." }
2533
+ visible: { label: "Always shown", hint: "Anyone who can use it can show or copy it" },
2534
+ revealable: { label: "Hidden", hint: "Showing or copying it needs reveal access" },
2535
+ sealed: { label: "Never shown", hint: "No one can see it. Automation can still use it." }
2438
2536
  };
2439
2537
  function handlingChoices(current) {
2440
2538
  return current === "sealed" ? ["sealed"] : ["visible", "revealable", "sealed"];
@@ -2616,7 +2714,7 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2616
2714
  }
2617
2715
  ) }),
2618
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 }) }) }),
2619
- /* @__PURE__ */ jsx13(VaultRow, { label: "Active", hint: quietHint(field.active ? "In use" : "Kept, but nothing uses it"), children: /* @__PURE__ */ jsx13(Switch, { "aria-label": "Active", checked: field.active, disabled: locked, onCheckedChange: (v) => void update("active", { active: 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 }) }) })
2620
2718
  ] }),
2621
2719
  error ? /* @__PURE__ */ jsx13(Problem, { children: error }) : null
2622
2720
  ] });
@@ -2684,7 +2782,7 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
2684
2782
  f.envKey
2685
2783
  ] }) : null,
2686
2784
  f.injectIntoSandbox ? /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
2687
- !f.active ? /* @__PURE__ */ jsx13("span", { className: "text-disabled-foreground", children: " \xB7 Off" }) : null
2785
+ !f.active ? /* @__PURE__ */ jsx13("span", { className: "text-warning", children: " \xB7 Turned off" }) : null
2688
2786
  ] })
2689
2787
  },
2690
2788
  f.id
@@ -3080,7 +3178,7 @@ function startNewRestoreRun(preview) {
3080
3178
  import { useState as useState16 } from "react";
3081
3179
  import { Checkbox } from "@ai-matrx/design-system";
3082
3180
  import { Badge as Badge4, Button as Button12, DisclosureHeader as DisclosureHeader2 } from "@ai-matrx/design-system/controls";
3083
- import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
3181
+ import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
3084
3182
  function PickLine({ checked, onChange, disabled = false, title, detail, badge, tone }) {
3085
3183
  return /* @__PURE__ */ jsxs15("label", { className: "flex min-h-11 items-center gap-3 px-4 py-1", "data-clickable": disabled ? void 0 : "", children: [
3086
3184
  /* @__PURE__ */ jsx15(Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
@@ -3092,7 +3190,7 @@ function PickLine({ checked, onChange, disabled = false, title, detail, badge, t
3092
3190
  function Windowed({ items, size = 150, render }) {
3093
3191
  const [all, setAll] = useState16(false);
3094
3192
  const shown = all ? items : items.slice(0, size);
3095
- return /* @__PURE__ */ jsxs15(Fragment8, { children: [
3193
+ return /* @__PURE__ */ jsxs15(Fragment9, { children: [
3096
3194
  shown.map(render),
3097
3195
  items.length > shown.length ? /* @__PURE__ */ jsx15("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsxs15(Button12, { variant: "quiet", onClick: () => setAll(true), children: [
3098
3196
  "Show all ",
@@ -3113,7 +3211,7 @@ function ErrorLine({ message: message2 }) {
3113
3211
  var plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
3114
3212
 
3115
3213
  // src/io/BackupPanel.tsx
3116
- import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
3214
+ import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
3117
3215
  var RECORD_STATUS = {
3118
3216
  created: "Restored",
3119
3217
  replayed: "Already restored",
@@ -3241,13 +3339,13 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3241
3339
  const retryable = (result?.retryable_failure ?? 0) > 0;
3242
3340
  const mismatch = repeat !== "" && repeat !== passphrase;
3243
3341
  const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
3244
- const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
3342
+ const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3245
3343
  /* @__PURE__ */ jsx16(Button13, { variant: "quiet", onClick: onClose, children: "Close" }),
3246
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: [
3247
3345
  "Review ",
3248
3346
  selected.size
3249
3347
  ] })
3250
- ] }) : /* @__PURE__ */ jsxs16(Fragment9, { children: [
3348
+ ] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
3251
3349
  /* @__PURE__ */ jsx16(Button13, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
3252
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: [
3253
3351
  "Restore ",
@@ -3268,12 +3366,12 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3268
3366
  ]
3269
3367
  }
3270
3368
  ),
3271
- mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
3369
+ mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3272
3370
  /* @__PURE__ */ jsx16(
3273
3371
  VaultSection,
3274
3372
  {
3275
3373
  title: `${selected.size} of ${plural(items.length, "credential")}`,
3276
- actions: /* @__PURE__ */ jsxs16(Fragment9, { children: [
3374
+ actions: /* @__PURE__ */ jsxs16(Fragment10, { children: [
3277
3375
  /* @__PURE__ */ jsx16(Button13, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
3278
3376
  setBackup(null);
3279
3377
  setSelected(new Set(items.map((i) => i.id)));
@@ -3286,7 +3384,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3286
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) })
3287
3385
  }
3288
3386
  ),
3289
- backup ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
3387
+ backup ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3290
3388
  /* @__PURE__ */ jsx16(Summary, { value: backup }),
3291
3389
  /* @__PURE__ */ jsx16(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3292
3390
  /* @__PURE__ */ jsxs16(VaultSection, { children: [
@@ -3294,7 +3392,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3294
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) }) })
3295
3393
  ] })
3296
3394
  ] }) : null
3297
- ] }) : /* @__PURE__ */ jsx16(Fragment9, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
3395
+ ] }) : /* @__PURE__ */ jsx16(Fragment10, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
3298
3396
  /* @__PURE__ */ jsxs16(VaultRow, { label: "File", ...file ? { hint: file.name } : {}, children: [
3299
3397
  /* @__PURE__ */ jsx16(Button13, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
3300
3398
  /* @__PURE__ */ jsx16(
@@ -3312,7 +3410,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3312
3410
  )
3313
3411
  ] }),
3314
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) }) })
3315
- ] }) : /* @__PURE__ */ jsxs16(Fragment9, { children: [
3413
+ ] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
3316
3414
  /* @__PURE__ */ jsx16(Summary, { value: restorePreview }),
3317
3415
  !result ? /* @__PURE__ */ jsx16(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
3318
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: [
@@ -3329,7 +3427,7 @@ import { useMemo, useState as useState18 } from "react";
3329
3427
  import { Button as Button14, Select as Select5 } from "@ai-matrx/design-system/controls";
3330
3428
  import { downloadFile as downloadFile2 } from "@ai-matrx/kit/download";
3331
3429
  import { confirm as confirm5 } from "@ai-matrx/kit/confirm-opener";
3332
- import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
3430
+ import { Fragment as Fragment11, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
3333
3431
  var FORMATS = [
3334
3432
  { value: "matrx_login_csv_v1", label: "Matrx", hint: "Website logins, all common fields" },
3335
3433
  { value: "firefox_csv_v1", label: "Firefox", hint: "Address, username and password" },
@@ -3409,7 +3507,7 @@ function ExportPanel({ port, items, onClose }) {
3409
3507
  title: "Export logins",
3410
3508
  size: "lg",
3411
3509
  onClose,
3412
- footer: /* @__PURE__ */ jsxs17(Fragment10, { children: [
3510
+ footer: /* @__PURE__ */ jsxs17(Fragment11, { children: [
3413
3511
  /* @__PURE__ */ jsx17(Button14, { variant: "quiet", onClick: onClose, children: "Close" }),
3414
3512
  preview ? /* @__PURE__ */ jsxs17(Button14, { variant: "primary", icon: /* @__PURE__ */ jsx17(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
3415
3513
  "Download ",
@@ -3436,7 +3534,7 @@ function ExportPanel({ port, items, onClose }) {
3436
3534
  VaultSection,
3437
3535
  {
3438
3536
  title: `${selected.size} of ${plural(logins.length, "login")}`,
3439
- actions: /* @__PURE__ */ jsxs17(Fragment10, { children: [
3537
+ actions: /* @__PURE__ */ jsxs17(Fragment11, { children: [
3440
3538
  /* @__PURE__ */ jsx17(Button14, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
3441
3539
  changed();
3442
3540
  setSelected(new Set(logins.map((l) => l.id)));
@@ -4318,7 +4416,7 @@ async function parseImportFile(source, file, limits, signal) {
4318
4416
  }
4319
4417
 
4320
4418
  // src/io/ImportPanel.tsx
4321
- import { Fragment as Fragment11, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4419
+ import { Fragment as Fragment12, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4322
4420
  var ROLE_OPTIONS = [
4323
4421
  { value: "keep", label: "Keep encrypted" },
4324
4422
  { value: "title", label: "Name" },
@@ -4493,13 +4591,13 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4493
4591
  const help = EXPORT_HELP[source];
4494
4592
  const stage = done ? "done" : draft ? "review" : "choose";
4495
4593
  const busy = running !== null || reading;
4496
- const footer = stage === "choose" ? /* @__PURE__ */ jsxs18(Fragment11, { children: [
4594
+ const footer = stage === "choose" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4497
4595
  /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: onClose, children: "Close" }),
4498
4596
  source === "env" ? /* @__PURE__ */ jsx18(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx18(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
4499
- ] }) : stage === "review" ? /* @__PURE__ */ jsxs18(Fragment11, { children: [
4597
+ ] }) : stage === "review" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4500
4598
  running ? /* @__PURE__ */ jsx18(Button15, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: reset, children: "Back" }),
4501
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}` })
4502
- ] }) : /* @__PURE__ */ jsxs18(Fragment11, { children: [
4600
+ ] }) : /* @__PURE__ */ jsxs18(Fragment12, { children: [
4503
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,
4504
4602
  /* @__PURE__ */ jsx18(Button15, { variant: "primary", onClick: onClose, children: "Done" })
4505
4603
  ] });
@@ -4529,13 +4627,13 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4529
4627
  ] }),
4530
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
4531
4629
  ] }) : null,
4532
- stage === "review" && draft ? /* @__PURE__ */ jsxs18(Fragment11, { children: [
4630
+ stage === "review" && draft ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4533
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,
4534
4632
  /* @__PURE__ */ jsx18(
4535
4633
  VaultSection,
4536
4634
  {
4537
4635
  title: draft.kind === "env" ? plural(envKeys.length, "value") : summary(counts, lines.length),
4538
- actions: /* @__PURE__ */ jsxs18(Fragment11, { children: [
4636
+ actions: /* @__PURE__ */ jsxs18(Fragment12, { children: [
4539
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" }),
4540
4638
  /* @__PURE__ */ jsx18(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
4541
4639
  ] }),
@@ -4629,7 +4727,7 @@ import { jsx as jsx19 } from "react/jsx-runtime";
4629
4727
  function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
4630
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";
4631
4729
  const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
4632
- const initial = site.replace(/^(app|my|login|accounts|signin|auth|console|portal|admin|www)\./, "").charAt(0).toUpperCase();
4730
+ const initial = siteDomainOf(site).charAt(0).toUpperCase();
4633
4731
  if (initial && /[A-Z0-9]/.test(initial)) {
4634
4732
  return /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn8("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
4635
4733
  }
@@ -4652,13 +4750,13 @@ import { cn as cn9 } from "@ai-matrx/design-system";
4652
4750
  import { confirm as confirm6 } from "@ai-matrx/kit/confirm-opener";
4653
4751
  import { downloadFile as downloadFile3 } from "@ai-matrx/kit/download";
4654
4752
  import { DownloadIcon } from "@ai-matrx/design-system/icons";
4655
- import { Fragment as Fragment12, jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
4753
+ import { Fragment as Fragment13, jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
4656
4754
  var messageOf2 = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
4657
4755
  var TOO_BIG = "Files over 25 MB can't be stored.";
4658
4756
  var PROTECTION = [
4659
4757
  { value: "visible", label: "Open", title: "Anyone who can use the item can download it" },
4660
4758
  { value: "revealable", label: "Protected", title: "Downloading needs a recent sign-in" },
4661
- { value: "sealed", label: "Sealed", title: "Only automation can use it, never a person" }
4759
+ { value: "sealed", label: "Never shown", title: "Only automation can use it, never a person" }
4662
4760
  ];
4663
4761
  function FilesSection({ item, port, editing, onChanged }) {
4664
4762
  const confirmed = useConfirmed();
@@ -4725,7 +4823,7 @@ function FilesSection({ item, port, editing, onChanged }) {
4725
4823
  {
4726
4824
  label: a.label,
4727
4825
  hint: a.description || void 0,
4728
- actions: a.handling === "sealed" ? /* @__PURE__ */ jsx20(Badge5, { icon: /* @__PURE__ */ jsx20(LockGlyph, {}), children: "Sealed" }) : canDownload(item, a) ? /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(DownloadIcon, {}), "aria-label": `Download ${a.fileName}`, disabled: busy !== null, onClick: () => void download(a) }) : null,
4826
+ actions: a.handling === "sealed" ? /* @__PURE__ */ jsx20(Badge5, { icon: /* @__PURE__ */ jsx20(LockGlyph, {}), children: "Never shown" }) : canDownload(item, a) ? /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(DownloadIcon, {}), "aria-label": `Download ${a.fileName}`, disabled: busy !== null, onClick: () => void download(a) }) : null,
4729
4827
  children: /* @__PURE__ */ jsx20(FileName, { file: a })
4730
4828
  },
4731
4829
  a.id
@@ -4749,13 +4847,13 @@ function EditableFile({ port, item: it, file: a, busy: b, run: go, onDownload, o
4749
4847
  const yes = await confirm6({ title: `Delete ${a.label}?`, description: "The encrypted file is removed from this item.", confirmLabel: "Delete", variant: "destructive" });
4750
4848
  if (yes) await go(`delete:${a.id}`, () => port.activity.deleteFile(it, a.id), "The file could not be deleted.");
4751
4849
  }
4752
- return /* @__PURE__ */ jsxs19(Fragment12, { children: [
4850
+ return /* @__PURE__ */ jsxs19(Fragment13, { children: [
4753
4851
  /* @__PURE__ */ jsx20(
4754
4852
  VaultRow,
4755
4853
  {
4756
4854
  label: a.label,
4757
4855
  hint: a.description || void 0,
4758
- actions: /* @__PURE__ */ jsxs19(Fragment12, { children: [
4856
+ actions: /* @__PURE__ */ jsxs19(Fragment13, { children: [
4759
4857
  canDownload(it, a) ? /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(DownloadIcon, {}), "aria-label": `Download ${a.fileName}`, disabled: b !== null, onClick: onDownload }) : null,
4760
4858
  /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(PencilGlyph, {}), "aria-label": `Edit ${a.label}`, pressed: open, disabled: b !== null, onClick: () => setOpen((o) => !o) }),
4761
4859
  /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(UploadGlyph, {}), "aria-label": `Replace ${a.fileName}`, disabled: b !== null, onClick: () => replacer.current?.click() }),
@@ -4809,7 +4907,7 @@ function NewFile({ file, busy, onAdd, onCancel }) {
4809
4907
  if (handling === "sealed" && !await confirmSeal(label)) return;
4810
4908
  await onAdd({ label: label.trim(), description: description.trim() || null, handling });
4811
4909
  }
4812
- return /* @__PURE__ */ jsxs19(Fragment12, { children: [
4910
+ return /* @__PURE__ */ jsxs19(Fragment13, { children: [
4813
4911
  /* @__PURE__ */ jsx20(VaultRow, { label: "New file", children: /* @__PURE__ */ jsxs19("span", { className: "flex min-w-0 items-center gap-2", children: [
4814
4912
  /* @__PURE__ */ jsx20("span", { className: "truncate", children: file.name }),
4815
4913
  /* @__PURE__ */ jsx20("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatFileSize2(file.size) })
@@ -4833,7 +4931,7 @@ function FileDetailsRows({ file, busy, onSave, onCancel }) {
4833
4931
  if (handling === "sealed" && file.handling !== "sealed" && !await confirmSeal(label)) return;
4834
4932
  await onSave({ label: label.trim(), description: description.trim() || null, fileName: fileName.trim(), handling });
4835
4933
  }
4836
- return /* @__PURE__ */ jsxs19(Fragment12, { children: [
4934
+ return /* @__PURE__ */ jsxs19(Fragment13, { children: [
4837
4935
  /* @__PURE__ */ jsx20(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx20(Field7, { "aria-label": "Name", value: label, onChange: (e) => setLabel(e.target.value) }) }),
4838
4936
  /* @__PURE__ */ jsx20(VaultRow, { label: "Purpose", children: /* @__PURE__ */ jsx20(Field7, { "aria-label": "Purpose", placeholder: "What it is for", value: description, onChange: (e) => setDescription(e.target.value) }) }),
4839
4937
  /* @__PURE__ */ jsx20(VaultRow, { label: "File name", children: /* @__PURE__ */ jsx20(Field7, { "aria-label": "File name", value: fileName, onChange: (e) => setFileName(e.target.value) }) }),
@@ -4857,7 +4955,7 @@ function confirmSeal(label) {
4857
4955
  import { useState as useState21 } from "react";
4858
4956
  import { Badge as Badge6, Button as Button17, Textarea as Textarea3 } from "@ai-matrx/design-system/controls";
4859
4957
  import { CopyIcon as CopyIcon6 } from "@ai-matrx/design-system/icons";
4860
- import { Fragment as Fragment13, jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
4958
+ import { Fragment as Fragment14, jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
4861
4959
  var MASK4 = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
4862
4960
  var say2 = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
4863
4961
  function RecoveryCodesRow({ item, field, port, onChanged }) {
@@ -4901,7 +4999,7 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4901
4999
  {
4902
5000
  label: "Recovery codes",
4903
5001
  top: true,
4904
- actions: /* @__PURE__ */ jsxs20(Fragment13, { children: [
5002
+ actions: /* @__PURE__ */ jsxs20(Fragment14, { children: [
4905
5003
  /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: /* @__PURE__ */ jsx21(CheckGlyph, {}), "aria-label": "Save codes", disabled: busy || !draft.trim(), onClick: () => void saveDraft() }),
4906
5004
  /* @__PURE__ */ jsx21(Button17, { variant: "quiet", removes: true, icon: /* @__PURE__ */ jsx21(XGlyph, {}), "aria-label": "Cancel", disabled: busy, onClick: () => {
4907
5005
  setDraft(null);
@@ -4943,11 +5041,11 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4943
5041
  {
4944
5042
  label: "Recovery codes",
4945
5043
  top: codes !== null,
4946
- actions: /* @__PURE__ */ jsxs20(Fragment13, { children: [
5044
+ actions: /* @__PURE__ */ jsxs20(Fragment14, { children: [
4947
5045
  canSee ? /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: codes ? /* @__PURE__ */ jsx21(EyeOffGlyph, {}) : /* @__PURE__ */ jsx21(EyeGlyph, {}), "aria-label": codes ? "Hide codes" : "Show codes", disabled: busy, onClick: () => void toggle2() }) : null,
4948
5046
  canEdit && !sealed ? /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: /* @__PURE__ */ jsx21(PencilGlyph, {}), "aria-label": "Replace all codes", disabled: busy, onClick: () => setDraft("") }) : null
4949
5047
  ] }),
4950
- children: sealed ? /* @__PURE__ */ jsx21(Badge6, { icon: /* @__PURE__ */ jsx21(LockGlyph, {}), children: "Sealed" }) : codes ? /* @__PURE__ */ jsxs20("ul", { className: "flex flex-col gap-0.5", "aria-label": "Recovery codes", children: [
5048
+ children: sealed ? /* @__PURE__ */ jsx21(Badge6, { icon: /* @__PURE__ */ jsx21(LockGlyph, {}), children: "Never shown" }) : codes ? /* @__PURE__ */ jsxs20("ul", { className: "flex flex-col gap-0.5", "aria-label": "Recovery codes", children: [
4951
5049
  codes.map((code, i) => /* @__PURE__ */ jsxs20("li", { className: "flex min-h-7 items-center gap-1", children: [
4952
5050
  /* @__PURE__ */ jsx21("code", { className: "min-w-0 flex-1 break-all font-mono text-sm text-foreground", children: code }),
4953
5051
  /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: /* @__PURE__ */ jsx21(CopyIcon6, {}), "aria-label": `Copy code ${i + 1}`, onClick: () => void copyValue2(code, "Recovery code") }),
@@ -5270,10 +5368,7 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5270
5368
  title: "Move or copy",
5271
5369
  size: "md",
5272
5370
  onClose,
5273
- footer: /* @__PURE__ */ jsxs22("div", { className: "flex w-full items-center gap-2", children: [
5274
- /* @__PURE__ */ jsx24(StatusLine, { error, notice }),
5275
- /* @__PURE__ */ jsx24(Button19, { variant: "outline", className: "ml-auto", onClick: onClose, children: "Close" })
5276
- ] }),
5371
+ footer: /* @__PURE__ */ jsx24(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx24(StatusLine, { error, notice }) : null, children: /* @__PURE__ */ jsx24(Button19, { variant: "primary", onClick: onClose, children: "Done" }) }),
5277
5372
  children: nothing ? /* @__PURE__ */ jsx24(VaultSection, { children: /* @__PURE__ */ jsx24(Empty, { children: passkey ? "A passkey stays where it was created." : "Only the owner can move or give this item." }) }) : /* @__PURE__ */ jsxs22(VaultSection, { children: [
5278
5373
  canMove && moves.length > 0 ? /* @__PURE__ */ jsx24(
5279
5374
  VaultRow,
@@ -5305,7 +5400,7 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5305
5400
  }
5306
5401
 
5307
5402
  // src/sharing/SharePanel.tsx
5308
- import { Fragment as Fragment14, jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
5403
+ import { Fragment as Fragment15, jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
5309
5404
  function useGrants(port, item) {
5310
5405
  const [grants, setGrants] = useState25(null);
5311
5406
  const [loadError, setLoadError] = useState25(null);
@@ -5388,10 +5483,7 @@ function SharePanel({ item, port, organizations, onClose, onChanged }) {
5388
5483
  title: "Share",
5389
5484
  size: "md",
5390
5485
  onClose,
5391
- footer: /* @__PURE__ */ jsxs23("div", { className: "flex w-full items-center gap-2", children: [
5392
- /* @__PURE__ */ jsx25(StatusLine, { error, notice }),
5393
- /* @__PURE__ */ jsx25(Button20, { variant: "primary", className: "ml-auto", onClick: onClose, children: "Done" })
5394
- ] }),
5486
+ footer: /* @__PURE__ */ jsx25(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx25(StatusLine, { error, notice }) : null, children: /* @__PURE__ */ jsx25(Button20, { variant: "primary", onClick: onClose, children: "Done" }) }),
5395
5487
  children: [
5396
5488
  /* @__PURE__ */ jsxs23(VaultSection, { title: "Who has access", children: [
5397
5489
  isOrg ? /* @__PURE__ */ jsx25(AccessLine, { glyph: BuildingGlyph, who: `${orgName} admins`, children: /* @__PURE__ */ jsx25("span", { className: "px-2 text-sm text-muted-foreground", children: "Full access" }) }) : /* @__PURE__ */ jsx25(AccessLine, { glyph: PersonGlyph, who: "You", meta: "Owner", children: /* @__PURE__ */ jsx25("span", { className: "px-2 text-sm text-muted-foreground", children: "Full access" }) }),
@@ -5456,7 +5548,7 @@ function IntoOrganization({ item, port, organizations, grants, onShared }) {
5456
5548
  const [level, setLevel] = useState25("use");
5457
5549
  const chosen = open.find((o) => o.id === target);
5458
5550
  if (item.owner.kind !== "me") return null;
5459
- return /* @__PURE__ */ jsx25(VaultSection, { title: "Share into organization", children: open.length > 0 ? /* @__PURE__ */ jsxs23(Fragment14, { children: [
5551
+ return /* @__PURE__ */ jsx25(VaultSection, { title: "Share into organization", children: open.length > 0 ? /* @__PURE__ */ jsxs23(Fragment15, { children: [
5460
5552
  /* @__PURE__ */ jsxs23("div", { className: "flex min-h-11 items-center gap-2 px-3 sm:px-4", children: [
5461
5553
  /* @__PURE__ */ jsx25(BuildingGlyph, { className: "size-4 shrink-0 text-muted-foreground" }),
5462
5554
  /* @__PURE__ */ jsx25(Select8, { className: "min-w-0 flex-1", "aria-label": "Organization", value: target || "__none__", onValueChange: (v) => setTarget(v === "__none__" ? "" : v), options: [{ value: "__none__", label: "Choose an organization" }, ...open.map((o) => ({ value: o.id, label: o.name }))] }),
@@ -5542,7 +5634,7 @@ function SharingSummary({ item, port, className }) {
5542
5634
 
5543
5635
  // src/ItemPane.tsx
5544
5636
  import { formatAbsoluteDate as formatAbsoluteDate3 } from "@ai-matrx/kit/format";
5545
- import { Fragment as Fragment15, jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
5637
+ import { Fragment as Fragment16, jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
5546
5638
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
5547
5639
  function ItemPane(props) {
5548
5640
  const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
@@ -5677,6 +5769,21 @@ function ItemPane(props) {
5677
5769
  if (!wasCancelled(e)) setError(e instanceof Error ? e.message : "The value could not be read.");
5678
5770
  }
5679
5771
  }
5772
+ const offFields = item ? item.fields.filter((f) => !f.active) : [];
5773
+ const seedField = item?.fields.find((f) => f.key === OTP_SEED_KEY) ?? null;
5774
+ const canTurnOn = Boolean(item && canEdit);
5775
+ async function turnOn(fields) {
5776
+ if (!item || fields.length === 0) return;
5777
+ setError(null);
5778
+ try {
5779
+ for (const f of fields) await confirmed(() => port.fields.updateField(item, f.id, { active: true }));
5780
+ onChanged();
5781
+ } catch (e) {
5782
+ if (!wasCancelled(e)) setError(e instanceof Error ? e.message : "It could not be turned on.");
5783
+ onChanged();
5784
+ }
5785
+ }
5786
+ const turnOnOne = canTurnOn ? (f) => () => turnOn([f]) : () => void 0;
5680
5787
  const declaredKeys = new Set(declared.map((f) => f.key));
5681
5788
  const extraFields = (item?.fields ?? []).filter((f) => !declaredKeys.has(f.key) && !draft.removed.includes(f.id) && !isAuthenticatorStorage(f.key));
5682
5789
  const metaValue = (key) => draft.values[key] ?? item?.custom.find((c) => c.key === key)?.value ?? "";
@@ -5685,6 +5792,10 @@ function ItemPane(props) {
5685
5792
  const isLogin = (item?.definitionKey ?? definition?.key) === "website_login" || (item?.definitionKey ?? definition?.key) === "username_password";
5686
5793
  const typeLabel = definition?.label ?? item?.definitionLabel ?? "";
5687
5794
  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;
5795
+ const sameNamed = (id) => {
5796
+ const name = organizations.find((o) => o.id === id)?.name.trim().toLowerCase();
5797
+ return Boolean(name) && organizations.filter((o) => o.name.trim().toLowerCase() === name).length > 1;
5798
+ };
5688
5799
  const tagsVisible = showTags || draft.tags.length > 0 || tagsText !== "";
5689
5800
  const descriptionVisible = showDescription || draft.description !== "";
5690
5801
  const header = /* @__PURE__ */ jsxs24("header", { className: "flex min-h-14 items-center gap-2 border-b border-border px-3 py-2 sm:gap-3 sm:px-5", children: [
@@ -5752,7 +5863,7 @@ function ItemPane(props) {
5752
5863
  /* @__PURE__ */ jsx26(SettingsIcon, { className: "size-4" }),
5753
5864
  "Settings"
5754
5865
  ] }) : null,
5755
- canManage ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
5866
+ canManage ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5756
5867
  /* @__PURE__ */ jsx26(DropdownMenuSeparator3, {}),
5757
5868
  /* @__PURE__ */ jsxs24(DropdownMenuItem3, { className: "text-destructive", disabled: busy, onSelect: () => void remove(), children: [
5758
5869
  /* @__PURE__ */ jsx26(TrashGlyph, { className: "size-4" }),
@@ -5768,7 +5879,11 @@ function ItemPane(props) {
5768
5879
  header,
5769
5880
  error ? /* @__PURE__ */ jsx26("div", { role: "alert", className: "border-b border-border bg-muted px-5 py-2 text-sm text-destructive", children: error }) : null,
5770
5881
  notice && !editing ? /* @__PURE__ */ jsx26("div", { role: "status", className: "border-b border-border bg-muted px-5 py-2 text-sm text-warning", children: notice }) : null,
5771
- /* @__PURE__ */ jsx26("div", { className: "min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx26("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5 px-3 py-4 sm:px-6 sm:py-6", children: editing ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
5882
+ item && !editing && offFields.length > 0 ? /* @__PURE__ */ jsxs24("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-5 py-2 text-sm", children: [
5883
+ /* @__PURE__ */ jsx26("span", { className: "min-w-0 flex-1 truncate text-warning", children: offFields.length === 1 ? "1 field is turned off" : `${offFields.length} fields are turned off` }),
5884
+ canTurnOn ? /* @__PURE__ */ jsx26(TurnOnButton, { label: "all fields", onTurnOn: () => turnOn(offFields) }) : null
5885
+ ] }) : null,
5886
+ /* @__PURE__ */ jsx26("div", { className: "min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx26("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5 px-3 py-4 sm:px-6 sm:py-6", children: editing ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5772
5887
  /* @__PURE__ */ jsxs24(VaultSection, { children: [
5773
5888
  mode === "new" ? /* @__PURE__ */ jsx26(VaultRow, { label: "Save to", children: /* @__PURE__ */ jsx26(
5774
5889
  OptionCombobox,
@@ -5780,16 +5895,16 @@ function ItemPane(props) {
5780
5895
  searchPlaceholder: "Find an organization",
5781
5896
  groups: [
5782
5897
  { heading: "Just you", options: ["me"] },
5783
- ...organizations.length > 0 ? [{ heading: "Organizations", options: organizations.map((o) => o.id) }] : [],
5898
+ ...organizations.length > 0 ? [{ heading: "Organizations", options: [...organizations].sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }) || a.id.localeCompare(b.id)).map((o) => o.id) }] : [],
5784
5899
  { heading: "Send", options: ["person"] }
5785
5900
  ],
5786
5901
  getLabel: (v) => v === "me" ? "Personal" : v === "person" ? "Someone else" : organizations.find((o) => o.id === v)?.name ?? v,
5787
- getHint: (v) => v === "me" ? "Only you" : v === "person" ? "Sent by email" : null,
5902
+ getHint: (v) => v === "me" ? "Only you" : v === "person" ? "Sent by email" : sameNamed(v) ? `ID ${v.slice(0, 8)}` : null,
5788
5903
  renderIcon: (v) => v === "me" ? /* @__PURE__ */ jsx26(PersonGlyph, { className: "size-4" }) : v === "person" ? /* @__PURE__ */ jsx26(SendGlyph, { className: "size-4" }) : /* @__PURE__ */ jsx26(BuildingGlyph, { className: "size-4" })
5789
5904
  }
5790
5905
  ) }) : null,
5791
5906
  /* @__PURE__ */ jsx26(EditRow, { label: "Name", value: draft.name, onChange: (v) => patch({ name: v }), placeholder: "Name", autoFocus: mode === "new" }),
5792
- mode === "new" && owner === "person" ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
5907
+ mode === "new" && owner === "person" ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5793
5908
  /* @__PURE__ */ jsx26(EditRow, { label: "Their email", value: recipient, placeholder: "name@company.com", inputMode: "email", onChange: setRecipient }),
5794
5909
  declared.some((f) => f.storage === "encrypted" && f.kind === "secret") ? /* @__PURE__ */ jsx26(VaultRow, { label: "Password", hint: "You never see it", children: /* @__PURE__ */ jsx26(Switch5, { checked: generatePrivately, onCheckedChange: setGeneratePrivately, "aria-label": "Generate the password privately" }) }) : null
5795
5910
  ] }) : null,
@@ -5808,7 +5923,7 @@ function ItemPane(props) {
5808
5923
  multiline: f.kind === "multiline",
5809
5924
  inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
5810
5925
  onChange: (v) => setValue(f.key, v),
5811
- actions: /* @__PURE__ */ jsxs24(Fragment15, { children: [
5926
+ actions: /* @__PURE__ */ jsxs24(Fragment16, { children: [
5812
5927
  hidden && existing?.handling === "revealable" && item?.capabilities.canReveal ? /* @__PURE__ */ jsx26(Button21, { variant: "quiet", icon: /* @__PURE__ */ jsx26(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
5813
5928
  secretKind ? /* @__PURE__ */ jsx26(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
5814
5929
  existing ? /* @__PURE__ */ jsx26(Button21, { variant: "quiet", icon: /* @__PURE__ */ jsx26(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null
@@ -5827,7 +5942,7 @@ function ItemPane(props) {
5827
5942
  onChange: (v) => setValue(f.key, v),
5828
5943
  onRemove: () => patch({ removed: [...draft.removed, f.id] }),
5829
5944
  removeLabel: `Delete ${f.label}`,
5830
- actions: /* @__PURE__ */ jsxs24(Fragment15, { children: [
5945
+ actions: /* @__PURE__ */ jsxs24(Fragment16, { children: [
5831
5946
  f.handling === "revealable" && !(f.key in loaded) && item?.capabilities.canReveal ? /* @__PURE__ */ jsx26(Button21, { variant: "quiet", icon: /* @__PURE__ */ jsx26(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
5832
5947
  /* @__PURE__ */ jsx26(Button21, { variant: "quiet", icon: /* @__PURE__ */ jsx26(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) })
5833
5948
  ] })
@@ -5859,7 +5974,7 @@ function ItemPane(props) {
5859
5974
  i
5860
5975
  )),
5861
5976
  /* @__PURE__ */ jsx26(AddRow, { label: draft.loginUrls.length === 0 ? "Website" : "", onAdd: () => patch({ loginUrls: [...draft.loginUrls, ""] }), children: draft.loginUrls.length === 0 ? "Add website" : "Add another website" }),
5862
- /* @__PURE__ */ jsx26(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator), item, port, onEnrolled: onChanged }),
5977
+ /* @__PURE__ */ jsx26(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }),
5863
5978
  item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
5864
5979
  ] }),
5865
5980
  /* @__PURE__ */ jsx26(VaultSection, { children: /* @__PURE__ */ jsx26(EditRow, { label: "Notes", value: draft.notes, multiline: true, placeholder: "Anything worth remembering", onChange: (v) => patch({ notes: v }) }) }),
@@ -5892,12 +6007,12 @@ function ItemPane(props) {
5892
6007
  ) : null,
5893
6008
  draft.secrets.map((s, i) => /* @__PURE__ */ jsx26(CustomEditRow, { label: s.label, value: s.value, secret: true, onLabel: (v) => patch({ secrets: draft.secrets.map((x, j) => j === i ? { ...x, label: v } : x) }), onValue: (v) => patch({ secrets: draft.secrets.map((x, j) => j === i ? { ...x, value: v } : x) }), onRemove: () => patch({ secrets: draft.secrets.filter((_, j) => j !== i) }) }, `s${i}`)),
5894
6009
  extraCustom.map((c) => /* @__PURE__ */ jsx26(CustomEditRow, { label: c.label, value: c.value, secret: false, onLabel: (v) => patch({ custom: draft.custom.map((x) => x.key === c.key ? { ...x, label: v } : x) }), onValue: (v) => patch({ custom: draft.custom.map((x) => x.key === c.key ? { ...x, value: v } : x) }), onRemove: () => patch({ custom: draft.custom.filter((x) => x.key !== c.key) }) }, c.key)),
5895
- !descriptionVisible && !tagsVisible && draft.secrets.length === 0 && extraCustom.length === 0 ? /* @__PURE__ */ jsx26(AddRow, { label: "Fields, tags", onAdd: null, children: "None" }) : null
6010
+ !descriptionVisible && !tagsVisible && draft.secrets.length === 0 && extraCustom.length === 0 ? /* @__PURE__ */ jsx26(AddRow, { label: "Tags", onAdd: () => setShowTags(true), children: "Add tags" }) : null
5896
6011
  ]
5897
6012
  }
5898
6013
  ),
5899
6014
  filesProps ? /* @__PURE__ */ jsx26(FilesSection, { ...filesProps, editing: true }) : null
5900
- ] }) : item ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
6015
+ ] }) : item ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5901
6016
  /* @__PURE__ */ jsxs24(VaultSection, { children: [
5902
6017
  declared.map((f) => {
5903
6018
  if (f.storage === "metadata") {
@@ -5906,11 +6021,15 @@ function ItemPane(props) {
5906
6021
  }
5907
6022
  const field = item.fields.find((x) => x.key === f.key);
5908
6023
  if (isRecoveryCodesField(f)) return /* @__PURE__ */ jsx26(RecoveryCodesRow, { item, field, port, onChanged }, f.key);
5909
- return field ? /* @__PURE__ */ jsx26(SecretRow, { port, item, field }, field.id) : null;
6024
+ return field ? /* @__PURE__ */ jsx26(SecretRow, { port, item, field, onTurnOn: turnOnOne(field) }, field.id) : null;
5910
6025
  }),
5911
- extraFields.map((f) => isRecoveryCodesField(f) ? /* @__PURE__ */ jsx26(RecoveryCodesRow, { item, field: f, port, onChanged }, f.id) : /* @__PURE__ */ jsx26(SecretRow, { port, item, field: f }, f.id)),
6026
+ extraFields.map((f) => isRecoveryCodesField(f) ? /* @__PURE__ */ jsx26(RecoveryCodesRow, { item, field: f, port, onChanged }, f.id) : /* @__PURE__ */ jsx26(SecretRow, { port, item, field: f, onTurnOn: turnOnOne(f) }, f.id)),
5912
6027
  item.loginUrls.length > 0 ? item.loginUrls.map((u, i) => /* @__PURE__ */ jsx26(PlainRow, { label: i === 0 ? "Website" : `Website ${i + 1}`, value: u, openUrl: host.openUrl }, `${u}-${i}`)) : /* @__PURE__ */ jsx26(AddRow, { label: "Website", onAdd: canEdit ? () => onMode("edit") : null, children: "Add website" }),
5913
- item.authenticator ? /* @__PURE__ */ jsx26(TotpRow, { port, item, onChanged }) : /* @__PURE__ */ jsx26(AddRow, { label: "One-time password", onAdd: canEdit ? () => onMode("edit") : null, children: "Set up" }),
6028
+ item.authenticator ? /* @__PURE__ */ jsx26(TotpRow, { port, item, onChanged }) : seedField && !seedField.active ? (
6029
+ // The secret is still there, only off: turning it on brings the codes back. Never "Set up",
6030
+ // which would invite replacing a working two-factor secret.
6031
+ /* @__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" }) })
6032
+ ) : /* @__PURE__ */ jsx26(AddRow, { label: "One-time password", onAdd: canEdit ? () => onMode("edit") : null, children: "Set up" }),
5914
6033
  !item.authenticator && item.custom.some((c) => c.key === MFA_METHOD_KEY && c.value) ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
5915
6034
  ] }),
5916
6035
  /* @__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" }) }),
@@ -5942,7 +6061,7 @@ function subtitleOf(item, withOwner) {
5942
6061
  const what = loginSiteOf(item) || item.definitionLabel;
5943
6062
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
5944
6063
  }
5945
- function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header }) {
6064
+ function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner }) {
5946
6065
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
5947
6066
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
5948
6067
  const ordered = [...pinned, ...rest];
@@ -5961,7 +6080,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
5961
6080
  listRef.current?.querySelector(`[data-item="${next.id}"]`)?.scrollIntoView({ block: "nearest" });
5962
6081
  }
5963
6082
  };
5964
- const mixedOwners = new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
6083
+ const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
5965
6084
  const row = (i) => /* @__PURE__ */ jsx27(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners }, i.id);
5966
6085
  const sortLabel = SORTS2.find((s) => s.value === sort)?.label ?? "Name";
5967
6086
  return /* @__PURE__ */ jsxs25("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
@@ -6114,7 +6233,7 @@ function refusalText(reason) {
6114
6233
  // src/lists/RestorePanel.tsx
6115
6234
  import { useEffect as useEffect18, useState as useState28 } from "react";
6116
6235
  import { Button as Button23 } from "@ai-matrx/design-system/controls";
6117
- import { Fragment as Fragment16, jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
6236
+ import { Fragment as Fragment17, jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
6118
6237
  var count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
6119
6238
  function RestorePanel({ lists, item, restore, onClose }) {
6120
6239
  const [preview, setPreview] = useState28(null);
@@ -6159,7 +6278,7 @@ function RestorePanel({ lists, item, restore, onClose }) {
6159
6278
  title: `Restore "${item.name}"`,
6160
6279
  size: "md",
6161
6280
  onClose,
6162
- footer: /* @__PURE__ */ jsxs26(Fragment16, { children: [
6281
+ footer: /* @__PURE__ */ jsxs26(Fragment17, { children: [
6163
6282
  /* @__PURE__ */ jsx28(Button23, { variant: "quiet", onClick: onClose, children: refused ? "Close" : "Cancel" }),
6164
6283
  refused ? null : /* @__PURE__ */ jsx28(Button23, { variant: "primary", icon: /* @__PURE__ */ jsx28(RestoreGlyph, {}), disabled: busy || loading || lists.preview !== void 0 && preview === null, onClick: () => void run(), children: busy ? "Restoring\u2026" : "Restore" })
6165
6284
  ] }),
@@ -6179,6 +6298,9 @@ function RestorePanel({ lists, item, restore, onClose }) {
6179
6298
 
6180
6299
  // src/lists/TrashView.tsx
6181
6300
  import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
6301
+ function trashLineOf(item) {
6302
+ return [item.definitionLabel || null, item.owner.label, item.deletion?.deletedAt ? `Deleted ${formatAbsoluteDate4(item.deletion.deletedAt, DAY_FORMAT, "")}` : null].filter(Boolean).join(" \xB7 ");
6303
+ }
6182
6304
  function TrashView({ port, deleted, query, header, onChanged }) {
6183
6305
  const confirmed = useConfirmed();
6184
6306
  const [busyId, setBusyId] = useState29(null);
@@ -6190,7 +6312,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6190
6312
  setMessage(null);
6191
6313
  try {
6192
6314
  const r = await confirmed(() => port.lists.restore(item, deletionId));
6193
- setMessage({ tone: "ok", text: r.alreadyRestored ? `"${item.name}" was already restored.` : `Restored "${item.name}" with ${r.restoredFields} fields and ${r.restoredAttachments} files. It is turned off.` });
6315
+ setMessage({ tone: "ok", text: r.alreadyRestored ? `"${item.name}" was already restored.` : `Restored "${item.name}" with ${r.restoredFields} fields and ${r.restoredAttachments} files. Turn its fields on from the item.` });
6194
6316
  onChanged();
6195
6317
  return r;
6196
6318
  } catch (e) {
@@ -6208,7 +6330,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6208
6330
  /* @__PURE__ */ jsxs27("div", { className: "flex min-h-12 items-center gap-3 px-3 py-1.5 sm:px-4", children: [
6209
6331
  /* @__PURE__ */ jsxs27("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
6210
6332
  /* @__PURE__ */ jsx29("span", { className: "truncate text-sm text-foreground", children: item.name }),
6211
- /* @__PURE__ */ jsx29("span", { className: "truncate text-xs text-muted-foreground", children: [item.deletion?.deletedAt ? `Deleted ${formatAbsoluteDate4(item.deletion.deletedAt, DAY_FORMAT, "")}` : null, item.owner.kind !== "me" ? item.owner.label : null].filter(Boolean).join(" \xB7 ") })
6333
+ /* @__PURE__ */ jsx29("span", { className: "truncate text-xs text-muted-foreground", children: trashLineOf(item) })
6212
6334
  ] }),
6213
6335
  /* @__PURE__ */ jsx29(Button24, { variant: "outline", icon: /* @__PURE__ */ jsx29(RestoreGlyph, {}), disabled: busyId !== null || !item.deletion, onClick: () => setRestoring(item), children: "Restore" })
6214
6336
  ] }, item.id)
@@ -6499,10 +6621,9 @@ function SecureLinkPanel({ item, port, onClose }) {
6499
6621
  title: "Send securely",
6500
6622
  size: "md",
6501
6623
  onClose,
6502
- footer: /* @__PURE__ */ jsxs29("div", { className: "flex w-full flex-wrap items-center gap-2", children: [
6503
- /* @__PURE__ */ jsx31("div", { className: "w-full sm:w-auto sm:min-w-0 sm:flex-1", children: /* @__PURE__ */ jsx31(StatusLine, { error, notice }) }),
6624
+ footer: /* @__PURE__ */ jsxs29(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx31(StatusLine, { error, notice }) : null, children: [
6504
6625
  sameChannelSent && !error ? /* @__PURE__ */ jsx31(Badge8, { tone: "warning", children: "Both by email" }) : null,
6505
- /* @__PURE__ */ jsx31(Button26, { variant: "quiet", className: "ml-auto", onClick: onClose, children: "Close" }),
6626
+ /* @__PURE__ */ jsx31(Button26, { variant: "quiet", onClick: onClose, children: "Close" }),
6506
6627
  /* @__PURE__ */ jsx31(Button26, { variant: "primary", disabled: !canSend, onClick: () => void send(), children: busy === "send" ? "Sending\u2026" : "Send link" })
6507
6628
  ] }),
6508
6629
  children: [
@@ -6655,7 +6776,7 @@ function addPasskeyFailure(message2) {
6655
6776
  var isRevoked = (d) => d.revokedAt !== null;
6656
6777
 
6657
6778
  // src/devices/DevicesPanel.tsx
6658
- import { Fragment as Fragment17, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
6779
+ import { Fragment as Fragment18, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
6659
6780
  var messageOf3 = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
6660
6781
  function ErrorLine2({ text }) {
6661
6782
  return text ? /* @__PURE__ */ jsx32("p", { role: "alert", className: "px-1 text-sm text-destructive", children: text }) : null;
@@ -6786,7 +6907,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6786
6907
  };
6787
6908
  const canApprove = Boolean(key && passkeys && here && methods?.passkey && !approvedUntil);
6788
6909
  const title = isMac ? "Approve this Mac" : name ? `Approve ${name}` : "Approve this browser";
6789
- const footer = approvedUntil ? /* @__PURE__ */ jsx32(Button27, { variant: "primary", onClick: onClose, children: "Done" }) : /* @__PURE__ */ jsxs30(Fragment17, { children: [
6910
+ const footer = approvedUntil ? /* @__PURE__ */ jsx32(Button27, { variant: "primary", onClick: onClose, children: "Done" }) : /* @__PURE__ */ jsxs30(Fragment18, { children: [
6790
6911
  /* @__PURE__ */ jsx32(Button27, { variant: "quiet", disabled: busy !== null, onClick: refuse, children: key ? "Refuse" : "Close" }),
6791
6912
  canApprove ? /* @__PURE__ */ jsx32(Button27, { variant: "primary", icon: /* @__PURE__ */ jsx32(FingerprintGlyph, {}), disabled: busy !== null, onClick: () => void approve(), children: busy === "approve" ? "Approving\u2026" : "Approve with passkey" }) : null
6792
6913
  ] });
@@ -6794,7 +6915,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6794
6915
  "This link names no key. Start again from ",
6795
6916
  where,
6796
6917
  "."
6797
- ] }) }) : /* @__PURE__ */ jsxs30(Fragment17, { children: [
6918
+ ] }) }) : /* @__PURE__ */ jsxs30(Fragment18, { children: [
6798
6919
  /* @__PURE__ */ jsxs30(VaultSection, { title: "What is asking", children: [
6799
6920
  /* @__PURE__ */ jsx32(VaultRow, { label: "Asking", hint: "Approve only if you just asked", children: asker }),
6800
6921
  name ? /* @__PURE__ */ jsx32(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx32("span", { className: "block truncate", children: name }) }) : null,
@@ -7094,7 +7215,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7094
7215
  }
7095
7216
 
7096
7217
  // src/native/NativeImportPanel.tsx
7097
- import { Fragment as Fragment18, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7218
+ import { Fragment as Fragment19, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7098
7219
  var TONE = {
7099
7220
  eligible: "success",
7100
7221
  committed: "success",
@@ -7138,16 +7259,16 @@ function Importer({ bridge, readLimits, activeOrganizationId, organizations, onC
7138
7259
  const yes = await confirm12({ title: `Add ${plural2(ready, "passkey")} to ${orgName}?`, description: "Passkeys already there are skipped.", confirmLabel: "Import" });
7139
7260
  if (yes) await flow.confirm();
7140
7261
  };
7141
- const footer = preview ? /* @__PURE__ */ jsxs31(Fragment18, { children: [
7262
+ const footer = preview ? /* @__PURE__ */ jsxs31(Fragment19, { children: [
7142
7263
  /* @__PURE__ */ jsx33(Button28, { variant: "quiet", disabled: busy, onClick: () => void flow.cancel(), children: "Cancel" }),
7143
7264
  /* @__PURE__ */ jsxs31(Button28, { variant: "primary", icon: /* @__PURE__ */ jsx33(ImportGlyph, { className: "size-4" }), disabled: busy || ready === 0, onClick: () => void importNow(), children: [
7144
7265
  "Import ",
7145
7266
  ready
7146
7267
  ] })
7147
- ] }) : status ? active ? /* @__PURE__ */ jsxs31(Fragment18, { children: [
7268
+ ] }) : status ? active ? /* @__PURE__ */ jsxs31(Fragment19, { children: [
7148
7269
  /* @__PURE__ */ jsx33(Button28, { variant: "outline", disabled: busy, onClick: () => void flow.refresh(), children: "Refresh" }),
7149
7270
  /* @__PURE__ */ jsx33(Button28, { variant: "outline", disabled: busy, onClick: () => void flow.cancel(), children: "Cancel import" })
7150
- ] }) : /* @__PURE__ */ jsxs31(Fragment18, { children: [
7271
+ ] }) : /* @__PURE__ */ jsxs31(Fragment19, { children: [
7151
7272
  /* @__PURE__ */ jsx33(Button28, { variant: "quiet", onClick: flow.reset, children: "New file" }),
7152
7273
  /* @__PURE__ */ jsx33(Button28, { variant: "primary", onClick: onClose, children: "Done" })
7153
7274
  ] }) : /* @__PURE__ */ jsx33(Button28, { variant: "quiet", onClick: onClose, children: "Close" });
@@ -7186,7 +7307,7 @@ import { GlobeIcon as GlobeIcon4 } from "@ai-matrx/design-system/icons";
7186
7307
  // src/extension/CaptureCard.tsx
7187
7308
  import { useState as useState35, useSyncExternalStore as useSyncExternalStore3 } from "react";
7188
7309
  import { Button as Button29, SearchField as SearchField3 } from "@ai-matrx/design-system/controls";
7189
- import { Fragment as Fragment19, jsx as jsx34, jsxs as jsxs32 } from "react/jsx-runtime";
7310
+ import { Fragment as Fragment20, jsx as jsx34, jsxs as jsxs32 } from "react/jsx-runtime";
7190
7311
  var SEARCH_FROM = 4;
7191
7312
  function CaptureCard({ capture, onChanged }) {
7192
7313
  const prompt = useSyncExternalStore3(capture.subscribe, capture.get, capture.get);
@@ -7219,7 +7340,7 @@ function CaptureCard({ capture, onChanged }) {
7219
7340
  ] }),
7220
7341
  /* @__PURE__ */ jsx34(Button29, { variant: "quiet", icon: /* @__PURE__ */ jsx34(XGlyph, {}), "aria-label": "Not now", disabled: busy, onClick: () => void decide({ action: "dismiss" }) })
7221
7342
  ] }),
7222
- prompt.unavailable ? /* @__PURE__ */ jsx34("p", { className: "text-xs text-muted-foreground", children: "Save it by hand with Save a login." }) : /* @__PURE__ */ jsxs32(Fragment19, { children: [
7343
+ prompt.unavailable ? /* @__PURE__ */ jsx34("p", { className: "text-xs text-muted-foreground", children: "Save it by hand with Save a login." }) : /* @__PURE__ */ jsxs32(Fragment20, { children: [
7223
7344
  existing.length > 0 ? /* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1", children: [
7224
7345
  existing.length >= SEARCH_FROM ? /* @__PURE__ */ jsx34(SearchField3, { value: q, onChange: (e) => setQ(e.target.value), placeholder: "Search logins", "aria-label": "Search logins to update" }) : null,
7225
7346
  /* @__PURE__ */ jsxs32("ul", { className: "flex max-h-36 flex-col gap-px overflow-y-auto", children: [
@@ -7325,7 +7446,7 @@ import { useState as useState37 } from "react";
7325
7446
  import { Popover as Popover2, PopoverContent as PopoverContent2, PopoverTrigger as PopoverTrigger2 } from "@ai-matrx/design-system";
7326
7447
  import { Button as Button31 } from "@ai-matrx/design-system/controls";
7327
7448
  import { CopyIcon as CopyIcon7 } from "@ai-matrx/design-system/icons";
7328
- import { Fragment as Fragment20, jsx as jsx36, jsxs as jsxs34 } from "react/jsx-runtime";
7449
+ import { Fragment as Fragment21, jsx as jsx36, jsxs as jsxs34 } from "react/jsx-runtime";
7329
7450
  var HOLD_MS = 6e4;
7330
7451
  function PageGenerator({ fields }) {
7331
7452
  const limits = useGeneratorLimitsSource();
@@ -7376,12 +7497,12 @@ function PageGenerator({ fields }) {
7376
7497
  }
7377
7498
  };
7378
7499
  const value = held.value;
7379
- return /* @__PURE__ */ jsxs34(Fragment20, { children: [
7500
+ return /* @__PURE__ */ jsxs34(Fragment21, { children: [
7380
7501
  /* @__PURE__ */ jsx36(
7381
7502
  VaultRow,
7382
7503
  {
7383
7504
  label: "New password",
7384
- actions: /* @__PURE__ */ jsxs34(Fragment20, { children: [
7505
+ actions: /* @__PURE__ */ jsxs34(Fragment21, { children: [
7385
7506
  value ? /* @__PURE__ */ jsx36(Button31, { variant: "quiet", icon: /* @__PURE__ */ jsx36(CopyIcon7, {}), "aria-label": "Copy password", onClick: () => void copy(value, "Password") }) : null,
7386
7507
  /* @__PURE__ */ jsxs34(Popover2, { open, onOpenChange: setOpen, children: [
7387
7508
  /* @__PURE__ */ jsx36(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsx36(Button31, { variant: "outline", icon: /* @__PURE__ */ jsx36(KeyGlyph, {}), disabled: busy, children: "Generate" }) }),
@@ -7758,8 +7879,33 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7758
7879
  },
7759
7880
  [loc, move]
7760
7881
  );
7882
+ const keyState = useRef19({ loc, go });
7883
+ keyState.current = { loc, go };
7884
+ useEffect23(() => {
7885
+ const onKey = (e) => {
7886
+ if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.altKey) return;
7887
+ const root = rootRef.current;
7888
+ const at = document.activeElement;
7889
+ if (!root || at && at !== document.body && !root.contains(at)) return;
7890
+ if (at instanceof HTMLElement && (at.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(at.tagName))) return;
7891
+ if (document.querySelector('[role="dialog"], [role="alertdialog"]')) return;
7892
+ const { loc: here, go: to } = keyState.current;
7893
+ if (e.key === "/") {
7894
+ const search = root.querySelector('input[aria-label^="Search"]');
7895
+ if (!search) return;
7896
+ e.preventDefault();
7897
+ search.focus();
7898
+ } else if (e.key === "Escape" && here.itemId && here.mode === "view" && !here.panel) {
7899
+ e.preventDefault();
7900
+ to({ itemId: null, mode: "view", panel: null });
7901
+ }
7902
+ };
7903
+ document.addEventListener("keydown", onKey);
7904
+ return () => document.removeEventListener("keydown", onKey);
7905
+ }, []);
7761
7906
  const knobsRef = useRef19(host.knobs);
7762
7907
  knobsRef.current = host.knobs;
7908
+ const [signedOut, setSignedOut] = useState40(false);
7763
7909
  const load = useCallback5(() => {
7764
7910
  setError(null);
7765
7911
  port.load().then((s) => {
@@ -7770,7 +7916,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7770
7916
  (rows) => setDeleted([...s.deleted, ...rows.filter((r) => !s.deleted.some((d) => d.id === r.id))]),
7771
7917
  () => void 0
7772
7918
  );
7773
- }).catch((e) => setError(e instanceof Error ? e.message : "The vault could not be loaded."));
7919
+ }).catch((e) => {
7920
+ setSignedOut(e?.status === 401);
7921
+ setError(e instanceof Error ? e.message : "The vault could not be loaded.");
7922
+ });
7774
7923
  port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
7775
7924
  knobsRef.current?.().catch(() => void 0);
7776
7925
  port.lists.recents?.list().then(setRecents, () => void 0);
@@ -7784,6 +7933,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7784
7933
  const orgs = snap?.organizations ?? [];
7785
7934
  const definitions = snap?.definitions ?? [];
7786
7935
  const inThisVault = useMemo6(() => items.filter((i) => inVault(i, loc.vault)), [items, loc.vault]);
7936
+ const vaultMixesOwners = new Set(inThisVault.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
7787
7937
  const views = useMemo6(() => buildViews(inThisVault, definitions, favorites, recents), [inThisVault, definitions, favorites, recents]);
7788
7938
  const visible = useMemo6(() => {
7789
7939
  const filtered = inThisVault.filter((i) => {
@@ -7824,8 +7974,11 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7824
7974
  return /* @__PURE__ */ jsxs37("div", { ref: rootRef, role: "alert", className: "flex h-full flex-col items-center justify-center gap-3 bg-background p-6 text-center text-sm", children: [
7825
7975
  /* @__PURE__ */ jsx39(SafeGlyph, { className: "size-8 text-muted-foreground" }),
7826
7976
  /* @__PURE__ */ jsx39("div", { className: "font-medium", children: "The vault couldn\u2019t open" }),
7827
- /* @__PURE__ */ jsx39("div", { className: "max-w-xs text-muted-foreground", children: plainLoadError(error) }),
7828
- /* @__PURE__ */ jsx39("button", { type: "button", className: "text-primary hover:underline", onClick: load, children: "Try again" })
7977
+ /* @__PURE__ */ jsx39("div", { className: "max-w-xs text-muted-foreground", children: signedOut ? "You\u2019ve been signed out." : plainLoadError(error) }),
7978
+ /* @__PURE__ */ jsxs37("div", { className: "flex gap-2", children: [
7979
+ signedOut && host.signIn ? /* @__PURE__ */ jsx39(Button33, { variant: "primary", onClick: host.signIn, children: "Sign in" }) : null,
7980
+ /* @__PURE__ */ jsx39(Button33, { variant: signedOut && host.signIn ? "outline" : "primary", onClick: load, children: "Try again" })
7981
+ ] })
7829
7982
  ] });
7830
7983
  }
7831
7984
  if (!snap) {
@@ -7905,6 +8058,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7905
8058
  {
7906
8059
  title,
7907
8060
  items: visible,
8061
+ showOwner: vaultMixesOwners,
7908
8062
  definitions,
7909
8063
  port,
7910
8064
  selectedId: loc.itemId,
@@ -8677,6 +8831,7 @@ async function readNativeImportLimits(api, organizationId) {
8677
8831
  var recoveryOf = (w) => w.lifecycle?.vault_recovery;
8678
8832
  var isDeleted = (w) => recoveryOf(w)?.state === "deleted";
8679
8833
  var MATCH_MODES2 = ["host", "domain", "exact", "never"];
8834
+ var FIELD_WORDS = { totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
8680
8835
  var handlingOf = (v) => v === "visible" || v === "sealed" ? v : "revealable";
8681
8836
  function fieldOf(w, def) {
8682
8837
  const declared = def?.fields.find((f) => f.key === w.field_key);
@@ -8684,7 +8839,7 @@ function fieldOf(w, def) {
8684
8839
  return {
8685
8840
  id: w.id,
8686
8841
  key: w.field_key,
8687
- label: declared?.label ?? humanize(w.field_key),
8842
+ label: declared?.label ?? FIELD_WORDS[w.field_key] ?? humanize(w.field_key),
8688
8843
  kind: declared?.kind ?? (handling === "visible" ? "text" : "secret"),
8689
8844
  handling,
8690
8845
  editable: w.editable ?? true,
@@ -8902,9 +9057,11 @@ function createVaultKnobs(client, address) {
8902
9057
  }
8903
9058
  export {
8904
9059
  CATEGORIES,
9060
+ CONFIRM_RETURN_KEY,
8905
9061
  DEFAULT_LOCATION,
8906
9062
  GenerateButton,
8907
9063
  HIDDEN_ITEM_TAG,
9064
+ IdentityGate,
8908
9065
  LOCKED_MESSAGE,
8909
9066
  VaultApp,
8910
9067
  VaultLockedError,
@@ -8922,6 +9079,9 @@ export {
8922
9079
  matchesQuery,
8923
9080
  needsRecentSignIn,
8924
9081
  parseDefinitions,
8925
- sortItems
9082
+ siteDomainOf,
9083
+ sortItems,
9084
+ useConfirmed,
9085
+ wasCancelled
8926
9086
  };
8927
9087
  //# sourceMappingURL=index.js.map