@ai-matrx/vault 0.1.18 → 0.1.19
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 +5 -0
- package/dist/index.cjs +219 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +219 -77
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.19
|
|
4
|
+
|
|
5
|
+
UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
|
|
6
|
+
Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/vault/src`.
|
|
7
|
+
|
|
3
8
|
## 0.1.18
|
|
4
9
|
|
|
5
10
|
- The Authenticator list keeps the code and its countdown ring apart at every width (the code column never squeezes), the list header lines up with the list on wide screens, and the Add authenticator panel's camera button is icon-only so the setup-key box has room on a phone. Consumer action: none.
|
package/dist/index.cjs
CHANGED
|
@@ -929,6 +929,7 @@ function locationFromParams(params, itemId = params.get("item")) {
|
|
|
929
929
|
mode: mode === "edit" || mode === "new" ? mode : "view",
|
|
930
930
|
kind: params.get("kind"),
|
|
931
931
|
url: params.get("url") ?? "",
|
|
932
|
+
...params.get("name") ? { name: params.get("name") ?? "" } : {},
|
|
932
933
|
panel: params.get("panel")
|
|
933
934
|
};
|
|
934
935
|
}
|
|
@@ -941,6 +942,7 @@ function locationToParams(loc) {
|
|
|
941
942
|
if (loc.mode !== "view") q.set("mode", loc.mode);
|
|
942
943
|
if (loc.kind) q.set("kind", loc.kind);
|
|
943
944
|
if (loc.url) q.set("url", loc.url);
|
|
945
|
+
if (loc.name) q.set("name", loc.name);
|
|
944
946
|
if (loc.panel) q.set("panel", loc.panel);
|
|
945
947
|
return q;
|
|
946
948
|
}
|
|
@@ -1981,10 +1983,16 @@ function QrScanPanel({ open, onResult, onClose }) {
|
|
|
1981
1983
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
|
|
1982
1984
|
] });
|
|
1983
1985
|
}
|
|
1984
|
-
function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
|
|
1986
|
+
function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status, autoStartCamera = false }) {
|
|
1985
1987
|
const file = (0, import_react10.useRef)(null);
|
|
1986
1988
|
const canScan = useCanScan();
|
|
1987
1989
|
const [scanning, setScanning] = (0, import_react10.useState)(false);
|
|
1990
|
+
const autoStarted = (0, import_react10.useRef)(false);
|
|
1991
|
+
(0, import_react10.useEffect)(() => {
|
|
1992
|
+
if (!autoStartCamera || !canScan || disabled || autoStarted.current || value) return;
|
|
1993
|
+
autoStarted.current = true;
|
|
1994
|
+
setScanning(true);
|
|
1995
|
+
}, [autoStartCamera, canScan, disabled, value]);
|
|
1988
1996
|
const [reading, setReading] = (0, import_react10.useState)(false);
|
|
1989
1997
|
const [imageError, setImageError] = (0, import_react10.useState)(null);
|
|
1990
1998
|
const [dragging, setDragging] = (0, import_react10.useState)(false);
|
|
@@ -2369,6 +2377,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2369
2377
|
label: "Setup key",
|
|
2370
2378
|
value: input,
|
|
2371
2379
|
autoFocus: true,
|
|
2380
|
+
autoStartCamera: true,
|
|
2372
2381
|
disabled: busy,
|
|
2373
2382
|
onChange: (v) => {
|
|
2374
2383
|
setImage(null);
|
|
@@ -3918,7 +3927,7 @@ var MAX_FAILURES_IN_A_ROW = 5;
|
|
|
3918
3927
|
var failureText = (cause) => cause instanceof Error && cause.message ? cause.message : "The server refused this one.";
|
|
3919
3928
|
async function runImport(input) {
|
|
3920
3929
|
const { io, organizationId, lines, keys, confirmed } = input;
|
|
3921
|
-
const result = { created: [], failed: [], withOtp: 0, stoppedEarly: false, untried: [] };
|
|
3930
|
+
const result = { created: [], failed: [], withOtp: 0, needOtp: [], stoppedEarly: false, untried: [] };
|
|
3922
3931
|
let streak = 0;
|
|
3923
3932
|
for (let i = 0; i < lines.length; i += 1) {
|
|
3924
3933
|
const line = lines[i];
|
|
@@ -3937,7 +3946,10 @@ async function runImport(input) {
|
|
|
3937
3946
|
try {
|
|
3938
3947
|
const made = await confirmed(() => io.createImported(command.body, organizationId, key));
|
|
3939
3948
|
result.created.push(made);
|
|
3940
|
-
if (command.hasOtp)
|
|
3949
|
+
if (command.hasOtp) {
|
|
3950
|
+
result.withOtp += 1;
|
|
3951
|
+
result.needOtp.push(made);
|
|
3952
|
+
}
|
|
3941
3953
|
streak = 0;
|
|
3942
3954
|
} catch (cause) {
|
|
3943
3955
|
const reason = failureText(cause);
|
|
@@ -4537,8 +4549,9 @@ var STATE_BADGE = {
|
|
|
4537
4549
|
unsupported: { text: "Not supported", tone: "neutral" }
|
|
4538
4550
|
};
|
|
4539
4551
|
var ME = "me";
|
|
4540
|
-
function ImportPanel({ port, host, items, organizations, onClose, onChanged, limits }) {
|
|
4552
|
+
function ImportPanel({ port, host, items, definitions, organizations, onClose, onChanged, limits }) {
|
|
4541
4553
|
const confirmed = useConfirmed();
|
|
4554
|
+
const [otpFor, setOtpFor] = (0, import_react20.useState)(null);
|
|
4542
4555
|
const fileInput = (0, import_react20.useRef)(null);
|
|
4543
4556
|
const abort = (0, import_react20.useRef)(null);
|
|
4544
4557
|
const stop = (0, import_react20.useRef)(false);
|
|
@@ -4646,6 +4659,7 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4646
4659
|
failed: result.failed,
|
|
4647
4660
|
skipped,
|
|
4648
4661
|
withOtp: (previous?.withOtp ?? 0) + result.withOtp,
|
|
4662
|
+
needOtp: [...previous?.needOtp ?? [], ...result.needOtp],
|
|
4649
4663
|
untried: result.untried
|
|
4650
4664
|
};
|
|
4651
4665
|
setDone(merged);
|
|
@@ -4671,7 +4685,7 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4671
4685
|
setRunning({ done: 0, total: chosen });
|
|
4672
4686
|
try {
|
|
4673
4687
|
const made = await confirmed(() => port.io.importEnv(text, organizationId, injectSandbox));
|
|
4674
|
-
setDone({ created: made, failed: [], skipped: draft.entries.length - chosen, withOtp: 0, untried: [] });
|
|
4688
|
+
setDone({ created: made, failed: [], skipped: draft.entries.length - chosen, withOtp: 0, needOtp: [], untried: [] });
|
|
4675
4689
|
if (made.length > 0) onChanged();
|
|
4676
4690
|
} catch (cause) {
|
|
4677
4691
|
setError(cause instanceof Error ? cause.message : "The import could not finish.");
|
|
@@ -4786,9 +4800,32 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4786
4800
|
plural(done.withOtp, "login"),
|
|
4787
4801
|
" still need the one-time password added."
|
|
4788
4802
|
] }) }) : null,
|
|
4803
|
+
done.needOtp.map((made) => {
|
|
4804
|
+
const now = items.find((i) => i.id === made.id);
|
|
4805
|
+
if (now?.authenticator) return null;
|
|
4806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: "Authenticator", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { type: "button", className: "truncate text-left text-primary hover:underline", onClick: () => setOtpFor(made.id), children: [
|
|
4807
|
+
"Set up authenticator for ",
|
|
4808
|
+
made.name
|
|
4809
|
+
] }) }, `otp:${made.id}`);
|
|
4810
|
+
}),
|
|
4789
4811
|
done.failed.map((f) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(VaultRow, { label: f.title, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
|
|
4790
4812
|
] }) : null,
|
|
4791
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error })
|
|
4813
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error }),
|
|
4814
|
+
otpFor ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4815
|
+
AddOtpPanel,
|
|
4816
|
+
{
|
|
4817
|
+
port,
|
|
4818
|
+
items,
|
|
4819
|
+
definitions,
|
|
4820
|
+
itemId: otpFor,
|
|
4821
|
+
onClose: () => setOtpFor(null),
|
|
4822
|
+
onChanged,
|
|
4823
|
+
onDone: () => {
|
|
4824
|
+
setOtpFor(null);
|
|
4825
|
+
onChanged();
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
) : null
|
|
4792
4829
|
] });
|
|
4793
4830
|
}
|
|
4794
4831
|
function toggle(set, key, on) {
|
|
@@ -6410,6 +6447,13 @@ function ItemPane(props) {
|
|
|
6410
6447
|
|
|
6411
6448
|
// src/ItemList.tsx
|
|
6412
6449
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
6450
|
+
var RECOVERY_KEYS = /^recovery_codes?$/;
|
|
6451
|
+
function otherCopyFields(item, user, secret) {
|
|
6452
|
+
if (!item.capabilities.canUse) return [];
|
|
6453
|
+
return item.fields.filter(
|
|
6454
|
+
(f) => f !== user && f !== secret && f.active !== false && f.handling !== "sealed" && (f.handling === "visible" || item.capabilities.canReveal) && !isAuthenticatorStorage(f.key) && !RECOVERY_KEYS.test(f.key)
|
|
6455
|
+
);
|
|
6456
|
+
}
|
|
6413
6457
|
var SORTS2 = [
|
|
6414
6458
|
{ value: "name", label: "Name A\u2013Z" },
|
|
6415
6459
|
{ value: "name-desc", label: "Name Z\u2013A" },
|
|
@@ -6422,7 +6466,7 @@ function subtitleOf(item, withOwner) {
|
|
|
6422
6466
|
const what = loginSiteOf(item) || item.definitionLabel;
|
|
6423
6467
|
return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
|
|
6424
6468
|
}
|
|
6425
|
-
function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false }) {
|
|
6469
|
+
function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems }) {
|
|
6426
6470
|
const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
|
|
6427
6471
|
const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
|
|
6428
6472
|
const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
|
|
@@ -6443,7 +6487,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6443
6487
|
}
|
|
6444
6488
|
};
|
|
6445
6489
|
const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
|
|
6446
|
-
const row = (i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners }, i.id);
|
|
6490
|
+
const row = (i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners, ownContextMenu: rowContextMenu === "package", extra: rowMenuItems }, i.id);
|
|
6447
6491
|
const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
|
|
6448
6492
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
|
|
6449
6493
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
|
|
@@ -6458,9 +6502,12 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6458
6502
|
" ",
|
|
6459
6503
|
items.length === 1 ? "item" : "items"
|
|
6460
6504
|
] }),
|
|
6461
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6462
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
6463
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.
|
|
6505
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex shrink-0 items-center gap-0.5", children: [
|
|
6506
|
+
onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RefreshGlyph, { className: refreshing ? "animate-spin" : "" }), "aria-label": "Refresh", title: "Refresh the list", disabled: refreshing, onClick: onRefresh }) : null,
|
|
6507
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { children: [
|
|
6508
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SortGlyph, {}), "aria-label": `Sort: ${sortLabel}`, children: sortLabel }) }),
|
|
6509
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuContent, { align: "end", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuRadioGroup, { value: sort, onValueChange: (v) => onSort(sorts.find((s) => s.value === v)?.value ?? "name"), children: sorts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuRadioItem, { value: s.value, children: s.label }, s.value)) }) })
|
|
6510
|
+
] })
|
|
6464
6511
|
] })
|
|
6465
6512
|
] })
|
|
6466
6513
|
] }),
|
|
@@ -6475,7 +6522,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6475
6522
|
] }) })
|
|
6476
6523
|
] });
|
|
6477
6524
|
}
|
|
6478
|
-
function ListRow({ item, family, port, on, onSelect, withOwner }) {
|
|
6525
|
+
function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra }) {
|
|
6479
6526
|
const confirmed = useConfirmed();
|
|
6480
6527
|
const copy = useCopy();
|
|
6481
6528
|
const notify = useNotify();
|
|
@@ -6500,67 +6547,93 @@ function ListRow({ item, family, port, on, onSelect, withOwner }) {
|
|
|
6500
6547
|
const copyPassword = () => secret && void run("Password", () => port.reveal(item, secret.key), true);
|
|
6501
6548
|
const copyCode = () => code && void run("One-time code", async () => (await port.authenticator.code(item)).code, true);
|
|
6502
6549
|
const copyName = () => void copy(item.name, "Name", { secret: false });
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
]
|
|
6519
|
-
}
|
|
6520
|
-
),
|
|
6521
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-0.5", "data-surface": on ? "primary" : void 0, children: [
|
|
6522
|
-
user || secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system20.cn)("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
|
|
6523
|
-
user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
|
|
6524
|
-
secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
|
|
6525
|
-
] }) : null,
|
|
6526
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [
|
|
6527
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
6528
|
-
import_controls23.Button,
|
|
6550
|
+
const others = otherCopyFields(item, user, secret);
|
|
6551
|
+
const custom = canUse ? item.custom.filter((c) => c.value) : [];
|
|
6552
|
+
const hostEntries = extra?.(item) ?? [];
|
|
6553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6554
|
+
"li",
|
|
6555
|
+
{
|
|
6556
|
+
className: "group relative",
|
|
6557
|
+
"data-item": item.id,
|
|
6558
|
+
onContextMenu: ownContextMenu ? (e) => {
|
|
6559
|
+
e.preventDefault();
|
|
6560
|
+
setMenuOpen(true);
|
|
6561
|
+
} : void 0,
|
|
6562
|
+
children: [
|
|
6563
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6564
|
+
"button",
|
|
6529
6565
|
{
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
"
|
|
6533
|
-
|
|
6534
|
-
className: (0, import_design_system20.cn)("
|
|
6566
|
+
type: "button",
|
|
6567
|
+
"aria-current": on ? "true" : void 0,
|
|
6568
|
+
"data-surface": on ? "primary" : void 0,
|
|
6569
|
+
onClick: () => onSelect(item.id),
|
|
6570
|
+
className: (0, import_design_system20.cn)("flex h-12 w-full items-center gap-3 rounded-lg px-2 pr-10 text-left", on ? "bg-primary text-primary-foreground" : "hover:bg-accent"),
|
|
6571
|
+
children: [
|
|
6572
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item), inverted: on }),
|
|
6573
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
|
|
6574
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm font-medium", children: item.name }),
|
|
6575
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: subtitleOf(item, withOwner) })
|
|
6576
|
+
] })
|
|
6577
|
+
]
|
|
6535
6578
|
}
|
|
6536
|
-
)
|
|
6537
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6538
|
-
user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6539
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
6540
|
-
"Copy ",
|
|
6541
|
-
user.label.toLowerCase()
|
|
6542
|
-
] }) : null,
|
|
6543
|
-
secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
|
|
6544
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
|
|
6545
|
-
"Copy password"
|
|
6546
|
-
] }) : null,
|
|
6547
|
-
code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
|
|
6548
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
|
|
6549
|
-
"Copy one-time code"
|
|
6579
|
+
),
|
|
6580
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-0.5", "data-surface": on ? "primary" : void 0, children: [
|
|
6581
|
+
user || secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system20.cn)("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
|
|
6582
|
+
user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
|
|
6583
|
+
secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
|
|
6550
6584
|
] }) : null,
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6585
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [
|
|
6586
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
6587
|
+
import_controls23.Button,
|
|
6588
|
+
{
|
|
6589
|
+
variant: "quiet",
|
|
6590
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MoreGlyph, {}),
|
|
6591
|
+
"aria-label": `Actions for ${item.name}`,
|
|
6592
|
+
disabled: busy,
|
|
6593
|
+
className: (0, import_design_system20.cn)("[@media(hover:hover)]:invisible [@media(hover:hover)]:group-hover:visible [@media(hover:hover)]:group-focus-within:visible", menuOpen && "[@media(hover:hover)]:visible")
|
|
6594
|
+
}
|
|
6595
|
+
) }),
|
|
6596
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuContent, { align: "end", children: [
|
|
6597
|
+
user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyUser, children: [
|
|
6598
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }),
|
|
6599
|
+
"Copy ",
|
|
6600
|
+
user.label.toLowerCase()
|
|
6601
|
+
] }) : null,
|
|
6602
|
+
secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
|
|
6603
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
|
|
6604
|
+
"Copy password"
|
|
6605
|
+
] }) : null,
|
|
6606
|
+
code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
|
|
6607
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
|
|
6608
|
+
"Copy one-time code"
|
|
6609
|
+
] }) : null,
|
|
6610
|
+
others.map((f) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void run(f.label, () => port.reveal(item, f.key), f.handling !== "visible"), children: [
|
|
6611
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
|
|
6612
|
+
"Copy ",
|
|
6613
|
+
f.label.toLowerCase()
|
|
6614
|
+
] }, f.key)),
|
|
6615
|
+
custom.map((c) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void copy(c.value, c.label, { secret: false }), children: [
|
|
6616
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
|
|
6617
|
+
"Copy ",
|
|
6618
|
+
c.label.toLowerCase()
|
|
6619
|
+
] }, `custom:${c.key}`)),
|
|
6620
|
+
user || secret || code || others.length > 0 || custom.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
|
|
6621
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => onSelect(item.id), children: [
|
|
6622
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }),
|
|
6623
|
+
"Open"
|
|
6624
|
+
] }),
|
|
6625
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyName, children: [
|
|
6626
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
|
|
6627
|
+
"Copy name"
|
|
6628
|
+
] }),
|
|
6629
|
+
hostEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
|
|
6630
|
+
hostEntries.map((h) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuItem, { onSelect: h.onSelect, children: h.label }, h.label))
|
|
6631
|
+
] })
|
|
6559
6632
|
] })
|
|
6560
6633
|
] })
|
|
6561
|
-
]
|
|
6562
|
-
|
|
6563
|
-
|
|
6634
|
+
]
|
|
6635
|
+
}
|
|
6636
|
+
);
|
|
6564
6637
|
}
|
|
6565
6638
|
|
|
6566
6639
|
// src/lists/TrashView.tsx
|
|
@@ -8187,6 +8260,7 @@ var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
|
8187
8260
|
var FULL_AT = 1120;
|
|
8188
8261
|
var SPLIT_AT = 640;
|
|
8189
8262
|
var NO_HOST = {};
|
|
8263
|
+
var FOCUS_GAP_MS = 2e3;
|
|
8190
8264
|
var ITEM_PANELS = {
|
|
8191
8265
|
share: SharePanel,
|
|
8192
8266
|
move: MovePanel,
|
|
@@ -8315,9 +8389,13 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8315
8389
|
const knobsRef = (0, import_react42.useRef)(host.knobs);
|
|
8316
8390
|
knobsRef.current = host.knobs;
|
|
8317
8391
|
const [signedOut, setSignedOut] = (0, import_react42.useState)(false);
|
|
8318
|
-
const
|
|
8319
|
-
|
|
8320
|
-
|
|
8392
|
+
const [refreshError, setRefreshError] = (0, import_react42.useState)(null);
|
|
8393
|
+
const [refreshing, setRefreshing] = (0, import_react42.useState)(false);
|
|
8394
|
+
const loadedAt = (0, import_react42.useRef)(0);
|
|
8395
|
+
const inFlight = (0, import_react42.useRef)(false);
|
|
8396
|
+
const apply = (0, import_react42.useCallback)(
|
|
8397
|
+
(s) => {
|
|
8398
|
+
loadedAt.current = Date.now();
|
|
8321
8399
|
setSnap(s);
|
|
8322
8400
|
setItems(s.items);
|
|
8323
8401
|
setDeleted(s.deleted);
|
|
@@ -8325,15 +8403,71 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8325
8403
|
(rows) => setDeleted([...s.deleted, ...rows.filter((r) => !s.deleted.some((d) => d.id === r.id))]),
|
|
8326
8404
|
() => void 0
|
|
8327
8405
|
);
|
|
8328
|
-
}
|
|
8406
|
+
},
|
|
8407
|
+
[port]
|
|
8408
|
+
);
|
|
8409
|
+
const refresh = (0, import_react42.useCallback)(() => {
|
|
8410
|
+
if (inFlight.current) return;
|
|
8411
|
+
inFlight.current = true;
|
|
8412
|
+
setRefreshing(true);
|
|
8413
|
+
port.load().then((s) => {
|
|
8414
|
+
setRefreshError(null);
|
|
8415
|
+
apply(s);
|
|
8416
|
+
}).catch((e) => setRefreshError(plainLoadError(e instanceof Error && e.message ? e.message : "The vault could not be read."))).finally(() => {
|
|
8417
|
+
inFlight.current = false;
|
|
8418
|
+
setRefreshing(false);
|
|
8419
|
+
});
|
|
8420
|
+
port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
|
|
8421
|
+
port.lists.recents?.list().then(setRecents, () => void 0);
|
|
8422
|
+
}, [port, apply]);
|
|
8423
|
+
const load = (0, import_react42.useCallback)(() => {
|
|
8424
|
+
setError(null);
|
|
8425
|
+
setRefreshError(null);
|
|
8426
|
+
port.load().then(apply).catch((e) => {
|
|
8329
8427
|
setSignedOut(e?.status === 401);
|
|
8330
8428
|
setError(e instanceof Error ? e.message : "The vault could not be loaded.");
|
|
8331
8429
|
});
|
|
8332
8430
|
port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
|
|
8333
8431
|
knobsRef.current?.().catch(() => void 0);
|
|
8334
8432
|
port.lists.recents?.list().then(setRecents, () => void 0);
|
|
8335
|
-
}, [port]);
|
|
8433
|
+
}, [port, apply]);
|
|
8336
8434
|
(0, import_react42.useEffect)(load, [load]);
|
|
8435
|
+
const quietRef = (0, import_react42.useRef)({ loc, ready: false });
|
|
8436
|
+
quietRef.current = { loc, ready: snap !== null && !error };
|
|
8437
|
+
const [knobSeconds, setKnobSeconds] = (0, import_react42.useState)(null);
|
|
8438
|
+
(0, import_react42.useEffect)(() => {
|
|
8439
|
+
if (host.refreshSeconds !== void 0 || !knobsRef.current) return;
|
|
8440
|
+
let live = true;
|
|
8441
|
+
knobsRef.current().then(
|
|
8442
|
+
(r) => {
|
|
8443
|
+
const v = r["vault.list.refresh_seconds"];
|
|
8444
|
+
if (live && typeof v === "number" && v > 0) setKnobSeconds(v);
|
|
8445
|
+
},
|
|
8446
|
+
() => void 0
|
|
8447
|
+
);
|
|
8448
|
+
return () => {
|
|
8449
|
+
live = false;
|
|
8450
|
+
};
|
|
8451
|
+
}, [host.refreshSeconds]);
|
|
8452
|
+
const everySeconds = host.refreshSeconds ?? knobSeconds;
|
|
8453
|
+
(0, import_react42.useEffect)(() => {
|
|
8454
|
+
const quietly = (minGapMs) => {
|
|
8455
|
+
const { loc: here, ready } = quietRef.current;
|
|
8456
|
+
if (!ready || document.visibilityState === "hidden" || dirty.current || here.mode !== "view") return;
|
|
8457
|
+
if (Date.now() - loadedAt.current < minGapMs) return;
|
|
8458
|
+
refresh();
|
|
8459
|
+
};
|
|
8460
|
+
const onBack = () => quietly(FOCUS_GAP_MS);
|
|
8461
|
+
const onVisible = () => document.visibilityState === "visible" && onBack();
|
|
8462
|
+
window.addEventListener("focus", onBack);
|
|
8463
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
8464
|
+
const timer2 = everySeconds && everySeconds > 0 ? setInterval(() => quietly(0), everySeconds * 1e3) : null;
|
|
8465
|
+
return () => {
|
|
8466
|
+
window.removeEventListener("focus", onBack);
|
|
8467
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
8468
|
+
if (timer2) clearInterval(timer2);
|
|
8469
|
+
};
|
|
8470
|
+
}, [refresh, everySeconds]);
|
|
8337
8471
|
const inventoryRef = (0, import_react42.useRef)(onInventory);
|
|
8338
8472
|
inventoryRef.current = onInventory;
|
|
8339
8473
|
(0, import_react42.useEffect)(() => {
|
|
@@ -8499,6 +8633,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8499
8633
|
onSort: (sort) => go({ sort }, "replace"),
|
|
8500
8634
|
canSortViewed: port.lists.recents !== void 0,
|
|
8501
8635
|
onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
|
|
8636
|
+
onRefresh: refresh,
|
|
8637
|
+
refreshing,
|
|
8638
|
+
rowContextMenu: host.rowContextMenu ?? "package",
|
|
8639
|
+
...host.rowMenuItems ? { rowMenuItems: host.rowMenuItems } : {},
|
|
8502
8640
|
...matchingIds ? { matchingIds } : {},
|
|
8503
8641
|
...compactHeader ? { header: compactHeader } : {}
|
|
8504
8642
|
}
|
|
@@ -8523,14 +8661,14 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8523
8661
|
host,
|
|
8524
8662
|
organizations: orgs,
|
|
8525
8663
|
defaultOwner: ownerDefault,
|
|
8526
|
-
seed: { name: guess?.name
|
|
8664
|
+
seed: { name: loc.name?.trim() || guess?.name || "", url: guess?.url ?? loc.url.trim() },
|
|
8527
8665
|
onBack: () => go({ kind: null }),
|
|
8528
8666
|
onMode: () => void 0,
|
|
8529
8667
|
onSaved: (item, _wasNew, n, enrolled) => {
|
|
8530
8668
|
replaceItem(item);
|
|
8531
8669
|
dirty.current = false;
|
|
8532
8670
|
if (enrolled) setFirstCode(item);
|
|
8533
|
-
move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", panel: null }, "replace");
|
|
8671
|
+
move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", name: "", panel: null }, "replace");
|
|
8534
8672
|
setNotice(n);
|
|
8535
8673
|
notify("Saved");
|
|
8536
8674
|
},
|
|
@@ -8538,7 +8676,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8538
8676
|
onDirty: (d) => {
|
|
8539
8677
|
dirty.current = d;
|
|
8540
8678
|
},
|
|
8541
|
-
onCancelNew: () => go({ mode: "view", kind: null, url: "", itemId: null }),
|
|
8679
|
+
onCancelNew: () => go({ mode: "view", kind: null, url: "", name: "", itemId: null }),
|
|
8542
8680
|
onPanel: () => void 0,
|
|
8543
8681
|
onChanged: () => void 0
|
|
8544
8682
|
},
|
|
@@ -8629,13 +8767,17 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8629
8767
|
const grid = mode === "full" ? "grid-cols-[15rem_21rem_minmax(0,1fr)]" : mode === "split" ? "grid-cols-[19rem_minmax(0,1fr)]" : "grid-cols-1";
|
|
8630
8768
|
const shown = panesShown(mode, specialView !== null, showingDetail);
|
|
8631
8769
|
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { ref: rootRef, className: "flex h-full min-h-0 w-full flex-col bg-background text-foreground", children: [
|
|
8770
|
+
refreshError ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-4 py-2 text-sm", children: [
|
|
8771
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "min-w-0 flex-1 truncate text-warning", title: refreshError, children: "Couldn\u2019t refresh. Showing what was loaded." }),
|
|
8772
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
|
|
8773
|
+
] }) : null,
|
|
8632
8774
|
snap.unavailable.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-4 py-2 text-sm", children: [
|
|
8633
8775
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "min-w-0 flex-1 truncate text-warning", children: [
|
|
8634
8776
|
"Couldn\u2019t load ",
|
|
8635
8777
|
snap.unavailable.join(", "),
|
|
8636
8778
|
". Showing the rest."
|
|
8637
8779
|
] }),
|
|
8638
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick:
|
|
8780
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
|
|
8639
8781
|
] }) : null,
|
|
8640
8782
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: `grid min-h-0 w-full flex-1 ${grid} overflow-hidden`, children: [
|
|
8641
8783
|
mode === "full" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|