@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,879 @@
1
+ const require_icons = require("./icons-x0Tc05bs.cjs");
2
+ const require_annotation_fonts = require("./annotation-fonts-Dt0IQXhz.cjs");
3
+ const require_labels = require("./labels-BiM9wp9K.cjs");
4
+ let react = require("react");
5
+ let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
6
+ let _embedpdf_react_interaction = require("@embedpdf/react/interaction");
7
+ let _embedpdf_react_annotation = require("@embedpdf/react/annotation");
8
+ let _embedpdf_react_redaction = require("@embedpdf/react/redaction");
9
+ let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
10
+ let _embedpdf_react_commands = require("@embedpdf/react/commands");
11
+ let _embedpdf_web = require("@embedpdf/web");
12
+ let react_jsx_runtime = require("react/jsx-runtime");
13
+ //#region src/ui/annotation-flags.tsx
14
+ /**
15
+ * The annotation flags section: a live test surface for `/F` (ISO 32000
16
+ * Table 167). Select any annotation(s) and toggle flags. Flags are
17
+ * properties like any other: the reads come from `useAnnotationProperties()`
18
+ * (a flag the selection disagrees on is `mixed`, an indeterminate checkbox),
19
+ * each named as the schema names it, and the writes go through
20
+ * `selection.update({ locked: true })`. The hint under each is the viewer's.
21
+ *
22
+ * Things to try:
23
+ * - `locked`: the selection keeps its outline but loses handles/knob; move,
24
+ * restyle and delete are refused; unchecking it here still works.
25
+ * - `hidden` / `noView`: the annotation disappears (it stays selected, so
26
+ * this panel can bring it back).
27
+ * - `readOnly`: visible but no longer clickable — deselect and try.
28
+ * - `noZoom` / `noRotate`: zoom or rotate the page; the body keeps its
29
+ * screen size / stays upright, anchored at its top-left corner.
30
+ * - `print`: flip it off and print/flatten elsewhere — the annotation is
31
+ * excluded (viewer rendering is unaffected).
32
+ */
33
+ /** What each flag does, in a few words under its name: the hint's key. */
34
+ const FLAG_HINTS = {
35
+ print: "annotationFlags.print",
36
+ hidden: "annotationFlags.hidden",
37
+ noView: "annotationFlags.noView",
38
+ toggleNoView: "annotationFlags.toggleNoView",
39
+ readOnly: "annotationFlags.readOnly",
40
+ locked: "annotationFlags.locked",
41
+ lockedContents: "annotationFlags.lockedContents",
42
+ noZoom: "annotationFlags.noZoom",
43
+ noRotate: "annotationFlags.noRotate",
44
+ invisible: "annotationFlags.invisible"
45
+ };
46
+ function FlagRow({ label, hint, value, onToggle }) {
47
+ const ref = (0, react.useRef)(null);
48
+ (0, react.useEffect)(() => {
49
+ if (ref.current) ref.current.indeterminate = value === null;
50
+ }, [value]);
51
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
52
+ className: "hover:bg-hover flex cursor-pointer items-start gap-2.5 rounded px-1.5 py-1.5",
53
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
54
+ ref,
55
+ type: "checkbox",
56
+ className: "accent-accent mt-0.5",
57
+ checked: value === true,
58
+ onChange: () => onToggle(value !== true)
59
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
60
+ className: "min-w-0",
61
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
62
+ className: "text-fg block text-sm leading-tight",
63
+ children: label
64
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
65
+ className: "text-fg-muted block text-xs leading-tight",
66
+ children: hint
67
+ })]
68
+ })]
69
+ });
70
+ }
71
+ /** The `/F` flags of the current selection, live-editable. Renders nothing
72
+ * when nothing is selected (the style panel shows its tool defaults then). */
73
+ function AnnotationFlagsSection() {
74
+ const t = (0, _embedpdf_react_i18n.useT)();
75
+ const label = require_labels.useLabel();
76
+ const annotation = (0, _embedpdf_react_annotation.useAnnotation)();
77
+ const { properties, values, mixed } = (0, _embedpdf_react_annotation.useAnnotationProperties)();
78
+ const flags = properties.filter((property) => property.control === "flag");
79
+ if (!flags.length) return null;
80
+ const valueOf = (key) => mixed.includes(key) ? null : values[key] === true;
81
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
82
+ className: "border-border mt-2 border-t pt-4",
83
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
84
+ className: "text-fg-muted mb-2 text-[11px] font-semibold uppercase tracking-wide",
85
+ children: t("annotationFlags.title")
86
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
87
+ className: "flex flex-col",
88
+ children: flags.map((flag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FlagRow, {
89
+ label: label(flag),
90
+ hint: t(FLAG_HINTS[flag.key]),
91
+ value: valueOf(flag.key),
92
+ onToggle: (next) => void annotation.selection.update({ [flag.key]: next })
93
+ }, flag.key))
94
+ })]
95
+ });
96
+ }
97
+ //#endregion
98
+ //#region src/ui/annotation-style.tsx
99
+ /**
100
+ * The annotation style panel. The plugin owns the schema and the state; this
101
+ * file owns only how each property renders and the engine fields it writes
102
+ * back. There is no per-subtype branching here: a new annotation kind that
103
+ * declares its properties in the kind table gets a working style panel for free.
104
+ *
105
+ * Data flow (mirrors examples/react's AnnotationSidebar):
106
+ * selection present → useAnnotationProperties(), write selection.update()
107
+ * nothing selected → useAnnotationProperties(tool), write tools.updateDefaults(tool, …)
108
+ *
109
+ * The look: a six-column swatch grid, range slider, SVG stroke / line-ending
110
+ * dropdowns, font-size combo and align toggles, styled with this app's
111
+ * semantic tokens (bg-surface / text-fg / border-border …).
112
+ */
113
+ const PRESET_COLORS = [
114
+ "#000000",
115
+ "#5f6368",
116
+ "#e44234",
117
+ "#ff8b00",
118
+ "#ffd500",
119
+ "#00cc66",
120
+ "#00b8d9",
121
+ "#4a90e2",
122
+ "#9b51e0",
123
+ "#f272c8",
124
+ "#a0522d",
125
+ "#ffffff"
126
+ ];
127
+ /** The standard fonts the picker offers, of the schema's; the registered fonts follow them. */
128
+ const OFFERED_FONTS = [
129
+ "helvetica",
130
+ "helvetica-bold",
131
+ "times-roman",
132
+ "courier"
133
+ ];
134
+ const FONT_SIZES = [
135
+ 8,
136
+ 9,
137
+ 10,
138
+ 11,
139
+ 12,
140
+ 14,
141
+ 16,
142
+ 18,
143
+ 24,
144
+ 36,
145
+ 48,
146
+ 72
147
+ ];
148
+ /** The borders the picker offers: the schema's styles, a dash or a cloud in a few sizes. */
149
+ const BORDER_OPTS = [
150
+ {
151
+ key: "solid",
152
+ make: () => ({ kind: "solid" })
153
+ },
154
+ {
155
+ key: "dashed-6-2",
156
+ make: () => ({
157
+ kind: "dashed",
158
+ dash: [6, 2]
159
+ })
160
+ },
161
+ {
162
+ key: "dashed-3-3",
163
+ make: () => ({
164
+ kind: "dashed",
165
+ dash: [3, 3]
166
+ })
167
+ },
168
+ {
169
+ key: "dashed-1-2",
170
+ make: () => ({
171
+ kind: "dashed",
172
+ dash: [1, 2]
173
+ })
174
+ },
175
+ {
176
+ key: "cloudy-1",
177
+ make: () => ({
178
+ kind: "cloudy",
179
+ intensity: 1
180
+ })
181
+ },
182
+ {
183
+ key: "cloudy-2",
184
+ make: () => ({
185
+ kind: "cloudy",
186
+ intensity: 2
187
+ })
188
+ }
189
+ ];
190
+ const borderKey = (border) => border.kind === "cloudy" ? `cloudy-${border.intensity >= 2 ? 2 : 1}` : border.kind === "dashed" ? `dashed-${border.dash.join("-")}` : "solid";
191
+ /** The border the three engine fields describe: a cloud wins over the style under it. */
192
+ const borderOf = (values) => {
193
+ const intensity = values.cloudyIntensity;
194
+ if (intensity) return {
195
+ kind: "cloudy",
196
+ intensity
197
+ };
198
+ const dash = values.dashArray;
199
+ return values.borderStyle === "dashed" ? {
200
+ kind: "dashed",
201
+ dash: dash?.length ? dash : [3, 3]
202
+ } : { kind: "solid" };
203
+ };
204
+ /** The engine fields that state a border, every one of them (a patch replaces each whole). */
205
+ const borderFields = (border) => ({
206
+ borderStyle: border.kind === "dashed" ? "dashed" : "solid",
207
+ dashArray: border.kind === "dashed" ? border.dash : null,
208
+ cloudyIntensity: border.kind === "cloudy" ? border.intensity : null
209
+ });
210
+ /** The option of a choice with this value, for its label. */
211
+ const optionOf = (options, value) => options.find((option) => option.value === value);
212
+ function Field({ label, mixed, children }) {
213
+ const t = (0, _embedpdf_react_i18n.useT)();
214
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
215
+ className: "mb-5",
216
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
217
+ className: "text-fg mb-2 block text-sm font-medium",
218
+ children: [label, mixed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
219
+ className: "text-fg-muted ms-1.5 text-xs font-normal",
220
+ children: t("stylePanel.mixed")
221
+ })]
222
+ }), children]
223
+ });
224
+ }
225
+ const isTransparent = (color) => color == null || color === "transparent" || /^#([0-9a-f]{8})$/i.test(color) && color.slice(-2).toLowerCase() === "00";
226
+ function Swatch({ color, active, onSelect }) {
227
+ const t = (0, _embedpdf_react_i18n.useT)();
228
+ const style = isTransparent(color) ? {
229
+ backgroundColor: "#fff",
230
+ backgroundImage: "linear-gradient(45deg, transparent 40%, #ef4444 40%, #ef4444 60%, transparent 60%)",
231
+ backgroundSize: "100% 100%"
232
+ } : { backgroundColor: color };
233
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
234
+ type: "button",
235
+ title: isTransparent(color) ? t("stylePanel.transparent") : color,
236
+ onClick: () => onSelect(color),
237
+ className: `border-border-strong h-5 w-5 rounded-full border ${active ? "outline-accent outline outline-2 outline-offset-2" : ""}`,
238
+ style
239
+ });
240
+ }
241
+ function SwatchGrid({ value, onSelect, allowTransparent }) {
242
+ const active = (color) => color.toLowerCase() === (value ?? "").toLowerCase();
243
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
244
+ className: "grid grid-cols-6 gap-x-1 gap-y-3",
245
+ children: [PRESET_COLORS.map((color) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Swatch, {
246
+ color,
247
+ active: active(color),
248
+ onSelect
249
+ }, color)), allowTransparent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Swatch, {
250
+ color: "transparent",
251
+ active: isTransparent(value ?? null),
252
+ onSelect
253
+ })]
254
+ });
255
+ }
256
+ function RangeSlider({ value, min, max, step, onChange }) {
257
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
258
+ type: "range",
259
+ className: "accent-accent mb-1.5 h-1 w-full cursor-pointer",
260
+ value,
261
+ min,
262
+ max,
263
+ step,
264
+ onChange: (event) => onChange(parseFloat(event.target.value))
265
+ });
266
+ }
267
+ /** Close an open dropdown on a press outside it; the root holds its trigger too. */
268
+ function useOutsideClose(open, close) {
269
+ const rootRef = (0, react.useRef)(null);
270
+ (0, react.useEffect)(() => {
271
+ const root = rootRef.current;
272
+ return open && root ? (0, _embedpdf_web.observeOutsidePress)(root, close) : void 0;
273
+ }, [open, close]);
274
+ return rootRef;
275
+ }
276
+ function DropdownShell({ trigger, children }) {
277
+ const [open, setOpen] = (0, react.useState)(false);
278
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
279
+ ref: useOutsideClose(open, () => setOpen(false)),
280
+ className: "relative w-full",
281
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
282
+ type: "button",
283
+ onClick: () => setOpen((previous) => !previous),
284
+ className: "border-border bg-surface text-fg flex w-full items-center justify-between gap-2 rounded border px-3 py-1.5",
285
+ children: [trigger, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
286
+ name: "chevronDown",
287
+ size: 16,
288
+ className: "text-fg-secondary shrink-0"
289
+ })]
290
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
291
+ className: "border-border bg-elevated absolute z-10 mt-1 max-h-60 w-full overflow-y-auto rounded border p-1 shadow-lg",
292
+ children: children(() => setOpen(false))
293
+ })]
294
+ });
295
+ }
296
+ function OptionRow({ selected, title, onClick, children }) {
297
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
298
+ type: "button",
299
+ title,
300
+ "aria-label": title,
301
+ onClick,
302
+ className: `hover:bg-hover flex w-full items-center rounded px-2 py-1.5 text-start text-sm ${selected ? "bg-selected text-accent" : "text-fg"}`,
303
+ children
304
+ });
305
+ }
306
+ function borderSvg(border) {
307
+ if (border.kind === "cloudy") {
308
+ const radius = border.intensity >= 2 ? 5 : 3;
309
+ const bumps = Math.ceil(80 / (radius * 2));
310
+ const step = 80 / bumps;
311
+ const baseline = radius + 1;
312
+ const viewH = radius * 2 + 2;
313
+ const parts = [`M 0 ${baseline}`];
314
+ for (let i = 0; i < bumps; i++) parts.push(`A ${radius} ${radius} 0 0 1 ${step * (i + 1)} ${baseline}`);
315
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
316
+ width: "80",
317
+ height: viewH,
318
+ viewBox: `0 0 80 ${viewH}`,
319
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
320
+ d: parts.join(" "),
321
+ fill: "none",
322
+ stroke: "currentColor",
323
+ strokeWidth: "1.5",
324
+ strokeLinejoin: "round",
325
+ strokeLinecap: "round"
326
+ })
327
+ });
328
+ }
329
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
330
+ width: "80",
331
+ height: "8",
332
+ viewBox: "0 0 80 8",
333
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
334
+ x1: "0",
335
+ y1: "4",
336
+ x2: "80",
337
+ y2: "4",
338
+ stroke: "currentColor",
339
+ strokeWidth: "2",
340
+ strokeDasharray: border.kind === "dashed" ? border.dash.join(" ") : void 0
341
+ })
342
+ });
343
+ }
344
+ function BorderSelect({ value, styles, onChange }) {
345
+ const label = require_labels.useLabel();
346
+ const options = BORDER_OPTS.filter((option) => optionOf(styles, option.make().kind));
347
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownShell, {
348
+ trigger: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
349
+ className: "text-fg-secondary",
350
+ children: borderSvg(value)
351
+ }),
352
+ children: (close) => options.map((option) => {
353
+ const border = option.make();
354
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionRow, {
355
+ selected: borderKey(value) === option.key,
356
+ title: label(optionOf(styles, border.kind)),
357
+ onClick: () => {
358
+ onChange(border);
359
+ close();
360
+ },
361
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
362
+ className: "text-fg-secondary",
363
+ children: borderSvg(border)
364
+ })
365
+ }, option.key);
366
+ })
367
+ });
368
+ }
369
+ const LE_MARKERS = {
370
+ square: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M68 -4 L76 -4 L76 4 L68 4 Z" }),
371
+ circle: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
372
+ cx: "72",
373
+ cy: "0",
374
+ r: "4"
375
+ }),
376
+ diamond: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M72 -5 L77 0 L72 5 L67 0 Z" }),
377
+ "open-arrow": /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
378
+ d: "M67 -5 L77 0 L67 5",
379
+ fill: "none"
380
+ }),
381
+ "closed-arrow": /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M67 -5 L77 0 L67 5 Z" }),
382
+ "r-open-arrow": /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
383
+ d: "M77 -5 L67 0 L77 5",
384
+ fill: "none"
385
+ }),
386
+ "r-closed-arrow": /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M77 -5 L67 0 L77 5 Z" }),
387
+ butt: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
388
+ d: "M72 -5 L72 5",
389
+ fill: "none"
390
+ }),
391
+ slash: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
392
+ d: "M67 -5 L77 5",
393
+ fill: "none"
394
+ })
395
+ };
396
+ const LE_LINE_END = {
397
+ square: 68,
398
+ circle: 68,
399
+ diamond: 67,
400
+ "open-arrow": 76,
401
+ "closed-arrow": 67,
402
+ "r-open-arrow": 67,
403
+ "r-closed-arrow": 67,
404
+ butt: 72,
405
+ slash: 72
406
+ };
407
+ function LineEndingPreview({ name, side }) {
408
+ const marker = LE_MARKERS[name];
409
+ const lineEndX = LE_LINE_END[name] ?? 77;
410
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
411
+ width: "80",
412
+ height: "20",
413
+ viewBox: "0 0 80 20",
414
+ className: "text-fg",
415
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
416
+ transform: side === "start" ? "rotate(180 40 10)" : void 0,
417
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
418
+ x1: "4",
419
+ y1: "10",
420
+ x2: lineEndX,
421
+ y2: "10",
422
+ stroke: "currentColor",
423
+ strokeWidth: "1.5"
424
+ }), marker && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
425
+ transform: "translate(0, 10)",
426
+ fill: "currentColor",
427
+ stroke: "currentColor",
428
+ strokeWidth: "1.5",
429
+ children: marker
430
+ })]
431
+ })
432
+ });
433
+ }
434
+ function LineEndingSelect({ side, value, options, onChange }) {
435
+ const label = require_labels.useLabel();
436
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownShell, {
437
+ trigger: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineEndingPreview, {
438
+ name: value,
439
+ side
440
+ }),
441
+ children: (close) => options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionRow, {
442
+ selected: value === option.value,
443
+ title: label(option),
444
+ onClick: () => {
445
+ onChange(option.value);
446
+ close();
447
+ },
448
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineEndingPreview, {
449
+ name: option.value,
450
+ side
451
+ })
452
+ }, option.value))
453
+ });
454
+ }
455
+ function FontFamilySelect({ value, standard, onChange }) {
456
+ const label = require_labels.useLabel();
457
+ const configured = require_annotation_fonts.useAnnotationFonts();
458
+ const options = [...standard.filter((option) => OFFERED_FONTS.includes(option.value)).map((option) => ({
459
+ value: option.value,
460
+ label: label(option)
461
+ })), ...configured.map((font) => ({
462
+ value: font.key,
463
+ label: font.label
464
+ }))];
465
+ const named = optionOf(standard, value);
466
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownShell, {
467
+ trigger: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
468
+ className: "text-fg text-sm",
469
+ children: options.find((option) => option.value === value)?.label ?? (named ? label(named) : value)
470
+ }),
471
+ children: (close) => options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionRow, {
472
+ selected: option.value === value,
473
+ onClick: () => {
474
+ onChange(option.value);
475
+ close();
476
+ },
477
+ children: option.label
478
+ }, option.value))
479
+ });
480
+ }
481
+ function FontSizeCombo({ value, onChange }) {
482
+ const [open, setOpen] = (0, react.useState)(false);
483
+ const rootRef = useOutsideClose(open, () => setOpen(false));
484
+ const [draft, setDraft] = (0, react.useState)(null);
485
+ const commit = () => {
486
+ if (draft === null) return;
487
+ const size = parseInt(draft, 10);
488
+ setDraft(null);
489
+ if (Number.isFinite(size) && size > 0 && size !== value) onChange(size);
490
+ };
491
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
492
+ ref: rootRef,
493
+ className: "relative w-full",
494
+ children: [
495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
496
+ type: "number",
497
+ min: 1,
498
+ value: draft ?? value,
499
+ onChange: (event) => setDraft(event.target.value),
500
+ onBlur: commit,
501
+ onKeyDown: (event) => {
502
+ if (event.key === "Enter") {
503
+ event.preventDefault();
504
+ commit();
505
+ setOpen(false);
506
+ } else if (event.key === "Escape") {
507
+ setDraft(null);
508
+ setOpen(false);
509
+ }
510
+ },
511
+ onClick: () => setOpen(true),
512
+ className: "border-border bg-surface text-fg w-full rounded border px-2 py-1.5 pe-7 text-sm"
513
+ }),
514
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
515
+ type: "button",
516
+ tabIndex: -1,
517
+ onClick: () => setOpen((previous) => !previous),
518
+ className: "absolute inset-y-0 end-1 flex items-center",
519
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
520
+ name: "chevronDown",
521
+ size: 16,
522
+ className: "text-fg-secondary"
523
+ })
524
+ }),
525
+ open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
526
+ className: "border-border bg-elevated absolute z-10 mt-1 max-h-48 w-full overflow-y-auto rounded border p-1 shadow-lg",
527
+ children: FONT_SIZES.map((sz) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionRow, {
528
+ selected: sz === value,
529
+ onClick: () => {
530
+ setDraft(null);
531
+ onChange(sz);
532
+ setOpen(false);
533
+ },
534
+ children: sz
535
+ }, sz))
536
+ })
537
+ ]
538
+ });
539
+ }
540
+ function BlendModeSelect({ value, options, name, onChange }) {
541
+ const label = require_labels.useLabel();
542
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
543
+ value,
544
+ "aria-label": name,
545
+ onChange: (event) => onChange(event.target.value),
546
+ className: "border-border bg-surface text-fg w-full rounded border px-3 py-1.5 text-sm",
547
+ children: options.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
548
+ value: mode.value,
549
+ children: label(mode)
550
+ }, mode.value))
551
+ });
552
+ }
553
+ function Toggle({ active, title, onClick, keepFocus, children }) {
554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
555
+ type: "button",
556
+ title,
557
+ onClick,
558
+ onMouseDown: keepFocus ? (event) => event.preventDefault() : void 0,
559
+ className: `flex h-9 w-9 items-center justify-center rounded border transition-colors ${active ? "border-accent bg-accent text-on-accent" : "border-border bg-surface text-fg hover:bg-hover"}`,
560
+ children
561
+ });
562
+ }
563
+ const isFormatSpec = (spec) => spec.key === "bold" || spec.key === "italic" || spec.key === "underline";
564
+ /**
565
+ * The format toggles a free-text kind declares, in one row. While the text
566
+ * editor holds a range they read and write that range's runs (the plugin
567
+ * routes `toggleTextFormat`); otherwise the annotation's body. The buttons
568
+ * keep focus in the editor so the range survives the click.
569
+ */
570
+ function FormatToggles({ specs, values, mixed, onToggle }) {
571
+ const t = (0, _embedpdf_react_i18n.useT)();
572
+ const label = require_labels.useLabel();
573
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
574
+ label: t("demo.formatLabel"),
575
+ mixed: specs.some((spec) => mixed.includes(spec.key)),
576
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
577
+ className: "flex gap-2",
578
+ children: specs.map((spec) => {
579
+ const active = values[spec.key] === true && !mixed.includes(spec.key);
580
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
581
+ title: label(spec),
582
+ active,
583
+ keepFocus: true,
584
+ onClick: () => onToggle(spec.key, !active),
585
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
586
+ name: spec.key,
587
+ size: 18
588
+ })
589
+ }, spec.key);
590
+ })
591
+ })
592
+ });
593
+ }
594
+ function FieldControl({ spec, values, mixed, onChange, onChangeEach }) {
595
+ const t = (0, _embedpdf_react_i18n.useT)();
596
+ const label = require_labels.useLabel();
597
+ const value = values[spec.key];
598
+ switch (spec.key) {
599
+ case "color":
600
+ case "fontColor": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
601
+ label: label(spec),
602
+ mixed,
603
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwatchGrid, {
604
+ value,
605
+ onSelect: (color) => onChange({ [spec.key]: color })
606
+ })
607
+ });
608
+ case "interiorColor": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
609
+ label: label(spec),
610
+ mixed,
611
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwatchGrid, {
612
+ value,
613
+ allowTransparent: true,
614
+ onSelect: (color) => onChange({ interiorColor: color === "transparent" ? null : color })
615
+ })
616
+ });
617
+ case "opacity": {
618
+ const opacity = value ?? 1;
619
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
620
+ label: label(spec),
621
+ mixed,
622
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RangeSlider, {
623
+ value: opacity,
624
+ min: spec.min,
625
+ max: spec.max,
626
+ step: spec.step,
627
+ onChange: (next) => onChange({ opacity: next })
628
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
629
+ className: "text-fg-muted text-xs",
630
+ children: [Math.round(opacity * 100), "%"]
631
+ })]
632
+ });
633
+ }
634
+ case "strokeWidth": {
635
+ const strokeWidth = value ?? spec.min;
636
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
637
+ label: label(spec),
638
+ mixed,
639
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RangeSlider, {
640
+ value: strokeWidth,
641
+ min: spec.min,
642
+ max: spec.max,
643
+ step: spec.step,
644
+ onChange: (next) => onChange({ strokeWidth: next })
645
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
646
+ className: "text-fg-muted text-xs",
647
+ children: `${strokeWidth}px`
648
+ })]
649
+ });
650
+ }
651
+ case "fontSize": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
652
+ label: label(spec),
653
+ mixed,
654
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FontSizeCombo, {
655
+ value: value ?? 12,
656
+ onChange: (next) => onChange({ fontSize: next })
657
+ })
658
+ });
659
+ case "borderStyle": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
660
+ label: label(spec),
661
+ mixed,
662
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BorderSelect, {
663
+ value: borderOf(values),
664
+ styles: spec.options,
665
+ onChange: (border) => onChange(borderFields(border))
666
+ })
667
+ });
668
+ case "lineEndings": {
669
+ const le = value ?? {
670
+ start: "none",
671
+ end: "none"
672
+ };
673
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
674
+ label: label(spec),
675
+ mixed,
676
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
677
+ className: "grid grid-cols-2 gap-3",
678
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
679
+ className: "text-fg-muted mb-1.5 text-xs",
680
+ children: t("stylePanel.lineStart")
681
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineEndingSelect, {
682
+ side: "start",
683
+ value: le.start,
684
+ options: spec.options,
685
+ onChange: (ending) => onChangeEach((current) => ({ lineEndings: {
686
+ ...current.lineEndings,
687
+ start: ending
688
+ } }))
689
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
690
+ className: "text-fg-muted mb-1.5 text-xs",
691
+ children: t("stylePanel.lineEnd")
692
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineEndingSelect, {
693
+ side: "end",
694
+ value: le.end,
695
+ options: spec.options,
696
+ onChange: (ending) => onChangeEach((current) => ({ lineEndings: {
697
+ ...current.lineEndings,
698
+ end: ending
699
+ } }))
700
+ })] })]
701
+ })
702
+ });
703
+ }
704
+ case "fontFamily": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
705
+ label: label(spec),
706
+ mixed,
707
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FontFamilySelect, {
708
+ value: value ?? "helvetica",
709
+ standard: spec.options,
710
+ onChange: (family) => onChange({ fontFamily: family })
711
+ })
712
+ });
713
+ case "textAlign": {
714
+ const alignIcon = {
715
+ left: "alignLeft",
716
+ center: "alignCenter",
717
+ right: "alignRight"
718
+ };
719
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
720
+ label: label(spec),
721
+ mixed,
722
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
723
+ className: "flex gap-2",
724
+ children: spec.options.map((option) => {
725
+ const al = option.value;
726
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
727
+ title: label(option),
728
+ active: value === al,
729
+ onClick: () => onChange({ textAlign: al }),
730
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
731
+ name: alignIcon[al],
732
+ size: 18
733
+ })
734
+ }, al);
735
+ })
736
+ })
737
+ });
738
+ }
739
+ case "bold":
740
+ case "italic":
741
+ case "underline": return null;
742
+ case "blendMode": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
743
+ label: label(spec),
744
+ mixed,
745
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlendModeSelect, {
746
+ value: value ?? "normal",
747
+ options: spec.options,
748
+ name: label(spec),
749
+ onChange: (blendMode) => onChange({ blendMode })
750
+ })
751
+ });
752
+ case "link": return null;
753
+ default: return null;
754
+ }
755
+ }
756
+ function EmptyState() {
757
+ const t = (0, _embedpdf_react_i18n.useT)();
758
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
759
+ className: "text-fg-muted flex flex-1 flex-col items-center justify-center gap-3 p-8 text-center",
760
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
761
+ name: "palette",
762
+ size: 40,
763
+ className: "text-fg-disabled"
764
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
765
+ className: "max-w-[180px] text-sm",
766
+ children: t("stylePanel.empty")
767
+ })]
768
+ });
769
+ }
770
+ /** The rich body a format on or off is, merged over a tool's current one. */
771
+ const FORMAT_BODY = {
772
+ bold: (on) => ({ weight: on ? 700 : 400 }),
773
+ italic: (on) => ({ italic: on }),
774
+ underline: (on) => ({ decoration: on ? ["underline"] : [] })
775
+ };
776
+ /**
777
+ * The schema-driven style panel. Rendered inside the right sidebar's
778
+ * `annotation-style` surface (see ui/panels.tsx). Owns its own scroll.
779
+ */
780
+ function AnnotationStylePanel() {
781
+ const t = (0, _embedpdf_react_i18n.useT)();
782
+ const annotation = (0, _embedpdf_react_annotation.useAnnotation)();
783
+ const commands = (0, _embedpdf_react_commands.useCommands)();
784
+ const documentId = (0, _embedpdf_react_runtime.useDocumentId)() ?? void 0;
785
+ const activeToolId = (0, _embedpdf_react_interaction.useInteractionState)((state) => state.activeToolId) ?? "pointer";
786
+ const selection = (0, _embedpdf_react_annotation.useAnnotationProperties)();
787
+ const tool = (0, _embedpdf_react_annotation.useAnnotationProperties)(activeToolId);
788
+ const toolDefaults = (0, _embedpdf_react_annotation.useAnnotationDefaults)(activeToolId);
789
+ const selected = (0, _embedpdf_react_annotation.useAnnotationState)((state) => state.selected);
790
+ const toolLabel = (0, _embedpdf_react_runtime.useKernelValue)(() => commands.resolveCommand(`tool:${activeToolId}`, documentId)?.label ?? null, Object.is);
791
+ const hasSel = selection.properties.length > 0;
792
+ const { properties, values, mixed } = hasSel ? selection : tool;
793
+ const fields = properties.filter((property) => property.control !== "flag" && property.control !== "text");
794
+ const write = (patch) => hasSel ? annotation.selection.update(patch) : annotation.tools.updateDefaults(activeToolId, patch);
795
+ const writeEach = (patchOf) => hasSel ? annotation.selection.update((member) => patchOf(member)) : annotation.tools.updateDefaults(activeToolId, patchOf(toolDefaults));
796
+ const toggleFormat = (format, on) => {
797
+ if (hasSel) return void annotation.text.toggleFormat(format);
798
+ const body = toolDefaults.richText?.body;
799
+ annotation.tools.updateDefaults(activeToolId, { richText: { body: {
800
+ ...body,
801
+ ...FORMAT_BODY[format](on)
802
+ } } });
803
+ };
804
+ if (fields.length === 0 && selected.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {});
805
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
806
+ className: "flex-1 overflow-y-auto p-4",
807
+ children: [
808
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
809
+ className: "text-fg-muted mb-4 text-[11px] font-semibold uppercase tracking-wide",
810
+ children: hasSel ? t("stylePanel.selected", { params: { count: selected.length } }) : toolLabel === null ? t("stylePanel.defaults") : t("stylePanel.toolDefaults", { params: { tool: toolLabel } })
811
+ }),
812
+ fields.map((spec) => spec.key === "bold" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormatToggles, {
813
+ specs: fields.filter((spec) => isFormatSpec(spec)),
814
+ values,
815
+ mixed,
816
+ onToggle: toggleFormat
817
+ }, "format") : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldControl, {
818
+ spec,
819
+ values,
820
+ mixed: mixed.includes(spec.key),
821
+ onChange: write,
822
+ onChangeEach: writeEach
823
+ }, spec.key)),
824
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RedactionLabelSection, {}),
825
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnnotationFlagsSection, {})
826
+ ]
827
+ });
828
+ }
829
+ /**
830
+ * Label editor shown when exactly one redaction mark is selected. The text is
831
+ * what the destructive apply paints over the region (white-on-black by the
832
+ * tool defaults); `repeat` tiles it. Writes ride the annotation update verb
833
+ * via the redaction plugin, which preserves the label's `/DA` styling.
834
+ */
835
+ function RedactionLabelSection() {
836
+ const t = (0, _embedpdf_react_i18n.useT)();
837
+ const redaction = (0, _embedpdf_react_runtime.useOptionalCapability)(_embedpdf_react_redaction.RedactionToken);
838
+ const selected = (0, _embedpdf_react_annotation.useAnnotationState)((state) => state.selected);
839
+ const mark = selected.length === 1 && selected[0].subtype === "redact" ? selected[0] : null;
840
+ const [draft, setDraft] = (0, react.useState)(null);
841
+ (0, react.useEffect)(() => setDraft(null), [mark?.ref && (0, _embedpdf_react_annotation.annotationKey)(mark.ref)]);
842
+ if (!redaction || !mark || mark.subtype !== "redact") return null;
843
+ const value = draft ?? mark.overlayText ?? "";
844
+ const commit = () => {
845
+ if (draft === null) return;
846
+ redaction.updateLabel(mark.ref, { overlayText: draft.length > 0 ? draft : null });
847
+ setDraft(null);
848
+ };
849
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
850
+ className: "border-border-subtle mt-4 border-t pt-4",
851
+ children: [
852
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
853
+ className: "text-fg-muted mb-2 text-[11px] font-semibold uppercase tracking-wide",
854
+ children: t("demo.redactLabelTitle")
855
+ }),
856
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
857
+ type: "text",
858
+ value,
859
+ placeholder: t("demo.redactLabelPlaceholder"),
860
+ onChange: (event) => setDraft(event.target.value),
861
+ onBlur: commit,
862
+ onKeyDown: (event) => event.key === "Enter" && commit(),
863
+ className: "border-border bg-surface text-fg w-full rounded-md border px-2 py-1.5 text-sm"
864
+ }),
865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
866
+ className: "text-fg mt-2 flex items-center gap-2 text-sm",
867
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
868
+ type: "checkbox",
869
+ checked: mark.repeat,
870
+ onChange: (event) => void redaction.updateLabel(mark.ref, { repeat: event.target.checked })
871
+ }), t("demo.redactLabelRepeat")]
872
+ })
873
+ ]
874
+ });
875
+ }
876
+ //#endregion
877
+ exports.AnnotationStylePanel = AnnotationStylePanel;
878
+
879
+ //# sourceMappingURL=annotation-style-Bzvvp8-N.cjs.map