@anaralabs/lector 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/_tsup-dts-rollup.d.ts +176 -7
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1120 -327
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import React, { createContext, forwardRef, useCallback, useContext, useState, useRef, useEffect, cloneElement, createRef, useLayoutEffect } from 'react';
|
|
4
|
+
import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions, flip } from '@floating-ui/react';
|
|
5
5
|
import { useVirtualizer, elementScroll, debounce } from '@tanstack/react-virtual';
|
|
6
|
-
import { useStore, createStore } from 'zustand';
|
|
6
|
+
import { create, useStore, createStore } from 'zustand';
|
|
7
7
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
import { v4 } from 'uuid';
|
|
9
|
+
import { AnnotationLayer, TextLayer, getDocument } from 'pdfjs-dist';
|
|
8
10
|
import { useDebounce } from 'use-debounce';
|
|
9
11
|
import { Slot } from '@radix-ui/react-slot';
|
|
10
|
-
import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions } from '@floating-ui/react';
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
function r(e) {
|
|
14
|
-
var t, f, n = "";
|
|
15
|
-
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
16
|
-
else if ("object" == typeof e) if (Array.isArray(e)) {
|
|
17
|
-
var o = e.length;
|
|
18
|
-
for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
|
|
19
|
-
} else for (f in e) e[f] && (n && (n += " "), n += f);
|
|
20
|
-
return n;
|
|
21
|
-
}
|
|
22
|
-
function clsx() {
|
|
23
|
-
for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
|
|
24
|
-
return n;
|
|
25
|
-
}
|
|
26
|
-
var clsx_default = clsx;
|
|
13
|
+
// src/components/annotation-tooltip.tsx
|
|
27
14
|
|
|
28
15
|
// src/lib/clamp.ts
|
|
29
16
|
var clamp = (value, min, max) => {
|
|
@@ -130,12 +117,388 @@ var PDFStore = createZustandContext(
|
|
|
130
117
|
set({
|
|
131
118
|
customSelectionRects: val
|
|
132
119
|
});
|
|
133
|
-
}
|
|
120
|
+
},
|
|
121
|
+
coloredHighlights: [],
|
|
122
|
+
addColoredHighlight: (value) => set((prevState) => ({
|
|
123
|
+
coloredHighlights: [...prevState.coloredHighlights, value]
|
|
124
|
+
})),
|
|
125
|
+
deleteColoredHighlight: (uuid) => set((prevState) => ({
|
|
126
|
+
coloredHighlights: prevState.coloredHighlights.filter(
|
|
127
|
+
(rect) => rect.uuid !== uuid
|
|
128
|
+
)
|
|
129
|
+
}))
|
|
134
130
|
}));
|
|
135
131
|
}
|
|
136
132
|
);
|
|
137
133
|
var usePdf = (selector) => useStore(PDFStore.useContext(), selector);
|
|
138
134
|
|
|
135
|
+
// src/hooks/useAnnotationTooltip.ts
|
|
136
|
+
var defaultRect = {
|
|
137
|
+
width: 0,
|
|
138
|
+
height: 0,
|
|
139
|
+
x: 0,
|
|
140
|
+
y: 0,
|
|
141
|
+
top: 0,
|
|
142
|
+
right: 0,
|
|
143
|
+
bottom: 0,
|
|
144
|
+
left: 0
|
|
145
|
+
};
|
|
146
|
+
var useAnnotationTooltip = ({
|
|
147
|
+
annotation,
|
|
148
|
+
onOpenChange
|
|
149
|
+
}) => {
|
|
150
|
+
const isNewAnnotation = Date.now() - new Date(annotation.createdAt).getTime() < 1e3;
|
|
151
|
+
const [isOpen, setIsOpen] = useState(isNewAnnotation);
|
|
152
|
+
const viewportRef = usePdf((state) => state.viewportRef);
|
|
153
|
+
const {
|
|
154
|
+
refs,
|
|
155
|
+
floatingStyles,
|
|
156
|
+
context
|
|
157
|
+
} = useFloating({
|
|
158
|
+
placement: "top",
|
|
159
|
+
open: isOpen,
|
|
160
|
+
onOpenChange: (open) => {
|
|
161
|
+
setIsOpen(open);
|
|
162
|
+
onOpenChange?.(open);
|
|
163
|
+
},
|
|
164
|
+
whileElementsMounted: autoUpdate,
|
|
165
|
+
middleware: [
|
|
166
|
+
offset(10),
|
|
167
|
+
flip({
|
|
168
|
+
crossAxis: false
|
|
169
|
+
}),
|
|
170
|
+
shift({ padding: 8 })
|
|
171
|
+
]
|
|
172
|
+
});
|
|
173
|
+
const dismiss = useDismiss(context);
|
|
174
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
|
|
175
|
+
const updateTooltipPosition = useCallback(() => {
|
|
176
|
+
if (!annotation.highlights.length) return;
|
|
177
|
+
const lastHighlight = annotation.highlights[annotation.highlights.length - 1];
|
|
178
|
+
refs.setReference({
|
|
179
|
+
getBoundingClientRect: () => {
|
|
180
|
+
const viewportElement = viewportRef.current;
|
|
181
|
+
if (!viewportElement || !lastHighlight) return defaultRect;
|
|
182
|
+
const pageElement = viewportElement.querySelector(`[data-page-number="${annotation.pageNumber}"]`);
|
|
183
|
+
if (!pageElement) return defaultRect;
|
|
184
|
+
const pageRect = pageElement.getBoundingClientRect();
|
|
185
|
+
const left = pageRect.left + lastHighlight.left;
|
|
186
|
+
const top = pageRect.top + lastHighlight.top;
|
|
187
|
+
const width = lastHighlight.width;
|
|
188
|
+
const height = lastHighlight.height;
|
|
189
|
+
return {
|
|
190
|
+
width,
|
|
191
|
+
height,
|
|
192
|
+
x: left,
|
|
193
|
+
y: top,
|
|
194
|
+
top,
|
|
195
|
+
right: left + width,
|
|
196
|
+
bottom: top + height,
|
|
197
|
+
left
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}, [annotation.highlights, annotation.pageNumber, refs, viewportRef]);
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
updateTooltipPosition();
|
|
204
|
+
const handleScroll = () => {
|
|
205
|
+
requestAnimationFrame(updateTooltipPosition);
|
|
206
|
+
};
|
|
207
|
+
const handleResize = () => {
|
|
208
|
+
requestAnimationFrame(updateTooltipPosition);
|
|
209
|
+
};
|
|
210
|
+
if (viewportRef.current) {
|
|
211
|
+
viewportRef.current.addEventListener("scroll", handleScroll, {
|
|
212
|
+
passive: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
216
|
+
return () => {
|
|
217
|
+
if (viewportRef.current) {
|
|
218
|
+
viewportRef.current.removeEventListener("scroll", handleScroll);
|
|
219
|
+
}
|
|
220
|
+
window.removeEventListener("resize", handleResize);
|
|
221
|
+
};
|
|
222
|
+
}, [updateTooltipPosition, viewportRef]);
|
|
223
|
+
return {
|
|
224
|
+
isOpen,
|
|
225
|
+
setIsOpen,
|
|
226
|
+
refs,
|
|
227
|
+
floatingStyles,
|
|
228
|
+
getFloatingProps,
|
|
229
|
+
getReferenceProps
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
var AnnotationTooltip = ({
|
|
233
|
+
annotation,
|
|
234
|
+
children,
|
|
235
|
+
tooltipContent,
|
|
236
|
+
onOpenChange,
|
|
237
|
+
isOpen: controlledIsOpen
|
|
238
|
+
}) => {
|
|
239
|
+
const {
|
|
240
|
+
isOpen: uncontrolledIsOpen,
|
|
241
|
+
setIsOpen,
|
|
242
|
+
refs,
|
|
243
|
+
floatingStyles,
|
|
244
|
+
getFloatingProps,
|
|
245
|
+
getReferenceProps
|
|
246
|
+
} = useAnnotationTooltip({
|
|
247
|
+
annotation,
|
|
248
|
+
onOpenChange
|
|
249
|
+
});
|
|
250
|
+
const isOpen = controlledIsOpen ?? uncontrolledIsOpen;
|
|
251
|
+
const handleClick = useCallback(() => {
|
|
252
|
+
if (controlledIsOpen === void 0) {
|
|
253
|
+
setIsOpen(!isOpen);
|
|
254
|
+
}
|
|
255
|
+
}, [controlledIsOpen, isOpen, setIsOpen]);
|
|
256
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
257
|
+
/* @__PURE__ */ jsx(
|
|
258
|
+
"div",
|
|
259
|
+
{
|
|
260
|
+
ref: refs.setReference,
|
|
261
|
+
onClick: handleClick,
|
|
262
|
+
...getReferenceProps(),
|
|
263
|
+
children
|
|
264
|
+
}
|
|
265
|
+
),
|
|
266
|
+
isOpen && /* @__PURE__ */ jsx(
|
|
267
|
+
"div",
|
|
268
|
+
{
|
|
269
|
+
ref: refs.setFloating,
|
|
270
|
+
className: "bg-white shadow-lg rounded-lg p-3 z-50 min-w-[200px]",
|
|
271
|
+
style: {
|
|
272
|
+
...floatingStyles,
|
|
273
|
+
position: "fixed",
|
|
274
|
+
zIndex: 9999
|
|
275
|
+
},
|
|
276
|
+
...getFloatingProps(),
|
|
277
|
+
children: tooltipContent
|
|
278
|
+
}
|
|
279
|
+
)
|
|
280
|
+
] });
|
|
281
|
+
};
|
|
282
|
+
var useAnnotations = create((set) => ({
|
|
283
|
+
annotations: [],
|
|
284
|
+
addAnnotation: (annotation) => set((state) => ({
|
|
285
|
+
annotations: [
|
|
286
|
+
...state.annotations,
|
|
287
|
+
{
|
|
288
|
+
...annotation,
|
|
289
|
+
id: v4(),
|
|
290
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
291
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
})),
|
|
295
|
+
updateAnnotation: (id, updates) => set((state) => ({
|
|
296
|
+
annotations: state.annotations.map(
|
|
297
|
+
(annotation) => annotation.id === id ? {
|
|
298
|
+
...annotation,
|
|
299
|
+
...updates,
|
|
300
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
301
|
+
} : annotation
|
|
302
|
+
)
|
|
303
|
+
})),
|
|
304
|
+
deleteAnnotation: (id) => set((state) => ({
|
|
305
|
+
annotations: state.annotations.filter((annotation) => annotation.id !== id)
|
|
306
|
+
})),
|
|
307
|
+
setAnnotations: (annotations) => set({ annotations })
|
|
308
|
+
}));
|
|
309
|
+
var PDFPageNumberContext = createContext(0);
|
|
310
|
+
var usePDFPageNumber = () => {
|
|
311
|
+
return useContext(PDFPageNumberContext);
|
|
312
|
+
};
|
|
313
|
+
var useAnnotationActions = ({
|
|
314
|
+
annotation,
|
|
315
|
+
onClose
|
|
316
|
+
}) => {
|
|
317
|
+
const { updateAnnotation } = useAnnotations();
|
|
318
|
+
const [comment, setComment] = useState(annotation.comment || "");
|
|
319
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
320
|
+
const handleSaveComment = useCallback(() => {
|
|
321
|
+
updateAnnotation(annotation.id, { comment });
|
|
322
|
+
setIsEditing(false);
|
|
323
|
+
onClose?.();
|
|
324
|
+
}, [annotation.id, comment, updateAnnotation, onClose]);
|
|
325
|
+
const handleColorChange = useCallback(
|
|
326
|
+
(color) => {
|
|
327
|
+
updateAnnotation(annotation.id, { color });
|
|
328
|
+
onClose?.();
|
|
329
|
+
},
|
|
330
|
+
[annotation.id, updateAnnotation, onClose]
|
|
331
|
+
);
|
|
332
|
+
const handleCancelEdit = useCallback(() => {
|
|
333
|
+
setIsEditing(false);
|
|
334
|
+
onClose?.();
|
|
335
|
+
}, [onClose]);
|
|
336
|
+
const colors = [
|
|
337
|
+
"rgba(255, 255, 0, 0.3)",
|
|
338
|
+
// Yellow
|
|
339
|
+
"rgba(0, 255, 0, 0.3)",
|
|
340
|
+
// Green
|
|
341
|
+
"rgba(255, 182, 193, 0.3)",
|
|
342
|
+
// Pink
|
|
343
|
+
"rgba(135, 206, 235, 0.3)"
|
|
344
|
+
// Sky Blue
|
|
345
|
+
];
|
|
346
|
+
return {
|
|
347
|
+
comment,
|
|
348
|
+
isEditing,
|
|
349
|
+
setComment,
|
|
350
|
+
setIsEditing,
|
|
351
|
+
handleSaveComment,
|
|
352
|
+
handleColorChange,
|
|
353
|
+
handleCancelEdit,
|
|
354
|
+
colors
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
var DefaultAnnotationTooltipContent = ({
|
|
358
|
+
annotation,
|
|
359
|
+
onClose
|
|
360
|
+
}) => {
|
|
361
|
+
const {
|
|
362
|
+
comment,
|
|
363
|
+
isEditing,
|
|
364
|
+
setComment,
|
|
365
|
+
setIsEditing,
|
|
366
|
+
handleSaveComment,
|
|
367
|
+
handleColorChange,
|
|
368
|
+
handleCancelEdit,
|
|
369
|
+
colors
|
|
370
|
+
} = useAnnotationActions({
|
|
371
|
+
annotation,
|
|
372
|
+
onClose
|
|
373
|
+
});
|
|
374
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
375
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2", children: colors.map((color) => /* @__PURE__ */ jsx(
|
|
376
|
+
"button",
|
|
377
|
+
{
|
|
378
|
+
className: "w-6 h-6 rounded",
|
|
379
|
+
style: { backgroundColor: color },
|
|
380
|
+
onClick: () => handleColorChange(color)
|
|
381
|
+
},
|
|
382
|
+
color
|
|
383
|
+
)) }),
|
|
384
|
+
isEditing ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
385
|
+
/* @__PURE__ */ jsx(
|
|
386
|
+
"textarea",
|
|
387
|
+
{
|
|
388
|
+
value: comment,
|
|
389
|
+
onChange: (e) => setComment(e.target.value),
|
|
390
|
+
className: "border rounded p-2 text-sm",
|
|
391
|
+
placeholder: "Add a comment...",
|
|
392
|
+
rows: 3
|
|
393
|
+
}
|
|
394
|
+
),
|
|
395
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
396
|
+
/* @__PURE__ */ jsx(
|
|
397
|
+
"button",
|
|
398
|
+
{
|
|
399
|
+
onClick: handleCancelEdit,
|
|
400
|
+
className: "px-2 py-1 text-sm text-gray-600 hover:text-gray-800",
|
|
401
|
+
children: "Cancel"
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
/* @__PURE__ */ jsx(
|
|
405
|
+
"button",
|
|
406
|
+
{
|
|
407
|
+
onClick: handleSaveComment,
|
|
408
|
+
className: "px-2 py-1 text-sm bg-blue-500 text-white rounded hover:bg-blue-600",
|
|
409
|
+
children: "Save"
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
] })
|
|
413
|
+
] }) : /* @__PURE__ */ jsx("div", { children: annotation.comment ? /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-700", children: annotation.comment }) : /* @__PURE__ */ jsx(
|
|
414
|
+
"button",
|
|
415
|
+
{
|
|
416
|
+
onClick: () => setIsEditing(true),
|
|
417
|
+
className: "text-sm text-blue-500 hover:text-blue-600",
|
|
418
|
+
children: "Add comment"
|
|
419
|
+
}
|
|
420
|
+
) })
|
|
421
|
+
] });
|
|
422
|
+
};
|
|
423
|
+
var AnnotationHighlightLayer = ({
|
|
424
|
+
className,
|
|
425
|
+
style,
|
|
426
|
+
renderTooltipContent,
|
|
427
|
+
focusedAnnotationId,
|
|
428
|
+
onAnnotationClick
|
|
429
|
+
}) => {
|
|
430
|
+
const { annotations } = useAnnotations();
|
|
431
|
+
const pageNumber = usePDFPageNumber();
|
|
432
|
+
const pageAnnotations = annotations.filter(
|
|
433
|
+
(annotation) => annotation.pageNumber === pageNumber
|
|
434
|
+
);
|
|
435
|
+
return /* @__PURE__ */ jsx("div", { className, style, children: pageAnnotations.map((annotation) => /* @__PURE__ */ jsx(
|
|
436
|
+
AnnotationTooltip,
|
|
437
|
+
{
|
|
438
|
+
annotation,
|
|
439
|
+
isOpen: focusedAnnotationId === annotation.id,
|
|
440
|
+
onOpenChange: (open) => {
|
|
441
|
+
if (open && onAnnotationClick) {
|
|
442
|
+
onAnnotationClick(annotation);
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
tooltipContent: renderTooltipContent ? renderTooltipContent({
|
|
446
|
+
annotation,
|
|
447
|
+
onClose: () => {
|
|
448
|
+
}
|
|
449
|
+
}) : /* @__PURE__ */ jsx(
|
|
450
|
+
DefaultAnnotationTooltipContent,
|
|
451
|
+
{
|
|
452
|
+
annotation,
|
|
453
|
+
onClose: () => {
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
children: /* @__PURE__ */ jsx(
|
|
458
|
+
"div",
|
|
459
|
+
{
|
|
460
|
+
style: { cursor: "pointer" },
|
|
461
|
+
onClick: () => onAnnotationClick?.(annotation),
|
|
462
|
+
children: annotation.highlights.map((highlight, index) => /* @__PURE__ */ jsx(
|
|
463
|
+
"div",
|
|
464
|
+
{
|
|
465
|
+
style: {
|
|
466
|
+
position: "absolute",
|
|
467
|
+
top: highlight.top,
|
|
468
|
+
left: highlight.left,
|
|
469
|
+
width: highlight.width,
|
|
470
|
+
height: highlight.height,
|
|
471
|
+
backgroundColor: annotation.color || "rgba(255, 255, 0, 0.3)",
|
|
472
|
+
transition: "background-color 0.2s ease",
|
|
473
|
+
cursor: "pointer",
|
|
474
|
+
opacity: focusedAnnotationId === annotation.id ? 1 : 0.5
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
index
|
|
478
|
+
))
|
|
479
|
+
}
|
|
480
|
+
)
|
|
481
|
+
},
|
|
482
|
+
annotation.id
|
|
483
|
+
)) });
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// ../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
487
|
+
function r(e) {
|
|
488
|
+
var t, f, n = "";
|
|
489
|
+
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
490
|
+
else if ("object" == typeof e) if (Array.isArray(e)) {
|
|
491
|
+
var o = e.length;
|
|
492
|
+
for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
|
|
493
|
+
} else for (f in e) e[f] && (n && (n += " "), n += f);
|
|
494
|
+
return n;
|
|
495
|
+
}
|
|
496
|
+
function clsx() {
|
|
497
|
+
for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
|
|
498
|
+
return n;
|
|
499
|
+
}
|
|
500
|
+
var clsx_default = clsx;
|
|
501
|
+
|
|
139
502
|
// src/lib/cancellable.ts
|
|
140
503
|
var cancellable = (promise) => {
|
|
141
504
|
let isCancelled = false;
|
|
@@ -160,10 +523,74 @@ var cancellable = (promise) => {
|
|
|
160
523
|
}
|
|
161
524
|
};
|
|
162
525
|
};
|
|
526
|
+
|
|
527
|
+
// src/hooks/pages/usePdfJump.tsx
|
|
528
|
+
var usePdfJump = () => {
|
|
529
|
+
const virtualizer = usePdf((state) => state.virtualizer);
|
|
530
|
+
const setHighlight = usePdf((state) => state.setHighlight);
|
|
531
|
+
const jumpToPage = (pageIndex, options) => {
|
|
532
|
+
if (!virtualizer) return;
|
|
533
|
+
const defaultOptions = {
|
|
534
|
+
align: "start",
|
|
535
|
+
behavior: "smooth"
|
|
536
|
+
};
|
|
537
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
538
|
+
virtualizer.scrollToIndex(pageIndex - 1, finalOptions);
|
|
539
|
+
};
|
|
540
|
+
const jumpToOffset = (offset3) => {
|
|
541
|
+
if (!virtualizer) return;
|
|
542
|
+
virtualizer.scrollToOffset(offset3, {
|
|
543
|
+
align: "start",
|
|
544
|
+
behavior: "smooth"
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
const jumpToHighlightRects = (rects, type, align = "start", additionalOffset = 0) => {
|
|
548
|
+
if (!virtualizer) return;
|
|
549
|
+
setHighlight(rects);
|
|
550
|
+
const firstPage = Math.min(...rects.map((rect) => rect.pageNumber));
|
|
551
|
+
const pageOffset = virtualizer.getOffsetForIndex(firstPage - 1, "start");
|
|
552
|
+
if (pageOffset === null) return;
|
|
553
|
+
const targetRect = rects.find((rect) => rect.pageNumber === firstPage);
|
|
554
|
+
if (!targetRect) return;
|
|
555
|
+
const isNumber = pageOffset?.[0] != null;
|
|
556
|
+
if (!isNumber) return;
|
|
557
|
+
const pageStart = pageOffset[0] ?? 0;
|
|
558
|
+
let rectTop;
|
|
559
|
+
let rectHeight;
|
|
560
|
+
if (type === "percent") {
|
|
561
|
+
const estimatePageHeight = virtualizer.options.estimateSize(
|
|
562
|
+
firstPage - 1
|
|
563
|
+
);
|
|
564
|
+
rectTop = targetRect.top / 100 * estimatePageHeight;
|
|
565
|
+
rectHeight = targetRect.height / 100 * estimatePageHeight;
|
|
566
|
+
} else {
|
|
567
|
+
rectTop = targetRect.top;
|
|
568
|
+
rectHeight = targetRect.height;
|
|
569
|
+
}
|
|
570
|
+
let scrollOffset;
|
|
571
|
+
if (align === "center") {
|
|
572
|
+
const viewportHeight = virtualizer.scrollElement?.clientHeight || 0;
|
|
573
|
+
const rectCenter = pageStart + rectTop + rectHeight / 2;
|
|
574
|
+
scrollOffset = rectCenter - viewportHeight / 2;
|
|
575
|
+
} else {
|
|
576
|
+
scrollOffset = pageStart + rectTop;
|
|
577
|
+
}
|
|
578
|
+
scrollOffset += additionalOffset;
|
|
579
|
+
const adjustedOffset = Math.max(0, scrollOffset);
|
|
580
|
+
virtualizer.scrollToOffset(adjustedOffset, {
|
|
581
|
+
align: "start",
|
|
582
|
+
// Always use start when we've calculated our own centering
|
|
583
|
+
behavior: "smooth"
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
return { jumpToPage, jumpToOffset, jumpToHighlightRects };
|
|
587
|
+
};
|
|
163
588
|
var LinkService = class {
|
|
164
589
|
_pdfDocumentProxy;
|
|
165
590
|
externalLinkEnabled = true;
|
|
166
591
|
isInPresentationMode = false;
|
|
592
|
+
_currentPageNumber = 0;
|
|
593
|
+
_pageNavigationCallback;
|
|
167
594
|
get pdfDocumentProxy() {
|
|
168
595
|
if (!this._pdfDocumentProxy) {
|
|
169
596
|
throw new Error("pdfDocumentProxy is not set");
|
|
@@ -173,17 +600,66 @@ var LinkService = class {
|
|
|
173
600
|
constructor() {
|
|
174
601
|
}
|
|
175
602
|
get pagesCount() {
|
|
176
|
-
return this.
|
|
603
|
+
return this._pdfDocumentProxy?.numPages || 0;
|
|
604
|
+
}
|
|
605
|
+
get page() {
|
|
606
|
+
return this._currentPageNumber;
|
|
607
|
+
}
|
|
608
|
+
set page(value) {
|
|
609
|
+
this._currentPageNumber = value;
|
|
610
|
+
if (this._pageNavigationCallback) {
|
|
611
|
+
this._pageNavigationCallback(value);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// Required for link annotations to work
|
|
615
|
+
setDocument(pdfDocument) {
|
|
616
|
+
this._pdfDocumentProxy = pdfDocument;
|
|
617
|
+
}
|
|
618
|
+
setViewer() {
|
|
177
619
|
}
|
|
178
620
|
getDestinationHash(dest) {
|
|
179
|
-
return
|
|
621
|
+
if (!dest) return "";
|
|
622
|
+
const destRef = dest[0];
|
|
623
|
+
if (dest.length > 1 && typeof dest[1] === "object" && dest[1] !== null && "url" in dest[1]) {
|
|
624
|
+
const urlDest = dest[1];
|
|
625
|
+
return urlDest.url;
|
|
626
|
+
}
|
|
627
|
+
if (destRef && typeof destRef === "object") {
|
|
628
|
+
if ("num" in destRef) {
|
|
629
|
+
const numRef = destRef;
|
|
630
|
+
return `#page=${numRef.num + 1}`;
|
|
631
|
+
}
|
|
632
|
+
if ("gen" in destRef) {
|
|
633
|
+
const genRef = destRef;
|
|
634
|
+
const refNum = genRef.num ?? 0;
|
|
635
|
+
return `#page=${refNum + 1}`;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (typeof destRef === "number") {
|
|
639
|
+
return `#page=${destRef + 1}`;
|
|
640
|
+
}
|
|
641
|
+
return `#dest-${String(dest)}`;
|
|
180
642
|
}
|
|
181
|
-
getAnchorUrl() {
|
|
182
|
-
|
|
643
|
+
getAnchorUrl(hash) {
|
|
644
|
+
if (hash.startsWith("http://") || hash.startsWith("https://")) {
|
|
645
|
+
return hash;
|
|
646
|
+
}
|
|
647
|
+
return `#${hash}`;
|
|
183
648
|
}
|
|
184
649
|
addLinkAttributes(link, url, newWindow) {
|
|
185
|
-
link
|
|
186
|
-
|
|
650
|
+
if (!link) return;
|
|
651
|
+
const isExternalLink = url.startsWith("http://") || url.startsWith("https://");
|
|
652
|
+
if (isExternalLink && this.externalLinkEnabled) {
|
|
653
|
+
link.href = url;
|
|
654
|
+
link.target = newWindow === false ? "" : "_blank";
|
|
655
|
+
link.rel = "noopener noreferrer";
|
|
656
|
+
} else if (!isExternalLink) {
|
|
657
|
+
link.href = url;
|
|
658
|
+
link.target = "";
|
|
659
|
+
} else {
|
|
660
|
+
link.href = "#";
|
|
661
|
+
link.target = "";
|
|
662
|
+
}
|
|
187
663
|
}
|
|
188
664
|
async goToDestination(dest) {
|
|
189
665
|
let explicitDest;
|
|
@@ -197,18 +673,73 @@ var LinkService = class {
|
|
|
197
673
|
if (!explicitDest) {
|
|
198
674
|
return;
|
|
199
675
|
}
|
|
200
|
-
|
|
201
|
-
|
|
676
|
+
if (explicitDest.length > 1 && typeof explicitDest[1] === "object" && explicitDest[1] !== null && "url" in explicitDest[1]) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const destRef = explicitDest[0];
|
|
680
|
+
let pageIndex;
|
|
681
|
+
if (destRef && typeof destRef === "object") {
|
|
682
|
+
if ("num" in destRef) {
|
|
683
|
+
try {
|
|
684
|
+
const refProxy = destRef;
|
|
685
|
+
pageIndex = await this.pdfDocumentProxy.getPageIndex(refProxy);
|
|
686
|
+
} catch (_error) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
} else {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
} else if (typeof destRef === "number") {
|
|
693
|
+
pageIndex = destRef;
|
|
694
|
+
} else {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const pageNumber = pageIndex + 1;
|
|
698
|
+
if (this._pageNavigationCallback) {
|
|
699
|
+
this._pageNavigationCallback(pageNumber);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
executeNamedAction() {
|
|
703
|
+
}
|
|
704
|
+
navigateTo(dest) {
|
|
705
|
+
this.goToDestination(dest);
|
|
706
|
+
}
|
|
707
|
+
get rotation() {
|
|
708
|
+
return 0;
|
|
709
|
+
}
|
|
710
|
+
set rotation(value) {
|
|
711
|
+
}
|
|
712
|
+
goToPage(pageNumber) {
|
|
713
|
+
if (pageNumber >= 1 && pageNumber <= this.pagesCount) {
|
|
714
|
+
if (this._pageNavigationCallback) {
|
|
715
|
+
this._pageNavigationCallback(pageNumber);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
setHash(hash) {
|
|
720
|
+
if (hash.startsWith("#page=")) {
|
|
721
|
+
const pageNumber = parseInt(hash.substring(6), 10);
|
|
722
|
+
if (!isNaN(pageNumber)) {
|
|
723
|
+
this.goToPage(pageNumber);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
executeSetOCGState() {
|
|
728
|
+
}
|
|
729
|
+
// Method to register navigation callback
|
|
730
|
+
registerPageNavigationCallback(callback) {
|
|
731
|
+
this._pageNavigationCallback = callback;
|
|
732
|
+
}
|
|
733
|
+
// Method to unregister navigation callback
|
|
734
|
+
unregisterPageNavigationCallback() {
|
|
735
|
+
this._pageNavigationCallback = void 0;
|
|
202
736
|
}
|
|
203
737
|
};
|
|
204
|
-
var
|
|
738
|
+
var defaultLinkService = new LinkService();
|
|
739
|
+
var PDFLinkServiceContext = createContext(defaultLinkService);
|
|
205
740
|
var usePDFLinkService = () => {
|
|
206
741
|
return useContext(PDFLinkServiceContext);
|
|
207
742
|
};
|
|
208
|
-
var PDFPageNumberContext = createContext(0);
|
|
209
|
-
var usePDFPageNumber = () => {
|
|
210
|
-
return useContext(PDFPageNumberContext);
|
|
211
|
-
};
|
|
212
743
|
var useVisibility = ({
|
|
213
744
|
elementRef
|
|
214
745
|
}) => {
|
|
@@ -230,9 +761,12 @@ var useVisibility = ({
|
|
|
230
761
|
|
|
231
762
|
// src/hooks/layers/useAnnotationLayer.tsx
|
|
232
763
|
var defaultAnnotationLayerParams = {
|
|
233
|
-
renderForms: true
|
|
764
|
+
renderForms: true,
|
|
765
|
+
externalLinksEnabled: true,
|
|
766
|
+
jumpOptions: { behavior: "smooth", align: "start" }
|
|
234
767
|
};
|
|
235
768
|
var useAnnotationLayer = (params) => {
|
|
769
|
+
const mergedParams = { ...defaultAnnotationLayerParams, ...params };
|
|
236
770
|
const annotationLayerRef = useRef(null);
|
|
237
771
|
const annotationLayerObjectRef = useRef(null);
|
|
238
772
|
const linkService = usePDFLinkService();
|
|
@@ -241,6 +775,83 @@ var useAnnotationLayer = (params) => {
|
|
|
241
775
|
});
|
|
242
776
|
const pageNumber = usePDFPageNumber();
|
|
243
777
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
778
|
+
const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
|
|
779
|
+
useEffect(() => {
|
|
780
|
+
linkService.externalLinkEnabled = mergedParams.externalLinksEnabled;
|
|
781
|
+
}, [linkService, mergedParams.externalLinksEnabled]);
|
|
782
|
+
const { jumpToPage } = usePdfJump();
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
if (!jumpToPage) return;
|
|
785
|
+
const handlePageNavigation = (pageNumber2) => {
|
|
786
|
+
jumpToPage(pageNumber2, mergedParams.jumpOptions);
|
|
787
|
+
};
|
|
788
|
+
linkService.registerPageNavigationCallback(handlePageNavigation);
|
|
789
|
+
return () => {
|
|
790
|
+
linkService.unregisterPageNavigationCallback();
|
|
791
|
+
};
|
|
792
|
+
}, [jumpToPage, linkService, mergedParams.jumpOptions]);
|
|
793
|
+
useEffect(() => {
|
|
794
|
+
const style = document.createElement("style");
|
|
795
|
+
style.textContent = `
|
|
796
|
+
.annotationLayer {
|
|
797
|
+
position: absolute;
|
|
798
|
+
left: 0;
|
|
799
|
+
top: 0;
|
|
800
|
+
right: 0;
|
|
801
|
+
bottom: 0;
|
|
802
|
+
overflow: hidden;
|
|
803
|
+
opacity: 1;
|
|
804
|
+
z-index: 3;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.annotationLayer section {
|
|
808
|
+
position: absolute;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.annotationLayer .linkAnnotation > a,
|
|
812
|
+
.annotationLayer .buttonWidgetAnnotation.pushButton > a {
|
|
813
|
+
position: absolute;
|
|
814
|
+
font-size: 1em;
|
|
815
|
+
top: 0;
|
|
816
|
+
left: 0;
|
|
817
|
+
width: 100%;
|
|
818
|
+
height: 100%;
|
|
819
|
+
background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
|
|
820
|
+
cursor: pointer;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.annotationLayer .linkAnnotation > a:hover,
|
|
824
|
+
.annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
|
|
825
|
+
opacity: 0.2;
|
|
826
|
+
background: rgba(255, 255, 0, 1);
|
|
827
|
+
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
|
828
|
+
}
|
|
829
|
+
`;
|
|
830
|
+
document.head.appendChild(style);
|
|
831
|
+
return () => {
|
|
832
|
+
document.head.removeChild(style);
|
|
833
|
+
};
|
|
834
|
+
}, []);
|
|
835
|
+
useEffect(() => {
|
|
836
|
+
if (!annotationLayerRef.current) return;
|
|
837
|
+
const element = annotationLayerRef.current;
|
|
838
|
+
const handleLinkClick = (e) => {
|
|
839
|
+
if (!e.target || !(e.target instanceof HTMLAnchorElement)) return;
|
|
840
|
+
const target = e.target;
|
|
841
|
+
const href = target.getAttribute("href") || "";
|
|
842
|
+
if (href.startsWith("#page=")) {
|
|
843
|
+
e.preventDefault();
|
|
844
|
+
const pageNumber2 = parseInt(href.substring(6), 10);
|
|
845
|
+
if (!isNaN(pageNumber2)) {
|
|
846
|
+
linkService.goToPage(pageNumber2);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
element.addEventListener("click", handleLinkClick);
|
|
851
|
+
return () => {
|
|
852
|
+
element.removeEventListener("click", handleLinkClick);
|
|
853
|
+
};
|
|
854
|
+
}, [linkService]);
|
|
244
855
|
useEffect(() => {
|
|
245
856
|
if (!annotationLayerRef.current) {
|
|
246
857
|
return;
|
|
@@ -252,14 +863,20 @@ var useAnnotationLayer = (params) => {
|
|
|
252
863
|
}
|
|
253
864
|
}, [visible]);
|
|
254
865
|
useEffect(() => {
|
|
255
|
-
if (!annotationLayerRef.current) {
|
|
866
|
+
if (!annotationLayerRef.current || !pdfPageProxy || !pdfDocumentProxy) {
|
|
256
867
|
return;
|
|
257
868
|
}
|
|
869
|
+
if (linkService._pdfDocumentProxy !== pdfDocumentProxy) {
|
|
870
|
+
linkService.setDocument(pdfDocumentProxy);
|
|
871
|
+
}
|
|
258
872
|
annotationLayerRef.current.innerHTML = "";
|
|
873
|
+
annotationLayerRef.current.className = "annotationLayer";
|
|
874
|
+
const viewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
259
875
|
const annotationLayerConfig = {
|
|
260
876
|
div: annotationLayerRef.current,
|
|
261
|
-
viewport
|
|
877
|
+
viewport,
|
|
262
878
|
page: pdfPageProxy,
|
|
879
|
+
linkService,
|
|
263
880
|
accessibilityManager: void 0,
|
|
264
881
|
annotationCanvasMap: void 0,
|
|
265
882
|
annotationEditorUIManager: void 0,
|
|
@@ -269,33 +886,38 @@ var useAnnotationLayer = (params) => {
|
|
|
269
886
|
annotationLayerObjectRef.current = annotationLayer;
|
|
270
887
|
const { cancel } = cancellable(
|
|
271
888
|
(async () => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
889
|
+
try {
|
|
890
|
+
const annotations = await pdfPageProxy.getAnnotations();
|
|
891
|
+
await annotationLayer.render({
|
|
892
|
+
...annotationLayerConfig,
|
|
893
|
+
...mergedParams,
|
|
894
|
+
annotations,
|
|
895
|
+
linkService
|
|
896
|
+
});
|
|
897
|
+
} catch (_error) {
|
|
898
|
+
}
|
|
280
899
|
})()
|
|
281
900
|
);
|
|
282
|
-
cancel();
|
|
283
901
|
return () => {
|
|
284
902
|
cancel();
|
|
285
903
|
};
|
|
286
|
-
}, [pdfPageProxy,
|
|
904
|
+
}, [pdfPageProxy, pdfDocumentProxy, mergedParams, linkService]);
|
|
287
905
|
return {
|
|
288
906
|
annotationLayerRef
|
|
289
907
|
};
|
|
290
908
|
};
|
|
291
909
|
var AnnotationLayer2 = ({
|
|
292
910
|
renderForms = true,
|
|
911
|
+
externalLinksEnabled = true,
|
|
912
|
+
jumpOptions = { behavior: "smooth", align: "start" },
|
|
293
913
|
className,
|
|
294
914
|
style,
|
|
295
915
|
...props
|
|
296
916
|
}) => {
|
|
297
917
|
const { annotationLayerRef } = useAnnotationLayer({
|
|
298
|
-
renderForms
|
|
918
|
+
renderForms,
|
|
919
|
+
externalLinksEnabled,
|
|
920
|
+
jumpOptions
|
|
299
921
|
});
|
|
300
922
|
return /* @__PURE__ */ jsx(
|
|
301
923
|
"div",
|
|
@@ -374,27 +996,442 @@ var useCanvasLayer = ({ background }) => {
|
|
|
374
996
|
canvasRef
|
|
375
997
|
};
|
|
376
998
|
};
|
|
377
|
-
var CanvasLayer = ({
|
|
378
|
-
style,
|
|
379
|
-
background,
|
|
380
|
-
...props
|
|
999
|
+
var CanvasLayer = ({
|
|
1000
|
+
style,
|
|
1001
|
+
background,
|
|
1002
|
+
...props
|
|
1003
|
+
}) => {
|
|
1004
|
+
const { canvasRef } = useCanvasLayer({ background });
|
|
1005
|
+
return /* @__PURE__ */ jsx(
|
|
1006
|
+
"canvas",
|
|
1007
|
+
{
|
|
1008
|
+
style: {
|
|
1009
|
+
...style,
|
|
1010
|
+
position: "absolute",
|
|
1011
|
+
top: 0,
|
|
1012
|
+
left: 0,
|
|
1013
|
+
width: "100%",
|
|
1014
|
+
height: "100%"
|
|
1015
|
+
},
|
|
1016
|
+
...props,
|
|
1017
|
+
ref: canvasRef
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
// src/hooks/useSelectionDimensions.tsx
|
|
1023
|
+
var MERGE_THRESHOLD = 20;
|
|
1024
|
+
var shouldMergeRectangles = (rect1, rect2) => {
|
|
1025
|
+
const verticalOverlap = !(rect1.top > rect2.top + rect2.height || rect2.top > rect1.top + rect1.height);
|
|
1026
|
+
const horizontallyClose = Math.abs(rect1.left + rect1.width - rect2.left) < MERGE_THRESHOLD || Math.abs(rect2.left + rect2.width - rect1.left) < MERGE_THRESHOLD || rect1.left < rect2.left + rect2.width && rect2.left < rect1.left + rect1.width;
|
|
1027
|
+
return verticalOverlap && horizontallyClose;
|
|
1028
|
+
};
|
|
1029
|
+
var mergeRectangles = (rect1, rect2) => {
|
|
1030
|
+
return {
|
|
1031
|
+
left: Math.min(rect1.left, rect2.left),
|
|
1032
|
+
top: Math.min(rect1.top, rect2.top),
|
|
1033
|
+
width: Math.max(rect1.left + rect1.width, rect2.left + rect2.width) - Math.min(rect1.left, rect2.left),
|
|
1034
|
+
height: Math.max(rect1.top + rect1.height, rect2.top + rect2.height) - Math.min(rect1.top, rect2.top),
|
|
1035
|
+
pageNumber: rect1.pageNumber
|
|
1036
|
+
};
|
|
1037
|
+
};
|
|
1038
|
+
var consolidateRects = (rects) => {
|
|
1039
|
+
if (rects.length <= 1) return rects;
|
|
1040
|
+
const sortedRectangles = rects.toSorted((a, b) => a.top - b.top);
|
|
1041
|
+
let withinThresholdMergedRectangles = sortedRectangles[0];
|
|
1042
|
+
const allMergedRectangles = [];
|
|
1043
|
+
const blowUpMergedRectangles = (rect) => {
|
|
1044
|
+
return {
|
|
1045
|
+
...rect,
|
|
1046
|
+
left: rect.left - 2,
|
|
1047
|
+
top: rect.top - 2,
|
|
1048
|
+
width: rect.width + 4,
|
|
1049
|
+
height: rect.height + 2
|
|
1050
|
+
};
|
|
1051
|
+
};
|
|
1052
|
+
for (const [index, currentRectangle] of sortedRectangles.entries()) {
|
|
1053
|
+
withinThresholdMergedRectangles = mergeRectangles(
|
|
1054
|
+
withinThresholdMergedRectangles,
|
|
1055
|
+
currentRectangle
|
|
1056
|
+
);
|
|
1057
|
+
const nextRectangle = sortedRectangles[index + 1];
|
|
1058
|
+
if (!nextRectangle) {
|
|
1059
|
+
allMergedRectangles.push(
|
|
1060
|
+
blowUpMergedRectangles(withinThresholdMergedRectangles)
|
|
1061
|
+
);
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
if (!shouldMergeRectangles(withinThresholdMergedRectangles, nextRectangle)) {
|
|
1065
|
+
allMergedRectangles.push(
|
|
1066
|
+
blowUpMergedRectangles(withinThresholdMergedRectangles)
|
|
1067
|
+
);
|
|
1068
|
+
withinThresholdMergedRectangles = nextRectangle;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
return allMergedRectangles;
|
|
1072
|
+
};
|
|
1073
|
+
var useSelectionDimensions = () => {
|
|
1074
|
+
const store = PDFStore.useContext();
|
|
1075
|
+
const getDimension = () => {
|
|
1076
|
+
const selection = window.getSelection();
|
|
1077
|
+
if (!selection || selection.isCollapsed) return;
|
|
1078
|
+
const range = selection.getRangeAt(0);
|
|
1079
|
+
const highlights = [];
|
|
1080
|
+
const textLayerMap = /* @__PURE__ */ new Map();
|
|
1081
|
+
const clientRects = Array.from(range.getClientRects()).filter(
|
|
1082
|
+
(rect) => rect.width > 2 && rect.height > 2
|
|
1083
|
+
);
|
|
1084
|
+
clientRects.forEach((clientRect) => {
|
|
1085
|
+
const element = document.elementFromPoint(
|
|
1086
|
+
clientRect.left + 1,
|
|
1087
|
+
clientRect.top + clientRect.height / 2
|
|
1088
|
+
);
|
|
1089
|
+
const textLayer = element?.closest(".textLayer");
|
|
1090
|
+
if (!textLayer) return;
|
|
1091
|
+
const pageNumber = parseInt(
|
|
1092
|
+
textLayer.getAttribute("data-page-number") || "1",
|
|
1093
|
+
10
|
|
1094
|
+
);
|
|
1095
|
+
const textLayerRect = textLayer.getBoundingClientRect();
|
|
1096
|
+
const zoom = store.getState().zoom;
|
|
1097
|
+
const rect = {
|
|
1098
|
+
width: clientRect.width / zoom,
|
|
1099
|
+
height: clientRect.height / zoom,
|
|
1100
|
+
top: (clientRect.top - textLayerRect.top) / zoom,
|
|
1101
|
+
left: (clientRect.left - textLayerRect.left) / zoom,
|
|
1102
|
+
pageNumber
|
|
1103
|
+
};
|
|
1104
|
+
if (!textLayerMap.has(pageNumber)) {
|
|
1105
|
+
textLayerMap.set(pageNumber, []);
|
|
1106
|
+
}
|
|
1107
|
+
textLayerMap.get(pageNumber)?.push(rect);
|
|
1108
|
+
});
|
|
1109
|
+
textLayerMap.forEach((rects) => {
|
|
1110
|
+
if (rects.length > 0) {
|
|
1111
|
+
highlights.push(...consolidateRects(rects));
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
return {
|
|
1115
|
+
highlights: highlights.sort((a, b) => a.pageNumber - b.pageNumber),
|
|
1116
|
+
text: range.toString().trim(),
|
|
1117
|
+
isCollapsed: false
|
|
1118
|
+
};
|
|
1119
|
+
};
|
|
1120
|
+
const getSelection = () => getDimension();
|
|
1121
|
+
return { getDimension, getSelection };
|
|
1122
|
+
};
|
|
1123
|
+
var SelectionTooltip = ({ children }) => {
|
|
1124
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
1125
|
+
const lastSelectionRef = useRef(null);
|
|
1126
|
+
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1127
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
1128
|
+
placement: "bottom",
|
|
1129
|
+
open: isOpen,
|
|
1130
|
+
onOpenChange: setIsOpen,
|
|
1131
|
+
whileElementsMounted: autoUpdate,
|
|
1132
|
+
middleware: [offset(10), shift({ padding: 8 })]
|
|
1133
|
+
});
|
|
1134
|
+
const dismiss = useDismiss(context);
|
|
1135
|
+
const { getFloatingProps } = useInteractions([dismiss]);
|
|
1136
|
+
const updateTooltipPosition = useCallback(() => {
|
|
1137
|
+
const selection = document.getSelection();
|
|
1138
|
+
if (!selection || selection.isCollapsed) {
|
|
1139
|
+
setIsOpen(false);
|
|
1140
|
+
lastSelectionRef.current = null;
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const range = selection.getRangeAt(0);
|
|
1144
|
+
if (!range) return;
|
|
1145
|
+
const rects = range.getClientRects();
|
|
1146
|
+
const lastRect = rects[rects.length - 1];
|
|
1147
|
+
lastSelectionRef.current = range;
|
|
1148
|
+
if (lastRect) {
|
|
1149
|
+
refs.setReference({
|
|
1150
|
+
getBoundingClientRect: () => ({
|
|
1151
|
+
width: lastRect.width,
|
|
1152
|
+
height: lastRect.height,
|
|
1153
|
+
x: lastRect.left,
|
|
1154
|
+
y: lastRect.bottom,
|
|
1155
|
+
// Position below the last line of selection
|
|
1156
|
+
top: lastRect.bottom,
|
|
1157
|
+
right: lastRect.right,
|
|
1158
|
+
bottom: lastRect.bottom + lastRect.height,
|
|
1159
|
+
left: lastRect.left
|
|
1160
|
+
}),
|
|
1161
|
+
getClientRects: () => [lastRect]
|
|
1162
|
+
});
|
|
1163
|
+
setIsOpen(true);
|
|
1164
|
+
} else {
|
|
1165
|
+
setIsOpen(false);
|
|
1166
|
+
}
|
|
1167
|
+
}, [refs]);
|
|
1168
|
+
useEffect(() => {
|
|
1169
|
+
const handleSelectionChange = () => {
|
|
1170
|
+
const selection = document.getSelection();
|
|
1171
|
+
if (selection && viewportRef.current?.contains(selection.anchorNode)) {
|
|
1172
|
+
requestAnimationFrame(updateTooltipPosition);
|
|
1173
|
+
} else {
|
|
1174
|
+
setIsOpen(false);
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
const handleScroll = () => {
|
|
1178
|
+
if (!isOpen || !lastSelectionRef.current) return;
|
|
1179
|
+
requestAnimationFrame(updateTooltipPosition);
|
|
1180
|
+
};
|
|
1181
|
+
document.addEventListener("selectionchange", handleSelectionChange);
|
|
1182
|
+
if (viewportRef.current) {
|
|
1183
|
+
viewportRef.current.addEventListener("scroll", handleScroll, {
|
|
1184
|
+
passive: true
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
return () => {
|
|
1188
|
+
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
1189
|
+
if (viewportRef.current) {
|
|
1190
|
+
viewportRef.current.removeEventListener("scroll", handleScroll);
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
}, [refs, isOpen, viewportRef, updateTooltipPosition]);
|
|
1194
|
+
useEffect(() => {
|
|
1195
|
+
const handleFloatingClick = (e) => {
|
|
1196
|
+
if (refs.floating.current?.contains(e.target)) {
|
|
1197
|
+
e.stopPropagation();
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
document.addEventListener("click", handleFloatingClick);
|
|
1201
|
+
return () => document.removeEventListener("click", handleFloatingClick);
|
|
1202
|
+
}, [refs.floating]);
|
|
1203
|
+
return /* @__PURE__ */ jsx(Fragment, { children: isOpen && /* @__PURE__ */ jsx(
|
|
1204
|
+
"div",
|
|
1205
|
+
{
|
|
1206
|
+
ref: refs.setFloating,
|
|
1207
|
+
style: {
|
|
1208
|
+
...floatingStyles
|
|
1209
|
+
},
|
|
1210
|
+
...getFloatingProps(),
|
|
1211
|
+
children
|
|
1212
|
+
}
|
|
1213
|
+
) });
|
|
1214
|
+
};
|
|
1215
|
+
var defaultColors = [
|
|
1216
|
+
{
|
|
1217
|
+
color: "#e3b127",
|
|
1218
|
+
localization: {
|
|
1219
|
+
id: "yellow",
|
|
1220
|
+
defaultMessage: "Yellow"
|
|
1221
|
+
}
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
color: "#419931",
|
|
1225
|
+
localization: {
|
|
1226
|
+
id: "green",
|
|
1227
|
+
defaultMessage: "Green"
|
|
1228
|
+
}
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
color: "#4286c9",
|
|
1232
|
+
localization: {
|
|
1233
|
+
id: "blue",
|
|
1234
|
+
defaultMessage: "Blue"
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
color: "#f246b6",
|
|
1239
|
+
localization: {
|
|
1240
|
+
id: "pink",
|
|
1241
|
+
defaultMessage: "Pink"
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
color: "#a53dd1",
|
|
1246
|
+
localization: {
|
|
1247
|
+
id: "purple",
|
|
1248
|
+
defaultMessage: "Purple"
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
color: "#f09037",
|
|
1253
|
+
localization: {
|
|
1254
|
+
id: "orange",
|
|
1255
|
+
defaultMessage: "Orange"
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
color: "#37f0d4",
|
|
1260
|
+
localization: {
|
|
1261
|
+
id: "teal",
|
|
1262
|
+
defaultMessage: "Teal"
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
color: "#3d0ff5",
|
|
1267
|
+
localization: {
|
|
1268
|
+
id: "purple",
|
|
1269
|
+
defaultMessage: "Purple"
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
color: "#f50f26",
|
|
1274
|
+
localization: {
|
|
1275
|
+
id: "red",
|
|
1276
|
+
defaultMessage: "Red"
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
];
|
|
1280
|
+
var ColorSelectionTool = ({
|
|
1281
|
+
highlighterColors = defaultColors,
|
|
1282
|
+
onColorSelection
|
|
1283
|
+
}) => {
|
|
1284
|
+
return /* @__PURE__ */ jsx(SelectionTooltip, { children: /* @__PURE__ */ jsx(
|
|
1285
|
+
"div",
|
|
1286
|
+
{
|
|
1287
|
+
style: {
|
|
1288
|
+
display: "flex",
|
|
1289
|
+
gap: "0.5rem",
|
|
1290
|
+
padding: "0.5rem",
|
|
1291
|
+
backgroundColor: "#363636",
|
|
1292
|
+
borderRadius: "0.5rem"
|
|
1293
|
+
},
|
|
1294
|
+
children: highlighterColors.map((colorItem, index) => /* @__PURE__ */ jsx(
|
|
1295
|
+
"button",
|
|
1296
|
+
{
|
|
1297
|
+
onClick: () => onColorSelection(colorItem),
|
|
1298
|
+
title: colorItem.localization.defaultMessage,
|
|
1299
|
+
"aria-label": colorItem.localization.defaultMessage,
|
|
1300
|
+
style: {
|
|
1301
|
+
width: "1.25rem",
|
|
1302
|
+
height: "1.25rem",
|
|
1303
|
+
borderRadius: "0.25rem",
|
|
1304
|
+
cursor: "pointer",
|
|
1305
|
+
backgroundColor: colorItem.color
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
index
|
|
1309
|
+
))
|
|
1310
|
+
}
|
|
1311
|
+
) });
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
// src/utils/selectionUtils.ts
|
|
1315
|
+
var getEndOfHighlight = (selection) => {
|
|
1316
|
+
const lastRectangle = selection.rectangles[selection.rectangles.length - 1];
|
|
1317
|
+
return lastRectangle.left + lastRectangle.width + 10;
|
|
1318
|
+
};
|
|
1319
|
+
var getMidHeightOfHighlightLine = (selection) => {
|
|
1320
|
+
const lastRectangle = selection.rectangles[selection.rectangles.length - 1];
|
|
1321
|
+
return lastRectangle.top + lastRectangle.height / 2;
|
|
1322
|
+
};
|
|
1323
|
+
var ColoredHighlightComponent = ({
|
|
1324
|
+
selection
|
|
1325
|
+
}) => {
|
|
1326
|
+
const deleteColoredHighlight = usePdf(
|
|
1327
|
+
(state) => state.deleteColoredHighlight
|
|
1328
|
+
);
|
|
1329
|
+
const [showButton, setShowButton] = useState(false);
|
|
1330
|
+
return /* @__PURE__ */ jsxs("div", { className: "colored-highlight", children: [
|
|
1331
|
+
selection.rectangles.map((rect, index) => /* @__PURE__ */ jsx(
|
|
1332
|
+
"span",
|
|
1333
|
+
{
|
|
1334
|
+
onClick: () => setShowButton(!showButton),
|
|
1335
|
+
style: {
|
|
1336
|
+
position: "absolute",
|
|
1337
|
+
top: rect.top,
|
|
1338
|
+
left: rect.left,
|
|
1339
|
+
height: rect.height,
|
|
1340
|
+
width: rect.width,
|
|
1341
|
+
cursor: "pointer",
|
|
1342
|
+
zIndex: 30,
|
|
1343
|
+
backgroundColor: selection.color,
|
|
1344
|
+
// mixBlendMode: "lighten", // changes the color of the text
|
|
1345
|
+
mixBlendMode: "darken",
|
|
1346
|
+
// best results
|
|
1347
|
+
// mixBlendMode: "multiply", // works but coloring has some inconsistencies
|
|
1348
|
+
borderRadius: "0.2rem"
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
`${selection.uuid}-${index}`
|
|
1352
|
+
)),
|
|
1353
|
+
showButton && /* @__PURE__ */ jsx(
|
|
1354
|
+
"button",
|
|
1355
|
+
{
|
|
1356
|
+
style: {
|
|
1357
|
+
backgroundColor: "white",
|
|
1358
|
+
color: "white",
|
|
1359
|
+
borderRadius: "5px",
|
|
1360
|
+
padding: "5px",
|
|
1361
|
+
cursor: "pointer",
|
|
1362
|
+
boxShadow: "2px 2px 5px black",
|
|
1363
|
+
position: "absolute",
|
|
1364
|
+
top: getMidHeightOfHighlightLine(selection),
|
|
1365
|
+
left: getEndOfHighlight(selection),
|
|
1366
|
+
zIndex: 30,
|
|
1367
|
+
transform: "translateY(-50%)"
|
|
1368
|
+
},
|
|
1369
|
+
onClick: () => deleteColoredHighlight(selection.uuid),
|
|
1370
|
+
children: /* @__PURE__ */ jsx(
|
|
1371
|
+
"svg",
|
|
1372
|
+
{
|
|
1373
|
+
fill: "#000000",
|
|
1374
|
+
version: "1.1",
|
|
1375
|
+
id: "Capa_1",
|
|
1376
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1377
|
+
width: "15px",
|
|
1378
|
+
height: "15px",
|
|
1379
|
+
viewBox: "0 0 485 485",
|
|
1380
|
+
children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsxs("g", { children: [
|
|
1381
|
+
/* @__PURE__ */ jsx("rect", { x: "67.224", width: "350.535", height: "71.81" }),
|
|
1382
|
+
/* @__PURE__ */ jsx(
|
|
1383
|
+
"path",
|
|
1384
|
+
{
|
|
1385
|
+
d: "M417.776,92.829H67.237V485h350.537V92.829H417.776z M165.402,431.447h-28.362V146.383h28.362V431.447z M256.689,431.447\n h-28.363V146.383h28.363V431.447z M347.97,431.447h-28.361V146.383h28.361V431.447z"
|
|
1386
|
+
}
|
|
1387
|
+
)
|
|
1388
|
+
] }) })
|
|
1389
|
+
}
|
|
1390
|
+
)
|
|
1391
|
+
},
|
|
1392
|
+
`${selection.uuid}-delete-button`
|
|
1393
|
+
)
|
|
1394
|
+
] });
|
|
1395
|
+
};
|
|
1396
|
+
var ColoredHighlightLayer = ({
|
|
1397
|
+
onHighlight
|
|
381
1398
|
}) => {
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
style: {
|
|
387
|
-
...style,
|
|
388
|
-
position: "absolute",
|
|
389
|
-
top: 0,
|
|
390
|
-
left: 0,
|
|
391
|
-
width: "100%",
|
|
392
|
-
height: "100%"
|
|
393
|
-
},
|
|
394
|
-
...props,
|
|
395
|
-
ref: canvasRef
|
|
396
|
-
}
|
|
1399
|
+
const pageNumber = usePDFPageNumber();
|
|
1400
|
+
const { getSelection } = useSelectionDimensions();
|
|
1401
|
+
const highlights = usePdf(
|
|
1402
|
+
(state) => state.coloredHighlights
|
|
397
1403
|
);
|
|
1404
|
+
const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
|
|
1405
|
+
const handleHighlighting = useCallback((color) => {
|
|
1406
|
+
const { highlights: highlights2, text } = getSelection();
|
|
1407
|
+
if (highlights2[0]) {
|
|
1408
|
+
const highlight = {
|
|
1409
|
+
uuid: v4(),
|
|
1410
|
+
pageNumber: highlights2[0].pageNumber,
|
|
1411
|
+
// usePDFPageNumber() doesn't return the correct page number, so i'm getting the number directly from the first highlight
|
|
1412
|
+
color,
|
|
1413
|
+
rectangles: highlights2,
|
|
1414
|
+
text
|
|
1415
|
+
};
|
|
1416
|
+
addColoredHighlight(highlight);
|
|
1417
|
+
if (onHighlight) onHighlight(highlight);
|
|
1418
|
+
}
|
|
1419
|
+
}, []);
|
|
1420
|
+
return /* @__PURE__ */ jsxs("div", { className: "colored-highlights-layer", children: [
|
|
1421
|
+
highlights.filter((selection) => selection.pageNumber === pageNumber).map((selection) => /* @__PURE__ */ jsx(
|
|
1422
|
+
ColoredHighlightComponent,
|
|
1423
|
+
{
|
|
1424
|
+
selection
|
|
1425
|
+
},
|
|
1426
|
+
selection.uuid
|
|
1427
|
+
)),
|
|
1428
|
+
/* @__PURE__ */ jsx(
|
|
1429
|
+
ColorSelectionTool,
|
|
1430
|
+
{
|
|
1431
|
+
onColorSelection: (colorItem) => handleHighlighting(colorItem.color)
|
|
1432
|
+
}
|
|
1433
|
+
)
|
|
1434
|
+
] });
|
|
398
1435
|
};
|
|
399
1436
|
|
|
400
1437
|
// src/components/layers/custom-layer.tsx
|
|
@@ -491,68 +1528,6 @@ var TextLayer2 = ({
|
|
|
491
1528
|
}
|
|
492
1529
|
);
|
|
493
1530
|
};
|
|
494
|
-
|
|
495
|
-
// src/hooks/pages/usePdfJump.tsx
|
|
496
|
-
var usePdfJump = () => {
|
|
497
|
-
const virtualizer = usePdf((state) => state.virtualizer);
|
|
498
|
-
const setHighlight = usePdf((state) => state.setHighlight);
|
|
499
|
-
const jumpToPage = (pageIndex, options) => {
|
|
500
|
-
if (!virtualizer) return;
|
|
501
|
-
const defaultOptions = {
|
|
502
|
-
align: "start",
|
|
503
|
-
behavior: "smooth"
|
|
504
|
-
};
|
|
505
|
-
const finalOptions = { ...defaultOptions, ...options };
|
|
506
|
-
virtualizer.scrollToIndex(pageIndex - 1, finalOptions);
|
|
507
|
-
};
|
|
508
|
-
const jumpToOffset = (offset2) => {
|
|
509
|
-
if (!virtualizer) return;
|
|
510
|
-
virtualizer.scrollToOffset(offset2, {
|
|
511
|
-
align: "start",
|
|
512
|
-
behavior: "smooth"
|
|
513
|
-
});
|
|
514
|
-
};
|
|
515
|
-
const jumpToHighlightRects = (rects, type, align = "start", additionalOffset = 0) => {
|
|
516
|
-
if (!virtualizer) return;
|
|
517
|
-
setHighlight(rects);
|
|
518
|
-
const firstPage = Math.min(...rects.map((rect) => rect.pageNumber));
|
|
519
|
-
const pageOffset = virtualizer.getOffsetForIndex(firstPage - 1, "start");
|
|
520
|
-
if (pageOffset === null) return;
|
|
521
|
-
const targetRect = rects.find((rect) => rect.pageNumber === firstPage);
|
|
522
|
-
if (!targetRect) return;
|
|
523
|
-
const isNumber = pageOffset?.[0] != null;
|
|
524
|
-
if (!isNumber) return;
|
|
525
|
-
const pageStart = pageOffset[0] ?? 0;
|
|
526
|
-
let rectTop;
|
|
527
|
-
let rectHeight;
|
|
528
|
-
if (type === "percent") {
|
|
529
|
-
const estimatePageHeight = virtualizer.options.estimateSize(
|
|
530
|
-
firstPage - 1
|
|
531
|
-
);
|
|
532
|
-
rectTop = targetRect.top / 100 * estimatePageHeight;
|
|
533
|
-
rectHeight = targetRect.height / 100 * estimatePageHeight;
|
|
534
|
-
} else {
|
|
535
|
-
rectTop = targetRect.top;
|
|
536
|
-
rectHeight = targetRect.height;
|
|
537
|
-
}
|
|
538
|
-
let scrollOffset;
|
|
539
|
-
if (align === "center") {
|
|
540
|
-
const viewportHeight = virtualizer.scrollElement?.clientHeight || 0;
|
|
541
|
-
const rectCenter = pageStart + rectTop + rectHeight / 2;
|
|
542
|
-
scrollOffset = rectCenter - viewportHeight / 2;
|
|
543
|
-
} else {
|
|
544
|
-
scrollOffset = pageStart + rectTop;
|
|
545
|
-
}
|
|
546
|
-
scrollOffset += additionalOffset;
|
|
547
|
-
const adjustedOffset = Math.max(0, scrollOffset);
|
|
548
|
-
virtualizer.scrollToOffset(adjustedOffset, {
|
|
549
|
-
align: "start",
|
|
550
|
-
// Always use start when we've calculated our own centering
|
|
551
|
-
behavior: "smooth"
|
|
552
|
-
});
|
|
553
|
-
};
|
|
554
|
-
return { jumpToPage, jumpToOffset, jumpToHighlightRects };
|
|
555
|
-
};
|
|
556
1531
|
var usePDFOutline = () => {
|
|
557
1532
|
const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
|
|
558
1533
|
const [outline, setOutline] = useState();
|
|
@@ -710,6 +1685,7 @@ var Page = ({
|
|
|
710
1685
|
style: {
|
|
711
1686
|
...style,
|
|
712
1687
|
"--scale-factor": 1,
|
|
1688
|
+
"--total-scale-factor": 1,
|
|
713
1689
|
position: "relative",
|
|
714
1690
|
width,
|
|
715
1691
|
height
|
|
@@ -816,21 +1792,21 @@ var useObserveElement = () => {
|
|
|
816
1792
|
if (!targetWindow) {
|
|
817
1793
|
return;
|
|
818
1794
|
}
|
|
819
|
-
let
|
|
1795
|
+
let offset3 = 0;
|
|
820
1796
|
const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
|
|
821
1797
|
targetWindow,
|
|
822
1798
|
() => {
|
|
823
|
-
cb(
|
|
1799
|
+
cb(offset3, false);
|
|
824
1800
|
},
|
|
825
1801
|
instance.options.isScrollingResetDelay
|
|
826
1802
|
);
|
|
827
1803
|
const createHandler = (isScrolling) => () => {
|
|
828
1804
|
const { horizontal, isRtl } = instance.options;
|
|
829
|
-
|
|
1805
|
+
offset3 = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
|
|
830
1806
|
const zoom = store.getState().zoom;
|
|
831
|
-
|
|
1807
|
+
offset3 = offset3 / zoom;
|
|
832
1808
|
fallback();
|
|
833
|
-
cb(
|
|
1809
|
+
cb(offset3, isScrolling);
|
|
834
1810
|
};
|
|
835
1811
|
const handler = createHandler(true);
|
|
836
1812
|
const endHandler = createHandler(false);
|
|
@@ -851,8 +1827,8 @@ var useScrollFn = () => {
|
|
|
851
1827
|
const scrollToFn = useCallback(
|
|
852
1828
|
(_offset, canSmooth, instance) => {
|
|
853
1829
|
const zoom = store.getState().zoom;
|
|
854
|
-
const
|
|
855
|
-
elementScroll(
|
|
1830
|
+
const offset3 = _offset * zoom;
|
|
1831
|
+
elementScroll(offset3, canSmooth, instance);
|
|
856
1832
|
},
|
|
857
1833
|
[store]
|
|
858
1834
|
);
|
|
@@ -1967,10 +2943,10 @@ var PinchEngine = class extends Engine {
|
|
|
1967
2943
|
}
|
|
1968
2944
|
computeMovement() {
|
|
1969
2945
|
const {
|
|
1970
|
-
offset:
|
|
2946
|
+
offset: offset3,
|
|
1971
2947
|
lastOffset
|
|
1972
2948
|
} = this.state;
|
|
1973
|
-
this.state.movement = [
|
|
2949
|
+
this.state.movement = [offset3[0] / lastOffset[0], offset3[1] - lastOffset[1]];
|
|
1974
2950
|
}
|
|
1975
2951
|
axisIntent() {
|
|
1976
2952
|
const state = this.state;
|
|
@@ -2955,12 +3931,10 @@ var Pages = ({
|
|
|
2955
3931
|
toOffset -= size;
|
|
2956
3932
|
}
|
|
2957
3933
|
const scrollSizeProp = virtualizer.options.horizontal ? "scrollWidth" : "scrollHeight";
|
|
2958
|
-
|
|
3934
|
+
virtualizer.scrollElement ? "document" in virtualizer.scrollElement ? (
|
|
2959
3935
|
//@ts-expect-error this is a private stuff
|
|
2960
3936
|
virtualizer.scrollElement.document.documentElement[scrollSizeProp]
|
|
2961
3937
|
) : virtualizer.scrollElement[scrollSizeProp] : 0;
|
|
2962
|
-
const maxOffset = scrollSize - size;
|
|
2963
|
-
console.log(scrollSize, maxOffset, toOffset);
|
|
2964
3938
|
return Math.max(toOffset, 0);
|
|
2965
3939
|
};
|
|
2966
3940
|
}, [zoom, virtualizer]);
|
|
@@ -3153,98 +4127,6 @@ var Search = ({ children, loading = "Loading..." }) => {
|
|
|
3153
4127
|
}, [proxies, getTextContent]);
|
|
3154
4128
|
return isLoading ? loading : children;
|
|
3155
4129
|
};
|
|
3156
|
-
var SelectionTooltip = ({ children }) => {
|
|
3157
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
3158
|
-
const lastSelectionRef = useRef(null);
|
|
3159
|
-
const viewportRef = usePdf((state) => state.viewportRef);
|
|
3160
|
-
const { refs, floatingStyles, context } = useFloating({
|
|
3161
|
-
placement: "bottom",
|
|
3162
|
-
open: isOpen,
|
|
3163
|
-
onOpenChange: setIsOpen,
|
|
3164
|
-
whileElementsMounted: autoUpdate,
|
|
3165
|
-
middleware: [offset(10), shift({ padding: 8 })]
|
|
3166
|
-
});
|
|
3167
|
-
const dismiss = useDismiss(context);
|
|
3168
|
-
const { getFloatingProps } = useInteractions([dismiss]);
|
|
3169
|
-
const updateTooltipPosition = useCallback(() => {
|
|
3170
|
-
const selection = document.getSelection();
|
|
3171
|
-
if (!selection || selection.isCollapsed) {
|
|
3172
|
-
setIsOpen(false);
|
|
3173
|
-
lastSelectionRef.current = null;
|
|
3174
|
-
return;
|
|
3175
|
-
}
|
|
3176
|
-
const range = selection.getRangeAt(0);
|
|
3177
|
-
if (!range) return;
|
|
3178
|
-
const rects = range.getClientRects();
|
|
3179
|
-
const lastRect = rects[rects.length - 1];
|
|
3180
|
-
lastSelectionRef.current = range;
|
|
3181
|
-
if (lastRect) {
|
|
3182
|
-
refs.setReference({
|
|
3183
|
-
getBoundingClientRect: () => ({
|
|
3184
|
-
width: lastRect.width,
|
|
3185
|
-
height: lastRect.height,
|
|
3186
|
-
x: lastRect.left,
|
|
3187
|
-
y: lastRect.bottom,
|
|
3188
|
-
// Position below the last line of selection
|
|
3189
|
-
top: lastRect.bottom,
|
|
3190
|
-
right: lastRect.right,
|
|
3191
|
-
bottom: lastRect.bottom + lastRect.height,
|
|
3192
|
-
left: lastRect.left
|
|
3193
|
-
}),
|
|
3194
|
-
getClientRects: () => [lastRect]
|
|
3195
|
-
});
|
|
3196
|
-
setIsOpen(true);
|
|
3197
|
-
} else {
|
|
3198
|
-
setIsOpen(false);
|
|
3199
|
-
}
|
|
3200
|
-
}, [refs]);
|
|
3201
|
-
useEffect(() => {
|
|
3202
|
-
const handleSelectionChange = () => {
|
|
3203
|
-
const selection = document.getSelection();
|
|
3204
|
-
if (selection && viewportRef.current?.contains(selection.anchorNode)) {
|
|
3205
|
-
requestAnimationFrame(updateTooltipPosition);
|
|
3206
|
-
} else {
|
|
3207
|
-
setIsOpen(false);
|
|
3208
|
-
}
|
|
3209
|
-
};
|
|
3210
|
-
const handleScroll = () => {
|
|
3211
|
-
if (!isOpen || !lastSelectionRef.current) return;
|
|
3212
|
-
requestAnimationFrame(updateTooltipPosition);
|
|
3213
|
-
};
|
|
3214
|
-
document.addEventListener("selectionchange", handleSelectionChange);
|
|
3215
|
-
if (viewportRef.current) {
|
|
3216
|
-
viewportRef.current.addEventListener("scroll", handleScroll, {
|
|
3217
|
-
passive: true
|
|
3218
|
-
});
|
|
3219
|
-
}
|
|
3220
|
-
return () => {
|
|
3221
|
-
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
3222
|
-
if (viewportRef.current) {
|
|
3223
|
-
viewportRef.current.removeEventListener("scroll", handleScroll);
|
|
3224
|
-
}
|
|
3225
|
-
};
|
|
3226
|
-
}, [refs, isOpen, viewportRef, updateTooltipPosition]);
|
|
3227
|
-
useEffect(() => {
|
|
3228
|
-
const handleFloatingClick = (e) => {
|
|
3229
|
-
if (refs.floating.current?.contains(e.target)) {
|
|
3230
|
-
e.stopPropagation();
|
|
3231
|
-
}
|
|
3232
|
-
};
|
|
3233
|
-
document.addEventListener("click", handleFloatingClick);
|
|
3234
|
-
return () => document.removeEventListener("click", handleFloatingClick);
|
|
3235
|
-
}, [refs.floating]);
|
|
3236
|
-
return /* @__PURE__ */ jsx(Fragment, { children: isOpen && /* @__PURE__ */ jsx(
|
|
3237
|
-
"div",
|
|
3238
|
-
{
|
|
3239
|
-
ref: refs.setFloating,
|
|
3240
|
-
style: {
|
|
3241
|
-
...floatingStyles
|
|
3242
|
-
},
|
|
3243
|
-
...getFloatingProps(),
|
|
3244
|
-
children
|
|
3245
|
-
}
|
|
3246
|
-
) });
|
|
3247
|
-
};
|
|
3248
4130
|
var DEFAULT_CONFIG = {
|
|
3249
4131
|
maxHeight: 800,
|
|
3250
4132
|
maxWidth: 400
|
|
@@ -3444,7 +4326,8 @@ var useSearch = () => {
|
|
|
3444
4326
|
text: text.substr(matchIndex, searchText.length + textSize),
|
|
3445
4327
|
score: 1,
|
|
3446
4328
|
matchIndex,
|
|
3447
|
-
isExactMatch: true
|
|
4329
|
+
isExactMatch: true,
|
|
4330
|
+
searchText
|
|
3448
4331
|
});
|
|
3449
4332
|
index = matchIndex + searchText.length;
|
|
3450
4333
|
}
|
|
@@ -3473,7 +4356,8 @@ var useSearch = () => {
|
|
|
3473
4356
|
text: text.substr(index, searchLower.length + textSize),
|
|
3474
4357
|
score,
|
|
3475
4358
|
matchIndex: index,
|
|
3476
|
-
isExactMatch: false
|
|
4359
|
+
isExactMatch: false,
|
|
4360
|
+
searchText
|
|
3477
4361
|
});
|
|
3478
4362
|
index += searchLower.length;
|
|
3479
4363
|
} else {
|
|
@@ -3548,7 +4432,7 @@ var useSearch = () => {
|
|
|
3548
4432
|
async function calculateHighlightRects(pageProxy, textMatch) {
|
|
3549
4433
|
const textContent = await pageProxy.getTextContent();
|
|
3550
4434
|
const items = textContent.items;
|
|
3551
|
-
const matchLength = textMatch.text.length;
|
|
4435
|
+
const matchLength = textMatch.searchText ? textMatch.searchText.length : textMatch.text.length;
|
|
3552
4436
|
const matchRects = [];
|
|
3553
4437
|
let currentIndex = 0;
|
|
3554
4438
|
let remainingMatchLength = matchLength;
|
|
@@ -3619,95 +4503,4 @@ function mergeAdjacentRects(rects) {
|
|
|
3619
4503
|
return merged;
|
|
3620
4504
|
}
|
|
3621
4505
|
|
|
3622
|
-
|
|
3623
|
-
var MERGE_THRESHOLD = 5;
|
|
3624
|
-
var shouldMergeRects = (rect1, rect2) => {
|
|
3625
|
-
const verticalOverlap = !(rect1.top > rect2.top + rect2.height || rect2.top > rect1.top + rect1.height);
|
|
3626
|
-
const horizontallyClose = Math.abs(rect1.left + rect1.width - rect2.left) < MERGE_THRESHOLD || Math.abs(rect2.left + rect2.width - rect1.left) < MERGE_THRESHOLD || rect1.left < rect2.left + rect2.width && rect2.left < rect1.left + rect1.width;
|
|
3627
|
-
return verticalOverlap && horizontallyClose;
|
|
3628
|
-
};
|
|
3629
|
-
var mergeRects = (rect1, rect2) => {
|
|
3630
|
-
return {
|
|
3631
|
-
left: Math.min(rect1.left, rect2.left),
|
|
3632
|
-
top: Math.min(rect1.top, rect2.top),
|
|
3633
|
-
width: Math.max(rect1.left + rect1.width, rect2.left + rect2.width) - Math.min(rect1.left, rect2.left),
|
|
3634
|
-
height: Math.max(rect1.top + rect1.height, rect2.top + rect2.height) - Math.min(rect1.top, rect2.top),
|
|
3635
|
-
pageNumber: rect1.pageNumber
|
|
3636
|
-
};
|
|
3637
|
-
};
|
|
3638
|
-
var consolidateRects = (rects) => {
|
|
3639
|
-
if (rects.length <= 1) return rects;
|
|
3640
|
-
const sortedRects = [...rects].sort((a, b) => a.top - b.top);
|
|
3641
|
-
let hasChanges;
|
|
3642
|
-
do {
|
|
3643
|
-
hasChanges = false;
|
|
3644
|
-
let currentRect = sortedRects[0];
|
|
3645
|
-
const tempResult = [];
|
|
3646
|
-
for (let i = 1; i < sortedRects.length; i++) {
|
|
3647
|
-
const sorted = sortedRects[i];
|
|
3648
|
-
if (!currentRect || !sorted) continue;
|
|
3649
|
-
if (shouldMergeRects(currentRect, sorted)) {
|
|
3650
|
-
currentRect = mergeRects(currentRect, sorted);
|
|
3651
|
-
hasChanges = true;
|
|
3652
|
-
} else {
|
|
3653
|
-
tempResult.push(currentRect);
|
|
3654
|
-
currentRect = sorted;
|
|
3655
|
-
}
|
|
3656
|
-
}
|
|
3657
|
-
if (currentRect) tempResult.push(currentRect);
|
|
3658
|
-
sortedRects.length = 0;
|
|
3659
|
-
sortedRects.push(...tempResult);
|
|
3660
|
-
} while (hasChanges);
|
|
3661
|
-
return sortedRects;
|
|
3662
|
-
};
|
|
3663
|
-
var useSelectionDimensions = () => {
|
|
3664
|
-
const store = PDFStore.useContext();
|
|
3665
|
-
const getDimension = () => {
|
|
3666
|
-
const selection = window.getSelection();
|
|
3667
|
-
if (!selection || selection.isCollapsed) return;
|
|
3668
|
-
const range = selection.getRangeAt(0);
|
|
3669
|
-
const highlights = [];
|
|
3670
|
-
const textLayerMap = /* @__PURE__ */ new Map();
|
|
3671
|
-
const clientRects = Array.from(range.getClientRects()).filter(
|
|
3672
|
-
(rect) => rect.width > 2 && rect.height > 2
|
|
3673
|
-
);
|
|
3674
|
-
clientRects.forEach((clientRect) => {
|
|
3675
|
-
const element = document.elementFromPoint(
|
|
3676
|
-
clientRect.left + 1,
|
|
3677
|
-
clientRect.top + clientRect.height / 2
|
|
3678
|
-
);
|
|
3679
|
-
const textLayer = element?.closest(".textLayer");
|
|
3680
|
-
if (!textLayer) return;
|
|
3681
|
-
const pageNumber = parseInt(
|
|
3682
|
-
textLayer.getAttribute("data-page-number") || "1",
|
|
3683
|
-
10
|
|
3684
|
-
);
|
|
3685
|
-
const textLayerRect = textLayer.getBoundingClientRect();
|
|
3686
|
-
const zoom = store.getState().zoom;
|
|
3687
|
-
const rect = {
|
|
3688
|
-
width: clientRect.width / zoom,
|
|
3689
|
-
height: clientRect.height / zoom,
|
|
3690
|
-
top: (clientRect.top - textLayerRect.top) / zoom,
|
|
3691
|
-
left: (clientRect.left - textLayerRect.left) / zoom,
|
|
3692
|
-
pageNumber
|
|
3693
|
-
};
|
|
3694
|
-
if (!textLayerMap.has(pageNumber)) {
|
|
3695
|
-
textLayerMap.set(pageNumber, []);
|
|
3696
|
-
}
|
|
3697
|
-
textLayerMap.get(pageNumber)?.push(rect);
|
|
3698
|
-
});
|
|
3699
|
-
textLayerMap.forEach((rects) => {
|
|
3700
|
-
if (rects.length > 0) {
|
|
3701
|
-
highlights.push(...consolidateRects(rects));
|
|
3702
|
-
}
|
|
3703
|
-
});
|
|
3704
|
-
return {
|
|
3705
|
-
highlights: highlights.sort((a, b) => a.pageNumber - b.pageNumber),
|
|
3706
|
-
text: range.toString().trim(),
|
|
3707
|
-
isCollapsed: false
|
|
3708
|
-
};
|
|
3709
|
-
};
|
|
3710
|
-
return { getDimension };
|
|
3711
|
-
};
|
|
3712
|
-
|
|
3713
|
-
export { AnnotationLayer2 as AnnotationLayer, CanvasLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer2 as TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|
|
4506
|
+
export { AnnotationHighlightLayer, AnnotationLayer2 as AnnotationLayer, AnnotationTooltip, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer2 as TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, usePDFPageNumber, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|