@ai-matrx/vault 0.1.18 → 0.1.20
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 +29 -0
- package/dist/index.cjs +377 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -5
- package/dist/index.d.ts +49 -5
- package/dist/index.js +398 -173
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -740,7 +740,7 @@ var DAY_FORMAT = { year: "numeric", month: "short", day: "numeric" };
|
|
|
740
740
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
741
741
|
var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
742
742
|
function SecretText({ value, multiline }) {
|
|
743
|
-
if (multiline || value.length > 200) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "whitespace-pre-wrap break-all font-mono text-sm", children: value });
|
|
743
|
+
if (multiline || value.length > 200 || value.includes("\n")) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "whitespace-pre-wrap break-all font-mono text-sm", children: value });
|
|
744
744
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "break-all font-mono text-sm tracking-wide", children: [...value].map((ch, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: /[0-9]/.test(ch) ? "text-primary" : /[^A-Za-z0-9]/.test(ch) ? "text-warning" : void 0, children: ch }, i)) });
|
|
745
745
|
}
|
|
746
746
|
var quietError = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
|
|
@@ -876,18 +876,19 @@ function EditRow({ label, hint: hint2, value, onChange, placeholder, secret = fa
|
|
|
876
876
|
}
|
|
877
877
|
);
|
|
878
878
|
}
|
|
879
|
-
function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove, extra, onPasteValue }) {
|
|
879
|
+
function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove, extra, onPasteValue, multiline = false }) {
|
|
880
880
|
const [shown, setShown] = (0, import_react5.useState)(false);
|
|
881
881
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
882
882
|
VaultRow,
|
|
883
883
|
{
|
|
884
884
|
label: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Field, { className: "w-full", value: label, onChange: (e) => onLabel(e.target.value), placeholder: "Name", "aria-label": "Field name" }),
|
|
885
|
+
top: multiline,
|
|
885
886
|
actions: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
886
|
-
secret && value ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: shown ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeGlyph, {}), "aria-label": shown ? "Hide" : "Show", onClick: () => setShown((x) => !x) }) : null,
|
|
887
|
+
secret && value && !multiline ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: shown ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeGlyph, {}), "aria-label": shown ? "Hide" : "Show", onClick: () => setShown((x) => !x) }) : null,
|
|
887
888
|
extra,
|
|
888
889
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", removes: true, icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XGlyph, {}), "aria-label": "Remove field", onClick: onRemove })
|
|
889
890
|
] }),
|
|
890
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
891
|
+
children: multiline ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Textarea, { className: "w-full", value, onChange: (e) => onValue(e.target.value), rows: 4, placeholder: secret ? "Encrypted, hidden until shown" : "Value", autoComplete: "off", spellCheck: !secret, "aria-label": label ? `${label} value` : "Field value" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
891
892
|
import_controls2.Field,
|
|
892
893
|
{
|
|
893
894
|
className: "w-full",
|
|
@@ -929,6 +930,7 @@ function locationFromParams(params, itemId = params.get("item")) {
|
|
|
929
930
|
mode: mode === "edit" || mode === "new" ? mode : "view",
|
|
930
931
|
kind: params.get("kind"),
|
|
931
932
|
url: params.get("url") ?? "",
|
|
933
|
+
...params.get("name") ? { name: params.get("name") ?? "" } : {},
|
|
932
934
|
panel: params.get("panel")
|
|
933
935
|
};
|
|
934
936
|
}
|
|
@@ -941,6 +943,7 @@ function locationToParams(loc) {
|
|
|
941
943
|
if (loc.mode !== "view") q.set("mode", loc.mode);
|
|
942
944
|
if (loc.kind) q.set("kind", loc.kind);
|
|
943
945
|
if (loc.url) q.set("url", loc.url);
|
|
946
|
+
if (loc.name) q.set("name", loc.name);
|
|
944
947
|
if (loc.panel) q.set("panel", loc.panel);
|
|
945
948
|
return q;
|
|
946
949
|
}
|
|
@@ -1004,6 +1007,7 @@ function sortItems(items, sort, recents) {
|
|
|
1004
1007
|
if (sort === "updated") return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
1005
1008
|
return out.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
1006
1009
|
}
|
|
1010
|
+
var SECURE_NOTE_KEY = "secure_notes";
|
|
1007
1011
|
function draftOf(item) {
|
|
1008
1012
|
return {
|
|
1009
1013
|
name: item?.name ?? "",
|
|
@@ -1054,7 +1058,9 @@ function rebaseDraft(oldBase, newBase, draft) {
|
|
|
1054
1058
|
secrets: draft.secrets,
|
|
1055
1059
|
custom,
|
|
1056
1060
|
removed: draft.removed,
|
|
1057
|
-
authenticator: draft.authenticator
|
|
1061
|
+
authenticator: draft.authenticator,
|
|
1062
|
+
...draft.browser ? { browser: draft.browser } : {},
|
|
1063
|
+
...draft.fieldSettings ? { fieldSettings: draft.fieldSettings } : {}
|
|
1058
1064
|
};
|
|
1059
1065
|
}
|
|
1060
1066
|
function panesShown(mode, specialView, showingDetail) {
|
|
@@ -1082,7 +1088,8 @@ var FAMILY_LABELS = {
|
|
|
1082
1088
|
signing_files: "Signing & files"
|
|
1083
1089
|
};
|
|
1084
1090
|
var COMMON_DEFINITION_KEYS = ["website_login", "username_password", "api_key", "env_value", "database_login", "custom"];
|
|
1085
|
-
var MULTILINE = /(json|pem|private_key|certificate|cert|bundle|document|recovery_codes)/;
|
|
1091
|
+
var MULTILINE = /(json|pem|private_key|certificate|cert|bundle|document|recovery_codes|(^|_)notes?($|_))/;
|
|
1092
|
+
var isMultilineKey = (key) => MULTILINE.test(key);
|
|
1086
1093
|
var SECRET = /(password|secret|token|api_key|private|passphrase|credential|seed|key$|_key)/;
|
|
1087
1094
|
var URLISH = /(url|uri|endpoint|webhook|hook)/;
|
|
1088
1095
|
function humanize(key) {
|
|
@@ -1981,10 +1988,16 @@ function QrScanPanel({ open, onResult, onClose }) {
|
|
|
1981
1988
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
|
|
1982
1989
|
] });
|
|
1983
1990
|
}
|
|
1984
|
-
function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
|
|
1991
|
+
function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status, autoStartCamera = false }) {
|
|
1985
1992
|
const file = (0, import_react10.useRef)(null);
|
|
1986
1993
|
const canScan = useCanScan();
|
|
1987
1994
|
const [scanning, setScanning] = (0, import_react10.useState)(false);
|
|
1995
|
+
const autoStarted = (0, import_react10.useRef)(false);
|
|
1996
|
+
(0, import_react10.useEffect)(() => {
|
|
1997
|
+
if (!autoStartCamera || !canScan || disabled || autoStarted.current || value) return;
|
|
1998
|
+
autoStarted.current = true;
|
|
1999
|
+
setScanning(true);
|
|
2000
|
+
}, [autoStartCamera, canScan, disabled, value]);
|
|
1988
2001
|
const [reading, setReading] = (0, import_react10.useState)(false);
|
|
1989
2002
|
const [imageError, setImageError] = (0, import_react10.useState)(null);
|
|
1990
2003
|
const [dragging, setDragging] = (0, import_react10.useState)(false);
|
|
@@ -2290,7 +2303,7 @@ function normalizeWebsite(raw) {
|
|
|
2290
2303
|
return null;
|
|
2291
2304
|
}
|
|
2292
2305
|
}
|
|
2293
|
-
function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onDone }) {
|
|
2306
|
+
function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onAdded, onDone }) {
|
|
2294
2307
|
const confirmed = useConfirmed();
|
|
2295
2308
|
const candidates = items.filter((i) => !i.authenticator && !i.deletion && (i.capabilities.canEdit || i.capabilities.canManage)).sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }));
|
|
2296
2309
|
const [input, setInput] = (0, import_react12.useState)("");
|
|
@@ -2343,6 +2356,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2343
2356
|
setInput("");
|
|
2344
2357
|
setImage(null);
|
|
2345
2358
|
setSaved(next);
|
|
2359
|
+
onAdded?.(next);
|
|
2346
2360
|
onChanged();
|
|
2347
2361
|
} catch (e) {
|
|
2348
2362
|
setError(wasCancelled(e) ? "Not confirmed, so nothing was added." : otpActionError(e, "add it"));
|
|
@@ -2369,6 +2383,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2369
2383
|
label: "Setup key",
|
|
2370
2384
|
value: input,
|
|
2371
2385
|
autoFocus: true,
|
|
2386
|
+
autoStartCamera: true,
|
|
2372
2387
|
disabled: busy,
|
|
2373
2388
|
onChange: (v) => {
|
|
2374
2389
|
setImage(null);
|
|
@@ -2450,7 +2465,23 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
|
|
|
2450
2465
|
const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
|
|
2451
2466
|
return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
|
|
2452
2467
|
}
|
|
2453
|
-
function
|
|
2468
|
+
function withAdded(items, added) {
|
|
2469
|
+
if (added.size === 0) return items;
|
|
2470
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2471
|
+
const out = items.map((i) => {
|
|
2472
|
+
seen.add(i.id);
|
|
2473
|
+
const mine = added.get(i.id);
|
|
2474
|
+
return mine && !i.authenticator ? { ...i, authenticator: mine.authenticator, name: mine.name } : i;
|
|
2475
|
+
});
|
|
2476
|
+
for (const [id, a] of added) if (!seen.has(id)) out.push(a);
|
|
2477
|
+
return out;
|
|
2478
|
+
}
|
|
2479
|
+
function AuthenticatorView({ port, host, items: listed, definitions, query, onOpen, onChanged, header }) {
|
|
2480
|
+
const [added, setAdded] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
|
|
2481
|
+
const items = withAdded(listed, added);
|
|
2482
|
+
(0, import_react13.useEffect)(() => {
|
|
2483
|
+
if (added.size > 0 && [...added.keys()].every((id) => listed.some((i) => i.id === id && i.authenticator))) setAdded(/* @__PURE__ */ new Map());
|
|
2484
|
+
}, [listed, added]);
|
|
2454
2485
|
const [entries, setEntries] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
|
|
2455
2486
|
const [hidden, setHidden] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
|
|
2456
2487
|
const [adding, setAdding] = (0, import_react13.useState)(false);
|
|
@@ -2466,7 +2497,7 @@ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onCh
|
|
|
2466
2497
|
return () => {
|
|
2467
2498
|
live = false;
|
|
2468
2499
|
};
|
|
2469
|
-
}, [port,
|
|
2500
|
+
}, [port, listed, added]);
|
|
2470
2501
|
const groups = otpGroups(items, entries, query, hidden);
|
|
2471
2502
|
const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
|
|
2472
2503
|
const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
|
|
@@ -2512,6 +2543,7 @@ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onCh
|
|
|
2512
2543
|
definitions,
|
|
2513
2544
|
onClose: () => setAdding(false),
|
|
2514
2545
|
onChanged,
|
|
2546
|
+
onAdded: (item) => setAdded((cur) => new Map(cur).set(item.id, item)),
|
|
2515
2547
|
onDone: (id) => {
|
|
2516
2548
|
setAdding(false);
|
|
2517
2549
|
onOpen(id);
|
|
@@ -3918,7 +3950,7 @@ var MAX_FAILURES_IN_A_ROW = 5;
|
|
|
3918
3950
|
var failureText = (cause) => cause instanceof Error && cause.message ? cause.message : "The server refused this one.";
|
|
3919
3951
|
async function runImport(input) {
|
|
3920
3952
|
const { io, organizationId, lines, keys, confirmed } = input;
|
|
3921
|
-
const result = { created: [], failed: [], withOtp: 0, stoppedEarly: false, untried: [] };
|
|
3953
|
+
const result = { created: [], failed: [], withOtp: 0, needOtp: [], stoppedEarly: false, untried: [] };
|
|
3922
3954
|
let streak = 0;
|
|
3923
3955
|
for (let i = 0; i < lines.length; i += 1) {
|
|
3924
3956
|
const line = lines[i];
|
|
@@ -3937,7 +3969,10 @@ async function runImport(input) {
|
|
|
3937
3969
|
try {
|
|
3938
3970
|
const made = await confirmed(() => io.createImported(command.body, organizationId, key));
|
|
3939
3971
|
result.created.push(made);
|
|
3940
|
-
if (command.hasOtp)
|
|
3972
|
+
if (command.hasOtp) {
|
|
3973
|
+
result.withOtp += 1;
|
|
3974
|
+
result.needOtp.push(made);
|
|
3975
|
+
}
|
|
3941
3976
|
streak = 0;
|
|
3942
3977
|
} catch (cause) {
|
|
3943
3978
|
const reason = failureText(cause);
|
|
@@ -4537,8 +4572,9 @@ var STATE_BADGE = {
|
|
|
4537
4572
|
unsupported: { text: "Not supported", tone: "neutral" }
|
|
4538
4573
|
};
|
|
4539
4574
|
var ME = "me";
|
|
4540
|
-
function ImportPanel({ port, host, items, organizations, onClose, onChanged, limits }) {
|
|
4575
|
+
function ImportPanel({ port, host, items, definitions, organizations, onClose, onChanged, limits }) {
|
|
4541
4576
|
const confirmed = useConfirmed();
|
|
4577
|
+
const [otpFor, setOtpFor] = (0, import_react20.useState)(null);
|
|
4542
4578
|
const fileInput = (0, import_react20.useRef)(null);
|
|
4543
4579
|
const abort = (0, import_react20.useRef)(null);
|
|
4544
4580
|
const stop = (0, import_react20.useRef)(false);
|
|
@@ -4646,6 +4682,7 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4646
4682
|
failed: result.failed,
|
|
4647
4683
|
skipped,
|
|
4648
4684
|
withOtp: (previous?.withOtp ?? 0) + result.withOtp,
|
|
4685
|
+
needOtp: [...previous?.needOtp ?? [], ...result.needOtp],
|
|
4649
4686
|
untried: result.untried
|
|
4650
4687
|
};
|
|
4651
4688
|
setDone(merged);
|
|
@@ -4671,7 +4708,7 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4671
4708
|
setRunning({ done: 0, total: chosen });
|
|
4672
4709
|
try {
|
|
4673
4710
|
const made = await confirmed(() => port.io.importEnv(text, organizationId, injectSandbox));
|
|
4674
|
-
setDone({ created: made, failed: [], skipped: draft.entries.length - chosen, withOtp: 0, untried: [] });
|
|
4711
|
+
setDone({ created: made, failed: [], skipped: draft.entries.length - chosen, withOtp: 0, needOtp: [], untried: [] });
|
|
4675
4712
|
if (made.length > 0) onChanged();
|
|
4676
4713
|
} catch (cause) {
|
|
4677
4714
|
setError(cause instanceof Error ? cause.message : "The import could not finish.");
|
|
@@ -4786,9 +4823,32 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4786
4823
|
plural(done.withOtp, "login"),
|
|
4787
4824
|
" still need the one-time password added."
|
|
4788
4825
|
] }) }) : null,
|
|
4826
|
+
done.needOtp.map((made) => {
|
|
4827
|
+
const now = items.find((i) => i.id === made.id);
|
|
4828
|
+
if (now?.authenticator) return null;
|
|
4829
|
+
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: [
|
|
4830
|
+
"Set up authenticator for ",
|
|
4831
|
+
made.name
|
|
4832
|
+
] }) }, `otp:${made.id}`);
|
|
4833
|
+
}),
|
|
4789
4834
|
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
4835
|
] }) : null,
|
|
4791
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error })
|
|
4836
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ErrorLine, { message: error }),
|
|
4837
|
+
otpFor ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4838
|
+
AddOtpPanel,
|
|
4839
|
+
{
|
|
4840
|
+
port,
|
|
4841
|
+
items,
|
|
4842
|
+
definitions,
|
|
4843
|
+
itemId: otpFor,
|
|
4844
|
+
onClose: () => setOtpFor(null),
|
|
4845
|
+
onChanged,
|
|
4846
|
+
onDone: () => {
|
|
4847
|
+
setOtpFor(null);
|
|
4848
|
+
onChanged();
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
) : null
|
|
4792
4852
|
] });
|
|
4793
4853
|
}
|
|
4794
4854
|
function toggle(set, key, on) {
|
|
@@ -5208,14 +5268,12 @@ var import_confirm_opener7 = require("@ai-matrx/kit/confirm-opener");
|
|
|
5208
5268
|
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
5209
5269
|
var HANDLINGS = ["visible", "revealable", "sealed"];
|
|
5210
5270
|
var hint = (text, tone = "muted") => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
|
|
5211
|
-
var
|
|
5212
|
-
function
|
|
5213
|
-
const
|
|
5214
|
-
const env = field.envKey ?? "";
|
|
5271
|
+
var saysMore = (s, defaultHandling) => Boolean(s.envKey?.trim() || s.injectIntoSandbox || s.handling && s.handling !== defaultHandling);
|
|
5272
|
+
function FieldSettingsRows({ name, value, defaultHandling = "revealable", onChange }) {
|
|
5273
|
+
const env = value.envKey ?? "";
|
|
5215
5274
|
const envProblem = envKeyProblem(env);
|
|
5216
5275
|
const envOk = env.trim() !== "" && !envProblem;
|
|
5217
|
-
const handling =
|
|
5218
|
-
const name = field.label.trim() || "Secret";
|
|
5276
|
+
const handling = value.handling ?? defaultHandling;
|
|
5219
5277
|
const chooseHandling = async (next) => {
|
|
5220
5278
|
if (next === handling) return;
|
|
5221
5279
|
if (next === "sealed") {
|
|
@@ -5229,6 +5287,38 @@ function SecretBuilderRow({ field, onChange, onRemove }) {
|
|
|
5229
5287
|
}
|
|
5230
5288
|
onChange({ handling: next });
|
|
5231
5289
|
};
|
|
5290
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
5291
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Env key", hint: envProblem ? hint(envProblem, "destructive") : hint("The name sandboxes and workflows use"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5292
|
+
import_controls18.Field,
|
|
5293
|
+
{
|
|
5294
|
+
className: "w-full font-mono",
|
|
5295
|
+
"aria-label": `${name} env key`,
|
|
5296
|
+
"aria-invalid": Boolean(envProblem),
|
|
5297
|
+
placeholder: "e.g. GITHUB_TOKEN",
|
|
5298
|
+
autoComplete: "off",
|
|
5299
|
+
spellCheck: false,
|
|
5300
|
+
value: env,
|
|
5301
|
+
onChange: (e) => onChange({ envKey: e.target.value, ...e.target.value.trim() ? {} : { injectIntoSandbox: false } })
|
|
5302
|
+
}
|
|
5303
|
+
) }),
|
|
5304
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Who can see it", hint: hint(HANDLING_WORDS[handling].hint), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5305
|
+
import_controls18.SegmentedControl,
|
|
5306
|
+
{
|
|
5307
|
+
"aria-label": `Who can see ${name}`,
|
|
5308
|
+
value: handling,
|
|
5309
|
+
onValueChange: (v) => void chooseHandling(v),
|
|
5310
|
+
data: HANDLINGS.map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint }))
|
|
5311
|
+
}
|
|
5312
|
+
) }),
|
|
5313
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Use in sandbox", hint: envOk ? hint("Sandboxes get it as an env variable") : hint("Needs an env key"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_controls18.Switch, { "aria-label": `Use ${name} in sandbox`, checked: envOk && value.injectIntoSandbox === true, disabled: !envOk, onCheckedChange: (v) => onChange({ injectIntoSandbox: v }) }) })
|
|
5314
|
+
] });
|
|
5315
|
+
}
|
|
5316
|
+
var hasSecretSettings = (s) => saysMore(s, "revealable");
|
|
5317
|
+
var hasDeclaredSettings = (s, defaultHandling) => Boolean(s && saysMore(s, defaultHandling));
|
|
5318
|
+
function SecretBuilderRow({ field, onChange, onRemove }) {
|
|
5319
|
+
const [open, setOpen] = (0, import_react23.useState)(hasSecretSettings(field));
|
|
5320
|
+
const handling = field.handling ?? "revealable";
|
|
5321
|
+
const name = field.label.trim() || "Secret";
|
|
5232
5322
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
5233
5323
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5234
5324
|
CustomEditRow,
|
|
@@ -5236,47 +5326,26 @@ function SecretBuilderRow({ field, onChange, onRemove }) {
|
|
|
5236
5326
|
label: field.label,
|
|
5237
5327
|
value: field.value,
|
|
5238
5328
|
secret: true,
|
|
5329
|
+
multiline: field.multiline === true,
|
|
5239
5330
|
onLabel: (v) => onChange({ label: v }),
|
|
5240
5331
|
onValue: (v) => onChange({ value: v }),
|
|
5241
5332
|
onRemove,
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5333
|
+
...field.multiline ? {} : {
|
|
5334
|
+
onPasteValue: (text) => {
|
|
5335
|
+
const a = parseEnvAssignment(text);
|
|
5336
|
+
if (!a) return false;
|
|
5337
|
+
onChange({ envKey: a.key, value: a.value, ...field.label.trim() ? {} : { label: labelFromEnvKey(a.key) } });
|
|
5338
|
+
setOpen(true);
|
|
5339
|
+
return true;
|
|
5340
|
+
}
|
|
5248
5341
|
},
|
|
5249
5342
|
extra: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
5250
|
-
handling !== "sealed" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenerateButton, { onUse: (v) => onChange({ value: v }) }) : null,
|
|
5343
|
+
handling !== "sealed" && !field.multiline ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenerateButton, { onUse: (v) => onChange({ value: v }) }) : null,
|
|
5251
5344
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_controls18.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SlidersGlyph, {}), "aria-label": `${name} settings`, pressed: open, onClick: () => setOpen((o) => !o) })
|
|
5252
5345
|
] })
|
|
5253
5346
|
}
|
|
5254
5347
|
),
|
|
5255
|
-
open ? /* @__PURE__ */ (0, import_jsx_runtime22.
|
|
5256
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Env key", hint: envProblem ? hint(envProblem, "destructive") : hint("The name sandboxes and workflows use"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5257
|
-
import_controls18.Field,
|
|
5258
|
-
{
|
|
5259
|
-
className: "w-full font-mono",
|
|
5260
|
-
"aria-label": `${name} env key`,
|
|
5261
|
-
"aria-invalid": Boolean(envProblem),
|
|
5262
|
-
placeholder: "e.g. GITHUB_TOKEN",
|
|
5263
|
-
autoComplete: "off",
|
|
5264
|
-
spellCheck: false,
|
|
5265
|
-
value: env,
|
|
5266
|
-
onChange: (e) => onChange({ envKey: e.target.value, ...e.target.value.trim() ? {} : { injectIntoSandbox: false } })
|
|
5267
|
-
}
|
|
5268
|
-
) }),
|
|
5269
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Who can see it", hint: hint(HANDLING_WORDS[handling].hint), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5270
|
-
import_controls18.SegmentedControl,
|
|
5271
|
-
{
|
|
5272
|
-
"aria-label": `Who can see ${name}`,
|
|
5273
|
-
value: handling,
|
|
5274
|
-
onValueChange: (v) => void chooseHandling(v),
|
|
5275
|
-
data: HANDLINGS.map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint }))
|
|
5276
|
-
}
|
|
5277
|
-
) }),
|
|
5278
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VaultRow, { label: "Use in sandbox", hint: envOk ? hint("Sandboxes get it as an env variable") : hint("Needs an env key"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_controls18.Switch, { "aria-label": `Use ${name} in sandbox`, checked: envOk && field.injectIntoSandbox === true, disabled: !envOk, onCheckedChange: (v) => onChange({ injectIntoSandbox: v }) }) })
|
|
5279
|
-
] }) : null
|
|
5348
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FieldSettingsRows, { name, value: field, onChange }) : null
|
|
5280
5349
|
] });
|
|
5281
5350
|
}
|
|
5282
5351
|
function withMfaMethod(custom, v) {
|
|
@@ -5906,6 +5975,8 @@ function SharingSummary({ item, port, className }) {
|
|
|
5906
5975
|
// src/ItemPane.tsx
|
|
5907
5976
|
var import_format8 = require("@ai-matrx/kit/format");
|
|
5908
5977
|
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
5978
|
+
var ENV_VALUE_KEY = "env_value";
|
|
5979
|
+
var secureNoteDraft = () => ({ key: SECURE_NOTE_KEY, label: "Secure note", value: "", multiline: true, description: "Encrypted notes" });
|
|
5909
5980
|
var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
|
|
5910
5981
|
function ItemPane(props) {
|
|
5911
5982
|
const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
|
|
@@ -6096,6 +6167,16 @@ function ItemPane(props) {
|
|
|
6096
6167
|
const fieldSettings = (f) => canEdit ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SlidersGlyph, {}), "aria-label": `${f.label} settings`, title: `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }) : null;
|
|
6097
6168
|
const turnOnOne = canTurnOn ? (f) => () => turnOn([f]) : () => void 0;
|
|
6098
6169
|
const declaredKeys = new Set(declared.map((f) => f.key));
|
|
6170
|
+
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);
|
|
6171
|
+
const [settingsToggled, setSettingsToggled] = (0, import_react28.useState)(/* @__PURE__ */ new Set());
|
|
6172
|
+
const settingsOpen = (f) => settingsToggled.has(f.key) !== (definition?.key === ENV_VALUE_KEY || hasDeclaredSettings(draft.fieldSettings?.[f.key], f.handling));
|
|
6173
|
+
const toggleSettings = (key) => setSettingsToggled((cur) => {
|
|
6174
|
+
const next = new Set(cur);
|
|
6175
|
+
if (next.has(key)) next.delete(key);
|
|
6176
|
+
else next.add(key);
|
|
6177
|
+
return next;
|
|
6178
|
+
});
|
|
6179
|
+
const patchFieldSettings = (key, p) => setDraft((d) => ({ ...d, fieldSettings: { ...d.fieldSettings, [key]: { ...d.fieldSettings?.[key], ...p } } }));
|
|
6099
6180
|
const extraFields = (item?.fields ?? []).filter((f) => !declaredKeys.has(f.key) && !draft.removed.includes(f.id) && !isAuthenticatorStorage(f.key));
|
|
6100
6181
|
const metaValue = (key) => draft.values[key] ?? item?.custom.find((c) => c.key === key)?.value ?? "";
|
|
6101
6182
|
const extraCustom = draft.custom.filter((c) => !declaredKeys.has(c.key) && c.key !== MFA_METHOD_KEY && !isAuthenticatorStorage(c.key));
|
|
@@ -6228,24 +6309,29 @@ function ItemPane(props) {
|
|
|
6228
6309
|
const secretKind = f.storage === "encrypted" && f.kind === "secret";
|
|
6229
6310
|
const value = f.storage === "metadata" ? metaValue(f.key) : draft.values[f.key] ?? "";
|
|
6230
6311
|
const hidden = existing && existing.handling !== "visible" && !(f.key in loaded);
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6312
|
+
const newSecret = mode === "new" && f.storage === "encrypted" && owner !== "person";
|
|
6313
|
+
const open = newSecret && settingsOpen(f);
|
|
6314
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react28.Fragment, { children: [
|
|
6315
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
6316
|
+
EditRow,
|
|
6317
|
+
{
|
|
6318
|
+
label: f.label,
|
|
6319
|
+
value,
|
|
6320
|
+
placeholder: hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : f.required ? "Required" : isRecoveryCodesField(f) ? "Backup codes, one per line" : "",
|
|
6321
|
+
secret: secretKind,
|
|
6322
|
+
multiline: f.kind === "multiline" || value.includes("\n"),
|
|
6323
|
+
inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
|
|
6324
|
+
onChange: (v) => setValue(f.key, v),
|
|
6325
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
6326
|
+
hidden && existing?.handling === "revealable" && item?.capabilities.canReveal ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
|
|
6327
|
+
secretKind ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
|
|
6328
|
+
existing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_icons11.SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null,
|
|
6329
|
+
newSecret ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SlidersGlyph, {}), "aria-label": `${f.label} settings`, pressed: open, onClick: () => toggleSettings(f.key) }) : null
|
|
6330
|
+
] })
|
|
6331
|
+
}
|
|
6332
|
+
),
|
|
6333
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldSettingsRows, { name: f.label, value: draft.fieldSettings?.[f.key] ?? {}, defaultHandling: f.handling, onChange: (p) => patchFieldSettings(f.key, p) }) : null
|
|
6334
|
+
] }, f.key);
|
|
6249
6335
|
}),
|
|
6250
6336
|
extraFields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
6251
6337
|
EditRow,
|
|
@@ -6254,7 +6340,7 @@ function ItemPane(props) {
|
|
|
6254
6340
|
value: draft.values[f.key] ?? "",
|
|
6255
6341
|
placeholder: f.handling === "visible" || f.key in loaded ? "" : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
6256
6342
|
secret: f.handling !== "visible" && !isRecoveryCodesField(f),
|
|
6257
|
-
multiline: isRecoveryCodesField(f),
|
|
6343
|
+
multiline: f.kind === "multiline" || isRecoveryCodesField(f) || (draft.values[f.key] ?? "").includes("\n"),
|
|
6258
6344
|
onChange: (v) => setValue(f.key, v),
|
|
6259
6345
|
onRemove: () => patch({ removed: [...draft.removed, f.id] }),
|
|
6260
6346
|
removeLabel: `Delete ${f.label}`,
|
|
@@ -6323,6 +6409,7 @@ function ItemPane(props) {
|
|
|
6323
6409
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PlusGlyph, {}), children: "Add" }) }),
|
|
6324
6410
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuContent, { align: "end", children: [
|
|
6325
6411
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => patch({ secrets: [...draft.secrets, { key: "", label: "", value: "" }] }), children: "Secret field" }),
|
|
6412
|
+
!hasSecureNote ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => patch({ secrets: [...draft.secrets, secureNoteDraft()] }), children: "Secure note" }) : null,
|
|
6326
6413
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => patch({ custom: [...draft.custom, { key: `custom_${draft.custom.length + 1}_${Date.now() % 1e5}`, label: "", value: "" }] }), children: "Text field" }),
|
|
6327
6414
|
!tagsVisible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => setShowTags(true), children: "Tags" }) : null,
|
|
6328
6415
|
!descriptionVisible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuItem, { onSelect: () => setShowDescription(true), children: "Description" }) : null,
|
|
@@ -6410,6 +6497,13 @@ function ItemPane(props) {
|
|
|
6410
6497
|
|
|
6411
6498
|
// src/ItemList.tsx
|
|
6412
6499
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
6500
|
+
var RECOVERY_KEYS = /^recovery_codes?$/;
|
|
6501
|
+
function otherCopyFields(item, user, secret) {
|
|
6502
|
+
if (!item.capabilities.canUse) return [];
|
|
6503
|
+
return item.fields.filter(
|
|
6504
|
+
(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)
|
|
6505
|
+
);
|
|
6506
|
+
}
|
|
6413
6507
|
var SORTS2 = [
|
|
6414
6508
|
{ value: "name", label: "Name A\u2013Z" },
|
|
6415
6509
|
{ value: "name-desc", label: "Name Z\u2013A" },
|
|
@@ -6422,7 +6516,7 @@ function subtitleOf(item, withOwner) {
|
|
|
6422
6516
|
const what = loginSiteOf(item) || item.definitionLabel;
|
|
6423
6517
|
return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
|
|
6424
6518
|
}
|
|
6425
|
-
function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false }) {
|
|
6519
|
+
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
6520
|
const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
|
|
6427
6521
|
const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
|
|
6428
6522
|
const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
|
|
@@ -6443,7 +6537,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6443
6537
|
}
|
|
6444
6538
|
};
|
|
6445
6539
|
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);
|
|
6540
|
+
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
6541
|
const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
|
|
6448
6542
|
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
6543
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
|
|
@@ -6458,9 +6552,12 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6458
6552
|
" ",
|
|
6459
6553
|
items.length === 1 ? "item" : "items"
|
|
6460
6554
|
] }),
|
|
6461
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6462
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
6463
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.
|
|
6555
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex shrink-0 items-center gap-0.5", children: [
|
|
6556
|
+
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,
|
|
6557
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { children: [
|
|
6558
|
+
/* @__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 }) }),
|
|
6559
|
+
/* @__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)) }) })
|
|
6560
|
+
] })
|
|
6464
6561
|
] })
|
|
6465
6562
|
] })
|
|
6466
6563
|
] }),
|
|
@@ -6475,7 +6572,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6475
6572
|
] }) })
|
|
6476
6573
|
] });
|
|
6477
6574
|
}
|
|
6478
|
-
function ListRow({ item, family, port, on, onSelect, withOwner }) {
|
|
6575
|
+
function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra }) {
|
|
6479
6576
|
const confirmed = useConfirmed();
|
|
6480
6577
|
const copy = useCopy();
|
|
6481
6578
|
const notify = useNotify();
|
|
@@ -6500,67 +6597,93 @@ function ListRow({ item, family, port, on, onSelect, withOwner }) {
|
|
|
6500
6597
|
const copyPassword = () => secret && void run("Password", () => port.reveal(item, secret.key), true);
|
|
6501
6598
|
const copyCode = () => code && void run("One-time code", async () => (await port.authenticator.code(item)).code, true);
|
|
6502
6599
|
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,
|
|
6600
|
+
const others = otherCopyFields(item, user, secret);
|
|
6601
|
+
const custom = canUse ? item.custom.filter((c) => c.value) : [];
|
|
6602
|
+
const hostEntries = extra?.(item) ?? [];
|
|
6603
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6604
|
+
"li",
|
|
6605
|
+
{
|
|
6606
|
+
className: "group relative",
|
|
6607
|
+
"data-item": item.id,
|
|
6608
|
+
onContextMenu: ownContextMenu ? (e) => {
|
|
6609
|
+
e.preventDefault();
|
|
6610
|
+
setMenuOpen(true);
|
|
6611
|
+
} : void 0,
|
|
6612
|
+
children: [
|
|
6613
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
6614
|
+
"button",
|
|
6529
6615
|
{
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
"
|
|
6533
|
-
|
|
6534
|
-
className: (0, import_design_system20.cn)("
|
|
6616
|
+
type: "button",
|
|
6617
|
+
"aria-current": on ? "true" : void 0,
|
|
6618
|
+
"data-surface": on ? "primary" : void 0,
|
|
6619
|
+
onClick: () => onSelect(item.id),
|
|
6620
|
+
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"),
|
|
6621
|
+
children: [
|
|
6622
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item), inverted: on }),
|
|
6623
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
|
|
6624
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm font-medium", children: item.name }),
|
|
6625
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: subtitleOf(item, withOwner) })
|
|
6626
|
+
] })
|
|
6627
|
+
]
|
|
6535
6628
|
}
|
|
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"
|
|
6629
|
+
),
|
|
6630
|
+
/* @__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: [
|
|
6631
|
+
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: [
|
|
6632
|
+
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,
|
|
6633
|
+
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
6634
|
] }) : null,
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6635
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [
|
|
6636
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
6637
|
+
import_controls23.Button,
|
|
6638
|
+
{
|
|
6639
|
+
variant: "quiet",
|
|
6640
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MoreGlyph, {}),
|
|
6641
|
+
"aria-label": `Actions for ${item.name}`,
|
|
6642
|
+
disabled: busy,
|
|
6643
|
+
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")
|
|
6644
|
+
}
|
|
6645
|
+
) }),
|
|
6646
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuContent, { align: "end", children: [
|
|
6647
|
+
user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyUser, children: [
|
|
6648
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }),
|
|
6649
|
+
"Copy ",
|
|
6650
|
+
user.label.toLowerCase()
|
|
6651
|
+
] }) : null,
|
|
6652
|
+
secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
|
|
6653
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
|
|
6654
|
+
"Copy password"
|
|
6655
|
+
] }) : null,
|
|
6656
|
+
code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
|
|
6657
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
|
|
6658
|
+
"Copy one-time code"
|
|
6659
|
+
] }) : null,
|
|
6660
|
+
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: [
|
|
6661
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
|
|
6662
|
+
"Copy ",
|
|
6663
|
+
f.label.toLowerCase()
|
|
6664
|
+
] }, f.key)),
|
|
6665
|
+
custom.map((c) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void copy(c.value, c.label, { secret: false }), children: [
|
|
6666
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
|
|
6667
|
+
"Copy ",
|
|
6668
|
+
c.label.toLowerCase()
|
|
6669
|
+
] }, `custom:${c.key}`)),
|
|
6670
|
+
user || secret || code || others.length > 0 || custom.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
|
|
6671
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => onSelect(item.id), children: [
|
|
6672
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }),
|
|
6673
|
+
"Open"
|
|
6674
|
+
] }),
|
|
6675
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyName, children: [
|
|
6676
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
|
|
6677
|
+
"Copy name"
|
|
6678
|
+
] }),
|
|
6679
|
+
hostEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
|
|
6680
|
+
hostEntries.map((h) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuItem, { onSelect: h.onSelect, children: h.label }, h.label))
|
|
6681
|
+
] })
|
|
6559
6682
|
] })
|
|
6560
6683
|
] })
|
|
6561
|
-
]
|
|
6562
|
-
|
|
6563
|
-
|
|
6684
|
+
]
|
|
6685
|
+
}
|
|
6686
|
+
);
|
|
6564
6687
|
}
|
|
6565
6688
|
|
|
6566
6689
|
// src/lists/TrashView.tsx
|
|
@@ -8187,6 +8310,7 @@ var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
|
8187
8310
|
var FULL_AT = 1120;
|
|
8188
8311
|
var SPLIT_AT = 640;
|
|
8189
8312
|
var NO_HOST = {};
|
|
8313
|
+
var FOCUS_GAP_MS = 2e3;
|
|
8190
8314
|
var ITEM_PANELS = {
|
|
8191
8315
|
share: SharePanel,
|
|
8192
8316
|
move: MovePanel,
|
|
@@ -8315,9 +8439,13 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8315
8439
|
const knobsRef = (0, import_react42.useRef)(host.knobs);
|
|
8316
8440
|
knobsRef.current = host.knobs;
|
|
8317
8441
|
const [signedOut, setSignedOut] = (0, import_react42.useState)(false);
|
|
8318
|
-
const
|
|
8319
|
-
|
|
8320
|
-
|
|
8442
|
+
const [refreshError, setRefreshError] = (0, import_react42.useState)(null);
|
|
8443
|
+
const [refreshing, setRefreshing] = (0, import_react42.useState)(false);
|
|
8444
|
+
const loadedAt = (0, import_react42.useRef)(0);
|
|
8445
|
+
const inFlight = (0, import_react42.useRef)(false);
|
|
8446
|
+
const apply = (0, import_react42.useCallback)(
|
|
8447
|
+
(s) => {
|
|
8448
|
+
loadedAt.current = Date.now();
|
|
8321
8449
|
setSnap(s);
|
|
8322
8450
|
setItems(s.items);
|
|
8323
8451
|
setDeleted(s.deleted);
|
|
@@ -8325,15 +8453,71 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8325
8453
|
(rows) => setDeleted([...s.deleted, ...rows.filter((r) => !s.deleted.some((d) => d.id === r.id))]),
|
|
8326
8454
|
() => void 0
|
|
8327
8455
|
);
|
|
8328
|
-
}
|
|
8456
|
+
},
|
|
8457
|
+
[port]
|
|
8458
|
+
);
|
|
8459
|
+
const refresh = (0, import_react42.useCallback)(() => {
|
|
8460
|
+
if (inFlight.current) return;
|
|
8461
|
+
inFlight.current = true;
|
|
8462
|
+
setRefreshing(true);
|
|
8463
|
+
port.load().then((s) => {
|
|
8464
|
+
setRefreshError(null);
|
|
8465
|
+
apply(s);
|
|
8466
|
+
}).catch((e) => setRefreshError(plainLoadError(e instanceof Error && e.message ? e.message : "The vault could not be read."))).finally(() => {
|
|
8467
|
+
inFlight.current = false;
|
|
8468
|
+
setRefreshing(false);
|
|
8469
|
+
});
|
|
8470
|
+
port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
|
|
8471
|
+
port.lists.recents?.list().then(setRecents, () => void 0);
|
|
8472
|
+
}, [port, apply]);
|
|
8473
|
+
const load = (0, import_react42.useCallback)(() => {
|
|
8474
|
+
setError(null);
|
|
8475
|
+
setRefreshError(null);
|
|
8476
|
+
port.load().then(apply).catch((e) => {
|
|
8329
8477
|
setSignedOut(e?.status === 401);
|
|
8330
8478
|
setError(e instanceof Error ? e.message : "The vault could not be loaded.");
|
|
8331
8479
|
});
|
|
8332
8480
|
port.lists.favorites?.list().then((ids) => setFavorites(new Set(ids)), () => void 0);
|
|
8333
8481
|
knobsRef.current?.().catch(() => void 0);
|
|
8334
8482
|
port.lists.recents?.list().then(setRecents, () => void 0);
|
|
8335
|
-
}, [port]);
|
|
8483
|
+
}, [port, apply]);
|
|
8336
8484
|
(0, import_react42.useEffect)(load, [load]);
|
|
8485
|
+
const quietRef = (0, import_react42.useRef)({ loc, ready: false });
|
|
8486
|
+
quietRef.current = { loc, ready: snap !== null && !error };
|
|
8487
|
+
const [knobSeconds, setKnobSeconds] = (0, import_react42.useState)(null);
|
|
8488
|
+
(0, import_react42.useEffect)(() => {
|
|
8489
|
+
if (host.refreshSeconds !== void 0 || !knobsRef.current) return;
|
|
8490
|
+
let live = true;
|
|
8491
|
+
knobsRef.current().then(
|
|
8492
|
+
(r) => {
|
|
8493
|
+
const v = r["vault.list.refresh_seconds"];
|
|
8494
|
+
if (live && typeof v === "number" && v > 0) setKnobSeconds(v);
|
|
8495
|
+
},
|
|
8496
|
+
() => void 0
|
|
8497
|
+
);
|
|
8498
|
+
return () => {
|
|
8499
|
+
live = false;
|
|
8500
|
+
};
|
|
8501
|
+
}, [host.refreshSeconds]);
|
|
8502
|
+
const everySeconds = host.refreshSeconds ?? knobSeconds;
|
|
8503
|
+
(0, import_react42.useEffect)(() => {
|
|
8504
|
+
const quietly = (minGapMs) => {
|
|
8505
|
+
const { loc: here, ready } = quietRef.current;
|
|
8506
|
+
if (!ready || document.visibilityState === "hidden" || dirty.current || here.mode !== "view") return;
|
|
8507
|
+
if (Date.now() - loadedAt.current < minGapMs) return;
|
|
8508
|
+
refresh();
|
|
8509
|
+
};
|
|
8510
|
+
const onBack = () => quietly(FOCUS_GAP_MS);
|
|
8511
|
+
const onVisible = () => document.visibilityState === "visible" && onBack();
|
|
8512
|
+
window.addEventListener("focus", onBack);
|
|
8513
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
8514
|
+
const timer2 = everySeconds && everySeconds > 0 ? setInterval(() => quietly(0), everySeconds * 1e3) : null;
|
|
8515
|
+
return () => {
|
|
8516
|
+
window.removeEventListener("focus", onBack);
|
|
8517
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
8518
|
+
if (timer2) clearInterval(timer2);
|
|
8519
|
+
};
|
|
8520
|
+
}, [refresh, everySeconds]);
|
|
8337
8521
|
const inventoryRef = (0, import_react42.useRef)(onInventory);
|
|
8338
8522
|
inventoryRef.current = onInventory;
|
|
8339
8523
|
(0, import_react42.useEffect)(() => {
|
|
@@ -8499,6 +8683,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8499
8683
|
onSort: (sort) => go({ sort }, "replace"),
|
|
8500
8684
|
canSortViewed: port.lists.recents !== void 0,
|
|
8501
8685
|
onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
|
|
8686
|
+
onRefresh: refresh,
|
|
8687
|
+
refreshing,
|
|
8688
|
+
rowContextMenu: host.rowContextMenu ?? "package",
|
|
8689
|
+
...host.rowMenuItems ? { rowMenuItems: host.rowMenuItems } : {},
|
|
8502
8690
|
...matchingIds ? { matchingIds } : {},
|
|
8503
8691
|
...compactHeader ? { header: compactHeader } : {}
|
|
8504
8692
|
}
|
|
@@ -8523,14 +8711,14 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8523
8711
|
host,
|
|
8524
8712
|
organizations: orgs,
|
|
8525
8713
|
defaultOwner: ownerDefault,
|
|
8526
|
-
seed: { name: guess?.name
|
|
8714
|
+
seed: { name: loc.name?.trim() || guess?.name || "", url: guess?.url ?? loc.url.trim() },
|
|
8527
8715
|
onBack: () => go({ kind: null }),
|
|
8528
8716
|
onMode: () => void 0,
|
|
8529
8717
|
onSaved: (item, _wasNew, n, enrolled) => {
|
|
8530
8718
|
replaceItem(item);
|
|
8531
8719
|
dirty.current = false;
|
|
8532
8720
|
if (enrolled) setFirstCode(item);
|
|
8533
|
-
move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", panel: null }, "replace");
|
|
8721
|
+
move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", name: "", panel: null }, "replace");
|
|
8534
8722
|
setNotice(n);
|
|
8535
8723
|
notify("Saved");
|
|
8536
8724
|
},
|
|
@@ -8538,7 +8726,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8538
8726
|
onDirty: (d) => {
|
|
8539
8727
|
dirty.current = d;
|
|
8540
8728
|
},
|
|
8541
|
-
onCancelNew: () => go({ mode: "view", kind: null, url: "", itemId: null }),
|
|
8729
|
+
onCancelNew: () => go({ mode: "view", kind: null, url: "", name: "", itemId: null }),
|
|
8542
8730
|
onPanel: () => void 0,
|
|
8543
8731
|
onChanged: () => void 0
|
|
8544
8732
|
},
|
|
@@ -8629,13 +8817,17 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8629
8817
|
const grid = mode === "full" ? "grid-cols-[15rem_21rem_minmax(0,1fr)]" : mode === "split" ? "grid-cols-[19rem_minmax(0,1fr)]" : "grid-cols-1";
|
|
8630
8818
|
const shown = panesShown(mode, specialView !== null, showingDetail);
|
|
8631
8819
|
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: [
|
|
8820
|
+
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: [
|
|
8821
|
+
/* @__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." }),
|
|
8822
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
|
|
8823
|
+
] }) : null,
|
|
8632
8824
|
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
8825
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "min-w-0 flex-1 truncate text-warning", children: [
|
|
8634
8826
|
"Couldn\u2019t load ",
|
|
8635
8827
|
snap.unavailable.join(", "),
|
|
8636
8828
|
". Showing the rest."
|
|
8637
8829
|
] }),
|
|
8638
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick:
|
|
8830
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "shrink-0 text-primary hover:underline", onClick: refresh, children: "Try again" })
|
|
8639
8831
|
] }) : null,
|
|
8640
8832
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: `grid min-h-0 w-full flex-1 ${grid} overflow-hidden`, children: [
|
|
8641
8833
|
mode === "full" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
@@ -8986,6 +9178,28 @@ function trashItem(r, ctx) {
|
|
|
8986
9178
|
createdAt: ""
|
|
8987
9179
|
};
|
|
8988
9180
|
}
|
|
9181
|
+
async function withTypes(items, door, ctx) {
|
|
9182
|
+
if (!door.schema || items.length === 0) return items;
|
|
9183
|
+
const keys = /* @__PURE__ */ new Map();
|
|
9184
|
+
const CHUNK = 100;
|
|
9185
|
+
try {
|
|
9186
|
+
for (let i = 0; i < items.length; i += CHUNK) {
|
|
9187
|
+
const ids = items.slice(i, i + CHUNK).map((x) => x.id);
|
|
9188
|
+
const { data, error } = await door.schema("users").from("credential_items").select("id,definition_key").in("id", ids);
|
|
9189
|
+
if (error) throw new Error(error.message || "credential_items read failed");
|
|
9190
|
+
for (const r of Array.isArray(data) ? data : []) {
|
|
9191
|
+
if (typeof r.id === "string" && typeof r.definition_key === "string" && r.definition_key) keys.set(r.id, r.definition_key);
|
|
9192
|
+
}
|
|
9193
|
+
}
|
|
9194
|
+
} catch (e) {
|
|
9195
|
+
console.warn("[vault] Trash item types could not be read; rows say Credential", e);
|
|
9196
|
+
return items;
|
|
9197
|
+
}
|
|
9198
|
+
return items.map((it) => {
|
|
9199
|
+
const key = keys.get(it.id);
|
|
9200
|
+
return key ? { ...it, definitionKey: key, definitionLabel: ctx.definition(key)?.label ?? humanize(key) } : it;
|
|
9201
|
+
});
|
|
9202
|
+
}
|
|
8989
9203
|
var previewOf = async (door, id) => parseRecoveryPreview(await call(door, "vault_recovery_preview", { p_id: id }));
|
|
8990
9204
|
function createListsPort(ctx, door) {
|
|
8991
9205
|
return {
|
|
@@ -9018,7 +9232,8 @@ function createListsPort(ctx, door) {
|
|
|
9018
9232
|
if (rows.length < PAGE2) break;
|
|
9019
9233
|
}
|
|
9020
9234
|
const items = all.map((r) => trashItem(r, ctx)).filter((i) => i !== null);
|
|
9021
|
-
|
|
9235
|
+
const unique = [...new Map(items.map((i) => [i.id, i])).values()];
|
|
9236
|
+
return withTypes(unique, door, ctx);
|
|
9022
9237
|
}
|
|
9023
9238
|
},
|
|
9024
9239
|
favorites: {
|
|
@@ -9041,16 +9256,25 @@ function createListsPort(ctx, door) {
|
|
|
9041
9256
|
var account6 = __toESM(require("@ai-matrx/api/account"), 1);
|
|
9042
9257
|
|
|
9043
9258
|
// src/secret-field-in.ts
|
|
9044
|
-
function
|
|
9259
|
+
function settingsIn(s) {
|
|
9045
9260
|
const envKey = s.envKey?.trim() || null;
|
|
9046
9261
|
return {
|
|
9047
|
-
field_key: fieldKeyOf(s.label),
|
|
9048
|
-
value: s.value,
|
|
9049
|
-
handling: s.handling ?? "revealable",
|
|
9050
9262
|
...envKey ? { env_key: envKey } : {},
|
|
9051
9263
|
...envKey && s.injectIntoSandbox ? { inject_into_sandbox: true } : {}
|
|
9052
9264
|
};
|
|
9053
9265
|
}
|
|
9266
|
+
function secretFieldIn(s) {
|
|
9267
|
+
return {
|
|
9268
|
+
field_key: s.key.trim() || fieldKeyOf(s.label),
|
|
9269
|
+
value: s.value,
|
|
9270
|
+
handling: s.handling ?? "revealable",
|
|
9271
|
+
...s.description ? { description: s.description } : {},
|
|
9272
|
+
...settingsIn(s)
|
|
9273
|
+
};
|
|
9274
|
+
}
|
|
9275
|
+
function declaredFieldIn(f, value, settings) {
|
|
9276
|
+
return { field_key: f.key, value, handling: settings?.handling ?? f.handling, ...settingsIn(settings ?? {}) };
|
|
9277
|
+
}
|
|
9054
9278
|
|
|
9055
9279
|
// src/sharing/api.ts
|
|
9056
9280
|
function grantOf(w) {
|
|
@@ -9151,7 +9375,7 @@ function createSharingPort(ctx, options = {}) {
|
|
|
9151
9375
|
initiation: "user",
|
|
9152
9376
|
non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
|
|
9153
9377
|
fields: [
|
|
9154
|
-
...encrypted.map((f) => (
|
|
9378
|
+
...encrypted.map((f) => declaredFieldIn(f, draft.values[f.key] ?? "", draft.fieldSettings?.[f.key])),
|
|
9155
9379
|
...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map(secretFieldIn)
|
|
9156
9380
|
]
|
|
9157
9381
|
}
|
|
@@ -9280,7 +9504,7 @@ async function readNativeImportLimits(api, organizationId) {
|
|
|
9280
9504
|
var recoveryOf = (w) => w.lifecycle?.vault_recovery;
|
|
9281
9505
|
var isDeleted = (w) => recoveryOf(w)?.state === "deleted";
|
|
9282
9506
|
var MATCH_MODES2 = ["host", "domain", "exact", "never"];
|
|
9283
|
-
var FIELD_WORDS = { totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
|
|
9507
|
+
var FIELD_WORDS = { secure_notes: "Secure note", totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
|
|
9284
9508
|
var handlingOf = (v) => v === "visible" || v === "sealed" ? v : "revealable";
|
|
9285
9509
|
function fieldOf(w, def) {
|
|
9286
9510
|
const declared = def?.fields.find((f) => f.key === w.field_key);
|
|
@@ -9289,7 +9513,8 @@ function fieldOf(w, def) {
|
|
|
9289
9513
|
id: w.id,
|
|
9290
9514
|
key: w.field_key,
|
|
9291
9515
|
label: declared?.label ?? FIELD_WORDS[w.field_key] ?? humanize(w.field_key),
|
|
9292
|
-
|
|
9516
|
+
// A field no type declares still edits as several lines when its key says so (an old "Secure note").
|
|
9517
|
+
kind: declared?.kind ?? (isMultilineKey(w.field_key) ? "multiline" : handling === "visible" ? "text" : "secret"),
|
|
9293
9518
|
handling,
|
|
9294
9519
|
editable: w.editable ?? true,
|
|
9295
9520
|
envKey: w.env_key ?? null,
|
|
@@ -9444,7 +9669,7 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9444
9669
|
initiation: "user",
|
|
9445
9670
|
non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
|
|
9446
9671
|
fields: [
|
|
9447
|
-
...encrypted.map((f) => (
|
|
9672
|
+
...encrypted.map((f) => declaredFieldIn(f, draft.values[f.key] ?? "", draft.fieldSettings?.[f.key])),
|
|
9448
9673
|
...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map(secretFieldIn),
|
|
9449
9674
|
// Backup codes on a type that does not declare them (a website login): their own encrypted field.
|
|
9450
9675
|
...!declared.some((f) => f.key === RECOVERY_CODES_KEY) && (draft.values[RECOVERY_CODES_KEY] ?? "").trim() !== "" ? [{ field_key: RECOVERY_CODES_KEY, value: parseRecoveryCodes(draft.values[RECOVERY_CODES_KEY] ?? "").join("\n"), handling: "revealable", description: "One-time account recovery codes" }] : []
|