@embedpdf/viewer-chrome 3.0.0-next.13 → 3.0.0-next.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/README.md +175 -132
- package/dist/annotation-fonts-BqoLZ4Mp.js +80 -0
- package/dist/annotation-fonts-BqoLZ4Mp.js.map +1 -0
- package/dist/annotation-fonts-Dt0IQXhz.cjs +91 -0
- package/dist/annotation-fonts-Dt0IQXhz.cjs.map +1 -0
- package/dist/annotation-style--rPIZsuP.js +879 -0
- package/dist/annotation-style--rPIZsuP.js.map +1 -0
- package/dist/annotation-style-Bzvvp8-N.cjs +879 -0
- package/dist/annotation-style-Bzvvp8-N.cjs.map +1 -0
- package/dist/ar-D9XKAmDx.js +567 -0
- package/dist/ar-D9XKAmDx.js.map +1 -0
- package/dist/ar-DnEj5vbP.cjs +567 -0
- package/dist/ar-DnEj5vbP.cjs.map +1 -0
- package/dist/chrome.cjs +378 -221
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.d.cts +5 -34
- package/dist/chrome.d.ts +5 -34
- package/dist/chrome.js +377 -218
- package/dist/chrome.js.map +1 -1
- package/dist/commands-BmgxhJ8B.js +953 -0
- package/dist/commands-BmgxhJ8B.js.map +1 -0
- package/dist/commands-CYt4SFUX.cjs +1060 -0
- package/dist/commands-CYt4SFUX.cjs.map +1 -0
- package/dist/commands.cjs +12 -595
- package/dist/commands.d.cts +29 -4
- package/dist/commands.d.ts +29 -4
- package/dist/commands.js +2 -593
- package/dist/comments-panel-MUVgLCBI.js +514 -0
- package/dist/comments-panel-MUVgLCBI.js.map +1 -0
- package/dist/comments-panel-pEcQg4Vs.cjs +514 -0
- package/dist/comments-panel-pEcQg4Vs.cjs.map +1 -0
- package/dist/de-Bdr4aWzm.js +566 -0
- package/dist/de-Bdr4aWzm.js.map +1 -0
- package/dist/de-Dv6zvbnd.cjs +566 -0
- package/dist/de-Dv6zvbnd.cjs.map +1 -0
- package/dist/dialog-B5GBisKv.js +55 -0
- package/dist/dialog-B5GBisKv.js.map +1 -0
- package/dist/dialog-t8F13BL_.cjs +60 -0
- package/dist/dialog-t8F13BL_.cjs.map +1 -0
- package/dist/es-BP09DR9w.cjs +566 -0
- package/dist/es-BP09DR9w.cjs.map +1 -0
- package/dist/es-BSXz5tBv.js +566 -0
- package/dist/es-BSXz5tBv.js.map +1 -0
- package/dist/form-design-panel-B5MXTZ96.cjs +418 -0
- package/dist/form-design-panel-B5MXTZ96.cjs.map +1 -0
- package/dist/form-design-panel-vWWU9uB-.js +418 -0
- package/dist/form-design-panel-vWWU9uB-.js.map +1 -0
- package/dist/fr-BBQQn7tJ.cjs +566 -0
- package/dist/fr-BBQQn7tJ.cjs.map +1 -0
- package/dist/fr-Ckn3K9v1.js +566 -0
- package/dist/fr-Ckn3K9v1.js.map +1 -0
- package/dist/he-DRsG7bH7.cjs +567 -0
- package/dist/he-DRsG7bH7.cjs.map +1 -0
- package/dist/he-wdGU5PkU.js +567 -0
- package/dist/he-wdGU5PkU.js.map +1 -0
- package/dist/icons-BrK5iVL8.js +856 -0
- package/dist/icons-BrK5iVL8.js.map +1 -0
- package/dist/icons-x0Tc05bs.cjs +1010 -0
- package/dist/icons-x0Tc05bs.cjs.map +1 -0
- package/dist/index.cjs +3162 -5106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +636 -143
- package/dist/index.d.ts +636 -143
- package/dist/index.js +3083 -4975
- package/dist/index.js.map +1 -1
- package/dist/it-BuP4o0xQ.cjs +566 -0
- package/dist/it-BuP4o0xQ.cjs.map +1 -0
- package/dist/it-Us-VmvIu.js +566 -0
- package/dist/it-Us-VmvIu.js.map +1 -0
- package/dist/ja-Cizj0-5O.js +566 -0
- package/dist/ja-Cizj0-5O.js.map +1 -0
- package/dist/ja-DovEd6ts.cjs +566 -0
- package/dist/ja-DovEd6ts.cjs.map +1 -0
- package/dist/ko-B-qJEol2.cjs +566 -0
- package/dist/ko-B-qJEol2.cjs.map +1 -0
- package/dist/ko-DlrXliE1.js +566 -0
- package/dist/ko-DlrXliE1.js.map +1 -0
- package/dist/labels-BiM9wp9K.cjs +21 -0
- package/dist/labels-BiM9wp9K.cjs.map +1 -0
- package/dist/labels-C6_i87Oj.js +16 -0
- package/dist/labels-C6_i87Oj.js.map +1 -0
- package/dist/link-editor-CSjqPnj1.cjs +125 -0
- package/dist/link-editor-CSjqPnj1.cjs.map +1 -0
- package/dist/link-editor-SxM1nnJU.js +125 -0
- package/dist/link-editor-SxM1nnJU.js.map +1 -0
- package/dist/measurement-Bs61XWbJ.js +325 -0
- package/dist/measurement-Bs61XWbJ.js.map +1 -0
- package/dist/measurement-CcCsHPOX.cjs +326 -0
- package/dist/measurement-CcCsHPOX.cjs.map +1 -0
- package/dist/nl-0aF7M_0h.js +566 -0
- package/dist/nl-0aF7M_0h.js.map +1 -0
- package/dist/nl-D3KZDDiJ.cjs +566 -0
- package/dist/nl-D3KZDDiJ.cjs.map +1 -0
- package/dist/pl-BW--AaCI.js +566 -0
- package/dist/pl-BW--AaCI.js.map +1 -0
- package/dist/pl-DSFhAUqx.cjs +566 -0
- package/dist/pl-DSFhAUqx.cjs.map +1 -0
- package/dist/pt-C5S-8MC5.cjs +566 -0
- package/dist/pt-C5S-8MC5.cjs.map +1 -0
- package/dist/pt-DIhpU5pT.js +566 -0
- package/dist/pt-DIhpU5pT.js.map +1 -0
- package/dist/redact-confirm-B6j36TPw.js +83 -0
- package/dist/redact-confirm-B6j36TPw.js.map +1 -0
- package/dist/redact-confirm-CX_SY2ZN.cjs +83 -0
- package/dist/redact-confirm-CX_SY2ZN.cjs.map +1 -0
- package/dist/redaction-panel-BmoIl-Ll.cjs +96 -0
- package/dist/redaction-panel-BmoIl-Ll.cjs.map +1 -0
- package/dist/redaction-panel-IJDshSJI.js +96 -0
- package/dist/redaction-panel-IJDshSJI.js.map +1 -0
- package/dist/search-panel-HfIhJm1M.js +249 -0
- package/dist/search-panel-HfIhJm1M.js.map +1 -0
- package/dist/search-panel-s9IoiOtv.cjs +249 -0
- package/dist/search-panel-s9IoiOtv.cjs.map +1 -0
- package/dist/sheet-BDsa_Sup.cjs +442 -0
- package/dist/sheet-BDsa_Sup.cjs.map +1 -0
- package/dist/sheet-R6XDgyEf.js +365 -0
- package/dist/sheet-R6XDgyEf.js.map +1 -0
- package/dist/sign-dialog-CgnR7akj.cjs +155 -0
- package/dist/sign-dialog-CgnR7akj.cjs.map +1 -0
- package/dist/sign-dialog-GDqCoQhA.js +155 -0
- package/dist/sign-dialog-GDqCoQhA.js.map +1 -0
- package/dist/signature-inspector-DyO313Qf.cjs +166 -0
- package/dist/signature-inspector-DyO313Qf.cjs.map +1 -0
- package/dist/signature-inspector-okFz8do6.js +166 -0
- package/dist/signature-inspector-okFz8do6.js.map +1 -0
- package/dist/signature-maker-BhCmYyHD.js +393 -0
- package/dist/signature-maker-BhCmYyHD.js.map +1 -0
- package/dist/signature-maker-BoYa8vgA.cjs +393 -0
- package/dist/signature-maker-BoYa8vgA.cjs.map +1 -0
- package/dist/signatures-panel-B56PC6d9.cjs +300 -0
- package/dist/signatures-panel-B56PC6d9.cjs.map +1 -0
- package/dist/signatures-panel-CgnFfhFm.js +300 -0
- package/dist/signatures-panel-CgnFfhFm.js.map +1 -0
- package/dist/stamp-store-BHvRs-Do.cjs +147 -0
- package/dist/stamp-store-BHvRs-Do.cjs.map +1 -0
- package/dist/stamp-store-WuF-L3Jj.js +112 -0
- package/dist/stamp-store-WuF-L3Jj.js.map +1 -0
- package/dist/stamps-panel-B9NYGayn.cjs +231 -0
- package/dist/stamps-panel-B9NYGayn.cjs.map +1 -0
- package/dist/stamps-panel-CDO0FPyK.js +231 -0
- package/dist/stamps-panel-CDO0FPyK.js.map +1 -0
- package/dist/toolbar-CJu2wKTU.cjs +1659 -0
- package/dist/toolbar-CJu2wKTU.cjs.map +1 -0
- package/dist/toolbar-z9od3tKl.js +1564 -0
- package/dist/toolbar-z9od3tKl.js.map +1 -0
- package/dist/zh-cn-3tmMeHqv.cjs +566 -0
- package/dist/zh-cn-3tmMeHqv.cjs.map +1 -0
- package/dist/zh-cn-CxkUUBct.js +566 -0
- package/dist/zh-cn-CxkUUBct.js.map +1 -0
- package/package.json +6 -4
- package/src/styles.css +153 -89
- package/dist/commands.cjs.map +0 -1
- package/dist/commands.js.map +0 -1
- package/dist/es-DYwzfxyt.cjs +0 -293
- package/dist/es-DYwzfxyt.cjs.map +0 -1
- package/dist/es-sN6uqBgD.js +0 -293
- package/dist/es-sN6uqBgD.js.map +0 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { t as Dialog } from "./dialog-B5GBisKv.js";
|
|
2
|
+
import { useId, useState } from "react";
|
|
3
|
+
import { useStampAssetPreviewUrl } from "@embedpdf/react/stamp";
|
|
4
|
+
import { useSignature } from "@embedpdf/react/signature";
|
|
5
|
+
import { useT } from "@embedpdf/react/i18n";
|
|
6
|
+
import { useSurface } from "@embedpdf/react/shell";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/ui/sign-dialog.tsx
|
|
9
|
+
/**
|
|
10
|
+
* The sign dialog (`signature-sign` modal surface): a mark met a field and
|
|
11
|
+
* the mode is `ask` — or a "sign here" flow wants the facts first. Shows
|
|
12
|
+
* the mark as it will sit in the field, takes reason and location, offers
|
|
13
|
+
* a certification when the config allows it, and either seals the field
|
|
14
|
+
* (`sign`) or only draws the mark into it (`fillField`). Escape cancels,
|
|
15
|
+
* unless it's signing.
|
|
16
|
+
*/
|
|
17
|
+
function SignDialog() {
|
|
18
|
+
const t = useT();
|
|
19
|
+
const surface = useSurface("signature-sign");
|
|
20
|
+
const signature = useSignature();
|
|
21
|
+
const [reason, setReason] = useState("");
|
|
22
|
+
const [location, setLocation] = useState("");
|
|
23
|
+
const [signKind, setSignKind] = useState("approval");
|
|
24
|
+
const [busy, setBusy] = useState(null);
|
|
25
|
+
const [error, setError] = useState(null);
|
|
26
|
+
const titleId = useId();
|
|
27
|
+
const field = surface.props?.field;
|
|
28
|
+
const mark = surface.props?.mark;
|
|
29
|
+
const preview = useStampAssetPreviewUrl(mark && "assetId" in mark ? mark.assetId : null);
|
|
30
|
+
if (!surface.isOpen || !field || !mark) return null;
|
|
31
|
+
const signer = {
|
|
32
|
+
...reason.trim() ? { reason: reason.trim() } : {},
|
|
33
|
+
...location.trim() ? { location: location.trim() } : {}
|
|
34
|
+
};
|
|
35
|
+
const run = async (kind) => {
|
|
36
|
+
setBusy(kind);
|
|
37
|
+
setError(null);
|
|
38
|
+
try {
|
|
39
|
+
if (kind === "sign") await signature.sign({
|
|
40
|
+
field,
|
|
41
|
+
mark,
|
|
42
|
+
signer,
|
|
43
|
+
...signKind !== "approval" && signature.canCertify() ? { certify: { permission: Number(signKind.slice(-1)) } } : {}
|
|
44
|
+
});
|
|
45
|
+
else await signature.fillField(field, mark);
|
|
46
|
+
surface.close();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
49
|
+
} finally {
|
|
50
|
+
setBusy(null);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const input = "border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm";
|
|
54
|
+
return /* @__PURE__ */ jsxs(Dialog, {
|
|
55
|
+
labelledBy: titleId,
|
|
56
|
+
onClose: () => {
|
|
57
|
+
if (busy == null) surface.close();
|
|
58
|
+
},
|
|
59
|
+
className: "border-border bg-surface w-96 rounded-lg border p-4 shadow-xl",
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsx("h2", {
|
|
62
|
+
id: titleId,
|
|
63
|
+
className: "text-fg text-base font-semibold",
|
|
64
|
+
children: t("demo.signTitle")
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ jsx("div", {
|
|
67
|
+
className: "border-border-subtle mt-3 flex h-24 items-center justify-center rounded-md border p-2",
|
|
68
|
+
children: preview ? /* @__PURE__ */ jsx("img", {
|
|
69
|
+
src: preview,
|
|
70
|
+
alt: "",
|
|
71
|
+
className: "max-h-full max-w-full object-contain"
|
|
72
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
73
|
+
className: "text-fg-muted text-sm",
|
|
74
|
+
children: t("demo.signatureLabel")
|
|
75
|
+
})
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ jsxs("label", {
|
|
78
|
+
className: "text-fg-muted mt-3 block text-xs",
|
|
79
|
+
children: [t("demo.signReason"), /* @__PURE__ */ jsx("input", {
|
|
80
|
+
value: reason,
|
|
81
|
+
onChange: (event) => setReason(event.target.value),
|
|
82
|
+
className: input
|
|
83
|
+
})]
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ jsxs("label", {
|
|
86
|
+
className: "text-fg-muted mt-2 block text-xs",
|
|
87
|
+
children: [t("demo.signLocation"), /* @__PURE__ */ jsx("input", {
|
|
88
|
+
value: location,
|
|
89
|
+
onChange: (event) => setLocation(event.target.value),
|
|
90
|
+
className: input
|
|
91
|
+
})]
|
|
92
|
+
}),
|
|
93
|
+
signature.canCertify() ? /* @__PURE__ */ jsxs("label", {
|
|
94
|
+
className: "text-fg-muted mt-3 block text-xs",
|
|
95
|
+
children: [t("demo.signKind"), /* @__PURE__ */ jsxs("select", {
|
|
96
|
+
value: signKind,
|
|
97
|
+
onChange: (event) => setSignKind(event.target.value),
|
|
98
|
+
className: input,
|
|
99
|
+
children: [
|
|
100
|
+
/* @__PURE__ */ jsx("option", {
|
|
101
|
+
value: "approval",
|
|
102
|
+
children: t("demo.signKindApproval")
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ jsx("option", {
|
|
105
|
+
value: "certify-3",
|
|
106
|
+
children: t("demo.signKindCertifyAnnotate")
|
|
107
|
+
}),
|
|
108
|
+
/* @__PURE__ */ jsx("option", {
|
|
109
|
+
value: "certify-2",
|
|
110
|
+
children: t("demo.signKindCertifyFill")
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ jsx("option", {
|
|
113
|
+
value: "certify-1",
|
|
114
|
+
children: t("demo.signKindCertifyLocked")
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
})]
|
|
118
|
+
}) : null,
|
|
119
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
120
|
+
className: "mt-2 text-sm text-red-600",
|
|
121
|
+
children: error
|
|
122
|
+
}) : null,
|
|
123
|
+
/* @__PURE__ */ jsxs("div", {
|
|
124
|
+
className: "mt-4 flex justify-end gap-2",
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsx("button", {
|
|
127
|
+
type: "button",
|
|
128
|
+
disabled: busy != null,
|
|
129
|
+
onClick: surface.close,
|
|
130
|
+
className: "border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm",
|
|
131
|
+
children: t("demo.cancel")
|
|
132
|
+
}),
|
|
133
|
+
signature.canFill(field) ? /* @__PURE__ */ jsx("button", {
|
|
134
|
+
type: "button",
|
|
135
|
+
disabled: busy != null,
|
|
136
|
+
onClick: () => void run("fill"),
|
|
137
|
+
className: "border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-60",
|
|
138
|
+
children: t("demo.signFill")
|
|
139
|
+
}) : null,
|
|
140
|
+
signature.canSign(field) ? /* @__PURE__ */ jsx("button", {
|
|
141
|
+
type: "button",
|
|
142
|
+
disabled: busy != null,
|
|
143
|
+
onClick: () => void run("sign"),
|
|
144
|
+
className: "bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60",
|
|
145
|
+
children: busy === "sign" ? t("demo.signSigning") : t("demo.signButton")
|
|
146
|
+
}) : null
|
|
147
|
+
]
|
|
148
|
+
})
|
|
149
|
+
]
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
export { SignDialog };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=sign-dialog-GDqCoQhA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-dialog-GDqCoQhA.js","names":[],"sources":["../src/ui/sign-dialog.tsx"],"sourcesContent":["/**\n * The sign dialog (`signature-sign` modal surface): a mark met a field and\n * the mode is `ask` — or a \"sign here\" flow wants the facts first. Shows\n * the mark as it will sit in the field, takes reason and location, offers\n * a certification when the config allows it, and either seals the field\n * (`sign`) or only draws the mark into it (`fillField`). Escape cancels,\n * unless it's signing.\n */\nimport { useId, useState } from 'react';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStampAssetPreviewUrl } from '@embedpdf/react/stamp';\nimport { useSignature, type FormFieldRef, type Mark } from '@embedpdf/react/signature';\nimport { Dialog } from './dialog';\n\nexport function SignDialog() {\n const t = useT();\n const surface = useSurface('signature-sign');\n const signature = useSignature();\n const [reason, setReason] = useState('');\n const [location, setLocation] = useState('');\n // What the signature declares about later changes: a plain approval (form\n // filling and further signatures keep it valid; Acrobat's reading), or a\n // certification with its DocMDP permission. Only the first signature can certify.\n const [signKind, setSignKind] = useState<'approval' | 'certify-3' | 'certify-2' | 'certify-1'>(\n 'approval',\n );\n const [busy, setBusy] = useState<'sign' | 'fill' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const field = surface.props?.field as FormFieldRef | undefined;\n const mark = surface.props?.mark as Mark | undefined;\n const assetId = mark && 'assetId' in mark ? mark.assetId : null;\n const preview = useStampAssetPreviewUrl(assetId);\n if (!surface.isOpen || !field || !mark) return null;\n\n const signer = {\n ...(reason.trim() ? { reason: reason.trim() } : {}),\n ...(location.trim() ? { location: location.trim() } : {}),\n };\n const run = async (kind: 'sign' | 'fill') => {\n setBusy(kind);\n setError(null);\n try {\n if (kind === 'sign') {\n await signature.sign({\n field,\n mark,\n signer,\n ...(signKind !== 'approval' && signature.canCertify()\n ? { certify: { permission: Number(signKind.slice(-1)) as 1 | 2 | 3 } }\n : {}),\n });\n } else {\n await signature.fillField(field, mark);\n }\n surface.close();\n } catch (error) {\n setError(error instanceof Error ? error.message : String(error));\n } finally {\n setBusy(null);\n }\n };\n\n const input = 'border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm';\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (busy == null) surface.close();\n }}\n className=\"border-border bg-surface w-96 rounded-lg border p-4 shadow-xl\"\n >\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {t('demo.signTitle')}\n </h2>\n <div className=\"border-border-subtle mt-3 flex h-24 items-center justify-center rounded-md border p-2\">\n {preview ? (\n <img src={preview} alt=\"\" className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-sm\">{t('demo.signatureLabel')}</span>\n )}\n </div>\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signReason')}\n <input\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className={input}\n />\n </label>\n <label className=\"text-fg-muted mt-2 block text-xs\">\n {t('demo.signLocation')}\n <input\n value={location}\n onChange={(event) => setLocation(event.target.value)}\n className={input}\n />\n </label>\n {signature.canCertify() ? (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.signKind')}\n <select\n value={signKind}\n onChange={(event) => setSignKind(event.target.value as typeof signKind)}\n className={input}\n >\n <option value=\"approval\">{t('demo.signKindApproval')}</option>\n <option value=\"certify-3\">{t('demo.signKindCertifyAnnotate')}</option>\n <option value=\"certify-2\">{t('demo.signKindCertifyFill')}</option>\n <option value=\"certify-1\">{t('demo.signKindCertifyLocked')}</option>\n </select>\n </label>\n ) : null}\n {error ? <p className=\"mt-2 text-sm text-red-600\">{error}</p> : null}\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={surface.close}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm\"\n >\n {t('demo.cancel')}\n </button>\n {signature.canFill(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('fill')}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {t('demo.signFill')}\n </button>\n ) : null}\n {signature.canSign(field) ? (\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => void run('sign')}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy === 'sign' ? t('demo.signSigning') : t('demo.signButton')}\n </button>\n ) : null}\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const require_icons = require("./icons-x0Tc05bs.cjs");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
|
|
4
|
+
let _embedpdf_react_form = require("@embedpdf/react/form");
|
|
5
|
+
let _embedpdf_react_signature = require("@embedpdf/react/signature");
|
|
6
|
+
let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
|
|
7
|
+
let _embedpdf_react_shell = require("@embedpdf/react/shell");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
let _embedpdf_react_anchored = require("@embedpdf/react/anchored");
|
|
10
|
+
//#region src/ui/signature-inspector.tsx
|
|
11
|
+
/**
|
|
12
|
+
* The validation popover (`signature-inspector` surface): what a signed
|
|
13
|
+
* field's signature says and whether it holds — the four independent facts
|
|
14
|
+
* (integrity, cryptography, trust, changes since) and the signer's claims,
|
|
15
|
+
* anchored at the widget when its box is known, plus the signed revision as
|
|
16
|
+
* a download (the exact bytes the signature covers). Mounted in the Stage
|
|
17
|
+
* overlay so `<Anchored>` can project onto the page.
|
|
18
|
+
*/
|
|
19
|
+
const verdictKey = (verdict) => !verdict ? "demo.verdictIndeterminate" : verdict.summary === "valid" ? "demo.verdictValid" : verdict.summary === "valid-untrusted" ? "demo.verdictValidUntrusted" : verdict.summary === "invalid" ? verdict.modifications.basis === "working-copy" ? "demo.verdictWillInvalidate" : "demo.verdictInvalid" : "demo.verdictIndeterminate";
|
|
20
|
+
/** A PDF date string (`D:YYYYMMDDHHmmSSZ` or with a zone offset) as a locale date; the raw text when it is not one. */
|
|
21
|
+
/** A moment in the reader's locale; the text itself if it isn't one. */
|
|
22
|
+
const dateTime = (iso) => {
|
|
23
|
+
const date = new Date(iso);
|
|
24
|
+
return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();
|
|
25
|
+
};
|
|
26
|
+
const tone = (summary) => summary === "valid" ? "text-green-600" : summary === "invalid" ? "text-red-600" : summary === "valid-untrusted" ? "text-amber-600" : "text-fg-muted";
|
|
27
|
+
function SignatureInspector() {
|
|
28
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
29
|
+
const surface = (0, _embedpdf_react_shell.useSurface)("signature-inspector");
|
|
30
|
+
const signature = (0, _embedpdf_react_signature.useSignature)();
|
|
31
|
+
const status = (0, _embedpdf_react_signature.useSignatureState)((state) => state.status);
|
|
32
|
+
(0, _embedpdf_react_signature.useSignatureState)((state) => state.signatures);
|
|
33
|
+
const [validating, setValidating] = (0, react.useState)(false);
|
|
34
|
+
const field = surface.props?.field;
|
|
35
|
+
const dto = field ? signature.getSignature(field) : null;
|
|
36
|
+
const verdict = field ? signature.getVerdict(field) : null;
|
|
37
|
+
const widget = dto?.widget ?? null;
|
|
38
|
+
const form = (0, _embedpdf_react_runtime.useOptionalCapability)(_embedpdf_react_form.FormToken);
|
|
39
|
+
const box = (widget && form?.getWidget(widget)?.rect) ?? null;
|
|
40
|
+
(0, react.useEffect)(() => {
|
|
41
|
+
if (!surface.isOpen || !dto?.signed || signature.listVerdicts()) return;
|
|
42
|
+
setValidating(true);
|
|
43
|
+
signature.validate().finally(() => setValidating(false));
|
|
44
|
+
}, [surface.isOpen, dto?.index]);
|
|
45
|
+
if (!surface.isOpen || !field || !dto || status !== "ready") return null;
|
|
46
|
+
const revalidate = () => {
|
|
47
|
+
setValidating(true);
|
|
48
|
+
signature.validate().finally(() => setValidating(false));
|
|
49
|
+
};
|
|
50
|
+
const summary = verdict?.summary ?? null;
|
|
51
|
+
const modifications = verdict?.modifications.verdict ?? null;
|
|
52
|
+
const row = (label, value, className = "text-fg") => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53
|
+
className: "flex items-baseline justify-between gap-3 text-xs",
|
|
54
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55
|
+
className: "text-fg-muted",
|
|
56
|
+
children: label
|
|
57
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58
|
+
className: `text-end ${className}`,
|
|
59
|
+
children: value
|
|
60
|
+
})]
|
|
61
|
+
});
|
|
62
|
+
const card = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
63
|
+
className: "border-border bg-elevated w-72 rounded-lg border p-3 text-sm shadow-xl",
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
66
|
+
className: "flex items-center justify-between",
|
|
67
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
68
|
+
className: "text-fg font-semibold",
|
|
69
|
+
children: t("demo.inspectTitle")
|
|
70
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
71
|
+
type: "button",
|
|
72
|
+
onClick: surface.close,
|
|
73
|
+
className: "text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded",
|
|
74
|
+
"aria-label": t("demo.cancel"),
|
|
75
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
|
|
76
|
+
name: "x",
|
|
77
|
+
size: 14
|
|
78
|
+
})
|
|
79
|
+
})]
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
82
|
+
className: `mt-1 text-sm font-medium ${tone(summary)}`,
|
|
83
|
+
children: validating ? t("demo.signaturesValidating") : t(verdictKey(verdict))
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
86
|
+
className: "mt-2 flex flex-col gap-1",
|
|
87
|
+
children: [
|
|
88
|
+
row(t("demo.inspectSigner"), dto.signer.name ?? "—"),
|
|
89
|
+
dto.signer.reason ? row(t("demo.inspectReason"), dto.signer.reason) : null,
|
|
90
|
+
dto.signer.location ? row(t("demo.inspectLocation"), dto.signer.location) : null,
|
|
91
|
+
dto.signer.signedAt ? row(t("demo.inspectTime"), dateTime(dto.signer.signedAt)) : null
|
|
92
|
+
]
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
95
|
+
className: "border-border-subtle mt-2 flex flex-col gap-1 border-t pt-2",
|
|
96
|
+
children: [
|
|
97
|
+
row(t("demo.inspectIntegrity"), verdict?.integrity ?? "—", tone(verdict?.integrity === "valid" ? "valid" : verdict?.integrity === "invalid" ? "invalid" : null)),
|
|
98
|
+
row(t("demo.inspectCryptography"), verdict?.cryptography ?? "—"),
|
|
99
|
+
row(t("demo.inspectTrust"), verdict?.trust ?? "—"),
|
|
100
|
+
row(t("demo.inspectChanges"), modifications === "unchanged" ? verdict?.modifications.undone ? t("demo.verdictRestored") : t("demo.verdictUnchanged") : modifications === "permitted" ? t("demo.verdictPermitted") : modifications === "forbidden" ? t("demo.verdictForbidden") : "—", modifications === "forbidden" ? "text-red-600" : "text-fg"),
|
|
101
|
+
verdict?.modifications.laterRevisions ? row(t("demo.inspectLaterRevisions"), String(verdict.modifications.laterRevisions)) : null
|
|
102
|
+
]
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
105
|
+
className: "mt-3 flex justify-end gap-2",
|
|
106
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
107
|
+
type: "button",
|
|
108
|
+
disabled: validating,
|
|
109
|
+
onClick: revalidate,
|
|
110
|
+
className: "border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60",
|
|
111
|
+
children: t("demo.signaturesValidate")
|
|
112
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadRevision, { field })]
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
if (widget?.page && box) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_embedpdf_react_anchored.Anchored, {
|
|
117
|
+
anchor: {
|
|
118
|
+
page: widget.page,
|
|
119
|
+
bounds: box
|
|
120
|
+
},
|
|
121
|
+
placement: "bottom",
|
|
122
|
+
gap: 8,
|
|
123
|
+
children: card
|
|
124
|
+
});
|
|
125
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
126
|
+
className: "absolute end-4 top-4 z-40",
|
|
127
|
+
children: card
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** The signed revision — the exact bytes the signature covers — as a file. */
|
|
131
|
+
function DownloadRevision({ field }) {
|
|
132
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
133
|
+
const signature = (0, _embedpdf_react_signature.useSignature)();
|
|
134
|
+
const dto = signature.getSignature(field);
|
|
135
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
136
|
+
if (!dto || dto.revisionIndex === null) return null;
|
|
137
|
+
const revisionIndex = dto.revisionIndex;
|
|
138
|
+
const run = async () => {
|
|
139
|
+
setBusy(true);
|
|
140
|
+
try {
|
|
141
|
+
const bytes = await signature.readRevision({ revisionIndex });
|
|
142
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
143
|
+
const url = URL.createObjectURL(blob);
|
|
144
|
+
const anchor = document.createElement("a");
|
|
145
|
+
anchor.href = url;
|
|
146
|
+
anchor.download = `${dto.fieldName}-signed.pdf`;
|
|
147
|
+
anchor.click();
|
|
148
|
+
URL.revokeObjectURL(url);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error("[embedpdf] signed revision download failed:", error);
|
|
151
|
+
} finally {
|
|
152
|
+
setBusy(false);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
156
|
+
type: "button",
|
|
157
|
+
disabled: busy,
|
|
158
|
+
onClick: () => void run(),
|
|
159
|
+
className: "border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60",
|
|
160
|
+
children: t("demo.inspectDownload")
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
exports.SignatureInspector = SignatureInspector;
|
|
165
|
+
|
|
166
|
+
//# sourceMappingURL=signature-inspector-DyO313Qf.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-inspector-DyO313Qf.cjs","names":["FormToken","Icon","Anchored"],"sources":["../src/ui/signature-inspector.tsx"],"sourcesContent":["/**\n * The validation popover (`signature-inspector` surface): what a signed\n * field's signature says and whether it holds — the four independent facts\n * (integrity, cryptography, trust, changes since) and the signer's claims,\n * anchored at the widget when its box is known, plus the signed revision as\n * a download (the exact bytes the signature covers). Mounted in the Stage\n * overlay so `<Anchored>` can project onto the page.\n */\nimport { useEffect, useState } from 'react';\nimport { Anchored } from '@embedpdf/react/anchored';\nimport { FormToken } from '@embedpdf/react/form';\nimport { useOptionalCapability } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport {\n useSignature,\n useSignatureState,\n type FormFieldRef,\n type SignatureVerdict,\n} from '@embedpdf/react/signature';\nimport { Icon } from './icons';\n\nconst verdictKey = (verdict: SignatureVerdict | null): string =>\n !verdict\n ? 'demo.verdictIndeterminate'\n : verdict.summary === 'valid'\n ? 'demo.verdictValid'\n : verdict.summary === 'valid-untrusted'\n ? 'demo.verdictValidUntrusted'\n : verdict.summary === 'invalid'\n ? verdict.modifications.basis === 'working-copy'\n ? 'demo.verdictWillInvalidate'\n : 'demo.verdictInvalid'\n : 'demo.verdictIndeterminate';\n\n/** A PDF date string (`D:YYYYMMDDHHmmSSZ` or with a zone offset) as a locale date; the raw text when it is not one. */\n/** A moment in the reader's locale; the text itself if it isn't one. */\nconst dateTime = (iso: string): string => {\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();\n};\n\nconst tone = (summary: SignatureVerdict['summary'] | null): string =>\n summary === 'valid'\n ? 'text-green-600'\n : summary === 'invalid'\n ? 'text-red-600'\n : summary === 'valid-untrusted'\n ? 'text-amber-600'\n : 'text-fg-muted';\n\nexport function SignatureInspector() {\n const t = useT();\n const surface = useSurface('signature-inspector');\n const signature = useSignature();\n // Re-render when the signatures are read, and when one or its verdict changes.\n const status = useSignatureState((state) => state.status);\n useSignatureState((state) => state.signatures);\n const [validating, setValidating] = useState(false);\n const field = surface.props?.field as FormFieldRef | undefined;\n const dto = field ? signature.getSignature(field) : null;\n const verdict = field ? signature.getVerdict(field) : null;\n const widget = dto?.widget ?? null;\n // Where the signature shows is its widget's row in the form.\n const form = useOptionalCapability(FormToken);\n const box = (widget && form?.getWidget(widget)?.rect) ?? null;\n\n // Open on a field never validated: judge it now.\n useEffect(() => {\n if (!surface.isOpen || !dto?.signed || signature.listVerdicts()) return;\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [surface.isOpen, dto?.index]);\n\n if (!surface.isOpen || !field || !dto || status !== 'ready') return null;\n\n const revalidate = () => {\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n };\n const summary = verdict?.summary ?? null;\n const modifications = verdict?.modifications.verdict ?? null;\n const row = (label: string, value: string, className = 'text-fg') => (\n <div className=\"flex items-baseline justify-between gap-3 text-xs\">\n <span className=\"text-fg-muted\">{label}</span>\n <span className={`text-end ${className}`}>{value}</span>\n </div>\n );\n\n const card = (\n <div className=\"border-border bg-elevated w-72 rounded-lg border p-3 text-sm shadow-xl\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-fg font-semibold\">{t('demo.inspectTitle')}</span>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <p className={`mt-1 text-sm font-medium ${tone(summary)}`}>\n {validating ? t('demo.signaturesValidating') : t(verdictKey(verdict))}\n </p>\n <div className=\"mt-2 flex flex-col gap-1\">\n {row(t('demo.inspectSigner'), dto.signer.name ?? '—')}\n {dto.signer.reason ? row(t('demo.inspectReason'), dto.signer.reason) : null}\n {dto.signer.location ? row(t('demo.inspectLocation'), dto.signer.location) : null}\n {dto.signer.signedAt ? row(t('demo.inspectTime'), dateTime(dto.signer.signedAt)) : null}\n </div>\n <div className=\"border-border-subtle mt-2 flex flex-col gap-1 border-t pt-2\">\n {row(\n t('demo.inspectIntegrity'),\n verdict?.integrity ?? '—',\n tone(\n verdict?.integrity === 'valid'\n ? 'valid'\n : verdict?.integrity === 'invalid'\n ? 'invalid'\n : null,\n ),\n )}\n {row(t('demo.inspectCryptography'), verdict?.cryptography ?? '—')}\n {row(t('demo.inspectTrust'), verdict?.trust ?? '—')}\n {row(\n t('demo.inspectChanges'),\n modifications === 'unchanged'\n ? verdict?.modifications.undone\n ? t('demo.verdictRestored')\n : t('demo.verdictUnchanged')\n : modifications === 'permitted'\n ? t('demo.verdictPermitted')\n : modifications === 'forbidden'\n ? t('demo.verdictForbidden')\n : '—',\n modifications === 'forbidden' ? 'text-red-600' : 'text-fg',\n )}\n {verdict?.modifications.laterRevisions\n ? row(t('demo.inspectLaterRevisions'), String(verdict.modifications.laterRevisions))\n : null}\n </div>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={validating}\n onClick={revalidate}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60\"\n >\n {t('demo.signaturesValidate')}\n </button>\n <DownloadRevision field={field} />\n </div>\n </div>\n );\n\n if (widget?.page && box) {\n return (\n <Anchored anchor={{ page: widget.page, bounds: box }} placement=\"bottom\" gap={8}>\n {card}\n </Anchored>\n );\n }\n return <div className=\"absolute end-4 top-4 z-40\">{card}</div>;\n}\n\n/** The signed revision — the exact bytes the signature covers — as a file. */\nfunction DownloadRevision({ field }: { field: FormFieldRef }) {\n const t = useT();\n const signature = useSignature();\n const dto = signature.getSignature(field);\n const [busy, setBusy] = useState(false);\n if (!dto || dto.revisionIndex === null) return null;\n const revisionIndex = dto.revisionIndex;\n const run = async () => {\n setBusy(true);\n try {\n const bytes = await signature.readRevision({ revisionIndex });\n const blob = new Blob([bytes as BlobPart], { type: 'application/pdf' });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `${dto.fieldName}-signed.pdf`;\n anchor.click();\n URL.revokeObjectURL(url);\n } catch (error) {\n console.error('[embedpdf] signed revision download failed:', error);\n } finally {\n setBusy(false);\n }\n };\n return (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void run()}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60\"\n >\n {t('demo.inspectDownload')}\n </button>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { n as Icon } from "./icons-BrK5iVL8.js";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { useOptionalCapability } from "@embedpdf/react/runtime";
|
|
4
|
+
import { FormToken } from "@embedpdf/react/form";
|
|
5
|
+
import { useSignature, useSignatureState } from "@embedpdf/react/signature";
|
|
6
|
+
import { useT } from "@embedpdf/react/i18n";
|
|
7
|
+
import { useSurface } from "@embedpdf/react/shell";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { Anchored } from "@embedpdf/react/anchored";
|
|
10
|
+
//#region src/ui/signature-inspector.tsx
|
|
11
|
+
/**
|
|
12
|
+
* The validation popover (`signature-inspector` surface): what a signed
|
|
13
|
+
* field's signature says and whether it holds — the four independent facts
|
|
14
|
+
* (integrity, cryptography, trust, changes since) and the signer's claims,
|
|
15
|
+
* anchored at the widget when its box is known, plus the signed revision as
|
|
16
|
+
* a download (the exact bytes the signature covers). Mounted in the Stage
|
|
17
|
+
* overlay so `<Anchored>` can project onto the page.
|
|
18
|
+
*/
|
|
19
|
+
const verdictKey = (verdict) => !verdict ? "demo.verdictIndeterminate" : verdict.summary === "valid" ? "demo.verdictValid" : verdict.summary === "valid-untrusted" ? "demo.verdictValidUntrusted" : verdict.summary === "invalid" ? verdict.modifications.basis === "working-copy" ? "demo.verdictWillInvalidate" : "demo.verdictInvalid" : "demo.verdictIndeterminate";
|
|
20
|
+
/** A PDF date string (`D:YYYYMMDDHHmmSSZ` or with a zone offset) as a locale date; the raw text when it is not one. */
|
|
21
|
+
/** A moment in the reader's locale; the text itself if it isn't one. */
|
|
22
|
+
const dateTime = (iso) => {
|
|
23
|
+
const date = new Date(iso);
|
|
24
|
+
return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();
|
|
25
|
+
};
|
|
26
|
+
const tone = (summary) => summary === "valid" ? "text-green-600" : summary === "invalid" ? "text-red-600" : summary === "valid-untrusted" ? "text-amber-600" : "text-fg-muted";
|
|
27
|
+
function SignatureInspector() {
|
|
28
|
+
const t = useT();
|
|
29
|
+
const surface = useSurface("signature-inspector");
|
|
30
|
+
const signature = useSignature();
|
|
31
|
+
const status = useSignatureState((state) => state.status);
|
|
32
|
+
useSignatureState((state) => state.signatures);
|
|
33
|
+
const [validating, setValidating] = useState(false);
|
|
34
|
+
const field = surface.props?.field;
|
|
35
|
+
const dto = field ? signature.getSignature(field) : null;
|
|
36
|
+
const verdict = field ? signature.getVerdict(field) : null;
|
|
37
|
+
const widget = dto?.widget ?? null;
|
|
38
|
+
const form = useOptionalCapability(FormToken);
|
|
39
|
+
const box = (widget && form?.getWidget(widget)?.rect) ?? null;
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!surface.isOpen || !dto?.signed || signature.listVerdicts()) return;
|
|
42
|
+
setValidating(true);
|
|
43
|
+
signature.validate().finally(() => setValidating(false));
|
|
44
|
+
}, [surface.isOpen, dto?.index]);
|
|
45
|
+
if (!surface.isOpen || !field || !dto || status !== "ready") return null;
|
|
46
|
+
const revalidate = () => {
|
|
47
|
+
setValidating(true);
|
|
48
|
+
signature.validate().finally(() => setValidating(false));
|
|
49
|
+
};
|
|
50
|
+
const summary = verdict?.summary ?? null;
|
|
51
|
+
const modifications = verdict?.modifications.verdict ?? null;
|
|
52
|
+
const row = (label, value, className = "text-fg") => /* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: "flex items-baseline justify-between gap-3 text-xs",
|
|
54
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
55
|
+
className: "text-fg-muted",
|
|
56
|
+
children: label
|
|
57
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
58
|
+
className: `text-end ${className}`,
|
|
59
|
+
children: value
|
|
60
|
+
})]
|
|
61
|
+
});
|
|
62
|
+
const card = /* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: "border-border bg-elevated w-72 rounded-lg border p-3 text-sm shadow-xl",
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ jsxs("div", {
|
|
66
|
+
className: "flex items-center justify-between",
|
|
67
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
68
|
+
className: "text-fg font-semibold",
|
|
69
|
+
children: t("demo.inspectTitle")
|
|
70
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
71
|
+
type: "button",
|
|
72
|
+
onClick: surface.close,
|
|
73
|
+
className: "text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded",
|
|
74
|
+
"aria-label": t("demo.cancel"),
|
|
75
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
76
|
+
name: "x",
|
|
77
|
+
size: 14
|
|
78
|
+
})
|
|
79
|
+
})]
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ jsx("p", {
|
|
82
|
+
className: `mt-1 text-sm font-medium ${tone(summary)}`,
|
|
83
|
+
children: validating ? t("demo.signaturesValidating") : t(verdictKey(verdict))
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ jsxs("div", {
|
|
86
|
+
className: "mt-2 flex flex-col gap-1",
|
|
87
|
+
children: [
|
|
88
|
+
row(t("demo.inspectSigner"), dto.signer.name ?? "—"),
|
|
89
|
+
dto.signer.reason ? row(t("demo.inspectReason"), dto.signer.reason) : null,
|
|
90
|
+
dto.signer.location ? row(t("demo.inspectLocation"), dto.signer.location) : null,
|
|
91
|
+
dto.signer.signedAt ? row(t("demo.inspectTime"), dateTime(dto.signer.signedAt)) : null
|
|
92
|
+
]
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "border-border-subtle mt-2 flex flex-col gap-1 border-t pt-2",
|
|
96
|
+
children: [
|
|
97
|
+
row(t("demo.inspectIntegrity"), verdict?.integrity ?? "—", tone(verdict?.integrity === "valid" ? "valid" : verdict?.integrity === "invalid" ? "invalid" : null)),
|
|
98
|
+
row(t("demo.inspectCryptography"), verdict?.cryptography ?? "—"),
|
|
99
|
+
row(t("demo.inspectTrust"), verdict?.trust ?? "—"),
|
|
100
|
+
row(t("demo.inspectChanges"), modifications === "unchanged" ? verdict?.modifications.undone ? t("demo.verdictRestored") : t("demo.verdictUnchanged") : modifications === "permitted" ? t("demo.verdictPermitted") : modifications === "forbidden" ? t("demo.verdictForbidden") : "—", modifications === "forbidden" ? "text-red-600" : "text-fg"),
|
|
101
|
+
verdict?.modifications.laterRevisions ? row(t("demo.inspectLaterRevisions"), String(verdict.modifications.laterRevisions)) : null
|
|
102
|
+
]
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ jsxs("div", {
|
|
105
|
+
className: "mt-3 flex justify-end gap-2",
|
|
106
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
107
|
+
type: "button",
|
|
108
|
+
disabled: validating,
|
|
109
|
+
onClick: revalidate,
|
|
110
|
+
className: "border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60",
|
|
111
|
+
children: t("demo.signaturesValidate")
|
|
112
|
+
}), /* @__PURE__ */ jsx(DownloadRevision, { field })]
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
if (widget?.page && box) return /* @__PURE__ */ jsx(Anchored, {
|
|
117
|
+
anchor: {
|
|
118
|
+
page: widget.page,
|
|
119
|
+
bounds: box
|
|
120
|
+
},
|
|
121
|
+
placement: "bottom",
|
|
122
|
+
gap: 8,
|
|
123
|
+
children: card
|
|
124
|
+
});
|
|
125
|
+
return /* @__PURE__ */ jsx("div", {
|
|
126
|
+
className: "absolute end-4 top-4 z-40",
|
|
127
|
+
children: card
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** The signed revision — the exact bytes the signature covers — as a file. */
|
|
131
|
+
function DownloadRevision({ field }) {
|
|
132
|
+
const t = useT();
|
|
133
|
+
const signature = useSignature();
|
|
134
|
+
const dto = signature.getSignature(field);
|
|
135
|
+
const [busy, setBusy] = useState(false);
|
|
136
|
+
if (!dto || dto.revisionIndex === null) return null;
|
|
137
|
+
const revisionIndex = dto.revisionIndex;
|
|
138
|
+
const run = async () => {
|
|
139
|
+
setBusy(true);
|
|
140
|
+
try {
|
|
141
|
+
const bytes = await signature.readRevision({ revisionIndex });
|
|
142
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
143
|
+
const url = URL.createObjectURL(blob);
|
|
144
|
+
const anchor = document.createElement("a");
|
|
145
|
+
anchor.href = url;
|
|
146
|
+
anchor.download = `${dto.fieldName}-signed.pdf`;
|
|
147
|
+
anchor.click();
|
|
148
|
+
URL.revokeObjectURL(url);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error("[embedpdf] signed revision download failed:", error);
|
|
151
|
+
} finally {
|
|
152
|
+
setBusy(false);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return /* @__PURE__ */ jsx("button", {
|
|
156
|
+
type: "button",
|
|
157
|
+
disabled: busy,
|
|
158
|
+
onClick: () => void run(),
|
|
159
|
+
className: "border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60",
|
|
160
|
+
children: t("demo.inspectDownload")
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
export { SignatureInspector };
|
|
165
|
+
|
|
166
|
+
//# sourceMappingURL=signature-inspector-okFz8do6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-inspector-okFz8do6.js","names":[],"sources":["../src/ui/signature-inspector.tsx"],"sourcesContent":["/**\n * The validation popover (`signature-inspector` surface): what a signed\n * field's signature says and whether it holds — the four independent facts\n * (integrity, cryptography, trust, changes since) and the signer's claims,\n * anchored at the widget when its box is known, plus the signed revision as\n * a download (the exact bytes the signature covers). Mounted in the Stage\n * overlay so `<Anchored>` can project onto the page.\n */\nimport { useEffect, useState } from 'react';\nimport { Anchored } from '@embedpdf/react/anchored';\nimport { FormToken } from '@embedpdf/react/form';\nimport { useOptionalCapability } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport {\n useSignature,\n useSignatureState,\n type FormFieldRef,\n type SignatureVerdict,\n} from '@embedpdf/react/signature';\nimport { Icon } from './icons';\n\nconst verdictKey = (verdict: SignatureVerdict | null): string =>\n !verdict\n ? 'demo.verdictIndeterminate'\n : verdict.summary === 'valid'\n ? 'demo.verdictValid'\n : verdict.summary === 'valid-untrusted'\n ? 'demo.verdictValidUntrusted'\n : verdict.summary === 'invalid'\n ? verdict.modifications.basis === 'working-copy'\n ? 'demo.verdictWillInvalidate'\n : 'demo.verdictInvalid'\n : 'demo.verdictIndeterminate';\n\n/** A PDF date string (`D:YYYYMMDDHHmmSSZ` or with a zone offset) as a locale date; the raw text when it is not one. */\n/** A moment in the reader's locale; the text itself if it isn't one. */\nconst dateTime = (iso: string): string => {\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();\n};\n\nconst tone = (summary: SignatureVerdict['summary'] | null): string =>\n summary === 'valid'\n ? 'text-green-600'\n : summary === 'invalid'\n ? 'text-red-600'\n : summary === 'valid-untrusted'\n ? 'text-amber-600'\n : 'text-fg-muted';\n\nexport function SignatureInspector() {\n const t = useT();\n const surface = useSurface('signature-inspector');\n const signature = useSignature();\n // Re-render when the signatures are read, and when one or its verdict changes.\n const status = useSignatureState((state) => state.status);\n useSignatureState((state) => state.signatures);\n const [validating, setValidating] = useState(false);\n const field = surface.props?.field as FormFieldRef | undefined;\n const dto = field ? signature.getSignature(field) : null;\n const verdict = field ? signature.getVerdict(field) : null;\n const widget = dto?.widget ?? null;\n // Where the signature shows is its widget's row in the form.\n const form = useOptionalCapability(FormToken);\n const box = (widget && form?.getWidget(widget)?.rect) ?? null;\n\n // Open on a field never validated: judge it now.\n useEffect(() => {\n if (!surface.isOpen || !dto?.signed || signature.listVerdicts()) return;\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [surface.isOpen, dto?.index]);\n\n if (!surface.isOpen || !field || !dto || status !== 'ready') return null;\n\n const revalidate = () => {\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n };\n const summary = verdict?.summary ?? null;\n const modifications = verdict?.modifications.verdict ?? null;\n const row = (label: string, value: string, className = 'text-fg') => (\n <div className=\"flex items-baseline justify-between gap-3 text-xs\">\n <span className=\"text-fg-muted\">{label}</span>\n <span className={`text-end ${className}`}>{value}</span>\n </div>\n );\n\n const card = (\n <div className=\"border-border bg-elevated w-72 rounded-lg border p-3 text-sm shadow-xl\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-fg font-semibold\">{t('demo.inspectTitle')}</span>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <p className={`mt-1 text-sm font-medium ${tone(summary)}`}>\n {validating ? t('demo.signaturesValidating') : t(verdictKey(verdict))}\n </p>\n <div className=\"mt-2 flex flex-col gap-1\">\n {row(t('demo.inspectSigner'), dto.signer.name ?? '—')}\n {dto.signer.reason ? row(t('demo.inspectReason'), dto.signer.reason) : null}\n {dto.signer.location ? row(t('demo.inspectLocation'), dto.signer.location) : null}\n {dto.signer.signedAt ? row(t('demo.inspectTime'), dateTime(dto.signer.signedAt)) : null}\n </div>\n <div className=\"border-border-subtle mt-2 flex flex-col gap-1 border-t pt-2\">\n {row(\n t('demo.inspectIntegrity'),\n verdict?.integrity ?? '—',\n tone(\n verdict?.integrity === 'valid'\n ? 'valid'\n : verdict?.integrity === 'invalid'\n ? 'invalid'\n : null,\n ),\n )}\n {row(t('demo.inspectCryptography'), verdict?.cryptography ?? '—')}\n {row(t('demo.inspectTrust'), verdict?.trust ?? '—')}\n {row(\n t('demo.inspectChanges'),\n modifications === 'unchanged'\n ? verdict?.modifications.undone\n ? t('demo.verdictRestored')\n : t('demo.verdictUnchanged')\n : modifications === 'permitted'\n ? t('demo.verdictPermitted')\n : modifications === 'forbidden'\n ? t('demo.verdictForbidden')\n : '—',\n modifications === 'forbidden' ? 'text-red-600' : 'text-fg',\n )}\n {verdict?.modifications.laterRevisions\n ? row(t('demo.inspectLaterRevisions'), String(verdict.modifications.laterRevisions))\n : null}\n </div>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n disabled={validating}\n onClick={revalidate}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60\"\n >\n {t('demo.signaturesValidate')}\n </button>\n <DownloadRevision field={field} />\n </div>\n </div>\n );\n\n if (widget?.page && box) {\n return (\n <Anchored anchor={{ page: widget.page, bounds: box }} placement=\"bottom\" gap={8}>\n {card}\n </Anchored>\n );\n }\n return <div className=\"absolute end-4 top-4 z-40\">{card}</div>;\n}\n\n/** The signed revision — the exact bytes the signature covers — as a file. */\nfunction DownloadRevision({ field }: { field: FormFieldRef }) {\n const t = useT();\n const signature = useSignature();\n const dto = signature.getSignature(field);\n const [busy, setBusy] = useState(false);\n if (!dto || dto.revisionIndex === null) return null;\n const revisionIndex = dto.revisionIndex;\n const run = async () => {\n setBusy(true);\n try {\n const bytes = await signature.readRevision({ revisionIndex });\n const blob = new Blob([bytes as BlobPart], { type: 'application/pdf' });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `${dto.fieldName}-signed.pdf`;\n anchor.click();\n URL.revokeObjectURL(url);\n } catch (error) {\n console.error('[embedpdf] signed revision download failed:', error);\n } finally {\n setBusy(false);\n }\n };\n return (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void run()}\n className=\"border-border text-fg hover:bg-hover rounded-md border px-2 py-1 text-xs disabled:opacity-60\"\n >\n {t('demo.inspectDownload')}\n </button>\n );\n}\n"],"mappings":"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"}
|