@anaralabs/lector 3.15.1 → 3.15.2
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 +15 -0
- package/dist/index.js +489 -342
- package/package.json +1 -1
|
@@ -177,6 +177,9 @@ declare interface AsyncSearchOptions extends SearchOptions {
|
|
|
177
177
|
export { AsyncSearchOptions }
|
|
178
178
|
export { AsyncSearchOptions as AsyncSearchOptions_alias_1 }
|
|
179
179
|
|
|
180
|
+
/** One mouse-selection controller per viewer, shared by its mounted pages. */
|
|
181
|
+
export declare function bindTextSelection(layer: HTMLDivElement, viewport: ViewportRef, isPersistent?: () => boolean): () => void;
|
|
182
|
+
|
|
180
183
|
export declare function calculateHighlightRects(pageProxy: PDFPageProxy, textMatch: TextPosition): Promise<HighlightRect[]>;
|
|
181
184
|
|
|
182
185
|
export declare const cancellable: <T extends Promise<unknown>>(promise: T) => {
|
|
@@ -195,6 +198,11 @@ export declare const CanvasLayer: MemoExoticComponent<({ style, background, ...p
|
|
|
195
198
|
background?: string;
|
|
196
199
|
}) => JSX.Element>;
|
|
197
200
|
|
|
201
|
+
declare type Caret = {
|
|
202
|
+
node: Node;
|
|
203
|
+
offset: number;
|
|
204
|
+
};
|
|
205
|
+
|
|
198
206
|
export declare const clamp: (value: number, minimum: number, maximum: number) => number;
|
|
199
207
|
|
|
200
208
|
export declare function clampScaleForPage(targetScale: number, pageWidth: number, pageHeight: number, maxPixels?: number): number;
|
|
@@ -569,6 +577,7 @@ export declare class PDFSelectionController {
|
|
|
569
577
|
});
|
|
570
578
|
getSnapshot: () => PDFTextSelection | null;
|
|
571
579
|
subscribe: (listener: () => void) => () => void;
|
|
580
|
+
get isConnected(): boolean;
|
|
572
581
|
get isDragging(): boolean;
|
|
573
582
|
includesPage(page: number): boolean;
|
|
574
583
|
private publish;
|
|
@@ -729,6 +738,8 @@ export declare interface RenderColorMapRef {
|
|
|
729
738
|
current: RenderColorMap | null;
|
|
730
739
|
}
|
|
731
740
|
|
|
741
|
+
export declare function resolveTextSelectionCaret(viewport: HTMLElement, x: number, y: number): Caret | null | undefined;
|
|
742
|
+
|
|
732
743
|
export declare const Root: ForwardRefExoticComponent<Omit<Omit<HTMLProps<HTMLDivElement>, "onError"> & usePDFDocumentParams & {
|
|
733
744
|
loader?: ReactNode;
|
|
734
745
|
}, "ref"> & RefAttributes<unknown>>;
|
|
@@ -1098,6 +1109,10 @@ export declare const useSelectionDimensions: () => {
|
|
|
1098
1109
|
|
|
1099
1110
|
declare type ViewportCallback = () => void;
|
|
1100
1111
|
|
|
1112
|
+
declare type ViewportRef = {
|
|
1113
|
+
current: HTMLDivElement | null;
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1101
1116
|
/** Materialize one missing selected page as text only, never as a page canvas. */
|
|
1102
1117
|
export declare function withSelectionPage<T>(proxy: PDFPageProxy, owner: Document, signal: AbortSignal, read: (page: SelectionPage) => T): Promise<T>;
|
|
1103
1118
|
|
package/dist/index.js
CHANGED
|
@@ -474,6 +474,467 @@ function originalTextRange(normalized, start, end) {
|
|
|
474
474
|
return { start, end };
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
// src/lib/selection-rects.ts
|
|
478
|
+
var getTextNodeClientRects = (range) => {
|
|
479
|
+
const root = range.commonAncestorContainer;
|
|
480
|
+
const ownerDoc = root.ownerDocument ?? document;
|
|
481
|
+
if (root.nodeType === Node.TEXT_NODE) {
|
|
482
|
+
const parentElement = root.parentElement;
|
|
483
|
+
return Array.from(range.getClientRects()).map((rect) => ({
|
|
484
|
+
rect,
|
|
485
|
+
element: parentElement
|
|
486
|
+
}));
|
|
487
|
+
}
|
|
488
|
+
const walker = ownerDoc.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
489
|
+
acceptNode(node) {
|
|
490
|
+
if (!node.nodeValue || node.nodeValue.length === 0) {
|
|
491
|
+
return NodeFilter.FILTER_REJECT;
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
495
|
+
} catch {
|
|
496
|
+
return NodeFilter.FILTER_REJECT;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
const results = [];
|
|
501
|
+
let current = walker.nextNode();
|
|
502
|
+
while (current) {
|
|
503
|
+
const textNode = current;
|
|
504
|
+
const parentElement = textNode.parentElement;
|
|
505
|
+
const length = textNode.nodeValue?.length ?? 0;
|
|
506
|
+
const isStartNode = textNode === range.startContainer;
|
|
507
|
+
const isEndNode = textNode === range.endContainer;
|
|
508
|
+
const start = isStartNode ? range.startOffset : 0;
|
|
509
|
+
const end = isEndNode ? range.endOffset : length;
|
|
510
|
+
if (end > start) {
|
|
511
|
+
const sub = ownerDoc.createRange();
|
|
512
|
+
try {
|
|
513
|
+
sub.setStart(textNode, start);
|
|
514
|
+
sub.setEnd(textNode, end);
|
|
515
|
+
const subRects = sub.getClientRects();
|
|
516
|
+
for (let i = 0; i < subRects.length; i++) {
|
|
517
|
+
const r2 = subRects[i];
|
|
518
|
+
if (r2) results.push({ rect: r2, element: parentElement });
|
|
519
|
+
}
|
|
520
|
+
} catch {
|
|
521
|
+
} finally {
|
|
522
|
+
sub.detach?.();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
current = walker.nextNode();
|
|
526
|
+
}
|
|
527
|
+
return results;
|
|
528
|
+
};
|
|
529
|
+
var connected = (a, b) => {
|
|
530
|
+
const overlap = Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top);
|
|
531
|
+
const gap = Math.max(a.left, b.left) - Math.min(a.left + a.width, b.left + b.width);
|
|
532
|
+
return overlap >= Math.min(a.height, b.height) / 2 && gap <= Math.min(a.height, b.height) * 0.5;
|
|
533
|
+
};
|
|
534
|
+
var mergeTextRuns = (rects) => {
|
|
535
|
+
const groups = [];
|
|
536
|
+
for (const rect of rects) {
|
|
537
|
+
const group = [rect];
|
|
538
|
+
for (let i = groups.length - 1; i >= 0; i--) {
|
|
539
|
+
if (groups[i].some((member) => connected(member, rect))) {
|
|
540
|
+
group.push(...groups[i]);
|
|
541
|
+
groups.splice(i, 1);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
groups.push(group);
|
|
545
|
+
}
|
|
546
|
+
return groups.map((group) => {
|
|
547
|
+
const left = Math.min(...group.map((rect) => rect.left));
|
|
548
|
+
const top = Math.min(...group.map((rect) => rect.top));
|
|
549
|
+
return {
|
|
550
|
+
...group[0],
|
|
551
|
+
left,
|
|
552
|
+
top,
|
|
553
|
+
width: Math.max(...group.map((rect) => rect.left + rect.width)) - left,
|
|
554
|
+
height: Math.max(...group.map((rect) => rect.top + rect.height)) - top
|
|
555
|
+
};
|
|
556
|
+
});
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// src/lib/selection-background.ts
|
|
560
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
561
|
+
var createSelectionBackground = () => {
|
|
562
|
+
const overlays = /* @__PURE__ */ new Map();
|
|
563
|
+
let style;
|
|
564
|
+
const clear = (layer) => {
|
|
565
|
+
overlays.get(layer)?.remove();
|
|
566
|
+
overlays.delete(layer);
|
|
567
|
+
layer.removeAttribute("data-lector-selection-active");
|
|
568
|
+
if (overlays.size === 0) {
|
|
569
|
+
style?.remove();
|
|
570
|
+
style = void 0;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
const update = (selection, layers) => {
|
|
574
|
+
const registered = new Set(layers);
|
|
575
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
576
|
+
if (selection && !selection.isCollapsed) {
|
|
577
|
+
for (let i = 0; i < selection.rangeCount; i++) {
|
|
578
|
+
for (const { rect, element } of getTextNodeClientRects(
|
|
579
|
+
selection.getRangeAt(i)
|
|
580
|
+
)) {
|
|
581
|
+
const layer = element?.closest(".textLayer");
|
|
582
|
+
if (!layer || !element || !registered.has(layer) || rect.width <= 0 || rect.height <= 0)
|
|
583
|
+
continue;
|
|
584
|
+
let entry = byLayer.get(layer);
|
|
585
|
+
if (!entry) {
|
|
586
|
+
entry = { rects: [], element };
|
|
587
|
+
byLayer.set(layer, entry);
|
|
588
|
+
}
|
|
589
|
+
entry.rects.push(rect);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
for (const layer of registered) {
|
|
594
|
+
const entry = byLayer.get(layer);
|
|
595
|
+
if (!entry) {
|
|
596
|
+
clear(layer);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
layer.removeAttribute("data-lector-selection-active");
|
|
600
|
+
const color = getComputedStyle(
|
|
601
|
+
entry.element,
|
|
602
|
+
"::selection"
|
|
603
|
+
).backgroundColor;
|
|
604
|
+
if (!color || color === "transparent" || color === "rgba(0, 0, 0, 0)") {
|
|
605
|
+
clear(layer);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const bounds = layer.getBoundingClientRect();
|
|
609
|
+
if (!bounds.width || !bounds.height || !layer.clientWidth || !layer.clientHeight) {
|
|
610
|
+
clear(layer);
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
const scaleX = bounds.width / layer.clientWidth;
|
|
614
|
+
const scaleY = bounds.height / layer.clientHeight;
|
|
615
|
+
const rects = mergeTextRuns(
|
|
616
|
+
entry.rects.map((rect) => ({
|
|
617
|
+
left: (rect.left - bounds.left) / scaleX,
|
|
618
|
+
top: (rect.top - bounds.top) / scaleY,
|
|
619
|
+
width: rect.width / scaleX,
|
|
620
|
+
height: rect.height / scaleY
|
|
621
|
+
}))
|
|
622
|
+
);
|
|
623
|
+
let overlay = overlays.get(layer);
|
|
624
|
+
if (!overlay) {
|
|
625
|
+
overlay = document.createElementNS(SVG_NS, "svg");
|
|
626
|
+
overlay.dataset.lectorSelection = "";
|
|
627
|
+
overlay.setAttribute("aria-hidden", "true");
|
|
628
|
+
overlay.style.cssText = "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;user-select:none;transform:none;z-index:0;overflow:visible";
|
|
629
|
+
overlay.append(document.createElementNS(SVG_NS, "path"));
|
|
630
|
+
layer.append(overlay);
|
|
631
|
+
overlays.set(layer, overlay);
|
|
632
|
+
}
|
|
633
|
+
const path = overlay.firstElementChild;
|
|
634
|
+
path.setAttribute(
|
|
635
|
+
"d",
|
|
636
|
+
rects.map(
|
|
637
|
+
(rect) => `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h${-rect.width}Z`
|
|
638
|
+
).join(" ")
|
|
639
|
+
);
|
|
640
|
+
path.setAttribute("fill", color);
|
|
641
|
+
if (!style) {
|
|
642
|
+
style = document.createElement("style");
|
|
643
|
+
style.textContent = ".textLayer[data-lector-selection-active] ::selection { background-color: transparent !important; }";
|
|
644
|
+
document.head.append(style);
|
|
645
|
+
}
|
|
646
|
+
layer.setAttribute("data-lector-selection-active", "");
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
return { update, clear };
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
// src/lib/text-selection.ts
|
|
653
|
+
var interactive = 'a, button, input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="button"], [role="link"], [role="textbox"], [data-lector-selection-ignore]';
|
|
654
|
+
var distance = (value, start, end) => Math.max(start - value, 0, value - end);
|
|
655
|
+
function nearestBox(boxes, x, y) {
|
|
656
|
+
let nearest;
|
|
657
|
+
let best = Number.POSITIVE_INFINITY;
|
|
658
|
+
for (const box of boxes) {
|
|
659
|
+
const { rect } = box;
|
|
660
|
+
const dx = distance(x, rect.left, rect.right);
|
|
661
|
+
const dy = distance(y, rect.top, rect.bottom);
|
|
662
|
+
const score = dy * dy + Math.min(dx, 30) ** 2 / 100 + dx / 1e4;
|
|
663
|
+
if (score < best) {
|
|
664
|
+
best = score;
|
|
665
|
+
nearest = box;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return nearest;
|
|
669
|
+
}
|
|
670
|
+
function caretInBox({ node, rect }, x, y) {
|
|
671
|
+
const doc = node.ownerDocument;
|
|
672
|
+
const px = Math.min(Math.max(x, rect.left + 0.1), rect.right - 0.1);
|
|
673
|
+
const py = Math.min(Math.max(y, rect.top + 0.1), rect.bottom - 0.1);
|
|
674
|
+
const caretDoc = doc;
|
|
675
|
+
const position = caretDoc.caretPositionFromPoint?.(px, py);
|
|
676
|
+
if (position?.offsetNode === node) return { node, offset: position.offset };
|
|
677
|
+
const range = caretDoc.caretRangeFromPoint?.(px, py);
|
|
678
|
+
if (range?.startContainer === node)
|
|
679
|
+
return { node, offset: range.startOffset };
|
|
680
|
+
const probe = doc.createRange();
|
|
681
|
+
let offset3 = 0;
|
|
682
|
+
let best = Number.POSITIVE_INFINITY;
|
|
683
|
+
let previous;
|
|
684
|
+
for (let i = 0; i <= node.length; ) {
|
|
685
|
+
probe.setStart(node, i);
|
|
686
|
+
probe.collapse(true);
|
|
687
|
+
const rects = Array.from(probe.getClientRects());
|
|
688
|
+
if (!rects.length && i === node.length && previous) {
|
|
689
|
+
probe.setStart(node, previous.offset);
|
|
690
|
+
probe.setEnd(node, i);
|
|
691
|
+
const glyph = probe.getBoundingClientRect();
|
|
692
|
+
const prior = previous.rect;
|
|
693
|
+
rects.push(
|
|
694
|
+
new DOMRect(
|
|
695
|
+
glyph.left + glyph.right - prior.right,
|
|
696
|
+
glyph.top + glyph.bottom - prior.bottom,
|
|
697
|
+
prior.width,
|
|
698
|
+
prior.height
|
|
699
|
+
)
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
for (const r2 of rects) {
|
|
703
|
+
const score = distance(x, r2.left, r2.right) ** 2 + distance(y, r2.top, r2.bottom) ** 2;
|
|
704
|
+
if (score < best) {
|
|
705
|
+
best = score;
|
|
706
|
+
offset3 = i;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (rects[0]) previous = { offset: i, rect: rects[0] };
|
|
710
|
+
i += (node.data.codePointAt(i) ?? 0) > 65535 ? 2 : 1;
|
|
711
|
+
}
|
|
712
|
+
return { node, offset: offset3 };
|
|
713
|
+
}
|
|
714
|
+
function createSelectionManager(viewport, doc, isPersistent) {
|
|
715
|
+
const layers = /* @__PURE__ */ new Set();
|
|
716
|
+
const background = createSelectionBackground();
|
|
717
|
+
const container = viewport.current;
|
|
718
|
+
const win = doc.defaultView;
|
|
719
|
+
const controller = new AbortController();
|
|
720
|
+
const { signal } = controller;
|
|
721
|
+
let drag = null;
|
|
722
|
+
let boxes = null;
|
|
723
|
+
let scrollLeft = container?.scrollLeft;
|
|
724
|
+
let scrollTop = container?.scrollTop;
|
|
725
|
+
let frame = 0;
|
|
726
|
+
let mousePointer = true;
|
|
727
|
+
const invalidate = () => {
|
|
728
|
+
boxes = null;
|
|
729
|
+
if (drag && !frame) frame = win.requestAnimationFrame(tick);
|
|
730
|
+
};
|
|
731
|
+
const contains = (node) => !!node && [...layers].some((layer) => layer.contains(node));
|
|
732
|
+
const reset = () => {
|
|
733
|
+
drag = null;
|
|
734
|
+
boxes = null;
|
|
735
|
+
win.cancelAnimationFrame(frame);
|
|
736
|
+
frame = 0;
|
|
737
|
+
for (const layer of layers) layer.classList.remove("selecting");
|
|
738
|
+
};
|
|
739
|
+
const readBoxes = () => {
|
|
740
|
+
if (boxes) return boxes;
|
|
741
|
+
boxes = [];
|
|
742
|
+
for (const layer of layers) {
|
|
743
|
+
const walker = doc.createTreeWalker(layer, NodeFilter.SHOW_TEXT);
|
|
744
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
745
|
+
const parent = node.parentElement;
|
|
746
|
+
if (!node.textContent?.trim() || !parent || parent.closest('[role="img"], .endOfContent') || win.getComputedStyle(parent).userSelect === "none")
|
|
747
|
+
continue;
|
|
748
|
+
const range = doc.createRange();
|
|
749
|
+
range.selectNodeContents(node);
|
|
750
|
+
for (const rect of range.getClientRects()) {
|
|
751
|
+
if (rect.width > 0 && rect.height > 0)
|
|
752
|
+
boxes.push({ node, rect });
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return boxes;
|
|
757
|
+
};
|
|
758
|
+
const resolve = (x, y) => {
|
|
759
|
+
if (scrollLeft !== container?.scrollLeft || scrollTop !== container?.scrollTop) {
|
|
760
|
+
boxes = null;
|
|
761
|
+
scrollLeft = container?.scrollLeft;
|
|
762
|
+
scrollTop = container?.scrollTop;
|
|
763
|
+
}
|
|
764
|
+
const box = nearestBox(readBoxes(), x, y);
|
|
765
|
+
return box ? caretInBox(box, x, y) : null;
|
|
766
|
+
};
|
|
767
|
+
const update = () => {
|
|
768
|
+
if (!drag) return;
|
|
769
|
+
if (!drag.anchor.node.isConnected) {
|
|
770
|
+
reset();
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
const focus = resolve(drag.x, drag.y);
|
|
774
|
+
const selection = doc.getSelection();
|
|
775
|
+
if (!focus || !selection) return;
|
|
776
|
+
if (selection.anchorNode === drag.anchor.node && selection.anchorOffset === drag.anchor.offset && selection.focusNode === focus.node && selection.focusOffset === focus.offset)
|
|
777
|
+
return;
|
|
778
|
+
selection.setBaseAndExtent(
|
|
779
|
+
drag.anchor.node,
|
|
780
|
+
drag.anchor.offset,
|
|
781
|
+
focus.node,
|
|
782
|
+
focus.offset
|
|
783
|
+
);
|
|
784
|
+
};
|
|
785
|
+
const tick = () => {
|
|
786
|
+
frame = 0;
|
|
787
|
+
if (!drag) return;
|
|
788
|
+
let scrolled = false;
|
|
789
|
+
const container2 = viewport.current;
|
|
790
|
+
if (container2 && !isPersistent()) {
|
|
791
|
+
const r2 = container2.getBoundingClientRect();
|
|
792
|
+
const edge = 24;
|
|
793
|
+
const speed = (point, start, end) => point < start + edge ? -Math.min(18, (start + edge - point) / 2) : point > end - edge ? Math.min(18, (point - end + edge) / 2) : 0;
|
|
794
|
+
const dx = speed(drag.x, r2.left, r2.right);
|
|
795
|
+
const dy = speed(drag.y, r2.top, r2.bottom);
|
|
796
|
+
if (dx || dy) {
|
|
797
|
+
const left = container2.scrollLeft;
|
|
798
|
+
const top = container2.scrollTop;
|
|
799
|
+
container2.scrollBy(dx, dy);
|
|
800
|
+
if (container2.scrollLeft !== left || container2.scrollTop !== top) {
|
|
801
|
+
boxes = null;
|
|
802
|
+
scrolled = true;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
update();
|
|
807
|
+
if (drag && scrolled && !frame) frame = win.requestAnimationFrame(tick);
|
|
808
|
+
};
|
|
809
|
+
doc.addEventListener(
|
|
810
|
+
"pointerdown",
|
|
811
|
+
(event) => {
|
|
812
|
+
mousePointer = event.pointerType === "mouse";
|
|
813
|
+
if (!mousePointer) reset();
|
|
814
|
+
},
|
|
815
|
+
{ signal }
|
|
816
|
+
);
|
|
817
|
+
doc.addEventListener(
|
|
818
|
+
"mousedown",
|
|
819
|
+
(event) => {
|
|
820
|
+
reset();
|
|
821
|
+
if (!mousePointer || event.defaultPrevented || event.button !== 0 || event.ctrlKey || event.metaKey || event.altKey || event.detail > 1)
|
|
822
|
+
return;
|
|
823
|
+
const target = event.target;
|
|
824
|
+
if (!(target instanceof Element) || target.closest(interactive)) return;
|
|
825
|
+
const onPage = [...layers].some(
|
|
826
|
+
(layer) => layer.parentElement?.contains(target)
|
|
827
|
+
);
|
|
828
|
+
if (!onPage && !(viewport.current === target || viewport.current?.contains(target) && target.contains([...layers][0] ?? null)))
|
|
829
|
+
return;
|
|
830
|
+
if (target instanceof HTMLElement && /auto|scroll/.test(win.getComputedStyle(target).overflow)) {
|
|
831
|
+
const r2 = target.getBoundingClientRect();
|
|
832
|
+
const scrollbarX = (target.offsetWidth - target.clientWidth - target.clientLeft * 2) * r2.width / target.offsetWidth;
|
|
833
|
+
const scrollbarY = (target.offsetHeight - target.clientHeight - target.clientTop * 2) * r2.height / target.offsetHeight;
|
|
834
|
+
if (event.clientX >= r2.right - scrollbarX || event.clientY >= r2.bottom - scrollbarY)
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const focus = resolve(event.clientX, event.clientY);
|
|
838
|
+
const selection = doc.getSelection();
|
|
839
|
+
if (!focus || !selection) return;
|
|
840
|
+
const anchor = event.shiftKey && contains(selection.anchorNode) ? { node: selection.anchorNode, offset: selection.anchorOffset } : focus;
|
|
841
|
+
event.preventDefault();
|
|
842
|
+
if (viewport.current && viewport.current.tabIndex >= 0)
|
|
843
|
+
viewport.current.focus({ preventScroll: true });
|
|
844
|
+
drag = { anchor, x: event.clientX, y: event.clientY };
|
|
845
|
+
for (const layer of layers) layer.classList.add("selecting");
|
|
846
|
+
update();
|
|
847
|
+
},
|
|
848
|
+
{ signal }
|
|
849
|
+
);
|
|
850
|
+
doc.addEventListener(
|
|
851
|
+
"mousemove",
|
|
852
|
+
(event) => {
|
|
853
|
+
if (!drag) return;
|
|
854
|
+
if (!(event.buttons & 1)) {
|
|
855
|
+
reset();
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
event.preventDefault();
|
|
859
|
+
drag.x = event.clientX;
|
|
860
|
+
drag.y = event.clientY;
|
|
861
|
+
update();
|
|
862
|
+
if (!frame) frame = win.requestAnimationFrame(tick);
|
|
863
|
+
},
|
|
864
|
+
{ signal }
|
|
865
|
+
);
|
|
866
|
+
doc.addEventListener(
|
|
867
|
+
"mouseup",
|
|
868
|
+
(event) => {
|
|
869
|
+
if (drag && event.button === 0) {
|
|
870
|
+
drag.x = event.clientX;
|
|
871
|
+
drag.y = event.clientY;
|
|
872
|
+
update();
|
|
873
|
+
reset();
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
{ signal }
|
|
877
|
+
);
|
|
878
|
+
doc.addEventListener(
|
|
879
|
+
"selectionchange",
|
|
880
|
+
() => background.update(doc.getSelection(), layers),
|
|
881
|
+
{ signal }
|
|
882
|
+
);
|
|
883
|
+
doc.addEventListener("scroll", invalidate, {
|
|
884
|
+
signal,
|
|
885
|
+
capture: true,
|
|
886
|
+
passive: true
|
|
887
|
+
});
|
|
888
|
+
win.addEventListener("resize", invalidate, { signal });
|
|
889
|
+
win.addEventListener("blur", reset, { signal });
|
|
890
|
+
doc.addEventListener("pointercancel", reset, { signal });
|
|
891
|
+
doc.addEventListener(
|
|
892
|
+
"keydown",
|
|
893
|
+
(event) => {
|
|
894
|
+
if (event.key === "Escape") reset();
|
|
895
|
+
},
|
|
896
|
+
{ signal }
|
|
897
|
+
);
|
|
898
|
+
return {
|
|
899
|
+
resolve,
|
|
900
|
+
add(layer) {
|
|
901
|
+
layers.add(layer);
|
|
902
|
+
if (container) viewportManagers.set(container, { resolve });
|
|
903
|
+
invalidate();
|
|
904
|
+
return () => {
|
|
905
|
+
background.clear(layer);
|
|
906
|
+
layers.delete(layer);
|
|
907
|
+
layer.classList.remove("selecting");
|
|
908
|
+
invalidate();
|
|
909
|
+
if (drag && layer.contains(drag.anchor.node)) reset();
|
|
910
|
+
if (!layers.size) {
|
|
911
|
+
reset();
|
|
912
|
+
controller.abort();
|
|
913
|
+
managers.delete(viewport);
|
|
914
|
+
if (container) viewportManagers.delete(container);
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
var viewportManagers = /* @__PURE__ */ new WeakMap();
|
|
921
|
+
function resolveTextSelectionCaret(viewport, x, y) {
|
|
922
|
+
return viewportManagers.get(viewport)?.resolve(x, y);
|
|
923
|
+
}
|
|
924
|
+
var managers = /* @__PURE__ */ new WeakMap();
|
|
925
|
+
function bindTextSelection(layer, viewport, isPersistent = () => false) {
|
|
926
|
+
let manager = managers.get(viewport);
|
|
927
|
+
if (!manager) {
|
|
928
|
+
manager = createSelectionManager(
|
|
929
|
+
viewport,
|
|
930
|
+
layer.ownerDocument,
|
|
931
|
+
isPersistent
|
|
932
|
+
);
|
|
933
|
+
managers.set(viewport, manager);
|
|
934
|
+
}
|
|
935
|
+
return manager.add(layer);
|
|
936
|
+
}
|
|
937
|
+
|
|
477
938
|
// src/lib/pdf-selection.ts
|
|
478
939
|
var sameAnchor = (a, b) => a.pageNumber === b.pageNumber && a.offset === b.offset;
|
|
479
940
|
var compare = (a, b) => a.pageNumber - b.pageNumber || a.offset - b.offset;
|
|
@@ -504,6 +965,9 @@ var PDFSelectionController = class {
|
|
|
504
965
|
this.listeners.delete(listener);
|
|
505
966
|
};
|
|
506
967
|
};
|
|
968
|
+
get isConnected() {
|
|
969
|
+
return this.container !== null;
|
|
970
|
+
}
|
|
507
971
|
get isDragging() {
|
|
508
972
|
return this.drag !== null;
|
|
509
973
|
}
|
|
@@ -848,6 +1312,8 @@ var PDFSelectionController = class {
|
|
|
848
1312
|
});
|
|
849
1313
|
}
|
|
850
1314
|
caret(x, y) {
|
|
1315
|
+
const geometry = resolveTextSelectionCaret(this.container, x, y);
|
|
1316
|
+
if (geometry) return this.anchorFor(geometry.node, geometry.offset);
|
|
851
1317
|
const owner = this.container.ownerDocument;
|
|
852
1318
|
const doc = owner;
|
|
853
1319
|
const position = doc.caretPositionFromPoint?.(x, y);
|
|
@@ -936,13 +1402,15 @@ var PDFSelectionController = class {
|
|
|
936
1402
|
const down = (event) => {
|
|
937
1403
|
if (event.defaultPrevented) return;
|
|
938
1404
|
const target = event.target instanceof Element ? event.target : null;
|
|
939
|
-
if (target?.closest(
|
|
1405
|
+
if (target?.closest(
|
|
1406
|
+
'button, a, input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role=button], [role=link], [role=textbox], [data-selection-tooltip], [data-lector-selection-ignore]'
|
|
1407
|
+
))
|
|
940
1408
|
return;
|
|
941
1409
|
if (!container.contains(event.target)) {
|
|
942
1410
|
this.clear();
|
|
943
1411
|
return;
|
|
944
1412
|
}
|
|
945
|
-
if (event.defaultPrevented || event.button !== 0 || event.pointerType === "touch" || !container.contains(event.target))
|
|
1413
|
+
if (event.defaultPrevented || event.button !== 0 || event.pointerType === "touch" || event.ctrlKey || event.metaKey || event.altKey || !container.contains(event.target))
|
|
946
1414
|
return;
|
|
947
1415
|
const point = this.caret(event.clientX, event.clientY);
|
|
948
1416
|
if (!point) return;
|
|
@@ -3846,88 +4314,6 @@ var CanvasLayer = memo(
|
|
|
3846
4314
|
);
|
|
3847
4315
|
CanvasLayer.displayName = "CanvasLayer";
|
|
3848
4316
|
|
|
3849
|
-
// src/lib/selection-rects.ts
|
|
3850
|
-
var getTextNodeClientRects = (range) => {
|
|
3851
|
-
const root = range.commonAncestorContainer;
|
|
3852
|
-
const ownerDoc = root.ownerDocument ?? document;
|
|
3853
|
-
if (root.nodeType === Node.TEXT_NODE) {
|
|
3854
|
-
const parentElement = root.parentElement;
|
|
3855
|
-
return Array.from(range.getClientRects()).map((rect) => ({
|
|
3856
|
-
rect,
|
|
3857
|
-
element: parentElement
|
|
3858
|
-
}));
|
|
3859
|
-
}
|
|
3860
|
-
const walker = ownerDoc.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
3861
|
-
acceptNode(node) {
|
|
3862
|
-
if (!node.nodeValue || node.nodeValue.length === 0) {
|
|
3863
|
-
return NodeFilter.FILTER_REJECT;
|
|
3864
|
-
}
|
|
3865
|
-
try {
|
|
3866
|
-
return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
3867
|
-
} catch {
|
|
3868
|
-
return NodeFilter.FILTER_REJECT;
|
|
3869
|
-
}
|
|
3870
|
-
}
|
|
3871
|
-
});
|
|
3872
|
-
const results = [];
|
|
3873
|
-
let current = walker.nextNode();
|
|
3874
|
-
while (current) {
|
|
3875
|
-
const textNode = current;
|
|
3876
|
-
const parentElement = textNode.parentElement;
|
|
3877
|
-
const length = textNode.nodeValue?.length ?? 0;
|
|
3878
|
-
const isStartNode = textNode === range.startContainer;
|
|
3879
|
-
const isEndNode = textNode === range.endContainer;
|
|
3880
|
-
const start = isStartNode ? range.startOffset : 0;
|
|
3881
|
-
const end = isEndNode ? range.endOffset : length;
|
|
3882
|
-
if (end > start) {
|
|
3883
|
-
const sub = ownerDoc.createRange();
|
|
3884
|
-
try {
|
|
3885
|
-
sub.setStart(textNode, start);
|
|
3886
|
-
sub.setEnd(textNode, end);
|
|
3887
|
-
const subRects = sub.getClientRects();
|
|
3888
|
-
for (let i = 0; i < subRects.length; i++) {
|
|
3889
|
-
const r2 = subRects[i];
|
|
3890
|
-
if (r2) results.push({ rect: r2, element: parentElement });
|
|
3891
|
-
}
|
|
3892
|
-
} catch {
|
|
3893
|
-
} finally {
|
|
3894
|
-
sub.detach?.();
|
|
3895
|
-
}
|
|
3896
|
-
}
|
|
3897
|
-
current = walker.nextNode();
|
|
3898
|
-
}
|
|
3899
|
-
return results;
|
|
3900
|
-
};
|
|
3901
|
-
var connected = (a, b) => {
|
|
3902
|
-
const overlap = Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top);
|
|
3903
|
-
const gap = Math.max(a.left, b.left) - Math.min(a.left + a.width, b.left + b.width);
|
|
3904
|
-
return overlap >= Math.min(a.height, b.height) / 2 && gap <= Math.min(a.height, b.height) * 0.5;
|
|
3905
|
-
};
|
|
3906
|
-
var mergeTextRuns = (rects) => {
|
|
3907
|
-
const groups = [];
|
|
3908
|
-
for (const rect of rects) {
|
|
3909
|
-
const group = [rect];
|
|
3910
|
-
for (let i = groups.length - 1; i >= 0; i--) {
|
|
3911
|
-
if (groups[i].some((member) => connected(member, rect))) {
|
|
3912
|
-
group.push(...groups[i]);
|
|
3913
|
-
groups.splice(i, 1);
|
|
3914
|
-
}
|
|
3915
|
-
}
|
|
3916
|
-
groups.push(group);
|
|
3917
|
-
}
|
|
3918
|
-
return groups.map((group) => {
|
|
3919
|
-
const left = Math.min(...group.map((rect) => rect.left));
|
|
3920
|
-
const top = Math.min(...group.map((rect) => rect.top));
|
|
3921
|
-
return {
|
|
3922
|
-
...group[0],
|
|
3923
|
-
left,
|
|
3924
|
-
top,
|
|
3925
|
-
width: Math.max(...group.map((rect) => rect.left + rect.width)) - left,
|
|
3926
|
-
height: Math.max(...group.map((rect) => rect.top + rect.height)) - top
|
|
3927
|
-
};
|
|
3928
|
-
});
|
|
3929
|
-
};
|
|
3930
|
-
|
|
3931
4317
|
// src/lib/selection-text.ts
|
|
3932
4318
|
function textLayer(node) {
|
|
3933
4319
|
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
@@ -4760,244 +5146,6 @@ var HighlightLayer = forwardRef(({ asChild, className, style, ...props }, ref) =
|
|
|
4760
5146
|
}) });
|
|
4761
5147
|
});
|
|
4762
5148
|
HighlightLayer.displayName = "HighlightLayer";
|
|
4763
|
-
|
|
4764
|
-
// src/lib/selection-background.ts
|
|
4765
|
-
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
4766
|
-
var createSelectionBackground = () => {
|
|
4767
|
-
const overlays = /* @__PURE__ */ new Map();
|
|
4768
|
-
let style;
|
|
4769
|
-
const clear = (layer) => {
|
|
4770
|
-
overlays.get(layer)?.remove();
|
|
4771
|
-
overlays.delete(layer);
|
|
4772
|
-
layer.removeAttribute("data-lector-selection-active");
|
|
4773
|
-
if (overlays.size === 0) {
|
|
4774
|
-
style?.remove();
|
|
4775
|
-
style = void 0;
|
|
4776
|
-
}
|
|
4777
|
-
};
|
|
4778
|
-
const update = (selection, layers) => {
|
|
4779
|
-
const registered = new Set(layers);
|
|
4780
|
-
const byLayer = /* @__PURE__ */ new Map();
|
|
4781
|
-
if (selection && !selection.isCollapsed) {
|
|
4782
|
-
for (let i = 0; i < selection.rangeCount; i++) {
|
|
4783
|
-
for (const { rect, element } of getTextNodeClientRects(
|
|
4784
|
-
selection.getRangeAt(i)
|
|
4785
|
-
)) {
|
|
4786
|
-
const layer = element?.closest(".textLayer");
|
|
4787
|
-
if (!layer || !element || !registered.has(layer) || rect.width <= 0 || rect.height <= 0)
|
|
4788
|
-
continue;
|
|
4789
|
-
let entry = byLayer.get(layer);
|
|
4790
|
-
if (!entry) {
|
|
4791
|
-
entry = { rects: [], element };
|
|
4792
|
-
byLayer.set(layer, entry);
|
|
4793
|
-
}
|
|
4794
|
-
entry.rects.push(rect);
|
|
4795
|
-
}
|
|
4796
|
-
}
|
|
4797
|
-
}
|
|
4798
|
-
for (const layer of registered) {
|
|
4799
|
-
const entry = byLayer.get(layer);
|
|
4800
|
-
if (!entry) {
|
|
4801
|
-
clear(layer);
|
|
4802
|
-
continue;
|
|
4803
|
-
}
|
|
4804
|
-
layer.removeAttribute("data-lector-selection-active");
|
|
4805
|
-
const color = getComputedStyle(
|
|
4806
|
-
entry.element,
|
|
4807
|
-
"::selection"
|
|
4808
|
-
).backgroundColor;
|
|
4809
|
-
if (!color || color === "transparent" || color === "rgba(0, 0, 0, 0)") {
|
|
4810
|
-
clear(layer);
|
|
4811
|
-
continue;
|
|
4812
|
-
}
|
|
4813
|
-
const bounds = layer.getBoundingClientRect();
|
|
4814
|
-
if (!bounds.width || !bounds.height || !layer.clientWidth || !layer.clientHeight) {
|
|
4815
|
-
clear(layer);
|
|
4816
|
-
continue;
|
|
4817
|
-
}
|
|
4818
|
-
const scaleX = bounds.width / layer.clientWidth;
|
|
4819
|
-
const scaleY = bounds.height / layer.clientHeight;
|
|
4820
|
-
const rects = mergeTextRuns(
|
|
4821
|
-
entry.rects.map((rect) => ({
|
|
4822
|
-
left: (rect.left - bounds.left) / scaleX,
|
|
4823
|
-
top: (rect.top - bounds.top) / scaleY,
|
|
4824
|
-
width: rect.width / scaleX,
|
|
4825
|
-
height: rect.height / scaleY
|
|
4826
|
-
}))
|
|
4827
|
-
);
|
|
4828
|
-
let overlay = overlays.get(layer);
|
|
4829
|
-
if (!overlay) {
|
|
4830
|
-
overlay = document.createElementNS(SVG_NS, "svg");
|
|
4831
|
-
overlay.dataset.lectorSelection = "";
|
|
4832
|
-
overlay.setAttribute("aria-hidden", "true");
|
|
4833
|
-
overlay.style.cssText = "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;user-select:none;transform:none;z-index:0;overflow:visible";
|
|
4834
|
-
overlay.append(document.createElementNS(SVG_NS, "path"));
|
|
4835
|
-
layer.append(overlay);
|
|
4836
|
-
overlays.set(layer, overlay);
|
|
4837
|
-
}
|
|
4838
|
-
const path = overlay.firstElementChild;
|
|
4839
|
-
path.setAttribute(
|
|
4840
|
-
"d",
|
|
4841
|
-
rects.map(
|
|
4842
|
-
(rect) => `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h${-rect.width}Z`
|
|
4843
|
-
).join(" ")
|
|
4844
|
-
);
|
|
4845
|
-
path.setAttribute("fill", color);
|
|
4846
|
-
if (!style) {
|
|
4847
|
-
style = document.createElement("style");
|
|
4848
|
-
style.textContent = ".textLayer[data-lector-selection-active] ::selection { background-color: transparent !important; }";
|
|
4849
|
-
document.head.append(style);
|
|
4850
|
-
}
|
|
4851
|
-
layer.setAttribute("data-lector-selection-active", "");
|
|
4852
|
-
}
|
|
4853
|
-
};
|
|
4854
|
-
return { update, clear };
|
|
4855
|
-
};
|
|
4856
|
-
|
|
4857
|
-
// src/hooks/layers/useTextLayer.tsx
|
|
4858
|
-
var createTextSelectionManager = () => {
|
|
4859
|
-
const textLayers = /* @__PURE__ */ new Map();
|
|
4860
|
-
const selectionBackground = createSelectionBackground();
|
|
4861
|
-
let selectionChangeAbortController = null;
|
|
4862
|
-
let isPointerDown = false;
|
|
4863
|
-
let prevRange = null;
|
|
4864
|
-
let isFirefox;
|
|
4865
|
-
const removeGlobalSelectionListener = (textLayerDiv) => {
|
|
4866
|
-
selectionBackground.clear(textLayerDiv);
|
|
4867
|
-
textLayers.delete(textLayerDiv);
|
|
4868
|
-
if (textLayers.size === 0) {
|
|
4869
|
-
selectionChangeAbortController?.abort();
|
|
4870
|
-
selectionChangeAbortController = null;
|
|
4871
|
-
}
|
|
4872
|
-
};
|
|
4873
|
-
const enableGlobalSelectionListener = () => {
|
|
4874
|
-
if (selectionChangeAbortController) {
|
|
4875
|
-
return;
|
|
4876
|
-
}
|
|
4877
|
-
selectionChangeAbortController = new AbortController();
|
|
4878
|
-
const { signal } = selectionChangeAbortController;
|
|
4879
|
-
const reset = (endDiv, textLayer2) => {
|
|
4880
|
-
if (endDiv.parentNode !== textLayer2) {
|
|
4881
|
-
textLayer2.appendChild(endDiv);
|
|
4882
|
-
}
|
|
4883
|
-
endDiv.style.width = "";
|
|
4884
|
-
endDiv.style.height = "";
|
|
4885
|
-
textLayer2.classList.remove("selecting");
|
|
4886
|
-
};
|
|
4887
|
-
document.addEventListener(
|
|
4888
|
-
"pointerdown",
|
|
4889
|
-
() => {
|
|
4890
|
-
isPointerDown = true;
|
|
4891
|
-
},
|
|
4892
|
-
{ signal }
|
|
4893
|
-
);
|
|
4894
|
-
document.addEventListener(
|
|
4895
|
-
"pointerup",
|
|
4896
|
-
() => {
|
|
4897
|
-
isPointerDown = false;
|
|
4898
|
-
textLayers.forEach(reset);
|
|
4899
|
-
},
|
|
4900
|
-
{ signal }
|
|
4901
|
-
);
|
|
4902
|
-
window.addEventListener(
|
|
4903
|
-
"blur",
|
|
4904
|
-
() => {
|
|
4905
|
-
isPointerDown = false;
|
|
4906
|
-
textLayers.forEach(reset);
|
|
4907
|
-
},
|
|
4908
|
-
{ signal }
|
|
4909
|
-
);
|
|
4910
|
-
document.addEventListener(
|
|
4911
|
-
"keyup",
|
|
4912
|
-
() => {
|
|
4913
|
-
if (!isPointerDown) {
|
|
4914
|
-
textLayers.forEach(reset);
|
|
4915
|
-
}
|
|
4916
|
-
},
|
|
4917
|
-
{ signal }
|
|
4918
|
-
);
|
|
4919
|
-
document.addEventListener(
|
|
4920
|
-
"selectionchange",
|
|
4921
|
-
() => {
|
|
4922
|
-
const selection = document.getSelection();
|
|
4923
|
-
selectionBackground.update(selection, textLayers.keys());
|
|
4924
|
-
if (!selection || selection.rangeCount === 0) {
|
|
4925
|
-
textLayers.forEach(reset);
|
|
4926
|
-
return;
|
|
4927
|
-
}
|
|
4928
|
-
const activeTextLayers = /* @__PURE__ */ new Set();
|
|
4929
|
-
for (let i = 0; i < selection.rangeCount; i++) {
|
|
4930
|
-
const range = selection.getRangeAt(i);
|
|
4931
|
-
for (const textLayerDiv of textLayers.keys()) {
|
|
4932
|
-
if (!activeTextLayers.has(textLayerDiv) && range.intersectsNode(textLayerDiv)) {
|
|
4933
|
-
activeTextLayers.add(textLayerDiv);
|
|
4934
|
-
}
|
|
4935
|
-
}
|
|
4936
|
-
}
|
|
4937
|
-
for (const [textLayerDiv, endDiv] of textLayers) {
|
|
4938
|
-
if (activeTextLayers.has(textLayerDiv)) {
|
|
4939
|
-
textLayerDiv.classList.add("selecting");
|
|
4940
|
-
} else {
|
|
4941
|
-
reset(endDiv, textLayerDiv);
|
|
4942
|
-
}
|
|
4943
|
-
}
|
|
4944
|
-
if (isFirefox === void 0) {
|
|
4945
|
-
const firstTextLayer = textLayers.keys().next().value;
|
|
4946
|
-
if (firstTextLayer) {
|
|
4947
|
-
isFirefox = getComputedStyle(firstTextLayer).getPropertyValue(
|
|
4948
|
-
"-moz-user-select"
|
|
4949
|
-
) === "none";
|
|
4950
|
-
}
|
|
4951
|
-
}
|
|
4952
|
-
if (isFirefox) {
|
|
4953
|
-
return;
|
|
4954
|
-
}
|
|
4955
|
-
try {
|
|
4956
|
-
const range = selection.getRangeAt(0);
|
|
4957
|
-
const modifyStart = prevRange && (range.compareBoundaryPoints(Range.END_TO_END, prevRange) === 0 || range.compareBoundaryPoints(Range.START_TO_END, prevRange) === 0);
|
|
4958
|
-
let anchor = modifyStart ? range.startContainer : range.endContainer;
|
|
4959
|
-
if (anchor.nodeType === Node.TEXT_NODE) {
|
|
4960
|
-
anchor = anchor.parentNode;
|
|
4961
|
-
}
|
|
4962
|
-
const parentTextLayer = anchor.parentElement?.closest(
|
|
4963
|
-
".textLayer"
|
|
4964
|
-
);
|
|
4965
|
-
const endDiv = textLayers.get(parentTextLayer);
|
|
4966
|
-
if (endDiv && parentTextLayer) {
|
|
4967
|
-
endDiv.style.width = parentTextLayer.style.width;
|
|
4968
|
-
endDiv.style.height = parentTextLayer.style.height;
|
|
4969
|
-
const insertTarget = modifyStart ? anchor : anchor.nextSibling;
|
|
4970
|
-
if (anchor.parentElement && insertTarget) {
|
|
4971
|
-
anchor.parentElement.insertBefore(endDiv, insertTarget);
|
|
4972
|
-
}
|
|
4973
|
-
}
|
|
4974
|
-
prevRange = range.cloneRange();
|
|
4975
|
-
} catch {
|
|
4976
|
-
}
|
|
4977
|
-
},
|
|
4978
|
-
{ signal }
|
|
4979
|
-
);
|
|
4980
|
-
};
|
|
4981
|
-
const bindMouseEvents2 = (textLayerDiv, endOfContent) => {
|
|
4982
|
-
if (textLayerDiv._textSelectionBound) {
|
|
4983
|
-
return;
|
|
4984
|
-
}
|
|
4985
|
-
textLayerDiv._textSelectionBound = true;
|
|
4986
|
-
textLayers.set(textLayerDiv, endOfContent);
|
|
4987
|
-
enableGlobalSelectionListener();
|
|
4988
|
-
const handleMouseDown = () => {
|
|
4989
|
-
textLayerDiv.classList.add("selecting");
|
|
4990
|
-
};
|
|
4991
|
-
textLayerDiv.addEventListener("mousedown", handleMouseDown);
|
|
4992
|
-
textLayerDiv._cleanupTextSelection = () => {
|
|
4993
|
-
textLayerDiv.removeEventListener("mousedown", handleMouseDown);
|
|
4994
|
-
removeGlobalSelectionListener(textLayerDiv);
|
|
4995
|
-
delete textLayerDiv._textSelectionBound;
|
|
4996
|
-
};
|
|
4997
|
-
};
|
|
4998
|
-
return bindMouseEvents2;
|
|
4999
|
-
};
|
|
5000
|
-
var bindMouseEvents = createTextSelectionManager();
|
|
5001
5149
|
var TEXT_BUILD_IDLE_MS = 300;
|
|
5002
5150
|
var TEXT_SCROLL_SETTLE_MS = 160;
|
|
5003
5151
|
var VIEWPORT_ATTACH_MAX_FRAMES = 300;
|
|
@@ -5016,6 +5164,7 @@ var useTextLayer = () => {
|
|
|
5016
5164
|
}
|
|
5017
5165
|
let isCancelled = false;
|
|
5018
5166
|
let unregisterSelection;
|
|
5167
|
+
let cleanupMouseSelection;
|
|
5019
5168
|
let buildTimer = null;
|
|
5020
5169
|
let unsubscribe = null;
|
|
5021
5170
|
let built = false;
|
|
@@ -5043,10 +5192,11 @@ var useTextLayer = () => {
|
|
|
5043
5192
|
if (isCancelled || gen !== buildGen || textContainerRef.current !== textContainer) {
|
|
5044
5193
|
return;
|
|
5045
5194
|
}
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5195
|
+
cleanupMouseSelection = bindTextSelection(
|
|
5196
|
+
textContainer,
|
|
5197
|
+
viewportRef,
|
|
5198
|
+
() => selection?.isConnected ?? false
|
|
5199
|
+
);
|
|
5050
5200
|
const viewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
5051
5201
|
const unrotated = pdfPageProxy.getViewport({ scale: 1, rotation: 0 });
|
|
5052
5202
|
textContainer.style.width = `${unrotated.width}px`;
|
|
@@ -5121,10 +5271,7 @@ var useTextLayer = () => {
|
|
|
5121
5271
|
textLayerRef.current.cancel();
|
|
5122
5272
|
textLayerRef.current = null;
|
|
5123
5273
|
}
|
|
5124
|
-
|
|
5125
|
-
textContainer._cleanupTextSelection();
|
|
5126
|
-
delete textContainer._cleanupTextSelection;
|
|
5127
|
-
}
|
|
5274
|
+
cleanupMouseSelection?.();
|
|
5128
5275
|
};
|
|
5129
5276
|
}, [
|
|
5130
5277
|
pdfPageProxy.streamTextContent,
|
|
@@ -5727,9 +5874,9 @@ var useVisiblePage = ({
|
|
|
5727
5874
|
let smallestDistance = Infinity;
|
|
5728
5875
|
for (const item of virtualItems) {
|
|
5729
5876
|
const itemCenter = item.start + item.size / 2;
|
|
5730
|
-
const
|
|
5731
|
-
if (
|
|
5732
|
-
smallestDistance =
|
|
5877
|
+
const distance2 = Math.abs(itemCenter - viewportCenter);
|
|
5878
|
+
if (distance2 < smallestDistance * 0.8) {
|
|
5879
|
+
smallestDistance = distance2;
|
|
5733
5880
|
closestIndex = item.index;
|
|
5734
5881
|
}
|
|
5735
5882
|
}
|
|
@@ -5789,9 +5936,9 @@ var V = {
|
|
|
5789
5936
|
v1[1] -= v2[1];
|
|
5790
5937
|
}
|
|
5791
5938
|
};
|
|
5792
|
-
function rubberband(
|
|
5793
|
-
if (dimension === 0 || Math.abs(dimension) === Infinity) return Math.pow(
|
|
5794
|
-
return
|
|
5939
|
+
function rubberband(distance2, dimension, constant) {
|
|
5940
|
+
if (dimension === 0 || Math.abs(dimension) === Infinity) return Math.pow(distance2, constant * 5);
|
|
5941
|
+
return distance2 * dimension * constant / (dimension + constant * distance2);
|
|
5795
5942
|
}
|
|
5796
5943
|
function rubberbandIfOutOfBounds(position, min, max, constant = 0.15) {
|
|
5797
5944
|
if (constant === 0) return clamp2(position, min, max);
|
|
@@ -5934,12 +6081,12 @@ function distanceAngle(P1, P2) {
|
|
|
5934
6081
|
const dy = P2.clientY - P1.clientY;
|
|
5935
6082
|
const cx = (P2.clientX + P1.clientX) / 2;
|
|
5936
6083
|
const cy = (P2.clientY + P1.clientY) / 2;
|
|
5937
|
-
const
|
|
6084
|
+
const distance2 = Math.hypot(dx, dy);
|
|
5938
6085
|
const angle = -(Math.atan2(dx, dy) * 180) / Math.PI;
|
|
5939
6086
|
const origin = [cx, cy];
|
|
5940
6087
|
return {
|
|
5941
6088
|
angle,
|
|
5942
|
-
distance,
|
|
6089
|
+
distance: distance2,
|
|
5943
6090
|
origin
|
|
5944
6091
|
};
|
|
5945
6092
|
} catch (_unused) {
|
|
@@ -6733,12 +6880,12 @@ var dragConfigResolver = _objectSpread2(_objectSpread2({}, coordinatesConfigReso
|
|
|
6733
6880
|
},
|
|
6734
6881
|
swipe({
|
|
6735
6882
|
velocity = DEFAULT_SWIPE_VELOCITY,
|
|
6736
|
-
distance = DEFAULT_SWIPE_DISTANCE,
|
|
6883
|
+
distance: distance2 = DEFAULT_SWIPE_DISTANCE,
|
|
6737
6884
|
duration = DEFAULT_SWIPE_DURATION
|
|
6738
6885
|
} = {}) {
|
|
6739
6886
|
return {
|
|
6740
6887
|
velocity: this.transform(V.toVector(velocity)),
|
|
6741
|
-
distance: this.transform(V.toVector(
|
|
6888
|
+
distance: this.transform(V.toVector(distance2)),
|
|
6742
6889
|
duration
|
|
6743
6890
|
};
|
|
6744
6891
|
},
|
|
@@ -9305,7 +9452,7 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
9305
9452
|
const query = normalizeSearchText(searchText, options).text;
|
|
9306
9453
|
if (!query.trim()) return empty;
|
|
9307
9454
|
const maxDistance = Math.floor(query.length * (1 - threshold));
|
|
9308
|
-
const
|
|
9455
|
+
const distance2 = maxDistance > 0 ? createBoundedDistance(query, maxDistance) : null;
|
|
9309
9456
|
const exactMatches = [];
|
|
9310
9457
|
const fuzzyMatches = [];
|
|
9311
9458
|
let exactCount = 0;
|
|
@@ -9374,7 +9521,7 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
9374
9521
|
index = exactStart + query.length;
|
|
9375
9522
|
continue;
|
|
9376
9523
|
}
|
|
9377
|
-
const edits =
|
|
9524
|
+
const edits = distance2(lower, index);
|
|
9378
9525
|
if (edits > 0 && edits <= maxDistance) {
|
|
9379
9526
|
fuzzyCount++;
|
|
9380
9527
|
const score = 1 - edits / query.length;
|