@ai-matrx/vault 0.1.36 → 0.1.37

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
@@ -58,7 +58,7 @@ import {
58
58
  } from "./chunk-B5TOAIY6.js";
59
59
 
60
60
  // src/VaultApp.tsx
61
- import { useCallback as useCallback4, useEffect as useEffect21, useMemo as useMemo6, useRef as useRef17, useState as useState39, useSyncExternalStore as useSyncExternalStore5 } from "react";
61
+ import { useCallback as useCallback4, useEffect as useEffect22, useMemo as useMemo6, useRef as useRef18, useState as useState40, useSyncExternalStore as useSyncExternalStore5 } from "react";
62
62
  import { DropdownMenu as DropdownMenu5, DropdownMenuContent as DropdownMenuContent5, DropdownMenuItem as DropdownMenuItem5, DropdownMenuTrigger as DropdownMenuTrigger5, LookScope } from "@ai-matrx/design-system";
63
63
  import { Button as Button33, SearchField as SearchField4, Select as Select12 } from "@ai-matrx/design-system/controls";
64
64
  import { confirm as confirm14 } from "@ai-matrx/kit/confirm-opener";
@@ -881,11 +881,11 @@ function whenInDay(iso, now = /* @__PURE__ */ new Date()) {
881
881
  if (dayKey(d) === dayKey(now)) return formatRelativeTime(iso, { now: now.getTime() });
882
882
  return d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
883
883
  }
884
- function collapseRuns(rows, keyOf) {
884
+ function collapseRuns(rows, keyOf2) {
885
885
  const out = [];
886
886
  const at = /* @__PURE__ */ new Map();
887
887
  for (const row of rows) {
888
- const k = keyOf(row);
888
+ const k = keyOf2(row);
889
889
  const seen = at.get(k);
890
890
  if (seen) seen.times += 1;
891
891
  else {
@@ -1408,6 +1408,14 @@ function canDecodeQrLocally() {
1408
1408
  function hasCamera() {
1409
1409
  return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
1410
1410
  }
1411
+ async function hasCameraDevice() {
1412
+ if (!hasCamera() || typeof navigator.mediaDevices?.enumerateDevices !== "function") return false;
1413
+ try {
1414
+ return (await navigator.mediaDevices.enumerateDevices()).some((d) => d.kind === "videoinput");
1415
+ } catch {
1416
+ return false;
1417
+ }
1418
+ }
1411
1419
  async function decodeQr(source) {
1412
1420
  return decodeQrFromElement(source);
1413
1421
  }
@@ -1439,17 +1447,15 @@ function useCanScan() {
1439
1447
  }, []);
1440
1448
  return ok;
1441
1449
  }
1442
- function QrScanPanel({ open, onResult, onClose }) {
1450
+ function QrScanInline({ onResult, onClose }) {
1443
1451
  const video = useRef6(null);
1444
1452
  const [error, setError] = useState8(null);
1445
1453
  const done = useRef6(onResult);
1446
1454
  done.current = onResult;
1447
1455
  useEffect8(() => {
1448
- if (!open) return;
1449
1456
  let stream = null;
1450
1457
  let live = true;
1451
1458
  let timer2 = null;
1452
- setError(null);
1453
1459
  const stop = () => {
1454
1460
  if (timer2) clearTimeout(timer2);
1455
1461
  stream?.getTracks().forEach((t) => t.stop());
@@ -1492,13 +1498,26 @@ function QrScanPanel({ open, onResult, onClose }) {
1492
1498
  live = false;
1493
1499
  stop();
1494
1500
  };
1495
- }, [open]);
1496
- return /* @__PURE__ */ jsxs6(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */ jsx7(Button5, { variant: "quiet", onClick: onClose, children: "Cancel" }), children: [
1497
- /* @__PURE__ */ jsxs6("div", { className: "relative mx-auto aspect-square w-full max-w-sm overflow-hidden rounded-xl bg-muted", children: [
1501
+ }, []);
1502
+ if (error) {
1503
+ return /* @__PURE__ */ jsxs6("p", { className: "flex items-center gap-2 px-1 pt-1 text-xs text-muted-foreground", role: "status", "data-camera-note": "", children: [
1504
+ /* @__PURE__ */ jsx7(CameraGlyph, { className: "size-3.5 shrink-0", "aria-hidden": true }),
1505
+ /* @__PURE__ */ jsxs6("span", { className: "min-w-0 flex-1 truncate", children: [
1506
+ error,
1507
+ " Paste the setup key or choose a QR image."
1508
+ ] }),
1509
+ /* @__PURE__ */ jsx7(Button5, { variant: "quiet", onClick: onClose, children: "Dismiss" })
1510
+ ] });
1511
+ }
1512
+ return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center gap-1.5 pt-2", role: "region", "aria-label": "Scan QR code", "data-camera-preview": "", children: [
1513
+ /* @__PURE__ */ jsxs6("div", { className: "relative aspect-square w-full max-w-56 overflow-hidden rounded-xl bg-muted", children: [
1498
1514
  /* @__PURE__ */ jsx7("video", { ref: video, className: "size-full object-cover", playsInline: true, muted: true, "aria-label": "Camera" }),
1499
- error ? /* @__PURE__ */ jsx7("div", { className: "absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-destructive", children: error }) : /* @__PURE__ */ jsx7("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
1515
+ /* @__PURE__ */ jsx7("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
1500
1516
  ] }),
1501
- /* @__PURE__ */ jsx7("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
1517
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
1518
+ /* @__PURE__ */ jsx7("span", { className: "text-xs text-muted-foreground", children: "Point at the site's QR code" }),
1519
+ /* @__PURE__ */ jsx7(Button5, { variant: "quiet", onClick: onClose, children: "Stop camera" })
1520
+ ] })
1502
1521
  ] });
1503
1522
  }
1504
1523
  function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status, autoStartCamera = false }) {
@@ -1508,8 +1527,15 @@ function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "
1508
1527
  const autoStarted = useRef6(false);
1509
1528
  useEffect8(() => {
1510
1529
  if (!autoStartCamera || !canScan || disabled || autoStarted.current || value) return;
1511
- autoStarted.current = true;
1512
- setScanning(true);
1530
+ let live = true;
1531
+ void hasCameraDevice().then((has) => {
1532
+ if (!live || !has || autoStarted.current) return;
1533
+ autoStarted.current = true;
1534
+ setScanning(true);
1535
+ });
1536
+ return () => {
1537
+ live = false;
1538
+ };
1513
1539
  }, [autoStartCamera, canScan, disabled, value]);
1514
1540
  const [reading, setReading] = useState8(false);
1515
1541
  const [imageError, setImageError] = useState8(null);
@@ -1611,10 +1637,9 @@ function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "
1611
1637
  ]
1612
1638
  }
1613
1639
  ),
1614
- canScan ? /* @__PURE__ */ jsx7(
1615
- QrScanPanel,
1640
+ canScan && scanning ? /* @__PURE__ */ jsx7(
1641
+ QrScanInline,
1616
1642
  {
1617
- open: scanning,
1618
1643
  onClose: () => setScanning(false),
1619
1644
  onResult: (text) => {
1620
1645
  setScanning(false);
@@ -2728,12 +2753,56 @@ function createVaultKnobs(client, address) {
2728
2753
  }
2729
2754
  var REVEAL_SECONDS_KNOB = "desktop.vault.reveal_hide_seconds";
2730
2755
  var BACKUP_PASSPHRASE_MIN_KNOB = "desktop.vault.backup_passphrase_min_length";
2756
+ var ORGANIZATION_SEARCH_AT_KNOB = "vault.organization_search_at";
2757
+ var ORGANIZATION_SEARCH_AT_UNREAD = 8;
2731
2758
  function positiveKnob(resolved, key) {
2732
2759
  const v = resolved[key];
2733
2760
  const n = typeof v === "string" && v.trim() !== "" ? Number(v) : v;
2734
2761
  return typeof n === "number" && Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
2735
2762
  }
2736
2763
 
2764
+ // src/organizations.ts
2765
+ import { shortUuid } from "@ai-matrx/design-system/record-id-format";
2766
+ function orderOrganizations(orgs, items, recentItemIds) {
2767
+ const counts = /* @__PURE__ */ new Map();
2768
+ for (const i of items) if (i.owner.organizationId) counts.set(i.owner.organizationId, (counts.get(i.owner.organizationId) ?? 0) + 1);
2769
+ const byId = new Map(items.map((i) => [i.id, i]));
2770
+ const recent = /* @__PURE__ */ new Map();
2771
+ for (const id of recentItemIds ?? []) {
2772
+ const org = byId.get(id)?.owner.organizationId;
2773
+ if (org && !recent.has(org)) recent.set(org, recent.size);
2774
+ }
2775
+ const group = (o) => recent.has(o.id) ? 0 : (counts.get(o.id) ?? 0) > 0 ? 1 : 2;
2776
+ return [...orgs].sort((a, b) => group(a) - group(b) || (recent.get(a.id) ?? 0) - (recent.get(b.id) ?? 0) || a.name.localeCompare(b.name, void 0, { sensitivity: "base" }) || a.id.localeCompare(b.id));
2777
+ }
2778
+ function organizationHints(orgs) {
2779
+ const seen = /* @__PURE__ */ new Map();
2780
+ for (const o of orgs) seen.set(o.name.trim().toLowerCase(), (seen.get(o.name.trim().toLowerCase()) ?? 0) + 1);
2781
+ return new Map(orgs.filter((o) => (seen.get(o.name.trim().toLowerCase()) ?? 0) > 1).map((o) => [o.id, shortUuid(o.id)]));
2782
+ }
2783
+
2784
+ // src/shown-until-move.ts
2785
+ import { useEffect as useEffect11, useRef as useRef9, useState as useState14 } from "react";
2786
+ var keyOf = (loc) => `${String(loc.vault)}|${loc.view}|${loc.itemId ?? ""}|${loc.mode}|${loc.panel ?? ""}|${loc.kind ?? ""}`;
2787
+ function useShownUntilMove(loc) {
2788
+ const [shown, setShown] = useState14(null);
2789
+ const home = useRef9(null);
2790
+ const key = keyOf(loc);
2791
+ useEffect11(() => {
2792
+ if (!shown) {
2793
+ home.current = null;
2794
+ return;
2795
+ }
2796
+ if (home.current?.id !== shown.id) home.current = { id: shown.id, key: null };
2797
+ if (home.current.key === null) {
2798
+ if (loc.itemId === shown.id && loc.mode === "view") home.current.key = key;
2799
+ return;
2800
+ }
2801
+ if (key !== home.current.key) setShown(null);
2802
+ }, [shown, key, loc.itemId, loc.mode]);
2803
+ return [shown, setShown];
2804
+ }
2805
+
2737
2806
  // src/lock.ts
2738
2807
  import { useSyncExternalStore as useSyncExternalStore2 } from "react";
2739
2808
  var LOCKED_MESSAGE = "The vault is locked. Unlock it first.";
@@ -2775,7 +2844,7 @@ function useLocked(lock) {
2775
2844
 
2776
2845
  // src/io/BackupPanel.tsx
2777
2846
  import { formatFileSize } from "@ai-matrx/kit/format";
2778
- import { useEffect as useEffect11, useRef as useRef9, useState as useState15 } from "react";
2847
+ import { useEffect as useEffect12, useRef as useRef10, useState as useState16 } from "react";
2779
2848
  import { Button as Button12, Field as Field5, SegmentedControl as SegmentedControl3, Switch as Switch3 } from "@ai-matrx/design-system/controls";
2780
2849
  import { downloadFile } from "@ai-matrx/kit/download";
2781
2850
  import { confirm as confirm4 } from "@ai-matrx/kit/confirm-opener";
@@ -2868,7 +2937,7 @@ function startNewRestoreRun(preview) {
2868
2937
  }
2869
2938
 
2870
2939
  // src/io/lines.tsx
2871
- import { useState as useState14 } from "react";
2940
+ import { useState as useState15 } from "react";
2872
2941
  import { Checkbox } from "@ai-matrx/design-system";
2873
2942
  import { Badge as Badge4, Button as Button11, DisclosureHeader as DisclosureHeader2 } from "@ai-matrx/design-system/controls";
2874
2943
  import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
@@ -2881,7 +2950,7 @@ function PickLine({ checked, onChange, disabled = false, title, detail, badge, t
2881
2950
  ] });
2882
2951
  }
2883
2952
  function Windowed({ items, size = 150, render }) {
2884
- const [all, setAll] = useState14(false);
2953
+ const [all, setAll] = useState15(false);
2885
2954
  const shown = all ? items : items.slice(0, size);
2886
2955
  return /* @__PURE__ */ jsxs12(Fragment8, { children: [
2887
2956
  shown.map(render),
@@ -2892,7 +2961,7 @@ function Windowed({ items, size = 150, render }) {
2892
2961
  ] });
2893
2962
  }
2894
2963
  function More({ title = "More", children }) {
2895
- const [open, setOpen] = useState14(false);
2964
+ const [open, setOpen] = useState15(false);
2896
2965
  return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", children: [
2897
2966
  /* @__PURE__ */ jsx13(DisclosureHeader2, { open, title, onClick: () => setOpen((o) => !o) }),
2898
2967
  open ? children : null
@@ -2932,21 +3001,21 @@ function Omissions({ omissions, accepted, onAccepted, verb }) {
2932
3001
  function BackupPanel({ port, host, items, onClose, onChanged }) {
2933
3002
  const minLength = passphraseMinimum(host.backupPassphraseMinLength);
2934
3003
  const confirmed = useConfirmed();
2935
- const fileInput = useRef9(null);
2936
- const abort = useRef9(null);
2937
- const [mode, setMode] = useState15("backup");
2938
- const [busy, setBusy] = useState15(false);
2939
- const [error, setError] = useState15(null);
2940
- const [passphrase, setPassphrase] = useState15("");
2941
- const [repeat, setRepeat] = useState15("");
2942
- const [accepted, setAccepted] = useState15(false);
2943
- const [selected, setSelected] = useState15(/* @__PURE__ */ new Set());
2944
- const [backup, setBackup] = useState15(null);
2945
- const [file, setFile] = useState15(null);
2946
- const [restorePreview, setRestorePreview] = useState15(null);
2947
- const [runId, setRunId] = useState15(null);
2948
- const [result, setResult] = useState15(null);
2949
- useEffect11(() => () => abort.current?.abort(), []);
3004
+ const fileInput = useRef10(null);
3005
+ const abort = useRef10(null);
3006
+ const [mode, setMode] = useState16("backup");
3007
+ const [busy, setBusy] = useState16(false);
3008
+ const [error, setError] = useState16(null);
3009
+ const [passphrase, setPassphrase] = useState16("");
3010
+ const [repeat, setRepeat] = useState16("");
3011
+ const [accepted, setAccepted] = useState16(false);
3012
+ const [selected, setSelected] = useState16(/* @__PURE__ */ new Set());
3013
+ const [backup, setBackup] = useState16(null);
3014
+ const [file, setFile] = useState16(null);
3015
+ const [restorePreview, setRestorePreview] = useState16(null);
3016
+ const [runId, setRunId] = useState16(null);
3017
+ const [result, setResult] = useState16(null);
3018
+ useEffect12(() => () => abort.current?.abort(), []);
2950
3019
  const switchMode = (next) => {
2951
3020
  abort.current?.abort();
2952
3021
  setMode(next);
@@ -3131,7 +3200,7 @@ function tooShort(passphrase, minLength) {
3131
3200
  }
3132
3201
 
3133
3202
  // src/io/ExportPanel.tsx
3134
- import { useMemo, useState as useState16 } from "react";
3203
+ import { useMemo, useState as useState17 } from "react";
3135
3204
  import { Button as Button13, Select as Select5 } from "@ai-matrx/design-system/controls";
3136
3205
  import { downloadFile as downloadFile2 } from "@ai-matrx/kit/download";
3137
3206
  import { confirm as confirm5 } from "@ai-matrx/kit/confirm-opener";
@@ -3156,11 +3225,11 @@ function omissionText2(omissions) {
3156
3225
  function ExportPanel({ port, items, onClose }) {
3157
3226
  const confirmed = useConfirmed();
3158
3227
  const logins = useMemo(() => items.filter((i) => i.definitionKey === WEBSITE_LOGIN), [items]);
3159
- const [profile, setProfile] = useState16("matrx_login_csv_v1");
3160
- const [selected, setSelected] = useState16(/* @__PURE__ */ new Set());
3161
- const [preview, setPreview] = useState16(null);
3162
- const [busy, setBusy] = useState16(false);
3163
- const [error, setError] = useState16(null);
3228
+ const [profile, setProfile] = useState17("matrx_login_csv_v1");
3229
+ const [selected, setSelected] = useState17(/* @__PURE__ */ new Set());
3230
+ const [preview, setPreview] = useState17(null);
3231
+ const [busy, setBusy] = useState17(false);
3232
+ const [error, setError] = useState17(null);
3164
3233
  const format = FORMATS.find((f) => f.value === profile);
3165
3234
  const reasons = new Map(preview?.items.map((p) => [p.item_id, p]) ?? []);
3166
3235
  const eligible = preview?.items.filter((p) => p.eligible).length ?? 0;
@@ -3285,7 +3354,7 @@ function ExportPanel({ port, items, onClose }) {
3285
3354
  }
3286
3355
 
3287
3356
  // src/io/ImportPanel.tsx
3288
- import { useEffect as useEffect12, useMemo as useMemo2, useRef as useRef10, useState as useState17 } from "react";
3357
+ import { useEffect as useEffect13, useMemo as useMemo2, useRef as useRef11, useState as useState18 } from "react";
3289
3358
  import { Button as Button14, Select as Select6, Switch as Switch4, Textarea as Textarea2 } from "@ai-matrx/design-system/controls";
3290
3359
 
3291
3360
  // src/io/csv-import.ts
@@ -4147,25 +4216,25 @@ var STATE_BADGE = {
4147
4216
  var ME = "me";
4148
4217
  function ImportPanel({ port, host, items, definitions, organizations, onClose, onChanged, limits }) {
4149
4218
  const confirmed = useConfirmed();
4150
- const [otpFor, setOtpFor] = useState17(null);
4151
- const fileInput = useRef10(null);
4152
- const abort = useRef10(null);
4153
- const stop = useRef10(false);
4154
- const keys = useRef10(/* @__PURE__ */ new Map());
4155
- const [source, setSource] = useState17("auto");
4156
- const [envText, setEnvText] = useState17("");
4157
- const [injectSandbox, setInjectSandbox] = useState17(false);
4158
- const [fileName, setFileName] = useState17(null);
4159
- const [reading, setReading] = useState17(false);
4160
- const [draft, setDraft] = useState17(null);
4161
- const [destination, setDestination] = useState17(ME);
4162
- const [browserFill, setBrowserFill] = useState17(false);
4163
- const [approvedVisible, setApprovedVisible] = useState17(false);
4164
- const [selected, setSelected] = useState17(/* @__PURE__ */ new Set());
4165
- const [running, setRunning] = useState17(null);
4166
- const [done, setDone] = useState17(null);
4167
- const [error, setError] = useState17(null);
4168
- useEffect12(
4219
+ const [otpFor, setOtpFor] = useState18(null);
4220
+ const fileInput = useRef11(null);
4221
+ const abort = useRef11(null);
4222
+ const stop = useRef11(false);
4223
+ const keys = useRef11(/* @__PURE__ */ new Map());
4224
+ const [source, setSource] = useState18("auto");
4225
+ const [envText, setEnvText] = useState18("");
4226
+ const [injectSandbox, setInjectSandbox] = useState18(false);
4227
+ const [fileName, setFileName] = useState18(null);
4228
+ const [reading, setReading] = useState18(false);
4229
+ const [draft, setDraft] = useState18(null);
4230
+ const [destination, setDestination] = useState18(ME);
4231
+ const [browserFill, setBrowserFill] = useState18(false);
4232
+ const [approvedVisible, setApprovedVisible] = useState18(false);
4233
+ const [selected, setSelected] = useState18(/* @__PURE__ */ new Set());
4234
+ const [running, setRunning] = useState18(null);
4235
+ const [done, setDone] = useState18(null);
4236
+ const [error, setError] = useState18(null);
4237
+ useEffect13(
4169
4238
  () => () => {
4170
4239
  abort.current?.abort();
4171
4240
  stop.current = true;
@@ -4452,14 +4521,14 @@ function Notices({ draft }) {
4452
4521
  }
4453
4522
 
4454
4523
  // src/ItemList.tsx
4455
- import { useEffect as useEffect15, useRef as useRef13, useState as useState26 } from "react";
4524
+ import { useEffect as useEffect16, useRef as useRef14, useState as useState27 } from "react";
4456
4525
  import { DropdownMenu as DropdownMenu4, DropdownMenuContent as DropdownMenuContent4, DropdownMenuItem as DropdownMenuItem4, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator as DropdownMenuSeparator4, DropdownMenuTrigger as DropdownMenuTrigger4 } from "@ai-matrx/design-system";
4457
4526
  import { cn as cn11 } from "@ai-matrx/design-system";
4458
4527
  import { Button as Button22, SearchField, Tile as Tile3 } from "@ai-matrx/design-system/controls";
4459
4528
  import { CopyIcon as CopyIcon7 } from "@ai-matrx/design-system/icons";
4460
4529
 
4461
4530
  // src/ItemPane.tsx
4462
- import { Fragment as Fragment16, useEffect as useEffect14, useMemo as useMemo3, useRef as useRef12, useState as useState25 } from "react";
4531
+ import { Fragment as Fragment16, useEffect as useEffect15, useMemo as useMemo3, useRef as useRef13, useState as useState26 } from "react";
4463
4532
  import { DropdownMenu as DropdownMenu3, DropdownMenuContent as DropdownMenuContent3, DropdownMenuItem as DropdownMenuItem3, DropdownMenuSeparator as DropdownMenuSeparator3, DropdownMenuTrigger as DropdownMenuTrigger3 } from "@ai-matrx/design-system";
4464
4533
  import { cn as cn10 } from "@ai-matrx/design-system";
4465
4534
  import { Badge as Badge7, Button as Button21, CONTROLS_CONTAINER_CLASS, OptionCombobox, Switch as Switch6 } from "@ai-matrx/design-system/controls";
@@ -4467,7 +4536,7 @@ import { confirm as confirm10 } from "@ai-matrx/kit/confirm-opener";
4467
4536
 
4468
4537
  // src/activity/FilesSection.tsx
4469
4538
  import { formatFileSize as formatFileSize2 } from "@ai-matrx/kit/format";
4470
- import { useRef as useRef11, useState as useState18 } from "react";
4539
+ import { useRef as useRef12, useState as useState19 } from "react";
4471
4540
  import { Badge as Badge5, Button as Button15, Field as Field6, SegmentedControl as SegmentedControl4 } from "@ai-matrx/design-system/controls";
4472
4541
  import { cn as cn7 } from "@ai-matrx/design-system";
4473
4542
  import { confirm as confirm6 } from "@ai-matrx/kit/confirm-opener";
@@ -4485,11 +4554,11 @@ function FilesSection({ item, port, editing, onChanged }) {
4485
4554
  const confirmed = useConfirmed();
4486
4555
  const canEdit = editing && item.capabilities.canEdit;
4487
4556
  const canAdd = item.capabilities.canEdit;
4488
- const [busy, setBusy] = useState18(null);
4489
- const [error, setError] = useState18(null);
4490
- const [pending, setPending] = useState18(null);
4491
- const [dragging, setDragging] = useState18(false);
4492
- const picker = useRef11(null);
4557
+ const [busy, setBusy] = useState19(null);
4558
+ const [error, setError] = useState19(null);
4559
+ const [pending, setPending] = useState19(null);
4560
+ const [dragging, setDragging] = useState19(false);
4561
+ const picker = useRef12(null);
4493
4562
  if (!canAdd && item.attachments.length === 0) return null;
4494
4563
  async function run(key, work, fallback) {
4495
4564
  setBusy(key);
@@ -4566,8 +4635,8 @@ function FilesSection({ item, port, editing, onChanged }) {
4566
4635
  );
4567
4636
  }
4568
4637
  function EditableFile({ port, item: it, file: a, busy: b, run: go, onDownload, onTooBig }) {
4569
- const [open, setOpen] = useState18(false);
4570
- const replacer = useRef11(null);
4638
+ const [open, setOpen] = useState19(false);
4639
+ const replacer = useRef12(null);
4571
4640
  async function remove() {
4572
4641
  const yes = await confirm6({ title: `Delete ${a.label}?`, description: "The encrypted file is removed from this item.", confirmLabel: "Delete", variant: "destructive" });
4573
4642
  if (yes) await go(`delete:${a.id}`, () => port.activity.deleteFile(it, a.id), "The file could not be deleted.");
@@ -4625,9 +4694,9 @@ function Protection({ value, locked, onChange }) {
4625
4694
  );
4626
4695
  }
4627
4696
  function NewFile({ file, busy, onAdd, onCancel }) {
4628
- const [label, setLabel] = useState18(file.name);
4629
- const [description, setDescription] = useState18("");
4630
- const [handling, setHandling] = useState18("revealable");
4697
+ const [label, setLabel] = useState19(file.name);
4698
+ const [description, setDescription] = useState19("");
4699
+ const [handling, setHandling] = useState19("revealable");
4631
4700
  async function add() {
4632
4701
  if (handling === "sealed" && !await confirmSeal(label)) return;
4633
4702
  await onAdd({ label: label.trim(), description: description.trim() || null, handling });
@@ -4647,10 +4716,10 @@ function NewFile({ file, busy, onAdd, onCancel }) {
4647
4716
  ] });
4648
4717
  }
4649
4718
  function FileDetailsRows({ file, busy, onSave, onCancel }) {
4650
- const [label, setLabel] = useState18(file.label);
4651
- const [description, setDescription] = useState18(file.description);
4652
- const [fileName, setFileName] = useState18(file.fileName);
4653
- const [handling, setHandling] = useState18(file.handling);
4719
+ const [label, setLabel] = useState19(file.label);
4720
+ const [description, setDescription] = useState19(file.description);
4721
+ const [fileName, setFileName] = useState19(file.fileName);
4722
+ const [handling, setHandling] = useState19(file.handling);
4654
4723
  const changed = label !== file.label || description !== file.description || fileName !== file.fileName || handling !== file.handling;
4655
4724
  async function save() {
4656
4725
  if (handling === "sealed" && file.handling !== "sealed" && !await confirmSeal(label)) return;
@@ -4676,10 +4745,10 @@ function confirmSeal(label) {
4676
4745
  });
4677
4746
  }
4678
4747
  function DraftFilesSection({ files, onFiles }) {
4679
- const [pending, setPending] = useState18(null);
4680
- const [error, setError] = useState18(null);
4681
- const [dragging, setDragging] = useState18(false);
4682
- const picker = useRef11(null);
4748
+ const [pending, setPending] = useState19(null);
4749
+ const [error, setError] = useState19(null);
4750
+ const [dragging, setDragging] = useState19(false);
4751
+ const picker = useRef12(null);
4683
4752
  const choose = (file) => {
4684
4753
  if (!file) return;
4685
4754
  if (fileTooBig(file.size)) return setError(TOO_BIG);
@@ -4728,7 +4797,7 @@ function DraftFilesSection({ files, onFiles }) {
4728
4797
  }
4729
4798
 
4730
4799
  // src/fields/RecoveryCodesRow.tsx
4731
- import { useState as useState19 } from "react";
4800
+ import { useState as useState20 } from "react";
4732
4801
  import { Badge as Badge6, Button as Button16, Textarea as Textarea3 } from "@ai-matrx/design-system/controls";
4733
4802
  import { CopyIcon as CopyIcon6 } from "@ai-matrx/design-system/icons";
4734
4803
  import { Fragment as Fragment13, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
@@ -4738,9 +4807,9 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4738
4807
  const confirmed = useConfirmed();
4739
4808
  const copyValue2 = useCopy();
4740
4809
  const secret = useTransientSecret();
4741
- const [busy, setBusy] = useState19(false);
4742
- const [error, setError] = useState19(null);
4743
- const [draft, setDraft] = useState19(null);
4810
+ const [busy, setBusy] = useState20(false);
4811
+ const [error, setError] = useState20(null);
4812
+ const [draft, setDraft] = useState20(null);
4744
4813
  const canEdit = item.capabilities.canEdit;
4745
4814
  const run = async (step) => {
4746
4815
  setBusy(true);
@@ -4835,7 +4904,7 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
4835
4904
  }
4836
4905
 
4837
4906
  // src/fields/DraftRows.tsx
4838
- import { useState as useState20 } from "react";
4907
+ import { useState as useState21 } from "react";
4839
4908
  import { Button as Button17, Field as Field7, SegmentedControl as SegmentedControl5, Select as Select7, Switch as Switch5 } from "@ai-matrx/design-system/controls";
4840
4909
  import { RecordId } from "@ai-matrx/design-system";
4841
4910
  import { confirm as confirm7 } from "@ai-matrx/kit/confirm-opener";
@@ -4890,7 +4959,7 @@ function FieldSettingsRows({ name, value, defaultHandling = "revealable", onChan
4890
4959
  var hasSecretSettings = (s) => saysMore(s, "revealable");
4891
4960
  var hasDeclaredSettings = (s, defaultHandling) => Boolean(s && saysMore(s, defaultHandling));
4892
4961
  function SecretBuilderRow({ field, onChange, onRemove }) {
4893
- const [open, setOpen] = useState20(hasSecretSettings(field));
4962
+ const [open, setOpen] = useState21(hasSecretSettings(field));
4894
4963
  const handling = field.handling ?? "revealable";
4895
4964
  const name = field.label.trim() || "Secret";
4896
4965
  return /* @__PURE__ */ jsxs18(Fragment14, { children: [
@@ -5131,7 +5200,7 @@ function targetLabel(t) {
5131
5200
  }
5132
5201
 
5133
5202
  // src/extension/UseOnSiteButton.tsx
5134
- import { useState as useState21 } from "react";
5203
+ import { useState as useState22 } from "react";
5135
5204
  import { Button as Button18 } from "@ai-matrx/design-system/controls";
5136
5205
  import { jsx as jsx20 } from "react/jsx-runtime";
5137
5206
  function canUseOnSite(item, pageUrl) {
@@ -5139,7 +5208,7 @@ function canUseOnSite(item, pageUrl) {
5139
5208
  }
5140
5209
  function UseOnSiteButton({ port, item, pageUrl, onChanged }) {
5141
5210
  const notify = useNotify();
5142
- const [busy, setBusy] = useState21(false);
5211
+ const [busy, setBusy] = useState22(false);
5143
5212
  if (!canUseOnSite(item, pageUrl)) return null;
5144
5213
  const site = pageSite(pageUrl);
5145
5214
  const run = async () => {
@@ -5158,7 +5227,7 @@ function UseOnSiteButton({ port, item, pageUrl, onChanged }) {
5158
5227
  }
5159
5228
 
5160
5229
  // src/sharing/SharePanel.tsx
5161
- import { useEffect as useEffect13, useState as useState24 } from "react";
5230
+ import { useEffect as useEffect14, useState as useState25 } from "react";
5162
5231
  import { confirm as confirm9 } from "@ai-matrx/kit/confirm-opener";
5163
5232
  import { Button as Button20, DisclosureHeader as DisclosureHeader3, Field as Field9, RegionSkeleton, Select as Select9 } from "@ai-matrx/design-system/controls";
5164
5233
  import { cn as cn9 } from "@ai-matrx/design-system";
@@ -5181,14 +5250,17 @@ function isEmail(value) {
5181
5250
  }
5182
5251
  var PERSONAL = "personal";
5183
5252
  var destinationOf = (key) => key === PERSONAL ? null : key;
5253
+ var orgChoice = (o, hints) => ({ value: o.id, label: o.name, ...hints.has(o.id) ? { hint: hints.get(o.id) } : {} });
5184
5254
  function moveDestinations(item, orgs) {
5185
5255
  if (item.owner.kind === "shared") return [];
5186
5256
  const here = item.owner.organizationId;
5187
- return [...here ? [{ value: PERSONAL, label: "Personal" }] : [], ...orgs.filter((o) => o.id !== here).map((o) => ({ value: o.id, label: o.name }))];
5257
+ const hints = organizationHints(orgs);
5258
+ return [...here ? [{ value: PERSONAL, label: "Personal" }] : [], ...orgs.filter((o) => o.id !== here).map((o) => orgChoice(o, hints))];
5188
5259
  }
5189
5260
  function copyDestinations(orgs, item) {
5190
5261
  const here = item ? item.owner.kind === "me" ? PERSONAL : item.owner.organizationId : void 0;
5191
- return [{ value: PERSONAL, label: "Personal" }, ...orgs.map((o) => ({ value: o.id, label: o.name }))].filter((d) => d.value !== here);
5262
+ const hints = organizationHints(orgs);
5263
+ return [{ value: PERSONAL, label: "Personal" }, ...orgs.map((o) => orgChoice(o, hints))].filter((d) => d.value !== here);
5192
5264
  }
5193
5265
  function copyRefusal(message2) {
5194
5266
  return /duplicate/i.test(message2) ? "There is already a copy of this item there." : null;
@@ -5216,7 +5288,7 @@ function shareSummary(grants) {
5216
5288
  var MEMBER_CHOICE_DEFAULT = "default";
5217
5289
 
5218
5290
  // src/sharing/parts.tsx
5219
- import { useState as useState22 } from "react";
5291
+ import { useState as useState23 } from "react";
5220
5292
  import { cn as cn8 } from "@ai-matrx/design-system";
5221
5293
  import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
5222
5294
  var message = (e, fallback) => {
@@ -5225,9 +5297,9 @@ var message = (e, fallback) => {
5225
5297
  };
5226
5298
  function useConfirmedChange() {
5227
5299
  const confirmed = useConfirmed();
5228
- const [busy, setBusy] = useState22(null);
5229
- const [error, setError] = useState22(null);
5230
- const [notice, setNotice] = useState22(null);
5300
+ const [busy, setBusy] = useState23(null);
5301
+ const [error, setError] = useState23(null);
5302
+ const [notice, setNotice] = useState23(null);
5231
5303
  async function run(key, fn, done) {
5232
5304
  setBusy(key);
5233
5305
  setError(null);
@@ -5261,18 +5333,20 @@ function StatusLine({ error, notice }) {
5261
5333
  }
5262
5334
 
5263
5335
  // src/sharing/MovePanel.tsx
5264
- import { useState as useState23 } from "react";
5336
+ import { useState as useState24 } from "react";
5265
5337
  import { confirm as confirm8 } from "@ai-matrx/kit/confirm-opener";
5266
5338
  import { Button as Button19, Field as Field8, Select as Select8 } from "@ai-matrx/design-system/controls";
5267
5339
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
5268
5340
  var NONE = "__none__";
5269
- function MovePanel({ item, port, organizations, onClose, onChanged }) {
5341
+ function MovePanel({ item, port, host, organizations, onClose, onChanged, onOpenItem }) {
5270
5342
  const { busy, error, notice, run, setNotice } = useConfirmedChange();
5271
5343
  const moves = moveDestinations(item, organizations);
5272
5344
  const copies = copyDestinations(organizations, item);
5273
- const [moveTo, setMoveTo] = useState23("");
5274
- const [copyTo, setCopyTo] = useState23("");
5275
- const [email, setEmail] = useState23("");
5345
+ const [moveTo, setMoveTo] = useState24("");
5346
+ const [copyTo, setCopyTo] = useState24("");
5347
+ const [email, setEmail] = useState24("");
5348
+ const [made, setMade] = useState24(null);
5349
+ const searchAt = host.organizationSearchAt ?? ORGANIZATION_SEARCH_AT_UNREAD;
5276
5350
  const passkey = isPasskey(item);
5277
5351
  const canMove = item.capabilities.canManage && !passkey && moves.length > 0;
5278
5352
  const canGive = item.capabilities.canManage && item.owner.kind === "me" && !passkey;
@@ -5306,8 +5380,10 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5306
5380
  };
5307
5381
  const copy = async () => {
5308
5382
  if (!copyChoice) return;
5309
- const made = await run("copy", () => port.sharing.copy(item, destinationOf(copyChoice.value)), `Copy made in ${copyChoice.label}`);
5310
- if (made) onChanged();
5383
+ const copied = await run("copy", () => port.sharing.copy(item, destinationOf(copyChoice.value)), `Copy made in ${copyChoice.label}`);
5384
+ if (!copied) return;
5385
+ setMade(copied);
5386
+ onChanged();
5311
5387
  };
5312
5388
  const nothing = !canMove && !canGive && !canCopy;
5313
5389
  return /* @__PURE__ */ jsx22(
@@ -5316,7 +5392,10 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5316
5392
  title: "Move or copy",
5317
5393
  size: "md",
5318
5394
  onClose,
5319
- footer: /* @__PURE__ */ jsx22(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx22(StatusLine, { error, notice }) : null, children: /* @__PURE__ */ jsx22(Button19, { variant: "primary", onClick: onClose, children: "Done" }) }),
5395
+ footer: /* @__PURE__ */ jsxs20(PanelFooter, { status: error || notice ? /* @__PURE__ */ jsx22(StatusLine, { error, notice }) : null, children: [
5396
+ made && onOpenItem ? /* @__PURE__ */ jsx22(Button19, { variant: "outline", onClick: () => onOpenItem(made), children: "Open copy" }) : null,
5397
+ /* @__PURE__ */ jsx22(Button19, { variant: "primary", onClick: onClose, children: "Done" })
5398
+ ] }),
5320
5399
  children: nothing ? /* @__PURE__ */ jsx22(VaultSection, { children: /* @__PURE__ */ jsx22(Empty, { children: passkey ? "A passkey stays where it was created." : "Only the owner can move or give this item." }) }) : /* @__PURE__ */ jsxs20(VaultSection, { children: [
5321
5400
  canMove && moves.length > 0 ? /* @__PURE__ */ jsx22(
5322
5401
  VaultRow,
@@ -5324,7 +5403,7 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5324
5403
  label: "Move to",
5325
5404
  hint: moveChoice ? itemMoveConsequence(item, destinationOf(moveChoice.value)) : void 0,
5326
5405
  actions: /* @__PURE__ */ jsx22(Button19, { variant: "outline", disabled: busy !== null || !moveChoice, onClick: () => void move(), children: "Move" }),
5327
- children: /* @__PURE__ */ jsx22(Select8, { className: "w-full", "aria-label": "Move to", value: moveTo || NONE, onValueChange: (v) => setMoveTo(v === NONE ? "" : v), options: [{ value: NONE, label: "Choose where" }, ...moves] })
5406
+ children: /* @__PURE__ */ jsx22(Select8, { className: "w-full", "aria-label": "Move to", searchAt, value: moveTo || NONE, onValueChange: (v) => setMoveTo(v === NONE ? "" : v), options: [{ value: NONE, label: "Choose where" }, ...moves] })
5328
5407
  }
5329
5408
  ) : null,
5330
5409
  canGive ? /* @__PURE__ */ jsx22(VaultRow, { label: "Give to", hint: "You lose all access; your shares end", actions: /* @__PURE__ */ jsx22(Button19, { variant: "danger", disabled: busy !== null || !isEmail(email), onClick: () => void give(), children: "Give" }), children: /* @__PURE__ */ jsx22(
@@ -5341,7 +5420,7 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5341
5420
  onChange: (e) => setEmail(e.target.value)
5342
5421
  }
5343
5422
  ) }) : null,
5344
- canCopy && copies.length > 0 ? /* @__PURE__ */ jsx22(VaultRow, { label: "Copy to", hint: "A separate item; edits no longer sync", actions: /* @__PURE__ */ jsx22(Button19, { variant: "outline", disabled: busy !== null || !copyChoice, onClick: () => void copy(), children: "Copy" }), children: /* @__PURE__ */ jsx22(Select8, { className: "w-full", "aria-label": "Copy to", value: copyTo || NONE, onValueChange: (v) => setCopyTo(v === NONE ? "" : v), options: [{ value: NONE, label: "Choose where" }, ...copies] }) }) : null
5423
+ canCopy && copies.length > 0 ? /* @__PURE__ */ jsx22(VaultRow, { label: "Copy to", hint: "A separate item; edits no longer sync", actions: /* @__PURE__ */ jsx22(Button19, { variant: "outline", disabled: busy !== null || !copyChoice, onClick: () => void copy(), children: "Copy" }), children: /* @__PURE__ */ jsx22(Select8, { className: "w-full", "aria-label": "Copy to", searchAt, value: copyTo || NONE, onValueChange: (v) => setCopyTo(v === NONE ? "" : v), options: [{ value: NONE, label: "Choose where" }, ...copies] }) }) : null
5345
5424
  ] })
5346
5425
  }
5347
5426
  );
@@ -5350,11 +5429,11 @@ function MovePanel({ item, port, organizations, onClose, onChanged }) {
5350
5429
  // src/sharing/SharePanel.tsx
5351
5430
  import { Fragment as Fragment15, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
5352
5431
  function useGrants(port, item) {
5353
- const [grants, setGrants] = useState24(null);
5354
- const [loadError, setLoadError] = useState24(null);
5355
- const [nonce, setNonce] = useState24(0);
5432
+ const [grants, setGrants] = useState25(null);
5433
+ const [loadError, setLoadError] = useState25(null);
5434
+ const [nonce, setNonce] = useState25(0);
5356
5435
  const allowed = item.capabilities.canManage;
5357
- useEffect13(() => {
5436
+ useEffect14(() => {
5358
5437
  if (!allowed) return;
5359
5438
  let live = true;
5360
5439
  setLoadError(null);
@@ -5371,9 +5450,9 @@ function useGrants(port, item) {
5371
5450
  function SharePanel({ item, port, organizations, onClose, onChanged }) {
5372
5451
  const { grants, setGrants, loadError, reload } = useGrants(port, item);
5373
5452
  const { busy, error, notice, run } = useConfirmedChange();
5374
- const [email, setEmail] = useState24("");
5375
- const [level, setLevel] = useState24("use");
5376
- const [more, setMore] = useState24(false);
5453
+ const [email, setEmail] = useState25("");
5454
+ const [level, setLevel] = useState25("use");
5455
+ const [more, setMore] = useState25(false);
5377
5456
  const isOrg = item.owner.kind === "organization" && item.owner.organizationId !== null;
5378
5457
  const orgName = item.owner.label;
5379
5458
  const people = (grants ?? []).filter((g) => g.kind === "person");
@@ -5492,8 +5571,8 @@ function SharePanel({ item, port, organizations, onClose, onChanged }) {
5492
5571
  function IntoOrganization({ item, port, organizations, grants, onShared }) {
5493
5572
  const { busy, error, notice, run } = useConfirmedChange();
5494
5573
  const open = organizations.filter((o) => !grants.some((g) => g.organizationId === o.id));
5495
- const [target, setTarget] = useState24("");
5496
- const [level, setLevel] = useState24("use");
5574
+ const [target, setTarget] = useState25("");
5575
+ const [level, setLevel] = useState25("use");
5497
5576
  const chosen = open.find((o) => o.id === target);
5498
5577
  if (item.owner.kind !== "me") return null;
5499
5578
  return /* @__PURE__ */ jsx23(VaultSection, { title: "Share into organization", children: open.length > 0 ? /* @__PURE__ */ jsxs21(Fragment15, { children: [
@@ -5528,10 +5607,10 @@ var CHOICES = [
5528
5607
  { value: "editor", label: "Can edit", meta: "See and change" }
5529
5608
  ];
5530
5609
  function MemberAccess({ port, organizationId, organizationName }) {
5531
- const [list, setList] = useState24(null);
5532
- const [loadError, setLoadError] = useState24(null);
5610
+ const [list, setList] = useState25(null);
5611
+ const [loadError, setLoadError] = useState25(null);
5533
5612
  const { busy, error, run } = useConfirmedChange();
5534
- useEffect13(() => {
5613
+ useEffect14(() => {
5535
5614
  let live = true;
5536
5615
  port.sharing.memberAccess(organizationId).then(
5537
5616
  (l) => live && setList(l),
@@ -5563,9 +5642,9 @@ function MemberAccess({ port, organizationId, organizationName }) {
5563
5642
  ] });
5564
5643
  }
5565
5644
  function SharingSummary({ item, port, className }) {
5566
- const [text, setText] = useState24("");
5645
+ const [text, setText] = useState25("");
5567
5646
  const allowed = item.capabilities.canManage;
5568
- useEffect13(() => {
5647
+ useEffect14(() => {
5569
5648
  if (!allowed) return;
5570
5649
  let live = true;
5571
5650
  port.sharing.grants(item).then(
@@ -5596,7 +5675,7 @@ function ItemPane(props) {
5596
5675
  const editing = mode !== "view";
5597
5676
  const seedName = seed?.name ?? "";
5598
5677
  const seedUrl = seed?.url ?? "";
5599
- const [loaded, setLoaded] = useState25({});
5678
+ const [loaded, setLoaded] = useState26({});
5600
5679
  const base = useMemo3(() => {
5601
5680
  const b = draftOf(item);
5602
5681
  if (!item) {
@@ -5606,28 +5685,28 @@ function ItemPane(props) {
5606
5685
  b.values = { ...loaded };
5607
5686
  return b;
5608
5687
  }, [item, seedName, seedUrl, loaded]);
5609
- const [draft, setDraft] = useState25(base);
5610
- const [tagsText, setTagsText] = useState25(base.tags.join(", "));
5611
- const [showTags, setShowTags] = useState25(false);
5612
- const [showDescription, setShowDescription] = useState25(false);
5613
- const [showRecovery, setShowRecovery] = useState25(false);
5614
- const [settingUpOtp, setSettingUpOtp] = useState25(false);
5615
- const [owner, setOwner] = useState25(defaultOwner ?? "me");
5616
- const [recipient, setRecipient] = useState25("");
5617
- const [generatePrivately, setGeneratePrivately] = useState25(false);
5618
- const [busy, setBusy] = useState25(false);
5619
- const [sent, setSent] = useState25(null);
5620
- const [queuedFiles, setQueuedFiles] = useState25([]);
5621
- const [showBrowser, setShowBrowser] = useState25(false);
5622
- const [error, setError] = useState25(null);
5623
- const [destinations, setDestinations] = useState25({});
5624
- const [savedNew, setSavedNew] = useState25(false);
5688
+ const [draft, setDraft] = useState26(base);
5689
+ const [tagsText, setTagsText] = useState26(base.tags.join(", "));
5690
+ const [showTags, setShowTags] = useState26(false);
5691
+ const [showDescription, setShowDescription] = useState26(false);
5692
+ const [showRecovery, setShowRecovery] = useState26(false);
5693
+ const [settingUpOtp, setSettingUpOtp] = useState26(false);
5694
+ const [owner, setOwner] = useState26(defaultOwner ?? "me");
5695
+ const [recipient, setRecipient] = useState26("");
5696
+ const [generatePrivately, setGeneratePrivately] = useState26(false);
5697
+ const [busy, setBusy] = useState26(false);
5698
+ const [sent, setSent] = useState26(null);
5699
+ const [queuedFiles, setQueuedFiles] = useState26([]);
5700
+ const [showBrowser, setShowBrowser] = useState26(false);
5701
+ const [error, setError] = useState26(null);
5702
+ const [destinations, setDestinations] = useState26({});
5703
+ const [savedNew, setSavedNew] = useState26(false);
5625
5704
  const dirty = editing && !savedNew && isDirty(base, draft);
5626
5705
  const declared = definition?.fields ?? [];
5627
- const prev = useRef12(null);
5628
- const paneRef = useRef12(null);
5706
+ const prev = useRef13(null);
5707
+ const paneRef = useRef13(null);
5629
5708
  const room = useRoomOf(paneRef);
5630
- useEffect14(() => {
5709
+ useEffect15(() => {
5631
5710
  const was = prev.current;
5632
5711
  const id = item?.id ?? null;
5633
5712
  if (was && was.id === id && was.mode === mode && editing) {
@@ -5640,22 +5719,22 @@ function ItemPane(props) {
5640
5719
  }
5641
5720
  prev.current = { base, id, mode };
5642
5721
  }, [base, mode, item?.id, editing]);
5643
- useEffect14(() => {
5722
+ useEffect15(() => {
5644
5723
  setLoaded({});
5645
5724
  setShowTags(false);
5646
5725
  setShowDescription(false);
5647
5726
  setShowRecovery(false);
5648
5727
  setSavedNew(false);
5649
5728
  }, [item?.id, mode]);
5650
- const onDirtyRef = useRef12(onDirty);
5729
+ const onDirtyRef = useRef13(onDirty);
5651
5730
  onDirtyRef.current = onDirty;
5652
- useEffect14(() => onDirtyRef.current(dirty), [dirty]);
5731
+ useEffect15(() => onDirtyRef.current(dirty), [dirty]);
5653
5732
  const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
5654
- useEffect14(() => {
5733
+ useEffect15(() => {
5655
5734
  if (editing && dirty) keepDraft(keepKey, keptFieldsOf(draft), { basedOn: item?.updatedAt ?? "new" });
5656
5735
  else if (!editing) dropKeptDraft(keepKey);
5657
5736
  }, [editing, dirty, draft, keepKey, item?.updatedAt]);
5658
- useEffect14(() => {
5737
+ useEffect15(() => {
5659
5738
  if (!editing || !item) return;
5660
5739
  let live = true;
5661
5740
  for (const f of item.fields) {
@@ -5780,7 +5859,7 @@ function ItemPane(props) {
5780
5859
  const turnOnOne = canTurnOn ? (f) => () => turnOn([f]) : () => void 0;
5781
5860
  const declaredKeys = new Set(declared.map((f) => f.key));
5782
5861
  const hasSecureNote = declaredKeys.has(SECURE_NOTE_KEY) || Boolean(item?.fields.some((f) => f.key === SECURE_NOTE_KEY)) || draft.secrets.some((x) => x.key === SECURE_NOTE_KEY);
5783
- const [settingsToggled, setSettingsToggled] = useState25(/* @__PURE__ */ new Set());
5862
+ const [settingsToggled, setSettingsToggled] = useState26(/* @__PURE__ */ new Set());
5784
5863
  const settingsOpen = (f) => settingsToggled.has(f.key) !== (definition?.key === ENV_VALUE_KEY || hasDeclaredSettings(draft.fieldSettings?.[f.key], f.handling));
5785
5864
  const toggleSettings = (key) => setSettingsToggled((cur) => {
5786
5865
  const next = new Set(cur);
@@ -6157,8 +6236,8 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6157
6236
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
6158
6237
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
6159
6238
  const ordered = [...pinned, ...rest];
6160
- const listRef = useRef13(null);
6161
- useEffect15(() => {
6239
+ const listRef = useRef14(null);
6240
+ useEffect16(() => {
6162
6241
  if (selectedId) listRef.current?.querySelector(`[data-item="${selectedId}"]`)?.scrollIntoView({ block: "nearest" });
6163
6242
  }, [selectedId, items.length]);
6164
6243
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
@@ -6221,8 +6300,8 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6221
6300
  const confirmed = useConfirmed();
6222
6301
  const copy = useCopy();
6223
6302
  const notify = useNotify();
6224
- const [busy, setBusy] = useState26(false);
6225
- const [menuOpen, setMenuOpen] = useState26(false);
6303
+ const [busy, setBusy] = useState27(false);
6304
+ const [menuOpen, setMenuOpen] = useState27(false);
6226
6305
  const canUse = item.capabilities.canUse;
6227
6306
  const user = canUse ? item.fields.find((f) => USERNAME_KEYS.test(f.key) && f.handling !== "sealed" && f.active !== false) : void 0;
6228
6307
  const secret = canUse && item.capabilities.canReveal ? item.fields.find((f) => f.key === "password" && f.handling === "revealable" && f.active !== false) : void 0;
@@ -6261,7 +6340,7 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6261
6340
  ...manageEntries(item, onAction),
6262
6341
  ...hostEntries.map((h) => ({ ...h, kind: h.kind ?? "host" }))
6263
6342
  ];
6264
- const entriesRef = useRef13(entries);
6343
+ const entriesRef = useRef14(entries);
6265
6344
  entriesRef.current = entries;
6266
6345
  const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ jsx25(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ jsx25(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ jsx25(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ jsx25(StarGlyph, { className: cn11("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ jsx25(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" || e.kind === "manage" ? null : /* @__PURE__ */ jsx25(CopyIcon7, { className: "size-4" });
6267
6346
  const menuItem = (e) => /* @__PURE__ */ jsxs23(DropdownMenuItem4, { disabled: e.disabled ?? false, onSelect: e.onSelect, children: [
@@ -6298,10 +6377,11 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6298
6377
  }
6299
6378
  ),
6300
6379
  /* @__PURE__ */ jsxs23("div", { className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-0.5", "data-surface": on ? "primary" : void 0, children: [
6301
- user || secret || favorite ? /* @__PURE__ */ jsxs23("div", { className: cn11("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
6380
+ user || secret || code || favorite ? /* @__PURE__ */ jsxs23("div", { className: cn11("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
6302
6381
  favorite ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx25(StarGlyph, { className: favorite.on ? "fill-current text-warning" : "" }), "aria-label": favorite.on ? "Remove from favorites" : "Add to favorites", "aria-pressed": favorite.on, onClick: favorite.toggle }) : null,
6303
6382
  user ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx25(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
6304
- secret ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx25(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
6383
+ secret ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx25(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null,
6384
+ code ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx25(ClockGlyph, {}), "aria-label": "Copy one-time code", disabled: busy, onClick: copyCode }) : null
6305
6385
  ] }) : null,
6306
6386
  /* @__PURE__ */ jsxs23(DropdownMenu4, { open: menuOpen, onOpenChange: setMenuOpen, children: [
6307
6387
  /* @__PURE__ */ jsx25(DropdownMenuTrigger4, { asChild: true, children: /* @__PURE__ */ jsx25(
@@ -6342,7 +6422,7 @@ function manageEntries(item, onAction) {
6342
6422
  }
6343
6423
 
6344
6424
  // src/lists/TrashView.tsx
6345
- import { useState as useState28 } from "react";
6425
+ import { useState as useState29 } from "react";
6346
6426
  import { Button as Button24 } from "@ai-matrx/design-system/controls";
6347
6427
  import { formatAbsoluteDate as formatAbsoluteDate4 } from "@ai-matrx/kit/format";
6348
6428
 
@@ -6416,7 +6496,7 @@ function refusalText(reason) {
6416
6496
  }
6417
6497
 
6418
6498
  // src/lists/RestorePanel.tsx
6419
- import { useEffect as useEffect16, useState as useState27 } from "react";
6499
+ import { useEffect as useEffect17, useState as useState28 } from "react";
6420
6500
  import { Button as Button23 } from "@ai-matrx/design-system/controls";
6421
6501
  import { Fragment as Fragment18, jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
6422
6502
  var count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
@@ -6427,13 +6507,13 @@ function RestorePanel({
6427
6507
  turnOn,
6428
6508
  onClose
6429
6509
  }) {
6430
- const [preview, setPreview] = useState27(null);
6431
- const [restored, setRestored] = useState27(null);
6432
- const [turnedOn, setTurnedOn] = useState27(null);
6433
- const [error, setError] = useState27(null);
6434
- const [busy, setBusy] = useState27(false);
6435
- const [loading, setLoading] = useState27(Boolean(lists.preview));
6436
- useEffect16(() => {
6510
+ const [preview, setPreview] = useState28(null);
6511
+ const [restored, setRestored] = useState28(null);
6512
+ const [turnedOn, setTurnedOn] = useState28(null);
6513
+ const [error, setError] = useState28(null);
6514
+ const [busy, setBusy] = useState28(false);
6515
+ const [loading, setLoading] = useState28(Boolean(lists.preview));
6516
+ useEffect17(() => {
6437
6517
  if (!lists.preview) return;
6438
6518
  let live = true;
6439
6519
  lists.preview(item).then(
@@ -6537,10 +6617,10 @@ function trashLineOf(item) {
6537
6617
  }
6538
6618
  function TrashView({ port, deleted, query, header, onChanged }) {
6539
6619
  const confirmed = useConfirmed();
6540
- const [busyId, setBusyId] = useState28(null);
6541
- const [message2, setMessage] = useState28(null);
6620
+ const [busyId, setBusyId] = useState29(null);
6621
+ const [message2, setMessage] = useState29(null);
6542
6622
  const shown = deleted.filter((i) => trashItemMatchesQuery(i, query));
6543
- const [restoring, setRestoring] = useState28(null);
6623
+ const [restoring, setRestoring] = useState29(null);
6544
6624
  const restore = async (item, deletionId) => {
6545
6625
  setBusyId(item.id);
6546
6626
  setMessage(null);
@@ -6588,12 +6668,12 @@ function TrashView({ port, deleted, query, header, onChanged }) {
6588
6668
  }
6589
6669
 
6590
6670
  // src/NewItemFlow.tsx
6591
- import { useMemo as useMemo4, useState as useState29 } from "react";
6671
+ import { useMemo as useMemo4, useState as useState30 } from "react";
6592
6672
  import { Button as Button25, Field as Field10, SearchField as SearchField2, Tile as Tile4 } from "@ai-matrx/design-system/controls";
6593
6673
  import { GlobeIcon as GlobeIcon2 } from "@ai-matrx/design-system/icons";
6594
6674
  import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
6595
6675
  function NewItemFlow({ definitions, url, onUrl, onChoose, onCancel }) {
6596
- const [search, setSearch] = useState29("");
6676
+ const [search, setSearch] = useState30("");
6597
6677
  const guess = useMemo4(() => inferFromUrl(url, definitions), [url, definitions]);
6598
6678
  const common = COMMON_DEFINITION_KEYS.map((k) => definitions.find((d) => d.key === k)).filter((d) => d !== void 0);
6599
6679
  const matches = useMemo4(() => {
@@ -6667,7 +6747,7 @@ function NewItemFlow({ definitions, url, onUrl, onChoose, onCancel }) {
6667
6747
  }
6668
6748
 
6669
6749
  // src/sharing/SecureLinkPanel.tsx
6670
- import { useEffect as useEffect17, useRef as useRef14, useState as useState30 } from "react";
6750
+ import { useEffect as useEffect18, useRef as useRef15, useState as useState31 } from "react";
6671
6751
  import { confirm as confirm11 } from "@ai-matrx/kit/confirm-opener";
6672
6752
  import { Checkbox as Checkbox2 } from "@ai-matrx/design-system";
6673
6753
  import { Badge as Badge8, Button as Button26, Field as Field11, RegionSkeleton as RegionSkeleton2, SegmentedControl as SegmentedControl6, Textarea as Textarea4 } from "@ai-matrx/design-system/controls";
@@ -6750,20 +6830,20 @@ import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
6750
6830
  function SecureLinkPanel({ item, port, onClose }) {
6751
6831
  const sharing = port.sharing;
6752
6832
  const { busy, error, notice, run, setNotice } = useConfirmedChange();
6753
- const [remedy, setRemedy] = useState30(null);
6754
- const [options, setOptions] = useState30(null);
6755
- const [optionsError, setOptionsError] = useState30(null);
6756
- const [sent, setSent] = useState30(null);
6757
- const [sentError, setSentError] = useState30(null);
6758
- const [nonce, setNonce] = useState30(0);
6759
- const [email, setEmail] = useState30("");
6760
- const [phone, setPhone] = useState30("");
6761
- const [name, setName] = useState30("");
6762
- const [linkBy, setLinkBy] = useState30("email");
6763
- const [fields, setFields] = useState30([]);
6764
- const [note, setNote] = useState30("");
6765
- const [sameChannelSent, setSameChannelSent] = useState30(false);
6766
- useEffect17(() => {
6833
+ const [remedy, setRemedy] = useState31(null);
6834
+ const [options, setOptions] = useState31(null);
6835
+ const [optionsError, setOptionsError] = useState31(null);
6836
+ const [sent, setSent] = useState31(null);
6837
+ const [sentError, setSentError] = useState31(null);
6838
+ const [nonce, setNonce] = useState31(0);
6839
+ const [email, setEmail] = useState31("");
6840
+ const [phone, setPhone] = useState31("");
6841
+ const [name, setName] = useState31("");
6842
+ const [linkBy, setLinkBy] = useState31("email");
6843
+ const [fields, setFields] = useState31([]);
6844
+ const [note, setNote] = useState31("");
6845
+ const [sameChannelSent, setSameChannelSent] = useState31(false);
6846
+ useEffect18(() => {
6767
6847
  let live = true;
6768
6848
  setOptionsError(null);
6769
6849
  sharing.secureLinkOptions(item).then(
@@ -6778,7 +6858,7 @@ function SecureLinkPanel({ item, port, onClose }) {
6778
6858
  live = false;
6779
6859
  };
6780
6860
  }, [sharing, item]);
6781
- useEffect17(() => {
6861
+ useEffect18(() => {
6782
6862
  const list = sharing.sentSecureLinks;
6783
6863
  if (!list) return;
6784
6864
  let live = true;
@@ -6791,9 +6871,9 @@ function SecureLinkPanel({ item, port, onClose }) {
6791
6871
  live = false;
6792
6872
  };
6793
6873
  }, [sharing, item, nonce]);
6794
- const [phoneTyped, setPhoneTyped] = useState30(false);
6795
- const prefilled = useRef14(false);
6796
- useEffect17(() => {
6874
+ const [phoneTyped, setPhoneTyped] = useState31(false);
6875
+ const prefilled = useRef15(false);
6876
+ useEffect18(() => {
6797
6877
  const lookup = sharing.phoneOnFile;
6798
6878
  if (!lookup || phoneTyped) return;
6799
6879
  const clear = () => {
@@ -6946,7 +7026,7 @@ function sentMeta(row, options) {
6946
7026
  }
6947
7027
 
6948
7028
  // src/devices/DevicesPanel.tsx
6949
- import { useCallback as useCallback3, useEffect as useEffect18, useState as useState31 } from "react";
7029
+ import { useCallback as useCallback3, useEffect as useEffect19, useState as useState32 } from "react";
6950
7030
  import { confirm as confirm12 } from "@ai-matrx/kit/confirm-opener";
6951
7031
  import { Button as Button27, RegionSkeleton as RegionSkeleton3 } from "@ai-matrx/design-system/controls";
6952
7032
  import { formatAbsoluteDate as formatAbsoluteDate6 } from "@ai-matrx/kit/format";
@@ -7037,9 +7117,9 @@ function ErrorLine2({ text }) {
7037
7117
  function DevicesPanel({ port, onClose }) {
7038
7118
  const confirmed = useConfirmed();
7039
7119
  const notify = useNotify();
7040
- const [devices, setDevices] = useState31(null);
7041
- const [error, setError] = useState31(null);
7042
- const [busyId, setBusyId] = useState31(null);
7120
+ const [devices, setDevices] = useState32(null);
7121
+ const [error, setError] = useState32(null);
7122
+ const [busyId, setBusyId] = useState32(null);
7043
7123
  const load = useCallback3(async () => {
7044
7124
  setError(null);
7045
7125
  try {
@@ -7048,7 +7128,7 @@ function DevicesPanel({ port, onClose }) {
7048
7128
  setError(messageOf3(e, "Your browsers could not be read."));
7049
7129
  }
7050
7130
  }, [port]);
7051
- useEffect18(() => {
7131
+ useEffect19(() => {
7052
7132
  void load();
7053
7133
  }, [load]);
7054
7134
  const revoke = async (device) => {
@@ -7106,10 +7186,10 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
7106
7186
  const where = isMac ? "the Mac app" : "the extension";
7107
7187
  const passkeys = port.devices.passkeys;
7108
7188
  const here = passkeys?.available() ?? false;
7109
- const [methods, setMethods] = useState31(null);
7110
- const [busy, setBusy] = useState31(null);
7111
- const [error, setError] = useState31(null);
7112
- const [approvedUntil, setApprovedUntil] = useState31(null);
7189
+ const [methods, setMethods] = useState32(null);
7190
+ const [busy, setBusy] = useState32(null);
7191
+ const [error, setError] = useState32(null);
7192
+ const [approvedUntil, setApprovedUntil] = useState32(null);
7113
7193
  const load = useCallback3(async () => {
7114
7194
  setError(null);
7115
7195
  try {
@@ -7118,7 +7198,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
7118
7198
  setError(messageOf3(e, "Your account could not be checked."));
7119
7199
  }
7120
7200
  }, [port]);
7121
- useEffect18(() => {
7201
+ useEffect19(() => {
7122
7202
  if (key && here) void load();
7123
7203
  }, [key, here, load]);
7124
7204
  const approve = async () => {
@@ -7206,7 +7286,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
7206
7286
  }
7207
7287
 
7208
7288
  // src/native/NativeImportPanel.tsx
7209
- import { useEffect as useEffect20, useMemo as useMemo5, useState as useState33 } from "react";
7289
+ import { useEffect as useEffect21, useMemo as useMemo5, useState as useState34 } from "react";
7210
7290
  import { confirm as confirm13 } from "@ai-matrx/kit/confirm-opener";
7211
7291
  import { Badge as Badge9, Button as Button28, Select as Select10 } from "@ai-matrx/design-system/controls";
7212
7292
 
@@ -7269,19 +7349,19 @@ async function loadFullPreview(bridge, operationId, current) {
7269
7349
  }
7270
7350
 
7271
7351
  // src/native/use-native-import.ts
7272
- import { useEffect as useEffect19, useRef as useRef15, useState as useState32 } from "react";
7352
+ import { useEffect as useEffect20, useRef as useRef16, useState as useState33 } from "react";
7273
7353
  var fail = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
7274
7354
  var RESTART = "Your account changed. The import was stopped; start it again.";
7275
7355
  function useNativeImport(bridge, stillAllowed, onImported) {
7276
- const [screen, setScreen] = useState32({ kind: "idle", message: null });
7277
- const [busy, setBusy] = useState32(false);
7278
- const generation = useRef15(0);
7279
- const mounted = useRef15(false);
7280
- const op = useRef15(null);
7281
- const last = useRef15(null);
7282
- const timer2 = useRef15(null);
7283
- const polls = useRef15(0);
7284
- const refreshRef = useRef15(async () => void 0);
7356
+ const [screen, setScreen] = useState33({ kind: "idle", message: null });
7357
+ const [busy, setBusy] = useState33(false);
7358
+ const generation = useRef16(0);
7359
+ const mounted = useRef16(false);
7360
+ const op = useRef16(null);
7361
+ const last = useRef16(null);
7362
+ const timer2 = useRef16(null);
7363
+ const polls = useRef16(0);
7364
+ const refreshRef = useRef16(async () => void 0);
7285
7365
  const current = (g) => mounted.current && g === generation.current;
7286
7366
  const stopPolling = () => {
7287
7367
  if (timer2.current !== null) clearTimeout(timer2.current);
@@ -7450,9 +7530,9 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7450
7530
  setBusy(false);
7451
7531
  setScreen({ kind: "idle", message: null });
7452
7532
  };
7453
- const invalidateRef = useRef15(invalidate);
7533
+ const invalidateRef = useRef16(invalidate);
7454
7534
  invalidateRef.current = invalidate;
7455
- useEffect19(() => {
7535
+ useEffect20(() => {
7456
7536
  mounted.current = true;
7457
7537
  const off = bridge.subscribe?.(() => {
7458
7538
  if (op.current) invalidateRef.current(RESTART);
@@ -7485,13 +7565,13 @@ function NativeImportPanel({ bridge, readLimits, activeOrganizationId, organizat
7485
7565
  }
7486
7566
  function Importer({ bridge, readLimits, activeOrganizationId, organizations, onClose, onChanged }) {
7487
7567
  const options = useMemo5(() => organizations.map((o) => ({ value: o.id, label: o.name })), [organizations]);
7488
- const [organizationId, setOrganizationId] = useState33(() => organizations.some((o) => o.id === activeOrganizationId) ? activeOrganizationId : organizations[0]?.id ?? "");
7489
- const [limits, setLimits] = useState33(null);
7490
- const [limitError, setLimitError] = useState33(null);
7568
+ const [organizationId, setOrganizationId] = useState34(() => organizations.some((o) => o.id === activeOrganizationId) ? activeOrganizationId : organizations[0]?.id ?? "");
7569
+ const [limits, setLimits] = useState34(null);
7570
+ const [limitError, setLimitError] = useState34(null);
7491
7571
  const flow = useNativeImport(bridge, (id) => organizations.some((o) => o.id === id), onChanged);
7492
7572
  const { screen, busy } = flow;
7493
7573
  const orgName = organizations.find((o) => o.id === organizationId)?.name ?? "this organization";
7494
- useEffect20(() => {
7574
+ useEffect21(() => {
7495
7575
  if (!readLimits || !organizationId) return;
7496
7576
  let live = true;
7497
7577
  setLimits(null);
@@ -7552,21 +7632,21 @@ function Importer({ bridge, readLimits, activeOrganizationId, organizations, onC
7552
7632
  }
7553
7633
 
7554
7634
  // src/extension/PageView.tsx
7555
- import { useState as useState37 } from "react";
7635
+ import { useState as useState38 } from "react";
7556
7636
  import { SiteIcon } from "@ai-matrx/design-system";
7557
7637
  import { Button as Button32, Tile as Tile5 } from "@ai-matrx/design-system/controls";
7558
7638
  import { GlobeIcon as GlobeIcon3 } from "@ai-matrx/design-system/icons";
7559
7639
 
7560
7640
  // src/extension/CaptureCard.tsx
7561
- import { useState as useState34, useSyncExternalStore as useSyncExternalStore3 } from "react";
7641
+ import { useState as useState35, useSyncExternalStore as useSyncExternalStore3 } from "react";
7562
7642
  import { Button as Button29, SearchField as SearchField3 } from "@ai-matrx/design-system/controls";
7563
7643
  import { Fragment as Fragment21, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
7564
7644
  var SEARCH_FROM = 4;
7565
7645
  function CaptureCard({ capture, onChanged }) {
7566
7646
  const prompt = useSyncExternalStore3(capture.subscribe, capture.get, capture.get);
7567
7647
  const notify = useNotify();
7568
- const [busy, setBusy] = useState34(false);
7569
- const [q, setQ] = useState34("");
7648
+ const [busy, setBusy] = useState35(false);
7649
+ const [q, setQ] = useState35("");
7570
7650
  if (!prompt) return null;
7571
7651
  const decide = async (d) => {
7572
7652
  setBusy(true);
@@ -7622,15 +7702,15 @@ function CaptureCard({ capture, onChanged }) {
7622
7702
  }
7623
7703
 
7624
7704
  // src/extension/FillSetupCard.tsx
7625
- import { useRef as useRef16, useState as useState35, useSyncExternalStore as useSyncExternalStore4 } from "react";
7705
+ import { useRef as useRef17, useState as useState36, useSyncExternalStore as useSyncExternalStore4 } from "react";
7626
7706
  import { Button as Button30, Field as Field12 } from "@ai-matrx/design-system/controls";
7627
7707
  import { jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7628
7708
  function FillSetupCard({ fillSetup }) {
7629
7709
  const notify = useNotify();
7630
7710
  const state = useSyncExternalStore4(fillSetup.subscribe, fillSetup.get, fillSetup.get);
7631
- const box = useRef16(null);
7632
- const [busy, setBusy] = useState35(false);
7633
- const [error, setError] = useState35(null);
7711
+ const box = useRef17(null);
7712
+ const [busy, setBusy] = useState36(false);
7713
+ const [error, setError] = useState36(null);
7634
7714
  if (state.status !== "off") return null;
7635
7715
  const finish = async (run) => {
7636
7716
  setBusy(true);
@@ -7695,7 +7775,7 @@ function FillSetupCard({ fillSetup }) {
7695
7775
  }
7696
7776
 
7697
7777
  // src/extension/PageGenerator.tsx
7698
- import { useState as useState36 } from "react";
7778
+ import { useState as useState37 } from "react";
7699
7779
  import { Popover as Popover2, PopoverContent as PopoverContent2, PopoverTrigger as PopoverTrigger2 } from "@ai-matrx/design-system";
7700
7780
  import { Button as Button31 } from "@ai-matrx/design-system/controls";
7701
7781
  import { CopyIcon as CopyIcon8 } from "@ai-matrx/design-system/icons";
@@ -7705,10 +7785,10 @@ function PageGenerator({ fields }) {
7705
7785
  const limits = useGeneratorLimitsSource();
7706
7786
  const copy = useCopy();
7707
7787
  const held = useTransientSecret(HOLD_MS);
7708
- const [open, setOpen] = useState36(false);
7709
- const [busy, setBusy] = useState36(false);
7710
- const [targets, setTargets] = useState36([]);
7711
- const [said, setSaid] = useState36(null);
7788
+ const [open, setOpen] = useState37(false);
7789
+ const [busy, setBusy] = useState37(false);
7790
+ const [targets, setTargets] = useState37([]);
7791
+ const [said, setSaid] = useState37(null);
7712
7792
  if (!limits) return null;
7713
7793
  const say3 = setSaid;
7714
7794
  const put = async (targetId, value2) => {
@@ -7772,8 +7852,8 @@ function PageGenerator({ fields }) {
7772
7852
  // src/extension/PageView.tsx
7773
7853
  import { jsx as jsx35, jsxs as jsxs33 } from "react/jsx-runtime";
7774
7854
  function PageView({ port, page, fill, capture, fillSetup, newPasswordFields, items, definitions, onOpen, onNewForPage, onBrowse, onChanged, header }) {
7775
- const [running, setRunning] = useState37(null);
7776
- const [said, setSaid] = useState37(null);
7855
+ const [running, setRunning] = useState38(null);
7856
+ const [said, setSaid] = useState38(null);
7777
7857
  const site = pageSite(page.url);
7778
7858
  const state = page.matchState ?? { status: "ready" };
7779
7859
  const fillable = new Set(page.fillableItemIds ?? page.matchingItemIds);
@@ -7857,7 +7937,7 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
7857
7937
  const confirmed = useConfirmed();
7858
7938
  const copy = useCopy();
7859
7939
  const notify = useNotify();
7860
- const [reading, setReading] = useState37(false);
7940
+ const [reading, setReading] = useState38(false);
7861
7941
  const usable = item?.capabilities.canUse ?? false;
7862
7942
  const user = usable ? item?.fields.find((f) => USERNAME_KEYS.test(f.key) && f.handling !== "sealed" && f.active) : void 0;
7863
7943
  const secret = usable && item?.capabilities.canReveal ? item?.fields.find((f) => f.key === "password" && f.handling === "revealable" && f.active) : void 0;
@@ -7895,7 +7975,7 @@ function MatchRow({ itemId, item, tag, family, port, busy, running, onOpen, onFi
7895
7975
  }
7896
7976
 
7897
7977
  // src/Sidebar.tsx
7898
- import { useState as useState38 } from "react";
7978
+ import { useState as useState39 } from "react";
7899
7979
  import { cn as cn12, useLook as useLook2 } from "@ai-matrx/design-system";
7900
7980
  import { DisclosureHeader as DisclosureHeader4, Select as Select11, Tile as Tile6 } from "@ai-matrx/design-system/controls";
7901
7981
  import { GlobeIcon as GlobeIcon4 } from "@ai-matrx/design-system/icons";
@@ -7904,6 +7984,7 @@ var AUTHENTICATOR_LABEL = "Authenticator";
7904
7984
  function vaultChoices(items, organizations, _current) {
7905
7985
  const n = (f) => items.filter(f).length;
7906
7986
  const shared = n((i) => i.owner.kind === "shared");
7987
+ const hints = organizationHints(organizations);
7907
7988
  return [
7908
7989
  { value: "all", label: "All items", count: items.length },
7909
7990
  { value: "mine", label: "Personal", count: n((i) => i.owner.kind === "me") },
@@ -7911,11 +7992,11 @@ function vaultChoices(items, organizations, _current) {
7911
7992
  // Every organization the person belongs to, empty ones too: an empty vault opens on its own
7912
7993
  // "Nothing here yet" with New item, so the first credential can go there. (The organization
7913
7994
  // list carries no role, so who may add is the server's answer to the save, said in place.)
7914
- ...organizations.map((o) => ({ value: o.id, label: o.name, count: n((i) => i.owner.organizationId === o.id) }))
7995
+ ...organizations.map((o) => ({ value: o.id, label: o.name, count: n((i) => i.owner.organizationId === o.id), ...hints.has(o.id) ? { hint: hints.get(o.id) } : {} }))
7915
7996
  ];
7916
7997
  }
7917
- function VaultSelect({ value, choices, onChange }) {
7918
- return /* @__PURE__ */ jsx36(Select11, { value, onValueChange: onChange, "aria-label": "Vault", options: choices.map((c) => ({ value: c.value, label: c.label, meta: String(c.count) })) });
7998
+ function VaultSelect({ value, choices, onChange, searchAt = ORGANIZATION_SEARCH_AT_UNREAD }) {
7999
+ return /* @__PURE__ */ jsx36(Select11, { value, onValueChange: onChange, "aria-label": "Vault", searchAt, options: choices.map((c) => ({ value: c.value, label: c.label, meta: String(c.count), ...c.hint ? { hint: c.hint } : {} })) });
7919
8000
  }
7920
8001
  var CATEGORY_GLYPHS = { logins: GlobeIcon4, passkeys: ShieldGlyph, api: KeyGlyph, oauth: PlugGlyph, infra: DatabaseGlyph, env: TerminalGlyph, files: FileGlyph, other: NoteGlyph };
7921
8002
  var familyOf = (key, definitions) => definitions.find((d) => d.key === key)?.family ?? "generic";
@@ -8006,12 +8087,12 @@ function Group({ title, children }) {
8006
8087
  children
8007
8088
  ] });
8008
8089
  }
8009
- function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false, onLock, onOpenWeb }) {
8010
- const [tagsOpen, setTagsOpen] = useState38(view.startsWith("tag:"));
8090
+ function VaultSidebar({ vault, choices, onVault, organizationSearchAt, view, allCount, deletedCount, views, hasFavorites, hasRecents, onView, onPanel, nativeProvider, hasNativeImport = false, hasPage = false, onLock, onOpenWeb }) {
8091
+ const [tagsOpen, setTagsOpen] = useState39(view.startsWith("tag:"));
8011
8092
  return /* @__PURE__ */ jsxs34("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: [
8012
8093
  /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2 px-3 pb-2 pt-3", children: [
8013
8094
  /* @__PURE__ */ jsx36(SafeGlyph, { className: "size-5 shrink-0 text-primary" }),
8014
- /* @__PURE__ */ jsx36("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx36(VaultSelect, { value: vault, choices, onChange: onVault }) })
8095
+ /* @__PURE__ */ jsx36("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx36(VaultSelect, { value: vault, choices, onChange: onVault, searchAt: organizationSearchAt }) })
8015
8096
  ] }),
8016
8097
  /* @__PURE__ */ jsxs34("div", { className: "flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto px-2 pb-3 pt-1", children: [
8017
8098
  /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-0.5", children: [
@@ -8088,8 +8169,8 @@ var generatorLimitsFrom = (knobs) => knobs ? async () => {
8088
8169
  } : void 0;
8089
8170
  function ImportWithLimits(props) {
8090
8171
  const { knobs, ...rest } = props;
8091
- const [state, setState] = useState39(null);
8092
- useEffect21(() => {
8172
+ const [state, setState] = useState40(null);
8173
+ useEffect22(() => {
8093
8174
  if (!knobs) return setState({ error: "Import is not set up for this organization yet." });
8094
8175
  knobs().then(
8095
8176
  (r) => {
@@ -8114,13 +8195,13 @@ function VaultApp(given) {
8114
8195
  }
8115
8196
  function useOrganizationSettings(host) {
8116
8197
  const knobs = host.knobs;
8117
- const needs = knobs !== void 0 && (host.revealSeconds === void 0 || host.backupPassphraseMinLength === void 0);
8118
- const [read, setRead] = useState39(null);
8119
- useEffect21(() => {
8198
+ const needs = knobs !== void 0 && (host.revealSeconds === void 0 || host.backupPassphraseMinLength === void 0 || host.organizationSearchAt === void 0);
8199
+ const [read, setRead] = useState40(null);
8200
+ useEffect22(() => {
8120
8201
  if (!needs || !knobs) return;
8121
8202
  let live = true;
8122
8203
  knobs().then(
8123
- (r) => live && setRead({ reveal: positiveKnob(r, REVEAL_SECONDS_KNOB), backupMin: positiveKnob(r, BACKUP_PASSPHRASE_MIN_KNOB) }),
8204
+ (r) => live && setRead({ reveal: positiveKnob(r, REVEAL_SECONDS_KNOB), backupMin: positiveKnob(r, BACKUP_PASSPHRASE_MIN_KNOB), searchAt: positiveKnob(r, ORGANIZATION_SEARCH_AT_KNOB) }),
8124
8205
  () => void 0
8125
8206
  );
8126
8207
  return () => {
@@ -8131,7 +8212,8 @@ function useOrganizationSettings(host) {
8131
8212
  return {
8132
8213
  ...host,
8133
8214
  ...host.revealSeconds === void 0 && read.reveal !== null ? { revealSeconds: read.reveal } : {},
8134
- ...host.backupPassphraseMinLength === void 0 && read.backupMin !== null ? { backupPassphraseMinLength: read.backupMin } : {}
8215
+ ...host.backupPassphraseMinLength === void 0 && read.backupMin !== null ? { backupPassphraseMinLength: read.backupMin } : {},
8216
+ ...host.organizationSearchAt === void 0 && read.searchAt !== null ? { organizationSearchAt: read.searchAt } : {}
8135
8217
  };
8136
8218
  }
8137
8219
  var NO_STORE = () => () => void 0;
@@ -8142,8 +8224,8 @@ function useHostPrompts(capture, fillSetup) {
8142
8224
  return prompt !== null || setup?.status === "off";
8143
8225
  }
8144
8226
  function LockedScreen({ lock }) {
8145
- const [busy, setBusy] = useState39(false);
8146
- const [error, setError] = useState39(null);
8227
+ const [busy, setBusy] = useState40(false);
8228
+ const [error, setError] = useState40(null);
8147
8229
  const unlock = async () => {
8148
8230
  setBusy(true);
8149
8231
  setError(null);
@@ -8163,21 +8245,21 @@ function LockedScreen({ lock }) {
8163
8245
  ] });
8164
8246
  }
8165
8247
  function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocationChange, onInventory }) {
8166
- const rootRef = useRef17(null);
8248
+ const rootRef = useRef18(null);
8167
8249
  const width = useElementWidth(rootRef);
8168
8250
  const mode = layout !== "auto" ? layout : width >= FULL_AT ? "full" : width >= SPLIT_AT ? "split" : "stack";
8169
8251
  const notify = useNotify();
8170
- const [snap, setSnap] = useState39(null);
8171
- const [items, setItems] = useState39([]);
8172
- const [deleted, setDeleted] = useState39([]);
8173
- const [favorites, setFavorites] = useState39(null);
8174
- const [recents, setRecents] = useState39(null);
8175
- const [error, setError] = useState39(null);
8176
- const [inner, setInner] = useState39(DEFAULT_LOCATION);
8252
+ const [snap, setSnap] = useState40(null);
8253
+ const [items, setItems] = useState40([]);
8254
+ const [deleted, setDeleted] = useState40([]);
8255
+ const [favorites, setFavorites] = useState40(null);
8256
+ const [recents, setRecents] = useState40(null);
8257
+ const [error, setError] = useState40(null);
8258
+ const [inner, setInner] = useState40(DEFAULT_LOCATION);
8177
8259
  const loc = location ?? inner;
8178
- const dirty = useRef17(false);
8179
- const [notice, setNotice] = useState39(null);
8180
- const [firstCode, setFirstCode] = useState39(null);
8260
+ const dirty = useRef18(false);
8261
+ const [notice, setNotice] = useState40(null);
8262
+ const [firstCode, setFirstCode] = useShownUntilMove(loc);
8181
8263
  const move = useCallback4(
8182
8264
  (next, how) => {
8183
8265
  if (!location) setInner(next);
@@ -8204,9 +8286,9 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8204
8286
  },
8205
8287
  [loc, move]
8206
8288
  );
8207
- const keyState = useRef17({ loc, go });
8289
+ const keyState = useRef18({ loc, go });
8208
8290
  keyState.current = { loc, go };
8209
- useEffect21(() => {
8291
+ useEffect22(() => {
8210
8292
  const onKey = (e) => {
8211
8293
  if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.altKey) return;
8212
8294
  const root = rootRef.current;
@@ -8228,8 +8310,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8228
8310
  document.addEventListener("keydown", onKey);
8229
8311
  return () => document.removeEventListener("keydown", onKey);
8230
8312
  }, []);
8231
- const keepSearchFocus = useRef17(false);
8232
- useEffect21(() => {
8313
+ const keepSearchFocus = useRef18(false);
8314
+ useEffect22(() => {
8233
8315
  if (!keepSearchFocus.current) return;
8234
8316
  keepSearchFocus.current = false;
8235
8317
  const search = rootRef.current?.querySelector('input[aria-label^="Search"]');
@@ -8238,13 +8320,13 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8238
8320
  search.setSelectionRange(search.value.length, search.value.length);
8239
8321
  }, [loc.q]);
8240
8322
  const hasPrompts = useHostPrompts(host.capture, host.fillSetup);
8241
- const knobsRef = useRef17(host.knobs);
8323
+ const knobsRef = useRef18(host.knobs);
8242
8324
  knobsRef.current = host.knobs;
8243
- const [signedOut, setSignedOut] = useState39(false);
8244
- const [refreshError, setRefreshError] = useState39(null);
8245
- const [refreshing, setRefreshing] = useState39(false);
8246
- const loadedAt = useRef17(0);
8247
- const inFlight = useRef17(false);
8325
+ const [signedOut, setSignedOut] = useState40(false);
8326
+ const [refreshError, setRefreshError] = useState40(null);
8327
+ const [refreshing, setRefreshing] = useState40(false);
8328
+ const loadedAt = useRef18(0);
8329
+ const inFlight = useRef18(false);
8248
8330
  const apply = useCallback4(
8249
8331
  (s) => {
8250
8332
  loadedAt.current = Date.now();
@@ -8292,11 +8374,11 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8292
8374
  knobsRef.current?.().catch(() => void 0);
8293
8375
  port.lists.recents?.list().then(setRecents, () => void 0);
8294
8376
  }, [port, apply]);
8295
- useEffect21(load, [load]);
8296
- const quietRef = useRef17({ loc, ready: false });
8377
+ useEffect22(load, [load]);
8378
+ const quietRef = useRef18({ loc, ready: false });
8297
8379
  quietRef.current = { loc, ready: snap !== null && !error };
8298
- const [knobSeconds, setKnobSeconds] = useState39(null);
8299
- useEffect21(() => {
8380
+ const [knobSeconds, setKnobSeconds] = useState40(null);
8381
+ useEffect22(() => {
8300
8382
  if (host.refreshSeconds !== void 0 || !knobsRef.current) return;
8301
8383
  let live = true;
8302
8384
  knobsRef.current().then(
@@ -8311,7 +8393,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8311
8393
  };
8312
8394
  }, [host.refreshSeconds]);
8313
8395
  const everySeconds = host.refreshSeconds ?? knobSeconds;
8314
- useEffect21(() => {
8396
+ useEffect22(() => {
8315
8397
  const quietly = (minGapMs) => {
8316
8398
  const { loc: here, ready } = quietRef.current;
8317
8399
  if (!ready || document.visibilityState === "hidden" || dirty.current || here.mode !== "view") return;
@@ -8329,17 +8411,17 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8329
8411
  if (timer2) clearInterval(timer2);
8330
8412
  };
8331
8413
  }, [refresh, everySeconds]);
8332
- const inventoryRef = useRef17(onInventory);
8414
+ const inventoryRef = useRef18(onInventory);
8333
8415
  inventoryRef.current = onInventory;
8334
- useEffect21(() => {
8416
+ useEffect22(() => {
8335
8417
  if (snap) inventoryRef.current?.({ items, definitions: snap.definitions });
8336
8418
  }, [snap, items]);
8337
- useEffect21(() => {
8419
+ useEffect22(() => {
8338
8420
  if (!loc.itemId || !port.lists.recents) return;
8339
8421
  const id = loc.itemId;
8340
8422
  port.lists.recents.record(id).then(() => setRecents((r) => [id, ...(r ?? []).filter((x) => x !== id)]), () => void 0);
8341
8423
  }, [loc.itemId, port]);
8342
- const orgs = snap?.organizations ?? [];
8424
+ const orgs = useMemo6(() => orderOrganizations(snap?.organizations ?? [], items, recents), [snap?.organizations, items, recents]);
8343
8425
  const definitions = snap?.definitions ?? [];
8344
8426
  const inThisVault = useMemo6(() => items.filter((i) => inVault(i, loc.vault)), [items, loc.vault]);
8345
8427
  const vaultMixesOwners = new Set(inThisVault.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
@@ -8422,7 +8504,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8422
8504
  const hasFavorites = port.lists.favorites !== void 0;
8423
8505
  const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
8424
8506
  const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ jsxs35("div", { className: "grid grid-cols-[repeat(auto-fit,minmax(11rem,1fr))] gap-2", "data-vault-compact-header": "", children: [
8425
- /* @__PURE__ */ jsx37(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8507
+ /* @__PURE__ */ jsx37(VaultSelect, { value: loc.vault, choices, searchAt: host.organizationSearchAt, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8426
8508
  /* @__PURE__ */ jsx37(Select12, { value: loc.view, onValueChange: (view) => go({ view, itemId: null, panel: null }), options: viewOptions(views, inThisVault.length, deleted.length, hasFavorites, port.lists.recents !== void 0, host.page !== void 0, loc.view), "aria-label": "Show" })
8427
8509
  ] });
8428
8510
  const compactActions = mode === "full" ? void 0 : /* @__PURE__ */ jsxs35("div", { className: "flex shrink-0 items-center gap-2", "data-vault-compact-actions": "", children: [
@@ -8659,6 +8741,11 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8659
8741
  arg: panelArg ?? null,
8660
8742
  onClose: closePanel,
8661
8743
  onPanel,
8744
+ onOpenItem: (next) => {
8745
+ replaceItem(next);
8746
+ const holds = loc.vault === "all" || loc.vault === "mine" && next.owner.kind === "me" || loc.vault === next.owner.organizationId;
8747
+ go({ ...holds ? {} : { vault: "all" }, itemId: next.id, mode: "view", panel: null });
8748
+ },
8662
8749
  onChanged: (next) => {
8663
8750
  if (next) replaceItem(next);
8664
8751
  else if (next === null) {
@@ -8698,6 +8785,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8698
8785
  vault: loc.vault,
8699
8786
  choices,
8700
8787
  onVault: (vault) => go({ vault, itemId: null, panel: null }),
8788
+ organizationSearchAt: host.organizationSearchAt,
8701
8789
  view: loc.view,
8702
8790
  allCount: inThisVault.length,
8703
8791
  deletedCount: deleted.length,