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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +175 -132
  2. package/dist/annotation-fonts-BqoLZ4Mp.js +80 -0
  3. package/dist/annotation-fonts-BqoLZ4Mp.js.map +1 -0
  4. package/dist/annotation-fonts-Dt0IQXhz.cjs +91 -0
  5. package/dist/annotation-fonts-Dt0IQXhz.cjs.map +1 -0
  6. package/dist/annotation-style--rPIZsuP.js +879 -0
  7. package/dist/annotation-style--rPIZsuP.js.map +1 -0
  8. package/dist/annotation-style-Bzvvp8-N.cjs +879 -0
  9. package/dist/annotation-style-Bzvvp8-N.cjs.map +1 -0
  10. package/dist/ar-D9XKAmDx.js +567 -0
  11. package/dist/ar-D9XKAmDx.js.map +1 -0
  12. package/dist/ar-DnEj5vbP.cjs +567 -0
  13. package/dist/ar-DnEj5vbP.cjs.map +1 -0
  14. package/dist/chrome.cjs +378 -221
  15. package/dist/chrome.cjs.map +1 -1
  16. package/dist/chrome.d.cts +5 -34
  17. package/dist/chrome.d.ts +5 -34
  18. package/dist/chrome.js +377 -218
  19. package/dist/chrome.js.map +1 -1
  20. package/dist/commands-BmgxhJ8B.js +953 -0
  21. package/dist/commands-BmgxhJ8B.js.map +1 -0
  22. package/dist/commands-CYt4SFUX.cjs +1060 -0
  23. package/dist/commands-CYt4SFUX.cjs.map +1 -0
  24. package/dist/commands.cjs +12 -595
  25. package/dist/commands.d.cts +29 -4
  26. package/dist/commands.d.ts +29 -4
  27. package/dist/commands.js +2 -593
  28. package/dist/comments-panel-MUVgLCBI.js +514 -0
  29. package/dist/comments-panel-MUVgLCBI.js.map +1 -0
  30. package/dist/comments-panel-pEcQg4Vs.cjs +514 -0
  31. package/dist/comments-panel-pEcQg4Vs.cjs.map +1 -0
  32. package/dist/de-Bdr4aWzm.js +566 -0
  33. package/dist/de-Bdr4aWzm.js.map +1 -0
  34. package/dist/de-Dv6zvbnd.cjs +566 -0
  35. package/dist/de-Dv6zvbnd.cjs.map +1 -0
  36. package/dist/dialog-B5GBisKv.js +55 -0
  37. package/dist/dialog-B5GBisKv.js.map +1 -0
  38. package/dist/dialog-t8F13BL_.cjs +60 -0
  39. package/dist/dialog-t8F13BL_.cjs.map +1 -0
  40. package/dist/es-BP09DR9w.cjs +566 -0
  41. package/dist/es-BP09DR9w.cjs.map +1 -0
  42. package/dist/es-BSXz5tBv.js +566 -0
  43. package/dist/es-BSXz5tBv.js.map +1 -0
  44. package/dist/form-design-panel-B5MXTZ96.cjs +418 -0
  45. package/dist/form-design-panel-B5MXTZ96.cjs.map +1 -0
  46. package/dist/form-design-panel-vWWU9uB-.js +418 -0
  47. package/dist/form-design-panel-vWWU9uB-.js.map +1 -0
  48. package/dist/fr-BBQQn7tJ.cjs +566 -0
  49. package/dist/fr-BBQQn7tJ.cjs.map +1 -0
  50. package/dist/fr-Ckn3K9v1.js +566 -0
  51. package/dist/fr-Ckn3K9v1.js.map +1 -0
  52. package/dist/he-DRsG7bH7.cjs +567 -0
  53. package/dist/he-DRsG7bH7.cjs.map +1 -0
  54. package/dist/he-wdGU5PkU.js +567 -0
  55. package/dist/he-wdGU5PkU.js.map +1 -0
  56. package/dist/icons-BrK5iVL8.js +856 -0
  57. package/dist/icons-BrK5iVL8.js.map +1 -0
  58. package/dist/icons-x0Tc05bs.cjs +1010 -0
  59. package/dist/icons-x0Tc05bs.cjs.map +1 -0
  60. package/dist/index.cjs +3162 -5106
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +636 -143
  63. package/dist/index.d.ts +636 -143
  64. package/dist/index.js +3083 -4975
  65. package/dist/index.js.map +1 -1
  66. package/dist/it-BuP4o0xQ.cjs +566 -0
  67. package/dist/it-BuP4o0xQ.cjs.map +1 -0
  68. package/dist/it-Us-VmvIu.js +566 -0
  69. package/dist/it-Us-VmvIu.js.map +1 -0
  70. package/dist/ja-Cizj0-5O.js +566 -0
  71. package/dist/ja-Cizj0-5O.js.map +1 -0
  72. package/dist/ja-DovEd6ts.cjs +566 -0
  73. package/dist/ja-DovEd6ts.cjs.map +1 -0
  74. package/dist/ko-B-qJEol2.cjs +566 -0
  75. package/dist/ko-B-qJEol2.cjs.map +1 -0
  76. package/dist/ko-DlrXliE1.js +566 -0
  77. package/dist/ko-DlrXliE1.js.map +1 -0
  78. package/dist/labels-BiM9wp9K.cjs +21 -0
  79. package/dist/labels-BiM9wp9K.cjs.map +1 -0
  80. package/dist/labels-C6_i87Oj.js +16 -0
  81. package/dist/labels-C6_i87Oj.js.map +1 -0
  82. package/dist/link-editor-CSjqPnj1.cjs +125 -0
  83. package/dist/link-editor-CSjqPnj1.cjs.map +1 -0
  84. package/dist/link-editor-SxM1nnJU.js +125 -0
  85. package/dist/link-editor-SxM1nnJU.js.map +1 -0
  86. package/dist/measurement-Bs61XWbJ.js +325 -0
  87. package/dist/measurement-Bs61XWbJ.js.map +1 -0
  88. package/dist/measurement-CcCsHPOX.cjs +326 -0
  89. package/dist/measurement-CcCsHPOX.cjs.map +1 -0
  90. package/dist/nl-0aF7M_0h.js +566 -0
  91. package/dist/nl-0aF7M_0h.js.map +1 -0
  92. package/dist/nl-D3KZDDiJ.cjs +566 -0
  93. package/dist/nl-D3KZDDiJ.cjs.map +1 -0
  94. package/dist/pl-BW--AaCI.js +566 -0
  95. package/dist/pl-BW--AaCI.js.map +1 -0
  96. package/dist/pl-DSFhAUqx.cjs +566 -0
  97. package/dist/pl-DSFhAUqx.cjs.map +1 -0
  98. package/dist/pt-C5S-8MC5.cjs +566 -0
  99. package/dist/pt-C5S-8MC5.cjs.map +1 -0
  100. package/dist/pt-DIhpU5pT.js +566 -0
  101. package/dist/pt-DIhpU5pT.js.map +1 -0
  102. package/dist/redact-confirm-B6j36TPw.js +83 -0
  103. package/dist/redact-confirm-B6j36TPw.js.map +1 -0
  104. package/dist/redact-confirm-CX_SY2ZN.cjs +83 -0
  105. package/dist/redact-confirm-CX_SY2ZN.cjs.map +1 -0
  106. package/dist/redaction-panel-BmoIl-Ll.cjs +96 -0
  107. package/dist/redaction-panel-BmoIl-Ll.cjs.map +1 -0
  108. package/dist/redaction-panel-IJDshSJI.js +96 -0
  109. package/dist/redaction-panel-IJDshSJI.js.map +1 -0
  110. package/dist/search-panel-HfIhJm1M.js +249 -0
  111. package/dist/search-panel-HfIhJm1M.js.map +1 -0
  112. package/dist/search-panel-s9IoiOtv.cjs +249 -0
  113. package/dist/search-panel-s9IoiOtv.cjs.map +1 -0
  114. package/dist/sheet-BDsa_Sup.cjs +442 -0
  115. package/dist/sheet-BDsa_Sup.cjs.map +1 -0
  116. package/dist/sheet-R6XDgyEf.js +365 -0
  117. package/dist/sheet-R6XDgyEf.js.map +1 -0
  118. package/dist/sign-dialog-CgnR7akj.cjs +155 -0
  119. package/dist/sign-dialog-CgnR7akj.cjs.map +1 -0
  120. package/dist/sign-dialog-GDqCoQhA.js +155 -0
  121. package/dist/sign-dialog-GDqCoQhA.js.map +1 -0
  122. package/dist/signature-inspector-DyO313Qf.cjs +166 -0
  123. package/dist/signature-inspector-DyO313Qf.cjs.map +1 -0
  124. package/dist/signature-inspector-okFz8do6.js +166 -0
  125. package/dist/signature-inspector-okFz8do6.js.map +1 -0
  126. package/dist/signature-maker-BhCmYyHD.js +393 -0
  127. package/dist/signature-maker-BhCmYyHD.js.map +1 -0
  128. package/dist/signature-maker-BoYa8vgA.cjs +393 -0
  129. package/dist/signature-maker-BoYa8vgA.cjs.map +1 -0
  130. package/dist/signatures-panel-B56PC6d9.cjs +300 -0
  131. package/dist/signatures-panel-B56PC6d9.cjs.map +1 -0
  132. package/dist/signatures-panel-CgnFfhFm.js +300 -0
  133. package/dist/signatures-panel-CgnFfhFm.js.map +1 -0
  134. package/dist/stamp-store-BHvRs-Do.cjs +147 -0
  135. package/dist/stamp-store-BHvRs-Do.cjs.map +1 -0
  136. package/dist/stamp-store-WuF-L3Jj.js +112 -0
  137. package/dist/stamp-store-WuF-L3Jj.js.map +1 -0
  138. package/dist/stamps-panel-B9NYGayn.cjs +231 -0
  139. package/dist/stamps-panel-B9NYGayn.cjs.map +1 -0
  140. package/dist/stamps-panel-CDO0FPyK.js +231 -0
  141. package/dist/stamps-panel-CDO0FPyK.js.map +1 -0
  142. package/dist/toolbar-CJu2wKTU.cjs +1659 -0
  143. package/dist/toolbar-CJu2wKTU.cjs.map +1 -0
  144. package/dist/toolbar-z9od3tKl.js +1564 -0
  145. package/dist/toolbar-z9od3tKl.js.map +1 -0
  146. package/dist/zh-cn-3tmMeHqv.cjs +566 -0
  147. package/dist/zh-cn-3tmMeHqv.cjs.map +1 -0
  148. package/dist/zh-cn-CxkUUBct.js +566 -0
  149. package/dist/zh-cn-CxkUUBct.js.map +1 -0
  150. package/package.json +6 -4
  151. package/src/styles.css +153 -89
  152. package/dist/commands.cjs.map +0 -1
  153. package/dist/commands.js.map +0 -1
  154. package/dist/es-DYwzfxyt.cjs +0 -293
  155. package/dist/es-DYwzfxyt.cjs.map +0 -1
  156. package/dist/es-sN6uqBgD.js +0 -293
  157. package/dist/es-sN6uqBgD.js.map +0 -1
@@ -0,0 +1,16 @@
1
+ import { useT } from "@embedpdf/react/i18n";
2
+ //#region src/ui/labels.ts
3
+ /**
4
+ * The labels the libraries give in English with a translation key beside them: the annotation
5
+ * kinds' style properties and their options, and the form plugin's field settings. The viewer
6
+ * shows each in its language, and the English where a language has no string for the key.
7
+ */
8
+ /** Reads a {@link Labelled} in the viewer's language. */
9
+ function useLabel() {
10
+ const t = useT();
11
+ return (item) => t(item.labelKey, { fallback: item.label });
12
+ }
13
+ //#endregion
14
+ export { useLabel as t };
15
+
16
+ //# sourceMappingURL=labels-C6_i87Oj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels-C6_i87Oj.js","names":[],"sources":["../src/ui/labels.ts"],"sourcesContent":["/**\n * The labels the libraries give in English with a translation key beside them: the annotation\n * kinds' style properties and their options, and the form plugin's field settings. The viewer\n * shows each in its language, and the English where a language has no string for the key.\n */\nimport { useT } from '@embedpdf/react/i18n';\n\n/** A label as the libraries give it: the English, and its key. */\nexport interface Labelled {\n readonly label: string;\n readonly labelKey: string;\n}\n\n/** Reads a {@link Labelled} in the viewer's language. */\nexport function useLabel(): (item: Labelled) => string {\n const t = useT();\n return (item) => t(item.labelKey, { fallback: item.label });\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,WAAuC;CACrD,MAAM,IAAI,KAAK;CACf,QAAQ,SAAS,EAAE,KAAK,UAAU,EAAE,UAAU,KAAK,MAAM,CAAC;AAC5D"}
@@ -0,0 +1,125 @@
1
+ const require_icons = require("./icons-x0Tc05bs.cjs");
2
+ let react = require("react");
3
+ let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
4
+ let _embedpdf_react_annotation = require("@embedpdf/react/annotation");
5
+ let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
6
+ let react_jsx_runtime = require("react/jsx-runtime");
7
+ //#region src/ui/link-editor.tsx
8
+ /**
9
+ * The link editor popover. It rides the same selection anchor as the action
10
+ * strip (`<AnnotationMenu>` solves where), opening from the strip's link icon
11
+ * (`annotation:link` toggles the `link-editor` shell surface) and rendering in
12
+ * place of the strip while open — one anchored card at a time.
13
+ *
14
+ * Reads the selection's current target from selection props (the `linkOf`
15
+ * lens — parents derive from their committed child annotations) and writes
16
+ * through the one `selection.updateLink(target)` path; the plugin's reconciler
17
+ * materializes/retargets the attached children. Links are a verb on the
18
+ * selection, not a style — which is why this is a popover, not a sidebar
19
+ * section.
20
+ */
21
+ function ModeToggle({ active, title, onClick, children }) {
22
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
23
+ type: "button",
24
+ title,
25
+ onClick,
26
+ className: `rounded border px-2.5 py-1 text-xs font-medium transition-colors ${active ? "border-accent bg-accent text-on-accent" : "border-border bg-surface text-fg hover:bg-hover"}`,
27
+ children
28
+ });
29
+ }
30
+ function LinkEditorCard({ onClose }) {
31
+ const t = (0, _embedpdf_react_i18n.useT)();
32
+ const kernel = (0, _embedpdf_react_runtime.useKernel)();
33
+ const anno = (0, _embedpdf_react_annotation.useAnnotation)();
34
+ const value = (0, _embedpdf_react_annotation.useAnnotationProperties)().values.link ?? null;
35
+ const [mode, setMode] = (0, react.useState)(value?.kind === "goto" ? "page" : "uri");
36
+ const [uri, setUri] = (0, react.useState)(value?.kind === "uri" ? value.uri : "");
37
+ const [pageNo, setPageNo] = (0, react.useState)("1");
38
+ const apply = () => {
39
+ if (mode === "uri") {
40
+ const trimmed = uri.trim();
41
+ if (!trimmed) return;
42
+ anno.selection.updateLink({
43
+ kind: "uri",
44
+ uri: trimmed
45
+ });
46
+ onClose();
47
+ return;
48
+ }
49
+ const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));
50
+ if (!page) return;
51
+ anno.selection.updateLink({
52
+ kind: "goto",
53
+ destination: {
54
+ kind: "fit",
55
+ page: page.ref
56
+ }
57
+ });
58
+ onClose();
59
+ };
60
+ const inputCls = "border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none";
61
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
62
+ className: "border-border bg-surface w-64 rounded-lg border p-3 shadow-lg",
63
+ children: [
64
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
65
+ className: "mb-2 flex items-center justify-between",
66
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
67
+ className: "text-fg text-sm font-semibold",
68
+ children: t("commands.annotate.link")
69
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
70
+ type: "button",
71
+ onClick: onClose,
72
+ "aria-label": t("panel.close"),
73
+ title: t("panel.close"),
74
+ className: "text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded",
75
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
76
+ name: "x",
77
+ size: 14
78
+ })
79
+ })]
80
+ }),
81
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
82
+ className: "mb-2 flex gap-1.5",
83
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModeToggle, {
84
+ title: t("linkEditor.toUrl"),
85
+ active: mode === "uri",
86
+ onClick: () => setMode("uri"),
87
+ children: t("linkEditor.url")
88
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModeToggle, {
89
+ title: t("linkEditor.toPage"),
90
+ active: mode === "page",
91
+ onClick: () => setMode("page"),
92
+ children: t("linkEditor.page")
93
+ })]
94
+ }),
95
+ mode === "uri" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
96
+ type: "url",
97
+ className: inputCls,
98
+ placeholder: t("linkEditor.urlPlaceholder"),
99
+ autoFocus: true,
100
+ value: uri,
101
+ onChange: (event) => setUri(event.target.value),
102
+ onKeyDown: (event) => event.key === "Enter" && apply()
103
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
104
+ type: "number",
105
+ min: 1,
106
+ className: inputCls,
107
+ placeholder: t("linkEditor.pagePlaceholder"),
108
+ autoFocus: true,
109
+ value: pageNo,
110
+ onChange: (event) => setPageNo(event.target.value),
111
+ onKeyDown: (event) => event.key === "Enter" && apply()
112
+ }),
113
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
114
+ type: "button",
115
+ className: "bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium",
116
+ onClick: apply,
117
+ children: value == null ? t("linkEditor.add") : t("linkEditor.update")
118
+ })
119
+ ]
120
+ });
121
+ }
122
+ //#endregion
123
+ exports.LinkEditorCard = LinkEditorCard;
124
+
125
+ //# sourceMappingURL=link-editor-CSjqPnj1.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-editor-CSjqPnj1.cjs","names":["Icon"],"sources":["../src/ui/link-editor.tsx"],"sourcesContent":["/**\n * The link editor popover. It rides the same selection anchor as the action\n * strip (`<AnnotationMenu>` solves where), opening from the strip's link icon\n * (`annotation:link` toggles the `link-editor` shell surface) and rendering in\n * place of the strip while open — one anchored card at a time.\n *\n * Reads the selection's current target from selection props (the `linkOf`\n * lens — parents derive from their committed child annotations) and writes\n * through the one `selection.updateLink(target)` path; the plugin's reconciler\n * materializes/retargets the attached children. Links are a verb on the\n * selection, not a style — which is why this is a popover, not a sidebar\n * section.\n */\nimport { useState, type ReactNode } from 'react';\nimport { useAnnotation, useAnnotationProperties } from '@embedpdf/react/annotation';\nimport type { PdfLinkTarget } from '@embedpdf/react/link';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { Icon } from './icons';\n\nfunction ModeToggle({\n active,\n title,\n onClick,\n children,\n}: {\n active: boolean;\n title: string;\n onClick: () => void;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n title={title}\n onClick={onClick}\n className={`rounded border px-2.5 py-1 text-xs font-medium transition-colors ${\n active\n ? 'border-accent bg-accent text-on-accent'\n : 'border-border bg-surface text-fg hover:bg-hover'\n }`}\n >\n {children}\n </button>\n );\n}\n\nexport function LinkEditorCard({ onClose }: { onClose: () => void }) {\n const t = useT();\n const kernel = useKernel();\n const anno = useAnnotation();\n const props = useAnnotationProperties();\n const value = (props.values.link ?? null) as PdfLinkTarget | null;\n const [mode, setMode] = useState<'uri' | 'page'>(value?.kind === 'goto' ? 'page' : 'uri');\n const [uri, setUri] = useState(value?.kind === 'uri' ? value.uri : '');\n const [pageNo, setPageNo] = useState('1');\n\n const apply = () => {\n if (mode === 'uri') {\n const trimmed = uri.trim();\n if (!trimmed) return;\n anno.selection.updateLink({ kind: 'uri', uri: trimmed });\n onClose();\n return;\n }\n // Page number (1-based) → the page's object number (stable across moves).\n const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));\n if (!page) return;\n anno.selection.updateLink({ kind: 'goto', destination: { kind: 'fit', page: page.ref } });\n onClose();\n };\n\n const inputCls =\n 'border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none';\n return (\n <div className=\"border-border bg-surface w-64 rounded-lg border p-3 shadow-lg\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-fg text-sm font-semibold\">{t('commands.annotate.link')}</span>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t('panel.close')}\n title={t('panel.close')}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <div className=\"mb-2 flex gap-1.5\">\n <ModeToggle\n title={t('linkEditor.toUrl')}\n active={mode === 'uri'}\n onClick={() => setMode('uri')}\n >\n {t('linkEditor.url')}\n </ModeToggle>\n <ModeToggle\n title={t('linkEditor.toPage')}\n active={mode === 'page'}\n onClick={() => setMode('page')}\n >\n {t('linkEditor.page')}\n </ModeToggle>\n </div>\n {mode === 'uri' ? (\n <input\n type=\"url\"\n className={inputCls}\n placeholder={t('linkEditor.urlPlaceholder')}\n autoFocus\n value={uri}\n onChange={(event) => setUri(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n className={inputCls}\n placeholder={t('linkEditor.pagePlaceholder')}\n autoFocus\n value={pageNo}\n onChange={(event) => setPageNo(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n )}\n <button\n type=\"button\"\n className=\"bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium\"\n onClick={apply}\n >\n {value == null ? t('linkEditor.add') : t('linkEditor.update')}\n </button>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ import { n as Icon } from "./icons-BrK5iVL8.js";
2
+ import { useState } from "react";
3
+ import { useKernel } from "@embedpdf/react/runtime";
4
+ import { useAnnotation, useAnnotationProperties } from "@embedpdf/react/annotation";
5
+ import { useT } from "@embedpdf/react/i18n";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/ui/link-editor.tsx
8
+ /**
9
+ * The link editor popover. It rides the same selection anchor as the action
10
+ * strip (`<AnnotationMenu>` solves where), opening from the strip's link icon
11
+ * (`annotation:link` toggles the `link-editor` shell surface) and rendering in
12
+ * place of the strip while open — one anchored card at a time.
13
+ *
14
+ * Reads the selection's current target from selection props (the `linkOf`
15
+ * lens — parents derive from their committed child annotations) and writes
16
+ * through the one `selection.updateLink(target)` path; the plugin's reconciler
17
+ * materializes/retargets the attached children. Links are a verb on the
18
+ * selection, not a style — which is why this is a popover, not a sidebar
19
+ * section.
20
+ */
21
+ function ModeToggle({ active, title, onClick, children }) {
22
+ return /* @__PURE__ */ jsx("button", {
23
+ type: "button",
24
+ title,
25
+ onClick,
26
+ className: `rounded border px-2.5 py-1 text-xs font-medium transition-colors ${active ? "border-accent bg-accent text-on-accent" : "border-border bg-surface text-fg hover:bg-hover"}`,
27
+ children
28
+ });
29
+ }
30
+ function LinkEditorCard({ onClose }) {
31
+ const t = useT();
32
+ const kernel = useKernel();
33
+ const anno = useAnnotation();
34
+ const value = useAnnotationProperties().values.link ?? null;
35
+ const [mode, setMode] = useState(value?.kind === "goto" ? "page" : "uri");
36
+ const [uri, setUri] = useState(value?.kind === "uri" ? value.uri : "");
37
+ const [pageNo, setPageNo] = useState("1");
38
+ const apply = () => {
39
+ if (mode === "uri") {
40
+ const trimmed = uri.trim();
41
+ if (!trimmed) return;
42
+ anno.selection.updateLink({
43
+ kind: "uri",
44
+ uri: trimmed
45
+ });
46
+ onClose();
47
+ return;
48
+ }
49
+ const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));
50
+ if (!page) return;
51
+ anno.selection.updateLink({
52
+ kind: "goto",
53
+ destination: {
54
+ kind: "fit",
55
+ page: page.ref
56
+ }
57
+ });
58
+ onClose();
59
+ };
60
+ const inputCls = "border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none";
61
+ return /* @__PURE__ */ jsxs("div", {
62
+ className: "border-border bg-surface w-64 rounded-lg border p-3 shadow-lg",
63
+ children: [
64
+ /* @__PURE__ */ jsxs("div", {
65
+ className: "mb-2 flex items-center justify-between",
66
+ children: [/* @__PURE__ */ jsx("span", {
67
+ className: "text-fg text-sm font-semibold",
68
+ children: t("commands.annotate.link")
69
+ }), /* @__PURE__ */ jsx("button", {
70
+ type: "button",
71
+ onClick: onClose,
72
+ "aria-label": t("panel.close"),
73
+ title: t("panel.close"),
74
+ className: "text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded",
75
+ children: /* @__PURE__ */ jsx(Icon, {
76
+ name: "x",
77
+ size: 14
78
+ })
79
+ })]
80
+ }),
81
+ /* @__PURE__ */ jsxs("div", {
82
+ className: "mb-2 flex gap-1.5",
83
+ children: [/* @__PURE__ */ jsx(ModeToggle, {
84
+ title: t("linkEditor.toUrl"),
85
+ active: mode === "uri",
86
+ onClick: () => setMode("uri"),
87
+ children: t("linkEditor.url")
88
+ }), /* @__PURE__ */ jsx(ModeToggle, {
89
+ title: t("linkEditor.toPage"),
90
+ active: mode === "page",
91
+ onClick: () => setMode("page"),
92
+ children: t("linkEditor.page")
93
+ })]
94
+ }),
95
+ mode === "uri" ? /* @__PURE__ */ jsx("input", {
96
+ type: "url",
97
+ className: inputCls,
98
+ placeholder: t("linkEditor.urlPlaceholder"),
99
+ autoFocus: true,
100
+ value: uri,
101
+ onChange: (event) => setUri(event.target.value),
102
+ onKeyDown: (event) => event.key === "Enter" && apply()
103
+ }) : /* @__PURE__ */ jsx("input", {
104
+ type: "number",
105
+ min: 1,
106
+ className: inputCls,
107
+ placeholder: t("linkEditor.pagePlaceholder"),
108
+ autoFocus: true,
109
+ value: pageNo,
110
+ onChange: (event) => setPageNo(event.target.value),
111
+ onKeyDown: (event) => event.key === "Enter" && apply()
112
+ }),
113
+ /* @__PURE__ */ jsx("button", {
114
+ type: "button",
115
+ className: "bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium",
116
+ onClick: apply,
117
+ children: value == null ? t("linkEditor.add") : t("linkEditor.update")
118
+ })
119
+ ]
120
+ });
121
+ }
122
+ //#endregion
123
+ export { LinkEditorCard };
124
+
125
+ //# sourceMappingURL=link-editor-SxM1nnJU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-editor-SxM1nnJU.js","names":[],"sources":["../src/ui/link-editor.tsx"],"sourcesContent":["/**\n * The link editor popover. It rides the same selection anchor as the action\n * strip (`<AnnotationMenu>` solves where), opening from the strip's link icon\n * (`annotation:link` toggles the `link-editor` shell surface) and rendering in\n * place of the strip while open — one anchored card at a time.\n *\n * Reads the selection's current target from selection props (the `linkOf`\n * lens — parents derive from their committed child annotations) and writes\n * through the one `selection.updateLink(target)` path; the plugin's reconciler\n * materializes/retargets the attached children. Links are a verb on the\n * selection, not a style — which is why this is a popover, not a sidebar\n * section.\n */\nimport { useState, type ReactNode } from 'react';\nimport { useAnnotation, useAnnotationProperties } from '@embedpdf/react/annotation';\nimport type { PdfLinkTarget } from '@embedpdf/react/link';\nimport { useKernel } from '@embedpdf/react/runtime';\nimport { useT } from '@embedpdf/react/i18n';\nimport { Icon } from './icons';\n\nfunction ModeToggle({\n active,\n title,\n onClick,\n children,\n}: {\n active: boolean;\n title: string;\n onClick: () => void;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n title={title}\n onClick={onClick}\n className={`rounded border px-2.5 py-1 text-xs font-medium transition-colors ${\n active\n ? 'border-accent bg-accent text-on-accent'\n : 'border-border bg-surface text-fg hover:bg-hover'\n }`}\n >\n {children}\n </button>\n );\n}\n\nexport function LinkEditorCard({ onClose }: { onClose: () => void }) {\n const t = useT();\n const kernel = useKernel();\n const anno = useAnnotation();\n const props = useAnnotationProperties();\n const value = (props.values.link ?? null) as PdfLinkTarget | null;\n const [mode, setMode] = useState<'uri' | 'page'>(value?.kind === 'goto' ? 'page' : 'uri');\n const [uri, setUri] = useState(value?.kind === 'uri' ? value.uri : '');\n const [pageNo, setPageNo] = useState('1');\n\n const apply = () => {\n if (mode === 'uri') {\n const trimmed = uri.trim();\n if (!trimmed) return;\n anno.selection.updateLink({ kind: 'uri', uri: trimmed });\n onClose();\n return;\n }\n // Page number (1-based) → the page's object number (stable across moves).\n const page = kernel.documents.getPage(Math.max(0, Number(pageNo) - 1));\n if (!page) return;\n anno.selection.updateLink({ kind: 'goto', destination: { kind: 'fit', page: page.ref } });\n onClose();\n };\n\n const inputCls =\n 'border-border bg-surface text-fg focus:border-accent w-full rounded border px-2.5 py-1.5 text-sm outline-none';\n return (\n <div className=\"border-border bg-surface w-64 rounded-lg border p-3 shadow-lg\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-fg text-sm font-semibold\">{t('commands.annotate.link')}</span>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t('panel.close')}\n title={t('panel.close')}\n className=\"text-fg-muted hover:bg-hover grid h-6 w-6 place-items-center rounded\"\n >\n <Icon name=\"x\" size={14} />\n </button>\n </div>\n <div className=\"mb-2 flex gap-1.5\">\n <ModeToggle\n title={t('linkEditor.toUrl')}\n active={mode === 'uri'}\n onClick={() => setMode('uri')}\n >\n {t('linkEditor.url')}\n </ModeToggle>\n <ModeToggle\n title={t('linkEditor.toPage')}\n active={mode === 'page'}\n onClick={() => setMode('page')}\n >\n {t('linkEditor.page')}\n </ModeToggle>\n </div>\n {mode === 'uri' ? (\n <input\n type=\"url\"\n className={inputCls}\n placeholder={t('linkEditor.urlPlaceholder')}\n autoFocus\n value={uri}\n onChange={(event) => setUri(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n className={inputCls}\n placeholder={t('linkEditor.pagePlaceholder')}\n autoFocus\n value={pageNo}\n onChange={(event) => setPageNo(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && apply()}\n />\n )}\n <button\n type=\"button\"\n className=\"bg-accent text-on-accent mt-2.5 w-full rounded px-2 py-1.5 text-sm font-medium\"\n onClick={apply}\n >\n {value == null ? t('linkEditor.add') : t('linkEditor.update')}\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAS,WAAW,EAClB,QACA,OACA,SACA,YAMC;CACD,OACE,oBAAC,UAAD;EACE,MAAK;EACE;EACE;EACT,WAAW,oEACT,SACI,2CACA;EAGL;CACK,CAAA;AAEZ;AAEA,SAAgB,eAAe,EAAE,WAAoC;CACnE,MAAM,IAAI,KAAK;CACf,MAAM,SAAS,UAAU;CACzB,MAAM,OAAO,cAAc;CAE3B,MAAM,QADQ,wBACK,CAAC,CAAC,OAAO,QAAQ;CACpC,MAAM,CAAC,MAAM,WAAW,SAAyB,OAAO,SAAS,SAAS,SAAS,KAAK;CACxF,MAAM,CAAC,KAAK,UAAU,SAAS,OAAO,SAAS,QAAQ,MAAM,MAAM,EAAE;CACrE,MAAM,CAAC,QAAQ,aAAa,SAAS,GAAG;CAExC,MAAM,cAAc;EAClB,IAAI,SAAS,OAAO;GAClB,MAAM,UAAU,IAAI,KAAK;GACzB,IAAI,CAAC,SAAS;GACd,KAAK,UAAU,WAAW;IAAE,MAAM;IAAO,KAAK;GAAQ,CAAC;GACvD,QAAQ;GACR;EACF;EAEA,MAAM,OAAO,OAAO,UAAU,QAAQ,KAAK,IAAI,GAAG,OAAO,MAAM,IAAI,CAAC,CAAC;EACrE,IAAI,CAAC,MAAM;EACX,KAAK,UAAU,WAAW;GAAE,MAAM;GAAQ,aAAa;IAAE,MAAM;IAAO,MAAM,KAAK;GAAI;EAAE,CAAC;EACxF,QAAQ;CACV;CAEA,MAAM,WACJ;CACF,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAAiC,UAAA,EAAE,wBAAwB;IAAQ,CAAA,GACnF,oBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,cAAY,EAAE,aAAa;KAC3B,OAAO,EAAE,aAAa;KACtB,WAAU;KAEV,UAAA,oBAAC,MAAD;MAAM,MAAK;MAAI,MAAM;KAAK,CAAA;IACpB,CAAA,CACL;;GACL,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,YAAD;KACE,OAAO,EAAE,kBAAkB;KAC3B,QAAQ,SAAS;KACjB,eAAe,QAAQ,KAAK;KAE3B,UAAA,EAAE,gBAAgB;IACT,CAAA,GACZ,oBAAC,YAAD;KACE,OAAO,EAAE,mBAAmB;KAC5B,QAAQ,SAAS;KACjB,eAAe,QAAQ,MAAM;KAE5B,UAAA,EAAE,iBAAiB;IACV,CAAA,CACT;;GACJ,SAAS,QACR,oBAAC,SAAD;IACE,MAAK;IACL,WAAW;IACX,aAAa,EAAE,2BAA2B;IAC1C,WAAA;IACA,OAAO;IACP,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;IAC9C,YAAY,UAAU,MAAM,QAAQ,WAAW,MAAM;GACtD,CAAA,IAED,oBAAC,SAAD;IACE,MAAK;IACL,KAAK;IACL,WAAW;IACX,aAAa,EAAE,4BAA4B;IAC3C,WAAA;IACA,OAAO;IACP,WAAW,UAAU,UAAU,MAAM,OAAO,KAAK;IACjD,YAAY,UAAU,MAAM,QAAQ,WAAW,MAAM;GACtD,CAAA;GAEH,oBAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,SAAS;IAER,UAAA,SAAS,OAAO,EAAE,gBAAgB,IAAI,EAAE,mBAAmB;GACtD,CAAA;EACL;;AAET"}
@@ -0,0 +1,325 @@
1
+ import { t as Dialog } from "./dialog-B5GBisKv.js";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { useCapability, useSelector } from "@embedpdf/react/runtime";
4
+ import { StageToken } from "@embedpdf/react/stage";
5
+ import { AnnotationToken, useAnnotationState } from "@embedpdf/react/annotation";
6
+ import { MeasurementToken, useMeasurement, useMeasurementState, usePageScale } from "@embedpdf/react/measurement";
7
+ import { useT } from "@embedpdf/react/i18n";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/ui/measurement.tsx
10
+ const control = "border-border bg-surface text-fg w-full rounded-md border px-2 py-1.5 text-sm disabled:opacity-50";
11
+ const button = "border-border text-fg hover:bg-hover rounded-md border px-3 py-1.5 text-sm disabled:opacity-50";
12
+ /** The current page's address, or null with no page (empty document). */
13
+ const useCurrentPage = () => useSelector(StageToken, (stage) => stage.getCurrentPage());
14
+ function MeasurementSection() {
15
+ const t = useT();
16
+ const page = useCurrentPage();
17
+ const measurement = useMeasurement();
18
+ const busy = useMeasurementState((state) => state.busy);
19
+ const canCalibrate = useSelector(MeasurementToken, (current) => current.canCalibrate());
20
+ const scale = usePageScale(page);
21
+ const anno = useCapability(AnnotationToken);
22
+ const selected = useAnnotationState((state) => state.selected);
23
+ const resettable = useSelector(AnnotationToken, (annotation) => annotation.selection.list().filter((candidate) => (candidate.subtype === "polygon" || candidate.subtype === "polyline") && candidate.captionCenter && annotation.canUpdate(candidate.ref) && !candidate.lockedContents), (left, right) => left.length === right.length && left.every((annotation, i) => annotation === right[i]));
24
+ const readouts = useSelector(MeasurementToken, (measurement) => anno.selection.list().map((annotation) => measurement.getReadout(annotation.ref)).filter((readout) => !("unavailable" in readout)), (left, right) => JSON.stringify(left) === JSON.stringify(right));
25
+ const reports = useMeasurementState((state) => state.lastReports);
26
+ const [allPages, setAllPages] = useState(false);
27
+ const [recalculate, setRecalculate] = useState(true);
28
+ const [error, setError] = useState(null);
29
+ const disabled = !page || busy || !canCalibrate || !scale?.ready;
30
+ const rectilinear = scale?.measure?.subtype === "rectilinear" ? scale.measure : null;
31
+ const target = allPages ? "all" : page;
32
+ const options = { recalculate };
33
+ const run = async (work) => {
34
+ setError(null);
35
+ try {
36
+ await work();
37
+ } catch (error) {
38
+ setError(error instanceof Error ? error.message : String(error));
39
+ }
40
+ };
41
+ const precision = rectilinear?.distance[rectilinear.distance.length - 1]?.precision ?? 100;
42
+ return /* @__PURE__ */ jsxs("div", {
43
+ className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 text-sm",
44
+ children: [
45
+ /* @__PURE__ */ jsx("p", {
46
+ className: "text-fg-muted",
47
+ children: t("measurement.instructions")
48
+ }),
49
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("span", {
50
+ className: "text-fg-muted",
51
+ children: [t("measurement.scale"), ": "]
52
+ }), /* @__PURE__ */ jsx("strong", { children: rectilinear?.ratio ?? t("measurement.unavailable") })] }),
53
+ scale?.error && /* @__PURE__ */ jsx("p", {
54
+ role: "alert",
55
+ className: "text-red-600",
56
+ children: scale.error.message
57
+ }),
58
+ scale && !scale.persistent && scale.ready && /* @__PURE__ */ jsx("p", {
59
+ className: "text-fg-muted",
60
+ children: t("measurement.sessionOnly")
61
+ }),
62
+ /* @__PURE__ */ jsxs("label", {
63
+ className: "flex items-center gap-2",
64
+ children: [/* @__PURE__ */ jsx("input", {
65
+ type: "checkbox",
66
+ checked: allPages,
67
+ disabled: busy,
68
+ onChange: (event) => setAllPages(event.target.checked)
69
+ }), t("measurement.allPages")]
70
+ }),
71
+ /* @__PURE__ */ jsxs("label", {
72
+ className: "flex items-center gap-2",
73
+ children: [/* @__PURE__ */ jsx("input", {
74
+ type: "checkbox",
75
+ checked: recalculate,
76
+ disabled: busy,
77
+ onChange: (event) => setRecalculate(event.target.checked)
78
+ }), t("measurement.recalculate")]
79
+ }),
80
+ /* @__PURE__ */ jsxs("label", { children: [t("measurement.preset"), /* @__PURE__ */ jsxs("select", {
81
+ "aria-label": t("measurement.preset"),
82
+ className: control,
83
+ value: "",
84
+ disabled,
85
+ onChange: (event) => void run(() => measurement.setPreset(target, event.target.value, options)),
86
+ children: [/* @__PURE__ */ jsx("option", {
87
+ value: "",
88
+ disabled: true,
89
+ children: t("measurement.choosePreset")
90
+ }), measurement.listPresets().map((preset) => /* @__PURE__ */ jsx("option", {
91
+ value: preset.id,
92
+ children: preset.label
93
+ }, preset.id))]
94
+ })] }),
95
+ /* @__PURE__ */ jsxs("label", { children: [t("measurement.unit"), /* @__PURE__ */ jsxs("select", {
96
+ "aria-label": t("measurement.unit"),
97
+ className: control,
98
+ value: rectilinear?.distance[0]?.unit.trim() ?? "",
99
+ disabled: disabled || !rectilinear,
100
+ onChange: (event) => void run(() => measurement.setUnit(target, event.target.value, options)),
101
+ children: [!measurement.listUnits().includes(rectilinear?.distance[0]?.unit.trim()) && /* @__PURE__ */ jsx("option", {
102
+ value: rectilinear?.distance[0]?.unit.trim() ?? "",
103
+ children: t("measurement.custom")
104
+ }), measurement.listUnits().map((unit) => /* @__PURE__ */ jsx("option", {
105
+ value: unit,
106
+ children: unit
107
+ }, unit))]
108
+ })] }),
109
+ /* @__PURE__ */ jsxs("label", { children: [t("measurement.areaUnit"), /* @__PURE__ */ jsxs("select", {
110
+ "aria-label": t("measurement.areaUnit"),
111
+ className: control,
112
+ value: rectilinear?.area[0]?.unit.trim().replace("²", "2") ?? "",
113
+ disabled: disabled || !rectilinear,
114
+ onChange: (event) => void run(() => measurement.setAreaUnit(target, event.target.value, options)),
115
+ children: [!measurement.listAreaUnits().includes(rectilinear?.area[0]?.unit.trim().replace("²", "2")) && /* @__PURE__ */ jsx("option", {
116
+ value: rectilinear?.area[0]?.unit.trim().replace("²", "2") ?? "",
117
+ children: t("measurement.custom")
118
+ }), measurement.listAreaUnits().map((unit) => /* @__PURE__ */ jsx("option", {
119
+ value: unit,
120
+ children: unit.replace("2", "²")
121
+ }, unit))]
122
+ })] }),
123
+ /* @__PURE__ */ jsxs("label", { children: [t("measurement.precision"), /* @__PURE__ */ jsxs("select", {
124
+ "aria-label": t("measurement.precision"),
125
+ className: control,
126
+ value: precision,
127
+ disabled: disabled || !rectilinear,
128
+ onChange: (event) => void run(() => measurement.setPrecision(target, Number(event.target.value), options)),
129
+ children: [![
130
+ 1,
131
+ 10,
132
+ 100,
133
+ 1e3,
134
+ 1e4
135
+ ].includes(precision) && /* @__PURE__ */ jsx("option", {
136
+ value: precision,
137
+ children: t("measurement.custom")
138
+ }), [
139
+ 1,
140
+ 10,
141
+ 100,
142
+ 1e3,
143
+ 1e4
144
+ ].map((candidate) => /* @__PURE__ */ jsx("option", {
145
+ value: candidate,
146
+ children: 1 / candidate
147
+ }, candidate))]
148
+ })] }),
149
+ /* @__PURE__ */ jsx("button", {
150
+ className: button,
151
+ disabled,
152
+ onClick: () => measurement.startCalibration(),
153
+ children: t("measurement.calibrate")
154
+ }),
155
+ selected.length > 0 && /* @__PURE__ */ jsxs("section", {
156
+ className: "border-border-subtle border-t pt-3",
157
+ children: [
158
+ /* @__PURE__ */ jsx("h3", {
159
+ className: "mb-1 font-medium",
160
+ children: t("measurement.selection")
161
+ }),
162
+ readouts.length ? readouts.map((readout, i) => /* @__PURE__ */ jsxs("div", {
163
+ className: "mb-2",
164
+ children: [/* @__PURE__ */ jsxs("p", { children: [
165
+ t(`measurement.${readout.kind}`),
166
+ ": ",
167
+ readout.label
168
+ ] }), readout.perimeter && /* @__PURE__ */ jsxs("p", { children: [
169
+ t("measurement.perimeter"),
170
+ ": ",
171
+ readout.perimeter
172
+ ] })]
173
+ }, i)) : /* @__PURE__ */ jsx("p", {
174
+ className: "text-fg-muted",
175
+ children: t("measurement.unavailable")
176
+ }),
177
+ resettable.length > 0 && /* @__PURE__ */ jsx("button", {
178
+ className: button,
179
+ disabled: busy,
180
+ onClick: () => void run(async () => {
181
+ for (const annotation of resettable) if (annotation.subtype === "polygon" || annotation.subtype === "polyline") await anno.update(annotation.ref, {
182
+ subtype: annotation.subtype,
183
+ captionCenter: null
184
+ });
185
+ }),
186
+ children: t("measurement.resetLabel")
187
+ })
188
+ ]
189
+ }),
190
+ reports.length > 0 && /* @__PURE__ */ jsx("p", {
191
+ role: "status",
192
+ children: t("measurement.report", { params: {
193
+ updated: reports.reduce((total, report) => total + report.updated.length, 0),
194
+ skipped: reports.reduce((total, report) => total + report.skipped.length, 0),
195
+ failed: reports.reduce((total, report) => total + report.failed.length + (report.scaleError || report.error ? 1 : 0), 0)
196
+ } })
197
+ }),
198
+ error && /* @__PURE__ */ jsx("p", {
199
+ role: "alert",
200
+ className: "text-red-600",
201
+ children: error
202
+ })
203
+ ]
204
+ });
205
+ }
206
+ function CalibrationDialog() {
207
+ const t = useT();
208
+ const measurement = useMeasurement();
209
+ const { busy, calibrationRequest: request } = useMeasurementState();
210
+ const canCalibrate = useSelector(MeasurementToken, (current) => current.canCalibrate());
211
+ const input = useRef(null);
212
+ const [value, setValue] = useState("");
213
+ const [unit, setUnit] = useState("m");
214
+ const [allPages, setAllPages] = useState(false);
215
+ const [recalculate, setRecalculate] = useState(true);
216
+ const [error, setError] = useState(null);
217
+ useEffect(() => {
218
+ setValue("");
219
+ setError(null);
220
+ input.current?.focus();
221
+ }, [request]);
222
+ if (!request) return null;
223
+ const submit = async () => {
224
+ setError(null);
225
+ try {
226
+ await measurement.calibrate({
227
+ page: request.page,
228
+ from: request.from,
229
+ to: request.to,
230
+ distance: {
231
+ value: Number(value),
232
+ unit
233
+ }
234
+ }, {
235
+ recalculate,
236
+ applyTo: allPages ? "all" : void 0
237
+ });
238
+ measurement.dismissCalibration();
239
+ } catch (error) {
240
+ setError(error instanceof Error ? error.message : String(error));
241
+ }
242
+ };
243
+ return /* @__PURE__ */ jsx(Dialog, {
244
+ labelledBy: "measurement-calibration-title",
245
+ onClose: () => {
246
+ if (!busy) measurement.dismissCalibration();
247
+ },
248
+ className: "border-border bg-surface w-96 rounded-lg border p-5 shadow-xl",
249
+ children: /* @__PURE__ */ jsxs("form", {
250
+ className: "flex flex-col gap-4",
251
+ onSubmit: (event) => {
252
+ event.preventDefault();
253
+ submit();
254
+ },
255
+ children: [
256
+ /* @__PURE__ */ jsx("h2", {
257
+ id: "measurement-calibration-title",
258
+ className: "font-semibold",
259
+ children: t("measurement.calibrate")
260
+ }),
261
+ /* @__PURE__ */ jsx("p", {
262
+ className: "text-fg-muted text-sm",
263
+ children: t("measurement.knownLength")
264
+ }),
265
+ /* @__PURE__ */ jsxs("label", { children: [t("measurement.length"), /* @__PURE__ */ jsx("input", {
266
+ ref: input,
267
+ "aria-label": t("measurement.length"),
268
+ className: control,
269
+ type: "number",
270
+ min: "0",
271
+ step: "any",
272
+ value,
273
+ onChange: (event) => setValue(event.target.value)
274
+ })] }),
275
+ /* @__PURE__ */ jsxs("label", { children: [t("measurement.unit"), /* @__PURE__ */ jsx("select", {
276
+ className: control,
277
+ "aria-label": t("measurement.unit"),
278
+ value: unit,
279
+ onChange: (event) => setUnit(event.target.value),
280
+ children: measurement.listUnits().map((lengthUnit) => /* @__PURE__ */ jsx("option", { children: lengthUnit }, lengthUnit))
281
+ })] }),
282
+ /* @__PURE__ */ jsxs("label", {
283
+ className: "flex items-center gap-2 text-sm",
284
+ children: [/* @__PURE__ */ jsx("input", {
285
+ type: "checkbox",
286
+ checked: allPages,
287
+ onChange: (event) => setAllPages(event.target.checked)
288
+ }), t("measurement.allPages")]
289
+ }),
290
+ /* @__PURE__ */ jsxs("label", {
291
+ className: "flex items-center gap-2 text-sm",
292
+ children: [/* @__PURE__ */ jsx("input", {
293
+ type: "checkbox",
294
+ checked: recalculate,
295
+ onChange: (event) => setRecalculate(event.target.checked)
296
+ }), t("measurement.recalculate")]
297
+ }),
298
+ error && /* @__PURE__ */ jsx("p", {
299
+ role: "alert",
300
+ className: "text-sm text-red-600",
301
+ children: error
302
+ }),
303
+ /* @__PURE__ */ jsxs("div", {
304
+ className: "flex justify-end gap-2",
305
+ children: [/* @__PURE__ */ jsx("button", {
306
+ type: "button",
307
+ className: button,
308
+ disabled: busy,
309
+ onClick: () => measurement.dismissCalibration(),
310
+ children: t("demo.cancel")
311
+ }), /* @__PURE__ */ jsx("button", {
312
+ className: button,
313
+ type: "submit",
314
+ disabled: busy || !canCalibrate || !(Number(value) > 0) || !Number.isFinite(Number(value)),
315
+ children: busy ? t("measurement.applying") : t("measurement.apply")
316
+ })]
317
+ })
318
+ ]
319
+ })
320
+ });
321
+ }
322
+ //#endregion
323
+ export { CalibrationDialog, MeasurementSection };
324
+
325
+ //# sourceMappingURL=measurement-Bs61XWbJ.js.map