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 +111 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -31
- package/dist/index.d.ts +14 -31
- package/dist/index.js +93 -153
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1858,40 +1858,19 @@ interface TutorModeContainerProps {
|
|
|
1858
1858
|
* Added in v0.4.2.
|
|
1859
1859
|
*/
|
|
1860
1860
|
onPageChange?: (page: number) => void;
|
|
1861
|
-
/**
|
|
1862
|
-
* PDF-points → CSS-pixels scale factor. Direct override: when set, the
|
|
1863
|
-
* tutor renders the PDF at exactly this scale regardless of source DPI.
|
|
1864
|
-
* Takes precedence over `scale` when both are passed.
|
|
1865
|
-
*
|
|
1866
|
-
* If neither `renderScale` nor `scale` is passed:
|
|
1867
|
-
* - Desktop (`pointer: fine`): defaults to `page.dpi / 72` — identical
|
|
1868
|
-
* to pre-v0.6 behavior.
|
|
1869
|
-
* - Touch (`pointer: coarse`): defaults to fit-to-viewport — much
|
|
1870
|
-
* smaller canvas on mobile, sidesteps the iOS Safari crash root
|
|
1871
|
-
* cause even when bbox data ships at very high source DPI.
|
|
1872
|
-
*
|
|
1873
|
-
* Added in v0.6.0.
|
|
1874
|
-
*/
|
|
1875
|
-
renderScale?: number;
|
|
1876
1861
|
className?: string;
|
|
1877
1862
|
}
|
|
1878
1863
|
/** Build a cross-page/block index from the raw bbox list. */
|
|
1879
1864
|
declare function buildBBoxIndex(bboxData: PageBBoxData[]): BBoxIndex;
|
|
1880
|
-
declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, storyboardProvider,
|
|
1865
|
+
declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, storyboardProvider, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element;
|
|
1881
1866
|
|
|
1882
1867
|
interface CinemaLayerProps {
|
|
1883
1868
|
page: PageBBoxData;
|
|
1884
1869
|
index: BBoxIndex;
|
|
1885
1870
|
overlays: ActiveOverlay[];
|
|
1886
|
-
|
|
1887
|
-
* Factor converting source-DPI pixel coords (the bbox space every
|
|
1888
|
-
* overlay primitive uses internally) into CSS-pixel coords. Applied
|
|
1889
|
-
* as `transform: scale(coordScale)` on the outer element so individual
|
|
1890
|
-
* overlay components don't need to know about CSS vs DPI space.
|
|
1891
|
-
*/
|
|
1892
|
-
coordScale: number;
|
|
1871
|
+
scale: number;
|
|
1893
1872
|
}
|
|
1894
|
-
declare function CinemaLayer({ page, index, overlays,
|
|
1873
|
+
declare function CinemaLayer({ page, index, overlays, scale, }: CinemaLayerProps): react_jsx_runtime.JSX.Element;
|
|
1895
1874
|
|
|
1896
1875
|
interface CameraViewProps {
|
|
1897
1876
|
camera: CameraState;
|
|
@@ -2086,14 +2065,18 @@ declare class StoryboardEngine {
|
|
|
2086
2065
|
/** Cancel every removal timer (used by resetVisuals and destroy). */
|
|
2087
2066
|
private cancelAllRemovalTimers;
|
|
2088
2067
|
/**
|
|
2089
|
-
* Full destructor — cancels
|
|
2090
|
-
* timers. Use this in component unmount/cleanup.
|
|
2091
|
-
*
|
|
2092
|
-
*
|
|
2093
|
-
*
|
|
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
|
|
2094
2076
|
* keep `deps` (narrationStore, the full bboxIndex) alive beyond the
|
|
2095
|
-
* lifetime of this engine.
|
|
2096
|
-
*
|
|
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.
|
|
2097
2080
|
*/
|
|
2098
2081
|
destroy(): void;
|
|
2099
2082
|
/** Test-only — exposes internal queue sizes for regression tests. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1858,40 +1858,19 @@ interface TutorModeContainerProps {
|
|
|
1858
1858
|
* Added in v0.4.2.
|
|
1859
1859
|
*/
|
|
1860
1860
|
onPageChange?: (page: number) => void;
|
|
1861
|
-
/**
|
|
1862
|
-
* PDF-points → CSS-pixels scale factor. Direct override: when set, the
|
|
1863
|
-
* tutor renders the PDF at exactly this scale regardless of source DPI.
|
|
1864
|
-
* Takes precedence over `scale` when both are passed.
|
|
1865
|
-
*
|
|
1866
|
-
* If neither `renderScale` nor `scale` is passed:
|
|
1867
|
-
* - Desktop (`pointer: fine`): defaults to `page.dpi / 72` — identical
|
|
1868
|
-
* to pre-v0.6 behavior.
|
|
1869
|
-
* - Touch (`pointer: coarse`): defaults to fit-to-viewport — much
|
|
1870
|
-
* smaller canvas on mobile, sidesteps the iOS Safari crash root
|
|
1871
|
-
* cause even when bbox data ships at very high source DPI.
|
|
1872
|
-
*
|
|
1873
|
-
* Added in v0.6.0.
|
|
1874
|
-
*/
|
|
1875
|
-
renderScale?: number;
|
|
1876
1861
|
className?: string;
|
|
1877
1862
|
}
|
|
1878
1863
|
/** Build a cross-page/block index from the raw bbox list. */
|
|
1879
1864
|
declare function buildBBoxIndex(bboxData: PageBBoxData[]): BBoxIndex;
|
|
1880
|
-
declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, storyboardProvider,
|
|
1865
|
+
declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, storyboardProvider, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element;
|
|
1881
1866
|
|
|
1882
1867
|
interface CinemaLayerProps {
|
|
1883
1868
|
page: PageBBoxData;
|
|
1884
1869
|
index: BBoxIndex;
|
|
1885
1870
|
overlays: ActiveOverlay[];
|
|
1886
|
-
|
|
1887
|
-
* Factor converting source-DPI pixel coords (the bbox space every
|
|
1888
|
-
* overlay primitive uses internally) into CSS-pixel coords. Applied
|
|
1889
|
-
* as `transform: scale(coordScale)` on the outer element so individual
|
|
1890
|
-
* overlay components don't need to know about CSS vs DPI space.
|
|
1891
|
-
*/
|
|
1892
|
-
coordScale: number;
|
|
1871
|
+
scale: number;
|
|
1893
1872
|
}
|
|
1894
|
-
declare function CinemaLayer({ page, index, overlays,
|
|
1873
|
+
declare function CinemaLayer({ page, index, overlays, scale, }: CinemaLayerProps): react_jsx_runtime.JSX.Element;
|
|
1895
1874
|
|
|
1896
1875
|
interface CameraViewProps {
|
|
1897
1876
|
camera: CameraState;
|
|
@@ -2086,14 +2065,18 @@ declare class StoryboardEngine {
|
|
|
2086
2065
|
/** Cancel every removal timer (used by resetVisuals and destroy). */
|
|
2087
2066
|
private cancelAllRemovalTimers;
|
|
2088
2067
|
/**
|
|
2089
|
-
* Full destructor — cancels
|
|
2090
|
-
* timers. Use this in component unmount/cleanup.
|
|
2091
|
-
*
|
|
2092
|
-
*
|
|
2093
|
-
*
|
|
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
|
|
2094
2076
|
* keep `deps` (narrationStore, the full bboxIndex) alive beyond the
|
|
2095
|
-
* lifetime of this engine.
|
|
2096
|
-
*
|
|
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.
|
|
2097
2080
|
*/
|
|
2098
2081
|
destroy(): void;
|
|
2099
2082
|
/** Test-only — exposes internal queue sizes for regression tests. */
|
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 isTouchDevice = 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 && isTouchDevice,
|
|
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 isTouchDevice = 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 && isTouchDevice
|
|
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 isTouchDevice = 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 && isTouchDevice
|
|
9987
9987
|
});
|
|
9988
9988
|
const setContainerRef = useCallback31(
|
|
9989
9989
|
(element) => {
|
|
@@ -13374,13 +13374,13 @@ 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
|
|
13378
|
-
const
|
|
13379
|
-
const
|
|
13380
|
-
const svgX = x1 -
|
|
13381
|
-
const svgY = y -
|
|
13382
|
-
const svgW = x2 - x1 + 2 *
|
|
13383
|
-
const svgH =
|
|
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;
|
|
13384
13384
|
return /* @__PURE__ */ jsxs36(
|
|
13385
13385
|
"svg",
|
|
13386
13386
|
{
|
|
@@ -13455,7 +13455,12 @@ function AnimatedUnderline({ bbox, action }) {
|
|
|
13455
13455
|
// src/components/TutorMode/AnimatedHighlight.tsx
|
|
13456
13456
|
import { motion as motion4 } from "framer-motion";
|
|
13457
13457
|
import { jsx as jsx44 } from "react/jsx-runtime";
|
|
13458
|
-
var
|
|
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
|
+
}
|
|
13459
13464
|
function AnimatedHighlight({ bbox, action }) {
|
|
13460
13465
|
const [x1, y1, x2, y2] = bbox;
|
|
13461
13466
|
const h = Math.max(1, y2 - y1);
|
|
@@ -13464,7 +13469,7 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13464
13469
|
const yBot = y2 + bleed;
|
|
13465
13470
|
const duration = action.draw_duration_ms / 1e3;
|
|
13466
13471
|
const isDefaultColour = !action.color || action.color === "rgba(250, 204, 21, 0.35)" || action.color === "rgba(250,204,21,0.35)";
|
|
13467
|
-
const fill = isDefaultColour ?
|
|
13472
|
+
const fill = stripAlpha(isDefaultColour ? DEFAULT_HUE : action.color);
|
|
13468
13473
|
const taper = Math.min(6, h * 0.2);
|
|
13469
13474
|
const pathD = `
|
|
13470
13475
|
M ${x1 - 2} ${yTop + taper}
|
|
@@ -13477,11 +13482,11 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13477
13482
|
L ${x1 - 2} ${yBot - taper}
|
|
13478
13483
|
Z
|
|
13479
13484
|
`;
|
|
13480
|
-
const
|
|
13481
|
-
const svgX = x1 -
|
|
13482
|
-
const svgY = yTop -
|
|
13483
|
-
const svgW = x2 - x1 + 2 *
|
|
13484
|
-
const svgH = yBot - yTop + 2 *
|
|
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;
|
|
13485
13490
|
return /* @__PURE__ */ jsx44(
|
|
13486
13491
|
"svg",
|
|
13487
13492
|
{
|
|
@@ -13501,6 +13506,7 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13501
13506
|
{
|
|
13502
13507
|
d: pathD,
|
|
13503
13508
|
fill,
|
|
13509
|
+
fillOpacity: WASH_OPACITY,
|
|
13504
13510
|
initial: { clipPath: `inset(0 100% 0 0)` },
|
|
13505
13511
|
animate: { clipPath: `inset(0 0% 0 0)` },
|
|
13506
13512
|
exit: { opacity: 0 },
|
|
@@ -13512,7 +13518,6 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13512
13518
|
}
|
|
13513
13519
|
|
|
13514
13520
|
// src/components/TutorMode/PulseOverlay.tsx
|
|
13515
|
-
import { useId as useId2 } from "react";
|
|
13516
13521
|
import { motion as motion5 } from "framer-motion";
|
|
13517
13522
|
import { jsx as jsx45, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
13518
13523
|
var INTENSITY = {
|
|
@@ -13521,7 +13526,6 @@ var INTENSITY = {
|
|
|
13521
13526
|
strong: { bracketLen: 26, strokeWeight: 3, coreOpacity: 1, ringScale: 1.22 }
|
|
13522
13527
|
};
|
|
13523
13528
|
function PulseOverlay({ bbox, action }) {
|
|
13524
|
-
const pulseId = useId2();
|
|
13525
13529
|
const [x1, y1, x2, y2] = bbox;
|
|
13526
13530
|
const w = Math.max(1, x2 - x1);
|
|
13527
13531
|
const h = Math.max(1, y2 - y1);
|
|
@@ -13530,13 +13534,13 @@ function PulseOverlay({ bbox, action }) {
|
|
|
13530
13534
|
const spec = INTENSITY[action.intensity] ?? INTENSITY.normal;
|
|
13531
13535
|
const L = Math.min(spec.bracketLen, Math.min(w, h) / 2.5);
|
|
13532
13536
|
const PAD = 6;
|
|
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;
|
|
13533
13541
|
const bracketExtent = PAD + L + 8;
|
|
13534
|
-
const
|
|
13535
|
-
const
|
|
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);
|
|
13542
|
+
const svgPadX = Math.ceil(Math.max(40, ringExtentX, glowExtentX, bracketExtent));
|
|
13543
|
+
const svgPadY = Math.ceil(Math.max(40, ringExtentY, glowExtentY, bracketExtent));
|
|
13540
13544
|
const svgX = x1 - svgPadX;
|
|
13541
13545
|
const svgY = y1 - svgPadY;
|
|
13542
13546
|
const svgW = w + 2 * svgPadX;
|
|
@@ -13556,22 +13560,18 @@ function PulseOverlay({ bbox, action }) {
|
|
|
13556
13560
|
},
|
|
13557
13561
|
"data-role": "pulse",
|
|
13558
13562
|
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
|
-
] }) }),
|
|
13564
13563
|
/* @__PURE__ */ jsx45(
|
|
13565
13564
|
motion5.ellipse,
|
|
13566
13565
|
{
|
|
13567
13566
|
cx,
|
|
13568
13567
|
cy,
|
|
13569
|
-
rx: w / 2 +
|
|
13570
|
-
ry: h / 2 +
|
|
13571
|
-
fill:
|
|
13568
|
+
rx: w / 2 + 10,
|
|
13569
|
+
ry: h / 2 + 10,
|
|
13570
|
+
fill: ACCENT_GLOW,
|
|
13572
13571
|
style: {
|
|
13573
13572
|
transformOrigin: `${cx}px ${cy}px`,
|
|
13574
|
-
transformBox: "fill-box"
|
|
13573
|
+
transformBox: "fill-box",
|
|
13574
|
+
filter: "blur(16px)"
|
|
13575
13575
|
},
|
|
13576
13576
|
initial: { opacity: 0, scale: 0.95 },
|
|
13577
13577
|
animate: {
|
|
@@ -13702,7 +13702,7 @@ function Bracket({
|
|
|
13702
13702
|
}
|
|
13703
13703
|
|
|
13704
13704
|
// src/components/TutorMode/CalloutArrow.tsx
|
|
13705
|
-
import { useId as
|
|
13705
|
+
import { useId as useId2 } from "react";
|
|
13706
13706
|
import { motion as motion6 } from "framer-motion";
|
|
13707
13707
|
import { jsx as jsx46, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
13708
13708
|
function centerOf(b) {
|
|
@@ -13740,7 +13740,7 @@ function arrowPath(from, to, curve) {
|
|
|
13740
13740
|
return `M ${from.x} ${from.y} Q ${cx} ${cy} ${to.x} ${to.y}`;
|
|
13741
13741
|
}
|
|
13742
13742
|
function CalloutArrow({ fromBbox, toBbox, action }) {
|
|
13743
|
-
const markerId =
|
|
13743
|
+
const markerId = useId2();
|
|
13744
13744
|
const glowId = `${markerId}-glow`;
|
|
13745
13745
|
const { from, to } = edgePoints(fromBbox, toBbox);
|
|
13746
13746
|
const d = arrowPath(from, to, action.curve);
|
|
@@ -13913,9 +13913,8 @@ function CinemaLayer({
|
|
|
13913
13913
|
page,
|
|
13914
13914
|
index,
|
|
13915
13915
|
overlays,
|
|
13916
|
-
|
|
13916
|
+
scale
|
|
13917
13917
|
}) {
|
|
13918
|
-
if (overlays.length === 0) return null;
|
|
13919
13918
|
return /* @__PURE__ */ jsx48(
|
|
13920
13919
|
"div",
|
|
13921
13920
|
{
|
|
@@ -13924,7 +13923,7 @@ function CinemaLayer({
|
|
|
13924
13923
|
position: "absolute",
|
|
13925
13924
|
inset: 0,
|
|
13926
13925
|
transformOrigin: "0 0",
|
|
13927
|
-
transform: `scale(${
|
|
13926
|
+
transform: `scale(${scale})`,
|
|
13928
13927
|
width: page.page_dimensions.width,
|
|
13929
13928
|
height: page.page_dimensions.height,
|
|
13930
13929
|
pointerEvents: "none",
|
|
@@ -14409,10 +14408,14 @@ function StickyLabel({ screenAnchor, action }) {
|
|
|
14409
14408
|
letterSpacing: 0.6,
|
|
14410
14409
|
textTransform: "uppercase",
|
|
14411
14410
|
fontWeight: 500,
|
|
14412
|
-
|
|
14411
|
+
// Wrap instead of truncating with an ellipsis. Short labels stay
|
|
14412
|
+
// single-line naturally; longer ones grow in height rather than
|
|
14413
|
+
// losing their tail. `overflowWrap: 'anywhere'` keeps a stray
|
|
14414
|
+
// long word from pushing the pill past `maxWidth` (normal word
|
|
14415
|
+
// breaking stops at whitespace and can't break inside words).
|
|
14413
14416
|
maxWidth: PILL_MAX_W_BODY,
|
|
14414
|
-
|
|
14415
|
-
|
|
14417
|
+
whiteSpace: "normal",
|
|
14418
|
+
overflowWrap: "anywhere",
|
|
14416
14419
|
// Warm two-layer shadow (matches GhostReference's palette).
|
|
14417
14420
|
boxShadow: "0 1px 2px rgba(42, 36, 32, 0.12), 0 8px 18px -6px rgba(42, 36, 32, 0.22)",
|
|
14418
14421
|
// Internal left accent rule — a 2px terracotta stripe.
|
|
@@ -14514,8 +14517,7 @@ function LabelOverlay({
|
|
|
14514
14517
|
index,
|
|
14515
14518
|
currentPage,
|
|
14516
14519
|
camera,
|
|
14517
|
-
viewport
|
|
14518
|
-
coordScale
|
|
14520
|
+
viewport
|
|
14519
14521
|
}) {
|
|
14520
14522
|
const labels = overlays.filter((o) => o.kind === "label");
|
|
14521
14523
|
const page = index.byPage.get(currentPage);
|
|
@@ -14542,8 +14544,7 @@ function LabelOverlay({
|
|
|
14542
14544
|
a.position,
|
|
14543
14545
|
page,
|
|
14544
14546
|
camera,
|
|
14545
|
-
viewport
|
|
14546
|
-
coordScale
|
|
14547
|
+
viewport
|
|
14547
14548
|
);
|
|
14548
14549
|
return /* @__PURE__ */ jsx52(
|
|
14549
14550
|
StickyLabel,
|
|
@@ -14557,7 +14558,7 @@ function LabelOverlay({
|
|
|
14557
14558
|
}
|
|
14558
14559
|
);
|
|
14559
14560
|
}
|
|
14560
|
-
function computeScreenAnchor(bbox, where, page, camera, viewport
|
|
14561
|
+
function computeScreenAnchor(bbox, where, page, camera, viewport) {
|
|
14561
14562
|
const [x1, y1, x2, y2] = bbox;
|
|
14562
14563
|
const pageCX = page.page_dimensions.width / 2;
|
|
14563
14564
|
const pageCY = page.page_dimensions.height / 2;
|
|
@@ -14583,8 +14584,8 @@ function computeScreenAnchor(bbox, where, page, camera, viewport, coordScale) {
|
|
|
14583
14584
|
px = (x1 + x2) / 2;
|
|
14584
14585
|
py = y1;
|
|
14585
14586
|
}
|
|
14586
|
-
const screenX = viewport.width / 2 + camera.x + (px - pageCX) *
|
|
14587
|
-
const screenY = viewport.height / 2 + camera.y + (py - pageCY) *
|
|
14587
|
+
const screenX = viewport.width / 2 + camera.x + (px - pageCX) * camera.scale;
|
|
14588
|
+
const screenY = viewport.height / 2 + camera.y + (py - pageCY) * camera.scale;
|
|
14588
14589
|
return { x: screenX, y: screenY };
|
|
14589
14590
|
}
|
|
14590
14591
|
|
|
@@ -14596,8 +14597,7 @@ function CalloutLabelOverlay({
|
|
|
14596
14597
|
index,
|
|
14597
14598
|
currentPage,
|
|
14598
14599
|
camera,
|
|
14599
|
-
viewport
|
|
14600
|
-
coordScale
|
|
14600
|
+
viewport
|
|
14601
14601
|
}) {
|
|
14602
14602
|
const callouts = overlays.filter(
|
|
14603
14603
|
(o) => o.kind === "callout" && o.action.label
|
|
@@ -14626,8 +14626,7 @@ function CalloutLabelOverlay({
|
|
|
14626
14626
|
toHit.block.bbox,
|
|
14627
14627
|
page,
|
|
14628
14628
|
camera,
|
|
14629
|
-
viewport
|
|
14630
|
-
coordScale
|
|
14629
|
+
viewport
|
|
14631
14630
|
);
|
|
14632
14631
|
return /* @__PURE__ */ jsx53(
|
|
14633
14632
|
CalloutLabelPill,
|
|
@@ -14642,7 +14641,7 @@ function CalloutLabelOverlay({
|
|
|
14642
14641
|
}
|
|
14643
14642
|
);
|
|
14644
14643
|
}
|
|
14645
|
-
function computePillAnchor(fromBbox, toBbox, page, camera, viewport
|
|
14644
|
+
function computePillAnchor(fromBbox, toBbox, page, camera, viewport) {
|
|
14646
14645
|
const aCX = (fromBbox[0] + fromBbox[2]) / 2;
|
|
14647
14646
|
const aCY = (fromBbox[1] + fromBbox[3]) / 2;
|
|
14648
14647
|
const bCX = (toBbox[0] + toBbox[2]) / 2;
|
|
@@ -14659,8 +14658,8 @@ function computePillAnchor(fromBbox, toBbox, page, camera, viewport, coordScale)
|
|
|
14659
14658
|
const toY = bCY - uy * bOff;
|
|
14660
14659
|
const pageCX = page.page_dimensions.width / 2;
|
|
14661
14660
|
const pageCY = page.page_dimensions.height / 2;
|
|
14662
|
-
const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) *
|
|
14663
|
-
const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) *
|
|
14661
|
+
const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) * camera.scale;
|
|
14662
|
+
const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) * camera.scale;
|
|
14664
14663
|
const isVertical = Math.abs(dy) >= Math.abs(dx);
|
|
14665
14664
|
const OFFSET = resolvePillOffset(viewport.width);
|
|
14666
14665
|
const MAX_PILL_W = resolveMaxPillW(viewport.width);
|
|
@@ -14713,10 +14712,13 @@ function CalloutLabelPill({
|
|
|
14713
14712
|
letterSpacing: 0.6,
|
|
14714
14713
|
textTransform: "uppercase",
|
|
14715
14714
|
fontWeight: 500,
|
|
14716
|
-
|
|
14715
|
+
// Wrap instead of truncating. Short labels stay single-line;
|
|
14716
|
+
// longer ones grow taller rather than losing their tail to an
|
|
14717
|
+
// ellipsis. `overflowWrap: 'anywhere'` guards against a stray
|
|
14718
|
+
// long word pushing the pill past `maxWidth`.
|
|
14717
14719
|
maxWidth: PILL_MAX_W_CAPS,
|
|
14718
|
-
|
|
14719
|
-
|
|
14720
|
+
whiteSpace: "normal",
|
|
14721
|
+
overflowWrap: "anywhere",
|
|
14720
14722
|
boxShadow: "0 1px 2px rgba(42, 36, 32, 0.12), 0 8px 18px -6px rgba(42, 36, 32, 0.22)",
|
|
14721
14723
|
// Accent rule on the "inward" edge (the one closest to the arrow).
|
|
14722
14724
|
backgroundImage: spec.accentGradient,
|
|
@@ -14803,41 +14805,6 @@ function SubtitleBar({ text }) {
|
|
|
14803
14805
|
) : null });
|
|
14804
14806
|
}
|
|
14805
14807
|
|
|
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
|
-
|
|
14841
14808
|
// src/director/storyboard-engine.ts
|
|
14842
14809
|
init_narration_store();
|
|
14843
14810
|
init_camera_math();
|
|
@@ -14934,14 +14901,18 @@ var StoryboardEngine = class {
|
|
|
14934
14901
|
this.overlayRemovalTimers.clear();
|
|
14935
14902
|
}
|
|
14936
14903
|
/**
|
|
14937
|
-
* Full destructor — cancels
|
|
14938
|
-
* timers. Use this in component unmount/cleanup.
|
|
14939
|
-
*
|
|
14940
|
-
*
|
|
14941
|
-
*
|
|
14904
|
+
* Full destructor — cancels BOTH pending step timers AND overlay
|
|
14905
|
+
* removal timers. Use this in component unmount / cleanup.
|
|
14906
|
+
*
|
|
14907
|
+
* The per-storyboard `cancelPending()` deliberately leaves overlay
|
|
14908
|
+
* removal timers alone so a mid-flight overlay doesn't get stranded
|
|
14909
|
+
* when a new storyboard arrives (see `overlayRemovalTimers` doc).
|
|
14910
|
+
* That invariant is correct inside a session, but at teardown we
|
|
14911
|
+
* must release every timer — otherwise their setTimeout closures
|
|
14942
14912
|
* keep `deps` (narrationStore, the full bboxIndex) alive beyond the
|
|
14943
|
-
* lifetime of this engine.
|
|
14944
|
-
*
|
|
14913
|
+
* lifetime of this engine. Over many component recreations on iOS
|
|
14914
|
+
* Safari (viewport state churns during address-bar scroll
|
|
14915
|
+
* animation), that cumulative retention contributes to tab reloads.
|
|
14945
14916
|
*/
|
|
14946
14917
|
destroy() {
|
|
14947
14918
|
this.cancelPending();
|
|
@@ -16094,7 +16065,6 @@ function TutorModeContainer({
|
|
|
16094
16065
|
loadingComponent,
|
|
16095
16066
|
onPageChange,
|
|
16096
16067
|
storyboardProvider,
|
|
16097
|
-
renderScale,
|
|
16098
16068
|
className
|
|
16099
16069
|
}) {
|
|
16100
16070
|
const containerRef = useRef27(null);
|
|
@@ -16109,27 +16079,6 @@ function TutorModeContainer({
|
|
|
16109
16079
|
const [viewport, setViewport] = useState30({ width: 800, height: 1e3 });
|
|
16110
16080
|
const camera = useStore2(narrationStore, (s) => s.camera);
|
|
16111
16081
|
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;
|
|
16133
16082
|
useEffect28(() => {
|
|
16134
16083
|
if (numPages <= 0) return;
|
|
16135
16084
|
if (pageNumber < 1 || pageNumber > numPages) return;
|
|
@@ -16145,13 +16094,7 @@ function TutorModeContainer({
|
|
|
16145
16094
|
useEffect28(() => {
|
|
16146
16095
|
if (!containerRef.current) return;
|
|
16147
16096
|
const el = containerRef.current;
|
|
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
|
-
};
|
|
16097
|
+
const update = () => setViewport({ width: el.clientWidth, height: el.clientHeight });
|
|
16155
16098
|
update();
|
|
16156
16099
|
const ro = new ResizeObserver(update);
|
|
16157
16100
|
ro.observe(el);
|
|
@@ -16179,29 +16122,23 @@ function TutorModeContainer({
|
|
|
16179
16122
|
const page2 = index.byPage.get(pageNumber);
|
|
16180
16123
|
if (!page2) return;
|
|
16181
16124
|
if (viewport.width === 0 || viewport.height === 0) return;
|
|
16182
|
-
if (baseW === 0 || baseH === 0) return;
|
|
16183
16125
|
if (narrationStore.getState().activeOverlays.length > 0) return;
|
|
16184
|
-
const fit = Math.min(
|
|
16126
|
+
const fit = Math.min(
|
|
16127
|
+
viewport.width / page2.page_dimensions.width,
|
|
16128
|
+
viewport.height / page2.page_dimensions.height
|
|
16129
|
+
) * 0.95;
|
|
16185
16130
|
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0 });
|
|
16186
|
-
}, [pageNumber, viewport, index, narrationStore
|
|
16187
|
-
const viewportRef = useRef27(viewport);
|
|
16188
|
-
useEffect28(() => {
|
|
16189
|
-
viewportRef.current = viewport;
|
|
16190
|
-
}, [viewport]);
|
|
16131
|
+
}, [pageNumber, viewport, index, narrationStore]);
|
|
16191
16132
|
const engineRef = useRef27(null);
|
|
16192
16133
|
useEffect28(() => {
|
|
16193
|
-
|
|
16134
|
+
engineRef.current = new StoryboardEngine({
|
|
16194
16135
|
narrationStore,
|
|
16195
16136
|
bboxIndex: index,
|
|
16196
|
-
getViewport: () =>
|
|
16137
|
+
getViewport: () => viewport,
|
|
16197
16138
|
minOverlayDurationMs
|
|
16198
16139
|
});
|
|
16199
|
-
engineRef.current
|
|
16200
|
-
|
|
16201
|
-
engine.destroy();
|
|
16202
|
-
if (engineRef.current === engine) engineRef.current = null;
|
|
16203
|
-
};
|
|
16204
|
-
}, [narrationStore, index, minOverlayDurationMs]);
|
|
16140
|
+
return () => engineRef.current?.destroy();
|
|
16141
|
+
}, [narrationStore, index, viewport, minOverlayDurationMs]);
|
|
16205
16142
|
const abortRef = useRef27(null);
|
|
16206
16143
|
const debounceRef = useRef27(null);
|
|
16207
16144
|
const lastChunkRef = useRef27(null);
|
|
@@ -16382,6 +16319,11 @@ function TutorModeContainer({
|
|
|
16382
16319
|
}, idleTimeoutMs + 100);
|
|
16383
16320
|
return () => clearTimeout(t);
|
|
16384
16321
|
}, [currentChunk, idleTimeoutMs, narrationStore]);
|
|
16322
|
+
const page = index.byPage.get(pageNumber);
|
|
16323
|
+
const dpiScale = page ? page.page_dimensions.dpi / 72 : 1;
|
|
16324
|
+
const rasterScale = dpiScale * (scale || 1);
|
|
16325
|
+
const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
|
|
16326
|
+
const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
|
|
16385
16327
|
const isReady = !!page && !!pageProxy;
|
|
16386
16328
|
return /* @__PURE__ */ jsxs41(
|
|
16387
16329
|
"div",
|
|
@@ -16460,7 +16402,7 @@ function TutorModeContainer({
|
|
|
16460
16402
|
page,
|
|
16461
16403
|
index,
|
|
16462
16404
|
overlays: activeOverlays,
|
|
16463
|
-
|
|
16405
|
+
scale: scale || 1
|
|
16464
16406
|
}
|
|
16465
16407
|
)
|
|
16466
16408
|
]
|
|
@@ -16473,8 +16415,7 @@ function TutorModeContainer({
|
|
|
16473
16415
|
index,
|
|
16474
16416
|
currentPage: pageNumber,
|
|
16475
16417
|
camera,
|
|
16476
|
-
viewport
|
|
16477
|
-
coordScale
|
|
16418
|
+
viewport
|
|
16478
16419
|
}
|
|
16479
16420
|
),
|
|
16480
16421
|
/* @__PURE__ */ jsx55(
|
|
@@ -16484,8 +16425,7 @@ function TutorModeContainer({
|
|
|
16484
16425
|
index,
|
|
16485
16426
|
currentPage: pageNumber,
|
|
16486
16427
|
camera,
|
|
16487
|
-
viewport
|
|
16488
|
-
coordScale
|
|
16428
|
+
viewport
|
|
16489
16429
|
}
|
|
16490
16430
|
),
|
|
16491
16431
|
/* @__PURE__ */ jsx55(GhostReferenceOverlay, { overlays: activeOverlays, index })
|