@anaralabs/lector 3.13.0 → 3.13.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 +11 -1
- package/dist/index.js +412 -123
- package/package.json +1 -1
|
@@ -168,6 +168,8 @@ export declare const cancellable: <T extends Promise<unknown>>(promise: T) => {
|
|
|
168
168
|
cancel: () => void;
|
|
169
169
|
};
|
|
170
170
|
|
|
171
|
+
export declare const CANVAS_SUPERSAMPLE = 1.3;
|
|
172
|
+
|
|
171
173
|
declare interface CanvasAndContext {
|
|
172
174
|
canvas: HTMLCanvasElement | null;
|
|
173
175
|
context: CanvasRenderingContext2D | null;
|
|
@@ -179,7 +181,7 @@ background?: string;
|
|
|
179
181
|
|
|
180
182
|
export declare const clamp: (value: number, minimum: number, maximum: number) => number;
|
|
181
183
|
|
|
182
|
-
export declare function clampScaleForPage(targetScale: number, pageWidth: number, pageHeight: number): number;
|
|
184
|
+
export declare function clampScaleForPage(targetScale: number, pageWidth: number, pageHeight: number, maxPixels?: number): number;
|
|
183
185
|
|
|
184
186
|
declare type CollapsibleSelection = {
|
|
185
187
|
highlights: HighlightRect[];
|
|
@@ -208,6 +210,10 @@ declare type ColorScheme = "light" | "dark";
|
|
|
208
210
|
export { ColorScheme }
|
|
209
211
|
export { ColorScheme as ColorScheme_alias_1 }
|
|
210
212
|
|
|
213
|
+
export declare function computeBaseScale(dpr: number, zoom: number, pageWidth: number, pageHeight: number): number;
|
|
214
|
+
|
|
215
|
+
export declare function computeTargetScale(dpr: number, zoom: number): number;
|
|
216
|
+
|
|
211
217
|
/**
|
|
212
218
|
* Builds a memoized color map that flips perceived lightness onto the
|
|
213
219
|
* background<->foreground ramp while preserving hue and chroma (OKLab).
|
|
@@ -272,6 +278,8 @@ export declare const ensureAnnotationLayerStyles: () => void;
|
|
|
272
278
|
|
|
273
279
|
export declare const firstMemo: <T>(first: boolean, memo: unknown, initializer: () => T) => T;
|
|
274
280
|
|
|
281
|
+
export declare function getCanvasPixelBudget(): number;
|
|
282
|
+
|
|
275
283
|
export declare const getDefaultPdfJsAssetUrls: (version: string) => PdfJsAssetUrls;
|
|
276
284
|
|
|
277
285
|
export declare const getEndOfHighlight: (selection: ColoredHighlight) => number;
|
|
@@ -321,6 +329,8 @@ export declare interface InitialPDFState {
|
|
|
321
329
|
renderColorMapRef?: RenderColorMapRef;
|
|
322
330
|
}
|
|
323
331
|
|
|
332
|
+
export declare const IS_MOBILE_DEVICE: boolean;
|
|
333
|
+
|
|
324
334
|
export declare class LinkService {
|
|
325
335
|
_pdfDocumentProxy?: PDFDocumentProxy;
|
|
326
336
|
externalLinkEnabled: boolean;
|
package/dist/index.js
CHANGED
|
@@ -1375,13 +1375,29 @@ AnnotationLayer.displayName = "AnnotationLayer";
|
|
|
1375
1375
|
// src/lib/canvas-utils.ts
|
|
1376
1376
|
var MAX_CANVAS_PIXELS = 16777216;
|
|
1377
1377
|
var MAX_CANVAS_DIMENSION = 32767;
|
|
1378
|
-
|
|
1378
|
+
var CANVAS_SUPERSAMPLE = 1.3;
|
|
1379
|
+
function computeTargetScale(dpr, zoom) {
|
|
1380
|
+
const safeZoom = Math.max(Number.isFinite(zoom) ? zoom : 0, 0.01);
|
|
1381
|
+
return dpr * safeZoom * (safeZoom > 1 ? CANVAS_SUPERSAMPLE : 1);
|
|
1382
|
+
}
|
|
1383
|
+
var IS_MOBILE_DEVICE = typeof navigator !== "undefined" && (/Android|iPhone|iPad|iPod/i.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
|
|
1384
|
+
var CANVAS_AREA_FACTOR = IS_MOBILE_DEVICE ? 1.5 : 3;
|
|
1385
|
+
function getCanvasPixelBudget() {
|
|
1386
|
+
if (typeof window === "undefined" || typeof screen === "undefined") {
|
|
1387
|
+
return MAX_CANVAS_PIXELS;
|
|
1388
|
+
}
|
|
1389
|
+
const dpr = window.devicePixelRatio || 1;
|
|
1390
|
+
const screenArea = (screen.width || 0) * (screen.height || 0) * dpr * dpr;
|
|
1391
|
+
if (!Number.isFinite(screenArea) || screenArea <= 0) {
|
|
1392
|
+
return MAX_CANVAS_PIXELS;
|
|
1393
|
+
}
|
|
1394
|
+
return Math.min(MAX_CANVAS_PIXELS, screenArea * CANVAS_AREA_FACTOR);
|
|
1395
|
+
}
|
|
1396
|
+
function clampScaleForPage(targetScale, pageWidth, pageHeight, maxPixels = MAX_CANVAS_PIXELS) {
|
|
1379
1397
|
if (!targetScale) {
|
|
1380
1398
|
return 0;
|
|
1381
1399
|
}
|
|
1382
|
-
const areaLimit = Math.sqrt(
|
|
1383
|
-
MAX_CANVAS_PIXELS / Math.max(pageWidth * pageHeight, 1)
|
|
1384
|
-
);
|
|
1400
|
+
const areaLimit = Math.sqrt(maxPixels / Math.max(pageWidth * pageHeight, 1));
|
|
1385
1401
|
const widthLimit = MAX_CANVAS_DIMENSION / Math.max(pageWidth, 1);
|
|
1386
1402
|
const heightLimit = MAX_CANVAS_DIMENSION / Math.max(pageHeight, 1);
|
|
1387
1403
|
const safeScale = Math.min(
|
|
@@ -1392,6 +1408,14 @@ function clampScaleForPage(targetScale, pageWidth, pageHeight) {
|
|
|
1392
1408
|
);
|
|
1393
1409
|
return Math.max(safeScale, 0);
|
|
1394
1410
|
}
|
|
1411
|
+
function computeBaseScale(dpr, zoom, pageWidth, pageHeight) {
|
|
1412
|
+
return clampScaleForPage(
|
|
1413
|
+
computeTargetScale(dpr, zoom),
|
|
1414
|
+
pageWidth,
|
|
1415
|
+
pageHeight,
|
|
1416
|
+
getCanvasPixelBudget()
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1395
1419
|
|
|
1396
1420
|
// src/lib/recolor-context.ts
|
|
1397
1421
|
var RECOLOR_CLEANUP = Symbol("lectorRecolorCleanup");
|
|
@@ -1515,47 +1539,89 @@ function applyContextRecolor(ctx, map) {
|
|
|
1515
1539
|
function removeContextRecolor(ctx) {
|
|
1516
1540
|
ctx[RECOLOR_CLEANUP]?.();
|
|
1517
1541
|
}
|
|
1542
|
+
var DPR_CAP = 3;
|
|
1543
|
+
var readDpr = () => typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio || 1, DPR_CAP);
|
|
1518
1544
|
var useDpr = () => {
|
|
1519
|
-
const [dpr,
|
|
1520
|
-
typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
|
|
1521
|
-
);
|
|
1545
|
+
const [dpr, setDpr] = useState(readDpr);
|
|
1522
1546
|
useEffect(() => {
|
|
1523
1547
|
if (typeof window === "undefined") {
|
|
1524
1548
|
return;
|
|
1525
1549
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1550
|
+
let mediaQuery = null;
|
|
1551
|
+
let cancelled = false;
|
|
1552
|
+
const handleChange = () => {
|
|
1553
|
+
if (cancelled) {
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
setDpr(readDpr());
|
|
1557
|
+
subscribe();
|
|
1528
1558
|
};
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1559
|
+
const unlisten = (mql) => {
|
|
1560
|
+
if (!mql) return;
|
|
1561
|
+
if (typeof mql.removeEventListener === "function") {
|
|
1562
|
+
mql.removeEventListener("change", handleChange);
|
|
1563
|
+
} else {
|
|
1564
|
+
mql.removeListener(handleChange);
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
const subscribe = () => {
|
|
1568
|
+
unlisten(mediaQuery);
|
|
1569
|
+
const ratio = window.devicePixelRatio || 1;
|
|
1570
|
+
mediaQuery = window.matchMedia(
|
|
1571
|
+
`(resolution: ${ratio}dppx), (-webkit-device-pixel-ratio: ${ratio})`
|
|
1572
|
+
);
|
|
1573
|
+
if (typeof mediaQuery.addEventListener === "function") {
|
|
1574
|
+
mediaQuery.addEventListener("change", handleChange, { once: true });
|
|
1575
|
+
} else {
|
|
1576
|
+
mediaQuery.addListener(handleChange);
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
setDpr(readDpr());
|
|
1580
|
+
subscribe();
|
|
1533
1581
|
return () => {
|
|
1534
|
-
|
|
1582
|
+
cancelled = true;
|
|
1583
|
+
unlisten(mediaQuery);
|
|
1535
1584
|
};
|
|
1536
|
-
}, [
|
|
1585
|
+
}, []);
|
|
1537
1586
|
return dpr;
|
|
1538
1587
|
};
|
|
1539
1588
|
|
|
1540
1589
|
// src/hooks/layers/useCanvasLayer.tsx
|
|
1541
|
-
var
|
|
1590
|
+
var CACHE_MAX_ENTRIES = 60;
|
|
1591
|
+
var CACHE_MAX_BYTES = (IS_MOBILE_DEVICE ? 64 : 192) * 1024 * 1024;
|
|
1592
|
+
var CACHE_MAX_ENTRY_BYTES = IS_MOBILE_DEVICE ? CACHE_MAX_BYTES / 3 : CACHE_MAX_BYTES / 4;
|
|
1593
|
+
var MAX_VARIANTS_PER_PROXY = 2;
|
|
1542
1594
|
var cacheEntries = [];
|
|
1543
1595
|
var canvasBitmapCache = /* @__PURE__ */ new WeakMap();
|
|
1596
|
+
var cacheBytes = 0;
|
|
1597
|
+
var documentIdFallbacks = /* @__PURE__ */ new WeakMap();
|
|
1598
|
+
var documentIdCounter = 0;
|
|
1599
|
+
function getDocumentCacheId(pdfDocumentProxy) {
|
|
1600
|
+
const fingerprint = pdfDocumentProxy.fingerprints?.[0];
|
|
1601
|
+
if (fingerprint) return fingerprint;
|
|
1602
|
+
let id = documentIdFallbacks.get(pdfDocumentProxy);
|
|
1603
|
+
if (!id) {
|
|
1604
|
+
documentIdCounter += 1;
|
|
1605
|
+
id = `anonymous-document-${documentIdCounter}`;
|
|
1606
|
+
documentIdFallbacks.set(pdfDocumentProxy, id);
|
|
1607
|
+
}
|
|
1608
|
+
return id;
|
|
1609
|
+
}
|
|
1610
|
+
function evictEntryAt(index) {
|
|
1611
|
+
const [entry] = cacheEntries.splice(index, 1);
|
|
1612
|
+
if (!entry) return;
|
|
1613
|
+
cacheBytes -= entry.bytes;
|
|
1614
|
+
const map = canvasBitmapCache.get(entry.proxy);
|
|
1615
|
+
if (map?.get(entry.key) === entry.bitmap) {
|
|
1616
|
+
map.delete(entry.key);
|
|
1617
|
+
}
|
|
1618
|
+
entry.bitmap.close();
|
|
1619
|
+
}
|
|
1544
1620
|
function clearBitmapCache(documentId) {
|
|
1545
|
-
if (documentId === void 0) {
|
|
1546
|
-
for (const entry of cacheEntries) {
|
|
1547
|
-
entry.bitmap.close();
|
|
1548
|
-
canvasBitmapCache.get(entry.proxy)?.delete(entry.key);
|
|
1549
|
-
}
|
|
1550
|
-
cacheEntries.length = 0;
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
1621
|
for (let i = cacheEntries.length - 1; i >= 0; i--) {
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
canvasBitmapCache.get(entry.proxy)?.delete(entry.key);
|
|
1558
|
-
cacheEntries.splice(i, 1);
|
|
1622
|
+
if (documentId === void 0 || cacheEntries[i].docId === documentId) {
|
|
1623
|
+
evictEntryAt(i);
|
|
1624
|
+
}
|
|
1559
1625
|
}
|
|
1560
1626
|
}
|
|
1561
1627
|
function cacheKey(scale, background, recolorKey) {
|
|
@@ -1578,40 +1644,49 @@ function getCachedBitmap(proxy, scale, background, recolorKey) {
|
|
|
1578
1644
|
return bitmap;
|
|
1579
1645
|
}
|
|
1580
1646
|
function setCachedBitmap(docId, proxy, scale, background, recolorKey, bitmap) {
|
|
1647
|
+
const bytes = bitmap.width * bitmap.height * 4;
|
|
1648
|
+
if (bytes > CACHE_MAX_ENTRY_BYTES) {
|
|
1649
|
+
bitmap.close();
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1581
1652
|
const key = cacheKey(scale, background, recolorKey);
|
|
1653
|
+
const existingIdx = cacheEntries.findIndex(
|
|
1654
|
+
(e) => e.proxy === proxy && e.key === key
|
|
1655
|
+
);
|
|
1656
|
+
if (existingIdx !== -1) {
|
|
1657
|
+
if (cacheEntries[existingIdx].bitmap === bitmap) return;
|
|
1658
|
+
evictEntryAt(existingIdx);
|
|
1659
|
+
}
|
|
1582
1660
|
let map = canvasBitmapCache.get(proxy);
|
|
1583
1661
|
if (!map) {
|
|
1584
1662
|
map = /* @__PURE__ */ new Map();
|
|
1585
1663
|
canvasBitmapCache.set(proxy, map);
|
|
1586
1664
|
}
|
|
1587
|
-
const existing = map.get(key);
|
|
1588
|
-
if (existing && existing !== bitmap) {
|
|
1589
|
-
existing.close();
|
|
1590
|
-
const idx = cacheEntries.findIndex(
|
|
1591
|
-
(e) => e.proxy === proxy && e.key === key
|
|
1592
|
-
);
|
|
1593
|
-
if (idx !== -1) cacheEntries.splice(idx, 1);
|
|
1594
|
-
}
|
|
1595
1665
|
map.set(key, bitmap);
|
|
1596
|
-
cacheEntries.push({ docId, proxy, key, bitmap });
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
if (
|
|
1601
|
-
|
|
1602
|
-
|
|
1666
|
+
cacheEntries.push({ docId, proxy, key, bitmap, bytes });
|
|
1667
|
+
cacheBytes += bytes;
|
|
1668
|
+
let variants = 0;
|
|
1669
|
+
for (let i = cacheEntries.length - 1; i >= 0; i--) {
|
|
1670
|
+
if (cacheEntries[i].proxy !== proxy) continue;
|
|
1671
|
+
variants++;
|
|
1672
|
+
if (variants > MAX_VARIANTS_PER_PROXY) {
|
|
1673
|
+
evictEntryAt(i);
|
|
1603
1674
|
}
|
|
1604
1675
|
}
|
|
1676
|
+
while (cacheEntries.length > 0 && (cacheEntries.length > CACHE_MAX_ENTRIES || cacheBytes > CACHE_MAX_BYTES)) {
|
|
1677
|
+
evictEntryAt(0);
|
|
1678
|
+
}
|
|
1605
1679
|
}
|
|
1606
1680
|
var useCanvasLayer = ({ background }) => {
|
|
1607
1681
|
const canvasRef = useRef(null);
|
|
1608
|
-
const
|
|
1682
|
+
const paintedRef = useRef(null);
|
|
1609
1683
|
const pageNumber = usePDFPageNumber();
|
|
1610
1684
|
const store = PDFStore.useContext();
|
|
1611
1685
|
const dpr = useDpr();
|
|
1612
1686
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1613
1687
|
const isResizing = usePdf((state) => state.isResizing);
|
|
1614
|
-
const
|
|
1688
|
+
const isPinching = usePdf((state) => state.isPinching);
|
|
1689
|
+
const docId = usePdf((state) => getDocumentCacheId(state.pdfDocumentProxy));
|
|
1615
1690
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1616
1691
|
const markPageRendered = usePdf((state) => state.markPageRendered);
|
|
1617
1692
|
const unmarkPageRendered = usePdf((state) => state.unmarkPageRendered);
|
|
@@ -1627,29 +1702,52 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1627
1702
|
const pageWidth = baseViewport.width;
|
|
1628
1703
|
const pageHeight = baseViewport.height;
|
|
1629
1704
|
const contentKey = `${background ?? "white"}|${recolorKey ?? ""}`;
|
|
1630
|
-
|
|
1705
|
+
const baseScale = computeBaseScale(dpr, zoom, pageWidth, pageHeight);
|
|
1706
|
+
const hasCurrentFrame = paintedRef.current?.proxy === pdfPageProxy && paintedRef.current?.key === contentKey;
|
|
1707
|
+
const geometryZoom = Math.max(Number.isFinite(zoom) ? zoom : 0, 0.01);
|
|
1708
|
+
const applyGeometry = () => {
|
|
1709
|
+
if (geometryZoom === 1) {
|
|
1710
|
+
baseCanvas.style.width = `${pageWidth}px`;
|
|
1711
|
+
baseCanvas.style.height = `${pageHeight}px`;
|
|
1712
|
+
baseCanvas.style.transform = "translate(0px, 0px)";
|
|
1713
|
+
} else {
|
|
1714
|
+
baseCanvas.style.width = `${pageWidth * geometryZoom}px`;
|
|
1715
|
+
baseCanvas.style.height = `${pageHeight * geometryZoom}px`;
|
|
1716
|
+
baseCanvas.style.transform = `scale3d(${1 / geometryZoom},${1 / geometryZoom},1)`;
|
|
1717
|
+
baseCanvas.style.transformOrigin = "0 0";
|
|
1718
|
+
}
|
|
1719
|
+
};
|
|
1720
|
+
if (hasCurrentFrame && paintedRef.current?.scale === baseScale) {
|
|
1721
|
+
applyGeometry();
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
if ((isResizing || isPinching) && hasCurrentFrame) {
|
|
1631
1725
|
baseCanvas.style.visibility = "";
|
|
1632
|
-
baseCanvas.style.width = `${pageWidth}px`;
|
|
1633
|
-
baseCanvas.style.height = `${pageHeight}px`;
|
|
1634
1726
|
return;
|
|
1635
1727
|
}
|
|
1636
|
-
paintedKeyRef.current = null;
|
|
1637
|
-
const targetBaseScale = dpr * Math.min(zoom, 1);
|
|
1638
|
-
const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
|
|
1639
|
-
baseCanvas.width = Math.floor(pageWidth * baseScale);
|
|
1640
|
-
baseCanvas.height = Math.floor(pageHeight * baseScale);
|
|
1641
1728
|
baseCanvas.style.position = "absolute";
|
|
1642
1729
|
baseCanvas.style.top = "0";
|
|
1643
1730
|
baseCanvas.style.left = "0";
|
|
1644
|
-
|
|
1645
|
-
baseCanvas.style.height = `${pageHeight}px`;
|
|
1646
|
-
baseCanvas.style.transform = "translate(0px, 0px)";
|
|
1731
|
+
applyGeometry();
|
|
1647
1732
|
baseCanvas.style.zIndex = "0";
|
|
1648
1733
|
baseCanvas.style.pointerEvents = "none";
|
|
1649
1734
|
baseCanvas.style.backgroundColor = recolor ? recolor(background ?? "#ffffff") : background ?? "white";
|
|
1650
|
-
|
|
1735
|
+
const backingWidth = Math.floor(pageWidth * baseScale);
|
|
1736
|
+
const backingHeight = Math.floor(pageHeight * baseScale);
|
|
1651
1737
|
const context = baseCanvas.getContext("2d");
|
|
1652
|
-
if (!context)
|
|
1738
|
+
if (!context) {
|
|
1739
|
+
if (!hasCurrentFrame) {
|
|
1740
|
+
paintedRef.current = null;
|
|
1741
|
+
baseCanvas.width = backingWidth;
|
|
1742
|
+
baseCanvas.height = backingHeight;
|
|
1743
|
+
baseCanvas.style.visibility = "hidden";
|
|
1744
|
+
}
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
const applyBackingSize = () => {
|
|
1748
|
+
baseCanvas.width = backingWidth;
|
|
1749
|
+
baseCanvas.height = backingHeight;
|
|
1750
|
+
};
|
|
1653
1751
|
const cached = getCachedBitmap(
|
|
1654
1752
|
pdfPageProxy,
|
|
1655
1753
|
baseScale,
|
|
@@ -1657,23 +1755,41 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1657
1755
|
recolorKey
|
|
1658
1756
|
);
|
|
1659
1757
|
if (cached) {
|
|
1758
|
+
applyBackingSize();
|
|
1660
1759
|
context.drawImage(cached, 0, 0);
|
|
1760
|
+
baseCanvas.style.visibility = "";
|
|
1661
1761
|
markPageRendered(pageNumber);
|
|
1662
|
-
|
|
1762
|
+
paintedRef.current = {
|
|
1763
|
+
proxy: pdfPageProxy,
|
|
1764
|
+
key: contentKey,
|
|
1765
|
+
scale: baseScale
|
|
1766
|
+
};
|
|
1663
1767
|
return;
|
|
1664
1768
|
}
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1769
|
+
const hideUntilPainted = () => {
|
|
1770
|
+
paintedRef.current = null;
|
|
1771
|
+
applyBackingSize();
|
|
1772
|
+
baseCanvas.style.visibility = "hidden";
|
|
1773
|
+
};
|
|
1774
|
+
if (!hasCurrentFrame) {
|
|
1775
|
+
hideUntilPainted();
|
|
1776
|
+
}
|
|
1668
1777
|
let cancelled = false;
|
|
1669
1778
|
const viewport = pdfPageProxy.getViewport({ scale: baseScale });
|
|
1670
1779
|
const buffer = document.createElement("canvas");
|
|
1671
|
-
buffer.width =
|
|
1672
|
-
buffer.height =
|
|
1780
|
+
buffer.width = backingWidth;
|
|
1781
|
+
buffer.height = backingHeight;
|
|
1673
1782
|
const bufferCtx = buffer.getContext("2d");
|
|
1674
1783
|
const useBuffer = bufferCtx !== null;
|
|
1784
|
+
if (!useBuffer && hasCurrentFrame) {
|
|
1785
|
+
hideUntilPainted();
|
|
1786
|
+
}
|
|
1675
1787
|
const renderCanvas = useBuffer ? buffer : baseCanvas;
|
|
1676
1788
|
const renderCtx = bufferCtx ?? context;
|
|
1789
|
+
const releaseBuffer = () => {
|
|
1790
|
+
buffer.width = 0;
|
|
1791
|
+
buffer.height = 0;
|
|
1792
|
+
};
|
|
1677
1793
|
let restoreRecolor = null;
|
|
1678
1794
|
if (recolor) {
|
|
1679
1795
|
restoreRecolor = applyContextRecolor(renderCtx, recolor);
|
|
@@ -1690,12 +1806,9 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1690
1806
|
});
|
|
1691
1807
|
} catch (error) {
|
|
1692
1808
|
restoreRecolor?.();
|
|
1809
|
+
releaseBuffer();
|
|
1693
1810
|
throw error;
|
|
1694
1811
|
}
|
|
1695
|
-
const releaseBuffer = () => {
|
|
1696
|
-
buffer.width = 0;
|
|
1697
|
-
buffer.height = 0;
|
|
1698
|
-
};
|
|
1699
1812
|
renderingTask.promise.finally(() => {
|
|
1700
1813
|
restoreRecolor?.();
|
|
1701
1814
|
}).then(() => {
|
|
@@ -1710,13 +1823,17 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1710
1823
|
return;
|
|
1711
1824
|
}
|
|
1712
1825
|
if (useBuffer) {
|
|
1713
|
-
|
|
1826
|
+
applyBackingSize();
|
|
1714
1827
|
context.drawImage(buffer, 0, 0);
|
|
1715
1828
|
}
|
|
1716
1829
|
baseCanvas.style.visibility = "";
|
|
1717
1830
|
markPageRendered(pageNumber);
|
|
1718
|
-
|
|
1719
|
-
|
|
1831
|
+
paintedRef.current = {
|
|
1832
|
+
proxy: pdfPageProxy,
|
|
1833
|
+
key: contentKey,
|
|
1834
|
+
scale: baseScale
|
|
1835
|
+
};
|
|
1836
|
+
if (typeof createImageBitmap !== "undefined" && renderCanvas.width * renderCanvas.height * 4 <= CACHE_MAX_ENTRY_BYTES) {
|
|
1720
1837
|
createImageBitmap(renderCanvas).then((bitmap) => {
|
|
1721
1838
|
if (cancelled) {
|
|
1722
1839
|
bitmap.close();
|
|
@@ -1752,6 +1869,7 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1752
1869
|
dpr,
|
|
1753
1870
|
zoom,
|
|
1754
1871
|
isResizing,
|
|
1872
|
+
isPinching,
|
|
1755
1873
|
pageNumber,
|
|
1756
1874
|
markPageRendered,
|
|
1757
1875
|
docId,
|
|
@@ -1767,6 +1885,7 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1767
1885
|
canvas.width = 0;
|
|
1768
1886
|
canvas.height = 0;
|
|
1769
1887
|
}
|
|
1888
|
+
paintedRef.current = null;
|
|
1770
1889
|
};
|
|
1771
1890
|
}, [pageNumber, unmarkPageRendered]);
|
|
1772
1891
|
return {
|
|
@@ -1839,6 +1958,12 @@ var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
|
1839
1958
|
|
|
1840
1959
|
// src/hooks/layers/useDetailCanvasLayer.tsx
|
|
1841
1960
|
var DETAIL_RENDER_IDLE_MS = 80;
|
|
1961
|
+
var SCROLL_POLL_MS = 160;
|
|
1962
|
+
var ZOOM_DEBOUNCE_MS = 200;
|
|
1963
|
+
var ZOOM_SETTLE_MS = ZOOM_DEBOUNCE_MS + 50;
|
|
1964
|
+
var OVERSCAN_X = 0.25;
|
|
1965
|
+
var OVERSCAN_Y = 0.5;
|
|
1966
|
+
var RELEASE_HIDDEN_MS = 2e3;
|
|
1842
1967
|
var useDetailCanvasLayer = ({
|
|
1843
1968
|
background,
|
|
1844
1969
|
baseCanvasRef
|
|
@@ -1849,15 +1974,23 @@ var useDetailCanvasLayer = ({
|
|
|
1849
1974
|
const dpr = useDpr();
|
|
1850
1975
|
const store = PDFStore.useContext();
|
|
1851
1976
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1852
|
-
const isPinching = usePdf((state) => state.isPinching);
|
|
1853
1977
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1854
1978
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1855
1979
|
const colorScheme = usePdf((state) => state.colorScheme);
|
|
1856
1980
|
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
1857
1981
|
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
1858
1982
|
const recolorKey = recolor ? `dark:${darkModeColors.background},${darkModeColors.foreground}` : "light";
|
|
1859
|
-
const
|
|
1860
|
-
const
|
|
1983
|
+
const paintedRef = useRef(null);
|
|
1984
|
+
const releaseTimerRef = useRef(null);
|
|
1985
|
+
const [zoom] = useDebounce(bouncyZoom, ZOOM_DEBOUNCE_MS);
|
|
1986
|
+
const lastZoomChangeRef = useRef(
|
|
1987
|
+
store.getState().isPinching ? performance.now() : 0
|
|
1988
|
+
);
|
|
1989
|
+
const prevBouncyZoomRef = useRef(bouncyZoom);
|
|
1990
|
+
if (prevBouncyZoomRef.current !== bouncyZoom) {
|
|
1991
|
+
prevBouncyZoomRef.current = bouncyZoom;
|
|
1992
|
+
lastZoomChangeRef.current = performance.now();
|
|
1993
|
+
}
|
|
1861
1994
|
const pageDimsRef = useRef(null);
|
|
1862
1995
|
if (!pageDimsRef.current || pageDimsRef.current.proxy !== pdfPageProxy) {
|
|
1863
1996
|
const vp = pdfPageProxy.getViewport({ scale: 1 });
|
|
@@ -1879,35 +2012,52 @@ var useDetailCanvasLayer = ({
|
|
|
1879
2012
|
}
|
|
1880
2013
|
let renderingTask = null;
|
|
1881
2014
|
let renderTimeoutId = null;
|
|
1882
|
-
const bgColor = recolor ? recolor(background ?? "#ffffff") : background ?? "white";
|
|
1883
2015
|
const contentKey = `${background ?? "white"}|${recolorKey}`;
|
|
1884
|
-
const detailBaseStyle =
|
|
2016
|
+
const detailBaseStyle = "position:absolute;top:0;left:0;pointer-events:none;z-index:1";
|
|
1885
2017
|
const hideDetailCanvas = () => {
|
|
1886
2018
|
renderingTask?.cancel();
|
|
2019
|
+
renderingTask = null;
|
|
1887
2020
|
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:0`;
|
|
1888
2021
|
container.style.cssText = "";
|
|
2022
|
+
paintedRef.current = null;
|
|
2023
|
+
if (releaseTimerRef.current === null) {
|
|
2024
|
+
releaseTimerRef.current = setTimeout(() => {
|
|
2025
|
+
releaseTimerRef.current = null;
|
|
2026
|
+
detailCanvas.width = 1;
|
|
2027
|
+
detailCanvas.height = 1;
|
|
2028
|
+
}, RELEASE_HIDDEN_MS);
|
|
2029
|
+
}
|
|
1889
2030
|
};
|
|
2031
|
+
const pageDims = pageDimsRef.current;
|
|
2032
|
+
if (!pageDims) {
|
|
2033
|
+
hideDetailCanvas();
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
const { width: pageWidth, height: pageHeight } = pageDims;
|
|
2037
|
+
const targetDetailScale = computeTargetScale(dpr, zoom);
|
|
2038
|
+
const baseScale = computeBaseScale(dpr, zoom, pageWidth, pageHeight);
|
|
2039
|
+
const needsDetail = targetDetailScale - baseScale > 1e-3;
|
|
1890
2040
|
const renderDetailCanvas = () => {
|
|
2041
|
+
if (targetDetailScale - computeBaseScale(dpr, zoom, pageWidth, pageHeight) <= 1e-3) {
|
|
2042
|
+
hideDetailCanvas();
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
1891
2045
|
const virtualizer = store.getState().virtualizer;
|
|
1892
2046
|
if (virtualizer?.isScrolling) {
|
|
1893
|
-
scheduleRender(
|
|
2047
|
+
scheduleRender(SCROLL_POLL_MS);
|
|
1894
2048
|
return;
|
|
1895
2049
|
}
|
|
1896
|
-
const
|
|
1897
|
-
if (
|
|
1898
|
-
|
|
2050
|
+
const settleElapsed = performance.now() - lastZoomChangeRef.current;
|
|
2051
|
+
if (store.getState().isPinching || settleElapsed < ZOOM_SETTLE_MS) {
|
|
2052
|
+
scheduleRender(Math.max(50, ZOOM_SETTLE_MS - settleElapsed + 10));
|
|
1899
2053
|
return;
|
|
1900
2054
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
pageHeight
|
|
1908
|
-
);
|
|
1909
|
-
const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
|
|
1910
|
-
if (isPinching || !needsDetail) {
|
|
2055
|
+
if (store.getState().zoom !== zoom) {
|
|
2056
|
+
scheduleRender(ZOOM_SETTLE_MS);
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
const pageContainer = baseCanvasRef.current?.parentElement ?? null;
|
|
2060
|
+
if (!pageContainer) {
|
|
1911
2061
|
hideDetailCanvas();
|
|
1912
2062
|
return;
|
|
1913
2063
|
}
|
|
@@ -1935,17 +2085,72 @@ var useDetailCanvasLayer = ({
|
|
|
1935
2085
|
hideDetailCanvas();
|
|
1936
2086
|
return;
|
|
1937
2087
|
}
|
|
2088
|
+
const budget = getCanvasPixelBudget();
|
|
2089
|
+
let effectiveScale = clampScaleForPage(
|
|
2090
|
+
targetDetailScale,
|
|
2091
|
+
visibleWidth,
|
|
2092
|
+
visibleHeight,
|
|
2093
|
+
budget
|
|
2094
|
+
);
|
|
2095
|
+
const padX = OVERSCAN_X * viewportWidth;
|
|
2096
|
+
const padY = OVERSCAN_Y * viewportHeight;
|
|
2097
|
+
const maxRectWidth = Math.min(pageWidth, visibleWidth + 2 * padX);
|
|
2098
|
+
const maxRectHeight = Math.min(pageHeight, visibleHeight + 2 * padY);
|
|
2099
|
+
effectiveScale = Math.min(
|
|
2100
|
+
effectiveScale,
|
|
2101
|
+
MAX_CANVAS_DIMENSION / Math.max(maxRectWidth, 1),
|
|
2102
|
+
MAX_CANVAS_DIMENSION / Math.max(maxRectHeight, 1)
|
|
2103
|
+
);
|
|
2104
|
+
const eps = 1 / effectiveScale;
|
|
2105
|
+
const painted = paintedRef.current;
|
|
2106
|
+
if (painted && painted.proxy === pdfPageProxy && painted.key === contentKey && painted.scale === effectiveScale && visibleLeft >= painted.left - eps && visibleTop >= painted.top - eps && visibleRight <= painted.left + painted.width + eps && visibleBottom <= painted.top + painted.height + eps) {
|
|
2107
|
+
renderingTask?.cancel();
|
|
2108
|
+
renderingTask = null;
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
const maxArea = budget / (effectiveScale * effectiveScale);
|
|
2112
|
+
const a = 4 * padX * padY;
|
|
2113
|
+
const b = 2 * (visibleWidth * padY + visibleHeight * padX);
|
|
2114
|
+
const c = visibleWidth * visibleHeight - maxArea;
|
|
2115
|
+
let padFactor = 1;
|
|
2116
|
+
if (a > 0) {
|
|
2117
|
+
padFactor = Math.min(
|
|
2118
|
+
1,
|
|
2119
|
+
Math.max(
|
|
2120
|
+
0,
|
|
2121
|
+
(-b + Math.sqrt(Math.max(b * b - 4 * a * c, 0))) / (2 * a)
|
|
2122
|
+
)
|
|
2123
|
+
);
|
|
2124
|
+
} else if (b > 0) {
|
|
2125
|
+
padFactor = Math.min(1, Math.max(0, -c / b));
|
|
2126
|
+
}
|
|
2127
|
+
const rectLeft = Math.max(0, visibleLeft - padX * padFactor);
|
|
2128
|
+
const rectTop = Math.max(0, visibleTop - padY * padFactor);
|
|
2129
|
+
const rectRight = Math.min(pageWidth, visibleRight + padX * padFactor);
|
|
2130
|
+
const rectBottom = Math.min(pageHeight, visibleBottom + padY * padFactor);
|
|
2131
|
+
const rectWidth = rectRight - rectLeft;
|
|
2132
|
+
const rectHeight = rectBottom - rectTop;
|
|
1938
2133
|
renderingTask?.cancel();
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
const
|
|
2134
|
+
if (releaseTimerRef.current !== null) {
|
|
2135
|
+
clearTimeout(releaseTimerRef.current);
|
|
2136
|
+
releaseTimerRef.current = null;
|
|
2137
|
+
}
|
|
2138
|
+
const actualWidth = Math.max(1, Math.floor(rectWidth * effectiveScale));
|
|
2139
|
+
const actualHeight = Math.max(1, Math.floor(rectHeight * effectiveScale));
|
|
1944
2140
|
const buffer = document.createElement("canvas");
|
|
1945
2141
|
buffer.width = actualWidth;
|
|
1946
2142
|
buffer.height = actualHeight;
|
|
1947
2143
|
const bufferCtx = buffer.getContext("2d");
|
|
1948
2144
|
if (!bufferCtx) {
|
|
2145
|
+
buffer.width = 0;
|
|
2146
|
+
buffer.height = 0;
|
|
2147
|
+
hideDetailCanvas();
|
|
2148
|
+
if (releaseTimerRef.current !== null) {
|
|
2149
|
+
clearTimeout(releaseTimerRef.current);
|
|
2150
|
+
releaseTimerRef.current = null;
|
|
2151
|
+
}
|
|
2152
|
+
detailCanvas.width = 1;
|
|
2153
|
+
detailCanvas.height = 1;
|
|
1949
2154
|
return;
|
|
1950
2155
|
}
|
|
1951
2156
|
bufferCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -1954,21 +2159,35 @@ var useDetailCanvasLayer = ({
|
|
|
1954
2159
|
0,
|
|
1955
2160
|
0,
|
|
1956
2161
|
1,
|
|
1957
|
-
-
|
|
1958
|
-
-
|
|
2162
|
+
-rectLeft * effectiveScale,
|
|
2163
|
+
-rectTop * effectiveScale
|
|
1959
2164
|
];
|
|
1960
2165
|
const detailViewport = pdfPageProxy.getViewport({
|
|
1961
2166
|
scale: effectiveScale
|
|
1962
2167
|
});
|
|
1963
2168
|
if (recolor) applyContextRecolor(bufferCtx, recolor);
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2169
|
+
const releaseBuffer = () => {
|
|
2170
|
+
buffer.width = 0;
|
|
2171
|
+
buffer.height = 0;
|
|
2172
|
+
};
|
|
2173
|
+
let currentRenderingTask;
|
|
2174
|
+
try {
|
|
2175
|
+
currentRenderingTask = pdfPageProxy.render({
|
|
2176
|
+
canvas: buffer,
|
|
2177
|
+
canvasContext: bufferCtx,
|
|
2178
|
+
viewport: detailViewport,
|
|
2179
|
+
background,
|
|
2180
|
+
transform
|
|
2181
|
+
});
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
releaseBuffer();
|
|
2184
|
+
hideDetailCanvas();
|
|
2185
|
+
console.error("PDF detail render error:", error);
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
1971
2188
|
renderingTask = currentRenderingTask;
|
|
2189
|
+
const cssWidth = actualWidth / effectiveScale * zoom;
|
|
2190
|
+
const cssHeight = actualHeight / effectiveScale * zoom;
|
|
1972
2191
|
currentRenderingTask.promise.then(() => {
|
|
1973
2192
|
if (renderingTask !== currentRenderingTask) return;
|
|
1974
2193
|
const state = store.getState();
|
|
@@ -1976,13 +2195,36 @@ var useDetailCanvasLayer = ({
|
|
|
1976
2195
|
if (currentRecolorKey !== recolorKey) return;
|
|
1977
2196
|
detailCanvas.width = actualWidth;
|
|
1978
2197
|
detailCanvas.height = actualHeight;
|
|
1979
|
-
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:1;width:${
|
|
2198
|
+
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:1;width:${cssWidth}px;height:${cssHeight}px;transform-origin:center center;transform:translate(${rectLeft * zoom}px,${rectTop * zoom}px)`;
|
|
1980
2199
|
container.style.cssText = `transform:scale3d(${1 / zoom},${1 / zoom},1);transform-origin:0 0`;
|
|
1981
2200
|
const ctx = detailCanvas.getContext("2d");
|
|
1982
|
-
if (ctx) {
|
|
2201
|
+
if (!ctx) {
|
|
2202
|
+
hideDetailCanvas();
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
try {
|
|
1983
2206
|
ctx.drawImage(buffer, 0, 0);
|
|
2207
|
+
} catch (error) {
|
|
2208
|
+
hideDetailCanvas();
|
|
2209
|
+
console.error("PDF detail canvas blit error:", error);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
paintedRef.current = {
|
|
2213
|
+
proxy: pdfPageProxy,
|
|
2214
|
+
key: contentKey,
|
|
2215
|
+
scale: effectiveScale,
|
|
2216
|
+
left: rectLeft,
|
|
2217
|
+
top: rectTop,
|
|
2218
|
+
// The truly painted extent comes from the floored backing,
|
|
2219
|
+
// not the requested rect.
|
|
2220
|
+
width: actualWidth / effectiveScale,
|
|
2221
|
+
height: actualHeight / effectiveScale
|
|
2222
|
+
};
|
|
2223
|
+
if (releaseTimerRef.current !== null) {
|
|
2224
|
+
clearTimeout(releaseTimerRef.current);
|
|
2225
|
+
releaseTimerRef.current = null;
|
|
1984
2226
|
}
|
|
1985
|
-
|
|
2227
|
+
scheduleRender();
|
|
1986
2228
|
}).catch((error) => {
|
|
1987
2229
|
if (error.name === "RenderingCancelledException") {
|
|
1988
2230
|
return;
|
|
@@ -1992,15 +2234,13 @@ var useDetailCanvasLayer = ({
|
|
|
1992
2234
|
if (renderingTask === currentRenderingTask) {
|
|
1993
2235
|
renderingTask = null;
|
|
1994
2236
|
}
|
|
1995
|
-
|
|
1996
|
-
buffer.height = 0;
|
|
2237
|
+
releaseBuffer();
|
|
1997
2238
|
});
|
|
1998
2239
|
};
|
|
1999
2240
|
const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
|
|
2000
2241
|
if (renderTimeoutId !== null) {
|
|
2001
2242
|
clearTimeout(renderTimeoutId);
|
|
2002
2243
|
}
|
|
2003
|
-
renderingTask?.cancel();
|
|
2004
2244
|
renderTimeoutId = setTimeout(() => {
|
|
2005
2245
|
renderTimeoutId = null;
|
|
2006
2246
|
renderDetailCanvas();
|
|
@@ -2010,12 +2250,11 @@ var useDetailCanvasLayer = ({
|
|
|
2010
2250
|
scrollContainer,
|
|
2011
2251
|
scheduleRender
|
|
2012
2252
|
);
|
|
2013
|
-
if (
|
|
2253
|
+
if (!needsDetail) {
|
|
2014
2254
|
hideDetailCanvas();
|
|
2015
2255
|
} else {
|
|
2016
|
-
if (
|
|
2256
|
+
if (paintedRef.current !== null && (paintedRef.current.key !== contentKey || paintedRef.current.proxy !== pdfPageProxy)) {
|
|
2017
2257
|
hideDetailCanvas();
|
|
2018
|
-
paintedKeyRef.current = null;
|
|
2019
2258
|
}
|
|
2020
2259
|
scheduleRender(0);
|
|
2021
2260
|
}
|
|
@@ -2029,7 +2268,6 @@ var useDetailCanvasLayer = ({
|
|
|
2029
2268
|
}, [
|
|
2030
2269
|
pdfPageProxy,
|
|
2031
2270
|
zoom,
|
|
2032
|
-
isPinching,
|
|
2033
2271
|
background,
|
|
2034
2272
|
dpr,
|
|
2035
2273
|
viewportRef,
|
|
@@ -2041,10 +2279,15 @@ var useDetailCanvasLayer = ({
|
|
|
2041
2279
|
useEffect(() => {
|
|
2042
2280
|
const canvas = detailCanvasRef.current;
|
|
2043
2281
|
return () => {
|
|
2282
|
+
if (releaseTimerRef.current !== null) {
|
|
2283
|
+
clearTimeout(releaseTimerRef.current);
|
|
2284
|
+
releaseTimerRef.current = null;
|
|
2285
|
+
}
|
|
2044
2286
|
if (canvas) {
|
|
2045
2287
|
canvas.width = 1;
|
|
2046
2288
|
canvas.height = 1;
|
|
2047
2289
|
}
|
|
2290
|
+
paintedRef.current = null;
|
|
2048
2291
|
};
|
|
2049
2292
|
}, []);
|
|
2050
2293
|
return {
|
|
@@ -3080,6 +3323,7 @@ var useTextLayer = () => {
|
|
|
3080
3323
|
const pageNumber = usePDFPageNumber();
|
|
3081
3324
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
3082
3325
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
3326
|
+
const store = PDFStore.useContext();
|
|
3083
3327
|
useEffect(() => {
|
|
3084
3328
|
const textContainer = textContainerRef.current;
|
|
3085
3329
|
if (!textContainer) {
|
|
@@ -3184,11 +3428,22 @@ var useTextLayer = () => {
|
|
|
3184
3428
|
const show = () => {
|
|
3185
3429
|
textContainer.style.visibility = "";
|
|
3186
3430
|
};
|
|
3187
|
-
const
|
|
3431
|
+
const restoreWhenSettled = () => {
|
|
3432
|
+
settleTimer = null;
|
|
3433
|
+
if (store.getState().isPinching) {
|
|
3434
|
+
settleTimer = setTimeout(restoreWhenSettled, TEXT_SCROLL_SETTLE_MS);
|
|
3435
|
+
return;
|
|
3436
|
+
}
|
|
3437
|
+
show();
|
|
3438
|
+
};
|
|
3439
|
+
const hideAndDebounce = () => {
|
|
3188
3440
|
if (selecting) return;
|
|
3189
3441
|
textContainer.style.visibility = "hidden";
|
|
3190
3442
|
if (settleTimer) clearTimeout(settleTimer);
|
|
3191
|
-
settleTimer = setTimeout(
|
|
3443
|
+
settleTimer = setTimeout(restoreWhenSettled, TEXT_SCROLL_SETTLE_MS);
|
|
3444
|
+
};
|
|
3445
|
+
const onScroll = () => {
|
|
3446
|
+
hideAndDebounce();
|
|
3192
3447
|
};
|
|
3193
3448
|
const onPointerDown = () => {
|
|
3194
3449
|
pointerDown = true;
|
|
@@ -3220,6 +3475,19 @@ var useTextLayer = () => {
|
|
|
3220
3475
|
unsubscribe = subscribeToViewportInvalidation(scrollViewport, onScroll);
|
|
3221
3476
|
};
|
|
3222
3477
|
attachScrollSubscription();
|
|
3478
|
+
const initialState = store.getState();
|
|
3479
|
+
let prevZoom = initialState.zoom;
|
|
3480
|
+
let prevPinching = initialState.isPinching;
|
|
3481
|
+
if (prevPinching) hideAndDebounce();
|
|
3482
|
+
const unsubscribeStore = store.subscribe(
|
|
3483
|
+
(state) => {
|
|
3484
|
+
const zoomChanged = state.zoom !== prevZoom;
|
|
3485
|
+
const pinchStarted = state.isPinching && !prevPinching;
|
|
3486
|
+
prevZoom = state.zoom;
|
|
3487
|
+
prevPinching = state.isPinching;
|
|
3488
|
+
if (zoomChanged || pinchStarted) hideAndDebounce();
|
|
3489
|
+
}
|
|
3490
|
+
);
|
|
3223
3491
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
3224
3492
|
document.addEventListener("pointerup", clearPointer, true);
|
|
3225
3493
|
document.addEventListener("pointercancel", clearPointer, true);
|
|
@@ -3228,6 +3496,7 @@ var useTextLayer = () => {
|
|
|
3228
3496
|
return () => {
|
|
3229
3497
|
if (attachRaf !== null) cancelAnimationFrame(attachRaf);
|
|
3230
3498
|
unsubscribe?.();
|
|
3499
|
+
unsubscribeStore();
|
|
3231
3500
|
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
3232
3501
|
document.removeEventListener("pointerup", clearPointer, true);
|
|
3233
3502
|
document.removeEventListener("pointercancel", clearPointer, true);
|
|
@@ -3236,7 +3505,7 @@ var useTextLayer = () => {
|
|
|
3236
3505
|
if (settleTimer) clearTimeout(settleTimer);
|
|
3237
3506
|
show();
|
|
3238
3507
|
};
|
|
3239
|
-
}, [viewportRef]);
|
|
3508
|
+
}, [viewportRef, store]);
|
|
3240
3509
|
return {
|
|
3241
3510
|
textContainerRef,
|
|
3242
3511
|
pageNumber: pdfPageProxy.pageNumber
|
|
@@ -5600,10 +5869,10 @@ var useViewportContainer = ({
|
|
|
5600
5869
|
const transformations = useRef({
|
|
5601
5870
|
translateX: 0,
|
|
5602
5871
|
translateY: 0,
|
|
5603
|
-
zoom
|
|
5872
|
+
zoom: 1
|
|
5604
5873
|
});
|
|
5605
5874
|
const zoomRafRef = useRef(null);
|
|
5606
|
-
const lastPushedZoomRef = useRef(
|
|
5875
|
+
const lastPushedZoomRef = useRef(null);
|
|
5607
5876
|
const updateTransform = useCallback(
|
|
5608
5877
|
(zoomUpdate) => {
|
|
5609
5878
|
if (!elementRef.current || !containerRef.current || !elementWrapperRef.current) {
|
|
@@ -5640,8 +5909,14 @@ var useViewportContainer = ({
|
|
|
5640
5909
|
return;
|
|
5641
5910
|
}
|
|
5642
5911
|
if (zoom === lastPushedZoomRef.current) {
|
|
5912
|
+
lastPushedZoomRef.current = null;
|
|
5643
5913
|
return;
|
|
5644
5914
|
}
|
|
5915
|
+
if (zoomRafRef.current !== null) {
|
|
5916
|
+
cancelAnimationFrame(zoomRafRef.current);
|
|
5917
|
+
zoomRafRef.current = null;
|
|
5918
|
+
}
|
|
5919
|
+
lastPushedZoomRef.current = null;
|
|
5645
5920
|
const prevZoom = transformations.current.zoom;
|
|
5646
5921
|
if (!prevZoom || !Number.isFinite(prevZoom)) {
|
|
5647
5922
|
transformations.current = {
|
|
@@ -5871,7 +6146,13 @@ var Pages = ({
|
|
|
5871
6146
|
overscan: virtualizerOptions?.overscan ?? 0,
|
|
5872
6147
|
scrollToFn,
|
|
5873
6148
|
gap,
|
|
5874
|
-
initialOffset
|
|
6149
|
+
initialOffset,
|
|
6150
|
+
// Never trust scrollend alone to reset isScrolling: a single missed
|
|
6151
|
+
// event (a documented browser flake — TanStack flipped this default to
|
|
6152
|
+
// false in later 3.x) would pin isScrolling=true forever, and every
|
|
6153
|
+
// consumer gated on it (text layer, detail canvas) would stop painting.
|
|
6154
|
+
// false keeps the isScrollingResetDelay debounce active as a fallback.
|
|
6155
|
+
useScrollendEvent: false
|
|
5875
6156
|
});
|
|
5876
6157
|
useEffect(() => {
|
|
5877
6158
|
if (onOffsetChange && virtualizer.scrollOffset)
|
|
@@ -6298,7 +6579,8 @@ var Root = forwardRef(
|
|
|
6298
6579
|
const linkService = useCreatePDFLinkService(
|
|
6299
6580
|
initialState?.pdfDocumentProxy ?? null
|
|
6300
6581
|
);
|
|
6301
|
-
const
|
|
6582
|
+
const pdfDocumentProxy = initialState?.pdfDocumentProxy ?? null;
|
|
6583
|
+
const documentId = pdfDocumentProxy ? getDocumentCacheId(pdfDocumentProxy) : null;
|
|
6302
6584
|
useEffect(() => {
|
|
6303
6585
|
if (!documentId) return;
|
|
6304
6586
|
return () => {
|
|
@@ -6357,6 +6639,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6357
6639
|
const containerRef = useRef(null);
|
|
6358
6640
|
const canvasRef = useRef(null);
|
|
6359
6641
|
const renderTaskRef = useRef(null);
|
|
6642
|
+
const renderedProxyRef = useRef(null);
|
|
6360
6643
|
const pageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
6361
6644
|
const { visible } = useVisibility({ elementRef: containerRef });
|
|
6362
6645
|
const [debouncedVisible] = useDebounce(visible, 50);
|
|
@@ -6366,6 +6649,8 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6366
6649
|
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
6367
6650
|
const isVisible = isFirstPage || debouncedVisible;
|
|
6368
6651
|
useEffect(() => {
|
|
6652
|
+
if (!isVisible && renderedProxyRef.current !== pageProxy) return;
|
|
6653
|
+
renderedProxyRef.current = pageProxy;
|
|
6369
6654
|
const renderThumbnail = async () => {
|
|
6370
6655
|
const canvas2 = canvasRef.current;
|
|
6371
6656
|
if (!canvas2 || !pageProxy) return;
|
|
@@ -6374,7 +6659,11 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6374
6659
|
renderTaskRef.current.cancel();
|
|
6375
6660
|
}
|
|
6376
6661
|
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
6377
|
-
const scale =
|
|
6662
|
+
const scale = clampScaleForPage(
|
|
6663
|
+
Math.min(maxWidth / viewport.width, maxHeight / viewport.height) * (isVisible ? dpr : 0.5),
|
|
6664
|
+
viewport.width,
|
|
6665
|
+
viewport.height
|
|
6666
|
+
);
|
|
6378
6667
|
const scaledViewport = pageProxy.getViewport({ scale });
|
|
6379
6668
|
canvas2.width = scaledViewport.width;
|
|
6380
6669
|
canvas2.height = scaledViewport.height;
|