pdfjs-reader-core 0.1.5 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +997 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +420 -6
- package/dist/index.d.ts +420 -6
- package/dist/index.js +976 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -310,6 +310,9 @@ var init_highlight_storage = __esm({
|
|
|
310
310
|
|
|
311
311
|
// src/store/viewer-store.ts
|
|
312
312
|
import { createStore } from "zustand/vanilla";
|
|
313
|
+
function generateRequestId() {
|
|
314
|
+
return `scroll-${Date.now()}-${++requestCounter}`;
|
|
315
|
+
}
|
|
313
316
|
function createViewerStore(initialOverrides = {}) {
|
|
314
317
|
return createStore()((set, get) => ({
|
|
315
318
|
...initialState,
|
|
@@ -362,6 +365,46 @@ function createViewerStore(initialOverrides = {}) {
|
|
|
362
365
|
set({ currentPage: currentPage - 1 });
|
|
363
366
|
}
|
|
364
367
|
},
|
|
368
|
+
// Scroll coordination actions
|
|
369
|
+
requestScrollToPage: (page, behavior = "smooth") => {
|
|
370
|
+
const { numPages } = get();
|
|
371
|
+
const validPage = Math.max(1, Math.min(page, numPages));
|
|
372
|
+
return new Promise((resolve) => {
|
|
373
|
+
const requestId = generateRequestId();
|
|
374
|
+
const timeoutId = setTimeout(() => {
|
|
375
|
+
const callback = scrollCallbacks.get(requestId);
|
|
376
|
+
if (callback) {
|
|
377
|
+
scrollCallbacks.delete(requestId);
|
|
378
|
+
callback.resolve();
|
|
379
|
+
}
|
|
380
|
+
const currentRequest = get().scrollToPageRequest;
|
|
381
|
+
if (currentRequest?.requestId === requestId) {
|
|
382
|
+
set({ scrollToPageRequest: null });
|
|
383
|
+
}
|
|
384
|
+
}, SCROLL_TIMEOUT_MS);
|
|
385
|
+
scrollCallbacks.set(requestId, { resolve, timeoutId });
|
|
386
|
+
set({
|
|
387
|
+
currentPage: validPage,
|
|
388
|
+
scrollToPageRequest: {
|
|
389
|
+
page: validPage,
|
|
390
|
+
requestId,
|
|
391
|
+
behavior
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
completeScrollRequest: (requestId) => {
|
|
397
|
+
const callback = scrollCallbacks.get(requestId);
|
|
398
|
+
if (callback) {
|
|
399
|
+
clearTimeout(callback.timeoutId);
|
|
400
|
+
scrollCallbacks.delete(requestId);
|
|
401
|
+
callback.resolve();
|
|
402
|
+
}
|
|
403
|
+
const currentRequest = get().scrollToPageRequest;
|
|
404
|
+
if (currentRequest?.requestId === requestId) {
|
|
405
|
+
set({ scrollToPageRequest: null });
|
|
406
|
+
}
|
|
407
|
+
},
|
|
365
408
|
// Zoom actions
|
|
366
409
|
setScale: (scale) => {
|
|
367
410
|
const clampedScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
|
|
@@ -428,13 +471,16 @@ function createViewerStore(initialOverrides = {}) {
|
|
|
428
471
|
}
|
|
429
472
|
}));
|
|
430
473
|
}
|
|
431
|
-
var ZOOM_LEVELS, MIN_SCALE, MAX_SCALE, initialState;
|
|
474
|
+
var ZOOM_LEVELS, MIN_SCALE, MAX_SCALE, SCROLL_TIMEOUT_MS, scrollCallbacks, requestCounter, initialState;
|
|
432
475
|
var init_viewer_store = __esm({
|
|
433
476
|
"src/store/viewer-store.ts"() {
|
|
434
477
|
"use strict";
|
|
435
478
|
ZOOM_LEVELS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
|
|
436
479
|
MIN_SCALE = 0.1;
|
|
437
480
|
MAX_SCALE = 10;
|
|
481
|
+
SCROLL_TIMEOUT_MS = 3e3;
|
|
482
|
+
scrollCallbacks = /* @__PURE__ */ new Map();
|
|
483
|
+
requestCounter = 0;
|
|
438
484
|
initialState = {
|
|
439
485
|
// Document state
|
|
440
486
|
document: null,
|
|
@@ -445,6 +491,8 @@ var init_viewer_store = __esm({
|
|
|
445
491
|
currentPage: 1,
|
|
446
492
|
scale: 1,
|
|
447
493
|
rotation: 0,
|
|
494
|
+
// Scroll coordination
|
|
495
|
+
scrollToPageRequest: null,
|
|
448
496
|
// UI state
|
|
449
497
|
viewMode: "single",
|
|
450
498
|
scrollMode: "single",
|
|
@@ -1253,6 +1301,267 @@ var init_agent_api = __esm({
|
|
|
1253
1301
|
}
|
|
1254
1302
|
});
|
|
1255
1303
|
|
|
1304
|
+
// src/utils/text-search.ts
|
|
1305
|
+
async function extractPageText(document2, pageNumber) {
|
|
1306
|
+
const page = await document2.getPage(pageNumber);
|
|
1307
|
+
const textContent = await page.getTextContent();
|
|
1308
|
+
const viewport = page.getViewport({ scale: 1 });
|
|
1309
|
+
let fullText = "";
|
|
1310
|
+
const textItems = [];
|
|
1311
|
+
for (const item of textContent.items) {
|
|
1312
|
+
if ("str" in item && item.str) {
|
|
1313
|
+
textItems.push({
|
|
1314
|
+
text: item.str,
|
|
1315
|
+
transform: item.transform,
|
|
1316
|
+
width: item.width ?? 0,
|
|
1317
|
+
height: item.height ?? 12
|
|
1318
|
+
});
|
|
1319
|
+
fullText += item.str;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return { fullText, textItems, viewport };
|
|
1323
|
+
}
|
|
1324
|
+
function calculateMatchRects(textItems, startOffset, length, viewport) {
|
|
1325
|
+
const rects = [];
|
|
1326
|
+
let currentOffset = 0;
|
|
1327
|
+
for (const item of textItems) {
|
|
1328
|
+
const itemStart = currentOffset;
|
|
1329
|
+
const itemEnd = currentOffset + item.text.length;
|
|
1330
|
+
if (itemEnd > startOffset && itemStart < startOffset + length) {
|
|
1331
|
+
const [, , c, d, tx, ty] = item.transform;
|
|
1332
|
+
const x = tx;
|
|
1333
|
+
const y = viewport.height - ty;
|
|
1334
|
+
const height = Math.sqrt(c * c + d * d);
|
|
1335
|
+
const matchStartInItem = Math.max(0, startOffset - itemStart);
|
|
1336
|
+
const matchEndInItem = Math.min(item.text.length, startOffset + length - itemStart);
|
|
1337
|
+
const charWidth = item.text.length > 0 ? item.width / item.text.length : item.width;
|
|
1338
|
+
const matchWidth = charWidth * (matchEndInItem - matchStartInItem);
|
|
1339
|
+
const matchX = x + charWidth * matchStartInItem;
|
|
1340
|
+
const yOffset = height * 0.15;
|
|
1341
|
+
rects.push({
|
|
1342
|
+
x: matchX,
|
|
1343
|
+
y: y - height + yOffset,
|
|
1344
|
+
width: matchWidth,
|
|
1345
|
+
height
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
currentOffset = itemEnd;
|
|
1349
|
+
}
|
|
1350
|
+
return rects;
|
|
1351
|
+
}
|
|
1352
|
+
async function findTextOnPage(document2, pageNumber, query, options = {}) {
|
|
1353
|
+
const { caseSensitive = false, wholeWord = false } = options;
|
|
1354
|
+
if (!query || pageNumber < 1 || pageNumber > document2.numPages) {
|
|
1355
|
+
return [];
|
|
1356
|
+
}
|
|
1357
|
+
const { fullText, textItems, viewport } = await extractPageText(document2, pageNumber);
|
|
1358
|
+
const matches = [];
|
|
1359
|
+
const searchText = caseSensitive ? query : query.toLowerCase();
|
|
1360
|
+
const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
|
|
1361
|
+
let startIndex = 0;
|
|
1362
|
+
while (true) {
|
|
1363
|
+
const matchIndex = textToSearch.indexOf(searchText, startIndex);
|
|
1364
|
+
if (matchIndex === -1) break;
|
|
1365
|
+
if (wholeWord) {
|
|
1366
|
+
const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
|
|
1367
|
+
const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
|
|
1368
|
+
if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
|
|
1369
|
+
startIndex = matchIndex + 1;
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
const matchRects = calculateMatchRects(textItems, matchIndex, query.length, viewport);
|
|
1374
|
+
if (matchRects.length > 0) {
|
|
1375
|
+
matches.push({
|
|
1376
|
+
text: fullText.substring(matchIndex, matchIndex + query.length),
|
|
1377
|
+
rects: matchRects,
|
|
1378
|
+
pageNumber,
|
|
1379
|
+
startIndex: matchIndex
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
startIndex = matchIndex + 1;
|
|
1383
|
+
}
|
|
1384
|
+
return matches;
|
|
1385
|
+
}
|
|
1386
|
+
async function findTextInDocument(document2, query, options = {}) {
|
|
1387
|
+
const { pageRange, ...findOptions } = options;
|
|
1388
|
+
const pagesToSearch = pageRange ?? Array.from({ length: document2.numPages }, (_, i) => i + 1);
|
|
1389
|
+
const allMatches = [];
|
|
1390
|
+
for (const pageNum of pagesToSearch) {
|
|
1391
|
+
if (pageNum < 1 || pageNum > document2.numPages) continue;
|
|
1392
|
+
try {
|
|
1393
|
+
const matches = await findTextOnPage(document2, pageNum, query, findOptions);
|
|
1394
|
+
allMatches.push(...matches);
|
|
1395
|
+
} catch {
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return allMatches;
|
|
1399
|
+
}
|
|
1400
|
+
function mergeAdjacentRects(rects) {
|
|
1401
|
+
if (rects.length === 0) return [];
|
|
1402
|
+
const sorted = [...rects].sort((a, b) => a.y - b.y || a.x - b.x);
|
|
1403
|
+
const merged = [];
|
|
1404
|
+
let current = { ...sorted[0] };
|
|
1405
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
1406
|
+
const rect = sorted[i];
|
|
1407
|
+
if (Math.abs(rect.y - current.y) < 2 && rect.x <= current.x + current.width + 2) {
|
|
1408
|
+
const newRight = Math.max(current.x + current.width, rect.x + rect.width);
|
|
1409
|
+
current.width = newRight - current.x;
|
|
1410
|
+
current.height = Math.max(current.height, rect.height);
|
|
1411
|
+
} else {
|
|
1412
|
+
merged.push(current);
|
|
1413
|
+
current = { ...rect };
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
merged.push(current);
|
|
1417
|
+
return merged;
|
|
1418
|
+
}
|
|
1419
|
+
async function getPageText(document2, pageNumber) {
|
|
1420
|
+
if (pageNumber < 1 || pageNumber > document2.numPages) {
|
|
1421
|
+
return "";
|
|
1422
|
+
}
|
|
1423
|
+
const page = await document2.getPage(pageNumber);
|
|
1424
|
+
const textContent = await page.getTextContent();
|
|
1425
|
+
return textContent.items.filter((item) => "str" in item).map((item) => item.str).join("");
|
|
1426
|
+
}
|
|
1427
|
+
async function countTextOnPage(document2, pageNumber, query, options = {}) {
|
|
1428
|
+
const { caseSensitive = false, wholeWord = false } = options;
|
|
1429
|
+
if (!query || pageNumber < 1 || pageNumber > document2.numPages) {
|
|
1430
|
+
return 0;
|
|
1431
|
+
}
|
|
1432
|
+
const text = await getPageText(document2, pageNumber);
|
|
1433
|
+
const searchText = caseSensitive ? query : query.toLowerCase();
|
|
1434
|
+
const textToSearch = caseSensitive ? text : text.toLowerCase();
|
|
1435
|
+
let count = 0;
|
|
1436
|
+
let startIndex = 0;
|
|
1437
|
+
while (true) {
|
|
1438
|
+
const matchIndex = textToSearch.indexOf(searchText, startIndex);
|
|
1439
|
+
if (matchIndex === -1) break;
|
|
1440
|
+
if (wholeWord) {
|
|
1441
|
+
const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
|
|
1442
|
+
const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
|
|
1443
|
+
if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
|
|
1444
|
+
startIndex = matchIndex + 1;
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
count++;
|
|
1449
|
+
startIndex = matchIndex + 1;
|
|
1450
|
+
}
|
|
1451
|
+
return count;
|
|
1452
|
+
}
|
|
1453
|
+
var init_text_search = __esm({
|
|
1454
|
+
"src/utils/text-search.ts"() {
|
|
1455
|
+
"use strict";
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
// src/utils/coordinates.ts
|
|
1460
|
+
function pdfToViewport(x, y, scale, pageHeight) {
|
|
1461
|
+
return {
|
|
1462
|
+
x: x * scale,
|
|
1463
|
+
y: (pageHeight - y) * scale
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
function viewportToPDF(x, y, scale, pageHeight) {
|
|
1467
|
+
return {
|
|
1468
|
+
x: x / scale,
|
|
1469
|
+
y: pageHeight - y / scale
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
function percentToPDF(xPercent, yPercent, pageWidth, pageHeight) {
|
|
1473
|
+
return {
|
|
1474
|
+
x: xPercent / 100 * pageWidth,
|
|
1475
|
+
y: yPercent / 100 * pageHeight
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
function pdfToPercent(x, y, pageWidth, pageHeight) {
|
|
1479
|
+
return {
|
|
1480
|
+
x: x / pageWidth * 100,
|
|
1481
|
+
y: y / pageHeight * 100
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
function percentToViewport(xPercent, yPercent, pageWidth, pageHeight, scale) {
|
|
1485
|
+
return {
|
|
1486
|
+
x: xPercent / 100 * pageWidth * scale,
|
|
1487
|
+
y: yPercent / 100 * pageHeight * scale
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function viewportToPercent(x, y, pageWidth, pageHeight, scale) {
|
|
1491
|
+
return {
|
|
1492
|
+
x: x / (pageWidth * scale) * 100,
|
|
1493
|
+
y: y / (pageHeight * scale) * 100
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function applyRotation(x, y, rotation, pageWidth, pageHeight) {
|
|
1497
|
+
const normalizedRotation = (rotation % 360 + 360) % 360;
|
|
1498
|
+
switch (normalizedRotation) {
|
|
1499
|
+
case 90:
|
|
1500
|
+
return { x: y, y: pageWidth - x };
|
|
1501
|
+
case 180:
|
|
1502
|
+
return { x: pageWidth - x, y: pageHeight - y };
|
|
1503
|
+
case 270:
|
|
1504
|
+
return { x: pageHeight - y, y: x };
|
|
1505
|
+
default:
|
|
1506
|
+
return { x, y };
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function removeRotation(x, y, rotation, pageWidth, pageHeight) {
|
|
1510
|
+
const normalizedRotation = (rotation % 360 + 360) % 360;
|
|
1511
|
+
switch (normalizedRotation) {
|
|
1512
|
+
case 90:
|
|
1513
|
+
return { x: pageWidth - y, y: x };
|
|
1514
|
+
case 180:
|
|
1515
|
+
return { x: pageWidth - x, y: pageHeight - y };
|
|
1516
|
+
case 270:
|
|
1517
|
+
return { x: y, y: pageHeight - x };
|
|
1518
|
+
default:
|
|
1519
|
+
return { x, y };
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
function getRotatedDimensions(width, height, rotation) {
|
|
1523
|
+
const normalizedRotation = (rotation % 360 + 360) % 360;
|
|
1524
|
+
if (normalizedRotation === 90 || normalizedRotation === 270) {
|
|
1525
|
+
return { width: height, height: width };
|
|
1526
|
+
}
|
|
1527
|
+
return { width, height };
|
|
1528
|
+
}
|
|
1529
|
+
function scaleRect(rect, fromScale, toScale) {
|
|
1530
|
+
const ratio = toScale / fromScale;
|
|
1531
|
+
return {
|
|
1532
|
+
x: rect.x * ratio,
|
|
1533
|
+
y: rect.y * ratio,
|
|
1534
|
+
width: rect.width * ratio,
|
|
1535
|
+
height: rect.height * ratio
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
function isPointInRect(point, rect) {
|
|
1539
|
+
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
1540
|
+
}
|
|
1541
|
+
function doRectsIntersect(rectA, rectB) {
|
|
1542
|
+
return !(rectA.x + rectA.width < rectB.x || rectB.x + rectB.width < rectA.x || rectA.y + rectA.height < rectB.y || rectB.y + rectB.height < rectA.y);
|
|
1543
|
+
}
|
|
1544
|
+
function getRectIntersection(rectA, rectB) {
|
|
1545
|
+
const x = Math.max(rectA.x, rectB.x);
|
|
1546
|
+
const y = Math.max(rectA.y, rectB.y);
|
|
1547
|
+
const right = Math.min(rectA.x + rectA.width, rectB.x + rectB.width);
|
|
1548
|
+
const bottom = Math.min(rectA.y + rectA.height, rectB.y + rectB.height);
|
|
1549
|
+
if (right <= x || bottom <= y) {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
return {
|
|
1553
|
+
x,
|
|
1554
|
+
y,
|
|
1555
|
+
width: right - x,
|
|
1556
|
+
height: bottom - y
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
var init_coordinates = __esm({
|
|
1560
|
+
"src/utils/coordinates.ts"() {
|
|
1561
|
+
"use strict";
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1256
1565
|
// src/utils/index.ts
|
|
1257
1566
|
var init_utils = __esm({
|
|
1258
1567
|
"src/utils/index.ts"() {
|
|
@@ -1265,6 +1574,8 @@ var init_utils = __esm({
|
|
|
1265
1574
|
init_export_annotations();
|
|
1266
1575
|
init_student_storage();
|
|
1267
1576
|
init_agent_api();
|
|
1577
|
+
init_text_search();
|
|
1578
|
+
init_coordinates();
|
|
1268
1579
|
}
|
|
1269
1580
|
});
|
|
1270
1581
|
|
|
@@ -1321,7 +1632,7 @@ function createSearchStore(initialOverrides = {}) {
|
|
|
1321
1632
|
}
|
|
1322
1633
|
}
|
|
1323
1634
|
const matchText = pageText.substring(startIndex, startIndex + query.length);
|
|
1324
|
-
const rects =
|
|
1635
|
+
const rects = calculateMatchRects2(textItems, startIndex, query.length, viewport);
|
|
1325
1636
|
results.push({
|
|
1326
1637
|
pageNumber: pageNum,
|
|
1327
1638
|
matchIndex: matchIndex++,
|
|
@@ -1382,7 +1693,7 @@ function createSearchStore(initialOverrides = {}) {
|
|
|
1382
1693
|
}
|
|
1383
1694
|
}));
|
|
1384
1695
|
}
|
|
1385
|
-
function
|
|
1696
|
+
function calculateMatchRects2(textItems, startOffset, length, viewport) {
|
|
1386
1697
|
const rects = [];
|
|
1387
1698
|
let currentOffset = 0;
|
|
1388
1699
|
for (const item of textItems) {
|
|
@@ -1397,9 +1708,10 @@ function calculateMatchRects(textItems, startOffset, length, viewport) {
|
|
|
1397
1708
|
const matchEndInItem = Math.min(item.text.length, startOffset + length - itemStart);
|
|
1398
1709
|
const matchWidth = item.width / item.text.length * (matchEndInItem - matchStartInItem);
|
|
1399
1710
|
const matchX = x + item.width / item.text.length * matchStartInItem;
|
|
1711
|
+
const yOffset = height * 0.15;
|
|
1400
1712
|
rects.push({
|
|
1401
1713
|
x: matchX,
|
|
1402
|
-
y: y - height,
|
|
1714
|
+
y: y - height + yOffset,
|
|
1403
1715
|
width: matchWidth,
|
|
1404
1716
|
height
|
|
1405
1717
|
});
|
|
@@ -7889,6 +8201,8 @@ var init_DocumentContainer = __esm({
|
|
|
7889
8201
|
nextPage,
|
|
7890
8202
|
previousPage
|
|
7891
8203
|
} = usePDFViewer();
|
|
8204
|
+
const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
|
|
8205
|
+
const { viewerStore } = usePDFViewerStores();
|
|
7892
8206
|
const [currentPageObj, setCurrentPageObj] = useState18(null);
|
|
7893
8207
|
const [isLoadingPage, setIsLoadingPage] = useState18(false);
|
|
7894
8208
|
const containerRef = useRef16(null);
|
|
@@ -7954,6 +8268,11 @@ var init_DocumentContainer = __esm({
|
|
|
7954
8268
|
const page = await document2.getPage(currentPage);
|
|
7955
8269
|
if (!cancelled && document2 === documentRef.current) {
|
|
7956
8270
|
setCurrentPageObj(page);
|
|
8271
|
+
if (scrollToPageRequest && scrollToPageRequest.page === currentPage) {
|
|
8272
|
+
requestAnimationFrame(() => {
|
|
8273
|
+
viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
|
|
8274
|
+
});
|
|
8275
|
+
}
|
|
7957
8276
|
}
|
|
7958
8277
|
} catch (error) {
|
|
7959
8278
|
if (!cancelled) {
|
|
@@ -7972,7 +8291,7 @@ var init_DocumentContainer = __esm({
|
|
|
7972
8291
|
return () => {
|
|
7973
8292
|
cancelled = true;
|
|
7974
8293
|
};
|
|
7975
|
-
}, [document2, currentPage]);
|
|
8294
|
+
}, [document2, currentPage, scrollToPageRequest, viewerStore]);
|
|
7976
8295
|
const getPageElement = useCallback29(() => {
|
|
7977
8296
|
return containerRef.current?.querySelector(`[data-page-number="${currentPage}"]`);
|
|
7978
8297
|
}, [currentPage]);
|
|
@@ -8114,6 +8433,8 @@ var init_VirtualizedDocumentContainer = __esm({
|
|
|
8114
8433
|
nextPage,
|
|
8115
8434
|
previousPage
|
|
8116
8435
|
} = usePDFViewer();
|
|
8436
|
+
const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
|
|
8437
|
+
const { viewerStore } = usePDFViewerStores();
|
|
8117
8438
|
const containerRef = useRef17(null);
|
|
8118
8439
|
const scrollContainerRef = useRef17(null);
|
|
8119
8440
|
const documentRef = useRef17(null);
|
|
@@ -8251,6 +8572,45 @@ var init_VirtualizedDocumentContainer = __esm({
|
|
|
8251
8572
|
loadPages();
|
|
8252
8573
|
}, [document2, visiblePages, pageObjects]);
|
|
8253
8574
|
useEffect20(() => {
|
|
8575
|
+
if (!scrollToPageRequest || !scrollContainerRef.current || pageInfos.length === 0) return;
|
|
8576
|
+
const { page, requestId, behavior } = scrollToPageRequest;
|
|
8577
|
+
const pageInfo = pageInfos.find((p) => p.pageNumber === page);
|
|
8578
|
+
if (!pageInfo) {
|
|
8579
|
+
viewerStore.getState().completeScrollRequest(requestId);
|
|
8580
|
+
return;
|
|
8581
|
+
}
|
|
8582
|
+
const container = scrollContainerRef.current;
|
|
8583
|
+
const targetScroll = pageInfo.top - pageGap;
|
|
8584
|
+
const scrollTop = container.scrollTop;
|
|
8585
|
+
const viewportHeight = container.clientHeight;
|
|
8586
|
+
const isVisible = targetScroll >= scrollTop && pageInfo.top + pageInfo.height <= scrollTop + viewportHeight;
|
|
8587
|
+
if (isVisible) {
|
|
8588
|
+
viewerStore.getState().completeScrollRequest(requestId);
|
|
8589
|
+
return;
|
|
8590
|
+
}
|
|
8591
|
+
container.scrollTo({
|
|
8592
|
+
top: targetScroll,
|
|
8593
|
+
behavior
|
|
8594
|
+
});
|
|
8595
|
+
if (behavior === "instant") {
|
|
8596
|
+
requestAnimationFrame(() => {
|
|
8597
|
+
viewerStore.getState().completeScrollRequest(requestId);
|
|
8598
|
+
});
|
|
8599
|
+
} else {
|
|
8600
|
+
let scrollEndTimeout;
|
|
8601
|
+
const handleScrollEnd = () => {
|
|
8602
|
+
clearTimeout(scrollEndTimeout);
|
|
8603
|
+
scrollEndTimeout = setTimeout(() => {
|
|
8604
|
+
container.removeEventListener("scroll", handleScrollEnd);
|
|
8605
|
+
viewerStore.getState().completeScrollRequest(requestId);
|
|
8606
|
+
}, 100);
|
|
8607
|
+
};
|
|
8608
|
+
container.addEventListener("scroll", handleScrollEnd, { passive: true });
|
|
8609
|
+
handleScrollEnd();
|
|
8610
|
+
}
|
|
8611
|
+
}, [scrollToPageRequest, pageInfos, pageGap, viewerStore]);
|
|
8612
|
+
useEffect20(() => {
|
|
8613
|
+
if (scrollToPageRequest) return;
|
|
8254
8614
|
if (!scrollContainerRef.current || pageInfos.length === 0) return;
|
|
8255
8615
|
const pageInfo = pageInfos.find((p) => p.pageNumber === currentPage);
|
|
8256
8616
|
if (pageInfo) {
|
|
@@ -8265,7 +8625,7 @@ var init_VirtualizedDocumentContainer = __esm({
|
|
|
8265
8625
|
});
|
|
8266
8626
|
}
|
|
8267
8627
|
}
|
|
8268
|
-
}, [currentPage, pageInfos, pageGap]);
|
|
8628
|
+
}, [currentPage, pageInfos, pageGap, scrollToPageRequest]);
|
|
8269
8629
|
const handlePinchZoom = useCallback30(
|
|
8270
8630
|
(pinchScale) => {
|
|
8271
8631
|
const newScale = Math.max(0.25, Math.min(4, baseScaleRef.current * pinchScale));
|
|
@@ -8472,6 +8832,8 @@ var init_DualPageContainer = __esm({
|
|
|
8472
8832
|
setScale,
|
|
8473
8833
|
goToPage
|
|
8474
8834
|
} = usePDFViewer();
|
|
8835
|
+
const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
|
|
8836
|
+
const { viewerStore } = usePDFViewerStores();
|
|
8475
8837
|
const containerRef = useRef18(null);
|
|
8476
8838
|
const documentRef = useRef18(null);
|
|
8477
8839
|
const baseScaleRef = useRef18(scale);
|
|
@@ -8557,6 +8919,14 @@ var init_DualPageContainer = __esm({
|
|
|
8557
8919
|
if (!cancelled) {
|
|
8558
8920
|
setLeftPage(left);
|
|
8559
8921
|
setRightPage(right);
|
|
8922
|
+
if (scrollToPageRequest) {
|
|
8923
|
+
const requestedPage = scrollToPageRequest.page;
|
|
8924
|
+
if (requestedPage === spread2.left || requestedPage === spread2.right) {
|
|
8925
|
+
requestAnimationFrame(() => {
|
|
8926
|
+
viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
|
|
8927
|
+
});
|
|
8928
|
+
}
|
|
8929
|
+
}
|
|
8560
8930
|
}
|
|
8561
8931
|
} catch (error) {
|
|
8562
8932
|
if (!cancelled) {
|
|
@@ -8572,7 +8942,7 @@ var init_DualPageContainer = __esm({
|
|
|
8572
8942
|
return () => {
|
|
8573
8943
|
cancelled = true;
|
|
8574
8944
|
};
|
|
8575
|
-
}, [document2, currentPage, getSpreadPages]);
|
|
8945
|
+
}, [document2, currentPage, getSpreadPages, scrollToPageRequest, viewerStore]);
|
|
8576
8946
|
const goToPreviousSpread = useCallback31(() => {
|
|
8577
8947
|
const spread2 = getSpreadPages(currentPage);
|
|
8578
8948
|
const leftmostPage = spread2.left || spread2.right || currentPage;
|
|
@@ -8769,10 +9139,14 @@ var init_FloatingZoomControls = __esm({
|
|
|
8769
9139
|
const scale = useViewerStore((s) => s.scale);
|
|
8770
9140
|
const document2 = useViewerStore((s) => s.document);
|
|
8771
9141
|
const handleZoomIn = useCallback32(() => {
|
|
8772
|
-
viewerStore.getState().
|
|
9142
|
+
const currentScale = viewerStore.getState().scale;
|
|
9143
|
+
const newScale = Math.min(4, currentScale + 0.05);
|
|
9144
|
+
viewerStore.getState().setScale(newScale);
|
|
8773
9145
|
}, [viewerStore]);
|
|
8774
9146
|
const handleZoomOut = useCallback32(() => {
|
|
8775
|
-
viewerStore.getState().
|
|
9147
|
+
const currentScale = viewerStore.getState().scale;
|
|
9148
|
+
const newScale = Math.max(0.1, currentScale - 0.05);
|
|
9149
|
+
viewerStore.getState().setScale(newScale);
|
|
8776
9150
|
}, [viewerStore]);
|
|
8777
9151
|
const handleFitToWidth = useCallback32(() => {
|
|
8778
9152
|
viewerStore.getState().setScale(1);
|
|
@@ -8909,24 +9283,33 @@ function getSrcIdentifier(src) {
|
|
|
8909
9283
|
const last = Array.from(data.slice(-4)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8910
9284
|
return `binary:${len}:${first}:${last}`;
|
|
8911
9285
|
}
|
|
8912
|
-
function
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
const
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
const
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
9286
|
+
function calculateMatchRects3(textItems, startOffset, length, viewport) {
|
|
9287
|
+
const rects = [];
|
|
9288
|
+
let currentOffset = 0;
|
|
9289
|
+
for (const item of textItems) {
|
|
9290
|
+
const itemStart = currentOffset;
|
|
9291
|
+
const itemEnd = currentOffset + item.text.length;
|
|
9292
|
+
if (itemEnd > startOffset && itemStart < startOffset + length) {
|
|
9293
|
+
const [, , c, d, tx, ty] = item.transform;
|
|
9294
|
+
const x = tx;
|
|
9295
|
+
const y = viewport.height - ty;
|
|
9296
|
+
const height = Math.sqrt(c * c + d * d);
|
|
9297
|
+
const matchStartInItem = Math.max(0, startOffset - itemStart);
|
|
9298
|
+
const matchEndInItem = Math.min(item.text.length, startOffset + length - itemStart);
|
|
9299
|
+
const charWidth = item.text.length > 0 ? item.width / item.text.length : item.width;
|
|
9300
|
+
const matchWidth = charWidth * (matchEndInItem - matchStartInItem);
|
|
9301
|
+
const matchX = x + charWidth * matchStartInItem;
|
|
9302
|
+
const yOffset = height * 0.15;
|
|
9303
|
+
rects.push({
|
|
9304
|
+
x: matchX,
|
|
9305
|
+
y: y - height + yOffset,
|
|
9306
|
+
width: matchWidth,
|
|
9307
|
+
height
|
|
9308
|
+
});
|
|
8926
9309
|
}
|
|
9310
|
+
currentOffset = itemEnd;
|
|
8927
9311
|
}
|
|
8928
|
-
|
|
8929
|
-
return merged;
|
|
9312
|
+
return rects;
|
|
8930
9313
|
}
|
|
8931
9314
|
var PDFViewerInner, PDFViewerInnerWithRef, PDFViewerClient;
|
|
8932
9315
|
var init_PDFViewerClient = __esm({
|
|
@@ -8945,6 +9328,7 @@ var init_PDFViewerClient = __esm({
|
|
|
8945
9328
|
PDFViewerInner = memo25(function PDFViewerInner2({
|
|
8946
9329
|
src,
|
|
8947
9330
|
initialPage = 1,
|
|
9331
|
+
page: controlledPage,
|
|
8948
9332
|
initialScale = "auto",
|
|
8949
9333
|
showToolbar = true,
|
|
8950
9334
|
showSidebar = true,
|
|
@@ -8954,9 +9338,17 @@ var init_PDFViewerClient = __esm({
|
|
|
8954
9338
|
onDocumentLoad,
|
|
8955
9339
|
onPageChange,
|
|
8956
9340
|
onScaleChange,
|
|
9341
|
+
onZoomChange,
|
|
8957
9342
|
onError,
|
|
9343
|
+
onPageRenderStart,
|
|
9344
|
+
onPageRenderComplete,
|
|
9345
|
+
onHighlightAdded,
|
|
9346
|
+
onHighlightRemoved,
|
|
9347
|
+
onAnnotationAdded,
|
|
8958
9348
|
workerSrc,
|
|
8959
9349
|
className,
|
|
9350
|
+
loadingComponent,
|
|
9351
|
+
errorComponent,
|
|
8960
9352
|
onReady
|
|
8961
9353
|
}) {
|
|
8962
9354
|
const { viewerStore, annotationStore, searchStore } = usePDFViewerStores();
|
|
@@ -8966,12 +9358,26 @@ var init_PDFViewerClient = __esm({
|
|
|
8966
9358
|
const onErrorRef = useRef19(onError);
|
|
8967
9359
|
const onPageChangeRef = useRef19(onPageChange);
|
|
8968
9360
|
const onScaleChangeRef = useRef19(onScaleChange);
|
|
9361
|
+
const onZoomChangeRef = useRef19(onZoomChange);
|
|
9362
|
+
const onPageRenderStartRef = useRef19(onPageRenderStart);
|
|
9363
|
+
const onPageRenderCompleteRef = useRef19(onPageRenderComplete);
|
|
9364
|
+
const onHighlightAddedRef = useRef19(onHighlightAdded);
|
|
9365
|
+
const onHighlightRemovedRef = useRef19(onHighlightRemoved);
|
|
9366
|
+
const onAnnotationAddedRef = useRef19(onAnnotationAdded);
|
|
8969
9367
|
const onReadyRef = useRef19(onReady);
|
|
8970
9368
|
onDocumentLoadRef.current = onDocumentLoad;
|
|
8971
9369
|
onErrorRef.current = onError;
|
|
8972
9370
|
onPageChangeRef.current = onPageChange;
|
|
8973
9371
|
onScaleChangeRef.current = onScaleChange;
|
|
9372
|
+
onZoomChangeRef.current = onZoomChange;
|
|
9373
|
+
onPageRenderStartRef.current = onPageRenderStart;
|
|
9374
|
+
onPageRenderCompleteRef.current = onPageRenderComplete;
|
|
9375
|
+
onHighlightAddedRef.current = onHighlightAdded;
|
|
9376
|
+
onHighlightRemovedRef.current = onHighlightRemoved;
|
|
9377
|
+
onAnnotationAddedRef.current = onAnnotationAdded;
|
|
8974
9378
|
onReadyRef.current = onReady;
|
|
9379
|
+
const isControlled = controlledPage !== void 0;
|
|
9380
|
+
const prevControlledPageRef = useRef19(controlledPage);
|
|
8975
9381
|
const srcIdRef = useRef19(null);
|
|
8976
9382
|
const currentPage = useViewerStore((s) => s.currentPage);
|
|
8977
9383
|
const scale = useViewerStore((s) => s.scale);
|
|
@@ -9000,26 +9406,15 @@ var init_PDFViewerClient = __esm({
|
|
|
9000
9406
|
const textContent = await page.getTextContent();
|
|
9001
9407
|
const viewport = page.getViewport({ scale: 1 });
|
|
9002
9408
|
let fullText = "";
|
|
9003
|
-
const
|
|
9409
|
+
const textItems = [];
|
|
9004
9410
|
for (const item of textContent.items) {
|
|
9005
9411
|
if ("str" in item && item.str) {
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
for (let i = 0; i < item.str.length; i++) {
|
|
9013
|
-
charPositions.push({
|
|
9014
|
-
char: item.str[i],
|
|
9015
|
-
rect: {
|
|
9016
|
-
x: x + i * charWidth,
|
|
9017
|
-
y: y - height,
|
|
9018
|
-
width: charWidth,
|
|
9019
|
-
height
|
|
9020
|
-
}
|
|
9021
|
-
});
|
|
9022
|
-
}
|
|
9412
|
+
textItems.push({
|
|
9413
|
+
text: item.str,
|
|
9414
|
+
transform: item.transform,
|
|
9415
|
+
width: item.width ?? 0,
|
|
9416
|
+
height: item.height ?? 12
|
|
9417
|
+
});
|
|
9023
9418
|
fullText += item.str;
|
|
9024
9419
|
}
|
|
9025
9420
|
}
|
|
@@ -9028,18 +9423,16 @@ var init_PDFViewerClient = __esm({
|
|
|
9028
9423
|
while (true) {
|
|
9029
9424
|
const matchIndex = textToSearch.indexOf(searchText, startIndex);
|
|
9030
9425
|
if (matchIndex === -1) break;
|
|
9031
|
-
const matchRects =
|
|
9032
|
-
|
|
9033
|
-
|
|
9426
|
+
const matchRects = calculateMatchRects3(textItems, matchIndex, text.length, viewport);
|
|
9427
|
+
if (matchRects.length > 0) {
|
|
9428
|
+
const highlight = annotationStore.getState().addHighlight({
|
|
9429
|
+
pageNumber: pageNum,
|
|
9430
|
+
rects: matchRects,
|
|
9431
|
+
color,
|
|
9432
|
+
text: fullText.substring(matchIndex, matchIndex + text.length)
|
|
9433
|
+
});
|
|
9434
|
+
highlightIds.push(highlight.id);
|
|
9034
9435
|
}
|
|
9035
|
-
const mergedRects = mergeRects2(matchRects);
|
|
9036
|
-
const highlight = annotationStore.getState().addHighlight({
|
|
9037
|
-
pageNumber: pageNum,
|
|
9038
|
-
rects: mergedRects,
|
|
9039
|
-
color,
|
|
9040
|
-
text: fullText.substring(matchIndex, matchIndex + text.length)
|
|
9041
|
-
});
|
|
9042
|
-
highlightIds.push(highlight.id);
|
|
9043
9436
|
startIndex = matchIndex + 1;
|
|
9044
9437
|
}
|
|
9045
9438
|
} catch {
|
|
@@ -9112,8 +9505,9 @@ var init_PDFViewerClient = __esm({
|
|
|
9112
9505
|
}
|
|
9113
9506
|
},
|
|
9114
9507
|
// ==================== Navigation ====================
|
|
9115
|
-
goToPage: (page) => {
|
|
9116
|
-
|
|
9508
|
+
goToPage: async (page, options) => {
|
|
9509
|
+
const behavior = options?.behavior ?? "smooth";
|
|
9510
|
+
await viewerStore.getState().requestScrollToPage(page, behavior);
|
|
9117
9511
|
},
|
|
9118
9512
|
nextPage: () => {
|
|
9119
9513
|
viewerStore.getState().nextPage();
|
|
@@ -9173,6 +9567,233 @@ var init_PDFViewerClient = __esm({
|
|
|
9173
9567
|
clearSearch: () => {
|
|
9174
9568
|
searchStore.getState().clearSearch();
|
|
9175
9569
|
},
|
|
9570
|
+
// ==================== Combined Search & Highlight ====================
|
|
9571
|
+
searchAndHighlight: async (query, options) => {
|
|
9572
|
+
const doc = viewerStore.getState().document;
|
|
9573
|
+
if (!doc) {
|
|
9574
|
+
return { matchCount: 0, highlightIds: [], matches: [] };
|
|
9575
|
+
}
|
|
9576
|
+
const color = options?.color ?? "yellow";
|
|
9577
|
+
const caseSensitive = options?.caseSensitive ?? false;
|
|
9578
|
+
const wholeWord = options?.wholeWord ?? false;
|
|
9579
|
+
const scrollToFirst = options?.scrollToFirst ?? true;
|
|
9580
|
+
const clearPrevious = options?.clearPrevious ?? true;
|
|
9581
|
+
if (clearPrevious) {
|
|
9582
|
+
const existingHighlights = annotationStore.getState().highlights;
|
|
9583
|
+
for (const h of existingHighlights) {
|
|
9584
|
+
if (h.source === "search") {
|
|
9585
|
+
annotationStore.getState().removeHighlight(h.id);
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9588
|
+
}
|
|
9589
|
+
let pagesToSearch;
|
|
9590
|
+
if (options?.pageRange) {
|
|
9591
|
+
if (Array.isArray(options.pageRange)) {
|
|
9592
|
+
pagesToSearch = options.pageRange;
|
|
9593
|
+
} else {
|
|
9594
|
+
const { start, end } = options.pageRange;
|
|
9595
|
+
pagesToSearch = Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
|
9596
|
+
}
|
|
9597
|
+
} else {
|
|
9598
|
+
pagesToSearch = Array.from({ length: doc.numPages }, (_, i) => i + 1);
|
|
9599
|
+
}
|
|
9600
|
+
const result = {
|
|
9601
|
+
matchCount: 0,
|
|
9602
|
+
highlightIds: [],
|
|
9603
|
+
matches: []
|
|
9604
|
+
};
|
|
9605
|
+
const searchText = caseSensitive ? query : query.toLowerCase();
|
|
9606
|
+
for (const pageNum of pagesToSearch) {
|
|
9607
|
+
if (pageNum < 1 || pageNum > doc.numPages) continue;
|
|
9608
|
+
try {
|
|
9609
|
+
const page = await doc.getPage(pageNum);
|
|
9610
|
+
const textContent = await page.getTextContent();
|
|
9611
|
+
const viewport = page.getViewport({ scale: 1 });
|
|
9612
|
+
let fullText = "";
|
|
9613
|
+
const textItems = [];
|
|
9614
|
+
for (const item of textContent.items) {
|
|
9615
|
+
if ("str" in item && item.str) {
|
|
9616
|
+
textItems.push({
|
|
9617
|
+
text: item.str,
|
|
9618
|
+
transform: item.transform,
|
|
9619
|
+
width: item.width ?? 0,
|
|
9620
|
+
height: item.height ?? 12
|
|
9621
|
+
});
|
|
9622
|
+
fullText += item.str;
|
|
9623
|
+
}
|
|
9624
|
+
}
|
|
9625
|
+
const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
|
|
9626
|
+
let startIndex = 0;
|
|
9627
|
+
while (true) {
|
|
9628
|
+
const matchIndex = textToSearch.indexOf(searchText, startIndex);
|
|
9629
|
+
if (matchIndex === -1) break;
|
|
9630
|
+
if (wholeWord) {
|
|
9631
|
+
const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
|
|
9632
|
+
const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
|
|
9633
|
+
if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
|
|
9634
|
+
startIndex = matchIndex + 1;
|
|
9635
|
+
continue;
|
|
9636
|
+
}
|
|
9637
|
+
}
|
|
9638
|
+
const matchRects = calculateMatchRects3(textItems, matchIndex, query.length, viewport);
|
|
9639
|
+
if (matchRects.length > 0) {
|
|
9640
|
+
const highlight = annotationStore.getState().addHighlight({
|
|
9641
|
+
pageNumber: pageNum,
|
|
9642
|
+
rects: matchRects,
|
|
9643
|
+
color,
|
|
9644
|
+
text: fullText.substring(matchIndex, matchIndex + query.length),
|
|
9645
|
+
source: "search"
|
|
9646
|
+
});
|
|
9647
|
+
result.matchCount++;
|
|
9648
|
+
result.highlightIds.push(highlight.id);
|
|
9649
|
+
result.matches.push({
|
|
9650
|
+
pageNumber: pageNum,
|
|
9651
|
+
text: fullText.substring(matchIndex, matchIndex + query.length),
|
|
9652
|
+
highlightId: highlight.id,
|
|
9653
|
+
rects: matchRects
|
|
9654
|
+
});
|
|
9655
|
+
}
|
|
9656
|
+
startIndex = matchIndex + 1;
|
|
9657
|
+
}
|
|
9658
|
+
} catch {
|
|
9659
|
+
}
|
|
9660
|
+
}
|
|
9661
|
+
if (scrollToFirst && result.matches.length > 0) {
|
|
9662
|
+
const firstMatch = result.matches[0];
|
|
9663
|
+
await viewerStore.getState().requestScrollToPage(firstMatch.pageNumber, "smooth");
|
|
9664
|
+
}
|
|
9665
|
+
return result;
|
|
9666
|
+
},
|
|
9667
|
+
// ==================== Agent Tools ====================
|
|
9668
|
+
agentTools: {
|
|
9669
|
+
navigateToPage: async (page) => {
|
|
9670
|
+
try {
|
|
9671
|
+
const { currentPage: currentPage2, numPages } = viewerStore.getState();
|
|
9672
|
+
if (numPages === 0) {
|
|
9673
|
+
return {
|
|
9674
|
+
success: false,
|
|
9675
|
+
error: { code: "NO_DOCUMENT", message: "No document is loaded" }
|
|
9676
|
+
};
|
|
9677
|
+
}
|
|
9678
|
+
if (page < 1 || page > numPages) {
|
|
9679
|
+
return {
|
|
9680
|
+
success: false,
|
|
9681
|
+
error: { code: "INVALID_PAGE", message: `Page ${page} is out of range (1-${numPages})` }
|
|
9682
|
+
};
|
|
9683
|
+
}
|
|
9684
|
+
const previousPage = currentPage2;
|
|
9685
|
+
await viewerStore.getState().requestScrollToPage(page, "smooth");
|
|
9686
|
+
return {
|
|
9687
|
+
success: true,
|
|
9688
|
+
data: { previousPage, currentPage: page }
|
|
9689
|
+
};
|
|
9690
|
+
} catch (err) {
|
|
9691
|
+
return {
|
|
9692
|
+
success: false,
|
|
9693
|
+
error: { code: "NAVIGATION_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
|
|
9694
|
+
};
|
|
9695
|
+
}
|
|
9696
|
+
},
|
|
9697
|
+
highlightText: async (text, options) => {
|
|
9698
|
+
try {
|
|
9699
|
+
const highlightIds = await handle.highlightText(text, options);
|
|
9700
|
+
return {
|
|
9701
|
+
success: true,
|
|
9702
|
+
data: { matchCount: highlightIds.length, highlightIds }
|
|
9703
|
+
};
|
|
9704
|
+
} catch (err) {
|
|
9705
|
+
return {
|
|
9706
|
+
success: false,
|
|
9707
|
+
error: { code: "HIGHLIGHT_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
|
|
9708
|
+
};
|
|
9709
|
+
}
|
|
9710
|
+
},
|
|
9711
|
+
getPageContent: async (page) => {
|
|
9712
|
+
try {
|
|
9713
|
+
const doc = viewerStore.getState().document;
|
|
9714
|
+
if (!doc) {
|
|
9715
|
+
return {
|
|
9716
|
+
success: false,
|
|
9717
|
+
error: { code: "NO_DOCUMENT", message: "No document is loaded" }
|
|
9718
|
+
};
|
|
9719
|
+
}
|
|
9720
|
+
if (page < 1 || page > doc.numPages) {
|
|
9721
|
+
return {
|
|
9722
|
+
success: false,
|
|
9723
|
+
error: { code: "INVALID_PAGE", message: `Page ${page} is out of range (1-${doc.numPages})` }
|
|
9724
|
+
};
|
|
9725
|
+
}
|
|
9726
|
+
const pageObj = await doc.getPage(page);
|
|
9727
|
+
const textContent = await pageObj.getTextContent();
|
|
9728
|
+
const text = textContent.items.filter((item) => "str" in item).map((item) => item.str).join("");
|
|
9729
|
+
return {
|
|
9730
|
+
success: true,
|
|
9731
|
+
data: { text }
|
|
9732
|
+
};
|
|
9733
|
+
} catch (err) {
|
|
9734
|
+
return {
|
|
9735
|
+
success: false,
|
|
9736
|
+
error: { code: "CONTENT_FETCH_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
|
|
9737
|
+
};
|
|
9738
|
+
}
|
|
9739
|
+
},
|
|
9740
|
+
clearAllVisuals: async () => {
|
|
9741
|
+
try {
|
|
9742
|
+
const highlights = annotationStore.getState().highlights;
|
|
9743
|
+
for (const h of highlights) {
|
|
9744
|
+
annotationStore.getState().removeHighlight(h.id);
|
|
9745
|
+
}
|
|
9746
|
+
const annotations = annotationStore.getState().annotations;
|
|
9747
|
+
for (const a of annotations) {
|
|
9748
|
+
annotationStore.getState().removeAnnotation(a.id);
|
|
9749
|
+
}
|
|
9750
|
+
return { success: true };
|
|
9751
|
+
} catch (err) {
|
|
9752
|
+
return {
|
|
9753
|
+
success: false,
|
|
9754
|
+
error: { code: "CLEAR_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
|
|
9755
|
+
};
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
},
|
|
9759
|
+
// ==================== Coordinate Helpers ====================
|
|
9760
|
+
coordinates: {
|
|
9761
|
+
getPageDimensions: (page) => {
|
|
9762
|
+
const doc = viewerStore.getState().document;
|
|
9763
|
+
if (!doc || page < 1 || page > doc.numPages) {
|
|
9764
|
+
return null;
|
|
9765
|
+
}
|
|
9766
|
+
try {
|
|
9767
|
+
return {
|
|
9768
|
+
width: 612,
|
|
9769
|
+
// Default US Letter width
|
|
9770
|
+
height: 792,
|
|
9771
|
+
// Default US Letter height
|
|
9772
|
+
rotation: viewerStore.getState().rotation
|
|
9773
|
+
};
|
|
9774
|
+
} catch {
|
|
9775
|
+
return null;
|
|
9776
|
+
}
|
|
9777
|
+
},
|
|
9778
|
+
percentToPixels: (xPercent, yPercent, page) => {
|
|
9779
|
+
const dimensions = handle.coordinates.getPageDimensions(page);
|
|
9780
|
+
if (!dimensions) return null;
|
|
9781
|
+
const scale2 = viewerStore.getState().scale;
|
|
9782
|
+
return {
|
|
9783
|
+
x: xPercent / 100 * dimensions.width * scale2,
|
|
9784
|
+
y: yPercent / 100 * dimensions.height * scale2
|
|
9785
|
+
};
|
|
9786
|
+
},
|
|
9787
|
+
pixelsToPercent: (x, y, page) => {
|
|
9788
|
+
const dimensions = handle.coordinates.getPageDimensions(page);
|
|
9789
|
+
if (!dimensions) return null;
|
|
9790
|
+
const scale2 = viewerStore.getState().scale;
|
|
9791
|
+
return {
|
|
9792
|
+
x: x / (dimensions.width * scale2) * 100,
|
|
9793
|
+
y: y / (dimensions.height * scale2) * 100
|
|
9794
|
+
};
|
|
9795
|
+
}
|
|
9796
|
+
},
|
|
9176
9797
|
// ==================== Document ====================
|
|
9177
9798
|
getDocument: () => {
|
|
9178
9799
|
return viewerStore.getState().document;
|
|
@@ -9259,10 +9880,36 @@ var init_PDFViewerClient = __esm({
|
|
|
9259
9880
|
if (prevScaleRef.current !== scale) {
|
|
9260
9881
|
prevScaleRef.current = scale;
|
|
9261
9882
|
onScaleChangeRef.current?.(scale);
|
|
9883
|
+
onZoomChangeRef.current?.(scale);
|
|
9262
9884
|
}
|
|
9263
9885
|
}, [scale]);
|
|
9886
|
+
useEffect22(() => {
|
|
9887
|
+
if (!isControlled || controlledPage === void 0) return;
|
|
9888
|
+
if (prevControlledPageRef.current === controlledPage) return;
|
|
9889
|
+
prevControlledPageRef.current = controlledPage;
|
|
9890
|
+
const { numPages, currentPage: currentPage2 } = viewerStore.getState();
|
|
9891
|
+
if (numPages > 0 && controlledPage !== currentPage2) {
|
|
9892
|
+
viewerStore.getState().requestScrollToPage(controlledPage, "smooth");
|
|
9893
|
+
}
|
|
9894
|
+
}, [controlledPage, isControlled, viewerStore]);
|
|
9264
9895
|
const themeClass = theme === "dark" ? "dark" : "";
|
|
9265
9896
|
if (error) {
|
|
9897
|
+
if (errorComponent) {
|
|
9898
|
+
const errorContent = typeof errorComponent === "function" ? errorComponent(error, handleRetry) : errorComponent;
|
|
9899
|
+
return /* @__PURE__ */ jsx26(
|
|
9900
|
+
"div",
|
|
9901
|
+
{
|
|
9902
|
+
className: cn(
|
|
9903
|
+
"pdf-viewer pdf-viewer-error",
|
|
9904
|
+
"flex flex-col h-full",
|
|
9905
|
+
"bg-white dark:bg-gray-900",
|
|
9906
|
+
themeClass,
|
|
9907
|
+
className
|
|
9908
|
+
),
|
|
9909
|
+
children: errorContent
|
|
9910
|
+
}
|
|
9911
|
+
);
|
|
9912
|
+
}
|
|
9266
9913
|
return /* @__PURE__ */ jsx26(
|
|
9267
9914
|
"div",
|
|
9268
9915
|
{
|
|
@@ -9318,7 +9965,7 @@ var init_PDFViewerClient = __esm({
|
|
|
9318
9965
|
renderContainer()
|
|
9319
9966
|
] }),
|
|
9320
9967
|
showFloatingZoom && /* @__PURE__ */ jsx26(FloatingZoomControls, { position: "bottom-right" }),
|
|
9321
|
-
isLoading && /* @__PURE__ */ jsx26("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-900/80", children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center", children: [
|
|
9968
|
+
isLoading && /* @__PURE__ */ jsx26("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-900/80", children: loadingComponent ?? /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center", children: [
|
|
9322
9969
|
/* @__PURE__ */ jsx26("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
|
|
9323
9970
|
/* @__PURE__ */ jsx26("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF..." })
|
|
9324
9971
|
] }) })
|
|
@@ -10692,10 +11339,247 @@ var Minimap = memo34(function Minimap2({
|
|
|
10692
11339
|
// src/components/index.ts
|
|
10693
11340
|
init_FloatingZoomControls2();
|
|
10694
11341
|
|
|
11342
|
+
// src/components/PDFThumbnailNav/PDFThumbnailNav.tsx
|
|
11343
|
+
init_hooks();
|
|
11344
|
+
init_utils();
|
|
11345
|
+
import { memo as memo35, useEffect as useEffect26, useState as useState28, useRef as useRef25, useCallback as useCallback41 } from "react";
|
|
11346
|
+
import { jsx as jsx36, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
11347
|
+
var DEFAULT_WIDTH = 612;
|
|
11348
|
+
var DEFAULT_HEIGHT = 792;
|
|
11349
|
+
var PDFThumbnailNav = memo35(function PDFThumbnailNav2({
|
|
11350
|
+
thumbnailScale = 0.15,
|
|
11351
|
+
orientation = "vertical",
|
|
11352
|
+
maxVisible = 10,
|
|
11353
|
+
className,
|
|
11354
|
+
onThumbnailClick,
|
|
11355
|
+
gap = 8,
|
|
11356
|
+
showPageNumbers = true
|
|
11357
|
+
}) {
|
|
11358
|
+
const { document: document2, numPages, currentPage } = usePDFViewer();
|
|
11359
|
+
const { viewerStore } = usePDFViewerStores();
|
|
11360
|
+
const containerRef = useRef25(null);
|
|
11361
|
+
const [thumbnails, setThumbnails] = useState28(/* @__PURE__ */ new Map());
|
|
11362
|
+
const [visibleRange, setVisibleRange] = useState28({ start: 1, end: maxVisible });
|
|
11363
|
+
const renderQueueRef = useRef25(/* @__PURE__ */ new Set());
|
|
11364
|
+
const pageCache = useRef25(/* @__PURE__ */ new Map());
|
|
11365
|
+
const thumbnailWidth = Math.floor(DEFAULT_WIDTH * thumbnailScale);
|
|
11366
|
+
const thumbnailHeight = Math.floor(DEFAULT_HEIGHT * thumbnailScale);
|
|
11367
|
+
const updateVisibleRange = useCallback41(() => {
|
|
11368
|
+
if (!containerRef.current || numPages === 0) return;
|
|
11369
|
+
const container = containerRef.current;
|
|
11370
|
+
const isHorizontal2 = orientation === "horizontal";
|
|
11371
|
+
const scrollPosition = isHorizontal2 ? container.scrollLeft : container.scrollTop;
|
|
11372
|
+
const viewportSize = isHorizontal2 ? container.clientWidth : container.clientHeight;
|
|
11373
|
+
const itemSize = (isHorizontal2 ? thumbnailWidth : thumbnailHeight) + gap;
|
|
11374
|
+
const firstVisible = Math.max(1, Math.floor(scrollPosition / itemSize) + 1);
|
|
11375
|
+
const visibleCount = Math.ceil(viewportSize / itemSize) + 2;
|
|
11376
|
+
const lastVisible = Math.min(numPages, firstVisible + visibleCount);
|
|
11377
|
+
setVisibleRange({ start: firstVisible, end: lastVisible });
|
|
11378
|
+
}, [numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
|
|
11379
|
+
useEffect26(() => {
|
|
11380
|
+
const container = containerRef.current;
|
|
11381
|
+
if (!container) return;
|
|
11382
|
+
const handleScroll = () => {
|
|
11383
|
+
requestAnimationFrame(updateVisibleRange);
|
|
11384
|
+
};
|
|
11385
|
+
container.addEventListener("scroll", handleScroll, { passive: true });
|
|
11386
|
+
updateVisibleRange();
|
|
11387
|
+
return () => container.removeEventListener("scroll", handleScroll);
|
|
11388
|
+
}, [updateVisibleRange]);
|
|
11389
|
+
useEffect26(() => {
|
|
11390
|
+
if (!document2) {
|
|
11391
|
+
setThumbnails(/* @__PURE__ */ new Map());
|
|
11392
|
+
pageCache.current.clear();
|
|
11393
|
+
return;
|
|
11394
|
+
}
|
|
11395
|
+
const renderThumbnails = async () => {
|
|
11396
|
+
const newThumbnails = new Map(thumbnails);
|
|
11397
|
+
const pagesToRender = [];
|
|
11398
|
+
for (let i = visibleRange.start; i <= visibleRange.end; i++) {
|
|
11399
|
+
if (!newThumbnails.has(i) && !renderQueueRef.current.has(i)) {
|
|
11400
|
+
pagesToRender.push(i);
|
|
11401
|
+
renderQueueRef.current.add(i);
|
|
11402
|
+
}
|
|
11403
|
+
}
|
|
11404
|
+
for (const pageNum of pagesToRender) {
|
|
11405
|
+
try {
|
|
11406
|
+
let page = pageCache.current.get(pageNum);
|
|
11407
|
+
if (!page) {
|
|
11408
|
+
page = await document2.getPage(pageNum);
|
|
11409
|
+
pageCache.current.set(pageNum, page);
|
|
11410
|
+
}
|
|
11411
|
+
const viewport = page.getViewport({ scale: thumbnailScale });
|
|
11412
|
+
const canvas = window.document.createElement("canvas");
|
|
11413
|
+
canvas.width = Math.floor(viewport.width);
|
|
11414
|
+
canvas.height = Math.floor(viewport.height);
|
|
11415
|
+
const ctx = canvas.getContext("2d");
|
|
11416
|
+
if (ctx) {
|
|
11417
|
+
await page.render({
|
|
11418
|
+
canvasContext: ctx,
|
|
11419
|
+
viewport
|
|
11420
|
+
}).promise;
|
|
11421
|
+
newThumbnails.set(pageNum, {
|
|
11422
|
+
pageNumber: pageNum,
|
|
11423
|
+
canvas,
|
|
11424
|
+
width: canvas.width,
|
|
11425
|
+
height: canvas.height
|
|
11426
|
+
});
|
|
11427
|
+
}
|
|
11428
|
+
} catch (error) {
|
|
11429
|
+
console.error(`Failed to render thumbnail for page ${pageNum}:`, error);
|
|
11430
|
+
} finally {
|
|
11431
|
+
renderQueueRef.current.delete(pageNum);
|
|
11432
|
+
}
|
|
11433
|
+
}
|
|
11434
|
+
if (pagesToRender.length > 0) {
|
|
11435
|
+
setThumbnails(newThumbnails);
|
|
11436
|
+
}
|
|
11437
|
+
};
|
|
11438
|
+
renderThumbnails();
|
|
11439
|
+
}, [document2, visibleRange, thumbnailScale, thumbnails]);
|
|
11440
|
+
useEffect26(() => {
|
|
11441
|
+
if (!containerRef.current || numPages === 0) return;
|
|
11442
|
+
const container = containerRef.current;
|
|
11443
|
+
const isHorizontal2 = orientation === "horizontal";
|
|
11444
|
+
const itemSize = (isHorizontal2 ? thumbnailWidth : thumbnailHeight) + gap;
|
|
11445
|
+
const targetPosition = (currentPage - 1) * itemSize;
|
|
11446
|
+
const scrollPosition = isHorizontal2 ? container.scrollLeft : container.scrollTop;
|
|
11447
|
+
const viewportSize = isHorizontal2 ? container.clientWidth : container.clientHeight;
|
|
11448
|
+
if (targetPosition < scrollPosition || targetPosition + itemSize > scrollPosition + viewportSize) {
|
|
11449
|
+
const targetScroll = targetPosition - (viewportSize - itemSize) / 2;
|
|
11450
|
+
container.scrollTo({
|
|
11451
|
+
[isHorizontal2 ? "left" : "top"]: Math.max(0, targetScroll),
|
|
11452
|
+
behavior: "smooth"
|
|
11453
|
+
});
|
|
11454
|
+
}
|
|
11455
|
+
}, [currentPage, numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
|
|
11456
|
+
const handleThumbnailClick = useCallback41((pageNum) => {
|
|
11457
|
+
onThumbnailClick?.(pageNum);
|
|
11458
|
+
viewerStore.getState().requestScrollToPage(pageNum, "smooth");
|
|
11459
|
+
}, [onThumbnailClick, viewerStore]);
|
|
11460
|
+
if (!document2 || numPages === 0) {
|
|
11461
|
+
return /* @__PURE__ */ jsx36(
|
|
11462
|
+
"div",
|
|
11463
|
+
{
|
|
11464
|
+
className: cn(
|
|
11465
|
+
"pdf-thumbnail-nav",
|
|
11466
|
+
"flex items-center justify-center",
|
|
11467
|
+
"bg-gray-100 dark:bg-gray-800",
|
|
11468
|
+
"text-gray-500 dark:text-gray-400",
|
|
11469
|
+
"text-sm",
|
|
11470
|
+
className
|
|
11471
|
+
),
|
|
11472
|
+
style: {
|
|
11473
|
+
width: orientation === "vertical" ? thumbnailWidth + 24 : "100%",
|
|
11474
|
+
height: orientation === "horizontal" ? thumbnailHeight + 40 : "100%"
|
|
11475
|
+
},
|
|
11476
|
+
children: "No document"
|
|
11477
|
+
}
|
|
11478
|
+
);
|
|
11479
|
+
}
|
|
11480
|
+
const isHorizontal = orientation === "horizontal";
|
|
11481
|
+
const totalSize = numPages * ((isHorizontal ? thumbnailWidth : thumbnailHeight) + gap) - gap;
|
|
11482
|
+
return /* @__PURE__ */ jsx36(
|
|
11483
|
+
"div",
|
|
11484
|
+
{
|
|
11485
|
+
ref: containerRef,
|
|
11486
|
+
className: cn(
|
|
11487
|
+
"pdf-thumbnail-nav",
|
|
11488
|
+
"overflow-auto",
|
|
11489
|
+
"bg-gray-100 dark:bg-gray-800",
|
|
11490
|
+
isHorizontal ? "flex-row" : "flex-col",
|
|
11491
|
+
className
|
|
11492
|
+
),
|
|
11493
|
+
style: {
|
|
11494
|
+
...isHorizontal ? { overflowX: "auto", overflowY: "hidden" } : { overflowX: "hidden", overflowY: "auto" }
|
|
11495
|
+
},
|
|
11496
|
+
children: /* @__PURE__ */ jsx36(
|
|
11497
|
+
"div",
|
|
11498
|
+
{
|
|
11499
|
+
className: cn(
|
|
11500
|
+
"relative",
|
|
11501
|
+
isHorizontal ? "flex flex-row items-center" : "flex flex-col items-center"
|
|
11502
|
+
),
|
|
11503
|
+
style: {
|
|
11504
|
+
[isHorizontal ? "width" : "height"]: totalSize,
|
|
11505
|
+
[isHorizontal ? "minWidth" : "minHeight"]: totalSize,
|
|
11506
|
+
padding: gap / 2,
|
|
11507
|
+
gap
|
|
11508
|
+
},
|
|
11509
|
+
children: Array.from({ length: numPages }, (_, i) => i + 1).map((pageNum) => {
|
|
11510
|
+
const thumbnail = thumbnails.get(pageNum);
|
|
11511
|
+
const isActive = pageNum === currentPage;
|
|
11512
|
+
const isVisible = pageNum >= visibleRange.start && pageNum <= visibleRange.end;
|
|
11513
|
+
return /* @__PURE__ */ jsxs30(
|
|
11514
|
+
"div",
|
|
11515
|
+
{
|
|
11516
|
+
className: cn(
|
|
11517
|
+
"pdf-thumbnail",
|
|
11518
|
+
"flex-shrink-0 cursor-pointer transition-all duration-200",
|
|
11519
|
+
"border-2 rounded shadow-sm hover:shadow-md",
|
|
11520
|
+
isActive ? "border-blue-500 ring-2 ring-blue-200 dark:ring-blue-800" : "border-gray-300 dark:border-gray-600 hover:border-blue-400"
|
|
11521
|
+
),
|
|
11522
|
+
style: {
|
|
11523
|
+
width: thumbnailWidth,
|
|
11524
|
+
height: thumbnailHeight + (showPageNumbers ? 24 : 0)
|
|
11525
|
+
},
|
|
11526
|
+
onClick: () => handleThumbnailClick(pageNum),
|
|
11527
|
+
role: "button",
|
|
11528
|
+
tabIndex: 0,
|
|
11529
|
+
"aria-label": `Go to page ${pageNum}`,
|
|
11530
|
+
"aria-current": isActive ? "page" : void 0,
|
|
11531
|
+
onKeyDown: (e) => {
|
|
11532
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
11533
|
+
e.preventDefault();
|
|
11534
|
+
handleThumbnailClick(pageNum);
|
|
11535
|
+
}
|
|
11536
|
+
},
|
|
11537
|
+
children: [
|
|
11538
|
+
/* @__PURE__ */ jsx36(
|
|
11539
|
+
"div",
|
|
11540
|
+
{
|
|
11541
|
+
className: "relative bg-white dark:bg-gray-700",
|
|
11542
|
+
style: {
|
|
11543
|
+
width: thumbnailWidth,
|
|
11544
|
+
height: thumbnailHeight
|
|
11545
|
+
},
|
|
11546
|
+
children: isVisible && thumbnail?.canvas ? /* @__PURE__ */ jsx36(
|
|
11547
|
+
"img",
|
|
11548
|
+
{
|
|
11549
|
+
src: thumbnail.canvas.toDataURL(),
|
|
11550
|
+
alt: `Page ${pageNum}`,
|
|
11551
|
+
className: "w-full h-full object-contain",
|
|
11552
|
+
loading: "lazy"
|
|
11553
|
+
}
|
|
11554
|
+
) : /* @__PURE__ */ jsx36("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-xs", children: pageNum })
|
|
11555
|
+
}
|
|
11556
|
+
),
|
|
11557
|
+
showPageNumbers && /* @__PURE__ */ jsx36(
|
|
11558
|
+
"div",
|
|
11559
|
+
{
|
|
11560
|
+
className: cn(
|
|
11561
|
+
"text-center text-xs py-1",
|
|
11562
|
+
"bg-gray-50 dark:bg-gray-700",
|
|
11563
|
+
isActive ? "text-blue-600 dark:text-blue-400 font-medium" : "text-gray-600 dark:text-gray-400"
|
|
11564
|
+
),
|
|
11565
|
+
children: pageNum
|
|
11566
|
+
}
|
|
11567
|
+
)
|
|
11568
|
+
]
|
|
11569
|
+
},
|
|
11570
|
+
pageNum
|
|
11571
|
+
);
|
|
11572
|
+
})
|
|
11573
|
+
}
|
|
11574
|
+
)
|
|
11575
|
+
}
|
|
11576
|
+
);
|
|
11577
|
+
});
|
|
11578
|
+
|
|
10695
11579
|
// src/components/ErrorBoundary/PDFErrorBoundary.tsx
|
|
10696
11580
|
init_utils();
|
|
10697
11581
|
import { Component } from "react";
|
|
10698
|
-
import { jsx as
|
|
11582
|
+
import { jsx as jsx37, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
10699
11583
|
var PDFErrorBoundary = class extends Component {
|
|
10700
11584
|
constructor(props) {
|
|
10701
11585
|
super(props);
|
|
@@ -10723,7 +11607,7 @@ var PDFErrorBoundary = class extends Component {
|
|
|
10723
11607
|
return fallback;
|
|
10724
11608
|
}
|
|
10725
11609
|
if (showDefaultUI) {
|
|
10726
|
-
return /* @__PURE__ */
|
|
11610
|
+
return /* @__PURE__ */ jsx37(
|
|
10727
11611
|
DefaultErrorUI,
|
|
10728
11612
|
{
|
|
10729
11613
|
error,
|
|
@@ -10742,7 +11626,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10742
11626
|
const isNetworkError = error.message.includes("fetch") || error.message.includes("network") || error.message.includes("Failed to load");
|
|
10743
11627
|
let title = "Something went wrong";
|
|
10744
11628
|
let description = error.message;
|
|
10745
|
-
let icon = /* @__PURE__ */
|
|
11629
|
+
let icon = /* @__PURE__ */ jsx37("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx37(
|
|
10746
11630
|
"path",
|
|
10747
11631
|
{
|
|
10748
11632
|
strokeLinecap: "round",
|
|
@@ -10754,7 +11638,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10754
11638
|
if (isPDFError) {
|
|
10755
11639
|
title = "Unable to load PDF";
|
|
10756
11640
|
description = "The PDF file could not be loaded. It may be corrupted or in an unsupported format.";
|
|
10757
|
-
icon = /* @__PURE__ */
|
|
11641
|
+
icon = /* @__PURE__ */ jsx37("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx37(
|
|
10758
11642
|
"path",
|
|
10759
11643
|
{
|
|
10760
11644
|
strokeLinecap: "round",
|
|
@@ -10766,7 +11650,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10766
11650
|
} else if (isNetworkError) {
|
|
10767
11651
|
title = "Network error";
|
|
10768
11652
|
description = "Unable to fetch the PDF file. Please check your internet connection and try again.";
|
|
10769
|
-
icon = /* @__PURE__ */
|
|
11653
|
+
icon = /* @__PURE__ */ jsx37("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx37(
|
|
10770
11654
|
"path",
|
|
10771
11655
|
{
|
|
10772
11656
|
strokeLinecap: "round",
|
|
@@ -10776,7 +11660,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10776
11660
|
}
|
|
10777
11661
|
) });
|
|
10778
11662
|
}
|
|
10779
|
-
return /* @__PURE__ */
|
|
11663
|
+
return /* @__PURE__ */ jsxs31(
|
|
10780
11664
|
"div",
|
|
10781
11665
|
{
|
|
10782
11666
|
className: cn(
|
|
@@ -10789,14 +11673,14 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10789
11673
|
),
|
|
10790
11674
|
children: [
|
|
10791
11675
|
icon,
|
|
10792
|
-
/* @__PURE__ */
|
|
10793
|
-
/* @__PURE__ */
|
|
10794
|
-
/* @__PURE__ */
|
|
10795
|
-
/* @__PURE__ */
|
|
10796
|
-
/* @__PURE__ */
|
|
11676
|
+
/* @__PURE__ */ jsx37("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
|
|
11677
|
+
/* @__PURE__ */ jsx37("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
|
|
11678
|
+
/* @__PURE__ */ jsxs31("details", { className: "mt-4 text-left max-w-md w-full", children: [
|
|
11679
|
+
/* @__PURE__ */ jsx37("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" }),
|
|
11680
|
+
/* @__PURE__ */ jsx37("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
|
|
10797
11681
|
] }),
|
|
10798
|
-
/* @__PURE__ */
|
|
10799
|
-
/* @__PURE__ */
|
|
11682
|
+
/* @__PURE__ */ jsxs31("div", { className: "mt-6 flex gap-3", children: [
|
|
11683
|
+
/* @__PURE__ */ jsx37(
|
|
10800
11684
|
"button",
|
|
10801
11685
|
{
|
|
10802
11686
|
onClick: onReset,
|
|
@@ -10810,7 +11694,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10810
11694
|
children: "Try again"
|
|
10811
11695
|
}
|
|
10812
11696
|
),
|
|
10813
|
-
/* @__PURE__ */
|
|
11697
|
+
/* @__PURE__ */ jsx37(
|
|
10814
11698
|
"button",
|
|
10815
11699
|
{
|
|
10816
11700
|
onClick: () => window.location.reload(),
|
|
@@ -10830,7 +11714,7 @@ function DefaultErrorUI({ error, onReset, className }) {
|
|
|
10830
11714
|
);
|
|
10831
11715
|
}
|
|
10832
11716
|
function withErrorBoundary({ component, ...props }) {
|
|
10833
|
-
return /* @__PURE__ */
|
|
11717
|
+
return /* @__PURE__ */ jsx37(PDFErrorBoundary, { ...props, children: component });
|
|
10834
11718
|
}
|
|
10835
11719
|
|
|
10836
11720
|
// src/index.ts
|
|
@@ -10864,6 +11748,7 @@ export {
|
|
|
10864
11748
|
OutlinePanel,
|
|
10865
11749
|
PDFErrorBoundary,
|
|
10866
11750
|
PDFPage,
|
|
11751
|
+
PDFThumbnailNav,
|
|
10867
11752
|
PDFViewer,
|
|
10868
11753
|
PDFViewerClient,
|
|
10869
11754
|
PDFViewerContext,
|
|
@@ -10882,9 +11767,11 @@ export {
|
|
|
10882
11767
|
ThumbnailPanel,
|
|
10883
11768
|
Toolbar,
|
|
10884
11769
|
VirtualizedDocumentContainer,
|
|
11770
|
+
applyRotation,
|
|
10885
11771
|
clearHighlights,
|
|
10886
11772
|
clearStudentData,
|
|
10887
11773
|
cn,
|
|
11774
|
+
countTextOnPage,
|
|
10888
11775
|
createAgentAPI,
|
|
10889
11776
|
createAgentStore,
|
|
10890
11777
|
createAnnotationStore,
|
|
@@ -10893,6 +11780,7 @@ export {
|
|
|
10893
11780
|
createSearchStore,
|
|
10894
11781
|
createStudentStore,
|
|
10895
11782
|
createViewerStore,
|
|
11783
|
+
doRectsIntersect,
|
|
10896
11784
|
downloadAnnotationsAsJSON,
|
|
10897
11785
|
downloadAnnotationsAsMarkdown,
|
|
10898
11786
|
downloadFile,
|
|
@@ -10900,25 +11788,39 @@ export {
|
|
|
10900
11788
|
exportAnnotationsAsMarkdown,
|
|
10901
11789
|
exportHighlightsAsJSON,
|
|
10902
11790
|
exportHighlightsAsMarkdown,
|
|
11791
|
+
extractPageText,
|
|
11792
|
+
findTextInDocument,
|
|
11793
|
+
findTextOnPage,
|
|
10903
11794
|
generateDocumentId,
|
|
10904
11795
|
getAllDocumentIds,
|
|
10905
11796
|
getAllStudentDataDocumentIds,
|
|
10906
11797
|
getMetadata,
|
|
10907
11798
|
getOutline,
|
|
10908
11799
|
getPage,
|
|
11800
|
+
getPageText,
|
|
10909
11801
|
getPageTextContent,
|
|
10910
11802
|
getPluginManager,
|
|
11803
|
+
getRectIntersection,
|
|
11804
|
+
getRotatedDimensions,
|
|
10911
11805
|
getStorageStats,
|
|
10912
11806
|
importHighlightsFromJSON,
|
|
10913
11807
|
initializePDFJS,
|
|
10914
11808
|
isPDFJSInitialized,
|
|
11809
|
+
isPointInRect,
|
|
10915
11810
|
loadDocument,
|
|
10916
11811
|
loadHighlights,
|
|
10917
11812
|
loadStudentData,
|
|
11813
|
+
mergeAdjacentRects,
|
|
11814
|
+
pdfToPercent,
|
|
11815
|
+
pdfToViewport,
|
|
10918
11816
|
pdfjsLib,
|
|
11817
|
+
percentToPDF,
|
|
11818
|
+
percentToViewport,
|
|
10919
11819
|
quickViewer,
|
|
11820
|
+
removeRotation,
|
|
10920
11821
|
saveHighlights,
|
|
10921
11822
|
saveStudentData,
|
|
11823
|
+
scaleRect,
|
|
10922
11824
|
useAgentContext,
|
|
10923
11825
|
useAgentStore,
|
|
10924
11826
|
useAnnotationStore,
|
|
@@ -10940,6 +11842,8 @@ export {
|
|
|
10940
11842
|
useTouchGestures,
|
|
10941
11843
|
useViewerStore,
|
|
10942
11844
|
useZoom,
|
|
11845
|
+
viewportToPDF,
|
|
11846
|
+
viewportToPercent,
|
|
10943
11847
|
withErrorBoundary
|
|
10944
11848
|
};
|
|
10945
11849
|
//# sourceMappingURL=index.js.map
|