@embedpdf/viewer-chrome 3.0.0-next.15 → 3.0.0-next.16

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.
Files changed (200) hide show
  1. package/dist/{annotation-fonts-BqoLZ4Mp.js → annotation-fonts-DWjVOTP0.js} +2 -2
  2. package/dist/{annotation-fonts-BqoLZ4Mp.js.map → annotation-fonts-DWjVOTP0.js.map} +1 -1
  3. package/dist/{annotation-fonts-Dt0IQXhz.cjs → annotation-fonts-dpfVkhnq.cjs} +2 -2
  4. package/dist/{annotation-fonts-Dt0IQXhz.cjs.map → annotation-fonts-dpfVkhnq.cjs.map} +1 -1
  5. package/dist/{annotation-style-Bzvvp8-N.cjs → annotation-style-B3XKAyZ-.cjs} +3 -3
  6. package/dist/{annotation-style-Bzvvp8-N.cjs.map → annotation-style-B3XKAyZ-.cjs.map} +1 -1
  7. package/dist/{annotation-style--rPIZsuP.js → annotation-style-CXF3X47Z.js} +3 -3
  8. package/dist/{annotation-style--rPIZsuP.js.map → annotation-style-CXF3X47Z.js.map} +1 -1
  9. package/dist/{ar-DnEj5vbP.cjs → ar-B132r9bo.cjs} +93 -1
  10. package/dist/ar-B132r9bo.cjs.map +1 -0
  11. package/dist/{ar-D9XKAmDx.js → ar-BLWFJoA_.js} +93 -1
  12. package/dist/ar-BLWFJoA_.js.map +1 -0
  13. package/dist/attachments-panel-Cxln8xos.cjs +217 -0
  14. package/dist/attachments-panel-Cxln8xos.cjs.map +1 -0
  15. package/dist/attachments-panel-H1ucRbwo.js +217 -0
  16. package/dist/attachments-panel-H1ucRbwo.js.map +1 -0
  17. package/dist/chrome.cjs +21 -0
  18. package/dist/chrome.cjs.map +1 -1
  19. package/dist/chrome.js +21 -0
  20. package/dist/chrome.js.map +1 -1
  21. package/dist/{commands-BmgxhJ8B.js → commands-BkgLP3Om.js} +418 -23
  22. package/dist/commands-BkgLP3Om.js.map +1 -0
  23. package/dist/{commands-CYt4SFUX.cjs → commands-DL1gjZt7.cjs} +433 -20
  24. package/dist/commands-DL1gjZt7.cjs.map +1 -0
  25. package/dist/commands.cjs +2 -1
  26. package/dist/commands.d.cts +3 -1
  27. package/dist/commands.d.ts +3 -1
  28. package/dist/commands.js +2 -2
  29. package/dist/{comments-panel-pEcQg4Vs.cjs → comments-panel-BsnWedTh.cjs} +2 -2
  30. package/dist/{comments-panel-pEcQg4Vs.cjs.map → comments-panel-BsnWedTh.cjs.map} +1 -1
  31. package/dist/{comments-panel-MUVgLCBI.js → comments-panel-D7Eadws9.js} +2 -2
  32. package/dist/{comments-panel-MUVgLCBI.js.map → comments-panel-D7Eadws9.js.map} +1 -1
  33. package/dist/{de-Dv6zvbnd.cjs → de-7_MvFWzN.cjs} +93 -1
  34. package/dist/de-7_MvFWzN.cjs.map +1 -0
  35. package/dist/{de-Bdr4aWzm.js → de-BUhwc9-x.js} +93 -1
  36. package/dist/de-BUhwc9-x.js.map +1 -0
  37. package/dist/{dialog-B5GBisKv.js → dialog-BIb373cg.js} +3 -3
  38. package/dist/dialog-BIb373cg.js.map +1 -0
  39. package/dist/{dialog-t8F13BL_.cjs → dialog-CngKTnAM.cjs} +3 -3
  40. package/dist/dialog-CngKTnAM.cjs.map +1 -0
  41. package/dist/{es-BP09DR9w.cjs → es-DgYj-1L3.cjs} +93 -1
  42. package/dist/es-DgYj-1L3.cjs.map +1 -0
  43. package/dist/{es-BSXz5tBv.js → es-DoXTqFoO.js} +93 -1
  44. package/dist/es-DoXTqFoO.js.map +1 -0
  45. package/dist/{form-design-panel-vWWU9uB-.js → form-design-panel-CgV_5YWC.js} +3 -3
  46. package/dist/{form-design-panel-vWWU9uB-.js.map → form-design-panel-CgV_5YWC.js.map} +1 -1
  47. package/dist/{form-design-panel-B5MXTZ96.cjs → form-design-panel-D0FdmWXW.cjs} +3 -3
  48. package/dist/{form-design-panel-B5MXTZ96.cjs.map → form-design-panel-D0FdmWXW.cjs.map} +1 -1
  49. package/dist/{fr-Ckn3K9v1.js → fr-B-8NDhBA.js} +93 -1
  50. package/dist/fr-B-8NDhBA.js.map +1 -0
  51. package/dist/{fr-BBQQn7tJ.cjs → fr-C856dmQJ.cjs} +93 -1
  52. package/dist/fr-C856dmQJ.cjs.map +1 -0
  53. package/dist/{he-DRsG7bH7.cjs → he-BFTeNc4t.cjs} +93 -1
  54. package/dist/he-BFTeNc4t.cjs.map +1 -0
  55. package/dist/{he-wdGU5PkU.js → he-Dlh0fNXM.js} +93 -1
  56. package/dist/he-Dlh0fNXM.js.map +1 -0
  57. package/dist/{icons-x0Tc05bs.cjs → icons-1UfLUZ_y.cjs} +20 -1
  58. package/dist/icons-1UfLUZ_y.cjs.map +1 -0
  59. package/dist/{icons-BrK5iVL8.js → icons-7dB0Y1ct.js} +20 -1
  60. package/dist/icons-7dB0Y1ct.js.map +1 -0
  61. package/dist/index.cjs +740 -171
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +16 -2
  64. package/dist/index.d.ts +16 -2
  65. package/dist/index.js +727 -170
  66. package/dist/index.js.map +1 -1
  67. package/dist/{it-BuP4o0xQ.cjs → it-Bef-2Yxq.cjs} +93 -1
  68. package/dist/it-Bef-2Yxq.cjs.map +1 -0
  69. package/dist/{it-Us-VmvIu.js → it-BkirG7ol.js} +93 -1
  70. package/dist/it-BkirG7ol.js.map +1 -0
  71. package/dist/{ja-Cizj0-5O.js → ja-CGEjCE-s.js} +93 -1
  72. package/dist/ja-CGEjCE-s.js.map +1 -0
  73. package/dist/{ja-DovEd6ts.cjs → ja-DNw2zqSD.cjs} +93 -1
  74. package/dist/ja-DNw2zqSD.cjs.map +1 -0
  75. package/dist/{ko-DlrXliE1.js → ko-CTkl5vMh.js} +93 -1
  76. package/dist/ko-CTkl5vMh.js.map +1 -0
  77. package/dist/{ko-B-qJEol2.cjs → ko-YHzeBqP6.cjs} +93 -1
  78. package/dist/ko-YHzeBqP6.cjs.map +1 -0
  79. package/dist/{link-editor-CSjqPnj1.cjs → link-editor-CAS7MZM2.cjs} +2 -2
  80. package/dist/{link-editor-CSjqPnj1.cjs.map → link-editor-CAS7MZM2.cjs.map} +1 -1
  81. package/dist/{link-editor-SxM1nnJU.js → link-editor-CkwtqRvb.js} +2 -2
  82. package/dist/{link-editor-SxM1nnJU.js.map → link-editor-CkwtqRvb.js.map} +1 -1
  83. package/dist/{measurement-Bs61XWbJ.js → measurement-CQdbnIrn.js} +2 -2
  84. package/dist/{measurement-Bs61XWbJ.js.map → measurement-CQdbnIrn.js.map} +1 -1
  85. package/dist/{measurement-CcCsHPOX.cjs → measurement-Cxv1Ahxx.cjs} +2 -2
  86. package/dist/{measurement-CcCsHPOX.cjs.map → measurement-Cxv1Ahxx.cjs.map} +1 -1
  87. package/dist/{nl-D3KZDDiJ.cjs → nl-BjkbaLq3.cjs} +93 -1
  88. package/dist/nl-BjkbaLq3.cjs.map +1 -0
  89. package/dist/{nl-0aF7M_0h.js → nl-Dya4sR8_.js} +93 -1
  90. package/dist/nl-Dya4sR8_.js.map +1 -0
  91. package/dist/outline-panel-BpGtBdj0.cjs +707 -0
  92. package/dist/outline-panel-BpGtBdj0.cjs.map +1 -0
  93. package/dist/outline-panel-DO5_REYw.js +707 -0
  94. package/dist/outline-panel-DO5_REYw.js.map +1 -0
  95. package/dist/outline-properties-BCa2cc0t.js +330 -0
  96. package/dist/outline-properties-BCa2cc0t.js.map +1 -0
  97. package/dist/outline-properties-DT3FIxCw.cjs +330 -0
  98. package/dist/outline-properties-DT3FIxCw.cjs.map +1 -0
  99. package/dist/{pl-DSFhAUqx.cjs → pl-CKKdVnpZ.cjs} +93 -1
  100. package/dist/pl-CKKdVnpZ.cjs.map +1 -0
  101. package/dist/{pl-BW--AaCI.js → pl-DA9b5NVg.js} +93 -1
  102. package/dist/pl-DA9b5NVg.js.map +1 -0
  103. package/dist/print-dialog-BqIrHCBh.cjs +276 -0
  104. package/dist/print-dialog-BqIrHCBh.cjs.map +1 -0
  105. package/dist/print-dialog-DvcI2m2_.js +276 -0
  106. package/dist/print-dialog-DvcI2m2_.js.map +1 -0
  107. package/dist/{pt-C5S-8MC5.cjs → pt-DLcszlzi.cjs} +93 -1
  108. package/dist/pt-DLcszlzi.cjs.map +1 -0
  109. package/dist/{pt-DIhpU5pT.js → pt-Nm8yyjbX.js} +93 -1
  110. package/dist/pt-Nm8yyjbX.js.map +1 -0
  111. package/dist/{redact-confirm-CX_SY2ZN.cjs → redact-confirm-C6EiK8yN.cjs} +2 -2
  112. package/dist/{redact-confirm-CX_SY2ZN.cjs.map → redact-confirm-C6EiK8yN.cjs.map} +1 -1
  113. package/dist/{redact-confirm-B6j36TPw.js → redact-confirm-CKuv9iN_.js} +2 -2
  114. package/dist/{redact-confirm-B6j36TPw.js.map → redact-confirm-CKuv9iN_.js.map} +1 -1
  115. package/dist/{redaction-panel-IJDshSJI.js → redaction-panel-CgNZ-pQk.js} +2 -2
  116. package/dist/{redaction-panel-IJDshSJI.js.map → redaction-panel-CgNZ-pQk.js.map} +1 -1
  117. package/dist/{redaction-panel-BmoIl-Ll.cjs → redaction-panel-ZhX0dlMi.cjs} +2 -2
  118. package/dist/{redaction-panel-BmoIl-Ll.cjs.map → redaction-panel-ZhX0dlMi.cjs.map} +1 -1
  119. package/dist/{search-panel-HfIhJm1M.js → search-panel-BE4xzWlm.js} +4 -4
  120. package/dist/{search-panel-HfIhJm1M.js.map → search-panel-BE4xzWlm.js.map} +1 -1
  121. package/dist/{search-panel-s9IoiOtv.cjs → search-panel-wcbTNG1z.cjs} +4 -4
  122. package/dist/{search-panel-s9IoiOtv.cjs.map → search-panel-wcbTNG1z.cjs.map} +1 -1
  123. package/dist/{sheet-BDsa_Sup.cjs → sheet-DTBFiwde.cjs} +30 -11
  124. package/dist/sheet-DTBFiwde.cjs.map +1 -0
  125. package/dist/{sheet-R6XDgyEf.js → sheet-DyjKfAq0.js} +25 -12
  126. package/dist/sheet-DyjKfAq0.js.map +1 -0
  127. package/dist/{sign-dialog-CgnR7akj.cjs → sign-dialog-D5WGx-Yn.cjs} +2 -2
  128. package/dist/{sign-dialog-CgnR7akj.cjs.map → sign-dialog-D5WGx-Yn.cjs.map} +1 -1
  129. package/dist/{sign-dialog-GDqCoQhA.js → sign-dialog-DerNe8qT.js} +2 -2
  130. package/dist/{sign-dialog-GDqCoQhA.js.map → sign-dialog-DerNe8qT.js.map} +1 -1
  131. package/dist/{signature-inspector-DyO313Qf.cjs → signature-inspector-Bz6TAfZM.cjs} +4 -10
  132. package/dist/signature-inspector-Bz6TAfZM.cjs.map +1 -0
  133. package/dist/{signature-inspector-okFz8do6.js → signature-inspector-CsGr9EBk.js} +4 -10
  134. package/dist/signature-inspector-CsGr9EBk.js.map +1 -0
  135. package/dist/{signature-maker-BoYa8vgA.cjs → signature-maker-D_YfBXLn.cjs} +3 -3
  136. package/dist/{signature-maker-BoYa8vgA.cjs.map → signature-maker-D_YfBXLn.cjs.map} +1 -1
  137. package/dist/{signature-maker-BhCmYyHD.js → signature-maker-Dnth4rV4.js} +3 -3
  138. package/dist/{signature-maker-BhCmYyHD.js.map → signature-maker-Dnth4rV4.js.map} +1 -1
  139. package/dist/{signatures-panel-B56PC6d9.cjs → signatures-panel-Ci_LaI1C.cjs} +4 -10
  140. package/dist/signatures-panel-Ci_LaI1C.cjs.map +1 -0
  141. package/dist/{signatures-panel-CgnFfhFm.js → signatures-panel-DZ9ywrjz.js} +4 -10
  142. package/dist/signatures-panel-DZ9ywrjz.js.map +1 -0
  143. package/dist/{stamps-panel-CDO0FPyK.js → stamps-panel-BrczStaz.js} +4 -10
  144. package/dist/stamps-panel-BrczStaz.js.map +1 -0
  145. package/dist/{stamps-panel-B9NYGayn.cjs → stamps-panel-Cv9nJBwx.cjs} +4 -10
  146. package/dist/stamps-panel-Cv9nJBwx.cjs.map +1 -0
  147. package/dist/theme-Be0OZokZ.js +97 -0
  148. package/dist/theme-Be0OZokZ.js.map +1 -0
  149. package/dist/theme-FR26JI3r.cjs +126 -0
  150. package/dist/theme-FR26JI3r.cjs.map +1 -0
  151. package/dist/{toolbar-z9od3tKl.js → toolbar-DF361xWY.js} +7 -6
  152. package/dist/toolbar-DF361xWY.js.map +1 -0
  153. package/dist/{toolbar-CJu2wKTU.cjs → toolbar-ddvpsvTb.cjs} +7 -6
  154. package/dist/toolbar-ddvpsvTb.cjs.map +1 -0
  155. package/dist/{zh-cn-CxkUUBct.js → zh-cn-BMPPkLpL.js} +93 -1
  156. package/dist/zh-cn-BMPPkLpL.js.map +1 -0
  157. package/dist/{zh-cn-3tmMeHqv.cjs → zh-cn-CZOO3p4H.cjs} +93 -1
  158. package/dist/zh-cn-CZOO3p4H.cjs.map +1 -0
  159. package/package.json +4 -4
  160. package/src/styles.css +8 -0
  161. package/dist/ar-D9XKAmDx.js.map +0 -1
  162. package/dist/ar-DnEj5vbP.cjs.map +0 -1
  163. package/dist/commands-BmgxhJ8B.js.map +0 -1
  164. package/dist/commands-CYt4SFUX.cjs.map +0 -1
  165. package/dist/de-Bdr4aWzm.js.map +0 -1
  166. package/dist/de-Dv6zvbnd.cjs.map +0 -1
  167. package/dist/dialog-B5GBisKv.js.map +0 -1
  168. package/dist/dialog-t8F13BL_.cjs.map +0 -1
  169. package/dist/es-BP09DR9w.cjs.map +0 -1
  170. package/dist/es-BSXz5tBv.js.map +0 -1
  171. package/dist/fr-BBQQn7tJ.cjs.map +0 -1
  172. package/dist/fr-Ckn3K9v1.js.map +0 -1
  173. package/dist/he-DRsG7bH7.cjs.map +0 -1
  174. package/dist/he-wdGU5PkU.js.map +0 -1
  175. package/dist/icons-BrK5iVL8.js.map +0 -1
  176. package/dist/icons-x0Tc05bs.cjs.map +0 -1
  177. package/dist/it-BuP4o0xQ.cjs.map +0 -1
  178. package/dist/it-Us-VmvIu.js.map +0 -1
  179. package/dist/ja-Cizj0-5O.js.map +0 -1
  180. package/dist/ja-DovEd6ts.cjs.map +0 -1
  181. package/dist/ko-B-qJEol2.cjs.map +0 -1
  182. package/dist/ko-DlrXliE1.js.map +0 -1
  183. package/dist/nl-0aF7M_0h.js.map +0 -1
  184. package/dist/nl-D3KZDDiJ.cjs.map +0 -1
  185. package/dist/pl-BW--AaCI.js.map +0 -1
  186. package/dist/pl-DSFhAUqx.cjs.map +0 -1
  187. package/dist/pt-C5S-8MC5.cjs.map +0 -1
  188. package/dist/pt-DIhpU5pT.js.map +0 -1
  189. package/dist/sheet-BDsa_Sup.cjs.map +0 -1
  190. package/dist/sheet-R6XDgyEf.js.map +0 -1
  191. package/dist/signature-inspector-DyO313Qf.cjs.map +0 -1
  192. package/dist/signature-inspector-okFz8do6.js.map +0 -1
  193. package/dist/signatures-panel-B56PC6d9.cjs.map +0 -1
  194. package/dist/signatures-panel-CgnFfhFm.js.map +0 -1
  195. package/dist/stamps-panel-B9NYGayn.cjs.map +0 -1
  196. package/dist/stamps-panel-CDO0FPyK.js.map +0 -1
  197. package/dist/toolbar-CJu2wKTU.cjs.map +0 -1
  198. package/dist/toolbar-z9od3tKl.js.map +0 -1
  199. package/dist/zh-cn-3tmMeHqv.cjs.map +0 -1
  200. package/dist/zh-cn-CxkUUBct.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature-inspector-CsGr9EBk.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 { saveFile } from '@embedpdf/web';\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 saveFile(bytes, `${dto.fieldName}-signed.pdf`, 'application/pdf');\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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"}
@@ -1,5 +1,5 @@
1
- const require_icons = require("./icons-x0Tc05bs.cjs");
2
- const require_dialog = require("./dialog-t8F13BL_.cjs");
1
+ const require_icons = require("./icons-1UfLUZ_y.cjs");
2
+ const require_dialog = require("./dialog-CngKTnAM.cjs");
3
3
  let react = require("react");
4
4
  let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
5
5
  let _embedpdf_react_stamp = require("@embedpdf/react/stamp");
@@ -390,4 +390,4 @@ function SignatureMakerModal() {
390
390
  //#endregion
391
391
  exports.SignatureMakerModal = SignatureMakerModal;
392
392
 
393
- //# sourceMappingURL=signature-maker-BoYa8vgA.cjs.map
393
+ //# sourceMappingURL=signature-maker-D_YfBXLn.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"signature-maker-BoYa8vgA.cjs","names":["DEFAULT_SIGNATURE_FONTS","useSignatureSettings","INITIALS_MARK_NAME","SIGNATURES_LIBRARY_KIND","SIGNATURE_MARK_NAME","Dialog","Icon"],"sources":["../src/ui/signature-fonts.ts","../src/ui/signature-maker.tsx"],"sourcesContent":["/**\n * The fonts a typed signature is drawn in: `plugins.signature.fonts`, or three handwriting fonts\n * (`@embedpdf/default-signature-fonts`, each a lazy chunk of your build). A font's file is read\n * the first time the signature maker needs it and registered on the engine once, under its key,\n * which a typed mark's `fontFamily` names; saving embeds the characters the mark uses.\n */\nimport {\n DEFAULT_SIGNATURE_FONTS,\n loadSignatureFont,\n} from '@embedpdf/default-signature-fonts/library';\nimport { isLocalEngine, type Engine } from '@embedpdf/react/runtime';\n\nimport type { SignatureFont } from '../plugin-settings';\n\n/** A font the maker offers: its key, what the picker shows, and how its bytes are read. */\nexport interface MarkFont {\n readonly key: string;\n readonly label: string;\n readonly bytes: () => Promise<Uint8Array>;\n}\n\nconst DEFAULT_FONTS: readonly MarkFont[] = DEFAULT_SIGNATURE_FONTS.map((font) => ({\n key: font.key,\n label: font.label,\n bytes: () => loadSignatureFont(font.key),\n}));\n\n/** The bytes of an app's font: its `data`, or its `url` fetched. */\nconst bytesOf = (font: SignatureFont) => async (): Promise<Uint8Array> => {\n if (font.data) return new Uint8Array(font.data);\n if (!font.url) throw new Error(`[embedpdf] signature font \"${font.key}\" has no url or data`);\n const response = await fetch(font.url);\n if (!response.ok) throw new Error(`${font.url}: HTTP ${response.status}`);\n return new Uint8Array(await response.arrayBuffer());\n};\n\n/** The fonts the maker offers: the app's, which replace the default ones, or the default ones. */\nexport const markFontsOf = (fonts: readonly SignatureFont[] | undefined): readonly MarkFont[] =>\n fonts\n ? fonts.map((font) => ({ key: font.key, label: font.label, bytes: bytesOf(font) }))\n : DEFAULT_FONTS;\n\n/** Each engine's registrations, by font key: one read and one registration per font. */\nconst registrations = new WeakMap<Engine, Map<string, Promise<void>>>();\n\n/**\n * Register `font` on `engine`, unless it already is: what a typed mark in it needs. Engines that\n * aren't local take no fonts from the browser, so nothing happens there. A failed read is tried\n * again next time.\n */\nexport function registerMarkFont(engine: Engine, font: MarkFont): Promise<void> {\n if (!isLocalEngine(engine)) return Promise.resolve();\n let byKey = registrations.get(engine);\n if (!byKey) registrations.set(engine, (byKey = new Map()));\n const known = byKey.get(font.key);\n if (known) return known;\n const registering = font\n .bytes()\n .then((data) => engine.fonts.register({ key: font.key, data }))\n .then(() => undefined);\n byKey.set(font.key, registering);\n registering.catch(() => byKey.delete(font.key));\n return registering;\n}\n","/**\n * The mark maker (`signature-maker` modal surface): a person's signature and\n * initials, drawn, typed, or uploaded, saved as one library of kind\n * `signatures` — the file is the person (Preview's model). Chrome code over\n * the stamp plugin's generic calls: `createLibrary` + `createAsset({ mark })`;\n * rename/export/delete are the library verbs. Opened on an existing library\n * (`props.libraryId`) it only adds the missing initials. Escape cancels,\n * unless it's saving.\n */\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStamp, type MarkSource } from '@embedpdf/react/stamp';\nimport {\n INITIALS_MARK_NAME,\n SIGNATURE_MARK_NAME,\n SIGNATURES_LIBRARY_KIND,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Dialog } from './dialog';\nimport { Icon } from './icons';\nimport { markFontsOf, registerMarkFont, type MarkFont } from './signature-fonts';\n\ntype Tab = 'draw' | 'type' | 'upload';\ntype Stroke = Array<{ x: number; y: number }>;\n\nconst PAD = { width: 360, height: 120 };\n\n/** One pad: draw / type / upload, producing a `MarkSource` or null. */\nfunction MarkPad({\n value,\n onChange,\n fonts,\n registerFont,\n}: {\n value: MarkSource | null;\n onChange: (mark: MarkSource | null) => void;\n fonts: readonly MarkFont[];\n registerFont: (key: string) => Promise<void>;\n}) {\n const t = useT();\n const [tab, setTab] = useState<Tab>('draw');\n const [strokes, setStrokes] = useState<Stroke[]>([]);\n const [text, setText] = useState('');\n const [font, setFont] = useState(fonts[0]?.key ?? '');\n const [fileName, setFileName] = useState<string | null>(null);\n const canvas = useRef<HTMLCanvasElement>(null);\n const drawing = useRef<Stroke | null>(null);\n\n // Paint the strokes (device-pixel sharp).\n useEffect(() => {\n const element = canvas.current;\n if (!element) return;\n const dpr = window.devicePixelRatio || 1;\n element.width = PAD.width * dpr;\n element.height = PAD.height * dpr;\n const ctx = element.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, PAD.width, PAD.height);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.lineWidth = 2.5;\n ctx.strokeStyle = getComputedStyle(element).color || '#000';\n for (const stroke of strokes) {\n ctx.beginPath();\n stroke.forEach((vertex, i) =>\n i === 0 ? ctx.moveTo(vertex.x, vertex.y) : ctx.lineTo(vertex.x, vertex.y),\n );\n ctx.stroke();\n }\n }, [strokes, tab]);\n\n const point = (event: React.PointerEvent<HTMLCanvasElement>) => {\n const rect = event.currentTarget.getBoundingClientRect();\n return { x: event.clientX - rect.left, y: event.clientY - rect.top };\n };\n const commitStrokes = (next: Stroke[]) => {\n setStrokes(next);\n // A mark's strokes go down the page, as the pad's do.\n onChange(\n next.length ? { kind: 'ink', strokes: next, color: '#000000', strokeWidth: 2.5 } : null,\n );\n };\n const commitText = async (next: string, key: string) => {\n setText(next);\n setFont(key);\n if (!next.trim()) {\n onChange(null);\n return;\n }\n await registerFont(key);\n onChange({ kind: 'text', text: next.trim(), fontFamily: key, color: '#000000', fontSize: 36 });\n };\n const pickFile = (file: File) => {\n setFileName(file.name);\n onChange(\n file.type === 'application/pdf'\n ? { kind: 'pdf', source: file, pageIndex: 0 }\n : { kind: 'image', source: file },\n );\n };\n\n const tabClass = (id: Tab) =>\n `rounded px-2 py-1 text-xs font-medium ${tab === id ? 'bg-accent text-on-accent' : 'text-fg-muted hover:bg-hover'}`;\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1\">\n <button type=\"button\" className={tabClass('draw')} onClick={() => setTab('draw')}>\n {t('demo.makerDraw')}\n </button>\n {fonts.length > 0 ? (\n <button\n type=\"button\"\n className={tabClass('type')}\n onClick={() => {\n setTab('type');\n // The font is read while the name is typed, so the mark is ready when it is.\n registerFont(font).catch((error: unknown) =>\n console.error('[embedpdf] loading the signature font failed:', error),\n );\n }}\n >\n {t('demo.makerType')}\n </button>\n ) : null}\n <button type=\"button\" className={tabClass('upload')} onClick={() => setTab('upload')}>\n {t('demo.makerUpload')}\n </button>\n <div className=\"flex-1\" />\n {value ? (\n <button\n type=\"button\"\n onClick={() => {\n commitStrokes([]);\n setText('');\n setFileName(null);\n onChange(null);\n }}\n className=\"text-fg-muted hover:text-fg text-xs\"\n >\n {t('demo.makerClear')}\n </button>\n ) : null}\n </div>\n {tab === 'draw' ? (\n <canvas\n ref={canvas}\n style={{ width: PAD.width, height: PAD.height, touchAction: 'none' }}\n className=\"border-border-subtle bg-surface text-fg rounded-md border\"\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n drawing.current = [point(event)];\n setStrokes((previous) => [...previous, drawing.current!]);\n }}\n onPointerMove={(event) => {\n if (!drawing.current) return;\n drawing.current.push(point(event));\n setStrokes((previous) => [...previous.slice(0, -1), [...drawing.current!]]);\n }}\n onPointerUp={() => {\n if (!drawing.current) return;\n const done = drawing.current;\n drawing.current = null;\n setStrokes((previous) => {\n const next = [...previous.slice(0, -1), done];\n commitStrokes(next);\n return next;\n });\n }}\n />\n ) : tab === 'type' ? (\n <div className=\"flex flex-col gap-2\">\n <input\n value={text}\n placeholder={t('demo.makerTypePlaceholder')}\n onChange={(event) => void commitText(event.target.value, font)}\n className=\"border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm\"\n />\n <label className=\"text-fg-muted flex items-center gap-2 text-xs\">\n {t('demo.makerFont')}\n <select\n value={font}\n onChange={(event) => void commitText(text, event.target.value)}\n className=\"border-border bg-surface text-fg rounded border px-2 py-1 text-xs\"\n >\n {fonts.map((fontOption) => (\n <option key={fontOption.key} value={fontOption.key}>\n {fontOption.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n ) : (\n <label className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-24 cursor-pointer items-center justify-center rounded-md border border-dashed text-sm\">\n {fileName ?? t('demo.makerUploadHint')}\n <input\n type=\"file\"\n accept=\"image/png,image/jpeg,application/pdf\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.currentTarget.files?.[0];\n event.currentTarget.value = '';\n if (file) pickFile(file);\n }}\n />\n </label>\n )}\n </div>\n );\n}\n\nexport function SignatureMakerModal() {\n const t = useT();\n const surface = useSurface('signature-maker');\n const stamp = useStamp();\n const kernel = useKernel();\n const config = useSignatureSettings();\n const [name, setName] = useState('');\n const [signatureMark, setSignatureMark] = useState<MarkSource | null>(null);\n const [initialsMark, setInitialsMark] = useState<MarkSource | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const existingId = surface.props?.libraryId as string | undefined;\n const existing = existingId ? stamp.getLibrary(existingId) : null;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n const [fonts] = useState(() => markFontsOf(config.fonts));\n // A font is registered on the engine the stamp plugin authors with (the viewer's own) the\n // first time the Type tab opens on it, or a typed mark uses it.\n const registerFont = async (key: string) => {\n const font = fonts.find((each) => each.key === key);\n if (font) await registerMarkFont(kernel.engine, font);\n };\n\n if (!surface.isOpen) return null;\n\n const canSave = existing ? initialsMark != null : name.trim().length > 0 && signatureMark != null;\n const save = async () => {\n setBusy(true);\n setError(null);\n try {\n if (existing) {\n await stamp.createAsset({\n libraryId: existing.id,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark!,\n });\n } else {\n const { library } = await stamp.createLibrary(name.trim(), {\n kind: SIGNATURES_LIBRARY_KIND,\n });\n const libraryId = library.id;\n await stamp.createAsset({\n libraryId,\n name: SIGNATURE_MARK_NAME,\n label: t('demo.signatureLabel'),\n mark: signatureMark!,\n });\n if (initialsMark) {\n await stamp.createAsset({\n libraryId,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark,\n });\n }\n }\n setName('');\n setSignatureMark(null);\n setInitialsMark(null);\n surface.close();\n } catch (error) {\n console.error('[embedpdf] saving the signature failed:', error);\n setError(t('demo.makerError'));\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (!busy) surface.close();\n }}\n className=\"border-border bg-surface w-[26rem] rounded-lg border p-4 shadow-xl\"\n >\n <div className=\"flex items-center justify-between\">\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {existing ? t('demo.signaturesAddInitials') : t('demo.makerTitle')}\n </h2>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-7 w-7 place-items-center rounded-md\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={16} />\n </button>\n </div>\n {existing ? null : (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.makerName')}\n <input\n value={name}\n placeholder={t('demo.makerNamePlaceholder')}\n onChange={(event) => setName(event.target.value)}\n className=\"border-border bg-surface text-fg mt-1 w-full rounded border px-2 py-1.5 text-sm\"\n />\n </label>\n )}\n {existing ? null : (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signatureLabel')}\n </p>\n <MarkPad\n value={signatureMark}\n onChange={setSignatureMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\n )}\n {wantsInitials ? (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.initialsLabel')}\n {existing ? null : (\n <span className=\"text-fg-muted ms-1 font-normal normal-case tracking-normal\">\n {t('demo.makerOptional')}\n </span>\n )}\n </p>\n <MarkPad\n value={initialsMark}\n onChange={setInitialsMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\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}\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 <button\n type=\"button\"\n disabled={busy || !canSave}\n onClick={() => void save()}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy ? t('demo.makerSaving') : t('demo.makerSave')}\n </button>\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"signature-maker-D_YfBXLn.cjs","names":["DEFAULT_SIGNATURE_FONTS","useSignatureSettings","INITIALS_MARK_NAME","SIGNATURES_LIBRARY_KIND","SIGNATURE_MARK_NAME","Dialog","Icon"],"sources":["../src/ui/signature-fonts.ts","../src/ui/signature-maker.tsx"],"sourcesContent":["/**\n * The fonts a typed signature is drawn in: `plugins.signature.fonts`, or three handwriting fonts\n * (`@embedpdf/default-signature-fonts`, each a lazy chunk of your build). A font's file is read\n * the first time the signature maker needs it and registered on the engine once, under its key,\n * which a typed mark's `fontFamily` names; saving embeds the characters the mark uses.\n */\nimport {\n DEFAULT_SIGNATURE_FONTS,\n loadSignatureFont,\n} from '@embedpdf/default-signature-fonts/library';\nimport { isLocalEngine, type Engine } from '@embedpdf/react/runtime';\n\nimport type { SignatureFont } from '../plugin-settings';\n\n/** A font the maker offers: its key, what the picker shows, and how its bytes are read. */\nexport interface MarkFont {\n readonly key: string;\n readonly label: string;\n readonly bytes: () => Promise<Uint8Array>;\n}\n\nconst DEFAULT_FONTS: readonly MarkFont[] = DEFAULT_SIGNATURE_FONTS.map((font) => ({\n key: font.key,\n label: font.label,\n bytes: () => loadSignatureFont(font.key),\n}));\n\n/** The bytes of an app's font: its `data`, or its `url` fetched. */\nconst bytesOf = (font: SignatureFont) => async (): Promise<Uint8Array> => {\n if (font.data) return new Uint8Array(font.data);\n if (!font.url) throw new Error(`[embedpdf] signature font \"${font.key}\" has no url or data`);\n const response = await fetch(font.url);\n if (!response.ok) throw new Error(`${font.url}: HTTP ${response.status}`);\n return new Uint8Array(await response.arrayBuffer());\n};\n\n/** The fonts the maker offers: the app's, which replace the default ones, or the default ones. */\nexport const markFontsOf = (fonts: readonly SignatureFont[] | undefined): readonly MarkFont[] =>\n fonts\n ? fonts.map((font) => ({ key: font.key, label: font.label, bytes: bytesOf(font) }))\n : DEFAULT_FONTS;\n\n/** Each engine's registrations, by font key: one read and one registration per font. */\nconst registrations = new WeakMap<Engine, Map<string, Promise<void>>>();\n\n/**\n * Register `font` on `engine`, unless it already is: what a typed mark in it needs. Engines that\n * aren't local take no fonts from the browser, so nothing happens there. A failed read is tried\n * again next time.\n */\nexport function registerMarkFont(engine: Engine, font: MarkFont): Promise<void> {\n if (!isLocalEngine(engine)) return Promise.resolve();\n let byKey = registrations.get(engine);\n if (!byKey) registrations.set(engine, (byKey = new Map()));\n const known = byKey.get(font.key);\n if (known) return known;\n const registering = font\n .bytes()\n .then((data) => engine.fonts.register({ key: font.key, data }))\n .then(() => undefined);\n byKey.set(font.key, registering);\n registering.catch(() => byKey.delete(font.key));\n return registering;\n}\n","/**\n * The mark maker (`signature-maker` modal surface): a person's signature and\n * initials, drawn, typed, or uploaded, saved as one library of kind\n * `signatures` — the file is the person (Preview's model). Chrome code over\n * the stamp plugin's generic calls: `createLibrary` + `createAsset({ mark })`;\n * rename/export/delete are the library verbs. Opened on an existing library\n * (`props.libraryId`) it only adds the missing initials. Escape cancels,\n * unless it's saving.\n */\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStamp, type MarkSource } from '@embedpdf/react/stamp';\nimport {\n INITIALS_MARK_NAME,\n SIGNATURE_MARK_NAME,\n SIGNATURES_LIBRARY_KIND,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Dialog } from './dialog';\nimport { Icon } from './icons';\nimport { markFontsOf, registerMarkFont, type MarkFont } from './signature-fonts';\n\ntype Tab = 'draw' | 'type' | 'upload';\ntype Stroke = Array<{ x: number; y: number }>;\n\nconst PAD = { width: 360, height: 120 };\n\n/** One pad: draw / type / upload, producing a `MarkSource` or null. */\nfunction MarkPad({\n value,\n onChange,\n fonts,\n registerFont,\n}: {\n value: MarkSource | null;\n onChange: (mark: MarkSource | null) => void;\n fonts: readonly MarkFont[];\n registerFont: (key: string) => Promise<void>;\n}) {\n const t = useT();\n const [tab, setTab] = useState<Tab>('draw');\n const [strokes, setStrokes] = useState<Stroke[]>([]);\n const [text, setText] = useState('');\n const [font, setFont] = useState(fonts[0]?.key ?? '');\n const [fileName, setFileName] = useState<string | null>(null);\n const canvas = useRef<HTMLCanvasElement>(null);\n const drawing = useRef<Stroke | null>(null);\n\n // Paint the strokes (device-pixel sharp).\n useEffect(() => {\n const element = canvas.current;\n if (!element) return;\n const dpr = window.devicePixelRatio || 1;\n element.width = PAD.width * dpr;\n element.height = PAD.height * dpr;\n const ctx = element.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, PAD.width, PAD.height);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.lineWidth = 2.5;\n ctx.strokeStyle = getComputedStyle(element).color || '#000';\n for (const stroke of strokes) {\n ctx.beginPath();\n stroke.forEach((vertex, i) =>\n i === 0 ? ctx.moveTo(vertex.x, vertex.y) : ctx.lineTo(vertex.x, vertex.y),\n );\n ctx.stroke();\n }\n }, [strokes, tab]);\n\n const point = (event: React.PointerEvent<HTMLCanvasElement>) => {\n const rect = event.currentTarget.getBoundingClientRect();\n return { x: event.clientX - rect.left, y: event.clientY - rect.top };\n };\n const commitStrokes = (next: Stroke[]) => {\n setStrokes(next);\n // A mark's strokes go down the page, as the pad's do.\n onChange(\n next.length ? { kind: 'ink', strokes: next, color: '#000000', strokeWidth: 2.5 } : null,\n );\n };\n const commitText = async (next: string, key: string) => {\n setText(next);\n setFont(key);\n if (!next.trim()) {\n onChange(null);\n return;\n }\n await registerFont(key);\n onChange({ kind: 'text', text: next.trim(), fontFamily: key, color: '#000000', fontSize: 36 });\n };\n const pickFile = (file: File) => {\n setFileName(file.name);\n onChange(\n file.type === 'application/pdf'\n ? { kind: 'pdf', source: file, pageIndex: 0 }\n : { kind: 'image', source: file },\n );\n };\n\n const tabClass = (id: Tab) =>\n `rounded px-2 py-1 text-xs font-medium ${tab === id ? 'bg-accent text-on-accent' : 'text-fg-muted hover:bg-hover'}`;\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1\">\n <button type=\"button\" className={tabClass('draw')} onClick={() => setTab('draw')}>\n {t('demo.makerDraw')}\n </button>\n {fonts.length > 0 ? (\n <button\n type=\"button\"\n className={tabClass('type')}\n onClick={() => {\n setTab('type');\n // The font is read while the name is typed, so the mark is ready when it is.\n registerFont(font).catch((error: unknown) =>\n console.error('[embedpdf] loading the signature font failed:', error),\n );\n }}\n >\n {t('demo.makerType')}\n </button>\n ) : null}\n <button type=\"button\" className={tabClass('upload')} onClick={() => setTab('upload')}>\n {t('demo.makerUpload')}\n </button>\n <div className=\"flex-1\" />\n {value ? (\n <button\n type=\"button\"\n onClick={() => {\n commitStrokes([]);\n setText('');\n setFileName(null);\n onChange(null);\n }}\n className=\"text-fg-muted hover:text-fg text-xs\"\n >\n {t('demo.makerClear')}\n </button>\n ) : null}\n </div>\n {tab === 'draw' ? (\n <canvas\n ref={canvas}\n style={{ width: PAD.width, height: PAD.height, touchAction: 'none' }}\n className=\"border-border-subtle bg-surface text-fg rounded-md border\"\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n drawing.current = [point(event)];\n setStrokes((previous) => [...previous, drawing.current!]);\n }}\n onPointerMove={(event) => {\n if (!drawing.current) return;\n drawing.current.push(point(event));\n setStrokes((previous) => [...previous.slice(0, -1), [...drawing.current!]]);\n }}\n onPointerUp={() => {\n if (!drawing.current) return;\n const done = drawing.current;\n drawing.current = null;\n setStrokes((previous) => {\n const next = [...previous.slice(0, -1), done];\n commitStrokes(next);\n return next;\n });\n }}\n />\n ) : tab === 'type' ? (\n <div className=\"flex flex-col gap-2\">\n <input\n value={text}\n placeholder={t('demo.makerTypePlaceholder')}\n onChange={(event) => void commitText(event.target.value, font)}\n className=\"border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm\"\n />\n <label className=\"text-fg-muted flex items-center gap-2 text-xs\">\n {t('demo.makerFont')}\n <select\n value={font}\n onChange={(event) => void commitText(text, event.target.value)}\n className=\"border-border bg-surface text-fg rounded border px-2 py-1 text-xs\"\n >\n {fonts.map((fontOption) => (\n <option key={fontOption.key} value={fontOption.key}>\n {fontOption.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n ) : (\n <label className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-24 cursor-pointer items-center justify-center rounded-md border border-dashed text-sm\">\n {fileName ?? t('demo.makerUploadHint')}\n <input\n type=\"file\"\n accept=\"image/png,image/jpeg,application/pdf\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.currentTarget.files?.[0];\n event.currentTarget.value = '';\n if (file) pickFile(file);\n }}\n />\n </label>\n )}\n </div>\n );\n}\n\nexport function SignatureMakerModal() {\n const t = useT();\n const surface = useSurface('signature-maker');\n const stamp = useStamp();\n const kernel = useKernel();\n const config = useSignatureSettings();\n const [name, setName] = useState('');\n const [signatureMark, setSignatureMark] = useState<MarkSource | null>(null);\n const [initialsMark, setInitialsMark] = useState<MarkSource | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const existingId = surface.props?.libraryId as string | undefined;\n const existing = existingId ? stamp.getLibrary(existingId) : null;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n const [fonts] = useState(() => markFontsOf(config.fonts));\n // A font is registered on the engine the stamp plugin authors with (the viewer's own) the\n // first time the Type tab opens on it, or a typed mark uses it.\n const registerFont = async (key: string) => {\n const font = fonts.find((each) => each.key === key);\n if (font) await registerMarkFont(kernel.engine, font);\n };\n\n if (!surface.isOpen) return null;\n\n const canSave = existing ? initialsMark != null : name.trim().length > 0 && signatureMark != null;\n const save = async () => {\n setBusy(true);\n setError(null);\n try {\n if (existing) {\n await stamp.createAsset({\n libraryId: existing.id,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark!,\n });\n } else {\n const { library } = await stamp.createLibrary(name.trim(), {\n kind: SIGNATURES_LIBRARY_KIND,\n });\n const libraryId = library.id;\n await stamp.createAsset({\n libraryId,\n name: SIGNATURE_MARK_NAME,\n label: t('demo.signatureLabel'),\n mark: signatureMark!,\n });\n if (initialsMark) {\n await stamp.createAsset({\n libraryId,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark,\n });\n }\n }\n setName('');\n setSignatureMark(null);\n setInitialsMark(null);\n surface.close();\n } catch (error) {\n console.error('[embedpdf] saving the signature failed:', error);\n setError(t('demo.makerError'));\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (!busy) surface.close();\n }}\n className=\"border-border bg-surface w-[26rem] rounded-lg border p-4 shadow-xl\"\n >\n <div className=\"flex items-center justify-between\">\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {existing ? t('demo.signaturesAddInitials') : t('demo.makerTitle')}\n </h2>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-7 w-7 place-items-center rounded-md\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={16} />\n </button>\n </div>\n {existing ? null : (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.makerName')}\n <input\n value={name}\n placeholder={t('demo.makerNamePlaceholder')}\n onChange={(event) => setName(event.target.value)}\n className=\"border-border bg-surface text-fg mt-1 w-full rounded border px-2 py-1.5 text-sm\"\n />\n </label>\n )}\n {existing ? null : (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signatureLabel')}\n </p>\n <MarkPad\n value={signatureMark}\n onChange={setSignatureMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\n )}\n {wantsInitials ? (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.initialsLabel')}\n {existing ? null : (\n <span className=\"text-fg-muted ms-1 font-normal normal-case tracking-normal\">\n {t('demo.makerOptional')}\n </span>\n )}\n </p>\n <MarkPad\n value={initialsMark}\n onChange={setInitialsMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\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}\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 <button\n type=\"button\"\n disabled={busy || !canSave}\n onClick={() => void save()}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy ? t('demo.makerSaving') : t('demo.makerSave')}\n </button>\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { g as useSignatureSettings, n as Icon } from "./icons-BrK5iVL8.js";
2
- import { t as Dialog } from "./dialog-B5GBisKv.js";
1
+ import { g as useSignatureSettings, n as Icon } from "./icons-7dB0Y1ct.js";
2
+ import { t as Dialog } from "./dialog-BIb373cg.js";
3
3
  import { useEffect, useId, useRef, useState } from "react";
4
4
  import { isLocalEngine, useKernel } from "@embedpdf/react/runtime";
5
5
  import { useStamp } from "@embedpdf/react/stamp";
@@ -390,4 +390,4 @@ function SignatureMakerModal() {
390
390
  //#endregion
391
391
  export { SignatureMakerModal };
392
392
 
393
- //# sourceMappingURL=signature-maker-BhCmYyHD.js.map
393
+ //# sourceMappingURL=signature-maker-Dnth4rV4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signature-maker-BhCmYyHD.js","names":[],"sources":["../src/ui/signature-fonts.ts","../src/ui/signature-maker.tsx"],"sourcesContent":["/**\n * The fonts a typed signature is drawn in: `plugins.signature.fonts`, or three handwriting fonts\n * (`@embedpdf/default-signature-fonts`, each a lazy chunk of your build). A font's file is read\n * the first time the signature maker needs it and registered on the engine once, under its key,\n * which a typed mark's `fontFamily` names; saving embeds the characters the mark uses.\n */\nimport {\n DEFAULT_SIGNATURE_FONTS,\n loadSignatureFont,\n} from '@embedpdf/default-signature-fonts/library';\nimport { isLocalEngine, type Engine } from '@embedpdf/react/runtime';\n\nimport type { SignatureFont } from '../plugin-settings';\n\n/** A font the maker offers: its key, what the picker shows, and how its bytes are read. */\nexport interface MarkFont {\n readonly key: string;\n readonly label: string;\n readonly bytes: () => Promise<Uint8Array>;\n}\n\nconst DEFAULT_FONTS: readonly MarkFont[] = DEFAULT_SIGNATURE_FONTS.map((font) => ({\n key: font.key,\n label: font.label,\n bytes: () => loadSignatureFont(font.key),\n}));\n\n/** The bytes of an app's font: its `data`, or its `url` fetched. */\nconst bytesOf = (font: SignatureFont) => async (): Promise<Uint8Array> => {\n if (font.data) return new Uint8Array(font.data);\n if (!font.url) throw new Error(`[embedpdf] signature font \"${font.key}\" has no url or data`);\n const response = await fetch(font.url);\n if (!response.ok) throw new Error(`${font.url}: HTTP ${response.status}`);\n return new Uint8Array(await response.arrayBuffer());\n};\n\n/** The fonts the maker offers: the app's, which replace the default ones, or the default ones. */\nexport const markFontsOf = (fonts: readonly SignatureFont[] | undefined): readonly MarkFont[] =>\n fonts\n ? fonts.map((font) => ({ key: font.key, label: font.label, bytes: bytesOf(font) }))\n : DEFAULT_FONTS;\n\n/** Each engine's registrations, by font key: one read and one registration per font. */\nconst registrations = new WeakMap<Engine, Map<string, Promise<void>>>();\n\n/**\n * Register `font` on `engine`, unless it already is: what a typed mark in it needs. Engines that\n * aren't local take no fonts from the browser, so nothing happens there. A failed read is tried\n * again next time.\n */\nexport function registerMarkFont(engine: Engine, font: MarkFont): Promise<void> {\n if (!isLocalEngine(engine)) return Promise.resolve();\n let byKey = registrations.get(engine);\n if (!byKey) registrations.set(engine, (byKey = new Map()));\n const known = byKey.get(font.key);\n if (known) return known;\n const registering = font\n .bytes()\n .then((data) => engine.fonts.register({ key: font.key, data }))\n .then(() => undefined);\n byKey.set(font.key, registering);\n registering.catch(() => byKey.delete(font.key));\n return registering;\n}\n","/**\n * The mark maker (`signature-maker` modal surface): a person's signature and\n * initials, drawn, typed, or uploaded, saved as one library of kind\n * `signatures` — the file is the person (Preview's model). Chrome code over\n * the stamp plugin's generic calls: `createLibrary` + `createAsset({ mark })`;\n * rename/export/delete are the library verbs. Opened on an existing library\n * (`props.libraryId`) it only adds the missing initials. Escape cancels,\n * unless it's saving.\n */\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStamp, type MarkSource } from '@embedpdf/react/stamp';\nimport {\n INITIALS_MARK_NAME,\n SIGNATURE_MARK_NAME,\n SIGNATURES_LIBRARY_KIND,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Dialog } from './dialog';\nimport { Icon } from './icons';\nimport { markFontsOf, registerMarkFont, type MarkFont } from './signature-fonts';\n\ntype Tab = 'draw' | 'type' | 'upload';\ntype Stroke = Array<{ x: number; y: number }>;\n\nconst PAD = { width: 360, height: 120 };\n\n/** One pad: draw / type / upload, producing a `MarkSource` or null. */\nfunction MarkPad({\n value,\n onChange,\n fonts,\n registerFont,\n}: {\n value: MarkSource | null;\n onChange: (mark: MarkSource | null) => void;\n fonts: readonly MarkFont[];\n registerFont: (key: string) => Promise<void>;\n}) {\n const t = useT();\n const [tab, setTab] = useState<Tab>('draw');\n const [strokes, setStrokes] = useState<Stroke[]>([]);\n const [text, setText] = useState('');\n const [font, setFont] = useState(fonts[0]?.key ?? '');\n const [fileName, setFileName] = useState<string | null>(null);\n const canvas = useRef<HTMLCanvasElement>(null);\n const drawing = useRef<Stroke | null>(null);\n\n // Paint the strokes (device-pixel sharp).\n useEffect(() => {\n const element = canvas.current;\n if (!element) return;\n const dpr = window.devicePixelRatio || 1;\n element.width = PAD.width * dpr;\n element.height = PAD.height * dpr;\n const ctx = element.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, PAD.width, PAD.height);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.lineWidth = 2.5;\n ctx.strokeStyle = getComputedStyle(element).color || '#000';\n for (const stroke of strokes) {\n ctx.beginPath();\n stroke.forEach((vertex, i) =>\n i === 0 ? ctx.moveTo(vertex.x, vertex.y) : ctx.lineTo(vertex.x, vertex.y),\n );\n ctx.stroke();\n }\n }, [strokes, tab]);\n\n const point = (event: React.PointerEvent<HTMLCanvasElement>) => {\n const rect = event.currentTarget.getBoundingClientRect();\n return { x: event.clientX - rect.left, y: event.clientY - rect.top };\n };\n const commitStrokes = (next: Stroke[]) => {\n setStrokes(next);\n // A mark's strokes go down the page, as the pad's do.\n onChange(\n next.length ? { kind: 'ink', strokes: next, color: '#000000', strokeWidth: 2.5 } : null,\n );\n };\n const commitText = async (next: string, key: string) => {\n setText(next);\n setFont(key);\n if (!next.trim()) {\n onChange(null);\n return;\n }\n await registerFont(key);\n onChange({ kind: 'text', text: next.trim(), fontFamily: key, color: '#000000', fontSize: 36 });\n };\n const pickFile = (file: File) => {\n setFileName(file.name);\n onChange(\n file.type === 'application/pdf'\n ? { kind: 'pdf', source: file, pageIndex: 0 }\n : { kind: 'image', source: file },\n );\n };\n\n const tabClass = (id: Tab) =>\n `rounded px-2 py-1 text-xs font-medium ${tab === id ? 'bg-accent text-on-accent' : 'text-fg-muted hover:bg-hover'}`;\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1\">\n <button type=\"button\" className={tabClass('draw')} onClick={() => setTab('draw')}>\n {t('demo.makerDraw')}\n </button>\n {fonts.length > 0 ? (\n <button\n type=\"button\"\n className={tabClass('type')}\n onClick={() => {\n setTab('type');\n // The font is read while the name is typed, so the mark is ready when it is.\n registerFont(font).catch((error: unknown) =>\n console.error('[embedpdf] loading the signature font failed:', error),\n );\n }}\n >\n {t('demo.makerType')}\n </button>\n ) : null}\n <button type=\"button\" className={tabClass('upload')} onClick={() => setTab('upload')}>\n {t('demo.makerUpload')}\n </button>\n <div className=\"flex-1\" />\n {value ? (\n <button\n type=\"button\"\n onClick={() => {\n commitStrokes([]);\n setText('');\n setFileName(null);\n onChange(null);\n }}\n className=\"text-fg-muted hover:text-fg text-xs\"\n >\n {t('demo.makerClear')}\n </button>\n ) : null}\n </div>\n {tab === 'draw' ? (\n <canvas\n ref={canvas}\n style={{ width: PAD.width, height: PAD.height, touchAction: 'none' }}\n className=\"border-border-subtle bg-surface text-fg rounded-md border\"\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n drawing.current = [point(event)];\n setStrokes((previous) => [...previous, drawing.current!]);\n }}\n onPointerMove={(event) => {\n if (!drawing.current) return;\n drawing.current.push(point(event));\n setStrokes((previous) => [...previous.slice(0, -1), [...drawing.current!]]);\n }}\n onPointerUp={() => {\n if (!drawing.current) return;\n const done = drawing.current;\n drawing.current = null;\n setStrokes((previous) => {\n const next = [...previous.slice(0, -1), done];\n commitStrokes(next);\n return next;\n });\n }}\n />\n ) : tab === 'type' ? (\n <div className=\"flex flex-col gap-2\">\n <input\n value={text}\n placeholder={t('demo.makerTypePlaceholder')}\n onChange={(event) => void commitText(event.target.value, font)}\n className=\"border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm\"\n />\n <label className=\"text-fg-muted flex items-center gap-2 text-xs\">\n {t('demo.makerFont')}\n <select\n value={font}\n onChange={(event) => void commitText(text, event.target.value)}\n className=\"border-border bg-surface text-fg rounded border px-2 py-1 text-xs\"\n >\n {fonts.map((fontOption) => (\n <option key={fontOption.key} value={fontOption.key}>\n {fontOption.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n ) : (\n <label className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-24 cursor-pointer items-center justify-center rounded-md border border-dashed text-sm\">\n {fileName ?? t('demo.makerUploadHint')}\n <input\n type=\"file\"\n accept=\"image/png,image/jpeg,application/pdf\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.currentTarget.files?.[0];\n event.currentTarget.value = '';\n if (file) pickFile(file);\n }}\n />\n </label>\n )}\n </div>\n );\n}\n\nexport function SignatureMakerModal() {\n const t = useT();\n const surface = useSurface('signature-maker');\n const stamp = useStamp();\n const kernel = useKernel();\n const config = useSignatureSettings();\n const [name, setName] = useState('');\n const [signatureMark, setSignatureMark] = useState<MarkSource | null>(null);\n const [initialsMark, setInitialsMark] = useState<MarkSource | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const existingId = surface.props?.libraryId as string | undefined;\n const existing = existingId ? stamp.getLibrary(existingId) : null;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n const [fonts] = useState(() => markFontsOf(config.fonts));\n // A font is registered on the engine the stamp plugin authors with (the viewer's own) the\n // first time the Type tab opens on it, or a typed mark uses it.\n const registerFont = async (key: string) => {\n const font = fonts.find((each) => each.key === key);\n if (font) await registerMarkFont(kernel.engine, font);\n };\n\n if (!surface.isOpen) return null;\n\n const canSave = existing ? initialsMark != null : name.trim().length > 0 && signatureMark != null;\n const save = async () => {\n setBusy(true);\n setError(null);\n try {\n if (existing) {\n await stamp.createAsset({\n libraryId: existing.id,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark!,\n });\n } else {\n const { library } = await stamp.createLibrary(name.trim(), {\n kind: SIGNATURES_LIBRARY_KIND,\n });\n const libraryId = library.id;\n await stamp.createAsset({\n libraryId,\n name: SIGNATURE_MARK_NAME,\n label: t('demo.signatureLabel'),\n mark: signatureMark!,\n });\n if (initialsMark) {\n await stamp.createAsset({\n libraryId,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark,\n });\n }\n }\n setName('');\n setSignatureMark(null);\n setInitialsMark(null);\n surface.close();\n } catch (error) {\n console.error('[embedpdf] saving the signature failed:', error);\n setError(t('demo.makerError'));\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (!busy) surface.close();\n }}\n className=\"border-border bg-surface w-[26rem] rounded-lg border p-4 shadow-xl\"\n >\n <div className=\"flex items-center justify-between\">\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {existing ? t('demo.signaturesAddInitials') : t('demo.makerTitle')}\n </h2>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-7 w-7 place-items-center rounded-md\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={16} />\n </button>\n </div>\n {existing ? null : (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.makerName')}\n <input\n value={name}\n placeholder={t('demo.makerNamePlaceholder')}\n onChange={(event) => setName(event.target.value)}\n className=\"border-border bg-surface text-fg mt-1 w-full rounded border px-2 py-1.5 text-sm\"\n />\n </label>\n )}\n {existing ? null : (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signatureLabel')}\n </p>\n <MarkPad\n value={signatureMark}\n onChange={setSignatureMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\n )}\n {wantsInitials ? (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.initialsLabel')}\n {existing ? null : (\n <span className=\"text-fg-muted ms-1 font-normal normal-case tracking-normal\">\n {t('demo.makerOptional')}\n </span>\n )}\n </p>\n <MarkPad\n value={initialsMark}\n onChange={setInitialsMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\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}\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 <button\n type=\"button\"\n disabled={busy || !canSave}\n onClick={() => void save()}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy ? t('demo.makerSaving') : t('demo.makerSave')}\n </button>\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"signature-maker-Dnth4rV4.js","names":[],"sources":["../src/ui/signature-fonts.ts","../src/ui/signature-maker.tsx"],"sourcesContent":["/**\n * The fonts a typed signature is drawn in: `plugins.signature.fonts`, or three handwriting fonts\n * (`@embedpdf/default-signature-fonts`, each a lazy chunk of your build). A font's file is read\n * the first time the signature maker needs it and registered on the engine once, under its key,\n * which a typed mark's `fontFamily` names; saving embeds the characters the mark uses.\n */\nimport {\n DEFAULT_SIGNATURE_FONTS,\n loadSignatureFont,\n} from '@embedpdf/default-signature-fonts/library';\nimport { isLocalEngine, type Engine } from '@embedpdf/react/runtime';\n\nimport type { SignatureFont } from '../plugin-settings';\n\n/** A font the maker offers: its key, what the picker shows, and how its bytes are read. */\nexport interface MarkFont {\n readonly key: string;\n readonly label: string;\n readonly bytes: () => Promise<Uint8Array>;\n}\n\nconst DEFAULT_FONTS: readonly MarkFont[] = DEFAULT_SIGNATURE_FONTS.map((font) => ({\n key: font.key,\n label: font.label,\n bytes: () => loadSignatureFont(font.key),\n}));\n\n/** The bytes of an app's font: its `data`, or its `url` fetched. */\nconst bytesOf = (font: SignatureFont) => async (): Promise<Uint8Array> => {\n if (font.data) return new Uint8Array(font.data);\n if (!font.url) throw new Error(`[embedpdf] signature font \"${font.key}\" has no url or data`);\n const response = await fetch(font.url);\n if (!response.ok) throw new Error(`${font.url}: HTTP ${response.status}`);\n return new Uint8Array(await response.arrayBuffer());\n};\n\n/** The fonts the maker offers: the app's, which replace the default ones, or the default ones. */\nexport const markFontsOf = (fonts: readonly SignatureFont[] | undefined): readonly MarkFont[] =>\n fonts\n ? fonts.map((font) => ({ key: font.key, label: font.label, bytes: bytesOf(font) }))\n : DEFAULT_FONTS;\n\n/** Each engine's registrations, by font key: one read and one registration per font. */\nconst registrations = new WeakMap<Engine, Map<string, Promise<void>>>();\n\n/**\n * Register `font` on `engine`, unless it already is: what a typed mark in it needs. Engines that\n * aren't local take no fonts from the browser, so nothing happens there. A failed read is tried\n * again next time.\n */\nexport function registerMarkFont(engine: Engine, font: MarkFont): Promise<void> {\n if (!isLocalEngine(engine)) return Promise.resolve();\n let byKey = registrations.get(engine);\n if (!byKey) registrations.set(engine, (byKey = new Map()));\n const known = byKey.get(font.key);\n if (known) return known;\n const registering = font\n .bytes()\n .then((data) => engine.fonts.register({ key: font.key, data }))\n .then(() => undefined);\n byKey.set(font.key, registering);\n registering.catch(() => byKey.delete(font.key));\n return registering;\n}\n","/**\n * The mark maker (`signature-maker` modal surface): a person's signature and\n * initials, drawn, typed, or uploaded, saved as one library of kind\n * `signatures` — the file is the person (Preview's model). Chrome code over\n * the stamp plugin's generic calls: `createLibrary` + `createAsset({ mark })`;\n * rename/export/delete are the library verbs. Opened on an existing library\n * (`props.libraryId`) it only adds the missing initials. Escape cancels,\n * unless it's saving.\n */\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useStamp, type MarkSource } from '@embedpdf/react/stamp';\nimport {\n INITIALS_MARK_NAME,\n SIGNATURE_MARK_NAME,\n SIGNATURES_LIBRARY_KIND,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Dialog } from './dialog';\nimport { Icon } from './icons';\nimport { markFontsOf, registerMarkFont, type MarkFont } from './signature-fonts';\n\ntype Tab = 'draw' | 'type' | 'upload';\ntype Stroke = Array<{ x: number; y: number }>;\n\nconst PAD = { width: 360, height: 120 };\n\n/** One pad: draw / type / upload, producing a `MarkSource` or null. */\nfunction MarkPad({\n value,\n onChange,\n fonts,\n registerFont,\n}: {\n value: MarkSource | null;\n onChange: (mark: MarkSource | null) => void;\n fonts: readonly MarkFont[];\n registerFont: (key: string) => Promise<void>;\n}) {\n const t = useT();\n const [tab, setTab] = useState<Tab>('draw');\n const [strokes, setStrokes] = useState<Stroke[]>([]);\n const [text, setText] = useState('');\n const [font, setFont] = useState(fonts[0]?.key ?? '');\n const [fileName, setFileName] = useState<string | null>(null);\n const canvas = useRef<HTMLCanvasElement>(null);\n const drawing = useRef<Stroke | null>(null);\n\n // Paint the strokes (device-pixel sharp).\n useEffect(() => {\n const element = canvas.current;\n if (!element) return;\n const dpr = window.devicePixelRatio || 1;\n element.width = PAD.width * dpr;\n element.height = PAD.height * dpr;\n const ctx = element.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, PAD.width, PAD.height);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.lineWidth = 2.5;\n ctx.strokeStyle = getComputedStyle(element).color || '#000';\n for (const stroke of strokes) {\n ctx.beginPath();\n stroke.forEach((vertex, i) =>\n i === 0 ? ctx.moveTo(vertex.x, vertex.y) : ctx.lineTo(vertex.x, vertex.y),\n );\n ctx.stroke();\n }\n }, [strokes, tab]);\n\n const point = (event: React.PointerEvent<HTMLCanvasElement>) => {\n const rect = event.currentTarget.getBoundingClientRect();\n return { x: event.clientX - rect.left, y: event.clientY - rect.top };\n };\n const commitStrokes = (next: Stroke[]) => {\n setStrokes(next);\n // A mark's strokes go down the page, as the pad's do.\n onChange(\n next.length ? { kind: 'ink', strokes: next, color: '#000000', strokeWidth: 2.5 } : null,\n );\n };\n const commitText = async (next: string, key: string) => {\n setText(next);\n setFont(key);\n if (!next.trim()) {\n onChange(null);\n return;\n }\n await registerFont(key);\n onChange({ kind: 'text', text: next.trim(), fontFamily: key, color: '#000000', fontSize: 36 });\n };\n const pickFile = (file: File) => {\n setFileName(file.name);\n onChange(\n file.type === 'application/pdf'\n ? { kind: 'pdf', source: file, pageIndex: 0 }\n : { kind: 'image', source: file },\n );\n };\n\n const tabClass = (id: Tab) =>\n `rounded px-2 py-1 text-xs font-medium ${tab === id ? 'bg-accent text-on-accent' : 'text-fg-muted hover:bg-hover'}`;\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1\">\n <button type=\"button\" className={tabClass('draw')} onClick={() => setTab('draw')}>\n {t('demo.makerDraw')}\n </button>\n {fonts.length > 0 ? (\n <button\n type=\"button\"\n className={tabClass('type')}\n onClick={() => {\n setTab('type');\n // The font is read while the name is typed, so the mark is ready when it is.\n registerFont(font).catch((error: unknown) =>\n console.error('[embedpdf] loading the signature font failed:', error),\n );\n }}\n >\n {t('demo.makerType')}\n </button>\n ) : null}\n <button type=\"button\" className={tabClass('upload')} onClick={() => setTab('upload')}>\n {t('demo.makerUpload')}\n </button>\n <div className=\"flex-1\" />\n {value ? (\n <button\n type=\"button\"\n onClick={() => {\n commitStrokes([]);\n setText('');\n setFileName(null);\n onChange(null);\n }}\n className=\"text-fg-muted hover:text-fg text-xs\"\n >\n {t('demo.makerClear')}\n </button>\n ) : null}\n </div>\n {tab === 'draw' ? (\n <canvas\n ref={canvas}\n style={{ width: PAD.width, height: PAD.height, touchAction: 'none' }}\n className=\"border-border-subtle bg-surface text-fg rounded-md border\"\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n drawing.current = [point(event)];\n setStrokes((previous) => [...previous, drawing.current!]);\n }}\n onPointerMove={(event) => {\n if (!drawing.current) return;\n drawing.current.push(point(event));\n setStrokes((previous) => [...previous.slice(0, -1), [...drawing.current!]]);\n }}\n onPointerUp={() => {\n if (!drawing.current) return;\n const done = drawing.current;\n drawing.current = null;\n setStrokes((previous) => {\n const next = [...previous.slice(0, -1), done];\n commitStrokes(next);\n return next;\n });\n }}\n />\n ) : tab === 'type' ? (\n <div className=\"flex flex-col gap-2\">\n <input\n value={text}\n placeholder={t('demo.makerTypePlaceholder')}\n onChange={(event) => void commitText(event.target.value, font)}\n className=\"border-border bg-surface text-fg w-full rounded border px-2 py-1.5 text-sm\"\n />\n <label className=\"text-fg-muted flex items-center gap-2 text-xs\">\n {t('demo.makerFont')}\n <select\n value={font}\n onChange={(event) => void commitText(text, event.target.value)}\n className=\"border-border bg-surface text-fg rounded border px-2 py-1 text-xs\"\n >\n {fonts.map((fontOption) => (\n <option key={fontOption.key} value={fontOption.key}>\n {fontOption.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n ) : (\n <label className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-24 cursor-pointer items-center justify-center rounded-md border border-dashed text-sm\">\n {fileName ?? t('demo.makerUploadHint')}\n <input\n type=\"file\"\n accept=\"image/png,image/jpeg,application/pdf\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.currentTarget.files?.[0];\n event.currentTarget.value = '';\n if (file) pickFile(file);\n }}\n />\n </label>\n )}\n </div>\n );\n}\n\nexport function SignatureMakerModal() {\n const t = useT();\n const surface = useSurface('signature-maker');\n const stamp = useStamp();\n const kernel = useKernel();\n const config = useSignatureSettings();\n const [name, setName] = useState('');\n const [signatureMark, setSignatureMark] = useState<MarkSource | null>(null);\n const [initialsMark, setInitialsMark] = useState<MarkSource | null>(null);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n const existingId = surface.props?.libraryId as string | undefined;\n const existing = existingId ? stamp.getLibrary(existingId) : null;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n const [fonts] = useState(() => markFontsOf(config.fonts));\n // A font is registered on the engine the stamp plugin authors with (the viewer's own) the\n // first time the Type tab opens on it, or a typed mark uses it.\n const registerFont = async (key: string) => {\n const font = fonts.find((each) => each.key === key);\n if (font) await registerMarkFont(kernel.engine, font);\n };\n\n if (!surface.isOpen) return null;\n\n const canSave = existing ? initialsMark != null : name.trim().length > 0 && signatureMark != null;\n const save = async () => {\n setBusy(true);\n setError(null);\n try {\n if (existing) {\n await stamp.createAsset({\n libraryId: existing.id,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark!,\n });\n } else {\n const { library } = await stamp.createLibrary(name.trim(), {\n kind: SIGNATURES_LIBRARY_KIND,\n });\n const libraryId = library.id;\n await stamp.createAsset({\n libraryId,\n name: SIGNATURE_MARK_NAME,\n label: t('demo.signatureLabel'),\n mark: signatureMark!,\n });\n if (initialsMark) {\n await stamp.createAsset({\n libraryId,\n name: INITIALS_MARK_NAME,\n label: t('demo.initialsLabel'),\n mark: initialsMark,\n });\n }\n }\n setName('');\n setSignatureMark(null);\n setInitialsMark(null);\n surface.close();\n } catch (error) {\n console.error('[embedpdf] saving the signature failed:', error);\n setError(t('demo.makerError'));\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Dialog\n labelledBy={titleId}\n onClose={() => {\n if (!busy) surface.close();\n }}\n className=\"border-border bg-surface w-[26rem] rounded-lg border p-4 shadow-xl\"\n >\n <div className=\"flex items-center justify-between\">\n <h2 id={titleId} className=\"text-fg text-base font-semibold\">\n {existing ? t('demo.signaturesAddInitials') : t('demo.makerTitle')}\n </h2>\n <button\n type=\"button\"\n onClick={surface.close}\n className=\"text-fg-muted hover:bg-hover grid h-7 w-7 place-items-center rounded-md\"\n aria-label={t('demo.cancel')}\n >\n <Icon name=\"x\" size={16} />\n </button>\n </div>\n {existing ? null : (\n <label className=\"text-fg-muted mt-3 block text-xs\">\n {t('demo.makerName')}\n <input\n value={name}\n placeholder={t('demo.makerNamePlaceholder')}\n onChange={(event) => setName(event.target.value)}\n className=\"border-border bg-surface text-fg mt-1 w-full rounded border px-2 py-1.5 text-sm\"\n />\n </label>\n )}\n {existing ? null : (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signatureLabel')}\n </p>\n <MarkPad\n value={signatureMark}\n onChange={setSignatureMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\n )}\n {wantsInitials ? (\n <div className=\"mt-3\">\n <p className=\"text-fg mb-1 text-xs font-semibold uppercase tracking-wide\">\n {t('demo.initialsLabel')}\n {existing ? null : (\n <span className=\"text-fg-muted ms-1 font-normal normal-case tracking-normal\">\n {t('demo.makerOptional')}\n </span>\n )}\n </p>\n <MarkPad\n value={initialsMark}\n onChange={setInitialsMark}\n fonts={fonts}\n registerFont={registerFont}\n />\n </div>\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}\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 <button\n type=\"button\"\n disabled={busy || !canSave}\n onClick={() => void save()}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm disabled:opacity-60\"\n >\n {busy ? t('demo.makerSaving') : t('demo.makerSave')}\n </button>\n </div>\n </Dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqBA,MAAM,gBAAqC,wBAAwB,KAAK,UAAU;CAChF,KAAK,KAAK;CACV,OAAO,KAAK;CACZ,aAAa,kBAAkB,KAAK,GAAG;AACzC,EAAE;;AAGF,MAAM,WAAW,SAAwB,YAAiC;CACxE,IAAI,KAAK,MAAM,OAAO,IAAI,WAAW,KAAK,IAAI;CAC9C,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,MAAM,8BAA8B,KAAK,IAAI,qBAAqB;CAC3F,MAAM,WAAW,MAAM,MAAM,KAAK,GAAG;CACrC,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS,SAAS,QAAQ;CACxE,OAAO,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC;AACpD;;AAGA,MAAa,eAAe,UAC1B,QACI,MAAM,KAAK,UAAU;CAAE,KAAK,KAAK;CAAK,OAAO,KAAK;CAAO,OAAO,QAAQ,IAAI;AAAE,EAAE,IAChF;;AAGN,MAAM,gCAAgB,IAAI,QAA4C;;;;;;AAOtE,SAAgB,iBAAiB,QAAgB,MAA+B;CAC9E,IAAI,CAAC,cAAc,MAAM,GAAG,OAAO,QAAQ,QAAQ;CACnD,IAAI,QAAQ,cAAc,IAAI,MAAM;CACpC,IAAI,CAAC,OAAO,cAAc,IAAI,QAAS,wBAAQ,IAAI,IAAI,CAAE;CACzD,MAAM,QAAQ,MAAM,IAAI,KAAK,GAAG;CAChC,IAAI,OAAO,OAAO;CAClB,MAAM,cAAc,KACjB,MAAM,CAAC,CACP,MAAM,SAAS,OAAO,MAAM,SAAS;EAAE,KAAK,KAAK;EAAK;CAAK,CAAC,CAAC,CAAC,CAC9D,WAAW,KAAA,CAAS;CACvB,MAAM,IAAI,KAAK,KAAK,WAAW;CAC/B,YAAY,YAAY,MAAM,OAAO,KAAK,GAAG,CAAC;CAC9C,OAAO;AACT;;;;;;;;;;;;ACpCA,MAAM,MAAM;CAAE,OAAO;CAAK,QAAQ;AAAI;;AAGtC,SAAS,QAAQ,EACf,OACA,UACA,OACA,gBAMC;CACD,MAAM,IAAI,KAAK;CACf,MAAM,CAAC,KAAK,UAAU,SAAc,MAAM;CAC1C,MAAM,CAAC,SAAS,cAAc,SAAmB,CAAC,CAAC;CACnD,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CACnC,MAAM,CAAC,MAAM,WAAW,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE;CACpD,MAAM,CAAC,UAAU,eAAe,SAAwB,IAAI;CAC5D,MAAM,SAAS,OAA0B,IAAI;CAC7C,MAAM,UAAU,OAAsB,IAAI;CAG1C,gBAAgB;EACd,MAAM,UAAU,OAAO;EACvB,IAAI,CAAC,SAAS;EACd,MAAM,MAAM,OAAO,oBAAoB;EACvC,QAAQ,QAAQ,IAAI,QAAQ;EAC5B,QAAQ,SAAS,IAAI,SAAS;EAC9B,MAAM,MAAM,QAAQ,WAAW,IAAI;EACnC,IAAI,CAAC,KAAK;EACV,IAAI,aAAa,KAAK,GAAG,GAAG,KAAK,GAAG,CAAC;EACrC,IAAI,UAAU,GAAG,GAAG,IAAI,OAAO,IAAI,MAAM;EACzC,IAAI,UAAU;EACd,IAAI,WAAW;EACf,IAAI,YAAY;EAChB,IAAI,cAAc,iBAAiB,OAAO,CAAC,CAAC,SAAS;EACrD,KAAK,MAAM,UAAU,SAAS;GAC5B,IAAI,UAAU;GACd,OAAO,SAAS,QAAQ,MACtB,MAAM,IAAI,IAAI,OAAO,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,OAAO,GAAG,OAAO,CAAC,CAC1E;GACA,IAAI,OAAO;EACb;CACF,GAAG,CAAC,SAAS,GAAG,CAAC;CAEjB,MAAM,SAAS,UAAiD;EAC9D,MAAM,OAAO,MAAM,cAAc,sBAAsB;EACvD,OAAO;GAAE,GAAG,MAAM,UAAU,KAAK;GAAM,GAAG,MAAM,UAAU,KAAK;EAAI;CACrE;CACA,MAAM,iBAAiB,SAAmB;EACxC,WAAW,IAAI;EAEf,SACE,KAAK,SAAS;GAAE,MAAM;GAAO,SAAS;GAAM,OAAO;GAAW,aAAa;EAAI,IAAI,IACrF;CACF;CACA,MAAM,aAAa,OAAO,MAAc,QAAgB;EACtD,QAAQ,IAAI;EACZ,QAAQ,GAAG;EACX,IAAI,CAAC,KAAK,KAAK,GAAG;GAChB,SAAS,IAAI;GACb;EACF;EACA,MAAM,aAAa,GAAG;EACtB,SAAS;GAAE,MAAM;GAAQ,MAAM,KAAK,KAAK;GAAG,YAAY;GAAK,OAAO;GAAW,UAAU;EAAG,CAAC;CAC/F;CACA,MAAM,YAAY,SAAe;EAC/B,YAAY,KAAK,IAAI;EACrB,SACE,KAAK,SAAS,oBACV;GAAE,MAAM;GAAO,QAAQ;GAAM,WAAW;EAAE,IAC1C;GAAE,MAAM;GAAS,QAAQ;EAAK,CACpC;CACF;CAEA,MAAM,YAAY,OAChB,yCAAyC,QAAQ,KAAK,6BAA6B;CACrF,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACE,oBAAC,UAAD;KAAQ,MAAK;KAAS,WAAW,SAAS,MAAM;KAAG,eAAe,OAAO,MAAM;KAC5E,UAAA,EAAE,gBAAgB;IACb,CAAA;IACP,MAAM,SAAS,IACd,oBAAC,UAAD;KACE,MAAK;KACL,WAAW,SAAS,MAAM;KAC1B,eAAe;MACb,OAAO,MAAM;MAEb,aAAa,IAAI,CAAC,CAAC,OAAO,UACxB,QAAQ,MAAM,iDAAiD,KAAK,CACtE;KACF;KAEC,UAAA,EAAE,gBAAgB;IACb,CAAA,IACN;IACJ,oBAAC,UAAD;KAAQ,MAAK;KAAS,WAAW,SAAS,QAAQ;KAAG,eAAe,OAAO,QAAQ;KAChF,UAAA,EAAE,kBAAkB;IACf,CAAA;IACR,oBAAC,OAAD,EAAK,WAAU,SAAU,CAAA;IACxB,QACC,oBAAC,UAAD;KACE,MAAK;KACL,eAAe;MACb,cAAc,CAAC,CAAC;MAChB,QAAQ,EAAE;MACV,YAAY,IAAI;MAChB,SAAS,IAAI;KACf;KACA,WAAU;KAET,UAAA,EAAE,iBAAiB;IACd,CAAA,IACN;GACD;EACJ,CAAA,GAAA,QAAQ,SACP,oBAAC,UAAD;GACE,KAAK;GACL,OAAO;IAAE,OAAO,IAAI;IAAO,QAAQ,IAAI;IAAQ,aAAa;GAAO;GACnE,WAAU;GACV,gBAAgB,UAAU;IACxB,MAAM,cAAc,kBAAkB,MAAM,SAAS;IACrD,QAAQ,UAAU,CAAC,MAAM,KAAK,CAAC;IAC/B,YAAY,aAAa,CAAC,GAAG,UAAU,QAAQ,OAAQ,CAAC;GAC1D;GACA,gBAAgB,UAAU;IACxB,IAAI,CAAC,QAAQ,SAAS;IACtB,QAAQ,QAAQ,KAAK,MAAM,KAAK,CAAC;IACjC,YAAY,aAAa,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,CAAC,GAAG,QAAQ,OAAQ,CAAC,CAAC;GAC5E;GACA,mBAAmB;IACjB,IAAI,CAAC,QAAQ,SAAS;IACtB,MAAM,OAAO,QAAQ;IACrB,QAAQ,UAAU;IAClB,YAAY,aAAa;KACvB,MAAM,OAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,IAAI;KAC5C,cAAc,IAAI;KAClB,OAAO;IACT,CAAC;GACH;EACD,CAAA,IACC,QAAQ,SACV,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,oBAAC,SAAD;IACE,OAAO;IACP,aAAa,EAAE,2BAA2B;IAC1C,WAAW,UAAU,KAAK,WAAW,MAAM,OAAO,OAAO,IAAI;IAC7D,WAAU;GACX,CAAA,GACD,qBAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,gBAAgB,GACnB,oBAAC,UAAD;KACE,OAAO;KACP,WAAW,UAAU,KAAK,WAAW,MAAM,MAAM,OAAO,KAAK;KAC7D,WAAU;KAET,UAAA,MAAM,KAAK,eACV,oBAAC,UAAD;MAA6B,OAAO,WAAW;MAC5C,UAAA,WAAW;KACN,GAFK,WAAW,GAEhB,CACT;IACK,CAAA,CACH;GACJ,CAAA,CAAA;EAEL,CAAA,IAAA,qBAAC,SAAD;GAAO,WAAU;GAAjB,UAAA,CACG,YAAY,EAAE,sBAAsB,GACrC,oBAAC,SAAD;IACE,MAAK;IACL,QAAO;IACP,WAAU;IACV,WAAW,UAAU;KACnB,MAAM,OAAO,MAAM,cAAc,QAAQ;KACzC,MAAM,cAAc,QAAQ;KAC5B,IAAI,MAAM,SAAS,IAAI;IACzB;GACD,CAAA,CACI;EAEN,CAAA,CAAA;;AAET;AAEA,SAAgB,sBAAsB;CACpC,MAAM,IAAI,KAAK;CACf,MAAM,UAAU,WAAW,iBAAiB;CAC5C,MAAM,QAAQ,SAAS;CACvB,MAAM,SAAS,UAAU;CACzB,MAAM,SAAS,qBAAqB;CACpC,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CACnC,MAAM,CAAC,eAAe,oBAAoB,SAA4B,IAAI;CAC1E,MAAM,CAAC,cAAc,mBAAmB,SAA4B,IAAI;CACxE,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,UAAU,MAAM;CAEtB,MAAM,aAAa,QAAQ,OAAO;CAClC,MAAM,WAAW,aAAa,MAAM,WAAW,UAAU,IAAI;CAC7D,MAAM,iBAAiB,OAAO,SAAS,CAAC,aAAa,UAAU,EAAA,CAAG,SAAS,UAAU;CACrF,MAAM,CAAC,SAAS,eAAe,YAAY,OAAO,KAAK,CAAC;CAGxD,MAAM,eAAe,OAAO,QAAgB;EAC1C,MAAM,OAAO,MAAM,MAAM,SAAS,KAAK,QAAQ,GAAG;EAClD,IAAI,MAAM,MAAM,iBAAiB,OAAO,QAAQ,IAAI;CACtD;CAEA,IAAI,CAAC,QAAQ,QAAQ,OAAO;CAE5B,MAAM,UAAU,WAAW,gBAAgB,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,KAAK,iBAAiB;CAC7F,MAAM,OAAO,YAAY;EACvB,QAAQ,IAAI;EACZ,SAAS,IAAI;EACb,IAAI;GACF,IAAI,UACF,MAAM,MAAM,YAAY;IACtB,WAAW,SAAS;IACpB,MAAM;IACN,OAAO,EAAE,oBAAoB;IAC7B,MAAM;GACR,CAAC;QACI;IACL,MAAM,EAAE,YAAY,MAAM,MAAM,cAAc,KAAK,KAAK,GAAG,EACzD,MAAM,wBACR,CAAC;IACD,MAAM,YAAY,QAAQ;IAC1B,MAAM,MAAM,YAAY;KACtB;KACA,MAAM;KACN,OAAO,EAAE,qBAAqB;KAC9B,MAAM;IACR,CAAC;IACD,IAAI,cACF,MAAM,MAAM,YAAY;KACtB;KACA,MAAM;KACN,OAAO,EAAE,oBAAoB;KAC7B,MAAM;IACR,CAAC;GAEL;GACA,QAAQ,EAAE;GACV,iBAAiB,IAAI;GACrB,gBAAgB,IAAI;GACpB,QAAQ,MAAM;EAChB,SAAS,OAAO;GACd,QAAQ,MAAM,2CAA2C,KAAK;GAC9D,SAAS,EAAE,iBAAiB,CAAC;EAC/B,UAAU;GACR,QAAQ,KAAK;EACf;CACF;CAEA,OACE,qBAAC,QAAD;EACE,YAAY;EACZ,eAAe;GACb,IAAI,CAAC,MAAM,QAAQ,MAAM;EAC3B;EACA,WAAU;EALZ,UAAA;GAOE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,MAAD;KAAI,IAAI;KAAS,WAAU;KACxB,UAAA,WAAW,EAAE,4BAA4B,IAAI,EAAE,iBAAiB;IAC/D,CAAA,GACJ,oBAAC,UAAD;KACE,MAAK;KACL,SAAS,QAAQ;KACjB,WAAU;KACV,cAAY,EAAE,aAAa;KAE3B,UAAA,oBAAC,MAAD;MAAM,MAAK;MAAI,MAAM;KAAK,CAAA;IACpB,CAAA,CACL;;GACJ,WAAW,OACV,qBAAC,SAAD;IAAO,WAAU;IAAjB,UAAA,CACG,EAAE,gBAAgB,GACnB,oBAAC,SAAD;KACE,OAAO;KACP,aAAa,EAAE,2BAA2B;KAC1C,WAAW,UAAU,QAAQ,MAAM,OAAO,KAAK;KAC/C,WAAU;IACX,CAAA,CACI;;GAER,WAAW,OACV,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,KAAD;KAAG,WAAU;KACV,UAAA,EAAE,qBAAqB;IACvB,CAAA,GACH,oBAAC,SAAD;KACE,OAAO;KACP,UAAU;KACH;KACO;IACf,CAAA,CACE;;GAEN,gBACC,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,KAAD;KAAG,WAAU;KAAb,UAAA,CACG,EAAE,oBAAoB,GACtB,WAAW,OACV,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,EAAE,oBAAoB;KACnB,CAAA,CAEP;IACH,CAAA,GAAA,oBAAC,SAAD;KACE,OAAO;KACP,UAAU;KACH;KACO;IACf,CAAA,CACE;GACH,CAAA,IAAA;GACH,QAAQ,oBAAC,KAAD;IAAG,WAAU;IAA6B,UAAA;GAAS,CAAA,IAAI;GAChE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,SAAS,QAAQ;KACjB,WAAU;KAET,UAAA,EAAE,aAAa;IACV,CAAA,GACR,oBAAC,UAAD;KACE,MAAK;KACL,UAAU,QAAQ,CAAC;KACnB,eAAe,KAAK,KAAK;KACzB,WAAU;KAET,UAAA,OAAO,EAAE,kBAAkB,IAAI,EAAE,gBAAgB;IAC5C,CAAA,CACL;;EACC;;AAEZ"}
@@ -1,4 +1,4 @@
1
- const require_icons = require("./icons-x0Tc05bs.cjs");
1
+ const require_icons = require("./icons-1UfLUZ_y.cjs");
2
2
  const require_stamp_store = require("./stamp-store-BHvRs-Do.cjs");
3
3
  let react = require("react");
4
4
  let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
@@ -7,6 +7,7 @@ let _embedpdf_react_stamp = require("@embedpdf/react/stamp");
7
7
  let _embedpdf_react_signature = require("@embedpdf/react/signature");
8
8
  let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
9
9
  let _embedpdf_react_shell = require("@embedpdf/react/shell");
10
+ let _embedpdf_web = require("@embedpdf/web");
10
11
  let react_jsx_runtime = require("react/jsx-runtime");
11
12
  //#region src/ui/signatures-panel.tsx
12
13
  /**
@@ -124,14 +125,7 @@ function SignerRowView({ row, armedId, wantsInitials, onPick, onAddInitials }) {
124
125
  const [renaming, setRenaming] = (0, react.useState)(null);
125
126
  const exportPdf = async () => {
126
127
  const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);
127
- if (!bytes) return;
128
- const blob = new Blob([bytes], { type: "application/pdf" });
129
- const url = URL.createObjectURL(blob);
130
- const anchor = document.createElement("a");
131
- anchor.href = url;
132
- anchor.download = `${row.name || "signature"}.pdf`;
133
- anchor.click();
134
- URL.revokeObjectURL(url);
128
+ if (bytes) (0, _embedpdf_web.saveFile)(bytes, `${row.name || "signature"}.pdf`, "application/pdf");
135
129
  };
136
130
  const commitRename = () => {
137
131
  const name = renaming?.trim();
@@ -297,4 +291,4 @@ function DocumentSignatures() {
297
291
  //#endregion
298
292
  exports.SignaturesPanel = SignaturesPanel;
299
293
 
300
- //# sourceMappingURL=signatures-panel-B56PC6d9.cjs.map
294
+ //# sourceMappingURL=signatures-panel-Ci_LaI1C.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signatures-panel-Ci_LaI1C.cjs","names":["useSignatureSettings","restoreStampLibrariesOnce","Icon","SignatureToken"],"sources":["../src/ui/signatures-panel.tsx"],"sourcesContent":["/**\n * The signatures sidebar (right panel), a signature sheet like Preview's.\n * One row per person: a stamp library of kind `signatures` holding a\n * full signature and, optionally, initials. Nothing is stored beyond the\n * file; rename/export/delete are the library verbs.\n *\n * pick a mark → with a target field (\"sign here\" was clicked, or a\n * signature widget selected) the mark goes into that field by the\n * plugin's mode; otherwise it arms — hover a page for the ghost, click a\n * signature field to sign it, click anywhere else to drop it as a stamp.\n *\n * Below the people: the signature fields of this document, signed or not,\n * with their verdicts — click an unsigned one to target it, a signed one to\n * inspect it.\n */\nimport { useEffect, useState } from 'react';\nimport { useInteractionState } from '@embedpdf/react/interaction';\nimport { useSelector } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { saveFile } from '@embedpdf/web';\nimport { useStamp, useStampAssetPreviewUrl, type StampAsset } from '@embedpdf/react/stamp';\nimport {\n SignatureToken,\n useSignature,\n useSignatureState,\n useSignatureEvent,\n useSignerRows,\n type SignatureDTO,\n type SignatureVerdict,\n type SignerRow,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Icon } from './icons';\nimport { restoreStampLibrariesOnce } from './stamp-store';\n\nconst fieldLabel = (\n field: { kind: 'fqn'; name: string } | { kind: 'objectNumber'; objectNumber: number },\n): string => (field.kind === 'fqn' ? field.name : `#${field.objectNumber}`);\n\nexport function SignaturesPanel() {\n const t = useT();\n const stamp = useStamp();\n const signature = useSignature();\n const config = useSignatureSettings();\n const rows = useSignerRows();\n const target = useSignatureState((state) => state.target);\n const busy = useSignatureState((state) => state.busy);\n const { activeToolId } = useInteractionState();\n const maker = useSurface('signature-maker');\n const [armedId, setArmedId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n // Acrobat's warning: an unsaved edit just turned a signature that held into\n // one a save would invalidate. Shown until the next verdicts land.\n const [invalidating, setInvalidating] = useState<string | null>(null);\n useSignatureEvent(\n (signature) => signature.onInvalidationPredicted,\n (event) =>\n setInvalidating(signature.getSignature(event.field)?.fieldName ?? fieldLabel(event.field)),\n );\n useSignatureEvent(\n (signature) => signature.onValidated,\n (event) => {\n if (\n !event.verdicts.some(\n (verdict) =>\n verdict.summary === 'invalid' && verdict.modifications.basis === 'working-copy',\n )\n )\n setInvalidating(null);\n },\n );\n\n // The people live in the same store as the stamps: bring them back on\n // first open (a no-op when the store component already did).\n useEffect(() => {\n void restoreStampLibrariesOnce(stamp);\n }, [stamp]);\n useEffect(() => {\n if (activeToolId !== 'stamp') setArmedId(null);\n }, [activeToolId]);\n\n const pick = (asset: StampAsset) => {\n setError(null);\n if (target) {\n void signature.placeMark({ assetId: asset.id }, { field: target }).catch((error) => {\n console.error('[embedpdf] placing the mark failed:', error);\n setError(t('demo.signError'));\n });\n return;\n }\n setArmedId(asset.id);\n void stamp.armAsset(asset.id).catch((error) => {\n console.error('[embedpdf] arm mark failed:', error);\n setArmedId(null);\n setError(t('demo.stampsArmError'));\n });\n };\n\n const canAddPerson = (config.libraries ?? 'many') === 'many' || rows.length === 0;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n {rows.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-8 text-center\">\n <Icon name=\"signature\" size={32} className=\"text-fg-muted\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.signaturesEmpty')}</p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {rows.map((row) => (\n <SignerRowView\n key={row.libraryId}\n row={row}\n armedId={armedId}\n wantsInitials={wantsInitials}\n onPick={pick}\n onAddInitials={() =>\n maker.open({ exclusive: 'modal', props: { libraryId: row.libraryId } })\n }\n />\n ))}\n </ul>\n )}\n <DocumentSignatures />\n </div>\n\n {invalidating && (\n <div className=\"border-border-subtle shrink-0 border-t px-3 py-2 text-xs text-amber-700\">\n {t('demo.signaturesInvalidating', { params: { field: invalidating } })}\n </div>\n )}\n {(error || target || armedId) && (\n <div\n className={`border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${\n error ? 'text-red-600' : 'text-fg-muted'\n }`}\n >\n {error ??\n (target\n ? busy\n ? t('demo.signSigning')\n : t('demo.signaturesTargetHint')\n : t('demo.signaturesArmedHint'))}\n </div>\n )}\n\n {canAddPerson ? (\n <div className=\"border-border-subtle shrink-0 border-t p-3\">\n <button\n type=\"button\"\n onClick={() => maker.open({ exclusive: 'modal' })}\n className=\"border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm\"\n >\n <Icon name=\"plus\" size={16} />\n {t('demo.signaturesNew')}\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction SignerRowView({\n row,\n armedId,\n wantsInitials,\n onPick,\n onAddInitials,\n}: {\n row: SignerRow;\n armedId: string | null;\n wantsInitials: boolean;\n onPick: (asset: StampAsset) => void;\n onAddInitials: () => void;\n}) {\n const t = useT();\n const stamp = useStamp();\n const [renaming, setRenaming] = useState<string | null>(null);\n\n const exportPdf = async () => {\n const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);\n if (bytes) saveFile(bytes, `${row.name || 'signature'}.pdf`, 'application/pdf');\n };\n const commitRename = () => {\n const name = renaming?.trim();\n setRenaming(null);\n if (name && name !== row.name) void stamp.updateLibrary(row.libraryId, { name });\n };\n const iconButton = 'text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded';\n\n return (\n <li className=\"border-border-subtle rounded-md border p-2\">\n <div className=\"flex items-center gap-1\">\n {renaming !== null ? (\n <input\n autoFocus\n value={renaming}\n onChange={(event) => setRenaming(event.target.value)}\n onBlur={commitRename}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(null);\n }}\n className=\"border-border bg-surface text-fg min-w-0 flex-1 rounded border px-1.5 py-0.5 text-sm\"\n />\n ) : (\n <span className=\"text-fg min-w-0 flex-1 truncate text-sm font-medium\">{row.name}</span>\n )}\n <button\n type=\"button\"\n onClick={() => setRenaming(row.name)}\n title={t('demo.signaturesRename')}\n className={iconButton}\n >\n <Icon name=\"pencilMarker\" size={13} />\n </button>\n <button\n type=\"button\"\n onClick={exportPdf}\n title={t('demo.signaturesExport')}\n className={iconButton}\n >\n <Icon name=\"download\" size={13} />\n </button>\n <button\n type=\"button\"\n onClick={() => void stamp.deleteLibrary(row.libraryId)}\n title={t('demo.signaturesRemove')}\n className={iconButton}\n >\n <Icon name=\"trash\" size={13} />\n </button>\n </div>\n <div className=\"mt-2 grid grid-cols-[2fr_1fr] gap-2\">\n {row.signatures.map((asset) => (\n <MarkThumb\n key={asset.id}\n asset={asset}\n label={t('demo.signatureLabel')}\n armed={armedId === asset.id}\n onPick={() => onPick(asset)}\n />\n ))}\n {wantsInitials ? (\n row.initials ? (\n <MarkThumb\n asset={row.initials}\n label={t('demo.initialsLabel')}\n armed={armedId === row.initials.id}\n onPick={() => onPick(row.initials!)}\n />\n ) : (\n <button\n type=\"button\"\n onClick={onAddInitials}\n title={t('demo.signaturesAddInitials')}\n className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-16 items-center justify-center rounded-md border border-dashed text-xs\"\n >\n <Icon name=\"plus\" size={14} />\n </button>\n )\n ) : null}\n </div>\n </li>\n );\n}\n\nfunction MarkThumb({\n asset,\n label,\n armed,\n onPick,\n}: {\n asset: StampAsset;\n label: string;\n armed: boolean;\n onPick: () => void;\n}) {\n const url = useStampAssetPreviewUrl(asset.id);\n return (\n <button\n type=\"button\"\n onClick={onPick}\n title={label}\n aria-pressed={armed}\n className={`flex h-16 items-center justify-center rounded-md border p-1 ${\n armed\n ? 'border-accent bg-accent-light ring-accent ring-2'\n : 'border-border-subtle hover:border-border hover:bg-hover'\n }`}\n >\n {url ? (\n <img src={url} alt={label} className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-xs\">{label}</span>\n )}\n </button>\n );\n}\n\n/** The signature fields of this document, with their state. */\nfunction DocumentSignatures() {\n const t = useT();\n const signature = useSignature();\n // Every signature field, signed or not; the signed ones carry their verdict in the state.\n const snapshot = useSelector(SignatureToken, (capability) => capability.getSnapshot());\n const signed = useSignatureState((state) => state.signatures);\n const target = useSignatureState((state) => state.target);\n const [validating, setValidating] = useState(false);\n const fields = snapshot?.signatures ?? [];\n const verdictOf = (dto: SignatureDTO) =>\n signed.find((entry) => entry.index === dto.index)?.verdict ?? null;\n // Three honest states: valid; valid on disk but unsaved edits would\n // invalidate it (the verdict judged the working copy); invalid on disk.\n const verdictLabel = (verdict: SignatureVerdict | null): string =>\n !verdict\n ? t('demo.signaturesSigned')\n : verdict.summary === 'valid'\n ? t('demo.verdictValid')\n : verdict.summary === 'valid-untrusted'\n ? t('demo.verdictValidUntrusted')\n : verdict.summary === 'indeterminate'\n ? t('demo.verdictIndeterminate')\n : verdict.modifications.basis === 'working-copy'\n ? t('demo.verdictWillInvalidate')\n : t('demo.verdictInvalid');\n const verdictTone = (verdict: SignatureVerdict | null): string =>\n !verdict || verdict.summary === 'indeterminate'\n ? 'text-fg-muted'\n : verdict.summary === 'invalid'\n ? verdict.modifications.basis === 'working-copy'\n ? 'text-amber-600'\n : 'text-red-600'\n : verdict.summary === 'valid-untrusted'\n ? 'text-amber-600'\n : 'text-green-600';\n const isTarget = (dto: SignatureDTO) =>\n !!target &&\n (target.kind === 'fqn'\n ? target.name === dto.fieldName\n : target.objectNumber === (dto.field.kind === 'objectNumber' ? dto.field.objectNumber : -1));\n const validate = () => {\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n };\n\n return (\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-fg-muted text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signaturesInDocument')}\n </span>\n {fields.some((signature) => signature.signed) ? (\n <button\n type=\"button\"\n disabled={validating}\n onClick={validate}\n className=\"text-accent text-xs hover:underline disabled:opacity-60\"\n >\n {validating ? t('demo.signaturesValidating') : t('demo.signaturesValidate')}\n </button>\n ) : null}\n </div>\n {fields.length === 0 ? (\n <p className=\"text-fg-muted mt-1 text-xs\">{t('demo.signaturesNone')}</p>\n ) : (\n <ul className=\"mt-1 flex flex-col gap-1\">\n {fields.map((dto) => {\n const verdict = verdictOf(dto);\n return (\n <li key={dto.index}>\n <button\n type=\"button\"\n onClick={() =>\n dto.signed\n ? signature.requestInspection(dto.field)\n : signature.setTarget(dto.field)\n }\n className={`hover:bg-hover flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm ${\n isTarget(dto) ? 'bg-accent-light' : ''\n }`}\n >\n <Icon\n name={dto.signed ? 'check' : 'signature'}\n size={14}\n className={dto.signed ? 'text-green-600' : 'text-fg-muted'}\n />\n <span className=\"text-fg min-w-0 flex-1 truncate\">{dto.fieldName}</span>\n <span\n className={`shrink-0 text-end text-xs ${dto.signed ? verdictTone(verdict) : 'text-fg-muted'}`}\n >\n {dto.signed ? verdictLabel(verdict) : t('demo.signaturesUnsigned')}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { g as useSignatureSettings, n as Icon } from "./icons-BrK5iVL8.js";
1
+ import { g as useSignatureSettings, n as Icon } from "./icons-7dB0Y1ct.js";
2
2
  import { n as restoreStampLibrariesOnce } from "./stamp-store-WuF-L3Jj.js";
3
3
  import { useEffect, useState } from "react";
4
4
  import { useSelector } from "@embedpdf/react/runtime";
@@ -7,6 +7,7 @@ import { useStamp, useStampAssetPreviewUrl } from "@embedpdf/react/stamp";
7
7
  import { SignatureToken, useSignature, useSignatureEvent, useSignatureState, useSignerRows } from "@embedpdf/react/signature";
8
8
  import { useT } from "@embedpdf/react/i18n";
9
9
  import { useSurface } from "@embedpdf/react/shell";
10
+ import { saveFile } from "@embedpdf/web";
10
11
  import { jsx, jsxs } from "react/jsx-runtime";
11
12
  //#region src/ui/signatures-panel.tsx
12
13
  /**
@@ -124,14 +125,7 @@ function SignerRowView({ row, armedId, wantsInitials, onPick, onAddInitials }) {
124
125
  const [renaming, setRenaming] = useState(null);
125
126
  const exportPdf = async () => {
126
127
  const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);
127
- if (!bytes) return;
128
- const blob = new Blob([bytes], { type: "application/pdf" });
129
- const url = URL.createObjectURL(blob);
130
- const anchor = document.createElement("a");
131
- anchor.href = url;
132
- anchor.download = `${row.name || "signature"}.pdf`;
133
- anchor.click();
134
- URL.revokeObjectURL(url);
128
+ if (bytes) saveFile(bytes, `${row.name || "signature"}.pdf`, "application/pdf");
135
129
  };
136
130
  const commitRename = () => {
137
131
  const name = renaming?.trim();
@@ -297,4 +291,4 @@ function DocumentSignatures() {
297
291
  //#endregion
298
292
  export { SignaturesPanel };
299
293
 
300
- //# sourceMappingURL=signatures-panel-CgnFfhFm.js.map
294
+ //# sourceMappingURL=signatures-panel-DZ9ywrjz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signatures-panel-DZ9ywrjz.js","names":[],"sources":["../src/ui/signatures-panel.tsx"],"sourcesContent":["/**\n * The signatures sidebar (right panel), a signature sheet like Preview's.\n * One row per person: a stamp library of kind `signatures` holding a\n * full signature and, optionally, initials. Nothing is stored beyond the\n * file; rename/export/delete are the library verbs.\n *\n * pick a mark → with a target field (\"sign here\" was clicked, or a\n * signature widget selected) the mark goes into that field by the\n * plugin's mode; otherwise it arms — hover a page for the ghost, click a\n * signature field to sign it, click anywhere else to drop it as a stamp.\n *\n * Below the people: the signature fields of this document, signed or not,\n * with their verdicts — click an unsigned one to target it, a signed one to\n * inspect it.\n */\nimport { useEffect, useState } from 'react';\nimport { useInteractionState } from '@embedpdf/react/interaction';\nimport { useSelector } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { saveFile } from '@embedpdf/web';\nimport { useStamp, useStampAssetPreviewUrl, type StampAsset } from '@embedpdf/react/stamp';\nimport {\n SignatureToken,\n useSignature,\n useSignatureState,\n useSignatureEvent,\n useSignerRows,\n type SignatureDTO,\n type SignatureVerdict,\n type SignerRow,\n} from '@embedpdf/react/signature';\nimport { useSignatureSettings } from '../config-context';\nimport { Icon } from './icons';\nimport { restoreStampLibrariesOnce } from './stamp-store';\n\nconst fieldLabel = (\n field: { kind: 'fqn'; name: string } | { kind: 'objectNumber'; objectNumber: number },\n): string => (field.kind === 'fqn' ? field.name : `#${field.objectNumber}`);\n\nexport function SignaturesPanel() {\n const t = useT();\n const stamp = useStamp();\n const signature = useSignature();\n const config = useSignatureSettings();\n const rows = useSignerRows();\n const target = useSignatureState((state) => state.target);\n const busy = useSignatureState((state) => state.busy);\n const { activeToolId } = useInteractionState();\n const maker = useSurface('signature-maker');\n const [armedId, setArmedId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n // Acrobat's warning: an unsaved edit just turned a signature that held into\n // one a save would invalidate. Shown until the next verdicts land.\n const [invalidating, setInvalidating] = useState<string | null>(null);\n useSignatureEvent(\n (signature) => signature.onInvalidationPredicted,\n (event) =>\n setInvalidating(signature.getSignature(event.field)?.fieldName ?? fieldLabel(event.field)),\n );\n useSignatureEvent(\n (signature) => signature.onValidated,\n (event) => {\n if (\n !event.verdicts.some(\n (verdict) =>\n verdict.summary === 'invalid' && verdict.modifications.basis === 'working-copy',\n )\n )\n setInvalidating(null);\n },\n );\n\n // The people live in the same store as the stamps: bring them back on\n // first open (a no-op when the store component already did).\n useEffect(() => {\n void restoreStampLibrariesOnce(stamp);\n }, [stamp]);\n useEffect(() => {\n if (activeToolId !== 'stamp') setArmedId(null);\n }, [activeToolId]);\n\n const pick = (asset: StampAsset) => {\n setError(null);\n if (target) {\n void signature.placeMark({ assetId: asset.id }, { field: target }).catch((error) => {\n console.error('[embedpdf] placing the mark failed:', error);\n setError(t('demo.signError'));\n });\n return;\n }\n setArmedId(asset.id);\n void stamp.armAsset(asset.id).catch((error) => {\n console.error('[embedpdf] arm mark failed:', error);\n setArmedId(null);\n setError(t('demo.stampsArmError'));\n });\n };\n\n const canAddPerson = (config.libraries ?? 'many') === 'many' || rows.length === 0;\n const wantsInitials = (config.kinds ?? ['signature', 'initials']).includes('initials');\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n {rows.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-8 text-center\">\n <Icon name=\"signature\" size={32} className=\"text-fg-muted\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.signaturesEmpty')}</p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {rows.map((row) => (\n <SignerRowView\n key={row.libraryId}\n row={row}\n armedId={armedId}\n wantsInitials={wantsInitials}\n onPick={pick}\n onAddInitials={() =>\n maker.open({ exclusive: 'modal', props: { libraryId: row.libraryId } })\n }\n />\n ))}\n </ul>\n )}\n <DocumentSignatures />\n </div>\n\n {invalidating && (\n <div className=\"border-border-subtle shrink-0 border-t px-3 py-2 text-xs text-amber-700\">\n {t('demo.signaturesInvalidating', { params: { field: invalidating } })}\n </div>\n )}\n {(error || target || armedId) && (\n <div\n className={`border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${\n error ? 'text-red-600' : 'text-fg-muted'\n }`}\n >\n {error ??\n (target\n ? busy\n ? t('demo.signSigning')\n : t('demo.signaturesTargetHint')\n : t('demo.signaturesArmedHint'))}\n </div>\n )}\n\n {canAddPerson ? (\n <div className=\"border-border-subtle shrink-0 border-t p-3\">\n <button\n type=\"button\"\n onClick={() => maker.open({ exclusive: 'modal' })}\n className=\"border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm\"\n >\n <Icon name=\"plus\" size={16} />\n {t('demo.signaturesNew')}\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction SignerRowView({\n row,\n armedId,\n wantsInitials,\n onPick,\n onAddInitials,\n}: {\n row: SignerRow;\n armedId: string | null;\n wantsInitials: boolean;\n onPick: (asset: StampAsset) => void;\n onAddInitials: () => void;\n}) {\n const t = useT();\n const stamp = useStamp();\n const [renaming, setRenaming] = useState<string | null>(null);\n\n const exportPdf = async () => {\n const bytes = await stamp.exportLibrary(row.libraryId).catch(() => null);\n if (bytes) saveFile(bytes, `${row.name || 'signature'}.pdf`, 'application/pdf');\n };\n const commitRename = () => {\n const name = renaming?.trim();\n setRenaming(null);\n if (name && name !== row.name) void stamp.updateLibrary(row.libraryId, { name });\n };\n const iconButton = 'text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded';\n\n return (\n <li className=\"border-border-subtle rounded-md border p-2\">\n <div className=\"flex items-center gap-1\">\n {renaming !== null ? (\n <input\n autoFocus\n value={renaming}\n onChange={(event) => setRenaming(event.target.value)}\n onBlur={commitRename}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(null);\n }}\n className=\"border-border bg-surface text-fg min-w-0 flex-1 rounded border px-1.5 py-0.5 text-sm\"\n />\n ) : (\n <span className=\"text-fg min-w-0 flex-1 truncate text-sm font-medium\">{row.name}</span>\n )}\n <button\n type=\"button\"\n onClick={() => setRenaming(row.name)}\n title={t('demo.signaturesRename')}\n className={iconButton}\n >\n <Icon name=\"pencilMarker\" size={13} />\n </button>\n <button\n type=\"button\"\n onClick={exportPdf}\n title={t('demo.signaturesExport')}\n className={iconButton}\n >\n <Icon name=\"download\" size={13} />\n </button>\n <button\n type=\"button\"\n onClick={() => void stamp.deleteLibrary(row.libraryId)}\n title={t('demo.signaturesRemove')}\n className={iconButton}\n >\n <Icon name=\"trash\" size={13} />\n </button>\n </div>\n <div className=\"mt-2 grid grid-cols-[2fr_1fr] gap-2\">\n {row.signatures.map((asset) => (\n <MarkThumb\n key={asset.id}\n asset={asset}\n label={t('demo.signatureLabel')}\n armed={armedId === asset.id}\n onPick={() => onPick(asset)}\n />\n ))}\n {wantsInitials ? (\n row.initials ? (\n <MarkThumb\n asset={row.initials}\n label={t('demo.initialsLabel')}\n armed={armedId === row.initials.id}\n onPick={() => onPick(row.initials!)}\n />\n ) : (\n <button\n type=\"button\"\n onClick={onAddInitials}\n title={t('demo.signaturesAddInitials')}\n className=\"border-border-subtle text-fg-muted hover:bg-hover flex h-16 items-center justify-center rounded-md border border-dashed text-xs\"\n >\n <Icon name=\"plus\" size={14} />\n </button>\n )\n ) : null}\n </div>\n </li>\n );\n}\n\nfunction MarkThumb({\n asset,\n label,\n armed,\n onPick,\n}: {\n asset: StampAsset;\n label: string;\n armed: boolean;\n onPick: () => void;\n}) {\n const url = useStampAssetPreviewUrl(asset.id);\n return (\n <button\n type=\"button\"\n onClick={onPick}\n title={label}\n aria-pressed={armed}\n className={`flex h-16 items-center justify-center rounded-md border p-1 ${\n armed\n ? 'border-accent bg-accent-light ring-accent ring-2'\n : 'border-border-subtle hover:border-border hover:bg-hover'\n }`}\n >\n {url ? (\n <img src={url} alt={label} className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg-muted text-xs\">{label}</span>\n )}\n </button>\n );\n}\n\n/** The signature fields of this document, with their state. */\nfunction DocumentSignatures() {\n const t = useT();\n const signature = useSignature();\n // Every signature field, signed or not; the signed ones carry their verdict in the state.\n const snapshot = useSelector(SignatureToken, (capability) => capability.getSnapshot());\n const signed = useSignatureState((state) => state.signatures);\n const target = useSignatureState((state) => state.target);\n const [validating, setValidating] = useState(false);\n const fields = snapshot?.signatures ?? [];\n const verdictOf = (dto: SignatureDTO) =>\n signed.find((entry) => entry.index === dto.index)?.verdict ?? null;\n // Three honest states: valid; valid on disk but unsaved edits would\n // invalidate it (the verdict judged the working copy); invalid on disk.\n const verdictLabel = (verdict: SignatureVerdict | null): string =>\n !verdict\n ? t('demo.signaturesSigned')\n : verdict.summary === 'valid'\n ? t('demo.verdictValid')\n : verdict.summary === 'valid-untrusted'\n ? t('demo.verdictValidUntrusted')\n : verdict.summary === 'indeterminate'\n ? t('demo.verdictIndeterminate')\n : verdict.modifications.basis === 'working-copy'\n ? t('demo.verdictWillInvalidate')\n : t('demo.verdictInvalid');\n const verdictTone = (verdict: SignatureVerdict | null): string =>\n !verdict || verdict.summary === 'indeterminate'\n ? 'text-fg-muted'\n : verdict.summary === 'invalid'\n ? verdict.modifications.basis === 'working-copy'\n ? 'text-amber-600'\n : 'text-red-600'\n : verdict.summary === 'valid-untrusted'\n ? 'text-amber-600'\n : 'text-green-600';\n const isTarget = (dto: SignatureDTO) =>\n !!target &&\n (target.kind === 'fqn'\n ? target.name === dto.fieldName\n : target.objectNumber === (dto.field.kind === 'objectNumber' ? dto.field.objectNumber : -1));\n const validate = () => {\n setValidating(true);\n void signature.validate().finally(() => setValidating(false));\n };\n\n return (\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-fg-muted text-xs font-semibold uppercase tracking-wide\">\n {t('demo.signaturesInDocument')}\n </span>\n {fields.some((signature) => signature.signed) ? (\n <button\n type=\"button\"\n disabled={validating}\n onClick={validate}\n className=\"text-accent text-xs hover:underline disabled:opacity-60\"\n >\n {validating ? t('demo.signaturesValidating') : t('demo.signaturesValidate')}\n </button>\n ) : null}\n </div>\n {fields.length === 0 ? (\n <p className=\"text-fg-muted mt-1 text-xs\">{t('demo.signaturesNone')}</p>\n ) : (\n <ul className=\"mt-1 flex flex-col gap-1\">\n {fields.map((dto) => {\n const verdict = verdictOf(dto);\n return (\n <li key={dto.index}>\n <button\n type=\"button\"\n onClick={() =>\n dto.signed\n ? signature.requestInspection(dto.field)\n : signature.setTarget(dto.field)\n }\n className={`hover:bg-hover flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm ${\n isTarget(dto) ? 'bg-accent-light' : ''\n }`}\n >\n <Icon\n name={dto.signed ? 'check' : 'signature'}\n size={14}\n className={dto.signed ? 'text-green-600' : 'text-fg-muted'}\n />\n <span className=\"text-fg min-w-0 flex-1 truncate\">{dto.fieldName}</span>\n <span\n className={`shrink-0 text-end text-xs ${dto.signed ? verdictTone(verdict) : 'text-fg-muted'}`}\n >\n {dto.signed ? verdictLabel(verdict) : t('demo.signaturesUnsigned')}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
- import { _ as useStampSettings, n as Icon } from "./icons-BrK5iVL8.js";
1
+ import { _ as useStampSettings, n as Icon } from "./icons-7dB0Y1ct.js";
2
2
  import { i as ensureDefaultLibrary, n as restoreStampLibrariesOnce, o as resolveStampsLocale, r as DEFAULT_LIBRARY_ID } from "./stamp-store-WuF-L3Jj.js";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { useInteractionState } from "@embedpdf/react/interaction";
5
5
  import { useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries } from "@embedpdf/react/stamp";
6
6
  import { useI18nState, useT } from "@embedpdf/react/i18n";
7
7
  import { useSurface } from "@embedpdf/react/shell";
8
+ import { saveFile } from "@embedpdf/web";
8
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/ui/stamps-panel.tsx
10
11
  /**
@@ -80,14 +81,7 @@ function StampsPanel() {
80
81
  * or another viewer. */
81
82
  const exportPdf = async (libraryId, name) => {
82
83
  const bytes = await stamp.exportLibrary(libraryId).catch(() => null);
83
- if (!bytes) return;
84
- const blob = new Blob([bytes], { type: "application/pdf" });
85
- const url = URL.createObjectURL(blob);
86
- const anchor = document.createElement("a");
87
- anchor.href = url;
88
- anchor.download = `${name || "stamps"}.pdf`;
89
- anchor.click();
90
- URL.revokeObjectURL(url);
84
+ if (bytes) saveFile(bytes, `${name || "stamps"}.pdf`, "application/pdf");
91
85
  };
92
86
  const selectedId = libraries.length === 1 ? libraries[0].id : libraries.some((library) => library.id === picked) ? picked : ALL;
93
87
  const selected = selectedId === ALL ? null : libraries.find((library) => library.id === selectedId) ?? null;
@@ -228,4 +222,4 @@ function StampAssetCell({ asset, armed, onArm }) {
228
222
  //#endregion
229
223
  export { StampsPanel };
230
224
 
231
- //# sourceMappingURL=stamps-panel-CDO0FPyK.js.map
225
+ //# sourceMappingURL=stamps-panel-BrczStaz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamps-panel-BrczStaz.js","names":[],"sources":["../src/ui/stamps-panel.tsx"],"sourcesContent":["/**\n * The stamps sidebar (right panel). The stamp plugin owns the libraries, the\n * asset bytes and the cached previews; this panel is the picker over them:\n *\n * a library <select> (\"All stamps\" + one per library, shown once there\n * are two) → a two-column grid of thumbnails → click a stamp → `armAsset`\n * arms the annotation plugin's stamp tool → hovering a page ghosts the\n * exact placement → each click on a page places one.\n *\n * Deliberately not a file dialog: picking arbitrary image bytes is the Image\n * tool's job (a click-then-pick tool, `tool:image`). The stamp button\n * opens a library; a library is a set of reusable, named assets — the two\n * gestures are different, so they are different controls.\n *\n * The panel stays open while you place, so a stamp can be dropped on several\n * pages and swapped without a round trip through the toolbar. A command can\n * open it on a library (`annotation:stamp-from-selection` → \"My stamps\")\n * through the surface's open props.\n */\nimport { useEffect, useRef, useState } from 'react';\nimport { useInteractionState } from '@embedpdf/react/interaction';\nimport {\n useStamp,\n useStampAssetPreviewUrl,\n useStampAssets,\n useStampLibraries,\n type StampAsset,\n} from '@embedpdf/react/stamp';\nimport { useSurface } from '@embedpdf/react/shell';\nimport { useI18nState, useT } from '@embedpdf/react/i18n';\nimport { saveFile } from '@embedpdf/web';\nimport { useStampSettings } from '../config-context';\nimport {\n DEFAULT_LIBRARY_ID,\n ensureDefaultLibrary,\n resolveStampsLocale,\n} from '../config/default-stamps';\nimport { Icon } from './icons';\nimport { restoreStampLibrariesOnce } from './stamp-store';\n\nconst ALL = 'all';\n\nexport function StampsPanel() {\n const t = useT();\n const stamp = useStamp();\n const { standard, sidebar } = useStampSettings();\n // Which catalogs this sidebar shows (`plugins.stamp.sidebar`): the plain stamps by\n // default — never the people's marks, which have their own panel.\n const libraries = useStampLibraries({ kind: sidebar ?? ['stamps'] });\n const assets = useStampAssets();\n const { activeToolId } = useInteractionState();\n const { locale } = useI18nState();\n const surface = useSurface('stamps');\n const fileRef = useRef<HTMLInputElement>(null);\n const [armedId, setArmedId] = useState<string | null>(null);\n const [busy, setBusy] = useState<'loading' | 'importing' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [picked, setPicked] = useState<string>(ALL);\n\n // A command opened the panel on a library (the surface's `libraryId` prop).\n const openedOn = surface.props?.libraryId;\n useEffect(() => {\n if (typeof openedOn === 'string') setPicked(openedOn);\n }, [openedOn]);\n\n // First open: the user's stored libraries (the workspace store restores\n // them; this only awaits it), then the built-in library for this locale —\n // never at viewer boot, so a viewer whose user never opens this panel pays\n // nothing for it. A locale change while open swaps the built-in library;\n // the user's own are untouched.\n useEffect(() => {\n let live = true;\n setBusy('loading');\n restoreStampLibrariesOnce(stamp)\n .then(() => ensureDefaultLibrary(stamp, resolveStampsLocale(locale), standard))\n .catch((error: unknown) => {\n console.error('[embedpdf] default stamps failed:', error);\n if (live) setError(t('demo.stampsError'));\n })\n .finally(() => live && setBusy(null));\n return () => {\n live = false;\n };\n // `t` and `standard` are init-stable; only the locale re-runs this.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stamp, locale]);\n\n // The interaction hub owns tool state: leaving the stamp tool (picking\n // another tool, pressing Escape) un-highlights the gallery.\n useEffect(() => {\n if (activeToolId !== 'stamp') setArmedId(null);\n }, [activeToolId]);\n\n const arm = (asset: StampAsset) => {\n setError(null);\n setArmedId(asset.id);\n void stamp.armAsset(asset.id).catch((error) => {\n console.error('[embedpdf] arm stamp failed:', error);\n setArmedId(null);\n setError(t('demo.stampsArmError'));\n });\n };\n\n const importPdf = (file: File) => {\n setError(null);\n setBusy('importing');\n // The file name is only a fallback: an Acrobat-authored or previously\n // exported library names itself through its /Title.\n stamp\n .importLibrary(file, { name: file.name.replace(/\\.pdf$/i, '') })\n .then(({ library }) => setPicked(library.id))\n .catch((error) => {\n console.error('[embedpdf] stamp library import failed:', error);\n setError(t('demo.stampsImportError'));\n })\n .finally(() => setBusy(null));\n };\n\n /** The library as the PDF it is — title, registry, artwork — for Acrobat\n * or another viewer. */\n const exportPdf = async (libraryId: string, name: string) => {\n const bytes = await stamp.exportLibrary(libraryId).catch(() => null);\n if (bytes) saveFile(bytes, `${name || 'stamps'}.pdf`, 'application/pdf');\n };\n\n // A removed library falls back to \"All\"; the picker never names a ghost.\n // One library needs no picker: it is simply the selection (so its export\n // button is reachable).\n const selectedId =\n libraries.length === 1\n ? libraries[0].id\n : libraries.some((library) => library.id === picked)\n ? picked\n : ALL;\n const selected =\n selectedId === ALL ? null : (libraries.find((library) => library.id === selectedId) ?? null);\n const shown =\n selectedId === ALL ? assets : assets.filter((asset) => asset.libraryId === selectedId);\n const isDefault = (libraryId: string) => libraryId === DEFAULT_LIBRARY_ID;\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n {libraries.length > 0 && (\n <div className=\"border-border-subtle flex items-center gap-1 border-b p-2\">\n {libraries.length > 1 ? (\n <select\n value={selectedId}\n onChange={(event) => setPicked(event.target.value)}\n aria-label={t('panels.stamps')}\n className=\"border-border bg-surface text-fg min-w-0 flex-1 rounded border px-2 py-1.5 text-sm\"\n >\n <option value={ALL}>{t('demo.stampsAll')}</option>\n {libraries.map((library) => (\n <option key={library.id} value={library.id}>\n {library.name}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"text-fg-muted min-w-0 flex-1 truncate px-1 text-xs font-semibold uppercase tracking-wide\">\n {libraries[0].name}\n </span>\n )}\n {selected && (\n <>\n <button\n type=\"button\"\n onClick={() => exportPdf(selected.id, selected.name)}\n title={t('demo.stampsExportLibrary')}\n className=\"text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded\"\n >\n <Icon name=\"download\" size={14} />\n </button>\n {!isDefault(selected.id) && (\n <button\n type=\"button\"\n onClick={() => void stamp.deleteLibrary(selected.id)}\n title={t('demo.stampsRemoveLibrary')}\n className=\"text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded\"\n >\n <Icon name=\"trash\" size={14} />\n </button>\n )}\n </>\n )}\n </div>\n )}\n\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n {busy === 'loading' && shown.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-10 text-center\">\n <div className=\"border-border-subtle border-t-accent h-6 w-6 animate-spin rounded-full border-2\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.stampsLoading')}</p>\n </div>\n ) : shown.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-4 py-10 text-center\">\n <Icon name=\"rubberStamp\" size={32} className=\"text-fg-muted\" />\n <p className=\"text-fg-muted text-sm\">{t('demo.stampsEmpty')}</p>\n </div>\n ) : (\n <ul className=\"grid grid-cols-2 gap-3\">\n {shown.map((asset) => (\n <li key={asset.id} className=\"group relative\">\n <StampAssetCell\n asset={asset}\n armed={armedId === asset.id}\n onArm={() => arm(asset)}\n />\n {!isDefault(asset.libraryId) && (\n <button\n type=\"button\"\n onClick={() => void stamp.deleteAsset(asset.id)}\n title={t('demo.stampsRemoveStamp')}\n className=\"bg-surface border-border text-fg-muted hover:text-fg absolute end-1 top-1 grid h-5 w-5 place-items-center rounded-full border opacity-0 shadow-sm focus:opacity-100 group-hover:opacity-100\"\n >\n <Icon name=\"x\" size={12} />\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n\n {(error || armedId) && (\n <div\n className={`border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${\n error ? 'text-red-600' : 'text-fg-muted'\n }`}\n >\n {error ?? t('demo.stampsArmedHint')}\n </div>\n )}\n\n <div className=\"border-border-subtle shrink-0 border-t p-3\">\n <button\n type=\"button\"\n disabled={busy != null}\n onClick={() => fileRef.current?.click()}\n title={t('demo.stampsImportHint')}\n className=\"border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm disabled:opacity-40\"\n >\n <Icon name=\"rubberStampPlus\" size={16} />\n {busy === 'importing' ? t('demo.stampsImporting') : t('demo.stampsImport')}\n </button>\n <input\n ref={fileRef}\n type=\"file\"\n accept=\"application/pdf\"\n className=\"hidden\"\n onChange={(event) => {\n const file = event.currentTarget.files?.[0];\n event.currentTarget.value = ''; // allow re-picking the same file\n if (file) importPdf(file);\n }}\n />\n </div>\n </div>\n );\n}\n\n/** One grid cell: the plugin's cached preview through the object-URL hook\n * (the DOM resource's lifetime belongs to the framework layer, not the\n * store). Image only — the artwork is the label; the label is the\n * tooltip for the curious. */\nfunction StampAssetCell({\n asset,\n armed,\n onArm,\n}: {\n asset: StampAsset;\n armed: boolean;\n onArm: () => void;\n}) {\n const url = useStampAssetPreviewUrl(asset.id);\n return (\n <button\n type=\"button\"\n onClick={onArm}\n title={asset.label}\n aria-pressed={armed}\n className={`flex aspect-square w-full items-center justify-center rounded-md border p-2 ${\n armed\n ? 'border-accent bg-accent-light ring-accent ring-2'\n : 'border-border-subtle hover:border-border hover:bg-hover'\n }`}\n >\n {url ? (\n <img src={url} alt={asset.label} className=\"max-h-full max-w-full object-contain\" />\n ) : (\n <span className=\"text-fg truncate text-sm\">{asset.label}</span>\n )}\n </button>\n );\n}\n"],"mappings":"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"}