@ai-matrx/vault 0.1.13 → 0.1.15
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 +28 -0
- package/dist/index.cjs +425 -370
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +346 -291
- package/dist/index.js.map +1 -1
- package/dist/io/csv-import.worker.js.map +1 -1
- package/dist/io/onepux.worker.js +3 -3
- package/dist/io/onepux.worker.js.map +1 -1
- package/dist/io/proton-pass.worker.js +2 -2
- package/dist/io/proton-pass.worker.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -871,9 +871,11 @@ function inView(item, view) {
|
|
|
871
871
|
if (view.startsWith("tag:")) return item.tags.includes(view.slice(4));
|
|
872
872
|
return true;
|
|
873
873
|
}
|
|
874
|
+
var AUTHENTICATOR_WORDS = ["authenticator", "2fa", "two-factor", "two factor", "totp", "one-time password", "verification code", "2-step"];
|
|
874
875
|
function matchesQuery(item, query) {
|
|
875
876
|
const q = query.trim().toLowerCase();
|
|
876
877
|
if (!q) return true;
|
|
878
|
+
if (item.authenticator && AUTHENTICATOR_WORDS.some((w) => w === q || q.length >= 3 && w.startsWith(q))) return true;
|
|
877
879
|
return [item.name, item.definitionLabel, ...item.tags, ...item.loginUrls, ...item.custom.map((c) => `${c.label} ${c.value}`)].some((h) => h.toLowerCase().includes(q));
|
|
878
880
|
}
|
|
879
881
|
var HIDDEN_ITEM_TAG = "matrx:hidden";
|
|
@@ -1499,9 +1501,23 @@ function HistoryRow({ item, port, page, entry, label, onRestored, onConflict, on
|
|
|
1499
1501
|
// src/authenticator/AuthenticatorView.tsx
|
|
1500
1502
|
import { useEffect as useEffect11, useState as useState13 } from "react";
|
|
1501
1503
|
import { Button as Button9 } from "@ai-matrx/design-system/controls";
|
|
1502
|
-
import { cn as
|
|
1504
|
+
import { cn as cn7, DropdownMenu as DropdownMenu2, DropdownMenuContent as DropdownMenuContent2, DropdownMenuItem as DropdownMenuItem2, DropdownMenuSeparator as DropdownMenuSeparator2, DropdownMenuTrigger as DropdownMenuTrigger2, TextInputDialog } from "@ai-matrx/design-system";
|
|
1503
1505
|
import { CopyIcon as CopyIcon4 } from "@ai-matrx/design-system/icons";
|
|
1504
1506
|
|
|
1507
|
+
// src/ItemIcon.tsx
|
|
1508
|
+
import { cn as cn5 } from "@ai-matrx/design-system";
|
|
1509
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1510
|
+
function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
|
|
1511
|
+
const box = size === "lg" ? "size-10 rounded-xl text-base" : size === "md" ? "size-8 rounded-lg text-sm" : "size-6 rounded-md text-xs";
|
|
1512
|
+
const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
|
|
1513
|
+
const initial = siteDomainOf(site).charAt(0).toUpperCase();
|
|
1514
|
+
if (initial && /[A-Z0-9]/.test(initial)) {
|
|
1515
|
+
return /* @__PURE__ */ jsx8("span", { "aria-hidden": true, className: cn5("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
|
|
1516
|
+
}
|
|
1517
|
+
const Glyph = definitionGlyph(definitionKey, family);
|
|
1518
|
+
return /* @__PURE__ */ jsx8("span", { "aria-hidden": true, className: cn5("flex shrink-0 items-center justify-center", box, tone), children: /* @__PURE__ */ jsx8(Glyph, { className: size === "lg" ? "size-5" : "size-4" }) });
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1505
1521
|
// src/authenticator/AddOtpPanel.tsx
|
|
1506
1522
|
import { useState as useState12 } from "react";
|
|
1507
1523
|
import { Button as Button8, Field as Field4, Select as Select2 } from "@ai-matrx/design-system/controls";
|
|
@@ -1509,7 +1525,7 @@ import { Button as Button8, Field as Field4, Select as Select2 } from "@ai-matrx
|
|
|
1509
1525
|
// src/authenticator/CodeParts.tsx
|
|
1510
1526
|
import { useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
|
|
1511
1527
|
import { Button as Button5 } from "@ai-matrx/design-system/controls";
|
|
1512
|
-
import { cn as
|
|
1528
|
+
import { cn as cn6 } from "@ai-matrx/design-system";
|
|
1513
1529
|
import { CopyIcon as CopyIcon3 } from "@ai-matrx/design-system/icons";
|
|
1514
1530
|
import { groupCode as groupCode2 } from "@ai-matrx/kit/format";
|
|
1515
1531
|
|
|
@@ -1612,16 +1628,16 @@ function matchIssuerToSite(issuer, candidates, siteOf) {
|
|
|
1612
1628
|
}
|
|
1613
1629
|
|
|
1614
1630
|
// src/authenticator/CodeParts.tsx
|
|
1615
|
-
import { jsx as
|
|
1631
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1616
1632
|
function CountdownRing({ remaining, period, loading = false, className }) {
|
|
1617
1633
|
const r = 9.5;
|
|
1618
1634
|
const c = 2 * Math.PI * r;
|
|
1619
1635
|
const p = loading ? 0 : Math.max(0, Math.min(1, remaining / period));
|
|
1620
1636
|
const urgent = !loading && remaining <= 5;
|
|
1621
|
-
return /* @__PURE__ */ jsxs8("span", { className:
|
|
1637
|
+
return /* @__PURE__ */ jsxs8("span", { className: cn6("relative inline-flex size-6 shrink-0 items-center justify-center", className), role: "img", "aria-label": loading ? "Getting code" : `${remaining} seconds left`, children: [
|
|
1622
1638
|
/* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 24 24", className: "absolute inset-0 size-full -rotate-90", "aria-hidden": true, children: [
|
|
1623
|
-
/* @__PURE__ */
|
|
1624
|
-
/* @__PURE__ */
|
|
1639
|
+
/* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r, fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "text-muted" }),
|
|
1640
|
+
/* @__PURE__ */ jsx9(
|
|
1625
1641
|
"circle",
|
|
1626
1642
|
{
|
|
1627
1643
|
cx: "12",
|
|
@@ -1638,7 +1654,7 @@ function CountdownRing({ remaining, period, loading = false, className }) {
|
|
|
1638
1654
|
}
|
|
1639
1655
|
)
|
|
1640
1656
|
] }),
|
|
1641
|
-
/* @__PURE__ */
|
|
1657
|
+
/* @__PURE__ */ jsx9("span", { className: cn6("text-[10px] font-semibold leading-none tabular-nums", urgent ? "text-destructive" : "text-muted-foreground"), children: loading ? "" : remaining })
|
|
1642
1658
|
] });
|
|
1643
1659
|
}
|
|
1644
1660
|
function useCopied() {
|
|
@@ -1668,17 +1684,17 @@ function CodeButton({ live, digits, size = "md", label }) {
|
|
|
1668
1684
|
onClick: () => live.code && void copy(live.code),
|
|
1669
1685
|
"aria-label": live.code ? `Copy ${label} code` : `Getting ${label} code`,
|
|
1670
1686
|
title: live.code ? "Copy" : void 0,
|
|
1671
|
-
className:
|
|
1687
|
+
className: cn6("group -mx-1.5 inline-flex max-w-full items-center gap-2.5 rounded-md px-1.5 py-1 text-left transition-colors hover:bg-accent focus-visible:bg-accent focus-visible:outline-none", size === "lg" && "gap-3"),
|
|
1672
1688
|
children: [
|
|
1673
|
-
/* @__PURE__ */
|
|
1674
|
-
copied ? /* @__PURE__ */
|
|
1689
|
+
/* @__PURE__ */ jsx9("span", { className: cn6("whitespace-nowrap font-mono font-medium tabular-nums tracking-[0.08em]", size === "lg" ? "text-2xl" : "text-[1.0625rem]", live.code ? "text-foreground" : "text-disabled-foreground"), children: shown }),
|
|
1690
|
+
copied ? /* @__PURE__ */ jsx9("span", { className: "inline-flex size-6 items-center justify-center text-success", role: "status", "aria-label": "Copied", children: /* @__PURE__ */ jsx9(CheckGlyph, { className: "size-4" }) }) : /* @__PURE__ */ jsx9(CountdownRing, { remaining: live.remaining, period: live.period, loading: !live.code })
|
|
1675
1691
|
]
|
|
1676
1692
|
}
|
|
1677
1693
|
);
|
|
1678
1694
|
}
|
|
1679
1695
|
function CopyCodeButton({ live, label }) {
|
|
1680
1696
|
const { copied, copy } = useCopied();
|
|
1681
|
-
return /* @__PURE__ */
|
|
1697
|
+
return /* @__PURE__ */ jsx9(Button5, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx9(CheckGlyph, {}) : /* @__PURE__ */ jsx9(CopyIcon3, {}), ...copied ? { glyphTone: "success" } : {}, "aria-label": `Copy ${label} code`, disabled: !live.code, onClick: () => live.code && void copy(live.code) });
|
|
1682
1698
|
}
|
|
1683
1699
|
|
|
1684
1700
|
// src/authenticator/live-code.ts
|
|
@@ -1770,42 +1786,23 @@ import { useEffect as useEffect10, useRef as useRef8, useState as useState10 } f
|
|
|
1770
1786
|
import { Button as Button6, Field as Field3 } from "@ai-matrx/design-system/controls";
|
|
1771
1787
|
|
|
1772
1788
|
// src/authenticator/qr.ts
|
|
1773
|
-
|
|
1774
|
-
const c = globalThis.BarcodeDetector;
|
|
1775
|
-
return typeof c === "function" ? c : null;
|
|
1776
|
-
}
|
|
1777
|
-
var support = null;
|
|
1789
|
+
import { decodeQrFromElement, decodeQrFromImageFile } from "@ai-matrx/capture/qr";
|
|
1778
1790
|
function canDecodeQrLocally() {
|
|
1779
|
-
if (
|
|
1780
|
-
|
|
1781
|
-
|
|
1791
|
+
if (typeof document === "undefined") return Promise.resolve(false);
|
|
1792
|
+
try {
|
|
1793
|
+
return Promise.resolve(Boolean(document.createElement("canvas").getContext("2d")));
|
|
1794
|
+
} catch {
|
|
1795
|
+
return Promise.resolve(false);
|
|
1782
1796
|
}
|
|
1783
|
-
return support;
|
|
1784
1797
|
}
|
|
1785
1798
|
function hasCamera() {
|
|
1786
1799
|
return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
|
|
1787
1800
|
}
|
|
1788
|
-
var detector = null;
|
|
1789
|
-
function qrDetector() {
|
|
1790
|
-
const C = detectorCtor();
|
|
1791
|
-
if (!C) return null;
|
|
1792
|
-
detector ??= new C({ formats: ["qr_code"] });
|
|
1793
|
-
return detector;
|
|
1794
|
-
}
|
|
1795
1801
|
async function decodeQr(source) {
|
|
1796
|
-
|
|
1797
|
-
if (!d) return null;
|
|
1798
|
-
const found = await d.detect(source);
|
|
1799
|
-
return found.find((b) => b.rawValue)?.rawValue ?? null;
|
|
1802
|
+
return decodeQrFromElement(source);
|
|
1800
1803
|
}
|
|
1801
1804
|
async function decodeQrFile(file) {
|
|
1802
|
-
|
|
1803
|
-
const bitmap = await createImageBitmap(file);
|
|
1804
|
-
try {
|
|
1805
|
-
return await decodeQr(bitmap);
|
|
1806
|
-
} finally {
|
|
1807
|
-
bitmap.close();
|
|
1808
|
-
}
|
|
1805
|
+
return decodeQrFromImageFile(file);
|
|
1809
1806
|
}
|
|
1810
1807
|
function imageFrom(data) {
|
|
1811
1808
|
if (!data) return null;
|
|
@@ -1820,7 +1817,7 @@ function imageFrom(data) {
|
|
|
1820
1817
|
}
|
|
1821
1818
|
|
|
1822
1819
|
// src/authenticator/Setup.tsx
|
|
1823
|
-
import { Fragment as Fragment5, jsx as
|
|
1820
|
+
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1824
1821
|
function useCanScan() {
|
|
1825
1822
|
const [ok, setOk] = useState10(false);
|
|
1826
1823
|
useEffect10(() => {
|
|
@@ -1886,15 +1883,15 @@ function QrScanPanel({ open, onResult, onClose }) {
|
|
|
1886
1883
|
stop();
|
|
1887
1884
|
};
|
|
1888
1885
|
}, [open]);
|
|
1889
|
-
return /* @__PURE__ */ jsxs9(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */
|
|
1886
|
+
return /* @__PURE__ */ jsxs9(Panel, { title: "Scan QR code", open, onClose, size: "sm", footer: /* @__PURE__ */ jsx10(Button6, { variant: "quiet", onClick: onClose, children: "Cancel" }), children: [
|
|
1890
1887
|
/* @__PURE__ */ jsxs9("div", { className: "relative mx-auto aspect-square w-full max-w-sm overflow-hidden rounded-xl bg-muted", children: [
|
|
1891
|
-
/* @__PURE__ */
|
|
1892
|
-
error ? /* @__PURE__ */
|
|
1888
|
+
/* @__PURE__ */ jsx10("video", { ref: video, className: "size-full object-cover", playsInline: true, muted: true, "aria-label": "Camera" }),
|
|
1889
|
+
error ? /* @__PURE__ */ jsx10("div", { className: "absolute inset-0 flex items-center justify-center p-6 text-center text-sm text-destructive", children: error }) : /* @__PURE__ */ jsx10("div", { className: "pointer-events-none absolute inset-[18%] rounded-lg border-2 border-primary", "aria-hidden": true })
|
|
1893
1890
|
] }),
|
|
1894
|
-
/* @__PURE__ */
|
|
1891
|
+
/* @__PURE__ */ jsx10("p", { className: "text-center text-xs text-muted-foreground", children: "Point at the site's QR code" })
|
|
1895
1892
|
] });
|
|
1896
1893
|
}
|
|
1897
|
-
function OtpSetupRow({ value, onChange, label = "
|
|
1894
|
+
function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = "Paste setup key or otpauth link", disabled = false, autoFocus = false, onImage, onRecognized, status }) {
|
|
1898
1895
|
const file = useRef8(null);
|
|
1899
1896
|
const canScan = useCanScan();
|
|
1900
1897
|
const [scanning, setScanning] = useState10(false);
|
|
@@ -1927,9 +1924,9 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
|
|
|
1927
1924
|
setReading(false);
|
|
1928
1925
|
}
|
|
1929
1926
|
};
|
|
1930
|
-
const end = status ? /* @__PURE__ */
|
|
1931
|
-
/* @__PURE__ */
|
|
1932
|
-
/* @__PURE__ */
|
|
1927
|
+
const end = status ? /* @__PURE__ */ jsx10("span", { className: "max-w-48 truncate text-xs text-muted-foreground", children: status }) : reading ? /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : imageError || error ? /* @__PURE__ */ jsx10("span", { className: "max-w-56 truncate text-xs text-destructive", title: imageError ?? error ?? void 0, role: "alert", children: imageError ?? error }) : parsed ? /* @__PURE__ */ jsxs9("span", { className: "flex max-w-56 items-center gap-1 text-xs text-success", children: [
|
|
1928
|
+
/* @__PURE__ */ jsx10(CheckGlyph, { className: "size-3.5 shrink-0" }),
|
|
1929
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate", children: describeEnrollment(parsed) })
|
|
1933
1930
|
] }) : null;
|
|
1934
1931
|
return /* @__PURE__ */ jsxs9(
|
|
1935
1932
|
"div",
|
|
@@ -1954,11 +1951,11 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
|
|
|
1954
1951
|
{
|
|
1955
1952
|
label,
|
|
1956
1953
|
actions: /* @__PURE__ */ jsxs9(Fragment5, { children: [
|
|
1957
|
-
/* @__PURE__ */
|
|
1958
|
-
canScan ? /* @__PURE__ */
|
|
1954
|
+
/* @__PURE__ */ jsx10(Button6, { variant: "quiet", icon: /* @__PURE__ */ jsx10(QrGlyph, {}), "aria-label": "Choose QR image", title: "Choose or drop a QR image", disabled: disabled || reading, onClick: () => file.current?.click() }),
|
|
1955
|
+
canScan ? /* @__PURE__ */ jsx10(Button6, { variant: "quiet", icon: /* @__PURE__ */ jsx10(CameraGlyph, {}), "aria-label": "Scan QR code with camera", title: "Scan the site's QR code with the camera", disabled, onClick: () => setScanning(true), children: "Scan" }) : null
|
|
1959
1956
|
] }),
|
|
1960
1957
|
children: [
|
|
1961
|
-
/* @__PURE__ */
|
|
1958
|
+
/* @__PURE__ */ jsx10(
|
|
1962
1959
|
Field3,
|
|
1963
1960
|
{
|
|
1964
1961
|
className: "w-full",
|
|
@@ -1981,7 +1978,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
|
|
|
1981
1978
|
end
|
|
1982
1979
|
}
|
|
1983
1980
|
),
|
|
1984
|
-
/* @__PURE__ */
|
|
1981
|
+
/* @__PURE__ */ jsx10(
|
|
1985
1982
|
"input",
|
|
1986
1983
|
{
|
|
1987
1984
|
ref: file,
|
|
@@ -1998,7 +1995,7 @@ function OtpSetupRow({ value, onChange, label = "One-time password", placeholder
|
|
|
1998
1995
|
]
|
|
1999
1996
|
}
|
|
2000
1997
|
),
|
|
2001
|
-
canScan ? /* @__PURE__ */
|
|
1998
|
+
canScan ? /* @__PURE__ */ jsx10(
|
|
2002
1999
|
QrScanPanel,
|
|
2003
2000
|
{
|
|
2004
2001
|
open: scanning,
|
|
@@ -2029,7 +2026,7 @@ function mfaMethodOf(item) {
|
|
|
2029
2026
|
}
|
|
2030
2027
|
|
|
2031
2028
|
// src/authenticator/TotpRows.tsx
|
|
2032
|
-
import { Fragment as Fragment6, jsx as
|
|
2029
|
+
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2033
2030
|
function otpActionError(e, what) {
|
|
2034
2031
|
const status = e?.status;
|
|
2035
2032
|
if (status === 404) return "There is no one-time password here.";
|
|
@@ -2067,7 +2064,7 @@ function useOtpActions(port, item, onChanged) {
|
|
|
2067
2064
|
}),
|
|
2068
2065
|
remove: async () => {
|
|
2069
2066
|
const ok = await confirm2({
|
|
2070
|
-
title: `Remove the
|
|
2067
|
+
title: `Remove the authenticator for ${item.name}?`,
|
|
2071
2068
|
description: "Codes stop here. Two-factor stays on at the site, and your phone app and backup codes still work.",
|
|
2072
2069
|
confirmLabel: "Remove",
|
|
2073
2070
|
variant: "destructive"
|
|
@@ -2087,37 +2084,37 @@ function TotpRow({ port, item, onChanged }) {
|
|
|
2087
2084
|
const canManage = item.capabilities.canManage || item.capabilities.canEdit;
|
|
2088
2085
|
if (a.removed || !item.authenticator) return null;
|
|
2089
2086
|
const menu = canManage ? /* @__PURE__ */ jsxs10(DropdownMenu, { children: [
|
|
2090
|
-
/* @__PURE__ */
|
|
2087
|
+
/* @__PURE__ */ jsx11(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx11(Button7, { variant: "quiet", icon: /* @__PURE__ */ jsx11(MoreGlyph, {}), "aria-label": "Authenticator options", disabled: a.busy }) }),
|
|
2091
2088
|
/* @__PURE__ */ jsxs10(DropdownMenuContent, { align: "end", className: "min-w-44", children: [
|
|
2092
2089
|
/* @__PURE__ */ jsxs10(DropdownMenuItem, { onSelect: () => void a.setEnabled(!a.enabled), children: [
|
|
2093
|
-
/* @__PURE__ */
|
|
2090
|
+
/* @__PURE__ */ jsx11(PowerGlyph, { className: "size-4" }),
|
|
2094
2091
|
a.enabled ? "Turn off" : "Turn on"
|
|
2095
2092
|
] }),
|
|
2096
|
-
/* @__PURE__ */
|
|
2093
|
+
/* @__PURE__ */ jsx11(DropdownMenuSeparator, {}),
|
|
2097
2094
|
/* @__PURE__ */ jsxs10(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
|
|
2098
|
-
/* @__PURE__ */
|
|
2095
|
+
/* @__PURE__ */ jsx11(TrashGlyph, { className: "size-4" }),
|
|
2099
2096
|
"Remove"
|
|
2100
2097
|
] })
|
|
2101
2098
|
] })
|
|
2102
2099
|
] }) : null;
|
|
2103
|
-
return /* @__PURE__ */
|
|
2100
|
+
return /* @__PURE__ */ jsx11(
|
|
2104
2101
|
VaultRow,
|
|
2105
2102
|
{
|
|
2106
|
-
label: "
|
|
2103
|
+
label: "Authenticator",
|
|
2107
2104
|
actions: /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
2108
|
-
a.enabled ? /* @__PURE__ */
|
|
2105
|
+
a.enabled ? /* @__PURE__ */ jsx11(CopyCodeButton, { live, label: item.name }) : null,
|
|
2109
2106
|
menu
|
|
2110
2107
|
] }),
|
|
2111
2108
|
children: a.error ? /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2", children: [
|
|
2112
|
-
/* @__PURE__ */
|
|
2113
|
-
/* @__PURE__ */
|
|
2109
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-destructive", role: "alert", children: a.error }),
|
|
2110
|
+
/* @__PURE__ */ jsx11(Button7, { variant: "quiet", onClick: a.clearError, children: "Dismiss" })
|
|
2114
2111
|
] }) : !a.enabled ? /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2", children: [
|
|
2115
|
-
/* @__PURE__ */
|
|
2116
|
-
canManage ? /* @__PURE__ */
|
|
2112
|
+
/* @__PURE__ */ jsx11("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
|
|
2113
|
+
canManage ? /* @__PURE__ */ jsx11(Button7, { variant: "quiet", icon: /* @__PURE__ */ jsx11(PowerGlyph, {}), disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
|
|
2117
2114
|
] }) : live.error ? /* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2", children: [
|
|
2118
|
-
/* @__PURE__ */
|
|
2119
|
-
/* @__PURE__ */
|
|
2120
|
-
] }) : /* @__PURE__ */
|
|
2115
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-destructive", title: live.error, children: live.error }),
|
|
2116
|
+
/* @__PURE__ */ jsx11(Button7, { variant: "quiet", onClick: live.retry, children: "Retry" })
|
|
2117
|
+
] }) : /* @__PURE__ */ jsx11(CodeButton, { live, digits: item.authenticator.digits, label: item.name })
|
|
2121
2118
|
}
|
|
2122
2119
|
);
|
|
2123
2120
|
}
|
|
@@ -2135,7 +2132,7 @@ function TotpEditRow({ value, onChange, hasExisting, item, port, onEnrolled, onR
|
|
|
2135
2132
|
setStatus(wasCancelled(e) ? null : otpActionError(e, "read that image"));
|
|
2136
2133
|
}
|
|
2137
2134
|
} : void 0;
|
|
2138
|
-
return /* @__PURE__ */
|
|
2135
|
+
return /* @__PURE__ */ jsx11(
|
|
2139
2136
|
OtpSetupRow,
|
|
2140
2137
|
{
|
|
2141
2138
|
value,
|
|
@@ -2160,7 +2157,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
|
|
|
2160
2157
|
const [busy, setBusy] = useState11(false);
|
|
2161
2158
|
const [error, setError] = useState11(null);
|
|
2162
2159
|
const canEdit = item.capabilities.canEdit || item.capabilities.canManage;
|
|
2163
|
-
return /* @__PURE__ */
|
|
2160
|
+
return /* @__PURE__ */ jsx11(VaultRow, { label: "Two-factor", hint: error ? /* @__PURE__ */ jsx11("span", { className: "text-destructive", children: error }) : void 0, children: /* @__PURE__ */ jsx11(
|
|
2164
2161
|
Select,
|
|
2165
2162
|
{
|
|
2166
2163
|
value: value ?? "none",
|
|
@@ -2187,7 +2184,7 @@ function TwoFactorMethodRow({ port, item, onChanged }) {
|
|
|
2187
2184
|
}
|
|
2188
2185
|
|
|
2189
2186
|
// src/authenticator/AddOtpPanel.tsx
|
|
2190
|
-
import { Fragment as Fragment7, jsx as
|
|
2187
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2191
2188
|
var NEW = "__new__";
|
|
2192
2189
|
var LOGIN_KEYS = ["website_login", "username_password"];
|
|
2193
2190
|
function normalizeWebsite(raw) {
|
|
@@ -2263,23 +2260,23 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2263
2260
|
setBusy(false);
|
|
2264
2261
|
}
|
|
2265
2262
|
};
|
|
2266
|
-
if (saved) return /* @__PURE__ */
|
|
2263
|
+
if (saved) return /* @__PURE__ */ jsx12(AddedPanel, { port, item: saved, onClose, onDone: () => onDone(saved.id) });
|
|
2267
2264
|
return /* @__PURE__ */ jsxs11(
|
|
2268
2265
|
Panel,
|
|
2269
2266
|
{
|
|
2270
|
-
title: "Add
|
|
2267
|
+
title: "Add authenticator",
|
|
2271
2268
|
size: "md",
|
|
2272
2269
|
onClose,
|
|
2273
2270
|
footer: /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
2274
|
-
/* @__PURE__ */
|
|
2275
|
-
/* @__PURE__ */
|
|
2271
|
+
/* @__PURE__ */ jsx12(Button8, { variant: "quiet", onClick: onClose, disabled: busy, children: "Cancel" }),
|
|
2272
|
+
/* @__PURE__ */ jsx12(Button8, { variant: "primary", disabled: !ready, onClick: () => void save(), children: busy ? "Adding\u2026" : "Add" })
|
|
2276
2273
|
] }),
|
|
2277
2274
|
children: [
|
|
2278
2275
|
/* @__PURE__ */ jsxs11(VaultSection, { children: [
|
|
2279
|
-
/* @__PURE__ */
|
|
2276
|
+
/* @__PURE__ */ jsx12(
|
|
2280
2277
|
OtpSetupRow,
|
|
2281
2278
|
{
|
|
2282
|
-
label: "Setup
|
|
2279
|
+
label: "Setup key",
|
|
2283
2280
|
value: input,
|
|
2284
2281
|
autoFocus: true,
|
|
2285
2282
|
disabled: busy,
|
|
@@ -2297,7 +2294,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2297
2294
|
status: image ? `QR image ready \xB7 ${image.name}` : null
|
|
2298
2295
|
}
|
|
2299
2296
|
),
|
|
2300
|
-
/* @__PURE__ */
|
|
2297
|
+
itemId ? null : /* @__PURE__ */ jsx12(VaultRow, { label: "Add to", children: /* @__PURE__ */ jsx12(
|
|
2301
2298
|
Select2,
|
|
2302
2299
|
{
|
|
2303
2300
|
value: target,
|
|
@@ -2311,7 +2308,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2311
2308
|
}
|
|
2312
2309
|
) }),
|
|
2313
2310
|
isNew ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
2314
|
-
/* @__PURE__ */
|
|
2311
|
+
/* @__PURE__ */ jsx12(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx12(
|
|
2315
2312
|
Field4,
|
|
2316
2313
|
{
|
|
2317
2314
|
className: "w-full",
|
|
@@ -2325,24 +2322,27 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2325
2322
|
"aria-label": "Name"
|
|
2326
2323
|
}
|
|
2327
2324
|
) }),
|
|
2328
|
-
/* @__PURE__ */
|
|
2325
|
+
/* @__PURE__ */ jsx12(VaultRow, { label: "Website", hint: websiteBad ? /* @__PURE__ */ jsx12("span", { className: "text-destructive", children: "Not a web address" }) : void 0, children: /* @__PURE__ */ jsx12(Field4, { className: "w-full", type: "url", value: website, disabled: busy || created !== null, onChange: (e) => setWebsite(e.target.value), placeholder: "example.com", "aria-label": "Website", "aria-invalid": websiteBad || void 0 }) })
|
|
2329
2326
|
] }) : null
|
|
2330
2327
|
] }),
|
|
2331
|
-
error ? /* @__PURE__ */
|
|
2328
|
+
error ? /* @__PURE__ */ jsx12("p", { className: "px-1 text-sm text-destructive", role: "alert", children: error }) : null
|
|
2332
2329
|
]
|
|
2333
2330
|
}
|
|
2334
2331
|
);
|
|
2335
2332
|
}
|
|
2333
|
+
function FirstCodePanel(props) {
|
|
2334
|
+
return /* @__PURE__ */ jsx12(AddedPanel, { ...props });
|
|
2335
|
+
}
|
|
2336
2336
|
function AddedPanel({ port, item, onClose, onDone }) {
|
|
2337
2337
|
const live = useLiveCode(port, item, true);
|
|
2338
|
-
return /* @__PURE__ */
|
|
2339
|
-
live.error ? /* @__PURE__ */
|
|
2340
|
-
/* @__PURE__ */
|
|
2338
|
+
return /* @__PURE__ */ jsx12(Panel, { title: item.name, size: "sm", onClose, footer: /* @__PURE__ */ jsx12(Button8, { variant: "primary", onClick: onDone, children: "Done" }), children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 py-4", children: [
|
|
2339
|
+
live.error ? /* @__PURE__ */ jsx12("span", { className: "text-sm text-destructive", children: live.error }) : /* @__PURE__ */ jsx12(CodeButton, { live, digits: item.authenticator?.digits ?? 6, size: "lg", label: item.name }),
|
|
2340
|
+
/* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground", children: "Enter this code on the site to finish." })
|
|
2341
2341
|
] }) });
|
|
2342
2342
|
}
|
|
2343
2343
|
|
|
2344
2344
|
// src/authenticator/AuthenticatorView.tsx
|
|
2345
|
-
import { Fragment as Fragment8, jsx as
|
|
2345
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2346
2346
|
function matchesEntry(entry, q) {
|
|
2347
2347
|
if (!entry || !q) return false;
|
|
2348
2348
|
return [entry.issuer, entry.account].some((v) => v?.toLowerCase().includes(q));
|
|
@@ -2360,7 +2360,7 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
|
|
|
2360
2360
|
const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
|
|
2361
2361
|
return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
|
|
2362
2362
|
}
|
|
2363
|
-
function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged, header }) {
|
|
2363
|
+
function AuthenticatorView({ port, host, items, definitions, query, onOpen, onChanged, header }) {
|
|
2364
2364
|
const [entries, setEntries] = useState13(/* @__PURE__ */ new Map());
|
|
2365
2365
|
const [hidden, setHidden] = useState13(/* @__PURE__ */ new Set());
|
|
2366
2366
|
const [adding, setAdding] = useState13(false);
|
|
@@ -2380,20 +2380,20 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
|
|
|
2380
2380
|
const groups = otpGroups(items, entries, query, hidden);
|
|
2381
2381
|
const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
|
|
2382
2382
|
const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
|
|
2383
|
-
return /* @__PURE__ */ jsxs12("section", { "aria-label": "
|
|
2384
|
-
header ? /* @__PURE__ */
|
|
2383
|
+
return /* @__PURE__ */ jsxs12("section", { "aria-label": "Authenticator", className: "flex h-full min-h-0 flex-col bg-background", children: [
|
|
2384
|
+
header ? /* @__PURE__ */ jsx13("div", { className: "mx-auto w-full max-w-3xl px-3 pt-3 sm:px-4", children: header }) : null,
|
|
2385
2385
|
/* @__PURE__ */ jsxs12("div", { className: "flex min-h-12 items-center justify-between gap-2 px-4 pb-1 pt-3", children: [
|
|
2386
2386
|
/* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 items-baseline gap-2", children: [
|
|
2387
|
-
/* @__PURE__ */
|
|
2388
|
-
total > 0 ? /* @__PURE__ */
|
|
2387
|
+
/* @__PURE__ */ jsx13("h2", { className: "truncate text-sm font-semibold text-foreground", children: "Authenticator" }),
|
|
2388
|
+
total > 0 ? /* @__PURE__ */ jsx13("span", { className: "text-xs tabular-nums text-muted-foreground", children: total }) : null
|
|
2389
2389
|
] }),
|
|
2390
|
-
total > 0 ? /* @__PURE__ */
|
|
2390
|
+
total > 0 ? /* @__PURE__ */ jsx13(Button9, { variant: "outline", icon: /* @__PURE__ */ jsx13(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
|
|
2391
2391
|
] }),
|
|
2392
2392
|
total === 0 ? /* @__PURE__ */ jsxs12("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
|
|
2393
|
-
/* @__PURE__ */
|
|
2394
|
-
/* @__PURE__ */
|
|
2395
|
-
/* @__PURE__ */
|
|
2396
|
-
] }) : groups.length === 0 ? /* @__PURE__ */
|
|
2393
|
+
/* @__PURE__ */ jsx13("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx13(ShieldGlyph, { className: "size-6" }) }),
|
|
2394
|
+
/* @__PURE__ */ jsx13("p", { className: "text-sm font-medium text-foreground", children: "No authenticator codes yet" }),
|
|
2395
|
+
/* @__PURE__ */ jsx13(Button9, { variant: "primary", icon: /* @__PURE__ */ jsx13(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add authenticator" })
|
|
2396
|
+
] }) : groups.length === 0 ? /* @__PURE__ */ jsx13("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: "Nothing matches." }) : /* @__PURE__ */ jsx13("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 pb-6 pt-1 sm:px-4", children: /* @__PURE__ */ jsx13("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: groups.map((g) => /* @__PURE__ */ jsx13(VaultSection, { title: g.label, className: "[&>div]:px-1", children: g.items.map((item) => /* @__PURE__ */ jsx13(
|
|
2397
2397
|
OtpListRow,
|
|
2398
2398
|
{
|
|
2399
2399
|
port,
|
|
@@ -2401,6 +2401,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
|
|
|
2401
2401
|
family: familyOf2(item.definitionKey),
|
|
2402
2402
|
entry: entries.get(item.id),
|
|
2403
2403
|
onOpen: () => onOpen(item.id),
|
|
2404
|
+
...host.openItemInNewTab ? { onOpenInNewTab: () => host.openItemInNewTab?.(item.id) } : {},
|
|
2404
2405
|
onRename: () => {
|
|
2405
2406
|
setRenameError(null);
|
|
2406
2407
|
setRenaming(item);
|
|
@@ -2413,7 +2414,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
|
|
|
2413
2414
|
},
|
|
2414
2415
|
item.id
|
|
2415
2416
|
)) }, g.key)) }) }),
|
|
2416
|
-
adding ? /* @__PURE__ */
|
|
2417
|
+
adding ? /* @__PURE__ */ jsx13(
|
|
2417
2418
|
AddOtpPanel,
|
|
2418
2419
|
{
|
|
2419
2420
|
port,
|
|
@@ -2427,7 +2428,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
|
|
|
2427
2428
|
}
|
|
2428
2429
|
}
|
|
2429
2430
|
) : null,
|
|
2430
|
-
/* @__PURE__ */
|
|
2431
|
+
/* @__PURE__ */ jsx13(
|
|
2431
2432
|
TextInputDialog,
|
|
2432
2433
|
{
|
|
2433
2434
|
open: renaming !== null,
|
|
@@ -2435,7 +2436,7 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
|
|
|
2435
2436
|
if (!o && !renameBusy) setRenaming(null);
|
|
2436
2437
|
},
|
|
2437
2438
|
title: "Rename login",
|
|
2438
|
-
...renameError ? { description: /* @__PURE__ */
|
|
2439
|
+
...renameError ? { description: /* @__PURE__ */ jsx13("span", { className: "text-destructive", children: renameError }) } : {},
|
|
2439
2440
|
placeholder: "Login name",
|
|
2440
2441
|
defaultValue: renaming?.name ?? "",
|
|
2441
2442
|
confirmLabel: "Rename",
|
|
@@ -2458,50 +2459,53 @@ function AuthenticatorView({ port, items, definitions, query, onOpen, onChanged,
|
|
|
2458
2459
|
)
|
|
2459
2460
|
] });
|
|
2460
2461
|
}
|
|
2461
|
-
function OtpListRow({ port, item, family, entry, onOpen, onRename, onChanged, onRemoved }) {
|
|
2462
|
+
function OtpListRow({ port, item, family, entry, onOpen, onOpenInNewTab, onRename, onChanged, onRemoved }) {
|
|
2462
2463
|
const a = useOtpActions(port, item, (next) => next && next.authenticator === null ? onRemoved() : onChanged());
|
|
2463
2464
|
const live = useLiveCode(port, item, a.enabled && !a.removed);
|
|
2464
2465
|
const { copy } = useCopied();
|
|
2465
|
-
const Icon = definitionGlyph(item.definitionKey, family);
|
|
2466
2466
|
const canManage = item.capabilities.canManage || item.capabilities.canEdit;
|
|
2467
2467
|
if (a.removed) return null;
|
|
2468
2468
|
const sub = a.error ?? [entry?.account, loginSiteOf(item) || entry?.issuer].filter((v, i, all) => Boolean(v) && all.indexOf(v) === i).join(" \xB7 ");
|
|
2469
2469
|
return /* @__PURE__ */ jsxs12("div", { className: "grid min-h-14 grid-cols-[2rem_minmax(0,1fr)_auto_auto] items-center gap-x-3 rounded-lg px-2 py-1.5 transition-colors hover:bg-accent/50", children: [
|
|
2470
|
-
/* @__PURE__ */
|
|
2470
|
+
/* @__PURE__ */ jsx13("button", { type: "button", onClick: onOpen, "aria-label": `Open ${item.name}`, tabIndex: -1, className: "flex size-8 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx13(ItemIcon, { definitionKey: item.definitionKey, family, site: loginSiteOf(item) || (entry?.issuer ?? "") }) }),
|
|
2471
2471
|
/* @__PURE__ */ jsxs12("button", { type: "button", onClick: onOpen, className: "flex min-w-0 flex-col items-start rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [
|
|
2472
|
-
/* @__PURE__ */
|
|
2473
|
-
sub ? /* @__PURE__ */
|
|
2472
|
+
/* @__PURE__ */ jsx13("span", { className: cn7("max-w-full truncate text-sm font-medium", a.enabled ? "text-foreground" : "text-muted-foreground"), children: item.name }),
|
|
2473
|
+
sub ? /* @__PURE__ */ jsx13("span", { className: cn7("max-w-full truncate text-xs", a.error ? "text-destructive" : "text-muted-foreground"), title: sub, children: sub }) : null
|
|
2474
2474
|
] }),
|
|
2475
|
-
/* @__PURE__ */
|
|
2476
|
-
/* @__PURE__ */
|
|
2477
|
-
canManage ? /* @__PURE__ */
|
|
2475
|
+
/* @__PURE__ */ jsx13("div", { className: "flex min-w-0 justify-end", children: !a.enabled ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5", children: [
|
|
2476
|
+
/* @__PURE__ */ jsx13("span", { className: "text-sm text-disabled-foreground", children: "Off" }),
|
|
2477
|
+
canManage ? /* @__PURE__ */ jsx13(Button9, { variant: "quiet", disabled: a.busy, onClick: () => void a.setEnabled(true), children: "Turn on" }) : null
|
|
2478
2478
|
] }) : live.error ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5", children: [
|
|
2479
|
-
/* @__PURE__ */
|
|
2480
|
-
/* @__PURE__ */
|
|
2481
|
-
] }) : /* @__PURE__ */
|
|
2479
|
+
/* @__PURE__ */ jsx13("span", { className: "max-w-40 truncate text-xs text-destructive", title: live.error, children: live.error }),
|
|
2480
|
+
/* @__PURE__ */ jsx13(Button9, { variant: "quiet", onClick: live.retry, children: "Retry" })
|
|
2481
|
+
] }) : /* @__PURE__ */ jsx13(CodeButton, { live, digits: item.authenticator?.digits ?? 6, label: item.name }) }),
|
|
2482
2482
|
/* @__PURE__ */ jsxs12(DropdownMenu2, { children: [
|
|
2483
|
-
/* @__PURE__ */
|
|
2483
|
+
/* @__PURE__ */ jsx13(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx13(Button9, { variant: "quiet", icon: /* @__PURE__ */ jsx13(MoreGlyph, {}), "aria-label": `${item.name} options`, disabled: a.busy }) }),
|
|
2484
2484
|
/* @__PURE__ */ jsxs12(DropdownMenuContent2, { align: "end", className: "min-w-44", children: [
|
|
2485
2485
|
/* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onOpen, children: [
|
|
2486
|
-
/* @__PURE__ */
|
|
2486
|
+
/* @__PURE__ */ jsx13(ExternalGlyph, { className: "size-4" }),
|
|
2487
2487
|
"Open login"
|
|
2488
2488
|
] }),
|
|
2489
|
+
onOpenInNewTab ? /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onOpenInNewTab, children: [
|
|
2490
|
+
/* @__PURE__ */ jsx13(ExternalGlyph, { className: "size-4" }),
|
|
2491
|
+
"Open in new tab"
|
|
2492
|
+
] }) : null,
|
|
2489
2493
|
live.code ? /* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: () => live.code && void copy(live.code), children: [
|
|
2490
|
-
/* @__PURE__ */
|
|
2494
|
+
/* @__PURE__ */ jsx13(CopyIcon4, { className: "size-4" }),
|
|
2491
2495
|
"Copy code"
|
|
2492
2496
|
] }) : null,
|
|
2493
2497
|
canManage ? /* @__PURE__ */ jsxs12(Fragment8, { children: [
|
|
2494
2498
|
/* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: onRename, children: [
|
|
2495
|
-
/* @__PURE__ */
|
|
2499
|
+
/* @__PURE__ */ jsx13(PencilGlyph, { className: "size-4" }),
|
|
2496
2500
|
"Rename"
|
|
2497
2501
|
] }),
|
|
2498
2502
|
/* @__PURE__ */ jsxs12(DropdownMenuItem2, { onSelect: () => void a.setEnabled(!a.enabled), children: [
|
|
2499
|
-
/* @__PURE__ */
|
|
2503
|
+
/* @__PURE__ */ jsx13(PowerGlyph, { className: "size-4" }),
|
|
2500
2504
|
a.enabled ? "Turn off" : "Turn on"
|
|
2501
2505
|
] }),
|
|
2502
|
-
/* @__PURE__ */
|
|
2506
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator2, {}),
|
|
2503
2507
|
/* @__PURE__ */ jsxs12(DropdownMenuItem2, { className: "text-destructive focus:text-destructive", onSelect: () => void a.remove(), children: [
|
|
2504
|
-
/* @__PURE__ */
|
|
2508
|
+
/* @__PURE__ */ jsx13(TrashGlyph, { className: "size-4" }),
|
|
2505
2509
|
"Remove"
|
|
2506
2510
|
] })
|
|
2507
2511
|
] }) : null
|
|
@@ -2594,7 +2598,7 @@ function withoutCode(codes, index) {
|
|
|
2594
2598
|
}
|
|
2595
2599
|
|
|
2596
2600
|
// src/fields/SettingsPanels.tsx
|
|
2597
|
-
import { jsx as
|
|
2601
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2598
2602
|
var say = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
|
|
2599
2603
|
function useSaver(onChanged) {
|
|
2600
2604
|
const [busy, setBusy] = useState14(null);
|
|
@@ -2616,14 +2620,14 @@ function useSaver(onChanged) {
|
|
|
2616
2620
|
return { busy, error, setError, save };
|
|
2617
2621
|
}
|
|
2618
2622
|
function Problem({ children }) {
|
|
2619
|
-
return /* @__PURE__ */
|
|
2623
|
+
return /* @__PURE__ */ jsx14("p", { role: "alert", className: "px-1 text-sm text-destructive", children });
|
|
2620
2624
|
}
|
|
2621
|
-
var quietHint = (text, tone = "muted") => /* @__PURE__ */
|
|
2625
|
+
var quietHint = (text, tone = "muted") => /* @__PURE__ */ jsx14("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
|
|
2622
2626
|
function FieldSettingsPanel(props) {
|
|
2623
2627
|
const field = props.item.fields.find((f) => f.id === props.arg);
|
|
2624
2628
|
if (!field)
|
|
2625
|
-
return /* @__PURE__ */
|
|
2626
|
-
return /* @__PURE__ */
|
|
2629
|
+
return /* @__PURE__ */ jsx14(Panel, { title: "Field settings", size: "md", onClose: props.onClose, footer: /* @__PURE__ */ jsx14(Button10, { variant: "primary", onClick: props.onClose, children: "Done" }), children: /* @__PURE__ */ jsx14(Empty, { children: "This field is no longer on the item." }) });
|
|
2630
|
+
return /* @__PURE__ */ jsx14(FieldSettings, { ...props, field }, field.id);
|
|
2627
2631
|
}
|
|
2628
2632
|
function FieldSettings({ item, port, field, onClose, onChanged }) {
|
|
2629
2633
|
const { busy, error, setError, save } = useSaver((next) => onChanged(next));
|
|
@@ -2662,9 +2666,9 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
|
|
|
2662
2666
|
};
|
|
2663
2667
|
const handling = HANDLING_WORDS[field.handling];
|
|
2664
2668
|
const locked = !canEdit || busy !== null;
|
|
2665
|
-
return /* @__PURE__ */ jsxs13(Panel, { title: `${field.label} settings`, size: "md", onClose, footer: /* @__PURE__ */
|
|
2666
|
-
!canEdit ? /* @__PURE__ */
|
|
2667
|
-
/* @__PURE__ */
|
|
2669
|
+
return /* @__PURE__ */ jsxs13(Panel, { title: `${field.label} settings`, size: "md", onClose, footer: /* @__PURE__ */ jsx14(Button10, { variant: "primary", onClick: onClose, children: "Done" }), children: [
|
|
2670
|
+
!canEdit ? /* @__PURE__ */ jsx14("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
|
|
2671
|
+
/* @__PURE__ */ jsx14(VaultSection, { title: "Who can see it", children: /* @__PURE__ */ jsx14(VaultRow, { label: "Value", hint: quietHint(handling.hint), children: field.handling === "sealed" ? /* @__PURE__ */ jsx14(Badge3, { icon: /* @__PURE__ */ jsx14(LockGlyph, {}), children: "Sealed for good" }) : /* @__PURE__ */ jsx14(
|
|
2668
2672
|
SegmentedControl,
|
|
2669
2673
|
{
|
|
2670
2674
|
"aria-label": "Who can see it",
|
|
@@ -2674,7 +2678,7 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
|
|
|
2674
2678
|
}
|
|
2675
2679
|
) }) }),
|
|
2676
2680
|
/* @__PURE__ */ jsxs13(VaultSection, { title: "Automation", children: [
|
|
2677
|
-
/* @__PURE__ */
|
|
2681
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Env key", hint: envProblem ? quietHint(envProblem, "destructive") : quietHint("The name sandboxes and workflows use"), children: /* @__PURE__ */ jsx14(
|
|
2678
2682
|
Field5,
|
|
2679
2683
|
{
|
|
2680
2684
|
ref: envRef,
|
|
@@ -2694,10 +2698,10 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
|
|
|
2694
2698
|
}
|
|
2695
2699
|
}
|
|
2696
2700
|
) }),
|
|
2697
|
-
/* @__PURE__ */
|
|
2701
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Use in sandbox", hint: blocker && !field.injectIntoSandbox ? quietHint(blocker) : quietHint("Sandboxes get it as an env variable"), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Use in sandbox", checked: field.injectIntoSandbox, disabled: locked, onCheckedChange: (v) => void setSandbox(v) }) })
|
|
2698
2702
|
] }),
|
|
2699
2703
|
/* @__PURE__ */ jsxs13(VaultSection, { title: "Field", children: [
|
|
2700
|
-
/* @__PURE__ */
|
|
2704
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Description", children: /* @__PURE__ */ jsx14(
|
|
2701
2705
|
Field5,
|
|
2702
2706
|
{
|
|
2703
2707
|
className: "w-full",
|
|
@@ -2713,10 +2717,10 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
|
|
|
2713
2717
|
}
|
|
2714
2718
|
}
|
|
2715
2719
|
) }),
|
|
2716
|
-
/* @__PURE__ */
|
|
2717
|
-
/* @__PURE__ */
|
|
2720
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Editable", hint: quietHint(field.editable ? "The value can be changed" : "The value is locked"), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Editable", checked: field.editable, disabled: locked, onCheckedChange: (v) => void update("editable", { editable: v }) }) }),
|
|
2721
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Turned on", hint: quietHint(field.active ? "In use" : "Kept, but nothing uses it"), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Turned on", checked: field.active, disabled: locked, onCheckedChange: (v) => void update("active", { active: v }) }) })
|
|
2718
2722
|
] }),
|
|
2719
|
-
error ? /* @__PURE__ */
|
|
2723
|
+
error ? /* @__PURE__ */ jsx14(Problem, { children: error }) : null
|
|
2720
2724
|
] });
|
|
2721
2725
|
}
|
|
2722
2726
|
function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
|
|
@@ -2743,10 +2747,10 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
|
|
|
2743
2747
|
});
|
|
2744
2748
|
};
|
|
2745
2749
|
const fillHint = !hasWebsite ? "Add a website first" : item.browserFillEnabled ? "Offered on matching pages" : "Never filled automatically";
|
|
2746
|
-
return /* @__PURE__ */ jsxs13(Panel, { title: "Item settings", size: "md", onClose, footer: /* @__PURE__ */
|
|
2747
|
-
!canEdit ? /* @__PURE__ */
|
|
2750
|
+
return /* @__PURE__ */ jsxs13(Panel, { title: "Item settings", size: "md", onClose, footer: /* @__PURE__ */ jsx14(Button10, { variant: "primary", onClick: onClose, children: "Done" }), children: [
|
|
2751
|
+
!canEdit ? /* @__PURE__ */ jsx14("p", { className: "px-1 text-sm text-muted-foreground", children: "View only: you can\u2019t change these settings." }) : null,
|
|
2748
2752
|
/* @__PURE__ */ jsxs13(VaultSection, { title: "In the browser", children: [
|
|
2749
|
-
/* @__PURE__ */
|
|
2753
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Website matching", hint: quietHint(MATCH_WORDS[item.uriMatchMode].hint), children: /* @__PURE__ */ jsx14(
|
|
2750
2754
|
Select3,
|
|
2751
2755
|
{
|
|
2752
2756
|
"aria-label": "Website matching",
|
|
@@ -2756,45 +2760,45 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
|
|
|
2756
2760
|
onValueChange: (m) => void save("match", () => port.fields.updateItem(item, { uriMatchMode: m }))
|
|
2757
2761
|
}
|
|
2758
2762
|
) }),
|
|
2759
|
-
/* @__PURE__ */
|
|
2760
|
-
promotable.map((f) => /* @__PURE__ */
|
|
2763
|
+
/* @__PURE__ */ jsx14(VaultRow, { label: "Fill in browser", hint: quietHint(fillHint), children: /* @__PURE__ */ jsx14(Switch, { "aria-label": "Fill in browser", checked: hasWebsite && item.browserFillEnabled, disabled: locked || !hasWebsite, onCheckedChange: (v) => void save("fill", () => port.fields.updateItem(item, { browserFillEnabled: v })) }) }),
|
|
2764
|
+
promotable.map((f) => /* @__PURE__ */ jsx14(
|
|
2761
2765
|
VaultRow,
|
|
2762
2766
|
{
|
|
2763
2767
|
label: f.label,
|
|
2764
2768
|
hint: quietHint("Encrypted, so the browser can\u2019t match it"),
|
|
2765
|
-
actions: /* @__PURE__ */
|
|
2766
|
-
children: /* @__PURE__ */
|
|
2769
|
+
actions: /* @__PURE__ */ jsx14(Button10, { variant: "outline", icon: /* @__PURE__ */ jsx14(GlobeIcon2, {}), disabled: locked, onClick: () => void promote(f), children: "Use as website" }),
|
|
2770
|
+
children: /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Not a website yet" })
|
|
2767
2771
|
},
|
|
2768
2772
|
f.id
|
|
2769
2773
|
))
|
|
2770
2774
|
] }),
|
|
2771
2775
|
/* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1.5", children: [
|
|
2772
|
-
/* @__PURE__ */
|
|
2773
|
-
advanced ? /* @__PURE__ */
|
|
2776
|
+
/* @__PURE__ */ jsx14(DisclosureHeader, { open: advanced, title: "Advanced", meta: item.fields.length ? `${item.fields.length} fields` : void 0, onClick: () => setAdvanced((a) => !a) }),
|
|
2777
|
+
advanced ? /* @__PURE__ */ jsx14(VaultSection, { children: item.fields.length === 0 ? /* @__PURE__ */ jsx14(Empty, { children: "No encrypted fields." }) : item.fields.map((f) => /* @__PURE__ */ jsx14(
|
|
2774
2778
|
VaultRow,
|
|
2775
2779
|
{
|
|
2776
2780
|
label: f.label,
|
|
2777
|
-
actions: /* @__PURE__ */
|
|
2781
|
+
actions: /* @__PURE__ */ jsx14(Button10, { variant: "quiet", icon: /* @__PURE__ */ jsx14(SlidersGlyph, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }),
|
|
2778
2782
|
children: /* @__PURE__ */ jsxs13("span", { className: "block truncate", children: [
|
|
2779
|
-
/* @__PURE__ */
|
|
2783
|
+
/* @__PURE__ */ jsx14("span", { className: f.handling === "sealed" ? "text-foreground" : "text-muted-foreground", children: HANDLING_WORDS[f.handling].label }),
|
|
2780
2784
|
f.envKey ? /* @__PURE__ */ jsxs13("span", { className: "font-mono text-muted-foreground", children: [
|
|
2781
2785
|
" \xB7 ",
|
|
2782
2786
|
f.envKey
|
|
2783
2787
|
] }) : null,
|
|
2784
|
-
f.injectIntoSandbox ? /* @__PURE__ */
|
|
2785
|
-
!f.active ? /* @__PURE__ */
|
|
2788
|
+
f.injectIntoSandbox ? /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: " \xB7 Sandbox" }) : null,
|
|
2789
|
+
!f.active ? /* @__PURE__ */ jsx14("span", { className: "text-warning", children: " \xB7 Turned off" }) : null
|
|
2786
2790
|
] })
|
|
2787
2791
|
},
|
|
2788
2792
|
f.id
|
|
2789
2793
|
)) }) : null
|
|
2790
2794
|
] }),
|
|
2791
|
-
error ? /* @__PURE__ */
|
|
2795
|
+
error ? /* @__PURE__ */ jsx14(Problem, { children: error }) : null
|
|
2792
2796
|
] });
|
|
2793
2797
|
}
|
|
2794
2798
|
|
|
2795
2799
|
// src/generator/PasswordGenerator.tsx
|
|
2796
2800
|
import { createContext as createContext5, useContext as useContext5, useEffect as useEffect12, useRef as useRef10, useState as useState15 } from "react";
|
|
2797
|
-
import { Popover, PopoverContent, PopoverTrigger, cn as
|
|
2801
|
+
import { Popover, PopoverContent, PopoverTrigger, cn as cn8 } from "@ai-matrx/design-system";
|
|
2798
2802
|
import { Button as Button11, NumberStepper, SegmentedControl as SegmentedControl2, Select as Select4, Switch as Switch2 } from "@ai-matrx/design-system/controls";
|
|
2799
2803
|
import { CopyIcon as CopyIcon5 } from "@ai-matrx/design-system/icons";
|
|
2800
2804
|
|
|
@@ -2854,10 +2858,10 @@ function strengthOf(bits) {
|
|
|
2854
2858
|
}
|
|
2855
2859
|
|
|
2856
2860
|
// src/generator/PasswordGenerator.tsx
|
|
2857
|
-
import { jsx as
|
|
2861
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2858
2862
|
var LimitsCtx = createContext5(null);
|
|
2859
2863
|
function GeneratorLimitsProvider({ resolve: resolve2, children }) {
|
|
2860
|
-
return /* @__PURE__ */
|
|
2864
|
+
return /* @__PURE__ */ jsx15(LimitsCtx.Provider, { value: resolve2 ?? null, children });
|
|
2861
2865
|
}
|
|
2862
2866
|
function useGeneratorLimitsSource() {
|
|
2863
2867
|
return useContext5(LimitsCtx);
|
|
@@ -2918,11 +2922,11 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
|
|
|
2918
2922
|
useEffect12(() => () => {
|
|
2919
2923
|
if (expiry.current) clearTimeout(expiry.current);
|
|
2920
2924
|
}, []);
|
|
2921
|
-
if (limits.state === "loading") return /* @__PURE__ */
|
|
2925
|
+
if (limits.state === "loading") return /* @__PURE__ */ jsx15("p", { className: "px-1 py-3 text-sm text-muted-foreground", "aria-live": "polite", children: "Reading limits\u2026" });
|
|
2922
2926
|
if (limits.state === "unavailable")
|
|
2923
2927
|
return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-2 px-1 py-2", children: [
|
|
2924
|
-
/* @__PURE__ */
|
|
2925
|
-
/* @__PURE__ */
|
|
2928
|
+
/* @__PURE__ */ jsx15("p", { role: "alert", className: "text-sm text-destructive", children: limits.message }),
|
|
2929
|
+
/* @__PURE__ */ jsx15(Button11, { variant: "outline", icon: /* @__PURE__ */ jsx15(RefreshGlyph, {}), onClick: () => setRound((n) => n + 1), children: "Try again" })
|
|
2926
2930
|
] });
|
|
2927
2931
|
const ceilings = limits.limits;
|
|
2928
2932
|
const change = (next) => make(next, ceilings);
|
|
@@ -2940,7 +2944,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
|
|
|
2940
2944
|
onUse(value);
|
|
2941
2945
|
};
|
|
2942
2946
|
return /* @__PURE__ */ jsxs14("div", { className: "flex w-full flex-col gap-3", "aria-label": "Password generator", children: [
|
|
2943
|
-
/* @__PURE__ */
|
|
2947
|
+
/* @__PURE__ */ jsx15(
|
|
2944
2948
|
SegmentedControl2,
|
|
2945
2949
|
{
|
|
2946
2950
|
fill: true,
|
|
@@ -2952,13 +2956,13 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
|
|
|
2952
2956
|
),
|
|
2953
2957
|
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1 rounded-lg bg-muted px-3 py-2", children: [
|
|
2954
2958
|
/* @__PURE__ */ jsxs14("div", { className: "flex min-h-7 items-start gap-1", children: [
|
|
2955
|
-
/* @__PURE__ */
|
|
2956
|
-
/* @__PURE__ */
|
|
2957
|
-
/* @__PURE__ */
|
|
2958
|
-
/* @__PURE__ */
|
|
2959
|
+
/* @__PURE__ */ jsx15("code", { className: cn8("min-w-0 flex-1 break-all py-1 font-mono text-sm", candidate ? "text-foreground" : "text-muted-foreground"), "data-testid": "generated-value", children: candidate ? revealed ? candidate.value : MASK3 : "\u2014" }),
|
|
2960
|
+
/* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: revealed ? /* @__PURE__ */ jsx15(EyeOffGlyph, {}) : /* @__PURE__ */ jsx15(EyeGlyph, {}), "aria-label": revealed ? "Hide" : "Reveal", disabled: !candidate, onClick: () => setRevealed((r) => !r) }),
|
|
2961
|
+
/* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx15(CopyIcon5, {}), "aria-label": "Copy", disabled: !candidate, onClick: () => void copy() }),
|
|
2962
|
+
/* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx15(RefreshGlyph, {}), "aria-label": "Generate another", onClick: () => change(settings) })
|
|
2959
2963
|
] }),
|
|
2960
2964
|
strength && candidate ? /* @__PURE__ */ jsxs14("p", { className: "text-xs", children: [
|
|
2961
|
-
/* @__PURE__ */
|
|
2965
|
+
/* @__PURE__ */ jsx15("span", { className: cn8(strength.tone === "success" && "text-success", strength.tone === "primary" && "text-primary", strength.tone === "warning" && "text-warning"), children: strength.label }),
|
|
2962
2966
|
/* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground", children: [
|
|
2963
2967
|
" \xB7 ",
|
|
2964
2968
|
Math.floor(candidate.bits),
|
|
@@ -2967,7 +2971,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
|
|
|
2967
2971
|
] }) : null
|
|
2968
2972
|
] }),
|
|
2969
2973
|
settings.kind === "password" ? /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-0.5", children: [
|
|
2970
|
-
/* @__PURE__ */
|
|
2974
|
+
/* @__PURE__ */ jsx15(OptionLine, { label: "Length", children: /* @__PURE__ */ jsx15(NumberStepper, { value: settings.password.length, min: minPasswordLength(settings.password), max: ceilings.maxPasswordLength, fallback: 24, onChange: (length) => setPassword({ length }) }) }),
|
|
2971
2975
|
[
|
|
2972
2976
|
["lowercase", "Lowercase"],
|
|
2973
2977
|
["uppercase", "Uppercase"],
|
|
@@ -2976,26 +2980,26 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
|
|
|
2976
2980
|
].map(([key, label]) => {
|
|
2977
2981
|
const on = settings.password[key];
|
|
2978
2982
|
const last = on && minPasswordLength(settings.password) === 1;
|
|
2979
|
-
return /* @__PURE__ */
|
|
2983
|
+
return /* @__PURE__ */ jsx15(OptionLine, { label, children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": label, checked: on, disabled: last, onCheckedChange: (v) => setPassword({ [key]: v }) }) }, key);
|
|
2980
2984
|
}),
|
|
2981
|
-
/* @__PURE__ */
|
|
2985
|
+
/* @__PURE__ */ jsx15(OptionLine, { label: "Avoid look-alikes", hint: "No 0/O, 1/l/I", children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": "Avoid look-alikes", checked: settings.password.excludeAmbiguous, onCheckedChange: (v) => setPassword({ excludeAmbiguous: v }) }) })
|
|
2982
2986
|
] }) : /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-0.5", children: [
|
|
2983
|
-
/* @__PURE__ */
|
|
2984
|
-
/* @__PURE__ */
|
|
2985
|
-
/* @__PURE__ */
|
|
2986
|
-
/* @__PURE__ */
|
|
2987
|
+
/* @__PURE__ */ jsx15(OptionLine, { label: "Words", children: /* @__PURE__ */ jsx15(NumberStepper, { value: settings.passphrase.wordCount, min: MIN_PASSPHRASE_WORDS, max: ceilings.maxPassphraseWords, fallback: MIN_PASSPHRASE_WORDS, onChange: (wordCount) => change({ ...settings, passphrase: { ...settings.passphrase, wordCount } }) }) }),
|
|
2988
|
+
/* @__PURE__ */ jsx15(OptionLine, { label: "Separator", children: /* @__PURE__ */ jsx15(Select4, { "aria-label": "Separator", variant: "inline", align: "end", value: settings.passphrase.separator, options: SEPARATORS, onValueChange: (separator) => change({ ...settings, passphrase: { ...settings.passphrase, separator } }) }) }),
|
|
2989
|
+
/* @__PURE__ */ jsx15(OptionLine, { label: "Capitalize", children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": "Capitalize", checked: settings.passphrase.capitalize, onCheckedChange: (capitalize) => change({ ...settings, passphrase: { ...settings.passphrase, capitalize } }) }) }),
|
|
2990
|
+
/* @__PURE__ */ jsx15(OptionLine, { label: "Add a digit", children: /* @__PURE__ */ jsx15(Switch2, { "aria-label": "Add a digit", checked: settings.passphrase.appendDigit, onCheckedChange: (appendDigit) => change({ ...settings, passphrase: { ...settings.passphrase, appendDigit } }) }) })
|
|
2987
2991
|
] }),
|
|
2988
|
-
note ? /* @__PURE__ */
|
|
2989
|
-
/* @__PURE__ */
|
|
2992
|
+
note ? /* @__PURE__ */ jsx15("p", { "aria-live": "polite", className: cn8("text-xs", note.tone === "destructive" ? "text-destructive" : note.tone === "success" ? "text-success" : "text-muted-foreground"), children: note.text }) : null,
|
|
2993
|
+
/* @__PURE__ */ jsx15(Button11, { variant: "primary", disabled: !candidate, onClick: use, children: useLabel })
|
|
2990
2994
|
] });
|
|
2991
2995
|
}
|
|
2992
2996
|
function OptionLine({ label, hint, children }) {
|
|
2993
2997
|
return /* @__PURE__ */ jsxs14("div", { className: "flex min-h-9 items-center justify-between gap-3", children: [
|
|
2994
2998
|
/* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-col", children: [
|
|
2995
|
-
/* @__PURE__ */
|
|
2996
|
-
hint ? /* @__PURE__ */
|
|
2999
|
+
/* @__PURE__ */ jsx15("span", { className: "truncate text-sm text-foreground", children: label }),
|
|
3000
|
+
hint ? /* @__PURE__ */ jsx15("span", { className: "truncate text-xs text-muted-foreground", children: hint }) : null
|
|
2997
3001
|
] }),
|
|
2998
|
-
/* @__PURE__ */
|
|
3002
|
+
/* @__PURE__ */ jsx15("div", { className: "flex shrink-0 items-center", children })
|
|
2999
3003
|
] });
|
|
3000
3004
|
}
|
|
3001
3005
|
function GenerateButton({ onUse, label, limits, disabled }) {
|
|
@@ -3005,8 +3009,8 @@ function GenerateButton({ onUse, label, limits, disabled }) {
|
|
|
3005
3009
|
if (!source) return null;
|
|
3006
3010
|
const name = label ?? "Generate password";
|
|
3007
3011
|
return /* @__PURE__ */ jsxs14(Popover, { open, onOpenChange: setOpen, children: [
|
|
3008
|
-
/* @__PURE__ */
|
|
3009
|
-
/* @__PURE__ */
|
|
3012
|
+
/* @__PURE__ */ jsx15(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx15(Button11, { variant: "quiet", icon: /* @__PURE__ */ jsx15(KeyGlyph, {}), "aria-label": name, title: name, disabled }) }),
|
|
3013
|
+
/* @__PURE__ */ jsx15(PopoverContent, { sizing: "content", align: "end", children: open ? /* @__PURE__ */ jsx15(
|
|
3010
3014
|
PasswordGenerator,
|
|
3011
3015
|
{
|
|
3012
3016
|
limits: source,
|
|
@@ -3178,13 +3182,13 @@ function startNewRestoreRun(preview) {
|
|
|
3178
3182
|
import { useState as useState16 } from "react";
|
|
3179
3183
|
import { Checkbox } from "@ai-matrx/design-system";
|
|
3180
3184
|
import { Badge as Badge4, Button as Button12, DisclosureHeader as DisclosureHeader2 } from "@ai-matrx/design-system/controls";
|
|
3181
|
-
import { Fragment as Fragment9, jsx as
|
|
3185
|
+
import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3182
3186
|
function PickLine({ checked, onChange, disabled = false, title, detail, badge, tone }) {
|
|
3183
3187
|
return /* @__PURE__ */ jsxs15("label", { className: "flex min-h-11 items-center gap-3 px-4 py-1", "data-clickable": disabled ? void 0 : "", children: [
|
|
3184
|
-
/* @__PURE__ */
|
|
3185
|
-
/* @__PURE__ */
|
|
3186
|
-
detail ? /* @__PURE__ */
|
|
3187
|
-
badge ? /* @__PURE__ */
|
|
3188
|
+
/* @__PURE__ */ jsx16(Checkbox, { checked, disabled, onCheckedChange: (v) => onChange(v === true), "aria-label": title }),
|
|
3189
|
+
/* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }),
|
|
3190
|
+
detail ? /* @__PURE__ */ jsx16("span", { className: "hidden min-w-0 max-w-[45%] truncate text-xs text-muted-foreground sm:block", children: detail }) : null,
|
|
3191
|
+
badge ? /* @__PURE__ */ jsx16(Badge4, { tone: tone ?? "neutral", children: badge }) : null
|
|
3188
3192
|
] });
|
|
3189
3193
|
}
|
|
3190
3194
|
function Windowed({ items, size = 150, render }) {
|
|
@@ -3192,7 +3196,7 @@ function Windowed({ items, size = 150, render }) {
|
|
|
3192
3196
|
const shown = all ? items : items.slice(0, size);
|
|
3193
3197
|
return /* @__PURE__ */ jsxs15(Fragment9, { children: [
|
|
3194
3198
|
shown.map(render),
|
|
3195
|
-
items.length > shown.length ? /* @__PURE__ */
|
|
3199
|
+
items.length > shown.length ? /* @__PURE__ */ jsx16("div", { className: "px-4 py-2", children: /* @__PURE__ */ jsxs15(Button12, { variant: "quiet", onClick: () => setAll(true), children: [
|
|
3196
3200
|
"Show all ",
|
|
3197
3201
|
items.length
|
|
3198
3202
|
] }) }) : null
|
|
@@ -3201,17 +3205,17 @@ function Windowed({ items, size = 150, render }) {
|
|
|
3201
3205
|
function More({ title = "More", children }) {
|
|
3202
3206
|
const [open, setOpen] = useState16(false);
|
|
3203
3207
|
return /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-2", children: [
|
|
3204
|
-
/* @__PURE__ */
|
|
3208
|
+
/* @__PURE__ */ jsx16(DisclosureHeader2, { open, title, onClick: () => setOpen((o) => !o) }),
|
|
3205
3209
|
open ? children : null
|
|
3206
3210
|
] });
|
|
3207
3211
|
}
|
|
3208
3212
|
function ErrorLine({ message: message2 }) {
|
|
3209
|
-
return message2 ? /* @__PURE__ */
|
|
3213
|
+
return message2 ? /* @__PURE__ */ jsx16("p", { role: "alert", className: "px-1 text-sm text-destructive", children: message2 }) : null;
|
|
3210
3214
|
}
|
|
3211
3215
|
var plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
3212
3216
|
|
|
3213
3217
|
// src/io/BackupPanel.tsx
|
|
3214
|
-
import { Fragment as Fragment10, jsx as
|
|
3218
|
+
import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3215
3219
|
var RECORD_STATUS = {
|
|
3216
3220
|
created: "Restored",
|
|
3217
3221
|
replayed: "Already restored",
|
|
@@ -3224,16 +3228,16 @@ function omissionText(o) {
|
|
|
3224
3228
|
}
|
|
3225
3229
|
function Summary({ value }) {
|
|
3226
3230
|
return /* @__PURE__ */ jsxs16(VaultSection, { children: [
|
|
3227
|
-
/* @__PURE__ */
|
|
3228
|
-
/* @__PURE__ */
|
|
3229
|
-
/* @__PURE__ */
|
|
3231
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Credentials", children: value.record_count }),
|
|
3232
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Protected fields", children: value.field_count }),
|
|
3233
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Files", children: value.attachment_count > 0 ? `${value.attachment_count} \xB7 ${formatFileSize(value.attachment_bytes)}` : "0" })
|
|
3230
3234
|
] });
|
|
3231
3235
|
}
|
|
3232
3236
|
function Omissions({ omissions, accepted, onAccepted, verb }) {
|
|
3233
3237
|
if (omissions.length === 0) return null;
|
|
3234
3238
|
return /* @__PURE__ */ jsxs16(VaultSection, { title: "Left out", children: [
|
|
3235
|
-
omissions.map((o, i) => /* @__PURE__ */
|
|
3236
|
-
/* @__PURE__ */
|
|
3239
|
+
omissions.map((o, i) => /* @__PURE__ */ jsx17(VaultRow, { label: o.component.replaceAll("_", " "), children: /* @__PURE__ */ jsx17("span", { className: "block truncate text-muted-foreground", title: omissionText(o), children: omissionText(o) }) }, `${o.component}-${o.source_item_id ?? i}`)),
|
|
3240
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Accept", hint: `${verb} without these`, children: /* @__PURE__ */ jsx17(Switch3, { checked: accepted, onCheckedChange: onAccepted, "aria-label": `${verb} without these` }) })
|
|
3237
3241
|
] });
|
|
3238
3242
|
}
|
|
3239
3243
|
function BackupPanel({ port, items, onClose, onChanged }) {
|
|
@@ -3340,20 +3344,20 @@ function BackupPanel({ port, items, onClose, onChanged }) {
|
|
|
3340
3344
|
const mismatch = repeat !== "" && repeat !== passphrase;
|
|
3341
3345
|
const names = new Map(restorePreview?.records.map((r) => [r.source_item_id, r.display_name]) ?? []);
|
|
3342
3346
|
const footer = mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3343
|
-
/* @__PURE__ */
|
|
3344
|
-
backup ? /* @__PURE__ */
|
|
3347
|
+
/* @__PURE__ */ jsx17(Button13, { variant: "quiet", onClick: onClose, children: "Close" }),
|
|
3348
|
+
backup ? /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy || !passphrase || passphrase !== repeat || backup.omissions.length > 0 && !accepted, onClick: () => void downloadBackup(), children: "Encrypt and download" }) : /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void reviewBackup(), children: [
|
|
3345
3349
|
"Review ",
|
|
3346
3350
|
selected.size
|
|
3347
3351
|
] })
|
|
3348
3352
|
] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3349
|
-
/* @__PURE__ */
|
|
3350
|
-
result ? retryable ? /* @__PURE__ */
|
|
3353
|
+
/* @__PURE__ */ jsx17(Button13, { variant: "quiet", onClick: onClose, children: result && !retryable ? "Done" : "Close" }),
|
|
3354
|
+
result ? retryable ? /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy, onClick: () => void restore(), children: "Try again" }) : /* @__PURE__ */ jsx17(Button13, { variant: "outline", disabled: busy, onClick: () => void again(), children: "Restore again" }) : restorePreview ? /* @__PURE__ */ jsxs16(Button13, { variant: "primary", disabled: busy || restorePreview.omissions.length > 0 && !accepted, onClick: () => void restore(), children: [
|
|
3351
3355
|
"Restore ",
|
|
3352
3356
|
restorePreview.record_count
|
|
3353
|
-
] }) : /* @__PURE__ */
|
|
3357
|
+
] }) : /* @__PURE__ */ jsx17(Button13, { variant: "primary", disabled: busy || !file || !passphrase, onClick: () => void reviewRestore(), children: "Review" })
|
|
3354
3358
|
] });
|
|
3355
3359
|
return /* @__PURE__ */ jsxs16(Panel, { title: "Backup", size: "lg", onClose, footer, children: [
|
|
3356
|
-
/* @__PURE__ */
|
|
3360
|
+
/* @__PURE__ */ jsx17(
|
|
3357
3361
|
SegmentedControl3,
|
|
3358
3362
|
{
|
|
3359
3363
|
value: mode,
|
|
@@ -3367,35 +3371,35 @@ function BackupPanel({ port, items, onClose, onChanged }) {
|
|
|
3367
3371
|
}
|
|
3368
3372
|
),
|
|
3369
3373
|
mode === "backup" ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3370
|
-
/* @__PURE__ */
|
|
3374
|
+
/* @__PURE__ */ jsx17(
|
|
3371
3375
|
VaultSection,
|
|
3372
3376
|
{
|
|
3373
3377
|
title: `${selected.size} of ${plural(items.length, "credential")}`,
|
|
3374
3378
|
actions: /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3375
|
-
/* @__PURE__ */
|
|
3379
|
+
/* @__PURE__ */ jsx17(Button13, { variant: "quiet", disabled: busy || items.length === 0, onClick: () => {
|
|
3376
3380
|
setBackup(null);
|
|
3377
3381
|
setSelected(new Set(items.map((i) => i.id)));
|
|
3378
3382
|
}, children: "All" }),
|
|
3379
|
-
/* @__PURE__ */
|
|
3383
|
+
/* @__PURE__ */ jsx17(Button13, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
|
|
3380
3384
|
setBackup(null);
|
|
3381
3385
|
setSelected(/* @__PURE__ */ new Set());
|
|
3382
3386
|
}, children: "None" })
|
|
3383
3387
|
] }),
|
|
3384
|
-
children: items.length === 0 ? /* @__PURE__ */
|
|
3388
|
+
children: items.length === 0 ? /* @__PURE__ */ jsx17(Empty, { children: "Nothing to back up yet." }) : /* @__PURE__ */ jsx17(Windowed, { items, render: (i) => /* @__PURE__ */ jsx17(PickLine, { title: i.name, detail: i.definitionLabel, checked: selected.has(i.id), disabled: busy, onChange: (on) => pick(i.id, on) }, i.id) })
|
|
3385
3389
|
}
|
|
3386
3390
|
),
|
|
3387
3391
|
backup ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3388
|
-
/* @__PURE__ */
|
|
3389
|
-
/* @__PURE__ */
|
|
3392
|
+
/* @__PURE__ */ jsx17(Summary, { value: backup }),
|
|
3393
|
+
/* @__PURE__ */ jsx17(Omissions, { omissions: backup.omissions, accepted, onAccepted: setAccepted, verb: "Back up" }),
|
|
3390
3394
|
/* @__PURE__ */ jsxs16(VaultSection, { children: [
|
|
3391
|
-
/* @__PURE__ */
|
|
3392
|
-
/* @__PURE__ */
|
|
3395
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Passphrase", hint: "Not saved. It can't be recovered.", children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: passphrase, autoComplete: "new-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) }),
|
|
3396
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Repeat", ...mismatch ? { hint: "Doesn't match" } : {}, children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: repeat, autoComplete: "new-password", "aria-label": "Repeat the passphrase", onChange: (e) => setRepeat(e.target.value) }) })
|
|
3393
3397
|
] })
|
|
3394
3398
|
] }) : null
|
|
3395
|
-
] }) : /* @__PURE__ */
|
|
3399
|
+
] }) : /* @__PURE__ */ jsx17(Fragment10, { children: !restorePreview ? /* @__PURE__ */ jsxs16(VaultSection, { children: [
|
|
3396
3400
|
/* @__PURE__ */ jsxs16(VaultRow, { label: "File", ...file ? { hint: file.name } : {}, children: [
|
|
3397
|
-
/* @__PURE__ */
|
|
3398
|
-
/* @__PURE__ */
|
|
3401
|
+
/* @__PURE__ */ jsx17(Button13, { variant: "outline", disabled: busy, onClick: () => fileInput.current?.click(), children: "Choose a backup" }),
|
|
3402
|
+
/* @__PURE__ */ jsx17(
|
|
3399
3403
|
"input",
|
|
3400
3404
|
{
|
|
3401
3405
|
ref: fileInput,
|
|
@@ -3409,16 +3413,16 @@ function BackupPanel({ port, items, onClose, onChanged }) {
|
|
|
3409
3413
|
}
|
|
3410
3414
|
)
|
|
3411
3415
|
] }),
|
|
3412
|
-
/* @__PURE__ */
|
|
3416
|
+
/* @__PURE__ */ jsx17(VaultRow, { label: "Passphrase", children: /* @__PURE__ */ jsx17(Field6, { type: "password", value: passphrase, autoComplete: "current-password", "aria-label": "Backup passphrase", onChange: (e) => setPassphrase(e.target.value) }) })
|
|
3413
3417
|
] }) : /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
3414
|
-
/* @__PURE__ */
|
|
3415
|
-
!result ? /* @__PURE__ */
|
|
3416
|
-
!result ? /* @__PURE__ */
|
|
3418
|
+
/* @__PURE__ */ jsx17(Summary, { value: restorePreview }),
|
|
3419
|
+
!result ? /* @__PURE__ */ jsx17(Omissions, { omissions: restorePreview.omissions, accepted, onAccepted: setAccepted, verb: "Restore" }) : null,
|
|
3420
|
+
!result ? /* @__PURE__ */ jsx17("p", { className: "px-1 text-xs text-muted-foreground", children: "Restored items start disabled and restricted." }) : /* @__PURE__ */ jsx17(VaultSection, { title: `${result.created} restored \xB7 ${result.replayed} already \xB7 ${result.refused} refused \xB7 ${result.retryable_failure} to retry`, children: result.results.map((r) => /* @__PURE__ */ jsx17(VaultRow, { label: names.get(r.source_item_id) ?? "Credential", children: /* @__PURE__ */ jsxs16("span", { className: r.status === "refused" || r.status === "retryable_failure" ? "text-destructive" : "text-foreground", children: [
|
|
3417
3421
|
RECORD_STATUS[r.status],
|
|
3418
3422
|
r.reason === "alias_conflict" ? " \xB7 name in use" : ""
|
|
3419
3423
|
] }) }, r.source_item_id)) })
|
|
3420
3424
|
] }) }),
|
|
3421
|
-
/* @__PURE__ */
|
|
3425
|
+
/* @__PURE__ */ jsx17(ErrorLine, { message: error })
|
|
3422
3426
|
] });
|
|
3423
3427
|
}
|
|
3424
3428
|
|
|
@@ -3427,7 +3431,7 @@ import { useMemo, useState as useState18 } from "react";
|
|
|
3427
3431
|
import { Button as Button14, Select as Select5 } from "@ai-matrx/design-system/controls";
|
|
3428
3432
|
import { downloadFile as downloadFile2 } from "@ai-matrx/kit/download";
|
|
3429
3433
|
import { confirm as confirm5 } from "@ai-matrx/kit/confirm-opener";
|
|
3430
|
-
import { Fragment as Fragment11, jsx as
|
|
3434
|
+
import { Fragment as Fragment11, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3431
3435
|
var FORMATS = [
|
|
3432
3436
|
{ value: "matrx_login_csv_v1", label: "Matrx", hint: "Website logins, all common fields" },
|
|
3433
3437
|
{ value: "firefox_csv_v1", label: "Firefox", hint: "Address, username and password" },
|
|
@@ -3508,8 +3512,8 @@ function ExportPanel({ port, items, onClose }) {
|
|
|
3508
3512
|
size: "lg",
|
|
3509
3513
|
onClose,
|
|
3510
3514
|
footer: /* @__PURE__ */ jsxs17(Fragment11, { children: [
|
|
3511
|
-
/* @__PURE__ */
|
|
3512
|
-
preview ? /* @__PURE__ */ jsxs17(Button14, { variant: "primary", icon: /* @__PURE__ */
|
|
3515
|
+
/* @__PURE__ */ jsx18(Button14, { variant: "quiet", onClick: onClose, children: "Close" }),
|
|
3516
|
+
preview ? /* @__PURE__ */ jsxs17(Button14, { variant: "primary", icon: /* @__PURE__ */ jsx18(ExportGlyph, { className: "size-4" }), disabled: busy || eligible === 0, onClick: () => void download(), children: [
|
|
3513
3517
|
"Download ",
|
|
3514
3518
|
eligible
|
|
3515
3519
|
] }) : /* @__PURE__ */ jsxs17(Button14, { variant: "primary", disabled: busy || selected.size === 0, onClick: () => void review(), children: [
|
|
@@ -3518,7 +3522,7 @@ function ExportPanel({ port, items, onClose }) {
|
|
|
3518
3522
|
] })
|
|
3519
3523
|
] }),
|
|
3520
3524
|
children: [
|
|
3521
|
-
/* @__PURE__ */
|
|
3525
|
+
/* @__PURE__ */ jsx18(VaultSection, { children: /* @__PURE__ */ jsx18(VaultRow, { label: "Format", hint: format?.hint, children: /* @__PURE__ */ jsx18(
|
|
3522
3526
|
Select5,
|
|
3523
3527
|
{
|
|
3524
3528
|
value: profile,
|
|
@@ -3530,21 +3534,21 @@ function ExportPanel({ port, items, onClose }) {
|
|
|
3530
3534
|
}
|
|
3531
3535
|
}
|
|
3532
3536
|
) }) }),
|
|
3533
|
-
/* @__PURE__ */
|
|
3537
|
+
/* @__PURE__ */ jsx18(
|
|
3534
3538
|
VaultSection,
|
|
3535
3539
|
{
|
|
3536
3540
|
title: `${selected.size} of ${plural(logins.length, "login")}`,
|
|
3537
3541
|
actions: /* @__PURE__ */ jsxs17(Fragment11, { children: [
|
|
3538
|
-
/* @__PURE__ */
|
|
3542
|
+
/* @__PURE__ */ jsx18(Button14, { variant: "quiet", disabled: busy || logins.length === 0, onClick: () => {
|
|
3539
3543
|
changed();
|
|
3540
3544
|
setSelected(new Set(logins.map((l) => l.id)));
|
|
3541
3545
|
}, children: "All" }),
|
|
3542
|
-
/* @__PURE__ */
|
|
3546
|
+
/* @__PURE__ */ jsx18(Button14, { variant: "quiet", disabled: busy || selected.size === 0, onClick: () => {
|
|
3543
3547
|
changed();
|
|
3544
3548
|
setSelected(/* @__PURE__ */ new Set());
|
|
3545
3549
|
}, children: "None" })
|
|
3546
3550
|
] }),
|
|
3547
|
-
children: logins.length === 0 ? /* @__PURE__ */
|
|
3551
|
+
children: logins.length === 0 ? /* @__PURE__ */ jsx18(Empty, { children: "No website logins to export." }) : /* @__PURE__ */ jsx18(
|
|
3548
3552
|
Windowed,
|
|
3549
3553
|
{
|
|
3550
3554
|
items: logins,
|
|
@@ -3552,7 +3556,7 @@ function ExportPanel({ port, items, onClose }) {
|
|
|
3552
3556
|
const p = reasons.get(item.id);
|
|
3553
3557
|
const left = p && !p.eligible;
|
|
3554
3558
|
const omitted = p ? omissionText2(p.omissions) : "";
|
|
3555
|
-
return /* @__PURE__ */
|
|
3559
|
+
return /* @__PURE__ */ jsx18(
|
|
3556
3560
|
PickLine,
|
|
3557
3561
|
{
|
|
3558
3562
|
title: item.name,
|
|
@@ -3570,7 +3574,7 @@ function ExportPanel({ port, items, onClose }) {
|
|
|
3570
3574
|
)
|
|
3571
3575
|
}
|
|
3572
3576
|
),
|
|
3573
|
-
/* @__PURE__ */
|
|
3577
|
+
/* @__PURE__ */ jsx18(ErrorLine, { message: error })
|
|
3574
3578
|
]
|
|
3575
3579
|
}
|
|
3576
3580
|
);
|
|
@@ -4088,7 +4092,7 @@ import { BlobReader as BlobReader2, ZipReader as ZipReader2 } from "@zip.js/zip.
|
|
|
4088
4092
|
var safe = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
4089
4093
|
var unsafeName = (name) => /[\\\0-\x1F\x7F-\x9F]/.test(name);
|
|
4090
4094
|
function stopIfCancelled(signal) {
|
|
4091
|
-
if (signal?.aborted) throw new Error("The archive import was
|
|
4095
|
+
if (signal?.aborted) throw new Error("The archive import was canceled.");
|
|
4092
4096
|
}
|
|
4093
4097
|
function hasWarnings(reader, entry) {
|
|
4094
4098
|
return Boolean(reader.warnings?.length || entry.warnings?.length);
|
|
@@ -4189,7 +4193,7 @@ async function openBoundedZip(file, limits, signal) {
|
|
|
4189
4193
|
};
|
|
4190
4194
|
} catch (error) {
|
|
4191
4195
|
await reader.close();
|
|
4192
|
-
if (signal?.aborted) throw new Error("The archive import was
|
|
4196
|
+
if (signal?.aborted) throw new Error("The archive import was canceled.");
|
|
4193
4197
|
throw error;
|
|
4194
4198
|
}
|
|
4195
4199
|
}
|
|
@@ -4228,7 +4232,7 @@ async function readDashlaneCsvArchive(file, limits, signal) {
|
|
|
4228
4232
|
return { credentialsText, omittedCsvCount };
|
|
4229
4233
|
} catch (error) {
|
|
4230
4234
|
credentialsText = void 0;
|
|
4231
|
-
if (signal?.aborted) throw new Error("The Dashlane import was
|
|
4235
|
+
if (signal?.aborted) throw new Error("The Dashlane import was canceled.");
|
|
4232
4236
|
throw error;
|
|
4233
4237
|
} finally {
|
|
4234
4238
|
credentialsText = void 0;
|
|
@@ -4363,7 +4367,7 @@ function hasValidStructuredImportFileNotices(value, maxCount) {
|
|
|
4363
4367
|
}
|
|
4364
4368
|
|
|
4365
4369
|
// src/io/parse-source.ts
|
|
4366
|
-
var cancelled = () => new Error("The import was
|
|
4370
|
+
var cancelled = () => new Error("The import was canceled.");
|
|
4367
4371
|
function parseStructured(source, file, limits, signal) {
|
|
4368
4372
|
const descriptor = structuredImportSource(source);
|
|
4369
4373
|
if (!descriptor) return Promise.reject(new Error("This kind of file cannot be imported."));
|
|
@@ -4416,7 +4420,7 @@ async function parseImportFile(source, file, limits, signal) {
|
|
|
4416
4420
|
}
|
|
4417
4421
|
|
|
4418
4422
|
// src/io/ImportPanel.tsx
|
|
4419
|
-
import { Fragment as Fragment12, jsx as
|
|
4423
|
+
import { Fragment as Fragment12, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
4420
4424
|
var ROLE_OPTIONS = [
|
|
4421
4425
|
{ value: "keep", label: "Keep encrypted" },
|
|
4422
4426
|
{ value: "title", label: "Name" },
|
|
@@ -4592,25 +4596,25 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4592
4596
|
const stage = done ? "done" : draft ? "review" : "choose";
|
|
4593
4597
|
const busy = running !== null || reading;
|
|
4594
4598
|
const footer = stage === "choose" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
|
|
4595
|
-
/* @__PURE__ */
|
|
4596
|
-
source === "env" ? /* @__PURE__ */
|
|
4599
|
+
/* @__PURE__ */ jsx19(Button15, { variant: "quiet", onClick: onClose, children: "Close" }),
|
|
4600
|
+
source === "env" ? /* @__PURE__ */ jsx19(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx19(ImportGlyph, { className: "size-4" }), disabled: !envText.trim(), onClick: () => reviewEnv(envText), children: "Review" }) : null
|
|
4597
4601
|
] }) : stage === "review" ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
|
|
4598
|
-
running ? /* @__PURE__ */
|
|
4599
|
-
/* @__PURE__ */
|
|
4602
|
+
running ? /* @__PURE__ */ jsx19(Button15, { variant: "outline", onClick: () => stop.current = true, children: "Stop" }) : /* @__PURE__ */ jsx19(Button15, { variant: "quiet", onClick: reset, children: "Back" }),
|
|
4603
|
+
/* @__PURE__ */ jsx19(Button15, { variant: "primary", icon: /* @__PURE__ */ jsx19(ImportGlyph, { className: "size-4" }), disabled: busy || chosen === 0 || ambiguous === true || needsApproval, onClick: start, children: running ? `Importing ${running.done} of ${running.total}` : `Import ${chosen}` })
|
|
4600
4604
|
] }) : /* @__PURE__ */ jsxs18(Fragment12, { children: [
|
|
4601
|
-
done && done.failed.length + done.untried.length > 0 ? /* @__PURE__ */
|
|
4602
|
-
/* @__PURE__ */
|
|
4605
|
+
done && done.failed.length + done.untried.length > 0 ? /* @__PURE__ */ jsx19(Button15, { variant: "outline", disabled: busy, onClick: retry, children: running ? `Importing ${running.done} of ${running.total}` : `Try ${done.failed.length + done.untried.length} again` }) : null,
|
|
4606
|
+
/* @__PURE__ */ jsx19(Button15, { variant: "primary", onClick: onClose, children: "Done" })
|
|
4603
4607
|
] });
|
|
4604
4608
|
return /* @__PURE__ */ jsxs18(Panel, { title: "Import", size: "xl", onClose, footer, children: [
|
|
4605
4609
|
stage === "choose" ? /* @__PURE__ */ jsxs18(VaultSection, { children: [
|
|
4606
|
-
/* @__PURE__ */
|
|
4610
|
+
/* @__PURE__ */ jsx19(VaultRow, { label: "Source", actions: help && host.openUrl ? /* @__PURE__ */ jsx19(Button15, { variant: "quiet", onClick: () => host.openUrl?.(help), children: "How to export" }) : void 0, children: /* @__PURE__ */ jsx19(Select6, { value: source, options: SOURCE_OPTIONS, "aria-label": "Source", onValueChange: (s) => {
|
|
4607
4611
|
setSource(s);
|
|
4608
4612
|
setError(null);
|
|
4609
4613
|
} }) }),
|
|
4610
|
-
source === "env" ? /* @__PURE__ */
|
|
4614
|
+
source === "env" ? /* @__PURE__ */ jsx19(VaultRow, { label: ".env text", hint: "Same key updates in place", top: true, children: /* @__PURE__ */ jsx19(Textarea2, { mono: true, rows: 7, value: envText, autoComplete: "off", spellCheck: false, "aria-label": ".env text", placeholder: "OPENAI_API_KEY=sk-...\nDATABASE_URL=postgres://...", onChange: (e) => setEnvText(e.target.value) }) }) : null,
|
|
4611
4615
|
/* @__PURE__ */ jsxs18(VaultRow, { label: "File", hint: fileName ?? void 0, children: [
|
|
4612
|
-
/* @__PURE__ */
|
|
4613
|
-
/* @__PURE__ */
|
|
4616
|
+
/* @__PURE__ */ jsx19(Button15, { variant: "outline", disabled: reading, onClick: () => fileInput.current?.click(), children: reading ? "Reading\u2026" : source === "env" ? "Load a file" : "Choose a file" }),
|
|
4617
|
+
/* @__PURE__ */ jsx19(
|
|
4614
4618
|
"input",
|
|
4615
4619
|
{
|
|
4616
4620
|
ref: fileInput,
|
|
@@ -4625,44 +4629,44 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4625
4629
|
}
|
|
4626
4630
|
)
|
|
4627
4631
|
] }),
|
|
4628
|
-
source === "env" ? /* @__PURE__ */
|
|
4632
|
+
source === "env" ? /* @__PURE__ */ jsx19(VaultRow, { label: "Sandboxes", hint: "Pass these values to agent sandboxes", children: /* @__PURE__ */ jsx19(Switch4, { checked: injectSandbox, onCheckedChange: setInjectSandbox, "aria-label": "Use in sandboxes" }) }) : null
|
|
4629
4633
|
] }) : null,
|
|
4630
4634
|
stage === "review" && draft ? /* @__PURE__ */ jsxs18(Fragment12, { children: [
|
|
4631
|
-
organizations.length > 0 ? /* @__PURE__ */
|
|
4632
|
-
/* @__PURE__ */
|
|
4635
|
+
organizations.length > 0 ? /* @__PURE__ */ jsx19(VaultSection, { children: /* @__PURE__ */ jsx19(VaultRow, { label: "Save to", children: /* @__PURE__ */ jsx19(Select6, { value: destination, options: destinationOptions, "aria-label": "Save to", onValueChange: (v) => draft.kind === "file" ? restart({ destination: v }) : setDestination(v) }) }) }) : null,
|
|
4636
|
+
/* @__PURE__ */ jsx19(
|
|
4633
4637
|
VaultSection,
|
|
4634
4638
|
{
|
|
4635
4639
|
title: draft.kind === "env" ? plural(envKeys.length, "value") : summary(counts, lines.length),
|
|
4636
4640
|
actions: /* @__PURE__ */ jsxs18(Fragment12, { children: [
|
|
4637
|
-
/* @__PURE__ */
|
|
4638
|
-
/* @__PURE__ */
|
|
4641
|
+
/* @__PURE__ */ jsx19(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(draft.kind === "env" ? new Set(envKeys.map((e) => e.key + e.text)) : defaultSelection(lines)), children: "Reset" }),
|
|
4642
|
+
/* @__PURE__ */ jsx19(Button15, { variant: "quiet", disabled: busy, onClick: () => setSelected(/* @__PURE__ */ new Set()), children: "None" })
|
|
4639
4643
|
] }),
|
|
4640
|
-
children: draft.kind === "env" ? /* @__PURE__ */
|
|
4644
|
+
children: draft.kind === "env" ? /* @__PURE__ */ jsx19(
|
|
4641
4645
|
Windowed,
|
|
4642
4646
|
{
|
|
4643
4647
|
items: envKeys,
|
|
4644
4648
|
render: (e) => {
|
|
4645
4649
|
const id = e.key + e.text;
|
|
4646
|
-
return /* @__PURE__ */
|
|
4650
|
+
return /* @__PURE__ */ jsx19(PickLine, { title: e.key, detail: "Value hidden", checked: selected.has(id), disabled: busy, onChange: (on) => toggle(setSelected, id, on) }, id);
|
|
4647
4651
|
}
|
|
4648
4652
|
}
|
|
4649
|
-
) : lines.length === 0 ? /* @__PURE__ */
|
|
4653
|
+
) : lines.length === 0 ? /* @__PURE__ */ jsx19(Empty, { children: "Nothing to import in this file." }) : /* @__PURE__ */ jsx19(
|
|
4650
4654
|
Windowed,
|
|
4651
4655
|
{
|
|
4652
4656
|
items: lines,
|
|
4653
4657
|
render: (l) => {
|
|
4654
4658
|
const badge = STATE_BADGE[l.state];
|
|
4655
|
-
return /* @__PURE__ */
|
|
4659
|
+
return /* @__PURE__ */ jsx19(PickLine, { title: l.title, detail: l.reason ?? l.detail, checked: selected.has(l.key), disabled: busy || !selectable(l), badge: badge?.text, tone: badge?.tone, onChange: (on) => toggle(setSelected, l.key, on) }, l.key);
|
|
4656
4660
|
}
|
|
4657
4661
|
}
|
|
4658
4662
|
)
|
|
4659
4663
|
}
|
|
4660
4664
|
),
|
|
4661
|
-
draft.kind === "file" && draft.parsed.kind === "structured" ? /* @__PURE__ */
|
|
4662
|
-
draft.kind === "file" ? /* @__PURE__ */
|
|
4663
|
-
draft.kind === "file" ? /* @__PURE__ */
|
|
4664
|
-
/* @__PURE__ */
|
|
4665
|
-
draft.parsed.kind === "csv" ? draft.parsed.preview.headers.map((header, index) => /* @__PURE__ */
|
|
4665
|
+
draft.kind === "file" && draft.parsed.kind === "structured" ? /* @__PURE__ */ jsx19(VaultSection, { children: /* @__PURE__ */ jsx19(VaultRow, { label: "Visible details", hint: "Approve saving websites and public keys in the clear", children: /* @__PURE__ */ jsx19(Switch4, { checked: approvedVisible, disabled: busy, onCheckedChange: setApprovedVisible, "aria-label": "Approve visible details" }) }) }) : null,
|
|
4666
|
+
draft.kind === "file" ? /* @__PURE__ */ jsx19(Notices, { draft }) : null,
|
|
4667
|
+
draft.kind === "file" ? /* @__PURE__ */ jsx19(More, { children: /* @__PURE__ */ jsxs18(VaultSection, { children: [
|
|
4668
|
+
/* @__PURE__ */ jsx19(VaultRow, { label: "Browser fill", hint: "Needs username, password and an https address", children: /* @__PURE__ */ jsx19(Switch4, { checked: browserFill, disabled: busy, onCheckedChange: (on) => restart({ browserFill: on }), "aria-label": "Browser fill" }) }),
|
|
4669
|
+
draft.parsed.kind === "csv" ? draft.parsed.preview.headers.map((header, index) => /* @__PURE__ */ jsx19(VaultRow, { label: header || `Column ${index + 1}`, children: /* @__PURE__ */ jsx19(
|
|
4666
4670
|
Select6,
|
|
4667
4671
|
{
|
|
4668
4672
|
value: draft.mapping[index] ?? "keep",
|
|
@@ -4672,20 +4676,20 @@ function ImportPanel({ port, host, items, organizations, onClose, onChanged, lim
|
|
|
4672
4676
|
}
|
|
4673
4677
|
) }, `${header}-${index}`)) : null
|
|
4674
4678
|
] }) }) : null,
|
|
4675
|
-
ambiguous ? /* @__PURE__ */
|
|
4679
|
+
ambiguous ? /* @__PURE__ */ jsx19(ErrorLine, { message: "Each of name, username, password, notes and one-time password can be used by one column." }) : null
|
|
4676
4680
|
] }) : null,
|
|
4677
4681
|
stage === "done" && done ? /* @__PURE__ */ jsxs18(VaultSection, { title: "Result", children: [
|
|
4678
|
-
/* @__PURE__ */
|
|
4679
|
-
/* @__PURE__ */
|
|
4680
|
-
/* @__PURE__ */
|
|
4681
|
-
done.untried.length > 0 ? /* @__PURE__ */
|
|
4682
|
-
done.withOtp > 0 ? /* @__PURE__ */
|
|
4682
|
+
/* @__PURE__ */ jsx19(VaultRow, { label: "Created", children: /* @__PURE__ */ jsx19("span", { className: "text-foreground", children: done.created.length }) }),
|
|
4683
|
+
/* @__PURE__ */ jsx19(VaultRow, { label: "Skipped", children: /* @__PURE__ */ jsx19("span", { className: "text-foreground", children: done.skipped }) }),
|
|
4684
|
+
/* @__PURE__ */ jsx19(VaultRow, { label: "Failed", children: /* @__PURE__ */ jsx19("span", { className: done.failed.length > 0 ? "text-destructive" : "text-foreground", children: done.failed.length }) }),
|
|
4685
|
+
done.untried.length > 0 ? /* @__PURE__ */ jsx19(VaultRow, { label: "Not tried", children: /* @__PURE__ */ jsx19("span", { className: "text-foreground", children: done.untried.length }) }) : null,
|
|
4686
|
+
done.withOtp > 0 ? /* @__PURE__ */ jsx19(VaultRow, { label: "Codes", children: /* @__PURE__ */ jsxs18("span", { className: "text-muted-foreground", children: [
|
|
4683
4687
|
plural(done.withOtp, "login"),
|
|
4684
4688
|
" still need the one-time password added."
|
|
4685
4689
|
] }) }) : null,
|
|
4686
|
-
done.failed.map((f) => /* @__PURE__ */
|
|
4690
|
+
done.failed.map((f) => /* @__PURE__ */ jsx19(VaultRow, { label: f.title, children: /* @__PURE__ */ jsx19("span", { className: "block truncate text-destructive", title: f.reason, children: f.reason }) }, f.key))
|
|
4687
4691
|
] }) : null,
|
|
4688
|
-
/* @__PURE__ */
|
|
4692
|
+
/* @__PURE__ */ jsx19(ErrorLine, { message: error })
|
|
4689
4693
|
] });
|
|
4690
4694
|
}
|
|
4691
4695
|
function toggle(set, key, on) {
|
|
@@ -4712,7 +4716,7 @@ function Notices({ draft }) {
|
|
|
4712
4716
|
}
|
|
4713
4717
|
}
|
|
4714
4718
|
if (notes.length === 0) return null;
|
|
4715
|
-
return /* @__PURE__ */
|
|
4719
|
+
return /* @__PURE__ */ jsx19("div", { className: "flex flex-col gap-0.5 px-1 text-xs text-muted-foreground", children: notes.map((n) => /* @__PURE__ */ jsx19("span", { children: n }, n)) });
|
|
4716
4720
|
}
|
|
4717
4721
|
|
|
4718
4722
|
// src/ItemList.tsx
|
|
@@ -4721,20 +4725,6 @@ import { DropdownMenu as DropdownMenu4, DropdownMenuContent as DropdownMenuConte
|
|
|
4721
4725
|
import { cn as cn13 } from "@ai-matrx/design-system";
|
|
4722
4726
|
import { Button as Button22, SearchField } from "@ai-matrx/design-system/controls";
|
|
4723
4727
|
|
|
4724
|
-
// src/ItemIcon.tsx
|
|
4725
|
-
import { cn as cn8 } from "@ai-matrx/design-system";
|
|
4726
|
-
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
4727
|
-
function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }) {
|
|
4728
|
-
const box = size === "lg" ? "size-10 rounded-xl text-base" : size === "md" ? "size-8 rounded-lg text-sm" : "size-6 rounded-md text-xs";
|
|
4729
|
-
const tone = inverted ? "bg-primary-foreground text-primary" : "bg-muted text-foreground";
|
|
4730
|
-
const initial = siteDomainOf(site).charAt(0).toUpperCase();
|
|
4731
|
-
if (initial && /[A-Z0-9]/.test(initial)) {
|
|
4732
|
-
return /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn8("flex shrink-0 items-center justify-center font-semibold", box, tone), children: initial });
|
|
4733
|
-
}
|
|
4734
|
-
const Glyph = definitionGlyph(definitionKey, family);
|
|
4735
|
-
return /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn8("flex shrink-0 items-center justify-center", box, tone), children: /* @__PURE__ */ jsx19(Glyph, { className: size === "lg" ? "size-5" : "size-4" }) });
|
|
4736
|
-
}
|
|
4737
|
-
|
|
4738
4728
|
// src/ItemPane.tsx
|
|
4739
4729
|
import { useEffect as useEffect16, useMemo as useMemo3, useRef as useRef14, useState as useState26 } from "react";
|
|
4740
4730
|
import { DropdownMenu as DropdownMenu3, DropdownMenuContent as DropdownMenuContent3, DropdownMenuItem as DropdownMenuItem3, DropdownMenuSeparator as DropdownMenuSeparator3, DropdownMenuTrigger as DropdownMenuTrigger3 } from "@ai-matrx/design-system";
|
|
@@ -5657,6 +5647,8 @@ function ItemPane(props) {
|
|
|
5657
5647
|
const [tagsText, setTagsText] = useState26(base.tags.join(", "));
|
|
5658
5648
|
const [showTags, setShowTags] = useState26(false);
|
|
5659
5649
|
const [showDescription, setShowDescription] = useState26(false);
|
|
5650
|
+
const [showRecovery, setShowRecovery] = useState26(false);
|
|
5651
|
+
const [settingUpOtp, setSettingUpOtp] = useState26(false);
|
|
5660
5652
|
const [owner, setOwner] = useState26(defaultOwner ?? "me");
|
|
5661
5653
|
const [recipient, setRecipient] = useState26("");
|
|
5662
5654
|
const [generatePrivately, setGeneratePrivately] = useState26(false);
|
|
@@ -5685,6 +5677,7 @@ function ItemPane(props) {
|
|
|
5685
5677
|
setLoaded({});
|
|
5686
5678
|
setShowTags(false);
|
|
5687
5679
|
setShowDescription(false);
|
|
5680
|
+
setShowRecovery(false);
|
|
5688
5681
|
}, [item?.id, mode]);
|
|
5689
5682
|
useEffect16(() => onDirty(dirty), [dirty, onDirty]);
|
|
5690
5683
|
const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
|
|
@@ -5712,23 +5705,24 @@ function ItemPane(props) {
|
|
|
5712
5705
|
setBusy(true);
|
|
5713
5706
|
setError(null);
|
|
5714
5707
|
const out = { ...draft, values: Object.fromEntries(changedValues(base, draft)) };
|
|
5708
|
+
if (out.values[RECOVERY_CODES_KEY] !== void 0) out.values[RECOVERY_CODES_KEY] = parseRecoveryCodes(out.values[RECOVERY_CODES_KEY]).join("\n");
|
|
5715
5709
|
const withOtp = async (saved, notice2) => {
|
|
5716
5710
|
const setup = out.authenticator.trim();
|
|
5717
|
-
if (!setup) return [saved, notice2];
|
|
5711
|
+
if (!setup) return [saved, notice2, false];
|
|
5718
5712
|
try {
|
|
5719
5713
|
await confirmed(() => port.authenticator.enrol(saved, setup));
|
|
5720
|
-
return [await port.get(saved), notice2];
|
|
5714
|
+
return [await port.get(saved), notice2, true];
|
|
5721
5715
|
} catch (e) {
|
|
5722
5716
|
const why = wasCancelled(e) ? "it was not confirmed" : e instanceof Error ? e.message : "it was refused";
|
|
5723
|
-
return [saved, `Saved, but the
|
|
5717
|
+
return [saved, `Saved, but the authenticator was not set up: ${why}. Add it with Edit.`, false];
|
|
5724
5718
|
}
|
|
5725
5719
|
};
|
|
5726
5720
|
try {
|
|
5727
5721
|
if (item) {
|
|
5728
5722
|
const r = await port.save(item, definition, out);
|
|
5729
|
-
const [next, n] = await withOtp(r.item, r.notice);
|
|
5723
|
+
const [next, n, enrolled] = await withOtp(r.item, r.notice);
|
|
5730
5724
|
dropKeptDraft(keepKey);
|
|
5731
|
-
onSaved(next, false, n);
|
|
5725
|
+
onSaved(next, false, n, enrolled);
|
|
5732
5726
|
} else if (definition && owner === "person") {
|
|
5733
5727
|
const secretKey = declared.find((f) => f.storage === "encrypted" && f.kind === "secret")?.key ?? null;
|
|
5734
5728
|
const r = await port.sharing.assign({ recipientEmail: recipient.trim(), definition, draft: out, generateFieldKey: generatePrivately ? secretKey : null });
|
|
@@ -5736,9 +5730,9 @@ function ItemPane(props) {
|
|
|
5736
5730
|
onCancelNew();
|
|
5737
5731
|
} else if (definition) {
|
|
5738
5732
|
const r = await port.create(definition, owner === "me" ? null : owner, out);
|
|
5739
|
-
const [next, n] = await withOtp(r.item, r.notice);
|
|
5733
|
+
const [next, n, enrolled] = await withOtp(r.item, r.notice);
|
|
5740
5734
|
dropKeptDraft(keepKey);
|
|
5741
|
-
onSaved(next, true, n);
|
|
5735
|
+
onSaved(next, true, n, enrolled);
|
|
5742
5736
|
}
|
|
5743
5737
|
} catch (e) {
|
|
5744
5738
|
setError(e instanceof Error ? e.message : "Saving failed. Nothing was changed.");
|
|
@@ -5797,6 +5791,10 @@ function ItemPane(props) {
|
|
|
5797
5791
|
const viewCustom = (item?.custom ?? []).filter((c) => !declaredKeys.has(c.key) && c.key !== MFA_METHOD_KEY && !isAuthenticatorStorage(c.key));
|
|
5798
5792
|
const isLogin = (item?.definitionKey ?? definition?.key) === "website_login" || (item?.definitionKey ?? definition?.key) === "username_password";
|
|
5799
5793
|
const typeLabel = definition?.label ?? item?.definitionLabel ?? "";
|
|
5794
|
+
const recoveryDeclared = declaredKeys.has(RECOVERY_CODES_KEY);
|
|
5795
|
+
const recoveryExtra = extraFields.some(isRecoveryCodesField);
|
|
5796
|
+
const offerRecovery = isLogin || Boolean(item?.authenticator) || draft.authenticator.trim() !== "";
|
|
5797
|
+
const recoveryEditing = !recoveryDeclared && !recoveryExtra && (showRecovery || (draft.values[RECOVERY_CODES_KEY] ?? "") !== "");
|
|
5800
5798
|
const shownOwner = mode === "new" ? owner === "me" ? { label: "Personal", glyph: /* @__PURE__ */ jsx26(PersonGlyph, {}) } : owner === "person" ? { label: "Someone else", glyph: /* @__PURE__ */ jsx26(SendGlyph, {}) } : { label: organizations.find((o) => o.id === owner)?.name ?? "Organization", glyph: /* @__PURE__ */ jsx26(BuildingGlyph, {}) } : item ? { label: item.owner.label, glyph: item.owner.kind === "me" ? /* @__PURE__ */ jsx26(PersonGlyph, {}) : item.owner.kind === "organization" ? /* @__PURE__ */ jsx26(BuildingGlyph, {}) : /* @__PURE__ */ jsx26(UsersIcon2, {}) } : null;
|
|
5801
5799
|
const sameNamed = (id) => {
|
|
5802
5800
|
const name = organizations.find((o) => o.id === id)?.name.trim().toLowerCase();
|
|
@@ -5944,7 +5942,8 @@ function ItemPane(props) {
|
|
|
5944
5942
|
label: f.label,
|
|
5945
5943
|
value: draft.values[f.key] ?? "",
|
|
5946
5944
|
placeholder: f.handling === "visible" || f.key in loaded ? "" : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
5947
|
-
secret: f.handling !== "visible",
|
|
5945
|
+
secret: f.handling !== "visible" && !isRecoveryCodesField(f),
|
|
5946
|
+
multiline: isRecoveryCodesField(f),
|
|
5948
5947
|
onChange: (v) => setValue(f.key, v),
|
|
5949
5948
|
onRemove: () => patch({ removed: [...draft.removed, f.id] }),
|
|
5950
5949
|
removeLabel: `Delete ${f.label}`,
|
|
@@ -5981,7 +5980,27 @@ function ItemPane(props) {
|
|
|
5981
5980
|
)),
|
|
5982
5981
|
/* @__PURE__ */ jsx26(AddRow, { label: draft.loginUrls.length === 0 ? "Website" : "", onAdd: () => patch({ loginUrls: [...draft.loginUrls, ""] }), children: draft.loginUrls.length === 0 ? "Add website" : "Add another website" }),
|
|
5983
5982
|
/* @__PURE__ */ jsx26(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }),
|
|
5984
|
-
item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
|
|
5983
|
+
item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null,
|
|
5984
|
+
recoveryEditing ? /* @__PURE__ */ jsx26(
|
|
5985
|
+
EditRow,
|
|
5986
|
+
{
|
|
5987
|
+
label: "Recovery codes",
|
|
5988
|
+
value: draft.values[RECOVERY_CODES_KEY] ?? "",
|
|
5989
|
+
multiline: true,
|
|
5990
|
+
autoFocus: showRecovery && (draft.values[RECOVERY_CODES_KEY] ?? "") === "",
|
|
5991
|
+
placeholder: "Backup codes, one per line",
|
|
5992
|
+
onChange: (v) => setValue(RECOVERY_CODES_KEY, v),
|
|
5993
|
+
onRemove: () => {
|
|
5994
|
+
setShowRecovery(false);
|
|
5995
|
+
setDraft((d) => {
|
|
5996
|
+
const values = { ...d.values };
|
|
5997
|
+
delete values[RECOVERY_CODES_KEY];
|
|
5998
|
+
return { ...d, values };
|
|
5999
|
+
});
|
|
6000
|
+
},
|
|
6001
|
+
removeLabel: "Remove recovery codes"
|
|
6002
|
+
}
|
|
6003
|
+
) : !recoveryDeclared && !recoveryExtra && offerRecovery ? /* @__PURE__ */ jsx26(AddRow, { label: "Recovery codes", onAdd: () => setShowRecovery(true), children: "Add backup codes" }) : null
|
|
5985
6004
|
] }),
|
|
5986
6005
|
/* @__PURE__ */ jsx26(VaultSection, { children: /* @__PURE__ */ jsx26(EditRow, { label: "Notes", value: draft.notes, multiline: true, placeholder: "Anything worth remembering", onChange: (v) => patch({ notes: v }) }) }),
|
|
5987
6006
|
/* @__PURE__ */ jsxs24(
|
|
@@ -5994,7 +6013,8 @@ function ItemPane(props) {
|
|
|
5994
6013
|
/* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => patch({ secrets: [...draft.secrets, { key: "", label: "", value: "" }] }), children: "Secret field" }),
|
|
5995
6014
|
/* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => patch({ custom: [...draft.custom, { key: `custom_${draft.custom.length + 1}_${Date.now() % 1e5}`, label: "", value: "" }] }), children: "Text field" }),
|
|
5996
6015
|
!tagsVisible ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowTags(true), children: "Tags" }) : null,
|
|
5997
|
-
!descriptionVisible ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowDescription(true), children: "Description" }) : null
|
|
6016
|
+
!descriptionVisible ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowDescription(true), children: "Description" }) : null,
|
|
6017
|
+
!recoveryDeclared && !recoveryExtra && !recoveryEditing ? /* @__PURE__ */ jsx26(DropdownMenuItem3, { onSelect: () => setShowRecovery(true), children: "Recovery codes" }) : null
|
|
5998
6018
|
] })
|
|
5999
6019
|
] }),
|
|
6000
6020
|
children: [
|
|
@@ -6034,8 +6054,9 @@ function ItemPane(props) {
|
|
|
6034
6054
|
item.authenticator ? /* @__PURE__ */ jsx26(TotpRow, { port, item, onChanged }) : seedField && !seedField.active ? (
|
|
6035
6055
|
// The secret is still there, only off: turning it on brings the codes back. Never "Set up",
|
|
6036
6056
|
// which would invite replacing a working two-factor secret.
|
|
6037
|
-
/* @__PURE__ */ jsx26(VaultRow, { label: "
|
|
6038
|
-
) : /* @__PURE__ */ jsx26(AddRow, { label: "
|
|
6057
|
+
/* @__PURE__ */ jsx26(VaultRow, { label: "Authenticator", actions: canTurnOn ? /* @__PURE__ */ jsx26(TurnOnButton, { label: "Authenticator", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "Turned off" }) })
|
|
6058
|
+
) : /* @__PURE__ */ jsx26(AddRow, { label: "Authenticator", onAdd: canEdit ? () => setSettingUpOtp(true) : null, children: "Set up" }),
|
|
6059
|
+
!recoveryDeclared && !recoveryExtra && offerRecovery ? /* @__PURE__ */ jsx26(RecoveryCodesRow, { item, field: null, port, onChanged }) : null,
|
|
6039
6060
|
!item.authenticator && item.custom.some((c) => c.key === MFA_METHOD_KEY && c.value) ? /* @__PURE__ */ jsx26(TwoFactorMethodRow, { port, item, onChanged }) : null
|
|
6040
6061
|
] }),
|
|
6041
6062
|
/* @__PURE__ */ jsx26(VaultSection, { children: item.notes ? /* @__PURE__ */ jsx26(PlainRow, { label: "Notes", value: item.notes, multiline: true }) : /* @__PURE__ */ jsx26(AddRow, { label: "Notes", onAdd: canEdit ? () => onMode("edit") : null, children: "Add notes" }) }),
|
|
@@ -6051,7 +6072,22 @@ function ItemPane(props) {
|
|
|
6051
6072
|
" \xB7 Created ",
|
|
6052
6073
|
formatAbsoluteDate3(item.createdAt, DAY_FORMAT, "")
|
|
6053
6074
|
] })
|
|
6054
|
-
] }) : null }) })
|
|
6075
|
+
] }) : null }) }),
|
|
6076
|
+
settingUpOtp && item ? /* @__PURE__ */ jsx26(
|
|
6077
|
+
AddOtpPanel,
|
|
6078
|
+
{
|
|
6079
|
+
port,
|
|
6080
|
+
items: [item],
|
|
6081
|
+
definitions: definition ? [definition] : [],
|
|
6082
|
+
itemId: item.id,
|
|
6083
|
+
onClose: () => setSettingUpOtp(false),
|
|
6084
|
+
onChanged: () => onChanged(),
|
|
6085
|
+
onDone: () => {
|
|
6086
|
+
setSettingUpOtp(false);
|
|
6087
|
+
onChanged();
|
|
6088
|
+
}
|
|
6089
|
+
}
|
|
6090
|
+
) : null
|
|
6055
6091
|
] }) });
|
|
6056
6092
|
}
|
|
6057
6093
|
|
|
@@ -6989,7 +7025,7 @@ var plural2 = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
|
6989
7025
|
function terminalMessage(s) {
|
|
6990
7026
|
if (s.phase === "completed") return `Imported ${plural2(s.committed, "passkey")}.`;
|
|
6991
7027
|
if (s.phase === "partial") return `${s.committed} imported; ${s.unsupported + s.failed + s.uncertain + s.not_attempted} need attention.`;
|
|
6992
|
-
if (s.phase === "cancelled") return "Import
|
|
7028
|
+
if (s.phase === "cancelled") return "Import canceled. Nothing else will be imported.";
|
|
6993
7029
|
if (s.phase === "unavailable") return "Passkey import is unavailable here.";
|
|
6994
7030
|
if (s.phase === "failed") return "The import did not finish. Recover it before using a new file.";
|
|
6995
7031
|
return s.message;
|
|
@@ -7174,7 +7210,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
|
|
|
7174
7210
|
present(g, { kind: "status", status: result, message: terminalMessage(result) });
|
|
7175
7211
|
if (!done && ACTIVE_PHASES.has(result.phase)) schedulePoll(active.id);
|
|
7176
7212
|
} catch (e) {
|
|
7177
|
-
keepOperation(g, fail(e, "The import could not be
|
|
7213
|
+
keepOperation(g, fail(e, "The import could not be canceled."));
|
|
7178
7214
|
} finally {
|
|
7179
7215
|
if (current(g)) setBusy(false);
|
|
7180
7216
|
}
|
|
@@ -7660,6 +7696,7 @@ import { cn as cn15, useLook as useLook2 } from "@ai-matrx/design-system";
|
|
|
7660
7696
|
import { Select as Select10 } from "@ai-matrx/design-system/controls";
|
|
7661
7697
|
import { GlobeIcon as GlobeIcon5 } from "@ai-matrx/design-system/icons";
|
|
7662
7698
|
import { jsx as jsx38, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
7699
|
+
var AUTHENTICATOR_LABEL = "Authenticator";
|
|
7663
7700
|
function vaultChoices(items, organizations, current) {
|
|
7664
7701
|
const n = (f) => items.filter(f).length;
|
|
7665
7702
|
const shared = n((i) => i.owner.kind === "shared");
|
|
@@ -7699,7 +7736,7 @@ function buildViews(items, definitions, favoriteIds2, recentIds2) {
|
|
|
7699
7736
|
function viewLabel(view, views) {
|
|
7700
7737
|
if (view === "favorites") return "Favorites";
|
|
7701
7738
|
if (view === "recent") return "Recently opened";
|
|
7702
|
-
if (view === "authenticator") return
|
|
7739
|
+
if (view === "authenticator") return AUTHENTICATOR_LABEL;
|
|
7703
7740
|
if (view === "trash") return "Trash";
|
|
7704
7741
|
if (view === "page") return "This page";
|
|
7705
7742
|
return [...views.categories, ...views.tags].find((v) => v.view === view)?.label ?? "All items";
|
|
@@ -7710,7 +7747,7 @@ function viewOptions(views, allCount, deletedCount, hasFavorites, hasRecents, ha
|
|
|
7710
7747
|
{ value: "all", label: "Every category", meta: String(allCount) },
|
|
7711
7748
|
...hasFavorites ? [{ value: "favorites", label: "Favorites", meta: views.favorites === void 0 ? "" : String(views.favorites) }] : [],
|
|
7712
7749
|
...hasRecents ? [{ value: "recent", label: "Recently opened", meta: views.recent === void 0 ? "" : String(views.recent) }] : [],
|
|
7713
|
-
{ value: "authenticator", label:
|
|
7750
|
+
{ value: "authenticator", label: AUTHENTICATOR_LABEL, meta: String(views.authenticator) },
|
|
7714
7751
|
...views.categories.map((v) => ({ value: v.view, label: v.label, meta: String(v.count) })),
|
|
7715
7752
|
...views.tags.map((v) => ({ value: v.view, label: `#${v.label}`, meta: String(v.count) })),
|
|
7716
7753
|
{ value: "trash", label: "Trash", meta: String(deletedCount) }
|
|
@@ -7752,7 +7789,7 @@ function VaultSidebar({ vault, choices, onVault, view, allCount, deletedCount, v
|
|
|
7752
7789
|
/* @__PURE__ */ jsx38(Entry, { on: view === "all", glyph: GridGlyph, label: "All items", count: allCount, onClick: () => onView("all") }),
|
|
7753
7790
|
hasFavorites ? /* @__PURE__ */ jsx38(Entry, { on: view === "favorites", glyph: StarGlyph, label: "Favorites", count: views.favorites, onClick: () => onView("favorites") }) : null,
|
|
7754
7791
|
hasRecents ? /* @__PURE__ */ jsx38(Entry, { on: view === "recent", glyph: ClockGlyph, label: "Recently opened", count: views.recent, onClick: () => onView("recent") }) : null,
|
|
7755
|
-
/* @__PURE__ */ jsx38(Entry, { on: view === "authenticator", glyph: LockGlyph, label:
|
|
7792
|
+
/* @__PURE__ */ jsx38(Entry, { on: view === "authenticator", glyph: LockGlyph, label: AUTHENTICATOR_LABEL, count: views.authenticator, onClick: () => onView("authenticator") })
|
|
7756
7793
|
] }),
|
|
7757
7794
|
views.categories.length > 0 ? /* @__PURE__ */ jsx38(Group, { title: "Categories", children: views.categories.map((e) => /* @__PURE__ */ jsx38(Entry, { on: view === e.view, glyph: e.glyph, label: e.label, count: e.count, onClick: () => onView(e.view) }, e.view)) }) : null,
|
|
7758
7795
|
views.tags.length > 0 ? /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-0.5", children: [
|
|
@@ -7859,6 +7896,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
7859
7896
|
const loc = location ?? inner;
|
|
7860
7897
|
const dirty = useRef19(false);
|
|
7861
7898
|
const [notice, setNotice] = useState40(null);
|
|
7899
|
+
const [firstCode, setFirstCode] = useState40(null);
|
|
7862
7900
|
const move = useCallback5(
|
|
7863
7901
|
(next, how) => {
|
|
7864
7902
|
if (!location) setInner(next);
|
|
@@ -7998,9 +8036,21 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
7998
8036
|
}
|
|
7999
8037
|
const hasFavorites = port.lists.favorites !== void 0;
|
|
8000
8038
|
const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
|
|
8001
|
-
const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ jsxs37("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)
|
|
8039
|
+
const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ jsxs37("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_auto] gap-2", children: [
|
|
8002
8040
|
/* @__PURE__ */ jsx39(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
|
|
8003
8041
|
/* @__PURE__ */ jsx39(Select11, { 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" }),
|
|
8042
|
+
/* @__PURE__ */ jsx39(
|
|
8043
|
+
Button33,
|
|
8044
|
+
{
|
|
8045
|
+
variant: loc.view === "authenticator" ? "outline" : "quiet",
|
|
8046
|
+
icon: /* @__PURE__ */ jsx39(LockGlyph, {}),
|
|
8047
|
+
"aria-label": AUTHENTICATOR_LABEL,
|
|
8048
|
+
"aria-pressed": loc.view === "authenticator",
|
|
8049
|
+
title: AUTHENTICATOR_LABEL,
|
|
8050
|
+
onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null }),
|
|
8051
|
+
children: mode === "split" ? AUTHENTICATOR_LABEL : null
|
|
8052
|
+
}
|
|
8053
|
+
),
|
|
8004
8054
|
/* @__PURE__ */ jsxs37(DropdownMenu6, { children: [
|
|
8005
8055
|
/* @__PURE__ */ jsx39(DropdownMenuTrigger6, { asChild: true, children: /* @__PURE__ */ jsx39(Button33, { variant: "quiet", icon: /* @__PURE__ */ jsx39(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
|
|
8006
8056
|
/* @__PURE__ */ jsxs37(DropdownMenuContent6, { align: "end", children: [
|
|
@@ -8101,9 +8151,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8101
8151
|
seed: { name: guess?.name ?? "", url: guess?.url ?? loc.url.trim() },
|
|
8102
8152
|
onBack: () => go({ kind: null }),
|
|
8103
8153
|
onMode: () => void 0,
|
|
8104
|
-
onSaved: (item, _wasNew, n) => {
|
|
8154
|
+
onSaved: (item, _wasNew, n, enrolled) => {
|
|
8105
8155
|
replaceItem(item);
|
|
8106
8156
|
dirty.current = false;
|
|
8157
|
+
if (enrolled) setFirstCode(item);
|
|
8107
8158
|
move({ ...loc, itemId: item.id, mode: "view", kind: null, url: "", panel: null }, "replace");
|
|
8108
8159
|
setNotice(n);
|
|
8109
8160
|
notify("Saved");
|
|
@@ -8132,9 +8183,10 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8132
8183
|
onBack: back,
|
|
8133
8184
|
onMode: (m) => go({ mode: m, panel: null }),
|
|
8134
8185
|
notice,
|
|
8135
|
-
onSaved: (item, _wasNew, n) => {
|
|
8186
|
+
onSaved: (item, _wasNew, n, enrolled) => {
|
|
8136
8187
|
replaceItem(item);
|
|
8137
8188
|
dirty.current = false;
|
|
8189
|
+
if (enrolled) setFirstCode(item);
|
|
8138
8190
|
move({ ...loc, mode: "view" }, "replace");
|
|
8139
8191
|
setNotice(n);
|
|
8140
8192
|
notify("Saved");
|
|
@@ -8232,7 +8284,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8232
8284
|
) : null,
|
|
8233
8285
|
shown.list ? /* @__PURE__ */ jsx39("div", { className: specialView && mode !== "stack" ? "col-span-2 min-h-0" : "min-h-0", children: list }) : null,
|
|
8234
8286
|
shown.detail ? detail : null,
|
|
8235
|
-
panel
|
|
8287
|
+
panel,
|
|
8288
|
+
firstCode ? /* @__PURE__ */ jsx39(FirstCodePanel, { port, item: firstCode, onClose: () => setFirstCode(null), onDone: () => setFirstCode(null) }) : null
|
|
8236
8289
|
] })
|
|
8237
8290
|
] });
|
|
8238
8291
|
}
|
|
@@ -9003,7 +9056,9 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9003
9056
|
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 }))],
|
|
9004
9057
|
fields: [
|
|
9005
9058
|
...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
|
|
9006
|
-
...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map((s) => ({ field_key: fieldKeyOf(s.label), value: s.value, handling: "revealable" }))
|
|
9059
|
+
...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map((s) => ({ field_key: fieldKeyOf(s.label), value: s.value, handling: "revealable" })),
|
|
9060
|
+
// Backup codes on a type that does not declare them (a website login): their own encrypted field.
|
|
9061
|
+
...!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" }] : []
|
|
9007
9062
|
]
|
|
9008
9063
|
}
|
|
9009
9064
|
}, organizationId ? { organizationId } : void 0);
|