@ai-matrx/vault 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -138,24 +138,28 @@ __export(src_exports, {
138
138
  CATEGORIES: () => CATEGORIES,
139
139
  DEFAULT_LOCATION: () => DEFAULT_LOCATION,
140
140
  GenerateButton: () => GenerateButton,
141
+ LOCKED_MESSAGE: () => LOCKED_MESSAGE,
141
142
  VaultApp: () => VaultApp,
143
+ VaultLockedError: () => VaultLockedError,
142
144
  categoryOf: () => categoryOf,
145
+ copyValue: () => copyValue,
143
146
  createApiVaultPort: () => createApiVaultPort,
144
147
  createSupabaseIdentity: () => createSupabaseIdentity,
145
148
  createVaultKnobs: () => createVaultKnobs,
149
+ guardPort: () => guardPort,
146
150
  inferFromUrl: () => inferFromUrl,
147
151
  locationFromParams: () => locationFromParams,
148
152
  locationToParams: () => locationToParams,
153
+ loginSiteOf: () => loginSiteOf,
149
154
  matchesQuery: () => matchesQuery,
150
155
  needsRecentSignIn: () => needsRecentSignIn,
151
156
  parseDefinitions: () => parseDefinitions,
152
- siteOf: () => siteOf,
153
157
  sortItems: () => sortItems
154
158
  });
155
159
  module.exports = __toCommonJS(src_exports);
156
160
 
157
161
  // src/VaultApp.tsx
158
- var import_react40 = require("react");
162
+ var import_react41 = require("react");
159
163
  var import_design_system25 = require("@ai-matrx/design-system");
160
164
  var import_controls34 = require("@ai-matrx/design-system/controls");
161
165
  var import_confirm_opener13 = require("@ai-matrx/kit/confirm-opener");
@@ -169,9 +173,28 @@ var Ctx = (0, import_react.createContext)(() => void 0);
169
173
  function useNotify() {
170
174
  return (0, import_react.useContext)(Ctx);
171
175
  }
176
+ async function copyValue(text, label, options, deps) {
177
+ const secret = options.secret ?? true;
178
+ if (deps.clipboard) {
179
+ try {
180
+ await deps.clipboard.copy(text, { secret });
181
+ deps.notify(`${label} copied`, "success");
182
+ return true;
183
+ } catch (e) {
184
+ deps.notify(e instanceof Error && e.message ? e.message : `${label} could not be copied`, "error");
185
+ return false;
186
+ }
187
+ }
188
+ return (0, import_clipboard.copyText)(text, { successMessage: `${label} copied`, failureMessage: `${label} could not be copied`, notify: (m, k) => deps.notify(m, k === "error" ? "error" : "success") });
189
+ }
190
+ var ClipboardCtx = (0, import_react.createContext)(void 0);
191
+ function ClipboardProvider({ clipboard, children }) {
192
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClipboardCtx.Provider, { value: clipboard, children });
193
+ }
172
194
  function useCopy() {
173
195
  const notify = useNotify();
174
- return (0, import_react.useCallback)((text, label) => (0, import_clipboard.copyText)(text, { successMessage: `${label} copied`, failureMessage: `${label} could not be copied`, notify: (m, k) => notify(m, k === "error" ? "error" : "success") }), [notify]);
196
+ const clipboard = (0, import_react.useContext)(ClipboardCtx);
197
+ return (0, import_react.useCallback)((text, label, options = {}) => copyValue(text, label, options, { clipboard, notify }), [notify, clipboard]);
175
198
  }
176
199
  function NoticeProvider({ notify, children }) {
177
200
  const [shown, setShown] = (0, import_react.useState)(null);
@@ -549,7 +572,15 @@ var import_design_system5 = require("@ai-matrx/design-system");
549
572
 
550
573
  // src/hooks.ts
551
574
  var import_react3 = require("react");
552
- function useTransientSecret(ms = 2e4) {
575
+ var DEFAULT_REVEAL_MS = 2e4;
576
+ var RevealMs = (0, import_react3.createContext)(DEFAULT_REVEAL_MS);
577
+ function RevealSecondsProvider({ seconds, children }) {
578
+ const ms = seconds !== void 0 && Number.isFinite(seconds) && seconds > 0 ? seconds * 1e3 : DEFAULT_REVEAL_MS;
579
+ return (0, import_react3.createElement)(RevealMs.Provider, { value: ms }, children);
580
+ }
581
+ function useTransientSecret(given) {
582
+ const fromHost = (0, import_react3.useContext)(RevealMs);
583
+ const ms = given ?? fromHost;
553
584
  const [value, setValue] = (0, import_react3.useState)(null);
554
585
  const timer2 = (0, import_react3.useRef)(null);
555
586
  const clear = (0, import_react3.useCallback)(() => {
@@ -647,12 +678,7 @@ function formatBytes(n) {
647
678
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(n < 10 * 1024 ? 1 : 0)} KB`;
648
679
  return `${(n / 1024 / 1024).toFixed(1)} MB`;
649
680
  }
650
- function formatDate(iso, withTime = false) {
651
- if (!iso) return "";
652
- const d = new Date(iso);
653
- if (Number.isNaN(d.getTime())) return iso;
654
- return d.toLocaleString(void 0, withTime ? { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" } : { year: "numeric", month: "short", day: "numeric" });
655
- }
681
+ var DAY_FORMAT = { year: "numeric", month: "short", day: "numeric" };
656
682
 
657
683
  // src/FieldRows.tsx
658
684
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -664,7 +690,7 @@ function SecretText({ value, multiline }) {
664
690
  var quietError = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
665
691
  function SecretRow({ port, item, field, extra }) {
666
692
  const confirmed = useConfirmed();
667
- const copyValue = useCopy();
693
+ const copyValue2 = useCopy();
668
694
  const secret = useTransientSecret();
669
695
  const [plain, setPlain] = (0, import_react5.useState)(null);
670
696
  const [busy, setBusy] = (0, import_react5.useState)(false);
@@ -705,7 +731,7 @@ function SecretRow({ port, item, field, extra }) {
705
731
  }
706
732
  async function copy() {
707
733
  const v = await fetchValue();
708
- if (v !== null) await copyValue(v, field.label);
734
+ if (v !== null) await copyValue2(v, field.label, { secret: !visible });
709
735
  }
710
736
  const multiline = field.kind === "multiline";
711
737
  const shown = visible ? plain : secret.value;
@@ -724,7 +750,7 @@ function SecretRow({ port, item, field, extra }) {
724
750
  );
725
751
  }
726
752
  function PlainRow({ label, value, hint, openUrl, extra, multiline = false }) {
727
- const copyValue = useCopy();
753
+ const copyValue2 = useCopy();
728
754
  const isUrl = /^https?:\/\//i.test(value);
729
755
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
730
756
  Row,
@@ -735,7 +761,7 @@ function PlainRow({ label, value, hint, openUrl, extra, multiline = false }) {
735
761
  actions: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
736
762
  extra,
737
763
  isUrl && openUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExternalGlyph, {}), "aria-label": `Open ${value}`, onClick: () => openUrl(value) }) : null,
738
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CopyGlyph, {}), "aria-label": `Copy ${typeof label === "string" ? label : "value"}`, onClick: () => void copyValue(value, typeof label === "string" && label ? label : "Value") })
764
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CopyGlyph, {}), "aria-label": `Copy ${typeof label === "string" ? label : "value"}`, onClick: () => void copyValue2(value, typeof label === "string" && label ? label : "Value", { secret: false }) })
739
765
  ] }),
740
766
  children: isUrl && openUrl ? (
741
767
  // A real link (right-click, copy link address, open in a new tab); a click goes through the host's opener.
@@ -792,6 +818,9 @@ function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove }) {
792
818
  );
793
819
  }
794
820
 
821
+ // src/activity/HistoryPanel.tsx
822
+ var import_format3 = require("@ai-matrx/kit/format");
823
+
795
824
  // src/activity/logic.ts
796
825
  var import_format = require("@ai-matrx/kit/format");
797
826
 
@@ -843,7 +872,7 @@ function matchesQuery(item, query) {
843
872
  return [item.name, item.definitionLabel, ...item.tags, ...item.loginUrls, ...item.custom.map((c) => `${c.label} ${c.value}`)].some((h) => h.toLowerCase().includes(q));
844
873
  }
845
874
  var isAuthenticatorStorage = (key) => key === "totp_seed" || key.startsWith("totp_") || key === "authenticator_last_time_step";
846
- function siteOf(item) {
875
+ function loginSiteOf(item) {
847
876
  const raw = item.loginUrls[0];
848
877
  if (!raw) return "";
849
878
  try {
@@ -857,7 +886,7 @@ function sortItems(items, sort) {
857
886
  const byName = (a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
858
887
  if (sort === "name") return out.sort(byName);
859
888
  if (sort === "website") return out.sort((a, b) => {
860
- const x = siteOf(a), y = siteOf(b);
889
+ const x = loginSiteOf(a), y = loginSiteOf(b);
861
890
  return x && y ? x.localeCompare(y) || byName(a, b) : x ? -1 : y ? 1 : byName(a, b);
862
891
  });
863
892
  if (sort === "updated") return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
@@ -1249,6 +1278,7 @@ var HistoryChangedError = class extends Error {
1249
1278
  var import_react6 = require("react");
1250
1279
  var import_controls3 = require("@ai-matrx/design-system/controls");
1251
1280
  var import_design_system6 = require("@ai-matrx/design-system");
1281
+ var import_format2 = require("@ai-matrx/kit/format");
1252
1282
  var import_jsx_runtime6 = require("react/jsx-runtime");
1253
1283
  var PAGE = 50;
1254
1284
  var MAX = 500;
@@ -1285,7 +1315,7 @@ function ActivityPanel({ item, port, onClose }) {
1285
1315
  day.entries.map((entry) => ({ entry, s: auditSentence(entry, item, me) })),
1286
1316
  (r) => `${r.s.what}|${r.s.who ?? ""}`
1287
1317
  ).map(({ row: { entry, s }, times }) => {
1288
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex min-h-9 items-center gap-1.5 px-3 text-sm sm:px-4", title: formatDate(entry.at, true), children: [
1318
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex min-h-9 items-center gap-1.5 px-3 text-sm sm:px-4", title: (0, import_format2.formatAbsoluteDate)(entry.at, void 0, ""), children: [
1289
1319
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: (0, import_design_system6.cn)("truncate", s.refused ? "text-destructive" : "text-foreground"), children: s.what }),
1290
1320
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "shrink-0 text-muted-foreground", children: [
1291
1321
  times > 1 ? ` \xB7 ${times} times` : "",
@@ -1370,7 +1400,7 @@ function HistoryPanel({ item, port, arg, onClose, onChanged }) {
1370
1400
  page && (page.captureStartedAt || page.incomplete) ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 px-1 text-xs", children: [
1371
1401
  page.captureStartedAt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-muted-foreground", children: [
1372
1402
  "Kept since ",
1373
- formatDate(page.captureStartedAt, true)
1403
+ (0, import_format3.formatAbsoluteDate)(page.captureStartedAt, void 0, "")
1374
1404
  ] }) : null,
1375
1405
  page.incomplete ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-warning", children: "Some changes could not be read" }) : null
1376
1406
  ] }) : null
@@ -1381,9 +1411,9 @@ function HistoryPanel({ item, port, arg, onClose, onChanged }) {
1381
1411
  function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, onError }) {
1382
1412
  const confirmed = useConfirmed();
1383
1413
  const secret = useTransientSecret();
1384
- const copyValue = useCopy();
1414
+ const copyValue2 = useCopy();
1385
1415
  const [busy, setBusy] = (0, import_react7.useState)(false);
1386
- const when = formatDate(entry.recordedAt, true);
1416
+ const when = (0, import_format3.formatAbsoluteDate)(entry.recordedAt, void 0, "");
1387
1417
  async function read() {
1388
1418
  if (secret.value !== null) return secret.value;
1389
1419
  setBusy(true);
@@ -1404,7 +1434,7 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
1404
1434
  }
1405
1435
  async function copy() {
1406
1436
  const v = await read();
1407
- if (v !== null) await copyValue(v, `Earlier ${label.toLowerCase()}`);
1437
+ if (v !== null) await copyValue2(v, `Earlier ${label.toLowerCase()}`);
1408
1438
  }
1409
1439
  async function restore() {
1410
1440
  const yes = await (0, import_confirm_opener.confirm)({
@@ -1545,11 +1575,11 @@ function authenticatorName(current, parsed) {
1545
1575
  if (name && !PLACEHOLDER_NAMES.includes(name.toLowerCase())) return name;
1546
1576
  return parsed.issuer || parsed.account || name || "One-time password";
1547
1577
  }
1548
- function matchIssuerToSite(issuer, candidates, siteOf2) {
1578
+ function matchIssuerToSite(issuer, candidates, siteOf) {
1549
1579
  const key = (issuer ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
1550
1580
  if (key.length < 2) return null;
1551
1581
  for (const c of candidates) {
1552
- const host = siteOf2(c).toLowerCase();
1582
+ const host = siteOf(c).toLowerCase();
1553
1583
  if (!host) continue;
1554
1584
  const labels = host.split(".");
1555
1585
  if (labels.some((l) => l.replace(/[^a-z0-9]/g, "") === key)) return c;
@@ -1589,13 +1619,13 @@ function CountdownRing({ remaining, period, loading = false, className }) {
1589
1619
  }
1590
1620
  function useCopied() {
1591
1621
  const [copied, setCopied] = (0, import_react8.useState)(false);
1592
- const copyValue = useCopy();
1622
+ const copyValue2 = useCopy();
1593
1623
  const t = (0, import_react8.useRef)(null);
1594
1624
  (0, import_react8.useEffect)(() => () => void (t.current && clearTimeout(t.current)), []);
1595
1625
  return {
1596
1626
  copied,
1597
1627
  copy: async (code) => {
1598
- const ok = await copyValue(code, "One-time code");
1628
+ const ok = await copyValue2(code, "One-time code");
1599
1629
  if (!ok) return;
1600
1630
  setCopied(true);
1601
1631
  if (t.current) clearTimeout(t.current);
@@ -2170,7 +2200,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2170
2200
  const recognise = (p) => {
2171
2201
  if (!nameTouched) setName((n) => authenticatorName(n, p));
2172
2202
  if (!targetChosen) {
2173
- const match = matchIssuerToSite(p.issuer, candidates, siteOf);
2203
+ const match = matchIssuerToSite(p.issuer, candidates, loginSiteOf);
2174
2204
  setTarget(match ? match.id : NEW);
2175
2205
  }
2176
2206
  };
@@ -2248,7 +2278,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2248
2278
  value: target,
2249
2279
  disabled: busy || created !== null,
2250
2280
  "aria-label": "Add to",
2251
- options: [{ value: NEW, label: "New login" }, ...candidates.map((c) => ({ value: c.id, label: siteOf(c) ? `${c.name} \xB7 ${siteOf(c)}` : c.name }))],
2281
+ options: [{ value: NEW, label: "New login" }, ...candidates.map((c) => ({ value: c.id, label: loginSiteOf(c) ? `${c.name} \xB7 ${loginSiteOf(c)}` : c.name }))],
2252
2282
  onValueChange: (v) => {
2253
2283
  setTarget(v);
2254
2284
  setTargetChosen(true);
@@ -2410,7 +2440,7 @@ function OtpListRow({ port, item, family, entry, onOpen, onRename, onChanged, on
2410
2440
  const Icon = definitionGlyph(item.definitionKey, family);
2411
2441
  const canManage = item.capabilities.canManage || item.capabilities.canEdit;
2412
2442
  if (a.removed) return null;
2413
- const sub = a.error ?? [entry?.account, siteOf(item) || entry?.issuer].filter((v, i, all) => Boolean(v) && all.indexOf(v) === i).join(" \xB7 ");
2443
+ const sub = a.error ?? [entry?.account, loginSiteOf(item) || entry?.issuer].filter((v, i, all) => Boolean(v) && all.indexOf(v) === i).join(" \xB7 ");
2414
2444
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid min-h-14 grid-cols-[2rem_minmax(0,1fr)_auto_auto] items-center gap-x-3 rounded-lg px-2 py-1.5 transition-colors hover:bg-accent/50", children: [
2415
2445
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", onClick: onOpen, "aria-label": `Open ${item.name}`, tabIndex: -1, className: (0, import_design_system9.cn)("flex size-8 items-center justify-center rounded-lg bg-muted", a.enabled ? "text-foreground" : "text-disabled-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { className: "size-4" }) }),
2416
2446
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", onClick: onOpen, className: "flex min-w-0 flex-col items-start rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [
@@ -2810,7 +2840,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2810
2840
  const [candidate, setCandidate] = (0, import_react15.useState)(null);
2811
2841
  const [revealed, setRevealed] = (0, import_react15.useState)(false);
2812
2842
  const [note, setNote] = (0, import_react15.useState)(null);
2813
- const copyValue = useCopy();
2843
+ const copyValue2 = useCopy();
2814
2844
  const expiry = (0, import_react15.useRef)(null);
2815
2845
  const drop = () => {
2816
2846
  if (expiry.current) clearTimeout(expiry.current);
@@ -2870,7 +2900,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
2870
2900
  const strength = candidate ? strengthOf(candidate.bits) : null;
2871
2901
  const copy = async () => {
2872
2902
  if (!candidate) return;
2873
- const ok = await copyValue(candidate.value, "Generated password");
2903
+ const ok = await copyValue2(candidate.value, "Generated password");
2874
2904
  setNote(ok ? { tone: "success", text: "Copied. Your clipboard keeps it." } : { tone: "destructive", text: "Couldn\u2019t copy. Reveal it and copy by hand." });
2875
2905
  };
2876
2906
  const use = () => {
@@ -2981,8 +3011,47 @@ function importLimitsFrom(resolved) {
2981
3011
  return out;
2982
3012
  }
2983
3013
 
3014
+ // src/lock.ts
3015
+ var import_react16 = require("react");
3016
+ var LOCKED_MESSAGE = "The vault is locked. Unlock it first.";
3017
+ var VaultLockedError = class extends Error {
3018
+ code = "vault_locked";
3019
+ constructor() {
3020
+ super(LOCKED_MESSAGE);
3021
+ this.name = "VaultLockedError";
3022
+ }
3023
+ };
3024
+ function guardPort(port, locked) {
3025
+ const cache = /* @__PURE__ */ new WeakMap();
3026
+ const wrap = (target) => {
3027
+ const hit = cache.get(target);
3028
+ if (hit) return hit;
3029
+ const proxy = new Proxy(target, {
3030
+ get(obj2, key, receiver) {
3031
+ const value = Reflect.get(obj2, key, receiver);
3032
+ if (typeof value === "function") {
3033
+ return (...args) => {
3034
+ if (locked()) return Promise.reject(new VaultLockedError());
3035
+ return value.apply(obj2, args);
3036
+ };
3037
+ }
3038
+ if (value && typeof value === "object") return wrap(value);
3039
+ return value;
3040
+ }
3041
+ });
3042
+ cache.set(target, proxy);
3043
+ return proxy;
3044
+ };
3045
+ return wrap(port);
3046
+ }
3047
+ var NEVER = () => () => void 0;
3048
+ var UNLOCKED = () => false;
3049
+ function useLocked(lock) {
3050
+ return (0, import_react16.useSyncExternalStore)(lock ? lock.subscribe : NEVER, lock ? () => lock.locked() : UNLOCKED, UNLOCKED);
3051
+ }
3052
+
2984
3053
  // src/io/BackupPanel.tsx
2985
- var import_react17 = require("react");
3054
+ var import_react18 = require("react");
2986
3055
  var import_controls13 = require("@ai-matrx/design-system/controls");
2987
3056
  var import_download = require("@ai-matrx/kit/download");
2988
3057
  var import_confirm_opener4 = require("@ai-matrx/kit/confirm-opener");
@@ -3075,7 +3144,7 @@ function startNewRestoreRun(preview) {
3075
3144
  }
3076
3145
 
3077
3146
  // src/io/lines.tsx
3078
- var import_react16 = require("react");
3147
+ var import_react17 = require("react");
3079
3148
  var import_design_system11 = require("@ai-matrx/design-system");
3080
3149
  var import_controls12 = require("@ai-matrx/design-system/controls");
3081
3150
  var import_jsx_runtime15 = require("react/jsx-runtime");
@@ -3088,7 +3157,7 @@ function PickLine({ checked, onChange, disabled = false, title, detail, badge, t
3088
3157
  ] });
3089
3158
  }
3090
3159
  function Windowed({ items, size = 150, render }) {
3091
- const [all, setAll] = (0, import_react16.useState)(false);
3160
+ const [all, setAll] = (0, import_react17.useState)(false);
3092
3161
  const shown = all ? items : items.slice(0, size);
3093
3162
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
3094
3163
  shown.map(render),
@@ -3099,7 +3168,7 @@ function Windowed({ items, size = 150, render }) {
3099
3168
  ] });
3100
3169
  }
3101
3170
  function More({ title = "More", children }) {
3102
- const [open, setOpen] = (0, import_react16.useState)(false);
3171
+ const [open, setOpen] = (0, import_react17.useState)(false);
3103
3172
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-2", children: [
3104
3173
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_controls12.DisclosureHeader, { open, title, onClick: () => setOpen((o) => !o) }),
3105
3174
  open ? children : null
@@ -3138,21 +3207,21 @@ function Omissions({ omissions, accepted, onAccepted, verb }) {
3138
3207
  }
3139
3208
  function BackupPanel({ port, items, onClose, onChanged }) {
3140
3209
  const confirmed = useConfirmed();
3141
- const fileInput = (0, import_react17.useRef)(null);
3142
- const abort = (0, import_react17.useRef)(null);
3143
- const [mode, setMode] = (0, import_react17.useState)("backup");
3144
- const [busy, setBusy] = (0, import_react17.useState)(false);
3145
- const [error, setError] = (0, import_react17.useState)(null);
3146
- const [passphrase, setPassphrase] = (0, import_react17.useState)("");
3147
- const [repeat, setRepeat] = (0, import_react17.useState)("");
3148
- const [accepted, setAccepted] = (0, import_react17.useState)(false);
3149
- const [selected, setSelected] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
3150
- const [backup, setBackup] = (0, import_react17.useState)(null);
3151
- const [file, setFile] = (0, import_react17.useState)(null);
3152
- const [restorePreview, setRestorePreview] = (0, import_react17.useState)(null);
3153
- const [runId, setRunId] = (0, import_react17.useState)(null);
3154
- const [result, setResult] = (0, import_react17.useState)(null);
3155
- (0, import_react17.useEffect)(() => () => abort.current?.abort(), []);
3210
+ const fileInput = (0, import_react18.useRef)(null);
3211
+ const abort = (0, import_react18.useRef)(null);
3212
+ const [mode, setMode] = (0, import_react18.useState)("backup");
3213
+ const [busy, setBusy] = (0, import_react18.useState)(false);
3214
+ const [error, setError] = (0, import_react18.useState)(null);
3215
+ const [passphrase, setPassphrase] = (0, import_react18.useState)("");
3216
+ const [repeat, setRepeat] = (0, import_react18.useState)("");
3217
+ const [accepted, setAccepted] = (0, import_react18.useState)(false);
3218
+ const [selected, setSelected] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
3219
+ const [backup, setBackup] = (0, import_react18.useState)(null);
3220
+ const [file, setFile] = (0, import_react18.useState)(null);
3221
+ const [restorePreview, setRestorePreview] = (0, import_react18.useState)(null);
3222
+ const [runId, setRunId] = (0, import_react18.useState)(null);
3223
+ const [result, setResult] = (0, import_react18.useState)(null);
3224
+ (0, import_react18.useEffect)(() => () => abort.current?.abort(), []);
3156
3225
  const switchMode = (next) => {
3157
3226
  abort.current?.abort();
3158
3227
  setMode(next);
@@ -3323,7 +3392,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3323
3392
  }
3324
3393
 
3325
3394
  // src/io/ExportPanel.tsx
3326
- var import_react18 = require("react");
3395
+ var import_react19 = require("react");
3327
3396
  var import_controls14 = require("@ai-matrx/design-system/controls");
3328
3397
  var import_download2 = require("@ai-matrx/kit/download");
3329
3398
  var import_confirm_opener5 = require("@ai-matrx/kit/confirm-opener");
@@ -3347,12 +3416,12 @@ function omissionText2(omissions) {
3347
3416
  }
3348
3417
  function ExportPanel({ port, items, onClose }) {
3349
3418
  const confirmed = useConfirmed();
3350
- const logins = (0, import_react18.useMemo)(() => items.filter((i) => i.definitionKey === WEBSITE_LOGIN), [items]);
3351
- const [profile, setProfile] = (0, import_react18.useState)("matrx_login_csv_v1");
3352
- const [selected, setSelected] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
3353
- const [preview, setPreview] = (0, import_react18.useState)(null);
3354
- const [busy, setBusy] = (0, import_react18.useState)(false);
3355
- const [error, setError] = (0, import_react18.useState)(null);
3419
+ const logins = (0, import_react19.useMemo)(() => items.filter((i) => i.definitionKey === WEBSITE_LOGIN), [items]);
3420
+ const [profile, setProfile] = (0, import_react19.useState)("matrx_login_csv_v1");
3421
+ const [selected, setSelected] = (0, import_react19.useState)(/* @__PURE__ */ new Set());
3422
+ const [preview, setPreview] = (0, import_react19.useState)(null);
3423
+ const [busy, setBusy] = (0, import_react19.useState)(false);
3424
+ const [error, setError] = (0, import_react19.useState)(null);
3356
3425
  const format = FORMATS.find((f) => f.value === profile);
3357
3426
  const reasons = new Map(preview?.items.map((p) => [p.item_id, p]) ?? []);
3358
3427
  const eligible = preview?.items.filter((p) => p.eligible).length ?? 0;
@@ -3477,7 +3546,7 @@ function ExportPanel({ port, items, onClose }) {
3477
3546
  }
3478
3547
 
3479
3548
  // src/io/ImportPanel.tsx
3480
- var import_react19 = require("react");
3549
+ var import_react20 = require("react");
3481
3550
  var import_controls15 = require("@ai-matrx/design-system/controls");
3482
3551
 
3483
3552
  // src/io/csv-import.ts
@@ -4336,24 +4405,24 @@ var STATE_BADGE = {
4336
4405
  var ME = "me";
4337
4406
  function ImportPanel({ port, host, items, organizations, onClose, onChanged, limits }) {
4338
4407
  const confirmed = useConfirmed();
4339
- const fileInput = (0, import_react19.useRef)(null);
4340
- const abort = (0, import_react19.useRef)(null);
4341
- const stop = (0, import_react19.useRef)(false);
4342
- const keys = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
4343
- const [source, setSource] = (0, import_react19.useState)("auto");
4344
- const [envText, setEnvText] = (0, import_react19.useState)("");
4345
- const [injectSandbox, setInjectSandbox] = (0, import_react19.useState)(false);
4346
- const [fileName, setFileName] = (0, import_react19.useState)(null);
4347
- const [reading, setReading] = (0, import_react19.useState)(false);
4348
- const [draft, setDraft] = (0, import_react19.useState)(null);
4349
- const [destination, setDestination] = (0, import_react19.useState)(ME);
4350
- const [browserFill, setBrowserFill] = (0, import_react19.useState)(false);
4351
- const [approvedVisible, setApprovedVisible] = (0, import_react19.useState)(false);
4352
- const [selected, setSelected] = (0, import_react19.useState)(/* @__PURE__ */ new Set());
4353
- const [running, setRunning] = (0, import_react19.useState)(null);
4354
- const [done, setDone] = (0, import_react19.useState)(null);
4355
- const [error, setError] = (0, import_react19.useState)(null);
4356
- (0, import_react19.useEffect)(
4408
+ const fileInput = (0, import_react20.useRef)(null);
4409
+ const abort = (0, import_react20.useRef)(null);
4410
+ const stop = (0, import_react20.useRef)(false);
4411
+ const keys = (0, import_react20.useRef)(/* @__PURE__ */ new Map());
4412
+ const [source, setSource] = (0, import_react20.useState)("auto");
4413
+ const [envText, setEnvText] = (0, import_react20.useState)("");
4414
+ const [injectSandbox, setInjectSandbox] = (0, import_react20.useState)(false);
4415
+ const [fileName, setFileName] = (0, import_react20.useState)(null);
4416
+ const [reading, setReading] = (0, import_react20.useState)(false);
4417
+ const [draft, setDraft] = (0, import_react20.useState)(null);
4418
+ const [destination, setDestination] = (0, import_react20.useState)(ME);
4419
+ const [browserFill, setBrowserFill] = (0, import_react20.useState)(false);
4420
+ const [approvedVisible, setApprovedVisible] = (0, import_react20.useState)(false);
4421
+ const [selected, setSelected] = (0, import_react20.useState)(/* @__PURE__ */ new Set());
4422
+ const [running, setRunning] = (0, import_react20.useState)(null);
4423
+ const [done, setDone] = (0, import_react20.useState)(null);
4424
+ const [error, setError] = (0, import_react20.useState)(null);
4425
+ (0, import_react20.useEffect)(
4357
4426
  () => () => {
4358
4427
  abort.current?.abort();
4359
4428
  stop.current = true;
@@ -4362,9 +4431,9 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4362
4431
  );
4363
4432
  const organizationId = destination === ME ? null : destination;
4364
4433
  const existingIn = (org) => items.filter((i) => org === null ? i.owner.kind === "me" : i.owner.organizationId === org).map((i) => ({ displayName: i.name, loginUrls: i.loginUrls }));
4365
- const existing = (0, import_react19.useMemo)(() => existingIn(organizationId), [items, organizationId]);
4366
- const destinationOptions = (0, import_react19.useMemo)(() => [{ value: ME, label: "Personal" }, ...organizations.map((o) => ({ value: o.id, label: o.name }))], [organizations]);
4367
- const lines = (0, import_react19.useMemo)(
4434
+ const existing = (0, import_react20.useMemo)(() => existingIn(organizationId), [items, organizationId]);
4435
+ const destinationOptions = (0, import_react20.useMemo)(() => [{ value: ME, label: "Personal" }, ...organizations.map((o) => ({ value: o.id, label: o.name }))], [organizations]);
4436
+ const lines = (0, import_react20.useMemo)(
4368
4437
  () => draft?.kind === "file" ? reviewLines(draft.parsed, { organizationId, browserFill, existing, limits, mapping: draft.mapping }) : [],
4369
4438
  [draft, organizationId, browserFill, existing, limits]
4370
4439
  );
@@ -4616,7 +4685,7 @@ function Notices({ draft }) {
4616
4685
  }
4617
4686
 
4618
4687
  // src/ItemList.tsx
4619
- var import_react27 = require("react");
4688
+ var import_react28 = require("react");
4620
4689
  var import_design_system18 = require("@ai-matrx/design-system");
4621
4690
  var import_design_system19 = require("@ai-matrx/design-system");
4622
4691
  var import_controls22 = require("@ai-matrx/design-system/controls");
@@ -4636,14 +4705,14 @@ function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }
4636
4705
  }
4637
4706
 
4638
4707
  // src/ItemPane.tsx
4639
- var import_react26 = require("react");
4708
+ var import_react27 = require("react");
4640
4709
  var import_design_system16 = require("@ai-matrx/design-system");
4641
4710
  var import_design_system17 = require("@ai-matrx/design-system");
4642
4711
  var import_controls21 = require("@ai-matrx/design-system/controls");
4643
4712
  var import_confirm_opener9 = require("@ai-matrx/kit/confirm-opener");
4644
4713
 
4645
4714
  // src/activity/FilesSection.tsx
4646
- var import_react20 = require("react");
4715
+ var import_react21 = require("react");
4647
4716
  var import_controls16 = require("@ai-matrx/design-system/controls");
4648
4717
  var import_design_system13 = require("@ai-matrx/design-system");
4649
4718
  var import_confirm_opener6 = require("@ai-matrx/kit/confirm-opener");
@@ -4659,11 +4728,11 @@ var PROTECTION = [
4659
4728
  function FilesSection({ item, port, editing, onChanged }) {
4660
4729
  const confirmed = useConfirmed();
4661
4730
  const canEdit = editing && item.capabilities.canEdit;
4662
- const [busy, setBusy] = (0, import_react20.useState)(null);
4663
- const [error, setError] = (0, import_react20.useState)(null);
4664
- const [pending, setPending] = (0, import_react20.useState)(null);
4665
- const [dragging, setDragging] = (0, import_react20.useState)(false);
4666
- const picker = (0, import_react20.useRef)(null);
4731
+ const [busy, setBusy] = (0, import_react21.useState)(null);
4732
+ const [error, setError] = (0, import_react21.useState)(null);
4733
+ const [pending, setPending] = (0, import_react21.useState)(null);
4734
+ const [dragging, setDragging] = (0, import_react21.useState)(false);
4735
+ const picker = (0, import_react21.useRef)(null);
4667
4736
  if (!canEdit && item.attachments.length === 0) return null;
4668
4737
  async function run(key, work, fallback) {
4669
4738
  setBusy(key);
@@ -4739,8 +4808,8 @@ function FilesSection({ item, port, editing, onChanged }) {
4739
4808
  );
4740
4809
  }
4741
4810
  function EditableFile({ port, item: it, file: a, busy: b, run: go, onDownload, onTooBig }) {
4742
- const [open, setOpen] = (0, import_react20.useState)(false);
4743
- const replacer = (0, import_react20.useRef)(null);
4811
+ const [open, setOpen] = (0, import_react21.useState)(false);
4812
+ const replacer = (0, import_react21.useRef)(null);
4744
4813
  async function remove() {
4745
4814
  const yes = await (0, import_confirm_opener6.confirm)({ title: `Delete ${a.label}?`, description: "The encrypted file is removed from this item.", confirmLabel: "Delete", variant: "destructive" });
4746
4815
  if (yes) await go(`delete:${a.id}`, () => port.activity.deleteFile(it, a.id), "The file could not be deleted.");
@@ -4798,9 +4867,9 @@ function Protection({ value, locked, onChange }) {
4798
4867
  );
4799
4868
  }
4800
4869
  function NewFile({ file, busy, onAdd, onCancel }) {
4801
- const [label, setLabel] = (0, import_react20.useState)(file.name);
4802
- const [description, setDescription] = (0, import_react20.useState)("");
4803
- const [handling, setHandling] = (0, import_react20.useState)("revealable");
4870
+ const [label, setLabel] = (0, import_react21.useState)(file.name);
4871
+ const [description, setDescription] = (0, import_react21.useState)("");
4872
+ const [handling, setHandling] = (0, import_react21.useState)("revealable");
4804
4873
  async function add() {
4805
4874
  if (handling === "sealed" && !await confirmSeal(label)) return;
4806
4875
  await onAdd({ label: label.trim(), description: description.trim() || null, handling });
@@ -4820,10 +4889,10 @@ function NewFile({ file, busy, onAdd, onCancel }) {
4820
4889
  ] });
4821
4890
  }
4822
4891
  function FileDetailsRows({ file, busy, onSave, onCancel }) {
4823
- const [label, setLabel] = (0, import_react20.useState)(file.label);
4824
- const [description, setDescription] = (0, import_react20.useState)(file.description);
4825
- const [fileName, setFileName] = (0, import_react20.useState)(file.fileName);
4826
- const [handling, setHandling] = (0, import_react20.useState)(file.handling);
4892
+ const [label, setLabel] = (0, import_react21.useState)(file.label);
4893
+ const [description, setDescription] = (0, import_react21.useState)(file.description);
4894
+ const [fileName, setFileName] = (0, import_react21.useState)(file.fileName);
4895
+ const [handling, setHandling] = (0, import_react21.useState)(file.handling);
4827
4896
  const changed = label !== file.label || description !== file.description || fileName !== file.fileName || handling !== file.handling;
4828
4897
  async function save() {
4829
4898
  if (handling === "sealed" && file.handling !== "sealed" && !await confirmSeal(label)) return;
@@ -4850,18 +4919,18 @@ function confirmSeal(label) {
4850
4919
  }
4851
4920
 
4852
4921
  // src/fields/RecoveryCodesRow.tsx
4853
- var import_react21 = require("react");
4922
+ var import_react22 = require("react");
4854
4923
  var import_controls17 = require("@ai-matrx/design-system/controls");
4855
4924
  var import_jsx_runtime21 = require("react/jsx-runtime");
4856
4925
  var MASK4 = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
4857
4926
  var say2 = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
4858
4927
  function RecoveryCodesRow({ item, field, port, onChanged }) {
4859
4928
  const confirmed = useConfirmed();
4860
- const copyValue = useCopy();
4929
+ const copyValue2 = useCopy();
4861
4930
  const secret = useTransientSecret();
4862
- const [busy, setBusy] = (0, import_react21.useState)(false);
4863
- const [error, setError] = (0, import_react21.useState)(null);
4864
- const [draft, setDraft] = (0, import_react21.useState)(null);
4931
+ const [busy, setBusy] = (0, import_react22.useState)(false);
4932
+ const [error, setError] = (0, import_react22.useState)(null);
4933
+ const [draft, setDraft] = (0, import_react22.useState)(null);
4865
4934
  const canEdit = item.capabilities.canEdit;
4866
4935
  const run = async (step) => {
4867
4936
  setBusy(true);
@@ -4945,7 +5014,7 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4945
5014
  children: sealed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_controls17.Badge, { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LockGlyph, {}), children: "Sealed" }) : codes ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("ul", { className: "flex flex-col gap-0.5", "aria-label": "Recovery codes", children: [
4946
5015
  codes.map((code, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex min-h-7 items-center gap-1", children: [
4947
5016
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("code", { className: "min-w-0 flex-1 break-all font-mono text-sm text-foreground", children: code }),
4948
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_controls17.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CopyGlyph, {}), "aria-label": `Copy code ${i + 1}`, onClick: () => void copyValue(code, "Recovery code") }),
5017
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_controls17.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CopyGlyph, {}), "aria-label": `Copy code ${i + 1}`, onClick: () => void copyValue2(code, "Recovery code") }),
4949
5018
  canEdit ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_controls17.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CheckGlyph, {}), disabled: busy, onClick: () => void markUsed(i), children: "Mark used" }) : null
4950
5019
  ] }, `${i}-${code}`)),
4951
5020
  error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { role: "alert", className: "text-xs text-destructive", children: error }) : null
@@ -5096,7 +5165,7 @@ function targetLabel(t) {
5096
5165
  }
5097
5166
 
5098
5167
  // src/extension/UseOnSiteButton.tsx
5099
- var import_react22 = require("react");
5168
+ var import_react23 = require("react");
5100
5169
  var import_controls18 = require("@ai-matrx/design-system/controls");
5101
5170
  var import_jsx_runtime22 = require("react/jsx-runtime");
5102
5171
  function canUseOnSite(item, pageUrl) {
@@ -5104,7 +5173,7 @@ function canUseOnSite(item, pageUrl) {
5104
5173
  }
5105
5174
  function UseOnSiteButton({ port, item, pageUrl, onChanged }) {
5106
5175
  const notify = useNotify();
5107
- const [busy, setBusy] = (0, import_react22.useState)(false);
5176
+ const [busy, setBusy] = (0, import_react23.useState)(false);
5108
5177
  if (!canUseOnSite(item, pageUrl)) return null;
5109
5178
  const site = pageSite(pageUrl);
5110
5179
  const run = async () => {
@@ -5123,7 +5192,7 @@ function UseOnSiteButton({ port, item, pageUrl, onChanged }) {
5123
5192
  }
5124
5193
 
5125
5194
  // src/sharing/SharePanel.tsx
5126
- var import_react25 = require("react");
5195
+ var import_react26 = require("react");
5127
5196
  var import_confirm_opener8 = require("@ai-matrx/kit/confirm-opener");
5128
5197
  var import_controls20 = require("@ai-matrx/design-system/controls");
5129
5198
  var import_design_system15 = require("@ai-matrx/design-system");
@@ -5137,7 +5206,7 @@ var MEMBER_ACCESS_LABEL = { none: "No access", use: "Can use", editor: "Can edit
5137
5206
  function levelOf(flags) {
5138
5207
  return flags.can_manage ? "edit" : "use";
5139
5208
  }
5140
- function flagsOf(level) {
5209
+ function shareFlagsOf(level) {
5141
5210
  return { can_use: true, can_manage: level === "edit" };
5142
5211
  }
5143
5212
  function isEmail(value) {
@@ -5180,7 +5249,7 @@ function shareSummary(grants) {
5180
5249
  var MEMBER_CHOICE_DEFAULT = "default";
5181
5250
 
5182
5251
  // src/sharing/parts.tsx
5183
- var import_react23 = require("react");
5252
+ var import_react24 = require("react");
5184
5253
  var import_design_system14 = require("@ai-matrx/design-system");
5185
5254
  var import_jsx_runtime23 = require("react/jsx-runtime");
5186
5255
  var message = (e, fallback) => {
@@ -5189,9 +5258,9 @@ var message = (e, fallback) => {
5189
5258
  };
5190
5259
  function useAction() {
5191
5260
  const confirmed = useConfirmed();
5192
- const [busy, setBusy] = (0, import_react23.useState)(null);
5193
- const [error, setError] = (0, import_react23.useState)(null);
5194
- const [notice, setNotice] = (0, import_react23.useState)(null);
5261
+ const [busy, setBusy] = (0, import_react24.useState)(null);
5262
+ const [error, setError] = (0, import_react24.useState)(null);
5263
+ const [notice, setNotice] = (0, import_react24.useState)(null);
5195
5264
  async function run(key, fn, done) {
5196
5265
  setBusy(key);
5197
5266
  setError(null);
@@ -5225,7 +5294,7 @@ function StatusLine({ error, notice }) {
5225
5294
  }
5226
5295
 
5227
5296
  // src/sharing/MovePanel.tsx
5228
- var import_react24 = require("react");
5297
+ var import_react25 = require("react");
5229
5298
  var import_confirm_opener7 = require("@ai-matrx/kit/confirm-opener");
5230
5299
  var import_controls19 = require("@ai-matrx/design-system/controls");
5231
5300
  var import_jsx_runtime24 = require("react/jsx-runtime");
@@ -5234,9 +5303,9 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5234
5303
  const { busy, error, notice, run, setNotice } = useAction();
5235
5304
  const moves = moveDestinations(item, organizations);
5236
5305
  const copies = copyDestinations(organizations, item);
5237
- const [moveTo, setMoveTo] = (0, import_react24.useState)("");
5238
- const [copyTo, setCopyTo] = (0, import_react24.useState)("");
5239
- const [email, setEmail] = (0, import_react24.useState)("");
5306
+ const [moveTo, setMoveTo] = (0, import_react25.useState)("");
5307
+ const [copyTo, setCopyTo] = (0, import_react25.useState)("");
5308
+ const [email, setEmail] = (0, import_react25.useState)("");
5240
5309
  const passkey = isPasskey(item);
5241
5310
  const canMove = item.capabilities.canManage && !passkey && moves.length > 0;
5242
5311
  const canGive = item.capabilities.canManage && item.owner.kind === "me" && !passkey;
@@ -5317,11 +5386,11 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5317
5386
  // src/sharing/SharePanel.tsx
5318
5387
  var import_jsx_runtime25 = require("react/jsx-runtime");
5319
5388
  function useGrants(port, item) {
5320
- const [grants, setGrants] = (0, import_react25.useState)(null);
5321
- const [loadError, setLoadError] = (0, import_react25.useState)(null);
5322
- const [nonce, setNonce] = (0, import_react25.useState)(0);
5389
+ const [grants, setGrants] = (0, import_react26.useState)(null);
5390
+ const [loadError, setLoadError] = (0, import_react26.useState)(null);
5391
+ const [nonce, setNonce] = (0, import_react26.useState)(0);
5323
5392
  const allowed = item.capabilities.canManage;
5324
- (0, import_react25.useEffect)(() => {
5393
+ (0, import_react26.useEffect)(() => {
5325
5394
  if (!allowed) return;
5326
5395
  let live = true;
5327
5396
  setLoadError(null);
@@ -5338,9 +5407,9 @@ function useGrants(port, item) {
5338
5407
  function SharePanel({ item, port, organizations, onClose, onChanged }) {
5339
5408
  const { grants, setGrants, loadError, reload } = useGrants(port, item);
5340
5409
  const { busy, error, notice, run } = useAction();
5341
- const [email, setEmail] = (0, import_react25.useState)("");
5342
- const [level, setLevel] = (0, import_react25.useState)("use");
5343
- const [more, setMore] = (0, import_react25.useState)(false);
5410
+ const [email, setEmail] = (0, import_react26.useState)("");
5411
+ const [level, setLevel] = (0, import_react26.useState)("use");
5412
+ const [more, setMore] = (0, import_react26.useState)(false);
5344
5413
  const isOrg = item.owner.kind === "organization" && item.owner.organizationId !== null;
5345
5414
  const orgName = item.owner.label;
5346
5415
  const people = (grants ?? []).filter((g) => g.kind === "person");
@@ -5462,8 +5531,8 @@ function SharePanel({ item, port, organizations, onClose, onChanged }) {
5462
5531
  function IntoOrganization({ item, port, organizations, grants, onShared }) {
5463
5532
  const { busy, error, notice, run } = useAction();
5464
5533
  const open = organizations.filter((o) => !grants.some((g) => g.organizationId === o.id));
5465
- const [target, setTarget] = (0, import_react25.useState)("");
5466
- const [level, setLevel] = (0, import_react25.useState)("use");
5534
+ const [target, setTarget] = (0, import_react26.useState)("");
5535
+ const [level, setLevel] = (0, import_react26.useState)("use");
5467
5536
  const chosen = open.find((o) => o.id === target);
5468
5537
  if (item.owner.kind !== "me") return null;
5469
5538
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Section, { title: "Share into organization", children: open.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
@@ -5498,10 +5567,10 @@ var CHOICES = [
5498
5567
  { value: "editor", label: "Can edit", meta: "See and change" }
5499
5568
  ];
5500
5569
  function MemberAccess({ port, organizationId, organizationName }) {
5501
- const [list, setList] = (0, import_react25.useState)(null);
5502
- const [loadError, setLoadError] = (0, import_react25.useState)(null);
5570
+ const [list, setList] = (0, import_react26.useState)(null);
5571
+ const [loadError, setLoadError] = (0, import_react26.useState)(null);
5503
5572
  const { busy, error, run } = useAction();
5504
- (0, import_react25.useEffect)(() => {
5573
+ (0, import_react26.useEffect)(() => {
5505
5574
  let live = true;
5506
5575
  port.sharing.memberAccess(organizationId).then(
5507
5576
  (l) => live && setList(l),
@@ -5533,9 +5602,9 @@ function MemberAccess({ port, organizationId, organizationName }) {
5533
5602
  ] });
5534
5603
  }
5535
5604
  function SharingSummary({ item, port, className }) {
5536
- const [text, setText] = (0, import_react25.useState)("");
5605
+ const [text, setText] = (0, import_react26.useState)("");
5537
5606
  const allowed = item.capabilities.canManage;
5538
- (0, import_react25.useEffect)(() => {
5607
+ (0, import_react26.useEffect)(() => {
5539
5608
  if (!allowed) return;
5540
5609
  let live = true;
5541
5610
  port.sharing.grants(item).then(
@@ -5551,6 +5620,7 @@ function SharingSummary({ item, port, className }) {
5551
5620
  }
5552
5621
 
5553
5622
  // src/ItemPane.tsx
5623
+ var import_format4 = require("@ai-matrx/kit/format");
5554
5624
  var import_jsx_runtime26 = require("react/jsx-runtime");
5555
5625
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
5556
5626
  function ItemPane(props) {
@@ -5560,8 +5630,8 @@ function ItemPane(props) {
5560
5630
  const editing = mode !== "view";
5561
5631
  const seedName = seed?.name ?? "";
5562
5632
  const seedUrl = seed?.url ?? "";
5563
- const [loaded, setLoaded] = (0, import_react26.useState)({});
5564
- const base = (0, import_react26.useMemo)(() => {
5633
+ const [loaded, setLoaded] = (0, import_react27.useState)({});
5634
+ const base = (0, import_react27.useMemo)(() => {
5565
5635
  const b = draftOf(item);
5566
5636
  if (!item) {
5567
5637
  b.name = seedName;
@@ -5570,22 +5640,22 @@ function ItemPane(props) {
5570
5640
  b.values = { ...loaded };
5571
5641
  return b;
5572
5642
  }, [item, seedName, seedUrl, loaded]);
5573
- const [draft, setDraft] = (0, import_react26.useState)(base);
5574
- const [tagsText, setTagsText] = (0, import_react26.useState)(base.tags.join(", "));
5575
- const [showTags, setShowTags] = (0, import_react26.useState)(false);
5576
- const [showDescription, setShowDescription] = (0, import_react26.useState)(false);
5577
- const [owner, setOwner] = (0, import_react26.useState)(defaultOwner ?? "me");
5578
- const [recipient, setRecipient] = (0, import_react26.useState)("");
5579
- const [generatePrivately, setGeneratePrivately] = (0, import_react26.useState)(false);
5580
- const [busy, setBusy] = (0, import_react26.useState)(false);
5581
- const [error, setError] = (0, import_react26.useState)(null);
5582
- const [destinations, setDestinations] = (0, import_react26.useState)({});
5643
+ const [draft, setDraft] = (0, import_react27.useState)(base);
5644
+ const [tagsText, setTagsText] = (0, import_react27.useState)(base.tags.join(", "));
5645
+ const [showTags, setShowTags] = (0, import_react27.useState)(false);
5646
+ const [showDescription, setShowDescription] = (0, import_react27.useState)(false);
5647
+ const [owner, setOwner] = (0, import_react27.useState)(defaultOwner ?? "me");
5648
+ const [recipient, setRecipient] = (0, import_react27.useState)("");
5649
+ const [generatePrivately, setGeneratePrivately] = (0, import_react27.useState)(false);
5650
+ const [busy, setBusy] = (0, import_react27.useState)(false);
5651
+ const [error, setError] = (0, import_react27.useState)(null);
5652
+ const [destinations, setDestinations] = (0, import_react27.useState)({});
5583
5653
  const dirty = editing && isDirty(base, draft);
5584
5654
  const declared = definition?.fields ?? [];
5585
- const prev = (0, import_react26.useRef)(null);
5586
- const paneRef = (0, import_react26.useRef)(null);
5655
+ const prev = (0, import_react27.useRef)(null);
5656
+ const paneRef = (0, import_react27.useRef)(null);
5587
5657
  const room = useRoomOf(paneRef);
5588
- (0, import_react26.useEffect)(() => {
5658
+ (0, import_react27.useEffect)(() => {
5589
5659
  const was = prev.current;
5590
5660
  const id = item?.id ?? null;
5591
5661
  if (was && was.id === id && was.mode === mode && editing) {
@@ -5598,18 +5668,18 @@ function ItemPane(props) {
5598
5668
  }
5599
5669
  prev.current = { base, id, mode };
5600
5670
  }, [base, mode, item?.id, editing]);
5601
- (0, import_react26.useEffect)(() => {
5671
+ (0, import_react27.useEffect)(() => {
5602
5672
  setLoaded({});
5603
5673
  setShowTags(false);
5604
5674
  setShowDescription(false);
5605
5675
  }, [item?.id, mode]);
5606
- (0, import_react26.useEffect)(() => onDirty(dirty), [dirty, onDirty]);
5676
+ (0, import_react27.useEffect)(() => onDirty(dirty), [dirty, onDirty]);
5607
5677
  const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
5608
- (0, import_react26.useEffect)(() => {
5678
+ (0, import_react27.useEffect)(() => {
5609
5679
  if (editing && dirty) keepDraft(keepKey, item?.updatedAt ?? "new", draft);
5610
5680
  else if (!editing) dropKeptDraft(keepKey);
5611
5681
  }, [editing, dirty, draft, keepKey, item?.updatedAt]);
5612
- (0, import_react26.useEffect)(() => {
5682
+ (0, import_react27.useEffect)(() => {
5613
5683
  if (!editing || !item) return;
5614
5684
  let live = true;
5615
5685
  for (const f of item.fields) {
@@ -5698,7 +5768,7 @@ function ItemPane(props) {
5698
5768
  const descriptionVisible = showDescription || draft.description !== "";
5699
5769
  const header = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("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: [
5700
5770
  onBack ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_controls21.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ChevronLeftGlyph, {}), "aria-label": "Back", onClick: mode === "new" ? onCancelNew : onBack }) : null,
5701
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemIcon, { definitionKey: definition?.key ?? item?.definitionKey ?? "custom", family: definition?.family ?? "generic", site: item ? siteOf(item) : siteOf({ loginUrls: draft.loginUrls }), size: "lg" }),
5771
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemIcon, { definitionKey: definition?.key ?? item?.definitionKey ?? "custom", family: definition?.family ?? "generic", site: item ? loginSiteOf(item) : loginSiteOf({ loginUrls: draft.loginUrls }), size: "lg" }),
5702
5772
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col", children: [
5703
5773
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h2", { className: "truncate text-base font-semibold leading-tight", children: editing ? draft.name || (mode === "new" ? `New ${typeLabel.toLowerCase()}` : item?.name) : item?.name }),
5704
5774
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "flex min-w-0 items-center gap-1 text-xs text-muted-foreground", children: [
@@ -5931,9 +6001,9 @@ function ItemPane(props) {
5931
6001
  filesProps ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(FilesSection, { ...filesProps, editing: false }) : null,
5932
6002
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: (0, import_design_system17.cn)("px-1 text-xs text-muted-foreground"), children: [
5933
6003
  "Modified ",
5934
- formatDate(item.updatedAt),
6004
+ (0, import_format4.formatAbsoluteDate)(item.updatedAt, DAY_FORMAT, ""),
5935
6005
  " \xB7 Created ",
5936
- formatDate(item.createdAt)
6006
+ (0, import_format4.formatAbsoluteDate)(item.createdAt, DAY_FORMAT, "")
5937
6007
  ] })
5938
6008
  ] }) : null }) })
5939
6009
  ] }) });
@@ -5948,15 +6018,15 @@ var SORTS2 = [
5948
6018
  { value: "created", label: "Recently added" }
5949
6019
  ];
5950
6020
  function subtitleOf(item, withOwner) {
5951
- const what = siteOf(item) || item.definitionLabel;
6021
+ const what = loginSiteOf(item) || item.definitionLabel;
5952
6022
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
5953
6023
  }
5954
6024
  function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header }) {
5955
6025
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
5956
6026
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
5957
6027
  const ordered = [...pinned, ...rest];
5958
- const listRef = (0, import_react27.useRef)(null);
5959
- (0, import_react27.useEffect)(() => {
6028
+ const listRef = (0, import_react28.useRef)(null);
6029
+ (0, import_react28.useEffect)(() => {
5960
6030
  if (selectedId) listRef.current?.querySelector(`[data-item="${selectedId}"]`)?.scrollIntoView({ block: "nearest" });
5961
6031
  }, [selectedId, items.length]);
5962
6032
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
@@ -6007,14 +6077,14 @@ function ListRow({ item, family, port, on, onSelect, withOwner }) {
6007
6077
  const confirmed = useConfirmed();
6008
6078
  const copy = useCopy();
6009
6079
  const notify = useNotify();
6010
- const [busy, setBusy] = (0, import_react27.useState)(false);
6080
+ const [busy, setBusy] = (0, import_react28.useState)(false);
6011
6081
  const user = item.fields.find((f) => USERNAME_KEYS.test(f.key) && f.handling !== "sealed");
6012
6082
  const secret = item.fields.find((f) => f.key === "password" && f.handling === "revealable");
6013
6083
  const quick = async (key, label) => {
6014
6084
  setBusy(true);
6015
6085
  try {
6016
6086
  const v = await confirmed(() => port.reveal(item, key));
6017
- await copy(v, label);
6087
+ await copy(v, label, { secret: key !== user?.key || user.handling !== "visible" });
6018
6088
  } catch (e) {
6019
6089
  if (!wasCancelled(e)) notify(e instanceof Error ? e.message : `${label} could not be read`, "error");
6020
6090
  } finally {
@@ -6031,7 +6101,7 @@ function ListRow({ item, family, port, on, onSelect, withOwner }) {
6031
6101
  onClick: () => onSelect(item.id),
6032
6102
  className: (0, import_design_system19.cn)("flex h-12 w-full items-center gap-3 rounded-lg px-2 text-left", on ? "bg-primary text-primary-foreground" : "hover:bg-accent"),
6033
6103
  children: [
6034
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: siteOf(item), inverted: on }),
6104
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item), inverted: on }),
6035
6105
  /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
6036
6106
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate text-sm font-medium", children: item.name }),
6037
6107
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: subtitleOf(item, withOwner) })
@@ -6047,8 +6117,9 @@ function ListRow({ item, family, port, on, onSelect, withOwner }) {
6047
6117
  }
6048
6118
 
6049
6119
  // src/lists/TrashView.tsx
6050
- var import_react29 = require("react");
6120
+ var import_react30 = require("react");
6051
6121
  var import_controls24 = require("@ai-matrx/design-system/controls");
6122
+ var import_format5 = require("@ai-matrx/kit/format");
6052
6123
 
6053
6124
  // src/lists/logic.ts
6054
6125
  var ENTITY_TYPE = "credential_item";
@@ -6059,7 +6130,7 @@ function favoriteIds(data) {
6059
6130
  function recentIds(data, limit = 50) {
6060
6131
  return rowsOf(data).filter((r) => typeof r.last_viewed_at === "string" && r.last_viewed_at !== "").sort((a, b) => Date.parse(b.last_viewed_at) - Date.parse(a.last_viewed_at)).slice(0, limit).map((r) => r.entity_id);
6061
6132
  }
6062
- function matchesQuery2(item, query) {
6133
+ function trashItemMatchesQuery(item, query) {
6063
6134
  const q = query.trim().toLowerCase();
6064
6135
  if (!q) return true;
6065
6136
  return [item.name, item.definitionLabel, item.owner.label, ...item.tags].some((s) => s.toLowerCase().includes(q));
@@ -6120,16 +6191,16 @@ function refusalText(reason) {
6120
6191
  }
6121
6192
 
6122
6193
  // src/lists/RestorePanel.tsx
6123
- var import_react28 = require("react");
6194
+ var import_react29 = require("react");
6124
6195
  var import_controls23 = require("@ai-matrx/design-system/controls");
6125
6196
  var import_jsx_runtime28 = require("react/jsx-runtime");
6126
6197
  var count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
6127
6198
  function RestorePanel({ lists, item, restore, onClose }) {
6128
- const [preview, setPreview] = (0, import_react28.useState)(null);
6129
- const [error, setError] = (0, import_react28.useState)(null);
6130
- const [busy, setBusy] = (0, import_react28.useState)(false);
6131
- const [loading, setLoading] = (0, import_react28.useState)(Boolean(lists.preview));
6132
- (0, import_react28.useEffect)(() => {
6199
+ const [preview, setPreview] = (0, import_react29.useState)(null);
6200
+ const [error, setError] = (0, import_react29.useState)(null);
6201
+ const [busy, setBusy] = (0, import_react29.useState)(false);
6202
+ const [loading, setLoading] = (0, import_react29.useState)(Boolean(lists.preview));
6203
+ (0, import_react29.useEffect)(() => {
6133
6204
  if (!lists.preview) return;
6134
6205
  let live = true;
6135
6206
  lists.preview(item).then(
@@ -6189,10 +6260,10 @@ function RestorePanel({ lists, item, restore, onClose }) {
6189
6260
  var import_jsx_runtime29 = require("react/jsx-runtime");
6190
6261
  function TrashView({ port, deleted, query, header, onChanged }) {
6191
6262
  const confirmed = useConfirmed();
6192
- const [busyId, setBusyId] = (0, import_react29.useState)(null);
6193
- const [message2, setMessage] = (0, import_react29.useState)(null);
6194
- const shown = deleted.filter((i) => matchesQuery2(i, query));
6195
- const [restoring, setRestoring] = (0, import_react29.useState)(null);
6263
+ const [busyId, setBusyId] = (0, import_react30.useState)(null);
6264
+ const [message2, setMessage] = (0, import_react30.useState)(null);
6265
+ const shown = deleted.filter((i) => trashItemMatchesQuery(i, query));
6266
+ const [restoring, setRestoring] = (0, import_react30.useState)(null);
6196
6267
  const restore = async (item, deletionId) => {
6197
6268
  setBusyId(item.id);
6198
6269
  setMessage(null);
@@ -6216,7 +6287,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6216
6287
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex min-h-12 items-center gap-3 px-3 py-1.5 sm:px-4", children: [
6217
6288
  /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
6218
6289
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate text-sm text-foreground", children: item.name }),
6219
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: [item.deletion?.deletedAt ? `Deleted ${formatDate(item.deletion.deletedAt)}` : null, item.owner.kind !== "me" ? item.owner.label : null].filter(Boolean).join(" \xB7 ") })
6290
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: [item.deletion?.deletedAt ? `Deleted ${(0, import_format5.formatAbsoluteDate)(item.deletion.deletedAt, DAY_FORMAT, "")}` : null, item.owner.kind !== "me" ? item.owner.label : null].filter(Boolean).join(" \xB7 ") })
6220
6291
  ] }),
6221
6292
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_controls24.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RestoreGlyph, {}), disabled: busyId !== null || !item.deletion, onClick: () => setRestoring(item), children: "Restore" })
6222
6293
  ] }, item.id)
@@ -6227,15 +6298,15 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6227
6298
  }
6228
6299
 
6229
6300
  // src/NewItemFlow.tsx
6230
- var import_react30 = require("react");
6301
+ var import_react31 = require("react");
6231
6302
  var import_design_system20 = require("@ai-matrx/design-system");
6232
6303
  var import_controls25 = require("@ai-matrx/design-system/controls");
6233
6304
  var import_jsx_runtime30 = require("react/jsx-runtime");
6234
6305
  function NewItemFlow({ definitions, url, onUrl, onChoose, onCancel }) {
6235
- const [search, setSearch] = (0, import_react30.useState)("");
6236
- const guess = (0, import_react30.useMemo)(() => inferFromUrl(url, definitions), [url, definitions]);
6306
+ const [search, setSearch] = (0, import_react31.useState)("");
6307
+ const guess = (0, import_react31.useMemo)(() => inferFromUrl(url, definitions), [url, definitions]);
6237
6308
  const common = COMMON_DEFINITION_KEYS.map((k) => definitions.find((d) => d.key === k)).filter((d) => d !== void 0);
6238
- const matches = (0, import_react30.useMemo)(() => {
6309
+ const matches = (0, import_react31.useMemo)(() => {
6239
6310
  const q = search.trim().toLowerCase();
6240
6311
  if (!q) return [];
6241
6312
  return definitions.filter((d) => `${d.label} ${d.key} ${d.providerKey ?? ""} ${FAMILY_LABELS[d.family] ?? ""}`.toLowerCase().includes(q)).slice(0, 60);
@@ -6299,13 +6370,14 @@ function NewItemFlow({ definitions, url, onUrl, onChoose, onCancel }) {
6299
6370
  }
6300
6371
 
6301
6372
  // src/sharing/SecureLinkPanel.tsx
6302
- var import_react31 = require("react");
6373
+ var import_react32 = require("react");
6303
6374
  var import_confirm_opener10 = require("@ai-matrx/kit/confirm-opener");
6304
6375
  var import_design_system21 = require("@ai-matrx/design-system");
6305
6376
  var import_controls26 = require("@ai-matrx/design-system/controls");
6377
+ var import_format7 = require("@ai-matrx/kit/format");
6306
6378
 
6307
6379
  // src/sharing/secure-link-logic.ts
6308
- var import_format2 = require("@ai-matrx/kit/format");
6380
+ var import_format6 = require("@ai-matrx/kit/format");
6309
6381
  var SECURE_RESOURCE_TYPE = "credential_item";
6310
6382
  var NOTE_MAX = 1e3;
6311
6383
  var CHANNEL_WORD = { email: "email", sms: "text" };
@@ -6343,7 +6415,7 @@ function carriedWords(keys, fields) {
6343
6415
  const known = new Map(fields.map((f) => [f.key, f.label]));
6344
6416
  return keys.map((k) => known.get(k) ?? fieldNames([k])).join(", ");
6345
6417
  }
6346
- var lifetimeWords = (minutes) => (0, import_format2.formatDurationMinutes)(minutes, { style: "long" });
6418
+ var lifetimeWords = (minutes) => (0, import_format6.formatDurationMinutes)(minutes, { style: "long" });
6347
6419
  function remedyOf(e) {
6348
6420
  const body = e?.serverDetail;
6349
6421
  if (!body || typeof body !== "object") return null;
@@ -6381,20 +6453,20 @@ var import_jsx_runtime31 = require("react/jsx-runtime");
6381
6453
  function SecureLinkPanel({ item, port, onClose }) {
6382
6454
  const sharing = port.sharing;
6383
6455
  const { busy, error, notice, run, setNotice } = useAction();
6384
- const [remedy, setRemedy] = (0, import_react31.useState)(null);
6385
- const [options, setOptions] = (0, import_react31.useState)(null);
6386
- const [optionsError, setOptionsError] = (0, import_react31.useState)(null);
6387
- const [sent, setSent] = (0, import_react31.useState)(null);
6388
- const [sentError, setSentError] = (0, import_react31.useState)(null);
6389
- const [nonce, setNonce] = (0, import_react31.useState)(0);
6390
- const [email, setEmail] = (0, import_react31.useState)("");
6391
- const [phone, setPhone] = (0, import_react31.useState)("");
6392
- const [name, setName] = (0, import_react31.useState)("");
6393
- const [linkBy, setLinkBy] = (0, import_react31.useState)("email");
6394
- const [fields, setFields] = (0, import_react31.useState)([]);
6395
- const [note, setNote] = (0, import_react31.useState)("");
6396
- const [sameChannelSent, setSameChannelSent] = (0, import_react31.useState)(false);
6397
- (0, import_react31.useEffect)(() => {
6456
+ const [remedy, setRemedy] = (0, import_react32.useState)(null);
6457
+ const [options, setOptions] = (0, import_react32.useState)(null);
6458
+ const [optionsError, setOptionsError] = (0, import_react32.useState)(null);
6459
+ const [sent, setSent] = (0, import_react32.useState)(null);
6460
+ const [sentError, setSentError] = (0, import_react32.useState)(null);
6461
+ const [nonce, setNonce] = (0, import_react32.useState)(0);
6462
+ const [email, setEmail] = (0, import_react32.useState)("");
6463
+ const [phone, setPhone] = (0, import_react32.useState)("");
6464
+ const [name, setName] = (0, import_react32.useState)("");
6465
+ const [linkBy, setLinkBy] = (0, import_react32.useState)("email");
6466
+ const [fields, setFields] = (0, import_react32.useState)([]);
6467
+ const [note, setNote] = (0, import_react32.useState)("");
6468
+ const [sameChannelSent, setSameChannelSent] = (0, import_react32.useState)(false);
6469
+ (0, import_react32.useEffect)(() => {
6398
6470
  let live = true;
6399
6471
  setOptionsError(null);
6400
6472
  sharing.secureLinkOptions(item).then(
@@ -6409,7 +6481,7 @@ function SecureLinkPanel({ item, port, onClose }) {
6409
6481
  live = false;
6410
6482
  };
6411
6483
  }, [sharing, item]);
6412
- (0, import_react31.useEffect)(() => {
6484
+ (0, import_react32.useEffect)(() => {
6413
6485
  const list = sharing.sentSecureLinks;
6414
6486
  if (!list) return;
6415
6487
  let live = true;
@@ -6422,9 +6494,9 @@ function SecureLinkPanel({ item, port, onClose }) {
6422
6494
  live = false;
6423
6495
  };
6424
6496
  }, [sharing, item, nonce]);
6425
- const [phoneTyped, setPhoneTyped] = (0, import_react31.useState)(false);
6426
- const prefilled = (0, import_react31.useRef)(false);
6427
- (0, import_react31.useEffect)(() => {
6497
+ const [phoneTyped, setPhoneTyped] = (0, import_react32.useState)(false);
6498
+ const prefilled = (0, import_react32.useRef)(false);
6499
+ (0, import_react32.useEffect)(() => {
6428
6500
  const lookup = sharing.phoneOnFile;
6429
6501
  if (!lookup || phoneTyped) return;
6430
6502
  const clear = () => {
@@ -6572,15 +6644,16 @@ function SecureLinkPanel({ item, port, onClose }) {
6572
6644
  );
6573
6645
  }
6574
6646
  function sentMeta(row, options) {
6575
- const when = row.status === "pending" ? `Until ${formatDate(row.expiresAt, true)}` : row.status === "viewed" && row.viewedAt ? formatDate(row.viewedAt, true) : formatDate(row.createdAt);
6647
+ const when = row.status === "pending" ? `Until ${(0, import_format7.formatAbsoluteDate)(row.expiresAt, void 0, "")}` : row.status === "viewed" && row.viewedAt ? (0, import_format7.formatAbsoluteDate)(row.viewedAt, void 0, "") : (0, import_format7.formatAbsoluteDate)(row.createdAt, DAY_FORMAT, "");
6576
6648
  const carried = row.fieldKeys.length ? carriedWords(row.fieldKeys, options?.fields ?? []) : "";
6577
6649
  return [when, carried].filter(Boolean).join(" \xB7 ");
6578
6650
  }
6579
6651
 
6580
6652
  // src/devices/DevicesPanel.tsx
6581
- var import_react32 = require("react");
6653
+ var import_react33 = require("react");
6582
6654
  var import_confirm_opener11 = require("@ai-matrx/kit/confirm-opener");
6583
6655
  var import_controls27 = require("@ai-matrx/design-system/controls");
6656
+ var import_format8 = require("@ai-matrx/kit/format");
6584
6657
 
6585
6658
  // src/devices/logic.ts
6586
6659
  var PASSKEY_RP_ID = "aimatrx.com";
@@ -6668,10 +6741,10 @@ function ErrorLine2({ text }) {
6668
6741
  function DevicesPanel({ port, onClose }) {
6669
6742
  const confirmed = useConfirmed();
6670
6743
  const notify = useNotify();
6671
- const [devices, setDevices] = (0, import_react32.useState)(null);
6672
- const [error, setError] = (0, import_react32.useState)(null);
6673
- const [busyId, setBusyId] = (0, import_react32.useState)(null);
6674
- const load = (0, import_react32.useCallback)(async () => {
6744
+ const [devices, setDevices] = (0, import_react33.useState)(null);
6745
+ const [error, setError] = (0, import_react33.useState)(null);
6746
+ const [busyId, setBusyId] = (0, import_react33.useState)(null);
6747
+ const load = (0, import_react33.useCallback)(async () => {
6675
6748
  setError(null);
6676
6749
  try {
6677
6750
  setDevices(await port.devices.list());
@@ -6679,7 +6752,7 @@ function DevicesPanel({ port, onClose }) {
6679
6752
  setError(messageOf3(e, "Your browsers could not be read."));
6680
6753
  }
6681
6754
  }, [port]);
6682
- (0, import_react32.useEffect)(() => {
6755
+ (0, import_react33.useEffect)(() => {
6683
6756
  void load();
6684
6757
  }, [load]);
6685
6758
  const revoke = async (device) => {
@@ -6710,13 +6783,13 @@ function DevicesPanel({ port, onClose }) {
6710
6783
  Row,
6711
6784
  {
6712
6785
  label: off ? "Turned off" : "Can fill",
6713
- hint: off ? formatDate(device.revokedAt) : void 0,
6786
+ hint: off ? (0, import_format8.formatAbsoluteDate)(device.revokedAt, DAY_FORMAT, "") : void 0,
6714
6787
  actions: off ? null : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_controls27.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(PowerGlyph, {}), disabled: busyId !== null, onClick: () => void revoke(device), children: "Revoke" }),
6715
6788
  children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "flex min-w-0 flex-col", children: [
6716
6789
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: off ? "truncate text-muted-foreground" : "truncate text-foreground", children: device.label }),
6717
6790
  /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "truncate text-xs text-muted-foreground", children: [
6718
- device.lastUsedAt ? `Last used ${formatDate(device.lastUsedAt, true)}` : "Never used",
6719
- device.addedAt ? ` \xB7 added ${formatDate(device.addedAt)}` : ""
6791
+ device.lastUsedAt ? `Last used ${(0, import_format8.formatAbsoluteDate)(device.lastUsedAt, void 0, "")}` : "Never used",
6792
+ device.addedAt ? ` \xB7 added ${(0, import_format8.formatAbsoluteDate)(device.addedAt, DAY_FORMAT, "")}` : ""
6720
6793
  ] })
6721
6794
  ] })
6722
6795
  },
@@ -6737,11 +6810,11 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6737
6810
  const where = isMac ? "the Mac app" : "the extension";
6738
6811
  const passkeys = port.devices.passkeys;
6739
6812
  const here = passkeys?.available() ?? false;
6740
- const [methods, setMethods] = (0, import_react32.useState)(null);
6741
- const [busy, setBusy] = (0, import_react32.useState)(null);
6742
- const [error, setError] = (0, import_react32.useState)(null);
6743
- const [approvedUntil, setApprovedUntil] = (0, import_react32.useState)(null);
6744
- const load = (0, import_react32.useCallback)(async () => {
6813
+ const [methods, setMethods] = (0, import_react33.useState)(null);
6814
+ const [busy, setBusy] = (0, import_react33.useState)(null);
6815
+ const [error, setError] = (0, import_react33.useState)(null);
6816
+ const [approvedUntil, setApprovedUntil] = (0, import_react33.useState)(null);
6817
+ const load = (0, import_react33.useCallback)(async () => {
6745
6818
  setError(null);
6746
6819
  try {
6747
6820
  setMethods(await port.devices.stepUpMethods());
@@ -6749,7 +6822,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6749
6822
  setError(messageOf3(e, "Your account could not be checked."));
6750
6823
  }
6751
6824
  }, [port]);
6752
- (0, import_react32.useEffect)(() => {
6825
+ (0, import_react33.useEffect)(() => {
6753
6826
  if (key && here) void load();
6754
6827
  }, [key, here, load]);
6755
6828
  const approve = async () => {
@@ -6810,7 +6883,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6810
6883
  "Return to ",
6811
6884
  where,
6812
6885
  " by ",
6813
- formatDate(approvedUntil, true),
6886
+ (0, import_format8.formatAbsoluteDate)(approvedUntil, void 0, ""),
6814
6887
  "."
6815
6888
  ] }) }) : !passkeys ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Empty, { children: [
6816
6889
  "Approve with your password in ",
@@ -6837,7 +6910,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
6837
6910
  }
6838
6911
 
6839
6912
  // src/native/NativeImportPanel.tsx
6840
- var import_react34 = require("react");
6913
+ var import_react35 = require("react");
6841
6914
  var import_confirm_opener12 = require("@ai-matrx/kit/confirm-opener");
6842
6915
  var import_controls28 = require("@ai-matrx/design-system/controls");
6843
6916
 
@@ -6900,19 +6973,19 @@ async function loadFullPreview(bridge, operationId, current) {
6900
6973
  }
6901
6974
 
6902
6975
  // src/native/use-native-import.ts
6903
- var import_react33 = require("react");
6976
+ var import_react34 = require("react");
6904
6977
  var fail = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
6905
6978
  var RESTART = "Your account changed. The import was stopped; start it again.";
6906
6979
  function useNativeImport(bridge, stillAllowed, onImported) {
6907
- const [screen, setScreen] = (0, import_react33.useState)({ kind: "idle", message: null });
6908
- const [busy, setBusy] = (0, import_react33.useState)(false);
6909
- const generation = (0, import_react33.useRef)(0);
6910
- const mounted = (0, import_react33.useRef)(false);
6911
- const op = (0, import_react33.useRef)(null);
6912
- const last = (0, import_react33.useRef)(null);
6913
- const timer2 = (0, import_react33.useRef)(null);
6914
- const polls = (0, import_react33.useRef)(0);
6915
- const refreshRef = (0, import_react33.useRef)(async () => void 0);
6980
+ const [screen, setScreen] = (0, import_react34.useState)({ kind: "idle", message: null });
6981
+ const [busy, setBusy] = (0, import_react34.useState)(false);
6982
+ const generation = (0, import_react34.useRef)(0);
6983
+ const mounted = (0, import_react34.useRef)(false);
6984
+ const op = (0, import_react34.useRef)(null);
6985
+ const last = (0, import_react34.useRef)(null);
6986
+ const timer2 = (0, import_react34.useRef)(null);
6987
+ const polls = (0, import_react34.useRef)(0);
6988
+ const refreshRef = (0, import_react34.useRef)(async () => void 0);
6916
6989
  const current = (g) => mounted.current && g === generation.current;
6917
6990
  const stopPolling = () => {
6918
6991
  if (timer2.current !== null) clearTimeout(timer2.current);
@@ -7081,9 +7154,9 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7081
7154
  setBusy(false);
7082
7155
  setScreen({ kind: "idle", message: null });
7083
7156
  };
7084
- const invalidateRef = (0, import_react33.useRef)(invalidate);
7157
+ const invalidateRef = (0, import_react34.useRef)(invalidate);
7085
7158
  invalidateRef.current = invalidate;
7086
- (0, import_react33.useEffect)(() => {
7159
+ (0, import_react34.useEffect)(() => {
7087
7160
  mounted.current = true;
7088
7161
  const off = bridge.subscribe?.(() => {
7089
7162
  if (op.current) invalidateRef.current(RESTART);
@@ -7115,14 +7188,14 @@ function NativeImportPanel({ bridge, readLimits, activeOrganizationId, organizat
7115
7188
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Importer, { bridge, readLimits, activeOrganizationId, organizations, onClose, onChanged });
7116
7189
  }
7117
7190
  function Importer({ bridge, readLimits, activeOrganizationId, organizations, onClose, onChanged }) {
7118
- const options = (0, import_react34.useMemo)(() => organizations.map((o) => ({ value: o.id, label: o.name })), [organizations]);
7119
- const [organizationId, setOrganizationId] = (0, import_react34.useState)(() => organizations.some((o) => o.id === activeOrganizationId) ? activeOrganizationId : organizations[0]?.id ?? "");
7120
- const [limits, setLimits] = (0, import_react34.useState)(null);
7121
- const [limitError, setLimitError] = (0, import_react34.useState)(null);
7191
+ const options = (0, import_react35.useMemo)(() => organizations.map((o) => ({ value: o.id, label: o.name })), [organizations]);
7192
+ const [organizationId, setOrganizationId] = (0, import_react35.useState)(() => organizations.some((o) => o.id === activeOrganizationId) ? activeOrganizationId : organizations[0]?.id ?? "");
7193
+ const [limits, setLimits] = (0, import_react35.useState)(null);
7194
+ const [limitError, setLimitError] = (0, import_react35.useState)(null);
7122
7195
  const flow = useNativeImport(bridge, (id) => organizations.some((o) => o.id === id), onChanged);
7123
7196
  const { screen, busy } = flow;
7124
7197
  const orgName = organizations.find((o) => o.id === organizationId)?.name ?? "this organization";
7125
- (0, import_react34.useEffect)(() => {
7198
+ (0, import_react35.useEffect)(() => {
7126
7199
  if (!readLimits || !organizationId) return;
7127
7200
  let live = true;
7128
7201
  setLimits(null);
@@ -7183,20 +7256,20 @@ function Importer({ bridge, readLimits, activeOrganizationId, organizations, onC
7183
7256
  }
7184
7257
 
7185
7258
  // src/extension/PageView.tsx
7186
- var import_react38 = require("react");
7259
+ var import_react39 = require("react");
7187
7260
  var import_design_system23 = require("@ai-matrx/design-system");
7188
7261
  var import_controls32 = require("@ai-matrx/design-system/controls");
7189
7262
 
7190
7263
  // src/extension/CaptureCard.tsx
7191
- var import_react35 = require("react");
7264
+ var import_react36 = require("react");
7192
7265
  var import_controls29 = require("@ai-matrx/design-system/controls");
7193
7266
  var import_jsx_runtime34 = require("react/jsx-runtime");
7194
7267
  var SEARCH_FROM = 4;
7195
7268
  function CaptureCard({ capture, onChanged }) {
7196
- const prompt = (0, import_react35.useSyncExternalStore)(capture.subscribe, capture.get, capture.get);
7269
+ const prompt = (0, import_react36.useSyncExternalStore)(capture.subscribe, capture.get, capture.get);
7197
7270
  const notify = useNotify();
7198
- const [busy, setBusy] = (0, import_react35.useState)(false);
7199
- const [q, setQ] = (0, import_react35.useState)("");
7271
+ const [busy, setBusy] = (0, import_react36.useState)(false);
7272
+ const [q, setQ] = (0, import_react36.useState)("");
7200
7273
  if (!prompt) return null;
7201
7274
  const decide = async (d) => {
7202
7275
  setBusy(true);
@@ -7252,15 +7325,15 @@ function CaptureCard({ capture, onChanged }) {
7252
7325
  }
7253
7326
 
7254
7327
  // src/extension/FillSetupCard.tsx
7255
- var import_react36 = require("react");
7328
+ var import_react37 = require("react");
7256
7329
  var import_controls30 = require("@ai-matrx/design-system/controls");
7257
7330
  var import_jsx_runtime35 = require("react/jsx-runtime");
7258
7331
  function FillSetupCard({ fillSetup }) {
7259
7332
  const notify = useNotify();
7260
- const state = (0, import_react36.useSyncExternalStore)(fillSetup.subscribe, fillSetup.get, fillSetup.get);
7261
- const box = (0, import_react36.useRef)(null);
7262
- const [busy, setBusy] = (0, import_react36.useState)(false);
7263
- const [error, setError] = (0, import_react36.useState)(null);
7333
+ const state = (0, import_react37.useSyncExternalStore)(fillSetup.subscribe, fillSetup.get, fillSetup.get);
7334
+ const box = (0, import_react37.useRef)(null);
7335
+ const [busy, setBusy] = (0, import_react37.useState)(false);
7336
+ const [error, setError] = (0, import_react37.useState)(null);
7264
7337
  if (state.status !== "off") return null;
7265
7338
  const finish = async (run) => {
7266
7339
  setBusy(true);
@@ -7325,7 +7398,7 @@ function FillSetupCard({ fillSetup }) {
7325
7398
  }
7326
7399
 
7327
7400
  // src/extension/PageGenerator.tsx
7328
- var import_react37 = require("react");
7401
+ var import_react38 = require("react");
7329
7402
  var import_design_system22 = require("@ai-matrx/design-system");
7330
7403
  var import_controls31 = require("@ai-matrx/design-system/controls");
7331
7404
  var import_jsx_runtime36 = require("react/jsx-runtime");
@@ -7334,10 +7407,10 @@ function PageGenerator({ fields }) {
7334
7407
  const limits = useGeneratorLimitsSource();
7335
7408
  const copy = useCopy();
7336
7409
  const held = useTransientSecret(HOLD_MS);
7337
- const [open, setOpen] = (0, import_react37.useState)(false);
7338
- const [busy, setBusy] = (0, import_react37.useState)(false);
7339
- const [targets, setTargets] = (0, import_react37.useState)([]);
7340
- const [said, setSaid] = (0, import_react37.useState)(null);
7410
+ const [open, setOpen] = (0, import_react38.useState)(false);
7411
+ const [busy, setBusy] = (0, import_react38.useState)(false);
7412
+ const [targets, setTargets] = (0, import_react38.useState)([]);
7413
+ const [said, setSaid] = (0, import_react38.useState)(null);
7341
7414
  if (!limits) return null;
7342
7415
  const say3 = setSaid;
7343
7416
  const put = async (targetId, value2) => {
@@ -7401,8 +7474,8 @@ function PageGenerator({ fields }) {
7401
7474
  // src/extension/PageView.tsx
7402
7475
  var import_jsx_runtime37 = require("react/jsx-runtime");
7403
7476
  function PageView({ port, page, fill, capture, fillSetup, newPasswordFields, items, definitions, onOpen, onNewForPage, onBrowse, onChanged, header }) {
7404
- const [running, setRunning] = (0, import_react38.useState)(null);
7405
- const [said, setSaid] = (0, import_react38.useState)(null);
7477
+ const [running, setRunning] = (0, import_react39.useState)(null);
7478
+ const [said, setSaid] = (0, import_react39.useState)(null);
7406
7479
  const site = pageSite(page.url);
7407
7480
  const state = page.matchState ?? { status: "ready" };
7408
7481
  const fillable = new Set(page.fillableItemIds ?? page.matchingItemIds);
@@ -7486,7 +7559,7 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
7486
7559
  const confirmed = useConfirmed();
7487
7560
  const copy = useCopy();
7488
7561
  const notify = useNotify();
7489
- const [reading, setReading] = (0, import_react38.useState)(false);
7562
+ const [reading, setReading] = (0, import_react39.useState)(false);
7490
7563
  const usable = item?.capabilities.canUse ?? false;
7491
7564
  const user = usable ? item?.fields.find((f) => USERNAME_KEYS.test(f.key) && f.handling !== "sealed" && f.active) : void 0;
7492
7565
  const secret = usable && item?.capabilities.canReveal ? item?.fields.find((f) => f.key === "password" && f.handling === "revealable" && f.active) : void 0;
@@ -7501,10 +7574,10 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
7501
7574
  setReading(false);
7502
7575
  }
7503
7576
  };
7504
- const subtitle = [item ? siteOf(item) || item.definitionLabel : "", tag].filter(Boolean).join(" \xB7 ");
7577
+ const subtitle = [item ? loginSiteOf(item) || item.definitionLabel : "", tag].filter(Boolean).join(" \xB7 ");
7505
7578
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "flex h-12 items-center gap-1 pl-1 pr-2", "data-item": itemId, children: [
7506
7579
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("button", { type: "button", onClick: () => onOpen(itemId), className: "flex h-11 min-w-0 flex-1 items-center gap-3 rounded-lg px-2 text-left hover:bg-accent", children: [
7507
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ItemIcon, { definitionKey: item?.definitionKey ?? "website_login", family, site: item ? siteOf(item) : "" }),
7580
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ItemIcon, { definitionKey: item?.definitionKey ?? "website_login", family, site: item ? loginSiteOf(item) : "" }),
7508
7581
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
7509
7582
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate text-sm font-medium", children: item?.name ?? "Saved login" }),
7510
7583
  subtitle ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: subtitle }) : null
@@ -7531,7 +7604,7 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
7531
7604
  }
7532
7605
 
7533
7606
  // src/Sidebar.tsx
7534
- var import_react39 = require("react");
7607
+ var import_react40 = require("react");
7535
7608
  var import_design_system24 = require("@ai-matrx/design-system");
7536
7609
  var import_controls33 = require("@ai-matrx/design-system/controls");
7537
7610
  var import_jsx_runtime38 = require("react/jsx-runtime");
@@ -7614,7 +7687,7 @@ function Group({ title, children }) {
7614
7687
  ] });
7615
7688
  }
7616
7689
  function Sidebar({ vault, choices, onVault, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false }) {
7617
- const [tagsOpen, setTagsOpen] = (0, import_react39.useState)(view.startsWith("tag:"));
7690
+ const [tagsOpen, setTagsOpen] = (0, import_react40.useState)(view.startsWith("tag:"));
7618
7691
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("nav", { "aria-label": "Vault", className: "flex h-full min-h-0 flex-col border-r border-sidebar-border bg-sidebar-background text-sidebar-foreground", children: [
7619
7692
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2 px-3 pb-2 pt-3", children: [
7620
7693
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SafeGlyph, { className: "size-5 shrink-0 text-primary" }),
@@ -7674,8 +7747,8 @@ var generatorLimitsFrom = (knobs) => knobs ? async () => {
7674
7747
  } : void 0;
7675
7748
  function ImportWithLimits(props) {
7676
7749
  const { knobs, ...rest } = props;
7677
- const [state, setState] = (0, import_react40.useState)(null);
7678
- (0, import_react40.useEffect)(() => {
7750
+ const [state, setState] = (0, import_react41.useState)(null);
7751
+ (0, import_react41.useEffect)(() => {
7679
7752
  if (!knobs) return setState({ error: "Import is not set up for this organization yet." });
7680
7753
  knobs().then(
7681
7754
  (r) => {
@@ -7692,31 +7765,55 @@ function ImportWithLimits(props) {
7692
7765
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Panel, { title: "Import", onClose: rest.onClose, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Empty, { children: state ? state.error : "Reading this organization\u2019s import settings\u2026" }) });
7693
7766
  }
7694
7767
  function VaultApp(props) {
7695
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "relative h-full min-h-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(NoticeProvider, { notify: props.notify, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(IdentityGate, { identity: props.host?.identity, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GeneratorLimitsProvider, { resolve: generatorLimitsFrom(props.host?.knobs), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Workspace, { ...props }) }) }) }) });
7768
+ const lock = props.host?.lock;
7769
+ const locked = useLocked(lock);
7770
+ const port = (0, import_react41.useMemo)(() => lock ? guardPort(props.port, () => lock.locked()) : props.port, [props.port, lock]);
7771
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "relative h-full min-h-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(NoticeProvider, { notify: props.notify, children: lock && locked ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(LockedScreen, { lock }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ClipboardProvider, { clipboard: props.host?.clipboard, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(RevealSecondsProvider, { seconds: props.host?.revealSeconds, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(IdentityGate, { identity: props.host?.identity, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GeneratorLimitsProvider, { resolve: generatorLimitsFrom(props.host?.knobs), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Workspace, { ...props, port }) }) }) }) }) }) });
7772
+ }
7773
+ function LockedScreen({ lock }) {
7774
+ const [busy, setBusy] = (0, import_react41.useState)(false);
7775
+ const [error, setError] = (0, import_react41.useState)(null);
7776
+ const unlock = async () => {
7777
+ setBusy(true);
7778
+ setError(null);
7779
+ try {
7780
+ await lock.unlock();
7781
+ } catch (e) {
7782
+ setError(e instanceof Error && e.message ? e.message : "The vault stayed locked.");
7783
+ } finally {
7784
+ setBusy(false);
7785
+ }
7786
+ };
7787
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { role: "region", "aria-label": "Vault locked", className: "flex h-full min-h-0 flex-col items-center justify-center gap-3 p-6 text-center", children: [
7788
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(LockGlyph, { className: "size-8 text-muted-foreground" }),
7789
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "text-base font-medium text-foreground", children: "Vault locked" }),
7790
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_controls34.Button, { variant: "primary", disabled: busy, onClick: () => void unlock(), children: lock.unlockLabel ?? "Unlock" }),
7791
+ error ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { role: "alert", className: "max-w-sm text-sm text-destructive", children: error }) : null
7792
+ ] });
7696
7793
  }
7697
7794
  function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocationChange }) {
7698
- const rootRef = (0, import_react40.useRef)(null);
7795
+ const rootRef = (0, import_react41.useRef)(null);
7699
7796
  const width = useElementWidth(rootRef);
7700
7797
  const mode = layout !== "auto" ? layout : width >= FULL_AT ? "full" : width >= SPLIT_AT ? "split" : "stack";
7701
7798
  const notify = useNotify();
7702
- const [snap, setSnap] = (0, import_react40.useState)(null);
7703
- const [items, setItems] = (0, import_react40.useState)([]);
7704
- const [deleted, setDeleted] = (0, import_react40.useState)([]);
7705
- const [favorites, setFavorites] = (0, import_react40.useState)(null);
7706
- const [recents, setRecents] = (0, import_react40.useState)(null);
7707
- const [error, setError] = (0, import_react40.useState)(null);
7708
- const [inner, setInner] = (0, import_react40.useState)(DEFAULT_LOCATION);
7799
+ const [snap, setSnap] = (0, import_react41.useState)(null);
7800
+ const [items, setItems] = (0, import_react41.useState)([]);
7801
+ const [deleted, setDeleted] = (0, import_react41.useState)([]);
7802
+ const [favorites, setFavorites] = (0, import_react41.useState)(null);
7803
+ const [recents, setRecents] = (0, import_react41.useState)(null);
7804
+ const [error, setError] = (0, import_react41.useState)(null);
7805
+ const [inner, setInner] = (0, import_react41.useState)(DEFAULT_LOCATION);
7709
7806
  const loc = location ?? inner;
7710
- const dirty = (0, import_react40.useRef)(false);
7711
- const [notice, setNotice] = (0, import_react40.useState)(null);
7712
- const move = (0, import_react40.useCallback)(
7807
+ const dirty = (0, import_react41.useRef)(false);
7808
+ const [notice, setNotice] = (0, import_react41.useState)(null);
7809
+ const move = (0, import_react41.useCallback)(
7713
7810
  (next, how) => {
7714
7811
  if (!location) setInner(next);
7715
7812
  onLocationChange?.(next, how);
7716
7813
  },
7717
7814
  [location, onLocationChange]
7718
7815
  );
7719
- const go = (0, import_react40.useCallback)(
7816
+ const go = (0, import_react41.useCallback)(
7720
7817
  (patch, how = "push") => {
7721
7818
  const leaving = patch.itemId !== void 0 || patch.mode !== void 0 || patch.vault !== void 0 || patch.view !== void 0;
7722
7819
  const next = { ...loc, ...patch };
@@ -7735,9 +7832,9 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7735
7832
  },
7736
7833
  [loc, move]
7737
7834
  );
7738
- const knobsRef = (0, import_react40.useRef)(host.knobs);
7835
+ const knobsRef = (0, import_react41.useRef)(host.knobs);
7739
7836
  knobsRef.current = host.knobs;
7740
- const load = (0, import_react40.useCallback)(() => {
7837
+ const load = (0, import_react41.useCallback)(() => {
7741
7838
  setError(null);
7742
7839
  port.load().then((s) => {
7743
7840
  setSnap(s);
@@ -7752,17 +7849,17 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7752
7849
  knobsRef.current?.().catch(() => void 0);
7753
7850
  port.lists.recents?.list().then(setRecents, () => void 0);
7754
7851
  }, [port]);
7755
- (0, import_react40.useEffect)(load, [load]);
7756
- (0, import_react40.useEffect)(() => {
7852
+ (0, import_react41.useEffect)(load, [load]);
7853
+ (0, import_react41.useEffect)(() => {
7757
7854
  if (!loc.itemId || !port.lists.recents) return;
7758
7855
  const id = loc.itemId;
7759
7856
  port.lists.recents.record(id).then(() => setRecents((r) => [id, ...(r ?? []).filter((x) => x !== id)]), () => void 0);
7760
7857
  }, [loc.itemId, port]);
7761
7858
  const orgs = snap?.organizations ?? [];
7762
7859
  const definitions = snap?.definitions ?? [];
7763
- const inThisVault = (0, import_react40.useMemo)(() => items.filter((i) => inVault(i, loc.vault)), [items, loc.vault]);
7764
- const views = (0, import_react40.useMemo)(() => buildViews(inThisVault, definitions, favorites, recents), [inThisVault, definitions, favorites, recents]);
7765
- const visible = (0, import_react40.useMemo)(() => {
7860
+ const inThisVault = (0, import_react41.useMemo)(() => items.filter((i) => inVault(i, loc.vault)), [items, loc.vault]);
7861
+ const views = (0, import_react41.useMemo)(() => buildViews(inThisVault, definitions, favorites, recents), [inThisVault, definitions, favorites, recents]);
7862
+ const visible = (0, import_react41.useMemo)(() => {
7766
7863
  const filtered = inThisVault.filter((i) => {
7767
7864
  if (!matchesQuery(i, loc.q)) return false;
7768
7865
  if (loc.view === "favorites") return favorites?.has(i.id) ?? false;
@@ -7773,8 +7870,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7773
7870
  if (loc.view === "recent" && recents) return [...filtered].sort((a, b) => recents.indexOf(a.id) - recents.indexOf(b.id));
7774
7871
  return sortItems(filtered, loc.sort);
7775
7872
  }, [inThisVault, loc.view, loc.q, loc.sort, favorites, recents, definitions]);
7776
- const choices = (0, import_react40.useMemo)(() => vaultChoices(items, orgs, loc.vault), [items, orgs, loc.vault]);
7777
- const matchingIds = (0, import_react40.useMemo)(() => host.page ? new Set(host.page.matchingItemIds) : void 0, [host.page]);
7873
+ const choices = (0, import_react41.useMemo)(() => vaultChoices(items, orgs, loc.vault), [items, orgs, loc.vault]);
7874
+ const matchingIds = (0, import_react41.useMemo)(() => host.page ? new Set(host.page.matchingItemIds) : void 0, [host.page]);
7778
7875
  const selected = loc.itemId ? items.find((i) => i.id === loc.itemId) ?? null : null;
7779
7876
  const replaceItem = (next) => setItems((cur) => cur.some((i) => i.id === next.id) ? cur.map((i) => i.id === next.id ? next : i) : [...cur, next]);
7780
7877
  const toggleFavorite = (id) => {
@@ -7845,6 +7942,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
7845
7942
  host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_design_system25.DropdownMenuItem, { onSelect: host.nativeProvider.openSetup, children: [
7846
7943
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ShieldGlyph, { className: "size-4" }),
7847
7944
  "System AutoFill"
7945
+ ] }) : null,
7946
+ host.lock?.lock ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_design_system25.DropdownMenuItem, { onSelect: () => void host.lock?.lock?.(), children: [
7947
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(LockGlyph, { className: "size-4" }),
7948
+ "Lock"
7848
7949
  ] }) : null
7849
7950
  ] })
7850
7951
  ] })
@@ -8459,7 +8560,7 @@ function createSharingPort(ctx, options = {}) {
8459
8560
  return r.grants.map(grantOf);
8460
8561
  },
8461
8562
  async addPerson(item, email, level) {
8462
- const w = await account6.addGrant(api, { path: { item_id: item.id }, body: { recipient_email: email.trim(), ...flagsOf(level), initiation: "user" } }, ctx.optsFor(item));
8563
+ const w = await account6.addGrant(api, { path: { item_id: item.id }, body: { recipient_email: email.trim(), ...shareFlagsOf(level), initiation: "user" } }, ctx.optsFor(item));
8463
8564
  return grantOf(w);
8464
8565
  },
8465
8566
  async addOrganization(item, organizationId, level) {
@@ -8467,14 +8568,14 @@ function createSharingPort(ctx, options = {}) {
8467
8568
  return grantOf(w);
8468
8569
  },
8469
8570
  async setLevel(item, grant, level) {
8470
- const w = await account6.updateGrant(api, { path: { item_id: item.id, grant_id: grant.id }, body: { ...flagsOf(level), initiation: "user" } }, ctx.optsFor(item));
8571
+ const w = await account6.updateGrant(api, { path: { item_id: item.id, grant_id: grant.id }, body: { ...shareFlagsOf(level), initiation: "user" } }, ctx.optsFor(item));
8471
8572
  return grantOf(w);
8472
8573
  },
8473
8574
  async revoke(item, grant) {
8474
8575
  await account6.removeGrant(api, { path: { item_id: item.id, grant_id: grant.id } }, ctx.optsFor(item));
8475
8576
  },
8476
8577
  async setAccessMode(item, mode, people) {
8477
- const grantees = mode === "restricted" ? people.filter((g) => g.kind === "person" && g.userId).map((g) => ({ user_id: g.userId, ...flagsOf(g.level) })) : [];
8578
+ const grantees = mode === "restricted" ? people.filter((g) => g.kind === "person" && g.userId).map((g) => ({ user_id: g.userId, ...shareFlagsOf(g.level) })) : [];
8478
8579
  await account6.share(api, { path: { item_id: item.id }, body: { access_mode: mode, grantees, initiation: "user" } }, ctx.optsFor(item));
8479
8580
  return ctx.refetch(item.id, item.owner);
8480
8581
  },