pdfjs-reader-core 0.5.6 → 0.5.8

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.d.cts CHANGED
@@ -2062,8 +2062,28 @@ declare class StoryboardEngine {
2062
2062
  * `resetVisuals()` instead.
2063
2063
  */
2064
2064
  cancelPending(): void;
2065
- /** Cancel every removal timer (used by resetVisuals only). */
2065
+ /** Cancel every removal timer (used by resetVisuals and destroy). */
2066
2066
  private cancelAllRemovalTimers;
2067
+ /**
2068
+ * Full destructor — cancels BOTH pending step timers AND overlay
2069
+ * removal timers. Use this in component unmount / cleanup.
2070
+ *
2071
+ * The per-storyboard `cancelPending()` deliberately leaves overlay
2072
+ * removal timers alone so a mid-flight overlay doesn't get stranded
2073
+ * when a new storyboard arrives (see `overlayRemovalTimers` doc).
2074
+ * That invariant is correct inside a session, but at teardown we
2075
+ * must release every timer — otherwise their setTimeout closures
2076
+ * keep `deps` (narrationStore, the full bboxIndex) alive beyond the
2077
+ * lifetime of this engine. Over many component recreations on iOS
2078
+ * Safari (viewport state churns during address-bar scroll
2079
+ * animation), that cumulative retention contributes to tab reloads.
2080
+ */
2081
+ destroy(): void;
2082
+ /** Test-only — exposes internal queue sizes for regression tests. */
2083
+ _queueSizesForTest(): {
2084
+ pending: number;
2085
+ removals: number;
2086
+ };
2067
2087
  /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
2068
2088
  resetVisuals(): void;
2069
2089
  /** Execute one step — dispatch to narrationStore. Returns true if applied. */
package/dist/index.d.ts CHANGED
@@ -2062,8 +2062,28 @@ declare class StoryboardEngine {
2062
2062
  * `resetVisuals()` instead.
2063
2063
  */
2064
2064
  cancelPending(): void;
2065
- /** Cancel every removal timer (used by resetVisuals only). */
2065
+ /** Cancel every removal timer (used by resetVisuals and destroy). */
2066
2066
  private cancelAllRemovalTimers;
2067
+ /**
2068
+ * Full destructor — cancels BOTH pending step timers AND overlay
2069
+ * removal timers. Use this in component unmount / cleanup.
2070
+ *
2071
+ * The per-storyboard `cancelPending()` deliberately leaves overlay
2072
+ * removal timers alone so a mid-flight overlay doesn't get stranded
2073
+ * when a new storyboard arrives (see `overlayRemovalTimers` doc).
2074
+ * That invariant is correct inside a session, but at teardown we
2075
+ * must release every timer — otherwise their setTimeout closures
2076
+ * keep `deps` (narrationStore, the full bboxIndex) alive beyond the
2077
+ * lifetime of this engine. Over many component recreations on iOS
2078
+ * Safari (viewport state churns during address-bar scroll
2079
+ * animation), that cumulative retention contributes to tab reloads.
2080
+ */
2081
+ destroy(): void;
2082
+ /** Test-only — exposes internal queue sizes for regression tests. */
2083
+ _queueSizesForTest(): {
2084
+ pending: number;
2085
+ removals: number;
2086
+ };
2067
2087
  /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
2068
2088
  resetVisuals(): void;
2069
2089
  /** Execute one step — dispatch to narrationStore. Returns true if applied. */
package/dist/index.js CHANGED
@@ -13374,12 +13374,23 @@ function AnimatedUnderline({ bbox, action }) {
13374
13374
  const blotX = x2 + 4;
13375
13375
  const blotY = y;
13376
13376
  const strokeWeight = action.style === "wavy" ? 3 : 4;
13377
+ const uxPad = 8;
13378
+ const uAbove = 8;
13379
+ const uBelow = 24;
13380
+ const svgX = x1 - uxPad;
13381
+ const svgY = y - uAbove;
13382
+ const svgW = x2 - x1 + 2 * uxPad;
13383
+ const svgH = uAbove + uBelow;
13377
13384
  return /* @__PURE__ */ jsxs36(
13378
13385
  "svg",
13379
13386
  {
13387
+ width: svgW,
13388
+ height: svgH,
13389
+ viewBox: `${svgX} ${svgY} ${svgW} ${svgH}`,
13380
13390
  style: {
13381
13391
  position: "absolute",
13382
- inset: 0,
13392
+ left: svgX,
13393
+ top: svgY,
13383
13394
  pointerEvents: "none",
13384
13395
  overflow: "visible"
13385
13396
  },
@@ -13442,10 +13453,14 @@ function AnimatedUnderline({ bbox, action }) {
13442
13453
  }
13443
13454
 
13444
13455
  // src/components/TutorMode/AnimatedHighlight.tsx
13445
- import { useId as useId2 } from "react";
13446
13456
  import { motion as motion4 } from "framer-motion";
13447
- import { jsx as jsx44, jsxs as jsxs37 } from "react/jsx-runtime";
13448
- var WASH = "rgba(230, 180, 34, 0.22)";
13457
+ import { jsx as jsx44 } from "react/jsx-runtime";
13458
+ var DEFAULT_HUE = "rgb(230, 180, 34)";
13459
+ var WASH_OPACITY = 0.28;
13460
+ function stripAlpha(color) {
13461
+ const m = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
13462
+ return m ? `rgb(${m[1]}, ${m[2]}, ${m[3]})` : color;
13463
+ }
13449
13464
  function AnimatedHighlight({ bbox, action }) {
13450
13465
  const [x1, y1, x2, y2] = bbox;
13451
13466
  const h = Math.max(1, y2 - y1);
@@ -13453,9 +13468,8 @@ function AnimatedHighlight({ bbox, action }) {
13453
13468
  const yTop = y1 - bleed;
13454
13469
  const yBot = y2 + bleed;
13455
13470
  const duration = action.draw_duration_ms / 1e3;
13456
- const filterId = useId2();
13457
13471
  const isDefaultColour = !action.color || action.color === "rgba(250, 204, 21, 0.35)" || action.color === "rgba(250,204,21,0.35)";
13458
- const fill = isDefaultColour ? WASH : action.color;
13472
+ const fill = stripAlpha(isDefaultColour ? DEFAULT_HUE : action.color);
13459
13473
  const taper = Math.min(6, h * 0.2);
13460
13474
  const pathD = `
13461
13475
  M ${x1 - 2} ${yTop + taper}
@@ -13468,51 +13482,44 @@ function AnimatedHighlight({ bbox, action }) {
13468
13482
  L ${x1 - 2} ${yBot - taper}
13469
13483
  Z
13470
13484
  `;
13471
- return /* @__PURE__ */ jsxs37(
13485
+ const svgPad = 8;
13486
+ const svgX = x1 - svgPad;
13487
+ const svgY = yTop - svgPad;
13488
+ const svgW = x2 - x1 + 2 * svgPad;
13489
+ const svgH = yBot - yTop + 2 * svgPad;
13490
+ return /* @__PURE__ */ jsx44(
13472
13491
  "svg",
13473
13492
  {
13493
+ width: svgW,
13494
+ height: svgH,
13495
+ viewBox: `${svgX} ${svgY} ${svgW} ${svgH}`,
13474
13496
  style: {
13475
13497
  position: "absolute",
13476
- inset: 0,
13498
+ left: svgX,
13499
+ top: svgY,
13477
13500
  pointerEvents: "none",
13478
- overflow: "visible",
13479
- mixBlendMode: "multiply"
13501
+ overflow: "visible"
13480
13502
  },
13481
13503
  "data-role": "highlight",
13482
- children: [
13483
- /* @__PURE__ */ jsx44("defs", { children: /* @__PURE__ */ jsxs37("filter", { id: filterId, children: [
13484
- /* @__PURE__ */ jsx44(
13485
- "feTurbulence",
13486
- {
13487
- type: "fractalNoise",
13488
- baseFrequency: "1.8",
13489
- numOctaves: "1",
13490
- seed: 3,
13491
- result: "noise"
13492
- }
13493
- ),
13494
- /* @__PURE__ */ jsx44("feDisplacementMap", { in: "SourceGraphic", in2: "noise", scale: 1 })
13495
- ] }) }),
13496
- /* @__PURE__ */ jsx44(
13497
- motion4.path,
13498
- {
13499
- d: pathD,
13500
- fill,
13501
- initial: { clipPath: `inset(0 100% 0 0)` },
13502
- animate: { clipPath: `inset(0 0% 0 0)` },
13503
- exit: { opacity: 0 },
13504
- filter: `url(#${filterId})`,
13505
- transition: { duration, ease: EASE_OUT_EXPO }
13506
- }
13507
- )
13508
- ]
13504
+ children: /* @__PURE__ */ jsx44(
13505
+ motion4.path,
13506
+ {
13507
+ d: pathD,
13508
+ fill,
13509
+ fillOpacity: WASH_OPACITY,
13510
+ initial: { clipPath: `inset(0 100% 0 0)` },
13511
+ animate: { clipPath: `inset(0 0% 0 0)` },
13512
+ exit: { opacity: 0 },
13513
+ transition: { duration, ease: EASE_OUT_EXPO }
13514
+ }
13515
+ )
13509
13516
  }
13510
13517
  );
13511
13518
  }
13512
13519
 
13513
13520
  // src/components/TutorMode/PulseOverlay.tsx
13514
13521
  import { motion as motion5 } from "framer-motion";
13515
- import { jsx as jsx45, jsxs as jsxs38 } from "react/jsx-runtime";
13522
+ import { jsx as jsx45, jsxs as jsxs37 } from "react/jsx-runtime";
13516
13523
  var INTENSITY = {
13517
13524
  subtle: { bracketLen: 14, strokeWeight: 2, coreOpacity: 0.5, ringScale: 1.08 },
13518
13525
  normal: { bracketLen: 20, strokeWeight: 2.5, coreOpacity: 0.75, ringScale: 1.14 },
@@ -13527,12 +13534,27 @@ function PulseOverlay({ bbox, action }) {
13527
13534
  const spec = INTENSITY[action.intensity] ?? INTENSITY.normal;
13528
13535
  const L = Math.min(spec.bracketLen, Math.min(w, h) / 2.5);
13529
13536
  const PAD = 6;
13530
- return /* @__PURE__ */ jsxs38(
13537
+ const ringExtentX = (w / 2 + PAD) * spec.ringScale - w / 2;
13538
+ const ringExtentY = (h / 2 + PAD) * spec.ringScale - h / 2;
13539
+ const glowExtentX = (w / 2 + 10) * spec.ringScale - w / 2 + 24;
13540
+ const glowExtentY = (h / 2 + 10) * spec.ringScale - h / 2 + 24;
13541
+ const bracketExtent = PAD + L + 8;
13542
+ const svgPadX = Math.ceil(Math.max(40, ringExtentX, glowExtentX, bracketExtent));
13543
+ const svgPadY = Math.ceil(Math.max(40, ringExtentY, glowExtentY, bracketExtent));
13544
+ const svgX = x1 - svgPadX;
13545
+ const svgY = y1 - svgPadY;
13546
+ const svgW = w + 2 * svgPadX;
13547
+ const svgH = h + 2 * svgPadY;
13548
+ return /* @__PURE__ */ jsxs37(
13531
13549
  "svg",
13532
13550
  {
13551
+ width: svgW,
13552
+ height: svgH,
13553
+ viewBox: `${svgX} ${svgY} ${svgW} ${svgH}`,
13533
13554
  style: {
13534
13555
  position: "absolute",
13535
- inset: 0,
13556
+ left: svgX,
13557
+ top: svgY,
13536
13558
  pointerEvents: "none",
13537
13559
  overflow: "visible"
13538
13560
  },
@@ -13680,9 +13702,9 @@ function Bracket({
13680
13702
  }
13681
13703
 
13682
13704
  // src/components/TutorMode/CalloutArrow.tsx
13683
- import { useId as useId3 } from "react";
13705
+ import { useId as useId2 } from "react";
13684
13706
  import { motion as motion6 } from "framer-motion";
13685
- import { jsx as jsx46, jsxs as jsxs39 } from "react/jsx-runtime";
13707
+ import { jsx as jsx46, jsxs as jsxs38 } from "react/jsx-runtime";
13686
13708
  function centerOf(b) {
13687
13709
  return { x: (b[0] + b[2]) / 2, y: (b[1] + b[3]) / 2 };
13688
13710
  }
@@ -13718,22 +13740,37 @@ function arrowPath(from, to, curve) {
13718
13740
  return `M ${from.x} ${from.y} Q ${cx} ${cy} ${to.x} ${to.y}`;
13719
13741
  }
13720
13742
  function CalloutArrow({ fromBbox, toBbox, action }) {
13721
- const markerId = useId3();
13743
+ const markerId = useId2();
13722
13744
  const glowId = `${markerId}-glow`;
13723
13745
  const { from, to } = edgePoints(fromBbox, toBbox);
13724
13746
  const d = arrowPath(from, to, action.curve);
13725
- return /* @__PURE__ */ jsxs39(
13747
+ const rawMinX = Math.min(from.x, to.x);
13748
+ const rawMinY = Math.min(from.y, to.y);
13749
+ const rawMaxX = Math.max(from.x, to.x);
13750
+ const rawMaxY = Math.max(from.y, to.y);
13751
+ const dist = Math.hypot(to.x - from.x, to.y - from.y);
13752
+ const arcDev = action.curve === "curved" ? dist * 0.12 : 0;
13753
+ const basePad = 16;
13754
+ const svgX = rawMinX - basePad - arcDev;
13755
+ const svgY = rawMinY - basePad - arcDev;
13756
+ const svgW = rawMaxX - rawMinX + 2 * (basePad + arcDev);
13757
+ const svgH = rawMaxY - rawMinY + 2 * (basePad + arcDev);
13758
+ return /* @__PURE__ */ jsxs38(
13726
13759
  "svg",
13727
13760
  {
13761
+ width: svgW,
13762
+ height: svgH,
13763
+ viewBox: `${svgX} ${svgY} ${svgW} ${svgH}`,
13728
13764
  style: {
13729
13765
  position: "absolute",
13730
- inset: 0,
13766
+ left: svgX,
13767
+ top: svgY,
13731
13768
  pointerEvents: "none",
13732
13769
  overflow: "visible"
13733
13770
  },
13734
13771
  "data-role": "callout",
13735
13772
  children: [
13736
- /* @__PURE__ */ jsxs39("defs", { children: [
13773
+ /* @__PURE__ */ jsxs38("defs", { children: [
13737
13774
  /* @__PURE__ */ jsx46(
13738
13775
  "marker",
13739
13776
  {
@@ -13971,7 +14008,7 @@ import { AnimatePresence as AnimatePresence2 } from "framer-motion";
13971
14008
 
13972
14009
  // src/components/TutorMode/GhostReference.tsx
13973
14010
  import { motion as motion8 } from "framer-motion";
13974
- import { jsx as jsx49, jsxs as jsxs40 } from "react/jsx-runtime";
14011
+ import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
13975
14012
  var POSITIONS = {
13976
14013
  "top-right": {
13977
14014
  top: "clamp(12px, 4vw, 40px)",
@@ -14004,7 +14041,7 @@ function GhostReference({
14004
14041
  }) {
14005
14042
  const [x1, y1, x2, y2] = sourceBbox;
14006
14043
  const text = sourceBlockText ?? "(figure)";
14007
- return /* @__PURE__ */ jsxs40(
14044
+ return /* @__PURE__ */ jsxs39(
14008
14045
  motion8.div,
14009
14046
  {
14010
14047
  initial: { opacity: 0, y: 24, scale: 0.97 },
@@ -14067,7 +14104,7 @@ function GhostReference({
14067
14104
  }
14068
14105
  }
14069
14106
  ),
14070
- /* @__PURE__ */ jsxs40(
14107
+ /* @__PURE__ */ jsxs39(
14071
14108
  "div",
14072
14109
  {
14073
14110
  style: {
@@ -14097,7 +14134,7 @@ function GhostReference({
14097
14134
  overflow: "hidden",
14098
14135
  boxShadow: "inset 0 0 0 1px rgba(42, 36, 32, 0.12), 0 1px 3px rgba(42, 36, 32, 0.10)"
14099
14136
  },
14100
- children: /* @__PURE__ */ jsxs40(
14137
+ children: /* @__PURE__ */ jsxs39(
14101
14138
  "svg",
14102
14139
  {
14103
14140
  width: "100%",
@@ -14144,7 +14181,7 @@ function GhostReference({
14144
14181
  )
14145
14182
  }
14146
14183
  ),
14147
- /* @__PURE__ */ jsxs40(
14184
+ /* @__PURE__ */ jsxs39(
14148
14185
  motion8.div,
14149
14186
  {
14150
14187
  initial: { opacity: 0, y: 6 },
@@ -14286,12 +14323,12 @@ import { AnimatePresence as AnimatePresence3 } from "framer-motion";
14286
14323
 
14287
14324
  // src/components/TutorMode/StickyLabel.tsx
14288
14325
  import { motion as motion9 } from "framer-motion";
14289
- import { jsx as jsx51, jsxs as jsxs41 } from "react/jsx-runtime";
14326
+ import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
14290
14327
  var STEM = 18;
14291
14328
  function StickyLabel({ screenAnchor, action }) {
14292
14329
  const { x, y } = screenAnchor;
14293
14330
  const layout = LAYOUTS[action.position] ?? LAYOUTS.top;
14294
- return /* @__PURE__ */ jsxs41(
14331
+ return /* @__PURE__ */ jsxs40(
14295
14332
  motion9.div,
14296
14333
  {
14297
14334
  initial: { opacity: 0, scale: 0.88 },
@@ -14851,11 +14888,36 @@ var StoryboardEngine = class {
14851
14888
  this.pendingStepTimers.clear();
14852
14889
  this.deps.narrationStore.getState().setEngineStatus("idle");
14853
14890
  }
14854
- /** Cancel every removal timer (used by resetVisuals only). */
14891
+ /** Cancel every removal timer (used by resetVisuals and destroy). */
14855
14892
  cancelAllRemovalTimers() {
14856
14893
  for (const t of this.overlayRemovalTimers.values()) clearTimeout(t);
14857
14894
  this.overlayRemovalTimers.clear();
14858
14895
  }
14896
+ /**
14897
+ * Full destructor — cancels BOTH pending step timers AND overlay
14898
+ * removal timers. Use this in component unmount / cleanup.
14899
+ *
14900
+ * The per-storyboard `cancelPending()` deliberately leaves overlay
14901
+ * removal timers alone so a mid-flight overlay doesn't get stranded
14902
+ * when a new storyboard arrives (see `overlayRemovalTimers` doc).
14903
+ * That invariant is correct inside a session, but at teardown we
14904
+ * must release every timer — otherwise their setTimeout closures
14905
+ * keep `deps` (narrationStore, the full bboxIndex) alive beyond the
14906
+ * lifetime of this engine. Over many component recreations on iOS
14907
+ * Safari (viewport state churns during address-bar scroll
14908
+ * animation), that cumulative retention contributes to tab reloads.
14909
+ */
14910
+ destroy() {
14911
+ this.cancelPending();
14912
+ this.cancelAllRemovalTimers();
14913
+ }
14914
+ /** Test-only — exposes internal queue sizes for regression tests. */
14915
+ _queueSizesForTest() {
14916
+ return {
14917
+ pending: this.pendingStepTimers.size,
14918
+ removals: this.overlayRemovalTimers.size
14919
+ };
14920
+ }
14859
14921
  /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
14860
14922
  resetVisuals() {
14861
14923
  this.cancelPending();
@@ -15956,7 +16018,7 @@ function storyboardFromMatch(match, page) {
15956
16018
  }
15957
16019
 
15958
16020
  // src/components/TutorMode/TutorModeContainer.tsx
15959
- import { Fragment as Fragment4, jsx as jsx55, jsxs as jsxs42 } from "react/jsx-runtime";
16021
+ import { Fragment as Fragment4, jsx as jsx55, jsxs as jsxs41 } from "react/jsx-runtime";
15960
16022
  function buildBBoxIndex(bboxData) {
15961
16023
  const byPage = /* @__PURE__ */ new Map();
15962
16024
  const blockById = /* @__PURE__ */ new Map();
@@ -16068,7 +16130,7 @@ function TutorModeContainer({
16068
16130
  getViewport: () => viewport,
16069
16131
  minOverlayDurationMs
16070
16132
  });
16071
- return () => engineRef.current?.cancelPending();
16133
+ return () => engineRef.current?.destroy();
16072
16134
  }, [narrationStore, index, viewport, minOverlayDurationMs]);
16073
16135
  const abortRef = useRef27(null);
16074
16136
  const debounceRef = useRef27(null);
@@ -16256,7 +16318,7 @@ function TutorModeContainer({
16256
16318
  const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
16257
16319
  const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
16258
16320
  const isReady = !!page && !!pageProxy;
16259
- return /* @__PURE__ */ jsxs42(
16321
+ return /* @__PURE__ */ jsxs41(
16260
16322
  "div",
16261
16323
  {
16262
16324
  ref: containerRef,
@@ -16302,8 +16364,8 @@ function TutorModeContainer({
16302
16364
  children: "Reset view"
16303
16365
  }
16304
16366
  ) : null,
16305
- isReady ? /* @__PURE__ */ jsxs42(Fragment4, { children: [
16306
- /* @__PURE__ */ jsx55(CameraView, { camera, children: /* @__PURE__ */ jsxs42(
16367
+ isReady ? /* @__PURE__ */ jsxs41(Fragment4, { children: [
16368
+ /* @__PURE__ */ jsx55(CameraView, { camera, children: /* @__PURE__ */ jsxs41(
16307
16369
  "div",
16308
16370
  {
16309
16371
  style: {
@@ -16385,7 +16447,7 @@ function TutorLoadingState({
16385
16447
  }
16386
16448
  );
16387
16449
  }
16388
- return /* @__PURE__ */ jsxs42(
16450
+ return /* @__PURE__ */ jsxs41(
16389
16451
  "div",
16390
16452
  {
16391
16453
  style: {