@anaralabs/lector 3.15.1 → 3.15.3
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 -2
- package/dist/index.js +503 -349
- 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>>;
|
|
@@ -830,7 +841,6 @@ export declare interface SearchResults {
|
|
|
830
841
|
hasMoreResults: boolean;
|
|
831
842
|
}
|
|
832
843
|
|
|
833
|
-
/** A mounted text layer. Only this object owns DOM nodes; snapshots never do. */
|
|
834
844
|
export declare class SelectionPage {
|
|
835
845
|
readonly element: HTMLElement;
|
|
836
846
|
readonly pageNumber: number;
|
|
@@ -1098,7 +1108,10 @@ export declare const useSelectionDimensions: () => {
|
|
|
1098
1108
|
|
|
1099
1109
|
declare type ViewportCallback = () => void;
|
|
1100
1110
|
|
|
1101
|
-
|
|
1111
|
+
declare type ViewportRef = {
|
|
1112
|
+
current: HTMLDivElement | null;
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1102
1115
|
export declare function withSelectionPage<T>(proxy: PDFPageProxy, owner: Document, signal: AbortSignal, read: (page: SelectionPage) => T): Promise<T>;
|
|
1103
1116
|
|
|
1104
1117
|
export declare const ZoomIn: ({ ...props }: HTMLProps<HTMLButtonElement>) => JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -267,7 +267,10 @@ var SelectionPage = class {
|
|
|
267
267
|
point(offset3, end = false) {
|
|
268
268
|
const index = Math.max(0, Math.min(offset3, this.content.text.length));
|
|
269
269
|
const item = this.nodes.find((item2) => end ? item2.end >= index : item2.end > index) ?? this.nodes.at(-1);
|
|
270
|
-
return item
|
|
270
|
+
return item && this.element.contains(item.node) ? {
|
|
271
|
+
node: item.node,
|
|
272
|
+
offset: Math.max(0, Math.min(index - item.start, item.node.length))
|
|
273
|
+
} : null;
|
|
271
274
|
}
|
|
272
275
|
rects(from, to) {
|
|
273
276
|
const bounds = this.element.getBoundingClientRect();
|
|
@@ -275,13 +278,17 @@ var SelectionPage = class {
|
|
|
275
278
|
const scaleY = bounds.height / this.height || 1;
|
|
276
279
|
const rects = [];
|
|
277
280
|
for (const item of this.nodes) {
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
281
|
+
if (!this.element.contains(item.node)) continue;
|
|
282
|
+
const start = Math.max(0, from - item.start);
|
|
283
|
+
const end = Math.min(
|
|
284
|
+
to - item.start,
|
|
285
|
+
item.end - item.start,
|
|
286
|
+
item.node.length
|
|
287
|
+
);
|
|
288
|
+
if (end <= start || !item.node.data.slice(start, end).trim()) continue;
|
|
282
289
|
const range = this.element.ownerDocument.createRange();
|
|
283
|
-
range.setStart(item.node, start
|
|
284
|
-
range.setEnd(item.node, end
|
|
290
|
+
range.setStart(item.node, start);
|
|
291
|
+
range.setEnd(item.node, end);
|
|
285
292
|
for (const rect of range.getClientRects()) {
|
|
286
293
|
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
287
294
|
rects.push({
|
|
@@ -474,6 +481,467 @@ function originalTextRange(normalized, start, end) {
|
|
|
474
481
|
return { start, end };
|
|
475
482
|
}
|
|
476
483
|
|
|
484
|
+
// src/lib/selection-rects.ts
|
|
485
|
+
var getTextNodeClientRects = (range) => {
|
|
486
|
+
const root = range.commonAncestorContainer;
|
|
487
|
+
const ownerDoc = root.ownerDocument ?? document;
|
|
488
|
+
if (root.nodeType === Node.TEXT_NODE) {
|
|
489
|
+
const parentElement = root.parentElement;
|
|
490
|
+
return Array.from(range.getClientRects()).map((rect) => ({
|
|
491
|
+
rect,
|
|
492
|
+
element: parentElement
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
const walker = ownerDoc.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
496
|
+
acceptNode(node) {
|
|
497
|
+
if (!node.nodeValue || node.nodeValue.length === 0) {
|
|
498
|
+
return NodeFilter.FILTER_REJECT;
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
502
|
+
} catch {
|
|
503
|
+
return NodeFilter.FILTER_REJECT;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
const results = [];
|
|
508
|
+
let current = walker.nextNode();
|
|
509
|
+
while (current) {
|
|
510
|
+
const textNode = current;
|
|
511
|
+
const parentElement = textNode.parentElement;
|
|
512
|
+
const length = textNode.nodeValue?.length ?? 0;
|
|
513
|
+
const isStartNode = textNode === range.startContainer;
|
|
514
|
+
const isEndNode = textNode === range.endContainer;
|
|
515
|
+
const start = isStartNode ? range.startOffset : 0;
|
|
516
|
+
const end = isEndNode ? range.endOffset : length;
|
|
517
|
+
if (end > start) {
|
|
518
|
+
const sub = ownerDoc.createRange();
|
|
519
|
+
try {
|
|
520
|
+
sub.setStart(textNode, start);
|
|
521
|
+
sub.setEnd(textNode, end);
|
|
522
|
+
const subRects = sub.getClientRects();
|
|
523
|
+
for (let i = 0; i < subRects.length; i++) {
|
|
524
|
+
const r2 = subRects[i];
|
|
525
|
+
if (r2) results.push({ rect: r2, element: parentElement });
|
|
526
|
+
}
|
|
527
|
+
} catch {
|
|
528
|
+
} finally {
|
|
529
|
+
sub.detach?.();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
current = walker.nextNode();
|
|
533
|
+
}
|
|
534
|
+
return results;
|
|
535
|
+
};
|
|
536
|
+
var connected = (a, b) => {
|
|
537
|
+
const overlap = Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top);
|
|
538
|
+
const gap = Math.max(a.left, b.left) - Math.min(a.left + a.width, b.left + b.width);
|
|
539
|
+
return overlap >= Math.min(a.height, b.height) / 2 && gap <= Math.min(a.height, b.height) * 0.5;
|
|
540
|
+
};
|
|
541
|
+
var mergeTextRuns = (rects) => {
|
|
542
|
+
const groups = [];
|
|
543
|
+
for (const rect of rects) {
|
|
544
|
+
const group = [rect];
|
|
545
|
+
for (let i = groups.length - 1; i >= 0; i--) {
|
|
546
|
+
if (groups[i].some((member) => connected(member, rect))) {
|
|
547
|
+
group.push(...groups[i]);
|
|
548
|
+
groups.splice(i, 1);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
groups.push(group);
|
|
552
|
+
}
|
|
553
|
+
return groups.map((group) => {
|
|
554
|
+
const left = Math.min(...group.map((rect) => rect.left));
|
|
555
|
+
const top = Math.min(...group.map((rect) => rect.top));
|
|
556
|
+
return {
|
|
557
|
+
...group[0],
|
|
558
|
+
left,
|
|
559
|
+
top,
|
|
560
|
+
width: Math.max(...group.map((rect) => rect.left + rect.width)) - left,
|
|
561
|
+
height: Math.max(...group.map((rect) => rect.top + rect.height)) - top
|
|
562
|
+
};
|
|
563
|
+
});
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// src/lib/selection-background.ts
|
|
567
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
568
|
+
var createSelectionBackground = () => {
|
|
569
|
+
const overlays = /* @__PURE__ */ new Map();
|
|
570
|
+
let style;
|
|
571
|
+
const clear = (layer) => {
|
|
572
|
+
overlays.get(layer)?.remove();
|
|
573
|
+
overlays.delete(layer);
|
|
574
|
+
layer.removeAttribute("data-lector-selection-active");
|
|
575
|
+
if (overlays.size === 0) {
|
|
576
|
+
style?.remove();
|
|
577
|
+
style = void 0;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
const update = (selection, layers) => {
|
|
581
|
+
const registered = new Set(layers);
|
|
582
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
583
|
+
if (selection && !selection.isCollapsed) {
|
|
584
|
+
for (let i = 0; i < selection.rangeCount; i++) {
|
|
585
|
+
for (const { rect, element } of getTextNodeClientRects(
|
|
586
|
+
selection.getRangeAt(i)
|
|
587
|
+
)) {
|
|
588
|
+
const layer = element?.closest(".textLayer");
|
|
589
|
+
if (!layer || !element || !registered.has(layer) || rect.width <= 0 || rect.height <= 0)
|
|
590
|
+
continue;
|
|
591
|
+
let entry = byLayer.get(layer);
|
|
592
|
+
if (!entry) {
|
|
593
|
+
entry = { rects: [], element };
|
|
594
|
+
byLayer.set(layer, entry);
|
|
595
|
+
}
|
|
596
|
+
entry.rects.push(rect);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
for (const layer of registered) {
|
|
601
|
+
const entry = byLayer.get(layer);
|
|
602
|
+
if (!entry) {
|
|
603
|
+
clear(layer);
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
layer.removeAttribute("data-lector-selection-active");
|
|
607
|
+
const color = getComputedStyle(
|
|
608
|
+
entry.element,
|
|
609
|
+
"::selection"
|
|
610
|
+
).backgroundColor;
|
|
611
|
+
if (!color || color === "transparent" || color === "rgba(0, 0, 0, 0)") {
|
|
612
|
+
clear(layer);
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
const bounds = layer.getBoundingClientRect();
|
|
616
|
+
if (!bounds.width || !bounds.height || !layer.clientWidth || !layer.clientHeight) {
|
|
617
|
+
clear(layer);
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const scaleX = bounds.width / layer.clientWidth;
|
|
621
|
+
const scaleY = bounds.height / layer.clientHeight;
|
|
622
|
+
const rects = mergeTextRuns(
|
|
623
|
+
entry.rects.map((rect) => ({
|
|
624
|
+
left: (rect.left - bounds.left) / scaleX,
|
|
625
|
+
top: (rect.top - bounds.top) / scaleY,
|
|
626
|
+
width: rect.width / scaleX,
|
|
627
|
+
height: rect.height / scaleY
|
|
628
|
+
}))
|
|
629
|
+
);
|
|
630
|
+
let overlay = overlays.get(layer);
|
|
631
|
+
if (!overlay) {
|
|
632
|
+
overlay = document.createElementNS(SVG_NS, "svg");
|
|
633
|
+
overlay.dataset.lectorSelection = "";
|
|
634
|
+
overlay.setAttribute("aria-hidden", "true");
|
|
635
|
+
overlay.style.cssText = "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;user-select:none;transform:none;z-index:0;overflow:visible";
|
|
636
|
+
overlay.append(document.createElementNS(SVG_NS, "path"));
|
|
637
|
+
layer.append(overlay);
|
|
638
|
+
overlays.set(layer, overlay);
|
|
639
|
+
}
|
|
640
|
+
const path = overlay.firstElementChild;
|
|
641
|
+
path.setAttribute(
|
|
642
|
+
"d",
|
|
643
|
+
rects.map(
|
|
644
|
+
(rect) => `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h${-rect.width}Z`
|
|
645
|
+
).join(" ")
|
|
646
|
+
);
|
|
647
|
+
path.setAttribute("fill", color);
|
|
648
|
+
if (!style) {
|
|
649
|
+
style = document.createElement("style");
|
|
650
|
+
style.textContent = ".textLayer[data-lector-selection-active] ::selection { background-color: transparent !important; }";
|
|
651
|
+
document.head.append(style);
|
|
652
|
+
}
|
|
653
|
+
layer.setAttribute("data-lector-selection-active", "");
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
return { update, clear };
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
// src/lib/text-selection.ts
|
|
660
|
+
var interactive = 'a, button, input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="button"], [role="link"], [role="textbox"], [data-lector-selection-ignore]';
|
|
661
|
+
var distance = (value, start, end) => Math.max(start - value, 0, value - end);
|
|
662
|
+
function nearestBox(boxes, x, y) {
|
|
663
|
+
let nearest;
|
|
664
|
+
let best = Number.POSITIVE_INFINITY;
|
|
665
|
+
for (const box of boxes) {
|
|
666
|
+
const { rect } = box;
|
|
667
|
+
const dx = distance(x, rect.left, rect.right);
|
|
668
|
+
const dy = distance(y, rect.top, rect.bottom);
|
|
669
|
+
const score = dy * dy + Math.min(dx, 30) ** 2 / 100 + dx / 1e4;
|
|
670
|
+
if (score < best) {
|
|
671
|
+
best = score;
|
|
672
|
+
nearest = box;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return nearest;
|
|
676
|
+
}
|
|
677
|
+
function caretInBox({ node, rect }, x, y) {
|
|
678
|
+
const doc = node.ownerDocument;
|
|
679
|
+
const px = Math.min(Math.max(x, rect.left + 0.1), rect.right - 0.1);
|
|
680
|
+
const py = Math.min(Math.max(y, rect.top + 0.1), rect.bottom - 0.1);
|
|
681
|
+
const caretDoc = doc;
|
|
682
|
+
const position = caretDoc.caretPositionFromPoint?.(px, py);
|
|
683
|
+
if (position?.offsetNode === node) return { node, offset: position.offset };
|
|
684
|
+
const range = caretDoc.caretRangeFromPoint?.(px, py);
|
|
685
|
+
if (range?.startContainer === node)
|
|
686
|
+
return { node, offset: range.startOffset };
|
|
687
|
+
const probe = doc.createRange();
|
|
688
|
+
let offset3 = 0;
|
|
689
|
+
let best = Number.POSITIVE_INFINITY;
|
|
690
|
+
let previous;
|
|
691
|
+
for (let i = 0; i <= node.length; ) {
|
|
692
|
+
probe.setStart(node, i);
|
|
693
|
+
probe.collapse(true);
|
|
694
|
+
const rects = Array.from(probe.getClientRects());
|
|
695
|
+
if (!rects.length && i === node.length && previous) {
|
|
696
|
+
probe.setStart(node, previous.offset);
|
|
697
|
+
probe.setEnd(node, i);
|
|
698
|
+
const glyph = probe.getBoundingClientRect();
|
|
699
|
+
const prior = previous.rect;
|
|
700
|
+
rects.push(
|
|
701
|
+
new DOMRect(
|
|
702
|
+
glyph.left + glyph.right - prior.right,
|
|
703
|
+
glyph.top + glyph.bottom - prior.bottom,
|
|
704
|
+
prior.width,
|
|
705
|
+
prior.height
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
for (const r2 of rects) {
|
|
710
|
+
const score = distance(x, r2.left, r2.right) ** 2 + distance(y, r2.top, r2.bottom) ** 2;
|
|
711
|
+
if (score < best) {
|
|
712
|
+
best = score;
|
|
713
|
+
offset3 = i;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (rects[0]) previous = { offset: i, rect: rects[0] };
|
|
717
|
+
i += (node.data.codePointAt(i) ?? 0) > 65535 ? 2 : 1;
|
|
718
|
+
}
|
|
719
|
+
return { node, offset: offset3 };
|
|
720
|
+
}
|
|
721
|
+
function createSelectionManager(viewport, doc, isPersistent) {
|
|
722
|
+
const layers = /* @__PURE__ */ new Set();
|
|
723
|
+
const background = createSelectionBackground();
|
|
724
|
+
const container = viewport.current;
|
|
725
|
+
const win = doc.defaultView;
|
|
726
|
+
const controller = new AbortController();
|
|
727
|
+
const { signal } = controller;
|
|
728
|
+
let drag = null;
|
|
729
|
+
let boxes = null;
|
|
730
|
+
let scrollLeft = container?.scrollLeft;
|
|
731
|
+
let scrollTop = container?.scrollTop;
|
|
732
|
+
let frame = 0;
|
|
733
|
+
let mousePointer = true;
|
|
734
|
+
const invalidate = () => {
|
|
735
|
+
boxes = null;
|
|
736
|
+
if (drag && !frame) frame = win.requestAnimationFrame(tick);
|
|
737
|
+
};
|
|
738
|
+
const contains = (node) => !!node && [...layers].some((layer) => layer.contains(node));
|
|
739
|
+
const reset = () => {
|
|
740
|
+
drag = null;
|
|
741
|
+
boxes = null;
|
|
742
|
+
win.cancelAnimationFrame(frame);
|
|
743
|
+
frame = 0;
|
|
744
|
+
for (const layer of layers) layer.classList.remove("selecting");
|
|
745
|
+
};
|
|
746
|
+
const readBoxes = () => {
|
|
747
|
+
if (boxes) return boxes;
|
|
748
|
+
boxes = [];
|
|
749
|
+
for (const layer of layers) {
|
|
750
|
+
const walker = doc.createTreeWalker(layer, NodeFilter.SHOW_TEXT);
|
|
751
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
752
|
+
const parent = node.parentElement;
|
|
753
|
+
if (!node.textContent?.trim() || !parent || parent.closest('[role="img"], .endOfContent') || win.getComputedStyle(parent).userSelect === "none")
|
|
754
|
+
continue;
|
|
755
|
+
const range = doc.createRange();
|
|
756
|
+
range.selectNodeContents(node);
|
|
757
|
+
for (const rect of range.getClientRects()) {
|
|
758
|
+
if (rect.width > 0 && rect.height > 0)
|
|
759
|
+
boxes.push({ node, rect });
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return boxes;
|
|
764
|
+
};
|
|
765
|
+
const resolve = (x, y) => {
|
|
766
|
+
if (scrollLeft !== container?.scrollLeft || scrollTop !== container?.scrollTop) {
|
|
767
|
+
boxes = null;
|
|
768
|
+
scrollLeft = container?.scrollLeft;
|
|
769
|
+
scrollTop = container?.scrollTop;
|
|
770
|
+
}
|
|
771
|
+
const box = nearestBox(readBoxes(), x, y);
|
|
772
|
+
return box ? caretInBox(box, x, y) : null;
|
|
773
|
+
};
|
|
774
|
+
const update = () => {
|
|
775
|
+
if (!drag) return;
|
|
776
|
+
if (!drag.anchor.node.isConnected) {
|
|
777
|
+
reset();
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
const focus = resolve(drag.x, drag.y);
|
|
781
|
+
const selection = doc.getSelection();
|
|
782
|
+
if (!focus || !selection) return;
|
|
783
|
+
if (selection.anchorNode === drag.anchor.node && selection.anchorOffset === drag.anchor.offset && selection.focusNode === focus.node && selection.focusOffset === focus.offset)
|
|
784
|
+
return;
|
|
785
|
+
selection.setBaseAndExtent(
|
|
786
|
+
drag.anchor.node,
|
|
787
|
+
drag.anchor.offset,
|
|
788
|
+
focus.node,
|
|
789
|
+
focus.offset
|
|
790
|
+
);
|
|
791
|
+
};
|
|
792
|
+
const tick = () => {
|
|
793
|
+
frame = 0;
|
|
794
|
+
if (!drag) return;
|
|
795
|
+
let scrolled = false;
|
|
796
|
+
const container2 = viewport.current;
|
|
797
|
+
if (container2 && !isPersistent()) {
|
|
798
|
+
const r2 = container2.getBoundingClientRect();
|
|
799
|
+
const edge = 24;
|
|
800
|
+
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;
|
|
801
|
+
const dx = speed(drag.x, r2.left, r2.right);
|
|
802
|
+
const dy = speed(drag.y, r2.top, r2.bottom);
|
|
803
|
+
if (dx || dy) {
|
|
804
|
+
const left = container2.scrollLeft;
|
|
805
|
+
const top = container2.scrollTop;
|
|
806
|
+
container2.scrollBy(dx, dy);
|
|
807
|
+
if (container2.scrollLeft !== left || container2.scrollTop !== top) {
|
|
808
|
+
boxes = null;
|
|
809
|
+
scrolled = true;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
update();
|
|
814
|
+
if (drag && scrolled && !frame) frame = win.requestAnimationFrame(tick);
|
|
815
|
+
};
|
|
816
|
+
doc.addEventListener(
|
|
817
|
+
"pointerdown",
|
|
818
|
+
(event) => {
|
|
819
|
+
mousePointer = event.pointerType === "mouse";
|
|
820
|
+
if (!mousePointer) reset();
|
|
821
|
+
},
|
|
822
|
+
{ signal }
|
|
823
|
+
);
|
|
824
|
+
doc.addEventListener(
|
|
825
|
+
"mousedown",
|
|
826
|
+
(event) => {
|
|
827
|
+
reset();
|
|
828
|
+
if (!mousePointer || event.defaultPrevented || event.button !== 0 || event.ctrlKey || event.metaKey || event.altKey || event.detail > 1)
|
|
829
|
+
return;
|
|
830
|
+
const target = event.target;
|
|
831
|
+
if (!(target instanceof Element) || target.closest(interactive)) return;
|
|
832
|
+
const onPage = [...layers].some(
|
|
833
|
+
(layer) => layer.parentElement?.contains(target)
|
|
834
|
+
);
|
|
835
|
+
if (!onPage && !(viewport.current === target || viewport.current?.contains(target) && target.contains([...layers][0] ?? null)))
|
|
836
|
+
return;
|
|
837
|
+
if (target instanceof HTMLElement && /auto|scroll/.test(win.getComputedStyle(target).overflow)) {
|
|
838
|
+
const r2 = target.getBoundingClientRect();
|
|
839
|
+
const scrollbarX = (target.offsetWidth - target.clientWidth - target.clientLeft * 2) * r2.width / target.offsetWidth;
|
|
840
|
+
const scrollbarY = (target.offsetHeight - target.clientHeight - target.clientTop * 2) * r2.height / target.offsetHeight;
|
|
841
|
+
if (event.clientX >= r2.right - scrollbarX || event.clientY >= r2.bottom - scrollbarY)
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
const focus = resolve(event.clientX, event.clientY);
|
|
845
|
+
const selection = doc.getSelection();
|
|
846
|
+
if (!focus || !selection) return;
|
|
847
|
+
const anchor = event.shiftKey && contains(selection.anchorNode) ? { node: selection.anchorNode, offset: selection.anchorOffset } : focus;
|
|
848
|
+
event.preventDefault();
|
|
849
|
+
if (viewport.current && viewport.current.tabIndex >= 0)
|
|
850
|
+
viewport.current.focus({ preventScroll: true });
|
|
851
|
+
drag = { anchor, x: event.clientX, y: event.clientY };
|
|
852
|
+
for (const layer of layers) layer.classList.add("selecting");
|
|
853
|
+
update();
|
|
854
|
+
},
|
|
855
|
+
{ signal }
|
|
856
|
+
);
|
|
857
|
+
doc.addEventListener(
|
|
858
|
+
"mousemove",
|
|
859
|
+
(event) => {
|
|
860
|
+
if (!drag) return;
|
|
861
|
+
if (!(event.buttons & 1)) {
|
|
862
|
+
reset();
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
event.preventDefault();
|
|
866
|
+
drag.x = event.clientX;
|
|
867
|
+
drag.y = event.clientY;
|
|
868
|
+
update();
|
|
869
|
+
if (!frame) frame = win.requestAnimationFrame(tick);
|
|
870
|
+
},
|
|
871
|
+
{ signal }
|
|
872
|
+
);
|
|
873
|
+
doc.addEventListener(
|
|
874
|
+
"mouseup",
|
|
875
|
+
(event) => {
|
|
876
|
+
if (drag && event.button === 0) {
|
|
877
|
+
drag.x = event.clientX;
|
|
878
|
+
drag.y = event.clientY;
|
|
879
|
+
update();
|
|
880
|
+
reset();
|
|
881
|
+
}
|
|
882
|
+
},
|
|
883
|
+
{ signal }
|
|
884
|
+
);
|
|
885
|
+
doc.addEventListener(
|
|
886
|
+
"selectionchange",
|
|
887
|
+
() => background.update(doc.getSelection(), layers),
|
|
888
|
+
{ signal }
|
|
889
|
+
);
|
|
890
|
+
doc.addEventListener("scroll", invalidate, {
|
|
891
|
+
signal,
|
|
892
|
+
capture: true,
|
|
893
|
+
passive: true
|
|
894
|
+
});
|
|
895
|
+
win.addEventListener("resize", invalidate, { signal });
|
|
896
|
+
win.addEventListener("blur", reset, { signal });
|
|
897
|
+
doc.addEventListener("pointercancel", reset, { signal });
|
|
898
|
+
doc.addEventListener(
|
|
899
|
+
"keydown",
|
|
900
|
+
(event) => {
|
|
901
|
+
if (event.key === "Escape") reset();
|
|
902
|
+
},
|
|
903
|
+
{ signal }
|
|
904
|
+
);
|
|
905
|
+
return {
|
|
906
|
+
resolve,
|
|
907
|
+
add(layer) {
|
|
908
|
+
layers.add(layer);
|
|
909
|
+
if (container) viewportManagers.set(container, { resolve });
|
|
910
|
+
invalidate();
|
|
911
|
+
return () => {
|
|
912
|
+
background.clear(layer);
|
|
913
|
+
layers.delete(layer);
|
|
914
|
+
layer.classList.remove("selecting");
|
|
915
|
+
invalidate();
|
|
916
|
+
if (drag && layer.contains(drag.anchor.node)) reset();
|
|
917
|
+
if (!layers.size) {
|
|
918
|
+
reset();
|
|
919
|
+
controller.abort();
|
|
920
|
+
managers.delete(viewport);
|
|
921
|
+
if (container) viewportManagers.delete(container);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
var viewportManagers = /* @__PURE__ */ new WeakMap();
|
|
928
|
+
function resolveTextSelectionCaret(viewport, x, y) {
|
|
929
|
+
return viewportManagers.get(viewport)?.resolve(x, y);
|
|
930
|
+
}
|
|
931
|
+
var managers = /* @__PURE__ */ new WeakMap();
|
|
932
|
+
function bindTextSelection(layer, viewport, isPersistent = () => false) {
|
|
933
|
+
let manager = managers.get(viewport);
|
|
934
|
+
if (!manager) {
|
|
935
|
+
manager = createSelectionManager(
|
|
936
|
+
viewport,
|
|
937
|
+
layer.ownerDocument,
|
|
938
|
+
isPersistent
|
|
939
|
+
);
|
|
940
|
+
managers.set(viewport, manager);
|
|
941
|
+
}
|
|
942
|
+
return manager.add(layer);
|
|
943
|
+
}
|
|
944
|
+
|
|
477
945
|
// src/lib/pdf-selection.ts
|
|
478
946
|
var sameAnchor = (a, b) => a.pageNumber === b.pageNumber && a.offset === b.offset;
|
|
479
947
|
var compare = (a, b) => a.pageNumber - b.pageNumber || a.offset - b.offset;
|
|
@@ -504,6 +972,9 @@ var PDFSelectionController = class {
|
|
|
504
972
|
this.listeners.delete(listener);
|
|
505
973
|
};
|
|
506
974
|
};
|
|
975
|
+
get isConnected() {
|
|
976
|
+
return this.container !== null;
|
|
977
|
+
}
|
|
507
978
|
get isDragging() {
|
|
508
979
|
return this.drag !== null;
|
|
509
980
|
}
|
|
@@ -848,6 +1319,8 @@ var PDFSelectionController = class {
|
|
|
848
1319
|
});
|
|
849
1320
|
}
|
|
850
1321
|
caret(x, y) {
|
|
1322
|
+
const geometry = resolveTextSelectionCaret(this.container, x, y);
|
|
1323
|
+
if (geometry) return this.anchorFor(geometry.node, geometry.offset);
|
|
851
1324
|
const owner = this.container.ownerDocument;
|
|
852
1325
|
const doc = owner;
|
|
853
1326
|
const position = doc.caretPositionFromPoint?.(x, y);
|
|
@@ -936,13 +1409,15 @@ var PDFSelectionController = class {
|
|
|
936
1409
|
const down = (event) => {
|
|
937
1410
|
if (event.defaultPrevented) return;
|
|
938
1411
|
const target = event.target instanceof Element ? event.target : null;
|
|
939
|
-
if (target?.closest(
|
|
1412
|
+
if (target?.closest(
|
|
1413
|
+
'button, a, input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role=button], [role=link], [role=textbox], [data-selection-tooltip], [data-lector-selection-ignore]'
|
|
1414
|
+
))
|
|
940
1415
|
return;
|
|
941
1416
|
if (!container.contains(event.target)) {
|
|
942
1417
|
this.clear();
|
|
943
1418
|
return;
|
|
944
1419
|
}
|
|
945
|
-
if (event.defaultPrevented || event.button !== 0 || event.pointerType === "touch" || !container.contains(event.target))
|
|
1420
|
+
if (event.defaultPrevented || event.button !== 0 || event.pointerType === "touch" || event.ctrlKey || event.metaKey || event.altKey || !container.contains(event.target))
|
|
946
1421
|
return;
|
|
947
1422
|
const point = this.caret(event.clientX, event.clientY);
|
|
948
1423
|
if (!point) return;
|
|
@@ -3846,88 +4321,6 @@ var CanvasLayer = memo(
|
|
|
3846
4321
|
);
|
|
3847
4322
|
CanvasLayer.displayName = "CanvasLayer";
|
|
3848
4323
|
|
|
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
4324
|
// src/lib/selection-text.ts
|
|
3932
4325
|
function textLayer(node) {
|
|
3933
4326
|
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
@@ -4760,244 +5153,6 @@ var HighlightLayer = forwardRef(({ asChild, className, style, ...props }, ref) =
|
|
|
4760
5153
|
}) });
|
|
4761
5154
|
});
|
|
4762
5155
|
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
5156
|
var TEXT_BUILD_IDLE_MS = 300;
|
|
5002
5157
|
var TEXT_SCROLL_SETTLE_MS = 160;
|
|
5003
5158
|
var VIEWPORT_ATTACH_MAX_FRAMES = 300;
|
|
@@ -5016,6 +5171,7 @@ var useTextLayer = () => {
|
|
|
5016
5171
|
}
|
|
5017
5172
|
let isCancelled = false;
|
|
5018
5173
|
let unregisterSelection;
|
|
5174
|
+
let cleanupMouseSelection;
|
|
5019
5175
|
let buildTimer = null;
|
|
5020
5176
|
let unsubscribe = null;
|
|
5021
5177
|
let built = false;
|
|
@@ -5043,10 +5199,11 @@ var useTextLayer = () => {
|
|
|
5043
5199
|
if (isCancelled || gen !== buildGen || textContainerRef.current !== textContainer) {
|
|
5044
5200
|
return;
|
|
5045
5201
|
}
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5202
|
+
cleanupMouseSelection = bindTextSelection(
|
|
5203
|
+
textContainer,
|
|
5204
|
+
viewportRef,
|
|
5205
|
+
() => selection?.isConnected ?? false
|
|
5206
|
+
);
|
|
5050
5207
|
const viewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
5051
5208
|
const unrotated = pdfPageProxy.getViewport({ scale: 1, rotation: 0 });
|
|
5052
5209
|
textContainer.style.width = `${unrotated.width}px`;
|
|
@@ -5121,10 +5278,7 @@ var useTextLayer = () => {
|
|
|
5121
5278
|
textLayerRef.current.cancel();
|
|
5122
5279
|
textLayerRef.current = null;
|
|
5123
5280
|
}
|
|
5124
|
-
|
|
5125
|
-
textContainer._cleanupTextSelection();
|
|
5126
|
-
delete textContainer._cleanupTextSelection;
|
|
5127
|
-
}
|
|
5281
|
+
cleanupMouseSelection?.();
|
|
5128
5282
|
};
|
|
5129
5283
|
}, [
|
|
5130
5284
|
pdfPageProxy.streamTextContent,
|
|
@@ -5727,9 +5881,9 @@ var useVisiblePage = ({
|
|
|
5727
5881
|
let smallestDistance = Infinity;
|
|
5728
5882
|
for (const item of virtualItems) {
|
|
5729
5883
|
const itemCenter = item.start + item.size / 2;
|
|
5730
|
-
const
|
|
5731
|
-
if (
|
|
5732
|
-
smallestDistance =
|
|
5884
|
+
const distance2 = Math.abs(itemCenter - viewportCenter);
|
|
5885
|
+
if (distance2 < smallestDistance * 0.8) {
|
|
5886
|
+
smallestDistance = distance2;
|
|
5733
5887
|
closestIndex = item.index;
|
|
5734
5888
|
}
|
|
5735
5889
|
}
|
|
@@ -5789,9 +5943,9 @@ var V = {
|
|
|
5789
5943
|
v1[1] -= v2[1];
|
|
5790
5944
|
}
|
|
5791
5945
|
};
|
|
5792
|
-
function rubberband(
|
|
5793
|
-
if (dimension === 0 || Math.abs(dimension) === Infinity) return Math.pow(
|
|
5794
|
-
return
|
|
5946
|
+
function rubberband(distance2, dimension, constant) {
|
|
5947
|
+
if (dimension === 0 || Math.abs(dimension) === Infinity) return Math.pow(distance2, constant * 5);
|
|
5948
|
+
return distance2 * dimension * constant / (dimension + constant * distance2);
|
|
5795
5949
|
}
|
|
5796
5950
|
function rubberbandIfOutOfBounds(position, min, max, constant = 0.15) {
|
|
5797
5951
|
if (constant === 0) return clamp2(position, min, max);
|
|
@@ -5934,12 +6088,12 @@ function distanceAngle(P1, P2) {
|
|
|
5934
6088
|
const dy = P2.clientY - P1.clientY;
|
|
5935
6089
|
const cx = (P2.clientX + P1.clientX) / 2;
|
|
5936
6090
|
const cy = (P2.clientY + P1.clientY) / 2;
|
|
5937
|
-
const
|
|
6091
|
+
const distance2 = Math.hypot(dx, dy);
|
|
5938
6092
|
const angle = -(Math.atan2(dx, dy) * 180) / Math.PI;
|
|
5939
6093
|
const origin = [cx, cy];
|
|
5940
6094
|
return {
|
|
5941
6095
|
angle,
|
|
5942
|
-
distance,
|
|
6096
|
+
distance: distance2,
|
|
5943
6097
|
origin
|
|
5944
6098
|
};
|
|
5945
6099
|
} catch (_unused) {
|
|
@@ -6733,12 +6887,12 @@ var dragConfigResolver = _objectSpread2(_objectSpread2({}, coordinatesConfigReso
|
|
|
6733
6887
|
},
|
|
6734
6888
|
swipe({
|
|
6735
6889
|
velocity = DEFAULT_SWIPE_VELOCITY,
|
|
6736
|
-
distance = DEFAULT_SWIPE_DISTANCE,
|
|
6890
|
+
distance: distance2 = DEFAULT_SWIPE_DISTANCE,
|
|
6737
6891
|
duration = DEFAULT_SWIPE_DURATION
|
|
6738
6892
|
} = {}) {
|
|
6739
6893
|
return {
|
|
6740
6894
|
velocity: this.transform(V.toVector(velocity)),
|
|
6741
|
-
distance: this.transform(V.toVector(
|
|
6895
|
+
distance: this.transform(V.toVector(distance2)),
|
|
6742
6896
|
duration
|
|
6743
6897
|
};
|
|
6744
6898
|
},
|
|
@@ -9305,7 +9459,7 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
9305
9459
|
const query = normalizeSearchText(searchText, options).text;
|
|
9306
9460
|
if (!query.trim()) return empty;
|
|
9307
9461
|
const maxDistance = Math.floor(query.length * (1 - threshold));
|
|
9308
|
-
const
|
|
9462
|
+
const distance2 = maxDistance > 0 ? createBoundedDistance(query, maxDistance) : null;
|
|
9309
9463
|
const exactMatches = [];
|
|
9310
9464
|
const fuzzyMatches = [];
|
|
9311
9465
|
let exactCount = 0;
|
|
@@ -9374,7 +9528,7 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
9374
9528
|
index = exactStart + query.length;
|
|
9375
9529
|
continue;
|
|
9376
9530
|
}
|
|
9377
|
-
const edits =
|
|
9531
|
+
const edits = distance2(lower, index);
|
|
9378
9532
|
if (edits > 0 && edits <= maxDistance) {
|
|
9379
9533
|
fuzzyCount++;
|
|
9380
9534
|
const score = 1 - edits / query.length;
|