pdfjs-reader-core 0.2.17 → 0.4.0
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 +263 -0
- package/dist/index.cjs +2971 -347
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1073 -4
- package/dist/index.d.ts +1073 -4
- package/dist/index.js +2936 -341
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +23 -17
- package/LICENSE +0 -21
package/dist/index.cjs
CHANGED
|
@@ -140,8 +140,8 @@ async function loadDocument(options) {
|
|
|
140
140
|
signal.addEventListener("abort", abortHandler);
|
|
141
141
|
}
|
|
142
142
|
if (onProgress) {
|
|
143
|
-
loadingTask.onProgress = ({ loaded, total }) => {
|
|
144
|
-
onProgress({ loaded, total });
|
|
143
|
+
loadingTask.onProgress = ({ loaded: loaded2, total }) => {
|
|
144
|
+
onProgress({ loaded: loaded2, total });
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
147
|
let document2;
|
|
@@ -271,9 +271,9 @@ function loadDocumentWithCallbacks(options) {
|
|
|
271
271
|
};
|
|
272
272
|
abortController.signal.addEventListener("abort", abortHandler);
|
|
273
273
|
if (onProgress) {
|
|
274
|
-
loadingTask.onProgress = ({ loaded, total }) => {
|
|
274
|
+
loadingTask.onProgress = ({ loaded: loaded2, total }) => {
|
|
275
275
|
if (!abortController.signal.aborted) {
|
|
276
|
-
onProgress({ loaded, total });
|
|
276
|
+
onProgress({ loaded: loaded2, total });
|
|
277
277
|
}
|
|
278
278
|
};
|
|
279
279
|
}
|
|
@@ -627,13 +627,13 @@ function createViewerStore(initialOverrides = {}) {
|
|
|
627
627
|
},
|
|
628
628
|
zoomIn: () => {
|
|
629
629
|
const { scale } = get();
|
|
630
|
-
const currentIndex = ZOOM_LEVELS.findIndex((
|
|
630
|
+
const currentIndex = ZOOM_LEVELS.findIndex((z2) => z2 >= scale);
|
|
631
631
|
const nextIndex = Math.min(currentIndex + 1, ZOOM_LEVELS.length - 1);
|
|
632
632
|
set({ scale: ZOOM_LEVELS[nextIndex] ?? MAX_SCALE });
|
|
633
633
|
},
|
|
634
634
|
zoomOut: () => {
|
|
635
635
|
const { scale } = get();
|
|
636
|
-
const currentIndex = ZOOM_LEVELS.findIndex((
|
|
636
|
+
const currentIndex = ZOOM_LEVELS.findIndex((z2) => z2 >= scale);
|
|
637
637
|
const prevIndex = Math.max(currentIndex - 1, 0);
|
|
638
638
|
set({ scale: ZOOM_LEVELS[prevIndex] ?? MIN_SCALE });
|
|
639
639
|
},
|
|
@@ -1784,6 +1784,113 @@ var init_coordinates = __esm({
|
|
|
1784
1784
|
}
|
|
1785
1785
|
});
|
|
1786
1786
|
|
|
1787
|
+
// src/utils/page-turn-sound.ts
|
|
1788
|
+
function getAudioContext() {
|
|
1789
|
+
if (typeof window === "undefined") return null;
|
|
1790
|
+
if (!audioContext) {
|
|
1791
|
+
try {
|
|
1792
|
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
1793
|
+
} catch {
|
|
1794
|
+
return null;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return audioContext;
|
|
1798
|
+
}
|
|
1799
|
+
function playPageTurnSound(volume = 0.3) {
|
|
1800
|
+
const ctx = getAudioContext();
|
|
1801
|
+
if (!ctx) return;
|
|
1802
|
+
if (ctx.state === "suspended") {
|
|
1803
|
+
ctx.resume();
|
|
1804
|
+
}
|
|
1805
|
+
const now = ctx.currentTime;
|
|
1806
|
+
const duration = 0.35;
|
|
1807
|
+
const bufferSize = Math.floor(ctx.sampleRate * duration);
|
|
1808
|
+
const noiseBuffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
|
|
1809
|
+
const data = noiseBuffer.getChannelData(0);
|
|
1810
|
+
for (let i = 0; i < bufferSize; i++) {
|
|
1811
|
+
data[i] = Math.random() * 2 - 1;
|
|
1812
|
+
}
|
|
1813
|
+
const noiseSource = ctx.createBufferSource();
|
|
1814
|
+
noiseSource.buffer = noiseBuffer;
|
|
1815
|
+
const bandpass = ctx.createBiquadFilter();
|
|
1816
|
+
bandpass.type = "bandpass";
|
|
1817
|
+
bandpass.frequency.setValueAtTime(3e3, now);
|
|
1818
|
+
bandpass.frequency.exponentialRampToValueAtTime(800, now + duration * 0.6);
|
|
1819
|
+
bandpass.Q.setValueAtTime(0.8, now);
|
|
1820
|
+
const highpass = ctx.createBiquadFilter();
|
|
1821
|
+
highpass.type = "highpass";
|
|
1822
|
+
highpass.frequency.setValueAtTime(400, now);
|
|
1823
|
+
highpass.frequency.linearRampToValueAtTime(200, now + duration);
|
|
1824
|
+
const envelope = ctx.createGain();
|
|
1825
|
+
envelope.gain.setValueAtTime(0, now);
|
|
1826
|
+
envelope.gain.linearRampToValueAtTime(volume * 0.6, now + 0.02);
|
|
1827
|
+
envelope.gain.setValueAtTime(volume * 0.6, now + 0.05);
|
|
1828
|
+
envelope.gain.linearRampToValueAtTime(volume, now + duration * 0.3);
|
|
1829
|
+
envelope.gain.exponentialRampToValueAtTime(1e-3, now + duration);
|
|
1830
|
+
const snapBuffer = ctx.createBuffer(1, Math.floor(ctx.sampleRate * 0.08), ctx.sampleRate);
|
|
1831
|
+
const snapData = snapBuffer.getChannelData(0);
|
|
1832
|
+
for (let i = 0; i < snapData.length; i++) {
|
|
1833
|
+
snapData[i] = Math.random() * 2 - 1;
|
|
1834
|
+
}
|
|
1835
|
+
const snapSource = ctx.createBufferSource();
|
|
1836
|
+
snapSource.buffer = snapBuffer;
|
|
1837
|
+
const snapFilter = ctx.createBiquadFilter();
|
|
1838
|
+
snapFilter.type = "bandpass";
|
|
1839
|
+
snapFilter.frequency.setValueAtTime(2e3, now);
|
|
1840
|
+
snapFilter.Q.setValueAtTime(1.5, now);
|
|
1841
|
+
const snapEnvelope = ctx.createGain();
|
|
1842
|
+
snapEnvelope.gain.setValueAtTime(0, now);
|
|
1843
|
+
snapEnvelope.gain.setValueAtTime(0, now + duration * 0.7);
|
|
1844
|
+
snapEnvelope.gain.linearRampToValueAtTime(volume * 0.8, now + duration * 0.75);
|
|
1845
|
+
snapEnvelope.gain.exponentialRampToValueAtTime(1e-3, now + duration);
|
|
1846
|
+
noiseSource.connect(bandpass);
|
|
1847
|
+
bandpass.connect(highpass);
|
|
1848
|
+
highpass.connect(envelope);
|
|
1849
|
+
envelope.connect(ctx.destination);
|
|
1850
|
+
snapSource.connect(snapFilter);
|
|
1851
|
+
snapFilter.connect(snapEnvelope);
|
|
1852
|
+
snapEnvelope.connect(ctx.destination);
|
|
1853
|
+
noiseSource.start(now);
|
|
1854
|
+
noiseSource.stop(now + duration);
|
|
1855
|
+
snapSource.start(now);
|
|
1856
|
+
snapSource.stop(now + duration);
|
|
1857
|
+
}
|
|
1858
|
+
var audioContext;
|
|
1859
|
+
var init_page_turn_sound = __esm({
|
|
1860
|
+
"src/utils/page-turn-sound.ts"() {
|
|
1861
|
+
"use strict";
|
|
1862
|
+
audioContext = null;
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
// src/utils/camera-math.ts
|
|
1867
|
+
function fitPageScale(page, viewport) {
|
|
1868
|
+
const sx = viewport.width / page.width;
|
|
1869
|
+
const sy = viewport.height / page.height;
|
|
1870
|
+
return Math.min(sx, sy);
|
|
1871
|
+
}
|
|
1872
|
+
function computeCameraForBlock(bbox, page, viewport, opts = {}) {
|
|
1873
|
+
const targetScale = opts.targetScale ?? 1.5;
|
|
1874
|
+
const paddingPdf = opts.paddingPdf ?? 80;
|
|
1875
|
+
const [x1, y1, x2, y2] = bbox;
|
|
1876
|
+
const blockW = Math.max(1, x2 - x1 + paddingPdf * 2);
|
|
1877
|
+
const blockH = Math.max(1, y2 - y1 + paddingPdf * 2);
|
|
1878
|
+
const blockCX = (x1 + x2) / 2;
|
|
1879
|
+
const blockCY = (y1 + y2) / 2;
|
|
1880
|
+
const fitBlock = Math.min(viewport.width / blockW, viewport.height / blockH);
|
|
1881
|
+
const scale = fitBlock * targetScale;
|
|
1882
|
+
const pageCX = page.width / 2;
|
|
1883
|
+
const pageCY = page.height / 2;
|
|
1884
|
+
const x = (pageCX - blockCX) * scale;
|
|
1885
|
+
const y = (pageCY - blockCY) * scale;
|
|
1886
|
+
return { scale, x, y };
|
|
1887
|
+
}
|
|
1888
|
+
var init_camera_math = __esm({
|
|
1889
|
+
"src/utils/camera-math.ts"() {
|
|
1890
|
+
"use strict";
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
|
|
1787
1894
|
// src/utils/index.ts
|
|
1788
1895
|
var init_utils = __esm({
|
|
1789
1896
|
"src/utils/index.ts"() {
|
|
@@ -1798,6 +1905,7 @@ var init_utils = __esm({
|
|
|
1798
1905
|
init_agent_api();
|
|
1799
1906
|
init_text_search();
|
|
1800
1907
|
init_coordinates();
|
|
1908
|
+
init_page_turn_sound();
|
|
1801
1909
|
}
|
|
1802
1910
|
});
|
|
1803
1911
|
|
|
@@ -2201,6 +2309,78 @@ var init_student_store = __esm({
|
|
|
2201
2309
|
}
|
|
2202
2310
|
});
|
|
2203
2311
|
|
|
2312
|
+
// src/store/narration-store.ts
|
|
2313
|
+
function createNarrationStore(overrides = {}) {
|
|
2314
|
+
return (0, import_vanilla6.createStore)()((set) => ({
|
|
2315
|
+
...initialState6,
|
|
2316
|
+
...overrides,
|
|
2317
|
+
setCurrentChunk: (chunk) => set({ currentChunk: chunk }),
|
|
2318
|
+
setCurrentPage: (page) => set({ currentPage: page }),
|
|
2319
|
+
pushChunkHistory: (entry) => set((state) => ({
|
|
2320
|
+
chunkHistory: [
|
|
2321
|
+
...state.chunkHistory.slice(-(MAX_HISTORY - 1)),
|
|
2322
|
+
entry
|
|
2323
|
+
]
|
|
2324
|
+
})),
|
|
2325
|
+
setCamera: (camera) => set((state) => ({ camera: { ...state.camera, ...camera } })),
|
|
2326
|
+
addOverlay: (overlay) => set((state) => ({ activeOverlays: [...state.activeOverlays, overlay] })),
|
|
2327
|
+
removeOverlay: (id) => set((state) => ({
|
|
2328
|
+
activeOverlays: state.activeOverlays.filter((o) => o.id !== id)
|
|
2329
|
+
})),
|
|
2330
|
+
clearOverlays: (predicate) => set((state) => ({
|
|
2331
|
+
activeOverlays: predicate ? state.activeOverlays.filter((o) => !predicate(o)) : []
|
|
2332
|
+
})),
|
|
2333
|
+
setEngineStatus: (s) => set({ engineStatus: s }),
|
|
2334
|
+
setLlmStatus: (s, error = null) => set({ llmStatus: s, lastError: error }),
|
|
2335
|
+
setLastStoryboard: (sb) => set({ lastStoryboard: sb }),
|
|
2336
|
+
setPaused: (paused) => set({ isPaused: paused }),
|
|
2337
|
+
appendDebugEvent: (event) => set((state) => {
|
|
2338
|
+
debugEventCounter += 1;
|
|
2339
|
+
const next = {
|
|
2340
|
+
...event,
|
|
2341
|
+
id: `dbg-${debugEventCounter}`,
|
|
2342
|
+
timestamp: Date.now()
|
|
2343
|
+
};
|
|
2344
|
+
return {
|
|
2345
|
+
debugEvents: [
|
|
2346
|
+
...state.debugEvents.slice(-(MAX_DEBUG_EVENTS - 1)),
|
|
2347
|
+
next
|
|
2348
|
+
]
|
|
2349
|
+
};
|
|
2350
|
+
}),
|
|
2351
|
+
clearDebugEvents: () => set({ debugEvents: [] }),
|
|
2352
|
+
reset: () => set(initialState6)
|
|
2353
|
+
}));
|
|
2354
|
+
}
|
|
2355
|
+
function makeOverlayId(action) {
|
|
2356
|
+
overlayIdCounter += 1;
|
|
2357
|
+
return `ov-${action.type}-${overlayIdCounter}-${Date.now()}`;
|
|
2358
|
+
}
|
|
2359
|
+
var import_vanilla6, MAX_HISTORY, initialState6, MAX_DEBUG_EVENTS, debugEventCounter, overlayIdCounter;
|
|
2360
|
+
var init_narration_store = __esm({
|
|
2361
|
+
"src/store/narration-store.ts"() {
|
|
2362
|
+
"use strict";
|
|
2363
|
+
import_vanilla6 = require("zustand/vanilla");
|
|
2364
|
+
MAX_HISTORY = 5;
|
|
2365
|
+
initialState6 = {
|
|
2366
|
+
currentChunk: null,
|
|
2367
|
+
currentPage: 1,
|
|
2368
|
+
chunkHistory: [],
|
|
2369
|
+
camera: { scale: 1, x: 0, y: 0, easing: "ease-in-out" },
|
|
2370
|
+
activeOverlays: [],
|
|
2371
|
+
engineStatus: "idle",
|
|
2372
|
+
llmStatus: "idle",
|
|
2373
|
+
lastStoryboard: null,
|
|
2374
|
+
lastError: null,
|
|
2375
|
+
isPaused: false,
|
|
2376
|
+
debugEvents: []
|
|
2377
|
+
};
|
|
2378
|
+
MAX_DEBUG_EVENTS = 50;
|
|
2379
|
+
debugEventCounter = 0;
|
|
2380
|
+
overlayIdCounter = 0;
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2383
|
+
|
|
2204
2384
|
// src/store/index.ts
|
|
2205
2385
|
var init_store = __esm({
|
|
2206
2386
|
"src/store/index.ts"() {
|
|
@@ -2210,13 +2390,14 @@ var init_store = __esm({
|
|
|
2210
2390
|
init_search_store();
|
|
2211
2391
|
init_agent_store();
|
|
2212
2392
|
init_student_store();
|
|
2393
|
+
init_narration_store();
|
|
2213
2394
|
}
|
|
2214
2395
|
});
|
|
2215
2396
|
|
|
2216
2397
|
// src/hooks/PDFViewerContext.tsx
|
|
2217
2398
|
function PDFViewerProvider({
|
|
2218
2399
|
children,
|
|
2219
|
-
initialState:
|
|
2400
|
+
initialState: initialState7,
|
|
2220
2401
|
theme = "light",
|
|
2221
2402
|
defaultSidebarPanel = "thumbnails",
|
|
2222
2403
|
studentMode: _studentMode = false
|
|
@@ -2228,22 +2409,22 @@ function PDFViewerProvider({
|
|
|
2228
2409
|
const studentStoreRef = (0, import_react2.useRef)(null);
|
|
2229
2410
|
if (!viewerStoreRef.current) {
|
|
2230
2411
|
viewerStoreRef.current = createViewerStore({
|
|
2231
|
-
...
|
|
2412
|
+
...initialState7?.viewer,
|
|
2232
2413
|
theme,
|
|
2233
2414
|
sidebarPanel: defaultSidebarPanel
|
|
2234
2415
|
});
|
|
2235
2416
|
}
|
|
2236
2417
|
if (!annotationStoreRef.current) {
|
|
2237
|
-
annotationStoreRef.current = createAnnotationStore(
|
|
2418
|
+
annotationStoreRef.current = createAnnotationStore(initialState7?.annotation);
|
|
2238
2419
|
}
|
|
2239
2420
|
if (!searchStoreRef.current) {
|
|
2240
|
-
searchStoreRef.current = createSearchStore(
|
|
2421
|
+
searchStoreRef.current = createSearchStore(initialState7?.search);
|
|
2241
2422
|
}
|
|
2242
2423
|
if (!agentStoreRef.current) {
|
|
2243
|
-
agentStoreRef.current = createAgentStore(
|
|
2424
|
+
agentStoreRef.current = createAgentStore(initialState7?.agent);
|
|
2244
2425
|
}
|
|
2245
2426
|
if (!studentStoreRef.current) {
|
|
2246
|
-
studentStoreRef.current = createStudentStore(
|
|
2427
|
+
studentStoreRef.current = createStudentStore(initialState7?.student);
|
|
2247
2428
|
}
|
|
2248
2429
|
(0, import_react2.useEffect)(() => {
|
|
2249
2430
|
return () => {
|
|
@@ -3567,8 +3748,8 @@ var init_PluginManager = __esm({
|
|
|
3567
3748
|
/**
|
|
3568
3749
|
* Get toolbar items by position
|
|
3569
3750
|
*/
|
|
3570
|
-
getToolbarItemsByPosition(
|
|
3571
|
-
return this.getToolbarItems().filter((item) => item.position ===
|
|
3751
|
+
getToolbarItemsByPosition(position2) {
|
|
3752
|
+
return this.getToolbarItems().filter((item) => item.position === position2);
|
|
3572
3753
|
}
|
|
3573
3754
|
/**
|
|
3574
3755
|
* Get all sidebar panels from all plugins
|
|
@@ -4681,7 +4862,7 @@ var init_MobileToolbar = __esm({
|
|
|
4681
4862
|
sidebarOpen,
|
|
4682
4863
|
theme,
|
|
4683
4864
|
onThemeChange,
|
|
4684
|
-
position = "bottom",
|
|
4865
|
+
position: position2 = "bottom",
|
|
4685
4866
|
className
|
|
4686
4867
|
}) {
|
|
4687
4868
|
const [showMoreMenu, setShowMoreMenu] = (0, import_react17.useState)(false);
|
|
@@ -4715,8 +4896,8 @@ var init_MobileToolbar = __esm({
|
|
|
4715
4896
|
"bg-white dark:bg-gray-800",
|
|
4716
4897
|
"border-gray-200 dark:border-gray-700",
|
|
4717
4898
|
"px-2 py-1 safe-area-inset",
|
|
4718
|
-
|
|
4719
|
-
|
|
4899
|
+
position2 === "top" && "top-0 border-b",
|
|
4900
|
+
position2 === "bottom" && "bottom-0 border-t",
|
|
4720
4901
|
className
|
|
4721
4902
|
),
|
|
4722
4903
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between gap-1", children: [
|
|
@@ -4842,7 +5023,7 @@ var init_MobileToolbar = __esm({
|
|
|
4842
5023
|
"bg-white dark:bg-gray-800",
|
|
4843
5024
|
"rounded-lg shadow-lg",
|
|
4844
5025
|
"border border-gray-200 dark:border-gray-700",
|
|
4845
|
-
|
|
5026
|
+
position2 === "bottom" ? "bottom-full mb-2" : "top-full mt-2"
|
|
4846
5027
|
),
|
|
4847
5028
|
children: [
|
|
4848
5029
|
/* @__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" }),
|
|
@@ -6668,7 +6849,7 @@ var init_AnnotationToolbar = __esm({
|
|
|
6668
6849
|
onShapeTypeChange: onShapeTypeChangeProp,
|
|
6669
6850
|
onColorChange: onColorChangeProp,
|
|
6670
6851
|
onStrokeWidthChange: onStrokeWidthChangeProp,
|
|
6671
|
-
position = "top",
|
|
6852
|
+
position: position2 = "top",
|
|
6672
6853
|
className
|
|
6673
6854
|
}) {
|
|
6674
6855
|
const storeActiveTool = useAnnotationStore((s) => s.activeAnnotationTool);
|
|
@@ -6718,9 +6899,9 @@ var init_AnnotationToolbar = __esm({
|
|
|
6718
6899
|
{
|
|
6719
6900
|
className: cn(
|
|
6720
6901
|
"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",
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6902
|
+
position2 === "floating" && "fixed bottom-20 left-1/2 -translate-x-1/2 z-50",
|
|
6903
|
+
position2 === "top" && "sticky top-0 z-40",
|
|
6904
|
+
position2 === "bottom" && "sticky bottom-0 z-40",
|
|
6724
6905
|
!isActive && "opacity-90",
|
|
6725
6906
|
className
|
|
6726
6907
|
),
|
|
@@ -8369,7 +8550,7 @@ var init_SelectionToolbar = __esm({
|
|
|
8369
8550
|
activeColor = "yellow",
|
|
8370
8551
|
className
|
|
8371
8552
|
}) {
|
|
8372
|
-
const [
|
|
8553
|
+
const [position2, setPosition] = (0, import_react35.useState)({ top: 0, left: 0, visible: false });
|
|
8373
8554
|
const toolbarRef = (0, import_react35.useRef)(null);
|
|
8374
8555
|
(0, import_react35.useEffect)(() => {
|
|
8375
8556
|
if (selection && selection.text && selection.rects.length > 0) {
|
|
@@ -8408,7 +8589,7 @@ var init_SelectionToolbar = __esm({
|
|
|
8408
8589
|
const handleCopy = (0, import_react35.useCallback)(() => {
|
|
8409
8590
|
onCopy?.();
|
|
8410
8591
|
}, [onCopy]);
|
|
8411
|
-
if (!
|
|
8592
|
+
if (!position2.visible || !selection?.text) {
|
|
8412
8593
|
return null;
|
|
8413
8594
|
}
|
|
8414
8595
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
@@ -8426,8 +8607,8 @@ var init_SelectionToolbar = __esm({
|
|
|
8426
8607
|
className
|
|
8427
8608
|
),
|
|
8428
8609
|
style: {
|
|
8429
|
-
top:
|
|
8430
|
-
left:
|
|
8610
|
+
top: position2.top,
|
|
8611
|
+
left: position2.left,
|
|
8431
8612
|
transform: "translateX(-50%)"
|
|
8432
8613
|
},
|
|
8433
8614
|
onMouseDown: (e) => {
|
|
@@ -8544,7 +8725,7 @@ var init_HighlightPopover = __esm({
|
|
|
8544
8725
|
}) {
|
|
8545
8726
|
const [isEditingComment, setIsEditingComment] = (0, import_react36.useState)(false);
|
|
8546
8727
|
const [comment, setComment] = (0, import_react36.useState)(highlight?.comment ?? "");
|
|
8547
|
-
const [
|
|
8728
|
+
const [position2, setPosition] = (0, import_react36.useState)({ top: 0, left: 0, visible: false });
|
|
8548
8729
|
const popoverRef = (0, import_react36.useRef)(null);
|
|
8549
8730
|
const textareaRef = (0, import_react36.useRef)(null);
|
|
8550
8731
|
(0, import_react36.useEffect)(() => {
|
|
@@ -8592,11 +8773,11 @@ var init_HighlightPopover = __esm({
|
|
|
8592
8773
|
onClose();
|
|
8593
8774
|
}
|
|
8594
8775
|
}
|
|
8595
|
-
if (
|
|
8776
|
+
if (position2.visible) {
|
|
8596
8777
|
document.addEventListener("mousedown", handleClickOutside);
|
|
8597
8778
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
8598
8779
|
}
|
|
8599
|
-
}, [
|
|
8780
|
+
}, [position2.visible, onClose]);
|
|
8600
8781
|
(0, import_react36.useEffect)(() => {
|
|
8601
8782
|
function handleKeyDown(event) {
|
|
8602
8783
|
if (event.key === "Escape") {
|
|
@@ -8608,11 +8789,11 @@ var init_HighlightPopover = __esm({
|
|
|
8608
8789
|
}
|
|
8609
8790
|
}
|
|
8610
8791
|
}
|
|
8611
|
-
if (
|
|
8792
|
+
if (position2.visible) {
|
|
8612
8793
|
document.addEventListener("keydown", handleKeyDown);
|
|
8613
8794
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
8614
8795
|
}
|
|
8615
|
-
}, [
|
|
8796
|
+
}, [position2.visible, isEditingComment, highlight?.comment, onClose]);
|
|
8616
8797
|
const handleColorClick = (0, import_react36.useCallback)(
|
|
8617
8798
|
(color) => {
|
|
8618
8799
|
if (highlight) {
|
|
@@ -8639,7 +8820,7 @@ var init_HighlightPopover = __esm({
|
|
|
8639
8820
|
onCopy?.(highlight.text);
|
|
8640
8821
|
}
|
|
8641
8822
|
}, [highlight, onCopy]);
|
|
8642
|
-
if (!highlight || !
|
|
8823
|
+
if (!highlight || !position2.visible) {
|
|
8643
8824
|
return null;
|
|
8644
8825
|
}
|
|
8645
8826
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
@@ -8656,8 +8837,8 @@ var init_HighlightPopover = __esm({
|
|
|
8656
8837
|
className
|
|
8657
8838
|
),
|
|
8658
8839
|
style: {
|
|
8659
|
-
top:
|
|
8660
|
-
left:
|
|
8840
|
+
top: position2.top,
|
|
8841
|
+
left: position2.left,
|
|
8661
8842
|
transform: "translate(-50%, -100%)",
|
|
8662
8843
|
width: 280
|
|
8663
8844
|
},
|
|
@@ -9839,17 +10020,237 @@ var init_DualPageContainer = __esm({
|
|
|
9839
10020
|
}
|
|
9840
10021
|
});
|
|
9841
10022
|
|
|
10023
|
+
// src/components/PDFViewer/BookModeContainer.tsx
|
|
10024
|
+
var import_react41, import_react_pageflip, import_jsx_runtime27, BookPage, BookModeContainer;
|
|
10025
|
+
var init_BookModeContainer = __esm({
|
|
10026
|
+
"src/components/PDFViewer/BookModeContainer.tsx"() {
|
|
10027
|
+
"use strict";
|
|
10028
|
+
import_react41 = __toESM(require("react"), 1);
|
|
10029
|
+
import_react_pageflip = __toESM(require("react-pageflip"), 1);
|
|
10030
|
+
init_PDFPage2();
|
|
10031
|
+
init_PDFLoadingScreen2();
|
|
10032
|
+
init_hooks();
|
|
10033
|
+
init_utils();
|
|
10034
|
+
import_jsx_runtime27 = require("react/jsx-runtime");
|
|
10035
|
+
BookPage = import_react41.default.forwardRef(function BookPage2({ pageNumber, page, scale, rotation, width, height }, ref) {
|
|
10036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { ref, className: "book-page", "data-page-number": pageNumber, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { style: { width, height, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
10037
|
+
PDFPage,
|
|
10038
|
+
{
|
|
10039
|
+
pageNumber,
|
|
10040
|
+
page,
|
|
10041
|
+
scale,
|
|
10042
|
+
rotation,
|
|
10043
|
+
showTextLayer: false,
|
|
10044
|
+
showAnnotationLayer: false,
|
|
10045
|
+
showHighlightLayer: false
|
|
10046
|
+
}
|
|
10047
|
+
) }) });
|
|
10048
|
+
});
|
|
10049
|
+
BookModeContainer = (0, import_react41.memo)(function BookModeContainer2({
|
|
10050
|
+
className,
|
|
10051
|
+
flippingTime = 800,
|
|
10052
|
+
drawShadow = true,
|
|
10053
|
+
maxShadowOpacity = 0.7
|
|
10054
|
+
}) {
|
|
10055
|
+
const {
|
|
10056
|
+
document: document2,
|
|
10057
|
+
currentPage,
|
|
10058
|
+
numPages,
|
|
10059
|
+
scale,
|
|
10060
|
+
rotation,
|
|
10061
|
+
theme,
|
|
10062
|
+
isLoading,
|
|
10063
|
+
goToPage
|
|
10064
|
+
} = usePDFViewer();
|
|
10065
|
+
const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
|
|
10066
|
+
const { viewerStore } = usePDFViewerStores();
|
|
10067
|
+
const [pages, setPages] = (0, import_react41.useState)([]);
|
|
10068
|
+
const [rawPageDims, setRawPageDims] = (0, import_react41.useState)({ width: 612, height: 792 });
|
|
10069
|
+
const [isLoadingPages, setIsLoadingPages] = (0, import_react41.useState)(false);
|
|
10070
|
+
const containerRef = (0, import_react41.useRef)(null);
|
|
10071
|
+
const [containerSize, setContainerSize] = (0, import_react41.useState)({ width: 0, height: 0 });
|
|
10072
|
+
const flipBookRef = (0, import_react41.useRef)(null);
|
|
10073
|
+
const isSyncingRef = (0, import_react41.useRef)(false);
|
|
10074
|
+
(0, import_react41.useEffect)(() => {
|
|
10075
|
+
const el = containerRef.current;
|
|
10076
|
+
if (!el) return;
|
|
10077
|
+
const measure = () => {
|
|
10078
|
+
setContainerSize({ width: el.clientWidth, height: el.clientHeight });
|
|
10079
|
+
};
|
|
10080
|
+
measure();
|
|
10081
|
+
const ro = new ResizeObserver(measure);
|
|
10082
|
+
ro.observe(el);
|
|
10083
|
+
return () => ro.disconnect();
|
|
10084
|
+
}, []);
|
|
10085
|
+
(0, import_react41.useEffect)(() => {
|
|
10086
|
+
if (!document2) {
|
|
10087
|
+
setPages([]);
|
|
10088
|
+
return;
|
|
10089
|
+
}
|
|
10090
|
+
let cancelled = false;
|
|
10091
|
+
const loadAllPages = async () => {
|
|
10092
|
+
setIsLoadingPages(true);
|
|
10093
|
+
try {
|
|
10094
|
+
const pagePromises = [];
|
|
10095
|
+
for (let i = 1; i <= numPages; i++) {
|
|
10096
|
+
pagePromises.push(document2.getPage(i));
|
|
10097
|
+
}
|
|
10098
|
+
const results = await Promise.allSettled(pagePromises);
|
|
10099
|
+
if (!cancelled) {
|
|
10100
|
+
const loaded2 = results.map((r) => r.status === "fulfilled" ? r.value : null);
|
|
10101
|
+
setPages(loaded2);
|
|
10102
|
+
const firstPage = loaded2[0];
|
|
10103
|
+
if (firstPage) {
|
|
10104
|
+
const vp = firstPage.getViewport({ scale: 1, rotation });
|
|
10105
|
+
setRawPageDims({ width: vp.width, height: vp.height });
|
|
10106
|
+
}
|
|
10107
|
+
}
|
|
10108
|
+
} catch {
|
|
10109
|
+
} finally {
|
|
10110
|
+
if (!cancelled) setIsLoadingPages(false);
|
|
10111
|
+
}
|
|
10112
|
+
};
|
|
10113
|
+
loadAllPages();
|
|
10114
|
+
return () => {
|
|
10115
|
+
cancelled = true;
|
|
10116
|
+
};
|
|
10117
|
+
}, [document2, numPages, rotation]);
|
|
10118
|
+
(0, import_react41.useEffect)(() => {
|
|
10119
|
+
if (pages[0]) {
|
|
10120
|
+
const vp = pages[0].getViewport({ scale: 1, rotation });
|
|
10121
|
+
setRawPageDims({ width: vp.width, height: vp.height });
|
|
10122
|
+
}
|
|
10123
|
+
}, [pages, rotation]);
|
|
10124
|
+
const padding = 8;
|
|
10125
|
+
const fitWidth = Math.max(containerSize.width - padding * 2, 200);
|
|
10126
|
+
const fitHeight = Math.max(containerSize.height - padding * 2, 300);
|
|
10127
|
+
const pageAspect = rawPageDims.width / rawPageDims.height;
|
|
10128
|
+
let displayWidth;
|
|
10129
|
+
let displayHeight;
|
|
10130
|
+
if (fitWidth / fitHeight > pageAspect) {
|
|
10131
|
+
displayHeight = fitHeight;
|
|
10132
|
+
displayWidth = Math.floor(fitHeight * pageAspect);
|
|
10133
|
+
} else {
|
|
10134
|
+
displayWidth = fitWidth;
|
|
10135
|
+
displayHeight = Math.floor(fitWidth / pageAspect);
|
|
10136
|
+
}
|
|
10137
|
+
const renderScale = displayWidth / rawPageDims.width;
|
|
10138
|
+
(0, import_react41.useEffect)(() => {
|
|
10139
|
+
const pageFlip = flipBookRef.current?.pageFlip();
|
|
10140
|
+
if (!pageFlip) return;
|
|
10141
|
+
const flipBookPage = pageFlip.getCurrentPageIndex();
|
|
10142
|
+
const targetIndex = currentPage - 1;
|
|
10143
|
+
if (flipBookPage !== targetIndex) {
|
|
10144
|
+
isSyncingRef.current = true;
|
|
10145
|
+
pageFlip.turnToPage(targetIndex);
|
|
10146
|
+
setTimeout(() => {
|
|
10147
|
+
isSyncingRef.current = false;
|
|
10148
|
+
}, 100);
|
|
10149
|
+
}
|
|
10150
|
+
}, [currentPage]);
|
|
10151
|
+
(0, import_react41.useEffect)(() => {
|
|
10152
|
+
if (scrollToPageRequest) {
|
|
10153
|
+
requestAnimationFrame(() => {
|
|
10154
|
+
viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
|
|
10155
|
+
});
|
|
10156
|
+
}
|
|
10157
|
+
}, [scrollToPageRequest, viewerStore]);
|
|
10158
|
+
const handleFlip = (0, import_react41.useCallback)((e) => {
|
|
10159
|
+
if (isSyncingRef.current) return;
|
|
10160
|
+
const newPage = e.data + 1;
|
|
10161
|
+
if (newPage !== currentPage && newPage >= 1 && newPage <= numPages) {
|
|
10162
|
+
goToPage(newPage);
|
|
10163
|
+
}
|
|
10164
|
+
}, [currentPage, numPages, goToPage]);
|
|
10165
|
+
const themeStyles = {
|
|
10166
|
+
light: "bg-gray-100",
|
|
10167
|
+
dark: "bg-gray-900",
|
|
10168
|
+
sepia: "bg-amber-50"
|
|
10169
|
+
};
|
|
10170
|
+
const themeClass = theme === "dark" ? "dark" : theme === "sepia" ? "sepia" : "";
|
|
10171
|
+
const ready = !!document2 && !isLoadingPages && pages.length > 0;
|
|
10172
|
+
const hasContainer = containerSize.width > 0 && containerSize.height > 0;
|
|
10173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
10174
|
+
"div",
|
|
10175
|
+
{
|
|
10176
|
+
ref: containerRef,
|
|
10177
|
+
className: cn(
|
|
10178
|
+
"book-mode-container",
|
|
10179
|
+
"flex-1 h-full w-full overflow-hidden",
|
|
10180
|
+
"flex items-center justify-center",
|
|
10181
|
+
themeStyles[theme],
|
|
10182
|
+
themeClass,
|
|
10183
|
+
className
|
|
10184
|
+
),
|
|
10185
|
+
style: { userSelect: "none", WebkitUserSelect: "none" },
|
|
10186
|
+
children: [
|
|
10187
|
+
!ready && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
10188
|
+
PDFLoadingScreen,
|
|
10189
|
+
{
|
|
10190
|
+
phase: !document2 ? isLoading ? "fetching" : "initializing" : "rendering"
|
|
10191
|
+
}
|
|
10192
|
+
),
|
|
10193
|
+
ready && hasContainer && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
10194
|
+
import_react_pageflip.default,
|
|
10195
|
+
{
|
|
10196
|
+
ref: flipBookRef,
|
|
10197
|
+
width: displayWidth,
|
|
10198
|
+
height: displayHeight,
|
|
10199
|
+
size: "fixed",
|
|
10200
|
+
minWidth: displayWidth,
|
|
10201
|
+
maxWidth: displayWidth,
|
|
10202
|
+
minHeight: displayHeight,
|
|
10203
|
+
maxHeight: displayHeight,
|
|
10204
|
+
drawShadow,
|
|
10205
|
+
maxShadowOpacity,
|
|
10206
|
+
flippingTime,
|
|
10207
|
+
usePortrait: true,
|
|
10208
|
+
startPage: currentPage - 1,
|
|
10209
|
+
showCover: false,
|
|
10210
|
+
mobileScrollSupport: true,
|
|
10211
|
+
swipeDistance: 30,
|
|
10212
|
+
showPageCorners: true,
|
|
10213
|
+
useMouseEvents: true,
|
|
10214
|
+
clickEventForward: false,
|
|
10215
|
+
onFlip: handleFlip,
|
|
10216
|
+
className: "book-flipbook",
|
|
10217
|
+
style: {},
|
|
10218
|
+
startZIndex: 0,
|
|
10219
|
+
autoSize: false,
|
|
10220
|
+
renderOnlyPageLengthChange: false,
|
|
10221
|
+
disableFlipByClick: false,
|
|
10222
|
+
children: pages.map((page, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
10223
|
+
BookPage,
|
|
10224
|
+
{
|
|
10225
|
+
pageNumber: index + 1,
|
|
10226
|
+
page,
|
|
10227
|
+
scale: renderScale,
|
|
10228
|
+
rotation,
|
|
10229
|
+
width: displayWidth,
|
|
10230
|
+
height: displayHeight
|
|
10231
|
+
},
|
|
10232
|
+
index
|
|
10233
|
+
))
|
|
10234
|
+
}
|
|
10235
|
+
)
|
|
10236
|
+
]
|
|
10237
|
+
}
|
|
10238
|
+
);
|
|
10239
|
+
});
|
|
10240
|
+
}
|
|
10241
|
+
});
|
|
10242
|
+
|
|
9842
10243
|
// src/components/FloatingZoomControls/FloatingZoomControls.tsx
|
|
9843
|
-
var
|
|
10244
|
+
var import_react42, import_jsx_runtime28, FloatingZoomControls;
|
|
9844
10245
|
var init_FloatingZoomControls = __esm({
|
|
9845
10246
|
"src/components/FloatingZoomControls/FloatingZoomControls.tsx"() {
|
|
9846
10247
|
"use strict";
|
|
9847
|
-
|
|
10248
|
+
import_react42 = require("react");
|
|
9848
10249
|
init_hooks();
|
|
9849
10250
|
init_utils();
|
|
9850
|
-
|
|
9851
|
-
FloatingZoomControls = (0,
|
|
9852
|
-
position = "bottom-right",
|
|
10251
|
+
import_jsx_runtime28 = require("react/jsx-runtime");
|
|
10252
|
+
FloatingZoomControls = (0, import_react42.memo)(function FloatingZoomControls2({
|
|
10253
|
+
position: position2 = "bottom-right",
|
|
9853
10254
|
className,
|
|
9854
10255
|
showFitToWidth = true,
|
|
9855
10256
|
showFitToPage = false,
|
|
@@ -9858,20 +10259,20 @@ var init_FloatingZoomControls = __esm({
|
|
|
9858
10259
|
const { viewerStore } = usePDFViewerStores();
|
|
9859
10260
|
const scale = useViewerStore((s) => s.scale);
|
|
9860
10261
|
const document2 = useViewerStore((s) => s.document);
|
|
9861
|
-
const handleZoomIn = (0,
|
|
10262
|
+
const handleZoomIn = (0, import_react42.useCallback)(() => {
|
|
9862
10263
|
const currentScale = viewerStore.getState().scale;
|
|
9863
10264
|
const newScale = Math.min(4, currentScale + 0.05);
|
|
9864
10265
|
viewerStore.getState().setScale(newScale);
|
|
9865
10266
|
}, [viewerStore]);
|
|
9866
|
-
const handleZoomOut = (0,
|
|
10267
|
+
const handleZoomOut = (0, import_react42.useCallback)(() => {
|
|
9867
10268
|
const currentScale = viewerStore.getState().scale;
|
|
9868
10269
|
const newScale = Math.max(0.1, currentScale - 0.05);
|
|
9869
10270
|
viewerStore.getState().setScale(newScale);
|
|
9870
10271
|
}, [viewerStore]);
|
|
9871
|
-
const handleFitToWidth = (0,
|
|
10272
|
+
const handleFitToWidth = (0, import_react42.useCallback)(() => {
|
|
9872
10273
|
viewerStore.getState().setScale(1);
|
|
9873
10274
|
}, [viewerStore]);
|
|
9874
|
-
const handleFitToPage = (0,
|
|
10275
|
+
const handleFitToPage = (0, import_react42.useCallback)(() => {
|
|
9875
10276
|
viewerStore.getState().setScale(0.75);
|
|
9876
10277
|
}, [viewerStore]);
|
|
9877
10278
|
if (!document2) return null;
|
|
@@ -9882,7 +10283,7 @@ var init_FloatingZoomControls = __esm({
|
|
|
9882
10283
|
"top-left": "top-4 left-4"
|
|
9883
10284
|
};
|
|
9884
10285
|
const zoomPercentage = Math.round(scale * 100);
|
|
9885
|
-
return /* @__PURE__ */ (0,
|
|
10286
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
9886
10287
|
"div",
|
|
9887
10288
|
{
|
|
9888
10289
|
className: cn(
|
|
@@ -9890,11 +10291,11 @@ var init_FloatingZoomControls = __esm({
|
|
|
9890
10291
|
"bg-white dark:bg-gray-800 rounded-lg shadow-lg",
|
|
9891
10292
|
"border border-gray-200 dark:border-gray-700",
|
|
9892
10293
|
"p-1",
|
|
9893
|
-
positionClasses[
|
|
10294
|
+
positionClasses[position2],
|
|
9894
10295
|
className
|
|
9895
10296
|
),
|
|
9896
10297
|
children: [
|
|
9897
|
-
/* @__PURE__ */ (0,
|
|
10298
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
9898
10299
|
"button",
|
|
9899
10300
|
{
|
|
9900
10301
|
onClick: handleZoomOut,
|
|
@@ -9908,14 +10309,14 @@ var init_FloatingZoomControls = __esm({
|
|
|
9908
10309
|
disabled: scale <= 0.25,
|
|
9909
10310
|
title: "Zoom Out",
|
|
9910
10311
|
"aria-label": "Zoom Out",
|
|
9911
|
-
children: /* @__PURE__ */ (0,
|
|
10312
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) })
|
|
9912
10313
|
}
|
|
9913
10314
|
),
|
|
9914
|
-
showZoomLevel && /* @__PURE__ */ (0,
|
|
10315
|
+
showZoomLevel && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "min-w-[48px] text-center text-sm font-medium text-gray-700 dark:text-gray-300", children: [
|
|
9915
10316
|
zoomPercentage,
|
|
9916
10317
|
"%"
|
|
9917
10318
|
] }),
|
|
9918
|
-
/* @__PURE__ */ (0,
|
|
10319
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
9919
10320
|
"button",
|
|
9920
10321
|
{
|
|
9921
10322
|
onClick: handleZoomIn,
|
|
@@ -9929,11 +10330,11 @@ var init_FloatingZoomControls = __esm({
|
|
|
9929
10330
|
disabled: scale >= 4,
|
|
9930
10331
|
title: "Zoom In",
|
|
9931
10332
|
"aria-label": "Zoom In",
|
|
9932
|
-
children: /* @__PURE__ */ (0,
|
|
10333
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) })
|
|
9933
10334
|
}
|
|
9934
10335
|
),
|
|
9935
|
-
(showFitToWidth || showFitToPage) && /* @__PURE__ */ (0,
|
|
9936
|
-
showFitToWidth && /* @__PURE__ */ (0,
|
|
10336
|
+
(showFitToWidth || showFitToPage) && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1" }),
|
|
10337
|
+
showFitToWidth && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
9937
10338
|
"button",
|
|
9938
10339
|
{
|
|
9939
10340
|
onClick: handleFitToWidth,
|
|
@@ -9945,10 +10346,10 @@ var init_FloatingZoomControls = __esm({
|
|
|
9945
10346
|
),
|
|
9946
10347
|
title: "Fit to Width",
|
|
9947
10348
|
"aria-label": "Fit to Width",
|
|
9948
|
-
children: /* @__PURE__ */ (0,
|
|
10349
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) })
|
|
9949
10350
|
}
|
|
9950
10351
|
),
|
|
9951
|
-
showFitToPage && /* @__PURE__ */ (0,
|
|
10352
|
+
showFitToPage && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
9952
10353
|
"button",
|
|
9953
10354
|
{
|
|
9954
10355
|
onClick: handleFitToPage,
|
|
@@ -9960,7 +10361,7 @@ var init_FloatingZoomControls = __esm({
|
|
|
9960
10361
|
),
|
|
9961
10362
|
title: "Fit to Page",
|
|
9962
10363
|
"aria-label": "Fit to Page",
|
|
9963
|
-
children: /* @__PURE__ */ (0,
|
|
10364
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) })
|
|
9964
10365
|
}
|
|
9965
10366
|
)
|
|
9966
10367
|
]
|
|
@@ -10022,11 +10423,11 @@ function calculateMatchRects3(textItems, startOffset, length, viewport) {
|
|
|
10022
10423
|
}
|
|
10023
10424
|
return rects;
|
|
10024
10425
|
}
|
|
10025
|
-
var
|
|
10426
|
+
var import_react43, import_jsx_runtime29, PDFViewerInner, PDFViewerInnerWithRef, PDFViewerClient;
|
|
10026
10427
|
var init_PDFViewerClient = __esm({
|
|
10027
10428
|
"src/components/PDFViewer/PDFViewerClient.tsx"() {
|
|
10028
10429
|
"use strict";
|
|
10029
|
-
|
|
10430
|
+
import_react43 = require("react");
|
|
10030
10431
|
init_hooks();
|
|
10031
10432
|
init_utils();
|
|
10032
10433
|
init_Toolbar2();
|
|
@@ -10035,11 +10436,12 @@ var init_PDFViewerClient = __esm({
|
|
|
10035
10436
|
init_DocumentContainer();
|
|
10036
10437
|
init_ContinuousScrollContainer();
|
|
10037
10438
|
init_DualPageContainer();
|
|
10439
|
+
init_BookModeContainer();
|
|
10038
10440
|
init_FloatingZoomControls2();
|
|
10039
10441
|
init_PDFLoadingScreen2();
|
|
10040
10442
|
init_utils();
|
|
10041
|
-
|
|
10042
|
-
PDFViewerInner = (0,
|
|
10443
|
+
import_jsx_runtime29 = require("react/jsx-runtime");
|
|
10444
|
+
PDFViewerInner = (0, import_react43.memo)(function PDFViewerInner2({
|
|
10043
10445
|
src,
|
|
10044
10446
|
initialPage = 1,
|
|
10045
10447
|
page: controlledPage,
|
|
@@ -10066,19 +10468,19 @@ var init_PDFViewerClient = __esm({
|
|
|
10066
10468
|
onReady
|
|
10067
10469
|
}) {
|
|
10068
10470
|
const { viewerStore, annotationStore, searchStore } = usePDFViewerStores();
|
|
10069
|
-
const mountedRef = (0,
|
|
10070
|
-
const [, setLoadState] = (0,
|
|
10071
|
-
const onDocumentLoadRef = (0,
|
|
10072
|
-
const onErrorRef = (0,
|
|
10073
|
-
const onPageChangeRef = (0,
|
|
10074
|
-
const onScaleChangeRef = (0,
|
|
10075
|
-
const onZoomChangeRef = (0,
|
|
10076
|
-
const onPageRenderStartRef = (0,
|
|
10077
|
-
const onPageRenderCompleteRef = (0,
|
|
10078
|
-
const onHighlightAddedRef = (0,
|
|
10079
|
-
const onHighlightRemovedRef = (0,
|
|
10080
|
-
const onAnnotationAddedRef = (0,
|
|
10081
|
-
const onReadyRef = (0,
|
|
10471
|
+
const mountedRef = (0, import_react43.useRef)(true);
|
|
10472
|
+
const [, setLoadState] = (0, import_react43.useState)("idle");
|
|
10473
|
+
const onDocumentLoadRef = (0, import_react43.useRef)(onDocumentLoad);
|
|
10474
|
+
const onErrorRef = (0, import_react43.useRef)(onError);
|
|
10475
|
+
const onPageChangeRef = (0, import_react43.useRef)(onPageChange);
|
|
10476
|
+
const onScaleChangeRef = (0, import_react43.useRef)(onScaleChange);
|
|
10477
|
+
const onZoomChangeRef = (0, import_react43.useRef)(onZoomChange);
|
|
10478
|
+
const onPageRenderStartRef = (0, import_react43.useRef)(onPageRenderStart);
|
|
10479
|
+
const onPageRenderCompleteRef = (0, import_react43.useRef)(onPageRenderComplete);
|
|
10480
|
+
const onHighlightAddedRef = (0, import_react43.useRef)(onHighlightAdded);
|
|
10481
|
+
const onHighlightRemovedRef = (0, import_react43.useRef)(onHighlightRemoved);
|
|
10482
|
+
const onAnnotationAddedRef = (0, import_react43.useRef)(onAnnotationAdded);
|
|
10483
|
+
const onReadyRef = (0, import_react43.useRef)(onReady);
|
|
10082
10484
|
onDocumentLoadRef.current = onDocumentLoad;
|
|
10083
10485
|
onErrorRef.current = onError;
|
|
10084
10486
|
onPageChangeRef.current = onPageChange;
|
|
@@ -10091,8 +10493,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10091
10493
|
onAnnotationAddedRef.current = onAnnotationAdded;
|
|
10092
10494
|
onReadyRef.current = onReady;
|
|
10093
10495
|
const isControlled = controlledPage !== void 0;
|
|
10094
|
-
const prevControlledPageRef = (0,
|
|
10095
|
-
const srcIdRef = (0,
|
|
10496
|
+
const prevControlledPageRef = (0, import_react43.useRef)(controlledPage);
|
|
10497
|
+
const srcIdRef = (0, import_react43.useRef)(null);
|
|
10096
10498
|
const currentPage = useViewerStore((s) => s.currentPage);
|
|
10097
10499
|
const scale = useViewerStore((s) => s.scale);
|
|
10098
10500
|
const theme = useViewerStore((s) => s.theme);
|
|
@@ -10102,8 +10504,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10102
10504
|
const sidebarOpen = useViewerStore((s) => s.sidebarOpen);
|
|
10103
10505
|
const streamingProgress = useViewerStore((s) => s.streamingProgress);
|
|
10104
10506
|
const srcId = getSrcIdentifier(src);
|
|
10105
|
-
const handleRef = (0,
|
|
10106
|
-
(0,
|
|
10507
|
+
const handleRef = (0, import_react43.useRef)(null);
|
|
10508
|
+
(0, import_react43.useEffect)(() => {
|
|
10107
10509
|
const handle = {
|
|
10108
10510
|
// ==================== Text Highlighting ====================
|
|
10109
10511
|
highlightText: async (text, options) => {
|
|
@@ -10521,14 +10923,14 @@ var init_PDFViewerClient = __esm({
|
|
|
10521
10923
|
handleRef.current = handle;
|
|
10522
10924
|
onReadyRef.current?.(handle);
|
|
10523
10925
|
}, [viewerStore, annotationStore, searchStore]);
|
|
10524
|
-
const handleRetry = (0,
|
|
10926
|
+
const handleRetry = (0, import_react43.useCallback)(() => {
|
|
10525
10927
|
srcIdRef.current = null;
|
|
10526
10928
|
viewerStore.getState().setError(null);
|
|
10527
10929
|
setLoadState("idle");
|
|
10528
10930
|
}, [viewerStore]);
|
|
10529
|
-
const abortControllerRef = (0,
|
|
10530
|
-
const currentSrcRef = (0,
|
|
10531
|
-
(0,
|
|
10931
|
+
const abortControllerRef = (0, import_react43.useRef)(null);
|
|
10932
|
+
const currentSrcRef = (0, import_react43.useRef)(null);
|
|
10933
|
+
(0, import_react43.useEffect)(() => {
|
|
10532
10934
|
mountedRef.current = true;
|
|
10533
10935
|
return () => {
|
|
10534
10936
|
mountedRef.current = false;
|
|
@@ -10553,8 +10955,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10553
10955
|
viewerStore.getState().setError(null);
|
|
10554
10956
|
};
|
|
10555
10957
|
}, [viewerStore]);
|
|
10556
|
-
const cancelLoaderRef = (0,
|
|
10557
|
-
(0,
|
|
10958
|
+
const cancelLoaderRef = (0, import_react43.useRef)(null);
|
|
10959
|
+
(0, import_react43.useEffect)(() => {
|
|
10558
10960
|
if (srcIdRef.current === srcId && viewerStore.getState().document) {
|
|
10559
10961
|
return;
|
|
10560
10962
|
}
|
|
@@ -10595,12 +10997,12 @@ var init_PDFViewerClient = __esm({
|
|
|
10595
10997
|
src,
|
|
10596
10998
|
workerSrc,
|
|
10597
10999
|
signal: abortController.signal,
|
|
10598
|
-
onProgress: ({ loaded, total }) => {
|
|
11000
|
+
onProgress: ({ loaded: loaded2, total }) => {
|
|
10599
11001
|
if (!mountedRef.current || srcIdRef.current !== loadId || abortController.signal.aborted) {
|
|
10600
11002
|
return;
|
|
10601
11003
|
}
|
|
10602
11004
|
const now = Date.now();
|
|
10603
|
-
const percent = total > 0 ? Math.round(
|
|
11005
|
+
const percent = total > 0 ? Math.round(loaded2 / total * 100) : 0;
|
|
10604
11006
|
const timePassed = now - lastProgressUpdate >= PROGRESS_THROTTLE_MS;
|
|
10605
11007
|
const percentChanged = Math.abs(percent - lastPercent) >= PROGRESS_MIN_CHANGE;
|
|
10606
11008
|
const isComplete = percent >= 100;
|
|
@@ -10611,10 +11013,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10611
11013
|
loadingProgress: {
|
|
10612
11014
|
phase: "fetching",
|
|
10613
11015
|
percent,
|
|
10614
|
-
bytesLoaded:
|
|
11016
|
+
bytesLoaded: loaded2,
|
|
10615
11017
|
totalBytes: total
|
|
10616
11018
|
},
|
|
10617
|
-
streamingProgress: { loaded, total },
|
|
11019
|
+
streamingProgress: { loaded: loaded2, total },
|
|
10618
11020
|
documentLoadingState: "loading"
|
|
10619
11021
|
});
|
|
10620
11022
|
}
|
|
@@ -10688,22 +11090,22 @@ var init_PDFViewerClient = __esm({
|
|
|
10688
11090
|
}
|
|
10689
11091
|
};
|
|
10690
11092
|
}, [srcId, src, workerSrc, initialPage, initialScale, viewerStore]);
|
|
10691
|
-
const prevPageRef = (0,
|
|
10692
|
-
(0,
|
|
11093
|
+
const prevPageRef = (0, import_react43.useRef)(currentPage);
|
|
11094
|
+
(0, import_react43.useEffect)(() => {
|
|
10693
11095
|
if (prevPageRef.current !== currentPage) {
|
|
10694
11096
|
prevPageRef.current = currentPage;
|
|
10695
11097
|
onPageChangeRef.current?.(currentPage);
|
|
10696
11098
|
}
|
|
10697
11099
|
}, [currentPage]);
|
|
10698
|
-
const prevScaleRef = (0,
|
|
10699
|
-
(0,
|
|
11100
|
+
const prevScaleRef = (0, import_react43.useRef)(scale);
|
|
11101
|
+
(0, import_react43.useEffect)(() => {
|
|
10700
11102
|
if (prevScaleRef.current !== scale) {
|
|
10701
11103
|
prevScaleRef.current = scale;
|
|
10702
11104
|
onScaleChangeRef.current?.(scale);
|
|
10703
11105
|
onZoomChangeRef.current?.(scale);
|
|
10704
11106
|
}
|
|
10705
11107
|
}, [scale]);
|
|
10706
|
-
(0,
|
|
11108
|
+
(0, import_react43.useEffect)(() => {
|
|
10707
11109
|
if (!isControlled || controlledPage === void 0) return;
|
|
10708
11110
|
if (prevControlledPageRef.current === controlledPage) return;
|
|
10709
11111
|
prevControlledPageRef.current = controlledPage;
|
|
@@ -10716,7 +11118,7 @@ var init_PDFViewerClient = __esm({
|
|
|
10716
11118
|
if (error) {
|
|
10717
11119
|
if (errorComponent) {
|
|
10718
11120
|
const errorContent = typeof errorComponent === "function" ? errorComponent(error, handleRetry) : errorComponent;
|
|
10719
|
-
return /* @__PURE__ */ (0,
|
|
11121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
10720
11122
|
"div",
|
|
10721
11123
|
{
|
|
10722
11124
|
className: cn(
|
|
@@ -10730,7 +11132,7 @@ var init_PDFViewerClient = __esm({
|
|
|
10730
11132
|
}
|
|
10731
11133
|
);
|
|
10732
11134
|
}
|
|
10733
|
-
return /* @__PURE__ */ (0,
|
|
11135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
10734
11136
|
"div",
|
|
10735
11137
|
{
|
|
10736
11138
|
className: cn(
|
|
@@ -10740,10 +11142,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10740
11142
|
themeClass,
|
|
10741
11143
|
className
|
|
10742
11144
|
),
|
|
10743
|
-
children: /* @__PURE__ */ (0,
|
|
10744
|
-
/* @__PURE__ */ (0,
|
|
10745
|
-
/* @__PURE__ */ (0,
|
|
10746
|
-
/* @__PURE__ */ (0,
|
|
11145
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "text-center p-8", children: [
|
|
11146
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-red-500 text-lg font-semibold mb-2", children: "Failed to load PDF" }),
|
|
11147
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-gray-500 text-sm", children: error.message }),
|
|
11148
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
10747
11149
|
"button",
|
|
10748
11150
|
{
|
|
10749
11151
|
onClick: handleRetry,
|
|
@@ -10758,15 +11160,17 @@ var init_PDFViewerClient = __esm({
|
|
|
10758
11160
|
const renderContainer = () => {
|
|
10759
11161
|
switch (viewMode) {
|
|
10760
11162
|
case "continuous":
|
|
10761
|
-
return /* @__PURE__ */ (0,
|
|
11163
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ContinuousScrollContainer, {});
|
|
10762
11164
|
case "dual":
|
|
10763
|
-
return /* @__PURE__ */ (0,
|
|
11165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(DualPageContainer, {});
|
|
11166
|
+
case "book":
|
|
11167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(BookModeContainer, {});
|
|
10764
11168
|
case "single":
|
|
10765
11169
|
default:
|
|
10766
|
-
return /* @__PURE__ */ (0,
|
|
11170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(DocumentContainer, {});
|
|
10767
11171
|
}
|
|
10768
11172
|
};
|
|
10769
|
-
return /* @__PURE__ */ (0,
|
|
11173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
10770
11174
|
"div",
|
|
10771
11175
|
{
|
|
10772
11176
|
className: cn(
|
|
@@ -10778,14 +11182,14 @@ var init_PDFViewerClient = __esm({
|
|
|
10778
11182
|
className
|
|
10779
11183
|
),
|
|
10780
11184
|
children: [
|
|
10781
|
-
showToolbar && /* @__PURE__ */ (0,
|
|
10782
|
-
showAnnotationToolbar && /* @__PURE__ */ (0,
|
|
10783
|
-
/* @__PURE__ */ (0,
|
|
10784
|
-
showSidebar && sidebarOpen && /* @__PURE__ */ (0,
|
|
11185
|
+
showToolbar && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Toolbar, {}),
|
|
11186
|
+
showAnnotationToolbar && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(AnnotationToolbar, {}),
|
|
11187
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
11188
|
+
showSidebar && sidebarOpen && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Sidebar, {}),
|
|
10785
11189
|
renderContainer()
|
|
10786
11190
|
] }),
|
|
10787
|
-
showFloatingZoom && /* @__PURE__ */ (0,
|
|
10788
|
-
isLoading && /* @__PURE__ */ (0,
|
|
11191
|
+
showFloatingZoom && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(FloatingZoomControls, { position: "bottom-right" }),
|
|
11192
|
+
isLoading && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "absolute inset-0 z-50", children: loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
10789
11193
|
PDFLoadingScreen,
|
|
10790
11194
|
{
|
|
10791
11195
|
phase: loadingProgress?.phase ?? "fetching",
|
|
@@ -10794,10 +11198,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10794
11198
|
totalBytes: loadingProgress?.totalBytes
|
|
10795
11199
|
}
|
|
10796
11200
|
) }),
|
|
10797
|
-
!isLoading && streamingProgress && streamingProgress.total > 0 && streamingProgress.loaded < streamingProgress.total && /* @__PURE__ */ (0,
|
|
10798
|
-
/* @__PURE__ */ (0,
|
|
10799
|
-
/* @__PURE__ */ (0,
|
|
10800
|
-
/* @__PURE__ */ (0,
|
|
11201
|
+
!isLoading && streamingProgress && streamingProgress.total > 0 && streamingProgress.loaded < streamingProgress.total && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "absolute bottom-20 right-4 z-40 px-3 py-2 bg-gray-900/80 text-white text-xs rounded-lg shadow-lg flex items-center gap-2", children: [
|
|
11202
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-3 h-3 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
11203
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { children: "Loading pages..." }),
|
|
11204
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: "text-white/60", children: [
|
|
10801
11205
|
Math.round(streamingProgress.loaded / streamingProgress.total * 100),
|
|
10802
11206
|
"%"
|
|
10803
11207
|
] })
|
|
@@ -10806,10 +11210,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10806
11210
|
}
|
|
10807
11211
|
);
|
|
10808
11212
|
});
|
|
10809
|
-
PDFViewerInnerWithRef = (0,
|
|
11213
|
+
PDFViewerInnerWithRef = (0, import_react43.forwardRef)(
|
|
10810
11214
|
function PDFViewerInnerWithRef2(props, ref) {
|
|
10811
|
-
const handleRef = (0,
|
|
10812
|
-
const handleReady = (0,
|
|
11215
|
+
const handleRef = (0, import_react43.useRef)(null);
|
|
11216
|
+
const handleReady = (0, import_react43.useCallback)((handle) => {
|
|
10813
11217
|
handleRef.current = handle;
|
|
10814
11218
|
if (typeof ref === "function") {
|
|
10815
11219
|
ref(handle);
|
|
@@ -10817,17 +11221,17 @@ var init_PDFViewerClient = __esm({
|
|
|
10817
11221
|
ref.current = handle;
|
|
10818
11222
|
}
|
|
10819
11223
|
}, [ref]);
|
|
10820
|
-
return /* @__PURE__ */ (0,
|
|
11224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(PDFViewerInner, { ...props, onReady: handleReady });
|
|
10821
11225
|
}
|
|
10822
11226
|
);
|
|
10823
|
-
PDFViewerClient = (0,
|
|
10824
|
-
(0,
|
|
10825
|
-
return /* @__PURE__ */ (0,
|
|
11227
|
+
PDFViewerClient = (0, import_react43.memo)(
|
|
11228
|
+
(0, import_react43.forwardRef)(function PDFViewerClient2(props, ref) {
|
|
11229
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
10826
11230
|
PDFViewerProvider,
|
|
10827
11231
|
{
|
|
10828
11232
|
theme: props.theme,
|
|
10829
11233
|
defaultSidebarPanel: props.defaultSidebarPanel,
|
|
10830
|
-
children: /* @__PURE__ */ (0,
|
|
11234
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(PDFViewerInnerWithRef, { ref, ...props })
|
|
10831
11235
|
}
|
|
10832
11236
|
);
|
|
10833
11237
|
})
|
|
@@ -10836,20 +11240,20 @@ var init_PDFViewerClient = __esm({
|
|
|
10836
11240
|
});
|
|
10837
11241
|
|
|
10838
11242
|
// src/components/PDFViewer/PDFViewer.tsx
|
|
10839
|
-
var
|
|
11243
|
+
var import_react44, import_jsx_runtime30, PDFViewerClient3, PDFViewerLoading, PDFViewer;
|
|
10840
11244
|
var init_PDFViewer = __esm({
|
|
10841
11245
|
"src/components/PDFViewer/PDFViewer.tsx"() {
|
|
10842
11246
|
"use strict";
|
|
10843
|
-
|
|
11247
|
+
import_react44 = require("react");
|
|
10844
11248
|
init_utils();
|
|
10845
|
-
|
|
10846
|
-
PDFViewerClient3 = (0,
|
|
11249
|
+
import_jsx_runtime30 = require("react/jsx-runtime");
|
|
11250
|
+
PDFViewerClient3 = (0, import_react44.lazy)(
|
|
10847
11251
|
() => Promise.resolve().then(() => (init_PDFViewerClient(), PDFViewerClient_exports)).then((mod) => ({ default: mod.PDFViewerClient }))
|
|
10848
11252
|
);
|
|
10849
|
-
PDFViewerLoading = (0,
|
|
11253
|
+
PDFViewerLoading = (0, import_react44.memo)(function PDFViewerLoading2({
|
|
10850
11254
|
className
|
|
10851
11255
|
}) {
|
|
10852
|
-
return /* @__PURE__ */ (0,
|
|
11256
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
10853
11257
|
"div",
|
|
10854
11258
|
{
|
|
10855
11259
|
className: cn(
|
|
@@ -10858,18 +11262,18 @@ var init_PDFViewer = __esm({
|
|
|
10858
11262
|
"bg-white dark:bg-gray-900",
|
|
10859
11263
|
className
|
|
10860
11264
|
),
|
|
10861
|
-
children: /* @__PURE__ */ (0,
|
|
10862
|
-
/* @__PURE__ */ (0,
|
|
10863
|
-
/* @__PURE__ */ (0,
|
|
11265
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col items-center", children: [
|
|
11266
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
|
|
11267
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF viewer..." })
|
|
10864
11268
|
] }) })
|
|
10865
11269
|
}
|
|
10866
11270
|
);
|
|
10867
11271
|
});
|
|
10868
|
-
PDFViewer = (0,
|
|
11272
|
+
PDFViewer = (0, import_react44.memo)(function PDFViewer2(props) {
|
|
10869
11273
|
if (typeof window === "undefined") {
|
|
10870
|
-
return /* @__PURE__ */ (0,
|
|
11274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PDFViewerLoading, { className: props.className });
|
|
10871
11275
|
}
|
|
10872
|
-
return /* @__PURE__ */ (0,
|
|
11276
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react44.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PDFViewerLoading, { className: props.className }), children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PDFViewerClient3, { ...props }) });
|
|
10873
11277
|
});
|
|
10874
11278
|
}
|
|
10875
11279
|
});
|
|
@@ -10884,24 +11288,33 @@ var init_PDFViewer2 = __esm({
|
|
|
10884
11288
|
init_VirtualizedDocumentContainer();
|
|
10885
11289
|
init_ContinuousScrollContainer();
|
|
10886
11290
|
init_DualPageContainer();
|
|
11291
|
+
init_BookModeContainer();
|
|
10887
11292
|
}
|
|
10888
11293
|
});
|
|
10889
11294
|
|
|
10890
11295
|
// src/index.ts
|
|
10891
11296
|
var index_exports = {};
|
|
10892
11297
|
__export(index_exports, {
|
|
11298
|
+
AnimatedHighlight: () => AnimatedHighlight,
|
|
11299
|
+
AnimatedUnderline: () => AnimatedUnderline,
|
|
10893
11300
|
AnnotationLayer: () => AnnotationLayer,
|
|
10894
11301
|
AnnotationToolbar: () => AnnotationToolbar,
|
|
10895
11302
|
AskAboutOverlay: () => AskAboutOverlay,
|
|
10896
11303
|
AskAboutTrigger: () => AskAboutTrigger,
|
|
11304
|
+
BookModeContainer: () => BookModeContainer,
|
|
10897
11305
|
BookmarksPanel: () => BookmarksPanel,
|
|
11306
|
+
BoxOverlay: () => BoxOverlay,
|
|
11307
|
+
CalloutArrow: () => CalloutArrow,
|
|
11308
|
+
CameraView: () => CameraView,
|
|
10898
11309
|
CanvasLayer: () => CanvasLayer,
|
|
11310
|
+
CinemaLayer: () => CinemaLayer,
|
|
10899
11311
|
ContinuousScrollContainer: () => ContinuousScrollContainer,
|
|
10900
11312
|
DocumentContainer: () => DocumentContainer,
|
|
10901
11313
|
DrawingCanvas: () => DrawingCanvas,
|
|
10902
11314
|
DualPageContainer: () => DualPageContainer,
|
|
10903
11315
|
FloatingZoomControls: () => FloatingZoomControls,
|
|
10904
11316
|
FocusRegionLayer: () => FocusRegionLayer,
|
|
11317
|
+
GhostReference: () => GhostReference,
|
|
10905
11318
|
HighlightLayer: () => HighlightLayer,
|
|
10906
11319
|
HighlightPopover: () => HighlightPopover,
|
|
10907
11320
|
HighlightsPanel: () => HighlightsPanel,
|
|
@@ -10918,32 +11331,46 @@ __export(index_exports, {
|
|
|
10918
11331
|
PDFViewerContext: () => PDFViewerContext,
|
|
10919
11332
|
PDFViewerProvider: () => PDFViewerProvider,
|
|
10920
11333
|
PluginManager: () => PluginManager,
|
|
11334
|
+
PulseOverlay: () => PulseOverlay,
|
|
10921
11335
|
QuickNoteButton: () => QuickNoteButton,
|
|
10922
11336
|
QuickNotePopover: () => QuickNotePopover,
|
|
11337
|
+
SYSTEM_PROMPT: () => SYSTEM_PROMPT,
|
|
10923
11338
|
SearchPanel: () => SearchPanel,
|
|
10924
11339
|
SelectionToolbar: () => SelectionToolbar,
|
|
10925
11340
|
ShapePreview: () => ShapePreview,
|
|
10926
11341
|
ShapeRenderer: () => ShapeRenderer,
|
|
10927
11342
|
Sidebar: () => Sidebar,
|
|
11343
|
+
SpotlightMask: () => SpotlightMask,
|
|
11344
|
+
StickyLabel: () => StickyLabel,
|
|
10928
11345
|
StickyNote: () => StickyNote,
|
|
11346
|
+
StoryboardActionSchema: () => StoryboardActionSchema,
|
|
11347
|
+
StoryboardEngine: () => StoryboardEngine,
|
|
11348
|
+
StoryboardSchema: () => StoryboardSchema,
|
|
11349
|
+
SubtitleBar: () => SubtitleBar,
|
|
10929
11350
|
TakeawaysPanel: () => TakeawaysPanel,
|
|
10930
11351
|
TextLayer: () => TextLayer,
|
|
10931
11352
|
ThumbnailPanel: () => ThumbnailPanel,
|
|
10932
11353
|
Toolbar: () => Toolbar,
|
|
11354
|
+
TutorModeContainer: () => TutorModeContainer,
|
|
10933
11355
|
VirtualizedDocumentContainer: () => VirtualizedDocumentContainer,
|
|
10934
11356
|
applyRotation: () => applyRotation,
|
|
11357
|
+
buildBBoxIndex: () => buildBBoxIndex,
|
|
11358
|
+
buildUserPrompt: () => buildUserPrompt,
|
|
10935
11359
|
clearHighlights: () => clearHighlights,
|
|
10936
11360
|
clearStudentData: () => clearStudentData,
|
|
10937
11361
|
cn: () => cn,
|
|
11362
|
+
cosineSimilarity: () => cosineSimilarity,
|
|
10938
11363
|
countTextOnPage: () => countTextOnPage,
|
|
10939
11364
|
createAgentAPI: () => createAgentAPI,
|
|
10940
11365
|
createAgentStore: () => createAgentStore,
|
|
10941
11366
|
createAnnotationStore: () => createAnnotationStore,
|
|
11367
|
+
createNarrationStore: () => createNarrationStore,
|
|
10942
11368
|
createPDFViewer: () => createPDFViewer,
|
|
10943
11369
|
createPluginManager: () => createPluginManager,
|
|
10944
11370
|
createSearchStore: () => createSearchStore,
|
|
10945
11371
|
createStudentStore: () => createStudentStore,
|
|
10946
11372
|
createViewerStore: () => createViewerStore,
|
|
11373
|
+
directStoryboard: () => directStoryboard,
|
|
10947
11374
|
doRectsIntersect: () => doRectsIntersect,
|
|
10948
11375
|
downloadAnnotationsAsJSON: () => downloadAnnotationsAsJSON,
|
|
10949
11376
|
downloadAnnotationsAsMarkdown: () => downloadAnnotationsAsMarkdown,
|
|
@@ -10958,6 +11385,7 @@ __export(index_exports, {
|
|
|
10958
11385
|
generateDocumentId: () => generateDocumentId,
|
|
10959
11386
|
getAllDocumentIds: () => getAllDocumentIds,
|
|
10960
11387
|
getAllStudentDataDocumentIds: () => getAllStudentDataDocumentIds,
|
|
11388
|
+
getLocalMiniLM: () => getLocalMiniLM,
|
|
10961
11389
|
getMetadata: () => getMetadata,
|
|
10962
11390
|
getOutline: () => getOutline,
|
|
10963
11391
|
getPage: () => getPage,
|
|
@@ -10975,17 +11403,23 @@ __export(index_exports, {
|
|
|
10975
11403
|
loadDocumentWithCallbacks: () => loadDocumentWithCallbacks,
|
|
10976
11404
|
loadHighlights: () => loadHighlights,
|
|
10977
11405
|
loadStudentData: () => loadStudentData,
|
|
11406
|
+
makeOverlayId: () => makeOverlayId,
|
|
11407
|
+
matchChunkToBlock: () => matchChunkToBlock,
|
|
10978
11408
|
mergeAdjacentRects: () => mergeAdjacentRects,
|
|
10979
11409
|
pdfToPercent: () => pdfToPercent,
|
|
10980
11410
|
pdfToViewport: () => pdfToViewport,
|
|
10981
11411
|
pdfjsLib: () => pdfjsLib,
|
|
10982
11412
|
percentToPDF: () => percentToPDF,
|
|
10983
11413
|
percentToViewport: () => percentToViewport,
|
|
11414
|
+
playPageTurnSound: () => playPageTurnSound,
|
|
10984
11415
|
quickViewer: () => quickViewer,
|
|
10985
11416
|
removeRotation: () => removeRotation,
|
|
10986
11417
|
saveHighlights: () => saveHighlights,
|
|
10987
11418
|
saveStudentData: () => saveStudentData,
|
|
10988
11419
|
scaleRect: () => scaleRect,
|
|
11420
|
+
storyboardFromMatch: () => storyboardFromMatch,
|
|
11421
|
+
storyboardJsonSchema: () => storyboardJsonSchema,
|
|
11422
|
+
truncate: () => truncate,
|
|
10989
11423
|
useAgentContext: () => useAgentContext,
|
|
10990
11424
|
useAgentStore: () => useAgentStore,
|
|
10991
11425
|
useAnnotationStore: () => useAnnotationStore,
|
|
@@ -11023,9 +11457,9 @@ init_HighlightPopover2();
|
|
|
11023
11457
|
init_AnnotationToolbar2();
|
|
11024
11458
|
|
|
11025
11459
|
// src/components/Annotations/StickyNote.tsx
|
|
11026
|
-
var
|
|
11460
|
+
var import_react45 = require("react");
|
|
11027
11461
|
init_utils();
|
|
11028
|
-
var
|
|
11462
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
11029
11463
|
var NOTE_COLORS = [
|
|
11030
11464
|
"#fef08a",
|
|
11031
11465
|
// yellow
|
|
@@ -11038,7 +11472,7 @@ var NOTE_COLORS = [
|
|
|
11038
11472
|
"#fed7aa"
|
|
11039
11473
|
// orange
|
|
11040
11474
|
];
|
|
11041
|
-
var StickyNote = (0,
|
|
11475
|
+
var StickyNote = (0, import_react45.memo)(function StickyNote2({
|
|
11042
11476
|
note,
|
|
11043
11477
|
scale,
|
|
11044
11478
|
isSelected,
|
|
@@ -11051,37 +11485,37 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11051
11485
|
onDragStart,
|
|
11052
11486
|
className
|
|
11053
11487
|
}) {
|
|
11054
|
-
const [isExpanded, setIsExpanded] = (0,
|
|
11055
|
-
const [localContent, setLocalContent] = (0,
|
|
11056
|
-
const textareaRef = (0,
|
|
11057
|
-
const noteRef = (0,
|
|
11058
|
-
(0,
|
|
11488
|
+
const [isExpanded, setIsExpanded] = (0, import_react45.useState)(false);
|
|
11489
|
+
const [localContent, setLocalContent] = (0, import_react45.useState)(note.content);
|
|
11490
|
+
const textareaRef = (0, import_react45.useRef)(null);
|
|
11491
|
+
const noteRef = (0, import_react45.useRef)(null);
|
|
11492
|
+
(0, import_react45.useEffect)(() => {
|
|
11059
11493
|
setLocalContent(note.content);
|
|
11060
11494
|
}, [note.content]);
|
|
11061
|
-
(0,
|
|
11495
|
+
(0, import_react45.useEffect)(() => {
|
|
11062
11496
|
if (isEditing && textareaRef.current) {
|
|
11063
11497
|
textareaRef.current.focus();
|
|
11064
11498
|
textareaRef.current.select();
|
|
11065
11499
|
}
|
|
11066
11500
|
}, [isEditing]);
|
|
11067
|
-
const handleClick = (0,
|
|
11501
|
+
const handleClick = (0, import_react45.useCallback)((e) => {
|
|
11068
11502
|
e.stopPropagation();
|
|
11069
11503
|
onSelect?.();
|
|
11070
11504
|
if (!isExpanded) {
|
|
11071
11505
|
setIsExpanded(true);
|
|
11072
11506
|
}
|
|
11073
11507
|
}, [isExpanded, onSelect]);
|
|
11074
|
-
const handleDoubleClick = (0,
|
|
11508
|
+
const handleDoubleClick = (0, import_react45.useCallback)((e) => {
|
|
11075
11509
|
e.stopPropagation();
|
|
11076
11510
|
onStartEdit?.();
|
|
11077
11511
|
}, [onStartEdit]);
|
|
11078
|
-
const handleBlur = (0,
|
|
11512
|
+
const handleBlur = (0, import_react45.useCallback)(() => {
|
|
11079
11513
|
if (isEditing && localContent !== note.content) {
|
|
11080
11514
|
onUpdate?.({ content: localContent });
|
|
11081
11515
|
}
|
|
11082
11516
|
onEndEdit?.();
|
|
11083
11517
|
}, [isEditing, localContent, note.content, onUpdate, onEndEdit]);
|
|
11084
|
-
const handleKeyDown = (0,
|
|
11518
|
+
const handleKeyDown = (0, import_react45.useCallback)((e) => {
|
|
11085
11519
|
if (e.key === "Escape") {
|
|
11086
11520
|
setLocalContent(note.content);
|
|
11087
11521
|
onEndEdit?.();
|
|
@@ -11089,16 +11523,16 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11089
11523
|
handleBlur();
|
|
11090
11524
|
}
|
|
11091
11525
|
}, [note.content, onEndEdit, handleBlur]);
|
|
11092
|
-
const handleColorChange = (0,
|
|
11526
|
+
const handleColorChange = (0, import_react45.useCallback)((color) => {
|
|
11093
11527
|
onUpdate?.({ color });
|
|
11094
11528
|
}, [onUpdate]);
|
|
11095
|
-
const handleCollapse = (0,
|
|
11529
|
+
const handleCollapse = (0, import_react45.useCallback)((e) => {
|
|
11096
11530
|
e.stopPropagation();
|
|
11097
11531
|
setIsExpanded(false);
|
|
11098
11532
|
onEndEdit?.();
|
|
11099
11533
|
}, [onEndEdit]);
|
|
11100
11534
|
if (!isExpanded) {
|
|
11101
|
-
return /* @__PURE__ */ (0,
|
|
11535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11102
11536
|
"div",
|
|
11103
11537
|
{
|
|
11104
11538
|
ref: noteRef,
|
|
@@ -11119,14 +11553,14 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11119
11553
|
onMouseDown: onDragStart,
|
|
11120
11554
|
onTouchStart: onDragStart,
|
|
11121
11555
|
title: note.content || "Empty note",
|
|
11122
|
-
children: /* @__PURE__ */ (0,
|
|
11556
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11123
11557
|
"svg",
|
|
11124
11558
|
{
|
|
11125
11559
|
className: "w-4 h-4 opacity-70",
|
|
11126
11560
|
fill: "currentColor",
|
|
11127
11561
|
viewBox: "0 0 20 20",
|
|
11128
11562
|
style: { color: "#333" },
|
|
11129
|
-
children: /* @__PURE__ */ (0,
|
|
11563
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11130
11564
|
"path",
|
|
11131
11565
|
{
|
|
11132
11566
|
fillRule: "evenodd",
|
|
@@ -11139,7 +11573,7 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11139
11573
|
}
|
|
11140
11574
|
);
|
|
11141
11575
|
}
|
|
11142
|
-
return /* @__PURE__ */ (0,
|
|
11576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
11143
11577
|
"div",
|
|
11144
11578
|
{
|
|
11145
11579
|
ref: noteRef,
|
|
@@ -11157,14 +11591,14 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11157
11591
|
},
|
|
11158
11592
|
onClick: handleClick,
|
|
11159
11593
|
children: [
|
|
11160
|
-
/* @__PURE__ */ (0,
|
|
11594
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
11161
11595
|
"div",
|
|
11162
11596
|
{
|
|
11163
11597
|
className: "flex items-center justify-between px-2 py-1 border-b border-black/10 cursor-move",
|
|
11164
11598
|
onMouseDown: onDragStart,
|
|
11165
11599
|
onTouchStart: onDragStart,
|
|
11166
11600
|
children: [
|
|
11167
|
-
/* @__PURE__ */ (0,
|
|
11601
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex gap-1", children: NOTE_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11168
11602
|
"button",
|
|
11169
11603
|
{
|
|
11170
11604
|
className: cn(
|
|
@@ -11181,8 +11615,8 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11181
11615
|
},
|
|
11182
11616
|
color
|
|
11183
11617
|
)) }),
|
|
11184
|
-
/* @__PURE__ */ (0,
|
|
11185
|
-
/* @__PURE__ */ (0,
|
|
11618
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex gap-1", children: [
|
|
11619
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11186
11620
|
"button",
|
|
11187
11621
|
{
|
|
11188
11622
|
className: "p-0.5 hover:bg-black/10 rounded",
|
|
@@ -11191,23 +11625,23 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11191
11625
|
onDelete?.();
|
|
11192
11626
|
},
|
|
11193
11627
|
title: "Delete note",
|
|
11194
|
-
children: /* @__PURE__ */ (0,
|
|
11628
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) })
|
|
11195
11629
|
}
|
|
11196
11630
|
),
|
|
11197
|
-
/* @__PURE__ */ (0,
|
|
11631
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11198
11632
|
"button",
|
|
11199
11633
|
{
|
|
11200
11634
|
className: "p-0.5 hover:bg-black/10 rounded",
|
|
11201
11635
|
onClick: handleCollapse,
|
|
11202
11636
|
title: "Collapse note",
|
|
11203
|
-
children: /* @__PURE__ */ (0,
|
|
11637
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
11204
11638
|
}
|
|
11205
11639
|
)
|
|
11206
11640
|
] })
|
|
11207
11641
|
]
|
|
11208
11642
|
}
|
|
11209
11643
|
),
|
|
11210
|
-
/* @__PURE__ */ (0,
|
|
11644
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "p-2", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11211
11645
|
"textarea",
|
|
11212
11646
|
{
|
|
11213
11647
|
ref: textareaRef,
|
|
@@ -11222,7 +11656,7 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11222
11656
|
onKeyDown: handleKeyDown,
|
|
11223
11657
|
placeholder: "Enter note..."
|
|
11224
11658
|
}
|
|
11225
|
-
) : /* @__PURE__ */ (0,
|
|
11659
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11226
11660
|
"div",
|
|
11227
11661
|
{
|
|
11228
11662
|
className: cn(
|
|
@@ -11233,16 +11667,16 @@ var StickyNote = (0, import_react44.memo)(function StickyNote2({
|
|
|
11233
11667
|
children: note.content || "Double-click to edit..."
|
|
11234
11668
|
}
|
|
11235
11669
|
) }),
|
|
11236
|
-
/* @__PURE__ */ (0,
|
|
11670
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "px-2 pb-1 text-[10px] text-gray-500", children: new Date(note.updatedAt).toLocaleDateString() })
|
|
11237
11671
|
]
|
|
11238
11672
|
}
|
|
11239
11673
|
);
|
|
11240
11674
|
});
|
|
11241
11675
|
|
|
11242
11676
|
// src/components/Annotations/DrawingCanvas.tsx
|
|
11243
|
-
var
|
|
11677
|
+
var import_react46 = require("react");
|
|
11244
11678
|
init_utils();
|
|
11245
|
-
var
|
|
11679
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
11246
11680
|
function pointsToSvgPath(points) {
|
|
11247
11681
|
if (points.length === 0) return "";
|
|
11248
11682
|
if (points.length === 1) {
|
|
@@ -11280,7 +11714,7 @@ function simplifyPath(points, tolerance = 1) {
|
|
|
11280
11714
|
result.push(points[points.length - 1]);
|
|
11281
11715
|
return result;
|
|
11282
11716
|
}
|
|
11283
|
-
var DrawingCanvas = (0,
|
|
11717
|
+
var DrawingCanvas = (0, import_react46.memo)(function DrawingCanvas2({
|
|
11284
11718
|
width,
|
|
11285
11719
|
height,
|
|
11286
11720
|
scale,
|
|
@@ -11290,10 +11724,10 @@ var DrawingCanvas = (0, import_react45.memo)(function DrawingCanvas2({
|
|
|
11290
11724
|
onDrawingComplete,
|
|
11291
11725
|
className
|
|
11292
11726
|
}) {
|
|
11293
|
-
const svgRef = (0,
|
|
11294
|
-
const [isDrawing, setIsDrawing] = (0,
|
|
11295
|
-
const [currentPath, setCurrentPath] = (0,
|
|
11296
|
-
const getPoint = (0,
|
|
11727
|
+
const svgRef = (0, import_react46.useRef)(null);
|
|
11728
|
+
const [isDrawing, setIsDrawing] = (0, import_react46.useState)(false);
|
|
11729
|
+
const [currentPath, setCurrentPath] = (0, import_react46.useState)([]);
|
|
11730
|
+
const getPoint = (0, import_react46.useCallback)((e) => {
|
|
11297
11731
|
if (!svgRef.current) return null;
|
|
11298
11732
|
const svg = svgRef.current;
|
|
11299
11733
|
const rect = svg.getBoundingClientRect();
|
|
@@ -11313,7 +11747,7 @@ var DrawingCanvas = (0, import_react45.memo)(function DrawingCanvas2({
|
|
|
11313
11747
|
y: (clientY - rect.top) / scale
|
|
11314
11748
|
};
|
|
11315
11749
|
}, [scale]);
|
|
11316
|
-
const handleStart = (0,
|
|
11750
|
+
const handleStart = (0, import_react46.useCallback)((e) => {
|
|
11317
11751
|
if (!isActive) return;
|
|
11318
11752
|
const point = getPoint(e);
|
|
11319
11753
|
if (point) {
|
|
@@ -11321,14 +11755,14 @@ var DrawingCanvas = (0, import_react45.memo)(function DrawingCanvas2({
|
|
|
11321
11755
|
setCurrentPath([point]);
|
|
11322
11756
|
}
|
|
11323
11757
|
}, [isActive, getPoint]);
|
|
11324
|
-
const handleMove = (0,
|
|
11758
|
+
const handleMove = (0, import_react46.useCallback)((e) => {
|
|
11325
11759
|
if (!isDrawing || !isActive) return;
|
|
11326
11760
|
const point = getPoint(e);
|
|
11327
11761
|
if (point) {
|
|
11328
11762
|
setCurrentPath((prev) => [...prev, point]);
|
|
11329
11763
|
}
|
|
11330
11764
|
}, [isDrawing, isActive, getPoint]);
|
|
11331
|
-
const handleEnd = (0,
|
|
11765
|
+
const handleEnd = (0, import_react46.useCallback)(() => {
|
|
11332
11766
|
if (!isDrawing) return;
|
|
11333
11767
|
setIsDrawing(false);
|
|
11334
11768
|
if (currentPath.length >= 2) {
|
|
@@ -11337,7 +11771,7 @@ var DrawingCanvas = (0, import_react45.memo)(function DrawingCanvas2({
|
|
|
11337
11771
|
}
|
|
11338
11772
|
setCurrentPath([]);
|
|
11339
11773
|
}, [isDrawing, currentPath, onDrawingComplete]);
|
|
11340
|
-
return /* @__PURE__ */ (0,
|
|
11774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
11341
11775
|
"svg",
|
|
11342
11776
|
{
|
|
11343
11777
|
ref: svgRef,
|
|
@@ -11357,7 +11791,7 @@ var DrawingCanvas = (0, import_react45.memo)(function DrawingCanvas2({
|
|
|
11357
11791
|
onTouchStart: handleStart,
|
|
11358
11792
|
onTouchMove: handleMove,
|
|
11359
11793
|
onTouchEnd: handleEnd,
|
|
11360
|
-
children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ (0,
|
|
11794
|
+
children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
11361
11795
|
"path",
|
|
11362
11796
|
{
|
|
11363
11797
|
d: pointsToSvgPath(currentPath),
|
|
@@ -11374,10 +11808,10 @@ var DrawingCanvas = (0, import_react45.memo)(function DrawingCanvas2({
|
|
|
11374
11808
|
});
|
|
11375
11809
|
|
|
11376
11810
|
// src/components/Annotations/ShapeRenderer.tsx
|
|
11377
|
-
var
|
|
11811
|
+
var import_react47 = require("react");
|
|
11378
11812
|
init_utils();
|
|
11379
|
-
var
|
|
11380
|
-
var ShapeRenderer = (0,
|
|
11813
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
11814
|
+
var ShapeRenderer = (0, import_react47.memo)(function ShapeRenderer2({
|
|
11381
11815
|
shape,
|
|
11382
11816
|
scale,
|
|
11383
11817
|
isSelected,
|
|
@@ -11387,18 +11821,18 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11387
11821
|
onDelete: _onDelete,
|
|
11388
11822
|
className
|
|
11389
11823
|
}) {
|
|
11390
|
-
const [_isDragging, setIsDragging] = (0,
|
|
11391
|
-
const [_isResizing, setIsResizing] = (0,
|
|
11392
|
-
const [activeHandle, setActiveHandle] = (0,
|
|
11393
|
-
const startPosRef = (0,
|
|
11394
|
-
const startShapeRef = (0,
|
|
11824
|
+
const [_isDragging, setIsDragging] = (0, import_react47.useState)(false);
|
|
11825
|
+
const [_isResizing, setIsResizing] = (0, import_react47.useState)(false);
|
|
11826
|
+
const [activeHandle, setActiveHandle] = (0, import_react47.useState)(null);
|
|
11827
|
+
const startPosRef = (0, import_react47.useRef)({ x: 0, y: 0 });
|
|
11828
|
+
const startShapeRef = (0, import_react47.useRef)({ x: 0, y: 0, width: 0, height: 0 });
|
|
11395
11829
|
const { shapeType, x, y, width, height, color, strokeWidth, id: _id } = shape;
|
|
11396
11830
|
const scaledX = x * scale;
|
|
11397
11831
|
const scaledY = y * scale;
|
|
11398
11832
|
const scaledWidth = width * scale;
|
|
11399
11833
|
const scaledHeight = height * scale;
|
|
11400
11834
|
const scaledStroke = strokeWidth * scale;
|
|
11401
|
-
const getResizeHandles = (0,
|
|
11835
|
+
const getResizeHandles = (0, import_react47.useCallback)(() => {
|
|
11402
11836
|
const handleSize = 8;
|
|
11403
11837
|
const half = handleSize / 2;
|
|
11404
11838
|
return [
|
|
@@ -11412,7 +11846,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11412
11846
|
{ position: "w", cursor: "ew-resize", x: scaledX - half, y: scaledY + scaledHeight / 2 - half }
|
|
11413
11847
|
];
|
|
11414
11848
|
}, [scaledX, scaledY, scaledWidth, scaledHeight]);
|
|
11415
|
-
const handleMouseDown = (0,
|
|
11849
|
+
const handleMouseDown = (0, import_react47.useCallback)((e, handle) => {
|
|
11416
11850
|
e.stopPropagation();
|
|
11417
11851
|
onSelect?.();
|
|
11418
11852
|
if (!isEditing) return;
|
|
@@ -11488,7 +11922,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11488
11922
|
document.addEventListener("mousemove", handleMouseMove);
|
|
11489
11923
|
document.addEventListener("mouseup", handleMouseUp);
|
|
11490
11924
|
}, [isEditing, x, y, width, height, scale, onSelect, onUpdate]);
|
|
11491
|
-
const renderShape2 = (0,
|
|
11925
|
+
const renderShape2 = (0, import_react47.useCallback)(() => {
|
|
11492
11926
|
const commonProps = {
|
|
11493
11927
|
stroke: color,
|
|
11494
11928
|
strokeWidth: scaledStroke,
|
|
@@ -11500,7 +11934,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11500
11934
|
};
|
|
11501
11935
|
switch (shapeType) {
|
|
11502
11936
|
case "rect":
|
|
11503
|
-
return /* @__PURE__ */ (0,
|
|
11937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11504
11938
|
"rect",
|
|
11505
11939
|
{
|
|
11506
11940
|
x: scaledX,
|
|
@@ -11511,7 +11945,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11511
11945
|
}
|
|
11512
11946
|
);
|
|
11513
11947
|
case "circle":
|
|
11514
|
-
return /* @__PURE__ */ (0,
|
|
11948
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11515
11949
|
"ellipse",
|
|
11516
11950
|
{
|
|
11517
11951
|
cx: scaledX + scaledWidth / 2,
|
|
@@ -11522,7 +11956,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11522
11956
|
}
|
|
11523
11957
|
);
|
|
11524
11958
|
case "line":
|
|
11525
|
-
return /* @__PURE__ */ (0,
|
|
11959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11526
11960
|
"line",
|
|
11527
11961
|
{
|
|
11528
11962
|
x1: scaledX,
|
|
@@ -11542,22 +11976,22 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11542
11976
|
const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
|
|
11543
11977
|
const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
|
|
11544
11978
|
const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
|
|
11545
|
-
return /* @__PURE__ */ (0,
|
|
11546
|
-
/* @__PURE__ */ (0,
|
|
11547
|
-
/* @__PURE__ */ (0,
|
|
11548
|
-
/* @__PURE__ */ (0,
|
|
11979
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("g", { children: [
|
|
11980
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: scaledX, y1: scaledY, x2: endX, y2: endY, ...commonProps }),
|
|
11981
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
|
|
11982
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
|
|
11549
11983
|
] });
|
|
11550
11984
|
default:
|
|
11551
11985
|
return null;
|
|
11552
11986
|
}
|
|
11553
11987
|
}, [shapeType, scaledX, scaledY, scaledWidth, scaledHeight, color, scaledStroke, isSelected]);
|
|
11554
|
-
return /* @__PURE__ */ (0,
|
|
11988
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
11555
11989
|
"g",
|
|
11556
11990
|
{
|
|
11557
11991
|
className: cn("shape-renderer", className),
|
|
11558
11992
|
onMouseDown: (e) => handleMouseDown(e),
|
|
11559
11993
|
children: [
|
|
11560
|
-
/* @__PURE__ */ (0,
|
|
11994
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11561
11995
|
"rect",
|
|
11562
11996
|
{
|
|
11563
11997
|
x: scaledX - 5,
|
|
@@ -11570,7 +12004,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11570
12004
|
}
|
|
11571
12005
|
),
|
|
11572
12006
|
renderShape2(),
|
|
11573
|
-
isSelected && /* @__PURE__ */ (0,
|
|
12007
|
+
isSelected && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11574
12008
|
"rect",
|
|
11575
12009
|
{
|
|
11576
12010
|
x: scaledX - 2,
|
|
@@ -11583,7 +12017,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11583
12017
|
strokeDasharray: "4 2"
|
|
11584
12018
|
}
|
|
11585
12019
|
),
|
|
11586
|
-
isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ (0,
|
|
12020
|
+
isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11587
12021
|
"rect",
|
|
11588
12022
|
{
|
|
11589
12023
|
x: handle.x,
|
|
@@ -11603,7 +12037,7 @@ var ShapeRenderer = (0, import_react46.memo)(function ShapeRenderer2({
|
|
|
11603
12037
|
}
|
|
11604
12038
|
);
|
|
11605
12039
|
});
|
|
11606
|
-
var ShapePreview = (0,
|
|
12040
|
+
var ShapePreview = (0, import_react47.memo)(function ShapePreview2({
|
|
11607
12041
|
shapeType,
|
|
11608
12042
|
startPoint,
|
|
11609
12043
|
endPoint,
|
|
@@ -11624,9 +12058,9 @@ var ShapePreview = (0, import_react46.memo)(function ShapePreview2({
|
|
|
11624
12058
|
};
|
|
11625
12059
|
switch (shapeType) {
|
|
11626
12060
|
case "rect":
|
|
11627
|
-
return /* @__PURE__ */ (0,
|
|
12061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("rect", { x, y, width, height, ...commonProps });
|
|
11628
12062
|
case "circle":
|
|
11629
|
-
return /* @__PURE__ */ (0,
|
|
12063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11630
12064
|
"ellipse",
|
|
11631
12065
|
{
|
|
11632
12066
|
cx: x + width / 2,
|
|
@@ -11637,7 +12071,7 @@ var ShapePreview = (0, import_react46.memo)(function ShapePreview2({
|
|
|
11637
12071
|
}
|
|
11638
12072
|
);
|
|
11639
12073
|
case "line":
|
|
11640
|
-
return /* @__PURE__ */ (0,
|
|
12074
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11641
12075
|
"line",
|
|
11642
12076
|
{
|
|
11643
12077
|
x1: startPoint.x * scale,
|
|
@@ -11659,8 +12093,8 @@ var ShapePreview = (0, import_react46.memo)(function ShapePreview2({
|
|
|
11659
12093
|
const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
|
|
11660
12094
|
const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
|
|
11661
12095
|
const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
|
|
11662
|
-
return /* @__PURE__ */ (0,
|
|
11663
|
-
/* @__PURE__ */ (0,
|
|
12096
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("g", { children: [
|
|
12097
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
11664
12098
|
"line",
|
|
11665
12099
|
{
|
|
11666
12100
|
x1: startPoint.x * scale,
|
|
@@ -11670,8 +12104,8 @@ var ShapePreview = (0, import_react46.memo)(function ShapePreview2({
|
|
|
11670
12104
|
...commonProps
|
|
11671
12105
|
}
|
|
11672
12106
|
),
|
|
11673
|
-
/* @__PURE__ */ (0,
|
|
11674
|
-
/* @__PURE__ */ (0,
|
|
12107
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
|
|
12108
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
|
|
11675
12109
|
] });
|
|
11676
12110
|
default:
|
|
11677
12111
|
return null;
|
|
@@ -11679,31 +12113,31 @@ var ShapePreview = (0, import_react46.memo)(function ShapePreview2({
|
|
|
11679
12113
|
});
|
|
11680
12114
|
|
|
11681
12115
|
// src/components/Annotations/QuickNoteButton.tsx
|
|
11682
|
-
var
|
|
12116
|
+
var import_react48 = require("react");
|
|
11683
12117
|
init_utils();
|
|
11684
|
-
var
|
|
11685
|
-
var QuickNoteButton = (0,
|
|
12118
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
12119
|
+
var QuickNoteButton = (0, import_react48.memo)(function QuickNoteButton2({
|
|
11686
12120
|
pageNumber,
|
|
11687
12121
|
scale,
|
|
11688
|
-
position = "top-right",
|
|
12122
|
+
position: position2 = "top-right",
|
|
11689
12123
|
onClick,
|
|
11690
12124
|
className,
|
|
11691
12125
|
visible = true
|
|
11692
12126
|
}) {
|
|
11693
|
-
const [isHovered, setIsHovered] = (0,
|
|
11694
|
-
const handleClick = (0,
|
|
12127
|
+
const [isHovered, setIsHovered] = (0, import_react48.useState)(false);
|
|
12128
|
+
const handleClick = (0, import_react48.useCallback)(
|
|
11695
12129
|
(e) => {
|
|
11696
12130
|
e.stopPropagation();
|
|
11697
|
-
const x =
|
|
11698
|
-
const y =
|
|
12131
|
+
const x = position2 === "top-right" ? 80 : 80;
|
|
12132
|
+
const y = position2 === "top-right" ? 20 : 80;
|
|
11699
12133
|
onClick(pageNumber, x / scale, y / scale);
|
|
11700
12134
|
},
|
|
11701
|
-
[pageNumber, onClick,
|
|
12135
|
+
[pageNumber, onClick, position2, scale]
|
|
11702
12136
|
);
|
|
11703
12137
|
if (!visible) {
|
|
11704
12138
|
return null;
|
|
11705
12139
|
}
|
|
11706
|
-
return /* @__PURE__ */ (0,
|
|
12140
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
11707
12141
|
"button",
|
|
11708
12142
|
{
|
|
11709
12143
|
onClick: handleClick,
|
|
@@ -11719,13 +12153,13 @@ var QuickNoteButton = (0, import_react47.memo)(function QuickNoteButton2({
|
|
|
11719
12153
|
"transition-all duration-200",
|
|
11720
12154
|
"focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2",
|
|
11721
12155
|
isHovered && "scale-110",
|
|
11722
|
-
|
|
11723
|
-
|
|
12156
|
+
position2 === "top-right" && "top-3 right-3",
|
|
12157
|
+
position2 === "bottom-right" && "bottom-3 right-3",
|
|
11724
12158
|
className
|
|
11725
12159
|
),
|
|
11726
12160
|
title: "Add quick note",
|
|
11727
12161
|
"aria-label": "Add quick note",
|
|
11728
|
-
children: /* @__PURE__ */ (0,
|
|
12162
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
11729
12163
|
"svg",
|
|
11730
12164
|
{
|
|
11731
12165
|
className: "w-4 h-4 text-yellow-900",
|
|
@@ -11733,7 +12167,7 @@ var QuickNoteButton = (0, import_react47.memo)(function QuickNoteButton2({
|
|
|
11733
12167
|
viewBox: "0 0 24 24",
|
|
11734
12168
|
stroke: "currentColor",
|
|
11735
12169
|
strokeWidth: 2,
|
|
11736
|
-
children: /* @__PURE__ */ (0,
|
|
12170
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4v16m8-8H4" })
|
|
11737
12171
|
}
|
|
11738
12172
|
)
|
|
11739
12173
|
}
|
|
@@ -11741,37 +12175,37 @@ var QuickNoteButton = (0, import_react47.memo)(function QuickNoteButton2({
|
|
|
11741
12175
|
});
|
|
11742
12176
|
|
|
11743
12177
|
// src/components/Annotations/QuickNotePopover.tsx
|
|
11744
|
-
var
|
|
12178
|
+
var import_react49 = require("react");
|
|
11745
12179
|
init_utils();
|
|
11746
|
-
var
|
|
11747
|
-
var QuickNotePopover = (0,
|
|
12180
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
12181
|
+
var QuickNotePopover = (0, import_react49.memo)(function QuickNotePopover2({
|
|
11748
12182
|
visible,
|
|
11749
|
-
position,
|
|
12183
|
+
position: position2,
|
|
11750
12184
|
initialContent = "",
|
|
11751
12185
|
agentLastStatement,
|
|
11752
12186
|
onSave,
|
|
11753
12187
|
onCancel,
|
|
11754
12188
|
className
|
|
11755
12189
|
}) {
|
|
11756
|
-
const [content, setContent] = (0,
|
|
11757
|
-
const textareaRef = (0,
|
|
11758
|
-
const popoverRef = (0,
|
|
11759
|
-
const [adjustedPosition, setAdjustedPosition] = (0,
|
|
11760
|
-
(0,
|
|
12190
|
+
const [content, setContent] = (0, import_react49.useState)(initialContent);
|
|
12191
|
+
const textareaRef = (0, import_react49.useRef)(null);
|
|
12192
|
+
const popoverRef = (0, import_react49.useRef)(null);
|
|
12193
|
+
const [adjustedPosition, setAdjustedPosition] = (0, import_react49.useState)(position2);
|
|
12194
|
+
(0, import_react49.useEffect)(() => {
|
|
11761
12195
|
if (visible && textareaRef.current) {
|
|
11762
12196
|
textareaRef.current.focus();
|
|
11763
12197
|
}
|
|
11764
12198
|
}, [visible]);
|
|
11765
|
-
(0,
|
|
12199
|
+
(0, import_react49.useEffect)(() => {
|
|
11766
12200
|
if (visible) {
|
|
11767
12201
|
setContent(initialContent);
|
|
11768
12202
|
}
|
|
11769
12203
|
}, [visible, initialContent]);
|
|
11770
|
-
(0,
|
|
12204
|
+
(0, import_react49.useEffect)(() => {
|
|
11771
12205
|
if (!visible || !popoverRef.current) return;
|
|
11772
12206
|
const rect = popoverRef.current.getBoundingClientRect();
|
|
11773
12207
|
const padding = 10;
|
|
11774
|
-
let { x, y } =
|
|
12208
|
+
let { x, y } = position2;
|
|
11775
12209
|
if (x + rect.width > window.innerWidth - padding) {
|
|
11776
12210
|
x = window.innerWidth - rect.width - padding;
|
|
11777
12211
|
}
|
|
@@ -11785,15 +12219,15 @@ var QuickNotePopover = (0, import_react48.memo)(function QuickNotePopover2({
|
|
|
11785
12219
|
y = padding;
|
|
11786
12220
|
}
|
|
11787
12221
|
setAdjustedPosition({ x, y });
|
|
11788
|
-
}, [
|
|
11789
|
-
const handleSave = (0,
|
|
12222
|
+
}, [position2, visible]);
|
|
12223
|
+
const handleSave = (0, import_react49.useCallback)(() => {
|
|
11790
12224
|
if (content.trim()) {
|
|
11791
12225
|
onSave(content.trim());
|
|
11792
12226
|
} else {
|
|
11793
12227
|
onCancel();
|
|
11794
12228
|
}
|
|
11795
12229
|
}, [content, onSave, onCancel]);
|
|
11796
|
-
const handleKeyDown = (0,
|
|
12230
|
+
const handleKeyDown = (0, import_react49.useCallback)(
|
|
11797
12231
|
(e) => {
|
|
11798
12232
|
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
|
11799
12233
|
e.preventDefault();
|
|
@@ -11808,7 +12242,7 @@ var QuickNotePopover = (0, import_react48.memo)(function QuickNotePopover2({
|
|
|
11808
12242
|
if (!visible) {
|
|
11809
12243
|
return null;
|
|
11810
12244
|
}
|
|
11811
|
-
return /* @__PURE__ */ (0,
|
|
12245
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
11812
12246
|
"div",
|
|
11813
12247
|
{
|
|
11814
12248
|
ref: popoverRef,
|
|
@@ -11827,15 +12261,15 @@ var QuickNotePopover = (0, import_react48.memo)(function QuickNotePopover2({
|
|
|
11827
12261
|
top: adjustedPosition.y
|
|
11828
12262
|
},
|
|
11829
12263
|
children: [
|
|
11830
|
-
agentLastStatement && /* @__PURE__ */ (0,
|
|
11831
|
-
/* @__PURE__ */ (0,
|
|
11832
|
-
/* @__PURE__ */ (0,
|
|
12264
|
+
agentLastStatement && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "mb-2 p-2 bg-blue-50 dark:bg-blue-900/50 rounded text-xs text-blue-600 dark:text-blue-300 border border-blue-100 dark:border-blue-800", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-start gap-1", children: [
|
|
12265
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("svg", { className: "w-3 h-3 mt-0.5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }),
|
|
12266
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "line-clamp-2", children: [
|
|
11833
12267
|
"AI discussed: \u201C",
|
|
11834
12268
|
agentLastStatement,
|
|
11835
12269
|
"\u201D"
|
|
11836
12270
|
] })
|
|
11837
12271
|
] }) }),
|
|
11838
|
-
/* @__PURE__ */ (0,
|
|
12272
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
11839
12273
|
"textarea",
|
|
11840
12274
|
{
|
|
11841
12275
|
ref: textareaRef,
|
|
@@ -11854,13 +12288,13 @@ var QuickNotePopover = (0, import_react48.memo)(function QuickNotePopover2({
|
|
|
11854
12288
|
)
|
|
11855
12289
|
}
|
|
11856
12290
|
),
|
|
11857
|
-
/* @__PURE__ */ (0,
|
|
11858
|
-
/* @__PURE__ */ (0,
|
|
12291
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center justify-between mt-2", children: [
|
|
12292
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
11859
12293
|
navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
|
|
11860
12294
|
"+Enter to save"
|
|
11861
12295
|
] }),
|
|
11862
|
-
/* @__PURE__ */ (0,
|
|
11863
|
-
/* @__PURE__ */ (0,
|
|
12296
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex gap-2", children: [
|
|
12297
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
11864
12298
|
"button",
|
|
11865
12299
|
{
|
|
11866
12300
|
onClick: onCancel,
|
|
@@ -11873,7 +12307,7 @@ var QuickNotePopover = (0, import_react48.memo)(function QuickNotePopover2({
|
|
|
11873
12307
|
children: "Cancel"
|
|
11874
12308
|
}
|
|
11875
12309
|
),
|
|
11876
|
-
/* @__PURE__ */ (0,
|
|
12310
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
11877
12311
|
"button",
|
|
11878
12312
|
{
|
|
11879
12313
|
onClick: handleSave,
|
|
@@ -11896,24 +12330,24 @@ var QuickNotePopover = (0, import_react48.memo)(function QuickNotePopover2({
|
|
|
11896
12330
|
});
|
|
11897
12331
|
|
|
11898
12332
|
// src/components/AskAbout/AskAboutOverlay.tsx
|
|
11899
|
-
var
|
|
12333
|
+
var import_react50 = require("react");
|
|
11900
12334
|
init_utils();
|
|
11901
|
-
var
|
|
11902
|
-
var AskAboutOverlay = (0,
|
|
12335
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
12336
|
+
var AskAboutOverlay = (0, import_react50.memo)(function AskAboutOverlay2({
|
|
11903
12337
|
visible,
|
|
11904
12338
|
progress,
|
|
11905
|
-
position,
|
|
12339
|
+
position: position2,
|
|
11906
12340
|
size = 60,
|
|
11907
12341
|
className
|
|
11908
12342
|
}) {
|
|
11909
|
-
if (!visible || !
|
|
12343
|
+
if (!visible || !position2) {
|
|
11910
12344
|
return null;
|
|
11911
12345
|
}
|
|
11912
12346
|
const strokeWidth = 4;
|
|
11913
12347
|
const radius = (size - strokeWidth) / 2;
|
|
11914
12348
|
const circumference = 2 * Math.PI * radius;
|
|
11915
12349
|
const strokeDashoffset = circumference * (1 - progress);
|
|
11916
|
-
return /* @__PURE__ */ (0,
|
|
12350
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
11917
12351
|
"div",
|
|
11918
12352
|
{
|
|
11919
12353
|
className: cn(
|
|
@@ -11922,11 +12356,11 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
11922
12356
|
className
|
|
11923
12357
|
),
|
|
11924
12358
|
style: {
|
|
11925
|
-
left:
|
|
11926
|
-
top:
|
|
12359
|
+
left: position2.x - size / 2,
|
|
12360
|
+
top: position2.y - size / 2
|
|
11927
12361
|
},
|
|
11928
12362
|
children: [
|
|
11929
|
-
/* @__PURE__ */ (0,
|
|
12363
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
11930
12364
|
"svg",
|
|
11931
12365
|
{
|
|
11932
12366
|
width: size,
|
|
@@ -11934,7 +12368,7 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
11934
12368
|
viewBox: `0 0 ${size} ${size}`,
|
|
11935
12369
|
className: "transform -rotate-90",
|
|
11936
12370
|
children: [
|
|
11937
|
-
/* @__PURE__ */ (0,
|
|
12371
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
11938
12372
|
"circle",
|
|
11939
12373
|
{
|
|
11940
12374
|
cx: size / 2,
|
|
@@ -11945,7 +12379,7 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
11945
12379
|
strokeWidth
|
|
11946
12380
|
}
|
|
11947
12381
|
),
|
|
11948
|
-
/* @__PURE__ */ (0,
|
|
12382
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
11949
12383
|
"circle",
|
|
11950
12384
|
{
|
|
11951
12385
|
cx: size / 2,
|
|
@@ -11963,12 +12397,12 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
11963
12397
|
]
|
|
11964
12398
|
}
|
|
11965
12399
|
),
|
|
11966
|
-
/* @__PURE__ */ (0,
|
|
12400
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
11967
12401
|
"div",
|
|
11968
12402
|
{
|
|
11969
12403
|
className: "absolute inset-0 flex items-center justify-center",
|
|
11970
12404
|
style: { color: progress >= 1 ? "#22c55e" : "white" },
|
|
11971
|
-
children: progress >= 1 ? /* @__PURE__ */ (0,
|
|
12405
|
+
children: progress >= 1 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
11972
12406
|
"svg",
|
|
11973
12407
|
{
|
|
11974
12408
|
width: "24",
|
|
@@ -11979,9 +12413,9 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
11979
12413
|
strokeWidth: "2",
|
|
11980
12414
|
strokeLinecap: "round",
|
|
11981
12415
|
strokeLinejoin: "round",
|
|
11982
|
-
children: /* @__PURE__ */ (0,
|
|
12416
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("polyline", { points: "20 6 9 17 4 12" })
|
|
11983
12417
|
}
|
|
11984
|
-
) : /* @__PURE__ */ (0,
|
|
12418
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
11985
12419
|
"svg",
|
|
11986
12420
|
{
|
|
11987
12421
|
width: "20",
|
|
@@ -11993,9 +12427,9 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
11993
12427
|
strokeLinecap: "round",
|
|
11994
12428
|
strokeLinejoin: "round",
|
|
11995
12429
|
children: [
|
|
11996
|
-
/* @__PURE__ */ (0,
|
|
11997
|
-
/* @__PURE__ */ (0,
|
|
11998
|
-
/* @__PURE__ */ (0,
|
|
12430
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
12431
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
12432
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
11999
12433
|
]
|
|
12000
12434
|
}
|
|
12001
12435
|
)
|
|
@@ -12007,24 +12441,24 @@ var AskAboutOverlay = (0, import_react49.memo)(function AskAboutOverlay2({
|
|
|
12007
12441
|
});
|
|
12008
12442
|
|
|
12009
12443
|
// src/components/AskAbout/AskAboutTrigger.tsx
|
|
12010
|
-
var
|
|
12444
|
+
var import_react51 = require("react");
|
|
12011
12445
|
init_utils();
|
|
12012
|
-
var
|
|
12013
|
-
var AskAboutTrigger = (0,
|
|
12014
|
-
position,
|
|
12446
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
12447
|
+
var AskAboutTrigger = (0, import_react51.memo)(function AskAboutTrigger2({
|
|
12448
|
+
position: position2,
|
|
12015
12449
|
onConfirm,
|
|
12016
12450
|
onCancel,
|
|
12017
12451
|
visible,
|
|
12018
12452
|
autoHideDelay = 5e3,
|
|
12019
12453
|
className
|
|
12020
12454
|
}) {
|
|
12021
|
-
const [adjustedPosition, setAdjustedPosition] = (0,
|
|
12022
|
-
const triggerRef = (0,
|
|
12023
|
-
(0,
|
|
12455
|
+
const [adjustedPosition, setAdjustedPosition] = (0, import_react51.useState)(position2);
|
|
12456
|
+
const triggerRef = (0, import_react51.useRef)(null);
|
|
12457
|
+
(0, import_react51.useEffect)(() => {
|
|
12024
12458
|
if (!visible || !triggerRef.current) return;
|
|
12025
12459
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
12026
12460
|
const padding = 10;
|
|
12027
|
-
let { x, y } =
|
|
12461
|
+
let { x, y } = position2;
|
|
12028
12462
|
if (x + rect.width / 2 > window.innerWidth - padding) {
|
|
12029
12463
|
x = window.innerWidth - rect.width / 2 - padding;
|
|
12030
12464
|
}
|
|
@@ -12032,23 +12466,23 @@ var AskAboutTrigger = (0, import_react50.memo)(function AskAboutTrigger2({
|
|
|
12032
12466
|
x = rect.width / 2 + padding;
|
|
12033
12467
|
}
|
|
12034
12468
|
if (y + rect.height > window.innerHeight - padding) {
|
|
12035
|
-
y =
|
|
12469
|
+
y = position2.y - rect.height - 20;
|
|
12036
12470
|
}
|
|
12037
12471
|
setAdjustedPosition({ x, y });
|
|
12038
|
-
}, [
|
|
12039
|
-
(0,
|
|
12472
|
+
}, [position2, visible]);
|
|
12473
|
+
(0, import_react51.useEffect)(() => {
|
|
12040
12474
|
if (!visible || autoHideDelay === 0) return;
|
|
12041
12475
|
const timer = setTimeout(onCancel, autoHideDelay);
|
|
12042
12476
|
return () => clearTimeout(timer);
|
|
12043
12477
|
}, [visible, autoHideDelay, onCancel]);
|
|
12044
|
-
const handleConfirm = (0,
|
|
12478
|
+
const handleConfirm = (0, import_react51.useCallback)(
|
|
12045
12479
|
(e) => {
|
|
12046
12480
|
e.stopPropagation();
|
|
12047
12481
|
onConfirm();
|
|
12048
12482
|
},
|
|
12049
12483
|
[onConfirm]
|
|
12050
12484
|
);
|
|
12051
|
-
const handleCancel = (0,
|
|
12485
|
+
const handleCancel = (0, import_react51.useCallback)(
|
|
12052
12486
|
(e) => {
|
|
12053
12487
|
e.stopPropagation();
|
|
12054
12488
|
onCancel();
|
|
@@ -12058,7 +12492,7 @@ var AskAboutTrigger = (0, import_react50.memo)(function AskAboutTrigger2({
|
|
|
12058
12492
|
if (!visible) {
|
|
12059
12493
|
return null;
|
|
12060
12494
|
}
|
|
12061
|
-
return /* @__PURE__ */ (0,
|
|
12495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
12062
12496
|
"div",
|
|
12063
12497
|
{
|
|
12064
12498
|
ref: triggerRef,
|
|
@@ -12077,8 +12511,8 @@ var AskAboutTrigger = (0, import_react50.memo)(function AskAboutTrigger2({
|
|
|
12077
12511
|
transform: "translate(-50%, 0)"
|
|
12078
12512
|
},
|
|
12079
12513
|
children: [
|
|
12080
|
-
/* @__PURE__ */ (0,
|
|
12081
|
-
/* @__PURE__ */ (0,
|
|
12514
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "text-sm text-gray-600 dark:text-gray-300 px-2", children: "Ask about this?" }),
|
|
12515
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
12082
12516
|
"button",
|
|
12083
12517
|
{
|
|
12084
12518
|
onClick: handleConfirm,
|
|
@@ -12091,7 +12525,7 @@ var AskAboutTrigger = (0, import_react50.memo)(function AskAboutTrigger2({
|
|
|
12091
12525
|
children: "Ask"
|
|
12092
12526
|
}
|
|
12093
12527
|
),
|
|
12094
|
-
/* @__PURE__ */ (0,
|
|
12528
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
12095
12529
|
"button",
|
|
12096
12530
|
{
|
|
12097
12531
|
onClick: handleCancel,
|
|
@@ -12111,11 +12545,11 @@ var AskAboutTrigger = (0, import_react50.memo)(function AskAboutTrigger2({
|
|
|
12111
12545
|
});
|
|
12112
12546
|
|
|
12113
12547
|
// src/components/Minimap/Minimap.tsx
|
|
12114
|
-
var
|
|
12548
|
+
var import_react52 = require("react");
|
|
12115
12549
|
init_hooks();
|
|
12116
12550
|
init_utils();
|
|
12117
|
-
var
|
|
12118
|
-
var PageIndicator = (0,
|
|
12551
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
12552
|
+
var PageIndicator = (0, import_react52.memo)(function PageIndicator2({
|
|
12119
12553
|
pageNumber,
|
|
12120
12554
|
status,
|
|
12121
12555
|
isBookmarked,
|
|
@@ -12129,7 +12563,7 @@ var PageIndicator = (0, import_react51.memo)(function PageIndicator2({
|
|
|
12129
12563
|
if (status === "visited") return "bg-green-400";
|
|
12130
12564
|
return "bg-gray-200 dark:bg-gray-700";
|
|
12131
12565
|
};
|
|
12132
|
-
return /* @__PURE__ */ (0,
|
|
12566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
12133
12567
|
"button",
|
|
12134
12568
|
{
|
|
12135
12569
|
onClick,
|
|
@@ -12145,13 +12579,13 @@ var PageIndicator = (0, import_react51.memo)(function PageIndicator2({
|
|
|
12145
12579
|
title: `Page ${pageNumber}${isBookmarked ? " (bookmarked)" : ""}`,
|
|
12146
12580
|
"aria-label": `Go to page ${pageNumber}`,
|
|
12147
12581
|
children: [
|
|
12148
|
-
isBookmarked && !compact && /* @__PURE__ */ (0,
|
|
12149
|
-
showNumber && !compact && /* @__PURE__ */ (0,
|
|
12582
|
+
isBookmarked && !compact && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full border border-white" }),
|
|
12583
|
+
showNumber && !compact && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-medium text-white", children: pageNumber })
|
|
12150
12584
|
]
|
|
12151
12585
|
}
|
|
12152
12586
|
);
|
|
12153
12587
|
});
|
|
12154
|
-
var Minimap = (0,
|
|
12588
|
+
var Minimap = (0, import_react52.memo)(function Minimap2({
|
|
12155
12589
|
variant = "sidebar",
|
|
12156
12590
|
floatingPosition = "right",
|
|
12157
12591
|
maxHeight = 300,
|
|
@@ -12164,18 +12598,18 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12164
12598
|
const currentPage = useViewerStore((s) => s.currentPage);
|
|
12165
12599
|
const numPages = useViewerStore((s) => s.numPages);
|
|
12166
12600
|
const goToPage = useViewerStore((s) => s.goToPage);
|
|
12167
|
-
const bookmarkedPages = (0,
|
|
12601
|
+
const bookmarkedPages = (0, import_react52.useMemo)(() => {
|
|
12168
12602
|
return new Set(bookmarks.map((b) => b.pageNumber));
|
|
12169
12603
|
}, [bookmarks]);
|
|
12170
12604
|
const compact = numPages > 50;
|
|
12171
|
-
const handlePageClick = (0,
|
|
12605
|
+
const handlePageClick = (0, import_react52.useCallback)(
|
|
12172
12606
|
(pageNumber) => {
|
|
12173
12607
|
goToPage(pageNumber);
|
|
12174
12608
|
onPageClick?.(pageNumber);
|
|
12175
12609
|
},
|
|
12176
12610
|
[goToPage, onPageClick]
|
|
12177
12611
|
);
|
|
12178
|
-
const getPageStatus = (0,
|
|
12612
|
+
const getPageStatus = (0, import_react52.useCallback)(
|
|
12179
12613
|
(pageNumber) => {
|
|
12180
12614
|
if (pageNumber === currentPage) return "current";
|
|
12181
12615
|
if (bookmarkedPages.has(pageNumber)) return "bookmarked";
|
|
@@ -12184,11 +12618,11 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12184
12618
|
},
|
|
12185
12619
|
[currentPage, visitedPages, bookmarkedPages]
|
|
12186
12620
|
);
|
|
12187
|
-
const pageIndicators = (0,
|
|
12621
|
+
const pageIndicators = (0, import_react52.useMemo)(() => {
|
|
12188
12622
|
const pages = [];
|
|
12189
12623
|
for (let i = 1; i <= numPages; i++) {
|
|
12190
12624
|
pages.push(
|
|
12191
|
-
/* @__PURE__ */ (0,
|
|
12625
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
12192
12626
|
PageIndicator,
|
|
12193
12627
|
{
|
|
12194
12628
|
pageNumber: i,
|
|
@@ -12209,16 +12643,16 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12209
12643
|
if (numPages === 0) {
|
|
12210
12644
|
return null;
|
|
12211
12645
|
}
|
|
12212
|
-
const content = /* @__PURE__ */ (0,
|
|
12213
|
-
/* @__PURE__ */ (0,
|
|
12214
|
-
/* @__PURE__ */ (0,
|
|
12215
|
-
/* @__PURE__ */ (0,
|
|
12216
|
-
/* @__PURE__ */ (0,
|
|
12646
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
12647
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "mb-3", children: [
|
|
12648
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1", children: [
|
|
12649
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { children: "Progress" }),
|
|
12650
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { children: [
|
|
12217
12651
|
progressPercentage,
|
|
12218
12652
|
"%"
|
|
12219
12653
|
] })
|
|
12220
12654
|
] }),
|
|
12221
|
-
/* @__PURE__ */ (0,
|
|
12655
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
12222
12656
|
"div",
|
|
12223
12657
|
{
|
|
12224
12658
|
className: "h-full bg-green-500 rounded-full transition-all duration-300",
|
|
@@ -12226,7 +12660,7 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12226
12660
|
}
|
|
12227
12661
|
) })
|
|
12228
12662
|
] }),
|
|
12229
|
-
/* @__PURE__ */ (0,
|
|
12663
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
12230
12664
|
"div",
|
|
12231
12665
|
{
|
|
12232
12666
|
className: cn(
|
|
@@ -12237,21 +12671,21 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12237
12671
|
children: pageIndicators
|
|
12238
12672
|
}
|
|
12239
12673
|
),
|
|
12240
|
-
/* @__PURE__ */ (0,
|
|
12241
|
-
/* @__PURE__ */ (0,
|
|
12242
|
-
/* @__PURE__ */ (0,
|
|
12243
|
-
/* @__PURE__ */ (0,
|
|
12674
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "mt-3 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
12675
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
12676
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "w-2 h-2 rounded-sm bg-blue-500" }),
|
|
12677
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { children: "Current" })
|
|
12244
12678
|
] }),
|
|
12245
|
-
/* @__PURE__ */ (0,
|
|
12246
|
-
/* @__PURE__ */ (0,
|
|
12247
|
-
/* @__PURE__ */ (0,
|
|
12679
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
12680
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "w-2 h-2 rounded-sm bg-green-400" }),
|
|
12681
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { children: "Visited" })
|
|
12248
12682
|
] }),
|
|
12249
|
-
/* @__PURE__ */ (0,
|
|
12250
|
-
/* @__PURE__ */ (0,
|
|
12251
|
-
/* @__PURE__ */ (0,
|
|
12683
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
12684
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "w-2 h-2 rounded-sm bg-yellow-400" }),
|
|
12685
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { children: "Bookmarked" })
|
|
12252
12686
|
] })
|
|
12253
12687
|
] }) }),
|
|
12254
|
-
/* @__PURE__ */ (0,
|
|
12688
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
12255
12689
|
visitedCount,
|
|
12256
12690
|
" of ",
|
|
12257
12691
|
numPages,
|
|
@@ -12259,7 +12693,7 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12259
12693
|
] })
|
|
12260
12694
|
] });
|
|
12261
12695
|
if (variant === "floating") {
|
|
12262
|
-
return /* @__PURE__ */ (0,
|
|
12696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
12263
12697
|
"div",
|
|
12264
12698
|
{
|
|
12265
12699
|
className: cn(
|
|
@@ -12275,13 +12709,13 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12275
12709
|
),
|
|
12276
12710
|
style: { maxHeight },
|
|
12277
12711
|
children: [
|
|
12278
|
-
/* @__PURE__ */ (0,
|
|
12712
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 mb-2", children: "Reading Progress" }),
|
|
12279
12713
|
content
|
|
12280
12714
|
]
|
|
12281
12715
|
}
|
|
12282
12716
|
);
|
|
12283
12717
|
}
|
|
12284
|
-
return /* @__PURE__ */ (0,
|
|
12718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
12285
12719
|
"div",
|
|
12286
12720
|
{
|
|
12287
12721
|
className: cn(
|
|
@@ -12299,13 +12733,13 @@ var Minimap = (0, import_react51.memo)(function Minimap2({
|
|
|
12299
12733
|
init_FloatingZoomControls2();
|
|
12300
12734
|
|
|
12301
12735
|
// src/components/PDFThumbnailNav/PDFThumbnailNav.tsx
|
|
12302
|
-
var
|
|
12736
|
+
var import_react53 = require("react");
|
|
12303
12737
|
init_hooks();
|
|
12304
12738
|
init_utils();
|
|
12305
|
-
var
|
|
12739
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
12306
12740
|
var DEFAULT_WIDTH = 612;
|
|
12307
12741
|
var DEFAULT_HEIGHT = 792;
|
|
12308
|
-
var PDFThumbnailNav = (0,
|
|
12742
|
+
var PDFThumbnailNav = (0, import_react53.memo)(function PDFThumbnailNav2({
|
|
12309
12743
|
thumbnailScale = 0.15,
|
|
12310
12744
|
orientation = "vertical",
|
|
12311
12745
|
maxVisible = 10,
|
|
@@ -12316,14 +12750,14 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12316
12750
|
}) {
|
|
12317
12751
|
const { document: document2, numPages, currentPage } = usePDFViewer();
|
|
12318
12752
|
const { viewerStore } = usePDFViewerStores();
|
|
12319
|
-
const containerRef = (0,
|
|
12320
|
-
const [thumbnails, setThumbnails] = (0,
|
|
12321
|
-
const [visibleRange, setVisibleRange] = (0,
|
|
12322
|
-
const renderQueueRef = (0,
|
|
12323
|
-
const pageCache = (0,
|
|
12753
|
+
const containerRef = (0, import_react53.useRef)(null);
|
|
12754
|
+
const [thumbnails, setThumbnails] = (0, import_react53.useState)(/* @__PURE__ */ new Map());
|
|
12755
|
+
const [visibleRange, setVisibleRange] = (0, import_react53.useState)({ start: 1, end: maxVisible });
|
|
12756
|
+
const renderQueueRef = (0, import_react53.useRef)(/* @__PURE__ */ new Set());
|
|
12757
|
+
const pageCache = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
|
|
12324
12758
|
const thumbnailWidth = Math.floor(DEFAULT_WIDTH * thumbnailScale);
|
|
12325
12759
|
const thumbnailHeight = Math.floor(DEFAULT_HEIGHT * thumbnailScale);
|
|
12326
|
-
const updateVisibleRange = (0,
|
|
12760
|
+
const updateVisibleRange = (0, import_react53.useCallback)(() => {
|
|
12327
12761
|
if (!containerRef.current || numPages === 0) return;
|
|
12328
12762
|
const container = containerRef.current;
|
|
12329
12763
|
const isHorizontal2 = orientation === "horizontal";
|
|
@@ -12335,7 +12769,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12335
12769
|
const lastVisible = Math.min(numPages, firstVisible + visibleCount);
|
|
12336
12770
|
setVisibleRange({ start: firstVisible, end: lastVisible });
|
|
12337
12771
|
}, [numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
|
|
12338
|
-
(0,
|
|
12772
|
+
(0, import_react53.useEffect)(() => {
|
|
12339
12773
|
const container = containerRef.current;
|
|
12340
12774
|
if (!container) return;
|
|
12341
12775
|
const handleScroll = () => {
|
|
@@ -12345,7 +12779,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12345
12779
|
updateVisibleRange();
|
|
12346
12780
|
return () => container.removeEventListener("scroll", handleScroll);
|
|
12347
12781
|
}, [updateVisibleRange]);
|
|
12348
|
-
(0,
|
|
12782
|
+
(0, import_react53.useEffect)(() => {
|
|
12349
12783
|
if (!document2) {
|
|
12350
12784
|
setThumbnails(/* @__PURE__ */ new Map());
|
|
12351
12785
|
pageCache.current.clear();
|
|
@@ -12400,7 +12834,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12400
12834
|
};
|
|
12401
12835
|
renderThumbnails();
|
|
12402
12836
|
}, [document2, visibleRange, thumbnailScale, thumbnails]);
|
|
12403
|
-
(0,
|
|
12837
|
+
(0, import_react53.useEffect)(() => {
|
|
12404
12838
|
if (!containerRef.current || numPages === 0) return;
|
|
12405
12839
|
const container = containerRef.current;
|
|
12406
12840
|
const isHorizontal2 = orientation === "horizontal";
|
|
@@ -12416,12 +12850,12 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12416
12850
|
});
|
|
12417
12851
|
}
|
|
12418
12852
|
}, [currentPage, numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
|
|
12419
|
-
const handleThumbnailClick = (0,
|
|
12853
|
+
const handleThumbnailClick = (0, import_react53.useCallback)((pageNum) => {
|
|
12420
12854
|
onThumbnailClick?.(pageNum);
|
|
12421
12855
|
viewerStore.getState().requestScrollToPage(pageNum, "smooth");
|
|
12422
12856
|
}, [onThumbnailClick, viewerStore]);
|
|
12423
12857
|
if (!document2 || numPages === 0) {
|
|
12424
|
-
return /* @__PURE__ */ (0,
|
|
12858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
12425
12859
|
"div",
|
|
12426
12860
|
{
|
|
12427
12861
|
className: cn(
|
|
@@ -12442,7 +12876,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12442
12876
|
}
|
|
12443
12877
|
const isHorizontal = orientation === "horizontal";
|
|
12444
12878
|
const totalSize = numPages * ((isHorizontal ? thumbnailWidth : thumbnailHeight) + gap) - gap;
|
|
12445
|
-
return /* @__PURE__ */ (0,
|
|
12879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
12446
12880
|
"div",
|
|
12447
12881
|
{
|
|
12448
12882
|
ref: containerRef,
|
|
@@ -12456,7 +12890,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12456
12890
|
style: {
|
|
12457
12891
|
...isHorizontal ? { overflowX: "auto", overflowY: "hidden" } : { overflowX: "hidden", overflowY: "auto" }
|
|
12458
12892
|
},
|
|
12459
|
-
children: /* @__PURE__ */ (0,
|
|
12893
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
12460
12894
|
"div",
|
|
12461
12895
|
{
|
|
12462
12896
|
className: cn(
|
|
@@ -12473,7 +12907,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12473
12907
|
const thumbnail = thumbnails.get(pageNum);
|
|
12474
12908
|
const isActive = pageNum === currentPage;
|
|
12475
12909
|
const isVisible = pageNum >= visibleRange.start && pageNum <= visibleRange.end;
|
|
12476
|
-
return /* @__PURE__ */ (0,
|
|
12910
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
12477
12911
|
"div",
|
|
12478
12912
|
{
|
|
12479
12913
|
className: cn(
|
|
@@ -12498,7 +12932,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12498
12932
|
}
|
|
12499
12933
|
},
|
|
12500
12934
|
children: [
|
|
12501
|
-
/* @__PURE__ */ (0,
|
|
12935
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
12502
12936
|
"div",
|
|
12503
12937
|
{
|
|
12504
12938
|
className: "relative bg-white dark:bg-gray-700",
|
|
@@ -12506,7 +12940,7 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12506
12940
|
width: thumbnailWidth,
|
|
12507
12941
|
height: thumbnailHeight
|
|
12508
12942
|
},
|
|
12509
|
-
children: isVisible && thumbnail?.canvas ? /* @__PURE__ */ (0,
|
|
12943
|
+
children: isVisible && thumbnail?.canvas ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
12510
12944
|
"img",
|
|
12511
12945
|
{
|
|
12512
12946
|
src: thumbnail.canvas.toDataURL(),
|
|
@@ -12514,10 +12948,10 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12514
12948
|
className: "w-full h-full object-contain",
|
|
12515
12949
|
loading: "lazy"
|
|
12516
12950
|
}
|
|
12517
|
-
) : /* @__PURE__ */ (0,
|
|
12951
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-xs", children: pageNum })
|
|
12518
12952
|
}
|
|
12519
12953
|
),
|
|
12520
|
-
showPageNumbers && /* @__PURE__ */ (0,
|
|
12954
|
+
showPageNumbers && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
12521
12955
|
"div",
|
|
12522
12956
|
{
|
|
12523
12957
|
className: cn(
|
|
@@ -12540,10 +12974,10 @@ var PDFThumbnailNav = (0, import_react52.memo)(function PDFThumbnailNav2({
|
|
|
12540
12974
|
});
|
|
12541
12975
|
|
|
12542
12976
|
// src/components/ErrorBoundary/PDFErrorBoundary.tsx
|
|
12543
|
-
var
|
|
12977
|
+
var import_react54 = require("react");
|
|
12544
12978
|
init_utils();
|
|
12545
|
-
var
|
|
12546
|
-
var PDFErrorBoundary = class extends
|
|
12979
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
12980
|
+
var PDFErrorBoundary = class extends import_react54.Component {
|
|
12547
12981
|
constructor(props) {
|
|
12548
12982
|
super(props);
|
|
12549
12983
|
this.handleReset = () => {
|
|
@@ -12570,7 +13004,7 @@ var PDFErrorBoundary = class extends import_react53.Component {
|
|
|
12570
13004
|
return fallback;
|
|
12571
13005
|
}
|
|
12572
13006
|
if (showDefaultUI) {
|
|
12573
|
-
return /* @__PURE__ */ (0,
|
|
13007
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12574
13008
|
DefaultErrorUI,
|
|
12575
13009
|
{
|
|
12576
13010
|
error,
|
|
@@ -12589,7 +13023,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12589
13023
|
const isNetworkError = error.message.includes("fetch") || error.message.includes("network") || error.message.includes("Failed to load");
|
|
12590
13024
|
let title = "Something went wrong";
|
|
12591
13025
|
let description = error.message;
|
|
12592
|
-
let icon = /* @__PURE__ */ (0,
|
|
13026
|
+
let icon = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12593
13027
|
"path",
|
|
12594
13028
|
{
|
|
12595
13029
|
strokeLinecap: "round",
|
|
@@ -12601,7 +13035,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12601
13035
|
if (isPDFError) {
|
|
12602
13036
|
title = "Unable to load PDF";
|
|
12603
13037
|
description = "The PDF file could not be loaded. It may be corrupted or in an unsupported format.";
|
|
12604
|
-
icon = /* @__PURE__ */ (0,
|
|
13038
|
+
icon = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12605
13039
|
"path",
|
|
12606
13040
|
{
|
|
12607
13041
|
strokeLinecap: "round",
|
|
@@ -12613,7 +13047,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12613
13047
|
} else if (isNetworkError) {
|
|
12614
13048
|
title = "Network error";
|
|
12615
13049
|
description = "Unable to fetch the PDF file. Please check your internet connection and try again.";
|
|
12616
|
-
icon = /* @__PURE__ */ (0,
|
|
13050
|
+
icon = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12617
13051
|
"path",
|
|
12618
13052
|
{
|
|
12619
13053
|
strokeLinecap: "round",
|
|
@@ -12623,7 +13057,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12623
13057
|
}
|
|
12624
13058
|
) });
|
|
12625
13059
|
}
|
|
12626
|
-
return /* @__PURE__ */ (0,
|
|
13060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
12627
13061
|
"div",
|
|
12628
13062
|
{
|
|
12629
13063
|
className: cn(
|
|
@@ -12636,14 +13070,14 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12636
13070
|
),
|
|
12637
13071
|
children: [
|
|
12638
13072
|
icon,
|
|
12639
|
-
/* @__PURE__ */ (0,
|
|
12640
|
-
/* @__PURE__ */ (0,
|
|
12641
|
-
/* @__PURE__ */ (0,
|
|
12642
|
-
/* @__PURE__ */ (0,
|
|
12643
|
-
/* @__PURE__ */ (0,
|
|
13073
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
|
|
13074
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
|
|
13075
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("details", { className: "mt-4 text-left max-w-md w-full", children: [
|
|
13076
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("summary", { className: "cursor-pointer text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200", children: "Technical details" }),
|
|
13077
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
|
|
12644
13078
|
] }),
|
|
12645
|
-
/* @__PURE__ */ (0,
|
|
12646
|
-
/* @__PURE__ */ (0,
|
|
13079
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "mt-6 flex gap-3", children: [
|
|
13080
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12647
13081
|
"button",
|
|
12648
13082
|
{
|
|
12649
13083
|
onClick: onReset,
|
|
@@ -12657,7 +13091,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12657
13091
|
children: "Try again"
|
|
12658
13092
|
}
|
|
12659
13093
|
),
|
|
12660
|
-
/* @__PURE__ */ (0,
|
|
13094
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
12661
13095
|
"button",
|
|
12662
13096
|
{
|
|
12663
13097
|
onClick: () => window.location.reload(),
|
|
@@ -12677,12 +13111,2173 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12677
13111
|
);
|
|
12678
13112
|
}
|
|
12679
13113
|
function withErrorBoundary({ component, ...props }) {
|
|
12680
|
-
return /* @__PURE__ */ (0,
|
|
13114
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PDFErrorBoundary, { ...props, children: component });
|
|
12681
13115
|
}
|
|
12682
13116
|
|
|
12683
13117
|
// src/components/index.ts
|
|
12684
13118
|
init_PDFLoadingScreen2();
|
|
12685
13119
|
|
|
13120
|
+
// src/components/TutorMode/TutorModeContainer.tsx
|
|
13121
|
+
var import_react56 = require("react");
|
|
13122
|
+
var import_zustand2 = require("zustand");
|
|
13123
|
+
init_PDFPage2();
|
|
13124
|
+
init_hooks();
|
|
13125
|
+
|
|
13126
|
+
// src/components/TutorMode/CameraView.tsx
|
|
13127
|
+
var import_framer_motion = require("framer-motion");
|
|
13128
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
13129
|
+
function CameraView({
|
|
13130
|
+
camera,
|
|
13131
|
+
children,
|
|
13132
|
+
durationMs = 700,
|
|
13133
|
+
className
|
|
13134
|
+
}) {
|
|
13135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
13136
|
+
import_framer_motion.motion.div,
|
|
13137
|
+
{
|
|
13138
|
+
className,
|
|
13139
|
+
style: {
|
|
13140
|
+
transformOrigin: "50% 50%",
|
|
13141
|
+
willChange: "transform",
|
|
13142
|
+
width: "100%",
|
|
13143
|
+
height: "100%",
|
|
13144
|
+
position: "relative"
|
|
13145
|
+
},
|
|
13146
|
+
animate: {
|
|
13147
|
+
scale: camera.scale,
|
|
13148
|
+
x: camera.x,
|
|
13149
|
+
y: camera.y
|
|
13150
|
+
},
|
|
13151
|
+
transition: {
|
|
13152
|
+
duration: durationMs / 1e3,
|
|
13153
|
+
ease: camera.easing === "linear" ? "linear" : camera.easing === "ease-in" ? [0.42, 0, 1, 1] : camera.easing === "ease-out" ? [0, 0, 0.58, 1] : [0.42, 0, 0.58, 1]
|
|
13154
|
+
},
|
|
13155
|
+
children
|
|
13156
|
+
}
|
|
13157
|
+
);
|
|
13158
|
+
}
|
|
13159
|
+
|
|
13160
|
+
// src/components/TutorMode/CinemaLayer.tsx
|
|
13161
|
+
var import_framer_motion10 = require("framer-motion");
|
|
13162
|
+
|
|
13163
|
+
// src/components/TutorMode/SpotlightMask.tsx
|
|
13164
|
+
var import_react55 = require("react");
|
|
13165
|
+
var import_framer_motion2 = require("framer-motion");
|
|
13166
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
13167
|
+
function SpotlightMask({
|
|
13168
|
+
page,
|
|
13169
|
+
bbox,
|
|
13170
|
+
action,
|
|
13171
|
+
durationMs = 400
|
|
13172
|
+
}) {
|
|
13173
|
+
const maskId = (0, import_react55.useId)();
|
|
13174
|
+
const filterId = `${maskId}-blur`;
|
|
13175
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13176
|
+
const w = Math.max(0, x2 - x1);
|
|
13177
|
+
const h = Math.max(0, y2 - y1);
|
|
13178
|
+
const rx = action.shape === "rounded" ? 12 : action.shape === "ellipse" ? w / 2 : 0;
|
|
13179
|
+
const ry = action.shape === "rounded" ? 12 : action.shape === "ellipse" ? h / 2 : 0;
|
|
13180
|
+
const feather = action.feather_px;
|
|
13181
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
13182
|
+
"svg",
|
|
13183
|
+
{
|
|
13184
|
+
viewBox: `0 0 ${page.width} ${page.height}`,
|
|
13185
|
+
width: page.width,
|
|
13186
|
+
height: page.height,
|
|
13187
|
+
preserveAspectRatio: "none",
|
|
13188
|
+
style: {
|
|
13189
|
+
position: "absolute",
|
|
13190
|
+
inset: 0,
|
|
13191
|
+
pointerEvents: "none",
|
|
13192
|
+
width: page.width,
|
|
13193
|
+
height: page.height
|
|
13194
|
+
},
|
|
13195
|
+
"data-role": "spotlight-mask",
|
|
13196
|
+
children: [
|
|
13197
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("defs", { children: [
|
|
13198
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("filter", { id: filterId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("feGaussianBlur", { in: "SourceGraphic", stdDeviation: feather / 4 }) }),
|
|
13199
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("mask", { id: maskId, children: [
|
|
13200
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("rect", { x: 0, y: 0, width: page.width, height: page.height, fill: "white" }),
|
|
13201
|
+
action.shape === "ellipse" ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13202
|
+
"ellipse",
|
|
13203
|
+
{
|
|
13204
|
+
cx: (x1 + x2) / 2,
|
|
13205
|
+
cy: (y1 + y2) / 2,
|
|
13206
|
+
rx: w / 2,
|
|
13207
|
+
ry: h / 2,
|
|
13208
|
+
fill: "black",
|
|
13209
|
+
filter: `url(#${filterId})`
|
|
13210
|
+
}
|
|
13211
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13212
|
+
"rect",
|
|
13213
|
+
{
|
|
13214
|
+
x: x1,
|
|
13215
|
+
y: y1,
|
|
13216
|
+
width: w,
|
|
13217
|
+
height: h,
|
|
13218
|
+
rx,
|
|
13219
|
+
ry,
|
|
13220
|
+
fill: "black",
|
|
13221
|
+
filter: `url(#${filterId})`
|
|
13222
|
+
}
|
|
13223
|
+
)
|
|
13224
|
+
] })
|
|
13225
|
+
] }),
|
|
13226
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
13227
|
+
import_framer_motion2.motion.rect,
|
|
13228
|
+
{
|
|
13229
|
+
x: 0,
|
|
13230
|
+
y: 0,
|
|
13231
|
+
width: page.width,
|
|
13232
|
+
height: page.height,
|
|
13233
|
+
fill: "black",
|
|
13234
|
+
mask: `url(#${maskId})`,
|
|
13235
|
+
initial: { fillOpacity: 0 },
|
|
13236
|
+
animate: { fillOpacity: action.dim_opacity },
|
|
13237
|
+
exit: { fillOpacity: 0 },
|
|
13238
|
+
transition: { duration: durationMs / 1e3, ease: "easeOut" }
|
|
13239
|
+
}
|
|
13240
|
+
)
|
|
13241
|
+
]
|
|
13242
|
+
}
|
|
13243
|
+
);
|
|
13244
|
+
}
|
|
13245
|
+
|
|
13246
|
+
// src/components/TutorMode/AnimatedUnderline.tsx
|
|
13247
|
+
var import_framer_motion3 = require("framer-motion");
|
|
13248
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
13249
|
+
function pathForStyle(x1, x2, y, style) {
|
|
13250
|
+
if (style === "straight") return `M ${x1} ${y} L ${x2} ${y}`;
|
|
13251
|
+
if (style === "double")
|
|
13252
|
+
return `M ${x1} ${y - 3} L ${x2} ${y - 3} M ${x1} ${y + 3} L ${x2} ${y + 3}`;
|
|
13253
|
+
if (style === "wavy") {
|
|
13254
|
+
const steps = Math.max(8, Math.floor((x2 - x1) / 18));
|
|
13255
|
+
let d2 = `M ${x1} ${y}`;
|
|
13256
|
+
for (let i = 1; i <= steps; i++) {
|
|
13257
|
+
const px = x1 + (x2 - x1) * i / steps;
|
|
13258
|
+
const dy = i % 2 === 0 ? 4 : -4;
|
|
13259
|
+
d2 += ` Q ${px - (x2 - x1) / (2 * steps)} ${y + dy} ${px} ${y}`;
|
|
13260
|
+
}
|
|
13261
|
+
return d2;
|
|
13262
|
+
}
|
|
13263
|
+
const segs = 6;
|
|
13264
|
+
let d = `M ${x1} ${y}`;
|
|
13265
|
+
for (let i = 1; i <= segs; i++) {
|
|
13266
|
+
const px = x1 + (x2 - x1) * i / segs;
|
|
13267
|
+
const jitter = (Math.random() - 0.5) * 4;
|
|
13268
|
+
d += ` L ${px} ${y + jitter}`;
|
|
13269
|
+
}
|
|
13270
|
+
return d;
|
|
13271
|
+
}
|
|
13272
|
+
function AnimatedUnderline({ bbox, action }) {
|
|
13273
|
+
const [x1, , x2, y2] = bbox;
|
|
13274
|
+
const y = y2 + 6;
|
|
13275
|
+
const d = pathForStyle(x1, x2, y, action.style);
|
|
13276
|
+
const duration = action.draw_duration_ms / 1e3;
|
|
13277
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
13278
|
+
"svg",
|
|
13279
|
+
{
|
|
13280
|
+
style: {
|
|
13281
|
+
position: "absolute",
|
|
13282
|
+
inset: 0,
|
|
13283
|
+
pointerEvents: "none",
|
|
13284
|
+
overflow: "visible"
|
|
13285
|
+
},
|
|
13286
|
+
"data-role": "underline",
|
|
13287
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
13288
|
+
import_framer_motion3.motion.path,
|
|
13289
|
+
{
|
|
13290
|
+
d,
|
|
13291
|
+
fill: "none",
|
|
13292
|
+
stroke: action.color,
|
|
13293
|
+
strokeWidth: 4,
|
|
13294
|
+
strokeLinecap: "round",
|
|
13295
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13296
|
+
animate: { pathLength: 1, opacity: 1 },
|
|
13297
|
+
exit: { opacity: 0 },
|
|
13298
|
+
transition: { duration, ease: "easeOut" }
|
|
13299
|
+
}
|
|
13300
|
+
)
|
|
13301
|
+
}
|
|
13302
|
+
);
|
|
13303
|
+
}
|
|
13304
|
+
|
|
13305
|
+
// src/components/TutorMode/AnimatedHighlight.tsx
|
|
13306
|
+
var import_framer_motion4 = require("framer-motion");
|
|
13307
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
13308
|
+
function AnimatedHighlight({ bbox, action }) {
|
|
13309
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13310
|
+
const w = x2 - x1;
|
|
13311
|
+
const h = y2 - y1;
|
|
13312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
13313
|
+
import_framer_motion4.motion.div,
|
|
13314
|
+
{
|
|
13315
|
+
style: {
|
|
13316
|
+
position: "absolute",
|
|
13317
|
+
left: x1,
|
|
13318
|
+
top: y1,
|
|
13319
|
+
height: h,
|
|
13320
|
+
background: action.color,
|
|
13321
|
+
borderRadius: 4,
|
|
13322
|
+
mixBlendMode: "multiply",
|
|
13323
|
+
transformOrigin: "0% 50%",
|
|
13324
|
+
pointerEvents: "none"
|
|
13325
|
+
},
|
|
13326
|
+
initial: { width: 0, opacity: 0.9 },
|
|
13327
|
+
animate: { width: w, opacity: 0.9 },
|
|
13328
|
+
exit: { opacity: 0 },
|
|
13329
|
+
transition: { duration: action.draw_duration_ms / 1e3, ease: "easeOut" },
|
|
13330
|
+
"data-role": "highlight"
|
|
13331
|
+
}
|
|
13332
|
+
);
|
|
13333
|
+
}
|
|
13334
|
+
|
|
13335
|
+
// src/components/TutorMode/PulseOverlay.tsx
|
|
13336
|
+
var import_framer_motion5 = require("framer-motion");
|
|
13337
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
13338
|
+
var INTENSITY = {
|
|
13339
|
+
subtle: { scale: 1.02, border: "2px solid rgba(59,130,246,0.6)" },
|
|
13340
|
+
normal: { scale: 1.05, border: "3px solid rgba(59,130,246,0.8)" },
|
|
13341
|
+
strong: { scale: 1.1, border: "4px solid rgba(59,130,246,1.0)" }
|
|
13342
|
+
};
|
|
13343
|
+
function PulseOverlay({ bbox, action }) {
|
|
13344
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13345
|
+
const { scale, border } = INTENSITY[action.intensity];
|
|
13346
|
+
const repeat = action.count === 1 ? 0 : action.count - 1;
|
|
13347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
13348
|
+
import_framer_motion5.motion.div,
|
|
13349
|
+
{
|
|
13350
|
+
style: {
|
|
13351
|
+
position: "absolute",
|
|
13352
|
+
left: x1,
|
|
13353
|
+
top: y1,
|
|
13354
|
+
width: x2 - x1,
|
|
13355
|
+
height: y2 - y1,
|
|
13356
|
+
border,
|
|
13357
|
+
borderRadius: 8,
|
|
13358
|
+
pointerEvents: "none",
|
|
13359
|
+
boxSizing: "border-box"
|
|
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"
|
|
13368
|
+
},
|
|
13369
|
+
exit: { opacity: 0 },
|
|
13370
|
+
"data-role": "pulse"
|
|
13371
|
+
}
|
|
13372
|
+
);
|
|
13373
|
+
}
|
|
13374
|
+
|
|
13375
|
+
// src/components/TutorMode/CalloutArrow.tsx
|
|
13376
|
+
var import_framer_motion6 = require("framer-motion");
|
|
13377
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
13378
|
+
function centerOf(b) {
|
|
13379
|
+
return { x: (b[0] + b[2]) / 2, y: (b[1] + b[3]) / 2 };
|
|
13380
|
+
}
|
|
13381
|
+
function arrowPath(fromBbox, toBbox, curve) {
|
|
13382
|
+
const a = centerOf(fromBbox);
|
|
13383
|
+
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
|
+
const dx = b.x - a.x;
|
|
13390
|
+
const dy = b.y - a.y;
|
|
13391
|
+
const cx = (a.x + b.x) / 2 - dy * 0.25;
|
|
13392
|
+
const cy = (a.y + b.y) / 2 + dx * 0.25;
|
|
13393
|
+
return `M ${a.x} ${a.y} Q ${cx} ${cy} ${b.x} ${b.y}`;
|
|
13394
|
+
}
|
|
13395
|
+
function CalloutArrow({ fromBbox, toBbox, action }) {
|
|
13396
|
+
const d = arrowPath(fromBbox, toBbox, action.curve);
|
|
13397
|
+
const label = action.label;
|
|
13398
|
+
const target = centerOf(toBbox);
|
|
13399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
13400
|
+
"svg",
|
|
13401
|
+
{
|
|
13402
|
+
style: {
|
|
13403
|
+
position: "absolute",
|
|
13404
|
+
inset: 0,
|
|
13405
|
+
pointerEvents: "none",
|
|
13406
|
+
overflow: "visible"
|
|
13407
|
+
},
|
|
13408
|
+
"data-role": "callout",
|
|
13409
|
+
children: [
|
|
13410
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13411
|
+
"marker",
|
|
13412
|
+
{
|
|
13413
|
+
id: "arrowhead",
|
|
13414
|
+
viewBox: "0 0 10 10",
|
|
13415
|
+
refX: "8",
|
|
13416
|
+
refY: "5",
|
|
13417
|
+
markerWidth: "8",
|
|
13418
|
+
markerHeight: "8",
|
|
13419
|
+
orient: "auto",
|
|
13420
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "#3B82F6" })
|
|
13421
|
+
}
|
|
13422
|
+
) }),
|
|
13423
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
13424
|
+
import_framer_motion6.motion.path,
|
|
13425
|
+
{
|
|
13426
|
+
d,
|
|
13427
|
+
fill: "none",
|
|
13428
|
+
stroke: "#3B82F6",
|
|
13429
|
+
strokeWidth: 3,
|
|
13430
|
+
strokeLinecap: "round",
|
|
13431
|
+
markerEnd: "url(#arrowhead)",
|
|
13432
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13433
|
+
animate: { pathLength: 1, opacity: 1 },
|
|
13434
|
+
exit: { opacity: 0 },
|
|
13435
|
+
transition: { duration: 0.6, ease: "easeOut" }
|
|
13436
|
+
}
|
|
13437
|
+
),
|
|
13438
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
13439
|
+
import_framer_motion6.motion.g,
|
|
13440
|
+
{
|
|
13441
|
+
initial: { opacity: 0 },
|
|
13442
|
+
animate: { opacity: 1 },
|
|
13443
|
+
exit: { opacity: 0 },
|
|
13444
|
+
transition: { delay: 0.3, duration: 0.3 },
|
|
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
|
+
]
|
|
13469
|
+
}
|
|
13470
|
+
) : null
|
|
13471
|
+
]
|
|
13472
|
+
}
|
|
13473
|
+
);
|
|
13474
|
+
}
|
|
13475
|
+
|
|
13476
|
+
// src/components/TutorMode/GhostReference.tsx
|
|
13477
|
+
var import_framer_motion7 = require("framer-motion");
|
|
13478
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
13479
|
+
var POSITIONS = {
|
|
13480
|
+
"top-right": { top: 40, right: 40 },
|
|
13481
|
+
"top-left": { top: 40, left: 40 },
|
|
13482
|
+
"bottom-right": { bottom: 40, right: 40 },
|
|
13483
|
+
"bottom-left": { bottom: 40, left: 40 }
|
|
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)(
|
|
13495
|
+
import_framer_motion7.motion.div,
|
|
13496
|
+
{
|
|
13497
|
+
initial: { opacity: 0, y: 20, scale: 0.95 },
|
|
13498
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
13499
|
+
exit: { opacity: 0, y: 20, scale: 0.95 },
|
|
13500
|
+
transition: { duration: 0.4, ease: "easeOut" },
|
|
13501
|
+
style: {
|
|
13502
|
+
position: "absolute",
|
|
13503
|
+
width,
|
|
13504
|
+
background: "#111",
|
|
13505
|
+
color: "white",
|
|
13506
|
+
borderRadius: 12,
|
|
13507
|
+
padding: 12,
|
|
13508
|
+
boxShadow: "0 10px 40px rgba(0,0,0,0.5)",
|
|
13509
|
+
pointerEvents: "none",
|
|
13510
|
+
fontFamily: "system-ui, sans-serif",
|
|
13511
|
+
fontSize: 13,
|
|
13512
|
+
...POSITIONS[action.position]
|
|
13513
|
+
},
|
|
13514
|
+
"data-role": "ghost-reference",
|
|
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
|
|
13645
|
+
}
|
|
13646
|
+
);
|
|
13647
|
+
}
|
|
13648
|
+
|
|
13649
|
+
// src/components/TutorMode/CinemaLayer.tsx
|
|
13650
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
13651
|
+
function blockBbox(index, block_id) {
|
|
13652
|
+
return index.blockById.get(block_id)?.block.bbox;
|
|
13653
|
+
}
|
|
13654
|
+
function CinemaLayer({
|
|
13655
|
+
page,
|
|
13656
|
+
index,
|
|
13657
|
+
overlays,
|
|
13658
|
+
scale
|
|
13659
|
+
}) {
|
|
13660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
13661
|
+
"div",
|
|
13662
|
+
{
|
|
13663
|
+
"data-role": "cinema-layer",
|
|
13664
|
+
style: {
|
|
13665
|
+
position: "absolute",
|
|
13666
|
+
inset: 0,
|
|
13667
|
+
transformOrigin: "0 0",
|
|
13668
|
+
transform: `scale(${scale})`,
|
|
13669
|
+
width: page.page_dimensions.width,
|
|
13670
|
+
height: page.page_dimensions.height,
|
|
13671
|
+
pointerEvents: "none",
|
|
13672
|
+
// PDFPage renders internal layers at z-index 10/20/40/45/50
|
|
13673
|
+
// (canvas / text / highlight / focus / annotation). Without an
|
|
13674
|
+
// explicit z-index here, every tutor overlay stacks UNDER the
|
|
13675
|
+
// AnnotationLayer and becomes invisible. 100 puts us above all of
|
|
13676
|
+
// them while still letting the Exit button (z-index 60) remain
|
|
13677
|
+
// reachable because it sits OUTSIDE this stacking context.
|
|
13678
|
+
zIndex: 100
|
|
13679
|
+
},
|
|
13680
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_framer_motion10.AnimatePresence, { children: overlays.map((overlay) => {
|
|
13681
|
+
switch (overlay.kind) {
|
|
13682
|
+
case "spotlight": {
|
|
13683
|
+
const a = overlay.action;
|
|
13684
|
+
const b = blockBbox(index, a.target_block);
|
|
13685
|
+
if (!b) return null;
|
|
13686
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
13687
|
+
SpotlightMask,
|
|
13688
|
+
{
|
|
13689
|
+
page: page.page_dimensions,
|
|
13690
|
+
bbox: b,
|
|
13691
|
+
action: a
|
|
13692
|
+
},
|
|
13693
|
+
overlay.id
|
|
13694
|
+
);
|
|
13695
|
+
}
|
|
13696
|
+
case "underline": {
|
|
13697
|
+
const a = overlay.action;
|
|
13698
|
+
const b = blockBbox(index, a.target_block);
|
|
13699
|
+
if (!b) return null;
|
|
13700
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(AnimatedUnderline, { bbox: b, action: a }, overlay.id);
|
|
13701
|
+
}
|
|
13702
|
+
case "highlight": {
|
|
13703
|
+
const a = overlay.action;
|
|
13704
|
+
const b = blockBbox(index, a.target_block);
|
|
13705
|
+
if (!b) return null;
|
|
13706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(AnimatedHighlight, { bbox: b, action: a }, overlay.id);
|
|
13707
|
+
}
|
|
13708
|
+
case "pulse": {
|
|
13709
|
+
const a = overlay.action;
|
|
13710
|
+
const b = blockBbox(index, a.target_block);
|
|
13711
|
+
if (!b) return null;
|
|
13712
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(PulseOverlay, { bbox: b, action: a }, overlay.id);
|
|
13713
|
+
}
|
|
13714
|
+
case "callout": {
|
|
13715
|
+
const a = overlay.action;
|
|
13716
|
+
const from = blockBbox(index, a.from_block);
|
|
13717
|
+
const to = blockBbox(index, a.to_block);
|
|
13718
|
+
if (!from || !to) return null;
|
|
13719
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
13720
|
+
CalloutArrow,
|
|
13721
|
+
{
|
|
13722
|
+
fromBbox: from,
|
|
13723
|
+
toBbox: to,
|
|
13724
|
+
action: a
|
|
13725
|
+
},
|
|
13726
|
+
overlay.id
|
|
13727
|
+
);
|
|
13728
|
+
}
|
|
13729
|
+
case "ghost_reference": {
|
|
13730
|
+
const a = overlay.action;
|
|
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
|
+
}
|
|
13747
|
+
case "box": {
|
|
13748
|
+
const a = overlay.action;
|
|
13749
|
+
const b = blockBbox(index, a.target_block);
|
|
13750
|
+
if (!b) return null;
|
|
13751
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(BoxOverlay, { bbox: b, action: a }, overlay.id);
|
|
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);
|
|
13758
|
+
}
|
|
13759
|
+
case "clear":
|
|
13760
|
+
case "camera":
|
|
13761
|
+
return null;
|
|
13762
|
+
}
|
|
13763
|
+
}) })
|
|
13764
|
+
}
|
|
13765
|
+
);
|
|
13766
|
+
}
|
|
13767
|
+
|
|
13768
|
+
// src/components/TutorMode/SubtitleBar.tsx
|
|
13769
|
+
var import_framer_motion11 = require("framer-motion");
|
|
13770
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
13771
|
+
function SubtitleBar({ text }) {
|
|
13772
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_framer_motion11.AnimatePresence, { children: text ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
13773
|
+
import_framer_motion11.motion.div,
|
|
13774
|
+
{
|
|
13775
|
+
initial: { opacity: 0, y: 20 },
|
|
13776
|
+
animate: { opacity: 1, y: 0 },
|
|
13777
|
+
exit: { opacity: 0, y: 20 },
|
|
13778
|
+
transition: { duration: 0.3 },
|
|
13779
|
+
style: {
|
|
13780
|
+
position: "absolute",
|
|
13781
|
+
left: "50%",
|
|
13782
|
+
bottom: 32,
|
|
13783
|
+
transform: "translateX(-50%)",
|
|
13784
|
+
background: "rgba(0,0,0,0.75)",
|
|
13785
|
+
color: "white",
|
|
13786
|
+
padding: "10px 18px",
|
|
13787
|
+
borderRadius: 8,
|
|
13788
|
+
maxWidth: "80%",
|
|
13789
|
+
fontSize: 16,
|
|
13790
|
+
lineHeight: 1.4,
|
|
13791
|
+
fontFamily: "system-ui, sans-serif",
|
|
13792
|
+
pointerEvents: "none",
|
|
13793
|
+
zIndex: 50,
|
|
13794
|
+
textAlign: "center"
|
|
13795
|
+
},
|
|
13796
|
+
"data-role": "subtitle-bar",
|
|
13797
|
+
children: text
|
|
13798
|
+
},
|
|
13799
|
+
text
|
|
13800
|
+
) : null });
|
|
13801
|
+
}
|
|
13802
|
+
|
|
13803
|
+
// src/director/storyboard-engine.ts
|
|
13804
|
+
init_narration_store();
|
|
13805
|
+
init_camera_math();
|
|
13806
|
+
var DEFAULT_MIN_OVERLAY_MS = 3500;
|
|
13807
|
+
var StoryboardEngine = class {
|
|
13808
|
+
constructor(deps) {
|
|
13809
|
+
this.pendingTimers = /* @__PURE__ */ new Set();
|
|
13810
|
+
this.currentStoryboardId = 0;
|
|
13811
|
+
this.deps = deps;
|
|
13812
|
+
}
|
|
13813
|
+
/**
|
|
13814
|
+
* Execute a new storyboard. Cancels in-flight steps from the previous storyboard
|
|
13815
|
+
* and smoothly transitions the camera/overlays from the current state.
|
|
13816
|
+
*/
|
|
13817
|
+
execute(storyboard) {
|
|
13818
|
+
this.cancelPending();
|
|
13819
|
+
this.currentStoryboardId += 1;
|
|
13820
|
+
const storyboardId = this.currentStoryboardId;
|
|
13821
|
+
const { narrationStore } = this.deps;
|
|
13822
|
+
narrationStore.getState().setEngineStatus("transitioning");
|
|
13823
|
+
narrationStore.getState().setLastStoryboard(storyboard);
|
|
13824
|
+
let steps = [...storyboard.steps].sort((a, b) => a.at_ms - b.at_ms);
|
|
13825
|
+
const hasCamera = steps.some((s) => s.action.type === "camera");
|
|
13826
|
+
if (!hasCamera) {
|
|
13827
|
+
const focus = steps.find(
|
|
13828
|
+
(s) => s.action.type !== "clear" && "target_block" in s.action && s.action.target_block
|
|
13829
|
+
);
|
|
13830
|
+
if (focus && focus.action.type !== "clear" && "target_block" in focus.action) {
|
|
13831
|
+
steps = [
|
|
13832
|
+
{
|
|
13833
|
+
at_ms: 0,
|
|
13834
|
+
duration_ms: 700,
|
|
13835
|
+
action: {
|
|
13836
|
+
type: "camera",
|
|
13837
|
+
target_block: focus.action.target_block,
|
|
13838
|
+
scale: 1,
|
|
13839
|
+
padding: 60,
|
|
13840
|
+
easing: "ease-out"
|
|
13841
|
+
}
|
|
13842
|
+
},
|
|
13843
|
+
...steps
|
|
13844
|
+
];
|
|
13845
|
+
}
|
|
13846
|
+
}
|
|
13847
|
+
for (const step of steps) {
|
|
13848
|
+
const timer = setTimeout(() => {
|
|
13849
|
+
if (storyboardId !== this.currentStoryboardId) return;
|
|
13850
|
+
this.runStep(step);
|
|
13851
|
+
}, step.at_ms);
|
|
13852
|
+
this.pendingTimers.add(timer);
|
|
13853
|
+
}
|
|
13854
|
+
const markExecuting = setTimeout(() => {
|
|
13855
|
+
if (storyboardId !== this.currentStoryboardId) return;
|
|
13856
|
+
narrationStore.getState().setEngineStatus("executing");
|
|
13857
|
+
}, 0);
|
|
13858
|
+
this.pendingTimers.add(markExecuting);
|
|
13859
|
+
const last = steps[steps.length - 1];
|
|
13860
|
+
if (last) {
|
|
13861
|
+
const totalMs = last.at_ms + last.duration_ms;
|
|
13862
|
+
const markIdle = setTimeout(() => {
|
|
13863
|
+
if (storyboardId !== this.currentStoryboardId) return;
|
|
13864
|
+
narrationStore.getState().setEngineStatus("idle");
|
|
13865
|
+
}, totalMs + 50);
|
|
13866
|
+
this.pendingTimers.add(markIdle);
|
|
13867
|
+
}
|
|
13868
|
+
}
|
|
13869
|
+
/** Abort all pending steps and set engine status to idle. */
|
|
13870
|
+
cancelPending() {
|
|
13871
|
+
for (const t of this.pendingTimers) clearTimeout(t);
|
|
13872
|
+
this.pendingTimers.clear();
|
|
13873
|
+
this.deps.narrationStore.getState().setEngineStatus("idle");
|
|
13874
|
+
}
|
|
13875
|
+
/** Reset visuals: clear overlays, fit camera back to page. */
|
|
13876
|
+
resetVisuals() {
|
|
13877
|
+
this.cancelPending();
|
|
13878
|
+
const { narrationStore, bboxIndex, getViewport } = this.deps;
|
|
13879
|
+
narrationStore.getState().clearOverlays();
|
|
13880
|
+
const viewport = getViewport();
|
|
13881
|
+
const currentPage = narrationStore.getState().currentPage;
|
|
13882
|
+
const pageDims = bboxIndex.byPage.get(currentPage);
|
|
13883
|
+
const fit = pageDims && viewport.width > 0 && viewport.height > 0 ? fitPageScale(pageDims.page_dimensions, viewport) * 0.95 : 1;
|
|
13884
|
+
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0, easing: "ease-in-out" });
|
|
13885
|
+
}
|
|
13886
|
+
/** Execute one step — dispatch to narrationStore. Returns true if applied. */
|
|
13887
|
+
runStep(step) {
|
|
13888
|
+
const action = step.action;
|
|
13889
|
+
const { narrationStore, bboxIndex } = this.deps;
|
|
13890
|
+
if ("target_block" in action && action.target_block) {
|
|
13891
|
+
if (!bboxIndex.blockById.has(action.target_block)) {
|
|
13892
|
+
narrationStore.getState().appendDebugEvent({
|
|
13893
|
+
kind: "llm-error",
|
|
13894
|
+
summary: `dropped ${action.type} step \u2192 unknown target_block "${action.target_block}"`,
|
|
13895
|
+
payload: { action, validIds: [...bboxIndex.blockById.keys()] }
|
|
13896
|
+
});
|
|
13897
|
+
return false;
|
|
13898
|
+
}
|
|
13899
|
+
}
|
|
13900
|
+
if ("from_block" in action && action.from_block) {
|
|
13901
|
+
if (!bboxIndex.blockById.has(action.from_block)) {
|
|
13902
|
+
narrationStore.getState().appendDebugEvent({
|
|
13903
|
+
kind: "llm-error",
|
|
13904
|
+
summary: `dropped ${action.type} step \u2192 unknown from_block "${action.from_block}"`,
|
|
13905
|
+
payload: { action }
|
|
13906
|
+
});
|
|
13907
|
+
return false;
|
|
13908
|
+
}
|
|
13909
|
+
}
|
|
13910
|
+
if ("to_block" in action && action.to_block) {
|
|
13911
|
+
if (!bboxIndex.blockById.has(action.to_block)) {
|
|
13912
|
+
narrationStore.getState().appendDebugEvent({
|
|
13913
|
+
kind: "llm-error",
|
|
13914
|
+
summary: `dropped ${action.type} step \u2192 unknown to_block "${action.to_block}"`,
|
|
13915
|
+
payload: { action }
|
|
13916
|
+
});
|
|
13917
|
+
return false;
|
|
13918
|
+
}
|
|
13919
|
+
}
|
|
13920
|
+
if (action.type === "camera") {
|
|
13921
|
+
this.applyCamera(action, step.duration_ms);
|
|
13922
|
+
return true;
|
|
13923
|
+
}
|
|
13924
|
+
if (action.type === "clear") {
|
|
13925
|
+
const targets = action.targets;
|
|
13926
|
+
if (targets === "all" || targets === "overlays") {
|
|
13927
|
+
narrationStore.getState().clearOverlays();
|
|
13928
|
+
} else if (targets === "spotlights") {
|
|
13929
|
+
narrationStore.getState().clearOverlays((o) => o.kind === "spotlight");
|
|
13930
|
+
} else if (Array.isArray(targets)) {
|
|
13931
|
+
const ids = new Set(targets);
|
|
13932
|
+
narrationStore.getState().clearOverlays((o) => ids.has(o.id));
|
|
13933
|
+
}
|
|
13934
|
+
return true;
|
|
13935
|
+
}
|
|
13936
|
+
const minMs = this.deps.minOverlayDurationMs ?? DEFAULT_MIN_OVERLAY_MS;
|
|
13937
|
+
const visibleMs = Math.max(step.duration_ms, minMs);
|
|
13938
|
+
const overlay = {
|
|
13939
|
+
id: makeOverlayId(action),
|
|
13940
|
+
kind: action.type,
|
|
13941
|
+
action,
|
|
13942
|
+
createdAt: Date.now(),
|
|
13943
|
+
expiresAt: Date.now() + visibleMs
|
|
13944
|
+
};
|
|
13945
|
+
narrationStore.getState().addOverlay(overlay);
|
|
13946
|
+
const timer = setTimeout(() => {
|
|
13947
|
+
narrationStore.getState().removeOverlay(overlay.id);
|
|
13948
|
+
}, visibleMs);
|
|
13949
|
+
this.pendingTimers.add(timer);
|
|
13950
|
+
return true;
|
|
13951
|
+
}
|
|
13952
|
+
applyCamera(action, durationMs) {
|
|
13953
|
+
const { narrationStore, bboxIndex, getViewport } = this.deps;
|
|
13954
|
+
const viewport = getViewport();
|
|
13955
|
+
let bbox = action.target_bbox;
|
|
13956
|
+
let pageDims = void 0;
|
|
13957
|
+
if (!bbox && action.target_block) {
|
|
13958
|
+
const hit = bboxIndex.blockById.get(action.target_block);
|
|
13959
|
+
if (!hit) return;
|
|
13960
|
+
bbox = hit.block.bbox;
|
|
13961
|
+
pageDims = bboxIndex.byPage.get(hit.pageNumber);
|
|
13962
|
+
} else if (bbox) {
|
|
13963
|
+
pageDims = bboxIndex.byPage.get(narrationStore.getState().currentPage);
|
|
13964
|
+
}
|
|
13965
|
+
if (!bbox || !pageDims) return;
|
|
13966
|
+
const fit = fitPageScale(pageDims.page_dimensions, viewport);
|
|
13967
|
+
const requested = Math.max(0.5, Math.min(3, action.scale ?? 1));
|
|
13968
|
+
const finalScale = fit * requested;
|
|
13969
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13970
|
+
const blockCX = (x1 + x2) / 2;
|
|
13971
|
+
const blockCY = (y1 + y2) / 2;
|
|
13972
|
+
const pageCX = pageDims.page_dimensions.width / 2;
|
|
13973
|
+
const pageCY = pageDims.page_dimensions.height / 2;
|
|
13974
|
+
const x = (pageCX - blockCX) * finalScale;
|
|
13975
|
+
const y = (pageCY - blockCY) * finalScale;
|
|
13976
|
+
const camera = {
|
|
13977
|
+
scale: finalScale,
|
|
13978
|
+
x,
|
|
13979
|
+
y,
|
|
13980
|
+
easing: action.easing
|
|
13981
|
+
};
|
|
13982
|
+
narrationStore.getState().setCamera(camera);
|
|
13983
|
+
void durationMs;
|
|
13984
|
+
void computeCameraForBlock;
|
|
13985
|
+
}
|
|
13986
|
+
};
|
|
13987
|
+
|
|
13988
|
+
// src/director/storyboard-schema.ts
|
|
13989
|
+
var import_zod = require("zod");
|
|
13990
|
+
var BBoxCoordsSchema = import_zod.z.tuple([import_zod.z.number(), import_zod.z.number(), import_zod.z.number(), import_zod.z.number()]);
|
|
13991
|
+
var CameraSchema = import_zod.z.object({
|
|
13992
|
+
type: import_zod.z.literal("camera"),
|
|
13993
|
+
target_block: import_zod.z.string().optional(),
|
|
13994
|
+
target_bbox: BBoxCoordsSchema.optional(),
|
|
13995
|
+
scale: import_zod.z.number().min(0.5).max(4).default(1),
|
|
13996
|
+
padding: import_zod.z.number().min(0).max(400).default(80),
|
|
13997
|
+
easing: import_zod.z.enum(["linear", "ease-in", "ease-out", "ease-in-out"]).default("ease-in-out")
|
|
13998
|
+
}).refine((a) => !!a.target_block || !!a.target_bbox, {
|
|
13999
|
+
message: "camera requires target_block or target_bbox"
|
|
14000
|
+
});
|
|
14001
|
+
var SpotlightSchema = import_zod.z.object({
|
|
14002
|
+
type: import_zod.z.literal("spotlight"),
|
|
14003
|
+
target_block: import_zod.z.string(),
|
|
14004
|
+
dim_opacity: import_zod.z.number().min(0).max(1).default(0.65),
|
|
14005
|
+
feather_px: import_zod.z.number().min(0).max(200).default(40),
|
|
14006
|
+
shape: import_zod.z.enum(["rect", "rounded", "ellipse"]).default("rounded")
|
|
14007
|
+
});
|
|
14008
|
+
var UnderlineSchema = import_zod.z.object({
|
|
14009
|
+
type: import_zod.z.literal("underline"),
|
|
14010
|
+
target_block: import_zod.z.string(),
|
|
14011
|
+
color: import_zod.z.string().default("#FBBF24"),
|
|
14012
|
+
style: import_zod.z.enum(["straight", "sketch", "double", "wavy"]).default("sketch"),
|
|
14013
|
+
draw_duration_ms: import_zod.z.number().min(100).max(3e3).default(600)
|
|
14014
|
+
});
|
|
14015
|
+
var HighlightSchema = import_zod.z.object({
|
|
14016
|
+
type: import_zod.z.literal("highlight"),
|
|
14017
|
+
target_block: import_zod.z.string(),
|
|
14018
|
+
color: import_zod.z.string().default("rgba(250, 204, 21, 0.35)"),
|
|
14019
|
+
draw_duration_ms: import_zod.z.number().min(100).max(3e3).default(500)
|
|
14020
|
+
});
|
|
14021
|
+
var PulseSchema = import_zod.z.object({
|
|
14022
|
+
type: import_zod.z.literal("pulse"),
|
|
14023
|
+
target_block: import_zod.z.string(),
|
|
14024
|
+
count: import_zod.z.number().int().min(1).max(5).default(2),
|
|
14025
|
+
intensity: import_zod.z.enum(["subtle", "normal", "strong"]).default("normal")
|
|
14026
|
+
});
|
|
14027
|
+
var CalloutSchema = import_zod.z.object({
|
|
14028
|
+
type: import_zod.z.literal("callout"),
|
|
14029
|
+
from_block: import_zod.z.string(),
|
|
14030
|
+
to_block: import_zod.z.string(),
|
|
14031
|
+
label: import_zod.z.string().max(120).optional(),
|
|
14032
|
+
curve: import_zod.z.enum(["straight", "curved", "zigzag"]).default("curved")
|
|
14033
|
+
});
|
|
14034
|
+
var GhostReferenceSchema = import_zod.z.object({
|
|
14035
|
+
type: import_zod.z.literal("ghost_reference"),
|
|
14036
|
+
target_page: import_zod.z.number().int().min(1),
|
|
14037
|
+
target_block: import_zod.z.string(),
|
|
14038
|
+
position: import_zod.z.enum(["top-right", "top-left", "bottom-right", "bottom-left"]).default("top-right")
|
|
14039
|
+
});
|
|
14040
|
+
var BoxSchema = import_zod.z.object({
|
|
14041
|
+
type: import_zod.z.literal("box"),
|
|
14042
|
+
target_block: import_zod.z.string(),
|
|
14043
|
+
color: import_zod.z.string().default("#3B82F6"),
|
|
14044
|
+
style: import_zod.z.enum(["solid", "dashed"]).default("solid")
|
|
14045
|
+
});
|
|
14046
|
+
var LabelSchema = import_zod.z.object({
|
|
14047
|
+
type: import_zod.z.literal("label"),
|
|
14048
|
+
target_block: import_zod.z.string(),
|
|
14049
|
+
text: import_zod.z.string().min(1).max(120),
|
|
14050
|
+
position: import_zod.z.enum(["top", "bottom", "left", "right"]).default("top")
|
|
14051
|
+
});
|
|
14052
|
+
var ClearSchema = import_zod.z.object({
|
|
14053
|
+
type: import_zod.z.literal("clear"),
|
|
14054
|
+
targets: import_zod.z.union([import_zod.z.enum(["all", "spotlights", "overlays"]), import_zod.z.array(import_zod.z.string())]).default("overlays")
|
|
14055
|
+
});
|
|
14056
|
+
var StoryboardActionSchema = import_zod.z.union([
|
|
14057
|
+
CameraSchema,
|
|
14058
|
+
SpotlightSchema,
|
|
14059
|
+
UnderlineSchema,
|
|
14060
|
+
HighlightSchema,
|
|
14061
|
+
PulseSchema,
|
|
14062
|
+
CalloutSchema,
|
|
14063
|
+
GhostReferenceSchema,
|
|
14064
|
+
BoxSchema,
|
|
14065
|
+
LabelSchema,
|
|
14066
|
+
ClearSchema
|
|
14067
|
+
]);
|
|
14068
|
+
var StoryboardStepSchema = import_zod.z.object({
|
|
14069
|
+
at_ms: import_zod.z.number().min(0).max(5e3).default(0),
|
|
14070
|
+
duration_ms: import_zod.z.number().min(100).max(5e3).default(800),
|
|
14071
|
+
action: StoryboardActionSchema
|
|
14072
|
+
});
|
|
14073
|
+
var StoryboardSchema = import_zod.z.object({
|
|
14074
|
+
version: import_zod.z.literal(1),
|
|
14075
|
+
reasoning: import_zod.z.string().max(500).default(""),
|
|
14076
|
+
steps: import_zod.z.array(StoryboardStepSchema).min(1).max(4)
|
|
14077
|
+
});
|
|
14078
|
+
function storyboardJsonSchema(opts = {}) {
|
|
14079
|
+
const { validBlockIds, validCrossPageBlockIds } = opts;
|
|
14080
|
+
const blockIdSchema = validBlockIds && validBlockIds.length > 0 ? { type: ["string", "null"], enum: [...validBlockIds, null] } : { type: ["string", "null"] };
|
|
14081
|
+
const crossPageBlockIdSchema = validCrossPageBlockIds && validCrossPageBlockIds.length > 0 ? {
|
|
14082
|
+
type: ["string", "null"],
|
|
14083
|
+
enum: [...validCrossPageBlockIds, ...validBlockIds ?? [], null]
|
|
14084
|
+
} : blockIdSchema;
|
|
14085
|
+
const actionSchema = {
|
|
14086
|
+
type: "object",
|
|
14087
|
+
additionalProperties: false,
|
|
14088
|
+
required: [
|
|
14089
|
+
"type",
|
|
14090
|
+
"target_block",
|
|
14091
|
+
"target_bbox",
|
|
14092
|
+
"scale",
|
|
14093
|
+
"padding",
|
|
14094
|
+
"easing",
|
|
14095
|
+
"dim_opacity",
|
|
14096
|
+
"feather_px",
|
|
14097
|
+
"shape",
|
|
14098
|
+
"color",
|
|
14099
|
+
"style",
|
|
14100
|
+
"draw_duration_ms",
|
|
14101
|
+
"count",
|
|
14102
|
+
"intensity",
|
|
14103
|
+
"from_block",
|
|
14104
|
+
"to_block",
|
|
14105
|
+
"label",
|
|
14106
|
+
"curve",
|
|
14107
|
+
"target_page",
|
|
14108
|
+
"position",
|
|
14109
|
+
"text",
|
|
14110
|
+
"targets"
|
|
14111
|
+
],
|
|
14112
|
+
properties: {
|
|
14113
|
+
type: {
|
|
14114
|
+
type: "string",
|
|
14115
|
+
enum: [
|
|
14116
|
+
"camera",
|
|
14117
|
+
"spotlight",
|
|
14118
|
+
"underline",
|
|
14119
|
+
"highlight",
|
|
14120
|
+
"pulse",
|
|
14121
|
+
"callout",
|
|
14122
|
+
"ghost_reference",
|
|
14123
|
+
"box",
|
|
14124
|
+
"label",
|
|
14125
|
+
"clear"
|
|
14126
|
+
]
|
|
14127
|
+
},
|
|
14128
|
+
target_block: blockIdSchema,
|
|
14129
|
+
target_bbox: {
|
|
14130
|
+
type: ["array", "null"],
|
|
14131
|
+
items: { type: "number" },
|
|
14132
|
+
minItems: 4,
|
|
14133
|
+
maxItems: 4
|
|
14134
|
+
},
|
|
14135
|
+
scale: { type: ["number", "null"] },
|
|
14136
|
+
padding: { type: ["number", "null"] },
|
|
14137
|
+
easing: {
|
|
14138
|
+
type: ["string", "null"],
|
|
14139
|
+
enum: ["linear", "ease-in", "ease-out", "ease-in-out", null]
|
|
14140
|
+
},
|
|
14141
|
+
dim_opacity: { type: ["number", "null"] },
|
|
14142
|
+
feather_px: { type: ["number", "null"] },
|
|
14143
|
+
shape: {
|
|
14144
|
+
type: ["string", "null"],
|
|
14145
|
+
enum: ["rect", "rounded", "ellipse", null]
|
|
14146
|
+
},
|
|
14147
|
+
color: { type: ["string", "null"] },
|
|
14148
|
+
style: {
|
|
14149
|
+
type: ["string", "null"],
|
|
14150
|
+
enum: ["straight", "sketch", "double", "wavy", "solid", "dashed", null]
|
|
14151
|
+
},
|
|
14152
|
+
draw_duration_ms: { type: ["number", "null"] },
|
|
14153
|
+
count: { type: ["integer", "null"] },
|
|
14154
|
+
intensity: {
|
|
14155
|
+
type: ["string", "null"],
|
|
14156
|
+
enum: ["subtle", "normal", "strong", null]
|
|
14157
|
+
},
|
|
14158
|
+
from_block: blockIdSchema,
|
|
14159
|
+
to_block: crossPageBlockIdSchema,
|
|
14160
|
+
label: { type: ["string", "null"] },
|
|
14161
|
+
curve: {
|
|
14162
|
+
type: ["string", "null"],
|
|
14163
|
+
enum: ["straight", "curved", "zigzag", null]
|
|
14164
|
+
},
|
|
14165
|
+
target_page: { type: ["integer", "null"] },
|
|
14166
|
+
position: {
|
|
14167
|
+
type: ["string", "null"],
|
|
14168
|
+
enum: [
|
|
14169
|
+
"top",
|
|
14170
|
+
"bottom",
|
|
14171
|
+
"left",
|
|
14172
|
+
"right",
|
|
14173
|
+
"top-right",
|
|
14174
|
+
"top-left",
|
|
14175
|
+
"bottom-right",
|
|
14176
|
+
"bottom-left",
|
|
14177
|
+
null
|
|
14178
|
+
]
|
|
14179
|
+
},
|
|
14180
|
+
text: { type: ["string", "null"] },
|
|
14181
|
+
targets: {
|
|
14182
|
+
type: ["string", "null"],
|
|
14183
|
+
enum: ["all", "spotlights", "overlays", null]
|
|
14184
|
+
}
|
|
14185
|
+
}
|
|
14186
|
+
};
|
|
14187
|
+
return {
|
|
14188
|
+
type: "object",
|
|
14189
|
+
additionalProperties: false,
|
|
14190
|
+
required: ["version", "reasoning", "steps"],
|
|
14191
|
+
properties: {
|
|
14192
|
+
version: { type: "integer", enum: [1] },
|
|
14193
|
+
reasoning: { type: "string" },
|
|
14194
|
+
steps: {
|
|
14195
|
+
type: "array",
|
|
14196
|
+
minItems: 1,
|
|
14197
|
+
maxItems: 4,
|
|
14198
|
+
items: {
|
|
14199
|
+
type: "object",
|
|
14200
|
+
additionalProperties: false,
|
|
14201
|
+
required: ["at_ms", "duration_ms", "action"],
|
|
14202
|
+
properties: {
|
|
14203
|
+
at_ms: { type: "number" },
|
|
14204
|
+
duration_ms: { type: "number" },
|
|
14205
|
+
action: actionSchema
|
|
14206
|
+
}
|
|
14207
|
+
}
|
|
14208
|
+
}
|
|
14209
|
+
}
|
|
14210
|
+
};
|
|
14211
|
+
}
|
|
14212
|
+
|
|
14213
|
+
// src/director/prompts.ts
|
|
14214
|
+
var SYSTEM_PROMPT = `You are the cinematic director of an AI tutor's PDF visualization. The tutor speaks one "chunk" at a time; for each chunk you anchor the visuals onto the EXACT blocks on the page the tutor is talking about, so the reader sees the page react like a produced teaching video. Think of yourself as a motion designer layering effects on top of a document \u2014 zoom is only one tool in the kit, and often not the right one.
|
|
14215
|
+
|
|
14216
|
+
# Your primary task
|
|
14217
|
+
You are given a list of blocks for the current page under "Page blocks", each with a \`block_id\`, \`text\`, \`type\`, \`bbox\`, and \`default_action\`. Your #1 job is to decide WHICH block(s) the current chunk is referring to, and then pick the right visual action(s) to anchor there.
|
|
14218
|
+
|
|
14219
|
+
Anchoring rules:
|
|
14220
|
+
- EVERY action that references a block MUST set \`target_block\` (or \`from_block\`/\`to_block\` for callouts) to an EXISTING \`block_id\` from "Page blocks" (or "Cross-page figures index" for cross-page refs). Never invent an id. Never emit a step whose target can't be found in the provided lists.
|
|
14221
|
+
- Match the chunk to blocks by semantic overlap with the block's \`text\`: quoted phrases, named entities, keywords, figure references ("Fig 3.2", "the suture"), list enumerations.
|
|
14222
|
+
- If no block clearly matches, emit a single \`camera\` step that fits the page (no overlays) and explain in \`reasoning\`. Do NOT spray overlays onto random blocks.
|
|
14223
|
+
- If multiple blocks match, pick the most specific one, or use a \`callout\` from one to the other.
|
|
14224
|
+
|
|
14225
|
+
# Output shape
|
|
14226
|
+
Output ONLY this JSON, nothing else:
|
|
14227
|
+
{
|
|
14228
|
+
"version": 1,
|
|
14229
|
+
"reasoning": "<which block(s) you picked, which intent you used, and why \u2014 name the block_id>",
|
|
14230
|
+
"steps": [ { "at_ms": <int>, "duration_ms": <int>, "action": <action> }, ... ]
|
|
14231
|
+
}
|
|
14232
|
+
|
|
14233
|
+
# Action shapes \u2014 ALL fields shown are REQUIRED per action type
|
|
14234
|
+
- camera: { "type":"camera", "target_block":"<id>", "scale":1.1, "padding":80, "easing":"ease-out" }
|
|
14235
|
+
- spotlight: { "type":"spotlight", "target_block":"<id>", "dim_opacity":0.65, "feather_px":40, "shape":"rounded" }
|
|
14236
|
+
- underline: { "type":"underline", "target_block":"<id>", "color":"#FBBF24", "style":"sketch", "draw_duration_ms":600 }
|
|
14237
|
+
- highlight: { "type":"highlight", "target_block":"<id>", "color":"rgba(250,204,21,0.35)", "draw_duration_ms":500 }
|
|
14238
|
+
- pulse: { "type":"pulse", "target_block":"<id>", "count":2, "intensity":"normal" }
|
|
14239
|
+
- callout: { "type":"callout", "from_block":"<id>", "to_block":"<id>", "label":"<text>", "curve":"curved" }
|
|
14240
|
+
- ghost_reference: { "type":"ghost_reference", "target_page":<int>, "target_block":"<id>", "position":"top-right" }
|
|
14241
|
+
- box: { "type":"box", "target_block":"<id>", "color":"#3B82F6", "style":"solid" }
|
|
14242
|
+
- label: { "type":"label", "target_block":"<id>", "text":"<text>", "position":"top" }
|
|
14243
|
+
- clear: { "type":"clear", "targets":"overlays" }
|
|
14244
|
+
|
|
14245
|
+
# When to use each action (match the effect to the narration's intent, not just the block type)
|
|
14246
|
+
- camera \u2014 when focus SHIFTS to a new region. If the primary block is already on-screen and roughly centered, you may skip camera entirely and start with an overlay. When you do use camera, prefer scale 1.1\u20131.4 for gentle re-centering; reserve 1.5+ for dense figures you need to inspect closely.
|
|
14247
|
+
- spotlight \u2014 when narration ISOLATES one idea, term, or sentence. Great for definitions, principles, and "the key insight is\u2026" moments.
|
|
14248
|
+
- underline \u2014 when narration QUOTES a phrase or reads a line word-by-word. Use style "sketch" for handwritten feel, "straight" for formal, "wavy" for emphasis. Pairs well with spotlight.
|
|
14249
|
+
- highlight \u2014 when narration FLAGS a keyword inline without full focus. Cheap, fast, great for list items, definitions-in-context, callback references.
|
|
14250
|
+
- pulse \u2014 when narration says "notice this" / "see here" / "look at the diagram". Use with figures, icons, and anchor blocks that should catch the eye without blocking surrounding content.
|
|
14251
|
+
- callout \u2014 when narration CONNECTS two things on the page: a caption to its figure, a label to a region, one list item to another. Arrow implies directional meaning.
|
|
14252
|
+
- ghost_reference \u2014 when narration REFERS to a block on a DIFFERENT page ("as we saw on page 2\u2026"). Never use for same-page references.
|
|
14253
|
+
- box \u2014 when narration FRAMES a structural region: a table, a sidebar, a group of related items, a diagram subpart. Use dashed style for "in-progress" / "under discussion" regions.
|
|
14254
|
+
- label \u2014 when narration attaches a NAMED TAG to a block: "this is the definition", "this is an example", "step 3". Keep text \u226440 chars for readability.
|
|
14255
|
+
- clear \u2014 rarely needed; the engine auto-expires overlays. Use only when you explicitly want to wipe prior state before a new beat.
|
|
14256
|
+
|
|
14257
|
+
Respect each block's \`default_action\` as a soft hint, but override it freely when narration intent calls for a different effect. A paragraph labeled \`default_action: spotlight\` can absolutely take a highlight or underline if that's what the narration asks for.
|
|
14258
|
+
|
|
14259
|
+
# Intent Taxonomy \u2014 canonical "recipes" you should use as your default vocabulary
|
|
14260
|
+
When narration fits one of these patterns, emit the corresponding storyboard shape (fill in real block_ids from the page). You may also COMPOSE freely beyond these \u2014 they are a floor, not a ceiling.
|
|
14261
|
+
|
|
14262
|
+
## define \u2014 the narration introduces or defines a term
|
|
14263
|
+
Shape: spotlight the term + underline it + drop a label tag. No camera move if the block is already on-screen.
|
|
14264
|
+
{
|
|
14265
|
+
"version": 1,
|
|
14266
|
+
"reasoning": "define recipe: spotlighting and underlining the term, labeling as 'definition'",
|
|
14267
|
+
"steps": [
|
|
14268
|
+
{ "at_ms":0, "duration_ms":700, "action": { "type":"spotlight", "target_block":"p1_para0", "dim_opacity":0.6, "feather_px":40, "shape":"rounded" } },
|
|
14269
|
+
{ "at_ms":200, "duration_ms":800, "action": { "type":"underline", "target_block":"p1_para0", "color":"#FBBF24", "style":"sketch", "draw_duration_ms":700 } },
|
|
14270
|
+
{ "at_ms":900, "duration_ms":1200, "action": { "type":"label", "target_block":"p1_para0", "text":"definition", "position":"top" } }
|
|
14271
|
+
]
|
|
14272
|
+
}
|
|
14273
|
+
|
|
14274
|
+
## point_out \u2014 the narration directs the viewer's eye to a figure, diagram, or specific region
|
|
14275
|
+
Shape: gentle camera move + callout arrow from caption to figure + pulse the figure.
|
|
14276
|
+
{
|
|
14277
|
+
"version": 1,
|
|
14278
|
+
"reasoning": "point_out recipe: drawing attention from caption p1_cap1 to figure p1_fig0",
|
|
14279
|
+
"steps": [
|
|
14280
|
+
{ "at_ms":0, "duration_ms":600, "action": { "type":"camera", "target_block":"p1_fig0", "scale":1.3, "padding":80, "easing":"ease-out" } },
|
|
14281
|
+
{ "at_ms":400, "duration_ms":900, "action": { "type":"callout", "from_block":"p1_cap1", "to_block":"p1_fig0", "label":"see here", "curve":"curved" } },
|
|
14282
|
+
{ "at_ms":900, "duration_ms":1200, "action": { "type":"pulse", "target_block":"p1_fig0", "count":2, "intensity":"normal" } }
|
|
14283
|
+
]
|
|
14284
|
+
}
|
|
14285
|
+
|
|
14286
|
+
## compare \u2014 the narration contrasts two things on the page
|
|
14287
|
+
Shape: box A + box B + callout between them with a relational label.
|
|
14288
|
+
{
|
|
14289
|
+
"version": 1,
|
|
14290
|
+
"reasoning": "compare recipe: framing fibrous vs synovial joints",
|
|
14291
|
+
"steps": [
|
|
14292
|
+
{ "at_ms":0, "duration_ms":600, "action": { "type":"box", "target_block":"p1_list5", "color":"#3B82F6", "style":"solid" } },
|
|
14293
|
+
{ "at_ms":300, "duration_ms":600, "action": { "type":"box", "target_block":"p1_list12", "color":"#F472B6", "style":"solid" } },
|
|
14294
|
+
{ "at_ms":800, "duration_ms":1000, "action": { "type":"callout", "from_block":"p1_list5", "to_block":"p1_list12", "label":"vs", "curve":"curved" } }
|
|
14295
|
+
]
|
|
14296
|
+
}
|
|
14297
|
+
|
|
14298
|
+
## emphasize \u2014 the narration stresses a keyword, warning, or takeaway
|
|
14299
|
+
Shape: highlight + pulse. Fast, punchy, no camera.
|
|
14300
|
+
{
|
|
14301
|
+
"version": 1,
|
|
14302
|
+
"reasoning": "emphasize recipe: highlighting key keyword and pulsing for stress",
|
|
14303
|
+
"steps": [
|
|
14304
|
+
{ "at_ms":0, "duration_ms":500, "action": { "type":"highlight", "target_block":"p1_list0", "color":"rgba(250,204,21,0.35)", "draw_duration_ms":450 } },
|
|
14305
|
+
{ "at_ms":350, "duration_ms":800, "action": { "type":"pulse", "target_block":"p1_list0", "count":2, "intensity":"strong" } }
|
|
14306
|
+
]
|
|
14307
|
+
}
|
|
14308
|
+
|
|
14309
|
+
# Choreography rules
|
|
14310
|
+
- HARD REQUIREMENT: every storyboard MUST contain at least ONE non-camera step. A lone camera step is NEVER a valid output \u2014 the viewer needs an overlay to know WHY the camera moved. If you cannot find a good overlay target, emit a \`highlight\` or \`pulse\` on your primary block as the second step.
|
|
14311
|
+
- Favor VARIETY that matches narration texture \u2014 a definition earns different visuals than a comparison. Don't send every chunk through the same zoom+box motion.
|
|
14312
|
+
- Include a camera step only when focus genuinely shifts to a new region. If the primary target is already on-screen and roughly centred, SKIP the camera entirely and start directly with an overlay.
|
|
14313
|
+
- Camera scale: default to **1.1** (gentle re-centre). Use **1.2\u20131.3** for normal reading distance. Use **1.4\u20131.6** ONLY for dense figures or small inline details. NEVER use a scale below 0.5 or above 4.0 \u2014 the engine rejects those. When in doubt, use 1.1.
|
|
14314
|
+
- Prefer overlays that OVERLAP the camera move. A camera that takes 700ms to finish while overlays fire at 200ms feels cinematic; overlays waiting until after the camera settles feel sluggish. Stagger \`at_ms\`: camera at 0, first overlay at 150\u2013300ms, second overlay at 600\u2013900ms.
|
|
14315
|
+
- 2\u20134 steps is typical; single-step overlays (no camera) are PREFERRED when the target is already visible.
|
|
14316
|
+
- When narration compares two things, USE the compare recipe (box + box + callout), not a single camera step. When narration says "key takeaway", USE emphasize (highlight + pulse).
|
|
14317
|
+
- Never target a block_id not present in the provided lists. If no block matches, emit a single camera step at scale 1.0 + a subtle pulse on the nearest heading; explain in \`reasoning\`.
|
|
14318
|
+
- Output ONLY valid JSON. No markdown, no code fences, no commentary, no trailing whitespace inside property values.
|
|
14319
|
+
|
|
14320
|
+
# Forbidden outputs \u2014 these will be rejected:
|
|
14321
|
+
- A storyboard with only a camera step.
|
|
14322
|
+
- A camera step with scale < 0.5 or > 4.0.
|
|
14323
|
+
- target_block values not listed in "Page blocks" or "Cross-page figures index".
|
|
14324
|
+
- Tab characters, newlines, or explanatory text inside JSON string values.`;
|
|
14325
|
+
function truncate(text, max = 200) {
|
|
14326
|
+
if (!text) return "";
|
|
14327
|
+
if (text.length <= max) return text;
|
|
14328
|
+
const slice = text.slice(0, max);
|
|
14329
|
+
const last = slice.lastIndexOf(" ");
|
|
14330
|
+
return (last > 40 ? slice.slice(0, last) : slice) + "\u2026";
|
|
14331
|
+
}
|
|
14332
|
+
function buildUserPrompt(input) {
|
|
14333
|
+
const {
|
|
14334
|
+
chunk,
|
|
14335
|
+
pageNumber,
|
|
14336
|
+
page,
|
|
14337
|
+
index,
|
|
14338
|
+
history,
|
|
14339
|
+
camera,
|
|
14340
|
+
activeOverlays,
|
|
14341
|
+
maxSteps = 4
|
|
14342
|
+
} = input;
|
|
14343
|
+
const pageBlocks = page.blocks.map((b) => ({
|
|
14344
|
+
block_id: b.block_id,
|
|
14345
|
+
type: b.type,
|
|
14346
|
+
text: truncate(b.text, 200),
|
|
14347
|
+
bbox: b.bbox,
|
|
14348
|
+
default_action: b.default_action
|
|
14349
|
+
}));
|
|
14350
|
+
const xPageFigures = index.crossPageFigures.filter((f) => f.page !== pageNumber).slice(0, 20).map((f) => ({
|
|
14351
|
+
block_id: f.block_id,
|
|
14352
|
+
page: f.page,
|
|
14353
|
+
type: f.type,
|
|
14354
|
+
text: truncate(f.text, 200)
|
|
14355
|
+
}));
|
|
14356
|
+
const recent = history.slice(-3).map((h) => h.text);
|
|
14357
|
+
const overlaySummary = activeOverlays.map((o) => ({ id: o.id, kind: o.kind }));
|
|
14358
|
+
const blockIdList = pageBlocks.map((b) => b.block_id);
|
|
14359
|
+
return [
|
|
14360
|
+
`Current page: ${pageNumber}`,
|
|
14361
|
+
`Page blocks (${pageBlocks.length}) \u2014 you MUST pick target_block from this list:`,
|
|
14362
|
+
JSON.stringify(pageBlocks),
|
|
14363
|
+
"",
|
|
14364
|
+
`Valid block_ids for this page: ${JSON.stringify(blockIdList)}`,
|
|
14365
|
+
"",
|
|
14366
|
+
`Cross-page figures index: ${JSON.stringify(xPageFigures)}`,
|
|
14367
|
+
"",
|
|
14368
|
+
`Current chunk (what the tutor just said): ${JSON.stringify(chunk)}`,
|
|
14369
|
+
`Recent chunks: ${JSON.stringify(recent)}`,
|
|
14370
|
+
`Current camera: ${JSON.stringify(camera)}`,
|
|
14371
|
+
`Active overlays: ${JSON.stringify(overlaySummary)}`,
|
|
14372
|
+
"",
|
|
14373
|
+
`Max steps: ${maxSteps}`,
|
|
14374
|
+
`Output JSON storyboard. Every target_block MUST be one of the ids above.`
|
|
14375
|
+
].join("\n");
|
|
14376
|
+
}
|
|
14377
|
+
|
|
14378
|
+
// src/director/sse-parser.ts
|
|
14379
|
+
async function* parseSse(body) {
|
|
14380
|
+
const reader = body.getReader();
|
|
14381
|
+
const decoder = new TextDecoder();
|
|
14382
|
+
let buffer = "";
|
|
14383
|
+
try {
|
|
14384
|
+
while (true) {
|
|
14385
|
+
const { value, done } = await reader.read();
|
|
14386
|
+
if (done) break;
|
|
14387
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14388
|
+
let idx;
|
|
14389
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
14390
|
+
const rawLine = buffer.slice(0, idx).trim();
|
|
14391
|
+
buffer = buffer.slice(idx + 1);
|
|
14392
|
+
if (!rawLine.startsWith("data:")) continue;
|
|
14393
|
+
const payload = rawLine.slice(5).trim();
|
|
14394
|
+
if (!payload || payload === "[DONE]") continue;
|
|
14395
|
+
try {
|
|
14396
|
+
yield JSON.parse(payload);
|
|
14397
|
+
} catch {
|
|
14398
|
+
}
|
|
14399
|
+
}
|
|
14400
|
+
}
|
|
14401
|
+
} finally {
|
|
14402
|
+
reader.releaseLock();
|
|
14403
|
+
}
|
|
14404
|
+
}
|
|
14405
|
+
function extractDelta(chunk) {
|
|
14406
|
+
if (!chunk || typeof chunk !== "object") return null;
|
|
14407
|
+
const choices = chunk.choices;
|
|
14408
|
+
if (!choices || !choices.length) return null;
|
|
14409
|
+
return choices[0].delta?.content ?? null;
|
|
14410
|
+
}
|
|
14411
|
+
|
|
14412
|
+
// src/director/llm-director.ts
|
|
14413
|
+
async function directStoryboard(config, input) {
|
|
14414
|
+
const {
|
|
14415
|
+
endpointUrl,
|
|
14416
|
+
model,
|
|
14417
|
+
authToken,
|
|
14418
|
+
extraBody,
|
|
14419
|
+
maxTokens = 1024,
|
|
14420
|
+
temperature = 0.3,
|
|
14421
|
+
useJsonSchema = true,
|
|
14422
|
+
stream = false
|
|
14423
|
+
} = config;
|
|
14424
|
+
const userContent = buildUserPrompt(input);
|
|
14425
|
+
const body = {
|
|
14426
|
+
model,
|
|
14427
|
+
stream,
|
|
14428
|
+
temperature,
|
|
14429
|
+
max_tokens: maxTokens,
|
|
14430
|
+
messages: [
|
|
14431
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
14432
|
+
{ role: "user", content: userContent }
|
|
14433
|
+
],
|
|
14434
|
+
...extraBody ?? {}
|
|
14435
|
+
};
|
|
14436
|
+
if (useJsonSchema) {
|
|
14437
|
+
const validBlockIds = input.page.blocks.map((b) => b.block_id);
|
|
14438
|
+
const validCrossPageBlockIds = input.index.crossPageFigures.filter((f) => f.page !== input.pageNumber).map((f) => f.block_id);
|
|
14439
|
+
body.response_format = {
|
|
14440
|
+
type: "json_schema",
|
|
14441
|
+
json_schema: {
|
|
14442
|
+
name: "storyboard",
|
|
14443
|
+
strict: true,
|
|
14444
|
+
schema: storyboardJsonSchema({
|
|
14445
|
+
validBlockIds,
|
|
14446
|
+
validCrossPageBlockIds
|
|
14447
|
+
})
|
|
14448
|
+
}
|
|
14449
|
+
};
|
|
14450
|
+
}
|
|
14451
|
+
const headers = {
|
|
14452
|
+
"Content-Type": "application/json",
|
|
14453
|
+
Accept: stream ? "text/event-stream" : "application/json"
|
|
14454
|
+
};
|
|
14455
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
14456
|
+
const timeoutController = new AbortController();
|
|
14457
|
+
const timer = setTimeout(
|
|
14458
|
+
() => timeoutController.abort(),
|
|
14459
|
+
input.timeoutMs ?? 2500
|
|
14460
|
+
);
|
|
14461
|
+
const signal = mergeSignals(input.signal, timeoutController.signal);
|
|
14462
|
+
try {
|
|
14463
|
+
const response = await fetch(endpointUrl, {
|
|
14464
|
+
method: "POST",
|
|
14465
|
+
headers,
|
|
14466
|
+
body: JSON.stringify(body),
|
|
14467
|
+
signal
|
|
14468
|
+
});
|
|
14469
|
+
if (!response.ok || !response.body) {
|
|
14470
|
+
return {
|
|
14471
|
+
storyboard: null,
|
|
14472
|
+
raw: "",
|
|
14473
|
+
error: `HTTP ${response.status}`
|
|
14474
|
+
};
|
|
14475
|
+
}
|
|
14476
|
+
let raw = "";
|
|
14477
|
+
if (stream && response.body) {
|
|
14478
|
+
for await (const chunk of parseSse(response.body)) {
|
|
14479
|
+
const delta = extractDelta(chunk);
|
|
14480
|
+
if (delta) raw += delta;
|
|
14481
|
+
}
|
|
14482
|
+
} else {
|
|
14483
|
+
const json = await response.json();
|
|
14484
|
+
raw = json.choices?.[0]?.message?.content ?? "";
|
|
14485
|
+
}
|
|
14486
|
+
const stripped = collapseWhitespaceRuns(stripCodeFences(raw).trim());
|
|
14487
|
+
let parsed;
|
|
14488
|
+
try {
|
|
14489
|
+
parsed = JSON.parse(stripped);
|
|
14490
|
+
} catch (e) {
|
|
14491
|
+
return {
|
|
14492
|
+
storyboard: null,
|
|
14493
|
+
raw,
|
|
14494
|
+
error: `parse error: ${e.message}`
|
|
14495
|
+
};
|
|
14496
|
+
}
|
|
14497
|
+
const cleaned = clampNumericRanges(stripNullsDeep(parsed));
|
|
14498
|
+
const validation = StoryboardSchema.safeParse(cleaned);
|
|
14499
|
+
if (validation.success) {
|
|
14500
|
+
return {
|
|
14501
|
+
storyboard: enforceOverlayPresence(validation.data),
|
|
14502
|
+
raw
|
|
14503
|
+
};
|
|
14504
|
+
}
|
|
14505
|
+
const salvaged = salvageStoryboard(cleaned);
|
|
14506
|
+
if (salvaged) {
|
|
14507
|
+
return { storyboard: enforceOverlayPresence(salvaged), raw };
|
|
14508
|
+
}
|
|
14509
|
+
return {
|
|
14510
|
+
storyboard: null,
|
|
14511
|
+
raw,
|
|
14512
|
+
error: `validation failed: ${validation.error.message}`
|
|
14513
|
+
};
|
|
14514
|
+
} catch (e) {
|
|
14515
|
+
const name = e.name;
|
|
14516
|
+
const msg = name === "AbortError" ? "aborted" : e.message;
|
|
14517
|
+
return { storyboard: null, raw: "", error: msg };
|
|
14518
|
+
} finally {
|
|
14519
|
+
clearTimeout(timer);
|
|
14520
|
+
}
|
|
14521
|
+
}
|
|
14522
|
+
function stripCodeFences(s) {
|
|
14523
|
+
const m = s.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
14524
|
+
return m ? m[1] : s;
|
|
14525
|
+
}
|
|
14526
|
+
function collapseWhitespaceRuns(src) {
|
|
14527
|
+
let out = "";
|
|
14528
|
+
let inString = false;
|
|
14529
|
+
let escape = false;
|
|
14530
|
+
let run = 0;
|
|
14531
|
+
for (let i = 0; i < src.length; i++) {
|
|
14532
|
+
const c = src[i];
|
|
14533
|
+
if (inString) {
|
|
14534
|
+
out += c;
|
|
14535
|
+
if (escape) {
|
|
14536
|
+
escape = false;
|
|
14537
|
+
} else if (c === "\\") {
|
|
14538
|
+
escape = true;
|
|
14539
|
+
} else if (c === '"') {
|
|
14540
|
+
inString = false;
|
|
14541
|
+
}
|
|
14542
|
+
continue;
|
|
14543
|
+
}
|
|
14544
|
+
if (c === '"') {
|
|
14545
|
+
out += c;
|
|
14546
|
+
inString = true;
|
|
14547
|
+
run = 0;
|
|
14548
|
+
continue;
|
|
14549
|
+
}
|
|
14550
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
14551
|
+
run++;
|
|
14552
|
+
if (run <= 1) out += " ";
|
|
14553
|
+
continue;
|
|
14554
|
+
}
|
|
14555
|
+
run = 0;
|
|
14556
|
+
out += c;
|
|
14557
|
+
}
|
|
14558
|
+
return out;
|
|
14559
|
+
}
|
|
14560
|
+
function clampNumericRanges(input) {
|
|
14561
|
+
if (input === null || input === void 0) return input;
|
|
14562
|
+
if (Array.isArray(input)) return input.map(clampNumericRanges);
|
|
14563
|
+
if (typeof input !== "object") return input;
|
|
14564
|
+
const obj = input;
|
|
14565
|
+
const out = {};
|
|
14566
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
14567
|
+
out[k] = clampNumericRanges(v);
|
|
14568
|
+
}
|
|
14569
|
+
const type = typeof out.type === "string" ? out.type : void 0;
|
|
14570
|
+
if (type === "camera") {
|
|
14571
|
+
if (typeof out.scale === "number") out.scale = clamp(out.scale, 0.5, 4);
|
|
14572
|
+
if (typeof out.padding === "number") {
|
|
14573
|
+
out.padding = clamp(out.padding, 0, 400);
|
|
14574
|
+
}
|
|
14575
|
+
}
|
|
14576
|
+
if (typeof out.dim_opacity === "number") {
|
|
14577
|
+
out.dim_opacity = clamp(out.dim_opacity, 0, 1);
|
|
14578
|
+
}
|
|
14579
|
+
if (typeof out.feather_px === "number") {
|
|
14580
|
+
out.feather_px = clamp(out.feather_px, 0, 200);
|
|
14581
|
+
}
|
|
14582
|
+
if (typeof out.draw_duration_ms === "number") {
|
|
14583
|
+
out.draw_duration_ms = clamp(out.draw_duration_ms, 100, 3e3);
|
|
14584
|
+
}
|
|
14585
|
+
if (typeof out.count === "number") {
|
|
14586
|
+
out.count = Math.round(clamp(out.count, 1, 5));
|
|
14587
|
+
}
|
|
14588
|
+
if (typeof out.at_ms === "number") {
|
|
14589
|
+
out.at_ms = clamp(out.at_ms, 0, 5e3);
|
|
14590
|
+
}
|
|
14591
|
+
if (typeof out.duration_ms === "number" && type === void 0) {
|
|
14592
|
+
out.duration_ms = clamp(out.duration_ms, 100, 5e3);
|
|
14593
|
+
}
|
|
14594
|
+
return out;
|
|
14595
|
+
}
|
|
14596
|
+
function clamp(v, lo, hi) {
|
|
14597
|
+
return Math.min(hi, Math.max(lo, v));
|
|
14598
|
+
}
|
|
14599
|
+
function enforceOverlayPresence(sb) {
|
|
14600
|
+
if (sb.steps.length === 0) return sb;
|
|
14601
|
+
const hasOverlay = sb.steps.some(
|
|
14602
|
+
(s) => s.action.type !== "camera" && s.action.type !== "clear"
|
|
14603
|
+
);
|
|
14604
|
+
if (hasOverlay) return sb;
|
|
14605
|
+
const cameraStep = sb.steps.find((s) => s.action.type === "camera");
|
|
14606
|
+
if (!cameraStep || cameraStep.action.type !== "camera") return sb;
|
|
14607
|
+
const target = cameraStep.action.target_block;
|
|
14608
|
+
if (!target) return sb;
|
|
14609
|
+
return {
|
|
14610
|
+
...sb,
|
|
14611
|
+
reasoning: `${sb.reasoning} [auto-appended pulse: camera-only storyboards are forbidden]`,
|
|
14612
|
+
steps: [
|
|
14613
|
+
...sb.steps,
|
|
14614
|
+
{
|
|
14615
|
+
at_ms: Math.min(4800, (cameraStep.at_ms ?? 0) + 200),
|
|
14616
|
+
duration_ms: 900,
|
|
14617
|
+
action: {
|
|
14618
|
+
type: "pulse",
|
|
14619
|
+
target_block: target,
|
|
14620
|
+
count: 2,
|
|
14621
|
+
intensity: "normal"
|
|
14622
|
+
}
|
|
14623
|
+
}
|
|
14624
|
+
]
|
|
14625
|
+
};
|
|
14626
|
+
}
|
|
14627
|
+
function stripNullsDeep(input) {
|
|
14628
|
+
if (input === null) return void 0;
|
|
14629
|
+
if (Array.isArray(input)) {
|
|
14630
|
+
return input.map(stripNullsDeep).filter((v) => v !== void 0);
|
|
14631
|
+
}
|
|
14632
|
+
if (input && typeof input === "object") {
|
|
14633
|
+
const out = {};
|
|
14634
|
+
for (const [k, v] of Object.entries(input)) {
|
|
14635
|
+
const cleaned = stripNullsDeep(v);
|
|
14636
|
+
if (cleaned !== void 0) out[k] = cleaned;
|
|
14637
|
+
}
|
|
14638
|
+
return out;
|
|
14639
|
+
}
|
|
14640
|
+
return input;
|
|
14641
|
+
}
|
|
14642
|
+
function salvageStoryboard(parsed) {
|
|
14643
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
14644
|
+
const obj = parsed;
|
|
14645
|
+
if (!Array.isArray(obj.steps)) return null;
|
|
14646
|
+
const goodSteps = [];
|
|
14647
|
+
for (const step of obj.steps) {
|
|
14648
|
+
const r = StoryboardStepSchema.safeParse(step);
|
|
14649
|
+
if (r.success) goodSteps.push(r.data);
|
|
14650
|
+
if (goodSteps.length >= 4) break;
|
|
14651
|
+
}
|
|
14652
|
+
if (goodSteps.length === 0) return null;
|
|
14653
|
+
return {
|
|
14654
|
+
version: 1,
|
|
14655
|
+
reasoning: typeof obj.reasoning === "string" ? obj.reasoning + " (salvaged)" : "salvaged",
|
|
14656
|
+
steps: goodSteps
|
|
14657
|
+
};
|
|
14658
|
+
}
|
|
14659
|
+
function mergeSignals(a, b) {
|
|
14660
|
+
if (!a) return b;
|
|
14661
|
+
if (!b) return a;
|
|
14662
|
+
const ctrl = new AbortController();
|
|
14663
|
+
const onAbort = () => ctrl.abort();
|
|
14664
|
+
a.addEventListener("abort", onAbort);
|
|
14665
|
+
b.addEventListener("abort", onAbort);
|
|
14666
|
+
return ctrl.signal;
|
|
14667
|
+
}
|
|
14668
|
+
|
|
14669
|
+
// src/director/embedding-fallback.ts
|
|
14670
|
+
function cosineSimilarity(a, b) {
|
|
14671
|
+
let dot = 0;
|
|
14672
|
+
let na = 0;
|
|
14673
|
+
let nb = 0;
|
|
14674
|
+
const n = Math.min(a.length, b.length);
|
|
14675
|
+
for (let i = 0; i < n; i++) {
|
|
14676
|
+
dot += a[i] * b[i];
|
|
14677
|
+
na += a[i] * a[i];
|
|
14678
|
+
nb += b[i] * b[i];
|
|
14679
|
+
}
|
|
14680
|
+
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
|
14681
|
+
return denom === 0 ? 0 : dot / denom;
|
|
14682
|
+
}
|
|
14683
|
+
async function matchChunkToBlock(chunk, page, provider) {
|
|
14684
|
+
const textBlocks = page.blocks.filter(
|
|
14685
|
+
(b) => typeof b.text === "string" && b.text.trim().length > 0
|
|
14686
|
+
);
|
|
14687
|
+
if (textBlocks.length === 0) return null;
|
|
14688
|
+
const inputs = [chunk, ...textBlocks.map((b) => b.text)];
|
|
14689
|
+
const embeds = await provider.embed(inputs);
|
|
14690
|
+
if (embeds.length < 2) return null;
|
|
14691
|
+
const chunkEmbed = embeds[0];
|
|
14692
|
+
let best = null;
|
|
14693
|
+
for (let i = 0; i < textBlocks.length; i++) {
|
|
14694
|
+
const score = cosineSimilarity(chunkEmbed, embeds[i + 1]);
|
|
14695
|
+
if (!best || score > best.score) best = { block: textBlocks[i], score };
|
|
14696
|
+
}
|
|
14697
|
+
return best;
|
|
14698
|
+
}
|
|
14699
|
+
function nearestFigureOnPage(caption, page) {
|
|
14700
|
+
if (!page) return null;
|
|
14701
|
+
const [cx1, cy1, cx2, cy2] = caption.bbox;
|
|
14702
|
+
const ccx = (cx1 + cx2) / 2;
|
|
14703
|
+
const ccy = (cy1 + cy2) / 2;
|
|
14704
|
+
let best = null;
|
|
14705
|
+
for (const b of page.blocks) {
|
|
14706
|
+
if (b.block_id === caption.block_id) continue;
|
|
14707
|
+
if (b.type !== "figure" && b.type !== "figure_region") continue;
|
|
14708
|
+
const [x1, y1, x2, y2] = b.bbox;
|
|
14709
|
+
const fx = (x1 + x2) / 2;
|
|
14710
|
+
const fy = (y1 + y2) / 2;
|
|
14711
|
+
const dist = Math.hypot(fx - ccx, fy - ccy);
|
|
14712
|
+
if (!best || dist < best.dist) best = { block: b, dist };
|
|
14713
|
+
}
|
|
14714
|
+
return best?.block ?? null;
|
|
14715
|
+
}
|
|
14716
|
+
function truncateLabel(text, max) {
|
|
14717
|
+
if (!text) return "";
|
|
14718
|
+
const clean = text.replace(/\s+/g, " ").trim();
|
|
14719
|
+
if (clean.length <= max) return clean;
|
|
14720
|
+
return clean.slice(0, max - 1) + "\u2026";
|
|
14721
|
+
}
|
|
14722
|
+
function storyboardFromMatch(match, page) {
|
|
14723
|
+
if (!match) {
|
|
14724
|
+
return {
|
|
14725
|
+
version: 1,
|
|
14726
|
+
reasoning: "fallback: no match \u2014 clearing overlays",
|
|
14727
|
+
steps: [
|
|
14728
|
+
{
|
|
14729
|
+
at_ms: 0,
|
|
14730
|
+
duration_ms: 800,
|
|
14731
|
+
action: { type: "clear", targets: "overlays" }
|
|
14732
|
+
}
|
|
14733
|
+
]
|
|
14734
|
+
};
|
|
14735
|
+
}
|
|
14736
|
+
const { block } = match;
|
|
14737
|
+
const id = block.block_id;
|
|
14738
|
+
const reason = `fallback (block.type=${block.type}): matched ${id} (${match.score.toFixed(2)})`;
|
|
14739
|
+
switch (block.type) {
|
|
14740
|
+
case "heading": {
|
|
14741
|
+
return {
|
|
14742
|
+
version: 1,
|
|
14743
|
+
reasoning: reason,
|
|
14744
|
+
steps: [
|
|
14745
|
+
{
|
|
14746
|
+
at_ms: 0,
|
|
14747
|
+
duration_ms: 700,
|
|
14748
|
+
action: {
|
|
14749
|
+
type: "spotlight",
|
|
14750
|
+
target_block: id,
|
|
14751
|
+
dim_opacity: 0.6,
|
|
14752
|
+
feather_px: 40,
|
|
14753
|
+
shape: "rounded"
|
|
14754
|
+
}
|
|
14755
|
+
},
|
|
14756
|
+
{
|
|
14757
|
+
at_ms: 300,
|
|
14758
|
+
duration_ms: 1200,
|
|
14759
|
+
action: {
|
|
14760
|
+
type: "label",
|
|
14761
|
+
target_block: id,
|
|
14762
|
+
text: truncateLabel(block.text, 32) || "section",
|
|
14763
|
+
position: "top"
|
|
14764
|
+
}
|
|
14765
|
+
}
|
|
14766
|
+
]
|
|
14767
|
+
};
|
|
14768
|
+
}
|
|
14769
|
+
case "paragraph": {
|
|
14770
|
+
return {
|
|
14771
|
+
version: 1,
|
|
14772
|
+
reasoning: reason,
|
|
14773
|
+
steps: [
|
|
14774
|
+
{
|
|
14775
|
+
at_ms: 0,
|
|
14776
|
+
duration_ms: 600,
|
|
14777
|
+
action: {
|
|
14778
|
+
type: "camera",
|
|
14779
|
+
target_block: id,
|
|
14780
|
+
scale: 1.1,
|
|
14781
|
+
padding: 80,
|
|
14782
|
+
easing: "ease-out"
|
|
14783
|
+
}
|
|
14784
|
+
},
|
|
14785
|
+
{
|
|
14786
|
+
at_ms: 300,
|
|
14787
|
+
duration_ms: 900,
|
|
14788
|
+
action: {
|
|
14789
|
+
type: "underline",
|
|
14790
|
+
target_block: id,
|
|
14791
|
+
color: "#FBBF24",
|
|
14792
|
+
style: "sketch",
|
|
14793
|
+
draw_duration_ms: 800
|
|
14794
|
+
}
|
|
14795
|
+
}
|
|
14796
|
+
]
|
|
14797
|
+
};
|
|
14798
|
+
}
|
|
14799
|
+
case "list_item":
|
|
14800
|
+
case "mcq_option": {
|
|
14801
|
+
return {
|
|
14802
|
+
version: 1,
|
|
14803
|
+
reasoning: reason,
|
|
14804
|
+
steps: [
|
|
14805
|
+
{
|
|
14806
|
+
at_ms: 0,
|
|
14807
|
+
duration_ms: 500,
|
|
14808
|
+
action: {
|
|
14809
|
+
type: "highlight",
|
|
14810
|
+
target_block: id,
|
|
14811
|
+
color: "rgba(250, 204, 21, 0.35)",
|
|
14812
|
+
draw_duration_ms: 450
|
|
14813
|
+
}
|
|
14814
|
+
}
|
|
14815
|
+
]
|
|
14816
|
+
};
|
|
14817
|
+
}
|
|
14818
|
+
case "caption": {
|
|
14819
|
+
const figure = nearestFigureOnPage(block, page);
|
|
14820
|
+
if (figure) {
|
|
14821
|
+
return {
|
|
14822
|
+
version: 1,
|
|
14823
|
+
reasoning: `${reason}; caption \u2192 figure ${figure.block_id}`,
|
|
14824
|
+
steps: [
|
|
14825
|
+
{
|
|
14826
|
+
at_ms: 0,
|
|
14827
|
+
duration_ms: 900,
|
|
14828
|
+
action: {
|
|
14829
|
+
type: "callout",
|
|
14830
|
+
from_block: id,
|
|
14831
|
+
to_block: figure.block_id,
|
|
14832
|
+
label: "see",
|
|
14833
|
+
curve: "curved"
|
|
14834
|
+
}
|
|
14835
|
+
},
|
|
14836
|
+
{
|
|
14837
|
+
at_ms: 600,
|
|
14838
|
+
duration_ms: 1e3,
|
|
14839
|
+
action: {
|
|
14840
|
+
type: "pulse",
|
|
14841
|
+
target_block: figure.block_id,
|
|
14842
|
+
count: 2,
|
|
14843
|
+
intensity: "normal"
|
|
14844
|
+
}
|
|
14845
|
+
}
|
|
14846
|
+
]
|
|
14847
|
+
};
|
|
14848
|
+
}
|
|
14849
|
+
return {
|
|
14850
|
+
version: 1,
|
|
14851
|
+
reasoning: `${reason}; no figure on page, underlining caption`,
|
|
14852
|
+
steps: [
|
|
14853
|
+
{
|
|
14854
|
+
at_ms: 0,
|
|
14855
|
+
duration_ms: 800,
|
|
14856
|
+
action: {
|
|
14857
|
+
type: "underline",
|
|
14858
|
+
target_block: id,
|
|
14859
|
+
color: "#FBBF24",
|
|
14860
|
+
style: "sketch",
|
|
14861
|
+
draw_duration_ms: 700
|
|
14862
|
+
}
|
|
14863
|
+
}
|
|
14864
|
+
]
|
|
14865
|
+
};
|
|
14866
|
+
}
|
|
14867
|
+
case "figure": {
|
|
14868
|
+
return {
|
|
14869
|
+
version: 1,
|
|
14870
|
+
reasoning: reason,
|
|
14871
|
+
steps: [
|
|
14872
|
+
{
|
|
14873
|
+
at_ms: 0,
|
|
14874
|
+
duration_ms: 900,
|
|
14875
|
+
action: {
|
|
14876
|
+
type: "pulse",
|
|
14877
|
+
target_block: id,
|
|
14878
|
+
count: 2,
|
|
14879
|
+
intensity: "strong"
|
|
14880
|
+
}
|
|
14881
|
+
},
|
|
14882
|
+
{
|
|
14883
|
+
at_ms: 400,
|
|
14884
|
+
duration_ms: 1200,
|
|
14885
|
+
action: {
|
|
14886
|
+
type: "box",
|
|
14887
|
+
target_block: id,
|
|
14888
|
+
color: "#3B82F6",
|
|
14889
|
+
style: "solid"
|
|
14890
|
+
}
|
|
14891
|
+
}
|
|
14892
|
+
]
|
|
14893
|
+
};
|
|
14894
|
+
}
|
|
14895
|
+
case "figure_region": {
|
|
14896
|
+
return {
|
|
14897
|
+
version: 1,
|
|
14898
|
+
reasoning: reason,
|
|
14899
|
+
steps: [
|
|
14900
|
+
{
|
|
14901
|
+
at_ms: 0,
|
|
14902
|
+
duration_ms: 900,
|
|
14903
|
+
action: {
|
|
14904
|
+
type: "pulse",
|
|
14905
|
+
target_block: id,
|
|
14906
|
+
count: 2,
|
|
14907
|
+
intensity: "normal"
|
|
14908
|
+
}
|
|
14909
|
+
}
|
|
14910
|
+
]
|
|
14911
|
+
};
|
|
14912
|
+
}
|
|
14913
|
+
case "table": {
|
|
14914
|
+
return {
|
|
14915
|
+
version: 1,
|
|
14916
|
+
reasoning: reason,
|
|
14917
|
+
steps: [
|
|
14918
|
+
{
|
|
14919
|
+
at_ms: 0,
|
|
14920
|
+
duration_ms: 700,
|
|
14921
|
+
action: {
|
|
14922
|
+
type: "camera",
|
|
14923
|
+
target_block: id,
|
|
14924
|
+
scale: 1.2,
|
|
14925
|
+
padding: 60,
|
|
14926
|
+
easing: "ease-out"
|
|
14927
|
+
}
|
|
14928
|
+
},
|
|
14929
|
+
{
|
|
14930
|
+
at_ms: 300,
|
|
14931
|
+
duration_ms: 1e3,
|
|
14932
|
+
action: {
|
|
14933
|
+
type: "box",
|
|
14934
|
+
target_block: id,
|
|
14935
|
+
color: "#3B82F6",
|
|
14936
|
+
style: "dashed"
|
|
14937
|
+
}
|
|
14938
|
+
}
|
|
14939
|
+
]
|
|
14940
|
+
};
|
|
14941
|
+
}
|
|
14942
|
+
default: {
|
|
14943
|
+
return {
|
|
14944
|
+
version: 1,
|
|
14945
|
+
reasoning: `${reason}; unknown block.type, using highlight`,
|
|
14946
|
+
steps: [
|
|
14947
|
+
{
|
|
14948
|
+
at_ms: 0,
|
|
14949
|
+
duration_ms: 600,
|
|
14950
|
+
action: {
|
|
14951
|
+
type: "highlight",
|
|
14952
|
+
target_block: id,
|
|
14953
|
+
color: "rgba(250, 204, 21, 0.35)",
|
|
14954
|
+
draw_duration_ms: 500
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
]
|
|
14958
|
+
};
|
|
14959
|
+
}
|
|
14960
|
+
}
|
|
14961
|
+
}
|
|
14962
|
+
|
|
14963
|
+
// src/components/TutorMode/TutorModeContainer.tsx
|
|
14964
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
14965
|
+
function buildBBoxIndex(bboxData) {
|
|
14966
|
+
const byPage = /* @__PURE__ */ new Map();
|
|
14967
|
+
const blockById = /* @__PURE__ */ new Map();
|
|
14968
|
+
const crossPageFigures = [];
|
|
14969
|
+
for (const page of bboxData) {
|
|
14970
|
+
byPage.set(page.page_number, page);
|
|
14971
|
+
for (const block of page.blocks) {
|
|
14972
|
+
blockById.set(block.block_id, { block, pageNumber: page.page_number });
|
|
14973
|
+
if ((block.type === "figure" || block.type === "figure_region" || block.type === "caption") && typeof block.text === "string" && block.text.length > 0) {
|
|
14974
|
+
crossPageFigures.push({
|
|
14975
|
+
block_id: block.block_id,
|
|
14976
|
+
page: page.page_number,
|
|
14977
|
+
type: block.type,
|
|
14978
|
+
text: block.text
|
|
14979
|
+
});
|
|
14980
|
+
}
|
|
14981
|
+
}
|
|
14982
|
+
}
|
|
14983
|
+
return { byPage, blockById, crossPageFigures };
|
|
14984
|
+
}
|
|
14985
|
+
function TutorModeContainer({
|
|
14986
|
+
pageNumber,
|
|
14987
|
+
bboxData,
|
|
14988
|
+
narrationStore,
|
|
14989
|
+
scale,
|
|
14990
|
+
rotation = 0,
|
|
14991
|
+
currentChunk,
|
|
14992
|
+
llm,
|
|
14993
|
+
idleTimeoutMs = 5e3,
|
|
14994
|
+
llmTimeoutMs = 3e4,
|
|
14995
|
+
embeddingProvider,
|
|
14996
|
+
showSubtitles = false,
|
|
14997
|
+
showExitButton = true,
|
|
14998
|
+
onExitTutorMode,
|
|
14999
|
+
minOverlayDurationMs,
|
|
15000
|
+
className
|
|
15001
|
+
}) {
|
|
15002
|
+
const containerRef = (0, import_react56.useRef)(null);
|
|
15003
|
+
const index = (0, import_react56.useMemo)(() => buildBBoxIndex(bboxData), [bboxData]);
|
|
15004
|
+
const { document: document2 } = usePDFViewer();
|
|
15005
|
+
const [pageProxy, setPageProxy] = (0, import_react56.useState)(null);
|
|
15006
|
+
const [viewport, setViewport] = (0, import_react56.useState)({ width: 800, height: 1e3 });
|
|
15007
|
+
const camera = (0, import_zustand2.useStore)(narrationStore, (s) => s.camera);
|
|
15008
|
+
const activeOverlays = (0, import_zustand2.useStore)(narrationStore, (s) => s.activeOverlays);
|
|
15009
|
+
(0, import_react56.useEffect)(() => {
|
|
15010
|
+
if (!containerRef.current) return;
|
|
15011
|
+
const el = containerRef.current;
|
|
15012
|
+
const update = () => setViewport({ width: el.clientWidth, height: el.clientHeight });
|
|
15013
|
+
update();
|
|
15014
|
+
const ro = new ResizeObserver(update);
|
|
15015
|
+
ro.observe(el);
|
|
15016
|
+
return () => ro.disconnect();
|
|
15017
|
+
}, []);
|
|
15018
|
+
(0, import_react56.useEffect)(() => {
|
|
15019
|
+
if (!document2) {
|
|
15020
|
+
setPageProxy(null);
|
|
15021
|
+
return;
|
|
15022
|
+
}
|
|
15023
|
+
let cancelled = false;
|
|
15024
|
+
document2.getPage(pageNumber).then((p) => {
|
|
15025
|
+
if (!cancelled) setPageProxy(p);
|
|
15026
|
+
}).catch(() => {
|
|
15027
|
+
if (!cancelled) setPageProxy(null);
|
|
15028
|
+
});
|
|
15029
|
+
return () => {
|
|
15030
|
+
cancelled = true;
|
|
15031
|
+
};
|
|
15032
|
+
}, [document2, pageNumber]);
|
|
15033
|
+
(0, import_react56.useEffect)(() => {
|
|
15034
|
+
narrationStore.getState().setCurrentPage(pageNumber);
|
|
15035
|
+
}, [pageNumber, narrationStore]);
|
|
15036
|
+
(0, import_react56.useEffect)(() => {
|
|
15037
|
+
const page2 = index.byPage.get(pageNumber);
|
|
15038
|
+
if (!page2) return;
|
|
15039
|
+
if (viewport.width === 0 || viewport.height === 0) return;
|
|
15040
|
+
if (narrationStore.getState().activeOverlays.length > 0) return;
|
|
15041
|
+
const fit = Math.min(
|
|
15042
|
+
viewport.width / page2.page_dimensions.width,
|
|
15043
|
+
viewport.height / page2.page_dimensions.height
|
|
15044
|
+
) * 0.95;
|
|
15045
|
+
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0 });
|
|
15046
|
+
}, [pageNumber, viewport, index, narrationStore]);
|
|
15047
|
+
const engineRef = (0, import_react56.useRef)(null);
|
|
15048
|
+
(0, import_react56.useEffect)(() => {
|
|
15049
|
+
engineRef.current = new StoryboardEngine({
|
|
15050
|
+
narrationStore,
|
|
15051
|
+
bboxIndex: index,
|
|
15052
|
+
getViewport: () => viewport,
|
|
15053
|
+
minOverlayDurationMs
|
|
15054
|
+
});
|
|
15055
|
+
return () => engineRef.current?.cancelPending();
|
|
15056
|
+
}, [narrationStore, index, viewport, minOverlayDurationMs]);
|
|
15057
|
+
const abortRef = (0, import_react56.useRef)(null);
|
|
15058
|
+
const debounceRef = (0, import_react56.useRef)(null);
|
|
15059
|
+
const lastChunkRef = (0, import_react56.useRef)(null);
|
|
15060
|
+
(0, import_react56.useEffect)(() => {
|
|
15061
|
+
if (!llm) return;
|
|
15062
|
+
if (!currentChunk || currentChunk === lastChunkRef.current) return;
|
|
15063
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
15064
|
+
debounceRef.current = setTimeout(async () => {
|
|
15065
|
+
const chunk = currentChunk;
|
|
15066
|
+
if (chunk === lastChunkRef.current) return;
|
|
15067
|
+
lastChunkRef.current = chunk;
|
|
15068
|
+
const page2 = index.byPage.get(pageNumber);
|
|
15069
|
+
if (!page2) return;
|
|
15070
|
+
narrationStore.getState().pushChunkHistory({
|
|
15071
|
+
text: chunk,
|
|
15072
|
+
pageNumber,
|
|
15073
|
+
timestamp: Date.now()
|
|
15074
|
+
});
|
|
15075
|
+
narrationStore.getState().appendDebugEvent({
|
|
15076
|
+
kind: "chunk",
|
|
15077
|
+
summary: `chunk \u2192 ${chunk.slice(0, 80)}${chunk.length > 80 ? "\u2026" : ""}`,
|
|
15078
|
+
payload: { chunk, pageNumber }
|
|
15079
|
+
});
|
|
15080
|
+
abortRef.current?.abort();
|
|
15081
|
+
abortRef.current = new AbortController();
|
|
15082
|
+
narrationStore.getState().setLlmStatus("in-flight");
|
|
15083
|
+
narrationStore.getState().appendDebugEvent({
|
|
15084
|
+
kind: "llm-request",
|
|
15085
|
+
summary: `LLM ${llm.model} (page ${pageNumber}, ${page2.blocks.length} blocks)`,
|
|
15086
|
+
payload: { model: llm.model, pageNumber, blockCount: page2.blocks.length }
|
|
15087
|
+
});
|
|
15088
|
+
const result = await directStoryboard(llm, {
|
|
15089
|
+
chunk,
|
|
15090
|
+
pageNumber,
|
|
15091
|
+
page: page2,
|
|
15092
|
+
index,
|
|
15093
|
+
history: narrationStore.getState().chunkHistory,
|
|
15094
|
+
camera: narrationStore.getState().camera,
|
|
15095
|
+
activeOverlays: narrationStore.getState().activeOverlays,
|
|
15096
|
+
signal: abortRef.current.signal,
|
|
15097
|
+
timeoutMs: llmTimeoutMs
|
|
15098
|
+
});
|
|
15099
|
+
if (result.storyboard) {
|
|
15100
|
+
narrationStore.getState().setLlmStatus("idle");
|
|
15101
|
+
narrationStore.getState().appendDebugEvent({
|
|
15102
|
+
kind: "llm-response",
|
|
15103
|
+
summary: `storyboard \u2713 ${result.storyboard.steps.length} steps \u2014 ${result.storyboard.reasoning.slice(0, 60)}`,
|
|
15104
|
+
payload: { raw: result.raw, storyboard: result.storyboard }
|
|
15105
|
+
});
|
|
15106
|
+
engineRef.current?.execute(result.storyboard);
|
|
15107
|
+
narrationStore.getState().appendDebugEvent({
|
|
15108
|
+
kind: "storyboard-execute",
|
|
15109
|
+
summary: `engine executing ${result.storyboard.steps.length} steps`,
|
|
15110
|
+
payload: result.storyboard.steps.map((s) => ({
|
|
15111
|
+
at_ms: s.at_ms,
|
|
15112
|
+
type: s.action.type,
|
|
15113
|
+
target: "target_block" in s.action ? s.action.target_block : "target" in s.action ? s.action.target : void 0
|
|
15114
|
+
}))
|
|
15115
|
+
});
|
|
15116
|
+
} else {
|
|
15117
|
+
narrationStore.getState().setLlmStatus("failed", result.error ?? "unknown");
|
|
15118
|
+
narrationStore.getState().appendDebugEvent({
|
|
15119
|
+
kind: "llm-error",
|
|
15120
|
+
summary: `LLM failed: ${(result.error ?? "unknown").slice(0, 80)}`,
|
|
15121
|
+
payload: { error: result.error, raw: result.raw }
|
|
15122
|
+
});
|
|
15123
|
+
if (embeddingProvider) {
|
|
15124
|
+
try {
|
|
15125
|
+
const match = await matchChunkToBlock(chunk, page2, embeddingProvider);
|
|
15126
|
+
const fallbackSb = storyboardFromMatch(match, page2);
|
|
15127
|
+
narrationStore.getState().appendDebugEvent({
|
|
15128
|
+
kind: "fallback-fired",
|
|
15129
|
+
summary: `embedding fallback \u2192 ${match?.block.block_id ?? "no match"}`,
|
|
15130
|
+
payload: { match, storyboard: fallbackSb }
|
|
15131
|
+
});
|
|
15132
|
+
engineRef.current?.execute(fallbackSb);
|
|
15133
|
+
} catch (e) {
|
|
15134
|
+
narrationStore.getState().appendDebugEvent({
|
|
15135
|
+
kind: "llm-error",
|
|
15136
|
+
summary: `fallback also failed: ${e.message}`,
|
|
15137
|
+
payload: e
|
|
15138
|
+
});
|
|
15139
|
+
}
|
|
15140
|
+
}
|
|
15141
|
+
}
|
|
15142
|
+
}, 200);
|
|
15143
|
+
return () => {
|
|
15144
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
15145
|
+
};
|
|
15146
|
+
}, [currentChunk, llm, index, pageNumber, narrationStore, embeddingProvider, llmTimeoutMs]);
|
|
15147
|
+
(0, import_react56.useEffect)(() => {
|
|
15148
|
+
if (!currentChunk) return;
|
|
15149
|
+
const t = setTimeout(() => {
|
|
15150
|
+
if (!engineRef.current) return;
|
|
15151
|
+
const hist = narrationStore.getState().chunkHistory;
|
|
15152
|
+
const latest = hist.length > 0 ? hist[hist.length - 1] : null;
|
|
15153
|
+
if (!latest) return;
|
|
15154
|
+
if (Date.now() - latest.timestamp < idleTimeoutMs) return;
|
|
15155
|
+
engineRef.current.resetVisuals();
|
|
15156
|
+
}, idleTimeoutMs + 100);
|
|
15157
|
+
return () => clearTimeout(t);
|
|
15158
|
+
}, [currentChunk, idleTimeoutMs, narrationStore]);
|
|
15159
|
+
const page = index.byPage.get(pageNumber);
|
|
15160
|
+
const dpiScale = page ? page.page_dimensions.dpi / 72 : 1;
|
|
15161
|
+
const rasterScale = dpiScale * (scale || 1);
|
|
15162
|
+
const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
|
|
15163
|
+
const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
|
|
15164
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
15165
|
+
"div",
|
|
15166
|
+
{
|
|
15167
|
+
ref: containerRef,
|
|
15168
|
+
className,
|
|
15169
|
+
style: {
|
|
15170
|
+
position: "relative",
|
|
15171
|
+
width: "100%",
|
|
15172
|
+
height: "100%",
|
|
15173
|
+
overflow: "hidden",
|
|
15174
|
+
background: "#111"
|
|
15175
|
+
},
|
|
15176
|
+
"data-role": "tutor-mode-container",
|
|
15177
|
+
"data-page-loaded": page ? "true" : "false",
|
|
15178
|
+
children: [
|
|
15179
|
+
showExitButton ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
15180
|
+
"button",
|
|
15181
|
+
{
|
|
15182
|
+
onClick: () => {
|
|
15183
|
+
engineRef.current?.resetVisuals();
|
|
15184
|
+
onExitTutorMode?.();
|
|
15185
|
+
},
|
|
15186
|
+
style: {
|
|
15187
|
+
position: "absolute",
|
|
15188
|
+
top: 12,
|
|
15189
|
+
right: 12,
|
|
15190
|
+
zIndex: 60,
|
|
15191
|
+
minHeight: 40,
|
|
15192
|
+
minWidth: 40,
|
|
15193
|
+
padding: "8px 14px",
|
|
15194
|
+
border: "none",
|
|
15195
|
+
borderRadius: 8,
|
|
15196
|
+
background: "rgba(255,255,255,0.12)",
|
|
15197
|
+
color: "white",
|
|
15198
|
+
cursor: "pointer",
|
|
15199
|
+
fontFamily: "system-ui, sans-serif",
|
|
15200
|
+
fontSize: 14,
|
|
15201
|
+
touchAction: "manipulation"
|
|
15202
|
+
},
|
|
15203
|
+
"aria-label": "Reset view \u2014 clear overlays and fit the page",
|
|
15204
|
+
"data-role": "exit-tutor",
|
|
15205
|
+
children: "Reset view"
|
|
15206
|
+
}
|
|
15207
|
+
) : null,
|
|
15208
|
+
page ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(CameraView, { camera, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
15209
|
+
"div",
|
|
15210
|
+
{
|
|
15211
|
+
style: {
|
|
15212
|
+
position: "absolute",
|
|
15213
|
+
top: "50%",
|
|
15214
|
+
left: "50%",
|
|
15215
|
+
width: baseW,
|
|
15216
|
+
height: baseH,
|
|
15217
|
+
transform: "translate(-50%, -50%)"
|
|
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
|
+
]
|
|
15242
|
+
}
|
|
15243
|
+
) }) : null,
|
|
15244
|
+
showSubtitles ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SubtitleBar, { text: currentChunk ?? null }) : null
|
|
15245
|
+
]
|
|
15246
|
+
}
|
|
15247
|
+
);
|
|
15248
|
+
}
|
|
15249
|
+
|
|
15250
|
+
// src/director/transformers-embedding.ts
|
|
15251
|
+
var loaded = null;
|
|
15252
|
+
function getLocalMiniLM() {
|
|
15253
|
+
if (loaded) return loaded;
|
|
15254
|
+
loaded = (async () => {
|
|
15255
|
+
const mod = await import(
|
|
15256
|
+
/* webpackIgnore: true */
|
|
15257
|
+
"@xenova/transformers"
|
|
15258
|
+
);
|
|
15259
|
+
const { pipeline } = mod;
|
|
15260
|
+
const extractor = await pipeline(
|
|
15261
|
+
"feature-extraction",
|
|
15262
|
+
"Xenova/all-MiniLM-L6-v2"
|
|
15263
|
+
);
|
|
15264
|
+
return {
|
|
15265
|
+
async embed(texts) {
|
|
15266
|
+
const out = [];
|
|
15267
|
+
for (const t of texts) {
|
|
15268
|
+
const result = await extractor(t, {
|
|
15269
|
+
pooling: "mean",
|
|
15270
|
+
normalize: true
|
|
15271
|
+
});
|
|
15272
|
+
out.push(new Float32Array(result.data.slice()));
|
|
15273
|
+
}
|
|
15274
|
+
return out;
|
|
15275
|
+
}
|
|
15276
|
+
};
|
|
15277
|
+
})();
|
|
15278
|
+
return loaded;
|
|
15279
|
+
}
|
|
15280
|
+
|
|
12686
15281
|
// src/index.ts
|
|
12687
15282
|
init_hooks();
|
|
12688
15283
|
init_store();
|
|
@@ -12694,18 +15289,26 @@ init_PluginManager();
|
|
|
12694
15289
|
init_utils();
|
|
12695
15290
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12696
15291
|
0 && (module.exports = {
|
|
15292
|
+
AnimatedHighlight,
|
|
15293
|
+
AnimatedUnderline,
|
|
12697
15294
|
AnnotationLayer,
|
|
12698
15295
|
AnnotationToolbar,
|
|
12699
15296
|
AskAboutOverlay,
|
|
12700
15297
|
AskAboutTrigger,
|
|
15298
|
+
BookModeContainer,
|
|
12701
15299
|
BookmarksPanel,
|
|
15300
|
+
BoxOverlay,
|
|
15301
|
+
CalloutArrow,
|
|
15302
|
+
CameraView,
|
|
12702
15303
|
CanvasLayer,
|
|
15304
|
+
CinemaLayer,
|
|
12703
15305
|
ContinuousScrollContainer,
|
|
12704
15306
|
DocumentContainer,
|
|
12705
15307
|
DrawingCanvas,
|
|
12706
15308
|
DualPageContainer,
|
|
12707
15309
|
FloatingZoomControls,
|
|
12708
15310
|
FocusRegionLayer,
|
|
15311
|
+
GhostReference,
|
|
12709
15312
|
HighlightLayer,
|
|
12710
15313
|
HighlightPopover,
|
|
12711
15314
|
HighlightsPanel,
|
|
@@ -12722,32 +15325,46 @@ init_utils();
|
|
|
12722
15325
|
PDFViewerContext,
|
|
12723
15326
|
PDFViewerProvider,
|
|
12724
15327
|
PluginManager,
|
|
15328
|
+
PulseOverlay,
|
|
12725
15329
|
QuickNoteButton,
|
|
12726
15330
|
QuickNotePopover,
|
|
15331
|
+
SYSTEM_PROMPT,
|
|
12727
15332
|
SearchPanel,
|
|
12728
15333
|
SelectionToolbar,
|
|
12729
15334
|
ShapePreview,
|
|
12730
15335
|
ShapeRenderer,
|
|
12731
15336
|
Sidebar,
|
|
15337
|
+
SpotlightMask,
|
|
15338
|
+
StickyLabel,
|
|
12732
15339
|
StickyNote,
|
|
15340
|
+
StoryboardActionSchema,
|
|
15341
|
+
StoryboardEngine,
|
|
15342
|
+
StoryboardSchema,
|
|
15343
|
+
SubtitleBar,
|
|
12733
15344
|
TakeawaysPanel,
|
|
12734
15345
|
TextLayer,
|
|
12735
15346
|
ThumbnailPanel,
|
|
12736
15347
|
Toolbar,
|
|
15348
|
+
TutorModeContainer,
|
|
12737
15349
|
VirtualizedDocumentContainer,
|
|
12738
15350
|
applyRotation,
|
|
15351
|
+
buildBBoxIndex,
|
|
15352
|
+
buildUserPrompt,
|
|
12739
15353
|
clearHighlights,
|
|
12740
15354
|
clearStudentData,
|
|
12741
15355
|
cn,
|
|
15356
|
+
cosineSimilarity,
|
|
12742
15357
|
countTextOnPage,
|
|
12743
15358
|
createAgentAPI,
|
|
12744
15359
|
createAgentStore,
|
|
12745
15360
|
createAnnotationStore,
|
|
15361
|
+
createNarrationStore,
|
|
12746
15362
|
createPDFViewer,
|
|
12747
15363
|
createPluginManager,
|
|
12748
15364
|
createSearchStore,
|
|
12749
15365
|
createStudentStore,
|
|
12750
15366
|
createViewerStore,
|
|
15367
|
+
directStoryboard,
|
|
12751
15368
|
doRectsIntersect,
|
|
12752
15369
|
downloadAnnotationsAsJSON,
|
|
12753
15370
|
downloadAnnotationsAsMarkdown,
|
|
@@ -12762,6 +15379,7 @@ init_utils();
|
|
|
12762
15379
|
generateDocumentId,
|
|
12763
15380
|
getAllDocumentIds,
|
|
12764
15381
|
getAllStudentDataDocumentIds,
|
|
15382
|
+
getLocalMiniLM,
|
|
12765
15383
|
getMetadata,
|
|
12766
15384
|
getOutline,
|
|
12767
15385
|
getPage,
|
|
@@ -12779,17 +15397,23 @@ init_utils();
|
|
|
12779
15397
|
loadDocumentWithCallbacks,
|
|
12780
15398
|
loadHighlights,
|
|
12781
15399
|
loadStudentData,
|
|
15400
|
+
makeOverlayId,
|
|
15401
|
+
matchChunkToBlock,
|
|
12782
15402
|
mergeAdjacentRects,
|
|
12783
15403
|
pdfToPercent,
|
|
12784
15404
|
pdfToViewport,
|
|
12785
15405
|
pdfjsLib,
|
|
12786
15406
|
percentToPDF,
|
|
12787
15407
|
percentToViewport,
|
|
15408
|
+
playPageTurnSound,
|
|
12788
15409
|
quickViewer,
|
|
12789
15410
|
removeRotation,
|
|
12790
15411
|
saveHighlights,
|
|
12791
15412
|
saveStudentData,
|
|
12792
15413
|
scaleRect,
|
|
15414
|
+
storyboardFromMatch,
|
|
15415
|
+
storyboardJsonSchema,
|
|
15416
|
+
truncate,
|
|
12793
15417
|
useAgentContext,
|
|
12794
15418
|
useAgentStore,
|
|
12795
15419
|
useAnnotationStore,
|