pdfjs-reader-core 0.5.2 → 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.js CHANGED
@@ -1839,6 +1839,129 @@ var init_page_turn_sound = __esm({
1839
1839
  }
1840
1840
  });
1841
1841
 
1842
+ // src/utils/mobile-config.ts
1843
+ function detectDeviceCapabilities() {
1844
+ if (typeof window === "undefined") {
1845
+ return {
1846
+ isMobile: false,
1847
+ isTouch: false,
1848
+ devicePixelRatio: 1,
1849
+ deviceMemory: null,
1850
+ hardwareConcurrency: 4,
1851
+ isLowEnd: false,
1852
+ connectionType: "unknown",
1853
+ screenSize: "large"
1854
+ };
1855
+ }
1856
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
1857
+ navigator.userAgent
1858
+ ) || window.matchMedia("(max-width: 768px)").matches;
1859
+ const isTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1860
+ const devicePixelRatio = window.devicePixelRatio || 1;
1861
+ const deviceMemory = navigator.deviceMemory ?? null;
1862
+ const hardwareConcurrency = navigator.hardwareConcurrency || 2;
1863
+ const screenWidth = window.screen.width;
1864
+ const screenSize = screenWidth < 640 ? "small" : screenWidth < 1024 ? "medium" : "large";
1865
+ const connection = navigator.connection;
1866
+ let connectionType = "unknown";
1867
+ if (connection?.effectiveType) {
1868
+ connectionType = ["slow-2g", "2g", "3g"].includes(connection.effectiveType) ? "slow" : "fast";
1869
+ }
1870
+ const isLowEnd = deviceMemory !== null && deviceMemory < 4 || hardwareConcurrency <= 2 || isMobile && devicePixelRatio > 2 && screenSize === "small";
1871
+ return {
1872
+ isMobile,
1873
+ isTouch,
1874
+ devicePixelRatio,
1875
+ deviceMemory,
1876
+ hardwareConcurrency,
1877
+ isLowEnd,
1878
+ connectionType,
1879
+ screenSize
1880
+ };
1881
+ }
1882
+ function getRenderConfig(quality = "auto", capabilities) {
1883
+ const caps = capabilities ?? detectDeviceCapabilities();
1884
+ if (quality === "auto") {
1885
+ if (caps.isLowEnd) {
1886
+ quality = "low";
1887
+ } else if (caps.isMobile) {
1888
+ quality = "medium";
1889
+ } else {
1890
+ quality = "high";
1891
+ }
1892
+ }
1893
+ switch (quality) {
1894
+ case "low":
1895
+ return {
1896
+ canvasScaleFactor: Math.min(1, caps.devicePixelRatio * 0.5),
1897
+ maxCanvasDimension: 2048,
1898
+ overscanPages: 1,
1899
+ maxPagesInMemory: 3,
1900
+ scrollDebounceMs: 100,
1901
+ useLowResPreview: true,
1902
+ fullResDelayMs: 300,
1903
+ disableTextLayerOnScroll: true,
1904
+ currentPagePriority: 10,
1905
+ useIdleCallback: true,
1906
+ maxConcurrentRenders: 1
1907
+ };
1908
+ case "medium":
1909
+ return {
1910
+ canvasScaleFactor: Math.min(1.5, caps.devicePixelRatio * 0.75),
1911
+ maxCanvasDimension: 4096,
1912
+ overscanPages: 2,
1913
+ maxPagesInMemory: 5,
1914
+ scrollDebounceMs: 50,
1915
+ useLowResPreview: true,
1916
+ fullResDelayMs: 150,
1917
+ disableTextLayerOnScroll: true,
1918
+ currentPagePriority: 5,
1919
+ useIdleCallback: true,
1920
+ maxConcurrentRenders: 2
1921
+ };
1922
+ case "high":
1923
+ default:
1924
+ return {
1925
+ canvasScaleFactor: caps.devicePixelRatio,
1926
+ maxCanvasDimension: 8192,
1927
+ overscanPages: 3,
1928
+ maxPagesInMemory: 10,
1929
+ scrollDebounceMs: 16,
1930
+ useLowResPreview: false,
1931
+ fullResDelayMs: 0,
1932
+ disableTextLayerOnScroll: false,
1933
+ currentPagePriority: 1,
1934
+ useIdleCallback: false,
1935
+ maxConcurrentRenders: 4
1936
+ };
1937
+ }
1938
+ }
1939
+ function calculateOptimalCanvasDimensions(width, height, scaleFactor, maxDimension) {
1940
+ let canvasWidth = Math.floor(width * scaleFactor);
1941
+ let canvasHeight = Math.floor(height * scaleFactor);
1942
+ let actualScale = scaleFactor;
1943
+ if (canvasWidth > maxDimension || canvasHeight > maxDimension) {
1944
+ const scaleToFit = Math.min(maxDimension / canvasWidth, maxDimension / canvasHeight);
1945
+ canvasWidth = Math.floor(canvasWidth * scaleToFit);
1946
+ canvasHeight = Math.floor(canvasHeight * scaleToFit);
1947
+ actualScale = scaleFactor * scaleToFit;
1948
+ }
1949
+ return { width: canvasWidth, height: canvasHeight, actualScale };
1950
+ }
1951
+ function getDeviceCapabilities() {
1952
+ if (!globalCapabilities) {
1953
+ globalCapabilities = detectDeviceCapabilities();
1954
+ }
1955
+ return globalCapabilities;
1956
+ }
1957
+ var globalCapabilities;
1958
+ var init_mobile_config = __esm({
1959
+ "src/utils/mobile-config.ts"() {
1960
+ "use strict";
1961
+ globalCapabilities = null;
1962
+ }
1963
+ });
1964
+
1842
1965
  // src/utils/camera-math.ts
1843
1966
  function fitPageScale(page, viewport) {
1844
1967
  const sx = viewport.width / page.width;
@@ -1893,6 +2016,7 @@ var init_utils = __esm({
1893
2016
  init_text_search();
1894
2017
  init_coordinates();
1895
2018
  init_page_turn_sound();
2019
+ init_mobile_config();
1896
2020
  }
1897
2021
  });
1898
2022
 
@@ -7099,9 +7223,17 @@ var init_CanvasLayer = __esm({
7099
7223
  onRenderError?.(new Error("Failed to get canvas context"));
7100
7224
  return;
7101
7225
  }
7102
- const outputScale = window.devicePixelRatio || 1;
7103
- canvas.width = Math.floor(viewport.width * outputScale);
7104
- canvas.height = Math.floor(viewport.height * outputScale);
7226
+ const capabilities = getDeviceCapabilities();
7227
+ const renderConfig = getRenderConfig("auto", capabilities);
7228
+ const optimal = calculateOptimalCanvasDimensions(
7229
+ viewport.width,
7230
+ viewport.height,
7231
+ renderConfig.canvasScaleFactor,
7232
+ renderConfig.maxCanvasDimension
7233
+ );
7234
+ const outputScale = optimal.actualScale;
7235
+ canvas.width = optimal.width;
7236
+ canvas.height = optimal.height;
7105
7237
  canvas.style.width = `${Math.floor(viewport.width)}px`;
7106
7238
  canvas.style.height = `${Math.floor(viewport.height)}px`;
7107
7239
  context.scale(outputScale, outputScale);
@@ -10906,39 +11038,53 @@ var init_PDFViewerClient = __esm({
10906
11038
  }, [viewerStore, annotationStore, searchStore]);
10907
11039
  const handleRetry = useCallback34(() => {
10908
11040
  srcIdRef.current = null;
11041
+ if (abortControllerRef.current) {
11042
+ abortControllerRef.current.abort();
11043
+ abortControllerRef.current = null;
11044
+ }
10909
11045
  viewerStore.getState().setError(null);
10910
11046
  setLoadState("idle");
10911
11047
  }, [viewerStore]);
10912
11048
  const abortControllerRef = useRef20(null);
10913
11049
  const currentSrcRef = useRef20(null);
11050
+ const teardownTimerRef = useRef20(null);
10914
11051
  useEffect23(() => {
10915
11052
  mountedRef.current = true;
11053
+ if (teardownTimerRef.current !== null) {
11054
+ clearTimeout(teardownTimerRef.current);
11055
+ teardownTimerRef.current = null;
11056
+ }
10916
11057
  return () => {
10917
- mountedRef.current = false;
10918
- if (abortControllerRef.current) {
10919
- abortControllerRef.current.abort();
10920
- abortControllerRef.current = null;
10921
- }
10922
- if (currentSrcRef.current && typeof currentSrcRef.current === "string") {
10923
- clearDocumentCache(currentSrcRef.current);
10924
- }
10925
- srcIdRef.current = null;
10926
- currentSrcRef.current = null;
10927
- const currentDoc = viewerStore.getState().document;
10928
- if (currentDoc) {
10929
- try {
10930
- currentDoc.destroy();
10931
- } catch {
11058
+ teardownTimerRef.current = setTimeout(() => {
11059
+ teardownTimerRef.current = null;
11060
+ mountedRef.current = false;
11061
+ if (abortControllerRef.current) {
11062
+ abortControllerRef.current.abort();
11063
+ abortControllerRef.current = null;
10932
11064
  }
10933
- }
10934
- viewerStore.getState().setDocument(null);
10935
- viewerStore.getState().setLoading(false);
10936
- viewerStore.getState().setError(null);
11065
+ if (currentSrcRef.current && typeof currentSrcRef.current === "string") {
11066
+ clearDocumentCache(currentSrcRef.current);
11067
+ }
11068
+ srcIdRef.current = null;
11069
+ currentSrcRef.current = null;
11070
+ const currentDoc = viewerStore.getState().document;
11071
+ if (currentDoc) {
11072
+ try {
11073
+ currentDoc.destroy();
11074
+ } catch {
11075
+ }
11076
+ }
11077
+ viewerStore.getState().setDocument(null);
11078
+ viewerStore.getState().setLoading(false);
11079
+ viewerStore.getState().setError(null);
11080
+ }, 0);
10937
11081
  };
10938
11082
  }, [viewerStore]);
10939
11083
  const cancelLoaderRef = useRef20(null);
10940
11084
  useEffect23(() => {
10941
- if (srcIdRef.current === srcId && viewerStore.getState().document) {
11085
+ const existingAC = abortControllerRef.current;
11086
+ const loadInFlight = existingAC !== null && !existingAC.signal.aborted;
11087
+ if (srcIdRef.current === srcId && (viewerStore.getState().document || loadInFlight)) {
10942
11088
  return;
10943
11089
  }
10944
11090
  const loadId = srcId;
@@ -11063,14 +11209,7 @@ var init_PDFViewerClient = __esm({
11063
11209
  onErrorRef.current?.(error2);
11064
11210
  }
11065
11211
  });
11066
- return () => {
11067
- abortController.abort();
11068
- if (cancelLoaderRef.current) {
11069
- cancelLoaderRef.current();
11070
- cancelLoaderRef.current = null;
11071
- }
11072
- };
11073
- }, [srcId, src, workerSrc, initialPage, initialScale, viewerStore]);
11212
+ }, [srcId, workerSrc, initialPage, initialScale, viewerStore]);
11074
11213
  const prevPageRef = useRef20(currentPage);
11075
11214
  useEffect23(() => {
11076
11215
  if (prevPageRef.current !== currentPage) {
@@ -12950,6 +13089,7 @@ import { useEffect as useEffect28, useMemo as useMemo15, useRef as useRef27, use
12950
13089
  import { useStore as useStore2 } from "zustand";
12951
13090
 
12952
13091
  // src/components/TutorMode/CameraView.tsx
13092
+ init_utils();
12953
13093
  import { motion } from "framer-motion";
12954
13094
  import { jsx as jsx41 } from "react/jsx-runtime";
12955
13095
  function CameraView({
@@ -12958,13 +13098,14 @@ function CameraView({
12958
13098
  durationMs = 700,
12959
13099
  className
12960
13100
  }) {
13101
+ const isMobile = getDeviceCapabilities().isMobile;
12961
13102
  return /* @__PURE__ */ jsx41(
12962
13103
  motion.div,
12963
13104
  {
12964
13105
  className,
12965
13106
  style: {
12966
13107
  transformOrigin: "50% 50%",
12967
- willChange: "transform",
13108
+ ...isMobile ? {} : { willChange: "transform" },
12968
13109
  width: "100%",
12969
13110
  height: "100%",
12970
13111
  position: "relative"
@@ -12987,6 +13128,7 @@ function CameraView({
12987
13128
  import { AnimatePresence } from "framer-motion";
12988
13129
 
12989
13130
  // src/components/TutorMode/SpotlightMask.tsx
13131
+ init_utils();
12990
13132
  import { useId } from "react";
12991
13133
  import { motion as motion2 } from "framer-motion";
12992
13134
 
@@ -13039,7 +13181,9 @@ function SpotlightMask({
13039
13181
  const h = y2 - y1;
13040
13182
  const rx = action.shape === "rounded" ? 14 : action.shape === "ellipse" ? w / 2 : 0;
13041
13183
  const ry = action.shape === "rounded" ? 14 : action.shape === "ellipse" ? h / 2 : 0;
13042
- const feather = Math.max(16, action.feather_px);
13184
+ const isMobile = getDeviceCapabilities().isMobile;
13185
+ const requestedFeather = Math.max(16, action.feather_px);
13186
+ const feather = isMobile ? Math.min(12, requestedFeather) : requestedFeather;
13043
13187
  const cx = (x1 + x2) / 2;
13044
13188
  const cy = (y1 + y2) / 2;
13045
13189
  return /* @__PURE__ */ jsxs35(