@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.js CHANGED
@@ -101,8 +101,8 @@ var init_keepass_xml_worker_client = __esm({
101
101
  });
102
102
 
103
103
  // src/VaultApp.tsx
104
- import { useCallback as useCallback5, useEffect as useEffect23, useMemo as useMemo6, useRef as useRef19, useState as useState41 } from "react";
105
- import { DropdownMenu as DropdownMenu6, DropdownMenuContent as DropdownMenuContent6, DropdownMenuItem as DropdownMenuItem6, DropdownMenuTrigger as DropdownMenuTrigger6, LookScope } from "@ai-matrx/design-system";
104
+ import { useCallback as useCallback5, useEffect as useEffect23, useMemo as useMemo6, useRef as useRef19, useState as useState41, useSyncExternalStore as useSyncExternalStore5 } from "react";
105
+ import { DropdownMenu as DropdownMenu5, DropdownMenuContent as DropdownMenuContent5, DropdownMenuItem as DropdownMenuItem5, DropdownMenuTrigger as DropdownMenuTrigger5, LookScope } from "@ai-matrx/design-system";
106
106
  import { Button as Button34, SearchField as SearchField4, Select as Select12 } from "@ai-matrx/design-system/controls";
107
107
  import { confirm as confirm14 } from "@ai-matrx/kit/confirm-opener";
108
108
 
@@ -119,8 +119,8 @@ async function copyValue(text, label, options, deps) {
119
119
  const secret = options.secret ?? true;
120
120
  if (deps.clipboard) {
121
121
  try {
122
- await deps.clipboard.copy(text, { secret });
123
- deps.notify(`${label} copied`, "success");
122
+ const copied = await deps.clipboard.copy(text, { secret });
123
+ deps.notify(copiedLine(label, copied?.clearsAtMs ?? null), "success");
124
124
  return true;
125
125
  } catch (e) {
126
126
  deps.notify(e instanceof Error && e.message ? e.message : `${label} could not be copied`, "error");
@@ -129,6 +129,11 @@ async function copyValue(text, label, options, deps) {
129
129
  }
130
130
  return copyText(text, { successMessage: `${label} copied`, failureMessage: `${label} could not be copied`, notify: (m, k) => deps.notify(m, k === "error" ? "error" : "success") });
131
131
  }
132
+ function copiedLine(label, clearsAtMs) {
133
+ if (clearsAtMs === null || !Number.isFinite(clearsAtMs)) return `${label} copied`;
134
+ const at = new Date(clearsAtMs).toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
135
+ return `${label} copied. Clears at ${at}`;
136
+ }
132
137
  var ClipboardCtx = createContext(void 0);
133
138
  function ClipboardProvider({ clipboard, children }) {
134
139
  return /* @__PURE__ */ jsx(ClipboardCtx.Provider, { value: clipboard, children });
@@ -210,7 +215,8 @@ import {
210
215
  UploadIcon,
211
216
  UserIcon,
212
217
  UserRoundIcon,
213
- XIcon
218
+ XIcon,
219
+ withGlyphDefaults
214
220
  } from "@ai-matrx/design-system/icons";
215
221
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
216
222
  function glyph(paths, air = [1, 1]) {
@@ -219,27 +225,29 @@ function glyph(paths, air = [1, 1]) {
219
225
  return /* @__PURE__ */ jsx2("svg", { className, style, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: paths });
220
226
  };
221
227
  }
222
- var KeyGlyph = KeyIcon;
223
- var NoteGlyph = FileTextIcon;
224
- var PersonGlyph = UserRoundIcon;
225
- var DatabaseGlyph = DatabaseIcon;
226
- var ServerGlyph = ServerIcon;
227
- var TerminalGlyph = TerminalIcon;
228
- var FileGlyph = FilePageIcon;
229
- var CodeGlyph = CodeIcon;
230
- var StarGlyph = StarIcon;
231
- var PlusGlyph = PlusIcon;
232
- var EyeGlyph = EyeIcon;
233
- var EyeOffGlyph = EyeOffIcon;
234
- var LockGlyph = LockIcon;
235
- var ExternalGlyph = ExternalLinkIcon;
236
- var TrashGlyph = TrashIcon;
237
- var TagGlyph = TagIcon;
238
- var ShieldGlyph = ShieldIcon;
239
- var BuildingGlyph = BuildingIcon;
240
- var ChevronLeftGlyph = ChevronLeftIcon;
241
- var GridGlyph = LayoutGridIcon;
242
- var PlugGlyph = PlugIcon;
228
+ var KeyGlyph = withGlyphDefaults(KeyIcon, {}, { air: [1, 2] });
229
+ var NoteGlyph = withGlyphDefaults(FileTextIcon, {}, { air: [3, 3] });
230
+ var CardGlyph = withGlyphDefaults(CreditCardIcon, {}, { air: [1, 1] });
231
+ var PersonGlyph = withGlyphDefaults(UserRoundIcon, {}, { air: [3, 3] });
232
+ var DatabaseGlyph = withGlyphDefaults(DatabaseIcon, {}, { air: [2, 2] });
233
+ var ServerGlyph = withGlyphDefaults(ServerIcon, {}, { air: [1, 1] });
234
+ var TerminalGlyph = withGlyphDefaults(TerminalIcon, {}, { air: [3, 3] });
235
+ var FileGlyph = withGlyphDefaults(FilePageIcon, {}, { air: [3, 3] });
236
+ var CodeGlyph = withGlyphDefaults(CodeIcon, {}, { air: [1, 1] });
237
+ var StarGlyph = withGlyphDefaults(StarIcon, {}, { air: [1, 1] });
238
+ var PlusGlyph = withGlyphDefaults(PlusIcon, {}, { air: [4, 4] });
239
+ var EyeGlyph = withGlyphDefaults(EyeIcon, {}, { air: [1, 1] });
240
+ var EyeOffGlyph = withGlyphDefaults(EyeOffIcon, {}, { air: [1, 1] });
241
+ var LockGlyph = withGlyphDefaults(LockIcon, {}, { air: [2, 2] });
242
+ var ExternalGlyph = withGlyphDefaults(ExternalLinkIcon, {}, { air: [2, 2] });
243
+ var TrashGlyph = withGlyphDefaults(TrashIcon, {}, { air: [2, 2] });
244
+ var ArchiveGlyph = withGlyphDefaults(ArchiveIcon, {}, { air: [1, 1] });
245
+ var TagGlyph = withGlyphDefaults(TagIcon, {}, { air: [1, 1] });
246
+ var ShieldGlyph = withGlyphDefaults(ShieldIcon, {}, { air: [3, 3] });
247
+ var BuildingGlyph = withGlyphDefaults(BuildingIcon, {}, { air: [3, 3] });
248
+ var ChevronLeftGlyph = withGlyphDefaults(ChevronLeftIcon, {}, { air: [8, 8] });
249
+ var GridGlyph = withGlyphDefaults(LayoutGridIcon, {}, { air: [2, 2] });
250
+ var PlugGlyph = withGlyphDefaults(PlugIcon, {}, { air: [5, 5] });
243
251
  var FAMILY_GLYPHS = {
244
252
  databases: DatabaseGlyph,
245
253
  server_network: ServerGlyph,
@@ -253,34 +261,34 @@ function definitionGlyph(key, family) {
253
261
  if (key === "username_password" || key === "website_login") return GlobeIcon;
254
262
  return FAMILY_GLYPHS[family] ?? KeyGlyph;
255
263
  }
256
- var XGlyph = XIcon;
257
- var PencilGlyph = PencilIcon;
258
- var ClockGlyph = ClockIcon;
259
- var MoreGlyph = MoreHorizontalIcon;
260
- var ShareGlyph = ShareIcon;
261
- var ListGlyph = ListIcon;
262
- var ImportGlyph = DownloadIcon;
263
- var ExportGlyph = UploadIcon;
264
- var ChevronDownGlyph = ChevronDownIcon;
265
- var ChevronRightGlyph = ChevronRightIcon;
266
- var SortGlyph = ArrowUpDownIcon;
267
- var UserGlyph = UserIcon;
268
- var MoveGlyph = MoveIcon;
264
+ var XGlyph = withGlyphDefaults(XIcon, {}, { air: [5, 5] });
265
+ var PencilGlyph = withGlyphDefaults(PencilIcon, {}, { air: [2, 2] });
266
+ var ClockGlyph = withGlyphDefaults(ClockIcon, {}, { air: [1, 1] });
267
+ var MoreGlyph = withGlyphDefaults(MoreHorizontalIcon, {}, { air: [3, 3] });
268
+ var ShareGlyph = withGlyphDefaults(ShareIcon, {}, { air: [3, 3] });
269
+ var ListGlyph = withGlyphDefaults(ListIcon, {}, { air: [2, 2] });
270
+ var ImportGlyph = withGlyphDefaults(DownloadIcon, {}, { air: [2, 2] });
271
+ var ExportGlyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });
272
+ var ChevronDownGlyph = withGlyphDefaults(ChevronDownIcon, {}, { air: [5, 5] });
273
+ var ChevronRightGlyph = withGlyphDefaults(ChevronRightIcon, {}, { air: [8, 8] });
274
+ var SortGlyph = withGlyphDefaults(ArrowUpDownIcon, {}, { air: [2, 2] });
275
+ var UserGlyph = withGlyphDefaults(UserIcon, {}, { air: [5, 5] });
276
+ var MoveGlyph = withGlyphDefaults(MoveIcon, {}, { air: [1, 1] });
269
277
  var SafeGlyph = glyph(/* @__PURE__ */ jsxs2(Fragment, { children: [
270
278
  /* @__PURE__ */ jsx2("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
271
279
  /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "4" }),
272
280
  /* @__PURE__ */ jsx2("path", { d: "M12 8v1M12 15v1M8 12h1M15 12h1" })
273
281
  ] }), [2, 2]);
274
- var RestoreGlyph = RotateCcwIcon;
275
- var QrGlyph = QrCodeIcon;
276
- var CameraGlyph = CameraIcon;
277
- var CheckGlyph = CheckIcon;
278
- var PowerGlyph = PowerIcon;
279
- var RefreshGlyph = RefreshCwIcon;
280
- var SlidersGlyph = SlidersVerticalIcon;
281
- var UploadGlyph = UploadIcon;
282
- var FingerprintGlyph = FingerprintPatternIcon;
283
- var SendGlyph = SendIcon;
282
+ var RestoreGlyph = withGlyphDefaults(RotateCcwIcon, {}, { air: [2, 2] });
283
+ var QrGlyph = withGlyphDefaults(QrCodeIcon, {}, { air: [2, 2] });
284
+ var CameraGlyph = withGlyphDefaults(CameraIcon, {}, { air: [1, 1] });
285
+ var CheckGlyph = withGlyphDefaults(CheckIcon, {}, { air: [3, 3] });
286
+ var PowerGlyph = withGlyphDefaults(PowerIcon, {}, { air: [2, 2] });
287
+ var RefreshGlyph = withGlyphDefaults(RefreshCwIcon, {}, { air: [2, 2] });
288
+ var SlidersGlyph = withGlyphDefaults(SlidersVerticalIcon, {}, { air: [0, 0] });
289
+ var UploadGlyph = withGlyphDefaults(UploadIcon, {}, { air: [2, 2] });
290
+ var FingerprintGlyph = withGlyphDefaults(FingerprintPatternIcon, {}, { air: [1, 1] });
291
+ var SendGlyph = withGlyphDefaults(SendIcon, {}, { air: [1, 1] });
284
292
 
285
293
  // src/auth/identity.ts
286
294
  var CONFIRM_RETURN_KEY = "matrx-vault:confirming-as";
@@ -576,6 +584,17 @@ function useElementWidth(ref) {
576
584
  }, [ref]);
577
585
  return width;
578
586
  }
587
+ function useDisposable(create, deps) {
588
+ const [held, setHeld] = useState3(null);
589
+ const make = useRef3(create);
590
+ make.current = create;
591
+ useEffect3(() => {
592
+ const made = make.current();
593
+ setHeld(made);
594
+ return () => made.dispose();
595
+ }, deps);
596
+ return held;
597
+ }
579
598
 
580
599
  // src/ui.tsx
581
600
  import { createContext as createContext4, useContext as useContext4, useEffect as useEffect4, useState as useState4 } from "react";
@@ -3126,6 +3145,27 @@ function importLimitsFrom(resolved) {
3126
3145
  return out;
3127
3146
  }
3128
3147
 
3148
+ // src/knobs.ts
3149
+ import { createKnobReader } from "@ai-matrx/data/knobs";
3150
+ var FRESH_MS = 6e4;
3151
+ function createVaultKnobs(client, address) {
3152
+ const reader = createKnobReader(client);
3153
+ let kept = null;
3154
+ return async () => {
3155
+ if (kept && Date.now() - kept.at < FRESH_MS) return kept.resolved;
3156
+ const snap = await reader.snapshot(address);
3157
+ kept = { at: Date.now(), resolved: snap.resolved };
3158
+ return snap.resolved;
3159
+ };
3160
+ }
3161
+ var REVEAL_SECONDS_KNOB = "desktop.vault.reveal_hide_seconds";
3162
+ var BACKUP_PASSPHRASE_MIN_KNOB = "desktop.vault.backup_passphrase_min_length";
3163
+ function positiveKnob(resolved, key) {
3164
+ const v = resolved[key];
3165
+ const n = typeof v === "string" && v.trim() !== "" ? Number(v) : v;
3166
+ return typeof n === "number" && Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
3167
+ }
3168
+
3129
3169
  // src/lock.ts
3130
3170
  import { useSyncExternalStore as useSyncExternalStore2 } from "react";
3131
3171
  var LOCKED_MESSAGE = "The vault is locked. Unlock it first.";
@@ -3321,7 +3361,8 @@ function Omissions({ omissions, accepted, onAccepted, verb }) {
3321
3361
  /* @__PURE__ */ jsx17(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ jsx17(Switch3, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
3322
3362
  ] });
3323
3363
  }
3324
- function BackupPanel({ port, items, onClose, onChanged }) {
3364
+ function BackupPanel({ port, host, items, onClose, onChanged }) {
3365
+ const minLength = passphraseMinimum(host.backupPassphraseMinLength);
3325
3366
  const confirmed = useConfirmed();
3326
3367
  const fileInput = useRef11(null);
3327
3368
  const abort = useRef11(null);
@@ -3376,7 +3417,7 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3376
3417
  setAccepted(false);
3377
3418
  });
3378
3419
  const downloadBackup = () => guarded(async () => {
3379
- if (!backup) return;
3420
+ if (!backup || tooShort(passphrase, minLength)) return;
3380
3421
  const bytes2 = await confirmed(() => port.io.downloadBackup({ itemIds: [...selected], revision: backup.revision, passphrase, acceptOmissions: accepted }));
3381
3422
  downloadFile(BACKUP_FILENAME, bytes2, "application/octet-stream");
3382
3423
  setPassphrase("");
@@ -3423,10 +3464,11 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3423
3464
  };
3424
3465
  const retryable = (result?.retryable_failure ?? 0) > 0;
3425
3466
  const mismatch = repeat !== "" && repeat !== passphrase;
3467
+ const short = passphrase !== "" && tooShort(passphrase, minLength);
3426
3468
  const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
3427
3469
  const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
3428
3470
  /* @__PURE__ */ jsx17(Button13, { variant: "quiet", onClick: onClose, children: "Close" }),
3429
- backup ? /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3471
+ backup ? /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy || !passphrase || short || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
3430
3472
  "Review ",
3431
3473
  selected.size
3432
3474
  ] })
@@ -3473,7 +3515,14 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3473
3515
  /* @__PURE__ */ jsx17(Summary, { value: backup }),
3474
3516
  /* @__PURE__ */ jsx17(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
3475
3517
  /* @__PURE__ */ jsxs16(VaultSection, { children: [
3476
- /* @__PURE__ */ jsx17(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
3518
+ /* @__PURE__ */ jsx17(VaultRow, { label: "Passphrase", hint: minLength ? `At least ${minLength} characters. Not saved.` : "Not saved. It can't be recovered.", children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", "aria-invalid": short || void 0, onChange: (e) => setPassphrase(e.target.value) }) }),
3519
+ short ? /* @__PURE__ */ jsxs16("p", { role: "alert", className: "px-1 text-xs text-destructive", "data-passphrase-too-short": "", children: [
3520
+ "Use at least ",
3521
+ minLength,
3522
+ " characters. This one has ",
3523
+ passphrase.length,
3524
+ "."
3525
+ ] }) : null,
3477
3526
  /* @__PURE__ */ jsx17(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
3478
3527
  ] })
3479
3528
  ] }) : null
@@ -3506,6 +3555,12 @@ function BackupPanel({ port, items, onClose, onChanged }) {
3506
3555
  /* @__PURE__ */ jsx17(ErrorLine, { message: error })
3507
3556
  ] });
3508
3557
  }
3558
+ function passphraseMinimum(given) {
3559
+ return given !== void 0 && Number.isFinite(given) && given > 1 ? Math.floor(given) : null;
3560
+ }
3561
+ function tooShort(passphrase, minLength) {
3562
+ return minLength !== null && [...passphrase].length < minLength;
3563
+ }
3509
3564
 
3510
3565
  // src/io/ExportPanel.tsx
3511
3566
  import { useMemo, useState as useState18 } from "react";
@@ -5963,6 +6018,9 @@ import { Fragment as Fragment18, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-
5963
6018
  var ENV_VALUE_KEY = "env_value";
5964
6019
  var secureNoteDraft = () => ({ key: SECURE_NOTE_KEY, label: "Secure note", value: "", multiline: true, description: "Encrypted notes" });
5965
6020
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
6021
+ function confirmDelete(item) {
6022
+ return confirm10({ title: `Delete \u201C${item.name}\u201D?`, description: "It moves to Trash. You can restore it from there.", confirmLabel: "Delete", variant: "destructive" });
6023
+ }
5966
6024
  function ItemPane(props) {
5967
6025
  const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
5968
6026
  const confirmed = useConfirmed();
@@ -6115,8 +6173,7 @@ function ItemPane(props) {
6115
6173
  }
6116
6174
  async function remove() {
6117
6175
  if (!item) return;
6118
- const ok = await confirm10({ title: `Delete \u201C${item.name}\u201D?`, description: "It moves to Trash. You can restore it from there.", confirmLabel: "Delete", variant: "destructive" });
6119
- if (!ok) return;
6176
+ if (!await confirmDelete(item)) return;
6120
6177
  setBusy(true);
6121
6178
  try {
6122
6179
  await port.remove(item);
@@ -6527,7 +6584,7 @@ function subtitleOf(item, withOwner) {
6527
6584
  const what = loginSiteOf(item) || item.definitionLabel;
6528
6585
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
6529
6586
  }
6530
- 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 }) {
6587
+ 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 }) {
6531
6588
  const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
6532
6589
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
6533
6590
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
@@ -6548,7 +6605,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6548
6605
  }
6549
6606
  };
6550
6607
  const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
6551
- const row = (i) => /* @__PURE__ */ jsx28(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);
6608
+ const row = (i) => /* @__PURE__ */ jsx28(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);
6552
6609
  const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
6553
6610
  return /* @__PURE__ */ jsxs26("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
6554
6611
  /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
@@ -6592,7 +6649,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6592
6649
  ] }) })
6593
6650
  ] });
6594
6651
  }
6595
- function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, favorite }) {
6652
+ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, onAction, favorite }) {
6596
6653
  const confirmed = useConfirmed();
6597
6654
  const copy = useCopy();
6598
6655
  const notify = useNotify();
@@ -6633,11 +6690,12 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6633
6690
  ...favoriteEntry ? [favoriteEntry] : [],
6634
6691
  { id: "open", kind: "open", label: "Open", onSelect: () => onSelect(item.id) },
6635
6692
  { id: "copy-name", kind: "copy-name", label: "Copy name", onSelect: copyName },
6693
+ ...manageEntries(item, onAction),
6636
6694
  ...hostEntries.map((h) => ({ ...h, kind: h.kind ?? "host" }))
6637
6695
  ];
6638
6696
  const entriesRef = useRef15(entries);
6639
6697
  entriesRef.current = entries;
6640
- const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ jsx28(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ jsx28(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ jsx28(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ jsx28(StarGlyph, { className: cn13("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ jsx28(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" ? null : /* @__PURE__ */ jsx28(CopyIcon7, { className: "size-4" });
6698
+ const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ jsx28(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ jsx28(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ jsx28(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ jsx28(StarGlyph, { className: cn13("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ jsx28(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" || e.kind === "manage" ? null : /* @__PURE__ */ jsx28(CopyIcon7, { className: "size-4" });
6641
6699
  const menuItem = (e) => /* @__PURE__ */ jsxs26(DropdownMenuItem4, { disabled: e.disabled ?? false, onSelect: e.onSelect, children: [
6642
6700
  glyphOf(e),
6643
6701
  e.label
@@ -6691,6 +6749,8 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6691
6749
  copyEntries.map(menuItem),
6692
6750
  copyEntries.length > 0 ? /* @__PURE__ */ jsx28(DropdownMenuSeparator4, {}) : null,
6693
6751
  entries.filter((e) => e.kind === "favorite" || e.kind === "open" || e.kind === "copy-name").map(menuItem),
6752
+ entries.some((e) => e.kind === "manage") ? /* @__PURE__ */ jsx28(DropdownMenuSeparator4, {}) : null,
6753
+ entries.filter((e) => e.kind === "manage").map(menuItem),
6694
6754
  hostEntries.length > 0 ? /* @__PURE__ */ jsx28(DropdownMenuSeparator4, {}) : null,
6695
6755
  entries.filter((e) => e.kind === "host").map(menuItem)
6696
6756
  ] })
@@ -6700,6 +6760,17 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6700
6760
  }
6701
6761
  );
6702
6762
  }
6763
+ function manageEntries(item, onAction) {
6764
+ if (!onAction) return [];
6765
+ const canManage = item.capabilities.canManage;
6766
+ const offManage = !canManage && manageUnavailableReason(item) !== null;
6767
+ const run = (action) => () => onAction(item, action);
6768
+ return [
6769
+ { id: "activity", kind: "manage", label: "Activity", onSelect: run("activity") },
6770
+ ...canManage || offManage ? [{ id: "share", kind: "manage", label: "Share", onSelect: run("share"), disabled: !canManage }] : [],
6771
+ ...canManage || offManage ? [{ id: "delete", kind: "manage", label: "Delete", onSelect: run("delete"), disabled: !canManage }] : []
6772
+ ];
6773
+ }
6703
6774
 
6704
6775
  // src/lists/TrashView.tsx
6705
6776
  import { useState as useState30 } from "react";
@@ -7907,7 +7978,7 @@ function Importer({ bridge, readLimits, activeOrganizationId, organizations, onC
7907
7978
 
7908
7979
  // src/extension/PageView.tsx
7909
7980
  import { useState as useState39 } from "react";
7910
- import { DropdownMenu as DropdownMenu5, DropdownMenuContent as DropdownMenuContent5, DropdownMenuItem as DropdownMenuItem5, DropdownMenuTrigger as DropdownMenuTrigger5, SiteIcon } from "@ai-matrx/design-system";
7981
+ import { SiteIcon } from "@ai-matrx/design-system";
7911
7982
  import { Button as Button33 } from "@ai-matrx/design-system/controls";
7912
7983
  import { GlobeIcon as GlobeIcon4 } from "@ai-matrx/design-system/icons";
7913
7984
 
@@ -8236,22 +8307,10 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
8236
8307
  ] })
8237
8308
  ] }),
8238
8309
  onFill ? /* @__PURE__ */ jsx38(Button33, { variant: "primary", disabled: busy, "aria-busy": running || void 0, onClick: onFill, "aria-label": `Fill ${item?.name ?? "this login"}`, children: running ? "Filling\u2026" : "Fill" }) : null,
8310
+ onSignIn ? /* @__PURE__ */ jsx38(Button33, { variant: onFill ? "outline" : "primary", icon: /* @__PURE__ */ jsx38(ShieldGlyph, {}), disabled: busy, onClick: onSignIn, "aria-label": `Sign me in with ${item?.name ?? "this login"}`, title: "Sign me in", children: "Sign me in" }) : null,
8239
8311
  user && item ? /* @__PURE__ */ jsx38(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx38(UserGlyph, {}), "aria-label": `Copy ${user.label}`, title: `Copy ${user.label}`, disabled: reading, onClick: () => void read(user.label, () => port.reveal(item, user.key)) }) : null,
8240
8312
  secret && item ? /* @__PURE__ */ jsx38(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx38(KeyGlyph, {}), "aria-label": "Copy password", title: "Copy password", disabled: reading, onClick: () => void read("Password", () => port.reveal(item, secret.key)) }) : null,
8241
- otp ? /* @__PURE__ */ jsx38(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx38(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,
8242
- onSignIn ? /* @__PURE__ */ jsxs36(DropdownMenu5, { children: [
8243
- /* @__PURE__ */ jsx38(DropdownMenuTrigger5, { asChild: true, children: /* @__PURE__ */ jsx38(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx38(MoreGlyph, {}), "aria-label": "More", disabled: busy }) }),
8244
- /* @__PURE__ */ jsxs36(DropdownMenuContent5, { align: "end", children: [
8245
- /* @__PURE__ */ jsxs36(DropdownMenuItem5, { onSelect: onSignIn, children: [
8246
- /* @__PURE__ */ jsx38(ShieldGlyph, { className: "size-4" }),
8247
- "Sign me in"
8248
- ] }),
8249
- /* @__PURE__ */ jsxs36(DropdownMenuItem5, { onSelect: () => onOpen(itemId), children: [
8250
- /* @__PURE__ */ jsx38(ListGlyph, { className: "size-4" }),
8251
- "Open item"
8252
- ] })
8253
- ] })
8254
- ] }) : null
8313
+ otp ? /* @__PURE__ */ jsx38(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx38(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
8255
8314
  ] });
8256
8315
  }
8257
8316
 
@@ -8363,7 +8422,7 @@ function Group({ title, children }) {
8363
8422
  children
8364
8423
  ] });
8365
8424
  }
8366
- function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false }) {
8425
+ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false, onLock, onOpenWeb }) {
8367
8426
  const [tagsOpen, setTagsOpen] = useState40(view.startsWith("tag:"));
8368
8427
  return /* @__PURE__ */ jsxs37("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: [
8369
8428
  /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2 px-3 pb-2 pt-3", children: [
@@ -8402,7 +8461,9 @@ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, v
8402
8461
  /* @__PURE__ */ jsx39(ShieldGlyph, { className: "size-4 shrink-0" }),
8403
8462
  /* @__PURE__ */ jsx39("span", { className: "min-w-0 flex-1 truncate", children: "System AutoFill" }),
8404
8463
  /* @__PURE__ */ jsx39("span", { className: cn15("text-xs", nativeProvider.status === "ready" ? "text-success" : "text-warning"), children: nativeProvider.status === "ready" ? "On" : "Set up" })
8405
- ] }) : null
8464
+ ] }) : null,
8465
+ onOpenWeb ? /* @__PURE__ */ jsx39(Entry, { on: false, glyph: ExternalGlyph, label: "Open on the web", onClick: onOpenWeb }) : null,
8466
+ onLock ? /* @__PURE__ */ jsx39(Entry, { on: false, glyph: LockGlyph, label: "Lock", onClick: onLock }) : null
8406
8467
  ] })
8407
8468
  ] });
8408
8469
  }
@@ -8445,12 +8506,42 @@ function ImportWithLimits(props) {
8445
8506
  if (state && "limits" in state) return /* @__PURE__ */ jsx40(ImportPanel, { ...rest, limits: state.limits });
8446
8507
  return /* @__PURE__ */ jsx40(Panel, { title: "Import", onClose: rest.onClose, children: /* @__PURE__ */ jsx40(Empty, { children: state ? state.error : "Reading this organization\u2019s import settings\u2026" }) });
8447
8508
  }
8448
- function VaultApp(props) {
8509
+ function VaultApp(given) {
8510
+ const props = { ...given, host: useOrganizationSettings(given.host ?? NO_HOST) };
8449
8511
  const lock = props.host?.lock;
8450
8512
  const locked = useLocked(lock);
8451
8513
  const port = useMemo6(() => lock ? guardPort(props.port, () => lock.locked()) : props.port, [props.port, lock]);
8452
8514
  return /* @__PURE__ */ jsx40(LookScope, { look: props.look ?? "web", className: "h-full min-h-0 w-full", children: /* @__PURE__ */ jsx40("div", { className: "relative h-full min-h-0 w-full", children: /* @__PURE__ */ jsx40(NoticeProvider, { notify: props.notify, children: lock && locked ? /* @__PURE__ */ jsx40(LockedScreen, { lock }) : /* @__PURE__ */ jsx40(ClipboardProvider, { clipboard: props.host?.clipboard, children: /* @__PURE__ */ jsx40(RevealSecondsProvider, { seconds: props.host?.revealSeconds, children: /* @__PURE__ */ jsx40(IdentityGate, { identity: props.host?.identity, children: /* @__PURE__ */ jsx40(GeneratorLimitsProvider, { resolve: generatorLimitsFrom(props.host?.knobs), children: /* @__PURE__ */ jsx40(Workspace, { ...props, port }) }) }) }) }) }) }) });
8453
8515
  }
8516
+ function useOrganizationSettings(host) {
8517
+ const knobs = host.knobs;
8518
+ const needs = knobs !== void 0 && (host.revealSeconds === void 0 || host.backupPassphraseMinLength === void 0);
8519
+ const [read, setRead] = useState41(null);
8520
+ useEffect23(() => {
8521
+ if (!needs || !knobs) return;
8522
+ let live = true;
8523
+ knobs().then(
8524
+ (r) => live && setRead({ reveal: positiveKnob(r, REVEAL_SECONDS_KNOB), backupMin: positiveKnob(r, BACKUP_PASSPHRASE_MIN_KNOB) }),
8525
+ () => void 0
8526
+ );
8527
+ return () => {
8528
+ live = false;
8529
+ };
8530
+ }, [needs, knobs]);
8531
+ if (!read) return host;
8532
+ return {
8533
+ ...host,
8534
+ ...host.revealSeconds === void 0 && read.reveal !== null ? { revealSeconds: read.reveal } : {},
8535
+ ...host.backupPassphraseMinLength === void 0 && read.backupMin !== null ? { backupPassphraseMinLength: read.backupMin } : {}
8536
+ };
8537
+ }
8538
+ var NO_STORE = () => () => void 0;
8539
+ var NOTHING = () => null;
8540
+ function useHostPrompts(capture, fillSetup) {
8541
+ const prompt = useSyncExternalStore5(capture ? capture.subscribe : NO_STORE, capture ? capture.get : NOTHING, capture ? capture.get : NOTHING);
8542
+ const setup = useSyncExternalStore5(fillSetup ? fillSetup.subscribe : NO_STORE, fillSetup ? fillSetup.get : NOTHING, fillSetup ? fillSetup.get : NOTHING);
8543
+ return prompt !== null || setup?.status === "off";
8544
+ }
8454
8545
  function LockedScreen({ lock }) {
8455
8546
  const [busy, setBusy] = useState41(false);
8456
8547
  const [error, setError] = useState41(null);
@@ -8538,6 +8629,16 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8538
8629
  document.addEventListener("keydown", onKey);
8539
8630
  return () => document.removeEventListener("keydown", onKey);
8540
8631
  }, []);
8632
+ const keepSearchFocus = useRef19(false);
8633
+ useEffect23(() => {
8634
+ if (!keepSearchFocus.current) return;
8635
+ keepSearchFocus.current = false;
8636
+ const search = rootRef.current?.querySelector('input[aria-label^="Search"]');
8637
+ if (!search || document.activeElement === search) return;
8638
+ search.focus();
8639
+ search.setSelectionRange(search.value.length, search.value.length);
8640
+ }, [loc.q]);
8641
+ const hasPrompts = useHostPrompts(host.capture, host.fillSetup);
8541
8642
  const knobsRef = useRef19(host.knobs);
8542
8643
  knobsRef.current = host.knobs;
8543
8644
  const [signedOut, setSignedOut] = useState41(false);
@@ -8680,6 +8781,25 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8680
8781
  notify(e instanceof Error ? e.message : "Favorites could not be saved.", "error");
8681
8782
  });
8682
8783
  };
8784
+ const deletedItem = (id) => {
8785
+ setItems((cur) => cur.filter((i) => i.id !== id));
8786
+ dirty.current = false;
8787
+ if (loc.itemId === id) move({ ...loc, itemId: null, mode: "view", panel: null }, "push");
8788
+ notify("Moved to Trash");
8789
+ load();
8790
+ };
8791
+ const rowAction = (item, action) => {
8792
+ if (action !== "delete") return go({ view: loc.view === "page" ? "all" : loc.view, itemId: item.id, mode: "view", panel: action });
8793
+ void confirmDelete(item).then(async (ok) => {
8794
+ if (!ok) return;
8795
+ try {
8796
+ await port.remove(item);
8797
+ deletedItem(item.id);
8798
+ } catch (e) {
8799
+ notify(e instanceof Error && e.message ? e.message : "Deleting failed.", "error");
8800
+ }
8801
+ });
8802
+ };
8683
8803
  if (error) {
8684
8804
  return /* @__PURE__ */ jsxs38("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: [
8685
8805
  /* @__PURE__ */ jsx40(SafeGlyph, { className: "size-8 text-muted-foreground" }),
@@ -8718,38 +8838,38 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8718
8838
  onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null })
8719
8839
  }
8720
8840
  ),
8721
- /* @__PURE__ */ jsxs38(DropdownMenu6, { children: [
8722
- /* @__PURE__ */ jsx40(DropdownMenuTrigger6, { asChild: true, children: /* @__PURE__ */ jsx40(Button34, { variant: "quiet", icon: /* @__PURE__ */ jsx40(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
8723
- /* @__PURE__ */ jsxs38(DropdownMenuContent6, { align: "end", children: [
8724
- /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "import" }), children: [
8841
+ /* @__PURE__ */ jsxs38(DropdownMenu5, { children: [
8842
+ /* @__PURE__ */ jsx40(DropdownMenuTrigger5, { asChild: true, children: /* @__PURE__ */ jsx40(Button34, { variant: "quiet", icon: /* @__PURE__ */ jsx40(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
8843
+ /* @__PURE__ */ jsxs38(DropdownMenuContent5, { align: "end", children: [
8844
+ /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: () => go({ panel: "import" }), children: [
8725
8845
  /* @__PURE__ */ jsx40(ImportGlyph, { className: "size-4" }),
8726
8846
  "Import"
8727
8847
  ] }),
8728
- /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "export" }), children: [
8848
+ /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: () => go({ panel: "export" }), children: [
8729
8849
  /* @__PURE__ */ jsx40(ExportGlyph, { className: "size-4" }),
8730
8850
  "Export"
8731
8851
  ] }),
8732
- /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "backup" }), children: [
8852
+ /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: () => go({ panel: "backup" }), children: [
8733
8853
  /* @__PURE__ */ jsx40(SafeGlyph, { className: "size-4" }),
8734
8854
  "Backup"
8735
8855
  ] }),
8736
- /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "devices" }), children: [
8856
+ /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: () => go({ panel: "devices" }), children: [
8737
8857
  /* @__PURE__ */ jsx40(ShieldGlyph, { className: "size-4" }),
8738
8858
  "Browsers that fill"
8739
8859
  ] }),
8740
- host.nativeImport ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "import-passkeys" }), children: [
8860
+ host.nativeImport ? /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: () => go({ panel: "import-passkeys" }), children: [
8741
8861
  /* @__PURE__ */ jsx40(ImportGlyph, { className: "size-4" }),
8742
8862
  "Import passkeys"
8743
8863
  ] }) : null,
8744
- host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: host.nativeProvider.openSetup, children: [
8864
+ host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: host.nativeProvider.openSetup, children: [
8745
8865
  /* @__PURE__ */ jsx40(ShieldGlyph, { className: "size-4" }),
8746
8866
  "System AutoFill"
8747
8867
  ] }) : null,
8748
- host.lock?.lock ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => void host.lock?.lock?.(), children: [
8868
+ host.lock?.lock ? /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: () => void host.lock?.lock?.(), children: [
8749
8869
  /* @__PURE__ */ jsx40(LockGlyph, { className: "size-4" }),
8750
8870
  "Lock"
8751
8871
  ] }) : null,
8752
- host.openWebVault ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: host.openWebVault, children: [
8872
+ host.openWebVault ? /* @__PURE__ */ jsxs38(DropdownMenuItem5, { onSelect: host.openWebVault, children: [
8753
8873
  /* @__PURE__ */ jsx40(ExternalGlyph, { className: "size-4" }),
8754
8874
  "Open on the web"
8755
8875
  ] }) : null
@@ -8763,6 +8883,27 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8763
8883
  compactActions
8764
8884
  ] })
8765
8885
  ] });
8886
+ const newItem = () => go({ itemId: null, mode: "new", kind: null, panel: null });
8887
+ const pageHeader = /* @__PURE__ */ jsxs38("div", { className: "flex flex-col gap-2", "data-vault-page-header": "", children: [
8888
+ compactHeader,
8889
+ /* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-2", children: [
8890
+ /* @__PURE__ */ jsx40(
8891
+ SearchField4,
8892
+ {
8893
+ value: loc.q,
8894
+ onChange: (e) => {
8895
+ keepSearchFocus.current = true;
8896
+ go({ q: e.target.value }, "replace");
8897
+ },
8898
+ placeholder: "Search the vault",
8899
+ "aria-label": "Search the vault",
8900
+ className: "min-w-0 flex-1"
8901
+ }
8902
+ ),
8903
+ compactActions,
8904
+ /* @__PURE__ */ jsx40(Button34, { variant: "primary", icon: /* @__PURE__ */ jsx40(PlusGlyph, {}), "aria-label": "New item", onClick: newItem })
8905
+ ] })
8906
+ ] });
8766
8907
  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 };
8767
8908
  const pageView = loc.view === "page" && host.page && !loc.q ? /* @__PURE__ */ jsx40(
8768
8909
  PageView,
@@ -8770,8 +8911,6 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8770
8911
  port,
8771
8912
  page: host.page,
8772
8913
  fill: host.fill,
8773
- capture: host.capture,
8774
- fillSetup: host.fillSetup,
8775
8914
  newPasswordFields: host.newPasswordFields,
8776
8915
  items: inThisVault,
8777
8916
  definitions,
@@ -8779,10 +8918,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8779
8918
  onNewForPage: (url) => go({ mode: "new", kind: null, url, itemId: null, panel: null }),
8780
8919
  onBrowse: () => go({ view: "all", itemId: null }),
8781
8920
  onChanged: load,
8782
- ...compactHeader ? { header: /* @__PURE__ */ jsxs38("div", { className: "flex flex-col gap-2", children: [
8783
- compactHeader,
8784
- /* @__PURE__ */ jsx40("div", { className: "flex justify-end", children: compactActions })
8785
- ] }) } : {}
8921
+ header: pageHeader
8786
8922
  }
8787
8923
  ) : null;
8788
8924
  const specialView = pageView ?? (loc.view === "authenticator" ? /* @__PURE__ */ jsx40(AuthenticatorView, { ...viewProps }) : loc.view === "trash" ? /* @__PURE__ */ jsx40(TrashView, { ...viewProps }) : null);
@@ -8797,11 +8933,15 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8797
8933
  selectedId: loc.itemId,
8798
8934
  onSelect: (id) => go({ itemId: id, mode: "view", kind: null, panel: null }),
8799
8935
  query: loc.q,
8800
- onQuery: (q) => go({ q }, "replace"),
8936
+ onQuery: (q) => {
8937
+ if (loc.view === "page" && host.page && !q) keepSearchFocus.current = true;
8938
+ go({ q }, "replace");
8939
+ },
8801
8940
  sort: loc.sort,
8802
8941
  onSort: (sort) => go({ sort }, "replace"),
8803
8942
  canSortViewed: port.lists.recents !== void 0,
8804
- onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
8943
+ onNew: newItem,
8944
+ onItemAction: rowAction,
8805
8945
  onRefresh: refresh,
8806
8946
  refreshing,
8807
8947
  ...snap.pending && snap.pending.length > 0 ? { stillLoading: snap.pending } : {},
@@ -8876,13 +9016,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8876
9016
  setNotice(n);
8877
9017
  notify("Saved");
8878
9018
  },
8879
- onDeleted: (id) => {
8880
- setItems((cur) => cur.filter((i) => i.id !== id));
8881
- dirty.current = false;
8882
- move({ ...loc, itemId: null, mode: "view", panel: null }, "push");
8883
- notify("Moved to Trash");
8884
- load();
8885
- },
9019
+ onDeleted: deletedItem,
8886
9020
  onDirty: (d) => {
8887
9021
  dirty.current = d;
8888
9022
  },
@@ -8943,6 +9077,13 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8943
9077
  /* @__PURE__ */ jsx40("span", { className: "min-w-0 flex-1 truncate text-warning", title: refreshError, children: "Couldn\u2019t refresh. Showing what was loaded." }),
8944
9078
  /* @__PURE__ */ jsx40("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
8945
9079
  ] }) : null,
9080
+ hasPrompts ? (
9081
+ // "Save this login?" and "Turn on filling" show above the list in every view, not only "This page".
9082
+ /* @__PURE__ */ jsxs38("div", { className: "flex flex-col gap-3 border-b border-border px-3 py-3", "data-vault-host-prompts": "", children: [
9083
+ host.fillSetup ? /* @__PURE__ */ jsx40(FillSetupCard, { fillSetup: host.fillSetup }) : null,
9084
+ host.capture ? /* @__PURE__ */ jsx40(CaptureCard, { capture: host.capture, onChanged: load }) : null
9085
+ ] })
9086
+ ) : null,
8946
9087
  snap.unavailable.length > 0 ? /* @__PURE__ */ jsxs38("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-4 py-2 text-sm", children: [
8947
9088
  /* @__PURE__ */ jsxs38("span", { className: "min-w-0 flex-1 truncate text-warning", children: [
8948
9089
  "Couldn\u2019t load ",
@@ -8968,7 +9109,9 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8968
9109
  onPanel: (p) => go({ panel: p }),
8969
9110
  nativeProvider: host.nativeProvider,
8970
9111
  hasNativeImport: host.nativeImport !== void 0,
8971
- hasPage: host.page !== void 0
9112
+ hasPage: host.page !== void 0,
9113
+ onLock: host.lock?.lock ? () => void host.lock?.lock?.() : void 0,
9114
+ onOpenWeb: host.openWebVault
8972
9115
  }
8973
9116
  ) : null,
8974
9117
  shown.list ? /* @__PURE__ */ jsx40("div", { className: specialView && mode !== "stack" ? "col-span-2 min-h-0" : "min-h-0", children: list }) : null,
@@ -9941,20 +10084,6 @@ function createApiVaultPort(hostApi, options = {}) {
9941
10084
  };
9942
10085
  }
9943
10086
 
9944
- // src/knobs.ts
9945
- import { createKnobReader } from "@ai-matrx/data/knobs";
9946
- var FRESH_MS = 6e4;
9947
- function createVaultKnobs(client, address) {
9948
- const reader = createKnobReader(client);
9949
- let kept = null;
9950
- return async () => {
9951
- if (kept && Date.now() - kept.at < FRESH_MS) return kept.resolved;
9952
- const snap = await reader.snapshot(address);
9953
- kept = { at: Date.now(), resolved: snap.resolved };
9954
- return snap.resolved;
9955
- };
9956
- }
9957
-
9958
10087
  // src/agent-context.ts
9959
10088
  var hostOf = (url) => {
9960
10089
  try {
@@ -10022,6 +10151,7 @@ function emptyReadAlarm(total, emptyIds, holdings) {
10022
10151
  return `${staleAlarm(total)} (The check that tells an empty credential from an unreadable one did not answer either: ${holdings.why})`;
10023
10152
  }
10024
10153
  export {
10154
+ BACKUP_PASSPHRASE_MIN_KNOB,
10025
10155
  CATEGORIES,
10026
10156
  CONFIRM_RETURN_KEY,
10027
10157
  DEFAULT_LOCATION,
@@ -10030,9 +10160,11 @@ export {
10030
10160
  HOLDINGS_DOOR_MIGRATION,
10031
10161
  IdentityGate,
10032
10162
  LOCKED_MESSAGE,
10163
+ REVEAL_SECONDS_KNOB,
10033
10164
  VaultApp,
10034
10165
  VaultLockedError,
10035
10166
  categoryOf,
10167
+ copiedLine,
10036
10168
  copyValue,
10037
10169
  createApiVaultPort,
10038
10170
  createSupabaseIdentity,
@@ -10049,11 +10181,13 @@ export {
10049
10181
  matchesQuery,
10050
10182
  needsRecentSignIn,
10051
10183
  parseDefinitions,
10184
+ positiveKnob,
10052
10185
  providerOf,
10053
10186
  rowMenuEntriesAt,
10054
10187
  siteDomainOf,
10055
10188
  sortItems,
10056
10189
  useConfirmed,
10190
+ useDisposable,
10057
10191
  vaultInventoryScope,
10058
10192
  vaultItemFacts,
10059
10193
  wasCancelled