react-pdf-highlighter-plus 1.0.3 → 1.0.5

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 (85) hide show
  1. package/dist/esm/index.d.ts +1348 -19
  2. package/dist/esm/index.js +3557 -18
  3. package/dist/esm/index.js.map +1 -1
  4. package/package.json +3 -2
  5. package/dist/esm/components/AreaHighlight.d.ts +0 -82
  6. package/dist/esm/components/AreaHighlight.js +0 -109
  7. package/dist/esm/components/AreaHighlight.js.map +0 -1
  8. package/dist/esm/components/DrawingCanvas.d.ts +0 -48
  9. package/dist/esm/components/DrawingCanvas.js +0 -277
  10. package/dist/esm/components/DrawingCanvas.js.map +0 -1
  11. package/dist/esm/components/DrawingHighlight.d.ts +0 -70
  12. package/dist/esm/components/DrawingHighlight.js +0 -164
  13. package/dist/esm/components/DrawingHighlight.js.map +0 -1
  14. package/dist/esm/components/FreetextHighlight.d.ts +0 -112
  15. package/dist/esm/components/FreetextHighlight.js +0 -193
  16. package/dist/esm/components/FreetextHighlight.js.map +0 -1
  17. package/dist/esm/components/HighlightLayer.d.ts +0 -49
  18. package/dist/esm/components/HighlightLayer.js +0 -37
  19. package/dist/esm/components/HighlightLayer.js.map +0 -1
  20. package/dist/esm/components/ImageHighlight.d.ts +0 -63
  21. package/dist/esm/components/ImageHighlight.js +0 -65
  22. package/dist/esm/components/ImageHighlight.js.map +0 -1
  23. package/dist/esm/components/MonitoredHighlightContainer.d.ts +0 -37
  24. package/dist/esm/components/MonitoredHighlightContainer.js +0 -42
  25. package/dist/esm/components/MonitoredHighlightContainer.js.map +0 -1
  26. package/dist/esm/components/MouseMonitor.d.ts +0 -34
  27. package/dist/esm/components/MouseMonitor.js +0 -30
  28. package/dist/esm/components/MouseMonitor.js.map +0 -1
  29. package/dist/esm/components/MouseSelection.d.ts +0 -66
  30. package/dist/esm/components/MouseSelection.js +0 -122
  31. package/dist/esm/components/MouseSelection.js.map +0 -1
  32. package/dist/esm/components/PdfHighlighter.d.ts +0 -184
  33. package/dist/esm/components/PdfHighlighter.js +0 -410
  34. package/dist/esm/components/PdfHighlighter.js.map +0 -1
  35. package/dist/esm/components/PdfLoader.d.ts +0 -55
  36. package/dist/esm/components/PdfLoader.js +0 -57
  37. package/dist/esm/components/PdfLoader.js.map +0 -1
  38. package/dist/esm/components/ShapeCanvas.d.ts +0 -51
  39. package/dist/esm/components/ShapeCanvas.js +0 -205
  40. package/dist/esm/components/ShapeCanvas.js.map +0 -1
  41. package/dist/esm/components/ShapeHighlight.d.ts +0 -107
  42. package/dist/esm/components/ShapeHighlight.js +0 -140
  43. package/dist/esm/components/ShapeHighlight.js.map +0 -1
  44. package/dist/esm/components/SignaturePad.d.ts +0 -40
  45. package/dist/esm/components/SignaturePad.js +0 -138
  46. package/dist/esm/components/SignaturePad.js.map +0 -1
  47. package/dist/esm/components/TextHighlight.d.ts +0 -93
  48. package/dist/esm/components/TextHighlight.js +0 -115
  49. package/dist/esm/components/TextHighlight.js.map +0 -1
  50. package/dist/esm/components/TipContainer.d.ts +0 -27
  51. package/dist/esm/components/TipContainer.js +0 -58
  52. package/dist/esm/components/TipContainer.js.map +0 -1
  53. package/dist/esm/contexts/HighlightContext.d.ts +0 -44
  54. package/dist/esm/contexts/HighlightContext.js +0 -16
  55. package/dist/esm/contexts/HighlightContext.js.map +0 -1
  56. package/dist/esm/contexts/PdfHighlighterContext.d.ts +0 -89
  57. package/dist/esm/contexts/PdfHighlighterContext.js +0 -16
  58. package/dist/esm/contexts/PdfHighlighterContext.js.map +0 -1
  59. package/dist/esm/lib/coordinates.d.ts +0 -16
  60. package/dist/esm/lib/coordinates.js +0 -69
  61. package/dist/esm/lib/coordinates.js.map +0 -1
  62. package/dist/esm/lib/export-pdf.d.ts +0 -81
  63. package/dist/esm/lib/export-pdf.js +0 -511
  64. package/dist/esm/lib/export-pdf.js.map +0 -1
  65. package/dist/esm/lib/get-bounding-rect.d.ts +0 -3
  66. package/dist/esm/lib/get-bounding-rect.js +0 -35
  67. package/dist/esm/lib/get-bounding-rect.js.map +0 -1
  68. package/dist/esm/lib/get-client-rects.d.ts +0 -3
  69. package/dist/esm/lib/get-client-rects.js +0 -43
  70. package/dist/esm/lib/get-client-rects.js.map +0 -1
  71. package/dist/esm/lib/group-highlights-by-page.d.ts +0 -6
  72. package/dist/esm/lib/group-highlights-by-page.js +0 -23
  73. package/dist/esm/lib/group-highlights-by-page.js.map +0 -1
  74. package/dist/esm/lib/optimize-client-rects.d.ts +0 -3
  75. package/dist/esm/lib/optimize-client-rects.js +0 -65
  76. package/dist/esm/lib/optimize-client-rects.js.map +0 -1
  77. package/dist/esm/lib/pdfjs-dom.d.ts +0 -9
  78. package/dist/esm/lib/pdfjs-dom.js +0 -55
  79. package/dist/esm/lib/pdfjs-dom.js.map +0 -1
  80. package/dist/esm/lib/screenshot.d.ts +0 -4
  81. package/dist/esm/lib/screenshot.js +0 -24
  82. package/dist/esm/lib/screenshot.js.map +0 -1
  83. package/dist/esm/types.d.ts +0 -213
  84. package/dist/esm/types.js +0 -2
  85. package/dist/esm/types.js.map +0 -1
package/dist/esm/index.js CHANGED
@@ -1,19 +1,3558 @@
1
- import { PdfHighlighter, } from "./components/PdfHighlighter";
2
- import { TextHighlight, } from "./components/TextHighlight";
3
- import { MonitoredHighlightContainer, } from "./components/MonitoredHighlightContainer";
4
- import { AreaHighlight, } from "./components/AreaHighlight";
5
- import { FreetextHighlight, } from "./components/FreetextHighlight";
6
- import { ImageHighlight, } from "./components/ImageHighlight";
7
- import { SignaturePad, } from "./components/SignaturePad";
8
- import { DrawingCanvas, } from "./components/DrawingCanvas";
9
- import { DrawingHighlight, } from "./components/DrawingHighlight";
10
- import { ShapeCanvas, } from "./components/ShapeCanvas";
11
- import { ShapeHighlight, } from "./components/ShapeHighlight";
12
- import { PdfLoader } from "./components/PdfLoader";
13
- import { useHighlightContainerContext, } from "./contexts/HighlightContext";
14
- import { viewportPositionToScaled, scaledPositionToViewport, } from "./lib/coordinates";
15
- import { exportPdf, } from "./lib/export-pdf";
16
- import { usePdfHighlighterContext, } from "./contexts/PdfHighlighterContext";
17
- export { PdfHighlighter, PdfLoader, TextHighlight, MonitoredHighlightContainer, AreaHighlight, FreetextHighlight, ImageHighlight, SignaturePad, DrawingCanvas, DrawingHighlight, ShapeCanvas, ShapeHighlight, useHighlightContainerContext, viewportPositionToScaled, scaledPositionToViewport, usePdfHighlighterContext, exportPdf, };
18
- export * from "./types";
1
+ // src/components/PdfHighlighter.tsx
2
+ import debounce from "lodash.debounce";
3
+ import React6, {
4
+ useLayoutEffect as useLayoutEffect2,
5
+ useRef as useRef5,
6
+ useState as useState5
7
+ } from "react";
8
+ import { createRoot } from "react-dom/client";
9
+
10
+ // src/contexts/PdfHighlighterContext.ts
11
+ import { createContext, useContext } from "react";
12
+ var PdfHighlighterContext = createContext(void 0);
13
+ var usePdfHighlighterContext = () => {
14
+ const pdfHighlighterUtils = useContext(PdfHighlighterContext);
15
+ if (pdfHighlighterUtils === void 0) {
16
+ throw new Error(
17
+ "usePdfHighlighterContext must be used within PdfHighlighter!"
18
+ );
19
+ }
20
+ return pdfHighlighterUtils;
21
+ };
22
+
23
+ // src/lib/coordinates.ts
24
+ var viewportToScaled = (rect, { width, height }) => {
25
+ return {
26
+ x1: rect.left,
27
+ y1: rect.top,
28
+ x2: rect.left + rect.width,
29
+ y2: rect.top + rect.height,
30
+ width,
31
+ height,
32
+ pageNumber: rect.pageNumber
33
+ };
34
+ };
35
+ var viewportPositionToScaled = ({ boundingRect, rects }, viewer) => {
36
+ const pageNumber = boundingRect.pageNumber;
37
+ const viewport = viewer.getPageView(pageNumber - 1).viewport;
38
+ const scale = (obj) => viewportToScaled(obj, viewport);
39
+ return {
40
+ boundingRect: scale(boundingRect),
41
+ rects: (rects || []).map(scale)
42
+ };
43
+ };
44
+ var pdfToViewport = (pdf, viewport) => {
45
+ const [x1, y1, x2, y2] = viewport.convertToViewportRectangle([
46
+ pdf.x1,
47
+ pdf.y1,
48
+ pdf.x2,
49
+ pdf.y2
50
+ ]);
51
+ return {
52
+ left: Math.min(x1, x2),
53
+ top: Math.min(y1, y2),
54
+ width: Math.abs(x2 - x1),
55
+ height: Math.abs(y1 - y2),
56
+ pageNumber: pdf.pageNumber
57
+ };
58
+ };
59
+ var scaledToViewport = (scaled, viewport, usePdfCoordinates = false) => {
60
+ const { width, height } = viewport;
61
+ if (usePdfCoordinates) {
62
+ return pdfToViewport(scaled, viewport);
63
+ }
64
+ if (scaled.x1 === void 0) {
65
+ throw new Error("You are using old position format, please update");
66
+ }
67
+ const x1 = width * scaled.x1 / scaled.width;
68
+ const y1 = height * scaled.y1 / scaled.height;
69
+ const x2 = width * scaled.x2 / scaled.width;
70
+ const y2 = height * scaled.y2 / scaled.height;
71
+ return {
72
+ left: x1,
73
+ top: y1,
74
+ width: x2 - x1,
75
+ height: y2 - y1,
76
+ pageNumber: scaled.pageNumber
77
+ };
78
+ };
79
+ var scaledPositionToViewport = ({ boundingRect, rects, usePdfCoordinates }, viewer) => {
80
+ const pageNumber = boundingRect.pageNumber;
81
+ const viewport = viewer.getPageView(pageNumber - 1).viewport;
82
+ const scale = (obj) => scaledToViewport(obj, viewport, usePdfCoordinates);
83
+ return {
84
+ boundingRect: scale(boundingRect),
85
+ rects: (rects || []).map(scale)
86
+ };
87
+ };
88
+
89
+ // src/lib/get-bounding-rect.ts
90
+ var getBoundingRect = (clientRects) => {
91
+ const rects = Array.from(clientRects).map((rect) => {
92
+ const { left, top, width, height, pageNumber: pageNumber2 } = rect;
93
+ const X02 = left;
94
+ const X12 = left + width;
95
+ const Y02 = top;
96
+ const Y12 = top + height;
97
+ return { X0: X02, X1: X12, Y0: Y02, Y1: Y12, pageNumber: pageNumber2 };
98
+ });
99
+ let firstPageNumber = Number.MAX_SAFE_INTEGER;
100
+ rects.forEach((rect) => {
101
+ firstPageNumber = Math.min(
102
+ firstPageNumber,
103
+ rect.pageNumber ?? firstPageNumber
104
+ );
105
+ });
106
+ const rectsWithSizeOnFirstPage = rects.filter(
107
+ (rect) => (rect.X0 > 0 || rect.X1 > 0 || rect.Y0 > 0 || rect.Y1 > 0) && rect.pageNumber === firstPageNumber
108
+ );
109
+ const optimal = rectsWithSizeOnFirstPage.reduce((res, rect) => {
110
+ return {
111
+ X0: Math.min(res.X0, rect.X0),
112
+ X1: Math.max(res.X1, rect.X1),
113
+ Y0: Math.min(res.Y0, rect.Y0),
114
+ Y1: Math.max(res.Y1, rect.Y1),
115
+ pageNumber: firstPageNumber
116
+ };
117
+ }, rectsWithSizeOnFirstPage[0]);
118
+ const { X0, X1, Y0, Y1, pageNumber } = optimal;
119
+ return {
120
+ left: X0,
121
+ top: Y0,
122
+ width: X1 - X0,
123
+ height: Y1 - Y0,
124
+ pageNumber
125
+ };
126
+ };
127
+ var get_bounding_rect_default = getBoundingRect;
128
+
129
+ // src/lib/optimize-client-rects.ts
130
+ var sort = (rects) => rects.sort((A, B) => {
131
+ const top = (A.pageNumber || 0) * A.top - (B.pageNumber || 0) * B.top;
132
+ if (top === 0) {
133
+ return A.left - B.left;
134
+ }
135
+ return top;
136
+ });
137
+ var overlaps = (A, B) => A.pageNumber === B.pageNumber && A.left <= B.left && B.left <= A.left + A.width;
138
+ var sameLine = (A, B, yMargin = 5) => A.pageNumber === B.pageNumber && Math.abs(A.top - B.top) < yMargin && Math.abs(A.height - B.height) < yMargin;
139
+ var inside = (A, B) => A.pageNumber === B.pageNumber && A.top > B.top && A.left > B.left && A.top + A.height < B.top + B.height && A.left + A.width < B.left + B.width;
140
+ var nextTo = (A, B, xMargin = 10) => {
141
+ const Aright = A.left + A.width;
142
+ const Bright = B.left + B.width;
143
+ return A.pageNumber === B.pageNumber && A.left <= B.left && Aright <= Bright && B.left - Aright <= xMargin;
144
+ };
145
+ var extendWidth = (A, B) => {
146
+ A.width = Math.max(B.width - A.left + B.left, A.width);
147
+ };
148
+ var optimizeClientRects = (clientRects) => {
149
+ const rects = sort(clientRects);
150
+ const toRemove = /* @__PURE__ */ new Set();
151
+ const firstPass = rects.filter((rect) => {
152
+ return rects.every((otherRect) => {
153
+ return !inside(rect, otherRect);
154
+ });
155
+ });
156
+ let passCount = 0;
157
+ while (passCount <= 2) {
158
+ firstPass.forEach((A) => {
159
+ firstPass.forEach((B) => {
160
+ if (A === B || toRemove.has(A) || toRemove.has(B)) {
161
+ return;
162
+ }
163
+ if (!sameLine(A, B)) {
164
+ return;
165
+ }
166
+ if (overlaps(A, B)) {
167
+ extendWidth(A, B);
168
+ A.height = Math.max(A.height, B.height);
169
+ toRemove.add(B);
170
+ }
171
+ if (nextTo(A, B)) {
172
+ extendWidth(A, B);
173
+ toRemove.add(B);
174
+ }
175
+ });
176
+ });
177
+ passCount += 1;
178
+ }
179
+ return firstPass.filter((rect) => !toRemove.has(rect));
180
+ };
181
+ var optimize_client_rects_default = optimizeClientRects;
182
+
183
+ // src/lib/get-client-rects.ts
184
+ var isClientRectInsidePageRect = (clientRect, pageRect) => {
185
+ if (clientRect.top < pageRect.top) {
186
+ return false;
187
+ }
188
+ if (clientRect.bottom > pageRect.bottom) {
189
+ return false;
190
+ }
191
+ if (clientRect.right > pageRect.right) {
192
+ return false;
193
+ }
194
+ if (clientRect.left < pageRect.left) {
195
+ return false;
196
+ }
197
+ return true;
198
+ };
199
+ var getClientRects = (range, pages, shouldOptimize = true) => {
200
+ const clientRects = Array.from(range.getClientRects());
201
+ const rects = [];
202
+ for (const clientRect of clientRects) {
203
+ for (const page of pages) {
204
+ const pageRect = page.node.getBoundingClientRect();
205
+ if (isClientRectInsidePageRect(clientRect, pageRect) && clientRect.width > 0 && clientRect.height > 0 && clientRect.width < pageRect.width && clientRect.left > pageRect.left && clientRect.height < pageRect.height) {
206
+ const highlightedRect = {
207
+ top: clientRect.top + page.node.scrollTop - pageRect.top,
208
+ left: clientRect.left + page.node.scrollLeft - pageRect.left,
209
+ width: clientRect.width,
210
+ height: clientRect.height,
211
+ pageNumber: page.number
212
+ };
213
+ rects.push(highlightedRect);
214
+ }
215
+ }
216
+ }
217
+ return shouldOptimize ? optimize_client_rects_default(rects) : rects;
218
+ };
219
+ var get_client_rects_default = getClientRects;
220
+
221
+ // src/lib/group-highlights-by-page.ts
222
+ var groupHighlightsByPage = (highlights) => highlights.reduce((acc, highlight) => {
223
+ if (!highlight) {
224
+ return acc;
225
+ }
226
+ const pageNumbers = [
227
+ highlight.position.boundingRect.pageNumber,
228
+ ...highlight.position.rects.map((rect) => rect.pageNumber || 0)
229
+ ];
230
+ pageNumbers.forEach((pageNumber) => {
231
+ acc[pageNumber] ||= [];
232
+ const pageSpecificHighlight = {
233
+ ...highlight,
234
+ position: {
235
+ ...highlight.position,
236
+ rects: highlight.position.rects.filter(
237
+ (rect) => pageNumber === rect.pageNumber
238
+ )
239
+ }
240
+ };
241
+ acc[pageNumber].push(pageSpecificHighlight);
242
+ });
243
+ return acc;
244
+ }, {});
245
+ var group_highlights_by_page_default = groupHighlightsByPage;
246
+
247
+ // src/lib/pdfjs-dom.ts
248
+ var getDocument = (elm) => (elm || {}).ownerDocument || document;
249
+ var getWindow = (elm) => (getDocument(elm) || {}).defaultView || window;
250
+ var isHTMLElement = (elm) => elm instanceof HTMLElement || elm instanceof getWindow(elm).HTMLElement;
251
+ var isHTMLCanvasElement = (elm) => elm instanceof HTMLCanvasElement || elm instanceof getWindow(elm).HTMLCanvasElement;
252
+ var asElement = (x) => x;
253
+ var getPageFromElement = (target) => {
254
+ const node = asElement(target.closest(".page"));
255
+ if (!node || !isHTMLElement(node)) {
256
+ return null;
257
+ }
258
+ const number = Number(asElement(node).dataset.pageNumber);
259
+ return { node, number };
260
+ };
261
+ var getPagesFromRange = (range) => {
262
+ const startParentElement = range.startContainer.parentElement;
263
+ const endParentElement = range.endContainer.parentElement;
264
+ if (!isHTMLElement(startParentElement) || !isHTMLElement(endParentElement)) {
265
+ return [];
266
+ }
267
+ const startPage = getPageFromElement(asElement(startParentElement));
268
+ const endPage = getPageFromElement(asElement(endParentElement));
269
+ if (!startPage?.number || !endPage?.number) {
270
+ return [];
271
+ }
272
+ if (startPage.number === endPage.number) {
273
+ return [startPage];
274
+ }
275
+ if (startPage.number === endPage.number - 1) {
276
+ return [startPage, endPage];
277
+ }
278
+ const pages = [];
279
+ let currentPageNumber = startPage.number;
280
+ const document2 = startPage.node.ownerDocument;
281
+ while (currentPageNumber <= endPage.number) {
282
+ const currentPage = getPageFromElement(
283
+ document2.querySelector(
284
+ `[data-page-number='${currentPageNumber}'`
285
+ )
286
+ );
287
+ if (currentPage) {
288
+ pages.push(currentPage);
289
+ }
290
+ currentPageNumber++;
291
+ }
292
+ return pages;
293
+ };
294
+ var findOrCreateContainerLayer = (container, className) => {
295
+ const doc = getDocument(container);
296
+ let layer = container.querySelector(`.${className}`);
297
+ if (!layer && container.children.length) {
298
+ layer = doc.createElement("div");
299
+ layer.className = className;
300
+ container.appendChild(layer);
301
+ }
302
+ return layer;
303
+ };
304
+
305
+ // src/components/DrawingCanvas.tsx
306
+ import React, {
307
+ useRef,
308
+ useEffect,
309
+ useCallback,
310
+ useState
311
+ } from "react";
312
+ var DrawingCanvas = ({
313
+ isActive,
314
+ strokeColor = "#000000",
315
+ strokeWidth = 3,
316
+ viewer,
317
+ onComplete,
318
+ onCancel
319
+ }) => {
320
+ const canvasRef = useRef(null);
321
+ const [strokes, setStrokes] = useState([]);
322
+ const [currentStroke, setCurrentStroke] = useState(null);
323
+ const isDrawingRef = useRef(false);
324
+ const [pageNumber, setPageNumber] = useState(null);
325
+ const [pageElement, setPageElement] = useState(null);
326
+ const findPageFromPoint = useCallback(
327
+ (clientX, clientY) => {
328
+ if (!viewer) return null;
329
+ for (let i = 0; i < viewer.pagesCount; i++) {
330
+ const pageView = viewer.getPageView(i);
331
+ if (!pageView?.div) continue;
332
+ const rect = pageView.div.getBoundingClientRect();
333
+ if (clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) {
334
+ return {
335
+ pageNumber: i + 1,
336
+ element: pageView.div,
337
+ rect
338
+ };
339
+ }
340
+ }
341
+ return null;
342
+ },
343
+ [viewer]
344
+ );
345
+ const redrawCanvas = useCallback(() => {
346
+ const canvas = canvasRef.current;
347
+ const ctx = canvas?.getContext("2d");
348
+ if (!ctx || !canvas) return;
349
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
350
+ strokes.forEach((stroke) => {
351
+ if (stroke.points.length < 2) return;
352
+ ctx.strokeStyle = stroke.color;
353
+ ctx.lineWidth = stroke.width;
354
+ ctx.lineCap = "round";
355
+ ctx.lineJoin = "round";
356
+ ctx.beginPath();
357
+ ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
358
+ stroke.points.slice(1).forEach((point) => {
359
+ ctx.lineTo(point.x, point.y);
360
+ });
361
+ ctx.stroke();
362
+ });
363
+ if (currentStroke && currentStroke.points.length >= 2) {
364
+ ctx.strokeStyle = currentStroke.color;
365
+ ctx.lineWidth = currentStroke.width;
366
+ ctx.lineCap = "round";
367
+ ctx.lineJoin = "round";
368
+ ctx.beginPath();
369
+ ctx.moveTo(currentStroke.points[0].x, currentStroke.points[0].y);
370
+ currentStroke.points.slice(1).forEach((point) => {
371
+ ctx.lineTo(point.x, point.y);
372
+ });
373
+ ctx.stroke();
374
+ }
375
+ }, [strokes, currentStroke]);
376
+ useEffect(() => {
377
+ redrawCanvas();
378
+ }, [redrawCanvas]);
379
+ const handleStart = useCallback(
380
+ (clientX, clientY) => {
381
+ const pageInfo = findPageFromPoint(clientX, clientY);
382
+ if (!pageInfo) return;
383
+ console.log("DrawingCanvas: Started drawing on page", pageInfo.pageNumber);
384
+ if (pageNumber === null) {
385
+ setPageNumber(pageInfo.pageNumber);
386
+ setPageElement(pageInfo.element);
387
+ const canvas = canvasRef.current;
388
+ if (canvas) {
389
+ canvas.width = pageInfo.rect.width;
390
+ canvas.height = pageInfo.rect.height;
391
+ canvas.style.left = `${pageInfo.rect.left}px`;
392
+ canvas.style.top = `${pageInfo.rect.top}px`;
393
+ }
394
+ } else if (pageInfo.pageNumber !== pageNumber) {
395
+ console.log("DrawingCanvas: Ignoring - different page");
396
+ return;
397
+ }
398
+ isDrawingRef.current = true;
399
+ const pos = {
400
+ x: clientX - pageInfo.rect.left,
401
+ y: clientY - pageInfo.rect.top
402
+ };
403
+ setCurrentStroke({ points: [pos], color: strokeColor, width: strokeWidth });
404
+ },
405
+ [pageNumber, findPageFromPoint, strokeColor, strokeWidth]
406
+ );
407
+ const handleMove = useCallback(
408
+ (clientX, clientY) => {
409
+ if (!isDrawingRef.current || !pageElement) return;
410
+ const rect = pageElement.getBoundingClientRect();
411
+ const pos = {
412
+ x: clientX - rect.left,
413
+ y: clientY - rect.top
414
+ };
415
+ setCurrentStroke((prev) => {
416
+ if (!prev) return null;
417
+ return { ...prev, points: [...prev.points, pos] };
418
+ });
419
+ },
420
+ [pageElement]
421
+ );
422
+ const handleEnd = useCallback(() => {
423
+ if (!isDrawingRef.current) return;
424
+ isDrawingRef.current = false;
425
+ if (currentStroke && currentStroke.points.length >= 2) {
426
+ setStrokes((prev) => [...prev, currentStroke]);
427
+ }
428
+ setCurrentStroke(null);
429
+ }, [currentStroke]);
430
+ const handleMouseDown = useCallback(
431
+ (e) => {
432
+ e.preventDefault();
433
+ handleStart(e.clientX, e.clientY);
434
+ },
435
+ [handleStart]
436
+ );
437
+ const handleMouseMove = useCallback(
438
+ (e) => {
439
+ handleMove(e.clientX, e.clientY);
440
+ },
441
+ [handleMove]
442
+ );
443
+ const handleMouseUp = useCallback(() => {
444
+ handleEnd();
445
+ }, [handleEnd]);
446
+ const handleTouchStart = useCallback(
447
+ (e) => {
448
+ e.preventDefault();
449
+ if (e.touches.length > 0) {
450
+ handleStart(e.touches[0].clientX, e.touches[0].clientY);
451
+ }
452
+ },
453
+ [handleStart]
454
+ );
455
+ const handleTouchMove = useCallback(
456
+ (e) => {
457
+ e.preventDefault();
458
+ if (e.touches.length > 0) {
459
+ handleMove(e.touches[0].clientX, e.touches[0].clientY);
460
+ }
461
+ },
462
+ [handleMove]
463
+ );
464
+ const handleTouchEnd = useCallback(() => {
465
+ handleEnd();
466
+ }, [handleEnd]);
467
+ useEffect(() => {
468
+ if (!isActive) return;
469
+ const handleKeyDown = (e) => {
470
+ if (e.code === "Escape") {
471
+ console.log("DrawingCanvas: Cancelled via Escape");
472
+ onCancel();
473
+ }
474
+ };
475
+ document.addEventListener("keydown", handleKeyDown);
476
+ return () => document.removeEventListener("keydown", handleKeyDown);
477
+ }, [isActive, onCancel]);
478
+ const handleClear = () => {
479
+ console.log("DrawingCanvas: Cleared strokes");
480
+ setStrokes([]);
481
+ setCurrentStroke(null);
482
+ setPageNumber(null);
483
+ setPageElement(null);
484
+ };
485
+ const handleDone = () => {
486
+ if (strokes.length === 0 || pageNumber === null || !pageElement || !viewer) {
487
+ console.log("DrawingCanvas: No strokes to save");
488
+ onCancel();
489
+ return;
490
+ }
491
+ console.log("DrawingCanvas: Completing drawing with", strokes.length, "strokes");
492
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
493
+ strokes.forEach((stroke) => {
494
+ stroke.points.forEach((point) => {
495
+ minX = Math.min(minX, point.x);
496
+ minY = Math.min(minY, point.y);
497
+ maxX = Math.max(maxX, point.x);
498
+ maxY = Math.max(maxY, point.y);
499
+ });
500
+ });
501
+ const maxStrokeWidth = Math.max(...strokes.map((s) => s.width));
502
+ const padding = maxStrokeWidth * 2;
503
+ minX = Math.max(0, minX - padding);
504
+ minY = Math.max(0, minY - padding);
505
+ maxX = maxX + padding;
506
+ maxY = maxY + padding;
507
+ const width = maxX - minX;
508
+ const height = maxY - minY;
509
+ const outputCanvas = document.createElement("canvas");
510
+ outputCanvas.width = width;
511
+ outputCanvas.height = height;
512
+ const outputCtx = outputCanvas.getContext("2d");
513
+ if (!outputCtx) {
514
+ console.error("DrawingCanvas: Could not get output canvas context");
515
+ onCancel();
516
+ return;
517
+ }
518
+ strokes.forEach((stroke) => {
519
+ if (stroke.points.length < 2) return;
520
+ outputCtx.strokeStyle = stroke.color;
521
+ outputCtx.lineWidth = stroke.width;
522
+ outputCtx.lineCap = "round";
523
+ outputCtx.lineJoin = "round";
524
+ outputCtx.beginPath();
525
+ outputCtx.moveTo(stroke.points[0].x - minX, stroke.points[0].y - minY);
526
+ stroke.points.slice(1).forEach((point) => {
527
+ outputCtx.lineTo(point.x - minX, point.y - minY);
528
+ });
529
+ outputCtx.stroke();
530
+ });
531
+ const dataUrl = outputCanvas.toDataURL("image/png");
532
+ const viewportPosition = {
533
+ boundingRect: {
534
+ left: minX,
535
+ top: minY,
536
+ width,
537
+ height,
538
+ pageNumber
539
+ },
540
+ rects: []
541
+ };
542
+ const scaledPosition = viewportPositionToScaled(viewportPosition, viewer);
543
+ const normalizedStrokes = strokes.map((stroke) => ({
544
+ ...stroke,
545
+ points: stroke.points.map((point) => ({
546
+ x: point.x - minX,
547
+ y: point.y - minY
548
+ }))
549
+ }));
550
+ console.log("DrawingCanvas: Created drawing at position", scaledPosition);
551
+ onComplete(dataUrl, scaledPosition, normalizedStrokes);
552
+ setStrokes([]);
553
+ setCurrentStroke(null);
554
+ setPageNumber(null);
555
+ setPageElement(null);
556
+ };
557
+ if (!isActive) return null;
558
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
559
+ "canvas",
560
+ {
561
+ ref: canvasRef,
562
+ className: "DrawingCanvas",
563
+ style: {
564
+ width: pageElement ? pageElement.getBoundingClientRect().width : "100%",
565
+ height: pageElement ? pageElement.getBoundingClientRect().height : "100%",
566
+ position: "fixed"
567
+ },
568
+ onMouseDown: handleMouseDown,
569
+ onMouseMove: handleMouseMove,
570
+ onMouseUp: handleMouseUp,
571
+ onMouseLeave: handleMouseUp,
572
+ onTouchStart: handleTouchStart,
573
+ onTouchMove: handleTouchMove,
574
+ onTouchEnd: handleTouchEnd
575
+ }
576
+ ), /* @__PURE__ */ React.createElement("div", { className: "DrawingCanvas__controls" }, /* @__PURE__ */ React.createElement(
577
+ "button",
578
+ {
579
+ type: "button",
580
+ className: "DrawingCanvas__clearButton",
581
+ onClick: handleClear
582
+ },
583
+ "Clear"
584
+ ), /* @__PURE__ */ React.createElement(
585
+ "button",
586
+ {
587
+ type: "button",
588
+ className: "DrawingCanvas__cancelButton",
589
+ onClick: onCancel
590
+ },
591
+ "Cancel"
592
+ ), /* @__PURE__ */ React.createElement(
593
+ "button",
594
+ {
595
+ type: "button",
596
+ className: "DrawingCanvas__doneButton",
597
+ onClick: handleDone
598
+ },
599
+ "Done"
600
+ )));
601
+ };
602
+
603
+ // src/components/HighlightLayer.tsx
604
+ import React2 from "react";
605
+
606
+ // src/contexts/HighlightContext.ts
607
+ import { createContext as createContext2, useContext as useContext2 } from "react";
608
+ var HighlightContext = createContext2(void 0);
609
+ var useHighlightContainerContext = () => {
610
+ const highlightContainerUtils = useContext2(HighlightContext);
611
+ if (highlightContainerUtils === void 0) {
612
+ throw new Error(
613
+ "useHighlightContainerContext must be used within a child of PdfHighlighter!"
614
+ );
615
+ }
616
+ return highlightContainerUtils;
617
+ };
618
+
619
+ // src/lib/screenshot.ts
620
+ var getAreaAsPng = (canvas, position) => {
621
+ const { left, top, width, height } = position;
622
+ const doc = canvas ? canvas.ownerDocument : null;
623
+ const newCanvas = doc && doc.createElement("canvas");
624
+ if (!newCanvas || !isHTMLCanvasElement(newCanvas)) {
625
+ return "";
626
+ }
627
+ newCanvas.width = width;
628
+ newCanvas.height = height;
629
+ const newCanvasContext = newCanvas.getContext("2d");
630
+ if (!newCanvasContext || !canvas) {
631
+ return "";
632
+ }
633
+ const dpr = window.devicePixelRatio;
634
+ newCanvasContext.drawImage(
635
+ canvas,
636
+ left * dpr,
637
+ top * dpr,
638
+ width * dpr,
639
+ height * dpr,
640
+ 0,
641
+ 0,
642
+ width,
643
+ height
644
+ );
645
+ return newCanvas.toDataURL("image/png");
646
+ };
647
+ var screenshot = (position, pageNumber, viewer) => {
648
+ return getAreaAsPng(viewer.getPageView(pageNumber - 1).canvas, position);
649
+ };
650
+ var screenshot_default = screenshot;
651
+
652
+ // src/components/HighlightLayer.tsx
653
+ var EMPTY_ID = "empty-id";
654
+ var HighlightLayer = ({
655
+ highlightsByPage,
656
+ pageNumber,
657
+ scrolledToHighlightId,
658
+ viewer,
659
+ highlightBindings,
660
+ children
661
+ }) => {
662
+ const currentHighlights = highlightsByPage[pageNumber] || [];
663
+ return /* @__PURE__ */ React2.createElement("div", null, currentHighlights.map((highlight, index) => {
664
+ const viewportHighlight = {
665
+ ...highlight,
666
+ id: "id" in highlight ? highlight.id : EMPTY_ID,
667
+ // Give Empty ID to GhostHighlight
668
+ position: scaledPositionToViewport(highlight.position, viewer)
669
+ };
670
+ const isScrolledTo = Boolean(
671
+ scrolledToHighlightId === viewportHighlight.id
672
+ );
673
+ const highlightUtils = {
674
+ highlight: viewportHighlight,
675
+ viewportToScaled: (rect) => {
676
+ const viewport = viewer.getPageView(
677
+ (rect.pageNumber || pageNumber) - 1
678
+ // Convert to 0 index
679
+ ).viewport;
680
+ return viewportToScaled(rect, viewport);
681
+ },
682
+ screenshot: (boundingRect) => screenshot_default(boundingRect, pageNumber, viewer),
683
+ isScrolledTo,
684
+ highlightBindings
685
+ };
686
+ return /* @__PURE__ */ React2.createElement(HighlightContext.Provider, { value: highlightUtils, key: index }, children);
687
+ }));
688
+ };
689
+
690
+ // src/components/MouseSelection.tsx
691
+ import React3, { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
692
+ var getBoundingRect2 = (start, end) => {
693
+ return {
694
+ left: Math.min(end.x, start.x),
695
+ top: Math.min(end.y, start.y),
696
+ width: Math.abs(end.x - start.x),
697
+ height: Math.abs(end.y - start.y)
698
+ };
699
+ };
700
+ var getContainerCoords = (container, pageX, pageY) => {
701
+ const containerBoundingRect = container.getBoundingClientRect();
702
+ return {
703
+ x: pageX - containerBoundingRect.left + container.scrollLeft,
704
+ y: pageY - containerBoundingRect.top + container.scrollTop - window.scrollY
705
+ };
706
+ };
707
+ var MouseSelection = ({
708
+ viewer,
709
+ onSelection,
710
+ onReset,
711
+ onDragStart,
712
+ enableAreaSelection,
713
+ onChange,
714
+ style
715
+ }) => {
716
+ const [start, setStart] = useState2(null);
717
+ const [end, setEnd] = useState2(null);
718
+ const [locked, setLocked] = useState2(false);
719
+ const rootRef = useRef2(null);
720
+ const startTargetRef = useRef2(null);
721
+ const reset = () => {
722
+ onReset && onReset();
723
+ setStart(null);
724
+ setEnd(null);
725
+ setLocked(false);
726
+ };
727
+ useEffect2(() => {
728
+ onChange && onChange(Boolean(start && end));
729
+ if (!rootRef.current) return;
730
+ const container = asElement(rootRef.current.parentElement);
731
+ const handleMouseUp = (event) => {
732
+ if (!start || !end || !startTargetRef.current) return;
733
+ const boundingRect = getBoundingRect2(start, end);
734
+ const shouldEnd = boundingRect.width >= 1 && boundingRect.height >= 1;
735
+ if (!container.contains(asElement(event.target)) || !shouldEnd) {
736
+ reset();
737
+ return;
738
+ }
739
+ setLocked(true);
740
+ const page = getPageFromElement(startTargetRef.current);
741
+ if (!page) return;
742
+ const pageBoundingRect = {
743
+ ...boundingRect,
744
+ top: boundingRect.top - page.node.offsetTop,
745
+ left: boundingRect.left - page.node.offsetLeft,
746
+ pageNumber: page.number
747
+ };
748
+ const viewportPosition = {
749
+ boundingRect: pageBoundingRect,
750
+ rects: []
751
+ };
752
+ const scaledPosition = viewportPositionToScaled(viewportPosition, viewer);
753
+ const image = screenshot_default(
754
+ pageBoundingRect,
755
+ pageBoundingRect.pageNumber,
756
+ viewer
757
+ );
758
+ onSelection && onSelection(viewportPosition, scaledPosition, image, reset, event);
759
+ };
760
+ const handleMouseMove = (event) => {
761
+ if (!rootRef.current || !start || locked) return;
762
+ setEnd(getContainerCoords(container, event.pageX, event.pageY));
763
+ };
764
+ const handleMouseDown = (event) => {
765
+ const shouldStart = (event2) => enableAreaSelection(event2) && isHTMLElement(event2.target) && Boolean(asElement(event2.target).closest(".page"));
766
+ const shouldReset = (event2) => start && !asElement(event2.target).closest(".PdfHighlighter__tip-container");
767
+ if (!shouldStart(event)) {
768
+ if (shouldReset(event)) reset();
769
+ return;
770
+ }
771
+ startTargetRef.current = asElement(event.target);
772
+ onDragStart && onDragStart(event);
773
+ setStart(getContainerCoords(container, event.pageX, event.pageY));
774
+ setEnd(null);
775
+ setLocked(false);
776
+ };
777
+ container.addEventListener("mousemove", handleMouseMove);
778
+ container.addEventListener("mousedown", handleMouseDown);
779
+ document.addEventListener("mouseup", handleMouseUp);
780
+ return () => {
781
+ container.removeEventListener("mousemove", handleMouseMove);
782
+ container.removeEventListener("mousedown", handleMouseDown);
783
+ document.removeEventListener("mouseup", handleMouseUp);
784
+ };
785
+ }, [start, end, enableAreaSelection]);
786
+ return /* @__PURE__ */ React3.createElement("div", { className: "MouseSelection-container", ref: rootRef }, start && end && /* @__PURE__ */ React3.createElement(
787
+ "div",
788
+ {
789
+ className: "MouseSelection",
790
+ style: { ...getBoundingRect2(start, end), ...style }
791
+ }
792
+ ));
793
+ };
794
+
795
+ // src/components/ShapeCanvas.tsx
796
+ import React4, {
797
+ useRef as useRef3,
798
+ useEffect as useEffect3,
799
+ useCallback as useCallback2,
800
+ useState as useState3
801
+ } from "react";
802
+ var ShapeCanvas = ({
803
+ isActive,
804
+ shapeType,
805
+ strokeColor = "#000000",
806
+ strokeWidth = 2,
807
+ viewer,
808
+ onComplete,
809
+ onCancel
810
+ }) => {
811
+ const containerRef = useRef3(null);
812
+ const [startPoint, setStartPoint] = useState3(null);
813
+ const [currentPoint, setCurrentPoint] = useState3(null);
814
+ const [pageNumber, setPageNumber] = useState3(null);
815
+ const [pageRect, setPageRect] = useState3(null);
816
+ const isDrawingRef = useRef3(false);
817
+ const findPageFromPoint = useCallback2(
818
+ (clientX, clientY) => {
819
+ if (!viewer) return null;
820
+ for (let i = 0; i < viewer.pagesCount; i++) {
821
+ const pageView = viewer.getPageView(i);
822
+ if (!pageView?.div) continue;
823
+ const rect = pageView.div.getBoundingClientRect();
824
+ if (clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) {
825
+ return {
826
+ pageNumber: i + 1,
827
+ element: pageView.div,
828
+ rect
829
+ };
830
+ }
831
+ }
832
+ return null;
833
+ },
834
+ [viewer]
835
+ );
836
+ const handleStart = useCallback2(
837
+ (clientX, clientY) => {
838
+ const pageInfo = findPageFromPoint(clientX, clientY);
839
+ if (!pageInfo) return;
840
+ console.log("ShapeCanvas: Started drawing on page", pageInfo.pageNumber);
841
+ setPageNumber(pageInfo.pageNumber);
842
+ setPageRect(pageInfo.rect);
843
+ isDrawingRef.current = true;
844
+ const pos = {
845
+ x: clientX - pageInfo.rect.left,
846
+ y: clientY - pageInfo.rect.top
847
+ };
848
+ setStartPoint(pos);
849
+ setCurrentPoint(pos);
850
+ },
851
+ [findPageFromPoint]
852
+ );
853
+ const handleMove = useCallback2(
854
+ (clientX, clientY) => {
855
+ if (!isDrawingRef.current || !pageRect) return;
856
+ const pos = {
857
+ x: clientX - pageRect.left,
858
+ y: clientY - pageRect.top
859
+ };
860
+ setCurrentPoint(pos);
861
+ },
862
+ [pageRect]
863
+ );
864
+ const handleEnd = useCallback2(() => {
865
+ if (!isDrawingRef.current || !startPoint || !currentPoint || pageNumber === null || !viewer) {
866
+ isDrawingRef.current = false;
867
+ setStartPoint(null);
868
+ setCurrentPoint(null);
869
+ return;
870
+ }
871
+ isDrawingRef.current = false;
872
+ const minX = Math.min(startPoint.x, currentPoint.x);
873
+ const minY = Math.min(startPoint.y, currentPoint.y);
874
+ const maxX = Math.max(startPoint.x, currentPoint.x);
875
+ const maxY = Math.max(startPoint.y, currentPoint.y);
876
+ const width = maxX - minX;
877
+ const height = maxY - minY;
878
+ if (width < 10 || height < 10) {
879
+ console.log("ShapeCanvas: Shape too small, ignoring");
880
+ setStartPoint(null);
881
+ setCurrentPoint(null);
882
+ return;
883
+ }
884
+ console.log("ShapeCanvas: Creating shape", shapeType, "at", { minX, minY, width, height });
885
+ const viewportPosition = {
886
+ boundingRect: {
887
+ left: minX,
888
+ top: minY,
889
+ width,
890
+ height,
891
+ pageNumber
892
+ },
893
+ rects: []
894
+ };
895
+ const scaledPosition = viewportPositionToScaled(viewportPosition, viewer);
896
+ let shapeData = {
897
+ shapeType,
898
+ strokeColor,
899
+ strokeWidth
900
+ };
901
+ if (shapeType === "arrow") {
902
+ shapeData.startPoint = {
903
+ x: (startPoint.x - minX) / width,
904
+ y: (startPoint.y - minY) / height
905
+ };
906
+ shapeData.endPoint = {
907
+ x: (currentPoint.x - minX) / width,
908
+ y: (currentPoint.y - minY) / height
909
+ };
910
+ console.log("ShapeCanvas: Arrow points", shapeData.startPoint, "->", shapeData.endPoint);
911
+ }
912
+ console.log("ShapeCanvas: Created shape at position", scaledPosition);
913
+ onComplete(scaledPosition, shapeData);
914
+ setStartPoint(null);
915
+ setCurrentPoint(null);
916
+ setPageNumber(null);
917
+ setPageRect(null);
918
+ }, [startPoint, currentPoint, pageNumber, viewer, shapeType, strokeColor, strokeWidth, onComplete]);
919
+ const handleMouseDown = useCallback2(
920
+ (e) => {
921
+ e.preventDefault();
922
+ handleStart(e.clientX, e.clientY);
923
+ },
924
+ [handleStart]
925
+ );
926
+ const handleMouseMove = useCallback2(
927
+ (e) => {
928
+ handleMove(e.clientX, e.clientY);
929
+ },
930
+ [handleMove]
931
+ );
932
+ const handleMouseUp = useCallback2(() => {
933
+ handleEnd();
934
+ }, [handleEnd]);
935
+ const handleTouchStart = useCallback2(
936
+ (e) => {
937
+ e.preventDefault();
938
+ if (e.touches.length > 0) {
939
+ handleStart(e.touches[0].clientX, e.touches[0].clientY);
940
+ }
941
+ },
942
+ [handleStart]
943
+ );
944
+ const handleTouchMove = useCallback2(
945
+ (e) => {
946
+ e.preventDefault();
947
+ if (e.touches.length > 0) {
948
+ handleMove(e.touches[0].clientX, e.touches[0].clientY);
949
+ }
950
+ },
951
+ [handleMove]
952
+ );
953
+ const handleTouchEnd = useCallback2(() => {
954
+ handleEnd();
955
+ }, [handleEnd]);
956
+ useEffect3(() => {
957
+ if (!isActive) return;
958
+ const handleKeyDown = (e) => {
959
+ if (e.code === "Escape") {
960
+ console.log("ShapeCanvas: Cancelled via Escape");
961
+ onCancel();
962
+ }
963
+ };
964
+ document.addEventListener("keydown", handleKeyDown);
965
+ return () => document.removeEventListener("keydown", handleKeyDown);
966
+ }, [isActive, onCancel]);
967
+ const renderShapePreview = () => {
968
+ if (!startPoint || !currentPoint || !pageRect) return null;
969
+ const minX = Math.min(startPoint.x, currentPoint.x);
970
+ const minY = Math.min(startPoint.y, currentPoint.y);
971
+ const width = Math.abs(currentPoint.x - startPoint.x);
972
+ const height = Math.abs(currentPoint.y - startPoint.y);
973
+ const svgStyle = {
974
+ position: "fixed",
975
+ left: pageRect.left,
976
+ top: pageRect.top,
977
+ width: pageRect.width,
978
+ height: pageRect.height,
979
+ pointerEvents: "none",
980
+ zIndex: 1001
981
+ };
982
+ return /* @__PURE__ */ React4.createElement("svg", { style: svgStyle }, shapeType === "rectangle" && /* @__PURE__ */ React4.createElement(
983
+ "rect",
984
+ {
985
+ x: minX,
986
+ y: minY,
987
+ width,
988
+ height,
989
+ stroke: strokeColor,
990
+ strokeWidth,
991
+ fill: "none"
992
+ }
993
+ ), shapeType === "circle" && /* @__PURE__ */ React4.createElement(
994
+ "ellipse",
995
+ {
996
+ cx: minX + width / 2,
997
+ cy: minY + height / 2,
998
+ rx: width / 2,
999
+ ry: height / 2,
1000
+ stroke: strokeColor,
1001
+ strokeWidth,
1002
+ fill: "none"
1003
+ }
1004
+ ), shapeType === "arrow" && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement("defs", null, /* @__PURE__ */ React4.createElement(
1005
+ "marker",
1006
+ {
1007
+ id: "shape-canvas-arrowhead",
1008
+ markerWidth: "10",
1009
+ markerHeight: "7",
1010
+ refX: "9",
1011
+ refY: "3.5",
1012
+ orient: "auto"
1013
+ },
1014
+ /* @__PURE__ */ React4.createElement("polygon", { points: "0 0, 10 3.5, 0 7", fill: strokeColor })
1015
+ )), /* @__PURE__ */ React4.createElement(
1016
+ "line",
1017
+ {
1018
+ x1: startPoint.x,
1019
+ y1: startPoint.y,
1020
+ x2: currentPoint.x,
1021
+ y2: currentPoint.y,
1022
+ stroke: strokeColor,
1023
+ strokeWidth,
1024
+ markerEnd: "url(#shape-canvas-arrowhead)"
1025
+ }
1026
+ )));
1027
+ };
1028
+ if (!isActive) return null;
1029
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
1030
+ "div",
1031
+ {
1032
+ ref: containerRef,
1033
+ className: "ShapeCanvas",
1034
+ onMouseDown: handleMouseDown,
1035
+ onMouseMove: handleMouseMove,
1036
+ onMouseUp: handleMouseUp,
1037
+ onMouseLeave: handleMouseUp,
1038
+ onTouchStart: handleTouchStart,
1039
+ onTouchMove: handleTouchMove,
1040
+ onTouchEnd: handleTouchEnd
1041
+ }
1042
+ ), renderShapePreview(), /* @__PURE__ */ React4.createElement("div", { className: "ShapeCanvas__controls" }, /* @__PURE__ */ React4.createElement("div", { className: "ShapeCanvas__hint" }, "Click and drag to draw a ", shapeType, ". Press Escape to cancel."), /* @__PURE__ */ React4.createElement(
1043
+ "button",
1044
+ {
1045
+ type: "button",
1046
+ className: "ShapeCanvas__cancelButton",
1047
+ onClick: onCancel
1048
+ },
1049
+ "Cancel"
1050
+ )));
1051
+ };
1052
+
1053
+ // src/components/TipContainer.tsx
1054
+ import React5, {
1055
+ useLayoutEffect,
1056
+ useRef as useRef4,
1057
+ useState as useState4
1058
+ } from "react";
1059
+ var clamp = (value, left, right) => Math.min(Math.max(value, left), right);
1060
+ var VERTICAL_PADDING = 5;
1061
+ var TipContainer = ({
1062
+ viewer,
1063
+ updateTipPositionRef
1064
+ }) => {
1065
+ const [height, setHeight] = useState4(0);
1066
+ const [width, setWidth] = useState4(0);
1067
+ const containerRef = useRef4(null);
1068
+ const updatePosition = () => {
1069
+ if (!containerRef.current) return;
1070
+ const { offsetHeight, offsetWidth } = containerRef.current;
1071
+ setHeight(offsetHeight);
1072
+ setWidth(offsetWidth);
1073
+ };
1074
+ updateTipPositionRef.current = updatePosition;
1075
+ useLayoutEffect(() => {
1076
+ updatePosition();
1077
+ }, [updatePosition]);
1078
+ const { getTip } = usePdfHighlighterContext();
1079
+ const currentTip = getTip();
1080
+ if (!currentTip) return null;
1081
+ const { position, content } = currentTip;
1082
+ const { boundingRect } = position;
1083
+ const pageNumber = boundingRect.pageNumber;
1084
+ const pageNode = viewer.getPageView(pageNumber - 1).div;
1085
+ const pageBoundingClientRect = pageNode.getBoundingClientRect();
1086
+ const { left: pageLeft, width: pageWidth } = pageBoundingClientRect;
1087
+ const scrollTop = viewer.container.scrollTop;
1088
+ const left = pageNode.offsetLeft + boundingRect.left + boundingRect.width / 2;
1089
+ const highlightTop = boundingRect.top + pageNode.offsetTop;
1090
+ const highlightBottom = highlightTop + boundingRect.height;
1091
+ const shouldMove = highlightTop - height - VERTICAL_PADDING < scrollTop;
1092
+ const top = shouldMove ? highlightBottom + VERTICAL_PADDING : highlightTop - height - VERTICAL_PADDING;
1093
+ const clampedLeft = clamp(left - width / 2, 0, pageLeft + pageWidth - width);
1094
+ return /* @__PURE__ */ React5.createElement(
1095
+ "div",
1096
+ {
1097
+ className: "PdfHighlighter__tip-container",
1098
+ style: {
1099
+ top,
1100
+ left: clampedLeft,
1101
+ height: "max-content",
1102
+ width: "max-content"
1103
+ },
1104
+ ref: containerRef
1105
+ },
1106
+ content
1107
+ );
1108
+ };
1109
+
1110
+ // src/components/PdfHighlighter.tsx
1111
+ var EventBus;
1112
+ var PDFLinkService;
1113
+ var PDFViewer;
1114
+ (async () => {
1115
+ const pdfjs = await import("pdfjs-dist/web/pdf_viewer.mjs");
1116
+ EventBus = pdfjs.EventBus;
1117
+ PDFLinkService = pdfjs.PDFLinkService;
1118
+ PDFViewer = pdfjs.PDFViewer;
1119
+ })();
1120
+ var SCROLL_MARGIN = 10;
1121
+ var DEFAULT_SCALE_VALUE = "auto";
1122
+ var DEFAULT_TEXT_SELECTION_COLOR = "rgba(153,193,218,255)";
1123
+ var findOrCreateHighlightLayer = (textLayer) => {
1124
+ return findOrCreateContainerLayer(
1125
+ textLayer,
1126
+ "PdfHighlighter__highlight-layer"
1127
+ );
1128
+ };
1129
+ var disableTextSelection = (viewer, flag) => {
1130
+ viewer.viewer?.classList.toggle("PdfHighlighter--disable-selection", flag);
1131
+ };
1132
+ var PdfHighlighter = ({
1133
+ highlights,
1134
+ onScrollAway,
1135
+ pdfScaleValue = DEFAULT_SCALE_VALUE,
1136
+ onSelection: onSelectionFinished,
1137
+ onCreateGhostHighlight,
1138
+ onRemoveGhostHighlight,
1139
+ selectionTip,
1140
+ enableAreaSelection,
1141
+ areaSelectionMode,
1142
+ mouseSelectionStyle,
1143
+ pdfDocument,
1144
+ children,
1145
+ textSelectionColor = DEFAULT_TEXT_SELECTION_COLOR,
1146
+ utilsRef,
1147
+ style,
1148
+ enableFreetextCreation,
1149
+ onFreetextClick,
1150
+ enableImageCreation,
1151
+ onImageClick,
1152
+ enableDrawingMode,
1153
+ onDrawingComplete,
1154
+ onDrawingCancel,
1155
+ drawingStrokeColor = "#000000",
1156
+ drawingStrokeWidth = 3,
1157
+ enableShapeMode,
1158
+ onShapeComplete,
1159
+ onShapeCancel,
1160
+ shapeStrokeColor = "#000000",
1161
+ shapeStrokeWidth = 2
1162
+ }) => {
1163
+ const [tip, setTip] = useState5(null);
1164
+ const [isViewerReady, setIsViewerReady] = useState5(false);
1165
+ const containerNodeRef = useRef5(null);
1166
+ const highlightBindingsRef = useRef5(
1167
+ {}
1168
+ );
1169
+ const ghostHighlightRef = useRef5(null);
1170
+ const selectionRef = useRef5(null);
1171
+ const scrolledToHighlightIdRef = useRef5(null);
1172
+ const isAreaSelectionInProgressRef = useRef5(false);
1173
+ const isEditInProgressRef = useRef5(false);
1174
+ const updateTipPositionRef = useRef5(() => {
1175
+ });
1176
+ const eventBusRef = useRef5(new EventBus());
1177
+ const linkServiceRef = useRef5(
1178
+ new PDFLinkService({
1179
+ eventBus: eventBusRef.current,
1180
+ externalLinkTarget: 2
1181
+ })
1182
+ );
1183
+ const resizeObserverRef = useRef5(null);
1184
+ const viewerRef = useRef5(null);
1185
+ useLayoutEffect2(() => {
1186
+ if (!containerNodeRef.current) return;
1187
+ const debouncedDocumentInit = debounce(() => {
1188
+ viewerRef.current = viewerRef.current || new PDFViewer({
1189
+ container: containerNodeRef.current,
1190
+ eventBus: eventBusRef.current,
1191
+ textLayerMode: 2,
1192
+ removePageBorders: true,
1193
+ linkService: linkServiceRef.current
1194
+ });
1195
+ viewerRef.current.setDocument(pdfDocument);
1196
+ linkServiceRef.current.setDocument(pdfDocument);
1197
+ linkServiceRef.current.setViewer(viewerRef.current);
1198
+ setIsViewerReady(true);
1199
+ }, 100);
1200
+ debouncedDocumentInit();
1201
+ return () => {
1202
+ debouncedDocumentInit.cancel();
1203
+ };
1204
+ }, [document]);
1205
+ useLayoutEffect2(() => {
1206
+ if (!containerNodeRef.current) return;
1207
+ resizeObserverRef.current = new ResizeObserver(handleScaleValue);
1208
+ resizeObserverRef.current.observe(containerNodeRef.current);
1209
+ const doc = containerNodeRef.current.ownerDocument;
1210
+ eventBusRef.current.on("textlayerrendered", renderHighlightLayers);
1211
+ eventBusRef.current.on("pagesinit", handleScaleValue);
1212
+ doc.addEventListener("keydown", handleKeyDown);
1213
+ renderHighlightLayers();
1214
+ return () => {
1215
+ eventBusRef.current.off("pagesinit", handleScaleValue);
1216
+ eventBusRef.current.off("textlayerrendered", renderHighlightLayers);
1217
+ doc.removeEventListener("keydown", handleKeyDown);
1218
+ resizeObserverRef.current?.disconnect();
1219
+ };
1220
+ }, [selectionTip, highlights, onSelectionFinished]);
1221
+ const handleScroll = () => {
1222
+ onScrollAway && onScrollAway();
1223
+ scrolledToHighlightIdRef.current = null;
1224
+ renderHighlightLayers();
1225
+ };
1226
+ const handleMouseUp = () => {
1227
+ const container = containerNodeRef.current;
1228
+ const selection = getWindow(container).getSelection();
1229
+ if (!container || !selection || selection.isCollapsed || !viewerRef.current)
1230
+ return;
1231
+ const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
1232
+ if (!range || !container.contains(range.commonAncestorContainer)) return;
1233
+ const pages = getPagesFromRange(range);
1234
+ if (!pages || pages.length === 0) return;
1235
+ const rects = get_client_rects_default(range, pages);
1236
+ if (rects.length === 0) return;
1237
+ const viewportPosition = {
1238
+ boundingRect: get_bounding_rect_default(rects),
1239
+ rects
1240
+ };
1241
+ const scaledPosition = viewportPositionToScaled(
1242
+ viewportPosition,
1243
+ viewerRef.current
1244
+ );
1245
+ const content = {
1246
+ text: selection.toString().split("\n").join(" ")
1247
+ // Make all line breaks spaces
1248
+ };
1249
+ selectionRef.current = {
1250
+ content,
1251
+ type: "text",
1252
+ position: scaledPosition,
1253
+ makeGhostHighlight: () => {
1254
+ ghostHighlightRef.current = {
1255
+ content,
1256
+ type: "text",
1257
+ position: scaledPosition
1258
+ };
1259
+ onCreateGhostHighlight && onCreateGhostHighlight(ghostHighlightRef.current);
1260
+ clearTextSelection();
1261
+ renderHighlightLayers();
1262
+ return ghostHighlightRef.current;
1263
+ }
1264
+ };
1265
+ onSelectionFinished && onSelectionFinished(selectionRef.current);
1266
+ selectionTip && setTip({ position: viewportPosition, content: selectionTip });
1267
+ };
1268
+ const handleMouseDown = (event) => {
1269
+ if (!isHTMLElement(event.target) || asElement(event.target).closest(".PdfHighlighter__tip-container")) {
1270
+ return;
1271
+ }
1272
+ if (enableFreetextCreation?.(event.nativeEvent) && onFreetextClick && !isEditInProgressRef.current) {
1273
+ const target = asElement(event.target);
1274
+ const page = getPageFromElement(target);
1275
+ if (page && viewerRef.current) {
1276
+ const pageRect = page.node.getBoundingClientRect();
1277
+ const clickX = event.clientX - pageRect.left;
1278
+ const clickY = event.clientY - pageRect.top;
1279
+ const defaultWidth = 150;
1280
+ const defaultHeight = 80;
1281
+ const viewportPosition = {
1282
+ boundingRect: {
1283
+ left: clickX,
1284
+ top: clickY,
1285
+ width: defaultWidth,
1286
+ height: defaultHeight,
1287
+ pageNumber: page.number
1288
+ },
1289
+ rects: []
1290
+ };
1291
+ const scaledPosition = viewportPositionToScaled(
1292
+ viewportPosition,
1293
+ viewerRef.current
1294
+ );
1295
+ onFreetextClick(scaledPosition);
1296
+ return;
1297
+ }
1298
+ }
1299
+ if (enableImageCreation?.(event.nativeEvent) && onImageClick && !isEditInProgressRef.current) {
1300
+ const target = asElement(event.target);
1301
+ const page = getPageFromElement(target);
1302
+ if (page && viewerRef.current) {
1303
+ const pageRect = page.node.getBoundingClientRect();
1304
+ const clickX = event.clientX - pageRect.left;
1305
+ const clickY = event.clientY - pageRect.top;
1306
+ const defaultWidth = 150;
1307
+ const defaultHeight = 100;
1308
+ const viewportPosition = {
1309
+ boundingRect: {
1310
+ left: clickX,
1311
+ top: clickY,
1312
+ width: defaultWidth,
1313
+ height: defaultHeight,
1314
+ pageNumber: page.number
1315
+ },
1316
+ rects: []
1317
+ };
1318
+ const scaledPosition = viewportPositionToScaled(
1319
+ viewportPosition,
1320
+ viewerRef.current
1321
+ );
1322
+ onImageClick(scaledPosition);
1323
+ return;
1324
+ }
1325
+ }
1326
+ setTip(null);
1327
+ clearTextSelection();
1328
+ removeGhostHighlight();
1329
+ toggleEditInProgress(false);
1330
+ };
1331
+ const handleKeyDown = (event) => {
1332
+ if (event.code === "Escape") {
1333
+ clearTextSelection();
1334
+ removeGhostHighlight();
1335
+ setTip(null);
1336
+ }
1337
+ };
1338
+ const handleScaleValue = () => {
1339
+ if (viewerRef.current) {
1340
+ viewerRef.current.currentScaleValue = pdfScaleValue.toString();
1341
+ }
1342
+ };
1343
+ const renderHighlightLayer = (highlightBindings, pageNumber) => {
1344
+ if (!viewerRef.current) return;
1345
+ highlightBindings.reactRoot.render(
1346
+ /* @__PURE__ */ React6.createElement(PdfHighlighterContext.Provider, { value: pdfHighlighterUtils }, /* @__PURE__ */ React6.createElement(
1347
+ HighlightLayer,
1348
+ {
1349
+ highlightsByPage: group_highlights_by_page_default([
1350
+ ...highlights,
1351
+ ghostHighlightRef.current
1352
+ ]),
1353
+ pageNumber,
1354
+ scrolledToHighlightId: scrolledToHighlightIdRef.current,
1355
+ viewer: viewerRef.current,
1356
+ highlightBindings,
1357
+ children
1358
+ }
1359
+ ))
1360
+ );
1361
+ };
1362
+ const renderHighlightLayers = () => {
1363
+ if (!viewerRef.current) return;
1364
+ for (let pageNumber = 1; pageNumber <= pdfDocument.numPages; pageNumber++) {
1365
+ const highlightBindings = highlightBindingsRef.current[pageNumber];
1366
+ if (highlightBindings?.container?.isConnected) {
1367
+ renderHighlightLayer(highlightBindings, pageNumber);
1368
+ } else {
1369
+ const { textLayer } = viewerRef.current.getPageView(pageNumber - 1) || {};
1370
+ if (!textLayer) continue;
1371
+ const highlightLayer = findOrCreateHighlightLayer(
1372
+ textLayer.div
1373
+ );
1374
+ if (highlightLayer) {
1375
+ const reactRoot = createRoot(highlightLayer);
1376
+ highlightBindingsRef.current[pageNumber] = {
1377
+ reactRoot,
1378
+ container: highlightLayer,
1379
+ textLayer: textLayer.div
1380
+ // textLayer.div for version >=3.0 and textLayer.textLayerDiv otherwise.
1381
+ };
1382
+ renderHighlightLayer(
1383
+ highlightBindingsRef.current[pageNumber],
1384
+ pageNumber
1385
+ );
1386
+ }
1387
+ }
1388
+ }
1389
+ };
1390
+ const isEditingOrHighlighting = () => {
1391
+ return Boolean(selectionRef.current) || Boolean(ghostHighlightRef.current) || isAreaSelectionInProgressRef.current || isEditInProgressRef.current;
1392
+ };
1393
+ const toggleEditInProgress = (flag) => {
1394
+ if (flag !== void 0) {
1395
+ isEditInProgressRef.current = flag;
1396
+ } else {
1397
+ isEditInProgressRef.current = !isEditInProgressRef.current;
1398
+ }
1399
+ if (viewerRef.current)
1400
+ viewerRef.current.viewer?.classList.toggle(
1401
+ "PdfHighlighter--disable-selection",
1402
+ isEditInProgressRef.current
1403
+ );
1404
+ };
1405
+ const removeGhostHighlight = () => {
1406
+ if (onRemoveGhostHighlight && ghostHighlightRef.current)
1407
+ onRemoveGhostHighlight(ghostHighlightRef.current);
1408
+ ghostHighlightRef.current = null;
1409
+ renderHighlightLayers();
1410
+ };
1411
+ const clearTextSelection = () => {
1412
+ selectionRef.current = null;
1413
+ const container = containerNodeRef.current;
1414
+ const selection = getWindow(container).getSelection();
1415
+ if (!container || !selection) return;
1416
+ selection.removeAllRanges();
1417
+ };
1418
+ const scrollToHighlight = (highlight) => {
1419
+ const { boundingRect, usePdfCoordinates } = highlight.position;
1420
+ const pageNumber = boundingRect.pageNumber;
1421
+ viewerRef.current.container.removeEventListener("scroll", handleScroll);
1422
+ const pageViewport = viewerRef.current.getPageView(
1423
+ pageNumber - 1
1424
+ ).viewport;
1425
+ viewerRef.current.scrollPageIntoView({
1426
+ pageNumber,
1427
+ destArray: [
1428
+ null,
1429
+ // null since we pass pageNumber already as an arg
1430
+ { name: "XYZ" },
1431
+ ...pageViewport.convertToPdfPoint(
1432
+ 0,
1433
+ // Default x coord
1434
+ scaledToViewport(boundingRect, pageViewport, usePdfCoordinates).top - SCROLL_MARGIN
1435
+ ),
1436
+ 0
1437
+ // Default z coord
1438
+ ]
1439
+ });
1440
+ scrolledToHighlightIdRef.current = highlight.id;
1441
+ renderHighlightLayers();
1442
+ setTimeout(() => {
1443
+ viewerRef.current.container.addEventListener("scroll", handleScroll, {
1444
+ once: true
1445
+ });
1446
+ }, 100);
1447
+ };
1448
+ const pdfHighlighterUtils = {
1449
+ isEditingOrHighlighting,
1450
+ getCurrentSelection: () => selectionRef.current,
1451
+ getGhostHighlight: () => ghostHighlightRef.current,
1452
+ removeGhostHighlight,
1453
+ toggleEditInProgress,
1454
+ isEditInProgress: () => isEditInProgressRef.current,
1455
+ isSelectionInProgress: () => Boolean(selectionRef.current) || isAreaSelectionInProgressRef.current,
1456
+ scrollToHighlight,
1457
+ getViewer: () => viewerRef.current,
1458
+ getTip: () => tip,
1459
+ setTip,
1460
+ updateTipPosition: updateTipPositionRef.current
1461
+ };
1462
+ utilsRef(pdfHighlighterUtils);
1463
+ const isFreetextMode = enableFreetextCreation?.({}) ?? false;
1464
+ const isImageMode = enableImageCreation?.({}) ?? false;
1465
+ let containerClassName = "PdfHighlighter";
1466
+ if (isFreetextMode) containerClassName += " PdfHighlighter--freetext-mode";
1467
+ if (isImageMode) containerClassName += " PdfHighlighter--image-mode";
1468
+ if (enableDrawingMode) containerClassName += " PdfHighlighter--drawing-mode";
1469
+ if (enableShapeMode) containerClassName += " PdfHighlighter--shape-mode";
1470
+ if (areaSelectionMode) containerClassName += " PdfHighlighter--area-mode";
1471
+ return /* @__PURE__ */ React6.createElement(PdfHighlighterContext.Provider, { value: pdfHighlighterUtils }, /* @__PURE__ */ React6.createElement(
1472
+ "div",
1473
+ {
1474
+ ref: containerNodeRef,
1475
+ className: containerClassName,
1476
+ onPointerDown: handleMouseDown,
1477
+ onPointerUp: handleMouseUp,
1478
+ style
1479
+ },
1480
+ /* @__PURE__ */ React6.createElement("div", { className: "pdfViewer" }),
1481
+ /* @__PURE__ */ React6.createElement("style", null, `
1482
+ .textLayer ::selection {
1483
+ background: ${textSelectionColor};
1484
+ }
1485
+ `),
1486
+ isViewerReady && /* @__PURE__ */ React6.createElement(
1487
+ TipContainer,
1488
+ {
1489
+ viewer: viewerRef.current,
1490
+ updateTipPositionRef
1491
+ }
1492
+ ),
1493
+ isViewerReady && enableAreaSelection && /* @__PURE__ */ React6.createElement(
1494
+ MouseSelection,
1495
+ {
1496
+ viewer: viewerRef.current,
1497
+ onChange: (isVisible) => isAreaSelectionInProgressRef.current = isVisible,
1498
+ enableAreaSelection,
1499
+ style: mouseSelectionStyle,
1500
+ onDragStart: () => disableTextSelection(viewerRef.current, true),
1501
+ onReset: () => {
1502
+ selectionRef.current = null;
1503
+ disableTextSelection(viewerRef.current, false);
1504
+ },
1505
+ onSelection: (viewportPosition, scaledPosition, image, resetSelection) => {
1506
+ selectionRef.current = {
1507
+ content: { image },
1508
+ type: "area",
1509
+ position: scaledPosition,
1510
+ makeGhostHighlight: () => {
1511
+ ghostHighlightRef.current = {
1512
+ position: scaledPosition,
1513
+ type: "area",
1514
+ content: { image }
1515
+ };
1516
+ onCreateGhostHighlight && onCreateGhostHighlight(ghostHighlightRef.current);
1517
+ resetSelection();
1518
+ renderHighlightLayers();
1519
+ return ghostHighlightRef.current;
1520
+ }
1521
+ };
1522
+ onSelectionFinished && onSelectionFinished(selectionRef.current);
1523
+ selectionTip && setTip({ position: viewportPosition, content: selectionTip });
1524
+ }
1525
+ }
1526
+ ),
1527
+ isViewerReady && enableDrawingMode && /* @__PURE__ */ React6.createElement(
1528
+ DrawingCanvas,
1529
+ {
1530
+ isActive: enableDrawingMode,
1531
+ strokeColor: drawingStrokeColor,
1532
+ strokeWidth: drawingStrokeWidth,
1533
+ viewer: viewerRef.current,
1534
+ onComplete: (dataUrl, position, strokes) => {
1535
+ console.log("PdfHighlighter: Drawing complete");
1536
+ onDrawingComplete?.(dataUrl, position, strokes);
1537
+ },
1538
+ onCancel: () => {
1539
+ console.log("PdfHighlighter: Drawing cancelled");
1540
+ onDrawingCancel?.();
1541
+ }
1542
+ }
1543
+ ),
1544
+ isViewerReady && enableShapeMode && /* @__PURE__ */ React6.createElement(
1545
+ ShapeCanvas,
1546
+ {
1547
+ isActive: !!enableShapeMode,
1548
+ shapeType: enableShapeMode,
1549
+ strokeColor: shapeStrokeColor,
1550
+ strokeWidth: shapeStrokeWidth,
1551
+ viewer: viewerRef.current,
1552
+ onComplete: (position, shape) => {
1553
+ console.log("PdfHighlighter: Shape complete", shape.shapeType);
1554
+ onShapeComplete?.(position, shape);
1555
+ },
1556
+ onCancel: () => {
1557
+ console.log("PdfHighlighter: Shape cancelled");
1558
+ onShapeCancel?.();
1559
+ }
1560
+ }
1561
+ )
1562
+ ));
1563
+ };
1564
+
1565
+ // src/components/TextHighlight.tsx
1566
+ import React7, {
1567
+ useState as useState6,
1568
+ useRef as useRef6,
1569
+ useEffect as useEffect4
1570
+ } from "react";
1571
+ var DefaultStyleIcon = () => /* @__PURE__ */ React7.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React7.createElement("path", { d: "M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }));
1572
+ var DefaultDeleteIcon = () => /* @__PURE__ */ React7.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React7.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }));
1573
+ var HighlightIcon = () => /* @__PURE__ */ React7.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React7.createElement("path", { d: "M6 14l3 3v5h6v-5l3-3V9H6v5zm5-12h2v3h-2V2zM3.5 5.875L4.914 4.46l2.12 2.122L5.622 8 3.5 5.875zm13.46.71l2.123-2.12 1.414 1.414L18.375 8l-1.414-1.414z" }));
1574
+ var UnderlineIcon = () => /* @__PURE__ */ React7.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React7.createElement("path", { d: "M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z" }));
1575
+ var StrikethroughIcon = () => /* @__PURE__ */ React7.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React7.createElement("path", { d: "M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z" }));
1576
+ var DEFAULT_COLOR_PRESETS = [
1577
+ "rgba(255, 226, 143, 1)",
1578
+ // Yellow (default)
1579
+ "#ffcdd2",
1580
+ // Light red
1581
+ "#c8e6c9",
1582
+ // Light green
1583
+ "#bbdefb",
1584
+ // Light blue
1585
+ "#e1bee7"
1586
+ // Light purple
1587
+ ];
1588
+ var TextHighlight = ({
1589
+ highlight,
1590
+ onClick,
1591
+ onMouseOver,
1592
+ onMouseOut,
1593
+ isScrolledTo,
1594
+ onContextMenu,
1595
+ style,
1596
+ highlightColor = "rgba(255, 226, 143, 1)",
1597
+ highlightStyle = "highlight",
1598
+ onStyleChange,
1599
+ onDelete,
1600
+ styleIcon,
1601
+ deleteIcon,
1602
+ colorPresets = DEFAULT_COLOR_PRESETS
1603
+ }) => {
1604
+ const [isStylePanelOpen, setIsStylePanelOpen] = useState6(false);
1605
+ const [isHovered, setIsHovered] = useState6(false);
1606
+ const stylePanelRef = useRef6(null);
1607
+ const containerRef = useRef6(null);
1608
+ useEffect4(() => {
1609
+ if (!isStylePanelOpen) return;
1610
+ const handleClickOutside = (e) => {
1611
+ if (stylePanelRef.current && !stylePanelRef.current.contains(e.target)) {
1612
+ setIsStylePanelOpen(false);
1613
+ }
1614
+ };
1615
+ const timeoutId = setTimeout(() => {
1616
+ document.addEventListener("mousedown", handleClickOutside);
1617
+ }, 0);
1618
+ return () => {
1619
+ clearTimeout(timeoutId);
1620
+ document.removeEventListener("mousedown", handleClickOutside);
1621
+ };
1622
+ }, [isStylePanelOpen]);
1623
+ const highlightClass = isScrolledTo ? "TextHighlight--scrolledTo" : "";
1624
+ const { rects } = highlight.position;
1625
+ const firstRect = rects[0];
1626
+ const getPartStyleClass = () => {
1627
+ switch (highlightStyle) {
1628
+ case "underline":
1629
+ return "TextHighlight__part--underline";
1630
+ case "strikethrough":
1631
+ return "TextHighlight__part--strikethrough";
1632
+ default:
1633
+ return "";
1634
+ }
1635
+ };
1636
+ const getPartStyle = (rect) => {
1637
+ const baseStyle = { ...rect, ...style };
1638
+ if (highlightStyle === "highlight") {
1639
+ baseStyle.backgroundColor = highlightColor;
1640
+ } else {
1641
+ baseStyle.backgroundColor = "transparent";
1642
+ baseStyle.color = highlightColor;
1643
+ }
1644
+ return baseStyle;
1645
+ };
1646
+ return /* @__PURE__ */ React7.createElement(
1647
+ "div",
1648
+ {
1649
+ className: `TextHighlight ${highlightClass}`,
1650
+ onContextMenu,
1651
+ ref: containerRef
1652
+ },
1653
+ (onStyleChange || onDelete) && firstRect && /* @__PURE__ */ React7.createElement(
1654
+ "div",
1655
+ {
1656
+ className: "TextHighlight__toolbar-wrapper",
1657
+ style: {
1658
+ position: "absolute",
1659
+ left: firstRect.left,
1660
+ top: firstRect.top - 28,
1661
+ paddingBottom: 12
1662
+ },
1663
+ onMouseEnter: () => setIsHovered(true),
1664
+ onMouseLeave: () => setIsHovered(false)
1665
+ },
1666
+ /* @__PURE__ */ React7.createElement(
1667
+ "div",
1668
+ {
1669
+ className: `TextHighlight__toolbar ${isHovered || isStylePanelOpen ? "TextHighlight__toolbar--visible" : ""}`
1670
+ },
1671
+ onStyleChange && /* @__PURE__ */ React7.createElement(
1672
+ "button",
1673
+ {
1674
+ className: "TextHighlight__style-button",
1675
+ onClick: (e) => {
1676
+ e.stopPropagation();
1677
+ setIsStylePanelOpen(!isStylePanelOpen);
1678
+ },
1679
+ title: "Change style",
1680
+ type: "button"
1681
+ },
1682
+ styleIcon || /* @__PURE__ */ React7.createElement(DefaultStyleIcon, null)
1683
+ ),
1684
+ onDelete && /* @__PURE__ */ React7.createElement(
1685
+ "button",
1686
+ {
1687
+ className: "TextHighlight__delete-button",
1688
+ onClick: (e) => {
1689
+ e.stopPropagation();
1690
+ onDelete();
1691
+ },
1692
+ title: "Delete",
1693
+ type: "button"
1694
+ },
1695
+ deleteIcon || /* @__PURE__ */ React7.createElement(DefaultDeleteIcon, null)
1696
+ )
1697
+ ),
1698
+ isStylePanelOpen && onStyleChange && /* @__PURE__ */ React7.createElement(
1699
+ "div",
1700
+ {
1701
+ className: "TextHighlight__style-panel",
1702
+ ref: stylePanelRef,
1703
+ onClick: (e) => e.stopPropagation()
1704
+ },
1705
+ /* @__PURE__ */ React7.createElement("div", { className: "TextHighlight__style-row" }, /* @__PURE__ */ React7.createElement("label", null, "Style"), /* @__PURE__ */ React7.createElement("div", { className: "TextHighlight__style-buttons" }, /* @__PURE__ */ React7.createElement(
1706
+ "button",
1707
+ {
1708
+ type: "button",
1709
+ className: `TextHighlight__style-type-button ${highlightStyle === "highlight" ? "active" : ""}`,
1710
+ onClick: () => onStyleChange({ highlightStyle: "highlight" }),
1711
+ title: "Highlight"
1712
+ },
1713
+ /* @__PURE__ */ React7.createElement(HighlightIcon, null)
1714
+ ), /* @__PURE__ */ React7.createElement(
1715
+ "button",
1716
+ {
1717
+ type: "button",
1718
+ className: `TextHighlight__style-type-button ${highlightStyle === "underline" ? "active" : ""}`,
1719
+ onClick: () => onStyleChange({ highlightStyle: "underline" }),
1720
+ title: "Underline"
1721
+ },
1722
+ /* @__PURE__ */ React7.createElement(UnderlineIcon, null)
1723
+ ), /* @__PURE__ */ React7.createElement(
1724
+ "button",
1725
+ {
1726
+ type: "button",
1727
+ className: `TextHighlight__style-type-button ${highlightStyle === "strikethrough" ? "active" : ""}`,
1728
+ onClick: () => onStyleChange({ highlightStyle: "strikethrough" }),
1729
+ title: "Strikethrough"
1730
+ },
1731
+ /* @__PURE__ */ React7.createElement(StrikethroughIcon, null)
1732
+ ))),
1733
+ /* @__PURE__ */ React7.createElement("div", { className: "TextHighlight__style-row" }, /* @__PURE__ */ React7.createElement("label", null, "Color"), /* @__PURE__ */ React7.createElement("div", { className: "TextHighlight__color-options" }, /* @__PURE__ */ React7.createElement("div", { className: "TextHighlight__color-presets" }, colorPresets.map((c) => /* @__PURE__ */ React7.createElement(
1734
+ "button",
1735
+ {
1736
+ key: c,
1737
+ type: "button",
1738
+ className: `TextHighlight__color-preset ${highlightColor === c ? "active" : ""}`,
1739
+ style: { backgroundColor: c },
1740
+ onClick: () => onStyleChange({ highlightColor: c }),
1741
+ title: c
1742
+ }
1743
+ ))), /* @__PURE__ */ React7.createElement(
1744
+ "input",
1745
+ {
1746
+ type: "color",
1747
+ value: highlightColor,
1748
+ onChange: (e) => {
1749
+ onStyleChange({ highlightColor: e.target.value });
1750
+ }
1751
+ }
1752
+ )))
1753
+ )
1754
+ ),
1755
+ /* @__PURE__ */ React7.createElement(
1756
+ "div",
1757
+ {
1758
+ className: "TextHighlight__parts",
1759
+ onMouseEnter: () => setIsHovered(true),
1760
+ onMouseLeave: () => setIsHovered(false)
1761
+ },
1762
+ rects.map((rect, index) => /* @__PURE__ */ React7.createElement(
1763
+ "div",
1764
+ {
1765
+ onMouseOver,
1766
+ onMouseOut,
1767
+ onClick,
1768
+ key: index,
1769
+ style: getPartStyle(rect),
1770
+ className: `TextHighlight__part ${getPartStyleClass()}`
1771
+ }
1772
+ ))
1773
+ )
1774
+ );
1775
+ };
1776
+
1777
+ // src/components/MonitoredHighlightContainer.tsx
1778
+ import React9, { useRef as useRef8 } from "react";
1779
+
1780
+ // src/components/MouseMonitor.tsx
1781
+ import React8, { useEffect as useEffect5, useRef as useRef7 } from "react";
1782
+ var MouseMonitor = ({
1783
+ onMoveAway,
1784
+ paddingX,
1785
+ paddingY,
1786
+ children
1787
+ }) => {
1788
+ const containerRef = useRef7(null);
1789
+ const onMouseMove = (event) => {
1790
+ if (!containerRef.current) return;
1791
+ const { clientX, clientY } = event;
1792
+ const { left, top, width, height } = containerRef.current.getBoundingClientRect();
1793
+ const inBoundsX = clientX > left - paddingX && clientX < left + width + paddingX;
1794
+ const inBoundsY = clientY > top - paddingY && clientY < top + height + paddingY;
1795
+ if (!(inBoundsX && inBoundsY)) {
1796
+ onMoveAway();
1797
+ }
1798
+ };
1799
+ useEffect5(() => {
1800
+ document.addEventListener("mousemove", onMouseMove);
1801
+ return () => {
1802
+ document.removeEventListener("mousemove", onMouseMove);
1803
+ };
1804
+ }, []);
1805
+ return /* @__PURE__ */ React8.createElement("div", { ref: containerRef }, children);
1806
+ };
1807
+
1808
+ // src/components/MonitoredHighlightContainer.tsx
1809
+ var MonitoredHighlightContainer = ({
1810
+ onMouseEnter,
1811
+ highlightTip,
1812
+ onMouseLeave,
1813
+ children
1814
+ }) => {
1815
+ const mouseInRef = useRef8(false);
1816
+ const { setTip, isEditingOrHighlighting } = usePdfHighlighterContext();
1817
+ return /* @__PURE__ */ React9.createElement(
1818
+ "div",
1819
+ {
1820
+ onMouseEnter: () => {
1821
+ mouseInRef.current = true;
1822
+ onMouseEnter && onMouseEnter();
1823
+ if (isEditingOrHighlighting()) return;
1824
+ if (highlightTip) {
1825
+ const monitoredHighlightTip = /* @__PURE__ */ React9.createElement(
1826
+ MouseMonitor,
1827
+ {
1828
+ onMoveAway: () => {
1829
+ if (mouseInRef.current) {
1830
+ return;
1831
+ }
1832
+ setTip(null);
1833
+ onMouseLeave && onMouseLeave();
1834
+ },
1835
+ paddingX: 60,
1836
+ paddingY: 30
1837
+ },
1838
+ highlightTip.content
1839
+ );
1840
+ setTip({
1841
+ position: highlightTip.position,
1842
+ content: monitoredHighlightTip
1843
+ });
1844
+ }
1845
+ },
1846
+ onMouseLeave: () => {
1847
+ mouseInRef.current = false;
1848
+ !highlightTip && onMouseLeave && onMouseLeave();
1849
+ }
1850
+ },
1851
+ children
1852
+ );
1853
+ };
1854
+
1855
+ // src/components/AreaHighlight.tsx
1856
+ import React10, {
1857
+ useState as useState7,
1858
+ useRef as useRef9,
1859
+ useEffect as useEffect6
1860
+ } from "react";
1861
+ import { Rnd } from "react-rnd";
1862
+ var DefaultStyleIcon2 = () => /* @__PURE__ */ React10.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React10.createElement("path", { d: "M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }));
1863
+ var DefaultDeleteIcon2 = () => /* @__PURE__ */ React10.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React10.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }));
1864
+ var DEFAULT_COLOR_PRESETS2 = [
1865
+ "rgba(255, 226, 143, 1)",
1866
+ // Yellow (default)
1867
+ "#ffcdd2",
1868
+ // Light red
1869
+ "#c8e6c9",
1870
+ // Light green
1871
+ "#bbdefb",
1872
+ // Light blue
1873
+ "#e1bee7"
1874
+ // Light purple
1875
+ ];
1876
+ var AreaHighlight = ({
1877
+ highlight,
1878
+ onChange,
1879
+ isScrolledTo,
1880
+ bounds,
1881
+ onContextMenu,
1882
+ onEditStart,
1883
+ style,
1884
+ highlightColor = "rgba(255, 226, 143, 1)",
1885
+ onStyleChange,
1886
+ onDelete,
1887
+ styleIcon,
1888
+ deleteIcon,
1889
+ colorPresets = DEFAULT_COLOR_PRESETS2
1890
+ }) => {
1891
+ const [isStylePanelOpen, setIsStylePanelOpen] = useState7(false);
1892
+ const [isHovered, setIsHovered] = useState7(false);
1893
+ const stylePanelRef = useRef9(null);
1894
+ useEffect6(() => {
1895
+ if (!isStylePanelOpen) return;
1896
+ const handleClickOutside = (e) => {
1897
+ if (stylePanelRef.current && !stylePanelRef.current.contains(e.target)) {
1898
+ setIsStylePanelOpen(false);
1899
+ }
1900
+ };
1901
+ const timeoutId = setTimeout(() => {
1902
+ document.addEventListener("mousedown", handleClickOutside);
1903
+ }, 0);
1904
+ return () => {
1905
+ clearTimeout(timeoutId);
1906
+ document.removeEventListener("mousedown", handleClickOutside);
1907
+ };
1908
+ }, [isStylePanelOpen]);
1909
+ const highlightClass = isScrolledTo ? "AreaHighlight--scrolledTo" : "";
1910
+ const key = `${highlight.position.boundingRect.width}${highlight.position.boundingRect.height}${highlight.position.boundingRect.left}${highlight.position.boundingRect.top}`;
1911
+ const mergedStyle = {
1912
+ ...style,
1913
+ backgroundColor: highlightColor
1914
+ };
1915
+ return /* @__PURE__ */ React10.createElement(
1916
+ "div",
1917
+ {
1918
+ className: `AreaHighlight ${highlightClass}`,
1919
+ onContextMenu
1920
+ },
1921
+ (onStyleChange || onDelete) && /* @__PURE__ */ React10.createElement(
1922
+ "div",
1923
+ {
1924
+ className: "AreaHighlight__toolbar-wrapper",
1925
+ style: {
1926
+ position: "absolute",
1927
+ left: highlight.position.boundingRect.left,
1928
+ top: highlight.position.boundingRect.top - 28,
1929
+ paddingBottom: 12
1930
+ },
1931
+ onMouseEnter: () => setIsHovered(true),
1932
+ onMouseLeave: () => setIsHovered(false)
1933
+ },
1934
+ /* @__PURE__ */ React10.createElement(
1935
+ "div",
1936
+ {
1937
+ className: `AreaHighlight__toolbar ${isHovered || isStylePanelOpen ? "AreaHighlight__toolbar--visible" : ""}`
1938
+ },
1939
+ onStyleChange && /* @__PURE__ */ React10.createElement(
1940
+ "button",
1941
+ {
1942
+ className: "AreaHighlight__style-button",
1943
+ onClick: (e) => {
1944
+ e.stopPropagation();
1945
+ setIsStylePanelOpen(!isStylePanelOpen);
1946
+ },
1947
+ title: "Change color",
1948
+ type: "button"
1949
+ },
1950
+ styleIcon || /* @__PURE__ */ React10.createElement(DefaultStyleIcon2, null)
1951
+ ),
1952
+ onDelete && /* @__PURE__ */ React10.createElement(
1953
+ "button",
1954
+ {
1955
+ className: "AreaHighlight__delete-button",
1956
+ onClick: (e) => {
1957
+ e.stopPropagation();
1958
+ onDelete();
1959
+ },
1960
+ title: "Delete",
1961
+ type: "button"
1962
+ },
1963
+ deleteIcon || /* @__PURE__ */ React10.createElement(DefaultDeleteIcon2, null)
1964
+ )
1965
+ ),
1966
+ isStylePanelOpen && onStyleChange && /* @__PURE__ */ React10.createElement(
1967
+ "div",
1968
+ {
1969
+ className: "AreaHighlight__style-panel",
1970
+ ref: stylePanelRef,
1971
+ onClick: (e) => e.stopPropagation()
1972
+ },
1973
+ /* @__PURE__ */ React10.createElement("div", { className: "AreaHighlight__style-row" }, /* @__PURE__ */ React10.createElement("label", null, "Color"), /* @__PURE__ */ React10.createElement("div", { className: "AreaHighlight__color-options" }, /* @__PURE__ */ React10.createElement("div", { className: "AreaHighlight__color-presets" }, colorPresets.map((c) => /* @__PURE__ */ React10.createElement(
1974
+ "button",
1975
+ {
1976
+ key: c,
1977
+ type: "button",
1978
+ className: `AreaHighlight__color-preset ${highlightColor === c ? "active" : ""}`,
1979
+ style: { backgroundColor: c },
1980
+ onClick: () => onStyleChange({ highlightColor: c }),
1981
+ title: c
1982
+ }
1983
+ ))), /* @__PURE__ */ React10.createElement(
1984
+ "input",
1985
+ {
1986
+ type: "color",
1987
+ value: highlightColor,
1988
+ onChange: (e) => {
1989
+ onStyleChange({ highlightColor: e.target.value });
1990
+ }
1991
+ }
1992
+ )))
1993
+ )
1994
+ ),
1995
+ /* @__PURE__ */ React10.createElement(
1996
+ Rnd,
1997
+ {
1998
+ onMouseEnter: () => setIsHovered(true),
1999
+ onMouseLeave: () => setIsHovered(false),
2000
+ className: "AreaHighlight__part",
2001
+ onDragStop: (_, data) => {
2002
+ const boundingRect = {
2003
+ ...highlight.position.boundingRect,
2004
+ top: data.y,
2005
+ left: data.x
2006
+ };
2007
+ onChange && onChange(boundingRect);
2008
+ },
2009
+ onResizeStop: (_mouseEvent, _direction, ref, _delta, position) => {
2010
+ const boundingRect = {
2011
+ top: position.y,
2012
+ left: position.x,
2013
+ width: ref.offsetWidth,
2014
+ height: ref.offsetHeight,
2015
+ pageNumber: getPageFromElement(ref)?.number || -1
2016
+ };
2017
+ onChange && onChange(boundingRect);
2018
+ },
2019
+ onDragStart: onEditStart,
2020
+ onResizeStart: onEditStart,
2021
+ default: {
2022
+ x: highlight.position.boundingRect.left,
2023
+ y: highlight.position.boundingRect.top,
2024
+ width: highlight.position.boundingRect.width,
2025
+ height: highlight.position.boundingRect.height
2026
+ },
2027
+ key,
2028
+ bounds,
2029
+ onClick: (event) => {
2030
+ event.stopPropagation();
2031
+ event.preventDefault();
2032
+ },
2033
+ style: mergedStyle
2034
+ }
2035
+ )
2036
+ );
2037
+ };
2038
+
2039
+ // src/components/FreetextHighlight.tsx
2040
+ import React11, {
2041
+ useState as useState8,
2042
+ useRef as useRef10,
2043
+ useEffect as useEffect7
2044
+ } from "react";
2045
+ import { Rnd as Rnd2 } from "react-rnd";
2046
+ var DefaultDragIcon = () => /* @__PURE__ */ React11.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React11.createElement("circle", { cx: "8", cy: "6", r: "2" }), /* @__PURE__ */ React11.createElement("circle", { cx: "16", cy: "6", r: "2" }), /* @__PURE__ */ React11.createElement("circle", { cx: "8", cy: "12", r: "2" }), /* @__PURE__ */ React11.createElement("circle", { cx: "16", cy: "12", r: "2" }), /* @__PURE__ */ React11.createElement("circle", { cx: "8", cy: "18", r: "2" }), /* @__PURE__ */ React11.createElement("circle", { cx: "16", cy: "18", r: "2" }));
2047
+ var DefaultEditIcon = () => /* @__PURE__ */ React11.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React11.createElement("path", { d: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" }));
2048
+ var DefaultStyleIcon3 = () => /* @__PURE__ */ React11.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React11.createElement("path", { d: "M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }));
2049
+ var DefaultDeleteIcon3 = () => /* @__PURE__ */ React11.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React11.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }));
2050
+ var DEFAULT_BACKGROUND_PRESETS = ["transparent", "#ffffc8", "#ffcdd2", "#c8e6c9", "#bbdefb", "#e1bee7"];
2051
+ var DEFAULT_TEXT_PRESETS = ["#333333", "#d32f2f", "#1976d2", "#388e3c", "#7b1fa2"];
2052
+ var FreetextHighlight = ({
2053
+ highlight,
2054
+ onChange,
2055
+ onTextChange,
2056
+ onStyleChange,
2057
+ isScrolledTo,
2058
+ bounds,
2059
+ onContextMenu,
2060
+ onEditStart,
2061
+ onEditEnd,
2062
+ style,
2063
+ color = "#333333",
2064
+ backgroundColor = "#ffffc8",
2065
+ fontFamily = "inherit",
2066
+ fontSize = "14px",
2067
+ dragIcon,
2068
+ editIcon,
2069
+ styleIcon,
2070
+ backgroundColorPresets = DEFAULT_BACKGROUND_PRESETS,
2071
+ textColorPresets = DEFAULT_TEXT_PRESETS,
2072
+ onDelete,
2073
+ deleteIcon
2074
+ }) => {
2075
+ const [isEditing, setIsEditing] = useState8(false);
2076
+ const [isStylePanelOpen, setIsStylePanelOpen] = useState8(false);
2077
+ const [text, setText] = useState8(highlight.content?.text || "");
2078
+ const textareaRef = useRef10(null);
2079
+ const stylePanelRef = useRef10(null);
2080
+ useEffect7(() => {
2081
+ setText(highlight.content?.text || "");
2082
+ }, [highlight.content?.text]);
2083
+ useEffect7(() => {
2084
+ if (isEditing && textareaRef.current) {
2085
+ textareaRef.current.focus();
2086
+ textareaRef.current.select();
2087
+ }
2088
+ }, [isEditing]);
2089
+ useEffect7(() => {
2090
+ if (!isStylePanelOpen) return;
2091
+ const handleClickOutside = (e) => {
2092
+ if (stylePanelRef.current && !stylePanelRef.current.contains(e.target)) {
2093
+ setIsStylePanelOpen(false);
2094
+ }
2095
+ };
2096
+ const timeoutId = setTimeout(() => {
2097
+ document.addEventListener("mousedown", handleClickOutside);
2098
+ }, 0);
2099
+ return () => {
2100
+ clearTimeout(timeoutId);
2101
+ document.removeEventListener("mousedown", handleClickOutside);
2102
+ };
2103
+ }, [isStylePanelOpen]);
2104
+ const highlightClass = isScrolledTo ? "FreetextHighlight--scrolledTo" : "";
2105
+ const editingClass = isEditing ? "FreetextHighlight--editing" : "";
2106
+ const key = `${highlight.position.boundingRect.width}${highlight.position.boundingRect.height}${highlight.position.boundingRect.left}${highlight.position.boundingRect.top}`;
2107
+ const handleTextClick = (e) => {
2108
+ e.stopPropagation();
2109
+ if (!isEditing) {
2110
+ setIsEditing(true);
2111
+ onEditStart?.();
2112
+ }
2113
+ };
2114
+ const handleTextBlur = () => {
2115
+ if (isEditing) {
2116
+ setIsEditing(false);
2117
+ onTextChange?.(text);
2118
+ onEditEnd?.();
2119
+ }
2120
+ };
2121
+ const handleKeyDown = (e) => {
2122
+ if (e.key === "Escape") {
2123
+ e.preventDefault();
2124
+ setText(highlight.content?.text || "");
2125
+ setIsEditing(false);
2126
+ onEditEnd?.();
2127
+ } else if (e.key === "Enter" && !e.shiftKey) {
2128
+ e.preventDefault();
2129
+ setIsEditing(false);
2130
+ onTextChange?.(text);
2131
+ onEditEnd?.();
2132
+ }
2133
+ };
2134
+ const containerStyle = {
2135
+ backgroundColor,
2136
+ color,
2137
+ fontFamily,
2138
+ fontSize,
2139
+ ...style
2140
+ };
2141
+ return /* @__PURE__ */ React11.createElement(
2142
+ "div",
2143
+ {
2144
+ className: `FreetextHighlight ${highlightClass} ${editingClass}`,
2145
+ onContextMenu
2146
+ },
2147
+ /* @__PURE__ */ React11.createElement(
2148
+ Rnd2,
2149
+ {
2150
+ className: "FreetextHighlight__rnd",
2151
+ onDragStop: (_, data) => {
2152
+ const boundingRect = {
2153
+ ...highlight.position.boundingRect,
2154
+ top: data.y,
2155
+ left: data.x
2156
+ };
2157
+ onChange?.(boundingRect);
2158
+ },
2159
+ onDragStart: () => {
2160
+ if (!isEditing) {
2161
+ onEditStart?.();
2162
+ }
2163
+ },
2164
+ default: {
2165
+ x: highlight.position.boundingRect.left,
2166
+ y: highlight.position.boundingRect.top,
2167
+ width: highlight.position.boundingRect.width || 150,
2168
+ height: highlight.position.boundingRect.height || 80
2169
+ },
2170
+ minWidth: 100,
2171
+ minHeight: 50,
2172
+ key,
2173
+ bounds,
2174
+ enableResizing: {
2175
+ top: false,
2176
+ right: true,
2177
+ bottom: true,
2178
+ left: false,
2179
+ topRight: false,
2180
+ bottomRight: true,
2181
+ bottomLeft: false,
2182
+ topLeft: false
2183
+ },
2184
+ onResizeStop: (_e, _direction, ref, _delta, position) => {
2185
+ const boundingRect = {
2186
+ top: position.y,
2187
+ left: position.x,
2188
+ width: ref.offsetWidth,
2189
+ height: ref.offsetHeight,
2190
+ pageNumber: getPageFromElement(ref)?.number || highlight.position.boundingRect.pageNumber
2191
+ };
2192
+ onChange?.(boundingRect);
2193
+ },
2194
+ onResizeStart: () => {
2195
+ if (!isEditing) {
2196
+ onEditStart?.();
2197
+ }
2198
+ },
2199
+ cancel: ".FreetextHighlight__text, .FreetextHighlight__input, .FreetextHighlight__edit-button, .FreetextHighlight__style-button, .FreetextHighlight__style-panel, .FreetextHighlight__delete-button"
2200
+ },
2201
+ /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__container", style: containerStyle }, /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__toolbar" }, /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__drag-handle", title: "Drag to move" }, dragIcon || /* @__PURE__ */ React11.createElement(DefaultDragIcon, null)), /* @__PURE__ */ React11.createElement(
2202
+ "button",
2203
+ {
2204
+ className: "FreetextHighlight__edit-button",
2205
+ onClick: handleTextClick,
2206
+ title: "Edit text",
2207
+ type: "button"
2208
+ },
2209
+ editIcon || /* @__PURE__ */ React11.createElement(DefaultEditIcon, null)
2210
+ ), /* @__PURE__ */ React11.createElement(
2211
+ "button",
2212
+ {
2213
+ className: "FreetextHighlight__style-button",
2214
+ onClick: (e) => {
2215
+ e.stopPropagation();
2216
+ setIsStylePanelOpen(!isStylePanelOpen);
2217
+ },
2218
+ title: "Change style",
2219
+ type: "button"
2220
+ },
2221
+ styleIcon || /* @__PURE__ */ React11.createElement(DefaultStyleIcon3, null)
2222
+ ), onDelete && /* @__PURE__ */ React11.createElement(
2223
+ "button",
2224
+ {
2225
+ className: "FreetextHighlight__delete-button",
2226
+ onClick: (e) => {
2227
+ e.stopPropagation();
2228
+ onDelete();
2229
+ },
2230
+ title: "Delete",
2231
+ type: "button"
2232
+ },
2233
+ deleteIcon || /* @__PURE__ */ React11.createElement(DefaultDeleteIcon3, null)
2234
+ )), isStylePanelOpen && /* @__PURE__ */ React11.createElement(
2235
+ "div",
2236
+ {
2237
+ className: "FreetextHighlight__style-panel",
2238
+ ref: stylePanelRef,
2239
+ onClick: (e) => e.stopPropagation()
2240
+ },
2241
+ /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__style-row" }, /* @__PURE__ */ React11.createElement("label", null, "Background"), /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__color-options" }, /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__color-presets" }, backgroundColorPresets.map((c) => /* @__PURE__ */ React11.createElement(
2242
+ "button",
2243
+ {
2244
+ key: c,
2245
+ type: "button",
2246
+ className: `FreetextHighlight__color-preset ${c === "transparent" ? "FreetextHighlight__color-preset--transparent" : ""} ${backgroundColor === c ? "active" : ""}`,
2247
+ style: c !== "transparent" ? { backgroundColor: c } : void 0,
2248
+ onClick: () => onStyleChange?.({ backgroundColor: c }),
2249
+ title: c === "transparent" ? "No background" : c
2250
+ }
2251
+ ))), /* @__PURE__ */ React11.createElement(
2252
+ "input",
2253
+ {
2254
+ type: "color",
2255
+ value: backgroundColor === "transparent" ? "#ffffff" : backgroundColor,
2256
+ onChange: (e) => {
2257
+ onStyleChange?.({ backgroundColor: e.target.value });
2258
+ }
2259
+ }
2260
+ ))),
2261
+ /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__style-row" }, /* @__PURE__ */ React11.createElement(
2262
+ "label",
2263
+ null,
2264
+ "Text Color"
2265
+ ), /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__color-options" }, /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__color-presets" }, textColorPresets.map((c) => /* @__PURE__ */ React11.createElement(
2266
+ "button",
2267
+ {
2268
+ key: c,
2269
+ type: "button",
2270
+ className: `FreetextHighlight__color-preset ${color === c ? "active" : ""}`,
2271
+ style: { backgroundColor: c },
2272
+ onClick: () => onStyleChange?.({ color: c }),
2273
+ title: c
2274
+ }
2275
+ ))), /* @__PURE__ */ React11.createElement(
2276
+ "input",
2277
+ {
2278
+ type: "color",
2279
+ value: color,
2280
+ onChange: (e) => {
2281
+ onStyleChange?.({ color: e.target.value });
2282
+ }
2283
+ }
2284
+ ))),
2285
+ /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__style-row" }, /* @__PURE__ */ React11.createElement("label", null, "Font Size"), /* @__PURE__ */ React11.createElement(
2286
+ "select",
2287
+ {
2288
+ value: fontSize,
2289
+ onChange: (e) => {
2290
+ onStyleChange?.({ fontSize: e.target.value });
2291
+ }
2292
+ },
2293
+ /* @__PURE__ */ React11.createElement("option", { value: "10px" }, "10px"),
2294
+ /* @__PURE__ */ React11.createElement("option", { value: "12px" }, "12px"),
2295
+ /* @__PURE__ */ React11.createElement("option", { value: "14px" }, "14px"),
2296
+ /* @__PURE__ */ React11.createElement("option", { value: "16px" }, "16px"),
2297
+ /* @__PURE__ */ React11.createElement("option", { value: "18px" }, "18px"),
2298
+ /* @__PURE__ */ React11.createElement("option", { value: "20px" }, "20px"),
2299
+ /* @__PURE__ */ React11.createElement("option", { value: "24px" }, "24px")
2300
+ )),
2301
+ /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__style-row" }, /* @__PURE__ */ React11.createElement("label", null, "Font"), /* @__PURE__ */ React11.createElement(
2302
+ "select",
2303
+ {
2304
+ value: fontFamily,
2305
+ onChange: (e) => {
2306
+ onStyleChange?.({ fontFamily: e.target.value });
2307
+ }
2308
+ },
2309
+ /* @__PURE__ */ React11.createElement("option", { value: "inherit" }, "Default"),
2310
+ /* @__PURE__ */ React11.createElement("option", { value: "Arial, sans-serif" }, "Arial"),
2311
+ /* @__PURE__ */ React11.createElement("option", { value: "Georgia, serif" }, "Georgia"),
2312
+ /* @__PURE__ */ React11.createElement("option", { value: "'Courier New', monospace" }, "Courier"),
2313
+ /* @__PURE__ */ React11.createElement("option", { value: "'Times New Roman', serif" }, "Times")
2314
+ ))
2315
+ ), /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__content" }, isEditing ? /* @__PURE__ */ React11.createElement(
2316
+ "textarea",
2317
+ {
2318
+ ref: textareaRef,
2319
+ className: "FreetextHighlight__input",
2320
+ value: text,
2321
+ onChange: (e) => setText(e.target.value),
2322
+ onBlur: handleTextBlur,
2323
+ onKeyDown: handleKeyDown,
2324
+ onClick: (e) => e.stopPropagation()
2325
+ }
2326
+ ) : /* @__PURE__ */ React11.createElement("div", { className: "FreetextHighlight__text" }, text || "New note")))
2327
+ )
2328
+ );
2329
+ };
2330
+
2331
+ // src/components/ImageHighlight.tsx
2332
+ import React12 from "react";
2333
+ import { Rnd as Rnd3 } from "react-rnd";
2334
+ var DefaultDragIcon2 = () => /* @__PURE__ */ React12.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React12.createElement("circle", { cx: "8", cy: "6", r: "2" }), /* @__PURE__ */ React12.createElement("circle", { cx: "16", cy: "6", r: "2" }), /* @__PURE__ */ React12.createElement("circle", { cx: "8", cy: "12", r: "2" }), /* @__PURE__ */ React12.createElement("circle", { cx: "16", cy: "12", r: "2" }), /* @__PURE__ */ React12.createElement("circle", { cx: "8", cy: "18", r: "2" }), /* @__PURE__ */ React12.createElement("circle", { cx: "16", cy: "18", r: "2" }));
2335
+ var DefaultDeleteIcon4 = () => /* @__PURE__ */ React12.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React12.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }));
2336
+ var ImageHighlight = ({
2337
+ highlight,
2338
+ onChange,
2339
+ isScrolledTo,
2340
+ bounds,
2341
+ onContextMenu,
2342
+ onEditStart,
2343
+ onEditEnd,
2344
+ style,
2345
+ dragIcon,
2346
+ onDelete,
2347
+ deleteIcon
2348
+ }) => {
2349
+ const highlightClass = isScrolledTo ? "ImageHighlight--scrolledTo" : "";
2350
+ const key = `${highlight.position.boundingRect.width}${highlight.position.boundingRect.height}${highlight.position.boundingRect.left}${highlight.position.boundingRect.top}`;
2351
+ const imageUrl = highlight.content?.image;
2352
+ return /* @__PURE__ */ React12.createElement(
2353
+ "div",
2354
+ {
2355
+ className: `ImageHighlight ${highlightClass}`,
2356
+ onContextMenu
2357
+ },
2358
+ /* @__PURE__ */ React12.createElement(
2359
+ Rnd3,
2360
+ {
2361
+ className: "ImageHighlight__rnd",
2362
+ onDragStop: (_, data) => {
2363
+ const boundingRect = {
2364
+ ...highlight.position.boundingRect,
2365
+ top: data.y,
2366
+ left: data.x
2367
+ };
2368
+ onChange?.(boundingRect);
2369
+ onEditEnd?.();
2370
+ },
2371
+ onDragStart: onEditStart,
2372
+ onResizeStop: (_e, _direction, ref, _delta, position) => {
2373
+ const boundingRect = {
2374
+ top: position.y,
2375
+ left: position.x,
2376
+ width: ref.offsetWidth,
2377
+ height: ref.offsetHeight,
2378
+ pageNumber: getPageFromElement(ref)?.number || highlight.position.boundingRect.pageNumber
2379
+ };
2380
+ onChange?.(boundingRect);
2381
+ onEditEnd?.();
2382
+ },
2383
+ onResizeStart: onEditStart,
2384
+ default: {
2385
+ x: highlight.position.boundingRect.left,
2386
+ y: highlight.position.boundingRect.top,
2387
+ width: highlight.position.boundingRect.width || 150,
2388
+ height: highlight.position.boundingRect.height || 100
2389
+ },
2390
+ minWidth: 50,
2391
+ minHeight: 50,
2392
+ key,
2393
+ bounds,
2394
+ lockAspectRatio: true,
2395
+ dragHandleClassName: "ImageHighlight__drag-handle",
2396
+ onClick: (event) => {
2397
+ event.stopPropagation();
2398
+ event.preventDefault();
2399
+ },
2400
+ style
2401
+ },
2402
+ /* @__PURE__ */ React12.createElement("div", { className: "ImageHighlight__container" }, /* @__PURE__ */ React12.createElement("div", { className: "ImageHighlight__toolbar" }, /* @__PURE__ */ React12.createElement("div", { className: "ImageHighlight__drag-handle", title: "Drag to move" }, dragIcon || /* @__PURE__ */ React12.createElement(DefaultDragIcon2, null)), onDelete && /* @__PURE__ */ React12.createElement(
2403
+ "button",
2404
+ {
2405
+ className: "ImageHighlight__delete-button",
2406
+ onClick: (e) => {
2407
+ e.stopPropagation();
2408
+ onDelete();
2409
+ },
2410
+ title: "Delete",
2411
+ type: "button"
2412
+ },
2413
+ deleteIcon || /* @__PURE__ */ React12.createElement(DefaultDeleteIcon4, null)
2414
+ )), /* @__PURE__ */ React12.createElement("div", { className: "ImageHighlight__content" }, imageUrl ? /* @__PURE__ */ React12.createElement(
2415
+ "img",
2416
+ {
2417
+ src: imageUrl,
2418
+ alt: "Highlight",
2419
+ className: "ImageHighlight__image",
2420
+ draggable: false
2421
+ }
2422
+ ) : /* @__PURE__ */ React12.createElement("div", { className: "ImageHighlight__placeholder" }, "No image")))
2423
+ )
2424
+ );
2425
+ };
2426
+
2427
+ // src/components/SignaturePad.tsx
2428
+ import React13, { useRef as useRef11, useEffect as useEffect8, useCallback as useCallback3 } from "react";
2429
+ var SignaturePad = ({
2430
+ isOpen,
2431
+ onComplete,
2432
+ onClose,
2433
+ width = 400,
2434
+ height = 200
2435
+ }) => {
2436
+ const canvasRef = useRef11(null);
2437
+ const isDrawingRef = useRef11(false);
2438
+ const lastPosRef = useRef11({ x: 0, y: 0 });
2439
+ useEffect8(() => {
2440
+ if (!isOpen || !canvasRef.current) return;
2441
+ const canvas = canvasRef.current;
2442
+ const ctx = canvas.getContext("2d");
2443
+ if (!ctx) return;
2444
+ ctx.strokeStyle = "#000000";
2445
+ ctx.lineWidth = 2;
2446
+ ctx.lineCap = "round";
2447
+ ctx.lineJoin = "round";
2448
+ ctx.fillStyle = "white";
2449
+ ctx.fillRect(0, 0, width, height);
2450
+ }, [isOpen, width, height]);
2451
+ const getPosition = useCallback3(
2452
+ (e) => {
2453
+ const canvas = canvasRef.current;
2454
+ if (!canvas) return { x: 0, y: 0 };
2455
+ const rect = canvas.getBoundingClientRect();
2456
+ let clientX;
2457
+ let clientY;
2458
+ if ("touches" in e) {
2459
+ clientX = e.touches[0].clientX;
2460
+ clientY = e.touches[0].clientY;
2461
+ } else {
2462
+ clientX = e.clientX;
2463
+ clientY = e.clientY;
2464
+ }
2465
+ return {
2466
+ x: clientX - rect.left,
2467
+ y: clientY - rect.top
2468
+ };
2469
+ },
2470
+ []
2471
+ );
2472
+ const startDrawing = useCallback3(
2473
+ (e) => {
2474
+ e.preventDefault();
2475
+ isDrawingRef.current = true;
2476
+ lastPosRef.current = getPosition(e);
2477
+ },
2478
+ [getPosition]
2479
+ );
2480
+ const draw = useCallback3(
2481
+ (e) => {
2482
+ if (!isDrawingRef.current) return;
2483
+ e.preventDefault();
2484
+ const canvas = canvasRef.current;
2485
+ const ctx = canvas?.getContext("2d");
2486
+ if (!ctx) return;
2487
+ const currentPos = getPosition(e);
2488
+ ctx.beginPath();
2489
+ ctx.moveTo(lastPosRef.current.x, lastPosRef.current.y);
2490
+ ctx.lineTo(currentPos.x, currentPos.y);
2491
+ ctx.stroke();
2492
+ lastPosRef.current = currentPos;
2493
+ },
2494
+ [getPosition]
2495
+ );
2496
+ const stopDrawing = useCallback3(() => {
2497
+ isDrawingRef.current = false;
2498
+ }, []);
2499
+ useEffect8(() => {
2500
+ if (!isOpen) return;
2501
+ const canvas = canvasRef.current;
2502
+ if (!canvas) return;
2503
+ const handleMouseDown = (e) => startDrawing(e);
2504
+ const handleMouseMove = (e) => draw(e);
2505
+ const handleMouseUp = () => stopDrawing();
2506
+ const handleMouseLeave = () => stopDrawing();
2507
+ const handleTouchStart = (e) => startDrawing(e);
2508
+ const handleTouchMove = (e) => draw(e);
2509
+ const handleTouchEnd = () => stopDrawing();
2510
+ canvas.addEventListener("mousedown", handleMouseDown);
2511
+ canvas.addEventListener("mousemove", handleMouseMove);
2512
+ canvas.addEventListener("mouseup", handleMouseUp);
2513
+ canvas.addEventListener("mouseleave", handleMouseLeave);
2514
+ canvas.addEventListener("touchstart", handleTouchStart, { passive: false });
2515
+ canvas.addEventListener("touchmove", handleTouchMove, { passive: false });
2516
+ canvas.addEventListener("touchend", handleTouchEnd);
2517
+ return () => {
2518
+ canvas.removeEventListener("mousedown", handleMouseDown);
2519
+ canvas.removeEventListener("mousemove", handleMouseMove);
2520
+ canvas.removeEventListener("mouseup", handleMouseUp);
2521
+ canvas.removeEventListener("mouseleave", handleMouseLeave);
2522
+ canvas.removeEventListener("touchstart", handleTouchStart);
2523
+ canvas.removeEventListener("touchmove", handleTouchMove);
2524
+ canvas.removeEventListener("touchend", handleTouchEnd);
2525
+ };
2526
+ }, [isOpen, startDrawing, draw, stopDrawing]);
2527
+ const handleClear = () => {
2528
+ const canvas = canvasRef.current;
2529
+ const ctx = canvas?.getContext("2d");
2530
+ if (!ctx || !canvas) return;
2531
+ ctx.fillStyle = "white";
2532
+ ctx.fillRect(0, 0, width, height);
2533
+ };
2534
+ const handleDone = () => {
2535
+ const canvas = canvasRef.current;
2536
+ if (!canvas) return;
2537
+ const dataUrl = canvas.toDataURL("image/png");
2538
+ onComplete(dataUrl);
2539
+ };
2540
+ const handleOverlayClick = (e) => {
2541
+ if (e.target === e.currentTarget) {
2542
+ onClose();
2543
+ }
2544
+ };
2545
+ if (!isOpen) return null;
2546
+ return /* @__PURE__ */ React13.createElement("div", { className: "SignaturePad__overlay", onClick: handleOverlayClick }, /* @__PURE__ */ React13.createElement("div", { className: "SignaturePad__modal" }, /* @__PURE__ */ React13.createElement("h3", { className: "SignaturePad__title" }, "Draw your signature"), /* @__PURE__ */ React13.createElement(
2547
+ "canvas",
2548
+ {
2549
+ ref: canvasRef,
2550
+ className: "SignaturePad__canvas",
2551
+ width,
2552
+ height
2553
+ }
2554
+ ), /* @__PURE__ */ React13.createElement("div", { className: "SignaturePad__buttons" }, /* @__PURE__ */ React13.createElement(
2555
+ "button",
2556
+ {
2557
+ type: "button",
2558
+ className: "SignaturePad__button SignaturePad__button--clear",
2559
+ onClick: handleClear
2560
+ },
2561
+ "Clear"
2562
+ ), /* @__PURE__ */ React13.createElement(
2563
+ "button",
2564
+ {
2565
+ type: "button",
2566
+ className: "SignaturePad__button SignaturePad__button--cancel",
2567
+ onClick: onClose
2568
+ },
2569
+ "Cancel"
2570
+ ), /* @__PURE__ */ React13.createElement(
2571
+ "button",
2572
+ {
2573
+ type: "button",
2574
+ className: "SignaturePad__button SignaturePad__button--done",
2575
+ onClick: handleDone
2576
+ },
2577
+ "Done"
2578
+ ))));
2579
+ };
2580
+
2581
+ // src/components/DrawingHighlight.tsx
2582
+ import React14, { useState as useState9, useCallback as useCallback4, useEffect as useEffect9, useRef as useRef12 } from "react";
2583
+ import { Rnd as Rnd4 } from "react-rnd";
2584
+ var DRAWING_COLORS = ["#000000", "#FF0000", "#0000FF", "#00FF00", "#FFFF00"];
2585
+ var STROKE_WIDTHS = [
2586
+ { label: "Thin", value: 1 },
2587
+ { label: "Medium", value: 3 },
2588
+ { label: "Thick", value: 5 }
2589
+ ];
2590
+ var DefaultDragIcon3 = () => /* @__PURE__ */ React14.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React14.createElement("circle", { cx: "8", cy: "6", r: "2" }), /* @__PURE__ */ React14.createElement("circle", { cx: "16", cy: "6", r: "2" }), /* @__PURE__ */ React14.createElement("circle", { cx: "8", cy: "12", r: "2" }), /* @__PURE__ */ React14.createElement("circle", { cx: "16", cy: "12", r: "2" }), /* @__PURE__ */ React14.createElement("circle", { cx: "8", cy: "18", r: "2" }), /* @__PURE__ */ React14.createElement("circle", { cx: "16", cy: "18", r: "2" }));
2591
+ var DefaultDeleteIcon5 = () => /* @__PURE__ */ React14.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React14.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }));
2592
+ var renderStrokesToImage = (strokes, width, height) => {
2593
+ const canvas = document.createElement("canvas");
2594
+ canvas.width = width;
2595
+ canvas.height = height;
2596
+ const ctx = canvas.getContext("2d");
2597
+ if (!ctx) return "";
2598
+ strokes.forEach((stroke) => {
2599
+ if (stroke.points.length < 2) return;
2600
+ ctx.strokeStyle = stroke.color;
2601
+ ctx.lineWidth = stroke.width;
2602
+ ctx.lineCap = "round";
2603
+ ctx.lineJoin = "round";
2604
+ ctx.beginPath();
2605
+ ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
2606
+ stroke.points.slice(1).forEach((point) => {
2607
+ ctx.lineTo(point.x, point.y);
2608
+ });
2609
+ ctx.stroke();
2610
+ });
2611
+ return canvas.toDataURL("image/png");
2612
+ };
2613
+ var DrawingHighlight = ({
2614
+ highlight,
2615
+ onChange,
2616
+ isScrolledTo,
2617
+ bounds,
2618
+ onContextMenu,
2619
+ onEditStart,
2620
+ onEditEnd,
2621
+ style,
2622
+ dragIcon,
2623
+ onStyleChange,
2624
+ onDelete,
2625
+ deleteIcon
2626
+ }) => {
2627
+ const highlightClass = isScrolledTo ? "DrawingHighlight--scrolledTo" : "";
2628
+ const [showStyleControls, setShowStyleControls] = useState9(false);
2629
+ const styleControlsRef = useRef12(null);
2630
+ useEffect9(() => {
2631
+ if (!showStyleControls) return;
2632
+ const handleClickOutside = (e) => {
2633
+ if (styleControlsRef.current && !styleControlsRef.current.contains(e.target)) {
2634
+ setShowStyleControls(false);
2635
+ }
2636
+ };
2637
+ const timeoutId = setTimeout(() => {
2638
+ document.addEventListener("mousedown", handleClickOutside);
2639
+ }, 0);
2640
+ return () => {
2641
+ clearTimeout(timeoutId);
2642
+ document.removeEventListener("mousedown", handleClickOutside);
2643
+ };
2644
+ }, [showStyleControls]);
2645
+ const key = `${highlight.position.boundingRect.width}${highlight.position.boundingRect.height}${highlight.position.boundingRect.left}${highlight.position.boundingRect.top}`;
2646
+ const imageUrl = highlight.content?.image;
2647
+ const strokes = highlight.content?.strokes;
2648
+ const handleColorChange = useCallback4((newColor) => {
2649
+ if (!strokes || !onStyleChange) return;
2650
+ console.log("DrawingHighlight: Changing color to", newColor);
2651
+ const newStrokes = strokes.map((stroke) => ({
2652
+ ...stroke,
2653
+ color: newColor
2654
+ }));
2655
+ const newImage = renderStrokesToImage(
2656
+ newStrokes,
2657
+ highlight.position.boundingRect.width,
2658
+ highlight.position.boundingRect.height
2659
+ );
2660
+ onStyleChange(newImage, newStrokes);
2661
+ }, [strokes, onStyleChange, highlight.position.boundingRect.width, highlight.position.boundingRect.height]);
2662
+ const handleWidthChange = useCallback4((newWidth) => {
2663
+ if (!strokes || !onStyleChange) return;
2664
+ console.log("DrawingHighlight: Changing width to", newWidth);
2665
+ const newStrokes = strokes.map((stroke) => ({
2666
+ ...stroke,
2667
+ width: newWidth
2668
+ }));
2669
+ const newImage = renderStrokesToImage(
2670
+ newStrokes,
2671
+ highlight.position.boundingRect.width,
2672
+ highlight.position.boundingRect.height
2673
+ );
2674
+ onStyleChange(newImage, newStrokes);
2675
+ }, [strokes, onStyleChange, highlight.position.boundingRect.width, highlight.position.boundingRect.height]);
2676
+ const currentColor = strokes?.[0]?.color || "#000000";
2677
+ const currentWidth = strokes?.[0]?.width || 3;
2678
+ return /* @__PURE__ */ React14.createElement(
2679
+ "div",
2680
+ {
2681
+ className: `DrawingHighlight ${highlightClass}`,
2682
+ onContextMenu
2683
+ },
2684
+ /* @__PURE__ */ React14.createElement(
2685
+ Rnd4,
2686
+ {
2687
+ className: "DrawingHighlight__rnd",
2688
+ onDragStop: (_, data) => {
2689
+ const boundingRect = {
2690
+ ...highlight.position.boundingRect,
2691
+ top: data.y,
2692
+ left: data.x
2693
+ };
2694
+ onChange?.(boundingRect);
2695
+ onEditEnd?.();
2696
+ },
2697
+ onDragStart: onEditStart,
2698
+ onResizeStop: (_e, _direction, ref, _delta, position) => {
2699
+ const boundingRect = {
2700
+ top: position.y,
2701
+ left: position.x,
2702
+ width: ref.offsetWidth,
2703
+ height: ref.offsetHeight,
2704
+ pageNumber: getPageFromElement(ref)?.number || highlight.position.boundingRect.pageNumber
2705
+ };
2706
+ onChange?.(boundingRect);
2707
+ onEditEnd?.();
2708
+ },
2709
+ onResizeStart: onEditStart,
2710
+ default: {
2711
+ x: highlight.position.boundingRect.left,
2712
+ y: highlight.position.boundingRect.top,
2713
+ width: highlight.position.boundingRect.width || 150,
2714
+ height: highlight.position.boundingRect.height || 100
2715
+ },
2716
+ minWidth: 30,
2717
+ minHeight: 30,
2718
+ key,
2719
+ bounds,
2720
+ lockAspectRatio: false,
2721
+ dragHandleClassName: "DrawingHighlight__drag-handle",
2722
+ onClick: (event) => {
2723
+ event.stopPropagation();
2724
+ event.preventDefault();
2725
+ },
2726
+ style
2727
+ },
2728
+ /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__container" }, /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__toolbar" }, /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__drag-handle", title: "Drag to move" }, dragIcon || /* @__PURE__ */ React14.createElement(DefaultDragIcon3, null)), strokes && strokes.length > 0 && onStyleChange && /* @__PURE__ */ React14.createElement(
2729
+ "button",
2730
+ {
2731
+ type: "button",
2732
+ className: "DrawingHighlight__style-button",
2733
+ title: "Edit style",
2734
+ onClick: (e) => {
2735
+ e.stopPropagation();
2736
+ setShowStyleControls(!showStyleControls);
2737
+ }
2738
+ },
2739
+ /* @__PURE__ */ React14.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React14.createElement("path", { d: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" }))
2740
+ ), onDelete && /* @__PURE__ */ React14.createElement(
2741
+ "button",
2742
+ {
2743
+ className: "DrawingHighlight__delete-button",
2744
+ onClick: (e) => {
2745
+ e.stopPropagation();
2746
+ onDelete();
2747
+ },
2748
+ title: "Delete",
2749
+ type: "button"
2750
+ },
2751
+ deleteIcon || /* @__PURE__ */ React14.createElement(DefaultDeleteIcon5, null)
2752
+ )), showStyleControls && strokes && strokes.length > 0 && onStyleChange && /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__style-controls", ref: styleControlsRef }, /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__color-picker" }, DRAWING_COLORS.map((color) => /* @__PURE__ */ React14.createElement(
2753
+ "button",
2754
+ {
2755
+ key: color,
2756
+ type: "button",
2757
+ className: `DrawingHighlight__color-button ${currentColor === color ? "active" : ""}`,
2758
+ style: { backgroundColor: color },
2759
+ onClick: (e) => {
2760
+ e.stopPropagation();
2761
+ handleColorChange(color);
2762
+ },
2763
+ title: `Color: ${color}`
2764
+ }
2765
+ ))), /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__width-picker" }, STROKE_WIDTHS.map((w) => /* @__PURE__ */ React14.createElement(
2766
+ "button",
2767
+ {
2768
+ key: w.value,
2769
+ type: "button",
2770
+ className: `DrawingHighlight__width-button ${currentWidth === w.value ? "active" : ""}`,
2771
+ onClick: (e) => {
2772
+ e.stopPropagation();
2773
+ handleWidthChange(w.value);
2774
+ },
2775
+ title: w.label
2776
+ },
2777
+ w.label
2778
+ )))), /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__content" }, imageUrl ? /* @__PURE__ */ React14.createElement(
2779
+ "img",
2780
+ {
2781
+ src: imageUrl,
2782
+ alt: "Drawing",
2783
+ className: "DrawingHighlight__image",
2784
+ draggable: false
2785
+ }
2786
+ ) : /* @__PURE__ */ React14.createElement("div", { className: "DrawingHighlight__placeholder" }, "No drawing")))
2787
+ )
2788
+ );
2789
+ };
2790
+
2791
+ // src/components/ShapeHighlight.tsx
2792
+ import React15, {
2793
+ useState as useState10,
2794
+ useRef as useRef13,
2795
+ useEffect as useEffect10
2796
+ } from "react";
2797
+ import { Rnd as Rnd5 } from "react-rnd";
2798
+ var DefaultStyleIcon4 = () => /* @__PURE__ */ React15.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React15.createElement("path", { d: "M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" }));
2799
+ var DefaultDeleteIcon6 = () => /* @__PURE__ */ React15.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React15.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }));
2800
+ var DEFAULT_COLOR_PRESETS3 = [
2801
+ "#000000",
2802
+ // Black
2803
+ "#FF0000",
2804
+ // Red
2805
+ "#0000FF",
2806
+ // Blue
2807
+ "#00AA00",
2808
+ // Green
2809
+ "#FF6600"
2810
+ // Orange
2811
+ ];
2812
+ var STROKE_WIDTHS2 = [
2813
+ { label: "Thin", value: 1 },
2814
+ { label: "Medium", value: 2 },
2815
+ { label: "Thick", value: 4 }
2816
+ ];
2817
+ var ShapeHighlight = ({
2818
+ highlight,
2819
+ onChange,
2820
+ isScrolledTo,
2821
+ bounds,
2822
+ onContextMenu,
2823
+ onEditStart,
2824
+ onEditEnd,
2825
+ style,
2826
+ shapeType = "rectangle",
2827
+ strokeColor = "#000000",
2828
+ strokeWidth = 2,
2829
+ onStyleChange,
2830
+ onDelete,
2831
+ styleIcon,
2832
+ deleteIcon,
2833
+ colorPresets = DEFAULT_COLOR_PRESETS3,
2834
+ startPoint,
2835
+ endPoint
2836
+ }) => {
2837
+ const [isStylePanelOpen, setIsStylePanelOpen] = useState10(false);
2838
+ const [isHovered, setIsHovered] = useState10(false);
2839
+ const stylePanelRef = useRef13(null);
2840
+ useEffect10(() => {
2841
+ if (!isStylePanelOpen) return;
2842
+ const handleClickOutside = (e) => {
2843
+ if (stylePanelRef.current && !stylePanelRef.current.contains(e.target)) {
2844
+ setIsStylePanelOpen(false);
2845
+ }
2846
+ };
2847
+ const timeoutId = setTimeout(() => {
2848
+ document.addEventListener("mousedown", handleClickOutside);
2849
+ }, 0);
2850
+ return () => {
2851
+ clearTimeout(timeoutId);
2852
+ document.removeEventListener("mousedown", handleClickOutside);
2853
+ };
2854
+ }, [isStylePanelOpen]);
2855
+ const highlightClass = isScrolledTo ? "ShapeHighlight--scrolledTo" : "";
2856
+ const key = `${highlight.position.boundingRect.width}${highlight.position.boundingRect.height}${highlight.position.boundingRect.left}${highlight.position.boundingRect.top}`;
2857
+ const markerId = `arrowhead-${highlight.id}`;
2858
+ const renderShape = (width, height) => {
2859
+ switch (shapeType) {
2860
+ case "rectangle":
2861
+ return /* @__PURE__ */ React15.createElement(
2862
+ "svg",
2863
+ {
2864
+ className: "ShapeHighlight__svg",
2865
+ width,
2866
+ height,
2867
+ viewBox: `0 0 ${width} ${height}`
2868
+ },
2869
+ /* @__PURE__ */ React15.createElement(
2870
+ "rect",
2871
+ {
2872
+ x: strokeWidth / 2,
2873
+ y: strokeWidth / 2,
2874
+ width: width - strokeWidth,
2875
+ height: height - strokeWidth,
2876
+ stroke: strokeColor,
2877
+ strokeWidth,
2878
+ fill: "none"
2879
+ }
2880
+ )
2881
+ );
2882
+ case "circle":
2883
+ return /* @__PURE__ */ React15.createElement(
2884
+ "svg",
2885
+ {
2886
+ className: "ShapeHighlight__svg",
2887
+ width,
2888
+ height,
2889
+ viewBox: `0 0 ${width} ${height}`
2890
+ },
2891
+ /* @__PURE__ */ React15.createElement(
2892
+ "ellipse",
2893
+ {
2894
+ cx: width / 2,
2895
+ cy: height / 2,
2896
+ rx: width / 2 - strokeWidth / 2,
2897
+ ry: height / 2 - strokeWidth / 2,
2898
+ stroke: strokeColor,
2899
+ strokeWidth,
2900
+ fill: "none"
2901
+ }
2902
+ )
2903
+ );
2904
+ case "arrow": {
2905
+ const x1 = startPoint ? startPoint.x * width : strokeWidth;
2906
+ const y1 = startPoint ? startPoint.y * height : height / 2;
2907
+ const x2 = endPoint ? endPoint.x * width : width - strokeWidth - 10;
2908
+ const y2 = endPoint ? endPoint.y * height : height / 2;
2909
+ return /* @__PURE__ */ React15.createElement(
2910
+ "svg",
2911
+ {
2912
+ className: "ShapeHighlight__svg",
2913
+ width,
2914
+ height,
2915
+ viewBox: `0 0 ${width} ${height}`
2916
+ },
2917
+ /* @__PURE__ */ React15.createElement("defs", null, /* @__PURE__ */ React15.createElement(
2918
+ "marker",
2919
+ {
2920
+ id: markerId,
2921
+ markerWidth: "10",
2922
+ markerHeight: "7",
2923
+ refX: "9",
2924
+ refY: "3.5",
2925
+ orient: "auto"
2926
+ },
2927
+ /* @__PURE__ */ React15.createElement("polygon", { points: "0 0, 10 3.5, 0 7", fill: strokeColor })
2928
+ )),
2929
+ /* @__PURE__ */ React15.createElement(
2930
+ "line",
2931
+ {
2932
+ x1,
2933
+ y1,
2934
+ x2,
2935
+ y2,
2936
+ stroke: strokeColor,
2937
+ strokeWidth,
2938
+ markerEnd: `url(#${markerId})`
2939
+ }
2940
+ )
2941
+ );
2942
+ }
2943
+ default:
2944
+ return null;
2945
+ }
2946
+ };
2947
+ return /* @__PURE__ */ React15.createElement(
2948
+ "div",
2949
+ {
2950
+ className: `ShapeHighlight ${highlightClass}`,
2951
+ onContextMenu
2952
+ },
2953
+ (onStyleChange || onDelete) && /* @__PURE__ */ React15.createElement(
2954
+ "div",
2955
+ {
2956
+ className: "ShapeHighlight__toolbar-wrapper",
2957
+ style: {
2958
+ position: "absolute",
2959
+ left: highlight.position.boundingRect.left,
2960
+ top: highlight.position.boundingRect.top - 28,
2961
+ paddingBottom: 12
2962
+ },
2963
+ onMouseEnter: () => setIsHovered(true),
2964
+ onMouseLeave: () => setIsHovered(false)
2965
+ },
2966
+ /* @__PURE__ */ React15.createElement(
2967
+ "div",
2968
+ {
2969
+ className: `ShapeHighlight__toolbar ${isHovered || isStylePanelOpen ? "ShapeHighlight__toolbar--visible" : ""}`
2970
+ },
2971
+ onStyleChange && /* @__PURE__ */ React15.createElement(
2972
+ "button",
2973
+ {
2974
+ className: "ShapeHighlight__style-button",
2975
+ onClick: (e) => {
2976
+ e.stopPropagation();
2977
+ setIsStylePanelOpen(!isStylePanelOpen);
2978
+ },
2979
+ title: "Change style",
2980
+ type: "button"
2981
+ },
2982
+ styleIcon || /* @__PURE__ */ React15.createElement(DefaultStyleIcon4, null)
2983
+ ),
2984
+ onDelete && /* @__PURE__ */ React15.createElement(
2985
+ "button",
2986
+ {
2987
+ className: "ShapeHighlight__delete-button",
2988
+ onClick: (e) => {
2989
+ e.stopPropagation();
2990
+ onDelete();
2991
+ },
2992
+ title: "Delete",
2993
+ type: "button"
2994
+ },
2995
+ deleteIcon || /* @__PURE__ */ React15.createElement(DefaultDeleteIcon6, null)
2996
+ )
2997
+ ),
2998
+ isStylePanelOpen && onStyleChange && /* @__PURE__ */ React15.createElement(
2999
+ "div",
3000
+ {
3001
+ className: "ShapeHighlight__style-panel",
3002
+ ref: stylePanelRef,
3003
+ onClick: (e) => e.stopPropagation()
3004
+ },
3005
+ /* @__PURE__ */ React15.createElement("div", { className: "ShapeHighlight__style-row" }, /* @__PURE__ */ React15.createElement("label", null, "Color"), /* @__PURE__ */ React15.createElement("div", { className: "ShapeHighlight__color-options" }, /* @__PURE__ */ React15.createElement("div", { className: "ShapeHighlight__color-presets" }, colorPresets.map((c) => /* @__PURE__ */ React15.createElement(
3006
+ "button",
3007
+ {
3008
+ key: c,
3009
+ type: "button",
3010
+ className: `ShapeHighlight__color-preset ${strokeColor === c ? "active" : ""}`,
3011
+ style: { backgroundColor: c },
3012
+ onClick: () => onStyleChange({ strokeColor: c }),
3013
+ title: c
3014
+ }
3015
+ ))), /* @__PURE__ */ React15.createElement(
3016
+ "input",
3017
+ {
3018
+ type: "color",
3019
+ value: strokeColor,
3020
+ onChange: (e) => {
3021
+ onStyleChange({ strokeColor: e.target.value });
3022
+ }
3023
+ }
3024
+ ))),
3025
+ /* @__PURE__ */ React15.createElement("div", { className: "ShapeHighlight__style-row" }, /* @__PURE__ */ React15.createElement("label", null, "Width"), /* @__PURE__ */ React15.createElement("div", { className: "ShapeHighlight__width-options" }, STROKE_WIDTHS2.map((w) => /* @__PURE__ */ React15.createElement(
3026
+ "button",
3027
+ {
3028
+ key: w.value,
3029
+ type: "button",
3030
+ className: `ShapeHighlight__width-button ${strokeWidth === w.value ? "active" : ""}`,
3031
+ onClick: () => onStyleChange({ strokeWidth: w.value }),
3032
+ title: w.label
3033
+ },
3034
+ w.label
3035
+ ))))
3036
+ )
3037
+ ),
3038
+ /* @__PURE__ */ React15.createElement(
3039
+ Rnd5,
3040
+ {
3041
+ onMouseEnter: () => setIsHovered(true),
3042
+ onMouseLeave: () => setIsHovered(false),
3043
+ className: "ShapeHighlight__rnd",
3044
+ onDragStop: (_, data) => {
3045
+ const boundingRect = {
3046
+ ...highlight.position.boundingRect,
3047
+ top: data.y,
3048
+ left: data.x
3049
+ };
3050
+ onChange?.(boundingRect);
3051
+ onEditEnd?.();
3052
+ },
3053
+ onDragStart: onEditStart,
3054
+ onResizeStop: (_e, _direction, ref, _delta, position) => {
3055
+ const boundingRect = {
3056
+ top: position.y,
3057
+ left: position.x,
3058
+ width: ref.offsetWidth,
3059
+ height: ref.offsetHeight,
3060
+ pageNumber: getPageFromElement(ref)?.number || highlight.position.boundingRect.pageNumber
3061
+ };
3062
+ onChange?.(boundingRect);
3063
+ onEditEnd?.();
3064
+ },
3065
+ onResizeStart: onEditStart,
3066
+ default: {
3067
+ x: highlight.position.boundingRect.left,
3068
+ y: highlight.position.boundingRect.top,
3069
+ width: highlight.position.boundingRect.width || 100,
3070
+ height: highlight.position.boundingRect.height || 100
3071
+ },
3072
+ minWidth: 20,
3073
+ minHeight: 20,
3074
+ key,
3075
+ bounds,
3076
+ lockAspectRatio: shapeType === "circle",
3077
+ onClick: (event) => {
3078
+ event.stopPropagation();
3079
+ event.preventDefault();
3080
+ },
3081
+ style
3082
+ },
3083
+ /* @__PURE__ */ React15.createElement("div", { className: "ShapeHighlight__container" }, renderShape(
3084
+ highlight.position.boundingRect.width || 100,
3085
+ highlight.position.boundingRect.height || 100
3086
+ ))
3087
+ )
3088
+ );
3089
+ };
3090
+
3091
+ // src/components/PdfLoader.tsx
3092
+ import React16, { useEffect as useEffect11, useRef as useRef14, useState as useState11 } from "react";
3093
+ import { GlobalWorkerOptions, getDocument as getDocument2 } from "pdfjs-dist";
3094
+ var DEFAULT_BEFORE_LOAD = (progress) => /* @__PURE__ */ React16.createElement("div", { style: { color: "black" } }, "Loading ", Math.floor(progress.loaded / progress.total * 100), "%");
3095
+ var DEFAULT_ERROR_MESSAGE = (error) => /* @__PURE__ */ React16.createElement("div", { style: { color: "black" } }, error.message);
3096
+ var DEFAULT_ON_ERROR = (error) => {
3097
+ throw new Error(`Error loading PDF document: ${error.message}!`);
3098
+ };
3099
+ var DEFAULT_WORKER_SRC = "https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs";
3100
+ var PdfLoader = ({
3101
+ document: document2,
3102
+ beforeLoad = DEFAULT_BEFORE_LOAD,
3103
+ errorMessage = DEFAULT_ERROR_MESSAGE,
3104
+ children,
3105
+ onError = DEFAULT_ON_ERROR,
3106
+ workerSrc = DEFAULT_WORKER_SRC
3107
+ }) => {
3108
+ const pdfLoadingTaskRef = useRef14(null);
3109
+ const pdfDocumentRef = useRef14(null);
3110
+ const [error, setError] = useState11(null);
3111
+ const [loadingProgress, setLoadingProgress] = useState11(null);
3112
+ useEffect11(() => {
3113
+ GlobalWorkerOptions.workerSrc = workerSrc;
3114
+ pdfLoadingTaskRef.current = getDocument2(document2);
3115
+ pdfLoadingTaskRef.current.onProgress = (progress) => {
3116
+ setLoadingProgress(progress.loaded > progress.total ? null : progress);
3117
+ };
3118
+ pdfLoadingTaskRef.current.promise.then((pdfDocument) => {
3119
+ pdfDocumentRef.current = pdfDocument;
3120
+ }).catch((error2) => {
3121
+ if (error2.message != "Worker was destroyed") {
3122
+ setError(error2);
3123
+ onError(error2);
3124
+ }
3125
+ }).finally(() => {
3126
+ setLoadingProgress(null);
3127
+ });
3128
+ return () => {
3129
+ if (pdfLoadingTaskRef.current) {
3130
+ pdfLoadingTaskRef.current.destroy();
3131
+ }
3132
+ if (pdfDocumentRef.current) {
3133
+ pdfDocumentRef.current.destroy();
3134
+ }
3135
+ };
3136
+ }, [document2]);
3137
+ return error ? errorMessage(error) : loadingProgress ? beforeLoad(loadingProgress) : pdfDocumentRef.current && children(pdfDocumentRef.current);
3138
+ };
3139
+
3140
+ // src/lib/export-pdf.ts
3141
+ import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
3142
+ function parseColor(color) {
3143
+ const rgbaMatch = color.match(
3144
+ /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
3145
+ );
3146
+ if (rgbaMatch) {
3147
+ return {
3148
+ r: parseInt(rgbaMatch[1]) / 255,
3149
+ g: parseInt(rgbaMatch[2]) / 255,
3150
+ b: parseInt(rgbaMatch[3]) / 255,
3151
+ a: rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1
3152
+ };
3153
+ }
3154
+ const hex = color.replace("#", "");
3155
+ if (hex.length === 3) {
3156
+ return {
3157
+ r: parseInt(hex[0] + hex[0], 16) / 255,
3158
+ g: parseInt(hex[1] + hex[1], 16) / 255,
3159
+ b: parseInt(hex[2] + hex[2], 16) / 255,
3160
+ a: 1
3161
+ };
3162
+ }
3163
+ if (hex.length === 6) {
3164
+ return {
3165
+ r: parseInt(hex.slice(0, 2), 16) / 255,
3166
+ g: parseInt(hex.slice(2, 4), 16) / 255,
3167
+ b: parseInt(hex.slice(4, 6), 16) / 255,
3168
+ a: 1
3169
+ };
3170
+ }
3171
+ return { r: 1, g: 0.89, b: 0.56, a: 0.5 };
3172
+ }
3173
+ function scaledToPdfPoints(scaled, page) {
3174
+ const pdfWidth = page.getWidth();
3175
+ const pdfHeight = page.getHeight();
3176
+ const xRatio = pdfWidth / scaled.width;
3177
+ const yRatio = pdfHeight / scaled.height;
3178
+ const x = scaled.x1 * xRatio;
3179
+ const width = (scaled.x2 - scaled.x1) * xRatio;
3180
+ const height = (scaled.y2 - scaled.y1) * yRatio;
3181
+ const y = pdfHeight - scaled.y1 * yRatio - height;
3182
+ return { x, y, width, height };
3183
+ }
3184
+ function dataUrlToBytes(dataUrl) {
3185
+ const base64 = dataUrl.split(",")[1];
3186
+ const byteString = atob(base64);
3187
+ const bytes = new Uint8Array(byteString.length);
3188
+ for (let i = 0; i < byteString.length; i++) {
3189
+ bytes[i] = byteString.charCodeAt(i);
3190
+ }
3191
+ const type = dataUrl.includes("image/png") ? "png" : "jpg";
3192
+ return { bytes, type };
3193
+ }
3194
+ function wrapText(text, font, fontSize, maxWidth) {
3195
+ if (!text || maxWidth <= 0) return [];
3196
+ const lines = [];
3197
+ const paragraphs = text.split(/\n/);
3198
+ for (const paragraph of paragraphs) {
3199
+ if (!paragraph.trim()) {
3200
+ lines.push("");
3201
+ continue;
3202
+ }
3203
+ const words = paragraph.split(/\s+/);
3204
+ let currentLine = "";
3205
+ for (const word of words) {
3206
+ const testLine = currentLine ? `${currentLine} ${word}` : word;
3207
+ const testWidth = font.widthOfTextAtSize(testLine, fontSize);
3208
+ if (testWidth <= maxWidth) {
3209
+ currentLine = testLine;
3210
+ } else {
3211
+ if (currentLine) {
3212
+ lines.push(currentLine);
3213
+ currentLine = "";
3214
+ }
3215
+ if (font.widthOfTextAtSize(word, fontSize) > maxWidth) {
3216
+ let remaining = word;
3217
+ while (remaining.length > 0) {
3218
+ let charCount = 1;
3219
+ while (charCount < remaining.length && font.widthOfTextAtSize(remaining.substring(0, charCount + 1), fontSize) <= maxWidth) {
3220
+ charCount++;
3221
+ }
3222
+ const chunk = remaining.substring(0, charCount);
3223
+ remaining = remaining.substring(charCount);
3224
+ if (remaining.length > 0) {
3225
+ lines.push(chunk);
3226
+ } else {
3227
+ currentLine = chunk;
3228
+ }
3229
+ }
3230
+ } else {
3231
+ currentLine = word;
3232
+ }
3233
+ }
3234
+ }
3235
+ if (currentLine) lines.push(currentLine);
3236
+ }
3237
+ return lines;
3238
+ }
3239
+ function groupByPage(highlights) {
3240
+ const map = /* @__PURE__ */ new Map();
3241
+ for (const h of highlights) {
3242
+ const pageNum = h.position.boundingRect.pageNumber;
3243
+ if (!map.has(pageNum)) map.set(pageNum, []);
3244
+ map.get(pageNum).push(h);
3245
+ }
3246
+ return map;
3247
+ }
3248
+ async function renderTextHighlight(page, highlight, options) {
3249
+ const colorStr = highlight.highlightColor || options.textHighlightColor || "rgba(255, 226, 143, 0.5)";
3250
+ const color = parseColor(colorStr);
3251
+ const highlightStyle = highlight.highlightStyle || "highlight";
3252
+ const rects = highlight.position.rects.length > 0 ? highlight.position.rects : [highlight.position.boundingRect];
3253
+ for (const rect of rects) {
3254
+ const { x, y, width, height } = scaledToPdfPoints(rect, page);
3255
+ if (highlightStyle === "highlight") {
3256
+ page.drawRectangle({
3257
+ x,
3258
+ y,
3259
+ width,
3260
+ height,
3261
+ color: rgb(color.r, color.g, color.b),
3262
+ opacity: color.a
3263
+ });
3264
+ } else if (highlightStyle === "underline") {
3265
+ const lineThickness = Math.max(1, height * 0.1);
3266
+ page.drawRectangle({
3267
+ x,
3268
+ y,
3269
+ width,
3270
+ height: lineThickness,
3271
+ color: rgb(color.r, color.g, color.b),
3272
+ opacity: color.a
3273
+ });
3274
+ } else if (highlightStyle === "strikethrough") {
3275
+ const lineThickness = Math.max(1, height * 0.1);
3276
+ const lineY = y + height / 2 - lineThickness / 2;
3277
+ page.drawRectangle({
3278
+ x,
3279
+ y: lineY,
3280
+ width,
3281
+ height: lineThickness,
3282
+ color: rgb(color.r, color.g, color.b),
3283
+ opacity: color.a
3284
+ });
3285
+ }
3286
+ }
3287
+ }
3288
+ async function renderAreaHighlight(page, highlight, options) {
3289
+ const colorStr = highlight.highlightColor || options.areaHighlightColor || "rgba(255, 226, 143, 0.5)";
3290
+ const color = parseColor(colorStr);
3291
+ const { x, y, width, height } = scaledToPdfPoints(
3292
+ highlight.position.boundingRect,
3293
+ page
3294
+ );
3295
+ page.drawRectangle({
3296
+ x,
3297
+ y,
3298
+ width,
3299
+ height,
3300
+ color: rgb(color.r, color.g, color.b),
3301
+ opacity: color.a
3302
+ });
3303
+ }
3304
+ async function renderFreetextHighlight(page, highlight, options, font) {
3305
+ const text = highlight.content?.text || "";
3306
+ const textColor = parseColor(
3307
+ highlight.color || options.defaultFreetextColor || "#333333"
3308
+ );
3309
+ const { x, y, width, height } = scaledToPdfPoints(
3310
+ highlight.position.boundingRect,
3311
+ page
3312
+ );
3313
+ const pdfHeight = page.getHeight();
3314
+ const yRatio = pdfHeight / highlight.position.boundingRect.height;
3315
+ const storedFontSize = parseInt(highlight.fontSize || "") || options.defaultFreetextFontSize || 14;
3316
+ const fontSize = storedFontSize * yRatio;
3317
+ console.log("Freetext export:", {
3318
+ storedFontSize,
3319
+ yRatio,
3320
+ fontSize,
3321
+ boxDimensions: { x, y, width, height },
3322
+ text: text.substring(0, 50)
3323
+ });
3324
+ const bgColorValue = highlight.backgroundColor || options.defaultFreetextBgColor || "#ffffc8";
3325
+ if (bgColorValue !== "transparent") {
3326
+ const bgColor = parseColor(bgColorValue);
3327
+ page.drawRectangle({
3328
+ x,
3329
+ y,
3330
+ width,
3331
+ height,
3332
+ color: rgb(bgColor.r, bgColor.g, bgColor.b),
3333
+ opacity: bgColor.a
3334
+ });
3335
+ }
3336
+ const padding = 4 * yRatio;
3337
+ const maxWidth = width - padding * 2;
3338
+ const lineHeight = fontSize * 1.3;
3339
+ if (maxWidth > 0 && text) {
3340
+ const lines = wrapText(text, font, fontSize, maxWidth);
3341
+ let currentY = y + height - fontSize - padding;
3342
+ for (const line of lines) {
3343
+ if (currentY < y + padding) break;
3344
+ if (line.trim()) {
3345
+ page.drawText(line, {
3346
+ x: x + padding,
3347
+ y: currentY,
3348
+ size: fontSize,
3349
+ font,
3350
+ color: rgb(textColor.r, textColor.g, textColor.b)
3351
+ });
3352
+ }
3353
+ currentY -= lineHeight;
3354
+ }
3355
+ }
3356
+ }
3357
+ function transformToRawCoordinates(page, x, y, width, height) {
3358
+ const rotation = page.getRotation().angle;
3359
+ const pageWidth = page.getWidth();
3360
+ const pageHeight = page.getHeight();
3361
+ if (rotation === 90) {
3362
+ return {
3363
+ x: y,
3364
+ y: pageWidth - x - width,
3365
+ width: height,
3366
+ height: width
3367
+ };
3368
+ } else if (rotation === 180) {
3369
+ return {
3370
+ x: pageWidth - x - width,
3371
+ y: pageHeight - y - height,
3372
+ width,
3373
+ height
3374
+ };
3375
+ } else if (rotation === 270) {
3376
+ return {
3377
+ x: pageHeight - y - height,
3378
+ y: x,
3379
+ width: height,
3380
+ height: width
3381
+ };
3382
+ }
3383
+ return { x, y, width, height };
3384
+ }
3385
+ async function renderImageHighlight(pdfDoc, page, highlight) {
3386
+ const imageDataUrl = highlight.content?.image;
3387
+ if (!imageDataUrl) return;
3388
+ try {
3389
+ const { bytes, type } = dataUrlToBytes(imageDataUrl);
3390
+ const image = type === "png" ? await pdfDoc.embedPng(bytes) : await pdfDoc.embedJpg(bytes);
3391
+ const visualCoords = scaledToPdfPoints(
3392
+ highlight.position.boundingRect,
3393
+ page
3394
+ );
3395
+ const rawCoords = transformToRawCoordinates(
3396
+ page,
3397
+ visualCoords.x,
3398
+ visualCoords.y,
3399
+ visualCoords.width,
3400
+ visualCoords.height
3401
+ );
3402
+ console.log("Image export:", {
3403
+ rotation: page.getRotation().angle,
3404
+ visualCoords,
3405
+ rawCoords
3406
+ });
3407
+ page.drawImage(image, {
3408
+ x: rawCoords.x,
3409
+ y: rawCoords.y,
3410
+ width: rawCoords.width,
3411
+ height: rawCoords.height
3412
+ });
3413
+ } catch (error) {
3414
+ console.error("Failed to embed image:", error);
3415
+ }
3416
+ }
3417
+ async function renderShapeHighlight(page, highlight) {
3418
+ const shapeType = highlight.content?.shape?.shapeType || highlight.shapeType || "rectangle";
3419
+ const strokeColorStr = highlight.content?.shape?.strokeColor || highlight.strokeColor || "#000000";
3420
+ const strokeWidth = highlight.content?.shape?.strokeWidth || highlight.strokeWidth || 2;
3421
+ const color = parseColor(strokeColorStr);
3422
+ const { x, y, width, height } = scaledToPdfPoints(
3423
+ highlight.position.boundingRect,
3424
+ page
3425
+ );
3426
+ switch (shapeType) {
3427
+ case "rectangle":
3428
+ page.drawRectangle({
3429
+ x,
3430
+ y,
3431
+ width,
3432
+ height,
3433
+ borderColor: rgb(color.r, color.g, color.b),
3434
+ borderWidth: strokeWidth,
3435
+ opacity: color.a
3436
+ });
3437
+ break;
3438
+ case "circle":
3439
+ page.drawEllipse({
3440
+ x: x + width / 2,
3441
+ y: y + height / 2,
3442
+ xScale: width / 2,
3443
+ yScale: height / 2,
3444
+ borderColor: rgb(color.r, color.g, color.b),
3445
+ borderWidth: strokeWidth,
3446
+ opacity: color.a
3447
+ });
3448
+ break;
3449
+ case "arrow": {
3450
+ const startPt = highlight.content?.shape?.startPoint;
3451
+ const endPt = highlight.content?.shape?.endPoint;
3452
+ const startX = startPt ? x + startPt.x * width : x;
3453
+ const startY = startPt ? y + (1 - startPt.y) * height : y + height / 2;
3454
+ const endX = endPt ? x + endPt.x * width : x + width;
3455
+ const endY = endPt ? y + (1 - endPt.y) * height : y + height / 2;
3456
+ page.drawLine({
3457
+ start: { x: startX, y: startY },
3458
+ end: { x: endX, y: endY },
3459
+ color: rgb(color.r, color.g, color.b),
3460
+ thickness: strokeWidth,
3461
+ opacity: color.a
3462
+ });
3463
+ const angle = Math.atan2(endY - startY, endX - startX);
3464
+ const arrowSize = Math.min(15, width * 0.2, height * 0.4);
3465
+ const arrowAngle = Math.PI / 6;
3466
+ page.drawLine({
3467
+ start: {
3468
+ x: endX - arrowSize * Math.cos(angle - arrowAngle),
3469
+ y: endY - arrowSize * Math.sin(angle - arrowAngle)
3470
+ },
3471
+ end: { x: endX, y: endY },
3472
+ color: rgb(color.r, color.g, color.b),
3473
+ thickness: strokeWidth,
3474
+ opacity: color.a
3475
+ });
3476
+ page.drawLine({
3477
+ start: {
3478
+ x: endX - arrowSize * Math.cos(angle + arrowAngle),
3479
+ y: endY - arrowSize * Math.sin(angle + arrowAngle)
3480
+ },
3481
+ end: { x: endX, y: endY },
3482
+ color: rgb(color.r, color.g, color.b),
3483
+ thickness: strokeWidth,
3484
+ opacity: color.a
3485
+ });
3486
+ break;
3487
+ }
3488
+ }
3489
+ }
3490
+ async function exportPdf(pdfSource, highlights, options = {}) {
3491
+ let pdfBytes;
3492
+ if (typeof pdfSource === "string") {
3493
+ const response = await fetch(pdfSource);
3494
+ pdfBytes = await response.arrayBuffer();
3495
+ } else {
3496
+ pdfBytes = pdfSource instanceof Uint8Array ? pdfSource.buffer.slice(
3497
+ pdfSource.byteOffset,
3498
+ pdfSource.byteOffset + pdfSource.byteLength
3499
+ ) : pdfSource;
3500
+ }
3501
+ const pdfDoc = await PDFDocument.load(pdfBytes);
3502
+ const pages = pdfDoc.getPages();
3503
+ const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
3504
+ const byPage = groupByPage(highlights);
3505
+ const totalPages = byPage.size;
3506
+ let currentPage = 0;
3507
+ for (const [pageNum, pageHighlights] of byPage) {
3508
+ const page = pages[pageNum - 1];
3509
+ if (!page) continue;
3510
+ for (const highlight of pageHighlights) {
3511
+ switch (highlight.type) {
3512
+ case "text":
3513
+ await renderTextHighlight(page, highlight, options);
3514
+ break;
3515
+ case "area":
3516
+ await renderAreaHighlight(page, highlight, options);
3517
+ break;
3518
+ case "freetext":
3519
+ await renderFreetextHighlight(page, highlight, options, font);
3520
+ break;
3521
+ case "image":
3522
+ await renderImageHighlight(pdfDoc, page, highlight);
3523
+ break;
3524
+ case "drawing":
3525
+ await renderImageHighlight(pdfDoc, page, highlight);
3526
+ break;
3527
+ case "shape":
3528
+ await renderShapeHighlight(page, highlight);
3529
+ break;
3530
+ default:
3531
+ await renderAreaHighlight(page, highlight, options);
3532
+ }
3533
+ }
3534
+ currentPage++;
3535
+ options.onProgress?.(currentPage, totalPages);
3536
+ }
3537
+ return pdfDoc.save();
3538
+ }
3539
+ export {
3540
+ AreaHighlight,
3541
+ DrawingCanvas,
3542
+ DrawingHighlight,
3543
+ FreetextHighlight,
3544
+ ImageHighlight,
3545
+ MonitoredHighlightContainer,
3546
+ PdfHighlighter,
3547
+ PdfLoader,
3548
+ ShapeCanvas,
3549
+ ShapeHighlight,
3550
+ SignaturePad,
3551
+ TextHighlight,
3552
+ exportPdf,
3553
+ scaledPositionToViewport,
3554
+ useHighlightContainerContext,
3555
+ usePdfHighlighterContext,
3556
+ viewportPositionToScaled
3557
+ };
19
3558
  //# sourceMappingURL=index.js.map