@ai-matrx/vault 0.1.21 → 0.1.22
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/CHANGELOG.md +15 -2
- package/dist/index.cjs +245 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +245 -120
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -849,6 +849,19 @@ import { formatAbsoluteDate as formatAbsoluteDate2 } from "@ai-matrx/kit/format"
|
|
|
849
849
|
import { formatRelativeTime } from "@ai-matrx/kit/format";
|
|
850
850
|
|
|
851
851
|
// src/model.ts
|
|
852
|
+
function manageUnavailableReason(item) {
|
|
853
|
+
if (item.capabilities.canManage) return null;
|
|
854
|
+
if (item.owner.memberShared) return "Only the member who owns it can share, move or delete it";
|
|
855
|
+
if (item.owner.kind === "shared") return "Only its owner can share, move or delete it";
|
|
856
|
+
if (item.owner.kind === "organization") return "Only this organization's vault admins can change that";
|
|
857
|
+
return "You can use this item but not share, move or delete it";
|
|
858
|
+
}
|
|
859
|
+
function ownerDetail(owner) {
|
|
860
|
+
if (owner.memberShared) return `A member's own credential, shared into ${owner.label}`;
|
|
861
|
+
if (owner.kind === "organization") return `Organization: ${owner.label}`;
|
|
862
|
+
if (owner.kind === "shared") return "Shared with you by its owner";
|
|
863
|
+
return "Your own vault";
|
|
864
|
+
}
|
|
852
865
|
var DEFAULT_LOCATION = { vault: "all", view: "all", q: "", sort: "name", itemId: null, mode: "view", kind: null, url: "", panel: null };
|
|
853
866
|
var SORTS = ["name", "name-desc", "website", "updated", "created", "viewed"];
|
|
854
867
|
function locationFromParams(params, itemId = params.get("item")) {
|
|
@@ -5991,6 +6004,7 @@ function ItemPane(props) {
|
|
|
5991
6004
|
const setValue = (key, v) => setDraft((d) => ({ ...d, values: { ...d.values, [key]: v } }));
|
|
5992
6005
|
const canEdit = item ? item.capabilities.canEdit || item.capabilities.canManage : true;
|
|
5993
6006
|
const canManage = item?.capabilities.canManage ?? false;
|
|
6007
|
+
const manageReason = item ? manageUnavailableReason(item) : null;
|
|
5994
6008
|
async function save() {
|
|
5995
6009
|
if (!definition && !item) return;
|
|
5996
6010
|
setBusy(true);
|
|
@@ -6137,7 +6151,8 @@ function ItemPane(props) {
|
|
|
6137
6151
|
/* @__PURE__ */ jsx27("h2", { className: "truncate text-base font-semibold leading-tight", children: editing ? draft.name || (mode === "new" ? `New ${typeLabel.toLowerCase()}` : item?.name) : item?.name }),
|
|
6138
6152
|
/* @__PURE__ */ jsxs25("span", { className: "flex min-w-0 items-center gap-1 text-xs text-muted-foreground", children: [
|
|
6139
6153
|
typeLabel ? /* @__PURE__ */ jsx27("span", { className: "truncate", children: typeLabel }) : null,
|
|
6140
|
-
shownOwner ? /* @__PURE__ */ jsx27(Badge7, { className: "shrink-0", icon: shownOwner.glyph, "aria-label": `Saved in ${shownOwner.label}`, children: shownOwner.label }) : null,
|
|
6154
|
+
shownOwner ? /* @__PURE__ */ jsx27(Badge7, { className: "shrink-0", icon: shownOwner.glyph, "aria-label": `Saved in ${shownOwner.label}`, title: item && mode !== "new" ? ownerDetail(item.owner) : void 0, children: shownOwner.label }) : null,
|
|
6155
|
+
item && mode !== "new" && item.owner.memberShared ? /* @__PURE__ */ jsx27("span", { className: "shrink-0", "data-member-shared": "", children: "\xB7 A member\u2019s own" }) : null,
|
|
6141
6156
|
item && !editing ? /* @__PURE__ */ jsx27(SharingSummary, { item, port, className: "hidden truncate before:mr-1 before:content-['\xB7'] sm:inline" }, `${item.updatedAt}-${refreshKey ?? ""}`) : null
|
|
6142
6157
|
] })
|
|
6143
6158
|
] }),
|
|
@@ -6175,9 +6190,9 @@ function ItemPane(props) {
|
|
|
6175
6190
|
/* @__PURE__ */ jsx27(ShareGlyph, { className: "size-4" }),
|
|
6176
6191
|
"Share"
|
|
6177
6192
|
] }) : null,
|
|
6178
|
-
canManage ? /* @__PURE__ */ jsxs25(DropdownMenuItem3, { onSelect: () => onPanel("move"), children: [
|
|
6193
|
+
canManage || item.capabilities.canEdit ? /* @__PURE__ */ jsxs25(DropdownMenuItem3, { onSelect: () => onPanel("move"), children: [
|
|
6179
6194
|
/* @__PURE__ */ jsx27(MoveGlyph, { className: "size-4" }),
|
|
6180
|
-
"Move or copy"
|
|
6195
|
+
canManage ? "Move or copy" : "Copy to\u2026"
|
|
6181
6196
|
] }) : null,
|
|
6182
6197
|
item.capabilities.canReveal ? /* @__PURE__ */ jsxs25(DropdownMenuItem3, { onSelect: () => onPanel("send"), children: [
|
|
6183
6198
|
/* @__PURE__ */ jsx27(SendGlyph, { className: "size-4" }),
|
|
@@ -6201,6 +6216,17 @@ function ItemPane(props) {
|
|
|
6201
6216
|
/* @__PURE__ */ jsx27(TrashGlyph, { className: "size-4" }),
|
|
6202
6217
|
"Delete"
|
|
6203
6218
|
] })
|
|
6219
|
+
] }) : manageReason ? /* @__PURE__ */ jsxs25(Fragment18, { children: [
|
|
6220
|
+
/* @__PURE__ */ jsx27(DropdownMenuSeparator3, {}),
|
|
6221
|
+
/* @__PURE__ */ jsxs25(DropdownMenuItem3, { disabled: true, children: [
|
|
6222
|
+
/* @__PURE__ */ jsx27(ShareGlyph, { className: "size-4" }),
|
|
6223
|
+
"Share"
|
|
6224
|
+
] }),
|
|
6225
|
+
/* @__PURE__ */ jsxs25(DropdownMenuItem3, { disabled: true, children: [
|
|
6226
|
+
/* @__PURE__ */ jsx27(TrashGlyph, { className: "size-4" }),
|
|
6227
|
+
"Delete"
|
|
6228
|
+
] }),
|
|
6229
|
+
/* @__PURE__ */ jsx27("div", { className: "max-w-64 px-2 py-1.5 text-xs text-muted-foreground", "data-manage-reason": "", children: manageReason })
|
|
6204
6230
|
] }) : null
|
|
6205
6231
|
] })
|
|
6206
6232
|
] })
|
|
@@ -6431,6 +6457,17 @@ function ItemPane(props) {
|
|
|
6431
6457
|
] }) });
|
|
6432
6458
|
}
|
|
6433
6459
|
|
|
6460
|
+
// src/row-menu.ts
|
|
6461
|
+
var readers = /* @__PURE__ */ new WeakMap();
|
|
6462
|
+
function registerRowMenu(el, read) {
|
|
6463
|
+
if (el) readers.set(el, read);
|
|
6464
|
+
}
|
|
6465
|
+
function rowMenuEntriesAt(target) {
|
|
6466
|
+
const row = target?.closest("[data-item]");
|
|
6467
|
+
const read = row ? readers.get(row) : void 0;
|
|
6468
|
+
return read ? read() : [];
|
|
6469
|
+
}
|
|
6470
|
+
|
|
6434
6471
|
// src/ItemList.tsx
|
|
6435
6472
|
import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
6436
6473
|
var RECOVERY_KEYS = /^recovery_codes?$/;
|
|
@@ -6452,7 +6489,7 @@ function subtitleOf(item, withOwner) {
|
|
|
6452
6489
|
const what = loginSiteOf(item) || item.definitionLabel;
|
|
6453
6490
|
return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
|
|
6454
6491
|
}
|
|
6455
|
-
function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems }) {
|
|
6492
|
+
function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems, stillLoading, favorites, onToggleFavorite }) {
|
|
6456
6493
|
const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
|
|
6457
6494
|
const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
|
|
6458
6495
|
const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
|
|
@@ -6473,7 +6510,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6473
6510
|
}
|
|
6474
6511
|
};
|
|
6475
6512
|
const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
|
|
6476
|
-
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 }, i.id);
|
|
6513
|
+
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);
|
|
6477
6514
|
const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
|
|
6478
6515
|
return /* @__PURE__ */ jsxs26("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
|
|
6479
6516
|
/* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
|
|
@@ -6483,10 +6520,18 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6483
6520
|
/* @__PURE__ */ jsx28(Button23, { variant: "primary", icon: /* @__PURE__ */ jsx28(PlusGlyph, {}), "aria-label": "New item", onClick: onNew })
|
|
6484
6521
|
] }),
|
|
6485
6522
|
/* @__PURE__ */ jsxs26("div", { className: "flex items-center justify-between gap-2 px-1", children: [
|
|
6486
|
-
/* @__PURE__ */ jsxs26("span", { className: "truncate text-xs text-muted-foreground", children: [
|
|
6523
|
+
/* @__PURE__ */ jsxs26("span", { className: "flex min-w-0 items-center gap-1.5 truncate text-xs text-muted-foreground", role: "status", "aria-live": "polite", children: [
|
|
6487
6524
|
items.length,
|
|
6488
6525
|
" ",
|
|
6489
|
-
items.length === 1 ? "item" : "items"
|
|
6526
|
+
items.length === 1 ? "item" : "items",
|
|
6527
|
+
stillLoading && stillLoading.length > 0 ? /* @__PURE__ */ jsxs26("span", { className: "flex min-w-0 items-center gap-1 truncate", title: `Still loading ${stillLoading.join(", ")}`, "data-still-loading": "", children: [
|
|
6528
|
+
/* @__PURE__ */ jsx28(RefreshGlyph, { className: "size-3 shrink-0 animate-spin" }),
|
|
6529
|
+
/* @__PURE__ */ jsxs26("span", { className: "truncate", children: [
|
|
6530
|
+
"loading ",
|
|
6531
|
+
stillLoading.length === 1 ? stillLoading[0] : `${stillLoading.length} more`,
|
|
6532
|
+
"\u2026"
|
|
6533
|
+
] })
|
|
6534
|
+
] }) : null
|
|
6490
6535
|
] }),
|
|
6491
6536
|
/* @__PURE__ */ jsxs26("span", { className: "flex shrink-0 items-center gap-0.5", children: [
|
|
6492
6537
|
onRefresh ? /* @__PURE__ */ jsx28(Button23, { variant: "quiet", icon: /* @__PURE__ */ jsx28(RefreshGlyph, { className: refreshing ? "animate-spin" : "" }), "aria-label": "Refresh", title: "Refresh the list", disabled: refreshing, onClick: onRefresh }) : null,
|
|
@@ -6498,7 +6543,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6498
6543
|
] })
|
|
6499
6544
|
] }),
|
|
6500
6545
|
/* @__PURE__ */ jsx28("div", { className: "min-h-0 flex-1 overflow-y-auto px-2 pb-3", onKeyDown, children: items.length === 0 ? /* @__PURE__ */ jsxs26("div", { className: "flex flex-col items-center gap-3 px-4 py-12 text-center", children: [
|
|
6501
|
-
/* @__PURE__ */ jsx28("span", { className: "text-sm text-muted-foreground", children: query ? `Nothing matches \u201C${query}\u201D.` : "Nothing here yet." }),
|
|
6546
|
+
/* @__PURE__ */ jsx28("span", { className: "text-sm text-muted-foreground", children: query ? `Nothing matches \u201C${query}\u201D.` : stillLoading && stillLoading.length > 0 ? "Still loading\u2026" : "Nothing here yet." }),
|
|
6502
6547
|
query ? /* @__PURE__ */ jsx28(Button23, { variant: "outline", onClick: () => onQuery(""), children: "Clear search" }) : /* @__PURE__ */ jsx28(Button23, { variant: "outline", icon: /* @__PURE__ */ jsx28(PlusGlyph, {}), onClick: onNew, children: "New item" })
|
|
6503
6548
|
] }) : /* @__PURE__ */ jsxs26("ul", { ref: listRef, className: "flex flex-col gap-px", children: [
|
|
6504
6549
|
pinned.length > 0 ? /* @__PURE__ */ jsx28("li", { className: "px-2 pb-1 pt-1 text-xs font-medium text-muted-foreground", children: "This page" }) : null,
|
|
@@ -6508,7 +6553,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6508
6553
|
] }) })
|
|
6509
6554
|
] });
|
|
6510
6555
|
}
|
|
6511
|
-
function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra }) {
|
|
6556
|
+
function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, favorite }) {
|
|
6512
6557
|
const confirmed = useConfirmed();
|
|
6513
6558
|
const copy = useCopy();
|
|
6514
6559
|
const notify = useNotify();
|
|
@@ -6536,9 +6581,32 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
|
|
|
6536
6581
|
const others = otherCopyFields(item, user, secret);
|
|
6537
6582
|
const custom = canUse ? item.custom.filter((c) => c.value) : [];
|
|
6538
6583
|
const hostEntries = extra?.(item) ?? [];
|
|
6584
|
+
const copyEntries = [
|
|
6585
|
+
...user ? [{ id: "copy:username", kind: "copy", label: `Copy ${user.label.toLowerCase()}`, onSelect: copyUser }] : [],
|
|
6586
|
+
...secret ? [{ id: "copy:password", kind: "copy", label: "Copy password", onSelect: copyPassword }] : [],
|
|
6587
|
+
...code ? [{ id: "copy:code", kind: "copy", label: "Copy one-time code", onSelect: copyCode }] : [],
|
|
6588
|
+
...others.map((f) => ({ id: `copy:${f.key}`, kind: "copy", label: `Copy ${f.label.toLowerCase()}`, onSelect: () => void run(f.label, () => port.reveal(item, f.key), f.handling !== "visible") })),
|
|
6589
|
+
...custom.map((c) => ({ id: `copy:custom:${c.key}`, kind: "copy", label: `Copy ${c.label.toLowerCase()}`, onSelect: () => void copy(c.value, c.label, { secret: false }) }))
|
|
6590
|
+
];
|
|
6591
|
+
const favoriteEntry = favorite ? { id: "favorite", kind: "favorite", label: favorite.on ? "Remove from favorites" : "Add to favorites", onSelect: favorite.toggle } : null;
|
|
6592
|
+
const entries = [
|
|
6593
|
+
...copyEntries,
|
|
6594
|
+
...favoriteEntry ? [favoriteEntry] : [],
|
|
6595
|
+
{ id: "open", kind: "open", label: "Open", onSelect: () => onSelect(item.id) },
|
|
6596
|
+
{ id: "copy-name", kind: "copy-name", label: "Copy name", onSelect: copyName },
|
|
6597
|
+
...hostEntries.map((h) => ({ ...h, kind: h.kind ?? "host" }))
|
|
6598
|
+
];
|
|
6599
|
+
const entriesRef = useRef15(entries);
|
|
6600
|
+
entriesRef.current = entries;
|
|
6601
|
+
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" });
|
|
6602
|
+
const menuItem = (e) => /* @__PURE__ */ jsxs26(DropdownMenuItem4, { disabled: e.disabled ?? false, onSelect: e.onSelect, children: [
|
|
6603
|
+
glyphOf(e),
|
|
6604
|
+
e.label
|
|
6605
|
+
] }, e.id ?? e.label);
|
|
6539
6606
|
return /* @__PURE__ */ jsxs26(
|
|
6540
6607
|
"li",
|
|
6541
6608
|
{
|
|
6609
|
+
ref: (el) => registerRowMenu(el, () => entriesRef.current),
|
|
6542
6610
|
className: "group relative",
|
|
6543
6611
|
"data-item": item.id,
|
|
6544
6612
|
onContextMenu: ownContextMenu ? (e) => {
|
|
@@ -6564,7 +6632,8 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
|
|
|
6564
6632
|
}
|
|
6565
6633
|
),
|
|
6566
6634
|
/* @__PURE__ */ jsxs26("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: [
|
|
6567
|
-
user || secret ? /* @__PURE__ */ jsxs26("div", { className: cn13("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: [
|
|
6635
|
+
user || secret || favorite ? /* @__PURE__ */ jsxs26("div", { className: cn13("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: [
|
|
6636
|
+
favorite ? /* @__PURE__ */ jsx28(Button23, { variant: "quiet", icon: /* @__PURE__ */ jsx28(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,
|
|
6568
6637
|
user ? /* @__PURE__ */ jsx28(Button23, { variant: "quiet", icon: /* @__PURE__ */ jsx28(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
|
|
6569
6638
|
secret ? /* @__PURE__ */ jsx28(Button23, { variant: "quiet", icon: /* @__PURE__ */ jsx28(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
|
|
6570
6639
|
] }) : null,
|
|
@@ -6580,40 +6649,11 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
|
|
|
6580
6649
|
}
|
|
6581
6650
|
) }),
|
|
6582
6651
|
/* @__PURE__ */ jsxs26(DropdownMenuContent4, { align: "end", children: [
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
user.label.toLowerCase()
|
|
6587
|
-
] }) : null,
|
|
6588
|
-
secret ? /* @__PURE__ */ jsxs26(DropdownMenuItem4, { onSelect: copyPassword, children: [
|
|
6589
|
-
/* @__PURE__ */ jsx28(KeyGlyph, { className: "size-4" }),
|
|
6590
|
-
"Copy password"
|
|
6591
|
-
] }) : null,
|
|
6592
|
-
code ? /* @__PURE__ */ jsxs26(DropdownMenuItem4, { onSelect: copyCode, children: [
|
|
6593
|
-
/* @__PURE__ */ jsx28(ClockGlyph, { className: "size-4" }),
|
|
6594
|
-
"Copy one-time code"
|
|
6595
|
-
] }) : null,
|
|
6596
|
-
others.map((f) => /* @__PURE__ */ jsxs26(DropdownMenuItem4, { onSelect: () => void run(f.label, () => port.reveal(item, f.key), f.handling !== "visible"), children: [
|
|
6597
|
-
/* @__PURE__ */ jsx28(CopyIcon7, { className: "size-4" }),
|
|
6598
|
-
"Copy ",
|
|
6599
|
-
f.label.toLowerCase()
|
|
6600
|
-
] }, f.key)),
|
|
6601
|
-
custom.map((c) => /* @__PURE__ */ jsxs26(DropdownMenuItem4, { onSelect: () => void copy(c.value, c.label, { secret: false }), children: [
|
|
6602
|
-
/* @__PURE__ */ jsx28(CopyIcon7, { className: "size-4" }),
|
|
6603
|
-
"Copy ",
|
|
6604
|
-
c.label.toLowerCase()
|
|
6605
|
-
] }, `custom:${c.key}`)),
|
|
6606
|
-
user || secret || code || others.length > 0 || custom.length > 0 ? /* @__PURE__ */ jsx28(DropdownMenuSeparator4, {}) : null,
|
|
6607
|
-
/* @__PURE__ */ jsxs26(DropdownMenuItem4, { onSelect: () => onSelect(item.id), children: [
|
|
6608
|
-
/* @__PURE__ */ jsx28(ChevronRightGlyph, { className: "size-4" }),
|
|
6609
|
-
"Open"
|
|
6610
|
-
] }),
|
|
6611
|
-
/* @__PURE__ */ jsxs26(DropdownMenuItem4, { onSelect: copyName, children: [
|
|
6612
|
-
/* @__PURE__ */ jsx28(CopyIcon7, { className: "size-4" }),
|
|
6613
|
-
"Copy name"
|
|
6614
|
-
] }),
|
|
6652
|
+
copyEntries.map(menuItem),
|
|
6653
|
+
copyEntries.length > 0 ? /* @__PURE__ */ jsx28(DropdownMenuSeparator4, {}) : null,
|
|
6654
|
+
entries.filter((e) => e.kind === "favorite" || e.kind === "open" || e.kind === "copy-name").map(menuItem),
|
|
6615
6655
|
hostEntries.length > 0 ? /* @__PURE__ */ jsx28(DropdownMenuSeparator4, {}) : null,
|
|
6616
|
-
|
|
6656
|
+
entries.filter((e) => e.kind === "host").map(menuItem)
|
|
6617
6657
|
] })
|
|
6618
6658
|
] })
|
|
6619
6659
|
] })
|
|
@@ -8121,15 +8161,17 @@ import { Select as Select11 } from "@ai-matrx/design-system/controls";
|
|
|
8121
8161
|
import { GlobeIcon as GlobeIcon5 } from "@ai-matrx/design-system/icons";
|
|
8122
8162
|
import { jsx as jsx39, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
8123
8163
|
var AUTHENTICATOR_LABEL = "Authenticator";
|
|
8124
|
-
function vaultChoices(items, organizations,
|
|
8164
|
+
function vaultChoices(items, organizations, _current) {
|
|
8125
8165
|
const n = (f) => items.filter(f).length;
|
|
8126
8166
|
const shared = n((i) => i.owner.kind === "shared");
|
|
8127
8167
|
return [
|
|
8128
8168
|
{ value: "all", label: "All items", count: items.length },
|
|
8129
8169
|
{ value: "mine", label: "Personal", count: n((i) => i.owner.kind === "me") },
|
|
8130
8170
|
...shared > 0 ? [{ value: "shared", label: "Shared with me", count: shared }] : [],
|
|
8131
|
-
//
|
|
8132
|
-
|
|
8171
|
+
// Every organization the person belongs to, empty ones too: an empty vault opens on its own
|
|
8172
|
+
// "Nothing here yet" with New item, so the first credential can go there. (The organization
|
|
8173
|
+
// list carries no role, so who may add is the server's answer to the save, said in place.)
|
|
8174
|
+
...organizations.map((o) => ({ value: o.id, label: o.name, count: n((i) => i.owner.organizationId === o.id) }))
|
|
8133
8175
|
];
|
|
8134
8176
|
}
|
|
8135
8177
|
function VaultSelect({ value, choices, onChange }) {
|
|
@@ -8409,7 +8451,16 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8409
8451
|
const load = useCallback5(() => {
|
|
8410
8452
|
setError(null);
|
|
8411
8453
|
setRefreshError(null);
|
|
8412
|
-
|
|
8454
|
+
let finished = false;
|
|
8455
|
+
port.load((partial) => {
|
|
8456
|
+
if (finished) return;
|
|
8457
|
+
setSnap(partial);
|
|
8458
|
+
setItems(partial.items);
|
|
8459
|
+
setDeleted(partial.deleted);
|
|
8460
|
+
}).then((s) => {
|
|
8461
|
+
finished = true;
|
|
8462
|
+
apply(s);
|
|
8463
|
+
}).catch((e) => {
|
|
8413
8464
|
setSignedOut(e?.status === 401);
|
|
8414
8465
|
setError(e instanceof Error ? e.message : "The vault could not be loaded.");
|
|
8415
8466
|
});
|
|
@@ -8526,59 +8577,62 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8526
8577
|
}
|
|
8527
8578
|
const hasFavorites = port.lists.favorites !== void 0;
|
|
8528
8579
|
const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
|
|
8529
|
-
const compactHeader = mode === "full" ? void 0 :
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
/* @__PURE__ */
|
|
8533
|
-
|
|
8534
|
-
{
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
/* @__PURE__ */ jsxs38(
|
|
8547
|
-
/* @__PURE__ */
|
|
8548
|
-
|
|
8549
|
-
"
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
"
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
"
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
"
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
"
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
"
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8580
|
+
const compactHeader = mode === "full" ? void 0 : (
|
|
8581
|
+
// The two pickers keep a real width at every compact size (the list column is 19rem in split
|
|
8582
|
+
// mode): the Authenticator button is icon-only beside them, its name in its tooltip.
|
|
8583
|
+
/* @__PURE__ */ jsxs38("div", { className: "grid grid-cols-[minmax(6rem,1fr)_minmax(6rem,1fr)_auto_auto] gap-2", "data-vault-compact-header": "", children: [
|
|
8584
|
+
/* @__PURE__ */ jsx40(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
|
|
8585
|
+
/* @__PURE__ */ jsx40(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), "aria-label": "Show" }),
|
|
8586
|
+
/* @__PURE__ */ jsx40(
|
|
8587
|
+
Button34,
|
|
8588
|
+
{
|
|
8589
|
+
variant: loc.view === "authenticator" ? "outline" : "quiet",
|
|
8590
|
+
icon: /* @__PURE__ */ jsx40(LockGlyph, {}),
|
|
8591
|
+
"aria-label": AUTHENTICATOR_LABEL,
|
|
8592
|
+
"aria-pressed": loc.view === "authenticator",
|
|
8593
|
+
title: AUTHENTICATOR_LABEL,
|
|
8594
|
+
onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null })
|
|
8595
|
+
}
|
|
8596
|
+
),
|
|
8597
|
+
/* @__PURE__ */ jsxs38(DropdownMenu6, { children: [
|
|
8598
|
+
/* @__PURE__ */ jsx40(DropdownMenuTrigger6, { asChild: true, children: /* @__PURE__ */ jsx40(Button34, { variant: "quiet", icon: /* @__PURE__ */ jsx40(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
|
|
8599
|
+
/* @__PURE__ */ jsxs38(DropdownMenuContent6, { align: "end", children: [
|
|
8600
|
+
/* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "import" }), children: [
|
|
8601
|
+
/* @__PURE__ */ jsx40(ImportGlyph, { className: "size-4" }),
|
|
8602
|
+
"Import"
|
|
8603
|
+
] }),
|
|
8604
|
+
/* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "export" }), children: [
|
|
8605
|
+
/* @__PURE__ */ jsx40(ExportGlyph, { className: "size-4" }),
|
|
8606
|
+
"Export"
|
|
8607
|
+
] }),
|
|
8608
|
+
/* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "backup" }), children: [
|
|
8609
|
+
/* @__PURE__ */ jsx40(SafeGlyph, { className: "size-4" }),
|
|
8610
|
+
"Backup"
|
|
8611
|
+
] }),
|
|
8612
|
+
/* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "devices" }), children: [
|
|
8613
|
+
/* @__PURE__ */ jsx40(ShieldGlyph, { className: "size-4" }),
|
|
8614
|
+
"Browsers that fill"
|
|
8615
|
+
] }),
|
|
8616
|
+
host.nativeImport ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => go({ panel: "import-passkeys" }), children: [
|
|
8617
|
+
/* @__PURE__ */ jsx40(ImportGlyph, { className: "size-4" }),
|
|
8618
|
+
"Import passkeys"
|
|
8619
|
+
] }) : null,
|
|
8620
|
+
host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: host.nativeProvider.openSetup, children: [
|
|
8621
|
+
/* @__PURE__ */ jsx40(ShieldGlyph, { className: "size-4" }),
|
|
8622
|
+
"System AutoFill"
|
|
8623
|
+
] }) : null,
|
|
8624
|
+
host.lock?.lock ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: () => void host.lock?.lock?.(), children: [
|
|
8625
|
+
/* @__PURE__ */ jsx40(LockGlyph, { className: "size-4" }),
|
|
8626
|
+
"Lock"
|
|
8627
|
+
] }) : null,
|
|
8628
|
+
host.openWebVault ? /* @__PURE__ */ jsxs38(DropdownMenuItem6, { onSelect: host.openWebVault, children: [
|
|
8629
|
+
/* @__PURE__ */ jsx40(ExternalGlyph, { className: "size-4" }),
|
|
8630
|
+
"Open on the web"
|
|
8631
|
+
] }) : null
|
|
8632
|
+
] })
|
|
8579
8633
|
] })
|
|
8580
8634
|
] })
|
|
8581
|
-
|
|
8635
|
+
);
|
|
8582
8636
|
const viewHeader = /* @__PURE__ */ jsxs38("div", { className: "flex flex-col gap-2", children: [
|
|
8583
8637
|
compactHeader,
|
|
8584
8638
|
/* @__PURE__ */ jsx40(SearchField4, { value: loc.q, onChange: (e) => go({ q: e.target.value }, "replace"), placeholder: `Search ${viewLabel(loc.view, views)}`, "aria-label": `Search ${viewLabel(loc.view, views)}` })
|
|
@@ -8621,6 +8675,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8621
8675
|
onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
|
|
8622
8676
|
onRefresh: refresh,
|
|
8623
8677
|
refreshing,
|
|
8678
|
+
...snap.pending && snap.pending.length > 0 ? { stillLoading: snap.pending } : {},
|
|
8679
|
+
...hasFavorites ? { favorites: favorites ?? /* @__PURE__ */ new Set(), onToggleFavorite: toggleFavorite } : {},
|
|
8624
8680
|
rowContextMenu: host.rowContextMenu ?? "package",
|
|
8625
8681
|
...host.rowMenuItems ? { rowMenuItems: host.rowMenuItems } : {},
|
|
8626
8682
|
...matchingIds ? { matchingIds } : {},
|
|
@@ -9510,6 +9566,12 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9510
9566
|
if (id) return { kind: "organization", label: orgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id };
|
|
9511
9567
|
return mine || userId ? { kind: "me", label: "Personal", organizationId: null } : { kind: "shared", label: "Shared with me", organizationId: null };
|
|
9512
9568
|
};
|
|
9569
|
+
const ownerInOrgList = (w, via, meId) => {
|
|
9570
|
+
if (w.organization_id) return orgOf(w.organization_id, null, false);
|
|
9571
|
+
if (w.user_id && meId && w.user_id === meId) return { kind: "me", label: "Personal", organizationId: null };
|
|
9572
|
+
if (via) return { kind: "organization", label: via.name, organizationId: via.id, memberShared: { userId: w.user_id ?? null } };
|
|
9573
|
+
return { kind: "shared", label: "Shared with me", organizationId: null };
|
|
9574
|
+
};
|
|
9513
9575
|
async function refetch(id, owner) {
|
|
9514
9576
|
const w = await account9.getItem(api, { path: { item_id: id } });
|
|
9515
9577
|
return itemOf(w, owner, defs, auth);
|
|
@@ -9537,38 +9599,100 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9537
9599
|
io: createIoPort(ctx),
|
|
9538
9600
|
fields: createFieldsPort(ctx),
|
|
9539
9601
|
lists: createListsPort(ctx, options.userState),
|
|
9540
|
-
async load() {
|
|
9602
|
+
async load(onProgress) {
|
|
9541
9603
|
const catalogP = retrying(() => apps2.getAppCatalogKind(api, { path: { app: "matrx", kind: "credential_definition" } }));
|
|
9542
9604
|
const authP = retrying(() => apps2.listAuthenticators(api));
|
|
9543
9605
|
const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
|
|
9544
9606
|
const sharedP = retrying(() => account9.sharedWithMe(api));
|
|
9545
9607
|
const quiet = (p) => void p.catch(() => void 0);
|
|
9546
9608
|
[catalogP, authP, userP, sharedP].forEach(quiet);
|
|
9547
|
-
const
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
const [authSettled, sharedSettled, ...orgSettled] = await Promise.allSettled([authP, sharedP, ...orgListsP]);
|
|
9609
|
+
const raw = {
|
|
9610
|
+
orgLists: [],
|
|
9611
|
+
orgsKnown: options.organizations !== void 0,
|
|
9612
|
+
meId: null
|
|
9613
|
+
};
|
|
9553
9614
|
const unavailable = [];
|
|
9554
|
-
|
|
9555
|
-
const
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) continue;
|
|
9566
|
-
const owner = sharedList ? orgOf(w.organization_id, null, false) : orgOf(w.organization_id, w.user_id, i === 0);
|
|
9567
|
-
const item = itemOf(w, sharedList && !w.organization_id ? { kind: "shared", label: "Shared with me", organizationId: null } : owner, defs, auth);
|
|
9615
|
+
let done = false;
|
|
9616
|
+
const build = () => {
|
|
9617
|
+
const catalog2 = raw.catalog ?? [];
|
|
9618
|
+
defs = new Map(parseDefinitions(catalog2.filter((e) => e.is_active).map((e) => ({ key: e.key, payload: e.payload, sort_order: e.sort_order }))).map((d) => [d.key, d]));
|
|
9619
|
+
const mineItems = raw.mine?.items ?? [];
|
|
9620
|
+
const meId = raw.meId ?? mineItems.find((w) => w.user_id)?.user_id ?? null;
|
|
9621
|
+
const seen = /* @__PURE__ */ new Map();
|
|
9622
|
+
const deleted = /* @__PURE__ */ new Map();
|
|
9623
|
+
const take = (w, owner) => {
|
|
9624
|
+
if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) return;
|
|
9625
|
+
const item = itemOf(w, owner, defs, auth);
|
|
9568
9626
|
(isDeleted(w) ? deleted : seen).set(w.id, item);
|
|
9627
|
+
};
|
|
9628
|
+
for (const w of mineItems) take(w, orgOf(w.organization_id, null, true));
|
|
9629
|
+
raw.orgLists.forEach((l, i) => {
|
|
9630
|
+
const via = orgs[i];
|
|
9631
|
+
for (const w of l?.items ?? []) take(w, ownerInOrgList(w, via, meId));
|
|
9632
|
+
});
|
|
9633
|
+
for (const w of raw.shared?.items ?? []) take(w, w.organization_id ? orgOf(w.organization_id, null, false) : { kind: "shared", label: "Shared with me", organizationId: null });
|
|
9634
|
+
const pending = done ? [] : [...raw.orgsKnown ? orgs.filter((_, i) => raw.orgLists[i] === void 0).map((o) => o.name) : ["your organizations"], ...raw.shared ? [] : ["Shared with me"]];
|
|
9635
|
+
return {
|
|
9636
|
+
unavailable: [...unavailable],
|
|
9637
|
+
...pending.length > 0 ? { pending } : {},
|
|
9638
|
+
items: [...seen.values()],
|
|
9639
|
+
deleted: [...deleted.values()],
|
|
9640
|
+
organizations: orgs,
|
|
9641
|
+
definitions: [...defs.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.label.localeCompare(b.label))
|
|
9642
|
+
};
|
|
9643
|
+
};
|
|
9644
|
+
const progress = () => {
|
|
9645
|
+
if (!done && onProgress && raw.catalog && raw.mine) onProgress(build());
|
|
9646
|
+
};
|
|
9647
|
+
const orgReportP = options.organizations ? null : retrying(() => platform.listMyOrganizations(api));
|
|
9648
|
+
if (orgReportP) quiet(orgReportP);
|
|
9649
|
+
const orgListsReady = (async () => {
|
|
9650
|
+
const report = orgReportP ? await orgReportP : null;
|
|
9651
|
+
raw.meId = report?.user_id ?? null;
|
|
9652
|
+
orgs = [...new Map((options.organizations ?? (report?.organizations ?? []).filter((o) => !o.archived_at).map((o) => ({ id: o.id, name: o.name }))).map((o) => [o.id, o])).values()];
|
|
9653
|
+
raw.orgLists = orgs.map(() => void 0);
|
|
9654
|
+
raw.orgsKnown = true;
|
|
9655
|
+
progress();
|
|
9656
|
+
return orgs.map((o, i) => {
|
|
9657
|
+
const p = retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id }));
|
|
9658
|
+
const arrived = p.then((l) => {
|
|
9659
|
+
raw.orgLists[i] = l;
|
|
9660
|
+
progress();
|
|
9661
|
+
return l;
|
|
9662
|
+
});
|
|
9663
|
+
quiet(arrived);
|
|
9664
|
+
return arrived;
|
|
9665
|
+
});
|
|
9666
|
+
})();
|
|
9667
|
+
quiet(orgListsReady);
|
|
9668
|
+
const authDone = authP.then((a) => {
|
|
9669
|
+
auth = new Map((a.entries ?? []).map((e) => [e.credential_item_id, e]));
|
|
9670
|
+
progress();
|
|
9671
|
+
}, () => {
|
|
9672
|
+
unavailable.push("One-time codes");
|
|
9673
|
+
});
|
|
9674
|
+
const sharedDone = sharedP.then((sh) => {
|
|
9675
|
+
raw.shared = sh;
|
|
9676
|
+
progress();
|
|
9677
|
+
}, () => {
|
|
9678
|
+
raw.shared = { items: [], count: 0 };
|
|
9679
|
+
unavailable.push("Shared with me");
|
|
9680
|
+
});
|
|
9681
|
+
const [catalog, mine] = await Promise.all([catalogP, userP]);
|
|
9682
|
+
raw.catalog = catalog;
|
|
9683
|
+
raw.mine = mine;
|
|
9684
|
+
progress();
|
|
9685
|
+
const orgPs = await orgListsReady;
|
|
9686
|
+
const orgSettled = await Promise.allSettled(orgPs);
|
|
9687
|
+
orgSettled.forEach((r, i) => {
|
|
9688
|
+
if (r.status === "rejected") {
|
|
9689
|
+
raw.orgLists[i] = { items: [], count: 0 };
|
|
9690
|
+
unavailable.push(orgs[i]?.name ?? "An organization");
|
|
9569
9691
|
}
|
|
9570
9692
|
});
|
|
9571
|
-
|
|
9693
|
+
await Promise.all([authDone, sharedDone]);
|
|
9694
|
+
done = true;
|
|
9695
|
+
return build();
|
|
9572
9696
|
},
|
|
9573
9697
|
async reveal(item, fieldKey2) {
|
|
9574
9698
|
const field = item.fields.find((f) => f.key === fieldKey2);
|
|
@@ -9735,6 +9859,7 @@ export {
|
|
|
9735
9859
|
needsRecentSignIn,
|
|
9736
9860
|
parseDefinitions,
|
|
9737
9861
|
providerOf,
|
|
9862
|
+
rowMenuEntriesAt,
|
|
9738
9863
|
siteDomainOf,
|
|
9739
9864
|
sortItems,
|
|
9740
9865
|
useConfirmed,
|