pdfjs-reader-core 0.5.7 → 0.5.9

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
@@ -9191,7 +9191,7 @@ var init_DocumentContainer = __esm({
9191
9191
  const containerRef = (0, import_react37.useRef)(null);
9192
9192
  const documentRef = (0, import_react37.useRef)(null);
9193
9193
  const baseScaleRef = (0, import_react37.useRef)(scale);
9194
- const isTouchDevice2 = useIsTouchDevice();
9194
+ const isTouchDevice = useIsTouchDevice();
9195
9195
  const documentLoadingState = useViewerStore((s) => s.documentLoadingState);
9196
9196
  const { selection, clearSelection, copySelection } = useTextSelection();
9197
9197
  const handlePinchZoom = (0, import_react37.useCallback)(
@@ -9222,7 +9222,7 @@ var init_DocumentContainer = __esm({
9222
9222
  onSwipeLeft: handleSwipeLeft,
9223
9223
  onSwipeRight: handleSwipeRight,
9224
9224
  onDoubleTap: handleDoubleTap,
9225
- enabled: enableTouchGestures && isTouchDevice2,
9225
+ enabled: enableTouchGestures && isTouchDevice,
9226
9226
  swipeThreshold: 50,
9227
9227
  doubleTapInterval: 300
9228
9228
  });
@@ -9466,7 +9466,7 @@ var init_VirtualizedDocumentContainer = __esm({
9466
9466
  const pageCache = (0, import_react38.useRef)(/* @__PURE__ */ new Map());
9467
9467
  const pageDimensionsCache = (0, import_react38.useRef)(/* @__PURE__ */ new Map());
9468
9468
  const baseScaleRef = (0, import_react38.useRef)(scale);
9469
- const isTouchDevice2 = useIsTouchDevice();
9469
+ const isTouchDevice = useIsTouchDevice();
9470
9470
  const [visiblePages, setVisiblePages] = (0, import_react38.useState)([1]);
9471
9471
  const [pageObjects, setPageObjects] = (0, import_react38.useState)(/* @__PURE__ */ new Map());
9472
9472
  const [totalHeight, setTotalHeight] = (0, import_react38.useState)(0);
@@ -9669,7 +9669,7 @@ var init_VirtualizedDocumentContainer = __esm({
9669
9669
  onPinchZoom: handlePinchZoom,
9670
9670
  onSwipeLeft: nextPage,
9671
9671
  onSwipeRight: previousPage,
9672
- enabled: enableTouchGestures && isTouchDevice2
9672
+ enabled: enableTouchGestures && isTouchDevice
9673
9673
  });
9674
9674
  const setContainerRef = (0, import_react38.useCallback)(
9675
9675
  (element) => {
@@ -9877,7 +9877,7 @@ var init_DualPageContainer = __esm({
9877
9877
  const containerRef = (0, import_react40.useRef)(null);
9878
9878
  const documentRef = (0, import_react40.useRef)(null);
9879
9879
  const baseScaleRef = (0, import_react40.useRef)(scale);
9880
- const isTouchDevice2 = useIsTouchDevice();
9880
+ const isTouchDevice = useIsTouchDevice();
9881
9881
  const [leftPage, setLeftPage] = (0, import_react40.useState)(null);
9882
9882
  const [rightPage, setRightPage] = (0, import_react40.useState)(null);
9883
9883
  const [isLoading, setIsLoading] = (0, import_react40.useState)(false);
@@ -10020,7 +10020,7 @@ var init_DualPageContainer = __esm({
10020
10020
  onPinchZoom: handlePinchZoom,
10021
10021
  onSwipeLeft: goToNextSpread,
10022
10022
  onSwipeRight: goToPreviousSpread,
10023
- enabled: enableTouchGestures && isTouchDevice2
10023
+ enabled: enableTouchGestures && isTouchDevice
10024
10024
  });
10025
10025
  const setContainerRef = (0, import_react40.useCallback)(
10026
10026
  (element) => {
@@ -13268,7 +13268,7 @@ function withErrorBoundary({ component, ...props }) {
13268
13268
  init_PDFLoadingScreen2();
13269
13269
 
13270
13270
  // src/components/TutorMode/TutorModeContainer.tsx
13271
- var import_react58 = require("react");
13271
+ var import_react57 = require("react");
13272
13272
  var import_zustand2 = require("zustand");
13273
13273
  init_PDFPage2();
13274
13274
  init_hooks();
@@ -13559,13 +13559,13 @@ function AnimatedUnderline({ bbox, action }) {
13559
13559
  const blotX = x2 + 4;
13560
13560
  const blotY = y;
13561
13561
  const strokeWeight = action.style === "wavy" ? 3 : 4;
13562
- const xPad = 8;
13563
- const yAbove = 8;
13564
- const yBelow = 24;
13565
- const svgX = x1 - xPad;
13566
- const svgY = y - yAbove;
13567
- const svgW = x2 - x1 + 2 * xPad;
13568
- const svgH = yAbove + yBelow;
13562
+ const uxPad = 8;
13563
+ const uAbove = 8;
13564
+ const uBelow = 24;
13565
+ const svgX = x1 - uxPad;
13566
+ const svgY = y - uAbove;
13567
+ const svgW = x2 - x1 + 2 * uxPad;
13568
+ const svgH = uAbove + uBelow;
13569
13569
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
13570
13570
  "svg",
13571
13571
  {
@@ -13640,7 +13640,12 @@ function AnimatedUnderline({ bbox, action }) {
13640
13640
  // src/components/TutorMode/AnimatedHighlight.tsx
13641
13641
  var import_framer_motion4 = require("framer-motion");
13642
13642
  var import_jsx_runtime44 = require("react/jsx-runtime");
13643
- var WASH = "rgba(195, 145, 10, 0.32)";
13643
+ var DEFAULT_HUE = "rgb(230, 180, 34)";
13644
+ var WASH_OPACITY = 0.28;
13645
+ function stripAlpha(color) {
13646
+ const m = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
13647
+ return m ? `rgb(${m[1]}, ${m[2]}, ${m[3]})` : color;
13648
+ }
13644
13649
  function AnimatedHighlight({ bbox, action }) {
13645
13650
  const [x1, y1, x2, y2] = bbox;
13646
13651
  const h = Math.max(1, y2 - y1);
@@ -13649,7 +13654,7 @@ function AnimatedHighlight({ bbox, action }) {
13649
13654
  const yBot = y2 + bleed;
13650
13655
  const duration = action.draw_duration_ms / 1e3;
13651
13656
  const isDefaultColour = !action.color || action.color === "rgba(250, 204, 21, 0.35)" || action.color === "rgba(250,204,21,0.35)";
13652
- const fill = isDefaultColour ? WASH : action.color;
13657
+ const fill = stripAlpha(isDefaultColour ? DEFAULT_HUE : action.color);
13653
13658
  const taper = Math.min(6, h * 0.2);
13654
13659
  const pathD = `
13655
13660
  M ${x1 - 2} ${yTop + taper}
@@ -13662,11 +13667,11 @@ function AnimatedHighlight({ bbox, action }) {
13662
13667
  L ${x1 - 2} ${yBot - taper}
13663
13668
  Z
13664
13669
  `;
13665
- const pad = 8;
13666
- const svgX = x1 - pad;
13667
- const svgY = yTop - pad;
13668
- const svgW = x2 - x1 + 2 * pad;
13669
- const svgH = yBot - yTop + 2 * pad;
13670
+ const svgPad = 8;
13671
+ const svgX = x1 - svgPad;
13672
+ const svgY = yTop - svgPad;
13673
+ const svgW = x2 - x1 + 2 * svgPad;
13674
+ const svgH = yBot - yTop + 2 * svgPad;
13670
13675
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
13671
13676
  "svg",
13672
13677
  {
@@ -13686,6 +13691,7 @@ function AnimatedHighlight({ bbox, action }) {
13686
13691
  {
13687
13692
  d: pathD,
13688
13693
  fill,
13694
+ fillOpacity: WASH_OPACITY,
13689
13695
  initial: { clipPath: `inset(0 100% 0 0)` },
13690
13696
  animate: { clipPath: `inset(0 0% 0 0)` },
13691
13697
  exit: { opacity: 0 },
@@ -13697,7 +13703,6 @@ function AnimatedHighlight({ bbox, action }) {
13697
13703
  }
13698
13704
 
13699
13705
  // src/components/TutorMode/PulseOverlay.tsx
13700
- var import_react56 = require("react");
13701
13706
  var import_framer_motion5 = require("framer-motion");
13702
13707
  var import_jsx_runtime45 = require("react/jsx-runtime");
13703
13708
  var INTENSITY = {
@@ -13706,7 +13711,6 @@ var INTENSITY = {
13706
13711
  strong: { bracketLen: 26, strokeWeight: 3, coreOpacity: 1, ringScale: 1.22 }
13707
13712
  };
13708
13713
  function PulseOverlay({ bbox, action }) {
13709
- const pulseId = (0, import_react56.useId)();
13710
13714
  const [x1, y1, x2, y2] = bbox;
13711
13715
  const w = Math.max(1, x2 - x1);
13712
13716
  const h = Math.max(1, y2 - y1);
@@ -13715,13 +13719,13 @@ function PulseOverlay({ bbox, action }) {
13715
13719
  const spec = INTENSITY[action.intensity] ?? INTENSITY.normal;
13716
13720
  const L = Math.min(spec.bracketLen, Math.min(w, h) / 2.5);
13717
13721
  const PAD = 6;
13722
+ const ringExtentX = (w / 2 + PAD) * spec.ringScale - w / 2;
13723
+ const ringExtentY = (h / 2 + PAD) * spec.ringScale - h / 2;
13724
+ const glowExtentX = (w / 2 + 10) * spec.ringScale - w / 2 + 24;
13725
+ const glowExtentY = (h / 2 + 10) * spec.ringScale - h / 2 + 24;
13718
13726
  const bracketExtent = PAD + L + 8;
13719
- const glowExtent = (w / 2 + 24) * spec.ringScale - w / 2;
13720
- const ringExtent = (w / 2 + PAD) * spec.ringScale - w / 2;
13721
- const glowExtentV = (h / 2 + 24) * spec.ringScale - h / 2;
13722
- const ringExtentV = (h / 2 + PAD) * spec.ringScale - h / 2;
13723
- const svgPadX = Math.ceil(Math.max(bracketExtent, glowExtent, ringExtent) + 4);
13724
- const svgPadY = Math.ceil(Math.max(bracketExtent, glowExtentV, ringExtentV) + 4);
13727
+ const svgPadX = Math.ceil(Math.max(40, ringExtentX, glowExtentX, bracketExtent));
13728
+ const svgPadY = Math.ceil(Math.max(40, ringExtentY, glowExtentY, bracketExtent));
13725
13729
  const svgX = x1 - svgPadX;
13726
13730
  const svgY = y1 - svgPadY;
13727
13731
  const svgW = w + 2 * svgPadX;
@@ -13741,22 +13745,18 @@ function PulseOverlay({ bbox, action }) {
13741
13745
  },
13742
13746
  "data-role": "pulse",
13743
13747
  children: [
13744
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("radialGradient", { id: `${pulseId}-glow`, cx: "50%", cy: "50%", r: "50%", children: [
13745
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("stop", { offset: "0%", stopColor: ACCENT_GLOW, stopOpacity: 1 }),
13746
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("stop", { offset: "60%", stopColor: ACCENT_GLOW, stopOpacity: 0.45 }),
13747
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("stop", { offset: "100%", stopColor: ACCENT_GLOW, stopOpacity: 0 })
13748
- ] }) }),
13749
13748
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
13750
13749
  import_framer_motion5.motion.ellipse,
13751
13750
  {
13752
13751
  cx,
13753
13752
  cy,
13754
- rx: w / 2 + 24,
13755
- ry: h / 2 + 24,
13756
- fill: `url(#${pulseId}-glow)`,
13753
+ rx: w / 2 + 10,
13754
+ ry: h / 2 + 10,
13755
+ fill: ACCENT_GLOW,
13757
13756
  style: {
13758
13757
  transformOrigin: `${cx}px ${cy}px`,
13759
- transformBox: "fill-box"
13758
+ transformBox: "fill-box",
13759
+ filter: "blur(16px)"
13760
13760
  },
13761
13761
  initial: { opacity: 0, scale: 0.95 },
13762
13762
  animate: {
@@ -13887,7 +13887,7 @@ function Bracket({
13887
13887
  }
13888
13888
 
13889
13889
  // src/components/TutorMode/CalloutArrow.tsx
13890
- var import_react57 = require("react");
13890
+ var import_react56 = require("react");
13891
13891
  var import_framer_motion6 = require("framer-motion");
13892
13892
  var import_jsx_runtime46 = require("react/jsx-runtime");
13893
13893
  function centerOf(b) {
@@ -13925,7 +13925,7 @@ function arrowPath(from, to, curve) {
13925
13925
  return `M ${from.x} ${from.y} Q ${cx} ${cy} ${to.x} ${to.y}`;
13926
13926
  }
13927
13927
  function CalloutArrow({ fromBbox, toBbox, action }) {
13928
- const markerId = (0, import_react57.useId)();
13928
+ const markerId = (0, import_react56.useId)();
13929
13929
  const glowId = `${markerId}-glow`;
13930
13930
  const { from, to } = edgePoints(fromBbox, toBbox);
13931
13931
  const d = arrowPath(from, to, action.curve);
@@ -14098,9 +14098,8 @@ function CinemaLayer({
14098
14098
  page,
14099
14099
  index,
14100
14100
  overlays,
14101
- coordScale
14101
+ scale
14102
14102
  }) {
14103
- if (overlays.length === 0) return null;
14104
14103
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
14105
14104
  "div",
14106
14105
  {
@@ -14109,7 +14108,7 @@ function CinemaLayer({
14109
14108
  position: "absolute",
14110
14109
  inset: 0,
14111
14110
  transformOrigin: "0 0",
14112
- transform: `scale(${coordScale})`,
14111
+ transform: `scale(${scale})`,
14113
14112
  width: page.page_dimensions.width,
14114
14113
  height: page.page_dimensions.height,
14115
14114
  pointerEvents: "none",
@@ -14594,10 +14593,14 @@ function StickyLabel({ screenAnchor, action }) {
14594
14593
  letterSpacing: 0.6,
14595
14594
  textTransform: "uppercase",
14596
14595
  fontWeight: 500,
14597
- whiteSpace: "nowrap",
14596
+ // Wrap instead of truncating with an ellipsis. Short labels stay
14597
+ // single-line naturally; longer ones grow in height rather than
14598
+ // losing their tail. `overflowWrap: 'anywhere'` keeps a stray
14599
+ // long word from pushing the pill past `maxWidth` (normal word
14600
+ // breaking stops at whitespace and can't break inside words).
14598
14601
  maxWidth: PILL_MAX_W_BODY,
14599
- overflow: "hidden",
14600
- textOverflow: "ellipsis",
14602
+ whiteSpace: "normal",
14603
+ overflowWrap: "anywhere",
14601
14604
  // Warm two-layer shadow (matches GhostReference's palette).
14602
14605
  boxShadow: "0 1px 2px rgba(42, 36, 32, 0.12), 0 8px 18px -6px rgba(42, 36, 32, 0.22)",
14603
14606
  // Internal left accent rule — a 2px terracotta stripe.
@@ -14699,8 +14702,7 @@ function LabelOverlay({
14699
14702
  index,
14700
14703
  currentPage,
14701
14704
  camera,
14702
- viewport,
14703
- coordScale
14705
+ viewport
14704
14706
  }) {
14705
14707
  const labels = overlays.filter((o) => o.kind === "label");
14706
14708
  const page = index.byPage.get(currentPage);
@@ -14727,8 +14729,7 @@ function LabelOverlay({
14727
14729
  a.position,
14728
14730
  page,
14729
14731
  camera,
14730
- viewport,
14731
- coordScale
14732
+ viewport
14732
14733
  );
14733
14734
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
14734
14735
  StickyLabel,
@@ -14742,7 +14743,7 @@ function LabelOverlay({
14742
14743
  }
14743
14744
  );
14744
14745
  }
14745
- function computeScreenAnchor(bbox, where, page, camera, viewport, coordScale) {
14746
+ function computeScreenAnchor(bbox, where, page, camera, viewport) {
14746
14747
  const [x1, y1, x2, y2] = bbox;
14747
14748
  const pageCX = page.page_dimensions.width / 2;
14748
14749
  const pageCY = page.page_dimensions.height / 2;
@@ -14768,8 +14769,8 @@ function computeScreenAnchor(bbox, where, page, camera, viewport, coordScale) {
14768
14769
  px = (x1 + x2) / 2;
14769
14770
  py = y1;
14770
14771
  }
14771
- const screenX = viewport.width / 2 + camera.x + (px - pageCX) * coordScale * camera.scale;
14772
- const screenY = viewport.height / 2 + camera.y + (py - pageCY) * coordScale * camera.scale;
14772
+ const screenX = viewport.width / 2 + camera.x + (px - pageCX) * camera.scale;
14773
+ const screenY = viewport.height / 2 + camera.y + (py - pageCY) * camera.scale;
14773
14774
  return { x: screenX, y: screenY };
14774
14775
  }
14775
14776
 
@@ -14781,8 +14782,7 @@ function CalloutLabelOverlay({
14781
14782
  index,
14782
14783
  currentPage,
14783
14784
  camera,
14784
- viewport,
14785
- coordScale
14785
+ viewport
14786
14786
  }) {
14787
14787
  const callouts = overlays.filter(
14788
14788
  (o) => o.kind === "callout" && o.action.label
@@ -14811,8 +14811,7 @@ function CalloutLabelOverlay({
14811
14811
  toHit.block.bbox,
14812
14812
  page,
14813
14813
  camera,
14814
- viewport,
14815
- coordScale
14814
+ viewport
14816
14815
  );
14817
14816
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
14818
14817
  CalloutLabelPill,
@@ -14827,7 +14826,7 @@ function CalloutLabelOverlay({
14827
14826
  }
14828
14827
  );
14829
14828
  }
14830
- function computePillAnchor(fromBbox, toBbox, page, camera, viewport, coordScale) {
14829
+ function computePillAnchor(fromBbox, toBbox, page, camera, viewport) {
14831
14830
  const aCX = (fromBbox[0] + fromBbox[2]) / 2;
14832
14831
  const aCY = (fromBbox[1] + fromBbox[3]) / 2;
14833
14832
  const bCX = (toBbox[0] + toBbox[2]) / 2;
@@ -14844,8 +14843,8 @@ function computePillAnchor(fromBbox, toBbox, page, camera, viewport, coordScale)
14844
14843
  const toY = bCY - uy * bOff;
14845
14844
  const pageCX = page.page_dimensions.width / 2;
14846
14845
  const pageCY = page.page_dimensions.height / 2;
14847
- const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) * coordScale * camera.scale;
14848
- const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) * coordScale * camera.scale;
14846
+ const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) * camera.scale;
14847
+ const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) * camera.scale;
14849
14848
  const isVertical = Math.abs(dy) >= Math.abs(dx);
14850
14849
  const OFFSET = resolvePillOffset(viewport.width);
14851
14850
  const MAX_PILL_W = resolveMaxPillW(viewport.width);
@@ -14898,10 +14897,13 @@ function CalloutLabelPill({
14898
14897
  letterSpacing: 0.6,
14899
14898
  textTransform: "uppercase",
14900
14899
  fontWeight: 500,
14901
- whiteSpace: "nowrap",
14900
+ // Wrap instead of truncating. Short labels stay single-line;
14901
+ // longer ones grow taller rather than losing their tail to an
14902
+ // ellipsis. `overflowWrap: 'anywhere'` guards against a stray
14903
+ // long word pushing the pill past `maxWidth`.
14902
14904
  maxWidth: PILL_MAX_W_CAPS,
14903
- overflow: "hidden",
14904
- textOverflow: "ellipsis",
14905
+ whiteSpace: "normal",
14906
+ overflowWrap: "anywhere",
14905
14907
  boxShadow: "0 1px 2px rgba(42, 36, 32, 0.12), 0 8px 18px -6px rgba(42, 36, 32, 0.22)",
14906
14908
  // Accent rule on the "inward" edge (the one closest to the arrow).
14907
14909
  backgroundImage: spec.accentGradient,
@@ -14988,41 +14990,6 @@ function SubtitleBar({ text }) {
14988
14990
  ) : null });
14989
14991
  }
14990
14992
 
14991
- // src/utils/render-scale.ts
14992
- function computeFitScale(input) {
14993
- const { pageWidthPt, pageHeightPt, viewport, paddingFactor = 0.95 } = input;
14994
- if (!Number.isFinite(pageWidthPt) || !Number.isFinite(pageHeightPt) || pageWidthPt <= 0 || pageHeightPt <= 0) {
14995
- return 1;
14996
- }
14997
- const w = Math.max(1, viewport.width);
14998
- const h = Math.max(1, viewport.height);
14999
- const fit = Math.min(w / pageWidthPt, h / pageHeightPt) * paddingFactor;
15000
- return Number.isFinite(fit) && fit > 0 ? fit : 1;
15001
- }
15002
- function computeCoordScale(input) {
15003
- const { renderScale, dpi } = input;
15004
- if (!Number.isFinite(renderScale) || renderScale <= 0) return 0;
15005
- if (!Number.isFinite(dpi) || dpi <= 0) return 1;
15006
- return renderScale * 72 / dpi;
15007
- }
15008
- function isTouchDevice() {
15009
- if (typeof window === "undefined") return false;
15010
- if (typeof window.matchMedia !== "function") return false;
15011
- try {
15012
- return window.matchMedia("(pointer: coarse)").matches;
15013
- } catch {
15014
- return false;
15015
- }
15016
- }
15017
- function resolveRenderScale(input) {
15018
- const { renderScaleProp, scaleProp, defaultRenderScale } = input;
15019
- if (Number.isFinite(renderScaleProp) && renderScaleProp > 0) {
15020
- return renderScaleProp;
15021
- }
15022
- const multiplier = Number.isFinite(scaleProp) && scaleProp > 0 ? scaleProp : 1;
15023
- return defaultRenderScale * multiplier;
15024
- }
15025
-
15026
14993
  // src/director/storyboard-engine.ts
15027
14994
  init_narration_store();
15028
14995
  init_camera_math();
@@ -15119,14 +15086,18 @@ var StoryboardEngine = class {
15119
15086
  this.overlayRemovalTimers.clear();
15120
15087
  }
15121
15088
  /**
15122
- * Full destructor — cancels both pending step timers AND overlay removal
15123
- * timers. Use this in component unmount/cleanup. The per-storyboard
15124
- * `cancelPending()` deliberately leaves overlay removal timers alone so a
15125
- * mid-flight overlay doesn't get stranded (see `overlayRemovalTimers` doc),
15126
- * but at destruction time we must release every timer or their closures
15089
+ * Full destructor — cancels BOTH pending step timers AND overlay
15090
+ * removal timers. Use this in component unmount / cleanup.
15091
+ *
15092
+ * The per-storyboard `cancelPending()` deliberately leaves overlay
15093
+ * removal timers alone so a mid-flight overlay doesn't get stranded
15094
+ * when a new storyboard arrives (see `overlayRemovalTimers` doc).
15095
+ * That invariant is correct inside a session, but at teardown we
15096
+ * must release every timer — otherwise their setTimeout closures
15127
15097
  * keep `deps` (narrationStore, the full bboxIndex) alive beyond the
15128
- * lifetime of this engine. On iOS Safari with many engine recreations
15129
- * this causes cumulative memory pressure and eventual tab reload.
15098
+ * lifetime of this engine. Over many component recreations on iOS
15099
+ * Safari (viewport state churns during address-bar scroll
15100
+ * animation), that cumulative retention contributes to tab reloads.
15130
15101
  */
15131
15102
  destroy() {
15132
15103
  this.cancelPending();
@@ -16279,70 +16250,42 @@ function TutorModeContainer({
16279
16250
  loadingComponent,
16280
16251
  onPageChange,
16281
16252
  storyboardProvider,
16282
- renderScale,
16283
16253
  className
16284
16254
  }) {
16285
- const containerRef = (0, import_react58.useRef)(null);
16286
- const index = (0, import_react58.useMemo)(() => buildBBoxIndex(bboxData), [bboxData]);
16255
+ const containerRef = (0, import_react57.useRef)(null);
16256
+ const index = (0, import_react57.useMemo)(() => buildBBoxIndex(bboxData), [bboxData]);
16287
16257
  const {
16288
16258
  document: document2,
16289
16259
  currentPage: viewerCurrentPage,
16290
16260
  numPages,
16291
16261
  goToPage: viewerGoToPage
16292
16262
  } = usePDFViewer();
16293
- const [pageProxy, setPageProxy] = (0, import_react58.useState)(null);
16294
- const [viewport, setViewport] = (0, import_react58.useState)({ width: 800, height: 1e3 });
16263
+ const [pageProxy, setPageProxy] = (0, import_react57.useState)(null);
16264
+ const [viewport, setViewport] = (0, import_react57.useState)({ width: 800, height: 1e3 });
16295
16265
  const camera = (0, import_zustand2.useStore)(narrationStore, (s) => s.camera);
16296
16266
  const activeOverlays = (0, import_zustand2.useStore)(narrationStore, (s) => s.activeOverlays);
16297
- const page = index.byPage.get(pageNumber);
16298
- const pagePointsW = page ? page.page_dimensions.width * 72 / page.page_dimensions.dpi : 0;
16299
- const pagePointsH = page ? page.page_dimensions.height * 72 / page.page_dimensions.dpi : 0;
16300
- const touch = isTouchDevice();
16301
- const defaultRenderScale = page ? touch ? computeFitScale({
16302
- pageWidthPt: pagePointsW,
16303
- pageHeightPt: pagePointsH,
16304
- viewport
16305
- }) : page.page_dimensions.dpi / 72 : 1;
16306
- const effectiveRenderScale = resolveRenderScale({
16307
- renderScaleProp: renderScale,
16308
- scaleProp: scale,
16309
- defaultRenderScale
16310
- });
16311
- const coordScale = page ? computeCoordScale({
16312
- renderScale: effectiveRenderScale,
16313
- dpi: page.page_dimensions.dpi
16314
- }) : 1;
16315
- const rasterScale = effectiveRenderScale;
16316
- const baseW = pagePointsW * effectiveRenderScale;
16317
- const baseH = pagePointsH * effectiveRenderScale;
16318
- (0, import_react58.useEffect)(() => {
16267
+ (0, import_react57.useEffect)(() => {
16319
16268
  if (numPages <= 0) return;
16320
16269
  if (pageNumber < 1 || pageNumber > numPages) return;
16321
16270
  if (viewerCurrentPage === pageNumber) return;
16322
16271
  viewerGoToPage(pageNumber);
16323
16272
  }, [pageNumber, numPages, viewerCurrentPage, viewerGoToPage]);
16324
- (0, import_react58.useEffect)(() => {
16273
+ (0, import_react57.useEffect)(() => {
16325
16274
  if (!onPageChange) return;
16326
16275
  if (viewerCurrentPage === pageNumber) return;
16327
16276
  if (viewerCurrentPage < 1) return;
16328
16277
  onPageChange(viewerCurrentPage);
16329
16278
  }, [viewerCurrentPage, pageNumber, onPageChange]);
16330
- (0, import_react58.useEffect)(() => {
16279
+ (0, import_react57.useEffect)(() => {
16331
16280
  if (!containerRef.current) return;
16332
16281
  const el = containerRef.current;
16333
- const update = () => {
16334
- const w = el.clientWidth;
16335
- const h = el.clientHeight;
16336
- setViewport(
16337
- (prev) => prev.width === w && prev.height === h ? prev : { width: w, height: h }
16338
- );
16339
- };
16282
+ const update = () => setViewport({ width: el.clientWidth, height: el.clientHeight });
16340
16283
  update();
16341
16284
  const ro = new ResizeObserver(update);
16342
16285
  ro.observe(el);
16343
16286
  return () => ro.disconnect();
16344
16287
  }, []);
16345
- (0, import_react58.useEffect)(() => {
16288
+ (0, import_react57.useEffect)(() => {
16346
16289
  if (!document2) {
16347
16290
  setPageProxy(null);
16348
16291
  return;
@@ -16357,40 +16300,34 @@ function TutorModeContainer({
16357
16300
  cancelled = true;
16358
16301
  };
16359
16302
  }, [document2, pageNumber]);
16360
- (0, import_react58.useEffect)(() => {
16303
+ (0, import_react57.useEffect)(() => {
16361
16304
  narrationStore.getState().setCurrentPage(pageNumber);
16362
16305
  }, [pageNumber, narrationStore]);
16363
- (0, import_react58.useEffect)(() => {
16306
+ (0, import_react57.useEffect)(() => {
16364
16307
  const page2 = index.byPage.get(pageNumber);
16365
16308
  if (!page2) return;
16366
16309
  if (viewport.width === 0 || viewport.height === 0) return;
16367
- if (baseW === 0 || baseH === 0) return;
16368
16310
  if (narrationStore.getState().activeOverlays.length > 0) return;
16369
- const fit = Math.min(viewport.width / baseW, viewport.height / baseH) * 0.95;
16311
+ const fit = Math.min(
16312
+ viewport.width / page2.page_dimensions.width,
16313
+ viewport.height / page2.page_dimensions.height
16314
+ ) * 0.95;
16370
16315
  narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0 });
16371
- }, [pageNumber, viewport, index, narrationStore, baseW, baseH]);
16372
- const viewportRef = (0, import_react58.useRef)(viewport);
16373
- (0, import_react58.useEffect)(() => {
16374
- viewportRef.current = viewport;
16375
- }, [viewport]);
16376
- const engineRef = (0, import_react58.useRef)(null);
16377
- (0, import_react58.useEffect)(() => {
16378
- const engine = new StoryboardEngine({
16316
+ }, [pageNumber, viewport, index, narrationStore]);
16317
+ const engineRef = (0, import_react57.useRef)(null);
16318
+ (0, import_react57.useEffect)(() => {
16319
+ engineRef.current = new StoryboardEngine({
16379
16320
  narrationStore,
16380
16321
  bboxIndex: index,
16381
- getViewport: () => viewportRef.current,
16322
+ getViewport: () => viewport,
16382
16323
  minOverlayDurationMs
16383
16324
  });
16384
- engineRef.current = engine;
16385
- return () => {
16386
- engine.destroy();
16387
- if (engineRef.current === engine) engineRef.current = null;
16388
- };
16389
- }, [narrationStore, index, minOverlayDurationMs]);
16390
- const abortRef = (0, import_react58.useRef)(null);
16391
- const debounceRef = (0, import_react58.useRef)(null);
16392
- const lastChunkRef = (0, import_react58.useRef)(null);
16393
- (0, import_react58.useEffect)(() => {
16325
+ return () => engineRef.current?.destroy();
16326
+ }, [narrationStore, index, viewport, minOverlayDurationMs]);
16327
+ const abortRef = (0, import_react57.useRef)(null);
16328
+ const debounceRef = (0, import_react57.useRef)(null);
16329
+ const lastChunkRef = (0, import_react57.useRef)(null);
16330
+ (0, import_react57.useEffect)(() => {
16394
16331
  if (!storyboardProvider && !llm) return;
16395
16332
  if (!currentChunk || currentChunk === lastChunkRef.current) return;
16396
16333
  if (debounceRef.current) clearTimeout(debounceRef.current);
@@ -16555,7 +16492,7 @@ function TutorModeContainer({
16555
16492
  embeddingProvider,
16556
16493
  llmTimeoutMs
16557
16494
  ]);
16558
- (0, import_react58.useEffect)(() => {
16495
+ (0, import_react57.useEffect)(() => {
16559
16496
  if (!currentChunk) return;
16560
16497
  const t = setTimeout(() => {
16561
16498
  if (!engineRef.current) return;
@@ -16567,6 +16504,11 @@ function TutorModeContainer({
16567
16504
  }, idleTimeoutMs + 100);
16568
16505
  return () => clearTimeout(t);
16569
16506
  }, [currentChunk, idleTimeoutMs, narrationStore]);
16507
+ const page = index.byPage.get(pageNumber);
16508
+ const dpiScale = page ? page.page_dimensions.dpi / 72 : 1;
16509
+ const rasterScale = dpiScale * (scale || 1);
16510
+ const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
16511
+ const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
16570
16512
  const isReady = !!page && !!pageProxy;
16571
16513
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
16572
16514
  "div",
@@ -16645,7 +16587,7 @@ function TutorModeContainer({
16645
16587
  page,
16646
16588
  index,
16647
16589
  overlays: activeOverlays,
16648
- coordScale
16590
+ scale: scale || 1
16649
16591
  }
16650
16592
  )
16651
16593
  ]
@@ -16658,8 +16600,7 @@ function TutorModeContainer({
16658
16600
  index,
16659
16601
  currentPage: pageNumber,
16660
16602
  camera,
16661
- viewport,
16662
- coordScale
16603
+ viewport
16663
16604
  }
16664
16605
  ),
16665
16606
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
@@ -16669,8 +16610,7 @@ function TutorModeContainer({
16669
16610
  index,
16670
16611
  currentPage: pageNumber,
16671
16612
  camera,
16672
- viewport,
16673
- coordScale
16613
+ viewport
16674
16614
  }
16675
16615
  ),
16676
16616
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(GhostReferenceOverlay, { overlays: activeOverlays, index })