pdfjs-reader-core 0.5.5 → 0.5.7
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 +208 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +226 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9154,7 +9154,7 @@ var init_DocumentContainer = __esm({
|
|
|
9154
9154
|
const containerRef = useRef16(null);
|
|
9155
9155
|
const documentRef = useRef16(null);
|
|
9156
9156
|
const baseScaleRef = useRef16(scale);
|
|
9157
|
-
const
|
|
9157
|
+
const isTouchDevice2 = useIsTouchDevice();
|
|
9158
9158
|
const documentLoadingState = useViewerStore((s) => s.documentLoadingState);
|
|
9159
9159
|
const { selection, clearSelection, copySelection } = useTextSelection();
|
|
9160
9160
|
const handlePinchZoom = useCallback29(
|
|
@@ -9185,7 +9185,7 @@ var init_DocumentContainer = __esm({
|
|
|
9185
9185
|
onSwipeLeft: handleSwipeLeft,
|
|
9186
9186
|
onSwipeRight: handleSwipeRight,
|
|
9187
9187
|
onDoubleTap: handleDoubleTap,
|
|
9188
|
-
enabled: enableTouchGestures &&
|
|
9188
|
+
enabled: enableTouchGestures && isTouchDevice2,
|
|
9189
9189
|
swipeThreshold: 50,
|
|
9190
9190
|
doubleTapInterval: 300
|
|
9191
9191
|
});
|
|
@@ -9429,7 +9429,7 @@ var init_VirtualizedDocumentContainer = __esm({
|
|
|
9429
9429
|
const pageCache = useRef17(/* @__PURE__ */ new Map());
|
|
9430
9430
|
const pageDimensionsCache = useRef17(/* @__PURE__ */ new Map());
|
|
9431
9431
|
const baseScaleRef = useRef17(scale);
|
|
9432
|
-
const
|
|
9432
|
+
const isTouchDevice2 = useIsTouchDevice();
|
|
9433
9433
|
const [visiblePages, setVisiblePages] = useState19([1]);
|
|
9434
9434
|
const [pageObjects, setPageObjects] = useState19(/* @__PURE__ */ new Map());
|
|
9435
9435
|
const [totalHeight, setTotalHeight] = useState19(0);
|
|
@@ -9632,7 +9632,7 @@ var init_VirtualizedDocumentContainer = __esm({
|
|
|
9632
9632
|
onPinchZoom: handlePinchZoom,
|
|
9633
9633
|
onSwipeLeft: nextPage,
|
|
9634
9634
|
onSwipeRight: previousPage,
|
|
9635
|
-
enabled: enableTouchGestures &&
|
|
9635
|
+
enabled: enableTouchGestures && isTouchDevice2
|
|
9636
9636
|
});
|
|
9637
9637
|
const setContainerRef = useCallback30(
|
|
9638
9638
|
(element) => {
|
|
@@ -9840,7 +9840,7 @@ var init_DualPageContainer = __esm({
|
|
|
9840
9840
|
const containerRef = useRef18(null);
|
|
9841
9841
|
const documentRef = useRef18(null);
|
|
9842
9842
|
const baseScaleRef = useRef18(scale);
|
|
9843
|
-
const
|
|
9843
|
+
const isTouchDevice2 = useIsTouchDevice();
|
|
9844
9844
|
const [leftPage, setLeftPage] = useState20(null);
|
|
9845
9845
|
const [rightPage, setRightPage] = useState20(null);
|
|
9846
9846
|
const [isLoading, setIsLoading] = useState20(false);
|
|
@@ -9983,7 +9983,7 @@ var init_DualPageContainer = __esm({
|
|
|
9983
9983
|
onPinchZoom: handlePinchZoom,
|
|
9984
9984
|
onSwipeLeft: goToNextSpread,
|
|
9985
9985
|
onSwipeRight: goToPreviousSpread,
|
|
9986
|
-
enabled: enableTouchGestures &&
|
|
9986
|
+
enabled: enableTouchGestures && isTouchDevice2
|
|
9987
9987
|
});
|
|
9988
9988
|
const setContainerRef = useCallback31(
|
|
9989
9989
|
(element) => {
|
|
@@ -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 xPad = 8;
|
|
13378
|
+
const yAbove = 8;
|
|
13379
|
+
const yBelow = 24;
|
|
13380
|
+
const svgX = x1 - xPad;
|
|
13381
|
+
const svgY = y - yAbove;
|
|
13382
|
+
const svgW = x2 - x1 + 2 * xPad;
|
|
13383
|
+
const svgH = yAbove + yBelow;
|
|
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
|
-
|
|
13392
|
+
left: svgX,
|
|
13393
|
+
top: svgY,
|
|
13383
13394
|
pointerEvents: "none",
|
|
13384
13395
|
overflow: "visible"
|
|
13385
13396
|
},
|
|
@@ -13442,10 +13453,9 @@ 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
|
|
13448
|
-
var WASH = "rgba(
|
|
13457
|
+
import { jsx as jsx44 } from "react/jsx-runtime";
|
|
13458
|
+
var WASH = "rgba(195, 145, 10, 0.32)";
|
|
13449
13459
|
function AnimatedHighlight({ bbox, action }) {
|
|
13450
13460
|
const [x1, y1, x2, y2] = bbox;
|
|
13451
13461
|
const h = Math.max(1, y2 - y1);
|
|
@@ -13453,7 +13463,6 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13453
13463
|
const yTop = y1 - bleed;
|
|
13454
13464
|
const yBot = y2 + bleed;
|
|
13455
13465
|
const duration = action.draw_duration_ms / 1e3;
|
|
13456
|
-
const filterId = useId2();
|
|
13457
13466
|
const isDefaultColour = !action.color || action.color === "rgba(250, 204, 21, 0.35)" || action.color === "rgba(250,204,21,0.35)";
|
|
13458
13467
|
const fill = isDefaultColour ? WASH : action.color;
|
|
13459
13468
|
const taper = Math.min(6, h * 0.2);
|
|
@@ -13468,57 +13477,51 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13468
13477
|
L ${x1 - 2} ${yBot - taper}
|
|
13469
13478
|
Z
|
|
13470
13479
|
`;
|
|
13471
|
-
|
|
13480
|
+
const pad = 8;
|
|
13481
|
+
const svgX = x1 - pad;
|
|
13482
|
+
const svgY = yTop - pad;
|
|
13483
|
+
const svgW = x2 - x1 + 2 * pad;
|
|
13484
|
+
const svgH = yBot - yTop + 2 * pad;
|
|
13485
|
+
return /* @__PURE__ */ jsx44(
|
|
13472
13486
|
"svg",
|
|
13473
13487
|
{
|
|
13488
|
+
width: svgW,
|
|
13489
|
+
height: svgH,
|
|
13490
|
+
viewBox: `${svgX} ${svgY} ${svgW} ${svgH}`,
|
|
13474
13491
|
style: {
|
|
13475
13492
|
position: "absolute",
|
|
13476
|
-
|
|
13493
|
+
left: svgX,
|
|
13494
|
+
top: svgY,
|
|
13477
13495
|
pointerEvents: "none",
|
|
13478
|
-
overflow: "visible"
|
|
13479
|
-
mixBlendMode: "multiply"
|
|
13496
|
+
overflow: "visible"
|
|
13480
13497
|
},
|
|
13481
13498
|
"data-role": "highlight",
|
|
13482
|
-
children:
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
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
|
-
]
|
|
13499
|
+
children: /* @__PURE__ */ jsx44(
|
|
13500
|
+
motion4.path,
|
|
13501
|
+
{
|
|
13502
|
+
d: pathD,
|
|
13503
|
+
fill,
|
|
13504
|
+
initial: { clipPath: `inset(0 100% 0 0)` },
|
|
13505
|
+
animate: { clipPath: `inset(0 0% 0 0)` },
|
|
13506
|
+
exit: { opacity: 0 },
|
|
13507
|
+
transition: { duration, ease: EASE_OUT_EXPO }
|
|
13508
|
+
}
|
|
13509
|
+
)
|
|
13509
13510
|
}
|
|
13510
13511
|
);
|
|
13511
13512
|
}
|
|
13512
13513
|
|
|
13513
13514
|
// src/components/TutorMode/PulseOverlay.tsx
|
|
13515
|
+
import { useId as useId2 } from "react";
|
|
13514
13516
|
import { motion as motion5 } from "framer-motion";
|
|
13515
|
-
import { jsx as jsx45, jsxs as
|
|
13517
|
+
import { jsx as jsx45, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
13516
13518
|
var INTENSITY = {
|
|
13517
13519
|
subtle: { bracketLen: 14, strokeWeight: 2, coreOpacity: 0.5, ringScale: 1.08 },
|
|
13518
13520
|
normal: { bracketLen: 20, strokeWeight: 2.5, coreOpacity: 0.75, ringScale: 1.14 },
|
|
13519
13521
|
strong: { bracketLen: 26, strokeWeight: 3, coreOpacity: 1, ringScale: 1.22 }
|
|
13520
13522
|
};
|
|
13521
13523
|
function PulseOverlay({ bbox, action }) {
|
|
13524
|
+
const pulseId = useId2();
|
|
13522
13525
|
const [x1, y1, x2, y2] = bbox;
|
|
13523
13526
|
const w = Math.max(1, x2 - x1);
|
|
13524
13527
|
const h = Math.max(1, y2 - y1);
|
|
@@ -13527,29 +13530,48 @@ function PulseOverlay({ bbox, action }) {
|
|
|
13527
13530
|
const spec = INTENSITY[action.intensity] ?? INTENSITY.normal;
|
|
13528
13531
|
const L = Math.min(spec.bracketLen, Math.min(w, h) / 2.5);
|
|
13529
13532
|
const PAD = 6;
|
|
13530
|
-
|
|
13533
|
+
const bracketExtent = PAD + L + 8;
|
|
13534
|
+
const glowExtent = (w / 2 + 24) * spec.ringScale - w / 2;
|
|
13535
|
+
const ringExtent = (w / 2 + PAD) * spec.ringScale - w / 2;
|
|
13536
|
+
const glowExtentV = (h / 2 + 24) * spec.ringScale - h / 2;
|
|
13537
|
+
const ringExtentV = (h / 2 + PAD) * spec.ringScale - h / 2;
|
|
13538
|
+
const svgPadX = Math.ceil(Math.max(bracketExtent, glowExtent, ringExtent) + 4);
|
|
13539
|
+
const svgPadY = Math.ceil(Math.max(bracketExtent, glowExtentV, ringExtentV) + 4);
|
|
13540
|
+
const svgX = x1 - svgPadX;
|
|
13541
|
+
const svgY = y1 - svgPadY;
|
|
13542
|
+
const svgW = w + 2 * svgPadX;
|
|
13543
|
+
const svgH = h + 2 * svgPadY;
|
|
13544
|
+
return /* @__PURE__ */ jsxs37(
|
|
13531
13545
|
"svg",
|
|
13532
13546
|
{
|
|
13547
|
+
width: svgW,
|
|
13548
|
+
height: svgH,
|
|
13549
|
+
viewBox: `${svgX} ${svgY} ${svgW} ${svgH}`,
|
|
13533
13550
|
style: {
|
|
13534
13551
|
position: "absolute",
|
|
13535
|
-
|
|
13552
|
+
left: svgX,
|
|
13553
|
+
top: svgY,
|
|
13536
13554
|
pointerEvents: "none",
|
|
13537
13555
|
overflow: "visible"
|
|
13538
13556
|
},
|
|
13539
13557
|
"data-role": "pulse",
|
|
13540
13558
|
children: [
|
|
13559
|
+
/* @__PURE__ */ jsx45("defs", { children: /* @__PURE__ */ jsxs37("radialGradient", { id: `${pulseId}-glow`, cx: "50%", cy: "50%", r: "50%", children: [
|
|
13560
|
+
/* @__PURE__ */ jsx45("stop", { offset: "0%", stopColor: ACCENT_GLOW, stopOpacity: 1 }),
|
|
13561
|
+
/* @__PURE__ */ jsx45("stop", { offset: "60%", stopColor: ACCENT_GLOW, stopOpacity: 0.45 }),
|
|
13562
|
+
/* @__PURE__ */ jsx45("stop", { offset: "100%", stopColor: ACCENT_GLOW, stopOpacity: 0 })
|
|
13563
|
+
] }) }),
|
|
13541
13564
|
/* @__PURE__ */ jsx45(
|
|
13542
13565
|
motion5.ellipse,
|
|
13543
13566
|
{
|
|
13544
13567
|
cx,
|
|
13545
13568
|
cy,
|
|
13546
|
-
rx: w / 2 +
|
|
13547
|
-
ry: h / 2 +
|
|
13548
|
-
fill:
|
|
13569
|
+
rx: w / 2 + 24,
|
|
13570
|
+
ry: h / 2 + 24,
|
|
13571
|
+
fill: `url(#${pulseId}-glow)`,
|
|
13549
13572
|
style: {
|
|
13550
13573
|
transformOrigin: `${cx}px ${cy}px`,
|
|
13551
|
-
transformBox: "fill-box"
|
|
13552
|
-
filter: "blur(16px)"
|
|
13574
|
+
transformBox: "fill-box"
|
|
13553
13575
|
},
|
|
13554
13576
|
initial: { opacity: 0, scale: 0.95 },
|
|
13555
13577
|
animate: {
|
|
@@ -13682,7 +13704,7 @@ function Bracket({
|
|
|
13682
13704
|
// src/components/TutorMode/CalloutArrow.tsx
|
|
13683
13705
|
import { useId as useId3 } from "react";
|
|
13684
13706
|
import { motion as motion6 } from "framer-motion";
|
|
13685
|
-
import { jsx as jsx46, jsxs as
|
|
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
|
}
|
|
@@ -13722,18 +13744,33 @@ function CalloutArrow({ fromBbox, toBbox, action }) {
|
|
|
13722
13744
|
const glowId = `${markerId}-glow`;
|
|
13723
13745
|
const { from, to } = edgePoints(fromBbox, toBbox);
|
|
13724
13746
|
const d = arrowPath(from, to, action.curve);
|
|
13725
|
-
|
|
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
|
-
|
|
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__ */
|
|
13773
|
+
/* @__PURE__ */ jsxs38("defs", { children: [
|
|
13737
13774
|
/* @__PURE__ */ jsx46(
|
|
13738
13775
|
"marker",
|
|
13739
13776
|
{
|
|
@@ -13876,8 +13913,9 @@ function CinemaLayer({
|
|
|
13876
13913
|
page,
|
|
13877
13914
|
index,
|
|
13878
13915
|
overlays,
|
|
13879
|
-
|
|
13916
|
+
coordScale
|
|
13880
13917
|
}) {
|
|
13918
|
+
if (overlays.length === 0) return null;
|
|
13881
13919
|
return /* @__PURE__ */ jsx48(
|
|
13882
13920
|
"div",
|
|
13883
13921
|
{
|
|
@@ -13886,7 +13924,7 @@ function CinemaLayer({
|
|
|
13886
13924
|
position: "absolute",
|
|
13887
13925
|
inset: 0,
|
|
13888
13926
|
transformOrigin: "0 0",
|
|
13889
|
-
transform: `scale(${
|
|
13927
|
+
transform: `scale(${coordScale})`,
|
|
13890
13928
|
width: page.page_dimensions.width,
|
|
13891
13929
|
height: page.page_dimensions.height,
|
|
13892
13930
|
pointerEvents: "none",
|
|
@@ -13971,7 +14009,7 @@ import { AnimatePresence as AnimatePresence2 } from "framer-motion";
|
|
|
13971
14009
|
|
|
13972
14010
|
// src/components/TutorMode/GhostReference.tsx
|
|
13973
14011
|
import { motion as motion8 } from "framer-motion";
|
|
13974
|
-
import { jsx as jsx49, jsxs as
|
|
14012
|
+
import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
13975
14013
|
var POSITIONS = {
|
|
13976
14014
|
"top-right": {
|
|
13977
14015
|
top: "clamp(12px, 4vw, 40px)",
|
|
@@ -14004,7 +14042,7 @@ function GhostReference({
|
|
|
14004
14042
|
}) {
|
|
14005
14043
|
const [x1, y1, x2, y2] = sourceBbox;
|
|
14006
14044
|
const text = sourceBlockText ?? "(figure)";
|
|
14007
|
-
return /* @__PURE__ */
|
|
14045
|
+
return /* @__PURE__ */ jsxs39(
|
|
14008
14046
|
motion8.div,
|
|
14009
14047
|
{
|
|
14010
14048
|
initial: { opacity: 0, y: 24, scale: 0.97 },
|
|
@@ -14067,7 +14105,7 @@ function GhostReference({
|
|
|
14067
14105
|
}
|
|
14068
14106
|
}
|
|
14069
14107
|
),
|
|
14070
|
-
/* @__PURE__ */
|
|
14108
|
+
/* @__PURE__ */ jsxs39(
|
|
14071
14109
|
"div",
|
|
14072
14110
|
{
|
|
14073
14111
|
style: {
|
|
@@ -14097,7 +14135,7 @@ function GhostReference({
|
|
|
14097
14135
|
overflow: "hidden",
|
|
14098
14136
|
boxShadow: "inset 0 0 0 1px rgba(42, 36, 32, 0.12), 0 1px 3px rgba(42, 36, 32, 0.10)"
|
|
14099
14137
|
},
|
|
14100
|
-
children: /* @__PURE__ */
|
|
14138
|
+
children: /* @__PURE__ */ jsxs39(
|
|
14101
14139
|
"svg",
|
|
14102
14140
|
{
|
|
14103
14141
|
width: "100%",
|
|
@@ -14144,7 +14182,7 @@ function GhostReference({
|
|
|
14144
14182
|
)
|
|
14145
14183
|
}
|
|
14146
14184
|
),
|
|
14147
|
-
/* @__PURE__ */
|
|
14185
|
+
/* @__PURE__ */ jsxs39(
|
|
14148
14186
|
motion8.div,
|
|
14149
14187
|
{
|
|
14150
14188
|
initial: { opacity: 0, y: 6 },
|
|
@@ -14286,12 +14324,12 @@ import { AnimatePresence as AnimatePresence3 } from "framer-motion";
|
|
|
14286
14324
|
|
|
14287
14325
|
// src/components/TutorMode/StickyLabel.tsx
|
|
14288
14326
|
import { motion as motion9 } from "framer-motion";
|
|
14289
|
-
import { jsx as jsx51, jsxs as
|
|
14327
|
+
import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
14290
14328
|
var STEM = 18;
|
|
14291
14329
|
function StickyLabel({ screenAnchor, action }) {
|
|
14292
14330
|
const { x, y } = screenAnchor;
|
|
14293
14331
|
const layout = LAYOUTS[action.position] ?? LAYOUTS.top;
|
|
14294
|
-
return /* @__PURE__ */
|
|
14332
|
+
return /* @__PURE__ */ jsxs40(
|
|
14295
14333
|
motion9.div,
|
|
14296
14334
|
{
|
|
14297
14335
|
initial: { opacity: 0, scale: 0.88 },
|
|
@@ -14476,7 +14514,8 @@ function LabelOverlay({
|
|
|
14476
14514
|
index,
|
|
14477
14515
|
currentPage,
|
|
14478
14516
|
camera,
|
|
14479
|
-
viewport
|
|
14517
|
+
viewport,
|
|
14518
|
+
coordScale
|
|
14480
14519
|
}) {
|
|
14481
14520
|
const labels = overlays.filter((o) => o.kind === "label");
|
|
14482
14521
|
const page = index.byPage.get(currentPage);
|
|
@@ -14503,7 +14542,8 @@ function LabelOverlay({
|
|
|
14503
14542
|
a.position,
|
|
14504
14543
|
page,
|
|
14505
14544
|
camera,
|
|
14506
|
-
viewport
|
|
14545
|
+
viewport,
|
|
14546
|
+
coordScale
|
|
14507
14547
|
);
|
|
14508
14548
|
return /* @__PURE__ */ jsx52(
|
|
14509
14549
|
StickyLabel,
|
|
@@ -14517,7 +14557,7 @@ function LabelOverlay({
|
|
|
14517
14557
|
}
|
|
14518
14558
|
);
|
|
14519
14559
|
}
|
|
14520
|
-
function computeScreenAnchor(bbox, where, page, camera, viewport) {
|
|
14560
|
+
function computeScreenAnchor(bbox, where, page, camera, viewport, coordScale) {
|
|
14521
14561
|
const [x1, y1, x2, y2] = bbox;
|
|
14522
14562
|
const pageCX = page.page_dimensions.width / 2;
|
|
14523
14563
|
const pageCY = page.page_dimensions.height / 2;
|
|
@@ -14543,8 +14583,8 @@ function computeScreenAnchor(bbox, where, page, camera, viewport) {
|
|
|
14543
14583
|
px = (x1 + x2) / 2;
|
|
14544
14584
|
py = y1;
|
|
14545
14585
|
}
|
|
14546
|
-
const screenX = viewport.width / 2 + camera.x + (px - pageCX) * camera.scale;
|
|
14547
|
-
const screenY = viewport.height / 2 + camera.y + (py - pageCY) * camera.scale;
|
|
14586
|
+
const screenX = viewport.width / 2 + camera.x + (px - pageCX) * coordScale * camera.scale;
|
|
14587
|
+
const screenY = viewport.height / 2 + camera.y + (py - pageCY) * coordScale * camera.scale;
|
|
14548
14588
|
return { x: screenX, y: screenY };
|
|
14549
14589
|
}
|
|
14550
14590
|
|
|
@@ -14556,7 +14596,8 @@ function CalloutLabelOverlay({
|
|
|
14556
14596
|
index,
|
|
14557
14597
|
currentPage,
|
|
14558
14598
|
camera,
|
|
14559
|
-
viewport
|
|
14599
|
+
viewport,
|
|
14600
|
+
coordScale
|
|
14560
14601
|
}) {
|
|
14561
14602
|
const callouts = overlays.filter(
|
|
14562
14603
|
(o) => o.kind === "callout" && o.action.label
|
|
@@ -14585,7 +14626,8 @@ function CalloutLabelOverlay({
|
|
|
14585
14626
|
toHit.block.bbox,
|
|
14586
14627
|
page,
|
|
14587
14628
|
camera,
|
|
14588
|
-
viewport
|
|
14629
|
+
viewport,
|
|
14630
|
+
coordScale
|
|
14589
14631
|
);
|
|
14590
14632
|
return /* @__PURE__ */ jsx53(
|
|
14591
14633
|
CalloutLabelPill,
|
|
@@ -14600,7 +14642,7 @@ function CalloutLabelOverlay({
|
|
|
14600
14642
|
}
|
|
14601
14643
|
);
|
|
14602
14644
|
}
|
|
14603
|
-
function computePillAnchor(fromBbox, toBbox, page, camera, viewport) {
|
|
14645
|
+
function computePillAnchor(fromBbox, toBbox, page, camera, viewport, coordScale) {
|
|
14604
14646
|
const aCX = (fromBbox[0] + fromBbox[2]) / 2;
|
|
14605
14647
|
const aCY = (fromBbox[1] + fromBbox[3]) / 2;
|
|
14606
14648
|
const bCX = (toBbox[0] + toBbox[2]) / 2;
|
|
@@ -14617,8 +14659,8 @@ function computePillAnchor(fromBbox, toBbox, page, camera, viewport) {
|
|
|
14617
14659
|
const toY = bCY - uy * bOff;
|
|
14618
14660
|
const pageCX = page.page_dimensions.width / 2;
|
|
14619
14661
|
const pageCY = page.page_dimensions.height / 2;
|
|
14620
|
-
const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) * camera.scale;
|
|
14621
|
-
const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) * camera.scale;
|
|
14662
|
+
const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) * coordScale * camera.scale;
|
|
14663
|
+
const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) * coordScale * camera.scale;
|
|
14622
14664
|
const isVertical = Math.abs(dy) >= Math.abs(dx);
|
|
14623
14665
|
const OFFSET = resolvePillOffset(viewport.width);
|
|
14624
14666
|
const MAX_PILL_W = resolveMaxPillW(viewport.width);
|
|
@@ -14761,6 +14803,41 @@ function SubtitleBar({ text }) {
|
|
|
14761
14803
|
) : null });
|
|
14762
14804
|
}
|
|
14763
14805
|
|
|
14806
|
+
// src/utils/render-scale.ts
|
|
14807
|
+
function computeFitScale(input) {
|
|
14808
|
+
const { pageWidthPt, pageHeightPt, viewport, paddingFactor = 0.95 } = input;
|
|
14809
|
+
if (!Number.isFinite(pageWidthPt) || !Number.isFinite(pageHeightPt) || pageWidthPt <= 0 || pageHeightPt <= 0) {
|
|
14810
|
+
return 1;
|
|
14811
|
+
}
|
|
14812
|
+
const w = Math.max(1, viewport.width);
|
|
14813
|
+
const h = Math.max(1, viewport.height);
|
|
14814
|
+
const fit = Math.min(w / pageWidthPt, h / pageHeightPt) * paddingFactor;
|
|
14815
|
+
return Number.isFinite(fit) && fit > 0 ? fit : 1;
|
|
14816
|
+
}
|
|
14817
|
+
function computeCoordScale(input) {
|
|
14818
|
+
const { renderScale, dpi } = input;
|
|
14819
|
+
if (!Number.isFinite(renderScale) || renderScale <= 0) return 0;
|
|
14820
|
+
if (!Number.isFinite(dpi) || dpi <= 0) return 1;
|
|
14821
|
+
return renderScale * 72 / dpi;
|
|
14822
|
+
}
|
|
14823
|
+
function isTouchDevice() {
|
|
14824
|
+
if (typeof window === "undefined") return false;
|
|
14825
|
+
if (typeof window.matchMedia !== "function") return false;
|
|
14826
|
+
try {
|
|
14827
|
+
return window.matchMedia("(pointer: coarse)").matches;
|
|
14828
|
+
} catch {
|
|
14829
|
+
return false;
|
|
14830
|
+
}
|
|
14831
|
+
}
|
|
14832
|
+
function resolveRenderScale(input) {
|
|
14833
|
+
const { renderScaleProp, scaleProp, defaultRenderScale } = input;
|
|
14834
|
+
if (Number.isFinite(renderScaleProp) && renderScaleProp > 0) {
|
|
14835
|
+
return renderScaleProp;
|
|
14836
|
+
}
|
|
14837
|
+
const multiplier = Number.isFinite(scaleProp) && scaleProp > 0 ? scaleProp : 1;
|
|
14838
|
+
return defaultRenderScale * multiplier;
|
|
14839
|
+
}
|
|
14840
|
+
|
|
14764
14841
|
// src/director/storyboard-engine.ts
|
|
14765
14842
|
init_narration_store();
|
|
14766
14843
|
init_camera_math();
|
|
@@ -14851,11 +14928,32 @@ var StoryboardEngine = class {
|
|
|
14851
14928
|
this.pendingStepTimers.clear();
|
|
14852
14929
|
this.deps.narrationStore.getState().setEngineStatus("idle");
|
|
14853
14930
|
}
|
|
14854
|
-
/** Cancel every removal timer (used by resetVisuals
|
|
14931
|
+
/** Cancel every removal timer (used by resetVisuals and destroy). */
|
|
14855
14932
|
cancelAllRemovalTimers() {
|
|
14856
14933
|
for (const t of this.overlayRemovalTimers.values()) clearTimeout(t);
|
|
14857
14934
|
this.overlayRemovalTimers.clear();
|
|
14858
14935
|
}
|
|
14936
|
+
/**
|
|
14937
|
+
* Full destructor — cancels both pending step timers AND overlay removal
|
|
14938
|
+
* timers. Use this in component unmount/cleanup. The per-storyboard
|
|
14939
|
+
* `cancelPending()` deliberately leaves overlay removal timers alone so a
|
|
14940
|
+
* mid-flight overlay doesn't get stranded (see `overlayRemovalTimers` doc),
|
|
14941
|
+
* but at destruction time we must release every timer or their closures
|
|
14942
|
+
* keep `deps` (narrationStore, the full bboxIndex) alive beyond the
|
|
14943
|
+
* lifetime of this engine. On iOS Safari with many engine recreations
|
|
14944
|
+
* this causes cumulative memory pressure and eventual tab reload.
|
|
14945
|
+
*/
|
|
14946
|
+
destroy() {
|
|
14947
|
+
this.cancelPending();
|
|
14948
|
+
this.cancelAllRemovalTimers();
|
|
14949
|
+
}
|
|
14950
|
+
/** Test-only — exposes internal queue sizes for regression tests. */
|
|
14951
|
+
_queueSizesForTest() {
|
|
14952
|
+
return {
|
|
14953
|
+
pending: this.pendingStepTimers.size,
|
|
14954
|
+
removals: this.overlayRemovalTimers.size
|
|
14955
|
+
};
|
|
14956
|
+
}
|
|
14859
14957
|
/** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
|
|
14860
14958
|
resetVisuals() {
|
|
14861
14959
|
this.cancelPending();
|
|
@@ -15956,7 +16054,7 @@ function storyboardFromMatch(match, page) {
|
|
|
15956
16054
|
}
|
|
15957
16055
|
|
|
15958
16056
|
// src/components/TutorMode/TutorModeContainer.tsx
|
|
15959
|
-
import { Fragment as Fragment4, jsx as jsx55, jsxs as
|
|
16057
|
+
import { Fragment as Fragment4, jsx as jsx55, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
15960
16058
|
function buildBBoxIndex(bboxData) {
|
|
15961
16059
|
const byPage = /* @__PURE__ */ new Map();
|
|
15962
16060
|
const blockById = /* @__PURE__ */ new Map();
|
|
@@ -15996,6 +16094,7 @@ function TutorModeContainer({
|
|
|
15996
16094
|
loadingComponent,
|
|
15997
16095
|
onPageChange,
|
|
15998
16096
|
storyboardProvider,
|
|
16097
|
+
renderScale,
|
|
15999
16098
|
className
|
|
16000
16099
|
}) {
|
|
16001
16100
|
const containerRef = useRef27(null);
|
|
@@ -16010,6 +16109,27 @@ function TutorModeContainer({
|
|
|
16010
16109
|
const [viewport, setViewport] = useState30({ width: 800, height: 1e3 });
|
|
16011
16110
|
const camera = useStore2(narrationStore, (s) => s.camera);
|
|
16012
16111
|
const activeOverlays = useStore2(narrationStore, (s) => s.activeOverlays);
|
|
16112
|
+
const page = index.byPage.get(pageNumber);
|
|
16113
|
+
const pagePointsW = page ? page.page_dimensions.width * 72 / page.page_dimensions.dpi : 0;
|
|
16114
|
+
const pagePointsH = page ? page.page_dimensions.height * 72 / page.page_dimensions.dpi : 0;
|
|
16115
|
+
const touch = isTouchDevice();
|
|
16116
|
+
const defaultRenderScale = page ? touch ? computeFitScale({
|
|
16117
|
+
pageWidthPt: pagePointsW,
|
|
16118
|
+
pageHeightPt: pagePointsH,
|
|
16119
|
+
viewport
|
|
16120
|
+
}) : page.page_dimensions.dpi / 72 : 1;
|
|
16121
|
+
const effectiveRenderScale = resolveRenderScale({
|
|
16122
|
+
renderScaleProp: renderScale,
|
|
16123
|
+
scaleProp: scale,
|
|
16124
|
+
defaultRenderScale
|
|
16125
|
+
});
|
|
16126
|
+
const coordScale = page ? computeCoordScale({
|
|
16127
|
+
renderScale: effectiveRenderScale,
|
|
16128
|
+
dpi: page.page_dimensions.dpi
|
|
16129
|
+
}) : 1;
|
|
16130
|
+
const rasterScale = effectiveRenderScale;
|
|
16131
|
+
const baseW = pagePointsW * effectiveRenderScale;
|
|
16132
|
+
const baseH = pagePointsH * effectiveRenderScale;
|
|
16013
16133
|
useEffect28(() => {
|
|
16014
16134
|
if (numPages <= 0) return;
|
|
16015
16135
|
if (pageNumber < 1 || pageNumber > numPages) return;
|
|
@@ -16025,7 +16145,13 @@ function TutorModeContainer({
|
|
|
16025
16145
|
useEffect28(() => {
|
|
16026
16146
|
if (!containerRef.current) return;
|
|
16027
16147
|
const el = containerRef.current;
|
|
16028
|
-
const update = () =>
|
|
16148
|
+
const update = () => {
|
|
16149
|
+
const w = el.clientWidth;
|
|
16150
|
+
const h = el.clientHeight;
|
|
16151
|
+
setViewport(
|
|
16152
|
+
(prev) => prev.width === w && prev.height === h ? prev : { width: w, height: h }
|
|
16153
|
+
);
|
|
16154
|
+
};
|
|
16029
16155
|
update();
|
|
16030
16156
|
const ro = new ResizeObserver(update);
|
|
16031
16157
|
ro.observe(el);
|
|
@@ -16053,23 +16179,29 @@ function TutorModeContainer({
|
|
|
16053
16179
|
const page2 = index.byPage.get(pageNumber);
|
|
16054
16180
|
if (!page2) return;
|
|
16055
16181
|
if (viewport.width === 0 || viewport.height === 0) return;
|
|
16182
|
+
if (baseW === 0 || baseH === 0) return;
|
|
16056
16183
|
if (narrationStore.getState().activeOverlays.length > 0) return;
|
|
16057
|
-
const fit = Math.min(
|
|
16058
|
-
viewport.width / page2.page_dimensions.width,
|
|
16059
|
-
viewport.height / page2.page_dimensions.height
|
|
16060
|
-
) * 0.95;
|
|
16184
|
+
const fit = Math.min(viewport.width / baseW, viewport.height / baseH) * 0.95;
|
|
16061
16185
|
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0 });
|
|
16062
|
-
}, [pageNumber, viewport, index, narrationStore]);
|
|
16186
|
+
}, [pageNumber, viewport, index, narrationStore, baseW, baseH]);
|
|
16187
|
+
const viewportRef = useRef27(viewport);
|
|
16188
|
+
useEffect28(() => {
|
|
16189
|
+
viewportRef.current = viewport;
|
|
16190
|
+
}, [viewport]);
|
|
16063
16191
|
const engineRef = useRef27(null);
|
|
16064
16192
|
useEffect28(() => {
|
|
16065
|
-
|
|
16193
|
+
const engine = new StoryboardEngine({
|
|
16066
16194
|
narrationStore,
|
|
16067
16195
|
bboxIndex: index,
|
|
16068
|
-
getViewport: () =>
|
|
16196
|
+
getViewport: () => viewportRef.current,
|
|
16069
16197
|
minOverlayDurationMs
|
|
16070
16198
|
});
|
|
16071
|
-
|
|
16072
|
-
|
|
16199
|
+
engineRef.current = engine;
|
|
16200
|
+
return () => {
|
|
16201
|
+
engine.destroy();
|
|
16202
|
+
if (engineRef.current === engine) engineRef.current = null;
|
|
16203
|
+
};
|
|
16204
|
+
}, [narrationStore, index, minOverlayDurationMs]);
|
|
16073
16205
|
const abortRef = useRef27(null);
|
|
16074
16206
|
const debounceRef = useRef27(null);
|
|
16075
16207
|
const lastChunkRef = useRef27(null);
|
|
@@ -16250,13 +16382,8 @@ function TutorModeContainer({
|
|
|
16250
16382
|
}, idleTimeoutMs + 100);
|
|
16251
16383
|
return () => clearTimeout(t);
|
|
16252
16384
|
}, [currentChunk, idleTimeoutMs, narrationStore]);
|
|
16253
|
-
const page = index.byPage.get(pageNumber);
|
|
16254
|
-
const dpiScale = page ? page.page_dimensions.dpi / 72 : 1;
|
|
16255
|
-
const rasterScale = dpiScale * (scale || 1);
|
|
16256
|
-
const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
|
|
16257
|
-
const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
|
|
16258
16385
|
const isReady = !!page && !!pageProxy;
|
|
16259
|
-
return /* @__PURE__ */
|
|
16386
|
+
return /* @__PURE__ */ jsxs41(
|
|
16260
16387
|
"div",
|
|
16261
16388
|
{
|
|
16262
16389
|
ref: containerRef,
|
|
@@ -16302,8 +16429,8 @@ function TutorModeContainer({
|
|
|
16302
16429
|
children: "Reset view"
|
|
16303
16430
|
}
|
|
16304
16431
|
) : null,
|
|
16305
|
-
isReady ? /* @__PURE__ */
|
|
16306
|
-
/* @__PURE__ */ jsx55(CameraView, { camera, children: /* @__PURE__ */
|
|
16432
|
+
isReady ? /* @__PURE__ */ jsxs41(Fragment4, { children: [
|
|
16433
|
+
/* @__PURE__ */ jsx55(CameraView, { camera, children: /* @__PURE__ */ jsxs41(
|
|
16307
16434
|
"div",
|
|
16308
16435
|
{
|
|
16309
16436
|
style: {
|
|
@@ -16333,7 +16460,7 @@ function TutorModeContainer({
|
|
|
16333
16460
|
page,
|
|
16334
16461
|
index,
|
|
16335
16462
|
overlays: activeOverlays,
|
|
16336
|
-
|
|
16463
|
+
coordScale
|
|
16337
16464
|
}
|
|
16338
16465
|
)
|
|
16339
16466
|
]
|
|
@@ -16346,7 +16473,8 @@ function TutorModeContainer({
|
|
|
16346
16473
|
index,
|
|
16347
16474
|
currentPage: pageNumber,
|
|
16348
16475
|
camera,
|
|
16349
|
-
viewport
|
|
16476
|
+
viewport,
|
|
16477
|
+
coordScale
|
|
16350
16478
|
}
|
|
16351
16479
|
),
|
|
16352
16480
|
/* @__PURE__ */ jsx55(
|
|
@@ -16356,7 +16484,8 @@ function TutorModeContainer({
|
|
|
16356
16484
|
index,
|
|
16357
16485
|
currentPage: pageNumber,
|
|
16358
16486
|
camera,
|
|
16359
|
-
viewport
|
|
16487
|
+
viewport,
|
|
16488
|
+
coordScale
|
|
16360
16489
|
}
|
|
16361
16490
|
),
|
|
16362
16491
|
/* @__PURE__ */ jsx55(GhostReferenceOverlay, { overlays: activeOverlays, index })
|
|
@@ -16385,7 +16514,7 @@ function TutorLoadingState({
|
|
|
16385
16514
|
}
|
|
16386
16515
|
);
|
|
16387
16516
|
}
|
|
16388
|
-
return /* @__PURE__ */
|
|
16517
|
+
return /* @__PURE__ */ jsxs41(
|
|
16389
16518
|
"div",
|
|
16390
16519
|
{
|
|
16391
16520
|
style: {
|