@anaralabs/lector 3.9.1 → 3.10.1

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/index.js CHANGED
@@ -1,18 +1,15 @@
1
1
  'use client';
2
2
 
3
- import React, { createContext, forwardRef, useRef, useState, useCallback, useContext, useEffect, cloneElement, createRef, useMemo, useLayoutEffect } from 'react';
3
+ import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef, useState, useCallback, useContext, useLayoutEffect, createRef } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions, inline, flip } from '@floating-ui/react';
6
6
  import { create, useStore, createStore } from 'zustand';
7
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
- import { AnnotationLayer, TextLayer, getDocument } from 'pdfjs-dist';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
8
  import { useDebounce } from 'use-debounce';
10
9
  import { v4 } from 'uuid';
11
10
  import { Slot } from '@radix-ui/react-slot';
12
11
  import { useVirtualizer, elementScroll, debounce } from '@tanstack/react-virtual';
13
12
 
14
- // src/components/annotation-tooltip.tsx
15
-
16
13
  // src/lib/clamp.ts
17
14
  var clamp = (value, minimum, maximum) => {
18
15
  return Math.min(Math.max(value, minimum), maximum);
@@ -127,7 +124,19 @@ var PDFStore = createZustandContext(
127
124
  coloredHighlights: prevState.coloredHighlights.filter(
128
125
  (rect) => rect.uuid !== uuid
129
126
  )
130
- }))
127
+ })),
128
+ renderedPages: {},
129
+ markPageRendered: (pageNumber) => {
130
+ const current = get().renderedPages;
131
+ if (current[pageNumber]) return;
132
+ set({ renderedPages: { ...current, [pageNumber]: true } });
133
+ },
134
+ unmarkPageRendered: (pageNumber) => {
135
+ const current = get().renderedPages;
136
+ if (!current[pageNumber]) return;
137
+ const { [pageNumber]: _, ...rest } = current;
138
+ set({ renderedPages: rest });
139
+ }
131
140
  }));
132
141
  }
133
142
  );
@@ -465,12 +474,15 @@ var AnnotationHighlightLayer = ({
465
474
  }) => {
466
475
  const { annotations } = useAnnotations();
467
476
  const pageNumber = usePDFPageNumber();
468
- const pageAnnotations = annotations.filter(
469
- (annotation) => annotation.pageNumber === pageNumber
477
+ const isPageRendered = usePdf((state) => !!state.renderedPages[pageNumber]);
478
+ const pageAnnotations = useMemo(
479
+ () => annotations.filter((a) => a.pageNumber === pageNumber),
480
+ [annotations, pageNumber]
470
481
  );
482
+ if (!isPageRendered) return null;
471
483
  const getCommentIconPosition = (highlights) => {
472
484
  if (!highlights.length) return { top: 0, right: 10 };
473
- const sortedHighlights = [...highlights].sort((a, b) => {
485
+ const sortedHighlights = highlights.toSorted((a, b) => {
474
486
  const topDiff = a.top - b.top;
475
487
  return Math.abs(topDiff) < 3 ? a.left - b.left : topDiff;
476
488
  });
@@ -615,6 +627,56 @@ function clsx() {
615
627
  }
616
628
  var clsx_default = clsx;
617
629
 
630
+ // src/lib/annotation-layer-styles.ts
631
+ var ANNOTATION_LAYER_STYLE_ID = "lector-annotation-layer-styles";
632
+ var annotationLayerStyles = `
633
+ .annotationLayer {
634
+ position: absolute;
635
+ left: 0;
636
+ top: 0;
637
+ right: 0;
638
+ bottom: 0;
639
+ overflow: hidden;
640
+ opacity: 1;
641
+ z-index: 3;
642
+ }
643
+
644
+ .annotationLayer section {
645
+ position: absolute;
646
+ }
647
+
648
+ .annotationLayer .linkAnnotation > a,
649
+ .annotationLayer .buttonWidgetAnnotation.pushButton > a {
650
+ position: absolute;
651
+ font-size: 1em;
652
+ top: 0;
653
+ left: 0;
654
+ width: 100%;
655
+ height: 100%;
656
+ background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
657
+ cursor: pointer;
658
+ }
659
+
660
+ .annotationLayer .linkAnnotation > a:hover,
661
+ .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
662
+ opacity: 0.2;
663
+ background: rgba(255, 255, 0, 1);
664
+ box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
665
+ }
666
+ `;
667
+ var ensureAnnotationLayerStyles = () => {
668
+ if (typeof document === "undefined") {
669
+ return;
670
+ }
671
+ if (document.getElementById(ANNOTATION_LAYER_STYLE_ID)) {
672
+ return;
673
+ }
674
+ const style = document.createElement("style");
675
+ style.id = ANNOTATION_LAYER_STYLE_ID;
676
+ style.textContent = annotationLayerStyles;
677
+ document.head.appendChild(style);
678
+ };
679
+
618
680
  // src/lib/cancellable.ts
619
681
  var cancellable = (promise) => {
620
682
  let isCancelled = false;
@@ -639,6 +701,15 @@ var cancellable = (promise) => {
639
701
  }
640
702
  };
641
703
  };
704
+
705
+ // src/lib/pdfjs.ts
706
+ var pdfJsPromise = null;
707
+ var loadPdfJs = () => {
708
+ if (!pdfJsPromise) {
709
+ pdfJsPromise = import('pdfjs-dist/legacy/build/pdf.mjs');
710
+ }
711
+ return pdfJsPromise;
712
+ };
642
713
  var usePdfJump = () => {
643
714
  const virtualizer = usePdf((state) => state.virtualizer);
644
715
  const setHighlight = usePdf((state) => state.setHighlight);
@@ -931,46 +1002,7 @@ var useAnnotationLayer = (params) => {
931
1002
  };
932
1003
  }, [jumpToPage, linkService, mergedParams.jumpOptions]);
933
1004
  useEffect(() => {
934
- const style = document.createElement("style");
935
- style.textContent = `
936
- .annotationLayer {
937
- position: absolute;
938
- left: 0;
939
- top: 0;
940
- right: 0;
941
- bottom: 0;
942
- overflow: hidden;
943
- opacity: 1;
944
- z-index: 3;
945
- }
946
-
947
- .annotationLayer section {
948
- position: absolute;
949
- }
950
-
951
- .annotationLayer .linkAnnotation > a,
952
- .annotationLayer .buttonWidgetAnnotation.pushButton > a {
953
- position: absolute;
954
- font-size: 1em;
955
- top: 0;
956
- left: 0;
957
- width: 100%;
958
- height: 100%;
959
- background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
960
- cursor: pointer;
961
- }
962
-
963
- .annotationLayer .linkAnnotation > a:hover,
964
- .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
965
- opacity: 0.2;
966
- background: rgba(255, 255, 0, 1);
967
- box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
968
- }
969
- `;
970
- document.head.appendChild(style);
971
- return () => {
972
- document.head.removeChild(style);
973
- };
1005
+ ensureAnnotationLayerStyles();
974
1006
  }, []);
975
1007
  useEffect(() => {
976
1008
  if (!annotationLayerRef.current) return;
@@ -1011,22 +1043,27 @@ var useAnnotationLayer = (params) => {
1011
1043
  }
1012
1044
  annotationLayerRef.current.innerHTML = "";
1013
1045
  annotationLayerRef.current.className = "annotationLayer";
1014
- const viewport = pdfPageProxy.getViewport({ scale: 1 });
1046
+ const viewport = pdfPageProxy.getViewport({ scale: 1 }).clone({
1047
+ dontFlip: true
1048
+ });
1015
1049
  const annotationLayerConfig = {
1016
1050
  div: annotationLayerRef.current,
1017
1051
  viewport,
1018
1052
  page: pdfPageProxy,
1019
1053
  linkService,
1054
+ annotationStorage: void 0,
1020
1055
  accessibilityManager: void 0,
1021
1056
  annotationCanvasMap: void 0,
1022
1057
  annotationEditorUIManager: void 0,
1023
- structTreeLayer: void 0
1058
+ structTreeLayer: void 0,
1059
+ commentManager: void 0
1024
1060
  };
1025
- const annotationLayer = new AnnotationLayer(annotationLayerConfig);
1026
- annotationLayerObjectRef.current = annotationLayer;
1027
1061
  const { cancel } = cancellable(
1028
1062
  (async () => {
1029
1063
  try {
1064
+ const { AnnotationLayer: AnnotationLayer2 } = await loadPdfJs();
1065
+ const annotationLayer = new AnnotationLayer2(annotationLayerConfig);
1066
+ annotationLayerObjectRef.current = annotationLayer;
1030
1067
  const annotations = await pdfPageProxy.getAnnotations();
1031
1068
  await annotationLayer.render({
1032
1069
  ...annotationLayerConfig,
@@ -1046,34 +1083,177 @@ var useAnnotationLayer = (params) => {
1046
1083
  annotationLayerRef
1047
1084
  };
1048
1085
  };
1049
- var AnnotationLayer2 = ({
1050
- renderForms = true,
1051
- externalLinksEnabled = true,
1052
- jumpOptions = { behavior: "smooth", align: "start" },
1053
- className,
1054
- style,
1055
- ...props
1056
- }) => {
1057
- const { annotationLayerRef } = useAnnotationLayer({
1058
- renderForms,
1059
- externalLinksEnabled,
1060
- jumpOptions
1061
- });
1062
- return /* @__PURE__ */ jsx(
1063
- "div",
1064
- {
1065
- className: clsx_default("annotationLayer", className),
1066
- style: {
1067
- ...style,
1068
- position: "absolute",
1069
- top: 0,
1070
- left: 0
1071
- },
1072
- ...props,
1073
- ref: annotationLayerRef
1074
- }
1086
+ var AnnotationLayer = memo(
1087
+ ({
1088
+ renderForms = true,
1089
+ externalLinksEnabled = true,
1090
+ jumpOptions = { behavior: "smooth", align: "start" },
1091
+ className,
1092
+ style,
1093
+ ...props
1094
+ }) => {
1095
+ const { annotationLayerRef } = useAnnotationLayer({
1096
+ renderForms,
1097
+ externalLinksEnabled,
1098
+ jumpOptions
1099
+ });
1100
+ return /* @__PURE__ */ jsx(
1101
+ "div",
1102
+ {
1103
+ className: clsx_default("annotationLayer", className),
1104
+ style: {
1105
+ ...style,
1106
+ position: "absolute",
1107
+ top: 0,
1108
+ left: 0
1109
+ },
1110
+ ...props,
1111
+ ref: annotationLayerRef
1112
+ }
1113
+ );
1114
+ }
1115
+ );
1116
+ AnnotationLayer.displayName = "AnnotationLayer";
1117
+
1118
+ // src/lib/canvas-utils.ts
1119
+ var MAX_CANVAS_PIXELS = 16777216;
1120
+ var MAX_CANVAS_DIMENSION = 32767;
1121
+ function clampScaleForPage(targetScale, pageWidth, pageHeight) {
1122
+ if (!targetScale) {
1123
+ return 0;
1124
+ }
1125
+ const areaLimit = Math.sqrt(
1126
+ MAX_CANVAS_PIXELS / Math.max(pageWidth * pageHeight, 1)
1075
1127
  );
1128
+ const widthLimit = MAX_CANVAS_DIMENSION / Math.max(pageWidth, 1);
1129
+ const heightLimit = MAX_CANVAS_DIMENSION / Math.max(pageHeight, 1);
1130
+ const safeScale = Math.min(
1131
+ targetScale,
1132
+ Number.isFinite(areaLimit) ? areaLimit : targetScale,
1133
+ Number.isFinite(widthLimit) ? widthLimit : targetScale,
1134
+ Number.isFinite(heightLimit) ? heightLimit : targetScale
1135
+ );
1136
+ return Math.max(safeScale, 0);
1137
+ }
1138
+
1139
+ // src/lib/render-cache.ts
1140
+ var RenderCache = class {
1141
+ cache = /* @__PURE__ */ new Map();
1142
+ maxEntries;
1143
+ // Tracks invalidated documents so in-flight createImageBitmap calls
1144
+ // don't insert orphaned entries after invalidateDocument() runs.
1145
+ invalidatedDocs = /* @__PURE__ */ new Set();
1146
+ constructor(maxEntries = 30) {
1147
+ this.maxEntries = maxEntries;
1148
+ }
1149
+ key(documentId, pageNumber, scale, background) {
1150
+ return `${documentId}-${pageNumber}-${scale}-${background ?? "white"}`;
1151
+ }
1152
+ get(documentId, pageNumber, scale, background) {
1153
+ const k = this.key(documentId, pageNumber, scale, background);
1154
+ const entry = this.cache.get(k);
1155
+ if (!entry) return void 0;
1156
+ this.cache.delete(k);
1157
+ this.cache.set(k, entry);
1158
+ return entry;
1159
+ }
1160
+ async set(documentId, pageNumber, scale, canvas, background) {
1161
+ const k = this.key(documentId, pageNumber, scale, background);
1162
+ const w = canvas.width;
1163
+ const h = canvas.height;
1164
+ if (w === 0 || h === 0) return;
1165
+ while (this.cache.size >= this.maxEntries) {
1166
+ const oldest = this.cache.keys().next().value;
1167
+ if (oldest !== void 0) {
1168
+ const entry = this.cache.get(oldest);
1169
+ entry?.bitmap.close();
1170
+ this.cache.delete(oldest);
1171
+ }
1172
+ }
1173
+ try {
1174
+ const bitmap = await createImageBitmap(canvas);
1175
+ if (this.invalidatedDocs.has(documentId)) {
1176
+ bitmap.close();
1177
+ return;
1178
+ }
1179
+ this.cache.set(k, {
1180
+ bitmap,
1181
+ width: w,
1182
+ height: h
1183
+ });
1184
+ } catch {
1185
+ }
1186
+ }
1187
+ /** Remove all cached entries for a specific document (call on Root unmount). */
1188
+ invalidateDocument(documentId) {
1189
+ this.invalidatedDocs.add(documentId);
1190
+ for (const [k, entry] of this.cache) {
1191
+ if (k.startsWith(`${documentId}-`)) {
1192
+ entry.bitmap.close();
1193
+ this.cache.delete(k);
1194
+ }
1195
+ }
1196
+ setTimeout(() => {
1197
+ this.invalidatedDocs.delete(documentId);
1198
+ }, 1e3);
1199
+ }
1200
+ clear() {
1201
+ for (const entry of this.cache.values()) {
1202
+ entry.bitmap.close();
1203
+ }
1204
+ this.cache.clear();
1205
+ this.invalidatedDocs.clear();
1206
+ }
1207
+ };
1208
+ var renderCache = new RenderCache();
1209
+
1210
+ // src/lib/render-queue.ts
1211
+ var RenderQueue = class {
1212
+ queue = [];
1213
+ running = false;
1214
+ enqueue(execute) {
1215
+ let resolve;
1216
+ let reject;
1217
+ const promise = new Promise((res, rej) => {
1218
+ resolve = res;
1219
+ reject = rej;
1220
+ });
1221
+ const job = {
1222
+ execute,
1223
+ resolve,
1224
+ reject,
1225
+ cancelled: false
1226
+ };
1227
+ this.queue.push(job);
1228
+ this.flush();
1229
+ return {
1230
+ promise,
1231
+ cancel: () => {
1232
+ job.cancelled = true;
1233
+ job.resolve();
1234
+ }
1235
+ };
1236
+ }
1237
+ async flush() {
1238
+ if (this.running) return;
1239
+ this.running = true;
1240
+ while (this.queue.length > 0) {
1241
+ const job = this.queue.shift();
1242
+ if (job.cancelled) {
1243
+ job.resolve();
1244
+ continue;
1245
+ }
1246
+ try {
1247
+ await job.execute();
1248
+ job.resolve();
1249
+ } catch (error) {
1250
+ job.reject(error);
1251
+ }
1252
+ }
1253
+ this.running = false;
1254
+ }
1076
1255
  };
1256
+ var renderQueue = new RenderQueue();
1077
1257
  var useDpr = () => {
1078
1258
  const [dpr, setDPR] = useState(
1079
1259
  typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
@@ -1097,83 +1277,206 @@ var useDpr = () => {
1097
1277
  };
1098
1278
 
1099
1279
  // src/hooks/layers/useCanvasLayer.tsx
1100
- var MAX_CANVAS_PIXELS = 16777216;
1101
- var MAX_CANVAS_DIMENSION = 32767;
1102
1280
  var useCanvasLayer = ({ background }) => {
1103
1281
  const canvasRef = useRef(null);
1104
1282
  const pageNumber = usePDFPageNumber();
1105
1283
  const dpr = useDpr();
1106
1284
  const bouncyZoom = usePdf((state) => state.zoom);
1107
1285
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
1108
- const [zoom] = useDebounce(bouncyZoom, 100);
1109
- const clampScaleForPage = useCallback(
1110
- (targetScale, pageWidth, pageHeight) => {
1111
- if (!targetScale) {
1112
- return 0;
1113
- }
1114
- const areaLimit = Math.sqrt(
1115
- MAX_CANVAS_PIXELS / Math.max(pageWidth * pageHeight, 1)
1116
- );
1117
- const widthLimit = MAX_CANVAS_DIMENSION / Math.max(pageWidth, 1);
1118
- const heightLimit = MAX_CANVAS_DIMENSION / Math.max(pageHeight, 1);
1119
- const safeScale = Math.min(
1120
- targetScale,
1121
- Number.isFinite(areaLimit) ? areaLimit : targetScale,
1122
- Number.isFinite(widthLimit) ? widthLimit : targetScale,
1123
- Number.isFinite(heightLimit) ? heightLimit : targetScale
1124
- );
1125
- return Math.max(safeScale, 0);
1126
- },
1127
- []
1286
+ const markPageRendered = usePdf((state) => state.markPageRendered);
1287
+ const unmarkPageRendered = usePdf((state) => state.unmarkPageRendered);
1288
+ const documentId = usePdf(
1289
+ (state) => state.pdfDocumentProxy.fingerprints[0] ?? "default"
1128
1290
  );
1291
+ const [zoom] = useDebounce(bouncyZoom, 100);
1292
+ const lastRenderedScaleRef = useRef(0);
1293
+ const lastBackgroundRef = useRef(background);
1294
+ const lastPageProxyRef = useRef(pdfPageProxy);
1129
1295
  useLayoutEffect(() => {
1130
1296
  if (!canvasRef.current) {
1131
1297
  return;
1132
1298
  }
1133
- const baseCanvas = canvasRef.current;
1299
+ if (lastPageProxyRef.current !== pdfPageProxy || lastBackgroundRef.current !== background) {
1300
+ lastPageProxyRef.current = pdfPageProxy;
1301
+ lastBackgroundRef.current = background;
1302
+ lastRenderedScaleRef.current = 0;
1303
+ }
1134
1304
  const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1135
1305
  const pageWidth = baseViewport.width;
1136
1306
  const pageHeight = baseViewport.height;
1307
+ const bgColor = background ?? "white";
1137
1308
  const targetBaseScale = dpr * Math.min(zoom, 1);
1138
1309
  const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
1139
- baseCanvas.width = Math.floor(pageWidth * baseScale);
1140
- baseCanvas.height = Math.floor(pageHeight * baseScale);
1141
- baseCanvas.style.position = "absolute";
1142
- baseCanvas.style.top = "0";
1143
- baseCanvas.style.left = "0";
1144
- baseCanvas.style.width = `${pageWidth}px`;
1145
- baseCanvas.style.height = `${pageHeight}px`;
1146
- baseCanvas.style.transform = "translate(0px, 0px)";
1147
- baseCanvas.style.zIndex = "0";
1148
- baseCanvas.style.pointerEvents = "none";
1149
- const context = baseCanvas.getContext("2d");
1150
- if (!context) {
1310
+ if (baseScale === lastRenderedScaleRef.current) {
1151
1311
  return;
1152
1312
  }
1153
- context.setTransform(1, 0, 0, 1, 0, 0);
1154
- context.clearRect(0, 0, baseCanvas.width, baseCanvas.height);
1155
- const viewport = pdfPageProxy.getViewport({ scale: baseScale });
1156
- const renderingTask = pdfPageProxy.render({
1157
- canvasContext: context,
1158
- viewport,
1159
- background
1160
- });
1161
- renderingTask.promise.catch((error) => {
1162
- if (error.name === "RenderingCancelledException") {
1163
- return;
1313
+ const w = Math.floor(pageWidth * baseScale);
1314
+ const h = Math.floor(pageHeight * baseScale);
1315
+ const cssText = `position:absolute;top:0;left:0;width:${pageWidth}px;height:${pageHeight}px;transform:translate(0px,0px);z-index:0;pointer-events:none;background-color:${bgColor}`;
1316
+ const swapToCanvas = (source, sw, sh) => {
1317
+ const canvas = canvasRef.current;
1318
+ if (!canvas) return;
1319
+ canvas.width = sw;
1320
+ canvas.height = sh;
1321
+ canvas.style.cssText = cssText;
1322
+ const ctx = canvas.getContext("2d");
1323
+ if (ctx) {
1324
+ ctx.drawImage(source, 0, 0);
1164
1325
  }
1165
- throw error;
1326
+ lastRenderedScaleRef.current = baseScale;
1327
+ };
1328
+ const cached = renderCache.get(
1329
+ documentId,
1330
+ pageNumber,
1331
+ baseScale,
1332
+ background
1333
+ );
1334
+ if (cached) {
1335
+ swapToCanvas(cached.bitmap, cached.width, cached.height);
1336
+ markPageRendered(pageNumber);
1337
+ return;
1338
+ }
1339
+ let cancelled = false;
1340
+ let activeRenderingTask = null;
1341
+ const job = renderQueue.enqueue(() => {
1342
+ return new Promise((resolve, reject) => {
1343
+ if (cancelled || !canvasRef.current) {
1344
+ resolve();
1345
+ return;
1346
+ }
1347
+ const buffer = document.createElement("canvas");
1348
+ buffer.width = w;
1349
+ buffer.height = h;
1350
+ const bufferCtx = buffer.getContext("2d");
1351
+ if (!bufferCtx) {
1352
+ resolve();
1353
+ return;
1354
+ }
1355
+ const viewport = pdfPageProxy.getViewport({ scale: baseScale });
1356
+ const renderingTask = pdfPageProxy.render({
1357
+ canvas: buffer,
1358
+ canvasContext: bufferCtx,
1359
+ viewport,
1360
+ background
1361
+ });
1362
+ activeRenderingTask = renderingTask;
1363
+ renderingTask.promise.then(() => {
1364
+ activeRenderingTask = null;
1365
+ if (cancelled || !canvasRef.current) {
1366
+ resolve();
1367
+ return;
1368
+ }
1369
+ swapToCanvas(buffer, w, h);
1370
+ markPageRendered(pageNumber);
1371
+ renderCache.set(documentId, pageNumber, baseScale, buffer, background).finally(() => {
1372
+ buffer.width = 0;
1373
+ buffer.height = 0;
1374
+ });
1375
+ resolve();
1376
+ }).catch((error) => {
1377
+ activeRenderingTask = null;
1378
+ if (error.name === "RenderingCancelledException") {
1379
+ resolve();
1380
+ return;
1381
+ }
1382
+ reject(error);
1383
+ });
1384
+ });
1166
1385
  });
1167
1386
  return () => {
1168
- void renderingTask.cancel();
1387
+ cancelled = true;
1388
+ job.cancel();
1389
+ void activeRenderingTask?.cancel();
1169
1390
  };
1170
- }, [pdfPageProxy, background, dpr, zoom, clampScaleForPage]);
1391
+ }, [
1392
+ pdfPageProxy,
1393
+ background,
1394
+ dpr,
1395
+ zoom,
1396
+ pageNumber,
1397
+ documentId,
1398
+ markPageRendered
1399
+ ]);
1400
+ useEffect(() => {
1401
+ const canvas = canvasRef.current;
1402
+ return () => {
1403
+ unmarkPageRendered(pageNumber);
1404
+ if (canvas) {
1405
+ canvas.width = 1;
1406
+ canvas.height = 1;
1407
+ }
1408
+ };
1409
+ }, [pageNumber, unmarkPageRendered]);
1171
1410
  return {
1172
1411
  canvasRef
1173
1412
  };
1174
1413
  };
1175
- var MAX_CANVAS_PIXELS2 = 16777216;
1176
- var MAX_CANVAS_DIMENSION2 = 32767;
1414
+
1415
+ // src/lib/viewport-invalidation.ts
1416
+ var viewportInvalidationRegistry = /* @__PURE__ */ new WeakMap();
1417
+ var notifyCallbacks = (entry) => {
1418
+ entry.rafId = null;
1419
+ entry.callbacks.forEach((callback) => {
1420
+ callback();
1421
+ });
1422
+ };
1423
+ var scheduleInvalidation = (entry) => {
1424
+ if (entry.rafId !== null) {
1425
+ return;
1426
+ }
1427
+ entry.rafId = requestAnimationFrame(() => {
1428
+ notifyCallbacks(entry);
1429
+ });
1430
+ };
1431
+ var subscribeToViewportInvalidation = (viewport, callback) => {
1432
+ let entry = viewportInvalidationRegistry.get(viewport);
1433
+ if (!entry) {
1434
+ const callbacks = /* @__PURE__ */ new Set();
1435
+ const schedule = () => {
1436
+ const currentEntry = viewportInvalidationRegistry.get(viewport);
1437
+ if (!currentEntry) {
1438
+ return;
1439
+ }
1440
+ scheduleInvalidation(currentEntry);
1441
+ };
1442
+ entry = {
1443
+ callbacks,
1444
+ scrollListener: schedule,
1445
+ windowResizeListener: schedule,
1446
+ resizeObserver: typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule),
1447
+ rafId: null
1448
+ };
1449
+ viewport.addEventListener("scroll", entry.scrollListener, {
1450
+ passive: true
1451
+ });
1452
+ window.addEventListener("resize", entry.windowResizeListener, {
1453
+ passive: true
1454
+ });
1455
+ entry.resizeObserver?.observe(viewport);
1456
+ viewportInvalidationRegistry.set(viewport, entry);
1457
+ }
1458
+ entry.callbacks.add(callback);
1459
+ return () => {
1460
+ const currentEntry = viewportInvalidationRegistry.get(viewport);
1461
+ if (!currentEntry) {
1462
+ return;
1463
+ }
1464
+ currentEntry.callbacks.delete(callback);
1465
+ if (currentEntry.callbacks.size > 0) {
1466
+ return;
1467
+ }
1468
+ if (currentEntry.rafId !== null) {
1469
+ cancelAnimationFrame(currentEntry.rafId);
1470
+ }
1471
+ viewport.removeEventListener("scroll", currentEntry.scrollListener);
1472
+ window.removeEventListener("resize", currentEntry.windowResizeListener);
1473
+ currentEntry.resizeObserver?.disconnect();
1474
+ viewportInvalidationRegistry.delete(viewport);
1475
+ };
1476
+ };
1477
+
1478
+ // src/hooks/layers/useDetailCanvasLayer.tsx
1479
+ var DETAIL_RENDER_IDLE_MS = 80;
1177
1480
  var useDetailCanvasLayer = ({
1178
1481
  background,
1179
1482
  baseCanvasRef
@@ -1183,196 +1486,200 @@ var useDetailCanvasLayer = ({
1183
1486
  const pageNumber = usePDFPageNumber();
1184
1487
  const dpr = useDpr();
1185
1488
  const bouncyZoom = usePdf((state) => state.zoom);
1489
+ const isPinching = usePdf((state) => state.isPinching);
1186
1490
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
1187
1491
  const viewportRef = usePdf((state) => state.viewportRef);
1188
1492
  const [zoom] = useDebounce(bouncyZoom, 200);
1189
- const [scrollTick, setScrollTick] = useState(0);
1190
- const [debouncedScrollTick] = useDebounce(scrollTick, 20);
1191
- const ensureDetailCanvas = useCallback(() => {
1192
- let detailCanvas = detailCanvasRef.current;
1193
- if (!detailCanvas) {
1194
- const parent = baseCanvasRef.current?.parentElement;
1195
- if (!parent) {
1196
- return null;
1197
- }
1198
- detailCanvas = document.createElement("canvas");
1199
- parent.appendChild(detailCanvas);
1200
- detailCanvasRef.current = detailCanvas;
1201
- }
1202
- detailCanvas.style.position = "absolute";
1203
- detailCanvas.style.top = "0";
1204
- detailCanvas.style.left = "0";
1205
- detailCanvas.style.pointerEvents = "none";
1206
- detailCanvas.style.zIndex = "0";
1207
- return detailCanvas;
1208
- }, [baseCanvasRef]);
1209
- const clampScaleForPage = useCallback(
1210
- (targetScale, pageWidth, pageHeight) => {
1211
- if (!targetScale) {
1212
- return 0;
1213
- }
1214
- const areaLimit = Math.sqrt(
1215
- MAX_CANVAS_PIXELS2 / Math.max(pageWidth * pageHeight, 1)
1216
- );
1217
- const widthLimit = MAX_CANVAS_DIMENSION2 / Math.max(pageWidth, 1);
1218
- const heightLimit = MAX_CANVAS_DIMENSION2 / Math.max(pageHeight, 1);
1219
- const safeScale = Math.min(
1220
- targetScale,
1221
- Number.isFinite(areaLimit) ? areaLimit : targetScale,
1222
- Number.isFinite(widthLimit) ? widthLimit : targetScale,
1223
- Number.isFinite(heightLimit) ? heightLimit : targetScale
1224
- );
1225
- return Math.max(safeScale, 0);
1226
- },
1227
- []
1228
- );
1229
1493
  useLayoutEffect(() => {
1230
1494
  const scrollContainer = viewportRef?.current;
1231
- if (!scrollContainer) return;
1232
- const handleScroll = () => {
1233
- setScrollTick((prev) => prev + 1);
1234
- };
1235
- scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
1236
- return () => {
1237
- scrollContainer.removeEventListener("scroll", handleScroll);
1238
- };
1239
- }, [viewportRef?.current]);
1240
- useLayoutEffect(() => {
1241
- if (!viewportRef?.current) {
1495
+ if (!scrollContainer) {
1242
1496
  return;
1243
1497
  }
1244
- const detailCanvas = ensureDetailCanvas();
1498
+ const detailCanvas = detailCanvasRef.current;
1245
1499
  const container = containerRef.current;
1246
1500
  if (!detailCanvas || !container) {
1247
1501
  return;
1248
1502
  }
1249
- const scrollContainer = viewportRef.current;
1250
- const pageContainer = baseCanvasRef.current?.parentElement ?? null;
1251
- if (!pageContainer) {
1252
- detailCanvas.style.display = "none";
1253
- detailCanvas.width = 0;
1254
- detailCanvas.height = 0;
1255
- return;
1256
- }
1257
- const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1258
- const pageWidth = baseViewport.width;
1259
- const pageHeight = baseViewport.height;
1260
- const scrollX = scrollContainer.scrollLeft / zoom;
1261
- const scrollY = scrollContainer.scrollTop / zoom;
1262
- const viewportWidth = scrollContainer.clientWidth / zoom;
1263
- const viewportHeight = scrollContainer.clientHeight / zoom;
1264
- const pageRect = pageContainer.getBoundingClientRect();
1265
- const containerRect = scrollContainer.getBoundingClientRect();
1266
- const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
1267
- const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
1268
- const visibleLeft = Math.max(0, scrollX - pageLeft);
1269
- const visibleTop = Math.max(0, scrollY - pageTop);
1270
- const visibleRight = Math.min(
1271
- pageWidth,
1272
- scrollX + viewportWidth - pageLeft
1273
- );
1274
- const visibleBottom = Math.min(
1275
- pageHeight,
1276
- scrollY + viewportHeight - pageTop
1277
- );
1278
- const visibleWidth = Math.max(0, visibleRight - visibleLeft);
1279
- const visibleHeight = Math.max(0, visibleBottom - visibleTop);
1280
- const targetDetailScale = dpr * zoom * 1.3;
1281
- const baseTargetScale = dpr * Math.min(zoom, 1);
1282
- const baseScale = clampScaleForPage(baseTargetScale, pageWidth, pageHeight);
1283
- const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
1284
- if (!needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
1285
- detailCanvas.style.display = "none";
1286
- detailCanvas.width = 0;
1287
- detailCanvas.height = 0;
1288
- return;
1289
- }
1290
- detailCanvas.style.display = "block";
1291
- const pdfOffsetX = visibleLeft;
1292
- const pdfOffsetY = visibleTop;
1293
- const pdfWidth = visibleWidth * targetDetailScale;
1294
- const pdfHeight = visibleHeight * targetDetailScale;
1295
- const effectiveScale = targetDetailScale;
1296
- const actualWidth = pdfWidth;
1297
- const actualHeight = pdfHeight;
1298
- detailCanvas.width = actualWidth;
1299
- detailCanvas.height = actualHeight;
1300
- const scaledWidth = visibleWidth * zoom;
1301
- const scaledHeight = visibleHeight * zoom;
1302
- detailCanvas.style.width = `${scaledWidth}px`;
1303
- detailCanvas.style.height = `${scaledHeight}px`;
1304
- detailCanvas.style.transformOrigin = "center center";
1305
- detailCanvas.style.transform = `translate(${visibleLeft * zoom}px, ${visibleTop * zoom}px) `;
1306
- container.style.transform = `scale3d(${1 / zoom}, ${1 / zoom}, 1)`;
1307
- container.style.transformOrigin = `0 0`;
1308
- const context = detailCanvas.getContext("2d");
1309
- if (!context) {
1310
- return;
1311
- }
1312
- context.setTransform(1, 0, 0, 1, 0, 0);
1313
- context.clearRect(0, 0, detailCanvas.width, detailCanvas.height);
1314
- const transform = [
1315
- 1,
1316
- 0,
1317
- 0,
1318
- 1,
1319
- -pdfOffsetX * effectiveScale,
1320
- -pdfOffsetY * effectiveScale
1321
- ];
1322
- const detailViewport = pdfPageProxy.getViewport({ scale: effectiveScale });
1323
- const renderingTask = pdfPageProxy.render({
1324
- canvasContext: context,
1325
- viewport: detailViewport,
1326
- background,
1327
- transform
1328
- });
1329
- renderingTask.promise.catch((error) => {
1330
- if (error.name === "RenderingCancelledException") {
1503
+ let renderingTask = null;
1504
+ let renderTimeoutId = null;
1505
+ const bgColor = background ?? "white";
1506
+ const detailBaseStyle = `position:absolute;top:0;left:0;pointer-events:none;z-index:1;background-color:${bgColor}`;
1507
+ const hideDetailCanvas = () => {
1508
+ renderingTask?.cancel();
1509
+ detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:0`;
1510
+ container.style.cssText = "";
1511
+ };
1512
+ const renderDetailCanvas = () => {
1513
+ const pageContainer = baseCanvasRef.current?.parentElement ?? null;
1514
+ if (!pageContainer) {
1515
+ hideDetailCanvas();
1331
1516
  return;
1332
1517
  }
1333
- throw error;
1334
- });
1518
+ const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1519
+ const pageWidth = baseViewport.width;
1520
+ const pageHeight = baseViewport.height;
1521
+ const scrollX = scrollContainer.scrollLeft / zoom;
1522
+ const scrollY = scrollContainer.scrollTop / zoom;
1523
+ const viewportWidth = scrollContainer.clientWidth / zoom;
1524
+ const viewportHeight = scrollContainer.clientHeight / zoom;
1525
+ const pageRect = pageContainer.getBoundingClientRect();
1526
+ const containerRect = scrollContainer.getBoundingClientRect();
1527
+ const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
1528
+ const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
1529
+ const visibleLeft = Math.max(0, scrollX - pageLeft);
1530
+ const visibleTop = Math.max(0, scrollY - pageTop);
1531
+ const visibleRight = Math.min(
1532
+ pageWidth,
1533
+ scrollX + viewportWidth - pageLeft
1534
+ );
1535
+ const visibleBottom = Math.min(
1536
+ pageHeight,
1537
+ scrollY + viewportHeight - pageTop
1538
+ );
1539
+ const visibleWidth = Math.max(0, visibleRight - visibleLeft);
1540
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
1541
+ const targetDetailScale = dpr * zoom * 1.3;
1542
+ const baseTargetScale = dpr * Math.min(zoom, 1);
1543
+ const baseScale = clampScaleForPage(
1544
+ baseTargetScale,
1545
+ pageWidth,
1546
+ pageHeight
1547
+ );
1548
+ const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
1549
+ if (isPinching || !needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
1550
+ hideDetailCanvas();
1551
+ return;
1552
+ }
1553
+ renderingTask?.cancel();
1554
+ const pdfOffsetX = visibleLeft;
1555
+ const pdfOffsetY = visibleTop;
1556
+ const effectiveScale = targetDetailScale;
1557
+ const actualWidth = visibleWidth * effectiveScale;
1558
+ const actualHeight = visibleHeight * effectiveScale;
1559
+ const buffer = document.createElement("canvas");
1560
+ buffer.width = actualWidth;
1561
+ buffer.height = actualHeight;
1562
+ const bufferCtx = buffer.getContext("2d");
1563
+ if (!bufferCtx) {
1564
+ return;
1565
+ }
1566
+ bufferCtx.setTransform(1, 0, 0, 1, 0, 0);
1567
+ const transform = [
1568
+ 1,
1569
+ 0,
1570
+ 0,
1571
+ 1,
1572
+ -pdfOffsetX * effectiveScale,
1573
+ -pdfOffsetY * effectiveScale
1574
+ ];
1575
+ const detailViewport = pdfPageProxy.getViewport({
1576
+ scale: effectiveScale
1577
+ });
1578
+ const currentRenderingTask = pdfPageProxy.render({
1579
+ canvas: buffer,
1580
+ canvasContext: bufferCtx,
1581
+ viewport: detailViewport,
1582
+ background,
1583
+ transform
1584
+ });
1585
+ renderingTask = currentRenderingTask;
1586
+ currentRenderingTask.promise.then(() => {
1587
+ if (renderingTask !== currentRenderingTask) return;
1588
+ detailCanvas.width = actualWidth;
1589
+ detailCanvas.height = actualHeight;
1590
+ detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:1;width:${visibleWidth * zoom}px;height:${visibleHeight * zoom}px;transform-origin:center center;transform:translate(${visibleLeft * zoom}px,${visibleTop * zoom}px)`;
1591
+ container.style.cssText = `transform:scale3d(${1 / zoom},${1 / zoom},1);transform-origin:0 0`;
1592
+ const ctx = detailCanvas.getContext("2d");
1593
+ if (ctx) {
1594
+ ctx.drawImage(buffer, 0, 0);
1595
+ }
1596
+ }).catch((error) => {
1597
+ if (error.name === "RenderingCancelledException") {
1598
+ return;
1599
+ }
1600
+ throw error;
1601
+ }).finally(() => {
1602
+ if (renderingTask === currentRenderingTask) {
1603
+ renderingTask = null;
1604
+ }
1605
+ buffer.width = 0;
1606
+ buffer.height = 0;
1607
+ });
1608
+ };
1609
+ const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
1610
+ if (renderTimeoutId !== null) {
1611
+ clearTimeout(renderTimeoutId);
1612
+ }
1613
+ renderingTask?.cancel();
1614
+ renderTimeoutId = setTimeout(() => {
1615
+ renderTimeoutId = null;
1616
+ renderDetailCanvas();
1617
+ }, delay);
1618
+ };
1619
+ const unsubscribe = subscribeToViewportInvalidation(
1620
+ scrollContainer,
1621
+ scheduleRender
1622
+ );
1623
+ if (zoom <= 1 || isPinching) {
1624
+ hideDetailCanvas();
1625
+ } else {
1626
+ scheduleRender(0);
1627
+ }
1335
1628
  return () => {
1336
- void renderingTask.cancel();
1629
+ unsubscribe();
1630
+ if (renderTimeoutId !== null) {
1631
+ clearTimeout(renderTimeoutId);
1632
+ }
1633
+ void renderingTask?.cancel();
1337
1634
  };
1338
1635
  }, [
1339
1636
  pdfPageProxy,
1340
1637
  zoom,
1638
+ isPinching,
1341
1639
  background,
1342
1640
  dpr,
1343
1641
  viewportRef,
1344
- ensureDetailCanvas,
1345
- clampScaleForPage,
1346
- baseCanvasRef,
1347
- debouncedScrollTick
1642
+ baseCanvasRef
1348
1643
  ]);
1644
+ useEffect(() => {
1645
+ const canvas = detailCanvasRef.current;
1646
+ return () => {
1647
+ if (canvas) {
1648
+ canvas.width = 1;
1649
+ canvas.height = 1;
1650
+ }
1651
+ };
1652
+ }, []);
1349
1653
  return {
1350
1654
  detailCanvasRef,
1351
1655
  containerRef
1352
1656
  };
1353
1657
  };
1354
- var CanvasLayer = ({
1355
- style,
1356
- background,
1357
- ...props
1358
- }) => {
1359
- const { canvasRef } = useCanvasLayer({ background });
1360
- const { detailCanvasRef, containerRef } = useDetailCanvasLayer({
1658
+ var CanvasLayer = memo(
1659
+ ({
1660
+ style,
1361
1661
  background,
1362
- baseCanvasRef: canvasRef
1363
- });
1364
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1365
- /* @__PURE__ */ jsx("canvas", { ...props, ref: canvasRef, style }),
1366
- /* @__PURE__ */ jsx(
1367
- "div",
1368
- {
1369
- ref: containerRef,
1370
- className: "absolute top-0 left-0 w-full h-full flex items-center justify-center",
1371
- children: /* @__PURE__ */ jsx("canvas", { ref: detailCanvasRef })
1372
- }
1373
- )
1374
- ] });
1375
- };
1662
+ ...props
1663
+ }) => {
1664
+ const { canvasRef } = useCanvasLayer({ background });
1665
+ const { detailCanvasRef, containerRef } = useDetailCanvasLayer({
1666
+ background,
1667
+ baseCanvasRef: canvasRef
1668
+ });
1669
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1670
+ /* @__PURE__ */ jsx("canvas", { ...props, ref: canvasRef, style }),
1671
+ /* @__PURE__ */ jsx(
1672
+ "div",
1673
+ {
1674
+ ref: containerRef,
1675
+ className: "absolute top-0 left-0 w-full h-full flex items-center justify-center",
1676
+ children: /* @__PURE__ */ jsx("canvas", { ref: detailCanvasRef })
1677
+ }
1678
+ )
1679
+ ] });
1680
+ }
1681
+ );
1682
+ CanvasLayer.displayName = "CanvasLayer";
1376
1683
 
1377
1684
  // src/hooks/useSelectionDimensions.tsx
1378
1685
  var MERGE_THRESHOLD = 2;
@@ -2082,6 +2389,10 @@ var ColoredHighlightLayer = ({
2082
2389
  (state) => state.coloredHighlights
2083
2390
  );
2084
2391
  const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
2392
+ const pageHighlights = useMemo(
2393
+ () => highlights.filter((s) => s.pageNumber === pageNumber),
2394
+ [highlights, pageNumber]
2395
+ );
2085
2396
  const handleHighlighting = useCallback(
2086
2397
  (color) => {
2087
2398
  const dimension = getDimension();
@@ -2103,13 +2414,7 @@ var ColoredHighlightLayer = ({
2103
2414
  [onHighlight, getDimension, addColoredHighlight]
2104
2415
  );
2105
2416
  return /* @__PURE__ */ jsxs("div", { className: "colored-highlights-layer", children: [
2106
- highlights.filter((selection) => selection.pageNumber === pageNumber).map((selection) => /* @__PURE__ */ jsx(
2107
- ColoredHighlightComponent,
2108
- {
2109
- selection
2110
- },
2111
- selection.uuid
2112
- )),
2417
+ pageHighlights.map((selection) => /* @__PURE__ */ jsx(ColoredHighlightComponent, { selection }, selection.uuid)),
2113
2418
  /* @__PURE__ */ jsx(
2114
2419
  ColorSelectionTool,
2115
2420
  {
@@ -2118,14 +2423,13 @@ var ColoredHighlightLayer = ({
2118
2423
  )
2119
2424
  ] });
2120
2425
  };
2121
-
2122
- // src/components/layers/custom-layer.tsx
2123
- var CustomLayer = ({
2124
- children
2125
- }) => {
2126
- const pageNumber = usePDFPageNumber();
2127
- return children(pageNumber);
2128
- };
2426
+ var CustomLayer = memo(
2427
+ ({ children }) => {
2428
+ const pageNumber = usePDFPageNumber();
2429
+ return children(pageNumber);
2430
+ }
2431
+ );
2432
+ CustomLayer.displayName = "CustomLayer";
2129
2433
  var convertToPercentString = (rect) => {
2130
2434
  return {
2131
2435
  top: `${rect.top}%`,
@@ -2138,7 +2442,10 @@ var HighlightLayer = forwardRef(({ asChild, className, style, ...props }, ref) =
2138
2442
  const pageNumber = usePDFPageNumber();
2139
2443
  const highlights = usePdf((state) => state.highlights);
2140
2444
  const Comp = asChild ? Slot : "div";
2141
- const rects = highlights.filter((area) => area.pageNumber === pageNumber);
2445
+ const rects = useMemo(
2446
+ () => highlights.filter((area) => area.pageNumber === pageNumber),
2447
+ [highlights, pageNumber]
2448
+ );
2142
2449
  if (!rects?.length) return null;
2143
2450
  return /* @__PURE__ */ jsx(Fragment, { children: rects.map((rect, index) => {
2144
2451
  const { pageNumber: pageNumber2, type, style: customStyle, ...coordinates } = rect;
@@ -2319,25 +2626,33 @@ var useTextLayer = () => {
2319
2626
  if (!textContainer || isRenderingRef.current) {
2320
2627
  return;
2321
2628
  }
2629
+ let isCancelled = false;
2322
2630
  isRenderingRef.current = true;
2323
2631
  textContainer.innerHTML = "";
2324
2632
  if (textLayerRef.current) {
2325
2633
  textLayerRef.current.cancel();
2326
2634
  textLayerRef.current = null;
2327
2635
  }
2328
- const textLayer = new TextLayer({
2329
- textContentSource: pdfPageProxy.streamTextContent(),
2330
- container: textContainer,
2331
- viewport: pdfPageProxy.getViewport({ scale: 1 })
2332
- });
2333
- textLayerRef.current = textLayer;
2334
- textLayer.render().then(() => {
2335
- if (textLayerRef.current === textLayer && textContainer) {
2336
- const endOfContent = document.createElement("div");
2337
- endOfContent.className = "endOfContent";
2338
- textContainer.appendChild(endOfContent);
2339
- bindMouseEvents(textContainer, endOfContent);
2636
+ void loadPdfJs().then(({ TextLayer: TextLayer2 }) => {
2637
+ if (isCancelled || textContainerRef.current !== textContainer) {
2638
+ return;
2340
2639
  }
2640
+ const textLayer = new TextLayer2({
2641
+ textContentSource: pdfPageProxy.streamTextContent(),
2642
+ container: textContainer,
2643
+ viewport: pdfPageProxy.getViewport({ scale: 1 })
2644
+ });
2645
+ textLayerRef.current = textLayer;
2646
+ return textLayer.render();
2647
+ }).then(() => {
2648
+ const textLayer = textLayerRef.current;
2649
+ if (!textLayer || isCancelled || textContainerRef.current !== textContainer) {
2650
+ return;
2651
+ }
2652
+ const endOfContent = document.createElement("div");
2653
+ endOfContent.className = "endOfContent";
2654
+ textContainer.appendChild(endOfContent);
2655
+ bindMouseEvents(textContainer, endOfContent);
2341
2656
  }).catch((error) => {
2342
2657
  if (error.name !== "AbortException") {
2343
2658
  console.error("TextLayer rendering error:", error);
@@ -2346,6 +2661,7 @@ var useTextLayer = () => {
2346
2661
  isRenderingRef.current = false;
2347
2662
  });
2348
2663
  return () => {
2664
+ isCancelled = true;
2349
2665
  isRenderingRef.current = false;
2350
2666
  if (textLayerRef.current) {
2351
2667
  textLayerRef.current.cancel();
@@ -2362,30 +2678,29 @@ var useTextLayer = () => {
2362
2678
  pageNumber: pdfPageProxy.pageNumber
2363
2679
  };
2364
2680
  };
2365
- var TextLayer2 = ({
2366
- className,
2367
- style,
2368
- ...props
2369
- }) => {
2370
- const { textContainerRef, pageNumber } = useTextLayer();
2371
- return /* @__PURE__ */ jsx(
2372
- "div",
2373
- {
2374
- className: clsx_default("textLayer", className),
2375
- style: {
2376
- ...style,
2377
- position: "absolute",
2378
- top: 0,
2379
- left: 0
2380
- },
2381
- ...props,
2382
- ...{
2383
- "data-page-number": pageNumber
2384
- },
2385
- ref: textContainerRef
2386
- }
2387
- );
2388
- };
2681
+ var TextLayer = memo(
2682
+ ({ className, style, ...props }) => {
2683
+ const { textContainerRef, pageNumber } = useTextLayer();
2684
+ return /* @__PURE__ */ jsx(
2685
+ "div",
2686
+ {
2687
+ className: clsx_default("textLayer", className),
2688
+ style: {
2689
+ ...style,
2690
+ position: "absolute",
2691
+ top: 0,
2692
+ left: 0
2693
+ },
2694
+ ...props,
2695
+ ...{
2696
+ "data-page-number": pageNumber
2697
+ },
2698
+ ref: textContainerRef
2699
+ }
2700
+ );
2701
+ }
2702
+ );
2703
+ TextLayer.displayName = "TextLayer";
2389
2704
  var usePDFOutline = () => {
2390
2705
  const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
2391
2706
  const [outline, setOutline] = useState();
@@ -2523,40 +2838,44 @@ var Outline = ({
2523
2838
  });
2524
2839
  }) });
2525
2840
  };
2526
- var Page = ({
2527
- children,
2528
- pageNumber = 1,
2529
- style,
2530
- ...props
2531
- }) => {
2532
- const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
2533
- const viewport = usePdf((state) => state.viewports[pageNumber - 1]);
2534
- const width = viewport?.width ?? (pdfPageProxy.view[2] ?? 0) - (pdfPageProxy.view[0] ?? 0);
2535
- const height = viewport?.height ?? (pdfPageProxy.view[3] ?? 0) - (pdfPageProxy.view[1] ?? 0);
2536
- return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pdfPageProxy.pageNumber, children: /* @__PURE__ */ jsx(
2537
- Primitive.div,
2538
- {
2539
- style: {
2540
- display: "block"
2541
- },
2542
- children: /* @__PURE__ */ jsx(
2543
- "div",
2544
- {
2545
- style: {
2546
- ...style,
2547
- "--scale-factor": 1,
2548
- "--total-scale-factor": 1,
2549
- position: "relative",
2550
- width,
2551
- height
2552
- },
2553
- ...props,
2554
- children
2555
- }
2556
- )
2557
- }
2558
- ) });
2559
- };
2841
+ var Page = memo(
2842
+ ({
2843
+ children,
2844
+ pageNumber = 1,
2845
+ style,
2846
+ ...props
2847
+ }) => {
2848
+ const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
2849
+ const viewport = usePdf((state) => state.viewports[pageNumber - 1]);
2850
+ const width = viewport?.width ?? (pdfPageProxy.view[2] ?? 0) - (pdfPageProxy.view[0] ?? 0);
2851
+ const height = viewport?.height ?? (pdfPageProxy.view[3] ?? 0) - (pdfPageProxy.view[1] ?? 0);
2852
+ return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pdfPageProxy.pageNumber, children: /* @__PURE__ */ jsx(
2853
+ Primitive.div,
2854
+ {
2855
+ style: {
2856
+ display: "block"
2857
+ },
2858
+ children: /* @__PURE__ */ jsx(
2859
+ "div",
2860
+ {
2861
+ style: {
2862
+ "--scale-factor": 1,
2863
+ "--total-scale-factor": 1,
2864
+ backgroundColor: "white",
2865
+ position: "relative",
2866
+ width,
2867
+ height,
2868
+ ...style
2869
+ },
2870
+ ...props,
2871
+ children
2872
+ }
2873
+ )
2874
+ }
2875
+ ) });
2876
+ }
2877
+ );
2878
+ Page.displayName = "Page";
2560
2879
  var NextPage = () => {
2561
2880
  };
2562
2881
  var PreviousPage = () => {
@@ -2701,6 +3020,7 @@ var useVisiblePage = ({ items }) => {
2701
3020
  const isPinching = usePdf((state) => state.isPinching);
2702
3021
  const setCurrentPage = usePdf((state) => state.setCurrentPage);
2703
3022
  const scrollElement = usePdf((state) => state.viewportRef?.current);
3023
+ const lastPageRef = useRef(0);
2704
3024
  const calculateVisiblePageIndex = useCallback(
2705
3025
  (virtualItems) => {
2706
3026
  if (!scrollElement || virtualItems.length === 0) return 0;
@@ -2724,7 +3044,11 @@ var useVisiblePage = ({ items }) => {
2724
3044
  useEffect(() => {
2725
3045
  if (!isPinching && items.length > 0) {
2726
3046
  const mostVisibleIndex = calculateVisiblePageIndex(items);
2727
- setCurrentPage?.(mostVisibleIndex + 1);
3047
+ const page = mostVisibleIndex + 1;
3048
+ if (page !== lastPageRef.current) {
3049
+ lastPageRef.current = page;
3050
+ setCurrentPage?.(page);
3051
+ }
2728
3052
  }
2729
3053
  }, [items, isPinching, calculateVisiblePageIndex, setCurrentPage]);
2730
3054
  return null;
@@ -3177,10 +3501,10 @@ var Engine = class {
3177
3501
  const config = this.config;
3178
3502
  if (!state._active) this.clean();
3179
3503
  if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return;
3180
- const memo = this.handler(_objectSpread2(_objectSpread2(_objectSpread2({}, shared), state), {}, {
3504
+ const memo7 = this.handler(_objectSpread2(_objectSpread2(_objectSpread2({}, shared), state), {}, {
3181
3505
  [this.aliasKey]: state.values
3182
3506
  }));
3183
- if (memo !== void 0) state.memo = memo;
3507
+ if (memo7 !== void 0) state.memo = memo7;
3184
3508
  }
3185
3509
  clean() {
3186
3510
  this.eventStore.clean();
@@ -4529,11 +4853,11 @@ Please add \`${key}Action\` when creating your handler.`);
4529
4853
  const startKey = handlerKey + "Start";
4530
4854
  const endKey = handlerKey + "End";
4531
4855
  const fn = (state) => {
4532
- let memo = void 0;
4856
+ let memo7 = void 0;
4533
4857
  if (state.first && startKey in handlers) handlers[startKey](state);
4534
- if (handlerKey in handlers) memo = handlers[handlerKey](state);
4858
+ if (handlerKey in handlers) memo7 = handlers[handlerKey](state);
4535
4859
  if (state.last && endKey in handlers) handlers[endKey](state);
4536
- return memo;
4860
+ return memo7;
4537
4861
  };
4538
4862
  internalHandlers[key] = fn;
4539
4863
  config[key] = config[key] || {};
@@ -4585,8 +4909,8 @@ function useGesture(handlers, config) {
4585
4909
  }
4586
4910
 
4587
4911
  // src/lib/memo.ts
4588
- var firstMemo = (first, memo, initializer) => {
4589
- if (!first && !memo) {
4912
+ var firstMemo = (first, memo7, initializer) => {
4913
+ if (!first && !memo7) {
4590
4914
  throw new Error(
4591
4915
  "Missing memo initialization. You likely forgot to return the result of `firstMemo` in the event function"
4592
4916
  );
@@ -4594,7 +4918,7 @@ var firstMemo = (first, memo, initializer) => {
4594
4918
  if (first) {
4595
4919
  return initializer();
4596
4920
  }
4597
- return memo;
4921
+ return memo7;
4598
4922
  };
4599
4923
 
4600
4924
  // src/hooks/viewport/useViewportContainer.tsx
@@ -4726,13 +5050,13 @@ var useViewportContainer = ({
4726
5050
  useGesture(
4727
5051
  {
4728
5052
  onPinch: (state) => {
4729
- const { origin: origin2, first, movement, delta, event, memo } = state;
5053
+ const { origin: origin2, first, movement, delta, event, memo: memo7 } = state;
4730
5054
  const [ms] = movement;
4731
5055
  const [deltaScale] = delta;
4732
5056
  const currentElement = elementRef.current;
4733
5057
  const currentContainer = containerRef.current;
4734
5058
  if (!currentElement || !currentContainer) return;
4735
- const newMemo = firstMemo(first, memo, () => {
5059
+ const newMemo = firstMemo(first, memo7, () => {
4736
5060
  const elementRect = currentElement.getBoundingClientRect();
4737
5061
  const containerRect = currentContainer.getBoundingClientRect();
4738
5062
  const currentZoom = transformations.current.zoom;
@@ -4799,12 +5123,43 @@ var useViewportContainer = ({
4799
5123
  origin
4800
5124
  };
4801
5125
  };
5126
+ var selectLargestPageWidth = (state) => state.viewports.reduce((max, vp) => Math.max(max, vp.width), 0);
4802
5127
  var DEFAULT_HEIGHT = 600;
4803
5128
  var EXTRA_HEIGHT = 0;
5129
+ var DEFAULT_VIRTUALIZER_OPTIONS = { overscan: 1 };
5130
+ var VirtualizedPageItem = memo(
5131
+ ({ child, virtualItem, innerBoxWidth }) => {
5132
+ return /* @__PURE__ */ jsx(
5133
+ "div",
5134
+ {
5135
+ style: {
5136
+ width: innerBoxWidth,
5137
+ height: "0px"
5138
+ },
5139
+ children: /* @__PURE__ */ jsx(
5140
+ "div",
5141
+ {
5142
+ style: {
5143
+ height: `${virtualItem.size}px`,
5144
+ transform: `translateY(${virtualItem.start}px)`
5145
+ },
5146
+ children: cloneElement(child, {
5147
+ key: virtualItem.key,
5148
+ //@ts-expect-error pageNumber is not a valid react key
5149
+ pageNumber: virtualItem.index + 1
5150
+ })
5151
+ }
5152
+ )
5153
+ }
5154
+ );
5155
+ },
5156
+ (prev, next) => prev.child === next.child && prev.innerBoxWidth === next.innerBoxWidth && prev.virtualItem.key === next.virtualItem.key && prev.virtualItem.index === next.virtualItem.index && prev.virtualItem.size === next.virtualItem.size && prev.virtualItem.start === next.virtualItem.start
5157
+ );
5158
+ VirtualizedPageItem.displayName = "VirtualizedPageItem";
4804
5159
  var Pages = ({
4805
5160
  children,
4806
5161
  gap = 10,
4807
- virtualizerOptions = { overscan: 2 },
5162
+ virtualizerOptions = DEFAULT_VIRTUALIZER_OPTIONS,
4808
5163
  initialOffset,
4809
5164
  onOffsetChange,
4810
5165
  ...props
@@ -4824,12 +5179,16 @@ var Pages = ({
4824
5179
  const setVirtualizer = usePdf((state) => state.setVirtualizer);
4825
5180
  const { scrollToFn } = useScrollFn();
4826
5181
  const { observeElementOffset } = useObserveElement();
5182
+ const viewportsRef = useRef(viewports);
5183
+ viewportsRef.current = viewports;
4827
5184
  const estimateSize = useCallback(
4828
5185
  (index) => {
4829
- if (!viewports || !viewports[index]) return DEFAULT_HEIGHT;
4830
- return viewports[index].height + EXTRA_HEIGHT;
5186
+ const vp = viewportsRef.current;
5187
+ if (!vp || !vp[index]) return DEFAULT_HEIGHT;
5188
+ return vp[index].height + EXTRA_HEIGHT;
4831
5189
  },
4832
- [viewports]
5190
+ []
5191
+ // Stable — reads from ref
4833
5192
  );
4834
5193
  const virtualizer = useVirtualizer({
4835
5194
  count: numPages || 0,
@@ -4869,9 +5228,7 @@ var Pages = ({
4869
5228
  items
4870
5229
  });
4871
5230
  useFitWidth({ viewportRef: containerRef });
4872
- const largestPageWidth = usePdf(
4873
- (state) => Math.max(...state.viewports.map((v) => v.width))
4874
- );
5231
+ const largestPageWidth = usePdf(selectLargestPageWidth);
4875
5232
  useEffect(() => {
4876
5233
  virtualizer.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
4877
5234
  const size = virtualizer.getSize();
@@ -4931,26 +5288,11 @@ var Pages = ({
4931
5288
  const innerBoxWidth = viewports?.[virtualItem.index] ? viewports[virtualItem.index]?.width : 0;
4932
5289
  if (!innerBoxWidth) return null;
4933
5290
  return /* @__PURE__ */ jsx(
4934
- "div",
5291
+ VirtualizedPageItem,
4935
5292
  {
4936
- style: {
4937
- width: innerBoxWidth,
4938
- height: `0px`
4939
- },
4940
- children: /* @__PURE__ */ jsx(
4941
- "div",
4942
- {
4943
- style: {
4944
- height: `${virtualItem.size}px`,
4945
- transform: `translateY(${virtualItem.start}px)`
4946
- },
4947
- children: cloneElement(children, {
4948
- key: virtualItem.key,
4949
- //@ts-expect-error pageNumber is not a valid react key
4950
- pageNumber: virtualItem.index + 1
4951
- })
4952
- }
4953
- )
5293
+ child: children,
5294
+ virtualItem,
5295
+ innerBoxWidth
4954
5296
  },
4955
5297
  virtualItem.key
4956
5298
  );
@@ -4962,17 +5304,44 @@ var Pages = ({
4962
5304
  }
4963
5305
  );
4964
5306
  };
5307
+ function buildDocumentInitParams(source, overrides) {
5308
+ let params;
5309
+ if (typeof source === "string" || source instanceof URL) {
5310
+ params = { url: source };
5311
+ } else if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) {
5312
+ params = { data: source };
5313
+ } else {
5314
+ params = { ...source };
5315
+ }
5316
+ const defaults = {
5317
+ // Skip the slow _guessMax binary-search auto-detection of max canvas area.
5318
+ // 1 GiB = 256 megapixels at 4 bytes/pixel. If the device can't allocate
5319
+ // this, pdfjs falls back gracefully via try/catch.
5320
+ canvasMaxAreaInBytes: 1024 * 1024 * 1024,
5321
+ // Enable GPU-backed canvases (willReadFrequently: false). PDF viewers are
5322
+ // render-dominated — they don't call getImageData() — so GPU compositing
5323
+ // is the correct path. This is the browser's default when no hint is given.
5324
+ enableHWA: true,
5325
+ // Explicitly opt-in to OffscreenCanvas in the worker for image processing.
5326
+ // Already the browser default, but being explicit avoids edge cases.
5327
+ isOffscreenCanvasSupported: true
5328
+ };
5329
+ return { ...defaults, ...params, ...overrides };
5330
+ }
4965
5331
  var usePDFDocumentContext = ({
4966
5332
  onDocumentLoad,
4967
5333
  source,
4968
5334
  initialRotation = 0,
4969
5335
  isZoomFitWidth,
4970
5336
  zoom = 1,
4971
- zoomOptions
5337
+ zoomOptions,
5338
+ documentOptions
4972
5339
  }) => {
4973
5340
  const [_, setProgress] = useState(0);
4974
5341
  const [initialState, setInitialState] = useState();
4975
5342
  const [rotation] = useState(initialRotation);
5343
+ const documentOptionsRef = useRef(documentOptions);
5344
+ documentOptionsRef.current = documentOptions;
4976
5345
  useEffect(() => {
4977
5346
  const generateViewports = async (pdf) => {
4978
5347
  const pageProxies = [];
@@ -4990,9 +5359,9 @@ var usePDFDocumentContext = ({
4990
5359
  return viewport;
4991
5360
  })
4992
5361
  );
4993
- const sortedPageProxies = pageProxies.sort((a, b) => {
4994
- return a.pageNumber - b.pageNumber;
4995
- });
5362
+ const sortedPageProxies = pageProxies.toSorted(
5363
+ (a, b) => a.pageNumber - b.pageNumber
5364
+ );
4996
5365
  setInitialState((prev) => ({
4997
5366
  ...prev,
4998
5367
  isZoomFitWidth,
@@ -5006,28 +5375,46 @@ var usePDFDocumentContext = ({
5006
5375
  const loadDocument = () => {
5007
5376
  setInitialState(null);
5008
5377
  setProgress(0);
5009
- const loadingTask = getDocument(source);
5010
- loadingTask.onProgress = (progressEvent) => {
5011
- if (progressEvent.loaded === progressEvent.total) {
5378
+ let loadingTask = null;
5379
+ let isDisposed = false;
5380
+ void loadPdfJs().then(({ getDocument }) => {
5381
+ if (isDisposed) {
5012
5382
  return;
5013
5383
  }
5014
- setProgress(progressEvent.loaded / progressEvent.total);
5015
- };
5016
- const loadingPromise = loadingTask.promise.then((proxy) => {
5017
- onDocumentLoad?.({ proxy, source });
5018
- setProgress(1);
5019
- generateViewports(proxy);
5384
+ loadingTask = getDocument(
5385
+ buildDocumentInitParams(source, documentOptionsRef.current)
5386
+ );
5387
+ loadingTask.onProgress = (progressEvent) => {
5388
+ if (progressEvent.loaded === progressEvent.total) {
5389
+ return;
5390
+ }
5391
+ setProgress(progressEvent.loaded / progressEvent.total);
5392
+ };
5393
+ return loadingTask.promise.then((proxy) => {
5394
+ if (isDisposed || loadingTask?.destroyed) {
5395
+ return;
5396
+ }
5397
+ onDocumentLoad?.({ proxy, source });
5398
+ setProgress(1);
5399
+ return generateViewports(proxy);
5400
+ }).catch((error) => {
5401
+ if (isDisposed || loadingTask?.destroyed) {
5402
+ return;
5403
+ }
5404
+ console.error("Error loading PDF document", error);
5405
+ });
5020
5406
  }).catch((error) => {
5021
- if (loadingTask.destroyed) {
5407
+ if (isDisposed) {
5022
5408
  return;
5023
5409
  }
5024
- console.error("Error loading PDF document", error);
5410
+ console.error("Error loading PDF.js", error);
5025
5411
  });
5026
5412
  return () => {
5027
- loadingPromise.finally(() => loadingTask.destroy());
5413
+ isDisposed = true;
5414
+ void loadingTask?.destroy();
5028
5415
  };
5029
5416
  };
5030
- loadDocument();
5417
+ return loadDocument();
5031
5418
  }, [source]);
5032
5419
  return {
5033
5420
  initialState
@@ -5042,6 +5429,7 @@ var Root = forwardRef(
5042
5429
  isZoomFitWidth,
5043
5430
  zoom,
5044
5431
  zoomOptions,
5432
+ documentOptions,
5045
5433
  ...props
5046
5434
  }, ref) => {
5047
5435
  const { initialState } = usePDFDocumentContext({
@@ -5049,12 +5437,20 @@ var Root = forwardRef(
5049
5437
  onDocumentLoad,
5050
5438
  isZoomFitWidth,
5051
5439
  zoom,
5052
- zoomOptions
5440
+ zoomOptions,
5441
+ documentOptions
5053
5442
  });
5054
5443
  const linkService = useCreatePDFLinkService(
5055
5444
  initialState?.pdfDocumentProxy ?? null
5056
5445
  );
5057
- return /* @__PURE__ */ jsx(Primitive.div, { ref, ...props, children: initialState ? /* @__PURE__ */ jsx(PDFStore.Provider, { initialValue: initialState, children: /* @__PURE__ */ jsx(PDFLinkServiceContext.Provider, { value: linkService, children }) }) : loader || "Loading..." });
5446
+ const documentId = initialState?.pdfDocumentProxy.fingerprints[0];
5447
+ useEffect(() => {
5448
+ if (!documentId) return;
5449
+ return () => {
5450
+ renderCache.invalidateDocument(documentId);
5451
+ };
5452
+ }, [documentId]);
5453
+ return /* @__PURE__ */ jsx(Primitive.div, { ref, ...props, children: initialState ? /* @__PURE__ */ jsx(PDFStore.Provider, { initialValue: initialState, children: /* @__PURE__ */ jsx(PDFLinkServiceContext.Provider, { value: linkService, children }) }) : loader ?? "Loading..." });
5058
5454
  }
5059
5455
  );
5060
5456
  Root.displayName = "Root";
@@ -5104,8 +5500,8 @@ var useThumbnail = (pageNumber, config = {}) => {
5104
5500
  const isVisible = isFirstPage || debouncedVisible;
5105
5501
  useEffect(() => {
5106
5502
  const renderThumbnail = async () => {
5107
- const canvas = canvasRef.current;
5108
- if (!canvas || !pageProxy) return;
5503
+ const canvas2 = canvasRef.current;
5504
+ if (!canvas2 || !pageProxy) return;
5109
5505
  try {
5110
5506
  if (renderTaskRef.current) {
5111
5507
  renderTaskRef.current.cancel();
@@ -5113,11 +5509,12 @@ var useThumbnail = (pageNumber, config = {}) => {
5113
5509
  const viewport = pageProxy.getViewport({ scale: 1 });
5114
5510
  const scale = Math.min(maxWidth / viewport.width, maxHeight / viewport.height) * (isVisible ? dpr : 0.5);
5115
5511
  const scaledViewport = pageProxy.getViewport({ scale });
5116
- canvas.width = scaledViewport.width;
5117
- canvas.height = scaledViewport.height;
5118
- const context = canvas.getContext("2d");
5512
+ canvas2.width = scaledViewport.width;
5513
+ canvas2.height = scaledViewport.height;
5514
+ const context = canvas2.getContext("2d");
5119
5515
  if (!context) return;
5120
5516
  const renderTask = pageProxy.render({
5517
+ canvas: canvas2,
5121
5518
  canvasContext: context,
5122
5519
  viewport: scaledViewport
5123
5520
  });
@@ -5132,8 +5529,13 @@ var useThumbnail = (pageNumber, config = {}) => {
5132
5529
  }
5133
5530
  };
5134
5531
  renderThumbnail();
5532
+ const canvas = canvasRef.current;
5135
5533
  return () => {
5136
5534
  renderTaskRef.current?.cancel();
5535
+ if (canvas) {
5536
+ canvas.width = 1;
5537
+ canvas.height = 1;
5538
+ }
5137
5539
  };
5138
5540
  }, [pageProxy, isVisible, dpr, maxHeight, maxWidth]);
5139
5541
  return {
@@ -5212,12 +5614,12 @@ var ZoomOut = ({ ...props }) => {
5212
5614
  );
5213
5615
  };
5214
5616
  var CurrentZoom = ({ ...props }) => {
5215
- const setRealZoom = usePdf((state) => state.updateZoom);
5617
+ const updateZoom = usePdf((state) => state.updateZoom);
5216
5618
  const realZoom = usePdf((state) => state.zoom);
5217
5619
  const [zoom, setZoom] = useState((realZoom * 100).toFixed(0));
5218
- const isSelected = useRef(false);
5620
+ const isInputFocused = useRef(false);
5219
5621
  useEffect(() => {
5220
- if (isSelected.current) {
5622
+ if (isInputFocused.current) {
5221
5623
  return;
5222
5624
  }
5223
5625
  setZoom((realZoom * 100).toFixed(0));
@@ -5228,14 +5630,14 @@ var CurrentZoom = ({ ...props }) => {
5228
5630
  ...props,
5229
5631
  value: zoom,
5230
5632
  onClick: () => {
5231
- isSelected.current = true;
5633
+ isInputFocused.current = true;
5232
5634
  },
5233
5635
  onChange: (e) => {
5234
- setRealZoom(Number(e.target.value) / 100);
5636
+ updateZoom(Number(e.target.value) / 100);
5235
5637
  setZoom(e.target.value);
5236
5638
  },
5237
5639
  onBlur: () => {
5238
- isSelected.current = false;
5640
+ isInputFocused.current = false;
5239
5641
  setZoom((realZoom * 100).toFixed(0));
5240
5642
  }
5241
5643
  }
@@ -5468,4 +5870,7 @@ function mergeAdjacentRects(rects) {
5468
5870
  return merged;
5469
5871
  }
5470
5872
 
5471
- export { AnnotationHighlightLayer, AnnotationLayer2 as AnnotationLayer, AnnotationTooltip, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer2 as TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePdf, usePdfJump, useSearch, useSelectionDimensions };
5873
+ // src/hooks/usePageRendered.ts
5874
+ var usePageRendered = (pageNumber) => usePdf((state) => !!state.renderedPages[pageNumber]);
5875
+
5876
+ export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePageRendered, usePdf, usePdfJump, useSearch, useSelectionDimensions };