@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.
@@ -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: Array<{
28
- height: number;
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, hoverTooltipClassName, tooltipBubbleSize, }: AnnotationHighlightLayerProps) => JSX.Element;
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?: React.CSSProperties;
49
- renderTooltipContent: (props: AnnotationTooltipContentProps) => React.ReactNode;
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
- }) => React.ReactNode;
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
- tooltipBubbleSize?: number;
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, tooltipBubbleSize, hoverIsOpen: controlledHoverIsOpen, }: AnnotationTooltipProps) => JSX.Element;
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__ */ jsxs(
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
- hoverTooltipClassName,
456
- tooltipBubbleSize = 6
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
- return /* @__PURE__ */ jsx("div", { className, style, children: pageAnnotations.map((annotation) => /* @__PURE__ */ jsx(
464
- AnnotationTooltip,
465
- {
466
- annotation,
467
- className: tooltipClassName,
468
- hoverClassName: hoverTooltipClassName,
469
- isOpen: focusedAnnotationId === annotation.id,
470
- tooltipBubbleSize,
471
- hoverIsOpen: focusedHoverAnnotationId === annotation.id,
472
- onOpenChange: (open) => {
473
- if (open && onAnnotationClick) {
474
- onAnnotationClick(annotation);
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
- renderTooltipContent,
478
- hoverTooltipContent: renderHoverTooltipContent({
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
- onClose: () => {
481
- }
482
- }),
483
- children: /* @__PURE__ */ jsx(
484
- "div",
485
- {
486
- style: { cursor: "pointer" },
487
- onClick: () => onAnnotationClick?.(annotation),
488
- children: annotation.highlights.map((highlight, index) => /* @__PURE__ */ jsx(
489
- "div",
490
- {
491
- className: highlightClassName,
492
- style: {
493
- position: "absolute",
494
- top: highlight.top,
495
- left: highlight.left,
496
- width: highlight.width,
497
- height: highlight.height,
498
- backgroundColor: annotation.color
499
- },
500
- "data-highlight-id": annotation.id
501
- },
502
- index
503
- ))
504
- }
505
- )
506
- },
507
- annotation.id
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 = 5;
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 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;
1056
- return verticalOverlap && horizontallyClose;
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.5.9",
3
+ "version": "3.5.11",
4
4
  "description": "Headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",