@anaralabs/lector 3.13.1 → 3.13.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 +11 -1
- package/dist/index.js +373 -123
- package/package.json +3 -3
|
@@ -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,7 +1958,12 @@ var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
|
1839
1958
|
|
|
1840
1959
|
// src/hooks/layers/useDetailCanvasLayer.tsx
|
|
1841
1960
|
var DETAIL_RENDER_IDLE_MS = 80;
|
|
1842
|
-
var
|
|
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;
|
|
1843
1967
|
var useDetailCanvasLayer = ({
|
|
1844
1968
|
background,
|
|
1845
1969
|
baseCanvasRef
|
|
@@ -1850,15 +1974,15 @@ var useDetailCanvasLayer = ({
|
|
|
1850
1974
|
const dpr = useDpr();
|
|
1851
1975
|
const store = PDFStore.useContext();
|
|
1852
1976
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1853
|
-
const isPinching = usePdf((state) => state.isPinching);
|
|
1854
1977
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1855
1978
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1856
1979
|
const colorScheme = usePdf((state) => state.colorScheme);
|
|
1857
1980
|
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
1858
1981
|
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
1859
1982
|
const recolorKey = recolor ? `dark:${darkModeColors.background},${darkModeColors.foreground}` : "light";
|
|
1860
|
-
const
|
|
1861
|
-
const
|
|
1983
|
+
const paintedRef = useRef(null);
|
|
1984
|
+
const releaseTimerRef = useRef(null);
|
|
1985
|
+
const [zoom] = useDebounce(bouncyZoom, ZOOM_DEBOUNCE_MS);
|
|
1862
1986
|
const lastZoomChangeRef = useRef(
|
|
1863
1987
|
store.getState().isPinching ? performance.now() : 0
|
|
1864
1988
|
);
|
|
@@ -1888,39 +2012,52 @@ var useDetailCanvasLayer = ({
|
|
|
1888
2012
|
}
|
|
1889
2013
|
let renderingTask = null;
|
|
1890
2014
|
let renderTimeoutId = null;
|
|
1891
|
-
const bgColor = recolor ? recolor(background ?? "#ffffff") : background ?? "white";
|
|
1892
2015
|
const contentKey = `${background ?? "white"}|${recolorKey}`;
|
|
1893
|
-
const detailBaseStyle =
|
|
2016
|
+
const detailBaseStyle = "position:absolute;top:0;left:0;pointer-events:none;z-index:1";
|
|
1894
2017
|
const hideDetailCanvas = () => {
|
|
1895
2018
|
renderingTask?.cancel();
|
|
2019
|
+
renderingTask = null;
|
|
1896
2020
|
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:0`;
|
|
1897
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
|
+
}
|
|
1898
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;
|
|
1899
2040
|
const renderDetailCanvas = () => {
|
|
2041
|
+
if (targetDetailScale - computeBaseScale(dpr, zoom, pageWidth, pageHeight) <= 1e-3) {
|
|
2042
|
+
hideDetailCanvas();
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
1900
2045
|
const virtualizer = store.getState().virtualizer;
|
|
1901
2046
|
if (virtualizer?.isScrolling) {
|
|
1902
|
-
scheduleRender(
|
|
2047
|
+
scheduleRender(SCROLL_POLL_MS);
|
|
1903
2048
|
return;
|
|
1904
2049
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
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));
|
|
1907
2053
|
return;
|
|
1908
2054
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
hideDetailCanvas();
|
|
2055
|
+
if (store.getState().zoom !== zoom) {
|
|
2056
|
+
scheduleRender(ZOOM_SETTLE_MS);
|
|
1912
2057
|
return;
|
|
1913
2058
|
}
|
|
1914
|
-
const
|
|
1915
|
-
|
|
1916
|
-
const baseTargetScale = dpr * Math.min(zoom, 1);
|
|
1917
|
-
const baseScale = clampScaleForPage(
|
|
1918
|
-
baseTargetScale,
|
|
1919
|
-
pageWidth,
|
|
1920
|
-
pageHeight
|
|
1921
|
-
);
|
|
1922
|
-
const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
|
|
1923
|
-
if (isPinching || !needsDetail) {
|
|
2059
|
+
const pageContainer = baseCanvasRef.current?.parentElement ?? null;
|
|
2060
|
+
if (!pageContainer) {
|
|
1924
2061
|
hideDetailCanvas();
|
|
1925
2062
|
return;
|
|
1926
2063
|
}
|
|
@@ -1948,17 +2085,72 @@ var useDetailCanvasLayer = ({
|
|
|
1948
2085
|
hideDetailCanvas();
|
|
1949
2086
|
return;
|
|
1950
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;
|
|
1951
2133
|
renderingTask?.cancel();
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
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));
|
|
1957
2140
|
const buffer = document.createElement("canvas");
|
|
1958
2141
|
buffer.width = actualWidth;
|
|
1959
2142
|
buffer.height = actualHeight;
|
|
1960
2143
|
const bufferCtx = buffer.getContext("2d");
|
|
1961
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;
|
|
1962
2154
|
return;
|
|
1963
2155
|
}
|
|
1964
2156
|
bufferCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -1967,21 +2159,35 @@ var useDetailCanvasLayer = ({
|
|
|
1967
2159
|
0,
|
|
1968
2160
|
0,
|
|
1969
2161
|
1,
|
|
1970
|
-
-
|
|
1971
|
-
-
|
|
2162
|
+
-rectLeft * effectiveScale,
|
|
2163
|
+
-rectTop * effectiveScale
|
|
1972
2164
|
];
|
|
1973
2165
|
const detailViewport = pdfPageProxy.getViewport({
|
|
1974
2166
|
scale: effectiveScale
|
|
1975
2167
|
});
|
|
1976
2168
|
if (recolor) applyContextRecolor(bufferCtx, recolor);
|
|
1977
|
-
const
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
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
|
+
}
|
|
1984
2188
|
renderingTask = currentRenderingTask;
|
|
2189
|
+
const cssWidth = actualWidth / effectiveScale * zoom;
|
|
2190
|
+
const cssHeight = actualHeight / effectiveScale * zoom;
|
|
1985
2191
|
currentRenderingTask.promise.then(() => {
|
|
1986
2192
|
if (renderingTask !== currentRenderingTask) return;
|
|
1987
2193
|
const state = store.getState();
|
|
@@ -1989,13 +2195,36 @@ var useDetailCanvasLayer = ({
|
|
|
1989
2195
|
if (currentRecolorKey !== recolorKey) return;
|
|
1990
2196
|
detailCanvas.width = actualWidth;
|
|
1991
2197
|
detailCanvas.height = actualHeight;
|
|
1992
|
-
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)`;
|
|
1993
2199
|
container.style.cssText = `transform:scale3d(${1 / zoom},${1 / zoom},1);transform-origin:0 0`;
|
|
1994
2200
|
const ctx = detailCanvas.getContext("2d");
|
|
1995
|
-
if (ctx) {
|
|
2201
|
+
if (!ctx) {
|
|
2202
|
+
hideDetailCanvas();
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
try {
|
|
1996
2206
|
ctx.drawImage(buffer, 0, 0);
|
|
2207
|
+
} catch (error) {
|
|
2208
|
+
hideDetailCanvas();
|
|
2209
|
+
console.error("PDF detail canvas blit error:", error);
|
|
2210
|
+
return;
|
|
1997
2211
|
}
|
|
1998
|
-
|
|
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;
|
|
2226
|
+
}
|
|
2227
|
+
scheduleRender();
|
|
1999
2228
|
}).catch((error) => {
|
|
2000
2229
|
if (error.name === "RenderingCancelledException") {
|
|
2001
2230
|
return;
|
|
@@ -2005,15 +2234,13 @@ var useDetailCanvasLayer = ({
|
|
|
2005
2234
|
if (renderingTask === currentRenderingTask) {
|
|
2006
2235
|
renderingTask = null;
|
|
2007
2236
|
}
|
|
2008
|
-
|
|
2009
|
-
buffer.height = 0;
|
|
2237
|
+
releaseBuffer();
|
|
2010
2238
|
});
|
|
2011
2239
|
};
|
|
2012
2240
|
const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
|
|
2013
2241
|
if (renderTimeoutId !== null) {
|
|
2014
2242
|
clearTimeout(renderTimeoutId);
|
|
2015
2243
|
}
|
|
2016
|
-
renderingTask?.cancel();
|
|
2017
2244
|
renderTimeoutId = setTimeout(() => {
|
|
2018
2245
|
renderTimeoutId = null;
|
|
2019
2246
|
renderDetailCanvas();
|
|
@@ -2023,12 +2250,11 @@ var useDetailCanvasLayer = ({
|
|
|
2023
2250
|
scrollContainer,
|
|
2024
2251
|
scheduleRender
|
|
2025
2252
|
);
|
|
2026
|
-
if (
|
|
2253
|
+
if (!needsDetail) {
|
|
2027
2254
|
hideDetailCanvas();
|
|
2028
2255
|
} else {
|
|
2029
|
-
if (
|
|
2256
|
+
if (paintedRef.current !== null && (paintedRef.current.key !== contentKey || paintedRef.current.proxy !== pdfPageProxy)) {
|
|
2030
2257
|
hideDetailCanvas();
|
|
2031
|
-
paintedKeyRef.current = null;
|
|
2032
2258
|
}
|
|
2033
2259
|
scheduleRender(0);
|
|
2034
2260
|
}
|
|
@@ -2042,7 +2268,6 @@ var useDetailCanvasLayer = ({
|
|
|
2042
2268
|
}, [
|
|
2043
2269
|
pdfPageProxy,
|
|
2044
2270
|
zoom,
|
|
2045
|
-
isPinching,
|
|
2046
2271
|
background,
|
|
2047
2272
|
dpr,
|
|
2048
2273
|
viewportRef,
|
|
@@ -2054,10 +2279,15 @@ var useDetailCanvasLayer = ({
|
|
|
2054
2279
|
useEffect(() => {
|
|
2055
2280
|
const canvas = detailCanvasRef.current;
|
|
2056
2281
|
return () => {
|
|
2282
|
+
if (releaseTimerRef.current !== null) {
|
|
2283
|
+
clearTimeout(releaseTimerRef.current);
|
|
2284
|
+
releaseTimerRef.current = null;
|
|
2285
|
+
}
|
|
2057
2286
|
if (canvas) {
|
|
2058
2287
|
canvas.width = 1;
|
|
2059
2288
|
canvas.height = 1;
|
|
2060
2289
|
}
|
|
2290
|
+
paintedRef.current = null;
|
|
2061
2291
|
};
|
|
2062
2292
|
}, []);
|
|
2063
2293
|
return {
|
|
@@ -5639,10 +5869,10 @@ var useViewportContainer = ({
|
|
|
5639
5869
|
const transformations = useRef({
|
|
5640
5870
|
translateX: 0,
|
|
5641
5871
|
translateY: 0,
|
|
5642
|
-
zoom
|
|
5872
|
+
zoom: 1
|
|
5643
5873
|
});
|
|
5644
5874
|
const zoomRafRef = useRef(null);
|
|
5645
|
-
const lastPushedZoomRef = useRef(
|
|
5875
|
+
const lastPushedZoomRef = useRef(null);
|
|
5646
5876
|
const updateTransform = useCallback(
|
|
5647
5877
|
(zoomUpdate) => {
|
|
5648
5878
|
if (!elementRef.current || !containerRef.current || !elementWrapperRef.current) {
|
|
@@ -5679,8 +5909,14 @@ var useViewportContainer = ({
|
|
|
5679
5909
|
return;
|
|
5680
5910
|
}
|
|
5681
5911
|
if (zoom === lastPushedZoomRef.current) {
|
|
5912
|
+
lastPushedZoomRef.current = null;
|
|
5682
5913
|
return;
|
|
5683
5914
|
}
|
|
5915
|
+
if (zoomRafRef.current !== null) {
|
|
5916
|
+
cancelAnimationFrame(zoomRafRef.current);
|
|
5917
|
+
zoomRafRef.current = null;
|
|
5918
|
+
}
|
|
5919
|
+
lastPushedZoomRef.current = null;
|
|
5684
5920
|
const prevZoom = transformations.current.zoom;
|
|
5685
5921
|
if (!prevZoom || !Number.isFinite(prevZoom)) {
|
|
5686
5922
|
transformations.current = {
|
|
@@ -5910,7 +6146,13 @@ var Pages = ({
|
|
|
5910
6146
|
overscan: virtualizerOptions?.overscan ?? 0,
|
|
5911
6147
|
scrollToFn,
|
|
5912
6148
|
gap,
|
|
5913
|
-
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
|
|
5914
6156
|
});
|
|
5915
6157
|
useEffect(() => {
|
|
5916
6158
|
if (onOffsetChange && virtualizer.scrollOffset)
|
|
@@ -6337,7 +6579,8 @@ var Root = forwardRef(
|
|
|
6337
6579
|
const linkService = useCreatePDFLinkService(
|
|
6338
6580
|
initialState?.pdfDocumentProxy ?? null
|
|
6339
6581
|
);
|
|
6340
|
-
const
|
|
6582
|
+
const pdfDocumentProxy = initialState?.pdfDocumentProxy ?? null;
|
|
6583
|
+
const documentId = pdfDocumentProxy ? getDocumentCacheId(pdfDocumentProxy) : null;
|
|
6341
6584
|
useEffect(() => {
|
|
6342
6585
|
if (!documentId) return;
|
|
6343
6586
|
return () => {
|
|
@@ -6396,6 +6639,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6396
6639
|
const containerRef = useRef(null);
|
|
6397
6640
|
const canvasRef = useRef(null);
|
|
6398
6641
|
const renderTaskRef = useRef(null);
|
|
6642
|
+
const renderedProxyRef = useRef(null);
|
|
6399
6643
|
const pageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
6400
6644
|
const { visible } = useVisibility({ elementRef: containerRef });
|
|
6401
6645
|
const [debouncedVisible] = useDebounce(visible, 50);
|
|
@@ -6405,6 +6649,8 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6405
6649
|
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
6406
6650
|
const isVisible = isFirstPage || debouncedVisible;
|
|
6407
6651
|
useEffect(() => {
|
|
6652
|
+
if (!isVisible && renderedProxyRef.current !== pageProxy) return;
|
|
6653
|
+
renderedProxyRef.current = pageProxy;
|
|
6408
6654
|
const renderThumbnail = async () => {
|
|
6409
6655
|
const canvas2 = canvasRef.current;
|
|
6410
6656
|
if (!canvas2 || !pageProxy) return;
|
|
@@ -6413,7 +6659,11 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6413
6659
|
renderTaskRef.current.cancel();
|
|
6414
6660
|
}
|
|
6415
6661
|
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
6416
|
-
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
|
+
);
|
|
6417
6667
|
const scaledViewport = pageProxy.getViewport({ scale });
|
|
6418
6668
|
canvas2.width = scaledViewport.width;
|
|
6419
6669
|
canvas2.height = scaledViewport.height;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anaralabs/lector",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.3",
|
|
4
4
|
"description": "A headless PDF viewer for React",
|
|
5
5
|
"author": "andrewdr (https://github.com/andrewdoro)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@types/react-dom": "^19.0.2",
|
|
43
43
|
"@use-gesture/react": "^10.3.1",
|
|
44
44
|
"@vitejs/plugin-react": "^4.3.4",
|
|
45
|
-
"@vitest/browser": "^3.2.
|
|
45
|
+
"@vitest/browser": "^3.2.6",
|
|
46
46
|
"clsx": "^2.1.1",
|
|
47
47
|
"pdfjs-dist": "^5.5.207",
|
|
48
48
|
"playwright": "^1.45.3",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"tsup": "^8.3.5",
|
|
54
54
|
"typescript": "^5.5.3",
|
|
55
55
|
"vite": "^6.4.2",
|
|
56
|
-
"vitest": "^3.2.
|
|
56
|
+
"vitest": "^3.2.6",
|
|
57
57
|
"webdriverio": "^8.39.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|