@ai-matrx/vault 0.1.28 → 0.1.30

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
@@ -135,6 +135,7 @@ var init_keepass_xml_worker_client = __esm({
135
135
  // src/index.ts
136
136
  var src_exports = {};
137
137
  __export(src_exports, {
138
+ BACKUP_PASSPHRASE_MIN_KNOB: () => BACKUP_PASSPHRASE_MIN_KNOB,
138
139
  CATEGORIES: () => CATEGORIES,
139
140
  CONFIRM_RETURN_KEY: () => CONFIRM_RETURN_KEY,
140
141
  DEFAULT_LOCATION: () => DEFAULT_LOCATION,
@@ -143,9 +144,11 @@ __export(src_exports, {
143
144
  HOLDINGS_DOOR_MIGRATION: () => HOLDINGS_DOOR_MIGRATION,
144
145
  IdentityGate: () => IdentityGate,
145
146
  LOCKED_MESSAGE: () => LOCKED_MESSAGE,
147
+ REVEAL_SECONDS_KNOB: () => REVEAL_SECONDS_KNOB,
146
148
  VaultApp: () => VaultApp,
147
149
  VaultLockedError: () => VaultLockedError,
148
150
  categoryOf: () => categoryOf,
151
+ copiedLine: () => copiedLine,
149
152
  copyValue: () => copyValue,
150
153
  createApiVaultPort: () => createApiVaultPort,
151
154
  createSupabaseIdentity: () => createSupabaseIdentity,
@@ -162,11 +165,13 @@ __export(src_exports, {
162
165
  matchesQuery: () => matchesQuery,
163
166
  needsRecentSignIn: () => needsRecentSignIn,
164
167
  parseDefinitions: () => parseDefinitions,
168
+ positiveKnob: () => positiveKnob,
165
169
  providerOf: () => providerOf,
166
170
  rowMenuEntriesAt: () => rowMenuEntriesAt,
167
171
  siteDomainOf: () => siteDomainOf,
168
172
  sortItems: () => sortItems,
169
173
  useConfirmed: () => useConfirmed,
174
+ useDisposable: () => useDisposable,
170
175
  vaultInventoryScope: () => vaultInventoryScope,
171
176
  vaultItemFacts: () => vaultItemFacts,
172
177
  wasCancelled: () => wasCancelled
@@ -192,8 +197,8 @@ async function copyValue(text, label, options, deps) {
192
197
  const secret = options.secret ?? true;
193
198
  if (deps.clipboard) {
194
199
  try {
195
- await deps.clipboard.copy(text, { secret });
196
- deps.notify(`${label} copied`, "success");
200
+ const copied = await deps.clipboard.copy(text, { secret });
201
+ deps.notify(copiedLine(label, copied?.clearsAtMs ?? null), "success");
197
202
  return true;
198
203
  } catch (e) {
199
204
  deps.notify(e instanceof Error && e.message ? e.message : `${label} could not be copied`, "error");
@@ -202,6 +207,11 @@ async function copyValue(text, label, options, deps) {
202
207
  }
203
208
  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") });
204
209
  }
210
+ function copiedLine(label, clearsAtMs) {
211
+ if (clearsAtMs === null || !Number.isFinite(clearsAtMs)) return `${label} copied`;
212
+ const at = new Date(clearsAtMs).toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
213
+ return `${label} copied. Clears at ${at}`;
214
+ }
205
215
  var ClipboardCtx = (0, import_react.createContext)(void 0);
206
216
  function ClipboardProvider({ clipboard, children }) {
207
217
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClipboardCtx.Provider, { value: clipboard, children });
@@ -245,27 +255,29 @@ function glyph(paths, air = [1, 1]) {
245
255
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className, style, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: paths });
246
256
  };
247
257
  }
248
- var KeyGlyph = import_icons.KeyIcon;
249
- var NoteGlyph = import_icons.FileTextIcon;
250
- var PersonGlyph = import_icons.UserRoundIcon;
251
- var DatabaseGlyph = import_icons.DatabaseIcon;
252
- var ServerGlyph = import_icons.ServerIcon;
253
- var TerminalGlyph = import_icons.TerminalIcon;
254
- var FileGlyph = import_icons.FilePageIcon;
255
- var CodeGlyph = import_icons.CodeIcon;
256
- var StarGlyph = import_icons.StarIcon;
257
- var PlusGlyph = import_icons.PlusIcon;
258
- var EyeGlyph = import_icons.EyeIcon;
259
- var EyeOffGlyph = import_icons.EyeOffIcon;
260
- var LockGlyph = import_icons.LockIcon;
261
- var ExternalGlyph = import_icons.ExternalLinkIcon;
262
- var TrashGlyph = import_icons.TrashIcon;
263
- var TagGlyph = import_icons.TagIcon;
264
- var ShieldGlyph = import_icons.ShieldIcon;
265
- var BuildingGlyph = import_icons.BuildingIcon;
266
- var ChevronLeftGlyph = import_icons.ChevronLeftIcon;
267
- var GridGlyph = import_icons.LayoutGridIcon;
268
- var PlugGlyph = import_icons.PlugIcon;
258
+ var KeyGlyph = (0, import_icons.withGlyphDefaults)(import_icons.KeyIcon, {}, { air: [1, 2] });
259
+ var NoteGlyph = (0, import_icons.withGlyphDefaults)(import_icons.FileTextIcon, {}, { air: [3, 3] });
260
+ var CardGlyph = (0, import_icons.withGlyphDefaults)(import_icons.CreditCardIcon, {}, { air: [1, 1] });
261
+ var PersonGlyph = (0, import_icons.withGlyphDefaults)(import_icons.UserRoundIcon, {}, { air: [3, 3] });
262
+ var DatabaseGlyph = (0, import_icons.withGlyphDefaults)(import_icons.DatabaseIcon, {}, { air: [2, 2] });
263
+ var ServerGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ServerIcon, {}, { air: [1, 1] });
264
+ var TerminalGlyph = (0, import_icons.withGlyphDefaults)(import_icons.TerminalIcon, {}, { air: [3, 3] });
265
+ var FileGlyph = (0, import_icons.withGlyphDefaults)(import_icons.FilePageIcon, {}, { air: [3, 3] });
266
+ var CodeGlyph = (0, import_icons.withGlyphDefaults)(import_icons.CodeIcon, {}, { air: [1, 1] });
267
+ var StarGlyph = (0, import_icons.withGlyphDefaults)(import_icons.StarIcon, {}, { air: [1, 1] });
268
+ var PlusGlyph = (0, import_icons.withGlyphDefaults)(import_icons.PlusIcon, {}, { air: [4, 4] });
269
+ var EyeGlyph = (0, import_icons.withGlyphDefaults)(import_icons.EyeIcon, {}, { air: [1, 1] });
270
+ var EyeOffGlyph = (0, import_icons.withGlyphDefaults)(import_icons.EyeOffIcon, {}, { air: [1, 1] });
271
+ var LockGlyph = (0, import_icons.withGlyphDefaults)(import_icons.LockIcon, {}, { air: [2, 2] });
272
+ var ExternalGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ExternalLinkIcon, {}, { air: [2, 2] });
273
+ var TrashGlyph = (0, import_icons.withGlyphDefaults)(import_icons.TrashIcon, {}, { air: [2, 2] });
274
+ var ArchiveGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ArchiveIcon, {}, { air: [1, 1] });
275
+ var TagGlyph = (0, import_icons.withGlyphDefaults)(import_icons.TagIcon, {}, { air: [1, 1] });
276
+ var ShieldGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ShieldIcon, {}, { air: [3, 3] });
277
+ var BuildingGlyph = (0, import_icons.withGlyphDefaults)(import_icons.BuildingIcon, {}, { air: [3, 3] });
278
+ var ChevronLeftGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ChevronLeftIcon, {}, { air: [8, 8] });
279
+ var GridGlyph = (0, import_icons.withGlyphDefaults)(import_icons.LayoutGridIcon, {}, { air: [2, 2] });
280
+ var PlugGlyph = (0, import_icons.withGlyphDefaults)(import_icons.PlugIcon, {}, { air: [5, 5] });
269
281
  var FAMILY_GLYPHS = {
270
282
  databases: DatabaseGlyph,
271
283
  server_network: ServerGlyph,
@@ -279,34 +291,34 @@ function definitionGlyph(key, family) {
279
291
  if (key === "username_password" || key === "website_login") return import_icons.GlobeIcon;
280
292
  return FAMILY_GLYPHS[family] ?? KeyGlyph;
281
293
  }
282
- var XGlyph = import_icons.XIcon;
283
- var PencilGlyph = import_icons.PencilIcon;
284
- var ClockGlyph = import_icons.ClockIcon;
285
- var MoreGlyph = import_icons.MoreHorizontalIcon;
286
- var ShareGlyph = import_icons.ShareIcon;
287
- var ListGlyph = import_icons.ListIcon;
288
- var ImportGlyph = import_icons.DownloadIcon;
289
- var ExportGlyph = import_icons.UploadIcon;
290
- var ChevronDownGlyph = import_icons.ChevronDownIcon;
291
- var ChevronRightGlyph = import_icons.ChevronRightIcon;
292
- var SortGlyph = import_icons.ArrowUpDownIcon;
293
- var UserGlyph = import_icons.UserIcon;
294
- var MoveGlyph = import_icons.MoveIcon;
294
+ var XGlyph = (0, import_icons.withGlyphDefaults)(import_icons.XIcon, {}, { air: [5, 5] });
295
+ var PencilGlyph = (0, import_icons.withGlyphDefaults)(import_icons.PencilIcon, {}, { air: [2, 2] });
296
+ var ClockGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ClockIcon, {}, { air: [1, 1] });
297
+ var MoreGlyph = (0, import_icons.withGlyphDefaults)(import_icons.MoreHorizontalIcon, {}, { air: [3, 3] });
298
+ var ShareGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ShareIcon, {}, { air: [3, 3] });
299
+ var ListGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ListIcon, {}, { air: [2, 2] });
300
+ var ImportGlyph = (0, import_icons.withGlyphDefaults)(import_icons.DownloadIcon, {}, { air: [2, 2] });
301
+ var ExportGlyph = (0, import_icons.withGlyphDefaults)(import_icons.UploadIcon, {}, { air: [2, 2] });
302
+ var ChevronDownGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ChevronDownIcon, {}, { air: [5, 5] });
303
+ var ChevronRightGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ChevronRightIcon, {}, { air: [8, 8] });
304
+ var SortGlyph = (0, import_icons.withGlyphDefaults)(import_icons.ArrowUpDownIcon, {}, { air: [2, 2] });
305
+ var UserGlyph = (0, import_icons.withGlyphDefaults)(import_icons.UserIcon, {}, { air: [5, 5] });
306
+ var MoveGlyph = (0, import_icons.withGlyphDefaults)(import_icons.MoveIcon, {}, { air: [1, 1] });
295
307
  var SafeGlyph = glyph(/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
296
308
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
297
309
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "4" }),
298
310
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 8v1M12 15v1M8 12h1M15 12h1" })
299
311
  ] }), [2, 2]);
300
- var RestoreGlyph = import_icons.RotateCcwIcon;
301
- var QrGlyph = import_icons.QrCodeIcon;
302
- var CameraGlyph = import_icons.CameraIcon;
303
- var CheckGlyph = import_icons.CheckIcon;
304
- var PowerGlyph = import_icons.PowerIcon;
305
- var RefreshGlyph = import_icons.RefreshCwIcon;
306
- var SlidersGlyph = import_icons.SlidersVerticalIcon;
307
- var UploadGlyph = import_icons.UploadIcon;
308
- var FingerprintGlyph = import_icons.FingerprintPatternIcon;
309
- var SendGlyph = import_icons.SendIcon;
312
+ var RestoreGlyph = (0, import_icons.withGlyphDefaults)(import_icons.RotateCcwIcon, {}, { air: [2, 2] });
313
+ var QrGlyph = (0, import_icons.withGlyphDefaults)(import_icons.QrCodeIcon, {}, { air: [2, 2] });
314
+ var CameraGlyph = (0, import_icons.withGlyphDefaults)(import_icons.CameraIcon, {}, { air: [1, 1] });
315
+ var CheckGlyph = (0, import_icons.withGlyphDefaults)(import_icons.CheckIcon, {}, { air: [3, 3] });
316
+ var PowerGlyph = (0, import_icons.withGlyphDefaults)(import_icons.PowerIcon, {}, { air: [2, 2] });
317
+ var RefreshGlyph = (0, import_icons.withGlyphDefaults)(import_icons.RefreshCwIcon, {}, { air: [2, 2] });
318
+ var SlidersGlyph = (0, import_icons.withGlyphDefaults)(import_icons.SlidersVerticalIcon, {}, { air: [0, 0] });
319
+ var UploadGlyph = (0, import_icons.withGlyphDefaults)(import_icons.UploadIcon, {}, { air: [2, 2] });
320
+ var FingerprintGlyph = (0, import_icons.withGlyphDefaults)(import_icons.FingerprintPatternIcon, {}, { air: [1, 1] });
321
+ var SendGlyph = (0, import_icons.withGlyphDefaults)(import_icons.SendIcon, {}, { air: [1, 1] });
310
322
 
311
323
  // src/auth/identity.ts
312
324
  var CONFIRM_RETURN_KEY = "matrx-vault:confirming-as";
@@ -602,6 +614,17 @@ function useElementWidth(ref) {
602
614
  }, [ref]);
603
615
  return width;
604
616
  }
617
+ function useDisposable(create, deps) {
618
+ const [held, setHeld] = (0, import_react3.useState)(null);
619
+ const make = (0, import_react3.useRef)(create);
620
+ make.current = create;
621
+ (0, import_react3.useEffect)(() => {
622
+ const made = make.current();
623
+ setHeld(made);
624
+ return () => made.dispose();
625
+ }, deps);
626
+ return held;
627
+ }
605
628
 
606
629
  // src/ui.tsx
607
630
  var import_react4 = require("react");
@@ -3149,6 +3172,27 @@ function importLimitsFrom(resolved) {
3149
3172
  return out;
3150
3173
  }
3151
3174
 
3175
+ // src/knobs.ts
3176
+ var import_knobs = require("@ai-matrx/data/knobs");
3177
+ var FRESH_MS = 6e4;
3178
+ function createVaultKnobs(client, address) {
3179
+ const reader = (0, import_knobs.createKnobReader)(client);
3180
+ let kept = null;
3181
+ return async () => {
3182
+ if (kept && Date.now() - kept.at < FRESH_MS) return kept.resolved;
3183
+ const snap = await reader.snapshot(address);
3184
+ kept = { at: Date.now(), resolved: snap.resolved };
3185
+ return snap.resolved;
3186
+ };
3187
+ }
3188
+ var REVEAL_SECONDS_KNOB = "desktop.vault.reveal_hide_seconds";
3189
+ var BACKUP_PASSPHRASE_MIN_KNOB = "desktop.vault.backup_passphrase_min_length";
3190
+ function positiveKnob(resolved, key) {
3191
+ const v = resolved[key];
3192
+ const n = typeof v === "string" && v.trim() !== "" ? Number(v) : v;
3193
+ return typeof n === "number" && Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
3194
+ }
3195
+
3152
3196
  // src/lock.ts
3153
3197
  var import_react16 = require("react");
3154
3198
  var LOCKED_MESSAGE = "The vault is locked. Unlock it first.";
@@ -3344,7 +3388,8 @@ function Omissions({ omissions, accepted, onAccepted, verb }) {
3344
3388
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Switch, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
3345
3389
  ] });
3346
3390
  }
3347
- function BackupPanel({ port, items, onClose, onChanged }) {
3391
+ function BackupPanel({ port, host, items, onClose, onChanged }) {
3392
+ const minLength = passphraseMinimum(host.backupPassphraseMinLength);
3348
3393
  const confirmed = useConfirmed();
3349
3394
  const fileInput = (0, import_react18.useRef)(null);
3350
3395
  const abort = (0, import_react18.useRef)(null);
@@ -3399,7 +3444,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3399
3444
  setAccepted(false);
3400
3445
  });
3401
3446
  const downloadBackup = () => guarded(async () => {
3402
- if (!backup) return;
3447
+ if (!backup || tooShort(passphrase, minLength)) return;
3403
3448
  const bytes2 = await confirmed(() => port.io.downloadBackup({ itemIds: [...selected], revision: backup.revision, passphrase, acceptOmissions: accepted }));
3404
3449
  (0, import_download.downloadFile)(BACKUP_FILENAME, bytes2, "application/octet-stream");
3405
3450
  setPassphrase("");
@@ -3446,10 +3491,11 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3446
3491
  };
3447
3492
  const retryable = (result?.retryable_failure ?? 0) > 0;
3448
3493
  const mismatch = repeat !== "" && repeat !== passphrase;
3494
+ const short = passphrase !== "" && tooShort(passphrase, minLength);
3449
3495
  const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
3450
3496
  const footer = mode === "backup" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3451
3497
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "quiet", onClick: onClose, children: "Close" }),
3452
- backup ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_controls13.Button, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3498
+ backup ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Button, { variant: "primary", disabled: busy || !passphrase || short || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_controls13.Button, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3453
3499
  "Review ",
3454
3500
  selected.size
3455
3501
  ] })
@@ -3496,7 +3542,14 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3496
3542
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Summary, { value: backup }),
3497
3543
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3498
3544
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(VaultSection, { children: [
3499
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Field, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
3545
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Passphrase", hint: minLength ? `At least ${minLength} characters. Not saved.` : "Not saved. It can't be recovered.", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Field, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", "aria-invalid": short || void 0, onChange: (e) => setPassphrase(e.target.value) }) }),
3546
+ short ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { role: "alert", className: "px-1 text-xs text-destructive", "data-passphrase-too-short": "", children: [
3547
+ "Use at least ",
3548
+ minLength,
3549
+ " characters. This one has ",
3550
+ passphrase.length,
3551
+ "."
3552
+ ] }) : null,
3500
3553
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_controls13.Field, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
3501
3554
  ] })
3502
3555
  ] }) : null
@@ -3529,6 +3582,12 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3529
3582
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorLine, { message: error })
3530
3583
  ] });
3531
3584
  }
3585
+ function passphraseMinimum(given) {
3586
+ return given !== void 0 && Number.isFinite(given) && given > 1 ? Math.floor(given) : null;
3587
+ }
3588
+ function tooShort(passphrase, minLength) {
3589
+ return minLength !== null && [...passphrase].length < minLength;
3590
+ }
3532
3591
 
3533
3592
  // src/io/ExportPanel.tsx
3534
3593
  var import_react19 = require("react");
@@ -5986,6 +6045,9 @@ var import_jsx_runtime27 = require("react/jsx-runtime");
5986
6045
  var ENV_VALUE_KEY = "env_value";
5987
6046
  var secureNoteDraft = () => ({ key: SECURE_NOTE_KEY, label: "Secure note", value: "", multiline: true, description: "Encrypted notes" });
5988
6047
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
6048
+ function confirmDelete(item) {
6049
+ return (0, import_confirm_opener10.confirm)({ title: `Delete \u201C${item.name}\u201D?`, description: "It moves to Trash. You can restore it from there.", confirmLabel: "Delete", variant: "destructive" });
6050
+ }
5989
6051
  function ItemPane(props) {
5990
6052
  const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
5991
6053
  const confirmed = useConfirmed();
@@ -6138,8 +6200,7 @@ function ItemPane(props) {
6138
6200
  }
6139
6201
  async function remove() {
6140
6202
  if (!item) return;
6141
- const ok = await (0, import_confirm_opener10.confirm)({ title: `Delete \u201C${item.name}\u201D?`, description: "It moves to Trash. You can restore it from there.", confirmLabel: "Delete", variant: "destructive" });
6142
- if (!ok) return;
6203
+ if (!await confirmDelete(item)) return;
6143
6204
  setBusy(true);
6144
6205
  try {
6145
6206
  await port.remove(item);
@@ -6550,7 +6611,7 @@ function subtitleOf(item, withOwner) {
6550
6611
  const what = loginSiteOf(item) || item.definitionLabel;
6551
6612
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
6552
6613
  }
6553
- function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, actions, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems, stillLoading, favorites, onToggleFavorite }) {
6614
+ function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, actions, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems, stillLoading, favorites, onToggleFavorite, onItemAction }) {
6554
6615
  const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
6555
6616
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
6556
6617
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
@@ -6571,7 +6632,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6571
6632
  }
6572
6633
  };
6573
6634
  const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
6574
- 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, favorite: onToggleFavorite ? { on: favorites?.has(i.id) ?? false, toggle: () => onToggleFavorite(i.id) } : void 0 }, i.id);
6635
+ 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, onAction: onItemAction, favorite: onToggleFavorite ? { on: favorites?.has(i.id) ?? false, toggle: () => onToggleFavorite(i.id) } : void 0 }, i.id);
6575
6636
  const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
6576
6637
  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: [
6577
6638
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
@@ -6615,7 +6676,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6615
6676
  ] }) })
6616
6677
  ] });
6617
6678
  }
6618
- function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, favorite }) {
6679
+ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, onAction, favorite }) {
6619
6680
  const confirmed = useConfirmed();
6620
6681
  const copy = useCopy();
6621
6682
  const notify = useNotify();
@@ -6656,11 +6717,12 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6656
6717
  ...favoriteEntry ? [favoriteEntry] : [],
6657
6718
  { id: "open", kind: "open", label: "Open", onSelect: () => onSelect(item.id) },
6658
6719
  { id: "copy-name", kind: "copy-name", label: "Copy name", onSelect: copyName },
6720
+ ...manageEntries(item, onAction),
6659
6721
  ...hostEntries.map((h) => ({ ...h, kind: h.kind ?? "host" }))
6660
6722
  ];
6661
6723
  const entriesRef = (0, import_react29.useRef)(entries);
6662
6724
  entriesRef.current = entries;
6663
- const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(StarGlyph, { className: (0, import_design_system20.cn)("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" ? null : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" });
6725
+ const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(StarGlyph, { className: (0, import_design_system20.cn)("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" || e.kind === "manage" ? null : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" });
6664
6726
  const menuItem = (e) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { disabled: e.disabled ?? false, onSelect: e.onSelect, children: [
6665
6727
  glyphOf(e),
6666
6728
  e.label
@@ -6714,6 +6776,8 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6714
6776
  copyEntries.map(menuItem),
6715
6777
  copyEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6716
6778
  entries.filter((e) => e.kind === "favorite" || e.kind === "open" || e.kind === "copy-name").map(menuItem),
6779
+ entries.some((e) => e.kind === "manage") ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6780
+ entries.filter((e) => e.kind === "manage").map(menuItem),
6717
6781
  hostEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6718
6782
  entries.filter((e) => e.kind === "host").map(menuItem)
6719
6783
  ] })
@@ -6723,6 +6787,17 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6723
6787
  }
6724
6788
  );
6725
6789
  }
6790
+ function manageEntries(item, onAction) {
6791
+ if (!onAction) return [];
6792
+ const canManage = item.capabilities.canManage;
6793
+ const offManage = !canManage && manageUnavailableReason(item) !== null;
6794
+ const run = (action) => () => onAction(item, action);
6795
+ return [
6796
+ { id: "activity", kind: "manage", label: "Activity", onSelect: run("activity") },
6797
+ ...canManage || offManage ? [{ id: "share", kind: "manage", label: "Share", onSelect: run("share"), disabled: !canManage }] : [],
6798
+ ...canManage || offManage ? [{ id: "delete", kind: "manage", label: "Delete", onSelect: run("delete"), disabled: !canManage }] : []
6799
+ ];
6800
+ }
6726
6801
 
6727
6802
  // src/lists/TrashView.tsx
6728
6803
  var import_react31 = require("react");
@@ -8259,22 +8334,10 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
8259
8334
  ] })
8260
8335
  ] }),
8261
8336
  onFill ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: "primary", disabled: busy, "aria-busy": running || void 0, onClick: onFill, "aria-label": `Fill ${item?.name ?? "this login"}`, children: running ? "Filling\u2026" : "Fill" }) : null,
8337
+ onSignIn ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: onFill ? "outline" : "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ShieldGlyph, {}), disabled: busy, onClick: onSignIn, "aria-label": `Sign me in with ${item?.name ?? "this login"}`, title: "Sign me in", children: "Sign me in" }) : null,
8262
8338
  user && item ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(UserGlyph, {}), "aria-label": `Copy ${user.label}`, title: `Copy ${user.label}`, disabled: reading, onClick: () => void read(user.label, () => port.reveal(item, user.key)) }) : null,
8263
8339
  secret && item ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(KeyGlyph, {}), "aria-label": "Copy password", title: "Copy password", disabled: reading, onClick: () => void read("Password", () => port.reveal(item, secret.key)) }) : null,
8264
- otp ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ClockGlyph, {}), "aria-label": "Copy one-time code", title: "Copy one-time code", disabled: reading, onClick: () => void read("One-time code", async () => (await port.authenticator.code(otp)).code) }) : null,
8265
- onSignIn ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_design_system24.DropdownMenu, { children: [
8266
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_design_system24.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MoreGlyph, {}), "aria-label": "More", disabled: busy }) }),
8267
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_design_system24.DropdownMenuContent, { align: "end", children: [
8268
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_design_system24.DropdownMenuItem, { onSelect: onSignIn, children: [
8269
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ShieldGlyph, { className: "size-4" }),
8270
- "Sign me in"
8271
- ] }),
8272
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_design_system24.DropdownMenuItem, { onSelect: () => onOpen(itemId), children: [
8273
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ListGlyph, { className: "size-4" }),
8274
- "Open item"
8275
- ] })
8276
- ] })
8277
- ] }) : null
8340
+ otp ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_controls33.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ClockGlyph, {}), "aria-label": "Copy one-time code", title: "Copy one-time code", disabled: reading, onClick: () => void read("One-time code", async () => (await port.authenticator.code(otp)).code) }) : null
8278
8341
  ] });
8279
8342
  }
8280
8343
 
@@ -8386,7 +8449,7 @@ function Group({ title, children }) {
8386
8449
  children
8387
8450
  ] });
8388
8451
  }
8389
- function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false }) {
8452
+ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false, onLock, onOpenWeb }) {
8390
8453
  const [tagsOpen, setTagsOpen] = (0, import_react41.useState)(view.startsWith("tag:"));
8391
8454
  return /* @__PURE__ */ (0, import_jsx_runtime39.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: [
8392
8455
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2 px-3 pb-2 pt-3", children: [
@@ -8425,7 +8488,9 @@ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, v
8425
8488
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ShieldGlyph, { className: "size-4 shrink-0" }),
8426
8489
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "min-w-0 flex-1 truncate", children: "System AutoFill" }),
8427
8490
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: (0, import_design_system25.cn)("text-xs", nativeProvider.status === "ready" ? "text-success" : "text-warning"), children: nativeProvider.status === "ready" ? "On" : "Set up" })
8428
- ] }) : null
8491
+ ] }) : null,
8492
+ onOpenWeb ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Entry, { on: false, glyph: ExternalGlyph, label: "Open on the web", onClick: onOpenWeb }) : null,
8493
+ onLock ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Entry, { on: false, glyph: LockGlyph, label: "Lock", onClick: onLock }) : null
8429
8494
  ] })
8430
8495
  ] });
8431
8496
  }
@@ -8468,12 +8533,42 @@ function ImportWithLimits(props) {
8468
8533
  if (state && "limits" in state) return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportPanel, { ...rest, limits: state.limits });
8469
8534
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Panel, { title: "Import", onClose: rest.onClose, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Empty, { children: state ? state.error : "Reading this organization\u2019s import settings\u2026" }) });
8470
8535
  }
8471
- function VaultApp(props) {
8536
+ function VaultApp(given) {
8537
+ const props = { ...given, host: useOrganizationSettings(given.host ?? NO_HOST) };
8472
8538
  const lock = props.host?.lock;
8473
8539
  const locked = useLocked(lock);
8474
8540
  const port = (0, import_react42.useMemo)(() => lock ? guardPort(props.port, () => lock.locked()) : props.port, [props.port, lock]);
8475
8541
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system26.LookScope, { look: props.look ?? "web", className: "h-full min-h-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "relative h-full min-h-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(NoticeProvider, { notify: props.notify, children: lock && locked ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockedScreen, { lock }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ClipboardProvider, { clipboard: props.host?.clipboard, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(RevealSecondsProvider, { seconds: props.host?.revealSeconds, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(IdentityGate, { identity: props.host?.identity, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(GeneratorLimitsProvider, { resolve: generatorLimitsFrom(props.host?.knobs), children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Workspace, { ...props, port }) }) }) }) }) }) }) });
8476
8542
  }
8543
+ function useOrganizationSettings(host) {
8544
+ const knobs = host.knobs;
8545
+ const needs = knobs !== void 0 && (host.revealSeconds === void 0 || host.backupPassphraseMinLength === void 0);
8546
+ const [read, setRead] = (0, import_react42.useState)(null);
8547
+ (0, import_react42.useEffect)(() => {
8548
+ if (!needs || !knobs) return;
8549
+ let live = true;
8550
+ knobs().then(
8551
+ (r) => live && setRead({ reveal: positiveKnob(r, REVEAL_SECONDS_KNOB), backupMin: positiveKnob(r, BACKUP_PASSPHRASE_MIN_KNOB) }),
8552
+ () => void 0
8553
+ );
8554
+ return () => {
8555
+ live = false;
8556
+ };
8557
+ }, [needs, knobs]);
8558
+ if (!read) return host;
8559
+ return {
8560
+ ...host,
8561
+ ...host.revealSeconds === void 0 && read.reveal !== null ? { revealSeconds: read.reveal } : {},
8562
+ ...host.backupPassphraseMinLength === void 0 && read.backupMin !== null ? { backupPassphraseMinLength: read.backupMin } : {}
8563
+ };
8564
+ }
8565
+ var NO_STORE = () => () => void 0;
8566
+ var NOTHING = () => null;
8567
+ function useHostPrompts(capture, fillSetup) {
8568
+ const prompt = (0, import_react42.useSyncExternalStore)(capture ? capture.subscribe : NO_STORE, capture ? capture.get : NOTHING, capture ? capture.get : NOTHING);
8569
+ const setup = (0, import_react42.useSyncExternalStore)(fillSetup ? fillSetup.subscribe : NO_STORE, fillSetup ? fillSetup.get : NOTHING, fillSetup ? fillSetup.get : NOTHING);
8570
+ return prompt !== null || setup?.status === "off";
8571
+ }
8477
8572
  function LockedScreen({ lock }) {
8478
8573
  const [busy, setBusy] = (0, import_react42.useState)(false);
8479
8574
  const [error, setError] = (0, import_react42.useState)(null);
@@ -8561,6 +8656,16 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8561
8656
  document.addEventListener("keydown", onKey);
8562
8657
  return () => document.removeEventListener("keydown", onKey);
8563
8658
  }, []);
8659
+ const keepSearchFocus = (0, import_react42.useRef)(false);
8660
+ (0, import_react42.useEffect)(() => {
8661
+ if (!keepSearchFocus.current) return;
8662
+ keepSearchFocus.current = false;
8663
+ const search = rootRef.current?.querySelector('input[aria-label^="Search"]');
8664
+ if (!search || document.activeElement === search) return;
8665
+ search.focus();
8666
+ search.setSelectionRange(search.value.length, search.value.length);
8667
+ }, [loc.q]);
8668
+ const hasPrompts = useHostPrompts(host.capture, host.fillSetup);
8564
8669
  const knobsRef = (0, import_react42.useRef)(host.knobs);
8565
8670
  knobsRef.current = host.knobs;
8566
8671
  const [signedOut, setSignedOut] = (0, import_react42.useState)(false);
@@ -8703,6 +8808,25 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8703
8808
  notify(e instanceof Error ? e.message : "Favorites could not be saved.", "error");
8704
8809
  });
8705
8810
  };
8811
+ const deletedItem = (id) => {
8812
+ setItems((cur) => cur.filter((i) => i.id !== id));
8813
+ dirty.current = false;
8814
+ if (loc.itemId === id) move({ ...loc, itemId: null, mode: "view", panel: null }, "push");
8815
+ notify("Moved to Trash");
8816
+ load();
8817
+ };
8818
+ const rowAction = (item, action) => {
8819
+ if (action !== "delete") return go({ view: loc.view === "page" ? "all" : loc.view, itemId: item.id, mode: "view", panel: action });
8820
+ void confirmDelete(item).then(async (ok) => {
8821
+ if (!ok) return;
8822
+ try {
8823
+ await port.remove(item);
8824
+ deletedItem(item.id);
8825
+ } catch (e) {
8826
+ notify(e instanceof Error && e.message ? e.message : "Deleting failed.", "error");
8827
+ }
8828
+ });
8829
+ };
8706
8830
  if (error) {
8707
8831
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ref: rootRef, role: "alert", className: "flex h-full flex-col items-center justify-center gap-3 bg-background p-6 text-center text-sm", children: [
8708
8832
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SafeGlyph, { className: "size-8 text-muted-foreground" }),
@@ -8786,6 +8910,27 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8786
8910
  compactActions
8787
8911
  ] })
8788
8912
  ] });
8913
+ const newItem = () => go({ itemId: null, mode: "new", kind: null, panel: null });
8914
+ const pageHeader = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-2", "data-vault-page-header": "", children: [
8915
+ compactHeader,
8916
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2", children: [
8917
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8918
+ import_controls35.SearchField,
8919
+ {
8920
+ value: loc.q,
8921
+ onChange: (e) => {
8922
+ keepSearchFocus.current = true;
8923
+ go({ q: e.target.value }, "replace");
8924
+ },
8925
+ placeholder: "Search the vault",
8926
+ "aria-label": "Search the vault",
8927
+ className: "min-w-0 flex-1"
8928
+ }
8929
+ ),
8930
+ compactActions,
8931
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PlusGlyph, {}), "aria-label": "New item", onClick: newItem })
8932
+ ] })
8933
+ ] });
8789
8934
  const viewProps = { port, host, items: inThisVault, deleted, definitions, query: loc.q, onOpen: (id) => go({ view: "all", itemId: id, mode: "view", panel: null }), onChanged: load, header: viewHeader };
8790
8935
  const pageView = loc.view === "page" && host.page && !loc.q ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8791
8936
  PageView,
@@ -8793,8 +8938,6 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8793
8938
  port,
8794
8939
  page: host.page,
8795
8940
  fill: host.fill,
8796
- capture: host.capture,
8797
- fillSetup: host.fillSetup,
8798
8941
  newPasswordFields: host.newPasswordFields,
8799
8942
  items: inThisVault,
8800
8943
  definitions,
@@ -8802,10 +8945,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8802
8945
  onNewForPage: (url) => go({ mode: "new", kind: null, url, itemId: null, panel: null }),
8803
8946
  onBrowse: () => go({ view: "all", itemId: null }),
8804
8947
  onChanged: load,
8805
- ...compactHeader ? { header: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-2", children: [
8806
- compactHeader,
8807
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex justify-end", children: compactActions })
8808
- ] }) } : {}
8948
+ header: pageHeader
8809
8949
  }
8810
8950
  ) : null;
8811
8951
  const specialView = pageView ?? (loc.view === "authenticator" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(AuthenticatorView, { ...viewProps }) : loc.view === "trash" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(TrashView, { ...viewProps }) : null);
@@ -8820,11 +8960,15 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8820
8960
  selectedId: loc.itemId,
8821
8961
  onSelect: (id) => go({ itemId: id, mode: "view", kind: null, panel: null }),
8822
8962
  query: loc.q,
8823
- onQuery: (q) => go({ q }, "replace"),
8963
+ onQuery: (q) => {
8964
+ if (loc.view === "page" && host.page && !q) keepSearchFocus.current = true;
8965
+ go({ q }, "replace");
8966
+ },
8824
8967
  sort: loc.sort,
8825
8968
  onSort: (sort) => go({ sort }, "replace"),
8826
8969
  canSortViewed: port.lists.recents !== void 0,
8827
- onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
8970
+ onNew: newItem,
8971
+ onItemAction: rowAction,
8828
8972
  onRefresh: refresh,
8829
8973
  refreshing,
8830
8974
  ...snap.pending && snap.pending.length > 0 ? { stillLoading: snap.pending } : {},
@@ -8899,13 +9043,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8899
9043
  setNotice(n);
8900
9044
  notify("Saved");
8901
9045
  },
8902
- onDeleted: (id) => {
8903
- setItems((cur) => cur.filter((i) => i.id !== id));
8904
- dirty.current = false;
8905
- move({ ...loc, itemId: null, mode: "view", panel: null }, "push");
8906
- notify("Moved to Trash");
8907
- load();
8908
- },
9046
+ onDeleted: deletedItem,
8909
9047
  onDirty: (d) => {
8910
9048
  dirty.current = d;
8911
9049
  },
@@ -8966,6 +9104,13 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8966
9104
  /* @__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." }),
8967
9105
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
8968
9106
  ] }) : null,
9107
+ hasPrompts ? (
9108
+ // "Save this login?" and "Turn on filling" show above the list in every view, not only "This page".
9109
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-3 border-b border-border px-3 py-3", "data-vault-host-prompts": "", children: [
9110
+ host.fillSetup ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(FillSetupCard, { fillSetup: host.fillSetup }) : null,
9111
+ host.capture ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(CaptureCard, { capture: host.capture, onChanged: load }) : null
9112
+ ] })
9113
+ ) : null,
8969
9114
  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: [
8970
9115
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "min-w-0 flex-1 truncate text-warning", children: [
8971
9116
  "Couldn\u2019t load ",
@@ -8991,7 +9136,9 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8991
9136
  onPanel: (p) => go({ panel: p }),
8992
9137
  nativeProvider: host.nativeProvider,
8993
9138
  hasNativeImport: host.nativeImport !== void 0,
8994
- hasPage: host.page !== void 0
9139
+ hasPage: host.page !== void 0,
9140
+ onLock: host.lock?.lock ? () => void host.lock?.lock?.() : void 0,
9141
+ onOpenWeb: host.openWebVault
8995
9142
  }
8996
9143
  ) : null,
8997
9144
  shown.list ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: specialView && mode !== "stack" ? "col-span-2 min-h-0" : "min-h-0", children: list }) : null,
@@ -9964,20 +10111,6 @@ function createApiVaultPort(hostApi, options = {}) {
9964
10111
  };
9965
10112
  }
9966
10113
 
9967
- // src/knobs.ts
9968
- var import_knobs = require("@ai-matrx/data/knobs");
9969
- var FRESH_MS = 6e4;
9970
- function createVaultKnobs(client, address) {
9971
- const reader = (0, import_knobs.createKnobReader)(client);
9972
- let kept = null;
9973
- return async () => {
9974
- if (kept && Date.now() - kept.at < FRESH_MS) return kept.resolved;
9975
- const snap = await reader.snapshot(address);
9976
- kept = { at: Date.now(), resolved: snap.resolved };
9977
- return snap.resolved;
9978
- };
9979
- }
9980
-
9981
10114
  // src/agent-context.ts
9982
10115
  var hostOf = (url) => {
9983
10116
  try {