pdfjs-reader-core 0.4.0 → 0.4.2
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/README.md +7 -0
- package/dist/index.cjs +1598 -453
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +151 -26
- package/dist/index.d.ts +151 -26
- package/dist/index.js +1587 -442
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1885,6 +1885,17 @@ function computeCameraForBlock(bbox, page, viewport, opts = {}) {
|
|
|
1885
1885
|
const y = (pageCY - blockCY) * scale;
|
|
1886
1886
|
return { scale, x, y };
|
|
1887
1887
|
}
|
|
1888
|
+
function clampCamera(target, page, viewport) {
|
|
1889
|
+
const pageWScreen = page.width * target.scale;
|
|
1890
|
+
const pageHScreen = page.height * target.scale;
|
|
1891
|
+
const maxOffsetX = Math.max(0, (pageWScreen - viewport.width) / 2);
|
|
1892
|
+
const maxOffsetY = Math.max(0, (pageHScreen - viewport.height) / 2);
|
|
1893
|
+
return {
|
|
1894
|
+
scale: target.scale,
|
|
1895
|
+
x: Math.max(-maxOffsetX, Math.min(maxOffsetX, target.x)),
|
|
1896
|
+
y: Math.max(-maxOffsetY, Math.min(maxOffsetY, target.y))
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1888
1899
|
var init_camera_math = __esm({
|
|
1889
1900
|
"src/utils/camera-math.ts"() {
|
|
1890
1901
|
"use strict";
|
|
@@ -3748,8 +3759,8 @@ var init_PluginManager = __esm({
|
|
|
3748
3759
|
/**
|
|
3749
3760
|
* Get toolbar items by position
|
|
3750
3761
|
*/
|
|
3751
|
-
getToolbarItemsByPosition(
|
|
3752
|
-
return this.getToolbarItems().filter((item) => item.position ===
|
|
3762
|
+
getToolbarItemsByPosition(position) {
|
|
3763
|
+
return this.getToolbarItems().filter((item) => item.position === position);
|
|
3753
3764
|
}
|
|
3754
3765
|
/**
|
|
3755
3766
|
* Get all sidebar panels from all plugins
|
|
@@ -4862,7 +4873,7 @@ var init_MobileToolbar = __esm({
|
|
|
4862
4873
|
sidebarOpen,
|
|
4863
4874
|
theme,
|
|
4864
4875
|
onThemeChange,
|
|
4865
|
-
position
|
|
4876
|
+
position = "bottom",
|
|
4866
4877
|
className
|
|
4867
4878
|
}) {
|
|
4868
4879
|
const [showMoreMenu, setShowMoreMenu] = (0, import_react17.useState)(false);
|
|
@@ -4896,8 +4907,8 @@ var init_MobileToolbar = __esm({
|
|
|
4896
4907
|
"bg-white dark:bg-gray-800",
|
|
4897
4908
|
"border-gray-200 dark:border-gray-700",
|
|
4898
4909
|
"px-2 py-1 safe-area-inset",
|
|
4899
|
-
|
|
4900
|
-
|
|
4910
|
+
position === "top" && "top-0 border-b",
|
|
4911
|
+
position === "bottom" && "bottom-0 border-t",
|
|
4901
4912
|
className
|
|
4902
4913
|
),
|
|
4903
4914
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between gap-1", children: [
|
|
@@ -5023,7 +5034,7 @@ var init_MobileToolbar = __esm({
|
|
|
5023
5034
|
"bg-white dark:bg-gray-800",
|
|
5024
5035
|
"rounded-lg shadow-lg",
|
|
5025
5036
|
"border border-gray-200 dark:border-gray-700",
|
|
5026
|
-
|
|
5037
|
+
position === "bottom" ? "bottom-full mb-2" : "top-full mt-2"
|
|
5027
5038
|
),
|
|
5028
5039
|
children: [
|
|
5029
5040
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "px-2 py-1 text-xs text-gray-500 dark:text-gray-400 font-medium", children: "Theme" }),
|
|
@@ -6849,7 +6860,7 @@ var init_AnnotationToolbar = __esm({
|
|
|
6849
6860
|
onShapeTypeChange: onShapeTypeChangeProp,
|
|
6850
6861
|
onColorChange: onColorChangeProp,
|
|
6851
6862
|
onStrokeWidthChange: onStrokeWidthChangeProp,
|
|
6852
|
-
position
|
|
6863
|
+
position = "top",
|
|
6853
6864
|
className
|
|
6854
6865
|
}) {
|
|
6855
6866
|
const storeActiveTool = useAnnotationStore((s) => s.activeAnnotationTool);
|
|
@@ -6899,9 +6910,9 @@ var init_AnnotationToolbar = __esm({
|
|
|
6899
6910
|
{
|
|
6900
6911
|
className: cn(
|
|
6901
6912
|
"annotation-toolbar flex items-center gap-1 p-2 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700",
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6913
|
+
position === "floating" && "fixed bottom-20 left-1/2 -translate-x-1/2 z-50",
|
|
6914
|
+
position === "top" && "sticky top-0 z-40",
|
|
6915
|
+
position === "bottom" && "sticky bottom-0 z-40",
|
|
6905
6916
|
!isActive && "opacity-90",
|
|
6906
6917
|
className
|
|
6907
6918
|
),
|
|
@@ -8550,7 +8561,7 @@ var init_SelectionToolbar = __esm({
|
|
|
8550
8561
|
activeColor = "yellow",
|
|
8551
8562
|
className
|
|
8552
8563
|
}) {
|
|
8553
|
-
const [
|
|
8564
|
+
const [position, setPosition] = (0, import_react35.useState)({ top: 0, left: 0, visible: false });
|
|
8554
8565
|
const toolbarRef = (0, import_react35.useRef)(null);
|
|
8555
8566
|
(0, import_react35.useEffect)(() => {
|
|
8556
8567
|
if (selection && selection.text && selection.rects.length > 0) {
|
|
@@ -8589,7 +8600,7 @@ var init_SelectionToolbar = __esm({
|
|
|
8589
8600
|
const handleCopy = (0, import_react35.useCallback)(() => {
|
|
8590
8601
|
onCopy?.();
|
|
8591
8602
|
}, [onCopy]);
|
|
8592
|
-
if (!
|
|
8603
|
+
if (!position.visible || !selection?.text) {
|
|
8593
8604
|
return null;
|
|
8594
8605
|
}
|
|
8595
8606
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
@@ -8607,8 +8618,8 @@ var init_SelectionToolbar = __esm({
|
|
|
8607
8618
|
className
|
|
8608
8619
|
),
|
|
8609
8620
|
style: {
|
|
8610
|
-
top:
|
|
8611
|
-
left:
|
|
8621
|
+
top: position.top,
|
|
8622
|
+
left: position.left,
|
|
8612
8623
|
transform: "translateX(-50%)"
|
|
8613
8624
|
},
|
|
8614
8625
|
onMouseDown: (e) => {
|
|
@@ -8725,7 +8736,7 @@ var init_HighlightPopover = __esm({
|
|
|
8725
8736
|
}) {
|
|
8726
8737
|
const [isEditingComment, setIsEditingComment] = (0, import_react36.useState)(false);
|
|
8727
8738
|
const [comment, setComment] = (0, import_react36.useState)(highlight?.comment ?? "");
|
|
8728
|
-
const [
|
|
8739
|
+
const [position, setPosition] = (0, import_react36.useState)({ top: 0, left: 0, visible: false });
|
|
8729
8740
|
const popoverRef = (0, import_react36.useRef)(null);
|
|
8730
8741
|
const textareaRef = (0, import_react36.useRef)(null);
|
|
8731
8742
|
(0, import_react36.useEffect)(() => {
|
|
@@ -8773,11 +8784,11 @@ var init_HighlightPopover = __esm({
|
|
|
8773
8784
|
onClose();
|
|
8774
8785
|
}
|
|
8775
8786
|
}
|
|
8776
|
-
if (
|
|
8787
|
+
if (position.visible) {
|
|
8777
8788
|
document.addEventListener("mousedown", handleClickOutside);
|
|
8778
8789
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
8779
8790
|
}
|
|
8780
|
-
}, [
|
|
8791
|
+
}, [position.visible, onClose]);
|
|
8781
8792
|
(0, import_react36.useEffect)(() => {
|
|
8782
8793
|
function handleKeyDown(event) {
|
|
8783
8794
|
if (event.key === "Escape") {
|
|
@@ -8789,11 +8800,11 @@ var init_HighlightPopover = __esm({
|
|
|
8789
8800
|
}
|
|
8790
8801
|
}
|
|
8791
8802
|
}
|
|
8792
|
-
if (
|
|
8803
|
+
if (position.visible) {
|
|
8793
8804
|
document.addEventListener("keydown", handleKeyDown);
|
|
8794
8805
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
8795
8806
|
}
|
|
8796
|
-
}, [
|
|
8807
|
+
}, [position.visible, isEditingComment, highlight?.comment, onClose]);
|
|
8797
8808
|
const handleColorClick = (0, import_react36.useCallback)(
|
|
8798
8809
|
(color) => {
|
|
8799
8810
|
if (highlight) {
|
|
@@ -8820,7 +8831,7 @@ var init_HighlightPopover = __esm({
|
|
|
8820
8831
|
onCopy?.(highlight.text);
|
|
8821
8832
|
}
|
|
8822
8833
|
}, [highlight, onCopy]);
|
|
8823
|
-
if (!highlight || !
|
|
8834
|
+
if (!highlight || !position.visible) {
|
|
8824
8835
|
return null;
|
|
8825
8836
|
}
|
|
8826
8837
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
@@ -8837,8 +8848,8 @@ var init_HighlightPopover = __esm({
|
|
|
8837
8848
|
className
|
|
8838
8849
|
),
|
|
8839
8850
|
style: {
|
|
8840
|
-
top:
|
|
8841
|
-
left:
|
|
8851
|
+
top: position.top,
|
|
8852
|
+
left: position.left,
|
|
8842
8853
|
transform: "translate(-50%, -100%)",
|
|
8843
8854
|
width: 280
|
|
8844
8855
|
},
|
|
@@ -10250,7 +10261,7 @@ var init_FloatingZoomControls = __esm({
|
|
|
10250
10261
|
init_utils();
|
|
10251
10262
|
import_jsx_runtime28 = require("react/jsx-runtime");
|
|
10252
10263
|
FloatingZoomControls = (0, import_react42.memo)(function FloatingZoomControls2({
|
|
10253
|
-
position
|
|
10264
|
+
position = "bottom-right",
|
|
10254
10265
|
className,
|
|
10255
10266
|
showFitToWidth = true,
|
|
10256
10267
|
showFitToPage = false,
|
|
@@ -10291,7 +10302,7 @@ var init_FloatingZoomControls = __esm({
|
|
|
10291
10302
|
"bg-white dark:bg-gray-800 rounded-lg shadow-lg",
|
|
10292
10303
|
"border border-gray-200 dark:border-gray-700",
|
|
10293
10304
|
"p-1",
|
|
10294
|
-
positionClasses[
|
|
10305
|
+
positionClasses[position],
|
|
10295
10306
|
className
|
|
10296
10307
|
),
|
|
10297
10308
|
children: [
|
|
@@ -12119,7 +12130,7 @@ var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
|
12119
12130
|
var QuickNoteButton = (0, import_react48.memo)(function QuickNoteButton2({
|
|
12120
12131
|
pageNumber,
|
|
12121
12132
|
scale,
|
|
12122
|
-
position
|
|
12133
|
+
position = "top-right",
|
|
12123
12134
|
onClick,
|
|
12124
12135
|
className,
|
|
12125
12136
|
visible = true
|
|
@@ -12128,11 +12139,11 @@ var QuickNoteButton = (0, import_react48.memo)(function QuickNoteButton2({
|
|
|
12128
12139
|
const handleClick = (0, import_react48.useCallback)(
|
|
12129
12140
|
(e) => {
|
|
12130
12141
|
e.stopPropagation();
|
|
12131
|
-
const x =
|
|
12132
|
-
const y =
|
|
12142
|
+
const x = position === "top-right" ? 80 : 80;
|
|
12143
|
+
const y = position === "top-right" ? 20 : 80;
|
|
12133
12144
|
onClick(pageNumber, x / scale, y / scale);
|
|
12134
12145
|
},
|
|
12135
|
-
[pageNumber, onClick,
|
|
12146
|
+
[pageNumber, onClick, position, scale]
|
|
12136
12147
|
);
|
|
12137
12148
|
if (!visible) {
|
|
12138
12149
|
return null;
|
|
@@ -12153,8 +12164,8 @@ var QuickNoteButton = (0, import_react48.memo)(function QuickNoteButton2({
|
|
|
12153
12164
|
"transition-all duration-200",
|
|
12154
12165
|
"focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2",
|
|
12155
12166
|
isHovered && "scale-110",
|
|
12156
|
-
|
|
12157
|
-
|
|
12167
|
+
position === "top-right" && "top-3 right-3",
|
|
12168
|
+
position === "bottom-right" && "bottom-3 right-3",
|
|
12158
12169
|
className
|
|
12159
12170
|
),
|
|
12160
12171
|
title: "Add quick note",
|
|
@@ -12180,7 +12191,7 @@ init_utils();
|
|
|
12180
12191
|
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
12181
12192
|
var QuickNotePopover = (0, import_react49.memo)(function QuickNotePopover2({
|
|
12182
12193
|
visible,
|
|
12183
|
-
position
|
|
12194
|
+
position,
|
|
12184
12195
|
initialContent = "",
|
|
12185
12196
|
agentLastStatement,
|
|
12186
12197
|
onSave,
|
|
@@ -12190,7 +12201,7 @@ var QuickNotePopover = (0, import_react49.memo)(function QuickNotePopover2({
|
|
|
12190
12201
|
const [content, setContent] = (0, import_react49.useState)(initialContent);
|
|
12191
12202
|
const textareaRef = (0, import_react49.useRef)(null);
|
|
12192
12203
|
const popoverRef = (0, import_react49.useRef)(null);
|
|
12193
|
-
const [adjustedPosition, setAdjustedPosition] = (0, import_react49.useState)(
|
|
12204
|
+
const [adjustedPosition, setAdjustedPosition] = (0, import_react49.useState)(position);
|
|
12194
12205
|
(0, import_react49.useEffect)(() => {
|
|
12195
12206
|
if (visible && textareaRef.current) {
|
|
12196
12207
|
textareaRef.current.focus();
|
|
@@ -12205,7 +12216,7 @@ var QuickNotePopover = (0, import_react49.memo)(function QuickNotePopover2({
|
|
|
12205
12216
|
if (!visible || !popoverRef.current) return;
|
|
12206
12217
|
const rect = popoverRef.current.getBoundingClientRect();
|
|
12207
12218
|
const padding = 10;
|
|
12208
|
-
let { x, y } =
|
|
12219
|
+
let { x, y } = position;
|
|
12209
12220
|
if (x + rect.width > window.innerWidth - padding) {
|
|
12210
12221
|
x = window.innerWidth - rect.width - padding;
|
|
12211
12222
|
}
|
|
@@ -12219,7 +12230,7 @@ var QuickNotePopover = (0, import_react49.memo)(function QuickNotePopover2({
|
|
|
12219
12230
|
y = padding;
|
|
12220
12231
|
}
|
|
12221
12232
|
setAdjustedPosition({ x, y });
|
|
12222
|
-
}, [
|
|
12233
|
+
}, [position, visible]);
|
|
12223
12234
|
const handleSave = (0, import_react49.useCallback)(() => {
|
|
12224
12235
|
if (content.trim()) {
|
|
12225
12236
|
onSave(content.trim());
|
|
@@ -12336,11 +12347,11 @@ var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
|
12336
12347
|
var AskAboutOverlay = (0, import_react50.memo)(function AskAboutOverlay2({
|
|
12337
12348
|
visible,
|
|
12338
12349
|
progress,
|
|
12339
|
-
position
|
|
12350
|
+
position,
|
|
12340
12351
|
size = 60,
|
|
12341
12352
|
className
|
|
12342
12353
|
}) {
|
|
12343
|
-
if (!visible || !
|
|
12354
|
+
if (!visible || !position) {
|
|
12344
12355
|
return null;
|
|
12345
12356
|
}
|
|
12346
12357
|
const strokeWidth = 4;
|
|
@@ -12356,8 +12367,8 @@ var AskAboutOverlay = (0, import_react50.memo)(function AskAboutOverlay2({
|
|
|
12356
12367
|
className
|
|
12357
12368
|
),
|
|
12358
12369
|
style: {
|
|
12359
|
-
left:
|
|
12360
|
-
top:
|
|
12370
|
+
left: position.x - size / 2,
|
|
12371
|
+
top: position.y - size / 2
|
|
12361
12372
|
},
|
|
12362
12373
|
children: [
|
|
12363
12374
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
@@ -12445,20 +12456,20 @@ var import_react51 = require("react");
|
|
|
12445
12456
|
init_utils();
|
|
12446
12457
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
12447
12458
|
var AskAboutTrigger = (0, import_react51.memo)(function AskAboutTrigger2({
|
|
12448
|
-
position
|
|
12459
|
+
position,
|
|
12449
12460
|
onConfirm,
|
|
12450
12461
|
onCancel,
|
|
12451
12462
|
visible,
|
|
12452
12463
|
autoHideDelay = 5e3,
|
|
12453
12464
|
className
|
|
12454
12465
|
}) {
|
|
12455
|
-
const [adjustedPosition, setAdjustedPosition] = (0, import_react51.useState)(
|
|
12466
|
+
const [adjustedPosition, setAdjustedPosition] = (0, import_react51.useState)(position);
|
|
12456
12467
|
const triggerRef = (0, import_react51.useRef)(null);
|
|
12457
12468
|
(0, import_react51.useEffect)(() => {
|
|
12458
12469
|
if (!visible || !triggerRef.current) return;
|
|
12459
12470
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
12460
12471
|
const padding = 10;
|
|
12461
|
-
let { x, y } =
|
|
12472
|
+
let { x, y } = position;
|
|
12462
12473
|
if (x + rect.width / 2 > window.innerWidth - padding) {
|
|
12463
12474
|
x = window.innerWidth - rect.width / 2 - padding;
|
|
12464
12475
|
}
|
|
@@ -12466,10 +12477,10 @@ var AskAboutTrigger = (0, import_react51.memo)(function AskAboutTrigger2({
|
|
|
12466
12477
|
x = rect.width / 2 + padding;
|
|
12467
12478
|
}
|
|
12468
12479
|
if (y + rect.height > window.innerHeight - padding) {
|
|
12469
|
-
y =
|
|
12480
|
+
y = position.y - rect.height - 20;
|
|
12470
12481
|
}
|
|
12471
12482
|
setAdjustedPosition({ x, y });
|
|
12472
|
-
}, [
|
|
12483
|
+
}, [position, visible]);
|
|
12473
12484
|
(0, import_react51.useEffect)(() => {
|
|
12474
12485
|
if (!visible || autoHideDelay === 0) return;
|
|
12475
12486
|
const timer = setTimeout(onCancel, autoHideDelay);
|
|
@@ -13118,7 +13129,7 @@ function withErrorBoundary({ component, ...props }) {
|
|
|
13118
13129
|
init_PDFLoadingScreen2();
|
|
13119
13130
|
|
|
13120
13131
|
// src/components/TutorMode/TutorModeContainer.tsx
|
|
13121
|
-
var
|
|
13132
|
+
var import_react58 = require("react");
|
|
13122
13133
|
var import_zustand2 = require("zustand");
|
|
13123
13134
|
init_PDFPage2();
|
|
13124
13135
|
init_hooks();
|
|
@@ -13158,26 +13169,48 @@ function CameraView({
|
|
|
13158
13169
|
}
|
|
13159
13170
|
|
|
13160
13171
|
// src/components/TutorMode/CinemaLayer.tsx
|
|
13161
|
-
var
|
|
13172
|
+
var import_framer_motion8 = require("framer-motion");
|
|
13162
13173
|
|
|
13163
13174
|
// src/components/TutorMode/SpotlightMask.tsx
|
|
13164
13175
|
var import_react55 = require("react");
|
|
13165
13176
|
var import_framer_motion2 = require("framer-motion");
|
|
13177
|
+
|
|
13178
|
+
// src/components/TutorMode/tokens.ts
|
|
13179
|
+
var INK = "#2a2420";
|
|
13180
|
+
var PAPER = "#faf6ec";
|
|
13181
|
+
var ACCENT = "#b04a1a";
|
|
13182
|
+
var ACCENT_SOFT = "rgba(176, 74, 26, 0.18)";
|
|
13183
|
+
var ACCENT_GLOW = "rgba(176, 74, 26, 0.35)";
|
|
13184
|
+
var MARKER = "#e6b422";
|
|
13185
|
+
var MARKER_SOFT = "rgba(230, 180, 34, 0.38)";
|
|
13186
|
+
var SERIF = "'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', 'EB Garamond', 'Hoefler Text', Georgia, serif";
|
|
13187
|
+
var EASE_OUT_EXPO = [0.22, 1, 0.36, 1];
|
|
13188
|
+
|
|
13189
|
+
// src/components/TutorMode/SpotlightMask.tsx
|
|
13166
13190
|
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
13167
13191
|
function SpotlightMask({
|
|
13168
13192
|
page,
|
|
13169
13193
|
bbox,
|
|
13170
13194
|
action,
|
|
13171
|
-
durationMs =
|
|
13195
|
+
durationMs = 500
|
|
13172
13196
|
}) {
|
|
13173
13197
|
const maskId = (0, import_react55.useId)();
|
|
13174
13198
|
const filterId = `${maskId}-blur`;
|
|
13175
|
-
const [
|
|
13176
|
-
const
|
|
13177
|
-
const
|
|
13178
|
-
const
|
|
13179
|
-
const
|
|
13180
|
-
const
|
|
13199
|
+
const [rawX1, rawY1, rawX2, rawY2] = bbox;
|
|
13200
|
+
const rawW = Math.max(0, rawX2 - rawX1);
|
|
13201
|
+
const rawH = Math.max(0, rawY2 - rawY1);
|
|
13202
|
+
const pad = Math.min(28, Math.max(10, Math.min(rawW, rawH) * 0.06));
|
|
13203
|
+
const x1 = rawX1 - pad;
|
|
13204
|
+
const y1 = rawY1 - pad;
|
|
13205
|
+
const x2 = rawX2 + pad;
|
|
13206
|
+
const y2 = rawY2 + pad;
|
|
13207
|
+
const w = x2 - x1;
|
|
13208
|
+
const h = y2 - y1;
|
|
13209
|
+
const rx = action.shape === "rounded" ? 14 : action.shape === "ellipse" ? w / 2 : 0;
|
|
13210
|
+
const ry = action.shape === "rounded" ? 14 : action.shape === "ellipse" ? h / 2 : 0;
|
|
13211
|
+
const feather = Math.max(16, action.feather_px);
|
|
13212
|
+
const cx = (x1 + x2) / 2;
|
|
13213
|
+
const cy = (y1 + y2) / 2;
|
|
13181
13214
|
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
13182
13215
|
"svg",
|
|
13183
13216
|
{
|
|
@@ -13190,7 +13223,8 @@ function SpotlightMask({
|
|
|
13190
13223
|
inset: 0,
|
|
13191
13224
|
pointerEvents: "none",
|
|
13192
13225
|
width: page.width,
|
|
13193
|
-
height: page.height
|
|
13226
|
+
height: page.height,
|
|
13227
|
+
overflow: "visible"
|
|
13194
13228
|
},
|
|
13195
13229
|
"data-role": "spotlight-mask",
|
|
13196
13230
|
children: [
|
|
@@ -13201,8 +13235,8 @@ function SpotlightMask({
|
|
|
13201
13235
|
action.shape === "ellipse" ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13202
13236
|
"ellipse",
|
|
13203
13237
|
{
|
|
13204
|
-
cx
|
|
13205
|
-
cy
|
|
13238
|
+
cx,
|
|
13239
|
+
cy,
|
|
13206
13240
|
rx: w / 2,
|
|
13207
13241
|
ry: h / 2,
|
|
13208
13242
|
fill: "black",
|
|
@@ -13230,14 +13264,82 @@ function SpotlightMask({
|
|
|
13230
13264
|
y: 0,
|
|
13231
13265
|
width: page.width,
|
|
13232
13266
|
height: page.height,
|
|
13233
|
-
fill:
|
|
13267
|
+
fill: INK,
|
|
13234
13268
|
mask: `url(#${maskId})`,
|
|
13235
13269
|
initial: { fillOpacity: 0 },
|
|
13236
13270
|
animate: { fillOpacity: action.dim_opacity },
|
|
13237
13271
|
exit: { fillOpacity: 0 },
|
|
13238
|
-
transition: { duration: durationMs / 1e3, ease:
|
|
13272
|
+
transition: { duration: durationMs / 1e3, ease: EASE_OUT_EXPO }
|
|
13239
13273
|
}
|
|
13240
|
-
)
|
|
13274
|
+
),
|
|
13275
|
+
action.shape === "ellipse" ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13276
|
+
import_framer_motion2.motion.ellipse,
|
|
13277
|
+
{
|
|
13278
|
+
cx,
|
|
13279
|
+
cy,
|
|
13280
|
+
rx: w / 2,
|
|
13281
|
+
ry: h / 2,
|
|
13282
|
+
fill: "none",
|
|
13283
|
+
stroke: ACCENT,
|
|
13284
|
+
strokeWidth: 3,
|
|
13285
|
+
initial: { opacity: 0, scale: 1.08 },
|
|
13286
|
+
animate: { opacity: 0.9, scale: 1 },
|
|
13287
|
+
exit: { opacity: 0 },
|
|
13288
|
+
style: { transformOrigin: `${cx}px ${cy}px`, transformBox: "fill-box" },
|
|
13289
|
+
transition: {
|
|
13290
|
+
duration: durationMs / 1e3,
|
|
13291
|
+
delay: 0.15,
|
|
13292
|
+
ease: EASE_OUT_EXPO
|
|
13293
|
+
}
|
|
13294
|
+
}
|
|
13295
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13296
|
+
import_framer_motion2.motion.rect,
|
|
13297
|
+
{
|
|
13298
|
+
x: x1,
|
|
13299
|
+
y: y1,
|
|
13300
|
+
width: w,
|
|
13301
|
+
height: h,
|
|
13302
|
+
rx,
|
|
13303
|
+
ry,
|
|
13304
|
+
fill: "none",
|
|
13305
|
+
stroke: ACCENT,
|
|
13306
|
+
strokeWidth: 3,
|
|
13307
|
+
initial: { opacity: 0, scale: 1.04 },
|
|
13308
|
+
animate: { opacity: 0.9, scale: 1 },
|
|
13309
|
+
exit: { opacity: 0 },
|
|
13310
|
+
style: {
|
|
13311
|
+
transformOrigin: `${cx}px ${cy}px`,
|
|
13312
|
+
transformBox: "fill-box"
|
|
13313
|
+
},
|
|
13314
|
+
transition: {
|
|
13315
|
+
duration: durationMs / 1e3,
|
|
13316
|
+
delay: 0.15,
|
|
13317
|
+
ease: EASE_OUT_EXPO
|
|
13318
|
+
}
|
|
13319
|
+
}
|
|
13320
|
+
),
|
|
13321
|
+
action.shape !== "ellipse" ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13322
|
+
import_framer_motion2.motion.rect,
|
|
13323
|
+
{
|
|
13324
|
+
x: x1 - 2,
|
|
13325
|
+
y: y1 - 2,
|
|
13326
|
+
width: w + 4,
|
|
13327
|
+
height: h + 4,
|
|
13328
|
+
rx: rx + 2,
|
|
13329
|
+
ry: ry + 2,
|
|
13330
|
+
fill: "none",
|
|
13331
|
+
stroke: ACCENT_GLOW,
|
|
13332
|
+
strokeWidth: 8,
|
|
13333
|
+
initial: { opacity: 0 },
|
|
13334
|
+
animate: { opacity: 0.6 },
|
|
13335
|
+
exit: { opacity: 0 },
|
|
13336
|
+
transition: {
|
|
13337
|
+
duration: durationMs / 1e3,
|
|
13338
|
+
delay: 0.2,
|
|
13339
|
+
ease: EASE_OUT_EXPO
|
|
13340
|
+
}
|
|
13341
|
+
}
|
|
13342
|
+
) : null
|
|
13241
13343
|
]
|
|
13242
13344
|
}
|
|
13243
13345
|
);
|
|
@@ -13246,35 +13348,58 @@ function SpotlightMask({
|
|
|
13246
13348
|
// src/components/TutorMode/AnimatedUnderline.tsx
|
|
13247
13349
|
var import_framer_motion3 = require("framer-motion");
|
|
13248
13350
|
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
13351
|
+
function jitterAt(i) {
|
|
13352
|
+
const h = Math.sin(i * 12.9898) * 43758.5453;
|
|
13353
|
+
return (h - Math.floor(h) - 0.5) * 4;
|
|
13354
|
+
}
|
|
13249
13355
|
function pathForStyle(x1, x2, y, style) {
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13356
|
+
const x1e = x1 - 4;
|
|
13357
|
+
const x2e = x2 + 4;
|
|
13358
|
+
if (style === "straight") {
|
|
13359
|
+
return { primary: `M ${x1e} ${y} L ${x2e} ${y}` };
|
|
13360
|
+
}
|
|
13361
|
+
if (style === "double") {
|
|
13362
|
+
return {
|
|
13363
|
+
primary: `M ${x1e} ${y - 3} L ${x2e} ${y - 3}`,
|
|
13364
|
+
ghost: `M ${x1e} ${y + 3} L ${x2e} ${y + 3}`
|
|
13365
|
+
};
|
|
13366
|
+
}
|
|
13253
13367
|
if (style === "wavy") {
|
|
13254
|
-
const
|
|
13255
|
-
|
|
13368
|
+
const len = x2e - x1e;
|
|
13369
|
+
const steps = Math.max(12, Math.floor(len / 10));
|
|
13370
|
+
const amp = 3.2;
|
|
13371
|
+
let d = `M ${x1e} ${y}`;
|
|
13256
13372
|
for (let i = 1; i <= steps; i++) {
|
|
13257
|
-
const
|
|
13258
|
-
const
|
|
13259
|
-
|
|
13373
|
+
const t = i / steps;
|
|
13374
|
+
const px = x1e + len * t;
|
|
13375
|
+
const py = y + Math.sin(t * Math.PI * 4) * amp;
|
|
13376
|
+
const prevT = (i - 1) / steps;
|
|
13377
|
+
const cpx = x1e + len * (prevT + (t - prevT) / 2);
|
|
13378
|
+
const cpy = y + Math.sin((prevT + (t - prevT) / 2) * Math.PI * 4) * amp;
|
|
13379
|
+
d += ` Q ${cpx} ${cpy} ${px} ${py}`;
|
|
13260
13380
|
}
|
|
13261
|
-
return
|
|
13381
|
+
return { primary: d };
|
|
13262
13382
|
}
|
|
13263
|
-
const segs =
|
|
13264
|
-
let
|
|
13383
|
+
const segs = 8;
|
|
13384
|
+
let primary = `M ${x1e} ${y + jitterAt(0)}`;
|
|
13385
|
+
let ghost = `M ${x1e} ${y + jitterAt(100) + 1.5}`;
|
|
13265
13386
|
for (let i = 1; i <= segs; i++) {
|
|
13266
|
-
const px =
|
|
13267
|
-
|
|
13268
|
-
|
|
13387
|
+
const px = x1e + (x2e - x1e) * i / segs;
|
|
13388
|
+
primary += ` L ${px} ${y + jitterAt(i)}`;
|
|
13389
|
+
ghost += ` L ${px} ${y + jitterAt(i + 100) + 1.5}`;
|
|
13269
13390
|
}
|
|
13270
|
-
return
|
|
13391
|
+
return { primary, ghost };
|
|
13271
13392
|
}
|
|
13272
13393
|
function AnimatedUnderline({ bbox, action }) {
|
|
13273
13394
|
const [x1, , x2, y2] = bbox;
|
|
13274
13395
|
const y = y2 + 6;
|
|
13275
|
-
const
|
|
13396
|
+
const { primary, ghost } = pathForStyle(x1, x2, y, action.style);
|
|
13276
13397
|
const duration = action.draw_duration_ms / 1e3;
|
|
13277
|
-
|
|
13398
|
+
const stroke = action.color && action.color !== "#FBBF24" ? action.color : ACCENT;
|
|
13399
|
+
const blotX = x2 + 4;
|
|
13400
|
+
const blotY = y;
|
|
13401
|
+
const strokeWeight = action.style === "wavy" ? 3 : 4;
|
|
13402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
13278
13403
|
"svg",
|
|
13279
13404
|
{
|
|
13280
13405
|
style: {
|
|
@@ -13284,50 +13409,148 @@ function AnimatedUnderline({ bbox, action }) {
|
|
|
13284
13409
|
overflow: "visible"
|
|
13285
13410
|
},
|
|
13286
13411
|
"data-role": "underline",
|
|
13287
|
-
children:
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13300
|
-
|
|
13412
|
+
children: [
|
|
13413
|
+
ghost ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
13414
|
+
import_framer_motion3.motion.path,
|
|
13415
|
+
{
|
|
13416
|
+
d: ghost,
|
|
13417
|
+
fill: "none",
|
|
13418
|
+
stroke,
|
|
13419
|
+
strokeWidth: strokeWeight - 1.5,
|
|
13420
|
+
strokeLinecap: "round",
|
|
13421
|
+
strokeOpacity: 0.35,
|
|
13422
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13423
|
+
animate: { pathLength: 1, opacity: 0.55 },
|
|
13424
|
+
exit: { opacity: 0 },
|
|
13425
|
+
transition: { duration, ease: EASE_OUT_EXPO }
|
|
13426
|
+
}
|
|
13427
|
+
) : null,
|
|
13428
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
13429
|
+
import_framer_motion3.motion.path,
|
|
13430
|
+
{
|
|
13431
|
+
d: primary,
|
|
13432
|
+
fill: "none",
|
|
13433
|
+
stroke,
|
|
13434
|
+
strokeWidth: strokeWeight,
|
|
13435
|
+
strokeLinecap: "round",
|
|
13436
|
+
strokeLinejoin: "round",
|
|
13437
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13438
|
+
animate: { pathLength: 1, opacity: 1 },
|
|
13439
|
+
exit: { opacity: 0 },
|
|
13440
|
+
transition: { duration, ease: EASE_OUT_EXPO }
|
|
13441
|
+
}
|
|
13442
|
+
),
|
|
13443
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
13444
|
+
import_framer_motion3.motion.circle,
|
|
13445
|
+
{
|
|
13446
|
+
cx: blotX,
|
|
13447
|
+
cy: blotY,
|
|
13448
|
+
r: strokeWeight / 2 + 0.5,
|
|
13449
|
+
fill: stroke,
|
|
13450
|
+
initial: { scale: 0, opacity: 0 },
|
|
13451
|
+
animate: { scale: 1, opacity: 0.9 },
|
|
13452
|
+
exit: { opacity: 0 },
|
|
13453
|
+
style: {
|
|
13454
|
+
transformOrigin: `${blotX}px ${blotY}px`,
|
|
13455
|
+
transformBox: "fill-box"
|
|
13456
|
+
},
|
|
13457
|
+
transition: {
|
|
13458
|
+
duration: 0.25,
|
|
13459
|
+
delay: duration - 0.1,
|
|
13460
|
+
ease: EASE_OUT_EXPO
|
|
13461
|
+
}
|
|
13462
|
+
}
|
|
13463
|
+
)
|
|
13464
|
+
]
|
|
13301
13465
|
}
|
|
13302
13466
|
);
|
|
13303
13467
|
}
|
|
13304
13468
|
|
|
13305
13469
|
// src/components/TutorMode/AnimatedHighlight.tsx
|
|
13470
|
+
var import_react56 = require("react");
|
|
13306
13471
|
var import_framer_motion4 = require("framer-motion");
|
|
13307
13472
|
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
13308
13473
|
function AnimatedHighlight({ bbox, action }) {
|
|
13309
13474
|
const [x1, y1, x2, y2] = bbox;
|
|
13310
|
-
const
|
|
13311
|
-
const
|
|
13312
|
-
|
|
13313
|
-
|
|
13475
|
+
const h = Math.max(1, y2 - y1);
|
|
13476
|
+
const bleed = Math.min(4, h * 0.12);
|
|
13477
|
+
const yTop = y1 - bleed;
|
|
13478
|
+
const yBot = y2 + bleed;
|
|
13479
|
+
const height = yBot - yTop;
|
|
13480
|
+
const duration = action.draw_duration_ms / 1e3;
|
|
13481
|
+
const filterId = (0, import_react56.useId)();
|
|
13482
|
+
const fill = action.color && action.color !== "rgba(250, 204, 21, 0.35)" && action.color !== "rgba(250,204,21,0.35)" ? action.color : MARKER_SOFT;
|
|
13483
|
+
const inner = action.color && action.color !== "rgba(250, 204, 21, 0.35)" && action.color !== "rgba(250,204,21,0.35)" ? action.color : MARKER;
|
|
13484
|
+
const taper = Math.min(6, h * 0.2);
|
|
13485
|
+
const pathD = `
|
|
13486
|
+
M ${x1 - 2} ${yTop + taper}
|
|
13487
|
+
L ${x1 + 2} ${yTop}
|
|
13488
|
+
L ${x2 - 2} ${yTop}
|
|
13489
|
+
L ${x2 + 2} ${yTop + taper}
|
|
13490
|
+
L ${x2 + 2} ${yBot - taper}
|
|
13491
|
+
L ${x2 - 2} ${yBot}
|
|
13492
|
+
L ${x1 + 2} ${yBot}
|
|
13493
|
+
L ${x1 - 2} ${yBot - taper}
|
|
13494
|
+
Z
|
|
13495
|
+
`;
|
|
13496
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
13497
|
+
"svg",
|
|
13314
13498
|
{
|
|
13315
13499
|
style: {
|
|
13316
13500
|
position: "absolute",
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
borderRadius: 4,
|
|
13322
|
-
mixBlendMode: "multiply",
|
|
13323
|
-
transformOrigin: "0% 50%",
|
|
13324
|
-
pointerEvents: "none"
|
|
13501
|
+
inset: 0,
|
|
13502
|
+
pointerEvents: "none",
|
|
13503
|
+
overflow: "visible",
|
|
13504
|
+
mixBlendMode: "multiply"
|
|
13325
13505
|
},
|
|
13326
|
-
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13506
|
+
"data-role": "highlight",
|
|
13507
|
+
children: [
|
|
13508
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("filter", { id: filterId, children: [
|
|
13509
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
13510
|
+
"feTurbulence",
|
|
13511
|
+
{
|
|
13512
|
+
type: "fractalNoise",
|
|
13513
|
+
baseFrequency: "1.6",
|
|
13514
|
+
numOctaves: "1",
|
|
13515
|
+
seed: 3,
|
|
13516
|
+
result: "noise"
|
|
13517
|
+
}
|
|
13518
|
+
),
|
|
13519
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("feDisplacementMap", { in: "SourceGraphic", in2: "noise", scale: 1.4 })
|
|
13520
|
+
] }) }),
|
|
13521
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
13522
|
+
import_framer_motion4.motion.g,
|
|
13523
|
+
{
|
|
13524
|
+
initial: { clipPath: `inset(0 100% 0 0)` },
|
|
13525
|
+
animate: { clipPath: `inset(0 0% 0 0)` },
|
|
13526
|
+
exit: { opacity: 0 },
|
|
13527
|
+
transition: { duration, ease: EASE_OUT_EXPO },
|
|
13528
|
+
children: [
|
|
13529
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
13530
|
+
"path",
|
|
13531
|
+
{
|
|
13532
|
+
d: pathD,
|
|
13533
|
+
fill,
|
|
13534
|
+
opacity: 0.85,
|
|
13535
|
+
filter: `url(#${filterId})`
|
|
13536
|
+
}
|
|
13537
|
+
),
|
|
13538
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
13539
|
+
"rect",
|
|
13540
|
+
{
|
|
13541
|
+
x: x1 - 1,
|
|
13542
|
+
y: y1 - bleed * 0.4,
|
|
13543
|
+
width: x2 - x1 + 2,
|
|
13544
|
+
height: height - bleed * 0.8,
|
|
13545
|
+
fill: inner,
|
|
13546
|
+
opacity: 0.5,
|
|
13547
|
+
filter: `url(#${filterId})`
|
|
13548
|
+
}
|
|
13549
|
+
)
|
|
13550
|
+
]
|
|
13551
|
+
}
|
|
13552
|
+
)
|
|
13553
|
+
]
|
|
13331
13554
|
}
|
|
13332
13555
|
);
|
|
13333
13556
|
}
|
|
@@ -13336,66 +13559,214 @@ function AnimatedHighlight({ bbox, action }) {
|
|
|
13336
13559
|
var import_framer_motion5 = require("framer-motion");
|
|
13337
13560
|
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
13338
13561
|
var INTENSITY = {
|
|
13339
|
-
subtle: {
|
|
13340
|
-
normal: {
|
|
13341
|
-
strong: {
|
|
13562
|
+
subtle: { bracketLen: 14, strokeWeight: 2, coreOpacity: 0.5, ringScale: 1.08 },
|
|
13563
|
+
normal: { bracketLen: 20, strokeWeight: 2.5, coreOpacity: 0.75, ringScale: 1.14 },
|
|
13564
|
+
strong: { bracketLen: 26, strokeWeight: 3, coreOpacity: 1, ringScale: 1.22 }
|
|
13342
13565
|
};
|
|
13343
13566
|
function PulseOverlay({ bbox, action }) {
|
|
13344
13567
|
const [x1, y1, x2, y2] = bbox;
|
|
13345
|
-
const
|
|
13346
|
-
const
|
|
13347
|
-
|
|
13348
|
-
|
|
13568
|
+
const w = Math.max(1, x2 - x1);
|
|
13569
|
+
const h = Math.max(1, y2 - y1);
|
|
13570
|
+
const cx = (x1 + x2) / 2;
|
|
13571
|
+
const cy = (y1 + y2) / 2;
|
|
13572
|
+
const spec = INTENSITY[action.intensity] ?? INTENSITY.normal;
|
|
13573
|
+
const L = Math.min(spec.bracketLen, Math.min(w, h) / 2.5);
|
|
13574
|
+
const PAD = 6;
|
|
13575
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
13576
|
+
"svg",
|
|
13349
13577
|
{
|
|
13350
13578
|
style: {
|
|
13351
13579
|
position: "absolute",
|
|
13352
|
-
|
|
13353
|
-
top: y1,
|
|
13354
|
-
width: x2 - x1,
|
|
13355
|
-
height: y2 - y1,
|
|
13356
|
-
border,
|
|
13357
|
-
borderRadius: 8,
|
|
13580
|
+
inset: 0,
|
|
13358
13581
|
pointerEvents: "none",
|
|
13359
|
-
|
|
13360
|
-
},
|
|
13361
|
-
animate: { scale: [1, scale, 1] },
|
|
13362
|
-
transition: {
|
|
13363
|
-
duration: 1.2,
|
|
13364
|
-
times: [0, 0.5, 1],
|
|
13365
|
-
ease: "easeInOut",
|
|
13366
|
-
repeat,
|
|
13367
|
-
repeatType: "loop"
|
|
13582
|
+
overflow: "visible"
|
|
13368
13583
|
},
|
|
13584
|
+
"data-role": "pulse",
|
|
13585
|
+
children: [
|
|
13586
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13587
|
+
import_framer_motion5.motion.ellipse,
|
|
13588
|
+
{
|
|
13589
|
+
cx,
|
|
13590
|
+
cy,
|
|
13591
|
+
rx: w / 2 + 10,
|
|
13592
|
+
ry: h / 2 + 10,
|
|
13593
|
+
fill: ACCENT_GLOW,
|
|
13594
|
+
style: {
|
|
13595
|
+
transformOrigin: `${cx}px ${cy}px`,
|
|
13596
|
+
transformBox: "fill-box",
|
|
13597
|
+
filter: "blur(16px)"
|
|
13598
|
+
},
|
|
13599
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
13600
|
+
animate: {
|
|
13601
|
+
opacity: [0, spec.coreOpacity * 0.45, 0.15],
|
|
13602
|
+
scale: [0.95, spec.ringScale, 1]
|
|
13603
|
+
},
|
|
13604
|
+
exit: { opacity: 0 },
|
|
13605
|
+
transition: {
|
|
13606
|
+
duration: 1.3,
|
|
13607
|
+
times: [0, 0.5, 1],
|
|
13608
|
+
ease: EASE_OUT_EXPO
|
|
13609
|
+
}
|
|
13610
|
+
}
|
|
13611
|
+
),
|
|
13612
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13613
|
+
Bracket,
|
|
13614
|
+
{
|
|
13615
|
+
originX: x1 - PAD,
|
|
13616
|
+
originY: y1 - PAD,
|
|
13617
|
+
direction: "tl",
|
|
13618
|
+
length: L,
|
|
13619
|
+
weight: spec.strokeWeight,
|
|
13620
|
+
delay: 0
|
|
13621
|
+
}
|
|
13622
|
+
),
|
|
13623
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13624
|
+
Bracket,
|
|
13625
|
+
{
|
|
13626
|
+
originX: x2 + PAD,
|
|
13627
|
+
originY: y1 - PAD,
|
|
13628
|
+
direction: "tr",
|
|
13629
|
+
length: L,
|
|
13630
|
+
weight: spec.strokeWeight,
|
|
13631
|
+
delay: 0.04
|
|
13632
|
+
}
|
|
13633
|
+
),
|
|
13634
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13635
|
+
Bracket,
|
|
13636
|
+
{
|
|
13637
|
+
originX: x1 - PAD,
|
|
13638
|
+
originY: y2 + PAD,
|
|
13639
|
+
direction: "bl",
|
|
13640
|
+
length: L,
|
|
13641
|
+
weight: spec.strokeWeight,
|
|
13642
|
+
delay: 0.08
|
|
13643
|
+
}
|
|
13644
|
+
),
|
|
13645
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13646
|
+
Bracket,
|
|
13647
|
+
{
|
|
13648
|
+
originX: x2 + PAD,
|
|
13649
|
+
originY: y2 + PAD,
|
|
13650
|
+
direction: "br",
|
|
13651
|
+
length: L,
|
|
13652
|
+
weight: spec.strokeWeight,
|
|
13653
|
+
delay: 0.12
|
|
13654
|
+
}
|
|
13655
|
+
),
|
|
13656
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13657
|
+
import_framer_motion5.motion.rect,
|
|
13658
|
+
{
|
|
13659
|
+
x: x1 - PAD,
|
|
13660
|
+
y: y1 - PAD,
|
|
13661
|
+
width: w + PAD * 2,
|
|
13662
|
+
height: h + PAD * 2,
|
|
13663
|
+
fill: "none",
|
|
13664
|
+
stroke: ACCENT,
|
|
13665
|
+
strokeWidth: 1,
|
|
13666
|
+
rx: 4,
|
|
13667
|
+
style: {
|
|
13668
|
+
transformOrigin: `${cx}px ${cy}px`,
|
|
13669
|
+
transformBox: "fill-box"
|
|
13670
|
+
},
|
|
13671
|
+
initial: { scale: 1, opacity: 0 },
|
|
13672
|
+
animate: {
|
|
13673
|
+
scale: [1, spec.ringScale, 1],
|
|
13674
|
+
opacity: [0, spec.coreOpacity * 0.5, 0]
|
|
13675
|
+
},
|
|
13676
|
+
exit: { opacity: 0 },
|
|
13677
|
+
transition: {
|
|
13678
|
+
duration: 1.3,
|
|
13679
|
+
times: [0, 0.5, 1],
|
|
13680
|
+
ease: EASE_OUT_EXPO,
|
|
13681
|
+
delay: 0.2
|
|
13682
|
+
}
|
|
13683
|
+
}
|
|
13684
|
+
)
|
|
13685
|
+
]
|
|
13686
|
+
}
|
|
13687
|
+
);
|
|
13688
|
+
}
|
|
13689
|
+
function Bracket({
|
|
13690
|
+
originX,
|
|
13691
|
+
originY,
|
|
13692
|
+
direction,
|
|
13693
|
+
length,
|
|
13694
|
+
weight,
|
|
13695
|
+
delay
|
|
13696
|
+
}) {
|
|
13697
|
+
const xSign = direction === "tl" || direction === "bl" ? 1 : -1;
|
|
13698
|
+
const ySign = direction === "tl" || direction === "tr" ? 1 : -1;
|
|
13699
|
+
const d = `
|
|
13700
|
+
M ${originX + xSign * length} ${originY}
|
|
13701
|
+
L ${originX} ${originY}
|
|
13702
|
+
L ${originX} ${originY + ySign * length}
|
|
13703
|
+
`;
|
|
13704
|
+
const slideX = -xSign * 8;
|
|
13705
|
+
const slideY = -ySign * 8;
|
|
13706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13707
|
+
import_framer_motion5.motion.path,
|
|
13708
|
+
{
|
|
13709
|
+
d,
|
|
13710
|
+
fill: "none",
|
|
13711
|
+
stroke: ACCENT,
|
|
13712
|
+
strokeWidth: weight,
|
|
13713
|
+
strokeLinecap: "round",
|
|
13714
|
+
strokeLinejoin: "round",
|
|
13715
|
+
initial: { opacity: 0, x: slideX, y: slideY },
|
|
13716
|
+
animate: { opacity: 1, x: 0, y: 0 },
|
|
13369
13717
|
exit: { opacity: 0 },
|
|
13370
|
-
|
|
13718
|
+
transition: {
|
|
13719
|
+
duration: 0.4,
|
|
13720
|
+
delay,
|
|
13721
|
+
ease: EASE_OUT_EXPO
|
|
13722
|
+
}
|
|
13371
13723
|
}
|
|
13372
13724
|
);
|
|
13373
13725
|
}
|
|
13374
13726
|
|
|
13375
13727
|
// src/components/TutorMode/CalloutArrow.tsx
|
|
13728
|
+
var import_react57 = require("react");
|
|
13376
13729
|
var import_framer_motion6 = require("framer-motion");
|
|
13377
13730
|
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
13378
13731
|
function centerOf(b) {
|
|
13379
13732
|
return { x: (b[0] + b[2]) / 2, y: (b[1] + b[3]) / 2 };
|
|
13380
13733
|
}
|
|
13381
|
-
function
|
|
13734
|
+
function edgePoints(fromBbox, toBbox) {
|
|
13382
13735
|
const a = centerOf(fromBbox);
|
|
13383
13736
|
const b = centerOf(toBbox);
|
|
13384
|
-
if (curve === "straight") return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
13385
|
-
if (curve === "zigzag") {
|
|
13386
|
-
const mx = (a.x + b.x) / 2;
|
|
13387
|
-
return `M ${a.x} ${a.y} L ${mx} ${a.y} L ${mx} ${b.y} L ${b.x} ${b.y}`;
|
|
13388
|
-
}
|
|
13389
13737
|
const dx = b.x - a.x;
|
|
13390
13738
|
const dy = b.y - a.y;
|
|
13391
|
-
const
|
|
13392
|
-
const
|
|
13393
|
-
|
|
13739
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
13740
|
+
const ux = dx / len;
|
|
13741
|
+
const uy = dy / len;
|
|
13742
|
+
const aHalfW = (fromBbox[2] - fromBbox[0]) / 2;
|
|
13743
|
+
const aHalfH = (fromBbox[3] - fromBbox[1]) / 2;
|
|
13744
|
+
const bHalfW = (toBbox[2] - toBbox[0]) / 2;
|
|
13745
|
+
const bHalfH = (toBbox[3] - toBbox[1]) / 2;
|
|
13746
|
+
const aOff = Math.min(Math.max(aHalfW, aHalfH), 60);
|
|
13747
|
+
const bOff = Math.min(Math.max(bHalfW, bHalfH), 60);
|
|
13748
|
+
return {
|
|
13749
|
+
from: { x: a.x + ux * aOff, y: a.y + uy * aOff },
|
|
13750
|
+
to: { x: b.x - ux * bOff, y: b.y - uy * bOff }
|
|
13751
|
+
};
|
|
13752
|
+
}
|
|
13753
|
+
function arrowPath(from, to, curve) {
|
|
13754
|
+
if (curve === "straight") return `M ${from.x} ${from.y} L ${to.x} ${to.y}`;
|
|
13755
|
+
if (curve === "zigzag") {
|
|
13756
|
+
const mx = (from.x + to.x) / 2;
|
|
13757
|
+
return `M ${from.x} ${from.y} L ${mx} ${from.y} L ${mx} ${to.y} L ${to.x} ${to.y}`;
|
|
13758
|
+
}
|
|
13759
|
+
const dx = to.x - from.x;
|
|
13760
|
+
const dy = to.y - from.y;
|
|
13761
|
+
const cx = (from.x + to.x) / 2 - dy * 0.22;
|
|
13762
|
+
const cy = (from.y + to.y) / 2 + dx * 0.22;
|
|
13763
|
+
return `M ${from.x} ${from.y} Q ${cx} ${cy} ${to.x} ${to.y}`;
|
|
13394
13764
|
}
|
|
13395
13765
|
function CalloutArrow({ fromBbox, toBbox, action }) {
|
|
13396
|
-
const
|
|
13397
|
-
const
|
|
13398
|
-
const
|
|
13766
|
+
const markerId = (0, import_react57.useId)();
|
|
13767
|
+
const glowId = `${markerId}-glow`;
|
|
13768
|
+
const { from, to } = edgePoints(fromBbox, toBbox);
|
|
13769
|
+
const d = arrowPath(from, to, action.curve);
|
|
13399
13770
|
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
13400
13771
|
"svg",
|
|
13401
13772
|
{
|
|
@@ -13407,247 +13778,142 @@ function CalloutArrow({ fromBbox, toBbox, action }) {
|
|
|
13407
13778
|
},
|
|
13408
13779
|
"data-role": "callout",
|
|
13409
13780
|
children: [
|
|
13410
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.
|
|
13411
|
-
|
|
13781
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("defs", { children: [
|
|
13782
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13783
|
+
"marker",
|
|
13784
|
+
{
|
|
13785
|
+
id: markerId,
|
|
13786
|
+
viewBox: "0 0 12 10",
|
|
13787
|
+
refX: 10,
|
|
13788
|
+
refY: 5,
|
|
13789
|
+
markerWidth: 10,
|
|
13790
|
+
markerHeight: 10,
|
|
13791
|
+
orient: "auto",
|
|
13792
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13793
|
+
"path",
|
|
13794
|
+
{
|
|
13795
|
+
d: "M 1 1 L 10 5 L 1 9",
|
|
13796
|
+
fill: "none",
|
|
13797
|
+
stroke: ACCENT,
|
|
13798
|
+
strokeWidth: 1.8,
|
|
13799
|
+
strokeLinecap: "round",
|
|
13800
|
+
strokeLinejoin: "round"
|
|
13801
|
+
}
|
|
13802
|
+
)
|
|
13803
|
+
}
|
|
13804
|
+
),
|
|
13805
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("filter", { id: glowId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("feGaussianBlur", { stdDeviation: "2.5" }) })
|
|
13806
|
+
] }),
|
|
13807
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13808
|
+
import_framer_motion6.motion.circle,
|
|
13809
|
+
{
|
|
13810
|
+
cx: from.x,
|
|
13811
|
+
cy: from.y,
|
|
13812
|
+
r: 4,
|
|
13813
|
+
fill: ACCENT,
|
|
13814
|
+
initial: { scale: 0, opacity: 0 },
|
|
13815
|
+
animate: { scale: 1, opacity: 1 },
|
|
13816
|
+
exit: { opacity: 0 },
|
|
13817
|
+
style: {
|
|
13818
|
+
transformOrigin: `${from.x}px ${from.y}px`,
|
|
13819
|
+
transformBox: "fill-box"
|
|
13820
|
+
},
|
|
13821
|
+
transition: { duration: 0.3, ease: EASE_OUT_EXPO }
|
|
13822
|
+
}
|
|
13823
|
+
),
|
|
13824
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13825
|
+
import_framer_motion6.motion.circle,
|
|
13412
13826
|
{
|
|
13413
|
-
|
|
13414
|
-
|
|
13415
|
-
|
|
13416
|
-
|
|
13417
|
-
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13827
|
+
cx: from.x,
|
|
13828
|
+
cy: from.y,
|
|
13829
|
+
r: 7,
|
|
13830
|
+
fill: "none",
|
|
13831
|
+
stroke: ACCENT,
|
|
13832
|
+
strokeWidth: 1.2,
|
|
13833
|
+
strokeOpacity: 0.4,
|
|
13834
|
+
initial: { scale: 0, opacity: 0 },
|
|
13835
|
+
animate: { scale: 1, opacity: 0.5 },
|
|
13836
|
+
exit: { opacity: 0 },
|
|
13837
|
+
style: {
|
|
13838
|
+
transformOrigin: `${from.x}px ${from.y}px`,
|
|
13839
|
+
transformBox: "fill-box"
|
|
13840
|
+
},
|
|
13841
|
+
transition: { duration: 0.4, delay: 0.08, ease: EASE_OUT_EXPO }
|
|
13421
13842
|
}
|
|
13422
|
-
)
|
|
13843
|
+
),
|
|
13423
13844
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13424
13845
|
import_framer_motion6.motion.path,
|
|
13425
13846
|
{
|
|
13426
13847
|
d,
|
|
13427
13848
|
fill: "none",
|
|
13428
|
-
stroke:
|
|
13429
|
-
strokeWidth:
|
|
13849
|
+
stroke: ACCENT,
|
|
13850
|
+
strokeWidth: 6,
|
|
13851
|
+
strokeOpacity: 0.18,
|
|
13430
13852
|
strokeLinecap: "round",
|
|
13431
|
-
|
|
13853
|
+
filter: `url(#${glowId})`,
|
|
13432
13854
|
initial: { pathLength: 0, opacity: 0 },
|
|
13433
|
-
animate: { pathLength: 1, opacity:
|
|
13855
|
+
animate: { pathLength: 1, opacity: 0.8 },
|
|
13434
13856
|
exit: { opacity: 0 },
|
|
13435
|
-
transition: { duration: 0.
|
|
13857
|
+
transition: { duration: 0.7, delay: 0.12, ease: EASE_OUT_EXPO }
|
|
13436
13858
|
}
|
|
13437
13859
|
),
|
|
13438
|
-
|
|
13439
|
-
import_framer_motion6.motion.
|
|
13860
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13861
|
+
import_framer_motion6.motion.path,
|
|
13440
13862
|
{
|
|
13441
|
-
|
|
13442
|
-
|
|
13863
|
+
d,
|
|
13864
|
+
fill: "none",
|
|
13865
|
+
stroke: ACCENT,
|
|
13866
|
+
strokeWidth: 2.4,
|
|
13867
|
+
strokeLinecap: "round",
|
|
13868
|
+
strokeLinejoin: "round",
|
|
13869
|
+
markerEnd: `url(#${markerId})`,
|
|
13870
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13871
|
+
animate: { pathLength: 1, opacity: 1 },
|
|
13443
13872
|
exit: { opacity: 0 },
|
|
13444
|
-
transition: {
|
|
13445
|
-
children: [
|
|
13446
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13447
|
-
"rect",
|
|
13448
|
-
{
|
|
13449
|
-
x: target.x - 4,
|
|
13450
|
-
y: target.y - 28,
|
|
13451
|
-
width: label.length * 9 + 12,
|
|
13452
|
-
height: 22,
|
|
13453
|
-
rx: 4,
|
|
13454
|
-
fill: "#1F2937"
|
|
13455
|
-
}
|
|
13456
|
-
),
|
|
13457
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13458
|
-
"text",
|
|
13459
|
-
{
|
|
13460
|
-
x: target.x + 2,
|
|
13461
|
-
y: target.y - 12,
|
|
13462
|
-
fill: "white",
|
|
13463
|
-
fontSize: 14,
|
|
13464
|
-
fontFamily: "system-ui, sans-serif",
|
|
13465
|
-
children: label
|
|
13466
|
-
}
|
|
13467
|
-
)
|
|
13468
|
-
]
|
|
13873
|
+
transition: { duration: 0.7, delay: 0.15, ease: EASE_OUT_EXPO }
|
|
13469
13874
|
}
|
|
13470
|
-
)
|
|
13875
|
+
)
|
|
13471
13876
|
]
|
|
13472
13877
|
}
|
|
13473
13878
|
);
|
|
13474
13879
|
}
|
|
13475
13880
|
|
|
13476
|
-
// src/components/TutorMode/
|
|
13881
|
+
// src/components/TutorMode/BoxOverlay.tsx
|
|
13477
13882
|
var import_framer_motion7 = require("framer-motion");
|
|
13478
13883
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
"
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
function GhostReference({
|
|
13486
|
-
page,
|
|
13487
|
-
sourceBbox,
|
|
13488
|
-
sourceBlockText,
|
|
13489
|
-
sourcePageNumber,
|
|
13490
|
-
action
|
|
13491
|
-
}) {
|
|
13492
|
-
const width = 360;
|
|
13493
|
-
const [x1, y1, x2, y2] = sourceBbox;
|
|
13494
|
-
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
13884
|
+
function BoxOverlay({ bbox, action }) {
|
|
13885
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13886
|
+
const useSystem = !action.color || action.color === "#3B82F6" || action.color === "#3b82f6";
|
|
13887
|
+
const borderColor = useSystem ? ACCENT : action.color;
|
|
13888
|
+
const washColor = useSystem ? ACCENT_SOFT : void 0;
|
|
13889
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
13495
13890
|
import_framer_motion7.motion.div,
|
|
13496
13891
|
{
|
|
13497
|
-
initial: { opacity: 0,
|
|
13498
|
-
animate: { opacity: 1,
|
|
13499
|
-
exit: { opacity: 0
|
|
13500
|
-
transition: { duration: 0.
|
|
13892
|
+
initial: { opacity: 0, scale: 0.98 },
|
|
13893
|
+
animate: { opacity: 1, scale: 1 },
|
|
13894
|
+
exit: { opacity: 0 },
|
|
13895
|
+
transition: { duration: 0.45, ease: EASE_OUT_EXPO },
|
|
13501
13896
|
style: {
|
|
13502
13897
|
position: "absolute",
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13898
|
+
left: x1 - 3,
|
|
13899
|
+
top: y1 - 3,
|
|
13900
|
+
width: x2 - x1 + 6,
|
|
13901
|
+
height: y2 - y1 + 6,
|
|
13902
|
+
border: `${action.style === "dashed" ? "2px dashed" : "2px solid"} ${borderColor}`,
|
|
13903
|
+
borderRadius: 4,
|
|
13904
|
+
background: washColor,
|
|
13509
13905
|
pointerEvents: "none",
|
|
13510
|
-
|
|
13511
|
-
|
|
13512
|
-
|
|
13906
|
+
boxSizing: "border-box",
|
|
13907
|
+
// Subtle outer glow for the accent version, tinted to match.
|
|
13908
|
+
boxShadow: useSystem ? `0 0 0 1px rgba(176, 74, 26, 0.10), 0 8px 24px -10px rgba(176, 74, 26, 0.35)` : void 0
|
|
13513
13909
|
},
|
|
13514
|
-
"data-role": "
|
|
13515
|
-
children: [
|
|
13516
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { style: { opacity: 0.7, fontSize: 11, marginBottom: 6 }, children: [
|
|
13517
|
-
"Page ",
|
|
13518
|
-
sourcePageNumber,
|
|
13519
|
-
" \u2014 ",
|
|
13520
|
-
action.target_block
|
|
13521
|
-
] }),
|
|
13522
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
13523
|
-
"svg",
|
|
13524
|
-
{
|
|
13525
|
-
width: width - 24,
|
|
13526
|
-
height: 160,
|
|
13527
|
-
viewBox: `0 0 ${page.width} ${page.height}`,
|
|
13528
|
-
style: { background: "#1F2937", borderRadius: 6, display: "block" },
|
|
13529
|
-
preserveAspectRatio: "xMidYMid meet",
|
|
13530
|
-
children: [
|
|
13531
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
13532
|
-
"rect",
|
|
13533
|
-
{
|
|
13534
|
-
x: 0,
|
|
13535
|
-
y: 0,
|
|
13536
|
-
width: page.width,
|
|
13537
|
-
height: page.height,
|
|
13538
|
-
fill: "#1F2937"
|
|
13539
|
-
}
|
|
13540
|
-
),
|
|
13541
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
13542
|
-
"rect",
|
|
13543
|
-
{
|
|
13544
|
-
x: x1,
|
|
13545
|
-
y: y1,
|
|
13546
|
-
width: x2 - x1,
|
|
13547
|
-
height: y2 - y1,
|
|
13548
|
-
fill: "rgba(250,204,21,0.45)",
|
|
13549
|
-
stroke: "#FBBF24",
|
|
13550
|
-
strokeWidth: 8
|
|
13551
|
-
}
|
|
13552
|
-
)
|
|
13553
|
-
]
|
|
13554
|
-
}
|
|
13555
|
-
),
|
|
13556
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
13557
|
-
"div",
|
|
13558
|
-
{
|
|
13559
|
-
style: {
|
|
13560
|
-
marginTop: 8,
|
|
13561
|
-
fontSize: 12,
|
|
13562
|
-
lineHeight: 1.4,
|
|
13563
|
-
opacity: 0.9
|
|
13564
|
-
},
|
|
13565
|
-
children: sourceBlockText ?? "(figure)"
|
|
13566
|
-
}
|
|
13567
|
-
)
|
|
13568
|
-
]
|
|
13569
|
-
}
|
|
13570
|
-
);
|
|
13571
|
-
}
|
|
13572
|
-
|
|
13573
|
-
// src/components/TutorMode/BoxOverlay.tsx
|
|
13574
|
-
var import_framer_motion8 = require("framer-motion");
|
|
13575
|
-
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
13576
|
-
function BoxOverlay({ bbox, action }) {
|
|
13577
|
-
const [x1, y1, x2, y2] = bbox;
|
|
13578
|
-
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
13579
|
-
import_framer_motion8.motion.div,
|
|
13580
|
-
{
|
|
13581
|
-
initial: { opacity: 0, scale: 0.97 },
|
|
13582
|
-
animate: { opacity: 1, scale: 1 },
|
|
13583
|
-
exit: { opacity: 0 },
|
|
13584
|
-
transition: { duration: 0.35, ease: "easeOut" },
|
|
13585
|
-
style: {
|
|
13586
|
-
position: "absolute",
|
|
13587
|
-
left: x1,
|
|
13588
|
-
top: y1,
|
|
13589
|
-
width: x2 - x1,
|
|
13590
|
-
height: y2 - y1,
|
|
13591
|
-
border: `${action.style === "dashed" ? "3px dashed" : "3px solid"} ${action.color}`,
|
|
13592
|
-
borderRadius: 6,
|
|
13593
|
-
pointerEvents: "none",
|
|
13594
|
-
boxSizing: "border-box"
|
|
13595
|
-
},
|
|
13596
|
-
"data-role": "box"
|
|
13597
|
-
}
|
|
13598
|
-
);
|
|
13599
|
-
}
|
|
13600
|
-
|
|
13601
|
-
// src/components/TutorMode/StickyLabel.tsx
|
|
13602
|
-
var import_framer_motion9 = require("framer-motion");
|
|
13603
|
-
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
13604
|
-
function position(bbox, where) {
|
|
13605
|
-
const [x1, y1, x2, y2] = bbox;
|
|
13606
|
-
const cx = (x1 + x2) / 2;
|
|
13607
|
-
const cy = (y1 + y2) / 2;
|
|
13608
|
-
const PAD = 16;
|
|
13609
|
-
switch (where) {
|
|
13610
|
-
case "top":
|
|
13611
|
-
return { left: cx, top: y1 - PAD, transform: "translate(-50%, -100%)" };
|
|
13612
|
-
case "bottom":
|
|
13613
|
-
return { left: cx, top: y2 + PAD, transform: "translate(-50%, 0)" };
|
|
13614
|
-
case "left":
|
|
13615
|
-
return { left: x1 - PAD, top: cy, transform: "translate(-100%, -50%)" };
|
|
13616
|
-
case "right":
|
|
13617
|
-
return { left: x2 + PAD, top: cy, transform: "translate(0, -50%)" };
|
|
13618
|
-
default:
|
|
13619
|
-
return { left: cx, top: y1, transform: "translate(-50%, -100%)" };
|
|
13620
|
-
}
|
|
13621
|
-
}
|
|
13622
|
-
function StickyLabel({ bbox, action }) {
|
|
13623
|
-
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
13624
|
-
import_framer_motion9.motion.div,
|
|
13625
|
-
{
|
|
13626
|
-
initial: { opacity: 0, scale: 0.9 },
|
|
13627
|
-
animate: { opacity: 1, scale: 1 },
|
|
13628
|
-
exit: { opacity: 0 },
|
|
13629
|
-
transition: { duration: 0.35, ease: "easeOut" },
|
|
13630
|
-
style: {
|
|
13631
|
-
position: "absolute",
|
|
13632
|
-
padding: "6px 10px",
|
|
13633
|
-
background: "#FEF3C7",
|
|
13634
|
-
color: "#78350F",
|
|
13635
|
-
borderRadius: 6,
|
|
13636
|
-
boxShadow: "0 3px 10px rgba(0,0,0,0.2)",
|
|
13637
|
-
fontSize: 14,
|
|
13638
|
-
fontFamily: "system-ui, sans-serif",
|
|
13639
|
-
maxWidth: 280,
|
|
13640
|
-
pointerEvents: "none",
|
|
13641
|
-
...position(bbox, action.position)
|
|
13642
|
-
},
|
|
13643
|
-
"data-role": "label",
|
|
13644
|
-
children: action.text
|
|
13910
|
+
"data-role": "box"
|
|
13645
13911
|
}
|
|
13646
13912
|
);
|
|
13647
13913
|
}
|
|
13648
13914
|
|
|
13649
13915
|
// src/components/TutorMode/CinemaLayer.tsx
|
|
13650
|
-
var
|
|
13916
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
13651
13917
|
function blockBbox(index, block_id) {
|
|
13652
13918
|
return index.blockById.get(block_id)?.block.bbox;
|
|
13653
13919
|
}
|
|
@@ -13657,7 +13923,7 @@ function CinemaLayer({
|
|
|
13657
13923
|
overlays,
|
|
13658
13924
|
scale
|
|
13659
13925
|
}) {
|
|
13660
|
-
return /* @__PURE__ */ (0,
|
|
13926
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
13661
13927
|
"div",
|
|
13662
13928
|
{
|
|
13663
13929
|
"data-role": "cinema-layer",
|
|
@@ -13677,13 +13943,13 @@ function CinemaLayer({
|
|
|
13677
13943
|
// reachable because it sits OUTSIDE this stacking context.
|
|
13678
13944
|
zIndex: 100
|
|
13679
13945
|
},
|
|
13680
|
-
children: /* @__PURE__ */ (0,
|
|
13946
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_framer_motion8.AnimatePresence, { children: overlays.map((overlay) => {
|
|
13681
13947
|
switch (overlay.kind) {
|
|
13682
13948
|
case "spotlight": {
|
|
13683
13949
|
const a = overlay.action;
|
|
13684
13950
|
const b = blockBbox(index, a.target_block);
|
|
13685
13951
|
if (!b) return null;
|
|
13686
|
-
return /* @__PURE__ */ (0,
|
|
13952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
13687
13953
|
SpotlightMask,
|
|
13688
13954
|
{
|
|
13689
13955
|
page: page.page_dimensions,
|
|
@@ -13697,26 +13963,26 @@ function CinemaLayer({
|
|
|
13697
13963
|
const a = overlay.action;
|
|
13698
13964
|
const b = blockBbox(index, a.target_block);
|
|
13699
13965
|
if (!b) return null;
|
|
13700
|
-
return /* @__PURE__ */ (0,
|
|
13966
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(AnimatedUnderline, { bbox: b, action: a }, overlay.id);
|
|
13701
13967
|
}
|
|
13702
13968
|
case "highlight": {
|
|
13703
13969
|
const a = overlay.action;
|
|
13704
13970
|
const b = blockBbox(index, a.target_block);
|
|
13705
13971
|
if (!b) return null;
|
|
13706
|
-
return /* @__PURE__ */ (0,
|
|
13972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(AnimatedHighlight, { bbox: b, action: a }, overlay.id);
|
|
13707
13973
|
}
|
|
13708
13974
|
case "pulse": {
|
|
13709
13975
|
const a = overlay.action;
|
|
13710
13976
|
const b = blockBbox(index, a.target_block);
|
|
13711
13977
|
if (!b) return null;
|
|
13712
|
-
return /* @__PURE__ */ (0,
|
|
13978
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PulseOverlay, { bbox: b, action: a }, overlay.id);
|
|
13713
13979
|
}
|
|
13714
13980
|
case "callout": {
|
|
13715
13981
|
const a = overlay.action;
|
|
13716
13982
|
const from = blockBbox(index, a.from_block);
|
|
13717
13983
|
const to = blockBbox(index, a.to_block);
|
|
13718
13984
|
if (!from || !to) return null;
|
|
13719
|
-
return /* @__PURE__ */ (0,
|
|
13985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
13720
13986
|
CalloutArrow,
|
|
13721
13987
|
{
|
|
13722
13988
|
fromBbox: from,
|
|
@@ -13726,36 +13992,16 @@ function CinemaLayer({
|
|
|
13726
13992
|
overlay.id
|
|
13727
13993
|
);
|
|
13728
13994
|
}
|
|
13729
|
-
case "ghost_reference":
|
|
13730
|
-
|
|
13731
|
-
const hit = index.blockById.get(a.target_block);
|
|
13732
|
-
if (!hit) return null;
|
|
13733
|
-
const targetPage = index.byPage.get(a.target_page);
|
|
13734
|
-
if (!targetPage) return null;
|
|
13735
|
-
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
13736
|
-
GhostReference,
|
|
13737
|
-
{
|
|
13738
|
-
page: targetPage.page_dimensions,
|
|
13739
|
-
sourceBbox: hit.block.bbox,
|
|
13740
|
-
sourceBlockText: hit.block.text,
|
|
13741
|
-
sourcePageNumber: hit.pageNumber,
|
|
13742
|
-
action: a
|
|
13743
|
-
},
|
|
13744
|
-
overlay.id
|
|
13745
|
-
);
|
|
13746
|
-
}
|
|
13995
|
+
case "ghost_reference":
|
|
13996
|
+
return null;
|
|
13747
13997
|
case "box": {
|
|
13748
13998
|
const a = overlay.action;
|
|
13749
13999
|
const b = blockBbox(index, a.target_block);
|
|
13750
14000
|
if (!b) return null;
|
|
13751
|
-
return /* @__PURE__ */ (0,
|
|
13752
|
-
}
|
|
13753
|
-
case "label": {
|
|
13754
|
-
const a = overlay.action;
|
|
13755
|
-
const b = blockBbox(index, a.target_block);
|
|
13756
|
-
if (!b) return null;
|
|
13757
|
-
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(StickyLabel, { bbox: b, action: a }, overlay.id);
|
|
14001
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(BoxOverlay, { bbox: b, action: a }, overlay.id);
|
|
13758
14002
|
}
|
|
14003
|
+
case "label":
|
|
14004
|
+
return null;
|
|
13759
14005
|
case "clear":
|
|
13760
14006
|
case "camera":
|
|
13761
14007
|
return null;
|
|
@@ -13765,12 +14011,774 @@ function CinemaLayer({
|
|
|
13765
14011
|
);
|
|
13766
14012
|
}
|
|
13767
14013
|
|
|
13768
|
-
// src/components/TutorMode/
|
|
14014
|
+
// src/components/TutorMode/GhostReferenceOverlay.tsx
|
|
14015
|
+
var import_framer_motion10 = require("framer-motion");
|
|
14016
|
+
|
|
14017
|
+
// src/components/TutorMode/GhostReference.tsx
|
|
14018
|
+
var import_framer_motion9 = require("framer-motion");
|
|
14019
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
14020
|
+
var POSITIONS = {
|
|
14021
|
+
"top-right": {
|
|
14022
|
+
top: "clamp(12px, 4vw, 40px)",
|
|
14023
|
+
right: "clamp(12px, 4vw, 40px)"
|
|
14024
|
+
},
|
|
14025
|
+
"top-left": {
|
|
14026
|
+
top: "clamp(12px, 4vw, 40px)",
|
|
14027
|
+
left: "clamp(12px, 4vw, 40px)"
|
|
14028
|
+
},
|
|
14029
|
+
"bottom-right": {
|
|
14030
|
+
bottom: "clamp(12px, 4vw, 40px)",
|
|
14031
|
+
right: "clamp(12px, 4vw, 40px)"
|
|
14032
|
+
},
|
|
14033
|
+
"bottom-left": {
|
|
14034
|
+
bottom: "clamp(12px, 4vw, 40px)",
|
|
14035
|
+
left: "clamp(12px, 4vw, 40px)"
|
|
14036
|
+
}
|
|
14037
|
+
};
|
|
14038
|
+
var INK2 = "#2a2420";
|
|
14039
|
+
var PAPER2 = "#faf6ec";
|
|
14040
|
+
var PAPER_DEEP = "#f3ece0";
|
|
14041
|
+
var ACCENT2 = "#b04a1a";
|
|
14042
|
+
var RULE = "rgba(42, 36, 32, 0.10)";
|
|
14043
|
+
var SERIF2 = "'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', 'EB Garamond', 'Hoefler Text', Georgia, serif";
|
|
14044
|
+
function GhostReference({
|
|
14045
|
+
page,
|
|
14046
|
+
sourceBbox,
|
|
14047
|
+
sourceBlockText,
|
|
14048
|
+
action
|
|
14049
|
+
}) {
|
|
14050
|
+
const [x1, y1, x2, y2] = sourceBbox;
|
|
14051
|
+
const text = sourceBlockText ?? "(figure)";
|
|
14052
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14053
|
+
import_framer_motion9.motion.div,
|
|
14054
|
+
{
|
|
14055
|
+
initial: { opacity: 0, y: 24, scale: 0.97 },
|
|
14056
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
14057
|
+
exit: { opacity: 0, y: 20, scale: 0.97 },
|
|
14058
|
+
transition: {
|
|
14059
|
+
duration: 0.55,
|
|
14060
|
+
ease: [0.22, 1, 0.36, 1]
|
|
14061
|
+
// custom out-expo for an unhurried settle
|
|
14062
|
+
},
|
|
14063
|
+
style: {
|
|
14064
|
+
position: "absolute",
|
|
14065
|
+
width: "min(420px, calc(100vw - clamp(24px, 8vw, 80px)))",
|
|
14066
|
+
background: PAPER2,
|
|
14067
|
+
color: INK2,
|
|
14068
|
+
border: `1px solid ${RULE}`,
|
|
14069
|
+
// Barely-there corner radius — square-ish corners feel editorial,
|
|
14070
|
+
// 12px+ radius feels SaaS-notification. 3px is the sweet spot.
|
|
14071
|
+
borderRadius: 3,
|
|
14072
|
+
overflow: "hidden",
|
|
14073
|
+
// Warm, two-layer shadow: a tight contact shadow for definition,
|
|
14074
|
+
// a wider diffuse one tinted toward ink rather than pure grey.
|
|
14075
|
+
boxShadow: "0 1px 2px rgba(42, 36, 32, 0.10), 0 20px 44px -14px rgba(42, 36, 32, 0.22), 0 8px 20px -10px rgba(42, 36, 32, 0.14)",
|
|
14076
|
+
pointerEvents: "none",
|
|
14077
|
+
...POSITIONS[action.position]
|
|
14078
|
+
},
|
|
14079
|
+
"data-role": "ghost-reference",
|
|
14080
|
+
children: [
|
|
14081
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14082
|
+
import_framer_motion9.motion.span,
|
|
14083
|
+
{
|
|
14084
|
+
"aria-hidden": true,
|
|
14085
|
+
initial: { scaleY: 0 },
|
|
14086
|
+
animate: { scaleY: 1 },
|
|
14087
|
+
exit: { scaleY: 0 },
|
|
14088
|
+
transition: { duration: 0.55, delay: 0.06, ease: [0.22, 1, 0.36, 1] },
|
|
14089
|
+
style: {
|
|
14090
|
+
position: "absolute",
|
|
14091
|
+
left: 0,
|
|
14092
|
+
top: 0,
|
|
14093
|
+
bottom: 0,
|
|
14094
|
+
width: 3,
|
|
14095
|
+
background: ACCENT2,
|
|
14096
|
+
transformOrigin: "top"
|
|
14097
|
+
}
|
|
14098
|
+
}
|
|
14099
|
+
),
|
|
14100
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14101
|
+
"span",
|
|
14102
|
+
{
|
|
14103
|
+
"aria-hidden": true,
|
|
14104
|
+
style: {
|
|
14105
|
+
position: "absolute",
|
|
14106
|
+
inset: 0,
|
|
14107
|
+
pointerEvents: "none",
|
|
14108
|
+
background: `
|
|
14109
|
+
radial-gradient(120% 80% at 0% 0%, rgba(255, 244, 220, 0.5) 0%, transparent 55%),
|
|
14110
|
+
radial-gradient(100% 80% at 100% 100%, rgba(243, 229, 200, 0.35) 0%, transparent 60%)
|
|
14111
|
+
`
|
|
14112
|
+
}
|
|
14113
|
+
}
|
|
14114
|
+
),
|
|
14115
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14116
|
+
"div",
|
|
14117
|
+
{
|
|
14118
|
+
style: {
|
|
14119
|
+
position: "relative",
|
|
14120
|
+
padding: "20px 22px 20px 26px",
|
|
14121
|
+
display: "flex",
|
|
14122
|
+
gap: 16,
|
|
14123
|
+
alignItems: "flex-start"
|
|
14124
|
+
},
|
|
14125
|
+
children: [
|
|
14126
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14127
|
+
import_framer_motion9.motion.div,
|
|
14128
|
+
{
|
|
14129
|
+
"aria-hidden": true,
|
|
14130
|
+
initial: { opacity: 0, scale: 0.92 },
|
|
14131
|
+
animate: { opacity: 1, scale: 1 },
|
|
14132
|
+
exit: { opacity: 0, scale: 0.92 },
|
|
14133
|
+
transition: { duration: 0.45, delay: 0.18, ease: [0.22, 1, 0.36, 1] },
|
|
14134
|
+
style: {
|
|
14135
|
+
flexShrink: 0,
|
|
14136
|
+
width: 46,
|
|
14137
|
+
aspectRatio: `${page.width} / ${page.height}`,
|
|
14138
|
+
background: PAPER_DEEP,
|
|
14139
|
+
borderRadius: 2,
|
|
14140
|
+
overflow: "hidden",
|
|
14141
|
+
boxShadow: "inset 0 0 0 1px rgba(42, 36, 32, 0.12), 0 1px 3px rgba(42, 36, 32, 0.10)"
|
|
14142
|
+
},
|
|
14143
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14144
|
+
"svg",
|
|
14145
|
+
{
|
|
14146
|
+
width: "100%",
|
|
14147
|
+
height: "100%",
|
|
14148
|
+
viewBox: `0 0 ${page.width} ${page.height}`,
|
|
14149
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
14150
|
+
style: { display: "block" },
|
|
14151
|
+
children: [
|
|
14152
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14153
|
+
"rect",
|
|
14154
|
+
{
|
|
14155
|
+
x: 0,
|
|
14156
|
+
y: 0,
|
|
14157
|
+
width: page.width,
|
|
14158
|
+
height: page.height,
|
|
14159
|
+
fill: PAPER_DEEP
|
|
14160
|
+
}
|
|
14161
|
+
),
|
|
14162
|
+
TEXT_LINES.map((ln, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14163
|
+
"rect",
|
|
14164
|
+
{
|
|
14165
|
+
x: page.width * ln.x,
|
|
14166
|
+
y: page.height * ln.y,
|
|
14167
|
+
width: page.width * ln.w,
|
|
14168
|
+
height: page.height * 0.012,
|
|
14169
|
+
fill: "rgba(42, 36, 32, 0.18)"
|
|
14170
|
+
},
|
|
14171
|
+
i
|
|
14172
|
+
)),
|
|
14173
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14174
|
+
"rect",
|
|
14175
|
+
{
|
|
14176
|
+
x: x1,
|
|
14177
|
+
y: y1,
|
|
14178
|
+
width: x2 - x1,
|
|
14179
|
+
height: y2 - y1,
|
|
14180
|
+
fill: "rgba(176, 74, 26, 0.28)",
|
|
14181
|
+
stroke: ACCENT2,
|
|
14182
|
+
strokeWidth: 18
|
|
14183
|
+
}
|
|
14184
|
+
)
|
|
14185
|
+
]
|
|
14186
|
+
}
|
|
14187
|
+
)
|
|
14188
|
+
}
|
|
14189
|
+
),
|
|
14190
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
14191
|
+
import_framer_motion9.motion.div,
|
|
14192
|
+
{
|
|
14193
|
+
initial: { opacity: 0, y: 6 },
|
|
14194
|
+
animate: { opacity: 1, y: 0 },
|
|
14195
|
+
exit: { opacity: 0 },
|
|
14196
|
+
transition: { duration: 0.5, delay: 0.26, ease: [0.22, 1, 0.36, 1] },
|
|
14197
|
+
style: {
|
|
14198
|
+
flex: 1,
|
|
14199
|
+
minWidth: 0,
|
|
14200
|
+
fontFamily: SERIF2,
|
|
14201
|
+
fontSize: 15.5,
|
|
14202
|
+
lineHeight: 1.55,
|
|
14203
|
+
color: INK2,
|
|
14204
|
+
fontFeatureSettings: "'liga' 1, 'kern' 1, 'onum' 1",
|
|
14205
|
+
textRendering: "optimizeLegibility",
|
|
14206
|
+
letterSpacing: 0.05,
|
|
14207
|
+
// Hang an ornamental opening glyph outside the text column
|
|
14208
|
+
// so the reader's eye falls into the paragraph as if into a
|
|
14209
|
+
// well-set pull quote.
|
|
14210
|
+
position: "relative",
|
|
14211
|
+
paddingLeft: 2
|
|
14212
|
+
},
|
|
14213
|
+
children: [
|
|
14214
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14215
|
+
"span",
|
|
14216
|
+
{
|
|
14217
|
+
"aria-hidden": true,
|
|
14218
|
+
style: {
|
|
14219
|
+
position: "absolute",
|
|
14220
|
+
left: -14,
|
|
14221
|
+
top: -2,
|
|
14222
|
+
color: ACCENT2,
|
|
14223
|
+
fontSize: 22,
|
|
14224
|
+
lineHeight: 1,
|
|
14225
|
+
fontWeight: 500
|
|
14226
|
+
// ornamental flourish anchoring the paragraph
|
|
14227
|
+
},
|
|
14228
|
+
children: "\u2767"
|
|
14229
|
+
}
|
|
14230
|
+
),
|
|
14231
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14232
|
+
"span",
|
|
14233
|
+
{
|
|
14234
|
+
style: {
|
|
14235
|
+
display: "-webkit-box",
|
|
14236
|
+
WebkitLineClamp: 8,
|
|
14237
|
+
WebkitBoxOrient: "vertical",
|
|
14238
|
+
overflow: "hidden"
|
|
14239
|
+
},
|
|
14240
|
+
children: text
|
|
14241
|
+
}
|
|
14242
|
+
)
|
|
14243
|
+
]
|
|
14244
|
+
}
|
|
14245
|
+
)
|
|
14246
|
+
]
|
|
14247
|
+
}
|
|
14248
|
+
),
|
|
14249
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
14250
|
+
import_framer_motion9.motion.span,
|
|
14251
|
+
{
|
|
14252
|
+
"aria-hidden": true,
|
|
14253
|
+
initial: { scaleX: 0, opacity: 0 },
|
|
14254
|
+
animate: { scaleX: 1, opacity: 0.6 },
|
|
14255
|
+
exit: { opacity: 0 },
|
|
14256
|
+
transition: { duration: 0.5, delay: 0.42, ease: [0.22, 1, 0.36, 1] },
|
|
14257
|
+
style: {
|
|
14258
|
+
position: "absolute",
|
|
14259
|
+
right: 22,
|
|
14260
|
+
bottom: 10,
|
|
14261
|
+
height: 1,
|
|
14262
|
+
width: 28,
|
|
14263
|
+
background: ACCENT2,
|
|
14264
|
+
transformOrigin: "right"
|
|
14265
|
+
}
|
|
14266
|
+
}
|
|
14267
|
+
)
|
|
14268
|
+
]
|
|
14269
|
+
}
|
|
14270
|
+
);
|
|
14271
|
+
}
|
|
14272
|
+
var TEXT_LINES = [
|
|
14273
|
+
{ x: 0.1, y: 0.12, w: 0.54 },
|
|
14274
|
+
{ x: 0.1, y: 0.18, w: 0.68 },
|
|
14275
|
+
{ x: 0.1, y: 0.24, w: 0.48 },
|
|
14276
|
+
{ x: 0.1, y: 0.34, w: 0.72 },
|
|
14277
|
+
{ x: 0.1, y: 0.4, w: 0.62 },
|
|
14278
|
+
{ x: 0.1, y: 0.46, w: 0.66 },
|
|
14279
|
+
{ x: 0.1, y: 0.56, w: 0.56 },
|
|
14280
|
+
{ x: 0.1, y: 0.62, w: 0.7 },
|
|
14281
|
+
{ x: 0.1, y: 0.68, w: 0.44 },
|
|
14282
|
+
{ x: 0.1, y: 0.78, w: 0.64 },
|
|
14283
|
+
{ x: 0.1, y: 0.84, w: 0.58 }
|
|
14284
|
+
];
|
|
14285
|
+
|
|
14286
|
+
// src/components/TutorMode/GhostReferenceOverlay.tsx
|
|
14287
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
14288
|
+
function GhostReferenceOverlay({
|
|
14289
|
+
overlays,
|
|
14290
|
+
index
|
|
14291
|
+
}) {
|
|
14292
|
+
const ghosts = overlays.filter((o) => o.kind === "ghost_reference");
|
|
14293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
14294
|
+
"div",
|
|
14295
|
+
{
|
|
14296
|
+
"data-role": "ghost-reference-overlay",
|
|
14297
|
+
style: {
|
|
14298
|
+
position: "absolute",
|
|
14299
|
+
inset: 0,
|
|
14300
|
+
pointerEvents: "none",
|
|
14301
|
+
// Sits above the Reset view button (z-60) so a ghost card doesn't
|
|
14302
|
+
// disappear behind it if the host also renders the Reset button.
|
|
14303
|
+
zIndex: 70
|
|
14304
|
+
},
|
|
14305
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_framer_motion10.AnimatePresence, { children: ghosts.map((overlay) => {
|
|
14306
|
+
const a = overlay.action;
|
|
14307
|
+
const hit = index.blockById.get(a.target_block);
|
|
14308
|
+
if (!hit) return null;
|
|
14309
|
+
const targetPage = index.byPage.get(a.target_page);
|
|
14310
|
+
if (!targetPage) return null;
|
|
14311
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
14312
|
+
GhostReference,
|
|
14313
|
+
{
|
|
14314
|
+
page: targetPage.page_dimensions,
|
|
14315
|
+
sourceBbox: hit.block.bbox,
|
|
14316
|
+
sourceBlockText: hit.block.text,
|
|
14317
|
+
sourcePageNumber: hit.pageNumber,
|
|
14318
|
+
action: a
|
|
14319
|
+
},
|
|
14320
|
+
overlay.id
|
|
14321
|
+
);
|
|
14322
|
+
}) })
|
|
14323
|
+
}
|
|
14324
|
+
);
|
|
14325
|
+
}
|
|
14326
|
+
|
|
14327
|
+
// src/components/TutorMode/LabelOverlay.tsx
|
|
14328
|
+
var import_framer_motion12 = require("framer-motion");
|
|
14329
|
+
|
|
14330
|
+
// src/components/TutorMode/StickyLabel.tsx
|
|
13769
14331
|
var import_framer_motion11 = require("framer-motion");
|
|
13770
14332
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
13771
|
-
|
|
13772
|
-
|
|
14333
|
+
var INK3 = "#2a2420";
|
|
14334
|
+
var PAPER3 = "#faf6ec";
|
|
14335
|
+
var ACCENT3 = "#b04a1a";
|
|
14336
|
+
var SERIF3 = "'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', 'EB Garamond', 'Hoefler Text', Georgia, serif";
|
|
14337
|
+
var STEM = 18;
|
|
14338
|
+
function StickyLabel({ screenAnchor, action }) {
|
|
14339
|
+
const { x, y } = screenAnchor;
|
|
14340
|
+
const layout = LAYOUTS[action.position] ?? LAYOUTS.top;
|
|
14341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
|
|
13773
14342
|
import_framer_motion11.motion.div,
|
|
14343
|
+
{
|
|
14344
|
+
initial: { opacity: 0, scale: 0.88 },
|
|
14345
|
+
animate: { opacity: 1, scale: 1 },
|
|
14346
|
+
exit: { opacity: 0, scale: 0.92 },
|
|
14347
|
+
transition: {
|
|
14348
|
+
duration: 0.4,
|
|
14349
|
+
ease: [0.22, 1, 0.36, 1]
|
|
14350
|
+
},
|
|
14351
|
+
style: {
|
|
14352
|
+
position: "absolute",
|
|
14353
|
+
left: x,
|
|
14354
|
+
top: y,
|
|
14355
|
+
// Wrapper positions at the anchor; the body's transform places
|
|
14356
|
+
// it on the correct side via `layout.containerTransform`.
|
|
14357
|
+
transform: layout.containerTransform,
|
|
14358
|
+
pointerEvents: "none",
|
|
14359
|
+
// Compose transform-origin toward the anchor so scale-in feels
|
|
14360
|
+
// tethered to the block rather than free-floating.
|
|
14361
|
+
transformOrigin: layout.origin
|
|
14362
|
+
},
|
|
14363
|
+
"data-role": "label",
|
|
14364
|
+
children: [
|
|
14365
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
14366
|
+
import_framer_motion11.motion.span,
|
|
14367
|
+
{
|
|
14368
|
+
"aria-hidden": true,
|
|
14369
|
+
initial: { scaleX: 0, scaleY: 0, opacity: 0 },
|
|
14370
|
+
animate: { scaleX: 1, scaleY: 1, opacity: 1 },
|
|
14371
|
+
exit: { opacity: 0 },
|
|
14372
|
+
transition: { duration: 0.35, ease: [0.22, 1, 0.36, 1] },
|
|
14373
|
+
style: {
|
|
14374
|
+
position: "absolute",
|
|
14375
|
+
background: ACCENT3,
|
|
14376
|
+
transformOrigin: layout.stemOrigin,
|
|
14377
|
+
...layout.stem
|
|
14378
|
+
}
|
|
14379
|
+
}
|
|
14380
|
+
),
|
|
14381
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
14382
|
+
import_framer_motion11.motion.span,
|
|
14383
|
+
{
|
|
14384
|
+
"aria-hidden": true,
|
|
14385
|
+
initial: { scale: 0 },
|
|
14386
|
+
animate: { scale: 1 },
|
|
14387
|
+
exit: { scale: 0 },
|
|
14388
|
+
transition: { duration: 0.3, delay: 0.15, ease: [0.22, 1, 0.36, 1] },
|
|
14389
|
+
style: {
|
|
14390
|
+
position: "absolute",
|
|
14391
|
+
width: 6,
|
|
14392
|
+
height: 6,
|
|
14393
|
+
borderRadius: "50%",
|
|
14394
|
+
background: ACCENT3,
|
|
14395
|
+
boxShadow: `0 0 0 2px ${PAPER3}, 0 0 0 3px rgba(176, 74, 26, 0.25)`,
|
|
14396
|
+
...layout.dot
|
|
14397
|
+
}
|
|
14398
|
+
}
|
|
14399
|
+
),
|
|
14400
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
14401
|
+
import_framer_motion11.motion.div,
|
|
14402
|
+
{
|
|
14403
|
+
initial: { y: layout.bodyIn.y, x: layout.bodyIn.x, opacity: 0 },
|
|
14404
|
+
animate: { y: 0, x: 0, opacity: 1 },
|
|
14405
|
+
exit: { opacity: 0 },
|
|
14406
|
+
transition: { duration: 0.4, delay: 0.08, ease: [0.22, 1, 0.36, 1] },
|
|
14407
|
+
style: {
|
|
14408
|
+
position: "absolute",
|
|
14409
|
+
...layout.bodyAnchor,
|
|
14410
|
+
background: PAPER3,
|
|
14411
|
+
color: INK3,
|
|
14412
|
+
border: "1px solid rgba(42, 36, 32, 0.10)",
|
|
14413
|
+
borderRadius: 3,
|
|
14414
|
+
padding: "6px 12px 6px 14px",
|
|
14415
|
+
fontFamily: SERIF3,
|
|
14416
|
+
fontSize: 12.5,
|
|
14417
|
+
lineHeight: 1.25,
|
|
14418
|
+
letterSpacing: 0.6,
|
|
14419
|
+
textTransform: "uppercase",
|
|
14420
|
+
fontWeight: 500,
|
|
14421
|
+
whiteSpace: "nowrap",
|
|
14422
|
+
maxWidth: 240,
|
|
14423
|
+
overflow: "hidden",
|
|
14424
|
+
textOverflow: "ellipsis",
|
|
14425
|
+
// Warm two-layer shadow (matches GhostReference's palette).
|
|
14426
|
+
boxShadow: "0 1px 2px rgba(42, 36, 32, 0.12), 0 8px 18px -6px rgba(42, 36, 32, 0.22)",
|
|
14427
|
+
// Internal left accent rule — a 2px terracotta stripe.
|
|
14428
|
+
backgroundImage: `linear-gradient(to right, ${ACCENT3} 0, ${ACCENT3} 2px, transparent 2px)`,
|
|
14429
|
+
backgroundRepeat: "no-repeat",
|
|
14430
|
+
backgroundSize: "2px 100%",
|
|
14431
|
+
backgroundPosition: "left top"
|
|
14432
|
+
},
|
|
14433
|
+
children: action.text
|
|
14434
|
+
}
|
|
14435
|
+
)
|
|
14436
|
+
]
|
|
14437
|
+
}
|
|
14438
|
+
);
|
|
14439
|
+
}
|
|
14440
|
+
var LAYOUTS = {
|
|
14441
|
+
top: {
|
|
14442
|
+
containerTransform: "translate(-50%, -100%)",
|
|
14443
|
+
origin: "50% 100%",
|
|
14444
|
+
stem: {
|
|
14445
|
+
left: "50%",
|
|
14446
|
+
bottom: -STEM,
|
|
14447
|
+
width: 1,
|
|
14448
|
+
height: STEM - 4,
|
|
14449
|
+
transform: "translateX(-50%)"
|
|
14450
|
+
},
|
|
14451
|
+
stemOrigin: "bottom",
|
|
14452
|
+
dot: {
|
|
14453
|
+
left: "50%",
|
|
14454
|
+
bottom: -STEM + 1,
|
|
14455
|
+
transform: "translate(-50%, 100%)"
|
|
14456
|
+
},
|
|
14457
|
+
bodyAnchor: { bottom: 0, left: "50%", transform: "translateX(-50%)" },
|
|
14458
|
+
bodyIn: { x: 0, y: -4 }
|
|
14459
|
+
},
|
|
14460
|
+
bottom: {
|
|
14461
|
+
containerTransform: "translate(-50%, 0%)",
|
|
14462
|
+
origin: "50% 0%",
|
|
14463
|
+
stem: {
|
|
14464
|
+
left: "50%",
|
|
14465
|
+
top: STEM - (STEM - 4),
|
|
14466
|
+
width: 1,
|
|
14467
|
+
height: STEM - 4,
|
|
14468
|
+
transform: "translateX(-50%)"
|
|
14469
|
+
},
|
|
14470
|
+
stemOrigin: "top",
|
|
14471
|
+
dot: {
|
|
14472
|
+
left: "50%",
|
|
14473
|
+
top: -1,
|
|
14474
|
+
transform: "translate(-50%, -100%)"
|
|
14475
|
+
},
|
|
14476
|
+
bodyAnchor: { top: STEM, left: "50%", transform: "translateX(-50%)" },
|
|
14477
|
+
bodyIn: { x: 0, y: 4 }
|
|
14478
|
+
},
|
|
14479
|
+
left: {
|
|
14480
|
+
containerTransform: "translate(-100%, -50%)",
|
|
14481
|
+
origin: "100% 50%",
|
|
14482
|
+
stem: {
|
|
14483
|
+
top: "50%",
|
|
14484
|
+
right: -STEM,
|
|
14485
|
+
width: STEM - 4,
|
|
14486
|
+
height: 1,
|
|
14487
|
+
transform: "translateY(-50%)"
|
|
14488
|
+
},
|
|
14489
|
+
stemOrigin: "right",
|
|
14490
|
+
dot: {
|
|
14491
|
+
right: -STEM + 1,
|
|
14492
|
+
top: "50%",
|
|
14493
|
+
transform: "translate(100%, -50%)"
|
|
14494
|
+
},
|
|
14495
|
+
bodyAnchor: { right: 0, top: "50%", transform: "translateY(-50%)" },
|
|
14496
|
+
bodyIn: { x: -4, y: 0 }
|
|
14497
|
+
},
|
|
14498
|
+
right: {
|
|
14499
|
+
containerTransform: "translate(0%, -50%)",
|
|
14500
|
+
origin: "0% 50%",
|
|
14501
|
+
stem: {
|
|
14502
|
+
top: "50%",
|
|
14503
|
+
left: STEM - (STEM - 4),
|
|
14504
|
+
width: STEM - 4,
|
|
14505
|
+
height: 1,
|
|
14506
|
+
transform: "translateY(-50%)"
|
|
14507
|
+
},
|
|
14508
|
+
stemOrigin: "left",
|
|
14509
|
+
dot: {
|
|
14510
|
+
left: -1,
|
|
14511
|
+
top: "50%",
|
|
14512
|
+
transform: "translate(-100%, -50%)"
|
|
14513
|
+
},
|
|
14514
|
+
bodyAnchor: { left: STEM, top: "50%", transform: "translateY(-50%)" },
|
|
14515
|
+
bodyIn: { x: 4, y: 0 }
|
|
14516
|
+
}
|
|
14517
|
+
};
|
|
14518
|
+
|
|
14519
|
+
// src/components/TutorMode/LabelOverlay.tsx
|
|
14520
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
14521
|
+
function LabelOverlay({
|
|
14522
|
+
overlays,
|
|
14523
|
+
index,
|
|
14524
|
+
currentPage,
|
|
14525
|
+
camera,
|
|
14526
|
+
viewport
|
|
14527
|
+
}) {
|
|
14528
|
+
const labels = overlays.filter((o) => o.kind === "label");
|
|
14529
|
+
const page = index.byPage.get(currentPage);
|
|
14530
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
14531
|
+
"div",
|
|
14532
|
+
{
|
|
14533
|
+
"data-role": "label-overlay",
|
|
14534
|
+
style: {
|
|
14535
|
+
position: "absolute",
|
|
14536
|
+
inset: 0,
|
|
14537
|
+
pointerEvents: "none",
|
|
14538
|
+
overflow: "hidden",
|
|
14539
|
+
// Above CameraView and the Reset button but not above
|
|
14540
|
+
// GhostReferenceOverlay (z:70) — labels are block-attached and
|
|
14541
|
+
// should not cover a cross-page reference card.
|
|
14542
|
+
zIndex: 65
|
|
14543
|
+
},
|
|
14544
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_framer_motion12.AnimatePresence, { children: page ? labels.map((overlay) => {
|
|
14545
|
+
const a = overlay.action;
|
|
14546
|
+
const hit = index.blockById.get(a.target_block);
|
|
14547
|
+
if (!hit) return null;
|
|
14548
|
+
const anchor = computeScreenAnchor(
|
|
14549
|
+
hit.block.bbox,
|
|
14550
|
+
a.position,
|
|
14551
|
+
page,
|
|
14552
|
+
camera,
|
|
14553
|
+
viewport
|
|
14554
|
+
);
|
|
14555
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
14556
|
+
StickyLabel,
|
|
14557
|
+
{
|
|
14558
|
+
screenAnchor: anchor,
|
|
14559
|
+
action: a
|
|
14560
|
+
},
|
|
14561
|
+
overlay.id
|
|
14562
|
+
);
|
|
14563
|
+
}) : null })
|
|
14564
|
+
}
|
|
14565
|
+
);
|
|
14566
|
+
}
|
|
14567
|
+
function computeScreenAnchor(bbox, where, page, camera, viewport) {
|
|
14568
|
+
const [x1, y1, x2, y2] = bbox;
|
|
14569
|
+
const pageCX = page.page_dimensions.width / 2;
|
|
14570
|
+
const pageCY = page.page_dimensions.height / 2;
|
|
14571
|
+
let px, py;
|
|
14572
|
+
switch (where) {
|
|
14573
|
+
case "top":
|
|
14574
|
+
px = (x1 + x2) / 2;
|
|
14575
|
+
py = y1;
|
|
14576
|
+
break;
|
|
14577
|
+
case "bottom":
|
|
14578
|
+
px = (x1 + x2) / 2;
|
|
14579
|
+
py = y2;
|
|
14580
|
+
break;
|
|
14581
|
+
case "left":
|
|
14582
|
+
px = x1;
|
|
14583
|
+
py = (y1 + y2) / 2;
|
|
14584
|
+
break;
|
|
14585
|
+
case "right":
|
|
14586
|
+
px = x2;
|
|
14587
|
+
py = (y1 + y2) / 2;
|
|
14588
|
+
break;
|
|
14589
|
+
default:
|
|
14590
|
+
px = (x1 + x2) / 2;
|
|
14591
|
+
py = y1;
|
|
14592
|
+
}
|
|
14593
|
+
const screenX = viewport.width / 2 + camera.x + (px - pageCX) * camera.scale;
|
|
14594
|
+
const screenY = viewport.height / 2 + camera.y + (py - pageCY) * camera.scale;
|
|
14595
|
+
return { x: screenX, y: screenY };
|
|
14596
|
+
}
|
|
14597
|
+
|
|
14598
|
+
// src/components/TutorMode/CalloutLabelOverlay.tsx
|
|
14599
|
+
var import_framer_motion13 = require("framer-motion");
|
|
14600
|
+
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
14601
|
+
function CalloutLabelOverlay({
|
|
14602
|
+
overlays,
|
|
14603
|
+
index,
|
|
14604
|
+
currentPage,
|
|
14605
|
+
camera,
|
|
14606
|
+
viewport
|
|
14607
|
+
}) {
|
|
14608
|
+
const callouts = overlays.filter(
|
|
14609
|
+
(o) => o.kind === "callout" && o.action.label
|
|
14610
|
+
);
|
|
14611
|
+
const page = index.byPage.get(currentPage);
|
|
14612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
14613
|
+
"div",
|
|
14614
|
+
{
|
|
14615
|
+
"data-role": "callout-label-overlay",
|
|
14616
|
+
style: {
|
|
14617
|
+
position: "absolute",
|
|
14618
|
+
inset: 0,
|
|
14619
|
+
pointerEvents: "none",
|
|
14620
|
+
overflow: "hidden",
|
|
14621
|
+
// Above the arrow stroke (which is inside CameraView) and the
|
|
14622
|
+
// reset button, below the ghost card.
|
|
14623
|
+
zIndex: 68
|
|
14624
|
+
},
|
|
14625
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_framer_motion13.AnimatePresence, { children: page ? callouts.map((overlay) => {
|
|
14626
|
+
const a = overlay.action;
|
|
14627
|
+
const fromHit = index.blockById.get(a.from_block);
|
|
14628
|
+
const toHit = index.blockById.get(a.to_block);
|
|
14629
|
+
if (!fromHit || !toHit || !a.label) return null;
|
|
14630
|
+
const pos = computePillAnchor(
|
|
14631
|
+
fromHit.block.bbox,
|
|
14632
|
+
toHit.block.bbox,
|
|
14633
|
+
page,
|
|
14634
|
+
camera,
|
|
14635
|
+
viewport
|
|
14636
|
+
);
|
|
14637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
14638
|
+
CalloutLabelPill,
|
|
14639
|
+
{
|
|
14640
|
+
label: a.label,
|
|
14641
|
+
anchor: pos,
|
|
14642
|
+
side: pos.side
|
|
14643
|
+
},
|
|
14644
|
+
overlay.id
|
|
14645
|
+
);
|
|
14646
|
+
}) : null })
|
|
14647
|
+
}
|
|
14648
|
+
);
|
|
14649
|
+
}
|
|
14650
|
+
function computePillAnchor(fromBbox, toBbox, page, camera, viewport) {
|
|
14651
|
+
const aCX = (fromBbox[0] + fromBbox[2]) / 2;
|
|
14652
|
+
const aCY = (fromBbox[1] + fromBbox[3]) / 2;
|
|
14653
|
+
const bCX = (toBbox[0] + toBbox[2]) / 2;
|
|
14654
|
+
const bCY = (toBbox[1] + toBbox[3]) / 2;
|
|
14655
|
+
const dx = bCX - aCX;
|
|
14656
|
+
const dy = bCY - aCY;
|
|
14657
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
14658
|
+
const ux = dx / len;
|
|
14659
|
+
const uy = dy / len;
|
|
14660
|
+
const bHalfW = (toBbox[2] - toBbox[0]) / 2;
|
|
14661
|
+
const bHalfH = (toBbox[3] - toBbox[1]) / 2;
|
|
14662
|
+
const bOff = Math.min(Math.max(bHalfW, bHalfH), 60);
|
|
14663
|
+
const toX = bCX - ux * bOff;
|
|
14664
|
+
const toY = bCY - uy * bOff;
|
|
14665
|
+
const pageCX = page.page_dimensions.width / 2;
|
|
14666
|
+
const pageCY = page.page_dimensions.height / 2;
|
|
14667
|
+
const tipScreenX = viewport.width / 2 + camera.x + (toX - pageCX) * camera.scale;
|
|
14668
|
+
const tipScreenY = viewport.height / 2 + camera.y + (toY - pageCY) * camera.scale;
|
|
14669
|
+
const isVertical = Math.abs(dy) >= Math.abs(dx);
|
|
14670
|
+
const OFFSET = 32;
|
|
14671
|
+
const MAX_PILL_W = 220;
|
|
14672
|
+
const MAX_PILL_H = 30;
|
|
14673
|
+
const SAFE = 16;
|
|
14674
|
+
if (isVertical) {
|
|
14675
|
+
const canFitRight = tipScreenX + OFFSET + MAX_PILL_W < viewport.width - SAFE;
|
|
14676
|
+
const side2 = canFitRight ? "right" : "left";
|
|
14677
|
+
return {
|
|
14678
|
+
x: tipScreenX + (side2 === "right" ? OFFSET : -OFFSET),
|
|
14679
|
+
y: tipScreenY,
|
|
14680
|
+
side: side2
|
|
14681
|
+
};
|
|
14682
|
+
}
|
|
14683
|
+
const canFitBelow = tipScreenY + OFFSET + MAX_PILL_H < viewport.height - SAFE;
|
|
14684
|
+
const side = canFitBelow ? "below" : "above";
|
|
14685
|
+
return {
|
|
14686
|
+
x: tipScreenX,
|
|
14687
|
+
y: tipScreenY + (side === "below" ? OFFSET : -OFFSET),
|
|
14688
|
+
side
|
|
14689
|
+
};
|
|
14690
|
+
}
|
|
14691
|
+
function CalloutLabelPill({
|
|
14692
|
+
label,
|
|
14693
|
+
anchor,
|
|
14694
|
+
side
|
|
14695
|
+
}) {
|
|
14696
|
+
const spec = PILL_SIDE_SPECS[side];
|
|
14697
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
14698
|
+
import_framer_motion13.motion.div,
|
|
14699
|
+
{
|
|
14700
|
+
initial: { opacity: 0, scale: 0.92, ...spec.slideIn },
|
|
14701
|
+
animate: { opacity: 1, scale: 1, x: 0, y: 0 },
|
|
14702
|
+
exit: { opacity: 0, scale: 0.94 },
|
|
14703
|
+
transition: { duration: 0.45, delay: 0.5, ease: EASE_OUT_EXPO },
|
|
14704
|
+
style: {
|
|
14705
|
+
position: "absolute",
|
|
14706
|
+
left: anchor.x,
|
|
14707
|
+
top: anchor.y,
|
|
14708
|
+
transform: spec.transform,
|
|
14709
|
+
pointerEvents: "none",
|
|
14710
|
+
background: PAPER,
|
|
14711
|
+
color: INK,
|
|
14712
|
+
border: "1px solid rgba(42, 36, 32, 0.10)",
|
|
14713
|
+
borderRadius: 3,
|
|
14714
|
+
padding: spec.padding,
|
|
14715
|
+
fontFamily: SERIF,
|
|
14716
|
+
fontSize: 11.5,
|
|
14717
|
+
lineHeight: 1.2,
|
|
14718
|
+
letterSpacing: 0.6,
|
|
14719
|
+
textTransform: "uppercase",
|
|
14720
|
+
fontWeight: 500,
|
|
14721
|
+
whiteSpace: "nowrap",
|
|
14722
|
+
maxWidth: 220,
|
|
14723
|
+
overflow: "hidden",
|
|
14724
|
+
textOverflow: "ellipsis",
|
|
14725
|
+
boxShadow: "0 1px 2px rgba(42, 36, 32, 0.12), 0 8px 18px -6px rgba(42, 36, 32, 0.22)",
|
|
14726
|
+
// Accent rule on the "inward" edge (the one closest to the arrow).
|
|
14727
|
+
backgroundImage: spec.accentGradient,
|
|
14728
|
+
backgroundRepeat: "no-repeat",
|
|
14729
|
+
backgroundSize: spec.accentSize,
|
|
14730
|
+
backgroundPosition: spec.accentPosition
|
|
14731
|
+
},
|
|
14732
|
+
"data-role": "callout-label",
|
|
14733
|
+
children: label
|
|
14734
|
+
}
|
|
14735
|
+
);
|
|
14736
|
+
}
|
|
14737
|
+
var PILL_SIDE_SPECS = {
|
|
14738
|
+
// Pill sits to the RIGHT of a vertical arrow → left edge anchors at
|
|
14739
|
+
// offset point, accent rule on the left (pointing back toward arrow).
|
|
14740
|
+
right: {
|
|
14741
|
+
transform: "translate(0, -50%)",
|
|
14742
|
+
slideIn: { x: -6 },
|
|
14743
|
+
padding: "5px 12px 5px 14px",
|
|
14744
|
+
accentGradient: `linear-gradient(to right, ${ACCENT} 0, ${ACCENT} 2px, transparent 2px)`,
|
|
14745
|
+
accentSize: "2px 100%",
|
|
14746
|
+
accentPosition: "left top"
|
|
14747
|
+
},
|
|
14748
|
+
left: {
|
|
14749
|
+
transform: "translate(-100%, -50%)",
|
|
14750
|
+
slideIn: { x: 6 },
|
|
14751
|
+
padding: "5px 14px 5px 12px",
|
|
14752
|
+
accentGradient: `linear-gradient(to left, ${ACCENT} 0, ${ACCENT} 2px, transparent 2px)`,
|
|
14753
|
+
accentSize: "2px 100%",
|
|
14754
|
+
accentPosition: "right top"
|
|
14755
|
+
},
|
|
14756
|
+
// Pill sits BELOW a horizontal arrow → top edge anchors at offset
|
|
14757
|
+
// point, accent rule on the top (pointing back up toward arrow).
|
|
14758
|
+
below: {
|
|
14759
|
+
transform: "translate(-50%, 0)",
|
|
14760
|
+
slideIn: { y: -6 },
|
|
14761
|
+
padding: "7px 12px 5px 12px",
|
|
14762
|
+
accentGradient: `linear-gradient(to bottom, ${ACCENT} 0, ${ACCENT} 2px, transparent 2px)`,
|
|
14763
|
+
accentSize: "100% 2px",
|
|
14764
|
+
accentPosition: "left top"
|
|
14765
|
+
},
|
|
14766
|
+
above: {
|
|
14767
|
+
transform: "translate(-50%, -100%)",
|
|
14768
|
+
slideIn: { y: 6 },
|
|
14769
|
+
padding: "5px 12px 7px 12px",
|
|
14770
|
+
accentGradient: `linear-gradient(to top, ${ACCENT} 0, ${ACCENT} 2px, transparent 2px)`,
|
|
14771
|
+
accentSize: "100% 2px",
|
|
14772
|
+
accentPosition: "left bottom"
|
|
14773
|
+
}
|
|
14774
|
+
};
|
|
14775
|
+
|
|
14776
|
+
// src/components/TutorMode/SubtitleBar.tsx
|
|
14777
|
+
var import_framer_motion14 = require("framer-motion");
|
|
14778
|
+
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
14779
|
+
function SubtitleBar({ text }) {
|
|
14780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_framer_motion14.AnimatePresence, { children: text ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
14781
|
+
import_framer_motion14.motion.div,
|
|
13774
14782
|
{
|
|
13775
14783
|
initial: { opacity: 0, y: 20 },
|
|
13776
14784
|
animate: { opacity: 1, y: 0 },
|
|
@@ -13806,7 +14814,20 @@ init_camera_math();
|
|
|
13806
14814
|
var DEFAULT_MIN_OVERLAY_MS = 3500;
|
|
13807
14815
|
var StoryboardEngine = class {
|
|
13808
14816
|
constructor(deps) {
|
|
13809
|
-
|
|
14817
|
+
/**
|
|
14818
|
+
* Timers that schedule the START of a step (via `setTimeout(runStep, at_ms)`).
|
|
14819
|
+
* These are storyboard-scoped: when a new storyboard arrives, anything still
|
|
14820
|
+
* pending should be abandoned.
|
|
14821
|
+
*/
|
|
14822
|
+
this.pendingStepTimers = /* @__PURE__ */ new Set();
|
|
14823
|
+
/**
|
|
14824
|
+
* Timers that auto-REMOVE an already-placed overlay after its visible
|
|
14825
|
+
* duration. Keyed by overlay id so we can cancel one specifically. These are
|
|
14826
|
+
* NOT cancelled when a new storyboard starts — otherwise the still-visible
|
|
14827
|
+
* overlay from the previous beat would get stranded in the store forever
|
|
14828
|
+
* (the "stuck spotlight" bug).
|
|
14829
|
+
*/
|
|
14830
|
+
this.overlayRemovalTimers = /* @__PURE__ */ new Map();
|
|
13810
14831
|
this.currentStoryboardId = 0;
|
|
13811
14832
|
this.deps = deps;
|
|
13812
14833
|
}
|
|
@@ -13849,13 +14870,13 @@ var StoryboardEngine = class {
|
|
|
13849
14870
|
if (storyboardId !== this.currentStoryboardId) return;
|
|
13850
14871
|
this.runStep(step);
|
|
13851
14872
|
}, step.at_ms);
|
|
13852
|
-
this.
|
|
14873
|
+
this.pendingStepTimers.add(timer);
|
|
13853
14874
|
}
|
|
13854
14875
|
const markExecuting = setTimeout(() => {
|
|
13855
14876
|
if (storyboardId !== this.currentStoryboardId) return;
|
|
13856
14877
|
narrationStore.getState().setEngineStatus("executing");
|
|
13857
14878
|
}, 0);
|
|
13858
|
-
this.
|
|
14879
|
+
this.pendingStepTimers.add(markExecuting);
|
|
13859
14880
|
const last = steps[steps.length - 1];
|
|
13860
14881
|
if (last) {
|
|
13861
14882
|
const totalMs = last.at_ms + last.duration_ms;
|
|
@@ -13863,18 +14884,29 @@ var StoryboardEngine = class {
|
|
|
13863
14884
|
if (storyboardId !== this.currentStoryboardId) return;
|
|
13864
14885
|
narrationStore.getState().setEngineStatus("idle");
|
|
13865
14886
|
}, totalMs + 50);
|
|
13866
|
-
this.
|
|
14887
|
+
this.pendingStepTimers.add(markIdle);
|
|
13867
14888
|
}
|
|
13868
14889
|
}
|
|
13869
|
-
/**
|
|
14890
|
+
/**
|
|
14891
|
+
* Abort pending STEP dispatches from the current storyboard. Overlay
|
|
14892
|
+
* removal timers are left alone so already-visible overlays still auto-
|
|
14893
|
+
* expire on their own schedule. To force-clear every overlay, call
|
|
14894
|
+
* `resetVisuals()` instead.
|
|
14895
|
+
*/
|
|
13870
14896
|
cancelPending() {
|
|
13871
|
-
for (const t of this.
|
|
13872
|
-
this.
|
|
14897
|
+
for (const t of this.pendingStepTimers) clearTimeout(t);
|
|
14898
|
+
this.pendingStepTimers.clear();
|
|
13873
14899
|
this.deps.narrationStore.getState().setEngineStatus("idle");
|
|
13874
14900
|
}
|
|
13875
|
-
/**
|
|
14901
|
+
/** Cancel every removal timer (used by resetVisuals only). */
|
|
14902
|
+
cancelAllRemovalTimers() {
|
|
14903
|
+
for (const t of this.overlayRemovalTimers.values()) clearTimeout(t);
|
|
14904
|
+
this.overlayRemovalTimers.clear();
|
|
14905
|
+
}
|
|
14906
|
+
/** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
|
|
13876
14907
|
resetVisuals() {
|
|
13877
14908
|
this.cancelPending();
|
|
14909
|
+
this.cancelAllRemovalTimers();
|
|
13878
14910
|
const { narrationStore, bboxIndex, getViewport } = this.deps;
|
|
13879
14911
|
narrationStore.getState().clearOverlays();
|
|
13880
14912
|
const viewport = getViewport();
|
|
@@ -13945,8 +14977,9 @@ var StoryboardEngine = class {
|
|
|
13945
14977
|
narrationStore.getState().addOverlay(overlay);
|
|
13946
14978
|
const timer = setTimeout(() => {
|
|
13947
14979
|
narrationStore.getState().removeOverlay(overlay.id);
|
|
14980
|
+
this.overlayRemovalTimers.delete(overlay.id);
|
|
13948
14981
|
}, visibleMs);
|
|
13949
|
-
this.
|
|
14982
|
+
this.overlayRemovalTimers.set(overlay.id, timer);
|
|
13950
14983
|
return true;
|
|
13951
14984
|
}
|
|
13952
14985
|
applyCamera(action, durationMs) {
|
|
@@ -13971,12 +15004,17 @@ var StoryboardEngine = class {
|
|
|
13971
15004
|
const blockCY = (y1 + y2) / 2;
|
|
13972
15005
|
const pageCX = pageDims.page_dimensions.width / 2;
|
|
13973
15006
|
const pageCY = pageDims.page_dimensions.height / 2;
|
|
13974
|
-
const
|
|
13975
|
-
const
|
|
15007
|
+
const rawX = (pageCX - blockCX) * finalScale;
|
|
15008
|
+
const rawY = (pageCY - blockCY) * finalScale;
|
|
15009
|
+
const clamped = clampCamera(
|
|
15010
|
+
{ scale: finalScale, x: rawX, y: rawY },
|
|
15011
|
+
pageDims.page_dimensions,
|
|
15012
|
+
viewport
|
|
15013
|
+
);
|
|
13976
15014
|
const camera = {
|
|
13977
|
-
scale:
|
|
13978
|
-
x,
|
|
13979
|
-
y,
|
|
15015
|
+
scale: clamped.scale,
|
|
15016
|
+
x: clamped.x,
|
|
15017
|
+
y: clamped.y,
|
|
13980
15018
|
easing: action.easing
|
|
13981
15019
|
};
|
|
13982
15020
|
narrationStore.getState().setCamera(camera);
|
|
@@ -14961,7 +15999,7 @@ function storyboardFromMatch(match, page) {
|
|
|
14961
15999
|
}
|
|
14962
16000
|
|
|
14963
16001
|
// src/components/TutorMode/TutorModeContainer.tsx
|
|
14964
|
-
var
|
|
16002
|
+
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
14965
16003
|
function buildBBoxIndex(bboxData) {
|
|
14966
16004
|
const byPage = /* @__PURE__ */ new Map();
|
|
14967
16005
|
const blockById = /* @__PURE__ */ new Map();
|
|
@@ -14997,16 +16035,36 @@ function TutorModeContainer({
|
|
|
14997
16035
|
showExitButton = true,
|
|
14998
16036
|
onExitTutorMode,
|
|
14999
16037
|
minOverlayDurationMs,
|
|
16038
|
+
backgroundColor = "#ffffff",
|
|
16039
|
+
loadingComponent,
|
|
16040
|
+
onPageChange,
|
|
15000
16041
|
className
|
|
15001
16042
|
}) {
|
|
15002
|
-
const containerRef = (0,
|
|
15003
|
-
const index = (0,
|
|
15004
|
-
const {
|
|
15005
|
-
|
|
15006
|
-
|
|
16043
|
+
const containerRef = (0, import_react58.useRef)(null);
|
|
16044
|
+
const index = (0, import_react58.useMemo)(() => buildBBoxIndex(bboxData), [bboxData]);
|
|
16045
|
+
const {
|
|
16046
|
+
document: document2,
|
|
16047
|
+
currentPage: viewerCurrentPage,
|
|
16048
|
+
numPages,
|
|
16049
|
+
goToPage: viewerGoToPage
|
|
16050
|
+
} = usePDFViewer();
|
|
16051
|
+
const [pageProxy, setPageProxy] = (0, import_react58.useState)(null);
|
|
16052
|
+
const [viewport, setViewport] = (0, import_react58.useState)({ width: 800, height: 1e3 });
|
|
15007
16053
|
const camera = (0, import_zustand2.useStore)(narrationStore, (s) => s.camera);
|
|
15008
16054
|
const activeOverlays = (0, import_zustand2.useStore)(narrationStore, (s) => s.activeOverlays);
|
|
15009
|
-
(0,
|
|
16055
|
+
(0, import_react58.useEffect)(() => {
|
|
16056
|
+
if (numPages <= 0) return;
|
|
16057
|
+
if (pageNumber < 1 || pageNumber > numPages) return;
|
|
16058
|
+
if (viewerCurrentPage === pageNumber) return;
|
|
16059
|
+
viewerGoToPage(pageNumber);
|
|
16060
|
+
}, [pageNumber, numPages, viewerCurrentPage, viewerGoToPage]);
|
|
16061
|
+
(0, import_react58.useEffect)(() => {
|
|
16062
|
+
if (!onPageChange) return;
|
|
16063
|
+
if (viewerCurrentPage === pageNumber) return;
|
|
16064
|
+
if (viewerCurrentPage < 1) return;
|
|
16065
|
+
onPageChange(viewerCurrentPage);
|
|
16066
|
+
}, [viewerCurrentPage, pageNumber, onPageChange]);
|
|
16067
|
+
(0, import_react58.useEffect)(() => {
|
|
15010
16068
|
if (!containerRef.current) return;
|
|
15011
16069
|
const el = containerRef.current;
|
|
15012
16070
|
const update = () => setViewport({ width: el.clientWidth, height: el.clientHeight });
|
|
@@ -15015,7 +16073,7 @@ function TutorModeContainer({
|
|
|
15015
16073
|
ro.observe(el);
|
|
15016
16074
|
return () => ro.disconnect();
|
|
15017
16075
|
}, []);
|
|
15018
|
-
(0,
|
|
16076
|
+
(0, import_react58.useEffect)(() => {
|
|
15019
16077
|
if (!document2) {
|
|
15020
16078
|
setPageProxy(null);
|
|
15021
16079
|
return;
|
|
@@ -15030,10 +16088,10 @@ function TutorModeContainer({
|
|
|
15030
16088
|
cancelled = true;
|
|
15031
16089
|
};
|
|
15032
16090
|
}, [document2, pageNumber]);
|
|
15033
|
-
(0,
|
|
16091
|
+
(0, import_react58.useEffect)(() => {
|
|
15034
16092
|
narrationStore.getState().setCurrentPage(pageNumber);
|
|
15035
16093
|
}, [pageNumber, narrationStore]);
|
|
15036
|
-
(0,
|
|
16094
|
+
(0, import_react58.useEffect)(() => {
|
|
15037
16095
|
const page2 = index.byPage.get(pageNumber);
|
|
15038
16096
|
if (!page2) return;
|
|
15039
16097
|
if (viewport.width === 0 || viewport.height === 0) return;
|
|
@@ -15044,8 +16102,8 @@ function TutorModeContainer({
|
|
|
15044
16102
|
) * 0.95;
|
|
15045
16103
|
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0 });
|
|
15046
16104
|
}, [pageNumber, viewport, index, narrationStore]);
|
|
15047
|
-
const engineRef = (0,
|
|
15048
|
-
(0,
|
|
16105
|
+
const engineRef = (0, import_react58.useRef)(null);
|
|
16106
|
+
(0, import_react58.useEffect)(() => {
|
|
15049
16107
|
engineRef.current = new StoryboardEngine({
|
|
15050
16108
|
narrationStore,
|
|
15051
16109
|
bboxIndex: index,
|
|
@@ -15054,10 +16112,10 @@ function TutorModeContainer({
|
|
|
15054
16112
|
});
|
|
15055
16113
|
return () => engineRef.current?.cancelPending();
|
|
15056
16114
|
}, [narrationStore, index, viewport, minOverlayDurationMs]);
|
|
15057
|
-
const abortRef = (0,
|
|
15058
|
-
const debounceRef = (0,
|
|
15059
|
-
const lastChunkRef = (0,
|
|
15060
|
-
(0,
|
|
16115
|
+
const abortRef = (0, import_react58.useRef)(null);
|
|
16116
|
+
const debounceRef = (0, import_react58.useRef)(null);
|
|
16117
|
+
const lastChunkRef = (0, import_react58.useRef)(null);
|
|
16118
|
+
(0, import_react58.useEffect)(() => {
|
|
15061
16119
|
if (!llm) return;
|
|
15062
16120
|
if (!currentChunk || currentChunk === lastChunkRef.current) return;
|
|
15063
16121
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
@@ -15144,7 +16202,7 @@ function TutorModeContainer({
|
|
|
15144
16202
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
15145
16203
|
};
|
|
15146
16204
|
}, [currentChunk, llm, index, pageNumber, narrationStore, embeddingProvider, llmTimeoutMs]);
|
|
15147
|
-
(0,
|
|
16205
|
+
(0, import_react58.useEffect)(() => {
|
|
15148
16206
|
if (!currentChunk) return;
|
|
15149
16207
|
const t = setTimeout(() => {
|
|
15150
16208
|
if (!engineRef.current) return;
|
|
@@ -15161,7 +16219,8 @@ function TutorModeContainer({
|
|
|
15161
16219
|
const rasterScale = dpiScale * (scale || 1);
|
|
15162
16220
|
const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
|
|
15163
16221
|
const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
|
|
15164
|
-
|
|
16222
|
+
const isReady = !!page && !!pageProxy;
|
|
16223
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
15165
16224
|
"div",
|
|
15166
16225
|
{
|
|
15167
16226
|
ref: containerRef,
|
|
@@ -15171,12 +16230,12 @@ function TutorModeContainer({
|
|
|
15171
16230
|
width: "100%",
|
|
15172
16231
|
height: "100%",
|
|
15173
16232
|
overflow: "hidden",
|
|
15174
|
-
background:
|
|
16233
|
+
background: backgroundColor
|
|
15175
16234
|
},
|
|
15176
16235
|
"data-role": "tutor-mode-container",
|
|
15177
|
-
"data-page-loaded":
|
|
16236
|
+
"data-page-loaded": isReady ? "true" : "false",
|
|
15178
16237
|
children: [
|
|
15179
|
-
showExitButton ? /* @__PURE__ */ (0,
|
|
16238
|
+
showExitButton && isReady ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
15180
16239
|
"button",
|
|
15181
16240
|
{
|
|
15182
16241
|
onClick: () => {
|
|
@@ -15193,7 +16252,9 @@ function TutorModeContainer({
|
|
|
15193
16252
|
padding: "8px 14px",
|
|
15194
16253
|
border: "none",
|
|
15195
16254
|
borderRadius: 8,
|
|
15196
|
-
|
|
16255
|
+
// Dark translucent pill with white text reads cleanly on both
|
|
16256
|
+
// light and dark container backgrounds.
|
|
16257
|
+
background: "rgba(17,24,39,0.72)",
|
|
15197
16258
|
color: "white",
|
|
15198
16259
|
cursor: "pointer",
|
|
15199
16260
|
fontFamily: "system-ui, sans-serif",
|
|
@@ -15205,43 +16266,127 @@ function TutorModeContainer({
|
|
|
15205
16266
|
children: "Reset view"
|
|
15206
16267
|
}
|
|
15207
16268
|
) : null,
|
|
15208
|
-
|
|
16269
|
+
isReady ? /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
|
|
16270
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(CameraView, { camera, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
16271
|
+
"div",
|
|
16272
|
+
{
|
|
16273
|
+
style: {
|
|
16274
|
+
position: "absolute",
|
|
16275
|
+
top: "50%",
|
|
16276
|
+
left: "50%",
|
|
16277
|
+
width: baseW,
|
|
16278
|
+
height: baseH,
|
|
16279
|
+
transform: "translate(-50%, -50%)"
|
|
16280
|
+
},
|
|
16281
|
+
children: [
|
|
16282
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
16283
|
+
PDFPage,
|
|
16284
|
+
{
|
|
16285
|
+
pageNumber,
|
|
16286
|
+
page: pageProxy,
|
|
16287
|
+
scale: rasterScale,
|
|
16288
|
+
rotation,
|
|
16289
|
+
showTextLayer: false,
|
|
16290
|
+
showHighlightLayer: false,
|
|
16291
|
+
showAnnotationLayer: false
|
|
16292
|
+
}
|
|
16293
|
+
),
|
|
16294
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
16295
|
+
CinemaLayer,
|
|
16296
|
+
{
|
|
16297
|
+
page,
|
|
16298
|
+
index,
|
|
16299
|
+
overlays: activeOverlays,
|
|
16300
|
+
scale: scale || 1
|
|
16301
|
+
}
|
|
16302
|
+
)
|
|
16303
|
+
]
|
|
16304
|
+
}
|
|
16305
|
+
) }),
|
|
16306
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
16307
|
+
LabelOverlay,
|
|
16308
|
+
{
|
|
16309
|
+
overlays: activeOverlays,
|
|
16310
|
+
index,
|
|
16311
|
+
currentPage: pageNumber,
|
|
16312
|
+
camera,
|
|
16313
|
+
viewport
|
|
16314
|
+
}
|
|
16315
|
+
),
|
|
16316
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
16317
|
+
CalloutLabelOverlay,
|
|
16318
|
+
{
|
|
16319
|
+
overlays: activeOverlays,
|
|
16320
|
+
index,
|
|
16321
|
+
currentPage: pageNumber,
|
|
16322
|
+
camera,
|
|
16323
|
+
viewport
|
|
16324
|
+
}
|
|
16325
|
+
),
|
|
16326
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(GhostReferenceOverlay, { overlays: activeOverlays, index })
|
|
16327
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TutorLoadingState, { custom: loadingComponent }),
|
|
16328
|
+
showSubtitles ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(SubtitleBar, { text: currentChunk ?? null }) : null
|
|
16329
|
+
]
|
|
16330
|
+
}
|
|
16331
|
+
);
|
|
16332
|
+
}
|
|
16333
|
+
function TutorLoadingState({
|
|
16334
|
+
custom
|
|
16335
|
+
}) {
|
|
16336
|
+
if (custom) {
|
|
16337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
16338
|
+
"div",
|
|
16339
|
+
{
|
|
16340
|
+
style: {
|
|
16341
|
+
position: "absolute",
|
|
16342
|
+
inset: 0,
|
|
16343
|
+
display: "flex",
|
|
16344
|
+
alignItems: "center",
|
|
16345
|
+
justifyContent: "center"
|
|
16346
|
+
},
|
|
16347
|
+
"data-role": "tutor-loading",
|
|
16348
|
+
children: custom
|
|
16349
|
+
}
|
|
16350
|
+
);
|
|
16351
|
+
}
|
|
16352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
16353
|
+
"div",
|
|
16354
|
+
{
|
|
16355
|
+
style: {
|
|
16356
|
+
position: "absolute",
|
|
16357
|
+
inset: 0,
|
|
16358
|
+
display: "flex",
|
|
16359
|
+
flexDirection: "column",
|
|
16360
|
+
alignItems: "center",
|
|
16361
|
+
justifyContent: "center",
|
|
16362
|
+
gap: 12,
|
|
16363
|
+
color: "rgba(0,0,0,0.55)",
|
|
16364
|
+
fontFamily: "system-ui, sans-serif",
|
|
16365
|
+
fontSize: 13
|
|
16366
|
+
},
|
|
16367
|
+
"data-role": "tutor-loading",
|
|
16368
|
+
children: [
|
|
16369
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
15209
16370
|
"div",
|
|
15210
16371
|
{
|
|
16372
|
+
"aria-hidden": true,
|
|
15211
16373
|
style: {
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
}
|
|
15219
|
-
children: [
|
|
15220
|
-
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
15221
|
-
PDFPage,
|
|
15222
|
-
{
|
|
15223
|
-
pageNumber,
|
|
15224
|
-
page: pageProxy,
|
|
15225
|
-
scale: rasterScale,
|
|
15226
|
-
rotation,
|
|
15227
|
-
showTextLayer: false,
|
|
15228
|
-
showHighlightLayer: false,
|
|
15229
|
-
showAnnotationLayer: false
|
|
15230
|
-
}
|
|
15231
|
-
),
|
|
15232
|
-
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
15233
|
-
CinemaLayer,
|
|
15234
|
-
{
|
|
15235
|
-
page,
|
|
15236
|
-
index,
|
|
15237
|
-
overlays: activeOverlays,
|
|
15238
|
-
scale: scale || 1
|
|
15239
|
-
}
|
|
15240
|
-
)
|
|
15241
|
-
]
|
|
16374
|
+
width: 36,
|
|
16375
|
+
height: 36,
|
|
16376
|
+
borderRadius: "50%",
|
|
16377
|
+
border: "3px solid rgba(0,0,0,0.1)",
|
|
16378
|
+
borderTopColor: "rgba(0,0,0,0.45)",
|
|
16379
|
+
animation: "pdf-tutor-spin 0.9s linear infinite"
|
|
16380
|
+
}
|
|
15242
16381
|
}
|
|
15243
|
-
)
|
|
15244
|
-
|
|
16382
|
+
),
|
|
16383
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { children: "Loading document\u2026" }),
|
|
16384
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("style", { children: `
|
|
16385
|
+
@keyframes pdf-tutor-spin {
|
|
16386
|
+
from { transform: rotate(0deg); }
|
|
16387
|
+
to { transform: rotate(360deg); }
|
|
16388
|
+
}
|
|
16389
|
+
` })
|
|
15245
16390
|
]
|
|
15246
16391
|
}
|
|
15247
16392
|
);
|