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.js
CHANGED
|
@@ -117,8 +117,8 @@ async function loadDocument(options) {
|
|
|
117
117
|
signal.addEventListener("abort", abortHandler);
|
|
118
118
|
}
|
|
119
119
|
if (onProgress) {
|
|
120
|
-
loadingTask.onProgress = ({ loaded, total }) => {
|
|
121
|
-
onProgress({ loaded, total });
|
|
120
|
+
loadingTask.onProgress = ({ loaded: loaded2, total }) => {
|
|
121
|
+
onProgress({ loaded: loaded2, total });
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
let document2;
|
|
@@ -248,9 +248,9 @@ function loadDocumentWithCallbacks(options) {
|
|
|
248
248
|
};
|
|
249
249
|
abortController.signal.addEventListener("abort", abortHandler);
|
|
250
250
|
if (onProgress) {
|
|
251
|
-
loadingTask.onProgress = ({ loaded, total }) => {
|
|
251
|
+
loadingTask.onProgress = ({ loaded: loaded2, total }) => {
|
|
252
252
|
if (!abortController.signal.aborted) {
|
|
253
|
-
onProgress({ loaded, total });
|
|
253
|
+
onProgress({ loaded: loaded2, total });
|
|
254
254
|
}
|
|
255
255
|
};
|
|
256
256
|
}
|
|
@@ -605,13 +605,13 @@ function createViewerStore(initialOverrides = {}) {
|
|
|
605
605
|
},
|
|
606
606
|
zoomIn: () => {
|
|
607
607
|
const { scale } = get();
|
|
608
|
-
const currentIndex = ZOOM_LEVELS.findIndex((
|
|
608
|
+
const currentIndex = ZOOM_LEVELS.findIndex((z2) => z2 >= scale);
|
|
609
609
|
const nextIndex = Math.min(currentIndex + 1, ZOOM_LEVELS.length - 1);
|
|
610
610
|
set({ scale: ZOOM_LEVELS[nextIndex] ?? MAX_SCALE });
|
|
611
611
|
},
|
|
612
612
|
zoomOut: () => {
|
|
613
613
|
const { scale } = get();
|
|
614
|
-
const currentIndex = ZOOM_LEVELS.findIndex((
|
|
614
|
+
const currentIndex = ZOOM_LEVELS.findIndex((z2) => z2 >= scale);
|
|
615
615
|
const prevIndex = Math.max(currentIndex - 1, 0);
|
|
616
616
|
set({ scale: ZOOM_LEVELS[prevIndex] ?? MIN_SCALE });
|
|
617
617
|
},
|
|
@@ -1760,6 +1760,113 @@ var init_coordinates = __esm({
|
|
|
1760
1760
|
}
|
|
1761
1761
|
});
|
|
1762
1762
|
|
|
1763
|
+
// src/utils/page-turn-sound.ts
|
|
1764
|
+
function getAudioContext() {
|
|
1765
|
+
if (typeof window === "undefined") return null;
|
|
1766
|
+
if (!audioContext) {
|
|
1767
|
+
try {
|
|
1768
|
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
1769
|
+
} catch {
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
return audioContext;
|
|
1774
|
+
}
|
|
1775
|
+
function playPageTurnSound(volume = 0.3) {
|
|
1776
|
+
const ctx = getAudioContext();
|
|
1777
|
+
if (!ctx) return;
|
|
1778
|
+
if (ctx.state === "suspended") {
|
|
1779
|
+
ctx.resume();
|
|
1780
|
+
}
|
|
1781
|
+
const now = ctx.currentTime;
|
|
1782
|
+
const duration = 0.35;
|
|
1783
|
+
const bufferSize = Math.floor(ctx.sampleRate * duration);
|
|
1784
|
+
const noiseBuffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
|
|
1785
|
+
const data = noiseBuffer.getChannelData(0);
|
|
1786
|
+
for (let i = 0; i < bufferSize; i++) {
|
|
1787
|
+
data[i] = Math.random() * 2 - 1;
|
|
1788
|
+
}
|
|
1789
|
+
const noiseSource = ctx.createBufferSource();
|
|
1790
|
+
noiseSource.buffer = noiseBuffer;
|
|
1791
|
+
const bandpass = ctx.createBiquadFilter();
|
|
1792
|
+
bandpass.type = "bandpass";
|
|
1793
|
+
bandpass.frequency.setValueAtTime(3e3, now);
|
|
1794
|
+
bandpass.frequency.exponentialRampToValueAtTime(800, now + duration * 0.6);
|
|
1795
|
+
bandpass.Q.setValueAtTime(0.8, now);
|
|
1796
|
+
const highpass = ctx.createBiquadFilter();
|
|
1797
|
+
highpass.type = "highpass";
|
|
1798
|
+
highpass.frequency.setValueAtTime(400, now);
|
|
1799
|
+
highpass.frequency.linearRampToValueAtTime(200, now + duration);
|
|
1800
|
+
const envelope = ctx.createGain();
|
|
1801
|
+
envelope.gain.setValueAtTime(0, now);
|
|
1802
|
+
envelope.gain.linearRampToValueAtTime(volume * 0.6, now + 0.02);
|
|
1803
|
+
envelope.gain.setValueAtTime(volume * 0.6, now + 0.05);
|
|
1804
|
+
envelope.gain.linearRampToValueAtTime(volume, now + duration * 0.3);
|
|
1805
|
+
envelope.gain.exponentialRampToValueAtTime(1e-3, now + duration);
|
|
1806
|
+
const snapBuffer = ctx.createBuffer(1, Math.floor(ctx.sampleRate * 0.08), ctx.sampleRate);
|
|
1807
|
+
const snapData = snapBuffer.getChannelData(0);
|
|
1808
|
+
for (let i = 0; i < snapData.length; i++) {
|
|
1809
|
+
snapData[i] = Math.random() * 2 - 1;
|
|
1810
|
+
}
|
|
1811
|
+
const snapSource = ctx.createBufferSource();
|
|
1812
|
+
snapSource.buffer = snapBuffer;
|
|
1813
|
+
const snapFilter = ctx.createBiquadFilter();
|
|
1814
|
+
snapFilter.type = "bandpass";
|
|
1815
|
+
snapFilter.frequency.setValueAtTime(2e3, now);
|
|
1816
|
+
snapFilter.Q.setValueAtTime(1.5, now);
|
|
1817
|
+
const snapEnvelope = ctx.createGain();
|
|
1818
|
+
snapEnvelope.gain.setValueAtTime(0, now);
|
|
1819
|
+
snapEnvelope.gain.setValueAtTime(0, now + duration * 0.7);
|
|
1820
|
+
snapEnvelope.gain.linearRampToValueAtTime(volume * 0.8, now + duration * 0.75);
|
|
1821
|
+
snapEnvelope.gain.exponentialRampToValueAtTime(1e-3, now + duration);
|
|
1822
|
+
noiseSource.connect(bandpass);
|
|
1823
|
+
bandpass.connect(highpass);
|
|
1824
|
+
highpass.connect(envelope);
|
|
1825
|
+
envelope.connect(ctx.destination);
|
|
1826
|
+
snapSource.connect(snapFilter);
|
|
1827
|
+
snapFilter.connect(snapEnvelope);
|
|
1828
|
+
snapEnvelope.connect(ctx.destination);
|
|
1829
|
+
noiseSource.start(now);
|
|
1830
|
+
noiseSource.stop(now + duration);
|
|
1831
|
+
snapSource.start(now);
|
|
1832
|
+
snapSource.stop(now + duration);
|
|
1833
|
+
}
|
|
1834
|
+
var audioContext;
|
|
1835
|
+
var init_page_turn_sound = __esm({
|
|
1836
|
+
"src/utils/page-turn-sound.ts"() {
|
|
1837
|
+
"use strict";
|
|
1838
|
+
audioContext = null;
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1842
|
+
// src/utils/camera-math.ts
|
|
1843
|
+
function fitPageScale(page, viewport) {
|
|
1844
|
+
const sx = viewport.width / page.width;
|
|
1845
|
+
const sy = viewport.height / page.height;
|
|
1846
|
+
return Math.min(sx, sy);
|
|
1847
|
+
}
|
|
1848
|
+
function computeCameraForBlock(bbox, page, viewport, opts = {}) {
|
|
1849
|
+
const targetScale = opts.targetScale ?? 1.5;
|
|
1850
|
+
const paddingPdf = opts.paddingPdf ?? 80;
|
|
1851
|
+
const [x1, y1, x2, y2] = bbox;
|
|
1852
|
+
const blockW = Math.max(1, x2 - x1 + paddingPdf * 2);
|
|
1853
|
+
const blockH = Math.max(1, y2 - y1 + paddingPdf * 2);
|
|
1854
|
+
const blockCX = (x1 + x2) / 2;
|
|
1855
|
+
const blockCY = (y1 + y2) / 2;
|
|
1856
|
+
const fitBlock = Math.min(viewport.width / blockW, viewport.height / blockH);
|
|
1857
|
+
const scale = fitBlock * targetScale;
|
|
1858
|
+
const pageCX = page.width / 2;
|
|
1859
|
+
const pageCY = page.height / 2;
|
|
1860
|
+
const x = (pageCX - blockCX) * scale;
|
|
1861
|
+
const y = (pageCY - blockCY) * scale;
|
|
1862
|
+
return { scale, x, y };
|
|
1863
|
+
}
|
|
1864
|
+
var init_camera_math = __esm({
|
|
1865
|
+
"src/utils/camera-math.ts"() {
|
|
1866
|
+
"use strict";
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
|
|
1763
1870
|
// src/utils/index.ts
|
|
1764
1871
|
var init_utils = __esm({
|
|
1765
1872
|
"src/utils/index.ts"() {
|
|
@@ -1774,6 +1881,7 @@ var init_utils = __esm({
|
|
|
1774
1881
|
init_agent_api();
|
|
1775
1882
|
init_text_search();
|
|
1776
1883
|
init_coordinates();
|
|
1884
|
+
init_page_turn_sound();
|
|
1777
1885
|
}
|
|
1778
1886
|
});
|
|
1779
1887
|
|
|
@@ -2177,6 +2285,78 @@ var init_student_store = __esm({
|
|
|
2177
2285
|
}
|
|
2178
2286
|
});
|
|
2179
2287
|
|
|
2288
|
+
// src/store/narration-store.ts
|
|
2289
|
+
import { createStore as createStore6 } from "zustand/vanilla";
|
|
2290
|
+
function createNarrationStore(overrides = {}) {
|
|
2291
|
+
return createStore6()((set) => ({
|
|
2292
|
+
...initialState6,
|
|
2293
|
+
...overrides,
|
|
2294
|
+
setCurrentChunk: (chunk) => set({ currentChunk: chunk }),
|
|
2295
|
+
setCurrentPage: (page) => set({ currentPage: page }),
|
|
2296
|
+
pushChunkHistory: (entry) => set((state) => ({
|
|
2297
|
+
chunkHistory: [
|
|
2298
|
+
...state.chunkHistory.slice(-(MAX_HISTORY - 1)),
|
|
2299
|
+
entry
|
|
2300
|
+
]
|
|
2301
|
+
})),
|
|
2302
|
+
setCamera: (camera) => set((state) => ({ camera: { ...state.camera, ...camera } })),
|
|
2303
|
+
addOverlay: (overlay) => set((state) => ({ activeOverlays: [...state.activeOverlays, overlay] })),
|
|
2304
|
+
removeOverlay: (id) => set((state) => ({
|
|
2305
|
+
activeOverlays: state.activeOverlays.filter((o) => o.id !== id)
|
|
2306
|
+
})),
|
|
2307
|
+
clearOverlays: (predicate) => set((state) => ({
|
|
2308
|
+
activeOverlays: predicate ? state.activeOverlays.filter((o) => !predicate(o)) : []
|
|
2309
|
+
})),
|
|
2310
|
+
setEngineStatus: (s) => set({ engineStatus: s }),
|
|
2311
|
+
setLlmStatus: (s, error = null) => set({ llmStatus: s, lastError: error }),
|
|
2312
|
+
setLastStoryboard: (sb) => set({ lastStoryboard: sb }),
|
|
2313
|
+
setPaused: (paused) => set({ isPaused: paused }),
|
|
2314
|
+
appendDebugEvent: (event) => set((state) => {
|
|
2315
|
+
debugEventCounter += 1;
|
|
2316
|
+
const next = {
|
|
2317
|
+
...event,
|
|
2318
|
+
id: `dbg-${debugEventCounter}`,
|
|
2319
|
+
timestamp: Date.now()
|
|
2320
|
+
};
|
|
2321
|
+
return {
|
|
2322
|
+
debugEvents: [
|
|
2323
|
+
...state.debugEvents.slice(-(MAX_DEBUG_EVENTS - 1)),
|
|
2324
|
+
next
|
|
2325
|
+
]
|
|
2326
|
+
};
|
|
2327
|
+
}),
|
|
2328
|
+
clearDebugEvents: () => set({ debugEvents: [] }),
|
|
2329
|
+
reset: () => set(initialState6)
|
|
2330
|
+
}));
|
|
2331
|
+
}
|
|
2332
|
+
function makeOverlayId(action) {
|
|
2333
|
+
overlayIdCounter += 1;
|
|
2334
|
+
return `ov-${action.type}-${overlayIdCounter}-${Date.now()}`;
|
|
2335
|
+
}
|
|
2336
|
+
var MAX_HISTORY, initialState6, MAX_DEBUG_EVENTS, debugEventCounter, overlayIdCounter;
|
|
2337
|
+
var init_narration_store = __esm({
|
|
2338
|
+
"src/store/narration-store.ts"() {
|
|
2339
|
+
"use strict";
|
|
2340
|
+
MAX_HISTORY = 5;
|
|
2341
|
+
initialState6 = {
|
|
2342
|
+
currentChunk: null,
|
|
2343
|
+
currentPage: 1,
|
|
2344
|
+
chunkHistory: [],
|
|
2345
|
+
camera: { scale: 1, x: 0, y: 0, easing: "ease-in-out" },
|
|
2346
|
+
activeOverlays: [],
|
|
2347
|
+
engineStatus: "idle",
|
|
2348
|
+
llmStatus: "idle",
|
|
2349
|
+
lastStoryboard: null,
|
|
2350
|
+
lastError: null,
|
|
2351
|
+
isPaused: false,
|
|
2352
|
+
debugEvents: []
|
|
2353
|
+
};
|
|
2354
|
+
MAX_DEBUG_EVENTS = 50;
|
|
2355
|
+
debugEventCounter = 0;
|
|
2356
|
+
overlayIdCounter = 0;
|
|
2357
|
+
}
|
|
2358
|
+
});
|
|
2359
|
+
|
|
2180
2360
|
// src/store/index.ts
|
|
2181
2361
|
var init_store = __esm({
|
|
2182
2362
|
"src/store/index.ts"() {
|
|
@@ -2186,6 +2366,7 @@ var init_store = __esm({
|
|
|
2186
2366
|
init_search_store();
|
|
2187
2367
|
init_agent_store();
|
|
2188
2368
|
init_student_store();
|
|
2369
|
+
init_narration_store();
|
|
2189
2370
|
}
|
|
2190
2371
|
});
|
|
2191
2372
|
|
|
@@ -2195,7 +2376,7 @@ import { useStore } from "zustand";
|
|
|
2195
2376
|
import { jsx } from "react/jsx-runtime";
|
|
2196
2377
|
function PDFViewerProvider({
|
|
2197
2378
|
children,
|
|
2198
|
-
initialState:
|
|
2379
|
+
initialState: initialState7,
|
|
2199
2380
|
theme = "light",
|
|
2200
2381
|
defaultSidebarPanel = "thumbnails",
|
|
2201
2382
|
studentMode: _studentMode = false
|
|
@@ -2207,22 +2388,22 @@ function PDFViewerProvider({
|
|
|
2207
2388
|
const studentStoreRef = useRef(null);
|
|
2208
2389
|
if (!viewerStoreRef.current) {
|
|
2209
2390
|
viewerStoreRef.current = createViewerStore({
|
|
2210
|
-
...
|
|
2391
|
+
...initialState7?.viewer,
|
|
2211
2392
|
theme,
|
|
2212
2393
|
sidebarPanel: defaultSidebarPanel
|
|
2213
2394
|
});
|
|
2214
2395
|
}
|
|
2215
2396
|
if (!annotationStoreRef.current) {
|
|
2216
|
-
annotationStoreRef.current = createAnnotationStore(
|
|
2397
|
+
annotationStoreRef.current = createAnnotationStore(initialState7?.annotation);
|
|
2217
2398
|
}
|
|
2218
2399
|
if (!searchStoreRef.current) {
|
|
2219
|
-
searchStoreRef.current = createSearchStore(
|
|
2400
|
+
searchStoreRef.current = createSearchStore(initialState7?.search);
|
|
2220
2401
|
}
|
|
2221
2402
|
if (!agentStoreRef.current) {
|
|
2222
|
-
agentStoreRef.current = createAgentStore(
|
|
2403
|
+
agentStoreRef.current = createAgentStore(initialState7?.agent);
|
|
2223
2404
|
}
|
|
2224
2405
|
if (!studentStoreRef.current) {
|
|
2225
|
-
studentStoreRef.current = createStudentStore(
|
|
2406
|
+
studentStoreRef.current = createStudentStore(initialState7?.student);
|
|
2226
2407
|
}
|
|
2227
2408
|
useEffect(() => {
|
|
2228
2409
|
return () => {
|
|
@@ -3536,8 +3717,8 @@ var init_PluginManager = __esm({
|
|
|
3536
3717
|
/**
|
|
3537
3718
|
* Get toolbar items by position
|
|
3538
3719
|
*/
|
|
3539
|
-
getToolbarItemsByPosition(
|
|
3540
|
-
return this.getToolbarItems().filter((item) => item.position ===
|
|
3720
|
+
getToolbarItemsByPosition(position2) {
|
|
3721
|
+
return this.getToolbarItems().filter((item) => item.position === position2);
|
|
3541
3722
|
}
|
|
3542
3723
|
/**
|
|
3543
3724
|
* Get all sidebar panels from all plugins
|
|
@@ -4644,7 +4825,7 @@ var init_MobileToolbar = __esm({
|
|
|
4644
4825
|
sidebarOpen,
|
|
4645
4826
|
theme,
|
|
4646
4827
|
onThemeChange,
|
|
4647
|
-
position = "bottom",
|
|
4828
|
+
position: position2 = "bottom",
|
|
4648
4829
|
className
|
|
4649
4830
|
}) {
|
|
4650
4831
|
const [showMoreMenu, setShowMoreMenu] = useState5(false);
|
|
@@ -4678,8 +4859,8 @@ var init_MobileToolbar = __esm({
|
|
|
4678
4859
|
"bg-white dark:bg-gray-800",
|
|
4679
4860
|
"border-gray-200 dark:border-gray-700",
|
|
4680
4861
|
"px-2 py-1 safe-area-inset",
|
|
4681
|
-
|
|
4682
|
-
|
|
4862
|
+
position2 === "top" && "top-0 border-b",
|
|
4863
|
+
position2 === "bottom" && "bottom-0 border-t",
|
|
4683
4864
|
className
|
|
4684
4865
|
),
|
|
4685
4866
|
children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-1", children: [
|
|
@@ -4805,7 +4986,7 @@ var init_MobileToolbar = __esm({
|
|
|
4805
4986
|
"bg-white dark:bg-gray-800",
|
|
4806
4987
|
"rounded-lg shadow-lg",
|
|
4807
4988
|
"border border-gray-200 dark:border-gray-700",
|
|
4808
|
-
|
|
4989
|
+
position2 === "bottom" ? "bottom-full mb-2" : "top-full mt-2"
|
|
4809
4990
|
),
|
|
4810
4991
|
children: [
|
|
4811
4992
|
/* @__PURE__ */ jsx3("div", { className: "px-2 py-1 text-xs text-gray-500 dark:text-gray-400 font-medium", children: "Theme" }),
|
|
@@ -6631,7 +6812,7 @@ var init_AnnotationToolbar = __esm({
|
|
|
6631
6812
|
onShapeTypeChange: onShapeTypeChangeProp,
|
|
6632
6813
|
onColorChange: onColorChangeProp,
|
|
6633
6814
|
onStrokeWidthChange: onStrokeWidthChangeProp,
|
|
6634
|
-
position = "top",
|
|
6815
|
+
position: position2 = "top",
|
|
6635
6816
|
className
|
|
6636
6817
|
}) {
|
|
6637
6818
|
const storeActiveTool = useAnnotationStore((s) => s.activeAnnotationTool);
|
|
@@ -6681,9 +6862,9 @@ var init_AnnotationToolbar = __esm({
|
|
|
6681
6862
|
{
|
|
6682
6863
|
className: cn(
|
|
6683
6864
|
"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",
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6865
|
+
position2 === "floating" && "fixed bottom-20 left-1/2 -translate-x-1/2 z-50",
|
|
6866
|
+
position2 === "top" && "sticky top-0 z-40",
|
|
6867
|
+
position2 === "bottom" && "sticky bottom-0 z-40",
|
|
6687
6868
|
!isActive && "opacity-90",
|
|
6688
6869
|
className
|
|
6689
6870
|
),
|
|
@@ -8332,7 +8513,7 @@ var init_SelectionToolbar = __esm({
|
|
|
8332
8513
|
activeColor = "yellow",
|
|
8333
8514
|
className
|
|
8334
8515
|
}) {
|
|
8335
|
-
const [
|
|
8516
|
+
const [position2, setPosition] = useState16({ top: 0, left: 0, visible: false });
|
|
8336
8517
|
const toolbarRef = useRef14(null);
|
|
8337
8518
|
useEffect17(() => {
|
|
8338
8519
|
if (selection && selection.text && selection.rects.length > 0) {
|
|
@@ -8371,7 +8552,7 @@ var init_SelectionToolbar = __esm({
|
|
|
8371
8552
|
const handleCopy = useCallback27(() => {
|
|
8372
8553
|
onCopy?.();
|
|
8373
8554
|
}, [onCopy]);
|
|
8374
|
-
if (!
|
|
8555
|
+
if (!position2.visible || !selection?.text) {
|
|
8375
8556
|
return null;
|
|
8376
8557
|
}
|
|
8377
8558
|
return /* @__PURE__ */ jsxs18(
|
|
@@ -8389,8 +8570,8 @@ var init_SelectionToolbar = __esm({
|
|
|
8389
8570
|
className
|
|
8390
8571
|
),
|
|
8391
8572
|
style: {
|
|
8392
|
-
top:
|
|
8393
|
-
left:
|
|
8573
|
+
top: position2.top,
|
|
8574
|
+
left: position2.left,
|
|
8394
8575
|
transform: "translateX(-50%)"
|
|
8395
8576
|
},
|
|
8396
8577
|
onMouseDown: (e) => {
|
|
@@ -8507,7 +8688,7 @@ var init_HighlightPopover = __esm({
|
|
|
8507
8688
|
}) {
|
|
8508
8689
|
const [isEditingComment, setIsEditingComment] = useState17(false);
|
|
8509
8690
|
const [comment, setComment] = useState17(highlight?.comment ?? "");
|
|
8510
|
-
const [
|
|
8691
|
+
const [position2, setPosition] = useState17({ top: 0, left: 0, visible: false });
|
|
8511
8692
|
const popoverRef = useRef15(null);
|
|
8512
8693
|
const textareaRef = useRef15(null);
|
|
8513
8694
|
useEffect18(() => {
|
|
@@ -8555,11 +8736,11 @@ var init_HighlightPopover = __esm({
|
|
|
8555
8736
|
onClose();
|
|
8556
8737
|
}
|
|
8557
8738
|
}
|
|
8558
|
-
if (
|
|
8739
|
+
if (position2.visible) {
|
|
8559
8740
|
document.addEventListener("mousedown", handleClickOutside);
|
|
8560
8741
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
8561
8742
|
}
|
|
8562
|
-
}, [
|
|
8743
|
+
}, [position2.visible, onClose]);
|
|
8563
8744
|
useEffect18(() => {
|
|
8564
8745
|
function handleKeyDown(event) {
|
|
8565
8746
|
if (event.key === "Escape") {
|
|
@@ -8571,11 +8752,11 @@ var init_HighlightPopover = __esm({
|
|
|
8571
8752
|
}
|
|
8572
8753
|
}
|
|
8573
8754
|
}
|
|
8574
|
-
if (
|
|
8755
|
+
if (position2.visible) {
|
|
8575
8756
|
document.addEventListener("keydown", handleKeyDown);
|
|
8576
8757
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
8577
8758
|
}
|
|
8578
|
-
}, [
|
|
8759
|
+
}, [position2.visible, isEditingComment, highlight?.comment, onClose]);
|
|
8579
8760
|
const handleColorClick = useCallback28(
|
|
8580
8761
|
(color) => {
|
|
8581
8762
|
if (highlight) {
|
|
@@ -8602,7 +8783,7 @@ var init_HighlightPopover = __esm({
|
|
|
8602
8783
|
onCopy?.(highlight.text);
|
|
8603
8784
|
}
|
|
8604
8785
|
}, [highlight, onCopy]);
|
|
8605
|
-
if (!highlight || !
|
|
8786
|
+
if (!highlight || !position2.visible) {
|
|
8606
8787
|
return null;
|
|
8607
8788
|
}
|
|
8608
8789
|
return /* @__PURE__ */ jsxs19(
|
|
@@ -8619,8 +8800,8 @@ var init_HighlightPopover = __esm({
|
|
|
8619
8800
|
className
|
|
8620
8801
|
),
|
|
8621
8802
|
style: {
|
|
8622
|
-
top:
|
|
8623
|
-
left:
|
|
8803
|
+
top: position2.top,
|
|
8804
|
+
left: position2.left,
|
|
8624
8805
|
transform: "translate(-50%, -100%)",
|
|
8625
8806
|
width: 280
|
|
8626
8807
|
},
|
|
@@ -9802,17 +9983,237 @@ var init_DualPageContainer = __esm({
|
|
|
9802
9983
|
}
|
|
9803
9984
|
});
|
|
9804
9985
|
|
|
9805
|
-
// src/components/
|
|
9806
|
-
import { memo as memo26, useCallback as useCallback32 } from "react";
|
|
9986
|
+
// src/components/PDFViewer/BookModeContainer.tsx
|
|
9987
|
+
import React, { memo as memo26, useEffect as useEffect22, useState as useState21, useRef as useRef19, useCallback as useCallback32 } from "react";
|
|
9988
|
+
import HTMLFlipBook from "react-pageflip";
|
|
9807
9989
|
import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
9990
|
+
var BookPage, BookModeContainer;
|
|
9991
|
+
var init_BookModeContainer = __esm({
|
|
9992
|
+
"src/components/PDFViewer/BookModeContainer.tsx"() {
|
|
9993
|
+
"use strict";
|
|
9994
|
+
init_PDFPage2();
|
|
9995
|
+
init_PDFLoadingScreen2();
|
|
9996
|
+
init_hooks();
|
|
9997
|
+
init_utils();
|
|
9998
|
+
BookPage = React.forwardRef(function BookPage2({ pageNumber, page, scale, rotation, width, height }, ref) {
|
|
9999
|
+
return /* @__PURE__ */ jsx27("div", { ref, className: "book-page", "data-page-number": pageNumber, children: /* @__PURE__ */ jsx27("div", { style: { width, height, overflow: "hidden" }, children: /* @__PURE__ */ jsx27(
|
|
10000
|
+
PDFPage,
|
|
10001
|
+
{
|
|
10002
|
+
pageNumber,
|
|
10003
|
+
page,
|
|
10004
|
+
scale,
|
|
10005
|
+
rotation,
|
|
10006
|
+
showTextLayer: false,
|
|
10007
|
+
showAnnotationLayer: false,
|
|
10008
|
+
showHighlightLayer: false
|
|
10009
|
+
}
|
|
10010
|
+
) }) });
|
|
10011
|
+
});
|
|
10012
|
+
BookModeContainer = memo26(function BookModeContainer2({
|
|
10013
|
+
className,
|
|
10014
|
+
flippingTime = 800,
|
|
10015
|
+
drawShadow = true,
|
|
10016
|
+
maxShadowOpacity = 0.7
|
|
10017
|
+
}) {
|
|
10018
|
+
const {
|
|
10019
|
+
document: document2,
|
|
10020
|
+
currentPage,
|
|
10021
|
+
numPages,
|
|
10022
|
+
scale,
|
|
10023
|
+
rotation,
|
|
10024
|
+
theme,
|
|
10025
|
+
isLoading,
|
|
10026
|
+
goToPage
|
|
10027
|
+
} = usePDFViewer();
|
|
10028
|
+
const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
|
|
10029
|
+
const { viewerStore } = usePDFViewerStores();
|
|
10030
|
+
const [pages, setPages] = useState21([]);
|
|
10031
|
+
const [rawPageDims, setRawPageDims] = useState21({ width: 612, height: 792 });
|
|
10032
|
+
const [isLoadingPages, setIsLoadingPages] = useState21(false);
|
|
10033
|
+
const containerRef = useRef19(null);
|
|
10034
|
+
const [containerSize, setContainerSize] = useState21({ width: 0, height: 0 });
|
|
10035
|
+
const flipBookRef = useRef19(null);
|
|
10036
|
+
const isSyncingRef = useRef19(false);
|
|
10037
|
+
useEffect22(() => {
|
|
10038
|
+
const el = containerRef.current;
|
|
10039
|
+
if (!el) return;
|
|
10040
|
+
const measure = () => {
|
|
10041
|
+
setContainerSize({ width: el.clientWidth, height: el.clientHeight });
|
|
10042
|
+
};
|
|
10043
|
+
measure();
|
|
10044
|
+
const ro = new ResizeObserver(measure);
|
|
10045
|
+
ro.observe(el);
|
|
10046
|
+
return () => ro.disconnect();
|
|
10047
|
+
}, []);
|
|
10048
|
+
useEffect22(() => {
|
|
10049
|
+
if (!document2) {
|
|
10050
|
+
setPages([]);
|
|
10051
|
+
return;
|
|
10052
|
+
}
|
|
10053
|
+
let cancelled = false;
|
|
10054
|
+
const loadAllPages = async () => {
|
|
10055
|
+
setIsLoadingPages(true);
|
|
10056
|
+
try {
|
|
10057
|
+
const pagePromises = [];
|
|
10058
|
+
for (let i = 1; i <= numPages; i++) {
|
|
10059
|
+
pagePromises.push(document2.getPage(i));
|
|
10060
|
+
}
|
|
10061
|
+
const results = await Promise.allSettled(pagePromises);
|
|
10062
|
+
if (!cancelled) {
|
|
10063
|
+
const loaded2 = results.map((r) => r.status === "fulfilled" ? r.value : null);
|
|
10064
|
+
setPages(loaded2);
|
|
10065
|
+
const firstPage = loaded2[0];
|
|
10066
|
+
if (firstPage) {
|
|
10067
|
+
const vp = firstPage.getViewport({ scale: 1, rotation });
|
|
10068
|
+
setRawPageDims({ width: vp.width, height: vp.height });
|
|
10069
|
+
}
|
|
10070
|
+
}
|
|
10071
|
+
} catch {
|
|
10072
|
+
} finally {
|
|
10073
|
+
if (!cancelled) setIsLoadingPages(false);
|
|
10074
|
+
}
|
|
10075
|
+
};
|
|
10076
|
+
loadAllPages();
|
|
10077
|
+
return () => {
|
|
10078
|
+
cancelled = true;
|
|
10079
|
+
};
|
|
10080
|
+
}, [document2, numPages, rotation]);
|
|
10081
|
+
useEffect22(() => {
|
|
10082
|
+
if (pages[0]) {
|
|
10083
|
+
const vp = pages[0].getViewport({ scale: 1, rotation });
|
|
10084
|
+
setRawPageDims({ width: vp.width, height: vp.height });
|
|
10085
|
+
}
|
|
10086
|
+
}, [pages, rotation]);
|
|
10087
|
+
const padding = 8;
|
|
10088
|
+
const fitWidth = Math.max(containerSize.width - padding * 2, 200);
|
|
10089
|
+
const fitHeight = Math.max(containerSize.height - padding * 2, 300);
|
|
10090
|
+
const pageAspect = rawPageDims.width / rawPageDims.height;
|
|
10091
|
+
let displayWidth;
|
|
10092
|
+
let displayHeight;
|
|
10093
|
+
if (fitWidth / fitHeight > pageAspect) {
|
|
10094
|
+
displayHeight = fitHeight;
|
|
10095
|
+
displayWidth = Math.floor(fitHeight * pageAspect);
|
|
10096
|
+
} else {
|
|
10097
|
+
displayWidth = fitWidth;
|
|
10098
|
+
displayHeight = Math.floor(fitWidth / pageAspect);
|
|
10099
|
+
}
|
|
10100
|
+
const renderScale = displayWidth / rawPageDims.width;
|
|
10101
|
+
useEffect22(() => {
|
|
10102
|
+
const pageFlip = flipBookRef.current?.pageFlip();
|
|
10103
|
+
if (!pageFlip) return;
|
|
10104
|
+
const flipBookPage = pageFlip.getCurrentPageIndex();
|
|
10105
|
+
const targetIndex = currentPage - 1;
|
|
10106
|
+
if (flipBookPage !== targetIndex) {
|
|
10107
|
+
isSyncingRef.current = true;
|
|
10108
|
+
pageFlip.turnToPage(targetIndex);
|
|
10109
|
+
setTimeout(() => {
|
|
10110
|
+
isSyncingRef.current = false;
|
|
10111
|
+
}, 100);
|
|
10112
|
+
}
|
|
10113
|
+
}, [currentPage]);
|
|
10114
|
+
useEffect22(() => {
|
|
10115
|
+
if (scrollToPageRequest) {
|
|
10116
|
+
requestAnimationFrame(() => {
|
|
10117
|
+
viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
|
|
10118
|
+
});
|
|
10119
|
+
}
|
|
10120
|
+
}, [scrollToPageRequest, viewerStore]);
|
|
10121
|
+
const handleFlip = useCallback32((e) => {
|
|
10122
|
+
if (isSyncingRef.current) return;
|
|
10123
|
+
const newPage = e.data + 1;
|
|
10124
|
+
if (newPage !== currentPage && newPage >= 1 && newPage <= numPages) {
|
|
10125
|
+
goToPage(newPage);
|
|
10126
|
+
}
|
|
10127
|
+
}, [currentPage, numPages, goToPage]);
|
|
10128
|
+
const themeStyles = {
|
|
10129
|
+
light: "bg-gray-100",
|
|
10130
|
+
dark: "bg-gray-900",
|
|
10131
|
+
sepia: "bg-amber-50"
|
|
10132
|
+
};
|
|
10133
|
+
const themeClass = theme === "dark" ? "dark" : theme === "sepia" ? "sepia" : "";
|
|
10134
|
+
const ready = !!document2 && !isLoadingPages && pages.length > 0;
|
|
10135
|
+
const hasContainer = containerSize.width > 0 && containerSize.height > 0;
|
|
10136
|
+
return /* @__PURE__ */ jsxs23(
|
|
10137
|
+
"div",
|
|
10138
|
+
{
|
|
10139
|
+
ref: containerRef,
|
|
10140
|
+
className: cn(
|
|
10141
|
+
"book-mode-container",
|
|
10142
|
+
"flex-1 h-full w-full overflow-hidden",
|
|
10143
|
+
"flex items-center justify-center",
|
|
10144
|
+
themeStyles[theme],
|
|
10145
|
+
themeClass,
|
|
10146
|
+
className
|
|
10147
|
+
),
|
|
10148
|
+
style: { userSelect: "none", WebkitUserSelect: "none" },
|
|
10149
|
+
children: [
|
|
10150
|
+
!ready && /* @__PURE__ */ jsx27(
|
|
10151
|
+
PDFLoadingScreen,
|
|
10152
|
+
{
|
|
10153
|
+
phase: !document2 ? isLoading ? "fetching" : "initializing" : "rendering"
|
|
10154
|
+
}
|
|
10155
|
+
),
|
|
10156
|
+
ready && hasContainer && /* @__PURE__ */ jsx27(
|
|
10157
|
+
HTMLFlipBook,
|
|
10158
|
+
{
|
|
10159
|
+
ref: flipBookRef,
|
|
10160
|
+
width: displayWidth,
|
|
10161
|
+
height: displayHeight,
|
|
10162
|
+
size: "fixed",
|
|
10163
|
+
minWidth: displayWidth,
|
|
10164
|
+
maxWidth: displayWidth,
|
|
10165
|
+
minHeight: displayHeight,
|
|
10166
|
+
maxHeight: displayHeight,
|
|
10167
|
+
drawShadow,
|
|
10168
|
+
maxShadowOpacity,
|
|
10169
|
+
flippingTime,
|
|
10170
|
+
usePortrait: true,
|
|
10171
|
+
startPage: currentPage - 1,
|
|
10172
|
+
showCover: false,
|
|
10173
|
+
mobileScrollSupport: true,
|
|
10174
|
+
swipeDistance: 30,
|
|
10175
|
+
showPageCorners: true,
|
|
10176
|
+
useMouseEvents: true,
|
|
10177
|
+
clickEventForward: false,
|
|
10178
|
+
onFlip: handleFlip,
|
|
10179
|
+
className: "book-flipbook",
|
|
10180
|
+
style: {},
|
|
10181
|
+
startZIndex: 0,
|
|
10182
|
+
autoSize: false,
|
|
10183
|
+
renderOnlyPageLengthChange: false,
|
|
10184
|
+
disableFlipByClick: false,
|
|
10185
|
+
children: pages.map((page, index) => /* @__PURE__ */ jsx27(
|
|
10186
|
+
BookPage,
|
|
10187
|
+
{
|
|
10188
|
+
pageNumber: index + 1,
|
|
10189
|
+
page,
|
|
10190
|
+
scale: renderScale,
|
|
10191
|
+
rotation,
|
|
10192
|
+
width: displayWidth,
|
|
10193
|
+
height: displayHeight
|
|
10194
|
+
},
|
|
10195
|
+
index
|
|
10196
|
+
))
|
|
10197
|
+
}
|
|
10198
|
+
)
|
|
10199
|
+
]
|
|
10200
|
+
}
|
|
10201
|
+
);
|
|
10202
|
+
});
|
|
10203
|
+
}
|
|
10204
|
+
});
|
|
10205
|
+
|
|
10206
|
+
// src/components/FloatingZoomControls/FloatingZoomControls.tsx
|
|
10207
|
+
import { memo as memo27, useCallback as useCallback33 } from "react";
|
|
10208
|
+
import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
9808
10209
|
var FloatingZoomControls;
|
|
9809
10210
|
var init_FloatingZoomControls = __esm({
|
|
9810
10211
|
"src/components/FloatingZoomControls/FloatingZoomControls.tsx"() {
|
|
9811
10212
|
"use strict";
|
|
9812
10213
|
init_hooks();
|
|
9813
10214
|
init_utils();
|
|
9814
|
-
FloatingZoomControls =
|
|
9815
|
-
position = "bottom-right",
|
|
10215
|
+
FloatingZoomControls = memo27(function FloatingZoomControls2({
|
|
10216
|
+
position: position2 = "bottom-right",
|
|
9816
10217
|
className,
|
|
9817
10218
|
showFitToWidth = true,
|
|
9818
10219
|
showFitToPage = false,
|
|
@@ -9821,20 +10222,20 @@ var init_FloatingZoomControls = __esm({
|
|
|
9821
10222
|
const { viewerStore } = usePDFViewerStores();
|
|
9822
10223
|
const scale = useViewerStore((s) => s.scale);
|
|
9823
10224
|
const document2 = useViewerStore((s) => s.document);
|
|
9824
|
-
const handleZoomIn =
|
|
10225
|
+
const handleZoomIn = useCallback33(() => {
|
|
9825
10226
|
const currentScale = viewerStore.getState().scale;
|
|
9826
10227
|
const newScale = Math.min(4, currentScale + 0.05);
|
|
9827
10228
|
viewerStore.getState().setScale(newScale);
|
|
9828
10229
|
}, [viewerStore]);
|
|
9829
|
-
const handleZoomOut =
|
|
10230
|
+
const handleZoomOut = useCallback33(() => {
|
|
9830
10231
|
const currentScale = viewerStore.getState().scale;
|
|
9831
10232
|
const newScale = Math.max(0.1, currentScale - 0.05);
|
|
9832
10233
|
viewerStore.getState().setScale(newScale);
|
|
9833
10234
|
}, [viewerStore]);
|
|
9834
|
-
const handleFitToWidth =
|
|
10235
|
+
const handleFitToWidth = useCallback33(() => {
|
|
9835
10236
|
viewerStore.getState().setScale(1);
|
|
9836
10237
|
}, [viewerStore]);
|
|
9837
|
-
const handleFitToPage =
|
|
10238
|
+
const handleFitToPage = useCallback33(() => {
|
|
9838
10239
|
viewerStore.getState().setScale(0.75);
|
|
9839
10240
|
}, [viewerStore]);
|
|
9840
10241
|
if (!document2) return null;
|
|
@@ -9845,7 +10246,7 @@ var init_FloatingZoomControls = __esm({
|
|
|
9845
10246
|
"top-left": "top-4 left-4"
|
|
9846
10247
|
};
|
|
9847
10248
|
const zoomPercentage = Math.round(scale * 100);
|
|
9848
|
-
return /* @__PURE__ */
|
|
10249
|
+
return /* @__PURE__ */ jsxs24(
|
|
9849
10250
|
"div",
|
|
9850
10251
|
{
|
|
9851
10252
|
className: cn(
|
|
@@ -9853,11 +10254,11 @@ var init_FloatingZoomControls = __esm({
|
|
|
9853
10254
|
"bg-white dark:bg-gray-800 rounded-lg shadow-lg",
|
|
9854
10255
|
"border border-gray-200 dark:border-gray-700",
|
|
9855
10256
|
"p-1",
|
|
9856
|
-
positionClasses[
|
|
10257
|
+
positionClasses[position2],
|
|
9857
10258
|
className
|
|
9858
10259
|
),
|
|
9859
10260
|
children: [
|
|
9860
|
-
/* @__PURE__ */
|
|
10261
|
+
/* @__PURE__ */ jsx28(
|
|
9861
10262
|
"button",
|
|
9862
10263
|
{
|
|
9863
10264
|
onClick: handleZoomOut,
|
|
@@ -9871,14 +10272,14 @@ var init_FloatingZoomControls = __esm({
|
|
|
9871
10272
|
disabled: scale <= 0.25,
|
|
9872
10273
|
title: "Zoom Out",
|
|
9873
10274
|
"aria-label": "Zoom Out",
|
|
9874
|
-
children: /* @__PURE__ */
|
|
10275
|
+
children: /* @__PURE__ */ jsx28("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx28("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) })
|
|
9875
10276
|
}
|
|
9876
10277
|
),
|
|
9877
|
-
showZoomLevel && /* @__PURE__ */
|
|
10278
|
+
showZoomLevel && /* @__PURE__ */ jsxs24("span", { className: "min-w-[48px] text-center text-sm font-medium text-gray-700 dark:text-gray-300", children: [
|
|
9878
10279
|
zoomPercentage,
|
|
9879
10280
|
"%"
|
|
9880
10281
|
] }),
|
|
9881
|
-
/* @__PURE__ */
|
|
10282
|
+
/* @__PURE__ */ jsx28(
|
|
9882
10283
|
"button",
|
|
9883
10284
|
{
|
|
9884
10285
|
onClick: handleZoomIn,
|
|
@@ -9892,11 +10293,11 @@ var init_FloatingZoomControls = __esm({
|
|
|
9892
10293
|
disabled: scale >= 4,
|
|
9893
10294
|
title: "Zoom In",
|
|
9894
10295
|
"aria-label": "Zoom In",
|
|
9895
|
-
children: /* @__PURE__ */
|
|
10296
|
+
children: /* @__PURE__ */ jsx28("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx28("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) })
|
|
9896
10297
|
}
|
|
9897
10298
|
),
|
|
9898
|
-
(showFitToWidth || showFitToPage) && /* @__PURE__ */
|
|
9899
|
-
showFitToWidth && /* @__PURE__ */
|
|
10299
|
+
(showFitToWidth || showFitToPage) && /* @__PURE__ */ jsx28("div", { className: "w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1" }),
|
|
10300
|
+
showFitToWidth && /* @__PURE__ */ jsx28(
|
|
9900
10301
|
"button",
|
|
9901
10302
|
{
|
|
9902
10303
|
onClick: handleFitToWidth,
|
|
@@ -9908,10 +10309,10 @@ var init_FloatingZoomControls = __esm({
|
|
|
9908
10309
|
),
|
|
9909
10310
|
title: "Fit to Width",
|
|
9910
10311
|
"aria-label": "Fit to Width",
|
|
9911
|
-
children: /* @__PURE__ */
|
|
10312
|
+
children: /* @__PURE__ */ jsx28("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx28("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" }) })
|
|
9912
10313
|
}
|
|
9913
10314
|
),
|
|
9914
|
-
showFitToPage && /* @__PURE__ */
|
|
10315
|
+
showFitToPage && /* @__PURE__ */ jsx28(
|
|
9915
10316
|
"button",
|
|
9916
10317
|
{
|
|
9917
10318
|
onClick: handleFitToPage,
|
|
@@ -9923,7 +10324,7 @@ var init_FloatingZoomControls = __esm({
|
|
|
9923
10324
|
),
|
|
9924
10325
|
title: "Fit to Page",
|
|
9925
10326
|
"aria-label": "Fit to Page",
|
|
9926
|
-
children: /* @__PURE__ */
|
|
10327
|
+
children: /* @__PURE__ */ jsx28("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx28("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" }) })
|
|
9927
10328
|
}
|
|
9928
10329
|
)
|
|
9929
10330
|
]
|
|
@@ -9947,14 +10348,14 @@ __export(PDFViewerClient_exports, {
|
|
|
9947
10348
|
PDFViewerClient: () => PDFViewerClient
|
|
9948
10349
|
});
|
|
9949
10350
|
import {
|
|
9950
|
-
useEffect as
|
|
9951
|
-
useCallback as
|
|
9952
|
-
memo as
|
|
9953
|
-
useRef as
|
|
9954
|
-
useState as
|
|
10351
|
+
useEffect as useEffect23,
|
|
10352
|
+
useCallback as useCallback34,
|
|
10353
|
+
memo as memo28,
|
|
10354
|
+
useRef as useRef20,
|
|
10355
|
+
useState as useState22,
|
|
9955
10356
|
forwardRef
|
|
9956
10357
|
} from "react";
|
|
9957
|
-
import { jsx as
|
|
10358
|
+
import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
9958
10359
|
function getSrcIdentifier(src) {
|
|
9959
10360
|
if (typeof src === "string") {
|
|
9960
10361
|
return src;
|
|
@@ -10006,10 +10407,11 @@ var init_PDFViewerClient = __esm({
|
|
|
10006
10407
|
init_DocumentContainer();
|
|
10007
10408
|
init_ContinuousScrollContainer();
|
|
10008
10409
|
init_DualPageContainer();
|
|
10410
|
+
init_BookModeContainer();
|
|
10009
10411
|
init_FloatingZoomControls2();
|
|
10010
10412
|
init_PDFLoadingScreen2();
|
|
10011
10413
|
init_utils();
|
|
10012
|
-
PDFViewerInner =
|
|
10414
|
+
PDFViewerInner = memo28(function PDFViewerInner2({
|
|
10013
10415
|
src,
|
|
10014
10416
|
initialPage = 1,
|
|
10015
10417
|
page: controlledPage,
|
|
@@ -10036,19 +10438,19 @@ var init_PDFViewerClient = __esm({
|
|
|
10036
10438
|
onReady
|
|
10037
10439
|
}) {
|
|
10038
10440
|
const { viewerStore, annotationStore, searchStore } = usePDFViewerStores();
|
|
10039
|
-
const mountedRef =
|
|
10040
|
-
const [, setLoadState] =
|
|
10041
|
-
const onDocumentLoadRef =
|
|
10042
|
-
const onErrorRef =
|
|
10043
|
-
const onPageChangeRef =
|
|
10044
|
-
const onScaleChangeRef =
|
|
10045
|
-
const onZoomChangeRef =
|
|
10046
|
-
const onPageRenderStartRef =
|
|
10047
|
-
const onPageRenderCompleteRef =
|
|
10048
|
-
const onHighlightAddedRef =
|
|
10049
|
-
const onHighlightRemovedRef =
|
|
10050
|
-
const onAnnotationAddedRef =
|
|
10051
|
-
const onReadyRef =
|
|
10441
|
+
const mountedRef = useRef20(true);
|
|
10442
|
+
const [, setLoadState] = useState22("idle");
|
|
10443
|
+
const onDocumentLoadRef = useRef20(onDocumentLoad);
|
|
10444
|
+
const onErrorRef = useRef20(onError);
|
|
10445
|
+
const onPageChangeRef = useRef20(onPageChange);
|
|
10446
|
+
const onScaleChangeRef = useRef20(onScaleChange);
|
|
10447
|
+
const onZoomChangeRef = useRef20(onZoomChange);
|
|
10448
|
+
const onPageRenderStartRef = useRef20(onPageRenderStart);
|
|
10449
|
+
const onPageRenderCompleteRef = useRef20(onPageRenderComplete);
|
|
10450
|
+
const onHighlightAddedRef = useRef20(onHighlightAdded);
|
|
10451
|
+
const onHighlightRemovedRef = useRef20(onHighlightRemoved);
|
|
10452
|
+
const onAnnotationAddedRef = useRef20(onAnnotationAdded);
|
|
10453
|
+
const onReadyRef = useRef20(onReady);
|
|
10052
10454
|
onDocumentLoadRef.current = onDocumentLoad;
|
|
10053
10455
|
onErrorRef.current = onError;
|
|
10054
10456
|
onPageChangeRef.current = onPageChange;
|
|
@@ -10061,8 +10463,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10061
10463
|
onAnnotationAddedRef.current = onAnnotationAdded;
|
|
10062
10464
|
onReadyRef.current = onReady;
|
|
10063
10465
|
const isControlled = controlledPage !== void 0;
|
|
10064
|
-
const prevControlledPageRef =
|
|
10065
|
-
const srcIdRef =
|
|
10466
|
+
const prevControlledPageRef = useRef20(controlledPage);
|
|
10467
|
+
const srcIdRef = useRef20(null);
|
|
10066
10468
|
const currentPage = useViewerStore((s) => s.currentPage);
|
|
10067
10469
|
const scale = useViewerStore((s) => s.scale);
|
|
10068
10470
|
const theme = useViewerStore((s) => s.theme);
|
|
@@ -10072,8 +10474,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10072
10474
|
const sidebarOpen = useViewerStore((s) => s.sidebarOpen);
|
|
10073
10475
|
const streamingProgress = useViewerStore((s) => s.streamingProgress);
|
|
10074
10476
|
const srcId = getSrcIdentifier(src);
|
|
10075
|
-
const handleRef =
|
|
10076
|
-
|
|
10477
|
+
const handleRef = useRef20(null);
|
|
10478
|
+
useEffect23(() => {
|
|
10077
10479
|
const handle = {
|
|
10078
10480
|
// ==================== Text Highlighting ====================
|
|
10079
10481
|
highlightText: async (text, options) => {
|
|
@@ -10491,14 +10893,14 @@ var init_PDFViewerClient = __esm({
|
|
|
10491
10893
|
handleRef.current = handle;
|
|
10492
10894
|
onReadyRef.current?.(handle);
|
|
10493
10895
|
}, [viewerStore, annotationStore, searchStore]);
|
|
10494
|
-
const handleRetry =
|
|
10896
|
+
const handleRetry = useCallback34(() => {
|
|
10495
10897
|
srcIdRef.current = null;
|
|
10496
10898
|
viewerStore.getState().setError(null);
|
|
10497
10899
|
setLoadState("idle");
|
|
10498
10900
|
}, [viewerStore]);
|
|
10499
|
-
const abortControllerRef =
|
|
10500
|
-
const currentSrcRef =
|
|
10501
|
-
|
|
10901
|
+
const abortControllerRef = useRef20(null);
|
|
10902
|
+
const currentSrcRef = useRef20(null);
|
|
10903
|
+
useEffect23(() => {
|
|
10502
10904
|
mountedRef.current = true;
|
|
10503
10905
|
return () => {
|
|
10504
10906
|
mountedRef.current = false;
|
|
@@ -10523,8 +10925,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10523
10925
|
viewerStore.getState().setError(null);
|
|
10524
10926
|
};
|
|
10525
10927
|
}, [viewerStore]);
|
|
10526
|
-
const cancelLoaderRef =
|
|
10527
|
-
|
|
10928
|
+
const cancelLoaderRef = useRef20(null);
|
|
10929
|
+
useEffect23(() => {
|
|
10528
10930
|
if (srcIdRef.current === srcId && viewerStore.getState().document) {
|
|
10529
10931
|
return;
|
|
10530
10932
|
}
|
|
@@ -10565,12 +10967,12 @@ var init_PDFViewerClient = __esm({
|
|
|
10565
10967
|
src,
|
|
10566
10968
|
workerSrc,
|
|
10567
10969
|
signal: abortController.signal,
|
|
10568
|
-
onProgress: ({ loaded, total }) => {
|
|
10970
|
+
onProgress: ({ loaded: loaded2, total }) => {
|
|
10569
10971
|
if (!mountedRef.current || srcIdRef.current !== loadId || abortController.signal.aborted) {
|
|
10570
10972
|
return;
|
|
10571
10973
|
}
|
|
10572
10974
|
const now = Date.now();
|
|
10573
|
-
const percent = total > 0 ? Math.round(
|
|
10975
|
+
const percent = total > 0 ? Math.round(loaded2 / total * 100) : 0;
|
|
10574
10976
|
const timePassed = now - lastProgressUpdate >= PROGRESS_THROTTLE_MS;
|
|
10575
10977
|
const percentChanged = Math.abs(percent - lastPercent) >= PROGRESS_MIN_CHANGE;
|
|
10576
10978
|
const isComplete = percent >= 100;
|
|
@@ -10581,10 +10983,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10581
10983
|
loadingProgress: {
|
|
10582
10984
|
phase: "fetching",
|
|
10583
10985
|
percent,
|
|
10584
|
-
bytesLoaded:
|
|
10986
|
+
bytesLoaded: loaded2,
|
|
10585
10987
|
totalBytes: total
|
|
10586
10988
|
},
|
|
10587
|
-
streamingProgress: { loaded, total },
|
|
10989
|
+
streamingProgress: { loaded: loaded2, total },
|
|
10588
10990
|
documentLoadingState: "loading"
|
|
10589
10991
|
});
|
|
10590
10992
|
}
|
|
@@ -10658,22 +11060,22 @@ var init_PDFViewerClient = __esm({
|
|
|
10658
11060
|
}
|
|
10659
11061
|
};
|
|
10660
11062
|
}, [srcId, src, workerSrc, initialPage, initialScale, viewerStore]);
|
|
10661
|
-
const prevPageRef =
|
|
10662
|
-
|
|
11063
|
+
const prevPageRef = useRef20(currentPage);
|
|
11064
|
+
useEffect23(() => {
|
|
10663
11065
|
if (prevPageRef.current !== currentPage) {
|
|
10664
11066
|
prevPageRef.current = currentPage;
|
|
10665
11067
|
onPageChangeRef.current?.(currentPage);
|
|
10666
11068
|
}
|
|
10667
11069
|
}, [currentPage]);
|
|
10668
|
-
const prevScaleRef =
|
|
10669
|
-
|
|
11070
|
+
const prevScaleRef = useRef20(scale);
|
|
11071
|
+
useEffect23(() => {
|
|
10670
11072
|
if (prevScaleRef.current !== scale) {
|
|
10671
11073
|
prevScaleRef.current = scale;
|
|
10672
11074
|
onScaleChangeRef.current?.(scale);
|
|
10673
11075
|
onZoomChangeRef.current?.(scale);
|
|
10674
11076
|
}
|
|
10675
11077
|
}, [scale]);
|
|
10676
|
-
|
|
11078
|
+
useEffect23(() => {
|
|
10677
11079
|
if (!isControlled || controlledPage === void 0) return;
|
|
10678
11080
|
if (prevControlledPageRef.current === controlledPage) return;
|
|
10679
11081
|
prevControlledPageRef.current = controlledPage;
|
|
@@ -10686,7 +11088,7 @@ var init_PDFViewerClient = __esm({
|
|
|
10686
11088
|
if (error) {
|
|
10687
11089
|
if (errorComponent) {
|
|
10688
11090
|
const errorContent = typeof errorComponent === "function" ? errorComponent(error, handleRetry) : errorComponent;
|
|
10689
|
-
return /* @__PURE__ */
|
|
11091
|
+
return /* @__PURE__ */ jsx29(
|
|
10690
11092
|
"div",
|
|
10691
11093
|
{
|
|
10692
11094
|
className: cn(
|
|
@@ -10700,7 +11102,7 @@ var init_PDFViewerClient = __esm({
|
|
|
10700
11102
|
}
|
|
10701
11103
|
);
|
|
10702
11104
|
}
|
|
10703
|
-
return /* @__PURE__ */
|
|
11105
|
+
return /* @__PURE__ */ jsx29(
|
|
10704
11106
|
"div",
|
|
10705
11107
|
{
|
|
10706
11108
|
className: cn(
|
|
@@ -10710,10 +11112,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10710
11112
|
themeClass,
|
|
10711
11113
|
className
|
|
10712
11114
|
),
|
|
10713
|
-
children: /* @__PURE__ */
|
|
10714
|
-
/* @__PURE__ */
|
|
10715
|
-
/* @__PURE__ */
|
|
10716
|
-
/* @__PURE__ */
|
|
11115
|
+
children: /* @__PURE__ */ jsx29("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs25("div", { className: "text-center p-8", children: [
|
|
11116
|
+
/* @__PURE__ */ jsx29("div", { className: "text-red-500 text-lg font-semibold mb-2", children: "Failed to load PDF" }),
|
|
11117
|
+
/* @__PURE__ */ jsx29("div", { className: "text-gray-500 text-sm", children: error.message }),
|
|
11118
|
+
/* @__PURE__ */ jsx29(
|
|
10717
11119
|
"button",
|
|
10718
11120
|
{
|
|
10719
11121
|
onClick: handleRetry,
|
|
@@ -10728,15 +11130,17 @@ var init_PDFViewerClient = __esm({
|
|
|
10728
11130
|
const renderContainer = () => {
|
|
10729
11131
|
switch (viewMode) {
|
|
10730
11132
|
case "continuous":
|
|
10731
|
-
return /* @__PURE__ */
|
|
11133
|
+
return /* @__PURE__ */ jsx29(ContinuousScrollContainer, {});
|
|
10732
11134
|
case "dual":
|
|
10733
|
-
return /* @__PURE__ */
|
|
11135
|
+
return /* @__PURE__ */ jsx29(DualPageContainer, {});
|
|
11136
|
+
case "book":
|
|
11137
|
+
return /* @__PURE__ */ jsx29(BookModeContainer, {});
|
|
10734
11138
|
case "single":
|
|
10735
11139
|
default:
|
|
10736
|
-
return /* @__PURE__ */
|
|
11140
|
+
return /* @__PURE__ */ jsx29(DocumentContainer, {});
|
|
10737
11141
|
}
|
|
10738
11142
|
};
|
|
10739
|
-
return /* @__PURE__ */
|
|
11143
|
+
return /* @__PURE__ */ jsxs25(
|
|
10740
11144
|
"div",
|
|
10741
11145
|
{
|
|
10742
11146
|
className: cn(
|
|
@@ -10748,14 +11152,14 @@ var init_PDFViewerClient = __esm({
|
|
|
10748
11152
|
className
|
|
10749
11153
|
),
|
|
10750
11154
|
children: [
|
|
10751
|
-
showToolbar && /* @__PURE__ */
|
|
10752
|
-
showAnnotationToolbar && /* @__PURE__ */
|
|
10753
|
-
/* @__PURE__ */
|
|
10754
|
-
showSidebar && sidebarOpen && /* @__PURE__ */
|
|
11155
|
+
showToolbar && /* @__PURE__ */ jsx29(Toolbar, {}),
|
|
11156
|
+
showAnnotationToolbar && /* @__PURE__ */ jsx29(AnnotationToolbar, {}),
|
|
11157
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
11158
|
+
showSidebar && sidebarOpen && /* @__PURE__ */ jsx29(Sidebar, {}),
|
|
10755
11159
|
renderContainer()
|
|
10756
11160
|
] }),
|
|
10757
|
-
showFloatingZoom && /* @__PURE__ */
|
|
10758
|
-
isLoading && /* @__PURE__ */
|
|
11161
|
+
showFloatingZoom && /* @__PURE__ */ jsx29(FloatingZoomControls, { position: "bottom-right" }),
|
|
11162
|
+
isLoading && /* @__PURE__ */ jsx29("div", { className: "absolute inset-0 z-50", children: loadingComponent ?? /* @__PURE__ */ jsx29(
|
|
10759
11163
|
PDFLoadingScreen,
|
|
10760
11164
|
{
|
|
10761
11165
|
phase: loadingProgress?.phase ?? "fetching",
|
|
@@ -10764,10 +11168,10 @@ var init_PDFViewerClient = __esm({
|
|
|
10764
11168
|
totalBytes: loadingProgress?.totalBytes
|
|
10765
11169
|
}
|
|
10766
11170
|
) }),
|
|
10767
|
-
!isLoading && streamingProgress && streamingProgress.total > 0 && streamingProgress.loaded < streamingProgress.total && /* @__PURE__ */
|
|
10768
|
-
/* @__PURE__ */
|
|
10769
|
-
/* @__PURE__ */
|
|
10770
|
-
/* @__PURE__ */
|
|
11171
|
+
!isLoading && streamingProgress && streamingProgress.total > 0 && streamingProgress.loaded < streamingProgress.total && /* @__PURE__ */ jsxs25("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: [
|
|
11172
|
+
/* @__PURE__ */ jsx29("div", { className: "w-3 h-3 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
11173
|
+
/* @__PURE__ */ jsx29("span", { children: "Loading pages..." }),
|
|
11174
|
+
/* @__PURE__ */ jsxs25("span", { className: "text-white/60", children: [
|
|
10771
11175
|
Math.round(streamingProgress.loaded / streamingProgress.total * 100),
|
|
10772
11176
|
"%"
|
|
10773
11177
|
] })
|
|
@@ -10778,8 +11182,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10778
11182
|
});
|
|
10779
11183
|
PDFViewerInnerWithRef = forwardRef(
|
|
10780
11184
|
function PDFViewerInnerWithRef2(props, ref) {
|
|
10781
|
-
const handleRef =
|
|
10782
|
-
const handleReady =
|
|
11185
|
+
const handleRef = useRef20(null);
|
|
11186
|
+
const handleReady = useCallback34((handle) => {
|
|
10783
11187
|
handleRef.current = handle;
|
|
10784
11188
|
if (typeof ref === "function") {
|
|
10785
11189
|
ref(handle);
|
|
@@ -10787,17 +11191,17 @@ var init_PDFViewerClient = __esm({
|
|
|
10787
11191
|
ref.current = handle;
|
|
10788
11192
|
}
|
|
10789
11193
|
}, [ref]);
|
|
10790
|
-
return /* @__PURE__ */
|
|
11194
|
+
return /* @__PURE__ */ jsx29(PDFViewerInner, { ...props, onReady: handleReady });
|
|
10791
11195
|
}
|
|
10792
11196
|
);
|
|
10793
|
-
PDFViewerClient =
|
|
11197
|
+
PDFViewerClient = memo28(
|
|
10794
11198
|
forwardRef(function PDFViewerClient2(props, ref) {
|
|
10795
|
-
return /* @__PURE__ */
|
|
11199
|
+
return /* @__PURE__ */ jsx29(
|
|
10796
11200
|
PDFViewerProvider,
|
|
10797
11201
|
{
|
|
10798
11202
|
theme: props.theme,
|
|
10799
11203
|
defaultSidebarPanel: props.defaultSidebarPanel,
|
|
10800
|
-
children: /* @__PURE__ */
|
|
11204
|
+
children: /* @__PURE__ */ jsx29(PDFViewerInnerWithRef, { ref, ...props })
|
|
10801
11205
|
}
|
|
10802
11206
|
);
|
|
10803
11207
|
})
|
|
@@ -10806,8 +11210,8 @@ var init_PDFViewerClient = __esm({
|
|
|
10806
11210
|
});
|
|
10807
11211
|
|
|
10808
11212
|
// src/components/PDFViewer/PDFViewer.tsx
|
|
10809
|
-
import { lazy, Suspense, memo as
|
|
10810
|
-
import { jsx as
|
|
11213
|
+
import { lazy, Suspense, memo as memo29 } from "react";
|
|
11214
|
+
import { jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
10811
11215
|
var PDFViewerClient3, PDFViewerLoading, PDFViewer;
|
|
10812
11216
|
var init_PDFViewer = __esm({
|
|
10813
11217
|
"src/components/PDFViewer/PDFViewer.tsx"() {
|
|
@@ -10816,10 +11220,10 @@ var init_PDFViewer = __esm({
|
|
|
10816
11220
|
PDFViewerClient3 = lazy(
|
|
10817
11221
|
() => Promise.resolve().then(() => (init_PDFViewerClient(), PDFViewerClient_exports)).then((mod) => ({ default: mod.PDFViewerClient }))
|
|
10818
11222
|
);
|
|
10819
|
-
PDFViewerLoading =
|
|
11223
|
+
PDFViewerLoading = memo29(function PDFViewerLoading2({
|
|
10820
11224
|
className
|
|
10821
11225
|
}) {
|
|
10822
|
-
return /* @__PURE__ */
|
|
11226
|
+
return /* @__PURE__ */ jsx30(
|
|
10823
11227
|
"div",
|
|
10824
11228
|
{
|
|
10825
11229
|
className: cn(
|
|
@@ -10828,18 +11232,18 @@ var init_PDFViewer = __esm({
|
|
|
10828
11232
|
"bg-white dark:bg-gray-900",
|
|
10829
11233
|
className
|
|
10830
11234
|
),
|
|
10831
|
-
children: /* @__PURE__ */
|
|
10832
|
-
/* @__PURE__ */
|
|
10833
|
-
/* @__PURE__ */
|
|
11235
|
+
children: /* @__PURE__ */ jsx30("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs26("div", { className: "flex flex-col items-center", children: [
|
|
11236
|
+
/* @__PURE__ */ jsx30("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
|
|
11237
|
+
/* @__PURE__ */ jsx30("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF viewer..." })
|
|
10834
11238
|
] }) })
|
|
10835
11239
|
}
|
|
10836
11240
|
);
|
|
10837
11241
|
});
|
|
10838
|
-
PDFViewer =
|
|
11242
|
+
PDFViewer = memo29(function PDFViewer2(props) {
|
|
10839
11243
|
if (typeof window === "undefined") {
|
|
10840
|
-
return /* @__PURE__ */
|
|
11244
|
+
return /* @__PURE__ */ jsx30(PDFViewerLoading, { className: props.className });
|
|
10841
11245
|
}
|
|
10842
|
-
return /* @__PURE__ */
|
|
11246
|
+
return /* @__PURE__ */ jsx30(Suspense, { fallback: /* @__PURE__ */ jsx30(PDFViewerLoading, { className: props.className }), children: /* @__PURE__ */ jsx30(PDFViewerClient3, { ...props }) });
|
|
10843
11247
|
});
|
|
10844
11248
|
}
|
|
10845
11249
|
});
|
|
@@ -10854,6 +11258,7 @@ var init_PDFViewer2 = __esm({
|
|
|
10854
11258
|
init_VirtualizedDocumentContainer();
|
|
10855
11259
|
init_ContinuousScrollContainer();
|
|
10856
11260
|
init_DualPageContainer();
|
|
11261
|
+
init_BookModeContainer();
|
|
10857
11262
|
}
|
|
10858
11263
|
});
|
|
10859
11264
|
|
|
@@ -10868,8 +11273,8 @@ init_AnnotationToolbar2();
|
|
|
10868
11273
|
|
|
10869
11274
|
// src/components/Annotations/StickyNote.tsx
|
|
10870
11275
|
init_utils();
|
|
10871
|
-
import { memo as
|
|
10872
|
-
import { jsx as
|
|
11276
|
+
import { memo as memo30, useState as useState23, useRef as useRef21, useEffect as useEffect24, useCallback as useCallback35 } from "react";
|
|
11277
|
+
import { jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
10873
11278
|
var NOTE_COLORS = [
|
|
10874
11279
|
"#fef08a",
|
|
10875
11280
|
// yellow
|
|
@@ -10882,7 +11287,7 @@ var NOTE_COLORS = [
|
|
|
10882
11287
|
"#fed7aa"
|
|
10883
11288
|
// orange
|
|
10884
11289
|
];
|
|
10885
|
-
var StickyNote =
|
|
11290
|
+
var StickyNote = memo30(function StickyNote2({
|
|
10886
11291
|
note,
|
|
10887
11292
|
scale,
|
|
10888
11293
|
isSelected,
|
|
@@ -10895,37 +11300,37 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
10895
11300
|
onDragStart,
|
|
10896
11301
|
className
|
|
10897
11302
|
}) {
|
|
10898
|
-
const [isExpanded, setIsExpanded] =
|
|
10899
|
-
const [localContent, setLocalContent] =
|
|
10900
|
-
const textareaRef =
|
|
10901
|
-
const noteRef =
|
|
10902
|
-
|
|
11303
|
+
const [isExpanded, setIsExpanded] = useState23(false);
|
|
11304
|
+
const [localContent, setLocalContent] = useState23(note.content);
|
|
11305
|
+
const textareaRef = useRef21(null);
|
|
11306
|
+
const noteRef = useRef21(null);
|
|
11307
|
+
useEffect24(() => {
|
|
10903
11308
|
setLocalContent(note.content);
|
|
10904
11309
|
}, [note.content]);
|
|
10905
|
-
|
|
11310
|
+
useEffect24(() => {
|
|
10906
11311
|
if (isEditing && textareaRef.current) {
|
|
10907
11312
|
textareaRef.current.focus();
|
|
10908
11313
|
textareaRef.current.select();
|
|
10909
11314
|
}
|
|
10910
11315
|
}, [isEditing]);
|
|
10911
|
-
const handleClick =
|
|
11316
|
+
const handleClick = useCallback35((e) => {
|
|
10912
11317
|
e.stopPropagation();
|
|
10913
11318
|
onSelect?.();
|
|
10914
11319
|
if (!isExpanded) {
|
|
10915
11320
|
setIsExpanded(true);
|
|
10916
11321
|
}
|
|
10917
11322
|
}, [isExpanded, onSelect]);
|
|
10918
|
-
const handleDoubleClick =
|
|
11323
|
+
const handleDoubleClick = useCallback35((e) => {
|
|
10919
11324
|
e.stopPropagation();
|
|
10920
11325
|
onStartEdit?.();
|
|
10921
11326
|
}, [onStartEdit]);
|
|
10922
|
-
const handleBlur =
|
|
11327
|
+
const handleBlur = useCallback35(() => {
|
|
10923
11328
|
if (isEditing && localContent !== note.content) {
|
|
10924
11329
|
onUpdate?.({ content: localContent });
|
|
10925
11330
|
}
|
|
10926
11331
|
onEndEdit?.();
|
|
10927
11332
|
}, [isEditing, localContent, note.content, onUpdate, onEndEdit]);
|
|
10928
|
-
const handleKeyDown =
|
|
11333
|
+
const handleKeyDown = useCallback35((e) => {
|
|
10929
11334
|
if (e.key === "Escape") {
|
|
10930
11335
|
setLocalContent(note.content);
|
|
10931
11336
|
onEndEdit?.();
|
|
@@ -10933,16 +11338,16 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
10933
11338
|
handleBlur();
|
|
10934
11339
|
}
|
|
10935
11340
|
}, [note.content, onEndEdit, handleBlur]);
|
|
10936
|
-
const handleColorChange =
|
|
11341
|
+
const handleColorChange = useCallback35((color) => {
|
|
10937
11342
|
onUpdate?.({ color });
|
|
10938
11343
|
}, [onUpdate]);
|
|
10939
|
-
const handleCollapse =
|
|
11344
|
+
const handleCollapse = useCallback35((e) => {
|
|
10940
11345
|
e.stopPropagation();
|
|
10941
11346
|
setIsExpanded(false);
|
|
10942
11347
|
onEndEdit?.();
|
|
10943
11348
|
}, [onEndEdit]);
|
|
10944
11349
|
if (!isExpanded) {
|
|
10945
|
-
return /* @__PURE__ */
|
|
11350
|
+
return /* @__PURE__ */ jsx31(
|
|
10946
11351
|
"div",
|
|
10947
11352
|
{
|
|
10948
11353
|
ref: noteRef,
|
|
@@ -10963,14 +11368,14 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
10963
11368
|
onMouseDown: onDragStart,
|
|
10964
11369
|
onTouchStart: onDragStart,
|
|
10965
11370
|
title: note.content || "Empty note",
|
|
10966
|
-
children: /* @__PURE__ */
|
|
11371
|
+
children: /* @__PURE__ */ jsx31(
|
|
10967
11372
|
"svg",
|
|
10968
11373
|
{
|
|
10969
11374
|
className: "w-4 h-4 opacity-70",
|
|
10970
11375
|
fill: "currentColor",
|
|
10971
11376
|
viewBox: "0 0 20 20",
|
|
10972
11377
|
style: { color: "#333" },
|
|
10973
|
-
children: /* @__PURE__ */
|
|
11378
|
+
children: /* @__PURE__ */ jsx31(
|
|
10974
11379
|
"path",
|
|
10975
11380
|
{
|
|
10976
11381
|
fillRule: "evenodd",
|
|
@@ -10983,7 +11388,7 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
10983
11388
|
}
|
|
10984
11389
|
);
|
|
10985
11390
|
}
|
|
10986
|
-
return /* @__PURE__ */
|
|
11391
|
+
return /* @__PURE__ */ jsxs27(
|
|
10987
11392
|
"div",
|
|
10988
11393
|
{
|
|
10989
11394
|
ref: noteRef,
|
|
@@ -11001,14 +11406,14 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
11001
11406
|
},
|
|
11002
11407
|
onClick: handleClick,
|
|
11003
11408
|
children: [
|
|
11004
|
-
/* @__PURE__ */
|
|
11409
|
+
/* @__PURE__ */ jsxs27(
|
|
11005
11410
|
"div",
|
|
11006
11411
|
{
|
|
11007
11412
|
className: "flex items-center justify-between px-2 py-1 border-b border-black/10 cursor-move",
|
|
11008
11413
|
onMouseDown: onDragStart,
|
|
11009
11414
|
onTouchStart: onDragStart,
|
|
11010
11415
|
children: [
|
|
11011
|
-
/* @__PURE__ */
|
|
11416
|
+
/* @__PURE__ */ jsx31("div", { className: "flex gap-1", children: NOTE_COLORS.map((color) => /* @__PURE__ */ jsx31(
|
|
11012
11417
|
"button",
|
|
11013
11418
|
{
|
|
11014
11419
|
className: cn(
|
|
@@ -11025,8 +11430,8 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
11025
11430
|
},
|
|
11026
11431
|
color
|
|
11027
11432
|
)) }),
|
|
11028
|
-
/* @__PURE__ */
|
|
11029
|
-
/* @__PURE__ */
|
|
11433
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex gap-1", children: [
|
|
11434
|
+
/* @__PURE__ */ jsx31(
|
|
11030
11435
|
"button",
|
|
11031
11436
|
{
|
|
11032
11437
|
className: "p-0.5 hover:bg-black/10 rounded",
|
|
@@ -11035,23 +11440,23 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
11035
11440
|
onDelete?.();
|
|
11036
11441
|
},
|
|
11037
11442
|
title: "Delete note",
|
|
11038
|
-
children: /* @__PURE__ */
|
|
11443
|
+
children: /* @__PURE__ */ jsx31("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx31("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" }) })
|
|
11039
11444
|
}
|
|
11040
11445
|
),
|
|
11041
|
-
/* @__PURE__ */
|
|
11446
|
+
/* @__PURE__ */ jsx31(
|
|
11042
11447
|
"button",
|
|
11043
11448
|
{
|
|
11044
11449
|
className: "p-0.5 hover:bg-black/10 rounded",
|
|
11045
11450
|
onClick: handleCollapse,
|
|
11046
11451
|
title: "Collapse note",
|
|
11047
|
-
children: /* @__PURE__ */
|
|
11452
|
+
children: /* @__PURE__ */ jsx31("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx31("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
11048
11453
|
}
|
|
11049
11454
|
)
|
|
11050
11455
|
] })
|
|
11051
11456
|
]
|
|
11052
11457
|
}
|
|
11053
11458
|
),
|
|
11054
|
-
/* @__PURE__ */
|
|
11459
|
+
/* @__PURE__ */ jsx31("div", { className: "p-2", children: isEditing ? /* @__PURE__ */ jsx31(
|
|
11055
11460
|
"textarea",
|
|
11056
11461
|
{
|
|
11057
11462
|
ref: textareaRef,
|
|
@@ -11066,7 +11471,7 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
11066
11471
|
onKeyDown: handleKeyDown,
|
|
11067
11472
|
placeholder: "Enter note..."
|
|
11068
11473
|
}
|
|
11069
|
-
) : /* @__PURE__ */
|
|
11474
|
+
) : /* @__PURE__ */ jsx31(
|
|
11070
11475
|
"div",
|
|
11071
11476
|
{
|
|
11072
11477
|
className: cn(
|
|
@@ -11077,7 +11482,7 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
11077
11482
|
children: note.content || "Double-click to edit..."
|
|
11078
11483
|
}
|
|
11079
11484
|
) }),
|
|
11080
|
-
/* @__PURE__ */
|
|
11485
|
+
/* @__PURE__ */ jsx31("div", { className: "px-2 pb-1 text-[10px] text-gray-500", children: new Date(note.updatedAt).toLocaleDateString() })
|
|
11081
11486
|
]
|
|
11082
11487
|
}
|
|
11083
11488
|
);
|
|
@@ -11085,8 +11490,8 @@ var StickyNote = memo29(function StickyNote2({
|
|
|
11085
11490
|
|
|
11086
11491
|
// src/components/Annotations/DrawingCanvas.tsx
|
|
11087
11492
|
init_utils();
|
|
11088
|
-
import { memo as
|
|
11089
|
-
import { jsx as
|
|
11493
|
+
import { memo as memo31, useRef as useRef22, useCallback as useCallback36, useState as useState24 } from "react";
|
|
11494
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
11090
11495
|
function pointsToSvgPath(points) {
|
|
11091
11496
|
if (points.length === 0) return "";
|
|
11092
11497
|
if (points.length === 1) {
|
|
@@ -11124,7 +11529,7 @@ function simplifyPath(points, tolerance = 1) {
|
|
|
11124
11529
|
result.push(points[points.length - 1]);
|
|
11125
11530
|
return result;
|
|
11126
11531
|
}
|
|
11127
|
-
var DrawingCanvas =
|
|
11532
|
+
var DrawingCanvas = memo31(function DrawingCanvas2({
|
|
11128
11533
|
width,
|
|
11129
11534
|
height,
|
|
11130
11535
|
scale,
|
|
@@ -11134,10 +11539,10 @@ var DrawingCanvas = memo30(function DrawingCanvas2({
|
|
|
11134
11539
|
onDrawingComplete,
|
|
11135
11540
|
className
|
|
11136
11541
|
}) {
|
|
11137
|
-
const svgRef =
|
|
11138
|
-
const [isDrawing, setIsDrawing] =
|
|
11139
|
-
const [currentPath, setCurrentPath] =
|
|
11140
|
-
const getPoint =
|
|
11542
|
+
const svgRef = useRef22(null);
|
|
11543
|
+
const [isDrawing, setIsDrawing] = useState24(false);
|
|
11544
|
+
const [currentPath, setCurrentPath] = useState24([]);
|
|
11545
|
+
const getPoint = useCallback36((e) => {
|
|
11141
11546
|
if (!svgRef.current) return null;
|
|
11142
11547
|
const svg = svgRef.current;
|
|
11143
11548
|
const rect = svg.getBoundingClientRect();
|
|
@@ -11157,7 +11562,7 @@ var DrawingCanvas = memo30(function DrawingCanvas2({
|
|
|
11157
11562
|
y: (clientY - rect.top) / scale
|
|
11158
11563
|
};
|
|
11159
11564
|
}, [scale]);
|
|
11160
|
-
const handleStart =
|
|
11565
|
+
const handleStart = useCallback36((e) => {
|
|
11161
11566
|
if (!isActive) return;
|
|
11162
11567
|
const point = getPoint(e);
|
|
11163
11568
|
if (point) {
|
|
@@ -11165,14 +11570,14 @@ var DrawingCanvas = memo30(function DrawingCanvas2({
|
|
|
11165
11570
|
setCurrentPath([point]);
|
|
11166
11571
|
}
|
|
11167
11572
|
}, [isActive, getPoint]);
|
|
11168
|
-
const handleMove =
|
|
11573
|
+
const handleMove = useCallback36((e) => {
|
|
11169
11574
|
if (!isDrawing || !isActive) return;
|
|
11170
11575
|
const point = getPoint(e);
|
|
11171
11576
|
if (point) {
|
|
11172
11577
|
setCurrentPath((prev) => [...prev, point]);
|
|
11173
11578
|
}
|
|
11174
11579
|
}, [isDrawing, isActive, getPoint]);
|
|
11175
|
-
const handleEnd =
|
|
11580
|
+
const handleEnd = useCallback36(() => {
|
|
11176
11581
|
if (!isDrawing) return;
|
|
11177
11582
|
setIsDrawing(false);
|
|
11178
11583
|
if (currentPath.length >= 2) {
|
|
@@ -11181,7 +11586,7 @@ var DrawingCanvas = memo30(function DrawingCanvas2({
|
|
|
11181
11586
|
}
|
|
11182
11587
|
setCurrentPath([]);
|
|
11183
11588
|
}, [isDrawing, currentPath, onDrawingComplete]);
|
|
11184
|
-
return /* @__PURE__ */
|
|
11589
|
+
return /* @__PURE__ */ jsx32(
|
|
11185
11590
|
"svg",
|
|
11186
11591
|
{
|
|
11187
11592
|
ref: svgRef,
|
|
@@ -11201,7 +11606,7 @@ var DrawingCanvas = memo30(function DrawingCanvas2({
|
|
|
11201
11606
|
onTouchStart: handleStart,
|
|
11202
11607
|
onTouchMove: handleMove,
|
|
11203
11608
|
onTouchEnd: handleEnd,
|
|
11204
|
-
children: isDrawing && currentPath.length > 0 && /* @__PURE__ */
|
|
11609
|
+
children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ jsx32(
|
|
11205
11610
|
"path",
|
|
11206
11611
|
{
|
|
11207
11612
|
d: pointsToSvgPath(currentPath),
|
|
@@ -11219,9 +11624,9 @@ var DrawingCanvas = memo30(function DrawingCanvas2({
|
|
|
11219
11624
|
|
|
11220
11625
|
// src/components/Annotations/ShapeRenderer.tsx
|
|
11221
11626
|
init_utils();
|
|
11222
|
-
import { memo as
|
|
11223
|
-
import { jsx as
|
|
11224
|
-
var ShapeRenderer =
|
|
11627
|
+
import { memo as memo32, useCallback as useCallback37, useState as useState25, useRef as useRef23 } from "react";
|
|
11628
|
+
import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
11629
|
+
var ShapeRenderer = memo32(function ShapeRenderer2({
|
|
11225
11630
|
shape,
|
|
11226
11631
|
scale,
|
|
11227
11632
|
isSelected,
|
|
@@ -11231,18 +11636,18 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11231
11636
|
onDelete: _onDelete,
|
|
11232
11637
|
className
|
|
11233
11638
|
}) {
|
|
11234
|
-
const [_isDragging, setIsDragging] =
|
|
11235
|
-
const [_isResizing, setIsResizing] =
|
|
11236
|
-
const [activeHandle, setActiveHandle] =
|
|
11237
|
-
const startPosRef =
|
|
11238
|
-
const startShapeRef =
|
|
11639
|
+
const [_isDragging, setIsDragging] = useState25(false);
|
|
11640
|
+
const [_isResizing, setIsResizing] = useState25(false);
|
|
11641
|
+
const [activeHandle, setActiveHandle] = useState25(null);
|
|
11642
|
+
const startPosRef = useRef23({ x: 0, y: 0 });
|
|
11643
|
+
const startShapeRef = useRef23({ x: 0, y: 0, width: 0, height: 0 });
|
|
11239
11644
|
const { shapeType, x, y, width, height, color, strokeWidth, id: _id } = shape;
|
|
11240
11645
|
const scaledX = x * scale;
|
|
11241
11646
|
const scaledY = y * scale;
|
|
11242
11647
|
const scaledWidth = width * scale;
|
|
11243
11648
|
const scaledHeight = height * scale;
|
|
11244
11649
|
const scaledStroke = strokeWidth * scale;
|
|
11245
|
-
const getResizeHandles =
|
|
11650
|
+
const getResizeHandles = useCallback37(() => {
|
|
11246
11651
|
const handleSize = 8;
|
|
11247
11652
|
const half = handleSize / 2;
|
|
11248
11653
|
return [
|
|
@@ -11256,7 +11661,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11256
11661
|
{ position: "w", cursor: "ew-resize", x: scaledX - half, y: scaledY + scaledHeight / 2 - half }
|
|
11257
11662
|
];
|
|
11258
11663
|
}, [scaledX, scaledY, scaledWidth, scaledHeight]);
|
|
11259
|
-
const handleMouseDown =
|
|
11664
|
+
const handleMouseDown = useCallback37((e, handle) => {
|
|
11260
11665
|
e.stopPropagation();
|
|
11261
11666
|
onSelect?.();
|
|
11262
11667
|
if (!isEditing) return;
|
|
@@ -11332,7 +11737,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11332
11737
|
document.addEventListener("mousemove", handleMouseMove);
|
|
11333
11738
|
document.addEventListener("mouseup", handleMouseUp);
|
|
11334
11739
|
}, [isEditing, x, y, width, height, scale, onSelect, onUpdate]);
|
|
11335
|
-
const renderShape2 =
|
|
11740
|
+
const renderShape2 = useCallback37(() => {
|
|
11336
11741
|
const commonProps = {
|
|
11337
11742
|
stroke: color,
|
|
11338
11743
|
strokeWidth: scaledStroke,
|
|
@@ -11344,7 +11749,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11344
11749
|
};
|
|
11345
11750
|
switch (shapeType) {
|
|
11346
11751
|
case "rect":
|
|
11347
|
-
return /* @__PURE__ */
|
|
11752
|
+
return /* @__PURE__ */ jsx33(
|
|
11348
11753
|
"rect",
|
|
11349
11754
|
{
|
|
11350
11755
|
x: scaledX,
|
|
@@ -11355,7 +11760,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11355
11760
|
}
|
|
11356
11761
|
);
|
|
11357
11762
|
case "circle":
|
|
11358
|
-
return /* @__PURE__ */
|
|
11763
|
+
return /* @__PURE__ */ jsx33(
|
|
11359
11764
|
"ellipse",
|
|
11360
11765
|
{
|
|
11361
11766
|
cx: scaledX + scaledWidth / 2,
|
|
@@ -11366,7 +11771,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11366
11771
|
}
|
|
11367
11772
|
);
|
|
11368
11773
|
case "line":
|
|
11369
|
-
return /* @__PURE__ */
|
|
11774
|
+
return /* @__PURE__ */ jsx33(
|
|
11370
11775
|
"line",
|
|
11371
11776
|
{
|
|
11372
11777
|
x1: scaledX,
|
|
@@ -11386,22 +11791,22 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11386
11791
|
const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
|
|
11387
11792
|
const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
|
|
11388
11793
|
const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
|
|
11389
|
-
return /* @__PURE__ */
|
|
11390
|
-
/* @__PURE__ */
|
|
11391
|
-
/* @__PURE__ */
|
|
11392
|
-
/* @__PURE__ */
|
|
11794
|
+
return /* @__PURE__ */ jsxs28("g", { children: [
|
|
11795
|
+
/* @__PURE__ */ jsx33("line", { x1: scaledX, y1: scaledY, x2: endX, y2: endY, ...commonProps }),
|
|
11796
|
+
/* @__PURE__ */ jsx33("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
|
|
11797
|
+
/* @__PURE__ */ jsx33("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
|
|
11393
11798
|
] });
|
|
11394
11799
|
default:
|
|
11395
11800
|
return null;
|
|
11396
11801
|
}
|
|
11397
11802
|
}, [shapeType, scaledX, scaledY, scaledWidth, scaledHeight, color, scaledStroke, isSelected]);
|
|
11398
|
-
return /* @__PURE__ */
|
|
11803
|
+
return /* @__PURE__ */ jsxs28(
|
|
11399
11804
|
"g",
|
|
11400
11805
|
{
|
|
11401
11806
|
className: cn("shape-renderer", className),
|
|
11402
11807
|
onMouseDown: (e) => handleMouseDown(e),
|
|
11403
11808
|
children: [
|
|
11404
|
-
/* @__PURE__ */
|
|
11809
|
+
/* @__PURE__ */ jsx33(
|
|
11405
11810
|
"rect",
|
|
11406
11811
|
{
|
|
11407
11812
|
x: scaledX - 5,
|
|
@@ -11414,7 +11819,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11414
11819
|
}
|
|
11415
11820
|
),
|
|
11416
11821
|
renderShape2(),
|
|
11417
|
-
isSelected && /* @__PURE__ */
|
|
11822
|
+
isSelected && /* @__PURE__ */ jsx33(
|
|
11418
11823
|
"rect",
|
|
11419
11824
|
{
|
|
11420
11825
|
x: scaledX - 2,
|
|
@@ -11427,7 +11832,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11427
11832
|
strokeDasharray: "4 2"
|
|
11428
11833
|
}
|
|
11429
11834
|
),
|
|
11430
|
-
isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */
|
|
11835
|
+
isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ jsx33(
|
|
11431
11836
|
"rect",
|
|
11432
11837
|
{
|
|
11433
11838
|
x: handle.x,
|
|
@@ -11447,7 +11852,7 @@ var ShapeRenderer = memo31(function ShapeRenderer2({
|
|
|
11447
11852
|
}
|
|
11448
11853
|
);
|
|
11449
11854
|
});
|
|
11450
|
-
var ShapePreview =
|
|
11855
|
+
var ShapePreview = memo32(function ShapePreview2({
|
|
11451
11856
|
shapeType,
|
|
11452
11857
|
startPoint,
|
|
11453
11858
|
endPoint,
|
|
@@ -11468,9 +11873,9 @@ var ShapePreview = memo31(function ShapePreview2({
|
|
|
11468
11873
|
};
|
|
11469
11874
|
switch (shapeType) {
|
|
11470
11875
|
case "rect":
|
|
11471
|
-
return /* @__PURE__ */
|
|
11876
|
+
return /* @__PURE__ */ jsx33("rect", { x, y, width, height, ...commonProps });
|
|
11472
11877
|
case "circle":
|
|
11473
|
-
return /* @__PURE__ */
|
|
11878
|
+
return /* @__PURE__ */ jsx33(
|
|
11474
11879
|
"ellipse",
|
|
11475
11880
|
{
|
|
11476
11881
|
cx: x + width / 2,
|
|
@@ -11481,7 +11886,7 @@ var ShapePreview = memo31(function ShapePreview2({
|
|
|
11481
11886
|
}
|
|
11482
11887
|
);
|
|
11483
11888
|
case "line":
|
|
11484
|
-
return /* @__PURE__ */
|
|
11889
|
+
return /* @__PURE__ */ jsx33(
|
|
11485
11890
|
"line",
|
|
11486
11891
|
{
|
|
11487
11892
|
x1: startPoint.x * scale,
|
|
@@ -11503,8 +11908,8 @@ var ShapePreview = memo31(function ShapePreview2({
|
|
|
11503
11908
|
const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
|
|
11504
11909
|
const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
|
|
11505
11910
|
const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
|
|
11506
|
-
return /* @__PURE__ */
|
|
11507
|
-
/* @__PURE__ */
|
|
11911
|
+
return /* @__PURE__ */ jsxs28("g", { children: [
|
|
11912
|
+
/* @__PURE__ */ jsx33(
|
|
11508
11913
|
"line",
|
|
11509
11914
|
{
|
|
11510
11915
|
x1: startPoint.x * scale,
|
|
@@ -11514,8 +11919,8 @@ var ShapePreview = memo31(function ShapePreview2({
|
|
|
11514
11919
|
...commonProps
|
|
11515
11920
|
}
|
|
11516
11921
|
),
|
|
11517
|
-
/* @__PURE__ */
|
|
11518
|
-
/* @__PURE__ */
|
|
11922
|
+
/* @__PURE__ */ jsx33("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
|
|
11923
|
+
/* @__PURE__ */ jsx33("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
|
|
11519
11924
|
] });
|
|
11520
11925
|
default:
|
|
11521
11926
|
return null;
|
|
@@ -11524,30 +11929,30 @@ var ShapePreview = memo31(function ShapePreview2({
|
|
|
11524
11929
|
|
|
11525
11930
|
// src/components/Annotations/QuickNoteButton.tsx
|
|
11526
11931
|
init_utils();
|
|
11527
|
-
import { memo as
|
|
11528
|
-
import { jsx as
|
|
11529
|
-
var QuickNoteButton =
|
|
11932
|
+
import { memo as memo33, useCallback as useCallback38, useState as useState26 } from "react";
|
|
11933
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
11934
|
+
var QuickNoteButton = memo33(function QuickNoteButton2({
|
|
11530
11935
|
pageNumber,
|
|
11531
11936
|
scale,
|
|
11532
|
-
position = "top-right",
|
|
11937
|
+
position: position2 = "top-right",
|
|
11533
11938
|
onClick,
|
|
11534
11939
|
className,
|
|
11535
11940
|
visible = true
|
|
11536
11941
|
}) {
|
|
11537
|
-
const [isHovered, setIsHovered] =
|
|
11538
|
-
const handleClick =
|
|
11942
|
+
const [isHovered, setIsHovered] = useState26(false);
|
|
11943
|
+
const handleClick = useCallback38(
|
|
11539
11944
|
(e) => {
|
|
11540
11945
|
e.stopPropagation();
|
|
11541
|
-
const x =
|
|
11542
|
-
const y =
|
|
11946
|
+
const x = position2 === "top-right" ? 80 : 80;
|
|
11947
|
+
const y = position2 === "top-right" ? 20 : 80;
|
|
11543
11948
|
onClick(pageNumber, x / scale, y / scale);
|
|
11544
11949
|
},
|
|
11545
|
-
[pageNumber, onClick,
|
|
11950
|
+
[pageNumber, onClick, position2, scale]
|
|
11546
11951
|
);
|
|
11547
11952
|
if (!visible) {
|
|
11548
11953
|
return null;
|
|
11549
11954
|
}
|
|
11550
|
-
return /* @__PURE__ */
|
|
11955
|
+
return /* @__PURE__ */ jsx34(
|
|
11551
11956
|
"button",
|
|
11552
11957
|
{
|
|
11553
11958
|
onClick: handleClick,
|
|
@@ -11563,13 +11968,13 @@ var QuickNoteButton = memo32(function QuickNoteButton2({
|
|
|
11563
11968
|
"transition-all duration-200",
|
|
11564
11969
|
"focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2",
|
|
11565
11970
|
isHovered && "scale-110",
|
|
11566
|
-
|
|
11567
|
-
|
|
11971
|
+
position2 === "top-right" && "top-3 right-3",
|
|
11972
|
+
position2 === "bottom-right" && "bottom-3 right-3",
|
|
11568
11973
|
className
|
|
11569
11974
|
),
|
|
11570
11975
|
title: "Add quick note",
|
|
11571
11976
|
"aria-label": "Add quick note",
|
|
11572
|
-
children: /* @__PURE__ */
|
|
11977
|
+
children: /* @__PURE__ */ jsx34(
|
|
11573
11978
|
"svg",
|
|
11574
11979
|
{
|
|
11575
11980
|
className: "w-4 h-4 text-yellow-900",
|
|
@@ -11577,7 +11982,7 @@ var QuickNoteButton = memo32(function QuickNoteButton2({
|
|
|
11577
11982
|
viewBox: "0 0 24 24",
|
|
11578
11983
|
stroke: "currentColor",
|
|
11579
11984
|
strokeWidth: 2,
|
|
11580
|
-
children: /* @__PURE__ */
|
|
11985
|
+
children: /* @__PURE__ */ jsx34("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4v16m8-8H4" })
|
|
11581
11986
|
}
|
|
11582
11987
|
)
|
|
11583
11988
|
}
|
|
@@ -11586,36 +11991,36 @@ var QuickNoteButton = memo32(function QuickNoteButton2({
|
|
|
11586
11991
|
|
|
11587
11992
|
// src/components/Annotations/QuickNotePopover.tsx
|
|
11588
11993
|
init_utils();
|
|
11589
|
-
import { memo as
|
|
11590
|
-
import { jsx as
|
|
11591
|
-
var QuickNotePopover =
|
|
11994
|
+
import { memo as memo34, useCallback as useCallback39, useState as useState27, useRef as useRef24, useEffect as useEffect25 } from "react";
|
|
11995
|
+
import { jsx as jsx35, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
11996
|
+
var QuickNotePopover = memo34(function QuickNotePopover2({
|
|
11592
11997
|
visible,
|
|
11593
|
-
position,
|
|
11998
|
+
position: position2,
|
|
11594
11999
|
initialContent = "",
|
|
11595
12000
|
agentLastStatement,
|
|
11596
12001
|
onSave,
|
|
11597
12002
|
onCancel,
|
|
11598
12003
|
className
|
|
11599
12004
|
}) {
|
|
11600
|
-
const [content, setContent] =
|
|
11601
|
-
const textareaRef =
|
|
11602
|
-
const popoverRef =
|
|
11603
|
-
const [adjustedPosition, setAdjustedPosition] =
|
|
11604
|
-
|
|
12005
|
+
const [content, setContent] = useState27(initialContent);
|
|
12006
|
+
const textareaRef = useRef24(null);
|
|
12007
|
+
const popoverRef = useRef24(null);
|
|
12008
|
+
const [adjustedPosition, setAdjustedPosition] = useState27(position2);
|
|
12009
|
+
useEffect25(() => {
|
|
11605
12010
|
if (visible && textareaRef.current) {
|
|
11606
12011
|
textareaRef.current.focus();
|
|
11607
12012
|
}
|
|
11608
12013
|
}, [visible]);
|
|
11609
|
-
|
|
12014
|
+
useEffect25(() => {
|
|
11610
12015
|
if (visible) {
|
|
11611
12016
|
setContent(initialContent);
|
|
11612
12017
|
}
|
|
11613
12018
|
}, [visible, initialContent]);
|
|
11614
|
-
|
|
12019
|
+
useEffect25(() => {
|
|
11615
12020
|
if (!visible || !popoverRef.current) return;
|
|
11616
12021
|
const rect = popoverRef.current.getBoundingClientRect();
|
|
11617
12022
|
const padding = 10;
|
|
11618
|
-
let { x, y } =
|
|
12023
|
+
let { x, y } = position2;
|
|
11619
12024
|
if (x + rect.width > window.innerWidth - padding) {
|
|
11620
12025
|
x = window.innerWidth - rect.width - padding;
|
|
11621
12026
|
}
|
|
@@ -11629,15 +12034,15 @@ var QuickNotePopover = memo33(function QuickNotePopover2({
|
|
|
11629
12034
|
y = padding;
|
|
11630
12035
|
}
|
|
11631
12036
|
setAdjustedPosition({ x, y });
|
|
11632
|
-
}, [
|
|
11633
|
-
const handleSave =
|
|
12037
|
+
}, [position2, visible]);
|
|
12038
|
+
const handleSave = useCallback39(() => {
|
|
11634
12039
|
if (content.trim()) {
|
|
11635
12040
|
onSave(content.trim());
|
|
11636
12041
|
} else {
|
|
11637
12042
|
onCancel();
|
|
11638
12043
|
}
|
|
11639
12044
|
}, [content, onSave, onCancel]);
|
|
11640
|
-
const handleKeyDown =
|
|
12045
|
+
const handleKeyDown = useCallback39(
|
|
11641
12046
|
(e) => {
|
|
11642
12047
|
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
|
11643
12048
|
e.preventDefault();
|
|
@@ -11652,7 +12057,7 @@ var QuickNotePopover = memo33(function QuickNotePopover2({
|
|
|
11652
12057
|
if (!visible) {
|
|
11653
12058
|
return null;
|
|
11654
12059
|
}
|
|
11655
|
-
return /* @__PURE__ */
|
|
12060
|
+
return /* @__PURE__ */ jsxs29(
|
|
11656
12061
|
"div",
|
|
11657
12062
|
{
|
|
11658
12063
|
ref: popoverRef,
|
|
@@ -11671,15 +12076,15 @@ var QuickNotePopover = memo33(function QuickNotePopover2({
|
|
|
11671
12076
|
top: adjustedPosition.y
|
|
11672
12077
|
},
|
|
11673
12078
|
children: [
|
|
11674
|
-
agentLastStatement && /* @__PURE__ */
|
|
11675
|
-
/* @__PURE__ */
|
|
11676
|
-
/* @__PURE__ */
|
|
12079
|
+
agentLastStatement && /* @__PURE__ */ jsx35("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__ */ jsxs29("div", { className: "flex items-start gap-1", children: [
|
|
12080
|
+
/* @__PURE__ */ jsx35("svg", { className: "w-3 h-3 mt-0.5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx35("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" }) }),
|
|
12081
|
+
/* @__PURE__ */ jsxs29("span", { className: "line-clamp-2", children: [
|
|
11677
12082
|
"AI discussed: \u201C",
|
|
11678
12083
|
agentLastStatement,
|
|
11679
12084
|
"\u201D"
|
|
11680
12085
|
] })
|
|
11681
12086
|
] }) }),
|
|
11682
|
-
/* @__PURE__ */
|
|
12087
|
+
/* @__PURE__ */ jsx35(
|
|
11683
12088
|
"textarea",
|
|
11684
12089
|
{
|
|
11685
12090
|
ref: textareaRef,
|
|
@@ -11698,13 +12103,13 @@ var QuickNotePopover = memo33(function QuickNotePopover2({
|
|
|
11698
12103
|
)
|
|
11699
12104
|
}
|
|
11700
12105
|
),
|
|
11701
|
-
/* @__PURE__ */
|
|
11702
|
-
/* @__PURE__ */
|
|
12106
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between mt-2", children: [
|
|
12107
|
+
/* @__PURE__ */ jsxs29("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
11703
12108
|
navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
|
|
11704
12109
|
"+Enter to save"
|
|
11705
12110
|
] }),
|
|
11706
|
-
/* @__PURE__ */
|
|
11707
|
-
/* @__PURE__ */
|
|
12111
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex gap-2", children: [
|
|
12112
|
+
/* @__PURE__ */ jsx35(
|
|
11708
12113
|
"button",
|
|
11709
12114
|
{
|
|
11710
12115
|
onClick: onCancel,
|
|
@@ -11717,7 +12122,7 @@ var QuickNotePopover = memo33(function QuickNotePopover2({
|
|
|
11717
12122
|
children: "Cancel"
|
|
11718
12123
|
}
|
|
11719
12124
|
),
|
|
11720
|
-
/* @__PURE__ */
|
|
12125
|
+
/* @__PURE__ */ jsx35(
|
|
11721
12126
|
"button",
|
|
11722
12127
|
{
|
|
11723
12128
|
onClick: handleSave,
|
|
@@ -11741,23 +12146,23 @@ var QuickNotePopover = memo33(function QuickNotePopover2({
|
|
|
11741
12146
|
|
|
11742
12147
|
// src/components/AskAbout/AskAboutOverlay.tsx
|
|
11743
12148
|
init_utils();
|
|
11744
|
-
import { memo as
|
|
11745
|
-
import { jsx as
|
|
11746
|
-
var AskAboutOverlay =
|
|
12149
|
+
import { memo as memo35 } from "react";
|
|
12150
|
+
import { jsx as jsx36, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
12151
|
+
var AskAboutOverlay = memo35(function AskAboutOverlay2({
|
|
11747
12152
|
visible,
|
|
11748
12153
|
progress,
|
|
11749
|
-
position,
|
|
12154
|
+
position: position2,
|
|
11750
12155
|
size = 60,
|
|
11751
12156
|
className
|
|
11752
12157
|
}) {
|
|
11753
|
-
if (!visible || !
|
|
12158
|
+
if (!visible || !position2) {
|
|
11754
12159
|
return null;
|
|
11755
12160
|
}
|
|
11756
12161
|
const strokeWidth = 4;
|
|
11757
12162
|
const radius = (size - strokeWidth) / 2;
|
|
11758
12163
|
const circumference = 2 * Math.PI * radius;
|
|
11759
12164
|
const strokeDashoffset = circumference * (1 - progress);
|
|
11760
|
-
return /* @__PURE__ */
|
|
12165
|
+
return /* @__PURE__ */ jsxs30(
|
|
11761
12166
|
"div",
|
|
11762
12167
|
{
|
|
11763
12168
|
className: cn(
|
|
@@ -11766,11 +12171,11 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11766
12171
|
className
|
|
11767
12172
|
),
|
|
11768
12173
|
style: {
|
|
11769
|
-
left:
|
|
11770
|
-
top:
|
|
12174
|
+
left: position2.x - size / 2,
|
|
12175
|
+
top: position2.y - size / 2
|
|
11771
12176
|
},
|
|
11772
12177
|
children: [
|
|
11773
|
-
/* @__PURE__ */
|
|
12178
|
+
/* @__PURE__ */ jsxs30(
|
|
11774
12179
|
"svg",
|
|
11775
12180
|
{
|
|
11776
12181
|
width: size,
|
|
@@ -11778,7 +12183,7 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11778
12183
|
viewBox: `0 0 ${size} ${size}`,
|
|
11779
12184
|
className: "transform -rotate-90",
|
|
11780
12185
|
children: [
|
|
11781
|
-
/* @__PURE__ */
|
|
12186
|
+
/* @__PURE__ */ jsx36(
|
|
11782
12187
|
"circle",
|
|
11783
12188
|
{
|
|
11784
12189
|
cx: size / 2,
|
|
@@ -11789,7 +12194,7 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11789
12194
|
strokeWidth
|
|
11790
12195
|
}
|
|
11791
12196
|
),
|
|
11792
|
-
/* @__PURE__ */
|
|
12197
|
+
/* @__PURE__ */ jsx36(
|
|
11793
12198
|
"circle",
|
|
11794
12199
|
{
|
|
11795
12200
|
cx: size / 2,
|
|
@@ -11807,12 +12212,12 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11807
12212
|
]
|
|
11808
12213
|
}
|
|
11809
12214
|
),
|
|
11810
|
-
/* @__PURE__ */
|
|
12215
|
+
/* @__PURE__ */ jsx36(
|
|
11811
12216
|
"div",
|
|
11812
12217
|
{
|
|
11813
12218
|
className: "absolute inset-0 flex items-center justify-center",
|
|
11814
12219
|
style: { color: progress >= 1 ? "#22c55e" : "white" },
|
|
11815
|
-
children: progress >= 1 ? /* @__PURE__ */
|
|
12220
|
+
children: progress >= 1 ? /* @__PURE__ */ jsx36(
|
|
11816
12221
|
"svg",
|
|
11817
12222
|
{
|
|
11818
12223
|
width: "24",
|
|
@@ -11823,9 +12228,9 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11823
12228
|
strokeWidth: "2",
|
|
11824
12229
|
strokeLinecap: "round",
|
|
11825
12230
|
strokeLinejoin: "round",
|
|
11826
|
-
children: /* @__PURE__ */
|
|
12231
|
+
children: /* @__PURE__ */ jsx36("polyline", { points: "20 6 9 17 4 12" })
|
|
11827
12232
|
}
|
|
11828
|
-
) : /* @__PURE__ */
|
|
12233
|
+
) : /* @__PURE__ */ jsxs30(
|
|
11829
12234
|
"svg",
|
|
11830
12235
|
{
|
|
11831
12236
|
width: "20",
|
|
@@ -11837,9 +12242,9 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11837
12242
|
strokeLinecap: "round",
|
|
11838
12243
|
strokeLinejoin: "round",
|
|
11839
12244
|
children: [
|
|
11840
|
-
/* @__PURE__ */
|
|
11841
|
-
/* @__PURE__ */
|
|
11842
|
-
/* @__PURE__ */
|
|
12245
|
+
/* @__PURE__ */ jsx36("circle", { cx: "12", cy: "12", r: "10" }),
|
|
12246
|
+
/* @__PURE__ */ jsx36("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
12247
|
+
/* @__PURE__ */ jsx36("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
11843
12248
|
]
|
|
11844
12249
|
}
|
|
11845
12250
|
)
|
|
@@ -11852,23 +12257,23 @@ var AskAboutOverlay = memo34(function AskAboutOverlay2({
|
|
|
11852
12257
|
|
|
11853
12258
|
// src/components/AskAbout/AskAboutTrigger.tsx
|
|
11854
12259
|
init_utils();
|
|
11855
|
-
import { memo as
|
|
11856
|
-
import { jsx as
|
|
11857
|
-
var AskAboutTrigger =
|
|
11858
|
-
position,
|
|
12260
|
+
import { memo as memo36, useCallback as useCallback40, useState as useState28, useRef as useRef25, useEffect as useEffect26 } from "react";
|
|
12261
|
+
import { jsx as jsx37, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
12262
|
+
var AskAboutTrigger = memo36(function AskAboutTrigger2({
|
|
12263
|
+
position: position2,
|
|
11859
12264
|
onConfirm,
|
|
11860
12265
|
onCancel,
|
|
11861
12266
|
visible,
|
|
11862
12267
|
autoHideDelay = 5e3,
|
|
11863
12268
|
className
|
|
11864
12269
|
}) {
|
|
11865
|
-
const [adjustedPosition, setAdjustedPosition] =
|
|
11866
|
-
const triggerRef =
|
|
11867
|
-
|
|
12270
|
+
const [adjustedPosition, setAdjustedPosition] = useState28(position2);
|
|
12271
|
+
const triggerRef = useRef25(null);
|
|
12272
|
+
useEffect26(() => {
|
|
11868
12273
|
if (!visible || !triggerRef.current) return;
|
|
11869
12274
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
11870
12275
|
const padding = 10;
|
|
11871
|
-
let { x, y } =
|
|
12276
|
+
let { x, y } = position2;
|
|
11872
12277
|
if (x + rect.width / 2 > window.innerWidth - padding) {
|
|
11873
12278
|
x = window.innerWidth - rect.width / 2 - padding;
|
|
11874
12279
|
}
|
|
@@ -11876,23 +12281,23 @@ var AskAboutTrigger = memo35(function AskAboutTrigger2({
|
|
|
11876
12281
|
x = rect.width / 2 + padding;
|
|
11877
12282
|
}
|
|
11878
12283
|
if (y + rect.height > window.innerHeight - padding) {
|
|
11879
|
-
y =
|
|
12284
|
+
y = position2.y - rect.height - 20;
|
|
11880
12285
|
}
|
|
11881
12286
|
setAdjustedPosition({ x, y });
|
|
11882
|
-
}, [
|
|
11883
|
-
|
|
12287
|
+
}, [position2, visible]);
|
|
12288
|
+
useEffect26(() => {
|
|
11884
12289
|
if (!visible || autoHideDelay === 0) return;
|
|
11885
12290
|
const timer = setTimeout(onCancel, autoHideDelay);
|
|
11886
12291
|
return () => clearTimeout(timer);
|
|
11887
12292
|
}, [visible, autoHideDelay, onCancel]);
|
|
11888
|
-
const handleConfirm =
|
|
12293
|
+
const handleConfirm = useCallback40(
|
|
11889
12294
|
(e) => {
|
|
11890
12295
|
e.stopPropagation();
|
|
11891
12296
|
onConfirm();
|
|
11892
12297
|
},
|
|
11893
12298
|
[onConfirm]
|
|
11894
12299
|
);
|
|
11895
|
-
const handleCancel =
|
|
12300
|
+
const handleCancel = useCallback40(
|
|
11896
12301
|
(e) => {
|
|
11897
12302
|
e.stopPropagation();
|
|
11898
12303
|
onCancel();
|
|
@@ -11902,7 +12307,7 @@ var AskAboutTrigger = memo35(function AskAboutTrigger2({
|
|
|
11902
12307
|
if (!visible) {
|
|
11903
12308
|
return null;
|
|
11904
12309
|
}
|
|
11905
|
-
return /* @__PURE__ */
|
|
12310
|
+
return /* @__PURE__ */ jsxs31(
|
|
11906
12311
|
"div",
|
|
11907
12312
|
{
|
|
11908
12313
|
ref: triggerRef,
|
|
@@ -11921,8 +12326,8 @@ var AskAboutTrigger = memo35(function AskAboutTrigger2({
|
|
|
11921
12326
|
transform: "translate(-50%, 0)"
|
|
11922
12327
|
},
|
|
11923
12328
|
children: [
|
|
11924
|
-
/* @__PURE__ */
|
|
11925
|
-
/* @__PURE__ */
|
|
12329
|
+
/* @__PURE__ */ jsx37("span", { className: "text-sm text-gray-600 dark:text-gray-300 px-2", children: "Ask about this?" }),
|
|
12330
|
+
/* @__PURE__ */ jsx37(
|
|
11926
12331
|
"button",
|
|
11927
12332
|
{
|
|
11928
12333
|
onClick: handleConfirm,
|
|
@@ -11935,7 +12340,7 @@ var AskAboutTrigger = memo35(function AskAboutTrigger2({
|
|
|
11935
12340
|
children: "Ask"
|
|
11936
12341
|
}
|
|
11937
12342
|
),
|
|
11938
|
-
/* @__PURE__ */
|
|
12343
|
+
/* @__PURE__ */ jsx37(
|
|
11939
12344
|
"button",
|
|
11940
12345
|
{
|
|
11941
12346
|
onClick: handleCancel,
|
|
@@ -11957,9 +12362,9 @@ var AskAboutTrigger = memo35(function AskAboutTrigger2({
|
|
|
11957
12362
|
// src/components/Minimap/Minimap.tsx
|
|
11958
12363
|
init_hooks();
|
|
11959
12364
|
init_utils();
|
|
11960
|
-
import { memo as
|
|
11961
|
-
import { Fragment as Fragment3, jsx as
|
|
11962
|
-
var PageIndicator =
|
|
12365
|
+
import { memo as memo37, useMemo as useMemo14, useCallback as useCallback41 } from "react";
|
|
12366
|
+
import { Fragment as Fragment3, jsx as jsx38, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
12367
|
+
var PageIndicator = memo37(function PageIndicator2({
|
|
11963
12368
|
pageNumber,
|
|
11964
12369
|
status,
|
|
11965
12370
|
isBookmarked,
|
|
@@ -11973,7 +12378,7 @@ var PageIndicator = memo36(function PageIndicator2({
|
|
|
11973
12378
|
if (status === "visited") return "bg-green-400";
|
|
11974
12379
|
return "bg-gray-200 dark:bg-gray-700";
|
|
11975
12380
|
};
|
|
11976
|
-
return /* @__PURE__ */
|
|
12381
|
+
return /* @__PURE__ */ jsxs32(
|
|
11977
12382
|
"button",
|
|
11978
12383
|
{
|
|
11979
12384
|
onClick,
|
|
@@ -11989,13 +12394,13 @@ var PageIndicator = memo36(function PageIndicator2({
|
|
|
11989
12394
|
title: `Page ${pageNumber}${isBookmarked ? " (bookmarked)" : ""}`,
|
|
11990
12395
|
"aria-label": `Go to page ${pageNumber}`,
|
|
11991
12396
|
children: [
|
|
11992
|
-
isBookmarked && !compact && /* @__PURE__ */
|
|
11993
|
-
showNumber && !compact && /* @__PURE__ */
|
|
12397
|
+
isBookmarked && !compact && /* @__PURE__ */ jsx38("div", { className: "absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full border border-white" }),
|
|
12398
|
+
showNumber && !compact && /* @__PURE__ */ jsx38("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-medium text-white", children: pageNumber })
|
|
11994
12399
|
]
|
|
11995
12400
|
}
|
|
11996
12401
|
);
|
|
11997
12402
|
});
|
|
11998
|
-
var Minimap =
|
|
12403
|
+
var Minimap = memo37(function Minimap2({
|
|
11999
12404
|
variant = "sidebar",
|
|
12000
12405
|
floatingPosition = "right",
|
|
12001
12406
|
maxHeight = 300,
|
|
@@ -12012,14 +12417,14 @@ var Minimap = memo36(function Minimap2({
|
|
|
12012
12417
|
return new Set(bookmarks.map((b) => b.pageNumber));
|
|
12013
12418
|
}, [bookmarks]);
|
|
12014
12419
|
const compact = numPages > 50;
|
|
12015
|
-
const handlePageClick =
|
|
12420
|
+
const handlePageClick = useCallback41(
|
|
12016
12421
|
(pageNumber) => {
|
|
12017
12422
|
goToPage(pageNumber);
|
|
12018
12423
|
onPageClick?.(pageNumber);
|
|
12019
12424
|
},
|
|
12020
12425
|
[goToPage, onPageClick]
|
|
12021
12426
|
);
|
|
12022
|
-
const getPageStatus =
|
|
12427
|
+
const getPageStatus = useCallback41(
|
|
12023
12428
|
(pageNumber) => {
|
|
12024
12429
|
if (pageNumber === currentPage) return "current";
|
|
12025
12430
|
if (bookmarkedPages.has(pageNumber)) return "bookmarked";
|
|
@@ -12032,7 +12437,7 @@ var Minimap = memo36(function Minimap2({
|
|
|
12032
12437
|
const pages = [];
|
|
12033
12438
|
for (let i = 1; i <= numPages; i++) {
|
|
12034
12439
|
pages.push(
|
|
12035
|
-
/* @__PURE__ */
|
|
12440
|
+
/* @__PURE__ */ jsx38(
|
|
12036
12441
|
PageIndicator,
|
|
12037
12442
|
{
|
|
12038
12443
|
pageNumber: i,
|
|
@@ -12053,16 +12458,16 @@ var Minimap = memo36(function Minimap2({
|
|
|
12053
12458
|
if (numPages === 0) {
|
|
12054
12459
|
return null;
|
|
12055
12460
|
}
|
|
12056
|
-
const content = /* @__PURE__ */
|
|
12057
|
-
/* @__PURE__ */
|
|
12058
|
-
/* @__PURE__ */
|
|
12059
|
-
/* @__PURE__ */
|
|
12060
|
-
/* @__PURE__ */
|
|
12461
|
+
const content = /* @__PURE__ */ jsxs32(Fragment3, { children: [
|
|
12462
|
+
/* @__PURE__ */ jsxs32("div", { className: "mb-3", children: [
|
|
12463
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1", children: [
|
|
12464
|
+
/* @__PURE__ */ jsx38("span", { children: "Progress" }),
|
|
12465
|
+
/* @__PURE__ */ jsxs32("span", { children: [
|
|
12061
12466
|
progressPercentage,
|
|
12062
12467
|
"%"
|
|
12063
12468
|
] })
|
|
12064
12469
|
] }),
|
|
12065
|
-
/* @__PURE__ */
|
|
12470
|
+
/* @__PURE__ */ jsx38("div", { className: "h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx38(
|
|
12066
12471
|
"div",
|
|
12067
12472
|
{
|
|
12068
12473
|
className: "h-full bg-green-500 rounded-full transition-all duration-300",
|
|
@@ -12070,7 +12475,7 @@ var Minimap = memo36(function Minimap2({
|
|
|
12070
12475
|
}
|
|
12071
12476
|
) })
|
|
12072
12477
|
] }),
|
|
12073
|
-
/* @__PURE__ */
|
|
12478
|
+
/* @__PURE__ */ jsx38(
|
|
12074
12479
|
"div",
|
|
12075
12480
|
{
|
|
12076
12481
|
className: cn(
|
|
@@ -12081,21 +12486,21 @@ var Minimap = memo36(function Minimap2({
|
|
|
12081
12486
|
children: pageIndicators
|
|
12082
12487
|
}
|
|
12083
12488
|
),
|
|
12084
|
-
/* @__PURE__ */
|
|
12085
|
-
/* @__PURE__ */
|
|
12086
|
-
/* @__PURE__ */
|
|
12087
|
-
/* @__PURE__ */
|
|
12489
|
+
/* @__PURE__ */ jsx38("div", { className: "mt-3 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxs32("div", { className: "flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
12490
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-1", children: [
|
|
12491
|
+
/* @__PURE__ */ jsx38("div", { className: "w-2 h-2 rounded-sm bg-blue-500" }),
|
|
12492
|
+
/* @__PURE__ */ jsx38("span", { children: "Current" })
|
|
12088
12493
|
] }),
|
|
12089
|
-
/* @__PURE__ */
|
|
12090
|
-
/* @__PURE__ */
|
|
12091
|
-
/* @__PURE__ */
|
|
12494
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-1", children: [
|
|
12495
|
+
/* @__PURE__ */ jsx38("div", { className: "w-2 h-2 rounded-sm bg-green-400" }),
|
|
12496
|
+
/* @__PURE__ */ jsx38("span", { children: "Visited" })
|
|
12092
12497
|
] }),
|
|
12093
|
-
/* @__PURE__ */
|
|
12094
|
-
/* @__PURE__ */
|
|
12095
|
-
/* @__PURE__ */
|
|
12498
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-1", children: [
|
|
12499
|
+
/* @__PURE__ */ jsx38("div", { className: "w-2 h-2 rounded-sm bg-yellow-400" }),
|
|
12500
|
+
/* @__PURE__ */ jsx38("span", { children: "Bookmarked" })
|
|
12096
12501
|
] })
|
|
12097
12502
|
] }) }),
|
|
12098
|
-
/* @__PURE__ */
|
|
12503
|
+
/* @__PURE__ */ jsxs32("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
12099
12504
|
visitedCount,
|
|
12100
12505
|
" of ",
|
|
12101
12506
|
numPages,
|
|
@@ -12103,7 +12508,7 @@ var Minimap = memo36(function Minimap2({
|
|
|
12103
12508
|
] })
|
|
12104
12509
|
] });
|
|
12105
12510
|
if (variant === "floating") {
|
|
12106
|
-
return /* @__PURE__ */
|
|
12511
|
+
return /* @__PURE__ */ jsxs32(
|
|
12107
12512
|
"div",
|
|
12108
12513
|
{
|
|
12109
12514
|
className: cn(
|
|
@@ -12119,13 +12524,13 @@ var Minimap = memo36(function Minimap2({
|
|
|
12119
12524
|
),
|
|
12120
12525
|
style: { maxHeight },
|
|
12121
12526
|
children: [
|
|
12122
|
-
/* @__PURE__ */
|
|
12527
|
+
/* @__PURE__ */ jsx38("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 mb-2", children: "Reading Progress" }),
|
|
12123
12528
|
content
|
|
12124
12529
|
]
|
|
12125
12530
|
}
|
|
12126
12531
|
);
|
|
12127
12532
|
}
|
|
12128
|
-
return /* @__PURE__ */
|
|
12533
|
+
return /* @__PURE__ */ jsx38(
|
|
12129
12534
|
"div",
|
|
12130
12535
|
{
|
|
12131
12536
|
className: cn(
|
|
@@ -12145,11 +12550,11 @@ init_FloatingZoomControls2();
|
|
|
12145
12550
|
// src/components/PDFThumbnailNav/PDFThumbnailNav.tsx
|
|
12146
12551
|
init_hooks();
|
|
12147
12552
|
init_utils();
|
|
12148
|
-
import { memo as
|
|
12149
|
-
import { jsx as
|
|
12553
|
+
import { memo as memo38, useEffect as useEffect27, useState as useState29, useRef as useRef26, useCallback as useCallback42 } from "react";
|
|
12554
|
+
import { jsx as jsx39, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
12150
12555
|
var DEFAULT_WIDTH = 612;
|
|
12151
12556
|
var DEFAULT_HEIGHT = 792;
|
|
12152
|
-
var PDFThumbnailNav =
|
|
12557
|
+
var PDFThumbnailNav = memo38(function PDFThumbnailNav2({
|
|
12153
12558
|
thumbnailScale = 0.15,
|
|
12154
12559
|
orientation = "vertical",
|
|
12155
12560
|
maxVisible = 10,
|
|
@@ -12160,14 +12565,14 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12160
12565
|
}) {
|
|
12161
12566
|
const { document: document2, numPages, currentPage } = usePDFViewer();
|
|
12162
12567
|
const { viewerStore } = usePDFViewerStores();
|
|
12163
|
-
const containerRef =
|
|
12164
|
-
const [thumbnails, setThumbnails] =
|
|
12165
|
-
const [visibleRange, setVisibleRange] =
|
|
12166
|
-
const renderQueueRef =
|
|
12167
|
-
const pageCache =
|
|
12568
|
+
const containerRef = useRef26(null);
|
|
12569
|
+
const [thumbnails, setThumbnails] = useState29(/* @__PURE__ */ new Map());
|
|
12570
|
+
const [visibleRange, setVisibleRange] = useState29({ start: 1, end: maxVisible });
|
|
12571
|
+
const renderQueueRef = useRef26(/* @__PURE__ */ new Set());
|
|
12572
|
+
const pageCache = useRef26(/* @__PURE__ */ new Map());
|
|
12168
12573
|
const thumbnailWidth = Math.floor(DEFAULT_WIDTH * thumbnailScale);
|
|
12169
12574
|
const thumbnailHeight = Math.floor(DEFAULT_HEIGHT * thumbnailScale);
|
|
12170
|
-
const updateVisibleRange =
|
|
12575
|
+
const updateVisibleRange = useCallback42(() => {
|
|
12171
12576
|
if (!containerRef.current || numPages === 0) return;
|
|
12172
12577
|
const container = containerRef.current;
|
|
12173
12578
|
const isHorizontal2 = orientation === "horizontal";
|
|
@@ -12179,7 +12584,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12179
12584
|
const lastVisible = Math.min(numPages, firstVisible + visibleCount);
|
|
12180
12585
|
setVisibleRange({ start: firstVisible, end: lastVisible });
|
|
12181
12586
|
}, [numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
|
|
12182
|
-
|
|
12587
|
+
useEffect27(() => {
|
|
12183
12588
|
const container = containerRef.current;
|
|
12184
12589
|
if (!container) return;
|
|
12185
12590
|
const handleScroll = () => {
|
|
@@ -12189,7 +12594,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12189
12594
|
updateVisibleRange();
|
|
12190
12595
|
return () => container.removeEventListener("scroll", handleScroll);
|
|
12191
12596
|
}, [updateVisibleRange]);
|
|
12192
|
-
|
|
12597
|
+
useEffect27(() => {
|
|
12193
12598
|
if (!document2) {
|
|
12194
12599
|
setThumbnails(/* @__PURE__ */ new Map());
|
|
12195
12600
|
pageCache.current.clear();
|
|
@@ -12244,7 +12649,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12244
12649
|
};
|
|
12245
12650
|
renderThumbnails();
|
|
12246
12651
|
}, [document2, visibleRange, thumbnailScale, thumbnails]);
|
|
12247
|
-
|
|
12652
|
+
useEffect27(() => {
|
|
12248
12653
|
if (!containerRef.current || numPages === 0) return;
|
|
12249
12654
|
const container = containerRef.current;
|
|
12250
12655
|
const isHorizontal2 = orientation === "horizontal";
|
|
@@ -12260,12 +12665,12 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12260
12665
|
});
|
|
12261
12666
|
}
|
|
12262
12667
|
}, [currentPage, numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
|
|
12263
|
-
const handleThumbnailClick =
|
|
12668
|
+
const handleThumbnailClick = useCallback42((pageNum) => {
|
|
12264
12669
|
onThumbnailClick?.(pageNum);
|
|
12265
12670
|
viewerStore.getState().requestScrollToPage(pageNum, "smooth");
|
|
12266
12671
|
}, [onThumbnailClick, viewerStore]);
|
|
12267
12672
|
if (!document2 || numPages === 0) {
|
|
12268
|
-
return /* @__PURE__ */
|
|
12673
|
+
return /* @__PURE__ */ jsx39(
|
|
12269
12674
|
"div",
|
|
12270
12675
|
{
|
|
12271
12676
|
className: cn(
|
|
@@ -12286,7 +12691,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12286
12691
|
}
|
|
12287
12692
|
const isHorizontal = orientation === "horizontal";
|
|
12288
12693
|
const totalSize = numPages * ((isHorizontal ? thumbnailWidth : thumbnailHeight) + gap) - gap;
|
|
12289
|
-
return /* @__PURE__ */
|
|
12694
|
+
return /* @__PURE__ */ jsx39(
|
|
12290
12695
|
"div",
|
|
12291
12696
|
{
|
|
12292
12697
|
ref: containerRef,
|
|
@@ -12300,7 +12705,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12300
12705
|
style: {
|
|
12301
12706
|
...isHorizontal ? { overflowX: "auto", overflowY: "hidden" } : { overflowX: "hidden", overflowY: "auto" }
|
|
12302
12707
|
},
|
|
12303
|
-
children: /* @__PURE__ */
|
|
12708
|
+
children: /* @__PURE__ */ jsx39(
|
|
12304
12709
|
"div",
|
|
12305
12710
|
{
|
|
12306
12711
|
className: cn(
|
|
@@ -12317,7 +12722,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12317
12722
|
const thumbnail = thumbnails.get(pageNum);
|
|
12318
12723
|
const isActive = pageNum === currentPage;
|
|
12319
12724
|
const isVisible = pageNum >= visibleRange.start && pageNum <= visibleRange.end;
|
|
12320
|
-
return /* @__PURE__ */
|
|
12725
|
+
return /* @__PURE__ */ jsxs33(
|
|
12321
12726
|
"div",
|
|
12322
12727
|
{
|
|
12323
12728
|
className: cn(
|
|
@@ -12342,7 +12747,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12342
12747
|
}
|
|
12343
12748
|
},
|
|
12344
12749
|
children: [
|
|
12345
|
-
/* @__PURE__ */
|
|
12750
|
+
/* @__PURE__ */ jsx39(
|
|
12346
12751
|
"div",
|
|
12347
12752
|
{
|
|
12348
12753
|
className: "relative bg-white dark:bg-gray-700",
|
|
@@ -12350,7 +12755,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12350
12755
|
width: thumbnailWidth,
|
|
12351
12756
|
height: thumbnailHeight
|
|
12352
12757
|
},
|
|
12353
|
-
children: isVisible && thumbnail?.canvas ? /* @__PURE__ */
|
|
12758
|
+
children: isVisible && thumbnail?.canvas ? /* @__PURE__ */ jsx39(
|
|
12354
12759
|
"img",
|
|
12355
12760
|
{
|
|
12356
12761
|
src: thumbnail.canvas.toDataURL(),
|
|
@@ -12358,10 +12763,10 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12358
12763
|
className: "w-full h-full object-contain",
|
|
12359
12764
|
loading: "lazy"
|
|
12360
12765
|
}
|
|
12361
|
-
) : /* @__PURE__ */
|
|
12766
|
+
) : /* @__PURE__ */ jsx39("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-xs", children: pageNum })
|
|
12362
12767
|
}
|
|
12363
12768
|
),
|
|
12364
|
-
showPageNumbers && /* @__PURE__ */
|
|
12769
|
+
showPageNumbers && /* @__PURE__ */ jsx39(
|
|
12365
12770
|
"div",
|
|
12366
12771
|
{
|
|
12367
12772
|
className: cn(
|
|
@@ -12386,7 +12791,7 @@ var PDFThumbnailNav = memo37(function PDFThumbnailNav2({
|
|
|
12386
12791
|
// src/components/ErrorBoundary/PDFErrorBoundary.tsx
|
|
12387
12792
|
init_utils();
|
|
12388
12793
|
import { Component } from "react";
|
|
12389
|
-
import { jsx as
|
|
12794
|
+
import { jsx as jsx40, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
12390
12795
|
var PDFErrorBoundary = class extends Component {
|
|
12391
12796
|
constructor(props) {
|
|
12392
12797
|
super(props);
|
|
@@ -12414,7 +12819,7 @@ var PDFErrorBoundary = class extends Component {
|
|
|
12414
12819
|
return fallback;
|
|
12415
12820
|
}
|
|
12416
12821
|
if (showDefaultUI) {
|
|
12417
|
-
return /* @__PURE__ */
|
|
12822
|
+
return /* @__PURE__ */ jsx40(
|
|
12418
12823
|
DefaultErrorUI,
|
|
12419
12824
|
{
|
|
12420
12825
|
error,
|
|
@@ -12433,7 +12838,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12433
12838
|
const isNetworkError = error.message.includes("fetch") || error.message.includes("network") || error.message.includes("Failed to load");
|
|
12434
12839
|
let title = "Something went wrong";
|
|
12435
12840
|
let description = error.message;
|
|
12436
|
-
let icon = /* @__PURE__ */
|
|
12841
|
+
let icon = /* @__PURE__ */ jsx40("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx40(
|
|
12437
12842
|
"path",
|
|
12438
12843
|
{
|
|
12439
12844
|
strokeLinecap: "round",
|
|
@@ -12445,7 +12850,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12445
12850
|
if (isPDFError) {
|
|
12446
12851
|
title = "Unable to load PDF";
|
|
12447
12852
|
description = "The PDF file could not be loaded. It may be corrupted or in an unsupported format.";
|
|
12448
|
-
icon = /* @__PURE__ */
|
|
12853
|
+
icon = /* @__PURE__ */ jsx40("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx40(
|
|
12449
12854
|
"path",
|
|
12450
12855
|
{
|
|
12451
12856
|
strokeLinecap: "round",
|
|
@@ -12457,7 +12862,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12457
12862
|
} else if (isNetworkError) {
|
|
12458
12863
|
title = "Network error";
|
|
12459
12864
|
description = "Unable to fetch the PDF file. Please check your internet connection and try again.";
|
|
12460
|
-
icon = /* @__PURE__ */
|
|
12865
|
+
icon = /* @__PURE__ */ jsx40("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx40(
|
|
12461
12866
|
"path",
|
|
12462
12867
|
{
|
|
12463
12868
|
strokeLinecap: "round",
|
|
@@ -12467,7 +12872,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12467
12872
|
}
|
|
12468
12873
|
) });
|
|
12469
12874
|
}
|
|
12470
|
-
return /* @__PURE__ */
|
|
12875
|
+
return /* @__PURE__ */ jsxs34(
|
|
12471
12876
|
"div",
|
|
12472
12877
|
{
|
|
12473
12878
|
className: cn(
|
|
@@ -12480,14 +12885,14 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12480
12885
|
),
|
|
12481
12886
|
children: [
|
|
12482
12887
|
icon,
|
|
12483
|
-
/* @__PURE__ */
|
|
12484
|
-
/* @__PURE__ */
|
|
12485
|
-
/* @__PURE__ */
|
|
12486
|
-
/* @__PURE__ */
|
|
12487
|
-
/* @__PURE__ */
|
|
12888
|
+
/* @__PURE__ */ jsx40("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
|
|
12889
|
+
/* @__PURE__ */ jsx40("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
|
|
12890
|
+
/* @__PURE__ */ jsxs34("details", { className: "mt-4 text-left max-w-md w-full", children: [
|
|
12891
|
+
/* @__PURE__ */ jsx40("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" }),
|
|
12892
|
+
/* @__PURE__ */ jsx40("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
|
|
12488
12893
|
] }),
|
|
12489
|
-
/* @__PURE__ */
|
|
12490
|
-
/* @__PURE__ */
|
|
12894
|
+
/* @__PURE__ */ jsxs34("div", { className: "mt-6 flex gap-3", children: [
|
|
12895
|
+
/* @__PURE__ */ jsx40(
|
|
12491
12896
|
"button",
|
|
12492
12897
|
{
|
|
12493
12898
|
onClick: onReset,
|
|
@@ -12501,7 +12906,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12501
12906
|
children: "Try again"
|
|
12502
12907
|
}
|
|
12503
12908
|
),
|
|
12504
|
-
/* @__PURE__ */
|
|
12909
|
+
/* @__PURE__ */ jsx40(
|
|
12505
12910
|
"button",
|
|
12506
12911
|
{
|
|
12507
12912
|
onClick: () => window.location.reload(),
|
|
@@ -12521,12 +12926,2173 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
12521
12926
|
);
|
|
12522
12927
|
}
|
|
12523
12928
|
function withErrorBoundary({ component, ...props }) {
|
|
12524
|
-
return /* @__PURE__ */
|
|
12929
|
+
return /* @__PURE__ */ jsx40(PDFErrorBoundary, { ...props, children: component });
|
|
12525
12930
|
}
|
|
12526
12931
|
|
|
12527
12932
|
// src/components/index.ts
|
|
12528
12933
|
init_PDFLoadingScreen2();
|
|
12529
12934
|
|
|
12935
|
+
// src/components/TutorMode/TutorModeContainer.tsx
|
|
12936
|
+
init_PDFPage2();
|
|
12937
|
+
init_hooks();
|
|
12938
|
+
import { useEffect as useEffect28, useMemo as useMemo15, useRef as useRef27, useState as useState30 } from "react";
|
|
12939
|
+
import { useStore as useStore2 } from "zustand";
|
|
12940
|
+
|
|
12941
|
+
// src/components/TutorMode/CameraView.tsx
|
|
12942
|
+
import { motion } from "framer-motion";
|
|
12943
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
12944
|
+
function CameraView({
|
|
12945
|
+
camera,
|
|
12946
|
+
children,
|
|
12947
|
+
durationMs = 700,
|
|
12948
|
+
className
|
|
12949
|
+
}) {
|
|
12950
|
+
return /* @__PURE__ */ jsx41(
|
|
12951
|
+
motion.div,
|
|
12952
|
+
{
|
|
12953
|
+
className,
|
|
12954
|
+
style: {
|
|
12955
|
+
transformOrigin: "50% 50%",
|
|
12956
|
+
willChange: "transform",
|
|
12957
|
+
width: "100%",
|
|
12958
|
+
height: "100%",
|
|
12959
|
+
position: "relative"
|
|
12960
|
+
},
|
|
12961
|
+
animate: {
|
|
12962
|
+
scale: camera.scale,
|
|
12963
|
+
x: camera.x,
|
|
12964
|
+
y: camera.y
|
|
12965
|
+
},
|
|
12966
|
+
transition: {
|
|
12967
|
+
duration: durationMs / 1e3,
|
|
12968
|
+
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]
|
|
12969
|
+
},
|
|
12970
|
+
children
|
|
12971
|
+
}
|
|
12972
|
+
);
|
|
12973
|
+
}
|
|
12974
|
+
|
|
12975
|
+
// src/components/TutorMode/CinemaLayer.tsx
|
|
12976
|
+
import { AnimatePresence } from "framer-motion";
|
|
12977
|
+
|
|
12978
|
+
// src/components/TutorMode/SpotlightMask.tsx
|
|
12979
|
+
import { useId } from "react";
|
|
12980
|
+
import { motion as motion2 } from "framer-motion";
|
|
12981
|
+
import { jsx as jsx42, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
12982
|
+
function SpotlightMask({
|
|
12983
|
+
page,
|
|
12984
|
+
bbox,
|
|
12985
|
+
action,
|
|
12986
|
+
durationMs = 400
|
|
12987
|
+
}) {
|
|
12988
|
+
const maskId = useId();
|
|
12989
|
+
const filterId = `${maskId}-blur`;
|
|
12990
|
+
const [x1, y1, x2, y2] = bbox;
|
|
12991
|
+
const w = Math.max(0, x2 - x1);
|
|
12992
|
+
const h = Math.max(0, y2 - y1);
|
|
12993
|
+
const rx = action.shape === "rounded" ? 12 : action.shape === "ellipse" ? w / 2 : 0;
|
|
12994
|
+
const ry = action.shape === "rounded" ? 12 : action.shape === "ellipse" ? h / 2 : 0;
|
|
12995
|
+
const feather = action.feather_px;
|
|
12996
|
+
return /* @__PURE__ */ jsxs35(
|
|
12997
|
+
"svg",
|
|
12998
|
+
{
|
|
12999
|
+
viewBox: `0 0 ${page.width} ${page.height}`,
|
|
13000
|
+
width: page.width,
|
|
13001
|
+
height: page.height,
|
|
13002
|
+
preserveAspectRatio: "none",
|
|
13003
|
+
style: {
|
|
13004
|
+
position: "absolute",
|
|
13005
|
+
inset: 0,
|
|
13006
|
+
pointerEvents: "none",
|
|
13007
|
+
width: page.width,
|
|
13008
|
+
height: page.height
|
|
13009
|
+
},
|
|
13010
|
+
"data-role": "spotlight-mask",
|
|
13011
|
+
children: [
|
|
13012
|
+
/* @__PURE__ */ jsxs35("defs", { children: [
|
|
13013
|
+
/* @__PURE__ */ jsx42("filter", { id: filterId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ jsx42("feGaussianBlur", { in: "SourceGraphic", stdDeviation: feather / 4 }) }),
|
|
13014
|
+
/* @__PURE__ */ jsxs35("mask", { id: maskId, children: [
|
|
13015
|
+
/* @__PURE__ */ jsx42("rect", { x: 0, y: 0, width: page.width, height: page.height, fill: "white" }),
|
|
13016
|
+
action.shape === "ellipse" ? /* @__PURE__ */ jsx42(
|
|
13017
|
+
"ellipse",
|
|
13018
|
+
{
|
|
13019
|
+
cx: (x1 + x2) / 2,
|
|
13020
|
+
cy: (y1 + y2) / 2,
|
|
13021
|
+
rx: w / 2,
|
|
13022
|
+
ry: h / 2,
|
|
13023
|
+
fill: "black",
|
|
13024
|
+
filter: `url(#${filterId})`
|
|
13025
|
+
}
|
|
13026
|
+
) : /* @__PURE__ */ jsx42(
|
|
13027
|
+
"rect",
|
|
13028
|
+
{
|
|
13029
|
+
x: x1,
|
|
13030
|
+
y: y1,
|
|
13031
|
+
width: w,
|
|
13032
|
+
height: h,
|
|
13033
|
+
rx,
|
|
13034
|
+
ry,
|
|
13035
|
+
fill: "black",
|
|
13036
|
+
filter: `url(#${filterId})`
|
|
13037
|
+
}
|
|
13038
|
+
)
|
|
13039
|
+
] })
|
|
13040
|
+
] }),
|
|
13041
|
+
/* @__PURE__ */ jsx42(
|
|
13042
|
+
motion2.rect,
|
|
13043
|
+
{
|
|
13044
|
+
x: 0,
|
|
13045
|
+
y: 0,
|
|
13046
|
+
width: page.width,
|
|
13047
|
+
height: page.height,
|
|
13048
|
+
fill: "black",
|
|
13049
|
+
mask: `url(#${maskId})`,
|
|
13050
|
+
initial: { fillOpacity: 0 },
|
|
13051
|
+
animate: { fillOpacity: action.dim_opacity },
|
|
13052
|
+
exit: { fillOpacity: 0 },
|
|
13053
|
+
transition: { duration: durationMs / 1e3, ease: "easeOut" }
|
|
13054
|
+
}
|
|
13055
|
+
)
|
|
13056
|
+
]
|
|
13057
|
+
}
|
|
13058
|
+
);
|
|
13059
|
+
}
|
|
13060
|
+
|
|
13061
|
+
// src/components/TutorMode/AnimatedUnderline.tsx
|
|
13062
|
+
import { motion as motion3 } from "framer-motion";
|
|
13063
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
13064
|
+
function pathForStyle(x1, x2, y, style) {
|
|
13065
|
+
if (style === "straight") return `M ${x1} ${y} L ${x2} ${y}`;
|
|
13066
|
+
if (style === "double")
|
|
13067
|
+
return `M ${x1} ${y - 3} L ${x2} ${y - 3} M ${x1} ${y + 3} L ${x2} ${y + 3}`;
|
|
13068
|
+
if (style === "wavy") {
|
|
13069
|
+
const steps = Math.max(8, Math.floor((x2 - x1) / 18));
|
|
13070
|
+
let d2 = `M ${x1} ${y}`;
|
|
13071
|
+
for (let i = 1; i <= steps; i++) {
|
|
13072
|
+
const px = x1 + (x2 - x1) * i / steps;
|
|
13073
|
+
const dy = i % 2 === 0 ? 4 : -4;
|
|
13074
|
+
d2 += ` Q ${px - (x2 - x1) / (2 * steps)} ${y + dy} ${px} ${y}`;
|
|
13075
|
+
}
|
|
13076
|
+
return d2;
|
|
13077
|
+
}
|
|
13078
|
+
const segs = 6;
|
|
13079
|
+
let d = `M ${x1} ${y}`;
|
|
13080
|
+
for (let i = 1; i <= segs; i++) {
|
|
13081
|
+
const px = x1 + (x2 - x1) * i / segs;
|
|
13082
|
+
const jitter = (Math.random() - 0.5) * 4;
|
|
13083
|
+
d += ` L ${px} ${y + jitter}`;
|
|
13084
|
+
}
|
|
13085
|
+
return d;
|
|
13086
|
+
}
|
|
13087
|
+
function AnimatedUnderline({ bbox, action }) {
|
|
13088
|
+
const [x1, , x2, y2] = bbox;
|
|
13089
|
+
const y = y2 + 6;
|
|
13090
|
+
const d = pathForStyle(x1, x2, y, action.style);
|
|
13091
|
+
const duration = action.draw_duration_ms / 1e3;
|
|
13092
|
+
return /* @__PURE__ */ jsx43(
|
|
13093
|
+
"svg",
|
|
13094
|
+
{
|
|
13095
|
+
style: {
|
|
13096
|
+
position: "absolute",
|
|
13097
|
+
inset: 0,
|
|
13098
|
+
pointerEvents: "none",
|
|
13099
|
+
overflow: "visible"
|
|
13100
|
+
},
|
|
13101
|
+
"data-role": "underline",
|
|
13102
|
+
children: /* @__PURE__ */ jsx43(
|
|
13103
|
+
motion3.path,
|
|
13104
|
+
{
|
|
13105
|
+
d,
|
|
13106
|
+
fill: "none",
|
|
13107
|
+
stroke: action.color,
|
|
13108
|
+
strokeWidth: 4,
|
|
13109
|
+
strokeLinecap: "round",
|
|
13110
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13111
|
+
animate: { pathLength: 1, opacity: 1 },
|
|
13112
|
+
exit: { opacity: 0 },
|
|
13113
|
+
transition: { duration, ease: "easeOut" }
|
|
13114
|
+
}
|
|
13115
|
+
)
|
|
13116
|
+
}
|
|
13117
|
+
);
|
|
13118
|
+
}
|
|
13119
|
+
|
|
13120
|
+
// src/components/TutorMode/AnimatedHighlight.tsx
|
|
13121
|
+
import { motion as motion4 } from "framer-motion";
|
|
13122
|
+
import { jsx as jsx44 } from "react/jsx-runtime";
|
|
13123
|
+
function AnimatedHighlight({ bbox, action }) {
|
|
13124
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13125
|
+
const w = x2 - x1;
|
|
13126
|
+
const h = y2 - y1;
|
|
13127
|
+
return /* @__PURE__ */ jsx44(
|
|
13128
|
+
motion4.div,
|
|
13129
|
+
{
|
|
13130
|
+
style: {
|
|
13131
|
+
position: "absolute",
|
|
13132
|
+
left: x1,
|
|
13133
|
+
top: y1,
|
|
13134
|
+
height: h,
|
|
13135
|
+
background: action.color,
|
|
13136
|
+
borderRadius: 4,
|
|
13137
|
+
mixBlendMode: "multiply",
|
|
13138
|
+
transformOrigin: "0% 50%",
|
|
13139
|
+
pointerEvents: "none"
|
|
13140
|
+
},
|
|
13141
|
+
initial: { width: 0, opacity: 0.9 },
|
|
13142
|
+
animate: { width: w, opacity: 0.9 },
|
|
13143
|
+
exit: { opacity: 0 },
|
|
13144
|
+
transition: { duration: action.draw_duration_ms / 1e3, ease: "easeOut" },
|
|
13145
|
+
"data-role": "highlight"
|
|
13146
|
+
}
|
|
13147
|
+
);
|
|
13148
|
+
}
|
|
13149
|
+
|
|
13150
|
+
// src/components/TutorMode/PulseOverlay.tsx
|
|
13151
|
+
import { motion as motion5 } from "framer-motion";
|
|
13152
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
13153
|
+
var INTENSITY = {
|
|
13154
|
+
subtle: { scale: 1.02, border: "2px solid rgba(59,130,246,0.6)" },
|
|
13155
|
+
normal: { scale: 1.05, border: "3px solid rgba(59,130,246,0.8)" },
|
|
13156
|
+
strong: { scale: 1.1, border: "4px solid rgba(59,130,246,1.0)" }
|
|
13157
|
+
};
|
|
13158
|
+
function PulseOverlay({ bbox, action }) {
|
|
13159
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13160
|
+
const { scale, border } = INTENSITY[action.intensity];
|
|
13161
|
+
const repeat = action.count === 1 ? 0 : action.count - 1;
|
|
13162
|
+
return /* @__PURE__ */ jsx45(
|
|
13163
|
+
motion5.div,
|
|
13164
|
+
{
|
|
13165
|
+
style: {
|
|
13166
|
+
position: "absolute",
|
|
13167
|
+
left: x1,
|
|
13168
|
+
top: y1,
|
|
13169
|
+
width: x2 - x1,
|
|
13170
|
+
height: y2 - y1,
|
|
13171
|
+
border,
|
|
13172
|
+
borderRadius: 8,
|
|
13173
|
+
pointerEvents: "none",
|
|
13174
|
+
boxSizing: "border-box"
|
|
13175
|
+
},
|
|
13176
|
+
animate: { scale: [1, scale, 1] },
|
|
13177
|
+
transition: {
|
|
13178
|
+
duration: 1.2,
|
|
13179
|
+
times: [0, 0.5, 1],
|
|
13180
|
+
ease: "easeInOut",
|
|
13181
|
+
repeat,
|
|
13182
|
+
repeatType: "loop"
|
|
13183
|
+
},
|
|
13184
|
+
exit: { opacity: 0 },
|
|
13185
|
+
"data-role": "pulse"
|
|
13186
|
+
}
|
|
13187
|
+
);
|
|
13188
|
+
}
|
|
13189
|
+
|
|
13190
|
+
// src/components/TutorMode/CalloutArrow.tsx
|
|
13191
|
+
import { motion as motion6 } from "framer-motion";
|
|
13192
|
+
import { jsx as jsx46, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
13193
|
+
function centerOf(b) {
|
|
13194
|
+
return { x: (b[0] + b[2]) / 2, y: (b[1] + b[3]) / 2 };
|
|
13195
|
+
}
|
|
13196
|
+
function arrowPath(fromBbox, toBbox, curve) {
|
|
13197
|
+
const a = centerOf(fromBbox);
|
|
13198
|
+
const b = centerOf(toBbox);
|
|
13199
|
+
if (curve === "straight") return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
13200
|
+
if (curve === "zigzag") {
|
|
13201
|
+
const mx = (a.x + b.x) / 2;
|
|
13202
|
+
return `M ${a.x} ${a.y} L ${mx} ${a.y} L ${mx} ${b.y} L ${b.x} ${b.y}`;
|
|
13203
|
+
}
|
|
13204
|
+
const dx = b.x - a.x;
|
|
13205
|
+
const dy = b.y - a.y;
|
|
13206
|
+
const cx = (a.x + b.x) / 2 - dy * 0.25;
|
|
13207
|
+
const cy = (a.y + b.y) / 2 + dx * 0.25;
|
|
13208
|
+
return `M ${a.x} ${a.y} Q ${cx} ${cy} ${b.x} ${b.y}`;
|
|
13209
|
+
}
|
|
13210
|
+
function CalloutArrow({ fromBbox, toBbox, action }) {
|
|
13211
|
+
const d = arrowPath(fromBbox, toBbox, action.curve);
|
|
13212
|
+
const label = action.label;
|
|
13213
|
+
const target = centerOf(toBbox);
|
|
13214
|
+
return /* @__PURE__ */ jsxs36(
|
|
13215
|
+
"svg",
|
|
13216
|
+
{
|
|
13217
|
+
style: {
|
|
13218
|
+
position: "absolute",
|
|
13219
|
+
inset: 0,
|
|
13220
|
+
pointerEvents: "none",
|
|
13221
|
+
overflow: "visible"
|
|
13222
|
+
},
|
|
13223
|
+
"data-role": "callout",
|
|
13224
|
+
children: [
|
|
13225
|
+
/* @__PURE__ */ jsx46("defs", { children: /* @__PURE__ */ jsx46(
|
|
13226
|
+
"marker",
|
|
13227
|
+
{
|
|
13228
|
+
id: "arrowhead",
|
|
13229
|
+
viewBox: "0 0 10 10",
|
|
13230
|
+
refX: "8",
|
|
13231
|
+
refY: "5",
|
|
13232
|
+
markerWidth: "8",
|
|
13233
|
+
markerHeight: "8",
|
|
13234
|
+
orient: "auto",
|
|
13235
|
+
children: /* @__PURE__ */ jsx46("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "#3B82F6" })
|
|
13236
|
+
}
|
|
13237
|
+
) }),
|
|
13238
|
+
/* @__PURE__ */ jsx46(
|
|
13239
|
+
motion6.path,
|
|
13240
|
+
{
|
|
13241
|
+
d,
|
|
13242
|
+
fill: "none",
|
|
13243
|
+
stroke: "#3B82F6",
|
|
13244
|
+
strokeWidth: 3,
|
|
13245
|
+
strokeLinecap: "round",
|
|
13246
|
+
markerEnd: "url(#arrowhead)",
|
|
13247
|
+
initial: { pathLength: 0, opacity: 0 },
|
|
13248
|
+
animate: { pathLength: 1, opacity: 1 },
|
|
13249
|
+
exit: { opacity: 0 },
|
|
13250
|
+
transition: { duration: 0.6, ease: "easeOut" }
|
|
13251
|
+
}
|
|
13252
|
+
),
|
|
13253
|
+
label ? /* @__PURE__ */ jsxs36(
|
|
13254
|
+
motion6.g,
|
|
13255
|
+
{
|
|
13256
|
+
initial: { opacity: 0 },
|
|
13257
|
+
animate: { opacity: 1 },
|
|
13258
|
+
exit: { opacity: 0 },
|
|
13259
|
+
transition: { delay: 0.3, duration: 0.3 },
|
|
13260
|
+
children: [
|
|
13261
|
+
/* @__PURE__ */ jsx46(
|
|
13262
|
+
"rect",
|
|
13263
|
+
{
|
|
13264
|
+
x: target.x - 4,
|
|
13265
|
+
y: target.y - 28,
|
|
13266
|
+
width: label.length * 9 + 12,
|
|
13267
|
+
height: 22,
|
|
13268
|
+
rx: 4,
|
|
13269
|
+
fill: "#1F2937"
|
|
13270
|
+
}
|
|
13271
|
+
),
|
|
13272
|
+
/* @__PURE__ */ jsx46(
|
|
13273
|
+
"text",
|
|
13274
|
+
{
|
|
13275
|
+
x: target.x + 2,
|
|
13276
|
+
y: target.y - 12,
|
|
13277
|
+
fill: "white",
|
|
13278
|
+
fontSize: 14,
|
|
13279
|
+
fontFamily: "system-ui, sans-serif",
|
|
13280
|
+
children: label
|
|
13281
|
+
}
|
|
13282
|
+
)
|
|
13283
|
+
]
|
|
13284
|
+
}
|
|
13285
|
+
) : null
|
|
13286
|
+
]
|
|
13287
|
+
}
|
|
13288
|
+
);
|
|
13289
|
+
}
|
|
13290
|
+
|
|
13291
|
+
// src/components/TutorMode/GhostReference.tsx
|
|
13292
|
+
import { motion as motion7 } from "framer-motion";
|
|
13293
|
+
import { jsx as jsx47, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
13294
|
+
var POSITIONS = {
|
|
13295
|
+
"top-right": { top: 40, right: 40 },
|
|
13296
|
+
"top-left": { top: 40, left: 40 },
|
|
13297
|
+
"bottom-right": { bottom: 40, right: 40 },
|
|
13298
|
+
"bottom-left": { bottom: 40, left: 40 }
|
|
13299
|
+
};
|
|
13300
|
+
function GhostReference({
|
|
13301
|
+
page,
|
|
13302
|
+
sourceBbox,
|
|
13303
|
+
sourceBlockText,
|
|
13304
|
+
sourcePageNumber,
|
|
13305
|
+
action
|
|
13306
|
+
}) {
|
|
13307
|
+
const width = 360;
|
|
13308
|
+
const [x1, y1, x2, y2] = sourceBbox;
|
|
13309
|
+
return /* @__PURE__ */ jsxs37(
|
|
13310
|
+
motion7.div,
|
|
13311
|
+
{
|
|
13312
|
+
initial: { opacity: 0, y: 20, scale: 0.95 },
|
|
13313
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
13314
|
+
exit: { opacity: 0, y: 20, scale: 0.95 },
|
|
13315
|
+
transition: { duration: 0.4, ease: "easeOut" },
|
|
13316
|
+
style: {
|
|
13317
|
+
position: "absolute",
|
|
13318
|
+
width,
|
|
13319
|
+
background: "#111",
|
|
13320
|
+
color: "white",
|
|
13321
|
+
borderRadius: 12,
|
|
13322
|
+
padding: 12,
|
|
13323
|
+
boxShadow: "0 10px 40px rgba(0,0,0,0.5)",
|
|
13324
|
+
pointerEvents: "none",
|
|
13325
|
+
fontFamily: "system-ui, sans-serif",
|
|
13326
|
+
fontSize: 13,
|
|
13327
|
+
...POSITIONS[action.position]
|
|
13328
|
+
},
|
|
13329
|
+
"data-role": "ghost-reference",
|
|
13330
|
+
children: [
|
|
13331
|
+
/* @__PURE__ */ jsxs37("div", { style: { opacity: 0.7, fontSize: 11, marginBottom: 6 }, children: [
|
|
13332
|
+
"Page ",
|
|
13333
|
+
sourcePageNumber,
|
|
13334
|
+
" \u2014 ",
|
|
13335
|
+
action.target_block
|
|
13336
|
+
] }),
|
|
13337
|
+
/* @__PURE__ */ jsxs37(
|
|
13338
|
+
"svg",
|
|
13339
|
+
{
|
|
13340
|
+
width: width - 24,
|
|
13341
|
+
height: 160,
|
|
13342
|
+
viewBox: `0 0 ${page.width} ${page.height}`,
|
|
13343
|
+
style: { background: "#1F2937", borderRadius: 6, display: "block" },
|
|
13344
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
13345
|
+
children: [
|
|
13346
|
+
/* @__PURE__ */ jsx47(
|
|
13347
|
+
"rect",
|
|
13348
|
+
{
|
|
13349
|
+
x: 0,
|
|
13350
|
+
y: 0,
|
|
13351
|
+
width: page.width,
|
|
13352
|
+
height: page.height,
|
|
13353
|
+
fill: "#1F2937"
|
|
13354
|
+
}
|
|
13355
|
+
),
|
|
13356
|
+
/* @__PURE__ */ jsx47(
|
|
13357
|
+
"rect",
|
|
13358
|
+
{
|
|
13359
|
+
x: x1,
|
|
13360
|
+
y: y1,
|
|
13361
|
+
width: x2 - x1,
|
|
13362
|
+
height: y2 - y1,
|
|
13363
|
+
fill: "rgba(250,204,21,0.45)",
|
|
13364
|
+
stroke: "#FBBF24",
|
|
13365
|
+
strokeWidth: 8
|
|
13366
|
+
}
|
|
13367
|
+
)
|
|
13368
|
+
]
|
|
13369
|
+
}
|
|
13370
|
+
),
|
|
13371
|
+
/* @__PURE__ */ jsx47(
|
|
13372
|
+
"div",
|
|
13373
|
+
{
|
|
13374
|
+
style: {
|
|
13375
|
+
marginTop: 8,
|
|
13376
|
+
fontSize: 12,
|
|
13377
|
+
lineHeight: 1.4,
|
|
13378
|
+
opacity: 0.9
|
|
13379
|
+
},
|
|
13380
|
+
children: sourceBlockText ?? "(figure)"
|
|
13381
|
+
}
|
|
13382
|
+
)
|
|
13383
|
+
]
|
|
13384
|
+
}
|
|
13385
|
+
);
|
|
13386
|
+
}
|
|
13387
|
+
|
|
13388
|
+
// src/components/TutorMode/BoxOverlay.tsx
|
|
13389
|
+
import { motion as motion8 } from "framer-motion";
|
|
13390
|
+
import { jsx as jsx48 } from "react/jsx-runtime";
|
|
13391
|
+
function BoxOverlay({ bbox, action }) {
|
|
13392
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13393
|
+
return /* @__PURE__ */ jsx48(
|
|
13394
|
+
motion8.div,
|
|
13395
|
+
{
|
|
13396
|
+
initial: { opacity: 0, scale: 0.97 },
|
|
13397
|
+
animate: { opacity: 1, scale: 1 },
|
|
13398
|
+
exit: { opacity: 0 },
|
|
13399
|
+
transition: { duration: 0.35, ease: "easeOut" },
|
|
13400
|
+
style: {
|
|
13401
|
+
position: "absolute",
|
|
13402
|
+
left: x1,
|
|
13403
|
+
top: y1,
|
|
13404
|
+
width: x2 - x1,
|
|
13405
|
+
height: y2 - y1,
|
|
13406
|
+
border: `${action.style === "dashed" ? "3px dashed" : "3px solid"} ${action.color}`,
|
|
13407
|
+
borderRadius: 6,
|
|
13408
|
+
pointerEvents: "none",
|
|
13409
|
+
boxSizing: "border-box"
|
|
13410
|
+
},
|
|
13411
|
+
"data-role": "box"
|
|
13412
|
+
}
|
|
13413
|
+
);
|
|
13414
|
+
}
|
|
13415
|
+
|
|
13416
|
+
// src/components/TutorMode/StickyLabel.tsx
|
|
13417
|
+
import { motion as motion9 } from "framer-motion";
|
|
13418
|
+
import { jsx as jsx49 } from "react/jsx-runtime";
|
|
13419
|
+
function position(bbox, where) {
|
|
13420
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13421
|
+
const cx = (x1 + x2) / 2;
|
|
13422
|
+
const cy = (y1 + y2) / 2;
|
|
13423
|
+
const PAD = 16;
|
|
13424
|
+
switch (where) {
|
|
13425
|
+
case "top":
|
|
13426
|
+
return { left: cx, top: y1 - PAD, transform: "translate(-50%, -100%)" };
|
|
13427
|
+
case "bottom":
|
|
13428
|
+
return { left: cx, top: y2 + PAD, transform: "translate(-50%, 0)" };
|
|
13429
|
+
case "left":
|
|
13430
|
+
return { left: x1 - PAD, top: cy, transform: "translate(-100%, -50%)" };
|
|
13431
|
+
case "right":
|
|
13432
|
+
return { left: x2 + PAD, top: cy, transform: "translate(0, -50%)" };
|
|
13433
|
+
default:
|
|
13434
|
+
return { left: cx, top: y1, transform: "translate(-50%, -100%)" };
|
|
13435
|
+
}
|
|
13436
|
+
}
|
|
13437
|
+
function StickyLabel({ bbox, action }) {
|
|
13438
|
+
return /* @__PURE__ */ jsx49(
|
|
13439
|
+
motion9.div,
|
|
13440
|
+
{
|
|
13441
|
+
initial: { opacity: 0, scale: 0.9 },
|
|
13442
|
+
animate: { opacity: 1, scale: 1 },
|
|
13443
|
+
exit: { opacity: 0 },
|
|
13444
|
+
transition: { duration: 0.35, ease: "easeOut" },
|
|
13445
|
+
style: {
|
|
13446
|
+
position: "absolute",
|
|
13447
|
+
padding: "6px 10px",
|
|
13448
|
+
background: "#FEF3C7",
|
|
13449
|
+
color: "#78350F",
|
|
13450
|
+
borderRadius: 6,
|
|
13451
|
+
boxShadow: "0 3px 10px rgba(0,0,0,0.2)",
|
|
13452
|
+
fontSize: 14,
|
|
13453
|
+
fontFamily: "system-ui, sans-serif",
|
|
13454
|
+
maxWidth: 280,
|
|
13455
|
+
pointerEvents: "none",
|
|
13456
|
+
...position(bbox, action.position)
|
|
13457
|
+
},
|
|
13458
|
+
"data-role": "label",
|
|
13459
|
+
children: action.text
|
|
13460
|
+
}
|
|
13461
|
+
);
|
|
13462
|
+
}
|
|
13463
|
+
|
|
13464
|
+
// src/components/TutorMode/CinemaLayer.tsx
|
|
13465
|
+
import { jsx as jsx50 } from "react/jsx-runtime";
|
|
13466
|
+
function blockBbox(index, block_id) {
|
|
13467
|
+
return index.blockById.get(block_id)?.block.bbox;
|
|
13468
|
+
}
|
|
13469
|
+
function CinemaLayer({
|
|
13470
|
+
page,
|
|
13471
|
+
index,
|
|
13472
|
+
overlays,
|
|
13473
|
+
scale
|
|
13474
|
+
}) {
|
|
13475
|
+
return /* @__PURE__ */ jsx50(
|
|
13476
|
+
"div",
|
|
13477
|
+
{
|
|
13478
|
+
"data-role": "cinema-layer",
|
|
13479
|
+
style: {
|
|
13480
|
+
position: "absolute",
|
|
13481
|
+
inset: 0,
|
|
13482
|
+
transformOrigin: "0 0",
|
|
13483
|
+
transform: `scale(${scale})`,
|
|
13484
|
+
width: page.page_dimensions.width,
|
|
13485
|
+
height: page.page_dimensions.height,
|
|
13486
|
+
pointerEvents: "none",
|
|
13487
|
+
// PDFPage renders internal layers at z-index 10/20/40/45/50
|
|
13488
|
+
// (canvas / text / highlight / focus / annotation). Without an
|
|
13489
|
+
// explicit z-index here, every tutor overlay stacks UNDER the
|
|
13490
|
+
// AnnotationLayer and becomes invisible. 100 puts us above all of
|
|
13491
|
+
// them while still letting the Exit button (z-index 60) remain
|
|
13492
|
+
// reachable because it sits OUTSIDE this stacking context.
|
|
13493
|
+
zIndex: 100
|
|
13494
|
+
},
|
|
13495
|
+
children: /* @__PURE__ */ jsx50(AnimatePresence, { children: overlays.map((overlay) => {
|
|
13496
|
+
switch (overlay.kind) {
|
|
13497
|
+
case "spotlight": {
|
|
13498
|
+
const a = overlay.action;
|
|
13499
|
+
const b = blockBbox(index, a.target_block);
|
|
13500
|
+
if (!b) return null;
|
|
13501
|
+
return /* @__PURE__ */ jsx50(
|
|
13502
|
+
SpotlightMask,
|
|
13503
|
+
{
|
|
13504
|
+
page: page.page_dimensions,
|
|
13505
|
+
bbox: b,
|
|
13506
|
+
action: a
|
|
13507
|
+
},
|
|
13508
|
+
overlay.id
|
|
13509
|
+
);
|
|
13510
|
+
}
|
|
13511
|
+
case "underline": {
|
|
13512
|
+
const a = overlay.action;
|
|
13513
|
+
const b = blockBbox(index, a.target_block);
|
|
13514
|
+
if (!b) return null;
|
|
13515
|
+
return /* @__PURE__ */ jsx50(AnimatedUnderline, { bbox: b, action: a }, overlay.id);
|
|
13516
|
+
}
|
|
13517
|
+
case "highlight": {
|
|
13518
|
+
const a = overlay.action;
|
|
13519
|
+
const b = blockBbox(index, a.target_block);
|
|
13520
|
+
if (!b) return null;
|
|
13521
|
+
return /* @__PURE__ */ jsx50(AnimatedHighlight, { bbox: b, action: a }, overlay.id);
|
|
13522
|
+
}
|
|
13523
|
+
case "pulse": {
|
|
13524
|
+
const a = overlay.action;
|
|
13525
|
+
const b = blockBbox(index, a.target_block);
|
|
13526
|
+
if (!b) return null;
|
|
13527
|
+
return /* @__PURE__ */ jsx50(PulseOverlay, { bbox: b, action: a }, overlay.id);
|
|
13528
|
+
}
|
|
13529
|
+
case "callout": {
|
|
13530
|
+
const a = overlay.action;
|
|
13531
|
+
const from = blockBbox(index, a.from_block);
|
|
13532
|
+
const to = blockBbox(index, a.to_block);
|
|
13533
|
+
if (!from || !to) return null;
|
|
13534
|
+
return /* @__PURE__ */ jsx50(
|
|
13535
|
+
CalloutArrow,
|
|
13536
|
+
{
|
|
13537
|
+
fromBbox: from,
|
|
13538
|
+
toBbox: to,
|
|
13539
|
+
action: a
|
|
13540
|
+
},
|
|
13541
|
+
overlay.id
|
|
13542
|
+
);
|
|
13543
|
+
}
|
|
13544
|
+
case "ghost_reference": {
|
|
13545
|
+
const a = overlay.action;
|
|
13546
|
+
const hit = index.blockById.get(a.target_block);
|
|
13547
|
+
if (!hit) return null;
|
|
13548
|
+
const targetPage = index.byPage.get(a.target_page);
|
|
13549
|
+
if (!targetPage) return null;
|
|
13550
|
+
return /* @__PURE__ */ jsx50(
|
|
13551
|
+
GhostReference,
|
|
13552
|
+
{
|
|
13553
|
+
page: targetPage.page_dimensions,
|
|
13554
|
+
sourceBbox: hit.block.bbox,
|
|
13555
|
+
sourceBlockText: hit.block.text,
|
|
13556
|
+
sourcePageNumber: hit.pageNumber,
|
|
13557
|
+
action: a
|
|
13558
|
+
},
|
|
13559
|
+
overlay.id
|
|
13560
|
+
);
|
|
13561
|
+
}
|
|
13562
|
+
case "box": {
|
|
13563
|
+
const a = overlay.action;
|
|
13564
|
+
const b = blockBbox(index, a.target_block);
|
|
13565
|
+
if (!b) return null;
|
|
13566
|
+
return /* @__PURE__ */ jsx50(BoxOverlay, { bbox: b, action: a }, overlay.id);
|
|
13567
|
+
}
|
|
13568
|
+
case "label": {
|
|
13569
|
+
const a = overlay.action;
|
|
13570
|
+
const b = blockBbox(index, a.target_block);
|
|
13571
|
+
if (!b) return null;
|
|
13572
|
+
return /* @__PURE__ */ jsx50(StickyLabel, { bbox: b, action: a }, overlay.id);
|
|
13573
|
+
}
|
|
13574
|
+
case "clear":
|
|
13575
|
+
case "camera":
|
|
13576
|
+
return null;
|
|
13577
|
+
}
|
|
13578
|
+
}) })
|
|
13579
|
+
}
|
|
13580
|
+
);
|
|
13581
|
+
}
|
|
13582
|
+
|
|
13583
|
+
// src/components/TutorMode/SubtitleBar.tsx
|
|
13584
|
+
import { AnimatePresence as AnimatePresence2, motion as motion10 } from "framer-motion";
|
|
13585
|
+
import { jsx as jsx51 } from "react/jsx-runtime";
|
|
13586
|
+
function SubtitleBar({ text }) {
|
|
13587
|
+
return /* @__PURE__ */ jsx51(AnimatePresence2, { children: text ? /* @__PURE__ */ jsx51(
|
|
13588
|
+
motion10.div,
|
|
13589
|
+
{
|
|
13590
|
+
initial: { opacity: 0, y: 20 },
|
|
13591
|
+
animate: { opacity: 1, y: 0 },
|
|
13592
|
+
exit: { opacity: 0, y: 20 },
|
|
13593
|
+
transition: { duration: 0.3 },
|
|
13594
|
+
style: {
|
|
13595
|
+
position: "absolute",
|
|
13596
|
+
left: "50%",
|
|
13597
|
+
bottom: 32,
|
|
13598
|
+
transform: "translateX(-50%)",
|
|
13599
|
+
background: "rgba(0,0,0,0.75)",
|
|
13600
|
+
color: "white",
|
|
13601
|
+
padding: "10px 18px",
|
|
13602
|
+
borderRadius: 8,
|
|
13603
|
+
maxWidth: "80%",
|
|
13604
|
+
fontSize: 16,
|
|
13605
|
+
lineHeight: 1.4,
|
|
13606
|
+
fontFamily: "system-ui, sans-serif",
|
|
13607
|
+
pointerEvents: "none",
|
|
13608
|
+
zIndex: 50,
|
|
13609
|
+
textAlign: "center"
|
|
13610
|
+
},
|
|
13611
|
+
"data-role": "subtitle-bar",
|
|
13612
|
+
children: text
|
|
13613
|
+
},
|
|
13614
|
+
text
|
|
13615
|
+
) : null });
|
|
13616
|
+
}
|
|
13617
|
+
|
|
13618
|
+
// src/director/storyboard-engine.ts
|
|
13619
|
+
init_narration_store();
|
|
13620
|
+
init_camera_math();
|
|
13621
|
+
var DEFAULT_MIN_OVERLAY_MS = 3500;
|
|
13622
|
+
var StoryboardEngine = class {
|
|
13623
|
+
constructor(deps) {
|
|
13624
|
+
this.pendingTimers = /* @__PURE__ */ new Set();
|
|
13625
|
+
this.currentStoryboardId = 0;
|
|
13626
|
+
this.deps = deps;
|
|
13627
|
+
}
|
|
13628
|
+
/**
|
|
13629
|
+
* Execute a new storyboard. Cancels in-flight steps from the previous storyboard
|
|
13630
|
+
* and smoothly transitions the camera/overlays from the current state.
|
|
13631
|
+
*/
|
|
13632
|
+
execute(storyboard) {
|
|
13633
|
+
this.cancelPending();
|
|
13634
|
+
this.currentStoryboardId += 1;
|
|
13635
|
+
const storyboardId = this.currentStoryboardId;
|
|
13636
|
+
const { narrationStore } = this.deps;
|
|
13637
|
+
narrationStore.getState().setEngineStatus("transitioning");
|
|
13638
|
+
narrationStore.getState().setLastStoryboard(storyboard);
|
|
13639
|
+
let steps = [...storyboard.steps].sort((a, b) => a.at_ms - b.at_ms);
|
|
13640
|
+
const hasCamera = steps.some((s) => s.action.type === "camera");
|
|
13641
|
+
if (!hasCamera) {
|
|
13642
|
+
const focus = steps.find(
|
|
13643
|
+
(s) => s.action.type !== "clear" && "target_block" in s.action && s.action.target_block
|
|
13644
|
+
);
|
|
13645
|
+
if (focus && focus.action.type !== "clear" && "target_block" in focus.action) {
|
|
13646
|
+
steps = [
|
|
13647
|
+
{
|
|
13648
|
+
at_ms: 0,
|
|
13649
|
+
duration_ms: 700,
|
|
13650
|
+
action: {
|
|
13651
|
+
type: "camera",
|
|
13652
|
+
target_block: focus.action.target_block,
|
|
13653
|
+
scale: 1,
|
|
13654
|
+
padding: 60,
|
|
13655
|
+
easing: "ease-out"
|
|
13656
|
+
}
|
|
13657
|
+
},
|
|
13658
|
+
...steps
|
|
13659
|
+
];
|
|
13660
|
+
}
|
|
13661
|
+
}
|
|
13662
|
+
for (const step of steps) {
|
|
13663
|
+
const timer = setTimeout(() => {
|
|
13664
|
+
if (storyboardId !== this.currentStoryboardId) return;
|
|
13665
|
+
this.runStep(step);
|
|
13666
|
+
}, step.at_ms);
|
|
13667
|
+
this.pendingTimers.add(timer);
|
|
13668
|
+
}
|
|
13669
|
+
const markExecuting = setTimeout(() => {
|
|
13670
|
+
if (storyboardId !== this.currentStoryboardId) return;
|
|
13671
|
+
narrationStore.getState().setEngineStatus("executing");
|
|
13672
|
+
}, 0);
|
|
13673
|
+
this.pendingTimers.add(markExecuting);
|
|
13674
|
+
const last = steps[steps.length - 1];
|
|
13675
|
+
if (last) {
|
|
13676
|
+
const totalMs = last.at_ms + last.duration_ms;
|
|
13677
|
+
const markIdle = setTimeout(() => {
|
|
13678
|
+
if (storyboardId !== this.currentStoryboardId) return;
|
|
13679
|
+
narrationStore.getState().setEngineStatus("idle");
|
|
13680
|
+
}, totalMs + 50);
|
|
13681
|
+
this.pendingTimers.add(markIdle);
|
|
13682
|
+
}
|
|
13683
|
+
}
|
|
13684
|
+
/** Abort all pending steps and set engine status to idle. */
|
|
13685
|
+
cancelPending() {
|
|
13686
|
+
for (const t of this.pendingTimers) clearTimeout(t);
|
|
13687
|
+
this.pendingTimers.clear();
|
|
13688
|
+
this.deps.narrationStore.getState().setEngineStatus("idle");
|
|
13689
|
+
}
|
|
13690
|
+
/** Reset visuals: clear overlays, fit camera back to page. */
|
|
13691
|
+
resetVisuals() {
|
|
13692
|
+
this.cancelPending();
|
|
13693
|
+
const { narrationStore, bboxIndex, getViewport } = this.deps;
|
|
13694
|
+
narrationStore.getState().clearOverlays();
|
|
13695
|
+
const viewport = getViewport();
|
|
13696
|
+
const currentPage = narrationStore.getState().currentPage;
|
|
13697
|
+
const pageDims = bboxIndex.byPage.get(currentPage);
|
|
13698
|
+
const fit = pageDims && viewport.width > 0 && viewport.height > 0 ? fitPageScale(pageDims.page_dimensions, viewport) * 0.95 : 1;
|
|
13699
|
+
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0, easing: "ease-in-out" });
|
|
13700
|
+
}
|
|
13701
|
+
/** Execute one step — dispatch to narrationStore. Returns true if applied. */
|
|
13702
|
+
runStep(step) {
|
|
13703
|
+
const action = step.action;
|
|
13704
|
+
const { narrationStore, bboxIndex } = this.deps;
|
|
13705
|
+
if ("target_block" in action && action.target_block) {
|
|
13706
|
+
if (!bboxIndex.blockById.has(action.target_block)) {
|
|
13707
|
+
narrationStore.getState().appendDebugEvent({
|
|
13708
|
+
kind: "llm-error",
|
|
13709
|
+
summary: `dropped ${action.type} step \u2192 unknown target_block "${action.target_block}"`,
|
|
13710
|
+
payload: { action, validIds: [...bboxIndex.blockById.keys()] }
|
|
13711
|
+
});
|
|
13712
|
+
return false;
|
|
13713
|
+
}
|
|
13714
|
+
}
|
|
13715
|
+
if ("from_block" in action && action.from_block) {
|
|
13716
|
+
if (!bboxIndex.blockById.has(action.from_block)) {
|
|
13717
|
+
narrationStore.getState().appendDebugEvent({
|
|
13718
|
+
kind: "llm-error",
|
|
13719
|
+
summary: `dropped ${action.type} step \u2192 unknown from_block "${action.from_block}"`,
|
|
13720
|
+
payload: { action }
|
|
13721
|
+
});
|
|
13722
|
+
return false;
|
|
13723
|
+
}
|
|
13724
|
+
}
|
|
13725
|
+
if ("to_block" in action && action.to_block) {
|
|
13726
|
+
if (!bboxIndex.blockById.has(action.to_block)) {
|
|
13727
|
+
narrationStore.getState().appendDebugEvent({
|
|
13728
|
+
kind: "llm-error",
|
|
13729
|
+
summary: `dropped ${action.type} step \u2192 unknown to_block "${action.to_block}"`,
|
|
13730
|
+
payload: { action }
|
|
13731
|
+
});
|
|
13732
|
+
return false;
|
|
13733
|
+
}
|
|
13734
|
+
}
|
|
13735
|
+
if (action.type === "camera") {
|
|
13736
|
+
this.applyCamera(action, step.duration_ms);
|
|
13737
|
+
return true;
|
|
13738
|
+
}
|
|
13739
|
+
if (action.type === "clear") {
|
|
13740
|
+
const targets = action.targets;
|
|
13741
|
+
if (targets === "all" || targets === "overlays") {
|
|
13742
|
+
narrationStore.getState().clearOverlays();
|
|
13743
|
+
} else if (targets === "spotlights") {
|
|
13744
|
+
narrationStore.getState().clearOverlays((o) => o.kind === "spotlight");
|
|
13745
|
+
} else if (Array.isArray(targets)) {
|
|
13746
|
+
const ids = new Set(targets);
|
|
13747
|
+
narrationStore.getState().clearOverlays((o) => ids.has(o.id));
|
|
13748
|
+
}
|
|
13749
|
+
return true;
|
|
13750
|
+
}
|
|
13751
|
+
const minMs = this.deps.minOverlayDurationMs ?? DEFAULT_MIN_OVERLAY_MS;
|
|
13752
|
+
const visibleMs = Math.max(step.duration_ms, minMs);
|
|
13753
|
+
const overlay = {
|
|
13754
|
+
id: makeOverlayId(action),
|
|
13755
|
+
kind: action.type,
|
|
13756
|
+
action,
|
|
13757
|
+
createdAt: Date.now(),
|
|
13758
|
+
expiresAt: Date.now() + visibleMs
|
|
13759
|
+
};
|
|
13760
|
+
narrationStore.getState().addOverlay(overlay);
|
|
13761
|
+
const timer = setTimeout(() => {
|
|
13762
|
+
narrationStore.getState().removeOverlay(overlay.id);
|
|
13763
|
+
}, visibleMs);
|
|
13764
|
+
this.pendingTimers.add(timer);
|
|
13765
|
+
return true;
|
|
13766
|
+
}
|
|
13767
|
+
applyCamera(action, durationMs) {
|
|
13768
|
+
const { narrationStore, bboxIndex, getViewport } = this.deps;
|
|
13769
|
+
const viewport = getViewport();
|
|
13770
|
+
let bbox = action.target_bbox;
|
|
13771
|
+
let pageDims = void 0;
|
|
13772
|
+
if (!bbox && action.target_block) {
|
|
13773
|
+
const hit = bboxIndex.blockById.get(action.target_block);
|
|
13774
|
+
if (!hit) return;
|
|
13775
|
+
bbox = hit.block.bbox;
|
|
13776
|
+
pageDims = bboxIndex.byPage.get(hit.pageNumber);
|
|
13777
|
+
} else if (bbox) {
|
|
13778
|
+
pageDims = bboxIndex.byPage.get(narrationStore.getState().currentPage);
|
|
13779
|
+
}
|
|
13780
|
+
if (!bbox || !pageDims) return;
|
|
13781
|
+
const fit = fitPageScale(pageDims.page_dimensions, viewport);
|
|
13782
|
+
const requested = Math.max(0.5, Math.min(3, action.scale ?? 1));
|
|
13783
|
+
const finalScale = fit * requested;
|
|
13784
|
+
const [x1, y1, x2, y2] = bbox;
|
|
13785
|
+
const blockCX = (x1 + x2) / 2;
|
|
13786
|
+
const blockCY = (y1 + y2) / 2;
|
|
13787
|
+
const pageCX = pageDims.page_dimensions.width / 2;
|
|
13788
|
+
const pageCY = pageDims.page_dimensions.height / 2;
|
|
13789
|
+
const x = (pageCX - blockCX) * finalScale;
|
|
13790
|
+
const y = (pageCY - blockCY) * finalScale;
|
|
13791
|
+
const camera = {
|
|
13792
|
+
scale: finalScale,
|
|
13793
|
+
x,
|
|
13794
|
+
y,
|
|
13795
|
+
easing: action.easing
|
|
13796
|
+
};
|
|
13797
|
+
narrationStore.getState().setCamera(camera);
|
|
13798
|
+
void durationMs;
|
|
13799
|
+
void computeCameraForBlock;
|
|
13800
|
+
}
|
|
13801
|
+
};
|
|
13802
|
+
|
|
13803
|
+
// src/director/storyboard-schema.ts
|
|
13804
|
+
import { z } from "zod";
|
|
13805
|
+
var BBoxCoordsSchema = z.tuple([z.number(), z.number(), z.number(), z.number()]);
|
|
13806
|
+
var CameraSchema = z.object({
|
|
13807
|
+
type: z.literal("camera"),
|
|
13808
|
+
target_block: z.string().optional(),
|
|
13809
|
+
target_bbox: BBoxCoordsSchema.optional(),
|
|
13810
|
+
scale: z.number().min(0.5).max(4).default(1),
|
|
13811
|
+
padding: z.number().min(0).max(400).default(80),
|
|
13812
|
+
easing: z.enum(["linear", "ease-in", "ease-out", "ease-in-out"]).default("ease-in-out")
|
|
13813
|
+
}).refine((a) => !!a.target_block || !!a.target_bbox, {
|
|
13814
|
+
message: "camera requires target_block or target_bbox"
|
|
13815
|
+
});
|
|
13816
|
+
var SpotlightSchema = z.object({
|
|
13817
|
+
type: z.literal("spotlight"),
|
|
13818
|
+
target_block: z.string(),
|
|
13819
|
+
dim_opacity: z.number().min(0).max(1).default(0.65),
|
|
13820
|
+
feather_px: z.number().min(0).max(200).default(40),
|
|
13821
|
+
shape: z.enum(["rect", "rounded", "ellipse"]).default("rounded")
|
|
13822
|
+
});
|
|
13823
|
+
var UnderlineSchema = z.object({
|
|
13824
|
+
type: z.literal("underline"),
|
|
13825
|
+
target_block: z.string(),
|
|
13826
|
+
color: z.string().default("#FBBF24"),
|
|
13827
|
+
style: z.enum(["straight", "sketch", "double", "wavy"]).default("sketch"),
|
|
13828
|
+
draw_duration_ms: z.number().min(100).max(3e3).default(600)
|
|
13829
|
+
});
|
|
13830
|
+
var HighlightSchema = z.object({
|
|
13831
|
+
type: z.literal("highlight"),
|
|
13832
|
+
target_block: z.string(),
|
|
13833
|
+
color: z.string().default("rgba(250, 204, 21, 0.35)"),
|
|
13834
|
+
draw_duration_ms: z.number().min(100).max(3e3).default(500)
|
|
13835
|
+
});
|
|
13836
|
+
var PulseSchema = z.object({
|
|
13837
|
+
type: z.literal("pulse"),
|
|
13838
|
+
target_block: z.string(),
|
|
13839
|
+
count: z.number().int().min(1).max(5).default(2),
|
|
13840
|
+
intensity: z.enum(["subtle", "normal", "strong"]).default("normal")
|
|
13841
|
+
});
|
|
13842
|
+
var CalloutSchema = z.object({
|
|
13843
|
+
type: z.literal("callout"),
|
|
13844
|
+
from_block: z.string(),
|
|
13845
|
+
to_block: z.string(),
|
|
13846
|
+
label: z.string().max(120).optional(),
|
|
13847
|
+
curve: z.enum(["straight", "curved", "zigzag"]).default("curved")
|
|
13848
|
+
});
|
|
13849
|
+
var GhostReferenceSchema = z.object({
|
|
13850
|
+
type: z.literal("ghost_reference"),
|
|
13851
|
+
target_page: z.number().int().min(1),
|
|
13852
|
+
target_block: z.string(),
|
|
13853
|
+
position: z.enum(["top-right", "top-left", "bottom-right", "bottom-left"]).default("top-right")
|
|
13854
|
+
});
|
|
13855
|
+
var BoxSchema = z.object({
|
|
13856
|
+
type: z.literal("box"),
|
|
13857
|
+
target_block: z.string(),
|
|
13858
|
+
color: z.string().default("#3B82F6"),
|
|
13859
|
+
style: z.enum(["solid", "dashed"]).default("solid")
|
|
13860
|
+
});
|
|
13861
|
+
var LabelSchema = z.object({
|
|
13862
|
+
type: z.literal("label"),
|
|
13863
|
+
target_block: z.string(),
|
|
13864
|
+
text: z.string().min(1).max(120),
|
|
13865
|
+
position: z.enum(["top", "bottom", "left", "right"]).default("top")
|
|
13866
|
+
});
|
|
13867
|
+
var ClearSchema = z.object({
|
|
13868
|
+
type: z.literal("clear"),
|
|
13869
|
+
targets: z.union([z.enum(["all", "spotlights", "overlays"]), z.array(z.string())]).default("overlays")
|
|
13870
|
+
});
|
|
13871
|
+
var StoryboardActionSchema = z.union([
|
|
13872
|
+
CameraSchema,
|
|
13873
|
+
SpotlightSchema,
|
|
13874
|
+
UnderlineSchema,
|
|
13875
|
+
HighlightSchema,
|
|
13876
|
+
PulseSchema,
|
|
13877
|
+
CalloutSchema,
|
|
13878
|
+
GhostReferenceSchema,
|
|
13879
|
+
BoxSchema,
|
|
13880
|
+
LabelSchema,
|
|
13881
|
+
ClearSchema
|
|
13882
|
+
]);
|
|
13883
|
+
var StoryboardStepSchema = z.object({
|
|
13884
|
+
at_ms: z.number().min(0).max(5e3).default(0),
|
|
13885
|
+
duration_ms: z.number().min(100).max(5e3).default(800),
|
|
13886
|
+
action: StoryboardActionSchema
|
|
13887
|
+
});
|
|
13888
|
+
var StoryboardSchema = z.object({
|
|
13889
|
+
version: z.literal(1),
|
|
13890
|
+
reasoning: z.string().max(500).default(""),
|
|
13891
|
+
steps: z.array(StoryboardStepSchema).min(1).max(4)
|
|
13892
|
+
});
|
|
13893
|
+
function storyboardJsonSchema(opts = {}) {
|
|
13894
|
+
const { validBlockIds, validCrossPageBlockIds } = opts;
|
|
13895
|
+
const blockIdSchema = validBlockIds && validBlockIds.length > 0 ? { type: ["string", "null"], enum: [...validBlockIds, null] } : { type: ["string", "null"] };
|
|
13896
|
+
const crossPageBlockIdSchema = validCrossPageBlockIds && validCrossPageBlockIds.length > 0 ? {
|
|
13897
|
+
type: ["string", "null"],
|
|
13898
|
+
enum: [...validCrossPageBlockIds, ...validBlockIds ?? [], null]
|
|
13899
|
+
} : blockIdSchema;
|
|
13900
|
+
const actionSchema = {
|
|
13901
|
+
type: "object",
|
|
13902
|
+
additionalProperties: false,
|
|
13903
|
+
required: [
|
|
13904
|
+
"type",
|
|
13905
|
+
"target_block",
|
|
13906
|
+
"target_bbox",
|
|
13907
|
+
"scale",
|
|
13908
|
+
"padding",
|
|
13909
|
+
"easing",
|
|
13910
|
+
"dim_opacity",
|
|
13911
|
+
"feather_px",
|
|
13912
|
+
"shape",
|
|
13913
|
+
"color",
|
|
13914
|
+
"style",
|
|
13915
|
+
"draw_duration_ms",
|
|
13916
|
+
"count",
|
|
13917
|
+
"intensity",
|
|
13918
|
+
"from_block",
|
|
13919
|
+
"to_block",
|
|
13920
|
+
"label",
|
|
13921
|
+
"curve",
|
|
13922
|
+
"target_page",
|
|
13923
|
+
"position",
|
|
13924
|
+
"text",
|
|
13925
|
+
"targets"
|
|
13926
|
+
],
|
|
13927
|
+
properties: {
|
|
13928
|
+
type: {
|
|
13929
|
+
type: "string",
|
|
13930
|
+
enum: [
|
|
13931
|
+
"camera",
|
|
13932
|
+
"spotlight",
|
|
13933
|
+
"underline",
|
|
13934
|
+
"highlight",
|
|
13935
|
+
"pulse",
|
|
13936
|
+
"callout",
|
|
13937
|
+
"ghost_reference",
|
|
13938
|
+
"box",
|
|
13939
|
+
"label",
|
|
13940
|
+
"clear"
|
|
13941
|
+
]
|
|
13942
|
+
},
|
|
13943
|
+
target_block: blockIdSchema,
|
|
13944
|
+
target_bbox: {
|
|
13945
|
+
type: ["array", "null"],
|
|
13946
|
+
items: { type: "number" },
|
|
13947
|
+
minItems: 4,
|
|
13948
|
+
maxItems: 4
|
|
13949
|
+
},
|
|
13950
|
+
scale: { type: ["number", "null"] },
|
|
13951
|
+
padding: { type: ["number", "null"] },
|
|
13952
|
+
easing: {
|
|
13953
|
+
type: ["string", "null"],
|
|
13954
|
+
enum: ["linear", "ease-in", "ease-out", "ease-in-out", null]
|
|
13955
|
+
},
|
|
13956
|
+
dim_opacity: { type: ["number", "null"] },
|
|
13957
|
+
feather_px: { type: ["number", "null"] },
|
|
13958
|
+
shape: {
|
|
13959
|
+
type: ["string", "null"],
|
|
13960
|
+
enum: ["rect", "rounded", "ellipse", null]
|
|
13961
|
+
},
|
|
13962
|
+
color: { type: ["string", "null"] },
|
|
13963
|
+
style: {
|
|
13964
|
+
type: ["string", "null"],
|
|
13965
|
+
enum: ["straight", "sketch", "double", "wavy", "solid", "dashed", null]
|
|
13966
|
+
},
|
|
13967
|
+
draw_duration_ms: { type: ["number", "null"] },
|
|
13968
|
+
count: { type: ["integer", "null"] },
|
|
13969
|
+
intensity: {
|
|
13970
|
+
type: ["string", "null"],
|
|
13971
|
+
enum: ["subtle", "normal", "strong", null]
|
|
13972
|
+
},
|
|
13973
|
+
from_block: blockIdSchema,
|
|
13974
|
+
to_block: crossPageBlockIdSchema,
|
|
13975
|
+
label: { type: ["string", "null"] },
|
|
13976
|
+
curve: {
|
|
13977
|
+
type: ["string", "null"],
|
|
13978
|
+
enum: ["straight", "curved", "zigzag", null]
|
|
13979
|
+
},
|
|
13980
|
+
target_page: { type: ["integer", "null"] },
|
|
13981
|
+
position: {
|
|
13982
|
+
type: ["string", "null"],
|
|
13983
|
+
enum: [
|
|
13984
|
+
"top",
|
|
13985
|
+
"bottom",
|
|
13986
|
+
"left",
|
|
13987
|
+
"right",
|
|
13988
|
+
"top-right",
|
|
13989
|
+
"top-left",
|
|
13990
|
+
"bottom-right",
|
|
13991
|
+
"bottom-left",
|
|
13992
|
+
null
|
|
13993
|
+
]
|
|
13994
|
+
},
|
|
13995
|
+
text: { type: ["string", "null"] },
|
|
13996
|
+
targets: {
|
|
13997
|
+
type: ["string", "null"],
|
|
13998
|
+
enum: ["all", "spotlights", "overlays", null]
|
|
13999
|
+
}
|
|
14000
|
+
}
|
|
14001
|
+
};
|
|
14002
|
+
return {
|
|
14003
|
+
type: "object",
|
|
14004
|
+
additionalProperties: false,
|
|
14005
|
+
required: ["version", "reasoning", "steps"],
|
|
14006
|
+
properties: {
|
|
14007
|
+
version: { type: "integer", enum: [1] },
|
|
14008
|
+
reasoning: { type: "string" },
|
|
14009
|
+
steps: {
|
|
14010
|
+
type: "array",
|
|
14011
|
+
minItems: 1,
|
|
14012
|
+
maxItems: 4,
|
|
14013
|
+
items: {
|
|
14014
|
+
type: "object",
|
|
14015
|
+
additionalProperties: false,
|
|
14016
|
+
required: ["at_ms", "duration_ms", "action"],
|
|
14017
|
+
properties: {
|
|
14018
|
+
at_ms: { type: "number" },
|
|
14019
|
+
duration_ms: { type: "number" },
|
|
14020
|
+
action: actionSchema
|
|
14021
|
+
}
|
|
14022
|
+
}
|
|
14023
|
+
}
|
|
14024
|
+
}
|
|
14025
|
+
};
|
|
14026
|
+
}
|
|
14027
|
+
|
|
14028
|
+
// src/director/prompts.ts
|
|
14029
|
+
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.
|
|
14030
|
+
|
|
14031
|
+
# Your primary task
|
|
14032
|
+
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.
|
|
14033
|
+
|
|
14034
|
+
Anchoring rules:
|
|
14035
|
+
- 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.
|
|
14036
|
+
- 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.
|
|
14037
|
+
- 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.
|
|
14038
|
+
- If multiple blocks match, pick the most specific one, or use a \`callout\` from one to the other.
|
|
14039
|
+
|
|
14040
|
+
# Output shape
|
|
14041
|
+
Output ONLY this JSON, nothing else:
|
|
14042
|
+
{
|
|
14043
|
+
"version": 1,
|
|
14044
|
+
"reasoning": "<which block(s) you picked, which intent you used, and why \u2014 name the block_id>",
|
|
14045
|
+
"steps": [ { "at_ms": <int>, "duration_ms": <int>, "action": <action> }, ... ]
|
|
14046
|
+
}
|
|
14047
|
+
|
|
14048
|
+
# Action shapes \u2014 ALL fields shown are REQUIRED per action type
|
|
14049
|
+
- camera: { "type":"camera", "target_block":"<id>", "scale":1.1, "padding":80, "easing":"ease-out" }
|
|
14050
|
+
- spotlight: { "type":"spotlight", "target_block":"<id>", "dim_opacity":0.65, "feather_px":40, "shape":"rounded" }
|
|
14051
|
+
- underline: { "type":"underline", "target_block":"<id>", "color":"#FBBF24", "style":"sketch", "draw_duration_ms":600 }
|
|
14052
|
+
- highlight: { "type":"highlight", "target_block":"<id>", "color":"rgba(250,204,21,0.35)", "draw_duration_ms":500 }
|
|
14053
|
+
- pulse: { "type":"pulse", "target_block":"<id>", "count":2, "intensity":"normal" }
|
|
14054
|
+
- callout: { "type":"callout", "from_block":"<id>", "to_block":"<id>", "label":"<text>", "curve":"curved" }
|
|
14055
|
+
- ghost_reference: { "type":"ghost_reference", "target_page":<int>, "target_block":"<id>", "position":"top-right" }
|
|
14056
|
+
- box: { "type":"box", "target_block":"<id>", "color":"#3B82F6", "style":"solid" }
|
|
14057
|
+
- label: { "type":"label", "target_block":"<id>", "text":"<text>", "position":"top" }
|
|
14058
|
+
- clear: { "type":"clear", "targets":"overlays" }
|
|
14059
|
+
|
|
14060
|
+
# When to use each action (match the effect to the narration's intent, not just the block type)
|
|
14061
|
+
- 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.
|
|
14062
|
+
- spotlight \u2014 when narration ISOLATES one idea, term, or sentence. Great for definitions, principles, and "the key insight is\u2026" moments.
|
|
14063
|
+
- 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.
|
|
14064
|
+
- highlight \u2014 when narration FLAGS a keyword inline without full focus. Cheap, fast, great for list items, definitions-in-context, callback references.
|
|
14065
|
+
- 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.
|
|
14066
|
+
- 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.
|
|
14067
|
+
- 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.
|
|
14068
|
+
- 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.
|
|
14069
|
+
- 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.
|
|
14070
|
+
- clear \u2014 rarely needed; the engine auto-expires overlays. Use only when you explicitly want to wipe prior state before a new beat.
|
|
14071
|
+
|
|
14072
|
+
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.
|
|
14073
|
+
|
|
14074
|
+
# Intent Taxonomy \u2014 canonical "recipes" you should use as your default vocabulary
|
|
14075
|
+
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.
|
|
14076
|
+
|
|
14077
|
+
## define \u2014 the narration introduces or defines a term
|
|
14078
|
+
Shape: spotlight the term + underline it + drop a label tag. No camera move if the block is already on-screen.
|
|
14079
|
+
{
|
|
14080
|
+
"version": 1,
|
|
14081
|
+
"reasoning": "define recipe: spotlighting and underlining the term, labeling as 'definition'",
|
|
14082
|
+
"steps": [
|
|
14083
|
+
{ "at_ms":0, "duration_ms":700, "action": { "type":"spotlight", "target_block":"p1_para0", "dim_opacity":0.6, "feather_px":40, "shape":"rounded" } },
|
|
14084
|
+
{ "at_ms":200, "duration_ms":800, "action": { "type":"underline", "target_block":"p1_para0", "color":"#FBBF24", "style":"sketch", "draw_duration_ms":700 } },
|
|
14085
|
+
{ "at_ms":900, "duration_ms":1200, "action": { "type":"label", "target_block":"p1_para0", "text":"definition", "position":"top" } }
|
|
14086
|
+
]
|
|
14087
|
+
}
|
|
14088
|
+
|
|
14089
|
+
## point_out \u2014 the narration directs the viewer's eye to a figure, diagram, or specific region
|
|
14090
|
+
Shape: gentle camera move + callout arrow from caption to figure + pulse the figure.
|
|
14091
|
+
{
|
|
14092
|
+
"version": 1,
|
|
14093
|
+
"reasoning": "point_out recipe: drawing attention from caption p1_cap1 to figure p1_fig0",
|
|
14094
|
+
"steps": [
|
|
14095
|
+
{ "at_ms":0, "duration_ms":600, "action": { "type":"camera", "target_block":"p1_fig0", "scale":1.3, "padding":80, "easing":"ease-out" } },
|
|
14096
|
+
{ "at_ms":400, "duration_ms":900, "action": { "type":"callout", "from_block":"p1_cap1", "to_block":"p1_fig0", "label":"see here", "curve":"curved" } },
|
|
14097
|
+
{ "at_ms":900, "duration_ms":1200, "action": { "type":"pulse", "target_block":"p1_fig0", "count":2, "intensity":"normal" } }
|
|
14098
|
+
]
|
|
14099
|
+
}
|
|
14100
|
+
|
|
14101
|
+
## compare \u2014 the narration contrasts two things on the page
|
|
14102
|
+
Shape: box A + box B + callout between them with a relational label.
|
|
14103
|
+
{
|
|
14104
|
+
"version": 1,
|
|
14105
|
+
"reasoning": "compare recipe: framing fibrous vs synovial joints",
|
|
14106
|
+
"steps": [
|
|
14107
|
+
{ "at_ms":0, "duration_ms":600, "action": { "type":"box", "target_block":"p1_list5", "color":"#3B82F6", "style":"solid" } },
|
|
14108
|
+
{ "at_ms":300, "duration_ms":600, "action": { "type":"box", "target_block":"p1_list12", "color":"#F472B6", "style":"solid" } },
|
|
14109
|
+
{ "at_ms":800, "duration_ms":1000, "action": { "type":"callout", "from_block":"p1_list5", "to_block":"p1_list12", "label":"vs", "curve":"curved" } }
|
|
14110
|
+
]
|
|
14111
|
+
}
|
|
14112
|
+
|
|
14113
|
+
## emphasize \u2014 the narration stresses a keyword, warning, or takeaway
|
|
14114
|
+
Shape: highlight + pulse. Fast, punchy, no camera.
|
|
14115
|
+
{
|
|
14116
|
+
"version": 1,
|
|
14117
|
+
"reasoning": "emphasize recipe: highlighting key keyword and pulsing for stress",
|
|
14118
|
+
"steps": [
|
|
14119
|
+
{ "at_ms":0, "duration_ms":500, "action": { "type":"highlight", "target_block":"p1_list0", "color":"rgba(250,204,21,0.35)", "draw_duration_ms":450 } },
|
|
14120
|
+
{ "at_ms":350, "duration_ms":800, "action": { "type":"pulse", "target_block":"p1_list0", "count":2, "intensity":"strong" } }
|
|
14121
|
+
]
|
|
14122
|
+
}
|
|
14123
|
+
|
|
14124
|
+
# Choreography rules
|
|
14125
|
+
- 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.
|
|
14126
|
+
- 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.
|
|
14127
|
+
- 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.
|
|
14128
|
+
- 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.
|
|
14129
|
+
- 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.
|
|
14130
|
+
- 2\u20134 steps is typical; single-step overlays (no camera) are PREFERRED when the target is already visible.
|
|
14131
|
+
- 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).
|
|
14132
|
+
- 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\`.
|
|
14133
|
+
- Output ONLY valid JSON. No markdown, no code fences, no commentary, no trailing whitespace inside property values.
|
|
14134
|
+
|
|
14135
|
+
# Forbidden outputs \u2014 these will be rejected:
|
|
14136
|
+
- A storyboard with only a camera step.
|
|
14137
|
+
- A camera step with scale < 0.5 or > 4.0.
|
|
14138
|
+
- target_block values not listed in "Page blocks" or "Cross-page figures index".
|
|
14139
|
+
- Tab characters, newlines, or explanatory text inside JSON string values.`;
|
|
14140
|
+
function truncate(text, max = 200) {
|
|
14141
|
+
if (!text) return "";
|
|
14142
|
+
if (text.length <= max) return text;
|
|
14143
|
+
const slice = text.slice(0, max);
|
|
14144
|
+
const last = slice.lastIndexOf(" ");
|
|
14145
|
+
return (last > 40 ? slice.slice(0, last) : slice) + "\u2026";
|
|
14146
|
+
}
|
|
14147
|
+
function buildUserPrompt(input) {
|
|
14148
|
+
const {
|
|
14149
|
+
chunk,
|
|
14150
|
+
pageNumber,
|
|
14151
|
+
page,
|
|
14152
|
+
index,
|
|
14153
|
+
history,
|
|
14154
|
+
camera,
|
|
14155
|
+
activeOverlays,
|
|
14156
|
+
maxSteps = 4
|
|
14157
|
+
} = input;
|
|
14158
|
+
const pageBlocks = page.blocks.map((b) => ({
|
|
14159
|
+
block_id: b.block_id,
|
|
14160
|
+
type: b.type,
|
|
14161
|
+
text: truncate(b.text, 200),
|
|
14162
|
+
bbox: b.bbox,
|
|
14163
|
+
default_action: b.default_action
|
|
14164
|
+
}));
|
|
14165
|
+
const xPageFigures = index.crossPageFigures.filter((f) => f.page !== pageNumber).slice(0, 20).map((f) => ({
|
|
14166
|
+
block_id: f.block_id,
|
|
14167
|
+
page: f.page,
|
|
14168
|
+
type: f.type,
|
|
14169
|
+
text: truncate(f.text, 200)
|
|
14170
|
+
}));
|
|
14171
|
+
const recent = history.slice(-3).map((h) => h.text);
|
|
14172
|
+
const overlaySummary = activeOverlays.map((o) => ({ id: o.id, kind: o.kind }));
|
|
14173
|
+
const blockIdList = pageBlocks.map((b) => b.block_id);
|
|
14174
|
+
return [
|
|
14175
|
+
`Current page: ${pageNumber}`,
|
|
14176
|
+
`Page blocks (${pageBlocks.length}) \u2014 you MUST pick target_block from this list:`,
|
|
14177
|
+
JSON.stringify(pageBlocks),
|
|
14178
|
+
"",
|
|
14179
|
+
`Valid block_ids for this page: ${JSON.stringify(blockIdList)}`,
|
|
14180
|
+
"",
|
|
14181
|
+
`Cross-page figures index: ${JSON.stringify(xPageFigures)}`,
|
|
14182
|
+
"",
|
|
14183
|
+
`Current chunk (what the tutor just said): ${JSON.stringify(chunk)}`,
|
|
14184
|
+
`Recent chunks: ${JSON.stringify(recent)}`,
|
|
14185
|
+
`Current camera: ${JSON.stringify(camera)}`,
|
|
14186
|
+
`Active overlays: ${JSON.stringify(overlaySummary)}`,
|
|
14187
|
+
"",
|
|
14188
|
+
`Max steps: ${maxSteps}`,
|
|
14189
|
+
`Output JSON storyboard. Every target_block MUST be one of the ids above.`
|
|
14190
|
+
].join("\n");
|
|
14191
|
+
}
|
|
14192
|
+
|
|
14193
|
+
// src/director/sse-parser.ts
|
|
14194
|
+
async function* parseSse(body) {
|
|
14195
|
+
const reader = body.getReader();
|
|
14196
|
+
const decoder = new TextDecoder();
|
|
14197
|
+
let buffer = "";
|
|
14198
|
+
try {
|
|
14199
|
+
while (true) {
|
|
14200
|
+
const { value, done } = await reader.read();
|
|
14201
|
+
if (done) break;
|
|
14202
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14203
|
+
let idx;
|
|
14204
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
14205
|
+
const rawLine = buffer.slice(0, idx).trim();
|
|
14206
|
+
buffer = buffer.slice(idx + 1);
|
|
14207
|
+
if (!rawLine.startsWith("data:")) continue;
|
|
14208
|
+
const payload = rawLine.slice(5).trim();
|
|
14209
|
+
if (!payload || payload === "[DONE]") continue;
|
|
14210
|
+
try {
|
|
14211
|
+
yield JSON.parse(payload);
|
|
14212
|
+
} catch {
|
|
14213
|
+
}
|
|
14214
|
+
}
|
|
14215
|
+
}
|
|
14216
|
+
} finally {
|
|
14217
|
+
reader.releaseLock();
|
|
14218
|
+
}
|
|
14219
|
+
}
|
|
14220
|
+
function extractDelta(chunk) {
|
|
14221
|
+
if (!chunk || typeof chunk !== "object") return null;
|
|
14222
|
+
const choices = chunk.choices;
|
|
14223
|
+
if (!choices || !choices.length) return null;
|
|
14224
|
+
return choices[0].delta?.content ?? null;
|
|
14225
|
+
}
|
|
14226
|
+
|
|
14227
|
+
// src/director/llm-director.ts
|
|
14228
|
+
async function directStoryboard(config, input) {
|
|
14229
|
+
const {
|
|
14230
|
+
endpointUrl,
|
|
14231
|
+
model,
|
|
14232
|
+
authToken,
|
|
14233
|
+
extraBody,
|
|
14234
|
+
maxTokens = 1024,
|
|
14235
|
+
temperature = 0.3,
|
|
14236
|
+
useJsonSchema = true,
|
|
14237
|
+
stream = false
|
|
14238
|
+
} = config;
|
|
14239
|
+
const userContent = buildUserPrompt(input);
|
|
14240
|
+
const body = {
|
|
14241
|
+
model,
|
|
14242
|
+
stream,
|
|
14243
|
+
temperature,
|
|
14244
|
+
max_tokens: maxTokens,
|
|
14245
|
+
messages: [
|
|
14246
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
14247
|
+
{ role: "user", content: userContent }
|
|
14248
|
+
],
|
|
14249
|
+
...extraBody ?? {}
|
|
14250
|
+
};
|
|
14251
|
+
if (useJsonSchema) {
|
|
14252
|
+
const validBlockIds = input.page.blocks.map((b) => b.block_id);
|
|
14253
|
+
const validCrossPageBlockIds = input.index.crossPageFigures.filter((f) => f.page !== input.pageNumber).map((f) => f.block_id);
|
|
14254
|
+
body.response_format = {
|
|
14255
|
+
type: "json_schema",
|
|
14256
|
+
json_schema: {
|
|
14257
|
+
name: "storyboard",
|
|
14258
|
+
strict: true,
|
|
14259
|
+
schema: storyboardJsonSchema({
|
|
14260
|
+
validBlockIds,
|
|
14261
|
+
validCrossPageBlockIds
|
|
14262
|
+
})
|
|
14263
|
+
}
|
|
14264
|
+
};
|
|
14265
|
+
}
|
|
14266
|
+
const headers = {
|
|
14267
|
+
"Content-Type": "application/json",
|
|
14268
|
+
Accept: stream ? "text/event-stream" : "application/json"
|
|
14269
|
+
};
|
|
14270
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
14271
|
+
const timeoutController = new AbortController();
|
|
14272
|
+
const timer = setTimeout(
|
|
14273
|
+
() => timeoutController.abort(),
|
|
14274
|
+
input.timeoutMs ?? 2500
|
|
14275
|
+
);
|
|
14276
|
+
const signal = mergeSignals(input.signal, timeoutController.signal);
|
|
14277
|
+
try {
|
|
14278
|
+
const response = await fetch(endpointUrl, {
|
|
14279
|
+
method: "POST",
|
|
14280
|
+
headers,
|
|
14281
|
+
body: JSON.stringify(body),
|
|
14282
|
+
signal
|
|
14283
|
+
});
|
|
14284
|
+
if (!response.ok || !response.body) {
|
|
14285
|
+
return {
|
|
14286
|
+
storyboard: null,
|
|
14287
|
+
raw: "",
|
|
14288
|
+
error: `HTTP ${response.status}`
|
|
14289
|
+
};
|
|
14290
|
+
}
|
|
14291
|
+
let raw = "";
|
|
14292
|
+
if (stream && response.body) {
|
|
14293
|
+
for await (const chunk of parseSse(response.body)) {
|
|
14294
|
+
const delta = extractDelta(chunk);
|
|
14295
|
+
if (delta) raw += delta;
|
|
14296
|
+
}
|
|
14297
|
+
} else {
|
|
14298
|
+
const json = await response.json();
|
|
14299
|
+
raw = json.choices?.[0]?.message?.content ?? "";
|
|
14300
|
+
}
|
|
14301
|
+
const stripped = collapseWhitespaceRuns(stripCodeFences(raw).trim());
|
|
14302
|
+
let parsed;
|
|
14303
|
+
try {
|
|
14304
|
+
parsed = JSON.parse(stripped);
|
|
14305
|
+
} catch (e) {
|
|
14306
|
+
return {
|
|
14307
|
+
storyboard: null,
|
|
14308
|
+
raw,
|
|
14309
|
+
error: `parse error: ${e.message}`
|
|
14310
|
+
};
|
|
14311
|
+
}
|
|
14312
|
+
const cleaned = clampNumericRanges(stripNullsDeep(parsed));
|
|
14313
|
+
const validation = StoryboardSchema.safeParse(cleaned);
|
|
14314
|
+
if (validation.success) {
|
|
14315
|
+
return {
|
|
14316
|
+
storyboard: enforceOverlayPresence(validation.data),
|
|
14317
|
+
raw
|
|
14318
|
+
};
|
|
14319
|
+
}
|
|
14320
|
+
const salvaged = salvageStoryboard(cleaned);
|
|
14321
|
+
if (salvaged) {
|
|
14322
|
+
return { storyboard: enforceOverlayPresence(salvaged), raw };
|
|
14323
|
+
}
|
|
14324
|
+
return {
|
|
14325
|
+
storyboard: null,
|
|
14326
|
+
raw,
|
|
14327
|
+
error: `validation failed: ${validation.error.message}`
|
|
14328
|
+
};
|
|
14329
|
+
} catch (e) {
|
|
14330
|
+
const name = e.name;
|
|
14331
|
+
const msg = name === "AbortError" ? "aborted" : e.message;
|
|
14332
|
+
return { storyboard: null, raw: "", error: msg };
|
|
14333
|
+
} finally {
|
|
14334
|
+
clearTimeout(timer);
|
|
14335
|
+
}
|
|
14336
|
+
}
|
|
14337
|
+
function stripCodeFences(s) {
|
|
14338
|
+
const m = s.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
14339
|
+
return m ? m[1] : s;
|
|
14340
|
+
}
|
|
14341
|
+
function collapseWhitespaceRuns(src) {
|
|
14342
|
+
let out = "";
|
|
14343
|
+
let inString = false;
|
|
14344
|
+
let escape = false;
|
|
14345
|
+
let run = 0;
|
|
14346
|
+
for (let i = 0; i < src.length; i++) {
|
|
14347
|
+
const c = src[i];
|
|
14348
|
+
if (inString) {
|
|
14349
|
+
out += c;
|
|
14350
|
+
if (escape) {
|
|
14351
|
+
escape = false;
|
|
14352
|
+
} else if (c === "\\") {
|
|
14353
|
+
escape = true;
|
|
14354
|
+
} else if (c === '"') {
|
|
14355
|
+
inString = false;
|
|
14356
|
+
}
|
|
14357
|
+
continue;
|
|
14358
|
+
}
|
|
14359
|
+
if (c === '"') {
|
|
14360
|
+
out += c;
|
|
14361
|
+
inString = true;
|
|
14362
|
+
run = 0;
|
|
14363
|
+
continue;
|
|
14364
|
+
}
|
|
14365
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
14366
|
+
run++;
|
|
14367
|
+
if (run <= 1) out += " ";
|
|
14368
|
+
continue;
|
|
14369
|
+
}
|
|
14370
|
+
run = 0;
|
|
14371
|
+
out += c;
|
|
14372
|
+
}
|
|
14373
|
+
return out;
|
|
14374
|
+
}
|
|
14375
|
+
function clampNumericRanges(input) {
|
|
14376
|
+
if (input === null || input === void 0) return input;
|
|
14377
|
+
if (Array.isArray(input)) return input.map(clampNumericRanges);
|
|
14378
|
+
if (typeof input !== "object") return input;
|
|
14379
|
+
const obj = input;
|
|
14380
|
+
const out = {};
|
|
14381
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
14382
|
+
out[k] = clampNumericRanges(v);
|
|
14383
|
+
}
|
|
14384
|
+
const type = typeof out.type === "string" ? out.type : void 0;
|
|
14385
|
+
if (type === "camera") {
|
|
14386
|
+
if (typeof out.scale === "number") out.scale = clamp(out.scale, 0.5, 4);
|
|
14387
|
+
if (typeof out.padding === "number") {
|
|
14388
|
+
out.padding = clamp(out.padding, 0, 400);
|
|
14389
|
+
}
|
|
14390
|
+
}
|
|
14391
|
+
if (typeof out.dim_opacity === "number") {
|
|
14392
|
+
out.dim_opacity = clamp(out.dim_opacity, 0, 1);
|
|
14393
|
+
}
|
|
14394
|
+
if (typeof out.feather_px === "number") {
|
|
14395
|
+
out.feather_px = clamp(out.feather_px, 0, 200);
|
|
14396
|
+
}
|
|
14397
|
+
if (typeof out.draw_duration_ms === "number") {
|
|
14398
|
+
out.draw_duration_ms = clamp(out.draw_duration_ms, 100, 3e3);
|
|
14399
|
+
}
|
|
14400
|
+
if (typeof out.count === "number") {
|
|
14401
|
+
out.count = Math.round(clamp(out.count, 1, 5));
|
|
14402
|
+
}
|
|
14403
|
+
if (typeof out.at_ms === "number") {
|
|
14404
|
+
out.at_ms = clamp(out.at_ms, 0, 5e3);
|
|
14405
|
+
}
|
|
14406
|
+
if (typeof out.duration_ms === "number" && type === void 0) {
|
|
14407
|
+
out.duration_ms = clamp(out.duration_ms, 100, 5e3);
|
|
14408
|
+
}
|
|
14409
|
+
return out;
|
|
14410
|
+
}
|
|
14411
|
+
function clamp(v, lo, hi) {
|
|
14412
|
+
return Math.min(hi, Math.max(lo, v));
|
|
14413
|
+
}
|
|
14414
|
+
function enforceOverlayPresence(sb) {
|
|
14415
|
+
if (sb.steps.length === 0) return sb;
|
|
14416
|
+
const hasOverlay = sb.steps.some(
|
|
14417
|
+
(s) => s.action.type !== "camera" && s.action.type !== "clear"
|
|
14418
|
+
);
|
|
14419
|
+
if (hasOverlay) return sb;
|
|
14420
|
+
const cameraStep = sb.steps.find((s) => s.action.type === "camera");
|
|
14421
|
+
if (!cameraStep || cameraStep.action.type !== "camera") return sb;
|
|
14422
|
+
const target = cameraStep.action.target_block;
|
|
14423
|
+
if (!target) return sb;
|
|
14424
|
+
return {
|
|
14425
|
+
...sb,
|
|
14426
|
+
reasoning: `${sb.reasoning} [auto-appended pulse: camera-only storyboards are forbidden]`,
|
|
14427
|
+
steps: [
|
|
14428
|
+
...sb.steps,
|
|
14429
|
+
{
|
|
14430
|
+
at_ms: Math.min(4800, (cameraStep.at_ms ?? 0) + 200),
|
|
14431
|
+
duration_ms: 900,
|
|
14432
|
+
action: {
|
|
14433
|
+
type: "pulse",
|
|
14434
|
+
target_block: target,
|
|
14435
|
+
count: 2,
|
|
14436
|
+
intensity: "normal"
|
|
14437
|
+
}
|
|
14438
|
+
}
|
|
14439
|
+
]
|
|
14440
|
+
};
|
|
14441
|
+
}
|
|
14442
|
+
function stripNullsDeep(input) {
|
|
14443
|
+
if (input === null) return void 0;
|
|
14444
|
+
if (Array.isArray(input)) {
|
|
14445
|
+
return input.map(stripNullsDeep).filter((v) => v !== void 0);
|
|
14446
|
+
}
|
|
14447
|
+
if (input && typeof input === "object") {
|
|
14448
|
+
const out = {};
|
|
14449
|
+
for (const [k, v] of Object.entries(input)) {
|
|
14450
|
+
const cleaned = stripNullsDeep(v);
|
|
14451
|
+
if (cleaned !== void 0) out[k] = cleaned;
|
|
14452
|
+
}
|
|
14453
|
+
return out;
|
|
14454
|
+
}
|
|
14455
|
+
return input;
|
|
14456
|
+
}
|
|
14457
|
+
function salvageStoryboard(parsed) {
|
|
14458
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
14459
|
+
const obj = parsed;
|
|
14460
|
+
if (!Array.isArray(obj.steps)) return null;
|
|
14461
|
+
const goodSteps = [];
|
|
14462
|
+
for (const step of obj.steps) {
|
|
14463
|
+
const r = StoryboardStepSchema.safeParse(step);
|
|
14464
|
+
if (r.success) goodSteps.push(r.data);
|
|
14465
|
+
if (goodSteps.length >= 4) break;
|
|
14466
|
+
}
|
|
14467
|
+
if (goodSteps.length === 0) return null;
|
|
14468
|
+
return {
|
|
14469
|
+
version: 1,
|
|
14470
|
+
reasoning: typeof obj.reasoning === "string" ? obj.reasoning + " (salvaged)" : "salvaged",
|
|
14471
|
+
steps: goodSteps
|
|
14472
|
+
};
|
|
14473
|
+
}
|
|
14474
|
+
function mergeSignals(a, b) {
|
|
14475
|
+
if (!a) return b;
|
|
14476
|
+
if (!b) return a;
|
|
14477
|
+
const ctrl = new AbortController();
|
|
14478
|
+
const onAbort = () => ctrl.abort();
|
|
14479
|
+
a.addEventListener("abort", onAbort);
|
|
14480
|
+
b.addEventListener("abort", onAbort);
|
|
14481
|
+
return ctrl.signal;
|
|
14482
|
+
}
|
|
14483
|
+
|
|
14484
|
+
// src/director/embedding-fallback.ts
|
|
14485
|
+
function cosineSimilarity(a, b) {
|
|
14486
|
+
let dot = 0;
|
|
14487
|
+
let na = 0;
|
|
14488
|
+
let nb = 0;
|
|
14489
|
+
const n = Math.min(a.length, b.length);
|
|
14490
|
+
for (let i = 0; i < n; i++) {
|
|
14491
|
+
dot += a[i] * b[i];
|
|
14492
|
+
na += a[i] * a[i];
|
|
14493
|
+
nb += b[i] * b[i];
|
|
14494
|
+
}
|
|
14495
|
+
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
|
14496
|
+
return denom === 0 ? 0 : dot / denom;
|
|
14497
|
+
}
|
|
14498
|
+
async function matchChunkToBlock(chunk, page, provider) {
|
|
14499
|
+
const textBlocks = page.blocks.filter(
|
|
14500
|
+
(b) => typeof b.text === "string" && b.text.trim().length > 0
|
|
14501
|
+
);
|
|
14502
|
+
if (textBlocks.length === 0) return null;
|
|
14503
|
+
const inputs = [chunk, ...textBlocks.map((b) => b.text)];
|
|
14504
|
+
const embeds = await provider.embed(inputs);
|
|
14505
|
+
if (embeds.length < 2) return null;
|
|
14506
|
+
const chunkEmbed = embeds[0];
|
|
14507
|
+
let best = null;
|
|
14508
|
+
for (let i = 0; i < textBlocks.length; i++) {
|
|
14509
|
+
const score = cosineSimilarity(chunkEmbed, embeds[i + 1]);
|
|
14510
|
+
if (!best || score > best.score) best = { block: textBlocks[i], score };
|
|
14511
|
+
}
|
|
14512
|
+
return best;
|
|
14513
|
+
}
|
|
14514
|
+
function nearestFigureOnPage(caption, page) {
|
|
14515
|
+
if (!page) return null;
|
|
14516
|
+
const [cx1, cy1, cx2, cy2] = caption.bbox;
|
|
14517
|
+
const ccx = (cx1 + cx2) / 2;
|
|
14518
|
+
const ccy = (cy1 + cy2) / 2;
|
|
14519
|
+
let best = null;
|
|
14520
|
+
for (const b of page.blocks) {
|
|
14521
|
+
if (b.block_id === caption.block_id) continue;
|
|
14522
|
+
if (b.type !== "figure" && b.type !== "figure_region") continue;
|
|
14523
|
+
const [x1, y1, x2, y2] = b.bbox;
|
|
14524
|
+
const fx = (x1 + x2) / 2;
|
|
14525
|
+
const fy = (y1 + y2) / 2;
|
|
14526
|
+
const dist = Math.hypot(fx - ccx, fy - ccy);
|
|
14527
|
+
if (!best || dist < best.dist) best = { block: b, dist };
|
|
14528
|
+
}
|
|
14529
|
+
return best?.block ?? null;
|
|
14530
|
+
}
|
|
14531
|
+
function truncateLabel(text, max) {
|
|
14532
|
+
if (!text) return "";
|
|
14533
|
+
const clean = text.replace(/\s+/g, " ").trim();
|
|
14534
|
+
if (clean.length <= max) return clean;
|
|
14535
|
+
return clean.slice(0, max - 1) + "\u2026";
|
|
14536
|
+
}
|
|
14537
|
+
function storyboardFromMatch(match, page) {
|
|
14538
|
+
if (!match) {
|
|
14539
|
+
return {
|
|
14540
|
+
version: 1,
|
|
14541
|
+
reasoning: "fallback: no match \u2014 clearing overlays",
|
|
14542
|
+
steps: [
|
|
14543
|
+
{
|
|
14544
|
+
at_ms: 0,
|
|
14545
|
+
duration_ms: 800,
|
|
14546
|
+
action: { type: "clear", targets: "overlays" }
|
|
14547
|
+
}
|
|
14548
|
+
]
|
|
14549
|
+
};
|
|
14550
|
+
}
|
|
14551
|
+
const { block } = match;
|
|
14552
|
+
const id = block.block_id;
|
|
14553
|
+
const reason = `fallback (block.type=${block.type}): matched ${id} (${match.score.toFixed(2)})`;
|
|
14554
|
+
switch (block.type) {
|
|
14555
|
+
case "heading": {
|
|
14556
|
+
return {
|
|
14557
|
+
version: 1,
|
|
14558
|
+
reasoning: reason,
|
|
14559
|
+
steps: [
|
|
14560
|
+
{
|
|
14561
|
+
at_ms: 0,
|
|
14562
|
+
duration_ms: 700,
|
|
14563
|
+
action: {
|
|
14564
|
+
type: "spotlight",
|
|
14565
|
+
target_block: id,
|
|
14566
|
+
dim_opacity: 0.6,
|
|
14567
|
+
feather_px: 40,
|
|
14568
|
+
shape: "rounded"
|
|
14569
|
+
}
|
|
14570
|
+
},
|
|
14571
|
+
{
|
|
14572
|
+
at_ms: 300,
|
|
14573
|
+
duration_ms: 1200,
|
|
14574
|
+
action: {
|
|
14575
|
+
type: "label",
|
|
14576
|
+
target_block: id,
|
|
14577
|
+
text: truncateLabel(block.text, 32) || "section",
|
|
14578
|
+
position: "top"
|
|
14579
|
+
}
|
|
14580
|
+
}
|
|
14581
|
+
]
|
|
14582
|
+
};
|
|
14583
|
+
}
|
|
14584
|
+
case "paragraph": {
|
|
14585
|
+
return {
|
|
14586
|
+
version: 1,
|
|
14587
|
+
reasoning: reason,
|
|
14588
|
+
steps: [
|
|
14589
|
+
{
|
|
14590
|
+
at_ms: 0,
|
|
14591
|
+
duration_ms: 600,
|
|
14592
|
+
action: {
|
|
14593
|
+
type: "camera",
|
|
14594
|
+
target_block: id,
|
|
14595
|
+
scale: 1.1,
|
|
14596
|
+
padding: 80,
|
|
14597
|
+
easing: "ease-out"
|
|
14598
|
+
}
|
|
14599
|
+
},
|
|
14600
|
+
{
|
|
14601
|
+
at_ms: 300,
|
|
14602
|
+
duration_ms: 900,
|
|
14603
|
+
action: {
|
|
14604
|
+
type: "underline",
|
|
14605
|
+
target_block: id,
|
|
14606
|
+
color: "#FBBF24",
|
|
14607
|
+
style: "sketch",
|
|
14608
|
+
draw_duration_ms: 800
|
|
14609
|
+
}
|
|
14610
|
+
}
|
|
14611
|
+
]
|
|
14612
|
+
};
|
|
14613
|
+
}
|
|
14614
|
+
case "list_item":
|
|
14615
|
+
case "mcq_option": {
|
|
14616
|
+
return {
|
|
14617
|
+
version: 1,
|
|
14618
|
+
reasoning: reason,
|
|
14619
|
+
steps: [
|
|
14620
|
+
{
|
|
14621
|
+
at_ms: 0,
|
|
14622
|
+
duration_ms: 500,
|
|
14623
|
+
action: {
|
|
14624
|
+
type: "highlight",
|
|
14625
|
+
target_block: id,
|
|
14626
|
+
color: "rgba(250, 204, 21, 0.35)",
|
|
14627
|
+
draw_duration_ms: 450
|
|
14628
|
+
}
|
|
14629
|
+
}
|
|
14630
|
+
]
|
|
14631
|
+
};
|
|
14632
|
+
}
|
|
14633
|
+
case "caption": {
|
|
14634
|
+
const figure = nearestFigureOnPage(block, page);
|
|
14635
|
+
if (figure) {
|
|
14636
|
+
return {
|
|
14637
|
+
version: 1,
|
|
14638
|
+
reasoning: `${reason}; caption \u2192 figure ${figure.block_id}`,
|
|
14639
|
+
steps: [
|
|
14640
|
+
{
|
|
14641
|
+
at_ms: 0,
|
|
14642
|
+
duration_ms: 900,
|
|
14643
|
+
action: {
|
|
14644
|
+
type: "callout",
|
|
14645
|
+
from_block: id,
|
|
14646
|
+
to_block: figure.block_id,
|
|
14647
|
+
label: "see",
|
|
14648
|
+
curve: "curved"
|
|
14649
|
+
}
|
|
14650
|
+
},
|
|
14651
|
+
{
|
|
14652
|
+
at_ms: 600,
|
|
14653
|
+
duration_ms: 1e3,
|
|
14654
|
+
action: {
|
|
14655
|
+
type: "pulse",
|
|
14656
|
+
target_block: figure.block_id,
|
|
14657
|
+
count: 2,
|
|
14658
|
+
intensity: "normal"
|
|
14659
|
+
}
|
|
14660
|
+
}
|
|
14661
|
+
]
|
|
14662
|
+
};
|
|
14663
|
+
}
|
|
14664
|
+
return {
|
|
14665
|
+
version: 1,
|
|
14666
|
+
reasoning: `${reason}; no figure on page, underlining caption`,
|
|
14667
|
+
steps: [
|
|
14668
|
+
{
|
|
14669
|
+
at_ms: 0,
|
|
14670
|
+
duration_ms: 800,
|
|
14671
|
+
action: {
|
|
14672
|
+
type: "underline",
|
|
14673
|
+
target_block: id,
|
|
14674
|
+
color: "#FBBF24",
|
|
14675
|
+
style: "sketch",
|
|
14676
|
+
draw_duration_ms: 700
|
|
14677
|
+
}
|
|
14678
|
+
}
|
|
14679
|
+
]
|
|
14680
|
+
};
|
|
14681
|
+
}
|
|
14682
|
+
case "figure": {
|
|
14683
|
+
return {
|
|
14684
|
+
version: 1,
|
|
14685
|
+
reasoning: reason,
|
|
14686
|
+
steps: [
|
|
14687
|
+
{
|
|
14688
|
+
at_ms: 0,
|
|
14689
|
+
duration_ms: 900,
|
|
14690
|
+
action: {
|
|
14691
|
+
type: "pulse",
|
|
14692
|
+
target_block: id,
|
|
14693
|
+
count: 2,
|
|
14694
|
+
intensity: "strong"
|
|
14695
|
+
}
|
|
14696
|
+
},
|
|
14697
|
+
{
|
|
14698
|
+
at_ms: 400,
|
|
14699
|
+
duration_ms: 1200,
|
|
14700
|
+
action: {
|
|
14701
|
+
type: "box",
|
|
14702
|
+
target_block: id,
|
|
14703
|
+
color: "#3B82F6",
|
|
14704
|
+
style: "solid"
|
|
14705
|
+
}
|
|
14706
|
+
}
|
|
14707
|
+
]
|
|
14708
|
+
};
|
|
14709
|
+
}
|
|
14710
|
+
case "figure_region": {
|
|
14711
|
+
return {
|
|
14712
|
+
version: 1,
|
|
14713
|
+
reasoning: reason,
|
|
14714
|
+
steps: [
|
|
14715
|
+
{
|
|
14716
|
+
at_ms: 0,
|
|
14717
|
+
duration_ms: 900,
|
|
14718
|
+
action: {
|
|
14719
|
+
type: "pulse",
|
|
14720
|
+
target_block: id,
|
|
14721
|
+
count: 2,
|
|
14722
|
+
intensity: "normal"
|
|
14723
|
+
}
|
|
14724
|
+
}
|
|
14725
|
+
]
|
|
14726
|
+
};
|
|
14727
|
+
}
|
|
14728
|
+
case "table": {
|
|
14729
|
+
return {
|
|
14730
|
+
version: 1,
|
|
14731
|
+
reasoning: reason,
|
|
14732
|
+
steps: [
|
|
14733
|
+
{
|
|
14734
|
+
at_ms: 0,
|
|
14735
|
+
duration_ms: 700,
|
|
14736
|
+
action: {
|
|
14737
|
+
type: "camera",
|
|
14738
|
+
target_block: id,
|
|
14739
|
+
scale: 1.2,
|
|
14740
|
+
padding: 60,
|
|
14741
|
+
easing: "ease-out"
|
|
14742
|
+
}
|
|
14743
|
+
},
|
|
14744
|
+
{
|
|
14745
|
+
at_ms: 300,
|
|
14746
|
+
duration_ms: 1e3,
|
|
14747
|
+
action: {
|
|
14748
|
+
type: "box",
|
|
14749
|
+
target_block: id,
|
|
14750
|
+
color: "#3B82F6",
|
|
14751
|
+
style: "dashed"
|
|
14752
|
+
}
|
|
14753
|
+
}
|
|
14754
|
+
]
|
|
14755
|
+
};
|
|
14756
|
+
}
|
|
14757
|
+
default: {
|
|
14758
|
+
return {
|
|
14759
|
+
version: 1,
|
|
14760
|
+
reasoning: `${reason}; unknown block.type, using highlight`,
|
|
14761
|
+
steps: [
|
|
14762
|
+
{
|
|
14763
|
+
at_ms: 0,
|
|
14764
|
+
duration_ms: 600,
|
|
14765
|
+
action: {
|
|
14766
|
+
type: "highlight",
|
|
14767
|
+
target_block: id,
|
|
14768
|
+
color: "rgba(250, 204, 21, 0.35)",
|
|
14769
|
+
draw_duration_ms: 500
|
|
14770
|
+
}
|
|
14771
|
+
}
|
|
14772
|
+
]
|
|
14773
|
+
};
|
|
14774
|
+
}
|
|
14775
|
+
}
|
|
14776
|
+
}
|
|
14777
|
+
|
|
14778
|
+
// src/components/TutorMode/TutorModeContainer.tsx
|
|
14779
|
+
import { jsx as jsx52, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
14780
|
+
function buildBBoxIndex(bboxData) {
|
|
14781
|
+
const byPage = /* @__PURE__ */ new Map();
|
|
14782
|
+
const blockById = /* @__PURE__ */ new Map();
|
|
14783
|
+
const crossPageFigures = [];
|
|
14784
|
+
for (const page of bboxData) {
|
|
14785
|
+
byPage.set(page.page_number, page);
|
|
14786
|
+
for (const block of page.blocks) {
|
|
14787
|
+
blockById.set(block.block_id, { block, pageNumber: page.page_number });
|
|
14788
|
+
if ((block.type === "figure" || block.type === "figure_region" || block.type === "caption") && typeof block.text === "string" && block.text.length > 0) {
|
|
14789
|
+
crossPageFigures.push({
|
|
14790
|
+
block_id: block.block_id,
|
|
14791
|
+
page: page.page_number,
|
|
14792
|
+
type: block.type,
|
|
14793
|
+
text: block.text
|
|
14794
|
+
});
|
|
14795
|
+
}
|
|
14796
|
+
}
|
|
14797
|
+
}
|
|
14798
|
+
return { byPage, blockById, crossPageFigures };
|
|
14799
|
+
}
|
|
14800
|
+
function TutorModeContainer({
|
|
14801
|
+
pageNumber,
|
|
14802
|
+
bboxData,
|
|
14803
|
+
narrationStore,
|
|
14804
|
+
scale,
|
|
14805
|
+
rotation = 0,
|
|
14806
|
+
currentChunk,
|
|
14807
|
+
llm,
|
|
14808
|
+
idleTimeoutMs = 5e3,
|
|
14809
|
+
llmTimeoutMs = 3e4,
|
|
14810
|
+
embeddingProvider,
|
|
14811
|
+
showSubtitles = false,
|
|
14812
|
+
showExitButton = true,
|
|
14813
|
+
onExitTutorMode,
|
|
14814
|
+
minOverlayDurationMs,
|
|
14815
|
+
className
|
|
14816
|
+
}) {
|
|
14817
|
+
const containerRef = useRef27(null);
|
|
14818
|
+
const index = useMemo15(() => buildBBoxIndex(bboxData), [bboxData]);
|
|
14819
|
+
const { document: document2 } = usePDFViewer();
|
|
14820
|
+
const [pageProxy, setPageProxy] = useState30(null);
|
|
14821
|
+
const [viewport, setViewport] = useState30({ width: 800, height: 1e3 });
|
|
14822
|
+
const camera = useStore2(narrationStore, (s) => s.camera);
|
|
14823
|
+
const activeOverlays = useStore2(narrationStore, (s) => s.activeOverlays);
|
|
14824
|
+
useEffect28(() => {
|
|
14825
|
+
if (!containerRef.current) return;
|
|
14826
|
+
const el = containerRef.current;
|
|
14827
|
+
const update = () => setViewport({ width: el.clientWidth, height: el.clientHeight });
|
|
14828
|
+
update();
|
|
14829
|
+
const ro = new ResizeObserver(update);
|
|
14830
|
+
ro.observe(el);
|
|
14831
|
+
return () => ro.disconnect();
|
|
14832
|
+
}, []);
|
|
14833
|
+
useEffect28(() => {
|
|
14834
|
+
if (!document2) {
|
|
14835
|
+
setPageProxy(null);
|
|
14836
|
+
return;
|
|
14837
|
+
}
|
|
14838
|
+
let cancelled = false;
|
|
14839
|
+
document2.getPage(pageNumber).then((p) => {
|
|
14840
|
+
if (!cancelled) setPageProxy(p);
|
|
14841
|
+
}).catch(() => {
|
|
14842
|
+
if (!cancelled) setPageProxy(null);
|
|
14843
|
+
});
|
|
14844
|
+
return () => {
|
|
14845
|
+
cancelled = true;
|
|
14846
|
+
};
|
|
14847
|
+
}, [document2, pageNumber]);
|
|
14848
|
+
useEffect28(() => {
|
|
14849
|
+
narrationStore.getState().setCurrentPage(pageNumber);
|
|
14850
|
+
}, [pageNumber, narrationStore]);
|
|
14851
|
+
useEffect28(() => {
|
|
14852
|
+
const page2 = index.byPage.get(pageNumber);
|
|
14853
|
+
if (!page2) return;
|
|
14854
|
+
if (viewport.width === 0 || viewport.height === 0) return;
|
|
14855
|
+
if (narrationStore.getState().activeOverlays.length > 0) return;
|
|
14856
|
+
const fit = Math.min(
|
|
14857
|
+
viewport.width / page2.page_dimensions.width,
|
|
14858
|
+
viewport.height / page2.page_dimensions.height
|
|
14859
|
+
) * 0.95;
|
|
14860
|
+
narrationStore.getState().setCamera({ scale: fit, x: 0, y: 0 });
|
|
14861
|
+
}, [pageNumber, viewport, index, narrationStore]);
|
|
14862
|
+
const engineRef = useRef27(null);
|
|
14863
|
+
useEffect28(() => {
|
|
14864
|
+
engineRef.current = new StoryboardEngine({
|
|
14865
|
+
narrationStore,
|
|
14866
|
+
bboxIndex: index,
|
|
14867
|
+
getViewport: () => viewport,
|
|
14868
|
+
minOverlayDurationMs
|
|
14869
|
+
});
|
|
14870
|
+
return () => engineRef.current?.cancelPending();
|
|
14871
|
+
}, [narrationStore, index, viewport, minOverlayDurationMs]);
|
|
14872
|
+
const abortRef = useRef27(null);
|
|
14873
|
+
const debounceRef = useRef27(null);
|
|
14874
|
+
const lastChunkRef = useRef27(null);
|
|
14875
|
+
useEffect28(() => {
|
|
14876
|
+
if (!llm) return;
|
|
14877
|
+
if (!currentChunk || currentChunk === lastChunkRef.current) return;
|
|
14878
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
14879
|
+
debounceRef.current = setTimeout(async () => {
|
|
14880
|
+
const chunk = currentChunk;
|
|
14881
|
+
if (chunk === lastChunkRef.current) return;
|
|
14882
|
+
lastChunkRef.current = chunk;
|
|
14883
|
+
const page2 = index.byPage.get(pageNumber);
|
|
14884
|
+
if (!page2) return;
|
|
14885
|
+
narrationStore.getState().pushChunkHistory({
|
|
14886
|
+
text: chunk,
|
|
14887
|
+
pageNumber,
|
|
14888
|
+
timestamp: Date.now()
|
|
14889
|
+
});
|
|
14890
|
+
narrationStore.getState().appendDebugEvent({
|
|
14891
|
+
kind: "chunk",
|
|
14892
|
+
summary: `chunk \u2192 ${chunk.slice(0, 80)}${chunk.length > 80 ? "\u2026" : ""}`,
|
|
14893
|
+
payload: { chunk, pageNumber }
|
|
14894
|
+
});
|
|
14895
|
+
abortRef.current?.abort();
|
|
14896
|
+
abortRef.current = new AbortController();
|
|
14897
|
+
narrationStore.getState().setLlmStatus("in-flight");
|
|
14898
|
+
narrationStore.getState().appendDebugEvent({
|
|
14899
|
+
kind: "llm-request",
|
|
14900
|
+
summary: `LLM ${llm.model} (page ${pageNumber}, ${page2.blocks.length} blocks)`,
|
|
14901
|
+
payload: { model: llm.model, pageNumber, blockCount: page2.blocks.length }
|
|
14902
|
+
});
|
|
14903
|
+
const result = await directStoryboard(llm, {
|
|
14904
|
+
chunk,
|
|
14905
|
+
pageNumber,
|
|
14906
|
+
page: page2,
|
|
14907
|
+
index,
|
|
14908
|
+
history: narrationStore.getState().chunkHistory,
|
|
14909
|
+
camera: narrationStore.getState().camera,
|
|
14910
|
+
activeOverlays: narrationStore.getState().activeOverlays,
|
|
14911
|
+
signal: abortRef.current.signal,
|
|
14912
|
+
timeoutMs: llmTimeoutMs
|
|
14913
|
+
});
|
|
14914
|
+
if (result.storyboard) {
|
|
14915
|
+
narrationStore.getState().setLlmStatus("idle");
|
|
14916
|
+
narrationStore.getState().appendDebugEvent({
|
|
14917
|
+
kind: "llm-response",
|
|
14918
|
+
summary: `storyboard \u2713 ${result.storyboard.steps.length} steps \u2014 ${result.storyboard.reasoning.slice(0, 60)}`,
|
|
14919
|
+
payload: { raw: result.raw, storyboard: result.storyboard }
|
|
14920
|
+
});
|
|
14921
|
+
engineRef.current?.execute(result.storyboard);
|
|
14922
|
+
narrationStore.getState().appendDebugEvent({
|
|
14923
|
+
kind: "storyboard-execute",
|
|
14924
|
+
summary: `engine executing ${result.storyboard.steps.length} steps`,
|
|
14925
|
+
payload: result.storyboard.steps.map((s) => ({
|
|
14926
|
+
at_ms: s.at_ms,
|
|
14927
|
+
type: s.action.type,
|
|
14928
|
+
target: "target_block" in s.action ? s.action.target_block : "target" in s.action ? s.action.target : void 0
|
|
14929
|
+
}))
|
|
14930
|
+
});
|
|
14931
|
+
} else {
|
|
14932
|
+
narrationStore.getState().setLlmStatus("failed", result.error ?? "unknown");
|
|
14933
|
+
narrationStore.getState().appendDebugEvent({
|
|
14934
|
+
kind: "llm-error",
|
|
14935
|
+
summary: `LLM failed: ${(result.error ?? "unknown").slice(0, 80)}`,
|
|
14936
|
+
payload: { error: result.error, raw: result.raw }
|
|
14937
|
+
});
|
|
14938
|
+
if (embeddingProvider) {
|
|
14939
|
+
try {
|
|
14940
|
+
const match = await matchChunkToBlock(chunk, page2, embeddingProvider);
|
|
14941
|
+
const fallbackSb = storyboardFromMatch(match, page2);
|
|
14942
|
+
narrationStore.getState().appendDebugEvent({
|
|
14943
|
+
kind: "fallback-fired",
|
|
14944
|
+
summary: `embedding fallback \u2192 ${match?.block.block_id ?? "no match"}`,
|
|
14945
|
+
payload: { match, storyboard: fallbackSb }
|
|
14946
|
+
});
|
|
14947
|
+
engineRef.current?.execute(fallbackSb);
|
|
14948
|
+
} catch (e) {
|
|
14949
|
+
narrationStore.getState().appendDebugEvent({
|
|
14950
|
+
kind: "llm-error",
|
|
14951
|
+
summary: `fallback also failed: ${e.message}`,
|
|
14952
|
+
payload: e
|
|
14953
|
+
});
|
|
14954
|
+
}
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
}, 200);
|
|
14958
|
+
return () => {
|
|
14959
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
14960
|
+
};
|
|
14961
|
+
}, [currentChunk, llm, index, pageNumber, narrationStore, embeddingProvider, llmTimeoutMs]);
|
|
14962
|
+
useEffect28(() => {
|
|
14963
|
+
if (!currentChunk) return;
|
|
14964
|
+
const t = setTimeout(() => {
|
|
14965
|
+
if (!engineRef.current) return;
|
|
14966
|
+
const hist = narrationStore.getState().chunkHistory;
|
|
14967
|
+
const latest = hist.length > 0 ? hist[hist.length - 1] : null;
|
|
14968
|
+
if (!latest) return;
|
|
14969
|
+
if (Date.now() - latest.timestamp < idleTimeoutMs) return;
|
|
14970
|
+
engineRef.current.resetVisuals();
|
|
14971
|
+
}, idleTimeoutMs + 100);
|
|
14972
|
+
return () => clearTimeout(t);
|
|
14973
|
+
}, [currentChunk, idleTimeoutMs, narrationStore]);
|
|
14974
|
+
const page = index.byPage.get(pageNumber);
|
|
14975
|
+
const dpiScale = page ? page.page_dimensions.dpi / 72 : 1;
|
|
14976
|
+
const rasterScale = dpiScale * (scale || 1);
|
|
14977
|
+
const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
|
|
14978
|
+
const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
|
|
14979
|
+
return /* @__PURE__ */ jsxs38(
|
|
14980
|
+
"div",
|
|
14981
|
+
{
|
|
14982
|
+
ref: containerRef,
|
|
14983
|
+
className,
|
|
14984
|
+
style: {
|
|
14985
|
+
position: "relative",
|
|
14986
|
+
width: "100%",
|
|
14987
|
+
height: "100%",
|
|
14988
|
+
overflow: "hidden",
|
|
14989
|
+
background: "#111"
|
|
14990
|
+
},
|
|
14991
|
+
"data-role": "tutor-mode-container",
|
|
14992
|
+
"data-page-loaded": page ? "true" : "false",
|
|
14993
|
+
children: [
|
|
14994
|
+
showExitButton ? /* @__PURE__ */ jsx52(
|
|
14995
|
+
"button",
|
|
14996
|
+
{
|
|
14997
|
+
onClick: () => {
|
|
14998
|
+
engineRef.current?.resetVisuals();
|
|
14999
|
+
onExitTutorMode?.();
|
|
15000
|
+
},
|
|
15001
|
+
style: {
|
|
15002
|
+
position: "absolute",
|
|
15003
|
+
top: 12,
|
|
15004
|
+
right: 12,
|
|
15005
|
+
zIndex: 60,
|
|
15006
|
+
minHeight: 40,
|
|
15007
|
+
minWidth: 40,
|
|
15008
|
+
padding: "8px 14px",
|
|
15009
|
+
border: "none",
|
|
15010
|
+
borderRadius: 8,
|
|
15011
|
+
background: "rgba(255,255,255,0.12)",
|
|
15012
|
+
color: "white",
|
|
15013
|
+
cursor: "pointer",
|
|
15014
|
+
fontFamily: "system-ui, sans-serif",
|
|
15015
|
+
fontSize: 14,
|
|
15016
|
+
touchAction: "manipulation"
|
|
15017
|
+
},
|
|
15018
|
+
"aria-label": "Reset view \u2014 clear overlays and fit the page",
|
|
15019
|
+
"data-role": "exit-tutor",
|
|
15020
|
+
children: "Reset view"
|
|
15021
|
+
}
|
|
15022
|
+
) : null,
|
|
15023
|
+
page ? /* @__PURE__ */ jsx52(CameraView, { camera, children: /* @__PURE__ */ jsxs38(
|
|
15024
|
+
"div",
|
|
15025
|
+
{
|
|
15026
|
+
style: {
|
|
15027
|
+
position: "absolute",
|
|
15028
|
+
top: "50%",
|
|
15029
|
+
left: "50%",
|
|
15030
|
+
width: baseW,
|
|
15031
|
+
height: baseH,
|
|
15032
|
+
transform: "translate(-50%, -50%)"
|
|
15033
|
+
},
|
|
15034
|
+
children: [
|
|
15035
|
+
/* @__PURE__ */ jsx52(
|
|
15036
|
+
PDFPage,
|
|
15037
|
+
{
|
|
15038
|
+
pageNumber,
|
|
15039
|
+
page: pageProxy,
|
|
15040
|
+
scale: rasterScale,
|
|
15041
|
+
rotation,
|
|
15042
|
+
showTextLayer: false,
|
|
15043
|
+
showHighlightLayer: false,
|
|
15044
|
+
showAnnotationLayer: false
|
|
15045
|
+
}
|
|
15046
|
+
),
|
|
15047
|
+
/* @__PURE__ */ jsx52(
|
|
15048
|
+
CinemaLayer,
|
|
15049
|
+
{
|
|
15050
|
+
page,
|
|
15051
|
+
index,
|
|
15052
|
+
overlays: activeOverlays,
|
|
15053
|
+
scale: scale || 1
|
|
15054
|
+
}
|
|
15055
|
+
)
|
|
15056
|
+
]
|
|
15057
|
+
}
|
|
15058
|
+
) }) : null,
|
|
15059
|
+
showSubtitles ? /* @__PURE__ */ jsx52(SubtitleBar, { text: currentChunk ?? null }) : null
|
|
15060
|
+
]
|
|
15061
|
+
}
|
|
15062
|
+
);
|
|
15063
|
+
}
|
|
15064
|
+
|
|
15065
|
+
// src/director/transformers-embedding.ts
|
|
15066
|
+
var loaded = null;
|
|
15067
|
+
function getLocalMiniLM() {
|
|
15068
|
+
if (loaded) return loaded;
|
|
15069
|
+
loaded = (async () => {
|
|
15070
|
+
const mod = await import(
|
|
15071
|
+
/* webpackIgnore: true */
|
|
15072
|
+
"@xenova/transformers"
|
|
15073
|
+
);
|
|
15074
|
+
const { pipeline } = mod;
|
|
15075
|
+
const extractor = await pipeline(
|
|
15076
|
+
"feature-extraction",
|
|
15077
|
+
"Xenova/all-MiniLM-L6-v2"
|
|
15078
|
+
);
|
|
15079
|
+
return {
|
|
15080
|
+
async embed(texts) {
|
|
15081
|
+
const out = [];
|
|
15082
|
+
for (const t of texts) {
|
|
15083
|
+
const result = await extractor(t, {
|
|
15084
|
+
pooling: "mean",
|
|
15085
|
+
normalize: true
|
|
15086
|
+
});
|
|
15087
|
+
out.push(new Float32Array(result.data.slice()));
|
|
15088
|
+
}
|
|
15089
|
+
return out;
|
|
15090
|
+
}
|
|
15091
|
+
};
|
|
15092
|
+
})();
|
|
15093
|
+
return loaded;
|
|
15094
|
+
}
|
|
15095
|
+
|
|
12530
15096
|
// src/index.ts
|
|
12531
15097
|
init_hooks();
|
|
12532
15098
|
init_store();
|
|
@@ -12537,18 +15103,26 @@ init_PluginManager();
|
|
|
12537
15103
|
// src/index.ts
|
|
12538
15104
|
init_utils();
|
|
12539
15105
|
export {
|
|
15106
|
+
AnimatedHighlight,
|
|
15107
|
+
AnimatedUnderline,
|
|
12540
15108
|
AnnotationLayer,
|
|
12541
15109
|
AnnotationToolbar,
|
|
12542
15110
|
AskAboutOverlay,
|
|
12543
15111
|
AskAboutTrigger,
|
|
15112
|
+
BookModeContainer,
|
|
12544
15113
|
BookmarksPanel,
|
|
15114
|
+
BoxOverlay,
|
|
15115
|
+
CalloutArrow,
|
|
15116
|
+
CameraView,
|
|
12545
15117
|
CanvasLayer,
|
|
15118
|
+
CinemaLayer,
|
|
12546
15119
|
ContinuousScrollContainer,
|
|
12547
15120
|
DocumentContainer,
|
|
12548
15121
|
DrawingCanvas,
|
|
12549
15122
|
DualPageContainer,
|
|
12550
15123
|
FloatingZoomControls,
|
|
12551
15124
|
FocusRegionLayer,
|
|
15125
|
+
GhostReference,
|
|
12552
15126
|
HighlightLayer,
|
|
12553
15127
|
HighlightPopover,
|
|
12554
15128
|
HighlightsPanel,
|
|
@@ -12565,32 +15139,46 @@ export {
|
|
|
12565
15139
|
PDFViewerContext,
|
|
12566
15140
|
PDFViewerProvider,
|
|
12567
15141
|
PluginManager,
|
|
15142
|
+
PulseOverlay,
|
|
12568
15143
|
QuickNoteButton,
|
|
12569
15144
|
QuickNotePopover,
|
|
15145
|
+
SYSTEM_PROMPT,
|
|
12570
15146
|
SearchPanel,
|
|
12571
15147
|
SelectionToolbar,
|
|
12572
15148
|
ShapePreview,
|
|
12573
15149
|
ShapeRenderer,
|
|
12574
15150
|
Sidebar,
|
|
15151
|
+
SpotlightMask,
|
|
15152
|
+
StickyLabel,
|
|
12575
15153
|
StickyNote,
|
|
15154
|
+
StoryboardActionSchema,
|
|
15155
|
+
StoryboardEngine,
|
|
15156
|
+
StoryboardSchema,
|
|
15157
|
+
SubtitleBar,
|
|
12576
15158
|
TakeawaysPanel,
|
|
12577
15159
|
TextLayer,
|
|
12578
15160
|
ThumbnailPanel,
|
|
12579
15161
|
Toolbar,
|
|
15162
|
+
TutorModeContainer,
|
|
12580
15163
|
VirtualizedDocumentContainer,
|
|
12581
15164
|
applyRotation,
|
|
15165
|
+
buildBBoxIndex,
|
|
15166
|
+
buildUserPrompt,
|
|
12582
15167
|
clearHighlights,
|
|
12583
15168
|
clearStudentData,
|
|
12584
15169
|
cn,
|
|
15170
|
+
cosineSimilarity,
|
|
12585
15171
|
countTextOnPage,
|
|
12586
15172
|
createAgentAPI,
|
|
12587
15173
|
createAgentStore,
|
|
12588
15174
|
createAnnotationStore,
|
|
15175
|
+
createNarrationStore,
|
|
12589
15176
|
createPDFViewer,
|
|
12590
15177
|
createPluginManager,
|
|
12591
15178
|
createSearchStore,
|
|
12592
15179
|
createStudentStore,
|
|
12593
15180
|
createViewerStore,
|
|
15181
|
+
directStoryboard,
|
|
12594
15182
|
doRectsIntersect,
|
|
12595
15183
|
downloadAnnotationsAsJSON,
|
|
12596
15184
|
downloadAnnotationsAsMarkdown,
|
|
@@ -12605,6 +15193,7 @@ export {
|
|
|
12605
15193
|
generateDocumentId,
|
|
12606
15194
|
getAllDocumentIds,
|
|
12607
15195
|
getAllStudentDataDocumentIds,
|
|
15196
|
+
getLocalMiniLM,
|
|
12608
15197
|
getMetadata,
|
|
12609
15198
|
getOutline,
|
|
12610
15199
|
getPage,
|
|
@@ -12622,17 +15211,23 @@ export {
|
|
|
12622
15211
|
loadDocumentWithCallbacks,
|
|
12623
15212
|
loadHighlights,
|
|
12624
15213
|
loadStudentData,
|
|
15214
|
+
makeOverlayId,
|
|
15215
|
+
matchChunkToBlock,
|
|
12625
15216
|
mergeAdjacentRects,
|
|
12626
15217
|
pdfToPercent,
|
|
12627
15218
|
pdfToViewport,
|
|
12628
15219
|
pdfjsLib,
|
|
12629
15220
|
percentToPDF,
|
|
12630
15221
|
percentToViewport,
|
|
15222
|
+
playPageTurnSound,
|
|
12631
15223
|
quickViewer,
|
|
12632
15224
|
removeRotation,
|
|
12633
15225
|
saveHighlights,
|
|
12634
15226
|
saveStudentData,
|
|
12635
15227
|
scaleRect,
|
|
15228
|
+
storyboardFromMatch,
|
|
15229
|
+
storyboardJsonSchema,
|
|
15230
|
+
truncate,
|
|
12636
15231
|
useAgentContext,
|
|
12637
15232
|
useAgentStore,
|
|
12638
15233
|
useAnnotationStore,
|