@anaralabs/lector 3.5.9 → 3.5.11
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/dist/_tsup-dts-rollup.d.ts +29 -15
- package/dist/index.js +403 -64
- package/package.json +1 -1
|
@@ -24,40 +24,40 @@ import { Virtualizer } from '@tanstack/react-virtual';
|
|
|
24
24
|
declare interface Annotation {
|
|
25
25
|
id: string;
|
|
26
26
|
pageNumber: number;
|
|
27
|
-
highlights:
|
|
28
|
-
|
|
29
|
-
left: number;
|
|
30
|
-
top: number;
|
|
31
|
-
width: number;
|
|
32
|
-
pageNumber: number;
|
|
33
|
-
}>;
|
|
27
|
+
highlights: HighlightRect_alias_2[];
|
|
28
|
+
underlines?: HighlightRect_alias_2[];
|
|
34
29
|
color: string;
|
|
35
30
|
borderColor: string;
|
|
31
|
+
comment?: string;
|
|
36
32
|
createdAt: Date;
|
|
37
33
|
updatedAt: Date;
|
|
38
|
-
comment?: string;
|
|
39
34
|
metadata?: Record<string, unknown>;
|
|
35
|
+
isCommentPending?: boolean;
|
|
40
36
|
}
|
|
41
37
|
export { Annotation }
|
|
42
38
|
export { Annotation as Annotation_alias_1 }
|
|
43
39
|
|
|
44
|
-
export declare const AnnotationHighlightLayer: ({ className, style, renderTooltipContent, renderHoverTooltipContent, tooltipClassName, highlightClassName, focusedAnnotationId, focusedHoverAnnotationId, onAnnotationClick,
|
|
40
|
+
export declare const AnnotationHighlightLayer: ({ className, style, renderTooltipContent, renderHoverTooltipContent, tooltipClassName, highlightClassName, underlineClassName, commentIconPosition, commmentIcon, commentIconClassName, focusedAnnotationId, focusedHoverAnnotationId, onAnnotationClick, onAnnotationTooltipClose, hoverTooltipClassName, }: AnnotationHighlightLayerProps) => JSX.Element;
|
|
45
41
|
|
|
46
42
|
declare interface AnnotationHighlightLayerProps {
|
|
47
43
|
className?: string;
|
|
48
|
-
style?:
|
|
49
|
-
renderTooltipContent: (props: AnnotationTooltipContentProps) =>
|
|
44
|
+
style?: React_2.CSSProperties;
|
|
45
|
+
renderTooltipContent: (props: AnnotationTooltipContentProps) => React_2.ReactNode;
|
|
50
46
|
renderHoverTooltipContent: (props: {
|
|
51
47
|
annotation: Annotation;
|
|
52
48
|
onClose: () => void;
|
|
53
|
-
}) =>
|
|
49
|
+
}) => React_2.ReactNode;
|
|
54
50
|
focusedAnnotationId?: string;
|
|
51
|
+
commmentIcon?: React_2.ReactNode;
|
|
55
52
|
focusedHoverAnnotationId?: string;
|
|
56
53
|
onAnnotationClick?: (annotation: Annotation) => void;
|
|
54
|
+
onAnnotationTooltipClose?: (annotation: Annotation) => void;
|
|
57
55
|
tooltipClassName?: string;
|
|
58
56
|
hoverTooltipClassName?: string;
|
|
59
57
|
highlightClassName?: string;
|
|
60
|
-
|
|
58
|
+
commentIconPosition?: 'highlight' | 'page';
|
|
59
|
+
underlineClassName?: string;
|
|
60
|
+
commentIconClassName?: string;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
@@ -97,7 +97,7 @@ declare interface AnnotationState {
|
|
|
97
97
|
setAnnotations: (annotations: Annotation[]) => void;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
export declare const AnnotationTooltip: ({ annotation, children, renderTooltipContent, hoverTooltipContent, onOpenChange, className, hoverClassName, isOpen: controlledIsOpen,
|
|
100
|
+
export declare const AnnotationTooltip: ({ annotation, children, renderTooltipContent, hoverTooltipContent, onOpenChange, className, hoverClassName, isOpen: controlledIsOpen, hoverIsOpen: controlledHoverIsOpen, }: AnnotationTooltipProps) => JSX.Element;
|
|
101
101
|
|
|
102
102
|
export declare type AnnotationTooltipContentProps = {
|
|
103
103
|
annotation: Annotation;
|
|
@@ -115,7 +115,6 @@ declare interface AnnotationTooltipProps {
|
|
|
115
115
|
className?: string;
|
|
116
116
|
hoverClassName?: string;
|
|
117
117
|
hoverIsOpen?: boolean;
|
|
118
|
-
tooltipBubbleSize: number;
|
|
119
118
|
renderHoverTooltipContent?: (props: {
|
|
120
119
|
annotation: Annotation;
|
|
121
120
|
onClose: () => void;
|
|
@@ -149,6 +148,7 @@ export declare const clamp: (value: number, min: number, max: number) => number;
|
|
|
149
148
|
|
|
150
149
|
declare type CollapsibleSelection = {
|
|
151
150
|
highlights: HighlightRect[];
|
|
151
|
+
underlines?: HighlightRect[];
|
|
152
152
|
text: string;
|
|
153
153
|
isCollapsed: boolean;
|
|
154
154
|
};
|
|
@@ -205,6 +205,14 @@ declare type HighlightRect = {
|
|
|
205
205
|
export { HighlightRect }
|
|
206
206
|
export { HighlightRect as HighlightRect_alias_1 }
|
|
207
207
|
|
|
208
|
+
export declare interface HighlightRect_alias_2 {
|
|
209
|
+
height: number;
|
|
210
|
+
left: number;
|
|
211
|
+
top: number;
|
|
212
|
+
width: number;
|
|
213
|
+
pageNumber: number;
|
|
214
|
+
}
|
|
215
|
+
|
|
208
216
|
export declare interface InitialPDFState {
|
|
209
217
|
pdfDocumentProxy: PDFDocumentProxy;
|
|
210
218
|
pageProxies: PDFPageProxy[];
|
|
@@ -470,6 +478,12 @@ export declare const useSelectionDimensions: () => {
|
|
|
470
478
|
isCollapsed: boolean;
|
|
471
479
|
} | undefined;
|
|
472
480
|
getSelection: () => CollapsibleSelection;
|
|
481
|
+
getAnnotationDimension: () => {
|
|
482
|
+
highlights: HighlightRect[];
|
|
483
|
+
underlines: HighlightRect[];
|
|
484
|
+
text: string;
|
|
485
|
+
isCollapsed: boolean;
|
|
486
|
+
} | undefined;
|
|
473
487
|
};
|
|
474
488
|
|
|
475
489
|
export declare const ZoomIn: ({ ...props }: HTMLProps<HTMLButtonElement>) => JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -276,7 +276,6 @@ var AnnotationTooltip = ({
|
|
|
276
276
|
className,
|
|
277
277
|
hoverClassName,
|
|
278
278
|
isOpen: controlledIsOpen,
|
|
279
|
-
tooltipBubbleSize,
|
|
280
279
|
hoverIsOpen: controlledHoverIsOpen
|
|
281
280
|
}) => {
|
|
282
281
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
@@ -345,7 +344,7 @@ var AnnotationTooltip = ({
|
|
|
345
344
|
setHoverIsOpen(false);
|
|
346
345
|
}, [setHoverIsOpen]);
|
|
347
346
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
348
|
-
/* @__PURE__ */
|
|
347
|
+
/* @__PURE__ */ jsx(
|
|
349
348
|
"div",
|
|
350
349
|
{
|
|
351
350
|
ref: (node) => {
|
|
@@ -357,18 +356,7 @@ var AnnotationTooltip = ({
|
|
|
357
356
|
onMouseLeave: handleMouseLeave,
|
|
358
357
|
...getReferenceProps(),
|
|
359
358
|
...getHoverReferenceProps(),
|
|
360
|
-
children
|
|
361
|
-
!isOpen && !hoverIsOpen && annotation.comment && /* @__PURE__ */ jsx("div", { className: "rounded-full", style: {
|
|
362
|
-
position: "absolute",
|
|
363
|
-
top: annotation.highlights[0]?.top ? annotation.highlights[0]?.top - tooltipBubbleSize / 2 : 0,
|
|
364
|
-
left: annotation.highlights[0]?.left ? annotation.highlights[0]?.left - tooltipBubbleSize / 2 : 0,
|
|
365
|
-
backgroundColor: annotation.borderColor,
|
|
366
|
-
height: `${tooltipBubbleSize}px`,
|
|
367
|
-
width: `${tooltipBubbleSize}px`,
|
|
368
|
-
zIndex: 50
|
|
369
|
-
} }),
|
|
370
|
-
children
|
|
371
|
-
]
|
|
359
|
+
children
|
|
372
360
|
}
|
|
373
361
|
),
|
|
374
362
|
isOpen && viewportRef.current && createPortal(
|
|
@@ -449,63 +437,146 @@ var AnnotationHighlightLayer = ({
|
|
|
449
437
|
renderHoverTooltipContent,
|
|
450
438
|
tooltipClassName,
|
|
451
439
|
highlightClassName,
|
|
440
|
+
underlineClassName,
|
|
441
|
+
commentIconPosition,
|
|
442
|
+
commmentIcon,
|
|
443
|
+
commentIconClassName,
|
|
452
444
|
focusedAnnotationId,
|
|
453
445
|
focusedHoverAnnotationId,
|
|
454
446
|
onAnnotationClick,
|
|
455
|
-
|
|
456
|
-
|
|
447
|
+
onAnnotationTooltipClose,
|
|
448
|
+
hoverTooltipClassName
|
|
457
449
|
}) => {
|
|
458
450
|
const { annotations } = useAnnotations();
|
|
459
451
|
const pageNumber = usePDFPageNumber();
|
|
460
452
|
const pageAnnotations = annotations.filter(
|
|
461
453
|
(annotation) => annotation.pageNumber === pageNumber
|
|
462
454
|
);
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
{
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
455
|
+
const getCommentIconPosition = (highlights) => {
|
|
456
|
+
if (!highlights.length) return { top: 0, right: 10 };
|
|
457
|
+
const sortedHighlights = [...highlights].sort((a, b) => {
|
|
458
|
+
const topDiff = a.top - b.top;
|
|
459
|
+
return Math.abs(topDiff) < 3 ? a.left - b.left : topDiff;
|
|
460
|
+
});
|
|
461
|
+
const lines = [];
|
|
462
|
+
let currentLine = [];
|
|
463
|
+
sortedHighlights.forEach((highlight) => {
|
|
464
|
+
if (currentLine.length === 0) {
|
|
465
|
+
currentLine.push(highlight);
|
|
466
|
+
} else {
|
|
467
|
+
const firstInLine = currentLine[0];
|
|
468
|
+
if (Math.abs(highlight.top - firstInLine.top) <= 3) {
|
|
469
|
+
currentLine.push(highlight);
|
|
470
|
+
} else {
|
|
471
|
+
lines.push([...currentLine]);
|
|
472
|
+
currentLine = [highlight];
|
|
475
473
|
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
if (currentLine.length > 0) {
|
|
477
|
+
lines.push(currentLine);
|
|
478
|
+
}
|
|
479
|
+
const PAGE_WIDTH = 600;
|
|
480
|
+
const hasLongLine = lines.some((line) => {
|
|
481
|
+
if (line.length === 0) return false;
|
|
482
|
+
const rightmost2 = Math.max(...line.map((h) => h.left + h.width));
|
|
483
|
+
return rightmost2 > PAGE_WIDTH * 0.8;
|
|
484
|
+
});
|
|
485
|
+
const firstHighlight = highlights[0];
|
|
486
|
+
const firstLine = lines[0] || [];
|
|
487
|
+
const leftmost = Math.min(...firstLine.map((h) => h.left));
|
|
488
|
+
const rightmost = Math.max(...firstLine.map((h) => h.left + h.width));
|
|
489
|
+
const lineCenter = leftmost + (rightmost - leftmost) / 2;
|
|
490
|
+
const shouldPositionRight = hasLongLine || lineCenter > PAGE_WIDTH * 0.5;
|
|
491
|
+
const rightPosition = commentIconPosition === "highlight" ? { left: rightmost + 8 } : { right: 10 };
|
|
492
|
+
const leftPosition = commentIconPosition === "highlight" ? { left: leftmost - 18 } : { left: 20 };
|
|
493
|
+
return {
|
|
494
|
+
top: firstHighlight.top + firstHighlight.height / 2 - 6,
|
|
495
|
+
...shouldPositionRight ? rightPosition : leftPosition
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
return /* @__PURE__ */ jsx("div", { className, style, children: pageAnnotations.map((annotation) => {
|
|
499
|
+
return /* @__PURE__ */ jsx(
|
|
500
|
+
AnnotationTooltip,
|
|
501
|
+
{
|
|
479
502
|
annotation,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
503
|
+
className: tooltipClassName,
|
|
504
|
+
hoverClassName: hoverTooltipClassName,
|
|
505
|
+
isOpen: focusedAnnotationId === annotation.id,
|
|
506
|
+
hoverIsOpen: focusedHoverAnnotationId === annotation.id,
|
|
507
|
+
onOpenChange: (open) => {
|
|
508
|
+
if (open && onAnnotationClick) {
|
|
509
|
+
onAnnotationClick(annotation);
|
|
510
|
+
} else if (!open && onAnnotationTooltipClose) {
|
|
511
|
+
onAnnotationTooltipClose(annotation);
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
renderTooltipContent,
|
|
515
|
+
hoverTooltipContent: renderHoverTooltipContent({
|
|
516
|
+
annotation,
|
|
517
|
+
onClose: () => {
|
|
518
|
+
}
|
|
519
|
+
}),
|
|
520
|
+
children: /* @__PURE__ */ jsxs(
|
|
521
|
+
"div",
|
|
522
|
+
{
|
|
523
|
+
style: { cursor: "pointer" },
|
|
524
|
+
onClick: () => onAnnotationClick?.(annotation),
|
|
525
|
+
children: [
|
|
526
|
+
annotation.highlights.map((highlight, index) => /* @__PURE__ */ jsx(
|
|
527
|
+
"div",
|
|
528
|
+
{
|
|
529
|
+
className: highlightClassName,
|
|
530
|
+
style: {
|
|
531
|
+
position: "absolute",
|
|
532
|
+
top: highlight.top,
|
|
533
|
+
left: highlight.left,
|
|
534
|
+
width: highlight.width,
|
|
535
|
+
height: highlight.height,
|
|
536
|
+
backgroundColor: annotation.color
|
|
537
|
+
},
|
|
538
|
+
"data-highlight-id": annotation.id
|
|
539
|
+
},
|
|
540
|
+
`highlight-${index}`
|
|
541
|
+
)),
|
|
542
|
+
annotation.comment && annotation.underlines?.map((rect, index) => /* @__PURE__ */ jsx(
|
|
543
|
+
"div",
|
|
544
|
+
{
|
|
545
|
+
className: underlineClassName,
|
|
546
|
+
style: {
|
|
547
|
+
position: "absolute",
|
|
548
|
+
top: rect.top,
|
|
549
|
+
left: rect.left,
|
|
550
|
+
width: rect.width,
|
|
551
|
+
height: 1.1,
|
|
552
|
+
backgroundColor: annotation.borderColor
|
|
553
|
+
},
|
|
554
|
+
"data-comment-id": annotation.id
|
|
555
|
+
},
|
|
556
|
+
`underline-${index}`
|
|
557
|
+
)),
|
|
558
|
+
annotation.comment && commmentIcon && /* @__PURE__ */ jsx(
|
|
559
|
+
"div",
|
|
560
|
+
{
|
|
561
|
+
className: commentIconClassName,
|
|
562
|
+
style: {
|
|
563
|
+
position: "absolute",
|
|
564
|
+
...getCommentIconPosition(annotation.highlights),
|
|
565
|
+
color: "gray",
|
|
566
|
+
cursor: "pointer",
|
|
567
|
+
zIndex: 10
|
|
568
|
+
},
|
|
569
|
+
"data-comment-icon-id": annotation.id,
|
|
570
|
+
children: commmentIcon
|
|
571
|
+
}
|
|
572
|
+
)
|
|
573
|
+
]
|
|
574
|
+
}
|
|
575
|
+
)
|
|
576
|
+
},
|
|
577
|
+
annotation.id
|
|
578
|
+
);
|
|
579
|
+
}) });
|
|
509
580
|
};
|
|
510
581
|
|
|
511
582
|
// ../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
@@ -1049,11 +1120,48 @@ var CanvasLayer = ({
|
|
|
1049
1120
|
};
|
|
1050
1121
|
|
|
1051
1122
|
// src/hooks/useSelectionDimensions.tsx
|
|
1052
|
-
var MERGE_THRESHOLD =
|
|
1123
|
+
var MERGE_THRESHOLD = 2;
|
|
1053
1124
|
var shouldMergeRects = (rect1, rect2) => {
|
|
1054
1125
|
const verticalOverlap = !(rect1.top > rect2.top + rect2.height || rect2.top > rect1.top + rect1.height);
|
|
1055
|
-
const
|
|
1056
|
-
return verticalOverlap &&
|
|
1126
|
+
const horizontallyConnected = 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;
|
|
1127
|
+
return verticalOverlap && horizontallyConnected;
|
|
1128
|
+
};
|
|
1129
|
+
var consolidateHighlightRects = (rects) => {
|
|
1130
|
+
if (rects.length <= 1) return rects;
|
|
1131
|
+
const consolidated = [];
|
|
1132
|
+
const sorted = [...rects].sort((a, b) => {
|
|
1133
|
+
const pageCompare = a.pageNumber - b.pageNumber;
|
|
1134
|
+
if (pageCompare !== 0) return pageCompare;
|
|
1135
|
+
const topDiff = a.top - b.top;
|
|
1136
|
+
return Math.abs(topDiff) < 2 ? a.left - b.left : topDiff;
|
|
1137
|
+
});
|
|
1138
|
+
let current = sorted[0];
|
|
1139
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
1140
|
+
const next = sorted[i];
|
|
1141
|
+
const samePageAndLine = current.pageNumber === next.pageNumber && Math.abs(current.top - next.top) < Math.max(current.height, next.height) * 0.5;
|
|
1142
|
+
const horizontallyConnected = samePageAndLine && // Adjacent (touching or very close)
|
|
1143
|
+
(Math.abs(current.left + current.width - next.left) <= MERGE_THRESHOLD || Math.abs(next.left + next.width - current.left) <= MERGE_THRESHOLD || // Overlapping
|
|
1144
|
+
current.left < next.left + next.width && next.left < current.left + current.width || // Very close (small gap)
|
|
1145
|
+
Math.abs(current.left + current.width - next.left) <= current.height * 0.2);
|
|
1146
|
+
if (horizontallyConnected) {
|
|
1147
|
+
const newLeft = Math.min(current.left, next.left);
|
|
1148
|
+
const newRight = Math.max(current.left + current.width, next.left + next.width);
|
|
1149
|
+
const newTop = Math.min(current.top, next.top);
|
|
1150
|
+
const newBottom = Math.max(current.top + current.height, next.top + next.height);
|
|
1151
|
+
current = {
|
|
1152
|
+
left: newLeft,
|
|
1153
|
+
top: newTop,
|
|
1154
|
+
width: newRight - newLeft,
|
|
1155
|
+
height: newBottom - newTop,
|
|
1156
|
+
pageNumber: current.pageNumber
|
|
1157
|
+
};
|
|
1158
|
+
} else {
|
|
1159
|
+
consolidated.push(current);
|
|
1160
|
+
current = next;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
consolidated.push(current);
|
|
1164
|
+
return consolidated;
|
|
1057
1165
|
};
|
|
1058
1166
|
var consolidateRects = (rects) => {
|
|
1059
1167
|
if (rects.length <= 1) return rects;
|
|
@@ -1121,6 +1229,237 @@ var mergeRectGroup = (rects) => {
|
|
|
1121
1229
|
};
|
|
1122
1230
|
var useSelectionDimensions = () => {
|
|
1123
1231
|
const store = PDFStore.useContext();
|
|
1232
|
+
const getAnnotationDimension = () => {
|
|
1233
|
+
const selection = window.getSelection();
|
|
1234
|
+
if (!selection || selection.isCollapsed) return;
|
|
1235
|
+
const range = selection.getRangeAt(0);
|
|
1236
|
+
const highlightRects = [];
|
|
1237
|
+
const underlineRects = [];
|
|
1238
|
+
const textLayerMapHighlight = /* @__PURE__ */ new Map();
|
|
1239
|
+
const textLayerMapUnderline = /* @__PURE__ */ new Map();
|
|
1240
|
+
const clientRects = Array.from(range.getClientRects()).filter(
|
|
1241
|
+
(rect) => rect.width > 2 && rect.height > 2
|
|
1242
|
+
);
|
|
1243
|
+
clientRects.forEach((clientRect) => {
|
|
1244
|
+
let element = document.elementFromPoint(
|
|
1245
|
+
clientRect.left + 1,
|
|
1246
|
+
clientRect.top + clientRect.height / 2
|
|
1247
|
+
);
|
|
1248
|
+
if (!element) {
|
|
1249
|
+
element = document.elementFromPoint(
|
|
1250
|
+
clientRect.left + clientRect.width / 2,
|
|
1251
|
+
clientRect.top + clientRect.height / 2
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
if (!element) {
|
|
1255
|
+
element = document.elementFromPoint(
|
|
1256
|
+
clientRect.right - 1,
|
|
1257
|
+
clientRect.top + clientRect.height / 2
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
if (!element) {
|
|
1261
|
+
element = document.elementFromPoint(
|
|
1262
|
+
clientRect.left + clientRect.width / 2,
|
|
1263
|
+
clientRect.top + 1
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
if (!element) {
|
|
1267
|
+
element = document.elementFromPoint(
|
|
1268
|
+
clientRect.left + clientRect.width / 2,
|
|
1269
|
+
clientRect.bottom - 1
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
const textLayer = element?.closest(".textLayer");
|
|
1273
|
+
if (!textLayer) return;
|
|
1274
|
+
const isSuperOrSubScript = (el) => {
|
|
1275
|
+
if (!el) return false;
|
|
1276
|
+
if (el.tagName.toLowerCase() === "sup" || el.tagName.toLowerCase() === "sub") {
|
|
1277
|
+
return true;
|
|
1278
|
+
}
|
|
1279
|
+
const classes = el.className;
|
|
1280
|
+
if (typeof classes === "string") {
|
|
1281
|
+
const superSubClasses = ["superscript", "subscript", "sup", "sub"];
|
|
1282
|
+
if (superSubClasses.some((c) => classes.includes(c))) {
|
|
1283
|
+
return true;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const elementRect = el.getBoundingClientRect();
|
|
1287
|
+
if (elementRect.height < 6 && elementRect.width < 15) {
|
|
1288
|
+
const textContent = el.textContent?.trim() || "";
|
|
1289
|
+
if (textContent.length <= 2 && /^[\d\w]{1,2}$/.test(textContent)) {
|
|
1290
|
+
const parentRect = el.parentElement?.getBoundingClientRect();
|
|
1291
|
+
if (parentRect && parentRect.height > elementRect.height * 2) {
|
|
1292
|
+
const elementCenter = elementRect.top + elementRect.height / 2;
|
|
1293
|
+
const parentCenter = parentRect.top + parentRect.height / 2;
|
|
1294
|
+
const verticalOffset = Math.abs(elementCenter - parentCenter);
|
|
1295
|
+
if (verticalOffset > parentRect.height * 0.4) {
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return false;
|
|
1302
|
+
};
|
|
1303
|
+
const pageNumber = parseInt(
|
|
1304
|
+
textLayer.getAttribute("data-page-number") || "1",
|
|
1305
|
+
10
|
|
1306
|
+
);
|
|
1307
|
+
const textLayerRect = textLayer.getBoundingClientRect();
|
|
1308
|
+
const zoom = store.getState().zoom;
|
|
1309
|
+
const highlightRect = {
|
|
1310
|
+
width: clientRect.width / zoom,
|
|
1311
|
+
height: clientRect.height / zoom,
|
|
1312
|
+
top: (clientRect.top - textLayerRect.top) / zoom,
|
|
1313
|
+
left: (clientRect.left - textLayerRect.left) / zoom,
|
|
1314
|
+
pageNumber
|
|
1315
|
+
};
|
|
1316
|
+
if (!textLayerMapHighlight.has(pageNumber)) {
|
|
1317
|
+
textLayerMapHighlight.set(pageNumber, []);
|
|
1318
|
+
}
|
|
1319
|
+
textLayerMapHighlight.get(pageNumber)?.push(highlightRect);
|
|
1320
|
+
const shouldCreateUnderline = !isSuperOrSubScript(element);
|
|
1321
|
+
if (shouldCreateUnderline) {
|
|
1322
|
+
const baselineOffset = clientRect.height * 0.85;
|
|
1323
|
+
const underlineHeight = 2;
|
|
1324
|
+
const underlineRect = {
|
|
1325
|
+
width: clientRect.width / zoom,
|
|
1326
|
+
height: underlineHeight / zoom,
|
|
1327
|
+
top: (clientRect.top - textLayerRect.top + baselineOffset) / zoom,
|
|
1328
|
+
left: (clientRect.left - textLayerRect.left) / zoom,
|
|
1329
|
+
pageNumber
|
|
1330
|
+
};
|
|
1331
|
+
if (!textLayerMapUnderline.has(pageNumber)) {
|
|
1332
|
+
textLayerMapUnderline.set(pageNumber, []);
|
|
1333
|
+
}
|
|
1334
|
+
textLayerMapUnderline.get(pageNumber)?.push(underlineRect);
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
textLayerMapHighlight.forEach((rects) => {
|
|
1338
|
+
if (rects.length > 0) {
|
|
1339
|
+
const consolidated = consolidateHighlightRects(rects);
|
|
1340
|
+
highlightRects.push(...consolidated);
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
const finalHighlights = consolidateHighlightRects(highlightRects);
|
|
1344
|
+
highlightRects.length = 0;
|
|
1345
|
+
highlightRects.push(...finalHighlights);
|
|
1346
|
+
textLayerMapUnderline.forEach((rects) => {
|
|
1347
|
+
if (rects.length > 0) {
|
|
1348
|
+
const lineGroups = groupRectsByLine(rects);
|
|
1349
|
+
lineGroups.forEach((group) => {
|
|
1350
|
+
if (group.length === 0) return;
|
|
1351
|
+
group.sort((a, b) => a.left - b.left);
|
|
1352
|
+
let i = 0;
|
|
1353
|
+
while (i < group.length) {
|
|
1354
|
+
const startRect = group[i];
|
|
1355
|
+
if (!startRect) {
|
|
1356
|
+
i++;
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
let endIndex = i;
|
|
1360
|
+
while (endIndex + 1 < group.length) {
|
|
1361
|
+
const currentRect = group[endIndex];
|
|
1362
|
+
const nextRect = group[endIndex + 1];
|
|
1363
|
+
if (!currentRect || !nextRect) break;
|
|
1364
|
+
const gap = nextRect.left - (currentRect.left + currentRect.width);
|
|
1365
|
+
const maxGapAllowed = Math.max(MERGE_THRESHOLD, currentRect.height * 0.3);
|
|
1366
|
+
if (gap <= maxGapAllowed) {
|
|
1367
|
+
endIndex++;
|
|
1368
|
+
} else {
|
|
1369
|
+
break;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
const endRect = group[endIndex];
|
|
1373
|
+
if (!endRect) {
|
|
1374
|
+
i++;
|
|
1375
|
+
continue;
|
|
1376
|
+
}
|
|
1377
|
+
const lineRect = {
|
|
1378
|
+
width: endRect.left + endRect.width - startRect.left,
|
|
1379
|
+
height: 1.5,
|
|
1380
|
+
top: startRect.top,
|
|
1381
|
+
left: startRect.left,
|
|
1382
|
+
pageNumber: startRect.pageNumber
|
|
1383
|
+
};
|
|
1384
|
+
underlineRects.push(lineRect);
|
|
1385
|
+
i = endIndex + 1;
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
});
|
|
1390
|
+
if (underlineRects.length === 0 && highlightRects.length > 0) {
|
|
1391
|
+
highlightRects.forEach((highlightRect) => {
|
|
1392
|
+
const baselineOffset = highlightRect.height * 0.85;
|
|
1393
|
+
const underlineHeight = 1.5;
|
|
1394
|
+
const underlineRect = {
|
|
1395
|
+
width: highlightRect.width,
|
|
1396
|
+
height: underlineHeight,
|
|
1397
|
+
top: highlightRect.top + baselineOffset,
|
|
1398
|
+
left: highlightRect.left,
|
|
1399
|
+
pageNumber: highlightRect.pageNumber
|
|
1400
|
+
};
|
|
1401
|
+
underlineRects.push(underlineRect);
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
highlights: highlightRects.sort((a, b) => a.pageNumber - b.pageNumber),
|
|
1406
|
+
underlines: consolidateUnderlines(underlineRects).sort((a, b) => a.pageNumber - b.pageNumber),
|
|
1407
|
+
text: range.toString().trim(),
|
|
1408
|
+
isCollapsed: false
|
|
1409
|
+
};
|
|
1410
|
+
};
|
|
1411
|
+
const groupRectsByLine = (rects) => {
|
|
1412
|
+
const VERTICAL_TOLERANCE = 3;
|
|
1413
|
+
const groups = [];
|
|
1414
|
+
rects.forEach((rect) => {
|
|
1415
|
+
const centerY = rect.top + rect.height / 2;
|
|
1416
|
+
let foundGroup = false;
|
|
1417
|
+
for (const group of groups) {
|
|
1418
|
+
if (group.length === 0) continue;
|
|
1419
|
+
const firstRect = group[0];
|
|
1420
|
+
if (!firstRect) continue;
|
|
1421
|
+
const groupCenterY = firstRect.top + firstRect.height / 2;
|
|
1422
|
+
if (Math.abs(centerY - groupCenterY) <= VERTICAL_TOLERANCE) {
|
|
1423
|
+
group.push(rect);
|
|
1424
|
+
foundGroup = true;
|
|
1425
|
+
break;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if (!foundGroup) {
|
|
1429
|
+
groups.push([rect]);
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
return groups;
|
|
1433
|
+
};
|
|
1434
|
+
const consolidateUnderlines = (underlines) => {
|
|
1435
|
+
if (underlines.length <= 1) return underlines;
|
|
1436
|
+
const consolidated = [];
|
|
1437
|
+
const sorted = [...underlines].sort((a, b) => {
|
|
1438
|
+
const pageCompare = a.pageNumber - b.pageNumber;
|
|
1439
|
+
if (pageCompare !== 0) return pageCompare;
|
|
1440
|
+
const topCompare = a.top - b.top;
|
|
1441
|
+
return Math.abs(topCompare) < 1 ? a.left - b.left : topCompare;
|
|
1442
|
+
});
|
|
1443
|
+
let current = sorted[0];
|
|
1444
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
1445
|
+
const next = sorted[i];
|
|
1446
|
+
const samePageAndLine = current.pageNumber === next.pageNumber && Math.abs(current.top - next.top) < 1;
|
|
1447
|
+
const horizontallyConnected = samePageAndLine && (Math.abs(current.left + current.width - next.left) <= MERGE_THRESHOLD || current.left < next.left + next.width && next.left < current.left + current.width);
|
|
1448
|
+
if (horizontallyConnected) {
|
|
1449
|
+
const newWidth = Math.max(current.left + current.width, next.left + next.width) - Math.min(current.left, next.left);
|
|
1450
|
+
current = {
|
|
1451
|
+
...current,
|
|
1452
|
+
left: Math.min(current.left, next.left),
|
|
1453
|
+
width: newWidth
|
|
1454
|
+
};
|
|
1455
|
+
} else {
|
|
1456
|
+
consolidated.push(current);
|
|
1457
|
+
current = next;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
consolidated.push(current);
|
|
1461
|
+
return consolidated;
|
|
1462
|
+
};
|
|
1124
1463
|
const getDimension = () => {
|
|
1125
1464
|
const selection = window.getSelection();
|
|
1126
1465
|
if (!selection || selection.isCollapsed) return;
|
|
@@ -1168,7 +1507,7 @@ var useSelectionDimensions = () => {
|
|
|
1168
1507
|
};
|
|
1169
1508
|
};
|
|
1170
1509
|
const getSelection = () => getDimension();
|
|
1171
|
-
return { getDimension, getSelection };
|
|
1510
|
+
return { getDimension, getSelection, getAnnotationDimension };
|
|
1172
1511
|
};
|
|
1173
1512
|
var SelectionTooltip = ({ children }) => {
|
|
1174
1513
|
const [isOpen, setIsOpen] = useState(false);
|