@ai-matrx/vault 0.1.23 → 0.1.24
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 +8 -0
- package/dist/index.cjs +80 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +80 -16
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.24
|
|
4
|
+
|
|
5
|
+
- **A copy or reveal that needs "Confirm it's you" is never silent.** Closing the prompt says "Not confirmed, so nothing was done" once, from `IdentityGate` (every caller already stays quiet about a cancel via `wasCancelled`), so the row buttons, the row's More menu, a host's right-click menu (`rowMenuEntriesAt`) and the keyboard all end in either the copy or a message. Guard: `src/review-0-1-23.test.tsx` § 1. Consumer action: none (a host that passes `notify` receives the line with kind `info`).
|
|
6
|
+
- **The authenticator's own settings are never copy entries or search hits.** `totp_enabled` / `totp_digits` / `totp_period` / `totp_algorithm` / issuer / label and the `mfa_method` marker are internal (`isInternalCustomKey`, `humanCustomFields`); the row menu no longer offers "Copy authenticator digits…". They are still kept on save. Guard: § 2. Consumer action: none.
|
|
7
|
+
- **Search reads everything an item says about itself, never a secret**: name, description, type label and key, the type's family ("AI & model providers"), tags, websites, every field's label, key and env key, and the person's own plaintext custom fields (`matchesQuery(item, query, familyLabel?)`). Guard: § 3. Consumer action: none (a host calling `matchesQuery` may pass the family label).
|
|
8
|
+
- **The old type families are back, nested under their category.** A category holding more than one family lists them indented beneath it while it is open (sidebar) and after it in the compact Show picker (`family:<family>` view, `ViewEntry.families`, `itemFamily`, `familyLabel`; `viewOptions(..., current)`). Guard: § 4. Consumer action: none.
|
|
9
|
+
- **A shown value says when it hides**: a quiet "Hides in Ns" beside every revealed secret, earlier value and recovery code set (`HidesIn`, `useTransientSecret().until`, `useSecondsLeft`). Guard: § 5. Consumer action: none.
|
|
10
|
+
|
|
3
11
|
## 0.1.23
|
|
4
12
|
|
|
5
13
|
- **The Vault and Show pickers read in full at every compact width (640-1119px).** They no longer share a row with the Authenticator and More buttons (which squeezed each to ~90px: "All ite…", long vault names cut): the pickers take the whole row (each at least 11rem; one above the other in the 19rem split column, side by side once there is room), and the Authenticator and More buttons moved onto the search row beside the + (`ItemList` `actions`; Trash, Authenticator and the extension's page view carry them on their own search row). Nothing removed, every button one press away as before. Guard: `src/parity-review-0-1-22.test.tsx` § 2. Consumer action: none.
|
package/dist/index.cjs
CHANGED
|
@@ -489,6 +489,7 @@ function needsRecentSignIn(e) {
|
|
|
489
489
|
// src/auth/IdentityGate.tsx
|
|
490
490
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
491
491
|
var CANCELLED = "Not confirmed.";
|
|
492
|
+
var NOT_CONFIRMED_NOTICE = "Not confirmed, so nothing was done";
|
|
492
493
|
var Ctx2 = (0, import_react2.createContext)((run) => run());
|
|
493
494
|
function useConfirmed() {
|
|
494
495
|
return (0, import_react2.useContext)(Ctx2);
|
|
@@ -497,6 +498,7 @@ var PROVIDER_LABEL = { google: "Google", github: "GitHub", azure: "Microsoft", a
|
|
|
497
498
|
var providerLabel = (p) => PROVIDER_LABEL[p] ?? p.charAt(0).toUpperCase() + p.slice(1);
|
|
498
499
|
function IdentityGate({ identity, children }) {
|
|
499
500
|
const [asking, setAsking] = (0, import_react2.useState)(false);
|
|
501
|
+
const notify = useNotify();
|
|
500
502
|
const waiters = (0, import_react2.useRef)([]);
|
|
501
503
|
const [returnProblem, setReturnProblem] = (0, import_react2.useState)(null);
|
|
502
504
|
(0, import_react2.useEffect)(() => {
|
|
@@ -527,6 +529,7 @@ function IdentityGate({ identity, children }) {
|
|
|
527
529
|
waiters.current = [];
|
|
528
530
|
setAsking(false);
|
|
529
531
|
for (const x of w) ok ? x.resolve() : x.reject(new Error(CANCELLED));
|
|
532
|
+
if (!ok && w.length > 0) notify(NOT_CONFIRMED_NOTICE, "info");
|
|
530
533
|
};
|
|
531
534
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Ctx2.Provider, { value: confirmed, children: [
|
|
532
535
|
children,
|
|
@@ -639,22 +642,35 @@ function useTransientSecret(given) {
|
|
|
639
642
|
const fromHost = (0, import_react3.useContext)(RevealMs);
|
|
640
643
|
const ms = given ?? fromHost;
|
|
641
644
|
const [value, setValue] = (0, import_react3.useState)(null);
|
|
645
|
+
const [until, setUntil] = (0, import_react3.useState)(null);
|
|
642
646
|
const timer2 = (0, import_react3.useRef)(null);
|
|
643
647
|
const clear = (0, import_react3.useCallback)(() => {
|
|
644
648
|
if (timer2.current) clearTimeout(timer2.current);
|
|
645
649
|
timer2.current = null;
|
|
646
650
|
setValue(null);
|
|
651
|
+
setUntil(null);
|
|
647
652
|
}, []);
|
|
648
653
|
const show = (0, import_react3.useCallback)(
|
|
649
654
|
(next) => {
|
|
650
655
|
if (timer2.current) clearTimeout(timer2.current);
|
|
651
656
|
setValue(next);
|
|
657
|
+
setUntil(Date.now() + ms);
|
|
652
658
|
timer2.current = setTimeout(clear, ms);
|
|
653
659
|
},
|
|
654
660
|
[clear, ms]
|
|
655
661
|
);
|
|
656
662
|
(0, import_react3.useEffect)(() => clear, [clear]);
|
|
657
|
-
return { value, show, clear };
|
|
663
|
+
return { value, show, clear, until };
|
|
664
|
+
}
|
|
665
|
+
function useSecondsLeft(until) {
|
|
666
|
+
const [now, setNow] = (0, import_react3.useState)(() => Date.now());
|
|
667
|
+
(0, import_react3.useEffect)(() => {
|
|
668
|
+
if (until === null) return;
|
|
669
|
+
setNow(Date.now());
|
|
670
|
+
const t = setInterval(() => setNow(Date.now()), 1e3);
|
|
671
|
+
return () => clearInterval(t);
|
|
672
|
+
}, [until]);
|
|
673
|
+
return until === null ? null : Math.max(0, Math.ceil((until - now) / 1e3));
|
|
658
674
|
}
|
|
659
675
|
function useElementWidth(ref) {
|
|
660
676
|
const [width, setWidth] = (0, import_react3.useState)(0);
|
|
@@ -747,6 +763,15 @@ function SecretText({ value, multiline }) {
|
|
|
747
763
|
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)) });
|
|
748
764
|
}
|
|
749
765
|
var quietError = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
|
|
766
|
+
function HidesIn({ until }) {
|
|
767
|
+
const left = useSecondsLeft(until);
|
|
768
|
+
if (left === null) return null;
|
|
769
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "shrink-0 px-1 text-xs tabular-nums text-muted-foreground", "data-hides-in": "", title: "Shown values hide on their own", children: [
|
|
770
|
+
"Hides in ",
|
|
771
|
+
left,
|
|
772
|
+
"s"
|
|
773
|
+
] });
|
|
774
|
+
}
|
|
750
775
|
function SecretRow({ port, item, field, extra, onTurnOn }) {
|
|
751
776
|
const confirmed = useConfirmed();
|
|
752
777
|
const copyValue2 = useCopy();
|
|
@@ -802,6 +827,7 @@ function SecretRow({ port, item, field, extra, onTurnOn }) {
|
|
|
802
827
|
actions: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
803
828
|
extra,
|
|
804
829
|
off && onTurnOn ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TurnOnButton, { label: field.label, onTurnOn }) : null,
|
|
830
|
+
!visible && secret.value !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HidesIn, { until: secret.until }) : null,
|
|
805
831
|
canSee && !visible ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: secret.value !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EyeGlyph, {}), "aria-label": secret.value !== null ? `Hide ${field.label}` : `Show ${field.label}`, disabled: busy, onClick: () => void toggle2() }) : null,
|
|
806
832
|
canSee ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_icons2.CopyIcon, {}), "aria-label": `Copy ${field.label}`, disabled: busy, onClick: () => void copy() }) : null
|
|
807
833
|
] }),
|
|
@@ -976,15 +1002,28 @@ function inView(item, view) {
|
|
|
976
1002
|
return true;
|
|
977
1003
|
}
|
|
978
1004
|
var AUTHENTICATOR_WORDS = ["authenticator", "2fa", "two-factor", "two factor", "totp", "one-time password", "verification code", "2-step"];
|
|
979
|
-
function matchesQuery(item, query) {
|
|
1005
|
+
function matchesQuery(item, query, familyLabel2) {
|
|
980
1006
|
const q = query.trim().toLowerCase();
|
|
981
1007
|
if (!q) return true;
|
|
982
1008
|
if (item.authenticator && AUTHENTICATOR_WORDS.some((w) => w === q || q.length >= 3 && w.startsWith(q))) return true;
|
|
983
|
-
return [
|
|
1009
|
+
return [
|
|
1010
|
+
item.name,
|
|
1011
|
+
item.description,
|
|
1012
|
+
item.definitionLabel,
|
|
1013
|
+
item.definitionKey,
|
|
1014
|
+
familyLabel2 ?? "",
|
|
1015
|
+
...item.tags,
|
|
1016
|
+
...item.loginUrls,
|
|
1017
|
+
...item.fields.filter((f) => !isAuthenticatorStorage(f.key)).flatMap((f) => [f.label, f.key, f.envKey ?? ""]),
|
|
1018
|
+
...humanCustomFields(item).map((c) => `${c.label} ${c.value}`)
|
|
1019
|
+
].some((h) => h !== "" && h.toLowerCase().includes(q));
|
|
984
1020
|
}
|
|
985
1021
|
var HIDDEN_ITEM_TAG = "matrx:hidden";
|
|
986
1022
|
var isHiddenItem = (item) => (item.tags ?? []).includes(HIDDEN_ITEM_TAG);
|
|
987
1023
|
var isAuthenticatorStorage = (key) => key === "totp_seed" || key.startsWith("totp_") || key === "authenticator_last_time_step";
|
|
1024
|
+
var MFA_METHOD_KEY = "mfa_method";
|
|
1025
|
+
var isInternalCustomKey = (key) => isAuthenticatorStorage(key) || key === MFA_METHOD_KEY;
|
|
1026
|
+
var humanCustomFields = (item) => item.custom.filter((c) => !isInternalCustomKey(c.key));
|
|
988
1027
|
function loginSiteOf(item) {
|
|
989
1028
|
const raw = item.loginUrls[0];
|
|
990
1029
|
if (!raw) return "";
|
|
@@ -1602,6 +1641,7 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
|
|
|
1602
1641
|
{
|
|
1603
1642
|
label: when,
|
|
1604
1643
|
actions: entry.available ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1644
|
+
secret.value !== null ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HidesIn, { until: secret.until }) : null,
|
|
1605
1645
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_controls4.Button, { variant: "quiet", icon: secret.value !== null ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EyeGlyph, {}), "aria-label": secret.value !== null ? "Hide" : "Show", disabled: busy, onClick: () => void toggle2() }),
|
|
1606
1646
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_controls4.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons3.CopyIcon, {}), "aria-label": "Copy", disabled: busy, onClick: () => void copy() }),
|
|
1607
1647
|
canRestore(page, entry) ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_controls4.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RestoreGlyph, {}), "aria-label": "Restore", disabled: busy, onClick: () => void restore() }) : null
|
|
@@ -2137,7 +2177,6 @@ var import_design_system9 = require("@ai-matrx/design-system");
|
|
|
2137
2177
|
var import_confirm_opener2 = require("@ai-matrx/kit/confirm-opener");
|
|
2138
2178
|
|
|
2139
2179
|
// src/authenticator/port.ts
|
|
2140
|
-
var MFA_METHOD_KEY = "mfa_method";
|
|
2141
2180
|
var OTP_SEED_KEY = "totp_seed";
|
|
2142
2181
|
function mfaMethodOf(item) {
|
|
2143
2182
|
const v = item.custom.find((c) => c.key === MFA_METHOD_KEY)?.value;
|
|
@@ -5261,6 +5300,7 @@ function RecoveryCodesRow({ item, field, port, onChanged }) {
|
|
|
5261
5300
|
label: "Recovery codes",
|
|
5262
5301
|
top: codes !== null,
|
|
5263
5302
|
actions: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
5303
|
+
codes ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(HidesIn, { until: secret.until }) : null,
|
|
5264
5304
|
canSee ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_controls17.Button, { variant: "quiet", icon: codes ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EyeOffGlyph, {}) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EyeGlyph, {}), "aria-label": codes ? "Hide codes" : "Show codes", disabled: busy, onClick: () => void toggle2() }) : null,
|
|
5265
5305
|
canEdit && !sealed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_controls17.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PencilGlyph, {}), "aria-label": "Replace all codes", disabled: busy, onClick: () => setDraft("") }) : null
|
|
5266
5306
|
] }),
|
|
@@ -6196,8 +6236,8 @@ function ItemPane(props) {
|
|
|
6196
6236
|
const patchFieldSettings = (key, p) => setDraft((d) => ({ ...d, fieldSettings: { ...d.fieldSettings, [key]: { ...d.fieldSettings?.[key], ...p } } }));
|
|
6197
6237
|
const extraFields = (item?.fields ?? []).filter((f) => !declaredKeys.has(f.key) && !draft.removed.includes(f.id) && !isAuthenticatorStorage(f.key));
|
|
6198
6238
|
const metaValue = (key) => draft.values[key] ?? item?.custom.find((c) => c.key === key)?.value ?? "";
|
|
6199
|
-
const extraCustom = draft.custom.filter((c) => !declaredKeys.has(c.key) &&
|
|
6200
|
-
const viewCustom = (item?.custom ?? []).filter((c) => !declaredKeys.has(c.key) &&
|
|
6239
|
+
const extraCustom = draft.custom.filter((c) => !declaredKeys.has(c.key) && !isInternalCustomKey(c.key));
|
|
6240
|
+
const viewCustom = (item?.custom ?? []).filter((c) => !declaredKeys.has(c.key) && !isInternalCustomKey(c.key));
|
|
6201
6241
|
const isLogin = (item?.definitionKey ?? definition?.key) === "website_login" || (item?.definitionKey ?? definition?.key) === "username_password";
|
|
6202
6242
|
const typeLabel = definition?.label ?? item?.definitionLabel ?? "";
|
|
6203
6243
|
const recoveryDeclared = declaredKeys.has(RECOVERY_CODES_KEY);
|
|
@@ -6647,7 +6687,7 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
|
|
|
6647
6687
|
const copyCode = () => code && void run("One-time code", async () => (await port.authenticator.code(item)).code, true);
|
|
6648
6688
|
const copyName = () => void copy(item.name, "Name", { secret: false });
|
|
6649
6689
|
const others = otherCopyFields(item, user, secret);
|
|
6650
|
-
const custom = canUse ? item.
|
|
6690
|
+
const custom = canUse ? humanCustomFields(item).filter((c) => c.value) : [];
|
|
6651
6691
|
const hostEntries = extra?.(item) ?? [];
|
|
6652
6692
|
const copyEntries = [
|
|
6653
6693
|
...user ? [{ id: "copy:username", kind: "copy", label: `Copy ${user.label.toLowerCase()}`, onSelect: copyUser }] : [],
|
|
@@ -8247,20 +8287,33 @@ function VaultSelect({ value, choices, onChange }) {
|
|
|
8247
8287
|
}
|
|
8248
8288
|
var CATEGORY_GLYPHS = { logins: import_icons16.GlobeIcon, passkeys: ShieldGlyph, api: KeyGlyph, oauth: PlugGlyph, infra: DatabaseGlyph, env: TerminalGlyph, files: FileGlyph, other: NoteGlyph };
|
|
8249
8289
|
var familyOf = (key, definitions) => definitions.find((d) => d.key === key)?.family ?? "generic";
|
|
8290
|
+
function itemFamily(item, definitions) {
|
|
8291
|
+
return familyOf(item.definitionKey, definitions);
|
|
8292
|
+
}
|
|
8293
|
+
var familyLabel = (family) => FAMILY_LABELS[family] ?? family.replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase());
|
|
8250
8294
|
function itemCategory(item, definitions) {
|
|
8251
8295
|
return categoryOf(item.definitionKey, familyOf(item.definitionKey, definitions));
|
|
8252
8296
|
}
|
|
8253
8297
|
function buildViews(items, definitions, favoriteIds2, recentIds2) {
|
|
8254
8298
|
const byCat = /* @__PURE__ */ new Map();
|
|
8299
|
+
const byFamily = /* @__PURE__ */ new Map();
|
|
8255
8300
|
const byTag = /* @__PURE__ */ new Map();
|
|
8256
8301
|
for (const i of items) {
|
|
8257
8302
|
const c = itemCategory(i, definitions);
|
|
8258
8303
|
byCat.set(c, (byCat.get(c) ?? 0) + 1);
|
|
8304
|
+
const fam = itemFamily(i, definitions);
|
|
8305
|
+
const fams = byFamily.get(c) ?? /* @__PURE__ */ new Map();
|
|
8306
|
+
fams.set(fam, (fams.get(fam) ?? 0) + 1);
|
|
8307
|
+
byFamily.set(c, fams);
|
|
8259
8308
|
for (const t of i.tags) byTag.set(t, (byTag.get(t) ?? 0) + 1);
|
|
8260
8309
|
}
|
|
8261
8310
|
const ids = new Set(items.map((i) => i.id));
|
|
8262
8311
|
return {
|
|
8263
|
-
categories: CATEGORIES.filter((c) => byCat.has(c.id)).map((c) =>
|
|
8312
|
+
categories: CATEGORIES.filter((c) => byCat.has(c.id)).map((c) => {
|
|
8313
|
+
const fams = [...(byFamily.get(c.id) ?? /* @__PURE__ */ new Map()).entries()];
|
|
8314
|
+
const families = fams.length > 1 ? fams.map(([f, count2]) => ({ view: `family:${f}`, label: familyLabel(f), count: count2, glyph: CATEGORY_GLYPHS[c.id] })).sort((a, b) => a.label.localeCompare(b.label)) : void 0;
|
|
8315
|
+
return { view: `category:${c.id}`, label: c.label, count: byCat.get(c.id) ?? 0, glyph: CATEGORY_GLYPHS[c.id], ...families ? { families } : {} };
|
|
8316
|
+
}),
|
|
8264
8317
|
tags: [...byTag.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([t, count2]) => ({ view: `tag:${t}`, label: t, count: count2, glyph: TagGlyph })),
|
|
8265
8318
|
favorites: favoriteIds2 ? [...favoriteIds2].filter((id) => ids.has(id)).length : void 0,
|
|
8266
8319
|
authenticator: items.filter((i) => i.authenticator).length,
|
|
@@ -8273,21 +8326,27 @@ function viewLabel(view, views) {
|
|
|
8273
8326
|
if (view === "authenticator") return AUTHENTICATOR_LABEL;
|
|
8274
8327
|
if (view === "trash") return "Trash";
|
|
8275
8328
|
if (view === "page") return "This page";
|
|
8329
|
+
if (view.startsWith("family:")) return familyLabel(view.slice(7));
|
|
8276
8330
|
return [...views.categories, ...views.tags].find((v) => v.view === view)?.label ?? "All items";
|
|
8277
8331
|
}
|
|
8278
|
-
function
|
|
8332
|
+
function openCategory(view, views) {
|
|
8333
|
+
return views.categories.find((c) => c.view === view || c.families?.some((f) => f.view === view));
|
|
8334
|
+
}
|
|
8335
|
+
function viewOptions(views, allCount, deletedCount, hasFavorites, hasRecents, hasPage = false, current = "all") {
|
|
8336
|
+
const open = openCategory(current, views);
|
|
8279
8337
|
return [
|
|
8280
8338
|
...hasPage ? [{ value: "page", label: "This page" }] : [],
|
|
8281
8339
|
{ value: "all", label: "Every category", meta: String(allCount) },
|
|
8282
8340
|
...hasFavorites ? [{ value: "favorites", label: "Favorites", meta: views.favorites === void 0 ? "" : String(views.favorites) }] : [],
|
|
8283
8341
|
...hasRecents ? [{ value: "recent", label: "Recently opened", meta: views.recent === void 0 ? "" : String(views.recent) }] : [],
|
|
8284
8342
|
{ value: "authenticator", label: AUTHENTICATOR_LABEL, meta: String(views.authenticator) },
|
|
8285
|
-
|
|
8343
|
+
// The open category's families follow it, indented by their sign; other categories stay one line.
|
|
8344
|
+
...views.categories.flatMap((v) => [{ value: v.view, label: v.label, meta: String(v.count) }, ...v === open ? (v.families ?? []).map((f) => ({ value: f.view, label: `\xB7 ${f.label}`, meta: String(f.count) })) : []]),
|
|
8286
8345
|
...views.tags.map((v) => ({ value: v.view, label: `#${v.label}`, meta: String(v.count) })),
|
|
8287
8346
|
{ value: "trash", label: "Trash", meta: String(deletedCount) }
|
|
8288
8347
|
];
|
|
8289
8348
|
}
|
|
8290
|
-
function Entry({ on, glyph: Icon, label, count: count2, onClick }) {
|
|
8349
|
+
function Entry({ on, glyph: Icon, label, count: count2, onClick, nested = false }) {
|
|
8291
8350
|
const desktop = (0, import_design_system25.useLook)() === "desktop";
|
|
8292
8351
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
8293
8352
|
"button",
|
|
@@ -8295,9 +8354,10 @@ function Entry({ on, glyph: Icon, label, count: count2, onClick }) {
|
|
|
8295
8354
|
type: "button",
|
|
8296
8355
|
"aria-current": on ? "page" : void 0,
|
|
8297
8356
|
onClick,
|
|
8298
|
-
|
|
8357
|
+
"data-nested": nested ? "" : void 0,
|
|
8358
|
+
className: (0, import_design_system25.cn)("flex h-8 w-full items-center gap-2.5 rounded-lg px-2.5 text-left text-sm", nested && "pl-9", on ? desktop ? "bg-sidebar-accent text-primary" : "bg-sidebar-accent font-medium text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent"),
|
|
8299
8359
|
children: [
|
|
8300
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Icon, { className: "size-4 shrink-0" }),
|
|
8360
|
+
nested ? null : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Icon, { className: "size-4 shrink-0" }),
|
|
8301
8361
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "min-w-0 flex-1 truncate", children: label }),
|
|
8302
8362
|
count2 !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-xs tabular-nums text-muted-foreground", children: count2 }) : null
|
|
8303
8363
|
]
|
|
@@ -8325,7 +8385,10 @@ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, v
|
|
|
8325
8385
|
hasRecents ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Entry, { on: view === "recent", glyph: ClockGlyph, label: "Recently opened", count: views.recent, onClick: () => onView("recent") }) : null,
|
|
8326
8386
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Entry, { on: view === "authenticator", glyph: LockGlyph, label: AUTHENTICATOR_LABEL, count: views.authenticator, onClick: () => onView("authenticator") })
|
|
8327
8387
|
] }),
|
|
8328
|
-
views.categories.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Group, { title: "Categories", children: views.categories.map((e) => /* @__PURE__ */ (0, import_jsx_runtime39.
|
|
8388
|
+
views.categories.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Group, { title: "Categories", children: views.categories.map((e) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
|
|
8389
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Entry, { on: view === e.view, glyph: e.glyph, label: e.label, count: e.count, onClick: () => onView(e.view) }),
|
|
8390
|
+
e === openCategory(view, views) ? e.families?.map((f) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Entry, { nested: true, on: view === f.view, glyph: f.glyph, label: f.label, count: f.count, onClick: () => onView(f.view) }, f.view)) : null
|
|
8391
|
+
] }, e.view)) }) : null,
|
|
8329
8392
|
views.tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
|
|
8330
8393
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("button", { type: "button", "aria-expanded": tagsOpen, onClick: () => setTagsOpen((o) => !o), className: "flex h-7 items-center gap-1 rounded-md px-2.5 text-left text-xs font-medium text-muted-foreground hover:text-foreground", children: [
|
|
8331
8394
|
tagsOpen ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ChevronDownGlyph, { className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ChevronRightGlyph, { className: "size-3.5" }),
|
|
@@ -8590,10 +8653,11 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8590
8653
|
const views = (0, import_react42.useMemo)(() => buildViews(inThisVault, definitions, favorites, recents), [inThisVault, definitions, favorites, recents]);
|
|
8591
8654
|
const visible = (0, import_react42.useMemo)(() => {
|
|
8592
8655
|
const filtered = inThisVault.filter((i) => {
|
|
8593
|
-
if (!matchesQuery(i, loc.q)) return false;
|
|
8656
|
+
if (!matchesQuery(i, loc.q, familyLabel(itemFamily(i, definitions)))) return false;
|
|
8594
8657
|
if (loc.view === "favorites") return favorites?.has(i.id) ?? false;
|
|
8595
8658
|
if (loc.view === "recent") return recents?.includes(i.id) ?? false;
|
|
8596
8659
|
if (loc.view.startsWith("category:")) return itemCategory(i, definitions) === loc.view.slice(9);
|
|
8660
|
+
if (loc.view.startsWith("family:")) return itemFamily(i, definitions) === loc.view.slice(7);
|
|
8597
8661
|
return inView(i, loc.view);
|
|
8598
8662
|
});
|
|
8599
8663
|
if (loc.view === "recent" && recents) return [...filtered].sort((a, b) => recents.indexOf(a.id) - recents.indexOf(b.id));
|
|
@@ -8647,7 +8711,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8647
8711
|
const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
|
|
8648
8712
|
const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "grid grid-cols-[repeat(auto-fit,minmax(11rem,1fr))] gap-2", "data-vault-compact-header": "", children: [
|
|
8649
8713
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
|
|
8650
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Select, { value: loc.view, onValueChange: (view) => go({ view, itemId: null, panel: null }), options: viewOptions(views, inThisVault.length, deleted.length, hasFavorites, port.lists.recents !== void 0, host.page !== void 0), "aria-label": "Show" })
|
|
8714
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Select, { value: loc.view, onValueChange: (view) => go({ view, itemId: null, panel: null }), options: viewOptions(views, inThisVault.length, deleted.length, hasFavorites, port.lists.recents !== void 0, host.page !== void 0, loc.view), "aria-label": "Show" })
|
|
8651
8715
|
] });
|
|
8652
8716
|
const compactActions = mode === "full" ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex shrink-0 items-center gap-2", "data-vault-compact-actions": "", children: [
|
|
8653
8717
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|