@ai-matrx/vault 0.1.10 → 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
  }
@@ -798,6 +876,8 @@ function matchesQuery(item, query) {
798
876
  if (!q) return true;
799
877
  return [item.name, item.definitionLabel, ...item.tags, ...item.loginUrls, ...item.custom.map((c) => `${c.label} ${c.value}`)].some((h) => h.toLowerCase().includes(q));
800
878
  }
879
+ var HIDDEN_ITEM_TAG = "matrx:hidden";
880
+ var isHiddenItem = (item) => (item.tags ?? []).includes(HIDDEN_ITEM_TAG);
801
881
  var isAuthenticatorStorage = (key) => key === "totp_seed" || key.startsWith("totp_") || key === "authenticator_last_time_step";
802
882
  function loginSiteOf(item) {
803
883
  const raw = item.loginUrls[0];
@@ -808,14 +888,27 @@ function loginSiteOf(item) {
808
888
  return raw.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0] ?? "";
809
889
  }
810
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
+ }
811
902
  function sortItems(items, sort) {
812
903
  const out = [...items];
813
904
  const byName = (a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
814
905
  if (sort === "name") return out.sort(byName);
815
- if (sort === "website") return out.sort((a, b) => {
816
- const x = loginSiteOf(a), y = loginSiteOf(b);
817
- return x && y ? x.localeCompare(y) || byName(a, b) : x ? -1 : y ? 1 : byName(a, b);
818
- });
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
+ });
819
912
  if (sort === "updated") return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
820
913
  return out.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
821
914
  }
@@ -1206,7 +1299,7 @@ import { useEffect as useEffect6, useRef as useRef4, useState as useState6 } fro
1206
1299
  import { Button as Button3 } from "@ai-matrx/design-system/controls";
1207
1300
  import { cn as cn4 } from "@ai-matrx/design-system";
1208
1301
  import { formatAbsoluteDate } from "@ai-matrx/kit/format";
1209
- 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";
1210
1303
  var PAGE = 50;
1211
1304
  var MAX = 500;
1212
1305
  function ActivityPanel({ item, port, onClose }) {
@@ -1235,7 +1328,10 @@ function ActivityPanel({ item, port, onClose }) {
1235
1328
  {
1236
1329
  title: "Activity",
1237
1330
  onClose,
1238
- 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
+ ] }),
1239
1335
  children: [
1240
1336
  error ? /* @__PURE__ */ jsx6("p", { role: "alert", className: "px-1 text-sm text-destructive", children: error }) : null,
1241
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(
@@ -1258,7 +1354,7 @@ function ActivityPanel({ item, port, onClose }) {
1258
1354
  }
1259
1355
 
1260
1356
  // src/activity/HistoryPanel.tsx
1261
- 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";
1262
1358
  var MASK2 = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
1263
1359
  var messageOf = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
1264
1360
  function ItemHistoryPanel({ item, port, arg, onClose, onChanged }) {
@@ -1318,8 +1414,11 @@ function ItemHistoryPanel({ item, port, arg, onClose, onChanged }) {
1318
1414
  {
1319
1415
  title: "Password history",
1320
1416
  onClose,
1321
- footer: page?.nextBefore ? /* @__PURE__ */ jsx7(Button4, { variant: "outline", disabled: more, onClick: () => void loadMore(), children: more ? "Loading\u2026" : "Show earlier" }) : void 0,
1322
- 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: [
1323
1422
  error ? /* @__PURE__ */ jsx7("p", { role: "alert", className: "px-1 text-sm text-destructive", children: error }) : null,
1324
1423
  notice ? /* @__PURE__ */ jsx7("p", { role: "status", className: "px-1 text-sm text-success", children: notice }) : null,
1325
1424
  page && groups.length === 0 ? /* @__PURE__ */ jsx7(Empty, { children: "No earlier values yet." }) : null,
@@ -1387,7 +1486,7 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
1387
1486
  VaultRow,
1388
1487
  {
1389
1488
  label: when,
1390
- actions: entry.available ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
1489
+ actions: entry.available ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
1391
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() }),
1392
1491
  /* @__PURE__ */ jsx7(Button4, { variant: "quiet", icon: /* @__PURE__ */ jsx7(CopyIcon2, {}), "aria-label": "Copy", disabled: busy, onClick: () => void copy() }),
1393
1492
  canRestore(page, entry) ? /* @__PURE__ */ jsx7(Button4, { variant: "quiet", icon: /* @__PURE__ */ jsx7(RestoreGlyph, {}), "aria-label": "Restore", disabled: busy, onClick: () => void restore() }) : null
@@ -1721,7 +1820,7 @@ function imageFrom(data) {
1721
1820
  }
1722
1821
 
1723
1822
  // src/authenticator/Setup.tsx
1724
- 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";
1725
1824
  function useCanScan() {
1726
1825
  const [ok, setOk] = useState10(false);
1727
1826
  useEffect10(() => {
@@ -1854,7 +1953,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
1854
1953
  VaultRow,
1855
1954
  {
1856
1955
  label,
1857
- actions: /* @__PURE__ */ jsxs9(Fragment4, { children: [
1956
+ actions: /* @__PURE__ */ jsxs9(Fragment5, { children: [
1858
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() }),
1859
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
1860
1959
  ] }),
@@ -1923,13 +2022,14 @@ import { confirm as confirm2 } from "@ai-matrx/kit/confirm-opener";
1923
2022
 
1924
2023
  // src/authenticator/port.ts
1925
2024
  var MFA_METHOD_KEY = "mfa_method";
2025
+ var OTP_SEED_KEY = "totp_seed";
1926
2026
  function mfaMethodOf(item) {
1927
2027
  const v = item.custom.find((c) => c.key === MFA_METHOD_KEY)?.value;
1928
2028
  return v === "sms" ? "sms" : v ? "push_or_other" : null;
1929
2029
  }
1930
2030
 
1931
2031
  // src/authenticator/TotpRows.tsx
1932
- 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";
1933
2033
  function otpActionError(e, what) {
1934
2034
  const status = e?.status;
1935
2035
  if (status === 404) return "There is no one-time password here.";
@@ -2004,7 +2104,7 @@ function TotpRow({ port, item, onChanged }) {
2004
2104
  VaultRow,
2005
2105
  {
2006
2106
  label: "One-time password",
2007
- actions: /* @__PURE__ */ jsxs10(Fragment5, { children: [
2107
+ actions: /* @__PURE__ */ jsxs10(Fragment6, { children: [
2008
2108
  a.enabled ? /* @__PURE__ */ jsx10(CopyCodeButton, { live, label: item.name }) : null,
2009
2109
  menu
2010
2110
  ] }),
@@ -2087,7 +2187,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
2087
2187
  }
2088
2188
 
2089
2189
  // src/authenticator/AddOtpPanel.tsx
2090
- 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";
2091
2191
  var NEW = "__new__";
2092
2192
  var LOGIN_KEYS = ["website_login", "username_password"];
2093
2193
  function normalizeWebsite(raw) {
@@ -2170,7 +2270,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2170
2270
  title: "Add one-time password",
2171
2271
  size: "md",
2172
2272
  onClose,
2173
- footer: /* @__PURE__ */ jsxs11(Fragment6, { children: [
2273
+ footer: /* @__PURE__ */ jsxs11(Fragment7, { children: [
2174
2274
  /* @__PURE__ */ jsx11(Button8, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
2175
2275
  /* @__PURE__ */ jsx11(Button8, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
2176
2276
  ] }),
@@ -2210,7 +2310,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2210
2310
  }
2211
2311
  }
2212
2312
  ) }),
2213
- isNew ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
2313
+ isNew ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
2214
2314
  /* @__PURE__ */ jsx11(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx11(
2215
2315
  Field4,
2216
2316
  {
@@ -2242,7 +2342,7 @@ function AddedPanel({ port, item, onClose, onDone }) {
2242
2342
  }
2243
2343
 
2244
2344
  // src/authenticator/AuthenticatorView.tsx
2245
- 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";
2246
2346
  function matchesEntry(entry, q) {
2247
2347
  if (!entry || !q) return false;
2248
2348
  return [entry.issuer, entry.account].some((v) => v?.toLowerCase().includes(q));
@@ -2281,7 +2381,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2281
2381
  const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
2282
2382
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
2283
2383
  return /* @__PURE__ */ jsxs12("section", { "aria-label": "One-time passwords", className: "flex h-full min-h-0 flex-col bg-background", children: [
2284
- header,
2384
+ header ? /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
2285
2385
  /* @__PURE__ */ jsxs12("div", { className: "flex min-h-12 items-center justify-between gap-2 px-4 pb-1 pt-3", children: [
2286
2386
  /* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 items-baseline gap-2", children: [
2287
2387
  /* @__PURE__ */ jsx12("h2", { className: "truncate text-sm font-semibold text-foreground", children: "One-time passwords" }),
@@ -2293,7 +2393,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
2293
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" }) }),
2294
2394
  /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-foreground", children: "No one-time passwords yet" }),
2295
2395
  /* @__PURE__ */ jsx12(Button9, { variant: "primary", icon: /* @__PURE__ */ jsx12(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add one-time password" })
2296
- ] }) : 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(
2297
2397
  OtpListRow,
2298
2398
  {
2299
2399
  port,
@@ -2390,7 +2490,7 @@ function OtpListRow({ port, item, family, entry, onOpen, onRename, onChanged, on
2390
2490
  /* @__PURE__ */ jsx12(CopyIcon4, { className: "size-4" }),
2391
2491
  "Copy code"
2392
2492
  ] }) : null,
2393
- canManage ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
2493
+ canManage ? /* @__PURE__ */ jsxs12(Fragment8, { children: [
2394
2494
  /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onRename, children: [
2395
2495
  /* @__PURE__ */ jsx12(PencilGlyph, { className: "size-4" }),
2396
2496
  "Rename"
@@ -2430,9 +2530,9 @@ function sandboxBlocker(envKeyDraft, savedEnvKey) {
2430
2530
  return null;
2431
2531
  }
2432
2532
  var HANDLING_WORDS = {
2433
- visible: { label: "Anyone", hint: "Anyone who can use it can show or copy it" },
2434
- revealable: { label: "On request", hint: "Showing or copying it needs reveal access" },
2435
- 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." }
2436
2536
  };
2437
2537
  function handlingChoices(current) {
2438
2538
  return current === "sealed" ? ["sealed"] : ["visible", "revealable", "sealed"];
@@ -2614,7 +2714,7 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
2614
2714
  }
2615
2715
  ) }),
2616
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 }) }) }),
2617
- /* @__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 }) }) })
2618
2718
  ] }),
2619
2719
  error ? /* @__PURE__ */ jsx13(Problem, { children: error }) : null
2620
2720
  ] });
@@ -2682,7 +2782,7 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
2682
2782
  f.envKey
2683
2783
  ] }) : null,
2684
2784
  f.injectIntoSandbox ? /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
2685
- !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
2686
2786
  ] })
2687
2787
  },
2688
2788
  f.id
@@ -3078,7 +3178,7 @@ function startNewRestoreRun(preview) {
3078
3178
  import { useState as useState16 } from "react";
3079
3179
  import { Checkbox } from "@ai-matrx/design-system";
3080
3180
  import { Badge as Badge4, Button as Button12, DisclosureHeader as DisclosureHeader2 } from "@ai-matrx/design-system/controls";
3081
- 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";
3082
3182
  function PickLine({ checked, onChange, disabled = false, title, detail, badge, tone }) {
3083
3183
  return /* @__PURE__ */ jsxs15("label", { className: "flex min-h-11 items-center gap-3 px-4 py-1", "data-clickable": disabled ? void 0 : "", children: [
3084
3184
  /* @__PURE__ */ jsx15(Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
@@ -3090,7 +3190,7 @@ function PickLine({ checked, onChange, disabled = false, title, detail, badge, t
3090
3190
  function Windowed({ items, size = 150, render }) {
3091
3191
  const [all, setAll] = useState16(false);
3092
3192
  const shown = all ? items : items.slice(0, size);
3093
- return /* @__PURE__ */ jsxs15(Fragment8, { children: [
3193
+ return /* @__PURE__ */ jsxs15(Fragment9, { children: [
3094
3194
  shown.map(render),
3095
3195
  items.length > shown.length ? /* @__PURE__ */ jsx15("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsxs15(Button12, { variant: "quiet", onClick: () => setAll(true), children: [
3096
3196
  "Show all ",
@@ -3111,7 +3211,7 @@ function ErrorLine({ message: message2 }) {
3111
3211
  var plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
3112
3212
 
3113
3213
  // src/io/BackupPanel.tsx
3114
- 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";
3115
3215
  var RECORD_STATUS = {
3116
3216
  created: "Restored",
3117
3217
  replayed: "Already restored",
@@ -3239,13 +3339,13 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3239
3339
  const retryable = (result?.retryable_failure ?? 0) > 0;
3240
3340
  const mismatch = repeat !== "" && repeat !== passphrase;
3241
3341
  const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
3242
- const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
3342
+ const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3243
3343
  /* @__PURE__ */ jsx16(Button13, { variant: "quiet", onClick: onClose, children: "Close" }),
3244
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: [
3245
3345
  "Review ",
3246
3346
  selected.size
3247
3347
  ] })
3248
- ] }) : /* @__PURE__ */ jsxs16(Fragment9, { children: [
3348
+ ] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
3249
3349
  /* @__PURE__ */ jsx16(Button13, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
3250
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: [
3251
3351
  "Restore ",
@@ -3266,12 +3366,12 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3266
3366
  ]
3267
3367
  }
3268
3368
  ),
3269
- mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
3369
+ mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3270
3370
  /* @__PURE__ */ jsx16(
3271
3371
  VaultSection,
3272
3372
  {
3273
3373
  title: `${selected.size} of ${plural(items.length, "credential")}`,
3274
- actions: /* @__PURE__ */ jsxs16(Fragment9, { children: [
3374
+ actions: /* @__PURE__ */ jsxs16(Fragment10, { children: [
3275
3375
  /* @__PURE__ */ jsx16(Button13, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
3276
3376
  setBackup(null);
3277
3377
  setSelected(new Set(items.map((i) => i.id)));
@@ -3284,7 +3384,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3284
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) })
3285
3385
  }
3286
3386
  ),
3287
- backup ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
3387
+ backup ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3288
3388
  /* @__PURE__ */ jsx16(Summary, { value: backup }),
3289
3389
  /* @__PURE__ */ jsx16(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3290
3390
  /* @__PURE__ */ jsxs16(VaultSection, { children: [
@@ -3292,7 +3392,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3292
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) }) })
3293
3393
  ] })
3294
3394
  ] }) : null
3295
- ] }) : /* @__PURE__ */ jsx16(Fragment9, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
3395
+ ] }) : /* @__PURE__ */ jsx16(Fragment10, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
3296
3396
  /* @__PURE__ */ jsxs16(VaultRow, { label: "File", ...file ? { hint: file.name } : {}, children: [
3297
3397
  /* @__PURE__ */ jsx16(Button13, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
3298
3398
  /* @__PURE__ */ jsx16(
@@ -3310,7 +3410,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3310
3410
  )
3311
3411
  ] }),
3312
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) }) })
3313
- ] }) : /* @__PURE__ */ jsxs16(Fragment9, { children: [
3413
+ ] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
3314
3414
  /* @__PURE__ */ jsx16(Summary, { value: restorePreview }),
3315
3415
  !result ? /* @__PURE__ */ jsx16(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
3316
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: [
@@ -3327,7 +3427,7 @@ import { useMemo, useState as useState18 } from "react";
3327
3427
  import { Button as Button14, Select as Select5 } from "@ai-matrx/design-system/controls";
3328
3428
  import { downloadFile as downloadFile2 } from "@ai-matrx/kit/download";
3329
3429
  import { confirm as confirm5 } from "@ai-matrx/kit/confirm-opener";
3330
- 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";
3331
3431
  var FORMATS = [
3332
3432
  { value: "matrx_login_csv_v1", label: "Matrx", hint: "Website logins, all common fields" },
3333
3433
  { value: "firefox_csv_v1", label: "Firefox", hint: "Address, username and password" },
@@ -3407,7 +3507,7 @@ function ExportPanel({ port, items, onClose }) {
3407
3507
  title: "Export logins",
3408
3508
  size: "lg",
3409
3509
  onClose,
3410
- footer: /* @__PURE__ */ jsxs17(Fragment10, { children: [
3510
+ footer: /* @__PURE__ */ jsxs17(Fragment11, { children: [
3411
3511
  /* @__PURE__ */ jsx17(Button14, { variant: "quiet", onClick: onClose, children: "Close" }),
3412
3512
  preview ? /* @__PURE__ */ jsxs17(Button14, { variant: "primary", icon: /* @__PURE__ */ jsx17(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
3413
3513
  "Download ",
@@ -3434,7 +3534,7 @@ function ExportPanel({ port, items, onClose }) {
3434
3534
  VaultSection,
3435
3535
  {
3436
3536
  title: `${selected.size} of ${plural(logins.length, "login")}`,
3437
- actions: /* @__PURE__ */ jsxs17(Fragment10, { children: [
3537
+ actions: /* @__PURE__ */ jsxs17(Fragment11, { children: [
3438
3538
  /* @__PURE__ */ jsx17(Button14, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
3439
3539
  changed();
3440
3540
  setSelected(new Set(logins.map((l) => l.id)));
@@ -4316,7 +4416,7 @@ async function parseImportFile(source, file, limits, signal) {
4316
4416
  }
4317
4417
 
4318
4418
  // src/io/ImportPanel.tsx
4319
- 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";
4320
4420
  var ROLE_OPTIONS = [
4321
4421
  { value: "keep", label: "Keep encrypted" },
4322
4422
  { value: "title", label: "Name" },
@@ -4491,13 +4591,13 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4491
4591
  const help = EXPORT_HELP[source];
4492
4592
  const stage = done ? "done" : draft ? "review" : "choose";
4493
4593
  const busy = running !== null || reading;
4494
- const footer = stage === "choose" ? /* @__PURE__ */ jsxs18(Fragment11, { children: [
4594
+ const footer = stage === "choose" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4495
4595
  /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: onClose, children: "Close" }),
4496
4596
  source === "env" ? /* @__PURE__ */ jsx18(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx18(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
4497
- ] }) : stage === "review" ? /* @__PURE__ */ jsxs18(Fragment11, { children: [
4597
+ ] }) : stage === "review" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4498
4598
  running ? /* @__PURE__ */ jsx18(Button15, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ jsx18(Button15, { variant: "quiet", onClick: reset, children: "Back" }),
4499
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}` })
4500
- ] }) : /* @__PURE__ */ jsxs18(Fragment11, { children: [
4600
+ ] }) : /* @__PURE__ */ jsxs18(Fragment12, { children: [
4501
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,
4502
4602
  /* @__PURE__ */ jsx18(Button15, { variant: "primary", onClick: onClose, children: "Done" })
4503
4603
  ] });
@@ -4527,13 +4627,13 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4527
4627
  ] }),
4528
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
4529
4629
  ] }) : null,
4530
- stage === "review" && draft ? /* @__PURE__ */ jsxs18(Fragment11, { children: [
4630
+ stage === "review" && draft ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
4531
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,
4532
4632
  /* @__PURE__ */ jsx18(
4533
4633
  VaultSection,
4534
4634
  {
4535
4635
  title: draft.kind === "env" ? plural(envKeys.length, "value") : summary(counts, lines.length),
4536
- actions: /* @__PURE__ */ jsxs18(Fragment11, { children: [
4636
+ actions: /* @__PURE__ */ jsxs18(Fragment12, { children: [
4537
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" }),
4538
4638
  /* @__PURE__ */ jsx18(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
4539
4639
  ] }),
@@ -4627,7 +4727,7 @@ import { jsx as jsx19 } from "react/jsx-runtime";
4627
4727
  function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
4628
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";
4629
4729
  const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
4630
- 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();
4631
4731
  if (initial && /[A-Z0-9]/.test(initial)) {
4632
4732
  return /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn8("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
4633
4733
  }
@@ -4650,13 +4750,13 @@ import { cn as cn9 } from "@ai-matrx/design-system";
4650
4750
  import { confirm as confirm6 } from "@ai-matrx/kit/confirm-opener";
4651
4751
  import { downloadFile as downloadFile3 } from "@ai-matrx/kit/download";
4652
4752
  import { DownloadIcon } from "@ai-matrx/design-system/icons";
4653
- 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";
4654
4754
  var messageOf2 = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
4655
4755
  var TOO_BIG = "Files over 25 MB can't be stored.";
4656
4756
  var PROTECTION = [
4657
4757
  { value: "visible", label: "Open", title: "Anyone who can use the item can download it" },
4658
4758
  { value: "revealable", label: "Protected", title: "Downloading needs a recent sign-in" },
4659
- { 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" }
4660
4760
  ];
4661
4761
  function FilesSection({ item, port, editing, onChanged }) {
4662
4762
  const confirmed = useConfirmed();
@@ -4723,7 +4823,7 @@ function FilesSection({ item, port, editing, onChanged }) {
4723
4823
  {
4724
4824
  label: a.label,
4725
4825
  hint: a.description || void 0,
4726
- 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,
4727
4827
  children: /* @__PURE__ */ jsx20(FileName, { file: a })
4728
4828
  },
4729
4829
  a.id
@@ -4747,13 +4847,13 @@ function EditableFile({ port, item: it, file: a, busy: b, run: go, onDownload, o
4747
4847
  const yes = await confirm6({ title: `Delete ${a.label}?`, description: "The encrypted file is removed from this item.", confirmLabel: "Delete", variant: "destructive" });
4748
4848
  if (yes) await go(`delete:${a.id}`, () => port.activity.deleteFile(it, a.id), "The file could not be deleted.");
4749
4849
  }
4750
- return /* @__PURE__ */ jsxs19(Fragment12, { children: [
4850
+ return /* @__PURE__ */ jsxs19(Fragment13, { children: [
4751
4851
  /* @__PURE__ */ jsx20(
4752
4852
  VaultRow,
4753
4853
  {
4754
4854
  label: a.label,
4755
4855
  hint: a.description || void 0,
4756
- actions: /* @__PURE__ */ jsxs19(Fragment12, { children: [
4856
+ actions: /* @__PURE__ */ jsxs19(Fragment13, { children: [
4757
4857
  canDownload(it, a) ? /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(DownloadIcon, {}), "aria-label": `Download ${a.fileName}`, disabled: b !== null, onClick: onDownload }) : null,
4758
4858
  /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(PencilGlyph, {}), "aria-label": `Edit ${a.label}`, pressed: open, disabled: b !== null, onClick: () => setOpen((o) => !o) }),
4759
4859
  /* @__PURE__ */ jsx20(Button16, { variant: "quiet", icon: /* @__PURE__ */ jsx20(UploadGlyph, {}), "aria-label": `Replace ${a.fileName}`, disabled: b !== null, onClick: () => replacer.current?.click() }),
@@ -4807,7 +4907,7 @@ function NewFile({ file, busy, onAdd, onCancel }) {
4807
4907
  if (handling === "sealed" && !await confirmSeal(label)) return;
4808
4908
  await onAdd({ label: label.trim(), description: description.trim() || null, handling });
4809
4909
  }
4810
- return /* @__PURE__ */ jsxs19(Fragment12, { children: [
4910
+ return /* @__PURE__ */ jsxs19(Fragment13, { children: [
4811
4911
  /* @__PURE__ */ jsx20(VaultRow, { label: "New file", children: /* @__PURE__ */ jsxs19("span", { className: "flex min-w-0 items-center gap-2", children: [
4812
4912
  /* @__PURE__ */ jsx20("span", { className: "truncate", children: file.name }),
4813
4913
  /* @__PURE__ */ jsx20("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatFileSize2(file.size) })
@@ -4831,7 +4931,7 @@ function FileDetailsRows({ file, busy, onSave, onCancel }) {
4831
4931
  if (handling === "sealed" && file.handling !== "sealed" && !await confirmSeal(label)) return;
4832
4932
  await onSave({ label: label.trim(), description: description.trim() || null, fileName: fileName.trim(), handling });
4833
4933
  }
4834
- return /* @__PURE__ */ jsxs19(Fragment12, { children: [
4934
+ return /* @__PURE__ */ jsxs19(Fragment13, { children: [
4835
4935
  /* @__PURE__ */ jsx20(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx20(Field7, { "aria-label": "Name", value: label, onChange: (e) => setLabel(e.target.value) }) }),
4836
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) }) }),
4837
4937
  /* @__PURE__ */ jsx20(VaultRow, { label: "File name", children: /* @__PURE__ */ jsx20(Field7, { "aria-label": "File name", value: fileName, onChange: (e) => setFileName(e.target.value) }) }),
@@ -4855,7 +4955,7 @@ function confirmSeal(label) {
4855
4955
  import { useState as useState21 } from "react";
4856
4956
  import { Badge as Badge6, Button as Button17, Textarea as Textarea3 } from "@ai-matrx/design-system/controls";
4857
4957
  import { CopyIcon as CopyIcon6 } from "@ai-matrx/design-system/icons";
4858
- 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";
4859
4959
  var MASK4 = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
4860
4960
  var say2 = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
4861
4961
  function RecoveryCodesRow({ item, field, port, onChanged }) {
@@ -4899,7 +4999,7 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4899
4999
  {
4900
5000
  label: "Recovery codes",
4901
5001
  top: true,
4902
- actions: /* @__PURE__ */ jsxs20(Fragment13, { children: [
5002
+ actions: /* @__PURE__ */ jsxs20(Fragment14, { children: [
4903
5003
  /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: /* @__PURE__ */ jsx21(CheckGlyph, {}), "aria-label": "Save codes", disabled: busy || !draft.trim(), onClick: () => void saveDraft() }),
4904
5004
  /* @__PURE__ */ jsx21(Button17, { variant: "quiet", removes: true, icon: /* @__PURE__ */ jsx21(XGlyph, {}), "aria-label": "Cancel", disabled: busy, onClick: () => {
4905
5005
  setDraft(null);
@@ -4941,11 +5041,11 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4941
5041
  {
4942
5042
  label: "Recovery codes",
4943
5043
  top: codes !== null,
4944
- actions: /* @__PURE__ */ jsxs20(Fragment13, { children: [
5044
+ actions: /* @__PURE__ */ jsxs20(Fragment14, { children: [
4945
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,
4946
5046
  canEdit && !sealed ? /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: /* @__PURE__ */ jsx21(PencilGlyph, {}), "aria-label": "Replace all codes", disabled: busy, onClick: () => setDraft("") }) : null
4947
5047
  ] }),
4948
- 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: [
4949
5049
  codes.map((code, i) => /* @__PURE__ */ jsxs20("li", { className: "flex min-h-7 items-center gap-1", children: [
4950
5050
  /* @__PURE__ */ jsx21("code", { className: "min-w-0 flex-1 break-all font-mono text-sm text-foreground", children: code }),
4951
5051
  /* @__PURE__ */ jsx21(Button17, { variant: "quiet", icon: /* @__PURE__ */ jsx21(CopyIcon6, {}), "aria-label": `Copy code ${i + 1}`, onClick: () => void copyValue2(code, "Recovery code") }),
@@ -5268,10 +5368,7 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5268
5368
  title: "Move or copy",
5269
5369
  size: "md",
5270
5370
  onClose,
5271
- footer: /* @__PURE__ */ jsxs22("div", { className: "flex w-full items-center gap-2", children: [
5272
- /* @__PURE__ */ jsx24(StatusLine, { error, notice }),
5273
- /* @__PURE__ */ jsx24(Button19, { variant: "outline", className: "ml-auto", onClick: onClose, children: "Close" })
5274
- ] }),
5371
+ footer: /* @__PURE__ */ jsx24(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx24(StatusLine, { error, notice }) : null, children: /* @__PURE__ */ jsx24(Button19, { variant: "primary", onClick: onClose, children: "Done" }) }),
5275
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: [
5276
5373
  canMove && moves.length > 0 ? /* @__PURE__ */ jsx24(
5277
5374
  VaultRow,
@@ -5303,7 +5400,7 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5303
5400
  }
5304
5401
 
5305
5402
  // src/sharing/SharePanel.tsx
5306
- 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";
5307
5404
  function useGrants(port, item) {
5308
5405
  const [grants, setGrants] = useState25(null);
5309
5406
  const [loadError, setLoadError] = useState25(null);
@@ -5386,10 +5483,7 @@ function SharePanel({ item, port, organizations, onClose, onChanged }) {
5386
5483
  title: "Share",
5387
5484
  size: "md",
5388
5485
  onClose,
5389
- footer: /* @__PURE__ */ jsxs23("div", { className: "flex w-full items-center gap-2", children: [
5390
- /* @__PURE__ */ jsx25(StatusLine, { error, notice }),
5391
- /* @__PURE__ */ jsx25(Button20, { variant: "primary", className: "ml-auto", onClick: onClose, children: "Done" })
5392
- ] }),
5486
+ footer: /* @__PURE__ */ jsx25(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx25(StatusLine, { error, notice }) : null, children: /* @__PURE__ */ jsx25(Button20, { variant: "primary", onClick: onClose, children: "Done" }) }),
5393
5487
  children: [
5394
5488
  /* @__PURE__ */ jsxs23(VaultSection, { title: "Who has access", children: [
5395
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" }) }),
@@ -5454,7 +5548,7 @@ function IntoOrganization({ item, port, organizations, grants, onShared }) {
5454
5548
  const [level, setLevel] = useState25("use");
5455
5549
  const chosen = open.find((o) => o.id === target);
5456
5550
  if (item.owner.kind !== "me") return null;
5457
- 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: [
5458
5552
  /* @__PURE__ */ jsxs23("div", { className: "flex min-h-11 items-center gap-2 px-3 sm:px-4", children: [
5459
5553
  /* @__PURE__ */ jsx25(BuildingGlyph, { className: "size-4 shrink-0 text-muted-foreground" }),
5460
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 }))] }),
@@ -5540,7 +5634,7 @@ function SharingSummary({ item, port, className }) {
5540
5634
 
5541
5635
  // src/ItemPane.tsx
5542
5636
  import { formatAbsoluteDate as formatAbsoluteDate3 } from "@ai-matrx/kit/format";
5543
- 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";
5544
5638
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
5545
5639
  function ItemPane(props) {
5546
5640
  const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
@@ -5675,6 +5769,21 @@ function ItemPane(props) {
5675
5769
  if (!wasCancelled(e)) setError(e instanceof Error ? e.message : "The value could not be read.");
5676
5770
  }
5677
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;
5678
5787
  const declaredKeys = new Set(declared.map((f) => f.key));
5679
5788
  const extraFields = (item?.fields ?? []).filter((f) => !declaredKeys.has(f.key) && !draft.removed.includes(f.id) && !isAuthenticatorStorage(f.key));
5680
5789
  const metaValue = (key) => draft.values[key] ?? item?.custom.find((c) => c.key === key)?.value ?? "";
@@ -5683,6 +5792,10 @@ function ItemPane(props) {
5683
5792
  const isLogin = (item?.definitionKey ?? definition?.key) === "website_login" || (item?.definitionKey ?? definition?.key) === "username_password";
5684
5793
  const typeLabel = definition?.label ?? item?.definitionLabel ?? "";
5685
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
+ };
5686
5799
  const tagsVisible = showTags || draft.tags.length > 0 || tagsText !== "";
5687
5800
  const descriptionVisible = showDescription || draft.description !== "";
5688
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: [
@@ -5750,7 +5863,7 @@ function ItemPane(props) {
5750
5863
  /* @__PURE__ */ jsx26(SettingsIcon, { className: "size-4" }),
5751
5864
  "Settings"
5752
5865
  ] }) : null,
5753
- canManage ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
5866
+ canManage ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5754
5867
  /* @__PURE__ */ jsx26(DropdownMenuSeparator3, {}),
5755
5868
  /* @__PURE__ */ jsxs24(DropdownMenuItem3, { className: "text-destructive", disabled: busy, onSelect: () => void remove(), children: [
5756
5869
  /* @__PURE__ */ jsx26(TrashGlyph, { className: "size-4" }),
@@ -5766,7 +5879,11 @@ function ItemPane(props) {
5766
5879
  header,
5767
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,
5768
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,
5769
- /* @__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: [
5770
5887
  /* @__PURE__ */ jsxs24(VaultSection, { children: [
5771
5888
  mode === "new" ? /* @__PURE__ */ jsx26(VaultRow, { label: "Save to", children: /* @__PURE__ */ jsx26(
5772
5889
  OptionCombobox,
@@ -5778,16 +5895,16 @@ function ItemPane(props) {
5778
5895
  searchPlaceholder: "Find an organization",
5779
5896
  groups: [
5780
5897
  { heading: "Just you", options: ["me"] },
5781
- ...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) }] : [],
5782
5899
  { heading: "Send", options: ["person"] }
5783
5900
  ],
5784
5901
  getLabel: (v) => v === "me" ? "Personal" : v === "person" ? "Someone else" : organizations.find((o) => o.id === v)?.name ?? v,
5785
- 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,
5786
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" })
5787
5904
  }
5788
5905
  ) }) : null,
5789
5906
  /* @__PURE__ */ jsx26(EditRow, { label: "Name", value: draft.name, onChange: (v) => patch({ name: v }), placeholder: "Name", autoFocus: mode === "new" }),
5790
- mode === "new" && owner === "person" ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
5907
+ mode === "new" && owner === "person" ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5791
5908
  /* @__PURE__ */ jsx26(EditRow, { label: "Their email", value: recipient, placeholder: "name@company.com", inputMode: "email", onChange: setRecipient }),
5792
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
5793
5910
  ] }) : null,
@@ -5806,7 +5923,7 @@ function ItemPane(props) {
5806
5923
  multiline: f.kind === "multiline",
5807
5924
  inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
5808
5925
  onChange: (v) => setValue(f.key, v),
5809
- actions: /* @__PURE__ */ jsxs24(Fragment15, { children: [
5926
+ actions: /* @__PURE__ */ jsxs24(Fragment16, { children: [
5810
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,
5811
5928
  secretKind ? /* @__PURE__ */ jsx26(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
5812
5929
  existing ? /* @__PURE__ */ jsx26(Button21, { variant: "quiet", icon: /* @__PURE__ */ jsx26(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null
@@ -5825,7 +5942,7 @@ function ItemPane(props) {
5825
5942
  onChange: (v) => setValue(f.key, v),
5826
5943
  onRemove: () => patch({ removed: [...draft.removed, f.id] }),
5827
5944
  removeLabel: `Delete ${f.label}`,
5828
- actions: /* @__PURE__ */ jsxs24(Fragment15, { children: [
5945
+ actions: /* @__PURE__ */ jsxs24(Fragment16, { children: [
5829
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,
5830
5947
  /* @__PURE__ */ jsx26(Button21, { variant: "quiet", icon: /* @__PURE__ */ jsx26(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) })
5831
5948
  ] })
@@ -5857,7 +5974,7 @@ function ItemPane(props) {
5857
5974
  i
5858
5975
  )),
5859
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" }),
5860
- /* @__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 }),
5861
5978
  item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
5862
5979
  ] }),
5863
5980
  /* @__PURE__ */ jsx26(VaultSection, { children: /* @__PURE__ */ jsx26(EditRow, { label: "Notes", value: draft.notes, multiline: true, placeholder: "Anything worth remembering", onChange: (v) => patch({ notes: v }) }) }),
@@ -5890,12 +6007,12 @@ function ItemPane(props) {
5890
6007
  ) : null,
5891
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}`)),
5892
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)),
5893
- !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
5894
6011
  ]
5895
6012
  }
5896
6013
  ),
5897
6014
  filesProps ? /* @__PURE__ */ jsx26(FilesSection, { ...filesProps, editing: true }) : null
5898
- ] }) : item ? /* @__PURE__ */ jsxs24(Fragment15, { children: [
6015
+ ] }) : item ? /* @__PURE__ */ jsxs24(Fragment16, { children: [
5899
6016
  /* @__PURE__ */ jsxs24(VaultSection, { children: [
5900
6017
  declared.map((f) => {
5901
6018
  if (f.storage === "metadata") {
@@ -5904,11 +6021,15 @@ function ItemPane(props) {
5904
6021
  }
5905
6022
  const field = item.fields.find((x) => x.key === f.key);
5906
6023
  if (isRecoveryCodesField(f)) return /* @__PURE__ */ jsx26(RecoveryCodesRow, { item, field, port, onChanged }, f.key);
5907
- 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;
5908
6025
  }),
5909
- 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)),
5910
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" }),
5911
- 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" }),
5912
6033
  !item.authenticator && item.custom.some((c) => c.key === MFA_METHOD_KEY && c.value) ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
5913
6034
  ] }),
5914
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" }) }),
@@ -5940,7 +6061,7 @@ function subtitleOf(item, withOwner) {
5940
6061
  const what = loginSiteOf(item) || item.definitionLabel;
5941
6062
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
5942
6063
  }
5943
- 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 }) {
5944
6065
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
5945
6066
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
5946
6067
  const ordered = [...pinned, ...rest];
@@ -5959,7 +6080,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
5959
6080
  listRef.current?.querySelector(`[data-item="${next.id}"]`)?.scrollIntoView({ block: "nearest" });
5960
6081
  }
5961
6082
  };
5962
- 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;
5963
6084
  const row = (i) => /* @__PURE__ */ jsx27(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners }, i.id);
5964
6085
  const sortLabel = SORTS2.find((s) => s.value === sort)?.label ?? "Name";
5965
6086
  return /* @__PURE__ */ jsxs25("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
@@ -6112,7 +6233,7 @@ function refusalText(reason) {
6112
6233
  // src/lists/RestorePanel.tsx
6113
6234
  import { useEffect as useEffect18, useState as useState28 } from "react";
6114
6235
  import { Button as Button23 } from "@ai-matrx/design-system/controls";
6115
- 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";
6116
6237
  var count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
6117
6238
  function RestorePanel({ lists, item, restore, onClose }) {
6118
6239
  const [preview, setPreview] = useState28(null);
@@ -6157,7 +6278,7 @@ function RestorePanel({ lists, item, restore, onClose }) {
6157
6278
  title: `Restore "${item.name}"`,
6158
6279
  size: "md",
6159
6280
  onClose,
6160
- footer: /* @__PURE__ */ jsxs26(Fragment16, { children: [
6281
+ footer: /* @__PURE__ */ jsxs26(Fragment17, { children: [
6161
6282
  /* @__PURE__ */ jsx28(Button23, { variant: "quiet", onClick: onClose, children: refused ? "Close" : "Cancel" }),
6162
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" })
6163
6284
  ] }),
@@ -6177,6 +6298,9 @@ function RestorePanel({ lists, item, restore, onClose }) {
6177
6298
 
6178
6299
  // src/lists/TrashView.tsx
6179
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
+ }
6180
6304
  function TrashView({ port, deleted, query, header, onChanged }) {
6181
6305
  const confirmed = useConfirmed();
6182
6306
  const [busyId, setBusyId] = useState29(null);
@@ -6188,7 +6312,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6188
6312
  setMessage(null);
6189
6313
  try {
6190
6314
  const r = await confirmed(() => port.lists.restore(item, deletionId));
6191
- 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.` });
6192
6316
  onChanged();
6193
6317
  return r;
6194
6318
  } catch (e) {
@@ -6206,7 +6330,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6206
6330
  /* @__PURE__ */ jsxs27("div", { className: "flex min-h-12 items-center gap-3 px-3 py-1.5 sm:px-4", children: [
6207
6331
  /* @__PURE__ */ jsxs27("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
6208
6332
  /* @__PURE__ */ jsx29("span", { className: "truncate text-sm text-foreground", children: item.name }),
6209
- /* @__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) })
6210
6334
  ] }),
6211
6335
  /* @__PURE__ */ jsx29(Button24, { variant: "outline", icon: /* @__PURE__ */ jsx29(RestoreGlyph, {}), disabled: busyId !== null || !item.deletion, onClick: () => setRestoring(item), children: "Restore" })
6212
6336
  ] }, item.id)
@@ -6497,10 +6621,9 @@ function SecureLinkPanel({ item, port, onClose }) {
6497
6621
  title: "Send securely",
6498
6622
  size: "md",
6499
6623
  onClose,
6500
- footer: /* @__PURE__ */ jsxs29("div", { className: "flex w-full flex-wrap items-center gap-2", children: [
6501
- /* @__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: [
6502
6625
  sameChannelSent && !error ? /* @__PURE__ */ jsx31(Badge8, { tone: "warning", children: "Both by email" }) : null,
6503
- /* @__PURE__ */ jsx31(Button26, { variant: "quiet", className: "ml-auto", onClick: onClose, children: "Close" }),
6626
+ /* @__PURE__ */ jsx31(Button26, { variant: "quiet", onClick: onClose, children: "Close" }),
6504
6627
  /* @__PURE__ */ jsx31(Button26, { variant: "primary", disabled: !canSend, onClick: () => void send(), children: busy === "send" ? "Sending\u2026" : "Send link" })
6505
6628
  ] }),
6506
6629
  children: [
@@ -6653,7 +6776,7 @@ function addPasskeyFailure(message2) {
6653
6776
  var isRevoked = (d) => d.revokedAt !== null;
6654
6777
 
6655
6778
  // src/devices/DevicesPanel.tsx
6656
- 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";
6657
6780
  var messageOf3 = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
6658
6781
  function ErrorLine2({ text }) {
6659
6782
  return text ? /* @__PURE__ */ jsx32("p", { role: "alert", className: "px-1 text-sm text-destructive", children: text }) : null;
@@ -6784,7 +6907,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6784
6907
  };
6785
6908
  const canApprove = Boolean(key && passkeys && here && methods?.passkey && !approvedUntil);
6786
6909
  const title = isMac ? "Approve this Mac" : name ? `Approve ${name}` : "Approve this browser";
6787
- 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: [
6788
6911
  /* @__PURE__ */ jsx32(Button27, { variant: "quiet", disabled: busy !== null, onClick: refuse, children: key ? "Refuse" : "Close" }),
6789
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
6790
6913
  ] });
@@ -6792,7 +6915,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6792
6915
  "This link names no key. Start again from ",
6793
6916
  where,
6794
6917
  "."
6795
- ] }) }) : /* @__PURE__ */ jsxs30(Fragment17, { children: [
6918
+ ] }) }) : /* @__PURE__ */ jsxs30(Fragment18, { children: [
6796
6919
  /* @__PURE__ */ jsxs30(VaultSection, { title: "What is asking", children: [
6797
6920
  /* @__PURE__ */ jsx32(VaultRow, { label: "Asking", hint: "Approve only if you just asked", children: asker }),
6798
6921
  name ? /* @__PURE__ */ jsx32(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx32("span", { className: "block truncate", children: name }) }) : null,
@@ -7092,7 +7215,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7092
7215
  }
7093
7216
 
7094
7217
  // src/native/NativeImportPanel.tsx
7095
- 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";
7096
7219
  var TONE = {
7097
7220
  eligible: "success",
7098
7221
  committed: "success",
@@ -7136,16 +7259,16 @@ function Importer({ bridge, readLimits, activeOrganizationId, organizations, onC
7136
7259
  const yes = await confirm12({ title: `Add ${plural2(ready, "passkey")} to ${orgName}?`, description: "Passkeys already there are skipped.", confirmLabel: "Import" });
7137
7260
  if (yes) await flow.confirm();
7138
7261
  };
7139
- const footer = preview ? /* @__PURE__ */ jsxs31(Fragment18, { children: [
7262
+ const footer = preview ? /* @__PURE__ */ jsxs31(Fragment19, { children: [
7140
7263
  /* @__PURE__ */ jsx33(Button28, { variant: "quiet", disabled: busy, onClick: () => void flow.cancel(), children: "Cancel" }),
7141
7264
  /* @__PURE__ */ jsxs31(Button28, { variant: "primary", icon: /* @__PURE__ */ jsx33(ImportGlyph, { className: "size-4" }), disabled: busy || ready === 0, onClick: () => void importNow(), children: [
7142
7265
  "Import ",
7143
7266
  ready
7144
7267
  ] })
7145
- ] }) : status ? active ? /* @__PURE__ */ jsxs31(Fragment18, { children: [
7268
+ ] }) : status ? active ? /* @__PURE__ */ jsxs31(Fragment19, { children: [
7146
7269
  /* @__PURE__ */ jsx33(Button28, { variant: "outline", disabled: busy, onClick: () => void flow.refresh(), children: "Refresh" }),
7147
7270
  /* @__PURE__ */ jsx33(Button28, { variant: "outline", disabled: busy, onClick: () => void flow.cancel(), children: "Cancel import" })
7148
- ] }) : /* @__PURE__ */ jsxs31(Fragment18, { children: [
7271
+ ] }) : /* @__PURE__ */ jsxs31(Fragment19, { children: [
7149
7272
  /* @__PURE__ */ jsx33(Button28, { variant: "quiet", onClick: flow.reset, children: "New file" }),
7150
7273
  /* @__PURE__ */ jsx33(Button28, { variant: "primary", onClick: onClose, children: "Done" })
7151
7274
  ] }) : /* @__PURE__ */ jsx33(Button28, { variant: "quiet", onClick: onClose, children: "Close" });
@@ -7184,7 +7307,7 @@ import { GlobeIcon as GlobeIcon4 } from "@ai-matrx/design-system/icons";
7184
7307
  // src/extension/CaptureCard.tsx
7185
7308
  import { useState as useState35, useSyncExternalStore as useSyncExternalStore3 } from "react";
7186
7309
  import { Button as Button29, SearchField as SearchField3 } from "@ai-matrx/design-system/controls";
7187
- 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";
7188
7311
  var SEARCH_FROM = 4;
7189
7312
  function CaptureCard({ capture, onChanged }) {
7190
7313
  const prompt = useSyncExternalStore3(capture.subscribe, capture.get, capture.get);
@@ -7217,7 +7340,7 @@ function CaptureCard({ capture, onChanged }) {
7217
7340
  ] }),
7218
7341
  /* @__PURE__ */ jsx34(Button29, { variant: "quiet", icon: /* @__PURE__ */ jsx34(XGlyph, {}), "aria-label": "Not now", disabled: busy, onClick: () => void decide({ action: "dismiss" }) })
7219
7342
  ] }),
7220
- 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: [
7221
7344
  existing.length > 0 ? /* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1", children: [
7222
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,
7223
7346
  /* @__PURE__ */ jsxs32("ul", { className: "flex max-h-36 flex-col gap-px overflow-y-auto", children: [
@@ -7323,7 +7446,7 @@ import { useState as useState37 } from "react";
7323
7446
  import { Popover as Popover2, PopoverContent as PopoverContent2, PopoverTrigger as PopoverTrigger2 } from "@ai-matrx/design-system";
7324
7447
  import { Button as Button31 } from "@ai-matrx/design-system/controls";
7325
7448
  import { CopyIcon as CopyIcon7 } from "@ai-matrx/design-system/icons";
7326
- 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";
7327
7450
  var HOLD_MS = 6e4;
7328
7451
  function PageGenerator({ fields }) {
7329
7452
  const limits = useGeneratorLimitsSource();
@@ -7374,12 +7497,12 @@ function PageGenerator({ fields }) {
7374
7497
  }
7375
7498
  };
7376
7499
  const value = held.value;
7377
- return /* @__PURE__ */ jsxs34(Fragment20, { children: [
7500
+ return /* @__PURE__ */ jsxs34(Fragment21, { children: [
7378
7501
  /* @__PURE__ */ jsx36(
7379
7502
  VaultRow,
7380
7503
  {
7381
7504
  label: "New password",
7382
- actions: /* @__PURE__ */ jsxs34(Fragment20, { children: [
7505
+ actions: /* @__PURE__ */ jsxs34(Fragment21, { children: [
7383
7506
  value ? /* @__PURE__ */ jsx36(Button31, { variant: "quiet", icon: /* @__PURE__ */ jsx36(CopyIcon7, {}), "aria-label": "Copy password", onClick: () => void copy(value, "Password") }) : null,
7384
7507
  /* @__PURE__ */ jsxs34(Popover2, { open, onOpenChange: setOpen, children: [
7385
7508
  /* @__PURE__ */ jsx36(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsx36(Button31, { variant: "outline", icon: /* @__PURE__ */ jsx36(KeyGlyph, {}), disabled: busy, children: "Generate" }) }),
@@ -7756,8 +7879,33 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7756
7879
  },
7757
7880
  [loc, move]
7758
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
+ }, []);
7759
7906
  const knobsRef = useRef19(host.knobs);
7760
7907
  knobsRef.current = host.knobs;
7908
+ const [signedOut, setSignedOut] = useState40(false);
7761
7909
  const load = useCallback5(() => {
7762
7910
  setError(null);
7763
7911
  port.load().then((s) => {
@@ -7768,7 +7916,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7768
7916
  (rows) => setDeleted([...s.deleted, ...rows.filter((r) => !s.deleted.some((d) => d.id === r.id))]),
7769
7917
  () => void 0
7770
7918
  );
7771
- }).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
+ });
7772
7923
  port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
7773
7924
  knobsRef.current?.().catch(() => void 0);
7774
7925
  port.lists.recents?.list().then(setRecents, () => void 0);
@@ -7782,6 +7933,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7782
7933
  const orgs = snap?.organizations ?? [];
7783
7934
  const definitions = snap?.definitions ?? [];
7784
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;
7785
7937
  const views = useMemo6(() => buildViews(inThisVault, definitions, favorites, recents), [inThisVault, definitions, favorites, recents]);
7786
7938
  const visible = useMemo6(() => {
7787
7939
  const filtered = inThisVault.filter((i) => {
@@ -7822,8 +7974,11 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7822
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: [
7823
7975
  /* @__PURE__ */ jsx39(SafeGlyph, { className: "size-8 text-muted-foreground" }),
7824
7976
  /* @__PURE__ */ jsx39("div", { className: "font-medium", children: "The vault couldn\u2019t open" }),
7825
- /* @__PURE__ */ jsx39("div", { className: "max-w-xs text-muted-foreground", children: plainLoadError(error) }),
7826
- /* @__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
+ ] })
7827
7982
  ] });
7828
7983
  }
7829
7984
  if (!snap) {
@@ -7903,6 +8058,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7903
8058
  {
7904
8059
  title,
7905
8060
  items: visible,
8061
+ showOwner: vaultMixesOwners,
7906
8062
  definitions,
7907
8063
  port,
7908
8064
  selectedId: loc.itemId,
@@ -8675,6 +8831,7 @@ async function readNativeImportLimits(api, organizationId) {
8675
8831
  var recoveryOf = (w) => w.lifecycle?.vault_recovery;
8676
8832
  var isDeleted = (w) => recoveryOf(w)?.state === "deleted";
8677
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" };
8678
8835
  var handlingOf = (v) => v === "visible" || v === "sealed" ? v : "revealable";
8679
8836
  function fieldOf(w, def) {
8680
8837
  const declared = def?.fields.find((f) => f.key === w.field_key);
@@ -8682,7 +8839,7 @@ function fieldOf(w, def) {
8682
8839
  return {
8683
8840
  id: w.id,
8684
8841
  key: w.field_key,
8685
- label: declared?.label ?? humanize(w.field_key),
8842
+ label: declared?.label ?? FIELD_WORDS[w.field_key] ?? humanize(w.field_key),
8686
8843
  kind: declared?.kind ?? (handling === "visible" ? "text" : "secret"),
8687
8844
  handling,
8688
8845
  editable: w.editable ?? true,
@@ -8791,7 +8948,7 @@ function createApiVaultPort(api, options = {}) {
8791
8948
  lists.forEach((l, i) => {
8792
8949
  const sharedList = i === lists.length - 1;
8793
8950
  for (const w of l.items) {
8794
- if (seen.has(w.id) || deleted.has(w.id)) continue;
8951
+ if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) continue;
8795
8952
  const owner = sharedList ? orgOf(w.organization_id, null, false) : orgOf(w.organization_id, w.user_id, i === 0);
8796
8953
  const item = itemOf(w, sharedList && !w.organization_id ? { kind: "shared", label: "Shared with me", organizationId: null } : owner, defs, auth);
8797
8954
  (isDeleted(w) ? deleted : seen).set(w.id, item);
@@ -8900,8 +9057,11 @@ function createVaultKnobs(client, address) {
8900
9057
  }
8901
9058
  export {
8902
9059
  CATEGORIES,
9060
+ CONFIRM_RETURN_KEY,
8903
9061
  DEFAULT_LOCATION,
8904
9062
  GenerateButton,
9063
+ HIDDEN_ITEM_TAG,
9064
+ IdentityGate,
8905
9065
  LOCKED_MESSAGE,
8906
9066
  VaultApp,
8907
9067
  VaultLockedError,
@@ -8912,12 +9072,16 @@ export {
8912
9072
  createVaultKnobs,
8913
9073
  guardPort,
8914
9074
  inferFromUrl,
9075
+ isHiddenItem,
8915
9076
  locationFromParams,
8916
9077
  locationToParams,
8917
9078
  loginSiteOf,
8918
9079
  matchesQuery,
8919
9080
  needsRecentSignIn,
8920
9081
  parseDefinitions,
8921
- sortItems
9082
+ siteDomainOf,
9083
+ sortItems,
9084
+ useConfirmed,
9085
+ wasCancelled
8922
9086
  };
8923
9087
  //# sourceMappingURL=index.js.map