@ai-matrx/vault 0.1.18 → 0.1.20

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
@@ -740,7 +740,7 @@ var DAY_FORMAT = { year: "numeric", month: "short", day: "numeric" };
740
740
  var import_jsx_runtime5 = require("react/jsx-runtime");
741
741
  var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
742
742
  function SecretText({ value, multiline }) {
743
- if (multiline || value.length > 200) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "whitespace-pre-wrap break-all font-mono text-sm", children: value });
743
+ if (multiline || value.length > 200 || value.includes("\n")) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "whitespace-pre-wrap break-all font-mono text-sm", children: value });
744
744
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "break-all font-mono text-sm tracking-wide", children: [...value].map((ch, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: /[0-9]/.test(ch) ? "text-primary" : /[^A-Za-z0-9]/.test(ch) ? "text-warning" : void 0, children: ch }, i)) });
745
745
  }
746
746
  var quietError = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
@@ -876,18 +876,19 @@ function EditRow({ label, hint: hint2, value, onChange, placeholder, secret = fa
876
876
  }
877
877
  );
878
878
  }
879
- function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove, extra, onPasteValue }) {
879
+ function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove, extra, onPasteValue, multiline = false }) {
880
880
  const [shown, setShown] = (0, import_react5.useState)(false);
881
881
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
882
882
  VaultRow,
883
883
  {
884
884
  label: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Field, { className: "w-full", value: label, onChange: (e) => onLabel(e.target.value), placeholder: "Name", "aria-label": "Field name" }),
885
+ top: multiline,
885
886
  actions: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
886
- secret && value ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: shown ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeGlyph, {}), "aria-label": shown ? "Hide" : "Show", onClick: () => setShown((x) => !x) }) : null,
887
+ secret && value && !multiline ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: shown ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeGlyph, {}), "aria-label": shown ? "Hide" : "Show", onClick: () => setShown((x) => !x) }) : null,
887
888
  extra,
888
889
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", removes: true, icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XGlyph, {}), "aria-label": "Remove field", onClick: onRemove })
889
890
  ] }),
890
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
891
+ children: multiline ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Textarea, { className: "w-full", value, onChange: (e) => onValue(e.target.value), rows: 4, placeholder: secret ? "Encrypted, hidden until shown" : "Value", autoComplete: "off", spellCheck: !secret, "aria-label": label ? `${label} value` : "Field value" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
891
892
  import_controls2.Field,
892
893
  {
893
894
  className: "w-full",
@@ -929,6 +930,7 @@ function locationFromParams(params, itemId = params.get("item")) {
929
930
  mode: mode === "edit" || mode === "new" ? mode : "view",
930
931
  kind: params.get("kind"),
931
932
  url: params.get("url") ?? "",
933
+ ...params.get("name") ? { name: params.get("name") ?? "" } : {},
932
934
  panel: params.get("panel")
933
935
  };
934
936
  }
@@ -941,6 +943,7 @@ function locationToParams(loc) {
941
943
  if (loc.mode !== "view") q.set("mode", loc.mode);
942
944
  if (loc.kind) q.set("kind", loc.kind);
943
945
  if (loc.url) q.set("url", loc.url);
946
+ if (loc.name) q.set("name", loc.name);
944
947
  if (loc.panel) q.set("panel", loc.panel);
945
948
  return q;
946
949
  }
@@ -1004,6 +1007,7 @@ function sortItems(items, sort, recents) {
1004
1007
  if (sort === "updated") return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1005
1008
  return out.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
1006
1009
  }
1010
+ var SECURE_NOTE_KEY = "secure_notes";
1007
1011
  function draftOf(item) {
1008
1012
  return {
1009
1013
  name: item?.name ?? "",
@@ -1054,7 +1058,9 @@ function rebaseDraft(oldBase, newBase, draft) {
1054
1058
  secrets: draft.secrets,
1055
1059
  custom,
1056
1060
  removed: draft.removed,
1057
- authenticator: draft.authenticator
1061
+ authenticator: draft.authenticator,
1062
+ ...draft.browser ? { browser: draft.browser } : {},
1063
+ ...draft.fieldSettings ? { fieldSettings: draft.fieldSettings } : {}
1058
1064
  };
1059
1065
  }
1060
1066
  function panesShown(mode, specialView, showingDetail) {
@@ -1082,7 +1088,8 @@ var FAMILY_LABELS = {
1082
1088
  signing_files: "Signing & files"
1083
1089
  };
1084
1090
  var COMMON_DEFINITION_KEYS = ["website_login", "username_password", "api_key", "env_value", "database_login", "custom"];
1085
- var MULTILINE = /(json|pem|private_key|certificate|cert|bundle|document|recovery_codes)/;
1091
+ var MULTILINE = /(json|pem|private_key|certificate|cert|bundle|document|recovery_codes|(^|_)notes?($|_))/;
1092
+ var isMultilineKey = (key) => MULTILINE.test(key);
1086
1093
  var SECRET = /(password|secret|token|api_key|private|passphrase|credential|seed|key$|_key)/;
1087
1094
  var URLISH = /(url|uri|endpoint|webhook|hook)/;
1088
1095
  function humanize(key) {
@@ -1981,10 +1988,16 @@ function QrScanPanel({ open, onResult, onClose }) {
1981
1988
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
1982
1989
  ] });
1983
1990
  }
1984
- function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
1991
+ function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status, autoStartCamera = false }) {
1985
1992
  const file = (0, import_react10.useRef)(null);
1986
1993
  const canScan = useCanScan();
1987
1994
  const [scanning, setScanning] = (0, import_react10.useState)(false);
1995
+ const autoStarted = (0, import_react10.useRef)(false);
1996
+ (0, import_react10.useEffect)(() => {
1997
+ if (!autoStartCamera || !canScan || disabled || autoStarted.current || value) return;
1998
+ autoStarted.current = true;
1999
+ setScanning(true);
2000
+ }, [autoStartCamera, canScan, disabled, value]);
1988
2001
  const [reading, setReading] = (0, import_react10.useState)(false);
1989
2002
  const [imageError, setImageError] = (0, import_react10.useState)(null);
1990
2003
  const [dragging, setDragging] = (0, import_react10.useState)(false);
@@ -2290,7 +2303,7 @@ function normalizeWebsite(raw) {
2290
2303
  return null;
2291
2304
  }
2292
2305
  }
2293
- function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onDone }) {
2306
+ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onAdded, onDone }) {
2294
2307
  const confirmed = useConfirmed();
2295
2308
  const candidates = items.filter((i) => !i.authenticator && !i.deletion && (i.capabilities.canEdit || i.capabilities.canManage)).sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }));
2296
2309
  const [input, setInput] = (0, import_react12.useState)("");
@@ -2343,6 +2356,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2343
2356
  setInput("");
2344
2357
  setImage(null);
2345
2358
  setSaved(next);
2359
+ onAdded?.(next);
2346
2360
  onChanged();
2347
2361
  } catch (e) {
2348
2362
  setError(wasCancelled(e) ? "Not confirmed, so nothing was added." : otpActionError(e, "add it"));
@@ -2369,6 +2383,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2369
2383
  label: "Setup key",
2370
2384
  value: input,
2371
2385
  autoFocus: true,
2386
+ autoStartCamera: true,
2372
2387
  disabled: busy,
2373
2388
  onChange: (v) => {
2374
2389
  setImage(null);
@@ -2450,7 +2465,23 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
2450
2465
  const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
2451
2466
  return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
2452
2467
  }
2453
- function AuthenticatorView({ port, host, items, definitions, query, onOpen, onChanged, header }) {
2468
+ function withAdded(items, added) {
2469
+ if (added.size === 0) return items;
2470
+ const seen = /* @__PURE__ */ new Set();
2471
+ const out = items.map((i) => {
2472
+ seen.add(i.id);
2473
+ const mine = added.get(i.id);
2474
+ return mine && !i.authenticator ? { ...i, authenticator: mine.authenticator, name: mine.name } : i;
2475
+ });
2476
+ for (const [id, a] of added) if (!seen.has(id)) out.push(a);
2477
+ return out;
2478
+ }
2479
+ function AuthenticatorView({ port, host, items: listed, definitions, query, onOpen, onChanged, header }) {
2480
+ const [added, setAdded] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
2481
+ const items = withAdded(listed, added);
2482
+ (0, import_react13.useEffect)(() => {
2483
+ if (added.size > 0 && [...added.keys()].every((id) => listed.some((i) => i.id === id && i.authenticator))) setAdded(/* @__PURE__ */ new Map());
2484
+ }, [listed, added]);
2454
2485
  const [entries, setEntries] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
2455
2486
  const [hidden, setHidden] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
2456
2487
  const [adding, setAdding] = (0, import_react13.useState)(false);
@@ -2466,7 +2497,7 @@ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onCh
2466
2497
  return () => {
2467
2498
  live = false;
2468
2499
  };
2469
- }, [port, items]);
2500
+ }, [port, listed, added]);
2470
2501
  const groups = otpGroups(items, entries, query, hidden);
2471
2502
  const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
2472
2503
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
@@ -2512,6 +2543,7 @@ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onCh
2512
2543
  definitions,
2513
2544
  onClose: () => setAdding(false),
2514
2545
  onChanged,
2546
+ onAdded: (item) => setAdded((cur) => new Map(cur).set(item.id, item)),
2515
2547
  onDone: (id) => {
2516
2548
  setAdding(false);
2517
2549
  onOpen(id);
@@ -3918,7 +3950,7 @@ var MAX_FAILURES_IN_A_ROW = 5;
3918
3950
  var failureText = (cause) => cause instanceof Error && cause.message ? cause.message : "The server refused this one.";
3919
3951
  async function runImport(input) {
3920
3952
  const { io, organizationId, lines, keys, confirmed } = input;
3921
- const result = { created: [], failed: [], withOtp: 0, stoppedEarly: false, untried: [] };
3953
+ const result = { created: [], failed: [], withOtp: 0, needOtp: [], stoppedEarly: false, untried: [] };
3922
3954
  let streak = 0;
3923
3955
  for (let i = 0; i < lines.length; i += 1) {
3924
3956
  const line = lines[i];
@@ -3937,7 +3969,10 @@ async function runImport(input) {
3937
3969
  try {
3938
3970
  const made = await confirmed(() => io.createImported(command.body, organizationId, key));
3939
3971
  result.created.push(made);
3940
- if (command.hasOtp) result.withOtp += 1;
3972
+ if (command.hasOtp) {
3973
+ result.withOtp += 1;
3974
+ result.needOtp.push(made);
3975
+ }
3941
3976
  streak = 0;
3942
3977
  } catch (cause) {
3943
3978
  const reason = failureText(cause);
@@ -4537,8 +4572,9 @@ var STATE_BADGE = {
4537
4572
  unsupported: { text: "Not supported", tone: "neutral" }
4538
4573
  };
4539
4574
  var ME = "me";
4540
- function ImportPanel({ port, host, items, organizations, onClose, onChanged, limits }) {
4575
+ function ImportPanel({ port, host, items, definitions, organizations, onClose, onChanged, limits }) {
4541
4576
  const confirmed = useConfirmed();
4577
+ const [otpFor, setOtpFor] = (0, import_react20.useState)(null);
4542
4578
  const fileInput = (0, import_react20.useRef)(null);
4543
4579
  const abort = (0, import_react20.useRef)(null);
4544
4580
  const stop = (0, import_react20.useRef)(false);
@@ -4646,6 +4682,7 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4646
4682
  failed: result.failed,
4647
4683
  skipped,
4648
4684
  withOtp: (previous?.withOtp ?? 0) + result.withOtp,
4685
+ needOtp: [...previous?.needOtp ?? [], ...result.needOtp],
4649
4686
  untried: result.untried
4650
4687
  };
4651
4688
  setDone(merged);
@@ -4671,7 +4708,7 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4671
4708
  setRunning({ done: 0, total: chosen });
4672
4709
  try {
4673
4710
  const made = await confirmed(() => port.io.importEnv(text, organizationId, injectSandbox));
4674
- setDone({ created: made, failed: [], skipped: draft.entries.length - chosen, withOtp: 0, untried: [] });
4711
+ setDone({ created: made, failed: [], skipped: draft.entries.length - chosen, withOtp: 0, needOtp: [], untried: [] });
4675
4712
  if (made.length > 0) onChanged();
4676
4713
  } catch (cause) {
4677
4714
  setError(cause instanceof Error ? cause.message : "The import could not finish.");
@@ -4786,9 +4823,32 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
4786
4823
  plural(done.withOtp, "login"),
4787
4824
  " still need the one-time password added."
4788
4825
  ] }) }) : null,
4826
+ done.needOtp.map((made) => {
4827
+ const now = items.find((i) => i.id === made.id);
4828
+ if (now?.authenticator) return null;
4829
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Authenticator", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { type: "button", className: "truncate text-left text-primary hover:underline", onClick: () => setOtpFor(made.id), children: [
4830
+ "Set up authenticator for ",
4831
+ made.name
4832
+ ] }) }, `otp:${made.id}`);
4833
+ }),
4789
4834
  done.failed.map((f) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: f.title, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
4790
4835
  ] }) : null,
4791
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error })
4836
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error }),
4837
+ otpFor ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4838
+ AddOtpPanel,
4839
+ {
4840
+ port,
4841
+ items,
4842
+ definitions,
4843
+ itemId: otpFor,
4844
+ onClose: () => setOtpFor(null),
4845
+ onChanged,
4846
+ onDone: () => {
4847
+ setOtpFor(null);
4848
+ onChanged();
4849
+ }
4850
+ }
4851
+ ) : null
4792
4852
  ] });
4793
4853
  }
4794
4854
  function toggle(set, key, on) {
@@ -5208,14 +5268,12 @@ var import_confirm_opener7 = require("@ai-matrx/kit/confirm-opener");
5208
5268
  var import_jsx_runtime22 = require("react/jsx-runtime");
5209
5269
  var HANDLINGS = ["visible", "revealable", "sealed"];
5210
5270
  var hint = (text, tone = "muted") => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
5211
- var hasSecretSettings = (s) => Boolean(s.envKey?.trim() || s.injectIntoSandbox || s.handling && s.handling !== "revealable");
5212
- function SecretBuilderRow({ field, onChange, onRemove }) {
5213
- const [open, setOpen] = (0, import_react23.useState)(hasSecretSettings(field));
5214
- const env = field.envKey ?? "";
5271
+ var saysMore = (s, defaultHandling) => Boolean(s.envKey?.trim() || s.injectIntoSandbox || s.handling && s.handling !== defaultHandling);
5272
+ function FieldSettingsRows({ name, value, defaultHandling = "revealable", onChange }) {
5273
+ const env = value.envKey ?? "";
5215
5274
  const envProblem = envKeyProblem(env);
5216
5275
  const envOk = env.trim() !== "" && !envProblem;
5217
- const handling = field.handling ?? "revealable";
5218
- const name = field.label.trim() || "Secret";
5276
+ const handling = value.handling ?? defaultHandling;
5219
5277
  const chooseHandling = async (next) => {
5220
5278
  if (next === handling) return;
5221
5279
  if (next === "sealed") {
@@ -5229,6 +5287,38 @@ function SecretBuilderRow({ field, onChange, onRemove }) {
5229
5287
  }
5230
5288
  onChange({ handling: next });
5231
5289
  };
5290
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5291
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Env key", hint: envProblem ? hint(envProblem, "destructive") : hint("The name sandboxes and workflows use"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5292
+ import_controls18.Field,
5293
+ {
5294
+ className: "w-full font-mono",
5295
+ "aria-label": `${name} env key`,
5296
+ "aria-invalid": Boolean(envProblem),
5297
+ placeholder: "e.g. GITHUB_TOKEN",
5298
+ autoComplete: "off",
5299
+ spellCheck: false,
5300
+ value: env,
5301
+ onChange: (e) => onChange({ envKey: e.target.value, ...e.target.value.trim() ? {} : { injectIntoSandbox: false } })
5302
+ }
5303
+ ) }),
5304
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Who can see it", hint: hint(HANDLING_WORDS[handling].hint), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5305
+ import_controls18.SegmentedControl,
5306
+ {
5307
+ "aria-label": `Who can see ${name}`,
5308
+ value: handling,
5309
+ onValueChange: (v) => void chooseHandling(v),
5310
+ data: HANDLINGS.map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint }))
5311
+ }
5312
+ ) }),
5313
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Use in sandbox", hint: envOk ? hint("Sandboxes get it as an env variable") : hint("Needs an env key"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_controls18.Switch, { "aria-label": `Use ${name} in sandbox`, checked: envOk && value.injectIntoSandbox === true, disabled: !envOk, onCheckedChange: (v) => onChange({ injectIntoSandbox: v }) }) })
5314
+ ] });
5315
+ }
5316
+ var hasSecretSettings = (s) => saysMore(s, "revealable");
5317
+ var hasDeclaredSettings = (s, defaultHandling) => Boolean(s && saysMore(s, defaultHandling));
5318
+ function SecretBuilderRow({ field, onChange, onRemove }) {
5319
+ const [open, setOpen] = (0, import_react23.useState)(hasSecretSettings(field));
5320
+ const handling = field.handling ?? "revealable";
5321
+ const name = field.label.trim() || "Secret";
5232
5322
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5233
5323
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5234
5324
  CustomEditRow,
@@ -5236,47 +5326,26 @@ function SecretBuilderRow({ field, onChange, onRemove }) {
5236
5326
  label: field.label,
5237
5327
  value: field.value,
5238
5328
  secret: true,
5329
+ multiline: field.multiline === true,
5239
5330
  onLabel: (v) => onChange({ label: v }),
5240
5331
  onValue: (v) => onChange({ value: v }),
5241
5332
  onRemove,
5242
- onPasteValue: (text) => {
5243
- const a = parseEnvAssignment(text);
5244
- if (!a) return false;
5245
- onChange({ envKey: a.key, value: a.value, ...field.label.trim() ? {} : { label: labelFromEnvKey(a.key) } });
5246
- setOpen(true);
5247
- return true;
5333
+ ...field.multiline ? {} : {
5334
+ onPasteValue: (text) => {
5335
+ const a = parseEnvAssignment(text);
5336
+ if (!a) return false;
5337
+ onChange({ envKey: a.key, value: a.value, ...field.label.trim() ? {} : { label: labelFromEnvKey(a.key) } });
5338
+ setOpen(true);
5339
+ return true;
5340
+ }
5248
5341
  },
5249
5342
  extra: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5250
- handling !== "sealed" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenerateButton, { onUse: (v) => onChange({ value: v }) }) : null,
5343
+ handling !== "sealed" && !field.multiline ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenerateButton, { onUse: (v) => onChange({ value: v }) }) : null,
5251
5344
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_controls18.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SlidersGlyph, {}), "aria-label": `${name} settings`, pressed: open, onClick: () => setOpen((o) => !o) })
5252
5345
  ] })
5253
5346
  }
5254
5347
  ),
5255
- open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5256
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Env key", hint: envProblem ? hint(envProblem, "destructive") : hint("The name sandboxes and workflows use"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5257
- import_controls18.Field,
5258
- {
5259
- className: "w-full font-mono",
5260
- "aria-label": `${name} env key`,
5261
- "aria-invalid": Boolean(envProblem),
5262
- placeholder: "e.g. GITHUB_TOKEN",
5263
- autoComplete: "off",
5264
- spellCheck: false,
5265
- value: env,
5266
- onChange: (e) => onChange({ envKey: e.target.value, ...e.target.value.trim() ? {} : { injectIntoSandbox: false } })
5267
- }
5268
- ) }),
5269
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Who can see it", hint: hint(HANDLING_WORDS[handling].hint), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5270
- import_controls18.SegmentedControl,
5271
- {
5272
- "aria-label": `Who can see ${name}`,
5273
- value: handling,
5274
- onValueChange: (v) => void chooseHandling(v),
5275
- data: HANDLINGS.map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint }))
5276
- }
5277
- ) }),
5278
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Use in sandbox", hint: envOk ? hint("Sandboxes get it as an env variable") : hint("Needs an env key"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_controls18.Switch, { "aria-label": `Use ${name} in sandbox`, checked: envOk && field.injectIntoSandbox === true, disabled: !envOk, onCheckedChange: (v) => onChange({ injectIntoSandbox: v }) }) })
5279
- ] }) : null
5348
+ open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FieldSettingsRows, { name, value: field, onChange }) : null
5280
5349
  ] });
5281
5350
  }
5282
5351
  function withMfaMethod(custom, v) {
@@ -5906,6 +5975,8 @@ function SharingSummary({ item, port, className }) {
5906
5975
  // src/ItemPane.tsx
5907
5976
  var import_format8 = require("@ai-matrx/kit/format");
5908
5977
  var import_jsx_runtime27 = require("react/jsx-runtime");
5978
+ var ENV_VALUE_KEY = "env_value";
5979
+ var secureNoteDraft = () => ({ key: SECURE_NOTE_KEY, label: "Secure note", value: "", multiline: true, description: "Encrypted notes" });
5909
5980
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
5910
5981
  function ItemPane(props) {
5911
5982
  const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
@@ -6096,6 +6167,16 @@ function ItemPane(props) {
6096
6167
  const fieldSettings = (f) => canEdit ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SlidersGlyph, {}), "aria-label": `${f.label} settings`, title: `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }) : null;
6097
6168
  const turnOnOne = canTurnOn ? (f) => () => turnOn([f]) : () => void 0;
6098
6169
  const declaredKeys = new Set(declared.map((f) => f.key));
6170
+ const hasSecureNote = declaredKeys.has(SECURE_NOTE_KEY) || Boolean(item?.fields.some((f) => f.key === SECURE_NOTE_KEY)) || draft.secrets.some((x) => x.key === SECURE_NOTE_KEY);
6171
+ const [settingsToggled, setSettingsToggled] = (0, import_react28.useState)(/* @__PURE__ */ new Set());
6172
+ const settingsOpen = (f) => settingsToggled.has(f.key) !== (definition?.key === ENV_VALUE_KEY || hasDeclaredSettings(draft.fieldSettings?.[f.key], f.handling));
6173
+ const toggleSettings = (key) => setSettingsToggled((cur) => {
6174
+ const next = new Set(cur);
6175
+ if (next.has(key)) next.delete(key);
6176
+ else next.add(key);
6177
+ return next;
6178
+ });
6179
+ const patchFieldSettings = (key, p) => setDraft((d) => ({ ...d, fieldSettings: { ...d.fieldSettings, [key]: { ...d.fieldSettings?.[key], ...p } } }));
6099
6180
  const extraFields = (item?.fields ?? []).filter((f) => !declaredKeys.has(f.key) && !draft.removed.includes(f.id) && !isAuthenticatorStorage(f.key));
6100
6181
  const metaValue = (key) => draft.values[key] ?? item?.custom.find((c) => c.key === key)?.value ?? "";
6101
6182
  const extraCustom = draft.custom.filter((c) => !declaredKeys.has(c.key) && c.key !== MFA_METHOD_KEY && !isAuthenticatorStorage(c.key));
@@ -6228,24 +6309,29 @@ function ItemPane(props) {
6228
6309
  const secretKind = f.storage === "encrypted" && f.kind === "secret";
6229
6310
  const value = f.storage === "metadata" ? metaValue(f.key) : draft.values[f.key] ?? "";
6230
6311
  const hidden = existing && existing.handling !== "visible" && !(f.key in loaded);
6231
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6232
- EditRow,
6233
- {
6234
- label: f.label,
6235
- value,
6236
- placeholder: hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : f.required ? "Required" : isRecoveryCodesField(f) ? "Backup codes, one per line" : "",
6237
- secret: secretKind,
6238
- multiline: f.kind === "multiline",
6239
- inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
6240
- onChange: (v) => setValue(f.key, v),
6241
- actions: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6242
- hidden && existing?.handling === "revealable" && item?.capabilities.canReveal ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
6243
- secretKind ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
6244
- existing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_icons11.SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null
6245
- ] })
6246
- },
6247
- f.key
6248
- );
6312
+ const newSecret = mode === "new" && f.storage === "encrypted" && owner !== "person";
6313
+ const open = newSecret && settingsOpen(f);
6314
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react28.Fragment, { children: [
6315
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6316
+ EditRow,
6317
+ {
6318
+ label: f.label,
6319
+ value,
6320
+ placeholder: hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : f.required ? "Required" : isRecoveryCodesField(f) ? "Backup codes, one per line" : "",
6321
+ secret: secretKind,
6322
+ multiline: f.kind === "multiline" || value.includes("\n"),
6323
+ inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
6324
+ onChange: (v) => setValue(f.key, v),
6325
+ actions: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6326
+ hidden && existing?.handling === "revealable" && item?.capabilities.canReveal ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
6327
+ secretKind ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
6328
+ existing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_icons11.SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null,
6329
+ newSecret ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SlidersGlyph, {}), "aria-label": `${f.label} settings`, pressed: open, onClick: () => toggleSettings(f.key) }) : null
6330
+ ] })
6331
+ }
6332
+ ),
6333
+ open ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldSettingsRows, { name: f.label, value: draft.fieldSettings?.[f.key] ?? {}, defaultHandling: f.handling, onChange: (p) => patchFieldSettings(f.key, p) }) : null
6334
+ ] }, f.key);
6249
6335
  }),
6250
6336
  extraFields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
6251
6337
  EditRow,
@@ -6254,7 +6340,7 @@ function ItemPane(props) {
6254
6340
  value: draft.values[f.key] ?? "",
6255
6341
  placeholder: f.handling === "visible" || f.key in loaded ? "" : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
6256
6342
  secret: f.handling !== "visible" && !isRecoveryCodesField(f),
6257
- multiline: isRecoveryCodesField(f),
6343
+ multiline: f.kind === "multiline" || isRecoveryCodesField(f) || (draft.values[f.key] ?? "").includes("\n"),
6258
6344
  onChange: (v) => setValue(f.key, v),
6259
6345
  onRemove: () => patch({ removed: [...draft.removed, f.id] }),
6260
6346
  removeLabel: `Delete ${f.label}`,
@@ -6323,6 +6409,7 @@ function ItemPane(props) {
6323
6409
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PlusGlyph, {}), children: "Add" }) }),
6324
6410
  /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuContent, { align: "end", children: [
6325
6411
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => patch({ secrets: [...draft.secrets, { key: "", label: "", value: "" }] }), children: "Secret field" }),
6412
+ !hasSecureNote ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => patch({ secrets: [...draft.secrets, secureNoteDraft()] }), children: "Secure note" }) : null,
6326
6413
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => patch({ custom: [...draft.custom, { key: `custom_${draft.custom.length + 1}_${Date.now() % 1e5}`, label: "", value: "" }] }), children: "Text field" }),
6327
6414
  !tagsVisible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => setShowTags(true), children: "Tags" }) : null,
6328
6415
  !descriptionVisible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => setShowDescription(true), children: "Description" }) : null,
@@ -6410,6 +6497,13 @@ function ItemPane(props) {
6410
6497
 
6411
6498
  // src/ItemList.tsx
6412
6499
  var import_jsx_runtime28 = require("react/jsx-runtime");
6500
+ var RECOVERY_KEYS = /^recovery_codes?$/;
6501
+ function otherCopyFields(item, user, secret) {
6502
+ if (!item.capabilities.canUse) return [];
6503
+ return item.fields.filter(
6504
+ (f) => f !== user && f !== secret && f.active !== false && f.handling !== "sealed" && (f.handling === "visible" || item.capabilities.canReveal) && !isAuthenticatorStorage(f.key) && !RECOVERY_KEYS.test(f.key)
6505
+ );
6506
+ }
6413
6507
  var SORTS2 = [
6414
6508
  { value: "name", label: "Name A\u2013Z" },
6415
6509
  { value: "name-desc", label: "Name Z\u2013A" },
@@ -6422,7 +6516,7 @@ function subtitleOf(item, withOwner) {
6422
6516
  const what = loginSiteOf(item) || item.definitionLabel;
6423
6517
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
6424
6518
  }
6425
- function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false }) {
6519
+ function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems }) {
6426
6520
  const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
6427
6521
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
6428
6522
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
@@ -6443,7 +6537,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6443
6537
  }
6444
6538
  };
6445
6539
  const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
6446
- const row = (i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners }, i.id);
6540
+ const row = (i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners, ownContextMenu: rowContextMenu === "package", extra: rowMenuItems }, i.id);
6447
6541
  const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
6448
6542
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
6449
6543
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
@@ -6458,9 +6552,12 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6458
6552
  " ",
6459
6553
  items.length === 1 ? "item" : "items"
6460
6554
  ] }),
6461
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { children: [
6462
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SortGlyph, {}), "aria-label": `Sort: ${sortLabel}`, children: sortLabel }) }),
6463
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuContent, { align: "end", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuRadioGroup, { value: sort, onValueChange: (v) => onSort(sorts.find((s) => s.value === v)?.value ?? "name"), children: sorts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuRadioItem, { value: s.value, children: s.label }, s.value)) }) })
6555
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex shrink-0 items-center gap-0.5", children: [
6556
+ onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RefreshGlyph, { className: refreshing ? "animate-spin" : "" }), "aria-label": "Refresh", title: "Refresh the list", disabled: refreshing, onClick: onRefresh }) : null,
6557
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { children: [
6558
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SortGlyph, {}), "aria-label": `Sort: ${sortLabel}`, children: sortLabel }) }),
6559
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuContent, { align: "end", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuRadioGroup, { value: sort, onValueChange: (v) => onSort(sorts.find((s) => s.value === v)?.value ?? "name"), children: sorts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuRadioItem, { value: s.value, children: s.label }, s.value)) }) })
6560
+ ] })
6464
6561
  ] })
6465
6562
  ] })
6466
6563
  ] }),
@@ -6475,7 +6572,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6475
6572
  ] }) })
6476
6573
  ] });
6477
6574
  }
6478
- function ListRow({ item, family, port, on, onSelect, withOwner }) {
6575
+ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra }) {
6479
6576
  const confirmed = useConfirmed();
6480
6577
  const copy = useCopy();
6481
6578
  const notify = useNotify();
@@ -6500,67 +6597,93 @@ function ListRow({ item, family, port, on, onSelect, withOwner }) {
6500
6597
  const copyPassword = () => secret && void run("Password", () => port.reveal(item, secret.key), true);
6501
6598
  const copyCode = () => code && void run("One-time code", async () => (await port.authenticator.code(item)).code, true);
6502
6599
  const copyName = () => void copy(item.name, "Name", { secret: false });
6503
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "group relative", "data-item": item.id, children: [
6504
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
6505
- "button",
6506
- {
6507
- type: "button",
6508
- "aria-current": on ? "true" : void 0,
6509
- "data-surface": on ? "primary" : void 0,
6510
- onClick: () => onSelect(item.id),
6511
- className: (0, import_design_system20.cn)("flex h-12 w-full items-center gap-3 rounded-lg px-2 pr-10 text-left", on ? "bg-primary text-primary-foreground" : "hover:bg-accent"),
6512
- children: [
6513
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item), inverted: on }),
6514
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
6515
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm font-medium", children: item.name }),
6516
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: subtitleOf(item, withOwner) })
6517
- ] })
6518
- ]
6519
- }
6520
- ),
6521
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-0.5", "data-surface": on ? "primary" : void 0, children: [
6522
- user || secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system20.cn)("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
6523
- user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
6524
- secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
6525
- ] }) : null,
6526
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [
6527
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6528
- import_controls23.Button,
6600
+ const others = otherCopyFields(item, user, secret);
6601
+ const custom = canUse ? item.custom.filter((c) => c.value) : [];
6602
+ const hostEntries = extra?.(item) ?? [];
6603
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
6604
+ "li",
6605
+ {
6606
+ className: "group relative",
6607
+ "data-item": item.id,
6608
+ onContextMenu: ownContextMenu ? (e) => {
6609
+ e.preventDefault();
6610
+ setMenuOpen(true);
6611
+ } : void 0,
6612
+ children: [
6613
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
6614
+ "button",
6529
6615
  {
6530
- variant: "quiet",
6531
- icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MoreGlyph, {}),
6532
- "aria-label": `Actions for ${item.name}`,
6533
- disabled: busy,
6534
- className: (0, import_design_system20.cn)("[@media(hover:hover)]:invisible [@media(hover:hover)]:group-hover:visible [@media(hover:hover)]:group-focus-within:visible", menuOpen && "[@media(hover:hover)]:visible")
6616
+ type: "button",
6617
+ "aria-current": on ? "true" : void 0,
6618
+ "data-surface": on ? "primary" : void 0,
6619
+ onClick: () => onSelect(item.id),
6620
+ className: (0, import_design_system20.cn)("flex h-12 w-full items-center gap-3 rounded-lg px-2 pr-10 text-left", on ? "bg-primary text-primary-foreground" : "hover:bg-accent"),
6621
+ children: [
6622
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item), inverted: on }),
6623
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
6624
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm font-medium", children: item.name }),
6625
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: subtitleOf(item, withOwner) })
6626
+ ] })
6627
+ ]
6535
6628
  }
6536
- ) }),
6537
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuContent, { align: "end", children: [
6538
- user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyUser, children: [
6539
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }),
6540
- "Copy ",
6541
- user.label.toLowerCase()
6542
- ] }) : null,
6543
- secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
6544
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
6545
- "Copy password"
6546
- ] }) : null,
6547
- code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
6548
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
6549
- "Copy one-time code"
6629
+ ),
6630
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-0.5", "data-surface": on ? "primary" : void 0, children: [
6631
+ user || secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system20.cn)("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
6632
+ user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
6633
+ secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
6550
6634
  ] }) : null,
6551
- user || secret || code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6552
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => onSelect(item.id), children: [
6553
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }),
6554
- "Open"
6555
- ] }),
6556
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyName, children: [
6557
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6558
- "Copy name"
6635
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [
6636
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6637
+ import_controls23.Button,
6638
+ {
6639
+ variant: "quiet",
6640
+ icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MoreGlyph, {}),
6641
+ "aria-label": `Actions for ${item.name}`,
6642
+ disabled: busy,
6643
+ className: (0, import_design_system20.cn)("[@media(hover:hover)]:invisible [@media(hover:hover)]:group-hover:visible [@media(hover:hover)]:group-focus-within:visible", menuOpen && "[@media(hover:hover)]:visible")
6644
+ }
6645
+ ) }),
6646
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuContent, { align: "end", children: [
6647
+ user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyUser, children: [
6648
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }),
6649
+ "Copy ",
6650
+ user.label.toLowerCase()
6651
+ ] }) : null,
6652
+ secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
6653
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
6654
+ "Copy password"
6655
+ ] }) : null,
6656
+ code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
6657
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
6658
+ "Copy one-time code"
6659
+ ] }) : null,
6660
+ others.map((f) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void run(f.label, () => port.reveal(item, f.key), f.handling !== "visible"), children: [
6661
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6662
+ "Copy ",
6663
+ f.label.toLowerCase()
6664
+ ] }, f.key)),
6665
+ custom.map((c) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void copy(c.value, c.label, { secret: false }), children: [
6666
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6667
+ "Copy ",
6668
+ c.label.toLowerCase()
6669
+ ] }, `custom:${c.key}`)),
6670
+ user || secret || code || others.length > 0 || custom.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6671
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => onSelect(item.id), children: [
6672
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }),
6673
+ "Open"
6674
+ ] }),
6675
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyName, children: [
6676
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6677
+ "Copy name"
6678
+ ] }),
6679
+ hostEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6680
+ hostEntries.map((h) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuItem, { onSelect: h.onSelect, children: h.label }, h.label))
6681
+ ] })
6559
6682
  ] })
6560
6683
  ] })
6561
- ] })
6562
- ] })
6563
- ] });
6684
+ ]
6685
+ }
6686
+ );
6564
6687
  }
6565
6688
 
6566
6689
  // src/lists/TrashView.tsx
@@ -8187,6 +8310,7 @@ var import_jsx_runtime40 = require("react/jsx-runtime");
8187
8310
  var FULL_AT = 1120;
8188
8311
  var SPLIT_AT = 640;
8189
8312
  var NO_HOST = {};
8313
+ var FOCUS_GAP_MS = 2e3;
8190
8314
  var ITEM_PANELS = {
8191
8315
  share: SharePanel,
8192
8316
  move: MovePanel,
@@ -8315,9 +8439,13 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8315
8439
  const knobsRef = (0, import_react42.useRef)(host.knobs);
8316
8440
  knobsRef.current = host.knobs;
8317
8441
  const [signedOut, setSignedOut] = (0, import_react42.useState)(false);
8318
- const load = (0, import_react42.useCallback)(() => {
8319
- setError(null);
8320
- port.load().then((s) => {
8442
+ const [refreshError, setRefreshError] = (0, import_react42.useState)(null);
8443
+ const [refreshing, setRefreshing] = (0, import_react42.useState)(false);
8444
+ const loadedAt = (0, import_react42.useRef)(0);
8445
+ const inFlight = (0, import_react42.useRef)(false);
8446
+ const apply = (0, import_react42.useCallback)(
8447
+ (s) => {
8448
+ loadedAt.current = Date.now();
8321
8449
  setSnap(s);
8322
8450
  setItems(s.items);
8323
8451
  setDeleted(s.deleted);
@@ -8325,15 +8453,71 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8325
8453
  (rows) => setDeleted([...s.deleted, ...rows.filter((r) => !s.deleted.some((d) => d.id === r.id))]),
8326
8454
  () => void 0
8327
8455
  );
8328
- }).catch((e) => {
8456
+ },
8457
+ [port]
8458
+ );
8459
+ const refresh = (0, import_react42.useCallback)(() => {
8460
+ if (inFlight.current) return;
8461
+ inFlight.current = true;
8462
+ setRefreshing(true);
8463
+ port.load().then((s) => {
8464
+ setRefreshError(null);
8465
+ apply(s);
8466
+ }).catch((e) => setRefreshError(plainLoadError(e instanceof Error && e.message ? e.message : "The vault could not be read."))).finally(() => {
8467
+ inFlight.current = false;
8468
+ setRefreshing(false);
8469
+ });
8470
+ port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
8471
+ port.lists.recents?.list().then(setRecents, () => void 0);
8472
+ }, [port, apply]);
8473
+ const load = (0, import_react42.useCallback)(() => {
8474
+ setError(null);
8475
+ setRefreshError(null);
8476
+ port.load().then(apply).catch((e) => {
8329
8477
  setSignedOut(e?.status === 401);
8330
8478
  setError(e instanceof Error ? e.message : "The vault could not be loaded.");
8331
8479
  });
8332
8480
  port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
8333
8481
  knobsRef.current?.().catch(() => void 0);
8334
8482
  port.lists.recents?.list().then(setRecents, () => void 0);
8335
- }, [port]);
8483
+ }, [port, apply]);
8336
8484
  (0, import_react42.useEffect)(load, [load]);
8485
+ const quietRef = (0, import_react42.useRef)({ loc, ready: false });
8486
+ quietRef.current = { loc, ready: snap !== null && !error };
8487
+ const [knobSeconds, setKnobSeconds] = (0, import_react42.useState)(null);
8488
+ (0, import_react42.useEffect)(() => {
8489
+ if (host.refreshSeconds !== void 0 || !knobsRef.current) return;
8490
+ let live = true;
8491
+ knobsRef.current().then(
8492
+ (r) => {
8493
+ const v = r["vault.list.refresh_seconds"];
8494
+ if (live && typeof v === "number" && v > 0) setKnobSeconds(v);
8495
+ },
8496
+ () => void 0
8497
+ );
8498
+ return () => {
8499
+ live = false;
8500
+ };
8501
+ }, [host.refreshSeconds]);
8502
+ const everySeconds = host.refreshSeconds ?? knobSeconds;
8503
+ (0, import_react42.useEffect)(() => {
8504
+ const quietly = (minGapMs) => {
8505
+ const { loc: here, ready } = quietRef.current;
8506
+ if (!ready || document.visibilityState === "hidden" || dirty.current || here.mode !== "view") return;
8507
+ if (Date.now() - loadedAt.current < minGapMs) return;
8508
+ refresh();
8509
+ };
8510
+ const onBack = () => quietly(FOCUS_GAP_MS);
8511
+ const onVisible = () => document.visibilityState === "visible" && onBack();
8512
+ window.addEventListener("focus", onBack);
8513
+ document.addEventListener("visibilitychange", onVisible);
8514
+ const timer2 = everySeconds && everySeconds > 0 ? setInterval(() => quietly(0), everySeconds * 1e3) : null;
8515
+ return () => {
8516
+ window.removeEventListener("focus", onBack);
8517
+ document.removeEventListener("visibilitychange", onVisible);
8518
+ if (timer2) clearInterval(timer2);
8519
+ };
8520
+ }, [refresh, everySeconds]);
8337
8521
  const inventoryRef = (0, import_react42.useRef)(onInventory);
8338
8522
  inventoryRef.current = onInventory;
8339
8523
  (0, import_react42.useEffect)(() => {
@@ -8499,6 +8683,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8499
8683
  onSort: (sort) => go({ sort }, "replace"),
8500
8684
  canSortViewed: port.lists.recents !== void 0,
8501
8685
  onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
8686
+ onRefresh: refresh,
8687
+ refreshing,
8688
+ rowContextMenu: host.rowContextMenu ?? "package",
8689
+ ...host.rowMenuItems ? { rowMenuItems: host.rowMenuItems } : {},
8502
8690
  ...matchingIds ? { matchingIds } : {},
8503
8691
  ...compactHeader ? { header: compactHeader } : {}
8504
8692
  }
@@ -8523,14 +8711,14 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8523
8711
  host,
8524
8712
  organizations: orgs,
8525
8713
  defaultOwner: ownerDefault,
8526
- seed: { name: guess?.name ?? "", url: guess?.url ?? loc.url.trim() },
8714
+ seed: { name: loc.name?.trim() || guess?.name || "", url: guess?.url ?? loc.url.trim() },
8527
8715
  onBack: () => go({ kind: null }),
8528
8716
  onMode: () => void 0,
8529
8717
  onSaved: (item, _wasNew, n, enrolled) => {
8530
8718
  replaceItem(item);
8531
8719
  dirty.current = false;
8532
8720
  if (enrolled) setFirstCode(item);
8533
- move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", panel: null }, "replace");
8721
+ move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", name: "", panel: null }, "replace");
8534
8722
  setNotice(n);
8535
8723
  notify("Saved");
8536
8724
  },
@@ -8538,7 +8726,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8538
8726
  onDirty: (d) => {
8539
8727
  dirty.current = d;
8540
8728
  },
8541
- onCancelNew: () => go({ mode: "view", kind: null, url: "", itemId: null }),
8729
+ onCancelNew: () => go({ mode: "view", kind: null, url: "", name: "", itemId: null }),
8542
8730
  onPanel: () => void 0,
8543
8731
  onChanged: () => void 0
8544
8732
  },
@@ -8629,13 +8817,17 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8629
8817
  const grid = mode === "full" ? "grid-cols-[15rem_21rem_minmax(0,1fr)]" : mode === "split" ? "grid-cols-[19rem_minmax(0,1fr)]" : "grid-cols-1";
8630
8818
  const shown = panesShown(mode, specialView !== null, showingDetail);
8631
8819
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ref: rootRef, className: "flex h-full min-h-0 w-full flex-col bg-background text-foreground", children: [
8820
+ refreshError ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-4 py-2 text-sm", children: [
8821
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "min-w-0 flex-1 truncate text-warning", title: refreshError, children: "Couldn\u2019t refresh. Showing what was loaded." }),
8822
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
8823
+ ] }) : null,
8632
8824
  snap.unavailable.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-4 py-2 text-sm", children: [
8633
8825
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "min-w-0 flex-1 truncate text-warning", children: [
8634
8826
  "Couldn\u2019t load ",
8635
8827
  snap.unavailable.join(", "),
8636
8828
  ". Showing the rest."
8637
8829
  ] }),
8638
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: load, children: "Try again" })
8830
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
8639
8831
  ] }) : null,
8640
8832
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: `grid min-h-0 w-full flex-1 ${grid} overflow-hidden`, children: [
8641
8833
  mode === "full" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
@@ -8986,6 +9178,28 @@ function trashItem(r, ctx) {
8986
9178
  createdAt: ""
8987
9179
  };
8988
9180
  }
9181
+ async function withTypes(items, door, ctx) {
9182
+ if (!door.schema || items.length === 0) return items;
9183
+ const keys = /* @__PURE__ */ new Map();
9184
+ const CHUNK = 100;
9185
+ try {
9186
+ for (let i = 0; i < items.length; i += CHUNK) {
9187
+ const ids = items.slice(i, i + CHUNK).map((x) => x.id);
9188
+ const { data, error } = await door.schema("users").from("credential_items").select("id,definition_key").in("id", ids);
9189
+ if (error) throw new Error(error.message || "credential_items read failed");
9190
+ for (const r of Array.isArray(data) ? data : []) {
9191
+ if (typeof r.id === "string" && typeof r.definition_key === "string" && r.definition_key) keys.set(r.id, r.definition_key);
9192
+ }
9193
+ }
9194
+ } catch (e) {
9195
+ console.warn("[vault] Trash item types could not be read; rows say Credential", e);
9196
+ return items;
9197
+ }
9198
+ return items.map((it) => {
9199
+ const key = keys.get(it.id);
9200
+ return key ? { ...it, definitionKey: key, definitionLabel: ctx.definition(key)?.label ?? humanize(key) } : it;
9201
+ });
9202
+ }
8989
9203
  var previewOf = async (door, id) => parseRecoveryPreview(await call(door, "vault_recovery_preview", { p_id: id }));
8990
9204
  function createListsPort(ctx, door) {
8991
9205
  return {
@@ -9018,7 +9232,8 @@ function createListsPort(ctx, door) {
9018
9232
  if (rows.length < PAGE2) break;
9019
9233
  }
9020
9234
  const items = all.map((r) => trashItem(r, ctx)).filter((i) => i !== null);
9021
- return [...new Map(items.map((i) => [i.id, i])).values()];
9235
+ const unique = [...new Map(items.map((i) => [i.id, i])).values()];
9236
+ return withTypes(unique, door, ctx);
9022
9237
  }
9023
9238
  },
9024
9239
  favorites: {
@@ -9041,16 +9256,25 @@ function createListsPort(ctx, door) {
9041
9256
  var account6 = __toESM(require("@ai-matrx/api/account"), 1);
9042
9257
 
9043
9258
  // src/secret-field-in.ts
9044
- function secretFieldIn(s) {
9259
+ function settingsIn(s) {
9045
9260
  const envKey = s.envKey?.trim() || null;
9046
9261
  return {
9047
- field_key: fieldKeyOf(s.label),
9048
- value: s.value,
9049
- handling: s.handling ?? "revealable",
9050
9262
  ...envKey ? { env_key: envKey } : {},
9051
9263
  ...envKey && s.injectIntoSandbox ? { inject_into_sandbox: true } : {}
9052
9264
  };
9053
9265
  }
9266
+ function secretFieldIn(s) {
9267
+ return {
9268
+ field_key: s.key.trim() || fieldKeyOf(s.label),
9269
+ value: s.value,
9270
+ handling: s.handling ?? "revealable",
9271
+ ...s.description ? { description: s.description } : {},
9272
+ ...settingsIn(s)
9273
+ };
9274
+ }
9275
+ function declaredFieldIn(f, value, settings) {
9276
+ return { field_key: f.key, value, handling: settings?.handling ?? f.handling, ...settingsIn(settings ?? {}) };
9277
+ }
9054
9278
 
9055
9279
  // src/sharing/api.ts
9056
9280
  function grantOf(w) {
@@ -9151,7 +9375,7 @@ function createSharingPort(ctx, options = {}) {
9151
9375
  initiation: "user",
9152
9376
  non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
9153
9377
  fields: [
9154
- ...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
9378
+ ...encrypted.map((f) => declaredFieldIn(f, draft.values[f.key] ?? "", draft.fieldSettings?.[f.key])),
9155
9379
  ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map(secretFieldIn)
9156
9380
  ]
9157
9381
  }
@@ -9280,7 +9504,7 @@ async function readNativeImportLimits(api, organizationId) {
9280
9504
  var recoveryOf = (w) => w.lifecycle?.vault_recovery;
9281
9505
  var isDeleted = (w) => recoveryOf(w)?.state === "deleted";
9282
9506
  var MATCH_MODES2 = ["host", "domain", "exact", "never"];
9283
- var FIELD_WORDS = { totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
9507
+ var FIELD_WORDS = { secure_notes: "Secure note", totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
9284
9508
  var handlingOf = (v) => v === "visible" || v === "sealed" ? v : "revealable";
9285
9509
  function fieldOf(w, def) {
9286
9510
  const declared = def?.fields.find((f) => f.key === w.field_key);
@@ -9289,7 +9513,8 @@ function fieldOf(w, def) {
9289
9513
  id: w.id,
9290
9514
  key: w.field_key,
9291
9515
  label: declared?.label ?? FIELD_WORDS[w.field_key] ?? humanize(w.field_key),
9292
- kind: declared?.kind ?? (handling === "visible" ? "text" : "secret"),
9516
+ // A field no type declares still edits as several lines when its key says so (an old "Secure note").
9517
+ kind: declared?.kind ?? (isMultilineKey(w.field_key) ? "multiline" : handling === "visible" ? "text" : "secret"),
9293
9518
  handling,
9294
9519
  editable: w.editable ?? true,
9295
9520
  envKey: w.env_key ?? null,
@@ -9444,7 +9669,7 @@ function createApiVaultPort(api, options = {}) {
9444
9669
  initiation: "user",
9445
9670
  non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
9446
9671
  fields: [
9447
- ...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
9672
+ ...encrypted.map((f) => declaredFieldIn(f, draft.values[f.key] ?? "", draft.fieldSettings?.[f.key])),
9448
9673
  ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map(secretFieldIn),
9449
9674
  // Backup codes on a type that does not declare them (a website login): their own encrypted field.
9450
9675
  ...!declared.some((f) => f.key === RECOVERY_CODES_KEY) && (draft.values[RECOVERY_CODES_KEY] ?? "").trim() !== "" ? [{ field_key: RECOVERY_CODES_KEY, value: parseRecoveryCodes(draft.values[RECOVERY_CODES_KEY] ?? "").join("\n"), handling: "revealable", description: "One-time account recovery codes" }] : []