pdfjs-reader-core 0.5.4 → 0.5.5

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.cjs CHANGED
@@ -1863,6 +1863,129 @@ var init_page_turn_sound = __esm({
1863
1863
  }
1864
1864
  });
1865
1865
 
1866
+ // src/utils/mobile-config.ts
1867
+ function detectDeviceCapabilities() {
1868
+ if (typeof window === "undefined") {
1869
+ return {
1870
+ isMobile: false,
1871
+ isTouch: false,
1872
+ devicePixelRatio: 1,
1873
+ deviceMemory: null,
1874
+ hardwareConcurrency: 4,
1875
+ isLowEnd: false,
1876
+ connectionType: "unknown",
1877
+ screenSize: "large"
1878
+ };
1879
+ }
1880
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
1881
+ navigator.userAgent
1882
+ ) || window.matchMedia("(max-width: 768px)").matches;
1883
+ const isTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1884
+ const devicePixelRatio = window.devicePixelRatio || 1;
1885
+ const deviceMemory = navigator.deviceMemory ?? null;
1886
+ const hardwareConcurrency = navigator.hardwareConcurrency || 2;
1887
+ const screenWidth = window.screen.width;
1888
+ const screenSize = screenWidth < 640 ? "small" : screenWidth < 1024 ? "medium" : "large";
1889
+ const connection = navigator.connection;
1890
+ let connectionType = "unknown";
1891
+ if (connection?.effectiveType) {
1892
+ connectionType = ["slow-2g", "2g", "3g"].includes(connection.effectiveType) ? "slow" : "fast";
1893
+ }
1894
+ const isLowEnd = deviceMemory !== null && deviceMemory < 4 || hardwareConcurrency <= 2 || isMobile && devicePixelRatio > 2 && screenSize === "small";
1895
+ return {
1896
+ isMobile,
1897
+ isTouch,
1898
+ devicePixelRatio,
1899
+ deviceMemory,
1900
+ hardwareConcurrency,
1901
+ isLowEnd,
1902
+ connectionType,
1903
+ screenSize
1904
+ };
1905
+ }
1906
+ function getRenderConfig(quality = "auto", capabilities) {
1907
+ const caps = capabilities ?? detectDeviceCapabilities();
1908
+ if (quality === "auto") {
1909
+ if (caps.isLowEnd) {
1910
+ quality = "low";
1911
+ } else if (caps.isMobile) {
1912
+ quality = "medium";
1913
+ } else {
1914
+ quality = "high";
1915
+ }
1916
+ }
1917
+ switch (quality) {
1918
+ case "low":
1919
+ return {
1920
+ canvasScaleFactor: Math.min(1, caps.devicePixelRatio * 0.5),
1921
+ maxCanvasDimension: 2048,
1922
+ overscanPages: 1,
1923
+ maxPagesInMemory: 3,
1924
+ scrollDebounceMs: 100,
1925
+ useLowResPreview: true,
1926
+ fullResDelayMs: 300,
1927
+ disableTextLayerOnScroll: true,
1928
+ currentPagePriority: 10,
1929
+ useIdleCallback: true,
1930
+ maxConcurrentRenders: 1
1931
+ };
1932
+ case "medium":
1933
+ return {
1934
+ canvasScaleFactor: Math.min(1.5, caps.devicePixelRatio * 0.75),
1935
+ maxCanvasDimension: 4096,
1936
+ overscanPages: 2,
1937
+ maxPagesInMemory: 5,
1938
+ scrollDebounceMs: 50,
1939
+ useLowResPreview: true,
1940
+ fullResDelayMs: 150,
1941
+ disableTextLayerOnScroll: true,
1942
+ currentPagePriority: 5,
1943
+ useIdleCallback: true,
1944
+ maxConcurrentRenders: 2
1945
+ };
1946
+ case "high":
1947
+ default:
1948
+ return {
1949
+ canvasScaleFactor: caps.devicePixelRatio,
1950
+ maxCanvasDimension: 8192,
1951
+ overscanPages: 3,
1952
+ maxPagesInMemory: 10,
1953
+ scrollDebounceMs: 16,
1954
+ useLowResPreview: false,
1955
+ fullResDelayMs: 0,
1956
+ disableTextLayerOnScroll: false,
1957
+ currentPagePriority: 1,
1958
+ useIdleCallback: false,
1959
+ maxConcurrentRenders: 4
1960
+ };
1961
+ }
1962
+ }
1963
+ function calculateOptimalCanvasDimensions(width, height, scaleFactor, maxDimension) {
1964
+ let canvasWidth = Math.floor(width * scaleFactor);
1965
+ let canvasHeight = Math.floor(height * scaleFactor);
1966
+ let actualScale = scaleFactor;
1967
+ if (canvasWidth > maxDimension || canvasHeight > maxDimension) {
1968
+ const scaleToFit = Math.min(maxDimension / canvasWidth, maxDimension / canvasHeight);
1969
+ canvasWidth = Math.floor(canvasWidth * scaleToFit);
1970
+ canvasHeight = Math.floor(canvasHeight * scaleToFit);
1971
+ actualScale = scaleFactor * scaleToFit;
1972
+ }
1973
+ return { width: canvasWidth, height: canvasHeight, actualScale };
1974
+ }
1975
+ function getDeviceCapabilities() {
1976
+ if (!globalCapabilities) {
1977
+ globalCapabilities = detectDeviceCapabilities();
1978
+ }
1979
+ return globalCapabilities;
1980
+ }
1981
+ var globalCapabilities;
1982
+ var init_mobile_config = __esm({
1983
+ "src/utils/mobile-config.ts"() {
1984
+ "use strict";
1985
+ globalCapabilities = null;
1986
+ }
1987
+ });
1988
+
1866
1989
  // src/utils/camera-math.ts
1867
1990
  function fitPageScale(page, viewport) {
1868
1991
  const sx = viewport.width / page.width;
@@ -1917,6 +2040,7 @@ var init_utils = __esm({
1917
2040
  init_text_search();
1918
2041
  init_coordinates();
1919
2042
  init_page_turn_sound();
2043
+ init_mobile_config();
1920
2044
  }
1921
2045
  });
1922
2046
 
@@ -7136,9 +7260,17 @@ var init_CanvasLayer = __esm({
7136
7260
  onRenderError?.(new Error("Failed to get canvas context"));
7137
7261
  return;
7138
7262
  }
7139
- const outputScale = window.devicePixelRatio || 1;
7140
- canvas.width = Math.floor(viewport.width * outputScale);
7141
- canvas.height = Math.floor(viewport.height * outputScale);
7263
+ const capabilities = getDeviceCapabilities();
7264
+ const renderConfig = getRenderConfig("auto", capabilities);
7265
+ const optimal = calculateOptimalCanvasDimensions(
7266
+ viewport.width,
7267
+ viewport.height,
7268
+ renderConfig.canvasScaleFactor,
7269
+ renderConfig.maxCanvasDimension
7270
+ );
7271
+ const outputScale = optimal.actualScale;
7272
+ canvas.width = optimal.width;
7273
+ canvas.height = optimal.height;
7142
7274
  canvas.style.width = `${Math.floor(viewport.width)}px`;
7143
7275
  canvas.style.height = `${Math.floor(viewport.height)}px`;
7144
7276
  context.scale(outputScale, outputScale);
@@ -10936,39 +11068,53 @@ var init_PDFViewerClient = __esm({
10936
11068
  }, [viewerStore, annotationStore, searchStore]);
10937
11069
  const handleRetry = (0, import_react43.useCallback)(() => {
10938
11070
  srcIdRef.current = null;
11071
+ if (abortControllerRef.current) {
11072
+ abortControllerRef.current.abort();
11073
+ abortControllerRef.current = null;
11074
+ }
10939
11075
  viewerStore.getState().setError(null);
10940
11076
  setLoadState("idle");
10941
11077
  }, [viewerStore]);
10942
11078
  const abortControllerRef = (0, import_react43.useRef)(null);
10943
11079
  const currentSrcRef = (0, import_react43.useRef)(null);
11080
+ const teardownTimerRef = (0, import_react43.useRef)(null);
10944
11081
  (0, import_react43.useEffect)(() => {
10945
11082
  mountedRef.current = true;
11083
+ if (teardownTimerRef.current !== null) {
11084
+ clearTimeout(teardownTimerRef.current);
11085
+ teardownTimerRef.current = null;
11086
+ }
10946
11087
  return () => {
10947
- mountedRef.current = false;
10948
- if (abortControllerRef.current) {
10949
- abortControllerRef.current.abort();
10950
- abortControllerRef.current = null;
10951
- }
10952
- if (currentSrcRef.current && typeof currentSrcRef.current === "string") {
10953
- clearDocumentCache(currentSrcRef.current);
10954
- }
10955
- srcIdRef.current = null;
10956
- currentSrcRef.current = null;
10957
- const currentDoc = viewerStore.getState().document;
10958
- if (currentDoc) {
10959
- try {
10960
- currentDoc.destroy();
10961
- } catch {
11088
+ teardownTimerRef.current = setTimeout(() => {
11089
+ teardownTimerRef.current = null;
11090
+ mountedRef.current = false;
11091
+ if (abortControllerRef.current) {
11092
+ abortControllerRef.current.abort();
11093
+ abortControllerRef.current = null;
10962
11094
  }
10963
- }
10964
- viewerStore.getState().setDocument(null);
10965
- viewerStore.getState().setLoading(false);
10966
- viewerStore.getState().setError(null);
11095
+ if (currentSrcRef.current && typeof currentSrcRef.current === "string") {
11096
+ clearDocumentCache(currentSrcRef.current);
11097
+ }
11098
+ srcIdRef.current = null;
11099
+ currentSrcRef.current = null;
11100
+ const currentDoc = viewerStore.getState().document;
11101
+ if (currentDoc) {
11102
+ try {
11103
+ currentDoc.destroy();
11104
+ } catch {
11105
+ }
11106
+ }
11107
+ viewerStore.getState().setDocument(null);
11108
+ viewerStore.getState().setLoading(false);
11109
+ viewerStore.getState().setError(null);
11110
+ }, 0);
10967
11111
  };
10968
11112
  }, [viewerStore]);
10969
11113
  const cancelLoaderRef = (0, import_react43.useRef)(null);
10970
11114
  (0, import_react43.useEffect)(() => {
10971
- if (srcIdRef.current === srcId && viewerStore.getState().document) {
11115
+ const existingAC = abortControllerRef.current;
11116
+ const loadInFlight = existingAC !== null && !existingAC.signal.aborted;
11117
+ if (srcIdRef.current === srcId && (viewerStore.getState().document || loadInFlight)) {
10972
11118
  return;
10973
11119
  }
10974
11120
  const loadId = srcId;
@@ -11093,14 +11239,7 @@ var init_PDFViewerClient = __esm({
11093
11239
  onErrorRef.current?.(error2);
11094
11240
  }
11095
11241
  });
11096
- return () => {
11097
- abortController.abort();
11098
- if (cancelLoaderRef.current) {
11099
- cancelLoaderRef.current();
11100
- cancelLoaderRef.current = null;
11101
- }
11102
- };
11103
- }, [srcId, src, workerSrc, initialPage, initialScale, viewerStore]);
11242
+ }, [srcId, workerSrc, initialPage, initialScale, viewerStore]);
11104
11243
  const prevPageRef = (0, import_react43.useRef)(currentPage);
11105
11244
  (0, import_react43.useEffect)(() => {
11106
11245
  if (prevPageRef.current !== currentPage) {
@@ -13136,6 +13275,7 @@ init_hooks();
13136
13275
 
13137
13276
  // src/components/TutorMode/CameraView.tsx
13138
13277
  var import_framer_motion = require("framer-motion");
13278
+ init_utils();
13139
13279
  var import_jsx_runtime41 = require("react/jsx-runtime");
13140
13280
  function CameraView({
13141
13281
  camera,
@@ -13143,13 +13283,14 @@ function CameraView({
13143
13283
  durationMs = 700,
13144
13284
  className
13145
13285
  }) {
13286
+ const isMobile = getDeviceCapabilities().isMobile;
13146
13287
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
13147
13288
  import_framer_motion.motion.div,
13148
13289
  {
13149
13290
  className,
13150
13291
  style: {
13151
13292
  transformOrigin: "50% 50%",
13152
- willChange: "transform",
13293
+ ...isMobile ? {} : { willChange: "transform" },
13153
13294
  width: "100%",
13154
13295
  height: "100%",
13155
13296
  position: "relative"
@@ -13174,6 +13315,7 @@ var import_framer_motion8 = require("framer-motion");
13174
13315
  // src/components/TutorMode/SpotlightMask.tsx
13175
13316
  var import_react55 = require("react");
13176
13317
  var import_framer_motion2 = require("framer-motion");
13318
+ init_utils();
13177
13319
 
13178
13320
  // src/components/TutorMode/tokens.ts
13179
13321
  var INK = "#2a2420";
@@ -13224,7 +13366,9 @@ function SpotlightMask({
13224
13366
  const h = y2 - y1;
13225
13367
  const rx = action.shape === "rounded" ? 14 : action.shape === "ellipse" ? w / 2 : 0;
13226
13368
  const ry = action.shape === "rounded" ? 14 : action.shape === "ellipse" ? h / 2 : 0;
13227
- const feather = Math.max(16, action.feather_px);
13369
+ const isMobile = getDeviceCapabilities().isMobile;
13370
+ const requestedFeather = Math.max(16, action.feather_px);
13371
+ const feather = isMobile ? Math.min(12, requestedFeather) : requestedFeather;
13228
13372
  const cx = (x1 + x2) / 2;
13229
13373
  const cy = (y1 + y2) / 2;
13230
13374
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(