pdfjs-reader-core 0.1.4 → 0.2.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/dist/index.cjs CHANGED
@@ -332,6 +332,9 @@ var init_highlight_storage = __esm({
332
332
  });
333
333
 
334
334
  // src/store/viewer-store.ts
335
+ function generateRequestId() {
336
+ return `scroll-${Date.now()}-${++requestCounter}`;
337
+ }
335
338
  function createViewerStore(initialOverrides = {}) {
336
339
  return (0, import_vanilla.createStore)()((set, get) => ({
337
340
  ...initialState,
@@ -384,6 +387,46 @@ function createViewerStore(initialOverrides = {}) {
384
387
  set({ currentPage: currentPage - 1 });
385
388
  }
386
389
  },
390
+ // Scroll coordination actions
391
+ requestScrollToPage: (page, behavior = "smooth") => {
392
+ const { numPages } = get();
393
+ const validPage = Math.max(1, Math.min(page, numPages));
394
+ return new Promise((resolve) => {
395
+ const requestId = generateRequestId();
396
+ const timeoutId = setTimeout(() => {
397
+ const callback = scrollCallbacks.get(requestId);
398
+ if (callback) {
399
+ scrollCallbacks.delete(requestId);
400
+ callback.resolve();
401
+ }
402
+ const currentRequest = get().scrollToPageRequest;
403
+ if (currentRequest?.requestId === requestId) {
404
+ set({ scrollToPageRequest: null });
405
+ }
406
+ }, SCROLL_TIMEOUT_MS);
407
+ scrollCallbacks.set(requestId, { resolve, timeoutId });
408
+ set({
409
+ currentPage: validPage,
410
+ scrollToPageRequest: {
411
+ page: validPage,
412
+ requestId,
413
+ behavior
414
+ }
415
+ });
416
+ });
417
+ },
418
+ completeScrollRequest: (requestId) => {
419
+ const callback = scrollCallbacks.get(requestId);
420
+ if (callback) {
421
+ clearTimeout(callback.timeoutId);
422
+ scrollCallbacks.delete(requestId);
423
+ callback.resolve();
424
+ }
425
+ const currentRequest = get().scrollToPageRequest;
426
+ if (currentRequest?.requestId === requestId) {
427
+ set({ scrollToPageRequest: null });
428
+ }
429
+ },
387
430
  // Zoom actions
388
431
  setScale: (scale) => {
389
432
  const clampedScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
@@ -450,7 +493,7 @@ function createViewerStore(initialOverrides = {}) {
450
493
  }
451
494
  }));
452
495
  }
453
- var import_vanilla, ZOOM_LEVELS, MIN_SCALE, MAX_SCALE, initialState;
496
+ var import_vanilla, ZOOM_LEVELS, MIN_SCALE, MAX_SCALE, SCROLL_TIMEOUT_MS, scrollCallbacks, requestCounter, initialState;
454
497
  var init_viewer_store = __esm({
455
498
  "src/store/viewer-store.ts"() {
456
499
  "use strict";
@@ -458,6 +501,9 @@ var init_viewer_store = __esm({
458
501
  ZOOM_LEVELS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
459
502
  MIN_SCALE = 0.1;
460
503
  MAX_SCALE = 10;
504
+ SCROLL_TIMEOUT_MS = 3e3;
505
+ scrollCallbacks = /* @__PURE__ */ new Map();
506
+ requestCounter = 0;
461
507
  initialState = {
462
508
  // Document state
463
509
  document: null,
@@ -468,6 +514,8 @@ var init_viewer_store = __esm({
468
514
  currentPage: 1,
469
515
  scale: 1,
470
516
  rotation: 0,
517
+ // Scroll coordination
518
+ scrollToPageRequest: null,
471
519
  // UI state
472
520
  viewMode: "single",
473
521
  scrollMode: "single",
@@ -1277,6 +1325,252 @@ var init_agent_api = __esm({
1277
1325
  }
1278
1326
  });
1279
1327
 
1328
+ // src/utils/text-search.ts
1329
+ async function extractPageText(document2, pageNumber) {
1330
+ const page = await document2.getPage(pageNumber);
1331
+ const textContent = await page.getTextContent();
1332
+ const viewport = page.getViewport({ scale: 1 });
1333
+ let fullText = "";
1334
+ const charPositions = [];
1335
+ for (const item of textContent.items) {
1336
+ if ("str" in item && item.str) {
1337
+ const tx = item.transform;
1338
+ const x = tx[4];
1339
+ const y = viewport.height - tx[5];
1340
+ const width = item.width ?? 0;
1341
+ const height = item.height ?? 12;
1342
+ const charWidth = item.str.length > 0 ? width / item.str.length : width;
1343
+ for (let i = 0; i < item.str.length; i++) {
1344
+ charPositions.push({
1345
+ char: item.str[i],
1346
+ rect: {
1347
+ x: x + i * charWidth,
1348
+ y: y - height,
1349
+ width: charWidth,
1350
+ height
1351
+ }
1352
+ });
1353
+ }
1354
+ fullText += item.str;
1355
+ }
1356
+ }
1357
+ return { fullText, charPositions };
1358
+ }
1359
+ async function findTextOnPage(document2, pageNumber, query, options = {}) {
1360
+ const { caseSensitive = false, wholeWord = false } = options;
1361
+ if (!query || pageNumber < 1 || pageNumber > document2.numPages) {
1362
+ return [];
1363
+ }
1364
+ const { fullText, charPositions } = await extractPageText(document2, pageNumber);
1365
+ const matches = [];
1366
+ const searchText = caseSensitive ? query : query.toLowerCase();
1367
+ const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
1368
+ let startIndex = 0;
1369
+ while (true) {
1370
+ const matchIndex = textToSearch.indexOf(searchText, startIndex);
1371
+ if (matchIndex === -1) break;
1372
+ if (wholeWord) {
1373
+ const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
1374
+ const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
1375
+ if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
1376
+ startIndex = matchIndex + 1;
1377
+ continue;
1378
+ }
1379
+ }
1380
+ const matchRects = [];
1381
+ for (let i = matchIndex; i < matchIndex + query.length && i < charPositions.length; i++) {
1382
+ matchRects.push(charPositions[i].rect);
1383
+ }
1384
+ const mergedRects = mergeAdjacentRects(matchRects);
1385
+ matches.push({
1386
+ text: fullText.substring(matchIndex, matchIndex + query.length),
1387
+ rects: mergedRects,
1388
+ pageNumber,
1389
+ startIndex: matchIndex
1390
+ });
1391
+ startIndex = matchIndex + 1;
1392
+ }
1393
+ return matches;
1394
+ }
1395
+ async function findTextInDocument(document2, query, options = {}) {
1396
+ const { pageRange, ...findOptions } = options;
1397
+ const pagesToSearch = pageRange ?? Array.from({ length: document2.numPages }, (_, i) => i + 1);
1398
+ const allMatches = [];
1399
+ for (const pageNum of pagesToSearch) {
1400
+ if (pageNum < 1 || pageNum > document2.numPages) continue;
1401
+ try {
1402
+ const matches = await findTextOnPage(document2, pageNum, query, findOptions);
1403
+ allMatches.push(...matches);
1404
+ } catch {
1405
+ }
1406
+ }
1407
+ return allMatches;
1408
+ }
1409
+ function mergeAdjacentRects(rects) {
1410
+ if (rects.length === 0) return [];
1411
+ const sorted = [...rects].sort((a, b) => a.y - b.y || a.x - b.x);
1412
+ const merged = [];
1413
+ let current = { ...sorted[0] };
1414
+ for (let i = 1; i < sorted.length; i++) {
1415
+ const rect = sorted[i];
1416
+ if (Math.abs(rect.y - current.y) < 2 && rect.x <= current.x + current.width + 2) {
1417
+ const newRight = Math.max(current.x + current.width, rect.x + rect.width);
1418
+ current.width = newRight - current.x;
1419
+ current.height = Math.max(current.height, rect.height);
1420
+ } else {
1421
+ merged.push(current);
1422
+ current = { ...rect };
1423
+ }
1424
+ }
1425
+ merged.push(current);
1426
+ return merged;
1427
+ }
1428
+ async function getPageText(document2, pageNumber) {
1429
+ if (pageNumber < 1 || pageNumber > document2.numPages) {
1430
+ return "";
1431
+ }
1432
+ const page = await document2.getPage(pageNumber);
1433
+ const textContent = await page.getTextContent();
1434
+ return textContent.items.filter((item) => "str" in item).map((item) => item.str).join("");
1435
+ }
1436
+ async function countTextOnPage(document2, pageNumber, query, options = {}) {
1437
+ const { caseSensitive = false, wholeWord = false } = options;
1438
+ if (!query || pageNumber < 1 || pageNumber > document2.numPages) {
1439
+ return 0;
1440
+ }
1441
+ const text = await getPageText(document2, pageNumber);
1442
+ const searchText = caseSensitive ? query : query.toLowerCase();
1443
+ const textToSearch = caseSensitive ? text : text.toLowerCase();
1444
+ let count = 0;
1445
+ let startIndex = 0;
1446
+ while (true) {
1447
+ const matchIndex = textToSearch.indexOf(searchText, startIndex);
1448
+ if (matchIndex === -1) break;
1449
+ if (wholeWord) {
1450
+ const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
1451
+ const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
1452
+ if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
1453
+ startIndex = matchIndex + 1;
1454
+ continue;
1455
+ }
1456
+ }
1457
+ count++;
1458
+ startIndex = matchIndex + 1;
1459
+ }
1460
+ return count;
1461
+ }
1462
+ var init_text_search = __esm({
1463
+ "src/utils/text-search.ts"() {
1464
+ "use strict";
1465
+ }
1466
+ });
1467
+
1468
+ // src/utils/coordinates.ts
1469
+ function pdfToViewport(x, y, scale, pageHeight) {
1470
+ return {
1471
+ x: x * scale,
1472
+ y: (pageHeight - y) * scale
1473
+ };
1474
+ }
1475
+ function viewportToPDF(x, y, scale, pageHeight) {
1476
+ return {
1477
+ x: x / scale,
1478
+ y: pageHeight - y / scale
1479
+ };
1480
+ }
1481
+ function percentToPDF(xPercent, yPercent, pageWidth, pageHeight) {
1482
+ return {
1483
+ x: xPercent / 100 * pageWidth,
1484
+ y: yPercent / 100 * pageHeight
1485
+ };
1486
+ }
1487
+ function pdfToPercent(x, y, pageWidth, pageHeight) {
1488
+ return {
1489
+ x: x / pageWidth * 100,
1490
+ y: y / pageHeight * 100
1491
+ };
1492
+ }
1493
+ function percentToViewport(xPercent, yPercent, pageWidth, pageHeight, scale) {
1494
+ return {
1495
+ x: xPercent / 100 * pageWidth * scale,
1496
+ y: yPercent / 100 * pageHeight * scale
1497
+ };
1498
+ }
1499
+ function viewportToPercent(x, y, pageWidth, pageHeight, scale) {
1500
+ return {
1501
+ x: x / (pageWidth * scale) * 100,
1502
+ y: y / (pageHeight * scale) * 100
1503
+ };
1504
+ }
1505
+ function applyRotation(x, y, rotation, pageWidth, pageHeight) {
1506
+ const normalizedRotation = (rotation % 360 + 360) % 360;
1507
+ switch (normalizedRotation) {
1508
+ case 90:
1509
+ return { x: y, y: pageWidth - x };
1510
+ case 180:
1511
+ return { x: pageWidth - x, y: pageHeight - y };
1512
+ case 270:
1513
+ return { x: pageHeight - y, y: x };
1514
+ default:
1515
+ return { x, y };
1516
+ }
1517
+ }
1518
+ function removeRotation(x, y, rotation, pageWidth, pageHeight) {
1519
+ const normalizedRotation = (rotation % 360 + 360) % 360;
1520
+ switch (normalizedRotation) {
1521
+ case 90:
1522
+ return { x: pageWidth - y, y: x };
1523
+ case 180:
1524
+ return { x: pageWidth - x, y: pageHeight - y };
1525
+ case 270:
1526
+ return { x: y, y: pageHeight - x };
1527
+ default:
1528
+ return { x, y };
1529
+ }
1530
+ }
1531
+ function getRotatedDimensions(width, height, rotation) {
1532
+ const normalizedRotation = (rotation % 360 + 360) % 360;
1533
+ if (normalizedRotation === 90 || normalizedRotation === 270) {
1534
+ return { width: height, height: width };
1535
+ }
1536
+ return { width, height };
1537
+ }
1538
+ function scaleRect(rect, fromScale, toScale) {
1539
+ const ratio = toScale / fromScale;
1540
+ return {
1541
+ x: rect.x * ratio,
1542
+ y: rect.y * ratio,
1543
+ width: rect.width * ratio,
1544
+ height: rect.height * ratio
1545
+ };
1546
+ }
1547
+ function isPointInRect(point, rect) {
1548
+ return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
1549
+ }
1550
+ function doRectsIntersect(rectA, rectB) {
1551
+ 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);
1552
+ }
1553
+ function getRectIntersection(rectA, rectB) {
1554
+ const x = Math.max(rectA.x, rectB.x);
1555
+ const y = Math.max(rectA.y, rectB.y);
1556
+ const right = Math.min(rectA.x + rectA.width, rectB.x + rectB.width);
1557
+ const bottom = Math.min(rectA.y + rectA.height, rectB.y + rectB.height);
1558
+ if (right <= x || bottom <= y) {
1559
+ return null;
1560
+ }
1561
+ return {
1562
+ x,
1563
+ y,
1564
+ width: right - x,
1565
+ height: bottom - y
1566
+ };
1567
+ }
1568
+ var init_coordinates = __esm({
1569
+ "src/utils/coordinates.ts"() {
1570
+ "use strict";
1571
+ }
1572
+ });
1573
+
1280
1574
  // src/utils/index.ts
1281
1575
  var init_utils = __esm({
1282
1576
  "src/utils/index.ts"() {
@@ -1289,6 +1583,8 @@ var init_utils = __esm({
1289
1583
  init_export_annotations();
1290
1584
  init_student_storage();
1291
1585
  init_agent_api();
1586
+ init_text_search();
1587
+ init_coordinates();
1292
1588
  }
1293
1589
  });
1294
1590
 
@@ -1384,6 +1680,12 @@ function createSearchStore(initialOverrides = {}) {
1384
1680
  set({ currentResultIndex: index });
1385
1681
  }
1386
1682
  },
1683
+ setCaseSensitive: (value) => {
1684
+ set({ caseSensitive: value });
1685
+ },
1686
+ setWholeWord: (value) => {
1687
+ set({ wholeWord: value });
1688
+ },
1387
1689
  toggleCaseSensitive: () => {
1388
1690
  set((state) => ({ caseSensitive: !state.caseSensitive }));
1389
1691
  },
@@ -7920,6 +8222,8 @@ var init_DocumentContainer = __esm({
7920
8222
  nextPage,
7921
8223
  previousPage
7922
8224
  } = usePDFViewer();
8225
+ const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
8226
+ const { viewerStore } = usePDFViewerStores();
7923
8227
  const [currentPageObj, setCurrentPageObj] = (0, import_react35.useState)(null);
7924
8228
  const [isLoadingPage, setIsLoadingPage] = (0, import_react35.useState)(false);
7925
8229
  const containerRef = (0, import_react35.useRef)(null);
@@ -7985,6 +8289,11 @@ var init_DocumentContainer = __esm({
7985
8289
  const page = await document2.getPage(currentPage);
7986
8290
  if (!cancelled && document2 === documentRef.current) {
7987
8291
  setCurrentPageObj(page);
8292
+ if (scrollToPageRequest && scrollToPageRequest.page === currentPage) {
8293
+ requestAnimationFrame(() => {
8294
+ viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
8295
+ });
8296
+ }
7988
8297
  }
7989
8298
  } catch (error) {
7990
8299
  if (!cancelled) {
@@ -8003,7 +8312,7 @@ var init_DocumentContainer = __esm({
8003
8312
  return () => {
8004
8313
  cancelled = true;
8005
8314
  };
8006
- }, [document2, currentPage]);
8315
+ }, [document2, currentPage, scrollToPageRequest, viewerStore]);
8007
8316
  const getPageElement = (0, import_react35.useCallback)(() => {
8008
8317
  return containerRef.current?.querySelector(`[data-page-number="${currentPage}"]`);
8009
8318
  }, [currentPage]);
@@ -8145,6 +8454,8 @@ var init_VirtualizedDocumentContainer = __esm({
8145
8454
  nextPage,
8146
8455
  previousPage
8147
8456
  } = usePDFViewer();
8457
+ const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
8458
+ const { viewerStore } = usePDFViewerStores();
8148
8459
  const containerRef = (0, import_react36.useRef)(null);
8149
8460
  const scrollContainerRef = (0, import_react36.useRef)(null);
8150
8461
  const documentRef = (0, import_react36.useRef)(null);
@@ -8282,6 +8593,45 @@ var init_VirtualizedDocumentContainer = __esm({
8282
8593
  loadPages();
8283
8594
  }, [document2, visiblePages, pageObjects]);
8284
8595
  (0, import_react36.useEffect)(() => {
8596
+ if (!scrollToPageRequest || !scrollContainerRef.current || pageInfos.length === 0) return;
8597
+ const { page, requestId, behavior } = scrollToPageRequest;
8598
+ const pageInfo = pageInfos.find((p) => p.pageNumber === page);
8599
+ if (!pageInfo) {
8600
+ viewerStore.getState().completeScrollRequest(requestId);
8601
+ return;
8602
+ }
8603
+ const container = scrollContainerRef.current;
8604
+ const targetScroll = pageInfo.top - pageGap;
8605
+ const scrollTop = container.scrollTop;
8606
+ const viewportHeight = container.clientHeight;
8607
+ const isVisible = targetScroll >= scrollTop && pageInfo.top + pageInfo.height <= scrollTop + viewportHeight;
8608
+ if (isVisible) {
8609
+ viewerStore.getState().completeScrollRequest(requestId);
8610
+ return;
8611
+ }
8612
+ container.scrollTo({
8613
+ top: targetScroll,
8614
+ behavior
8615
+ });
8616
+ if (behavior === "instant") {
8617
+ requestAnimationFrame(() => {
8618
+ viewerStore.getState().completeScrollRequest(requestId);
8619
+ });
8620
+ } else {
8621
+ let scrollEndTimeout;
8622
+ const handleScrollEnd = () => {
8623
+ clearTimeout(scrollEndTimeout);
8624
+ scrollEndTimeout = setTimeout(() => {
8625
+ container.removeEventListener("scroll", handleScrollEnd);
8626
+ viewerStore.getState().completeScrollRequest(requestId);
8627
+ }, 100);
8628
+ };
8629
+ container.addEventListener("scroll", handleScrollEnd, { passive: true });
8630
+ handleScrollEnd();
8631
+ }
8632
+ }, [scrollToPageRequest, pageInfos, pageGap, viewerStore]);
8633
+ (0, import_react36.useEffect)(() => {
8634
+ if (scrollToPageRequest) return;
8285
8635
  if (!scrollContainerRef.current || pageInfos.length === 0) return;
8286
8636
  const pageInfo = pageInfos.find((p) => p.pageNumber === currentPage);
8287
8637
  if (pageInfo) {
@@ -8296,7 +8646,7 @@ var init_VirtualizedDocumentContainer = __esm({
8296
8646
  });
8297
8647
  }
8298
8648
  }
8299
- }, [currentPage, pageInfos, pageGap]);
8649
+ }, [currentPage, pageInfos, pageGap, scrollToPageRequest]);
8300
8650
  const handlePinchZoom = (0, import_react36.useCallback)(
8301
8651
  (pinchScale) => {
8302
8652
  const newScale = Math.max(0.25, Math.min(4, baseScaleRef.current * pinchScale));
@@ -8503,6 +8853,8 @@ var init_DualPageContainer = __esm({
8503
8853
  setScale,
8504
8854
  goToPage
8505
8855
  } = usePDFViewer();
8856
+ const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
8857
+ const { viewerStore } = usePDFViewerStores();
8506
8858
  const containerRef = (0, import_react38.useRef)(null);
8507
8859
  const documentRef = (0, import_react38.useRef)(null);
8508
8860
  const baseScaleRef = (0, import_react38.useRef)(scale);
@@ -8588,6 +8940,14 @@ var init_DualPageContainer = __esm({
8588
8940
  if (!cancelled) {
8589
8941
  setLeftPage(left);
8590
8942
  setRightPage(right);
8943
+ if (scrollToPageRequest) {
8944
+ const requestedPage = scrollToPageRequest.page;
8945
+ if (requestedPage === spread2.left || requestedPage === spread2.right) {
8946
+ requestAnimationFrame(() => {
8947
+ viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
8948
+ });
8949
+ }
8950
+ }
8591
8951
  }
8592
8952
  } catch (error) {
8593
8953
  if (!cancelled) {
@@ -8603,7 +8963,7 @@ var init_DualPageContainer = __esm({
8603
8963
  return () => {
8604
8964
  cancelled = true;
8605
8965
  };
8606
- }, [document2, currentPage, getSpreadPages]);
8966
+ }, [document2, currentPage, getSpreadPages, scrollToPageRequest, viewerStore]);
8607
8967
  const goToPreviousSpread = (0, import_react38.useCallback)(() => {
8608
8968
  const spread2 = getSpreadPages(currentPage);
8609
8969
  const leftmostPage = spread2.left || spread2.right || currentPage;
@@ -8780,6 +9140,145 @@ var init_DualPageContainer = __esm({
8780
9140
  }
8781
9141
  });
8782
9142
 
9143
+ // src/components/FloatingZoomControls/FloatingZoomControls.tsx
9144
+ var import_react39, import_jsx_runtime25, FloatingZoomControls;
9145
+ var init_FloatingZoomControls = __esm({
9146
+ "src/components/FloatingZoomControls/FloatingZoomControls.tsx"() {
9147
+ "use strict";
9148
+ import_react39 = require("react");
9149
+ init_hooks();
9150
+ init_utils();
9151
+ import_jsx_runtime25 = require("react/jsx-runtime");
9152
+ FloatingZoomControls = (0, import_react39.memo)(function FloatingZoomControls2({
9153
+ position = "bottom-right",
9154
+ className,
9155
+ showFitToWidth = true,
9156
+ showFitToPage = false,
9157
+ showZoomLevel = true
9158
+ }) {
9159
+ const { viewerStore } = usePDFViewerStores();
9160
+ const scale = useViewerStore((s) => s.scale);
9161
+ const document2 = useViewerStore((s) => s.document);
9162
+ const handleZoomIn = (0, import_react39.useCallback)(() => {
9163
+ const currentScale = viewerStore.getState().scale;
9164
+ const newScale = Math.min(4, currentScale + 0.05);
9165
+ viewerStore.getState().setScale(newScale);
9166
+ }, [viewerStore]);
9167
+ const handleZoomOut = (0, import_react39.useCallback)(() => {
9168
+ const currentScale = viewerStore.getState().scale;
9169
+ const newScale = Math.max(0.1, currentScale - 0.05);
9170
+ viewerStore.getState().setScale(newScale);
9171
+ }, [viewerStore]);
9172
+ const handleFitToWidth = (0, import_react39.useCallback)(() => {
9173
+ viewerStore.getState().setScale(1);
9174
+ }, [viewerStore]);
9175
+ const handleFitToPage = (0, import_react39.useCallback)(() => {
9176
+ viewerStore.getState().setScale(0.75);
9177
+ }, [viewerStore]);
9178
+ if (!document2) return null;
9179
+ const positionClasses = {
9180
+ "bottom-right": "bottom-4 right-4",
9181
+ "bottom-left": "bottom-4 left-4",
9182
+ "top-right": "top-4 right-4",
9183
+ "top-left": "top-4 left-4"
9184
+ };
9185
+ const zoomPercentage = Math.round(scale * 100);
9186
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
9187
+ "div",
9188
+ {
9189
+ className: cn(
9190
+ "fixed z-50 flex items-center gap-1",
9191
+ "bg-white dark:bg-gray-800 rounded-lg shadow-lg",
9192
+ "border border-gray-200 dark:border-gray-700",
9193
+ "p-1",
9194
+ positionClasses[position],
9195
+ className
9196
+ ),
9197
+ children: [
9198
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9199
+ "button",
9200
+ {
9201
+ onClick: handleZoomOut,
9202
+ className: cn(
9203
+ "w-8 h-8 flex items-center justify-center rounded",
9204
+ "text-gray-700 dark:text-gray-300",
9205
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9206
+ "transition-colors",
9207
+ "disabled:opacity-50 disabled:cursor-not-allowed"
9208
+ ),
9209
+ disabled: scale <= 0.25,
9210
+ title: "Zoom Out",
9211
+ "aria-label": "Zoom Out",
9212
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) })
9213
+ }
9214
+ ),
9215
+ showZoomLevel && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "min-w-[48px] text-center text-sm font-medium text-gray-700 dark:text-gray-300", children: [
9216
+ zoomPercentage,
9217
+ "%"
9218
+ ] }),
9219
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9220
+ "button",
9221
+ {
9222
+ onClick: handleZoomIn,
9223
+ className: cn(
9224
+ "w-8 h-8 flex items-center justify-center rounded",
9225
+ "text-gray-700 dark:text-gray-300",
9226
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9227
+ "transition-colors",
9228
+ "disabled:opacity-50 disabled:cursor-not-allowed"
9229
+ ),
9230
+ disabled: scale >= 4,
9231
+ title: "Zoom In",
9232
+ "aria-label": "Zoom In",
9233
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) })
9234
+ }
9235
+ ),
9236
+ (showFitToWidth || showFitToPage) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1" }),
9237
+ showFitToWidth && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9238
+ "button",
9239
+ {
9240
+ onClick: handleFitToWidth,
9241
+ className: cn(
9242
+ "w-8 h-8 flex items-center justify-center rounded",
9243
+ "text-gray-700 dark:text-gray-300",
9244
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9245
+ "transition-colors"
9246
+ ),
9247
+ title: "Fit to Width",
9248
+ "aria-label": "Fit to Width",
9249
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) })
9250
+ }
9251
+ ),
9252
+ showFitToPage && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9253
+ "button",
9254
+ {
9255
+ onClick: handleFitToPage,
9256
+ className: cn(
9257
+ "w-8 h-8 flex items-center justify-center rounded",
9258
+ "text-gray-700 dark:text-gray-300",
9259
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9260
+ "transition-colors"
9261
+ ),
9262
+ title: "Fit to Page",
9263
+ "aria-label": "Fit to Page",
9264
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) })
9265
+ }
9266
+ )
9267
+ ]
9268
+ }
9269
+ );
9270
+ });
9271
+ }
9272
+ });
9273
+
9274
+ // src/components/FloatingZoomControls/index.ts
9275
+ var init_FloatingZoomControls2 = __esm({
9276
+ "src/components/FloatingZoomControls/index.ts"() {
9277
+ "use strict";
9278
+ init_FloatingZoomControls();
9279
+ }
9280
+ });
9281
+
8783
9282
  // src/components/PDFViewer/PDFViewerClient.tsx
8784
9283
  var PDFViewerClient_exports = {};
8785
9284
  __export(PDFViewerClient_exports, {
@@ -8796,11 +9295,30 @@ function getSrcIdentifier(src) {
8796
9295
  const last = Array.from(data.slice(-4)).map((b) => b.toString(16).padStart(2, "0")).join("");
8797
9296
  return `binary:${len}:${first}:${last}`;
8798
9297
  }
8799
- var import_react39, import_jsx_runtime25, PDFViewerInner, PDFViewerClient;
9298
+ function mergeRects2(rects) {
9299
+ if (rects.length === 0) return [];
9300
+ const sorted = [...rects].sort((a, b) => a.y - b.y || a.x - b.x);
9301
+ const merged = [];
9302
+ let current = { ...sorted[0] };
9303
+ for (let i = 1; i < sorted.length; i++) {
9304
+ const rect = sorted[i];
9305
+ if (Math.abs(rect.y - current.y) < 2 && rect.x <= current.x + current.width + 2) {
9306
+ const newRight = Math.max(current.x + current.width, rect.x + rect.width);
9307
+ current.width = newRight - current.x;
9308
+ current.height = Math.max(current.height, rect.height);
9309
+ } else {
9310
+ merged.push(current);
9311
+ current = { ...rect };
9312
+ }
9313
+ }
9314
+ merged.push(current);
9315
+ return merged;
9316
+ }
9317
+ var import_react40, import_jsx_runtime26, PDFViewerInner, PDFViewerInnerWithRef, PDFViewerClient;
8800
9318
  var init_PDFViewerClient = __esm({
8801
9319
  "src/components/PDFViewer/PDFViewerClient.tsx"() {
8802
9320
  "use strict";
8803
- import_react39 = require("react");
9321
+ import_react40 = require("react");
8804
9322
  init_hooks();
8805
9323
  init_utils();
8806
9324
  init_Toolbar2();
@@ -8809,35 +9327,63 @@ var init_PDFViewerClient = __esm({
8809
9327
  init_DocumentContainer();
8810
9328
  init_ContinuousScrollContainer();
8811
9329
  init_DualPageContainer();
9330
+ init_FloatingZoomControls2();
8812
9331
  init_utils();
8813
- import_jsx_runtime25 = require("react/jsx-runtime");
8814
- PDFViewerInner = (0, import_react39.memo)(function PDFViewerInner2({
9332
+ import_jsx_runtime26 = require("react/jsx-runtime");
9333
+ PDFViewerInner = (0, import_react40.memo)(function PDFViewerInner2({
8815
9334
  src,
8816
9335
  initialPage = 1,
8817
- initialScale = 1,
9336
+ page: controlledPage,
9337
+ initialScale = "auto",
8818
9338
  showToolbar = true,
8819
9339
  showSidebar = true,
8820
9340
  showAnnotationToolbar = false,
9341
+ showFloatingZoom = true,
8821
9342
  viewMode = "single",
8822
9343
  onDocumentLoad,
8823
9344
  onPageChange,
8824
9345
  onScaleChange,
9346
+ onZoomChange,
8825
9347
  onError,
9348
+ onPageRenderStart,
9349
+ onPageRenderComplete,
9350
+ onHighlightAdded,
9351
+ onHighlightRemoved,
9352
+ onAnnotationAdded,
8826
9353
  workerSrc,
8827
- className
9354
+ className,
9355
+ loadingComponent,
9356
+ errorComponent,
9357
+ onReady
8828
9358
  }) {
8829
- const { viewerStore } = usePDFViewerStores();
8830
- const mountedRef = (0, import_react39.useRef)(true);
8831
- const [, setLoadState] = (0, import_react39.useState)("idle");
8832
- const onDocumentLoadRef = (0, import_react39.useRef)(onDocumentLoad);
8833
- const onErrorRef = (0, import_react39.useRef)(onError);
8834
- const onPageChangeRef = (0, import_react39.useRef)(onPageChange);
8835
- const onScaleChangeRef = (0, import_react39.useRef)(onScaleChange);
9359
+ const { viewerStore, annotationStore, searchStore } = usePDFViewerStores();
9360
+ const mountedRef = (0, import_react40.useRef)(true);
9361
+ const [, setLoadState] = (0, import_react40.useState)("idle");
9362
+ const onDocumentLoadRef = (0, import_react40.useRef)(onDocumentLoad);
9363
+ const onErrorRef = (0, import_react40.useRef)(onError);
9364
+ const onPageChangeRef = (0, import_react40.useRef)(onPageChange);
9365
+ const onScaleChangeRef = (0, import_react40.useRef)(onScaleChange);
9366
+ const onZoomChangeRef = (0, import_react40.useRef)(onZoomChange);
9367
+ const onPageRenderStartRef = (0, import_react40.useRef)(onPageRenderStart);
9368
+ const onPageRenderCompleteRef = (0, import_react40.useRef)(onPageRenderComplete);
9369
+ const onHighlightAddedRef = (0, import_react40.useRef)(onHighlightAdded);
9370
+ const onHighlightRemovedRef = (0, import_react40.useRef)(onHighlightRemoved);
9371
+ const onAnnotationAddedRef = (0, import_react40.useRef)(onAnnotationAdded);
9372
+ const onReadyRef = (0, import_react40.useRef)(onReady);
8836
9373
  onDocumentLoadRef.current = onDocumentLoad;
8837
9374
  onErrorRef.current = onError;
8838
9375
  onPageChangeRef.current = onPageChange;
8839
9376
  onScaleChangeRef.current = onScaleChange;
8840
- const srcIdRef = (0, import_react39.useRef)(null);
9377
+ onZoomChangeRef.current = onZoomChange;
9378
+ onPageRenderStartRef.current = onPageRenderStart;
9379
+ onPageRenderCompleteRef.current = onPageRenderComplete;
9380
+ onHighlightAddedRef.current = onHighlightAdded;
9381
+ onHighlightRemovedRef.current = onHighlightRemoved;
9382
+ onAnnotationAddedRef.current = onAnnotationAdded;
9383
+ onReadyRef.current = onReady;
9384
+ const isControlled = controlledPage !== void 0;
9385
+ const prevControlledPageRef = (0, import_react40.useRef)(controlledPage);
9386
+ const srcIdRef = (0, import_react40.useRef)(null);
8841
9387
  const currentPage = useViewerStore((s) => s.currentPage);
8842
9388
  const scale = useViewerStore((s) => s.scale);
8843
9389
  const theme = useViewerStore((s) => s.theme);
@@ -8845,18 +9391,463 @@ var init_PDFViewerClient = __esm({
8845
9391
  const error = useViewerStore((s) => s.error);
8846
9392
  const sidebarOpen = useViewerStore((s) => s.sidebarOpen);
8847
9393
  const srcId = getSrcIdentifier(src);
8848
- const handleRetry = (0, import_react39.useCallback)(() => {
9394
+ const handleRef = (0, import_react40.useRef)(null);
9395
+ (0, import_react40.useEffect)(() => {
9396
+ const handle = {
9397
+ // ==================== Text Highlighting ====================
9398
+ highlightText: async (text, options) => {
9399
+ const doc = viewerStore.getState().document;
9400
+ if (!doc) return [];
9401
+ const color = options?.color ?? "yellow";
9402
+ const targetPage = options?.page;
9403
+ const caseSensitive = options?.caseSensitive ?? false;
9404
+ const scrollTo = options?.scrollTo ?? true;
9405
+ const highlightIds = [];
9406
+ const searchText = caseSensitive ? text : text.toLowerCase();
9407
+ const pagesToSearch = targetPage ? [targetPage] : Array.from({ length: doc.numPages }, (_, i) => i + 1);
9408
+ for (const pageNum of pagesToSearch) {
9409
+ try {
9410
+ const page = await doc.getPage(pageNum);
9411
+ const textContent = await page.getTextContent();
9412
+ const viewport = page.getViewport({ scale: 1 });
9413
+ let fullText = "";
9414
+ const charPositions = [];
9415
+ for (const item of textContent.items) {
9416
+ if ("str" in item && item.str) {
9417
+ const tx = item.transform;
9418
+ const x = tx[4];
9419
+ const y = viewport.height - tx[5];
9420
+ const width = item.width ?? 0;
9421
+ const height = item.height ?? 12;
9422
+ const charWidth = item.str.length > 0 ? width / item.str.length : width;
9423
+ for (let i = 0; i < item.str.length; i++) {
9424
+ charPositions.push({
9425
+ char: item.str[i],
9426
+ rect: {
9427
+ x: x + i * charWidth,
9428
+ y: y - height,
9429
+ width: charWidth,
9430
+ height
9431
+ }
9432
+ });
9433
+ }
9434
+ fullText += item.str;
9435
+ }
9436
+ }
9437
+ const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
9438
+ let startIndex = 0;
9439
+ while (true) {
9440
+ const matchIndex = textToSearch.indexOf(searchText, startIndex);
9441
+ if (matchIndex === -1) break;
9442
+ const matchRects = [];
9443
+ for (let i = matchIndex; i < matchIndex + text.length && i < charPositions.length; i++) {
9444
+ matchRects.push(charPositions[i].rect);
9445
+ }
9446
+ const mergedRects = mergeRects2(matchRects);
9447
+ const highlight = annotationStore.getState().addHighlight({
9448
+ pageNumber: pageNum,
9449
+ rects: mergedRects,
9450
+ color,
9451
+ text: fullText.substring(matchIndex, matchIndex + text.length)
9452
+ });
9453
+ highlightIds.push(highlight.id);
9454
+ startIndex = matchIndex + 1;
9455
+ }
9456
+ } catch {
9457
+ }
9458
+ }
9459
+ if (scrollTo && highlightIds.length > 0) {
9460
+ const firstHighlight = annotationStore.getState().highlights.find((h) => h.id === highlightIds[0]);
9461
+ if (firstHighlight) {
9462
+ viewerStore.getState().goToPage(firstHighlight.pageNumber);
9463
+ }
9464
+ }
9465
+ return highlightIds;
9466
+ },
9467
+ removeHighlight: (id) => {
9468
+ annotationStore.getState().removeHighlight(id);
9469
+ },
9470
+ clearHighlights: () => {
9471
+ const highlights = annotationStore.getState().highlights;
9472
+ for (const h of highlights) {
9473
+ annotationStore.getState().removeHighlight(h.id);
9474
+ }
9475
+ },
9476
+ // ==================== Annotations ====================
9477
+ drawRect: (options) => {
9478
+ const annotation = annotationStore.getState().addAnnotation({
9479
+ type: "shape",
9480
+ shapeType: "rect",
9481
+ pageNumber: options.page,
9482
+ x: options.x,
9483
+ y: options.y,
9484
+ width: options.width,
9485
+ height: options.height,
9486
+ color: options.color ?? "blue",
9487
+ strokeWidth: options.strokeWidth ?? 2
9488
+ });
9489
+ return annotation.id;
9490
+ },
9491
+ drawCircle: (options) => {
9492
+ const annotation = annotationStore.getState().addAnnotation({
9493
+ type: "shape",
9494
+ shapeType: "circle",
9495
+ pageNumber: options.page,
9496
+ x: options.x,
9497
+ y: options.y,
9498
+ width: options.radius * 2,
9499
+ height: options.radius * 2,
9500
+ color: options.color ?? "blue",
9501
+ strokeWidth: options.strokeWidth ?? 2
9502
+ });
9503
+ return annotation.id;
9504
+ },
9505
+ addNote: (options) => {
9506
+ const annotation = annotationStore.getState().addAnnotation({
9507
+ type: "note",
9508
+ pageNumber: options.page,
9509
+ x: options.x,
9510
+ y: options.y,
9511
+ content: options.content,
9512
+ color: options.color ?? "yellow"
9513
+ });
9514
+ return annotation.id;
9515
+ },
9516
+ removeAnnotation: (id) => {
9517
+ annotationStore.getState().removeAnnotation(id);
9518
+ },
9519
+ clearAnnotations: () => {
9520
+ const annotations = annotationStore.getState().annotations;
9521
+ for (const a of annotations) {
9522
+ annotationStore.getState().removeAnnotation(a.id);
9523
+ }
9524
+ },
9525
+ // ==================== Navigation ====================
9526
+ goToPage: async (page, options) => {
9527
+ const behavior = options?.behavior ?? "smooth";
9528
+ await viewerStore.getState().requestScrollToPage(page, behavior);
9529
+ },
9530
+ nextPage: () => {
9531
+ viewerStore.getState().nextPage();
9532
+ },
9533
+ previousPage: () => {
9534
+ viewerStore.getState().previousPage();
9535
+ },
9536
+ getCurrentPage: () => {
9537
+ return viewerStore.getState().currentPage;
9538
+ },
9539
+ getNumPages: () => {
9540
+ return viewerStore.getState().numPages;
9541
+ },
9542
+ // ==================== Zoom ====================
9543
+ setZoom: (scale2) => {
9544
+ viewerStore.getState().setScale(scale2);
9545
+ },
9546
+ getZoom: () => {
9547
+ return viewerStore.getState().scale;
9548
+ },
9549
+ zoomIn: () => {
9550
+ viewerStore.getState().zoomIn();
9551
+ },
9552
+ zoomOut: () => {
9553
+ viewerStore.getState().zoomOut();
9554
+ },
9555
+ // ==================== Search ====================
9556
+ search: async (query, options) => {
9557
+ const doc = viewerStore.getState().document;
9558
+ if (!doc) return [];
9559
+ searchStore.getState().setQuery(query);
9560
+ if (options?.caseSensitive !== void 0) {
9561
+ searchStore.getState().setCaseSensitive(options.caseSensitive);
9562
+ }
9563
+ if (options?.wholeWord !== void 0) {
9564
+ searchStore.getState().setWholeWord(options.wholeWord);
9565
+ }
9566
+ await searchStore.getState().search(doc);
9567
+ return searchStore.getState().results;
9568
+ },
9569
+ nextSearchResult: () => {
9570
+ searchStore.getState().nextResult();
9571
+ const results = searchStore.getState().results;
9572
+ const index = searchStore.getState().currentResultIndex;
9573
+ if (results[index]) {
9574
+ viewerStore.getState().goToPage(results[index].pageNumber);
9575
+ }
9576
+ },
9577
+ previousSearchResult: () => {
9578
+ searchStore.getState().previousResult();
9579
+ const results = searchStore.getState().results;
9580
+ const index = searchStore.getState().currentResultIndex;
9581
+ if (results[index]) {
9582
+ viewerStore.getState().goToPage(results[index].pageNumber);
9583
+ }
9584
+ },
9585
+ clearSearch: () => {
9586
+ searchStore.getState().clearSearch();
9587
+ },
9588
+ // ==================== Combined Search & Highlight ====================
9589
+ searchAndHighlight: async (query, options) => {
9590
+ const doc = viewerStore.getState().document;
9591
+ if (!doc) {
9592
+ return { matchCount: 0, highlightIds: [], matches: [] };
9593
+ }
9594
+ const color = options?.color ?? "yellow";
9595
+ const caseSensitive = options?.caseSensitive ?? false;
9596
+ const wholeWord = options?.wholeWord ?? false;
9597
+ const scrollToFirst = options?.scrollToFirst ?? true;
9598
+ const clearPrevious = options?.clearPrevious ?? true;
9599
+ if (clearPrevious) {
9600
+ const existingHighlights = annotationStore.getState().highlights;
9601
+ for (const h of existingHighlights) {
9602
+ if (h.source === "search") {
9603
+ annotationStore.getState().removeHighlight(h.id);
9604
+ }
9605
+ }
9606
+ }
9607
+ let pagesToSearch;
9608
+ if (options?.pageRange) {
9609
+ if (Array.isArray(options.pageRange)) {
9610
+ pagesToSearch = options.pageRange;
9611
+ } else {
9612
+ const { start, end } = options.pageRange;
9613
+ pagesToSearch = Array.from({ length: end - start + 1 }, (_, i) => start + i);
9614
+ }
9615
+ } else {
9616
+ pagesToSearch = Array.from({ length: doc.numPages }, (_, i) => i + 1);
9617
+ }
9618
+ const result = {
9619
+ matchCount: 0,
9620
+ highlightIds: [],
9621
+ matches: []
9622
+ };
9623
+ const searchText = caseSensitive ? query : query.toLowerCase();
9624
+ for (const pageNum of pagesToSearch) {
9625
+ if (pageNum < 1 || pageNum > doc.numPages) continue;
9626
+ try {
9627
+ const page = await doc.getPage(pageNum);
9628
+ const textContent = await page.getTextContent();
9629
+ const viewport = page.getViewport({ scale: 1 });
9630
+ let fullText = "";
9631
+ const charPositions = [];
9632
+ for (const item of textContent.items) {
9633
+ if ("str" in item && item.str) {
9634
+ const tx = item.transform;
9635
+ const x = tx[4];
9636
+ const y = viewport.height - tx[5];
9637
+ const width = item.width ?? 0;
9638
+ const height = item.height ?? 12;
9639
+ const charWidth = item.str.length > 0 ? width / item.str.length : width;
9640
+ for (let i = 0; i < item.str.length; i++) {
9641
+ charPositions.push({
9642
+ char: item.str[i],
9643
+ rect: {
9644
+ x: x + i * charWidth,
9645
+ y: y - height,
9646
+ width: charWidth,
9647
+ height
9648
+ }
9649
+ });
9650
+ }
9651
+ fullText += item.str;
9652
+ }
9653
+ }
9654
+ const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
9655
+ let startIndex = 0;
9656
+ while (true) {
9657
+ let matchIndex = textToSearch.indexOf(searchText, startIndex);
9658
+ if (matchIndex === -1) break;
9659
+ if (wholeWord) {
9660
+ const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
9661
+ const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
9662
+ if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
9663
+ startIndex = matchIndex + 1;
9664
+ continue;
9665
+ }
9666
+ }
9667
+ const matchRects = [];
9668
+ for (let i = matchIndex; i < matchIndex + query.length && i < charPositions.length; i++) {
9669
+ matchRects.push(charPositions[i].rect);
9670
+ }
9671
+ const mergedRects = mergeRects2(matchRects);
9672
+ const highlight = annotationStore.getState().addHighlight({
9673
+ pageNumber: pageNum,
9674
+ rects: mergedRects,
9675
+ color,
9676
+ text: fullText.substring(matchIndex, matchIndex + query.length),
9677
+ source: "search"
9678
+ });
9679
+ result.matchCount++;
9680
+ result.highlightIds.push(highlight.id);
9681
+ result.matches.push({
9682
+ pageNumber: pageNum,
9683
+ text: fullText.substring(matchIndex, matchIndex + query.length),
9684
+ highlightId: highlight.id,
9685
+ rects: mergedRects
9686
+ });
9687
+ startIndex = matchIndex + 1;
9688
+ }
9689
+ } catch {
9690
+ }
9691
+ }
9692
+ if (scrollToFirst && result.matches.length > 0) {
9693
+ const firstMatch = result.matches[0];
9694
+ await viewerStore.getState().requestScrollToPage(firstMatch.pageNumber, "smooth");
9695
+ }
9696
+ return result;
9697
+ },
9698
+ // ==================== Agent Tools ====================
9699
+ agentTools: {
9700
+ navigateToPage: async (page) => {
9701
+ try {
9702
+ const { currentPage: currentPage2, numPages } = viewerStore.getState();
9703
+ if (numPages === 0) {
9704
+ return {
9705
+ success: false,
9706
+ error: { code: "NO_DOCUMENT", message: "No document is loaded" }
9707
+ };
9708
+ }
9709
+ if (page < 1 || page > numPages) {
9710
+ return {
9711
+ success: false,
9712
+ error: { code: "INVALID_PAGE", message: `Page ${page} is out of range (1-${numPages})` }
9713
+ };
9714
+ }
9715
+ const previousPage = currentPage2;
9716
+ await viewerStore.getState().requestScrollToPage(page, "smooth");
9717
+ return {
9718
+ success: true,
9719
+ data: { previousPage, currentPage: page }
9720
+ };
9721
+ } catch (err) {
9722
+ return {
9723
+ success: false,
9724
+ error: { code: "NAVIGATION_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9725
+ };
9726
+ }
9727
+ },
9728
+ highlightText: async (text, options) => {
9729
+ try {
9730
+ const highlightIds = await handle.highlightText(text, options);
9731
+ return {
9732
+ success: true,
9733
+ data: { matchCount: highlightIds.length, highlightIds }
9734
+ };
9735
+ } catch (err) {
9736
+ return {
9737
+ success: false,
9738
+ error: { code: "HIGHLIGHT_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9739
+ };
9740
+ }
9741
+ },
9742
+ getPageContent: async (page) => {
9743
+ try {
9744
+ const doc = viewerStore.getState().document;
9745
+ if (!doc) {
9746
+ return {
9747
+ success: false,
9748
+ error: { code: "NO_DOCUMENT", message: "No document is loaded" }
9749
+ };
9750
+ }
9751
+ if (page < 1 || page > doc.numPages) {
9752
+ return {
9753
+ success: false,
9754
+ error: { code: "INVALID_PAGE", message: `Page ${page} is out of range (1-${doc.numPages})` }
9755
+ };
9756
+ }
9757
+ const pageObj = await doc.getPage(page);
9758
+ const textContent = await pageObj.getTextContent();
9759
+ const text = textContent.items.filter((item) => "str" in item).map((item) => item.str).join("");
9760
+ return {
9761
+ success: true,
9762
+ data: { text }
9763
+ };
9764
+ } catch (err) {
9765
+ return {
9766
+ success: false,
9767
+ error: { code: "CONTENT_FETCH_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9768
+ };
9769
+ }
9770
+ },
9771
+ clearAllVisuals: async () => {
9772
+ try {
9773
+ const highlights = annotationStore.getState().highlights;
9774
+ for (const h of highlights) {
9775
+ annotationStore.getState().removeHighlight(h.id);
9776
+ }
9777
+ const annotations = annotationStore.getState().annotations;
9778
+ for (const a of annotations) {
9779
+ annotationStore.getState().removeAnnotation(a.id);
9780
+ }
9781
+ return { success: true };
9782
+ } catch (err) {
9783
+ return {
9784
+ success: false,
9785
+ error: { code: "CLEAR_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9786
+ };
9787
+ }
9788
+ }
9789
+ },
9790
+ // ==================== Coordinate Helpers ====================
9791
+ coordinates: {
9792
+ getPageDimensions: (page) => {
9793
+ const doc = viewerStore.getState().document;
9794
+ if (!doc || page < 1 || page > doc.numPages) {
9795
+ return null;
9796
+ }
9797
+ try {
9798
+ return {
9799
+ width: 612,
9800
+ // Default US Letter width
9801
+ height: 792,
9802
+ // Default US Letter height
9803
+ rotation: viewerStore.getState().rotation
9804
+ };
9805
+ } catch {
9806
+ return null;
9807
+ }
9808
+ },
9809
+ percentToPixels: (xPercent, yPercent, page) => {
9810
+ const dimensions = handle.coordinates.getPageDimensions(page);
9811
+ if (!dimensions) return null;
9812
+ const scale2 = viewerStore.getState().scale;
9813
+ return {
9814
+ x: xPercent / 100 * dimensions.width * scale2,
9815
+ y: yPercent / 100 * dimensions.height * scale2
9816
+ };
9817
+ },
9818
+ pixelsToPercent: (x, y, page) => {
9819
+ const dimensions = handle.coordinates.getPageDimensions(page);
9820
+ if (!dimensions) return null;
9821
+ const scale2 = viewerStore.getState().scale;
9822
+ return {
9823
+ x: x / (dimensions.width * scale2) * 100,
9824
+ y: y / (dimensions.height * scale2) * 100
9825
+ };
9826
+ }
9827
+ },
9828
+ // ==================== Document ====================
9829
+ getDocument: () => {
9830
+ return viewerStore.getState().document;
9831
+ },
9832
+ isLoaded: () => {
9833
+ return viewerStore.getState().document !== null;
9834
+ }
9835
+ };
9836
+ handleRef.current = handle;
9837
+ onReadyRef.current?.(handle);
9838
+ }, [viewerStore, annotationStore, searchStore]);
9839
+ const handleRetry = (0, import_react40.useCallback)(() => {
8849
9840
  srcIdRef.current = null;
8850
9841
  viewerStore.getState().setError(null);
8851
9842
  setLoadState("idle");
8852
9843
  }, [viewerStore]);
8853
- (0, import_react39.useEffect)(() => {
9844
+ (0, import_react40.useEffect)(() => {
8854
9845
  mountedRef.current = true;
8855
9846
  return () => {
8856
9847
  mountedRef.current = false;
8857
9848
  };
8858
9849
  }, []);
8859
- (0, import_react39.useEffect)(() => {
9850
+ (0, import_react40.useEffect)(() => {
8860
9851
  if (srcIdRef.current === srcId && viewerStore.getState().document) {
8861
9852
  return;
8862
9853
  }
@@ -8883,8 +9874,12 @@ var init_PDFViewerClient = __esm({
8883
9874
  if (initialPage !== 1) {
8884
9875
  viewerStore.getState().goToPage(initialPage);
8885
9876
  }
8886
- if (typeof initialScale === "number" && initialScale !== 1) {
9877
+ if (typeof initialScale === "number") {
8887
9878
  viewerStore.getState().setScale(initialScale);
9879
+ } else if (initialScale === "auto" || initialScale === "page-width") {
9880
+ viewerStore.getState().setScale(1);
9881
+ } else if (initialScale === "page-fit") {
9882
+ viewerStore.getState().setScale(0.75);
8888
9883
  }
8889
9884
  onDocumentLoadRef.current?.({ document: document2, numPages });
8890
9885
  } else {
@@ -8904,23 +9899,49 @@ var init_PDFViewerClient = __esm({
8904
9899
  return () => {
8905
9900
  };
8906
9901
  }, [srcId, src, workerSrc, initialPage, initialScale, viewerStore]);
8907
- const prevPageRef = (0, import_react39.useRef)(currentPage);
8908
- (0, import_react39.useEffect)(() => {
9902
+ const prevPageRef = (0, import_react40.useRef)(currentPage);
9903
+ (0, import_react40.useEffect)(() => {
8909
9904
  if (prevPageRef.current !== currentPage) {
8910
9905
  prevPageRef.current = currentPage;
8911
9906
  onPageChangeRef.current?.(currentPage);
8912
9907
  }
8913
9908
  }, [currentPage]);
8914
- const prevScaleRef = (0, import_react39.useRef)(scale);
8915
- (0, import_react39.useEffect)(() => {
9909
+ const prevScaleRef = (0, import_react40.useRef)(scale);
9910
+ (0, import_react40.useEffect)(() => {
8916
9911
  if (prevScaleRef.current !== scale) {
8917
9912
  prevScaleRef.current = scale;
8918
9913
  onScaleChangeRef.current?.(scale);
9914
+ onZoomChangeRef.current?.(scale);
8919
9915
  }
8920
9916
  }, [scale]);
9917
+ (0, import_react40.useEffect)(() => {
9918
+ if (!isControlled || controlledPage === void 0) return;
9919
+ if (prevControlledPageRef.current === controlledPage) return;
9920
+ prevControlledPageRef.current = controlledPage;
9921
+ const { numPages, currentPage: currentPage2 } = viewerStore.getState();
9922
+ if (numPages > 0 && controlledPage !== currentPage2) {
9923
+ viewerStore.getState().requestScrollToPage(controlledPage, "smooth");
9924
+ }
9925
+ }, [controlledPage, isControlled, viewerStore]);
8921
9926
  const themeClass = theme === "dark" ? "dark" : "";
8922
9927
  if (error) {
8923
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9928
+ if (errorComponent) {
9929
+ const errorContent = typeof errorComponent === "function" ? errorComponent(error, handleRetry) : errorComponent;
9930
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
9931
+ "div",
9932
+ {
9933
+ className: cn(
9934
+ "pdf-viewer pdf-viewer-error",
9935
+ "flex flex-col h-full",
9936
+ "bg-white dark:bg-gray-900",
9937
+ themeClass,
9938
+ className
9939
+ ),
9940
+ children: errorContent
9941
+ }
9942
+ );
9943
+ }
9944
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8924
9945
  "div",
8925
9946
  {
8926
9947
  className: cn(
@@ -8930,10 +9951,10 @@ var init_PDFViewerClient = __esm({
8930
9951
  themeClass,
8931
9952
  className
8932
9953
  ),
8933
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "text-center p-8", children: [
8934
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "text-red-500 text-lg font-semibold mb-2", children: "Failed to load PDF" }),
8935
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "text-gray-500 text-sm", children: error.message }),
8936
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9954
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "text-center p-8", children: [
9955
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "text-red-500 text-lg font-semibold mb-2", children: "Failed to load PDF" }),
9956
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "text-gray-500 text-sm", children: error.message }),
9957
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8937
9958
  "button",
8938
9959
  {
8939
9960
  onClick: handleRetry,
@@ -8948,68 +9969,85 @@ var init_PDFViewerClient = __esm({
8948
9969
  const renderContainer = () => {
8949
9970
  switch (viewMode) {
8950
9971
  case "continuous":
8951
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ContinuousScrollContainer, {});
9972
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ContinuousScrollContainer, {});
8952
9973
  case "dual":
8953
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DualPageContainer, {});
9974
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(DualPageContainer, {});
8954
9975
  case "single":
8955
9976
  default:
8956
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DocumentContainer, {});
9977
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(DocumentContainer, {});
8957
9978
  }
8958
9979
  };
8959
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
9980
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
8960
9981
  "div",
8961
9982
  {
8962
9983
  className: cn(
8963
9984
  "pdf-viewer",
8964
- "flex flex-col h-full",
9985
+ "flex flex-col h-full relative",
8965
9986
  "bg-white dark:bg-gray-900",
8966
9987
  "text-gray-900 dark:text-gray-100",
8967
9988
  themeClass,
8968
9989
  className
8969
9990
  ),
8970
9991
  children: [
8971
- showToolbar && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Toolbar, {}),
8972
- showAnnotationToolbar && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AnnotationToolbar, {}),
8973
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-1 overflow-hidden", children: [
8974
- showSidebar && sidebarOpen && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Sidebar, {}),
9992
+ showToolbar && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Toolbar, {}),
9993
+ showAnnotationToolbar && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AnnotationToolbar, {}),
9994
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-1 overflow-hidden", children: [
9995
+ showSidebar && sidebarOpen && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Sidebar, {}),
8975
9996
  renderContainer()
8976
9997
  ] }),
8977
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-900/80", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-center", children: [
8978
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
8979
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF..." })
9998
+ showFloatingZoom && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(FloatingZoomControls, { position: "bottom-right" }),
9999
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-900/80", children: loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-col items-center", children: [
10000
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
10001
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF..." })
8980
10002
  ] }) })
8981
10003
  ]
8982
10004
  }
8983
10005
  );
8984
10006
  });
8985
- PDFViewerClient = (0, import_react39.memo)(function PDFViewerClient2(props) {
8986
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8987
- PDFViewerProvider,
8988
- {
8989
- theme: props.theme,
8990
- defaultSidebarPanel: props.defaultSidebarPanel,
8991
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(PDFViewerInner, { ...props })
8992
- }
8993
- );
8994
- });
10007
+ PDFViewerInnerWithRef = (0, import_react40.forwardRef)(
10008
+ function PDFViewerInnerWithRef2(props, ref) {
10009
+ const handleRef = (0, import_react40.useRef)(null);
10010
+ const handleReady = (0, import_react40.useCallback)((handle) => {
10011
+ handleRef.current = handle;
10012
+ if (typeof ref === "function") {
10013
+ ref(handle);
10014
+ } else if (ref) {
10015
+ ref.current = handle;
10016
+ }
10017
+ }, [ref]);
10018
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PDFViewerInner, { ...props, onReady: handleReady });
10019
+ }
10020
+ );
10021
+ PDFViewerClient = (0, import_react40.memo)(
10022
+ (0, import_react40.forwardRef)(function PDFViewerClient2(props, ref) {
10023
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10024
+ PDFViewerProvider,
10025
+ {
10026
+ theme: props.theme,
10027
+ defaultSidebarPanel: props.defaultSidebarPanel,
10028
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PDFViewerInnerWithRef, { ref, ...props })
10029
+ }
10030
+ );
10031
+ })
10032
+ );
8995
10033
  }
8996
10034
  });
8997
10035
 
8998
10036
  // src/components/PDFViewer/PDFViewer.tsx
8999
- var import_react40, import_jsx_runtime26, PDFViewerClient3, PDFViewerLoading, PDFViewer;
10037
+ var import_react41, import_jsx_runtime27, PDFViewerClient3, PDFViewerLoading, PDFViewer;
9000
10038
  var init_PDFViewer = __esm({
9001
10039
  "src/components/PDFViewer/PDFViewer.tsx"() {
9002
10040
  "use strict";
9003
- import_react40 = require("react");
10041
+ import_react41 = require("react");
9004
10042
  init_utils();
9005
- import_jsx_runtime26 = require("react/jsx-runtime");
9006
- PDFViewerClient3 = (0, import_react40.lazy)(
10043
+ import_jsx_runtime27 = require("react/jsx-runtime");
10044
+ PDFViewerClient3 = (0, import_react41.lazy)(
9007
10045
  () => Promise.resolve().then(() => (init_PDFViewerClient(), PDFViewerClient_exports)).then((mod) => ({ default: mod.PDFViewerClient }))
9008
10046
  );
9009
- PDFViewerLoading = (0, import_react40.memo)(function PDFViewerLoading2({
10047
+ PDFViewerLoading = (0, import_react41.memo)(function PDFViewerLoading2({
9010
10048
  className
9011
10049
  }) {
9012
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10050
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
9013
10051
  "div",
9014
10052
  {
9015
10053
  className: cn(
@@ -9018,18 +10056,18 @@ var init_PDFViewer = __esm({
9018
10056
  "bg-white dark:bg-gray-900",
9019
10057
  className
9020
10058
  ),
9021
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-col items-center", children: [
9022
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
9023
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF viewer..." })
10059
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col items-center", children: [
10060
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
10061
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF viewer..." })
9024
10062
  ] }) })
9025
10063
  }
9026
10064
  );
9027
10065
  });
9028
- PDFViewer = (0, import_react40.memo)(function PDFViewer2(props) {
10066
+ PDFViewer = (0, import_react41.memo)(function PDFViewer2(props) {
9029
10067
  if (typeof window === "undefined") {
9030
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PDFViewerLoading, { className: props.className });
10068
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PDFViewerLoading, { className: props.className });
9031
10069
  }
9032
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react40.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PDFViewerLoading, { className: props.className }), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PDFViewerClient3, { ...props }) });
10070
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react41.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PDFViewerLoading, { className: props.className }), children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PDFViewerClient3, { ...props }) });
9033
10071
  });
9034
10072
  }
9035
10073
  });
@@ -9060,6 +10098,7 @@ __export(index_exports, {
9060
10098
  DocumentContainer: () => DocumentContainer,
9061
10099
  DrawingCanvas: () => DrawingCanvas,
9062
10100
  DualPageContainer: () => DualPageContainer,
10101
+ FloatingZoomControls: () => FloatingZoomControls,
9063
10102
  FocusRegionLayer: () => FocusRegionLayer,
9064
10103
  HighlightLayer: () => HighlightLayer,
9065
10104
  HighlightPopover: () => HighlightPopover,
@@ -9070,6 +10109,7 @@ __export(index_exports, {
9070
10109
  OutlinePanel: () => OutlinePanel,
9071
10110
  PDFErrorBoundary: () => PDFErrorBoundary,
9072
10111
  PDFPage: () => PDFPage,
10112
+ PDFThumbnailNav: () => PDFThumbnailNav,
9073
10113
  PDFViewer: () => PDFViewer,
9074
10114
  PDFViewerClient: () => PDFViewerClient,
9075
10115
  PDFViewerContext: () => PDFViewerContext,
@@ -9088,9 +10128,11 @@ __export(index_exports, {
9088
10128
  ThumbnailPanel: () => ThumbnailPanel,
9089
10129
  Toolbar: () => Toolbar,
9090
10130
  VirtualizedDocumentContainer: () => VirtualizedDocumentContainer,
10131
+ applyRotation: () => applyRotation,
9091
10132
  clearHighlights: () => clearHighlights,
9092
10133
  clearStudentData: () => clearStudentData,
9093
10134
  cn: () => cn,
10135
+ countTextOnPage: () => countTextOnPage,
9094
10136
  createAgentAPI: () => createAgentAPI,
9095
10137
  createAgentStore: () => createAgentStore,
9096
10138
  createAnnotationStore: () => createAnnotationStore,
@@ -9099,6 +10141,7 @@ __export(index_exports, {
9099
10141
  createSearchStore: () => createSearchStore,
9100
10142
  createStudentStore: () => createStudentStore,
9101
10143
  createViewerStore: () => createViewerStore,
10144
+ doRectsIntersect: () => doRectsIntersect,
9102
10145
  downloadAnnotationsAsJSON: () => downloadAnnotationsAsJSON,
9103
10146
  downloadAnnotationsAsMarkdown: () => downloadAnnotationsAsMarkdown,
9104
10147
  downloadFile: () => downloadFile,
@@ -9106,25 +10149,39 @@ __export(index_exports, {
9106
10149
  exportAnnotationsAsMarkdown: () => exportAnnotationsAsMarkdown,
9107
10150
  exportHighlightsAsJSON: () => exportHighlightsAsJSON,
9108
10151
  exportHighlightsAsMarkdown: () => exportHighlightsAsMarkdown,
10152
+ extractPageText: () => extractPageText,
10153
+ findTextInDocument: () => findTextInDocument,
10154
+ findTextOnPage: () => findTextOnPage,
9109
10155
  generateDocumentId: () => generateDocumentId,
9110
10156
  getAllDocumentIds: () => getAllDocumentIds,
9111
10157
  getAllStudentDataDocumentIds: () => getAllStudentDataDocumentIds,
9112
10158
  getMetadata: () => getMetadata,
9113
10159
  getOutline: () => getOutline,
9114
10160
  getPage: () => getPage,
10161
+ getPageText: () => getPageText,
9115
10162
  getPageTextContent: () => getPageTextContent,
9116
10163
  getPluginManager: () => getPluginManager,
10164
+ getRectIntersection: () => getRectIntersection,
10165
+ getRotatedDimensions: () => getRotatedDimensions,
9117
10166
  getStorageStats: () => getStorageStats,
9118
10167
  importHighlightsFromJSON: () => importHighlightsFromJSON,
9119
10168
  initializePDFJS: () => initializePDFJS,
9120
10169
  isPDFJSInitialized: () => isPDFJSInitialized,
10170
+ isPointInRect: () => isPointInRect,
9121
10171
  loadDocument: () => loadDocument,
9122
10172
  loadHighlights: () => loadHighlights,
9123
10173
  loadStudentData: () => loadStudentData,
10174
+ mergeAdjacentRects: () => mergeAdjacentRects,
10175
+ pdfToPercent: () => pdfToPercent,
10176
+ pdfToViewport: () => pdfToViewport,
9124
10177
  pdfjsLib: () => pdfjsLib,
10178
+ percentToPDF: () => percentToPDF,
10179
+ percentToViewport: () => percentToViewport,
9125
10180
  quickViewer: () => quickViewer,
10181
+ removeRotation: () => removeRotation,
9126
10182
  saveHighlights: () => saveHighlights,
9127
10183
  saveStudentData: () => saveStudentData,
10184
+ scaleRect: () => scaleRect,
9128
10185
  useAgentContext: () => useAgentContext,
9129
10186
  useAgentStore: () => useAgentStore,
9130
10187
  useAnnotationStore: () => useAnnotationStore,
@@ -9146,6 +10203,8 @@ __export(index_exports, {
9146
10203
  useTouchGestures: () => useTouchGestures,
9147
10204
  useViewerStore: () => useViewerStore,
9148
10205
  useZoom: () => useZoom,
10206
+ viewportToPDF: () => viewportToPDF,
10207
+ viewportToPercent: () => viewportToPercent,
9149
10208
  withErrorBoundary: () => withErrorBoundary
9150
10209
  });
9151
10210
  module.exports = __toCommonJS(index_exports);
@@ -9160,9 +10219,9 @@ init_HighlightPopover2();
9160
10219
  init_AnnotationToolbar2();
9161
10220
 
9162
10221
  // src/components/Annotations/StickyNote.tsx
9163
- var import_react41 = require("react");
10222
+ var import_react42 = require("react");
9164
10223
  init_utils();
9165
- var import_jsx_runtime27 = require("react/jsx-runtime");
10224
+ var import_jsx_runtime28 = require("react/jsx-runtime");
9166
10225
  var NOTE_COLORS = [
9167
10226
  "#fef08a",
9168
10227
  // yellow
@@ -9175,7 +10234,7 @@ var NOTE_COLORS = [
9175
10234
  "#fed7aa"
9176
10235
  // orange
9177
10236
  ];
9178
- var StickyNote = (0, import_react41.memo)(function StickyNote2({
10237
+ var StickyNote = (0, import_react42.memo)(function StickyNote2({
9179
10238
  note,
9180
10239
  scale,
9181
10240
  isSelected,
@@ -9188,37 +10247,37 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9188
10247
  onDragStart,
9189
10248
  className
9190
10249
  }) {
9191
- const [isExpanded, setIsExpanded] = (0, import_react41.useState)(false);
9192
- const [localContent, setLocalContent] = (0, import_react41.useState)(note.content);
9193
- const textareaRef = (0, import_react41.useRef)(null);
9194
- const noteRef = (0, import_react41.useRef)(null);
9195
- (0, import_react41.useEffect)(() => {
10250
+ const [isExpanded, setIsExpanded] = (0, import_react42.useState)(false);
10251
+ const [localContent, setLocalContent] = (0, import_react42.useState)(note.content);
10252
+ const textareaRef = (0, import_react42.useRef)(null);
10253
+ const noteRef = (0, import_react42.useRef)(null);
10254
+ (0, import_react42.useEffect)(() => {
9196
10255
  setLocalContent(note.content);
9197
10256
  }, [note.content]);
9198
- (0, import_react41.useEffect)(() => {
10257
+ (0, import_react42.useEffect)(() => {
9199
10258
  if (isEditing && textareaRef.current) {
9200
10259
  textareaRef.current.focus();
9201
10260
  textareaRef.current.select();
9202
10261
  }
9203
10262
  }, [isEditing]);
9204
- const handleClick = (0, import_react41.useCallback)((e) => {
10263
+ const handleClick = (0, import_react42.useCallback)((e) => {
9205
10264
  e.stopPropagation();
9206
10265
  onSelect?.();
9207
10266
  if (!isExpanded) {
9208
10267
  setIsExpanded(true);
9209
10268
  }
9210
10269
  }, [isExpanded, onSelect]);
9211
- const handleDoubleClick = (0, import_react41.useCallback)((e) => {
10270
+ const handleDoubleClick = (0, import_react42.useCallback)((e) => {
9212
10271
  e.stopPropagation();
9213
10272
  onStartEdit?.();
9214
10273
  }, [onStartEdit]);
9215
- const handleBlur = (0, import_react41.useCallback)(() => {
10274
+ const handleBlur = (0, import_react42.useCallback)(() => {
9216
10275
  if (isEditing && localContent !== note.content) {
9217
10276
  onUpdate?.({ content: localContent });
9218
10277
  }
9219
10278
  onEndEdit?.();
9220
10279
  }, [isEditing, localContent, note.content, onUpdate, onEndEdit]);
9221
- const handleKeyDown = (0, import_react41.useCallback)((e) => {
10280
+ const handleKeyDown = (0, import_react42.useCallback)((e) => {
9222
10281
  if (e.key === "Escape") {
9223
10282
  setLocalContent(note.content);
9224
10283
  onEndEdit?.();
@@ -9226,16 +10285,16 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9226
10285
  handleBlur();
9227
10286
  }
9228
10287
  }, [note.content, onEndEdit, handleBlur]);
9229
- const handleColorChange = (0, import_react41.useCallback)((color) => {
10288
+ const handleColorChange = (0, import_react42.useCallback)((color) => {
9230
10289
  onUpdate?.({ color });
9231
10290
  }, [onUpdate]);
9232
- const handleCollapse = (0, import_react41.useCallback)((e) => {
10291
+ const handleCollapse = (0, import_react42.useCallback)((e) => {
9233
10292
  e.stopPropagation();
9234
10293
  setIsExpanded(false);
9235
10294
  onEndEdit?.();
9236
10295
  }, [onEndEdit]);
9237
10296
  if (!isExpanded) {
9238
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10297
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9239
10298
  "div",
9240
10299
  {
9241
10300
  ref: noteRef,
@@ -9256,14 +10315,14 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9256
10315
  onMouseDown: onDragStart,
9257
10316
  onTouchStart: onDragStart,
9258
10317
  title: note.content || "Empty note",
9259
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10318
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9260
10319
  "svg",
9261
10320
  {
9262
10321
  className: "w-4 h-4 opacity-70",
9263
10322
  fill: "currentColor",
9264
10323
  viewBox: "0 0 20 20",
9265
10324
  style: { color: "#333" },
9266
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10325
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9267
10326
  "path",
9268
10327
  {
9269
10328
  fillRule: "evenodd",
@@ -9276,7 +10335,7 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9276
10335
  }
9277
10336
  );
9278
10337
  }
9279
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
10338
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
9280
10339
  "div",
9281
10340
  {
9282
10341
  ref: noteRef,
@@ -9294,14 +10353,14 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9294
10353
  },
9295
10354
  onClick: handleClick,
9296
10355
  children: [
9297
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
10356
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
9298
10357
  "div",
9299
10358
  {
9300
10359
  className: "flex items-center justify-between px-2 py-1 border-b border-black/10 cursor-move",
9301
10360
  onMouseDown: onDragStart,
9302
10361
  onTouchStart: onDragStart,
9303
10362
  children: [
9304
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex gap-1", children: NOTE_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10363
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex gap-1", children: NOTE_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9305
10364
  "button",
9306
10365
  {
9307
10366
  className: cn(
@@ -9318,8 +10377,8 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9318
10377
  },
9319
10378
  color
9320
10379
  )) }),
9321
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-1", children: [
9322
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10380
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex gap-1", children: [
10381
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9323
10382
  "button",
9324
10383
  {
9325
10384
  className: "p-0.5 hover:bg-black/10 rounded",
@@ -9328,23 +10387,23 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9328
10387
  onDelete?.();
9329
10388
  },
9330
10389
  title: "Delete note",
9331
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) })
10390
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) })
9332
10391
  }
9333
10392
  ),
9334
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10393
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9335
10394
  "button",
9336
10395
  {
9337
10396
  className: "p-0.5 hover:bg-black/10 rounded",
9338
10397
  onClick: handleCollapse,
9339
10398
  title: "Collapse note",
9340
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
10399
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
9341
10400
  }
9342
10401
  )
9343
10402
  ] })
9344
10403
  ]
9345
10404
  }
9346
10405
  ),
9347
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "p-2", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10406
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "p-2", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9348
10407
  "textarea",
9349
10408
  {
9350
10409
  ref: textareaRef,
@@ -9359,7 +10418,7 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9359
10418
  onKeyDown: handleKeyDown,
9360
10419
  placeholder: "Enter note..."
9361
10420
  }
9362
- ) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10421
+ ) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
9363
10422
  "div",
9364
10423
  {
9365
10424
  className: cn(
@@ -9370,16 +10429,16 @@ var StickyNote = (0, import_react41.memo)(function StickyNote2({
9370
10429
  children: note.content || "Double-click to edit..."
9371
10430
  }
9372
10431
  ) }),
9373
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-2 pb-1 text-[10px] text-gray-500", children: new Date(note.updatedAt).toLocaleDateString() })
10432
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "px-2 pb-1 text-[10px] text-gray-500", children: new Date(note.updatedAt).toLocaleDateString() })
9374
10433
  ]
9375
10434
  }
9376
10435
  );
9377
10436
  });
9378
10437
 
9379
10438
  // src/components/Annotations/DrawingCanvas.tsx
9380
- var import_react42 = require("react");
10439
+ var import_react43 = require("react");
9381
10440
  init_utils();
9382
- var import_jsx_runtime28 = require("react/jsx-runtime");
10441
+ var import_jsx_runtime29 = require("react/jsx-runtime");
9383
10442
  function pointsToSvgPath(points) {
9384
10443
  if (points.length === 0) return "";
9385
10444
  if (points.length === 1) {
@@ -9417,7 +10476,7 @@ function simplifyPath(points, tolerance = 1) {
9417
10476
  result.push(points[points.length - 1]);
9418
10477
  return result;
9419
10478
  }
9420
- var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
10479
+ var DrawingCanvas = (0, import_react43.memo)(function DrawingCanvas2({
9421
10480
  width,
9422
10481
  height,
9423
10482
  scale,
@@ -9427,10 +10486,10 @@ var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
9427
10486
  onDrawingComplete,
9428
10487
  className
9429
10488
  }) {
9430
- const svgRef = (0, import_react42.useRef)(null);
9431
- const [isDrawing, setIsDrawing] = (0, import_react42.useState)(false);
9432
- const [currentPath, setCurrentPath] = (0, import_react42.useState)([]);
9433
- const getPoint = (0, import_react42.useCallback)((e) => {
10489
+ const svgRef = (0, import_react43.useRef)(null);
10490
+ const [isDrawing, setIsDrawing] = (0, import_react43.useState)(false);
10491
+ const [currentPath, setCurrentPath] = (0, import_react43.useState)([]);
10492
+ const getPoint = (0, import_react43.useCallback)((e) => {
9434
10493
  if (!svgRef.current) return null;
9435
10494
  const svg = svgRef.current;
9436
10495
  const rect = svg.getBoundingClientRect();
@@ -9450,7 +10509,7 @@ var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
9450
10509
  y: (clientY - rect.top) / scale
9451
10510
  };
9452
10511
  }, [scale]);
9453
- const handleStart = (0, import_react42.useCallback)((e) => {
10512
+ const handleStart = (0, import_react43.useCallback)((e) => {
9454
10513
  if (!isActive) return;
9455
10514
  const point = getPoint(e);
9456
10515
  if (point) {
@@ -9458,14 +10517,14 @@ var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
9458
10517
  setCurrentPath([point]);
9459
10518
  }
9460
10519
  }, [isActive, getPoint]);
9461
- const handleMove = (0, import_react42.useCallback)((e) => {
10520
+ const handleMove = (0, import_react43.useCallback)((e) => {
9462
10521
  if (!isDrawing || !isActive) return;
9463
10522
  const point = getPoint(e);
9464
10523
  if (point) {
9465
10524
  setCurrentPath((prev) => [...prev, point]);
9466
10525
  }
9467
10526
  }, [isDrawing, isActive, getPoint]);
9468
- const handleEnd = (0, import_react42.useCallback)(() => {
10527
+ const handleEnd = (0, import_react43.useCallback)(() => {
9469
10528
  if (!isDrawing) return;
9470
10529
  setIsDrawing(false);
9471
10530
  if (currentPath.length >= 2) {
@@ -9474,7 +10533,7 @@ var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
9474
10533
  }
9475
10534
  setCurrentPath([]);
9476
10535
  }, [isDrawing, currentPath, onDrawingComplete]);
9477
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
10536
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
9478
10537
  "svg",
9479
10538
  {
9480
10539
  ref: svgRef,
@@ -9494,7 +10553,7 @@ var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
9494
10553
  onTouchStart: handleStart,
9495
10554
  onTouchMove: handleMove,
9496
10555
  onTouchEnd: handleEnd,
9497
- children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
10556
+ children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
9498
10557
  "path",
9499
10558
  {
9500
10559
  d: pointsToSvgPath(currentPath),
@@ -9511,10 +10570,10 @@ var DrawingCanvas = (0, import_react42.memo)(function DrawingCanvas2({
9511
10570
  });
9512
10571
 
9513
10572
  // src/components/Annotations/ShapeRenderer.tsx
9514
- var import_react43 = require("react");
10573
+ var import_react44 = require("react");
9515
10574
  init_utils();
9516
- var import_jsx_runtime29 = require("react/jsx-runtime");
9517
- var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
10575
+ var import_jsx_runtime30 = require("react/jsx-runtime");
10576
+ var ShapeRenderer = (0, import_react44.memo)(function ShapeRenderer2({
9518
10577
  shape,
9519
10578
  scale,
9520
10579
  isSelected,
@@ -9524,18 +10583,18 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9524
10583
  onDelete: _onDelete,
9525
10584
  className
9526
10585
  }) {
9527
- const [_isDragging, setIsDragging] = (0, import_react43.useState)(false);
9528
- const [_isResizing, setIsResizing] = (0, import_react43.useState)(false);
9529
- const [activeHandle, setActiveHandle] = (0, import_react43.useState)(null);
9530
- const startPosRef = (0, import_react43.useRef)({ x: 0, y: 0 });
9531
- const startShapeRef = (0, import_react43.useRef)({ x: 0, y: 0, width: 0, height: 0 });
10586
+ const [_isDragging, setIsDragging] = (0, import_react44.useState)(false);
10587
+ const [_isResizing, setIsResizing] = (0, import_react44.useState)(false);
10588
+ const [activeHandle, setActiveHandle] = (0, import_react44.useState)(null);
10589
+ const startPosRef = (0, import_react44.useRef)({ x: 0, y: 0 });
10590
+ const startShapeRef = (0, import_react44.useRef)({ x: 0, y: 0, width: 0, height: 0 });
9532
10591
  const { shapeType, x, y, width, height, color, strokeWidth, id: _id } = shape;
9533
10592
  const scaledX = x * scale;
9534
10593
  const scaledY = y * scale;
9535
10594
  const scaledWidth = width * scale;
9536
10595
  const scaledHeight = height * scale;
9537
10596
  const scaledStroke = strokeWidth * scale;
9538
- const getResizeHandles = (0, import_react43.useCallback)(() => {
10597
+ const getResizeHandles = (0, import_react44.useCallback)(() => {
9539
10598
  const handleSize = 8;
9540
10599
  const half = handleSize / 2;
9541
10600
  return [
@@ -9549,7 +10608,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9549
10608
  { position: "w", cursor: "ew-resize", x: scaledX - half, y: scaledY + scaledHeight / 2 - half }
9550
10609
  ];
9551
10610
  }, [scaledX, scaledY, scaledWidth, scaledHeight]);
9552
- const handleMouseDown = (0, import_react43.useCallback)((e, handle) => {
10611
+ const handleMouseDown = (0, import_react44.useCallback)((e, handle) => {
9553
10612
  e.stopPropagation();
9554
10613
  onSelect?.();
9555
10614
  if (!isEditing) return;
@@ -9625,7 +10684,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9625
10684
  document.addEventListener("mousemove", handleMouseMove);
9626
10685
  document.addEventListener("mouseup", handleMouseUp);
9627
10686
  }, [isEditing, x, y, width, height, scale, onSelect, onUpdate]);
9628
- const renderShape2 = (0, import_react43.useCallback)(() => {
10687
+ const renderShape2 = (0, import_react44.useCallback)(() => {
9629
10688
  const commonProps = {
9630
10689
  stroke: color,
9631
10690
  strokeWidth: scaledStroke,
@@ -9637,7 +10696,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9637
10696
  };
9638
10697
  switch (shapeType) {
9639
10698
  case "rect":
9640
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10699
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9641
10700
  "rect",
9642
10701
  {
9643
10702
  x: scaledX,
@@ -9648,7 +10707,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9648
10707
  }
9649
10708
  );
9650
10709
  case "circle":
9651
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10710
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9652
10711
  "ellipse",
9653
10712
  {
9654
10713
  cx: scaledX + scaledWidth / 2,
@@ -9659,7 +10718,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9659
10718
  }
9660
10719
  );
9661
10720
  case "line":
9662
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10721
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9663
10722
  "line",
9664
10723
  {
9665
10724
  x1: scaledX,
@@ -9679,22 +10738,22 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9679
10738
  const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
9680
10739
  const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
9681
10740
  const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
9682
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("g", { children: [
9683
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("line", { x1: scaledX, y1: scaledY, x2: endX, y2: endY, ...commonProps }),
9684
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
9685
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
10741
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("g", { children: [
10742
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("line", { x1: scaledX, y1: scaledY, x2: endX, y2: endY, ...commonProps }),
10743
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
10744
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
9686
10745
  ] });
9687
10746
  default:
9688
10747
  return null;
9689
10748
  }
9690
10749
  }, [shapeType, scaledX, scaledY, scaledWidth, scaledHeight, color, scaledStroke, isSelected]);
9691
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
10750
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
9692
10751
  "g",
9693
10752
  {
9694
10753
  className: cn("shape-renderer", className),
9695
10754
  onMouseDown: (e) => handleMouseDown(e),
9696
10755
  children: [
9697
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10756
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9698
10757
  "rect",
9699
10758
  {
9700
10759
  x: scaledX - 5,
@@ -9707,7 +10766,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9707
10766
  }
9708
10767
  ),
9709
10768
  renderShape2(),
9710
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10769
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9711
10770
  "rect",
9712
10771
  {
9713
10772
  x: scaledX - 2,
@@ -9720,7 +10779,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9720
10779
  strokeDasharray: "4 2"
9721
10780
  }
9722
10781
  ),
9723
- isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10782
+ isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9724
10783
  "rect",
9725
10784
  {
9726
10785
  x: handle.x,
@@ -9740,7 +10799,7 @@ var ShapeRenderer = (0, import_react43.memo)(function ShapeRenderer2({
9740
10799
  }
9741
10800
  );
9742
10801
  });
9743
- var ShapePreview = (0, import_react43.memo)(function ShapePreview2({
10802
+ var ShapePreview = (0, import_react44.memo)(function ShapePreview2({
9744
10803
  shapeType,
9745
10804
  startPoint,
9746
10805
  endPoint,
@@ -9761,9 +10820,9 @@ var ShapePreview = (0, import_react43.memo)(function ShapePreview2({
9761
10820
  };
9762
10821
  switch (shapeType) {
9763
10822
  case "rect":
9764
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("rect", { x, y, width, height, ...commonProps });
10823
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("rect", { x, y, width, height, ...commonProps });
9765
10824
  case "circle":
9766
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10825
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9767
10826
  "ellipse",
9768
10827
  {
9769
10828
  cx: x + width / 2,
@@ -9774,7 +10833,7 @@ var ShapePreview = (0, import_react43.memo)(function ShapePreview2({
9774
10833
  }
9775
10834
  );
9776
10835
  case "line":
9777
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10836
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9778
10837
  "line",
9779
10838
  {
9780
10839
  x1: startPoint.x * scale,
@@ -9796,8 +10855,8 @@ var ShapePreview = (0, import_react43.memo)(function ShapePreview2({
9796
10855
  const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
9797
10856
  const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
9798
10857
  const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
9799
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("g", { children: [
9800
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10858
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("g", { children: [
10859
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
9801
10860
  "line",
9802
10861
  {
9803
10862
  x1: startPoint.x * scale,
@@ -9807,8 +10866,8 @@ var ShapePreview = (0, import_react43.memo)(function ShapePreview2({
9807
10866
  ...commonProps
9808
10867
  }
9809
10868
  ),
9810
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
9811
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
10869
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
10870
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
9812
10871
  ] });
9813
10872
  default:
9814
10873
  return null;
@@ -9816,10 +10875,10 @@ var ShapePreview = (0, import_react43.memo)(function ShapePreview2({
9816
10875
  });
9817
10876
 
9818
10877
  // src/components/Annotations/QuickNoteButton.tsx
9819
- var import_react44 = require("react");
10878
+ var import_react45 = require("react");
9820
10879
  init_utils();
9821
- var import_jsx_runtime30 = require("react/jsx-runtime");
9822
- var QuickNoteButton = (0, import_react44.memo)(function QuickNoteButton2({
10880
+ var import_jsx_runtime31 = require("react/jsx-runtime");
10881
+ var QuickNoteButton = (0, import_react45.memo)(function QuickNoteButton2({
9823
10882
  pageNumber,
9824
10883
  scale,
9825
10884
  position = "top-right",
@@ -9827,8 +10886,8 @@ var QuickNoteButton = (0, import_react44.memo)(function QuickNoteButton2({
9827
10886
  className,
9828
10887
  visible = true
9829
10888
  }) {
9830
- const [isHovered, setIsHovered] = (0, import_react44.useState)(false);
9831
- const handleClick = (0, import_react44.useCallback)(
10889
+ const [isHovered, setIsHovered] = (0, import_react45.useState)(false);
10890
+ const handleClick = (0, import_react45.useCallback)(
9832
10891
  (e) => {
9833
10892
  e.stopPropagation();
9834
10893
  const x = position === "top-right" ? 80 : 80;
@@ -9840,7 +10899,7 @@ var QuickNoteButton = (0, import_react44.memo)(function QuickNoteButton2({
9840
10899
  if (!visible) {
9841
10900
  return null;
9842
10901
  }
9843
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
10902
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
9844
10903
  "button",
9845
10904
  {
9846
10905
  onClick: handleClick,
@@ -9862,7 +10921,7 @@ var QuickNoteButton = (0, import_react44.memo)(function QuickNoteButton2({
9862
10921
  ),
9863
10922
  title: "Add quick note",
9864
10923
  "aria-label": "Add quick note",
9865
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
10924
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
9866
10925
  "svg",
9867
10926
  {
9868
10927
  className: "w-4 h-4 text-yellow-900",
@@ -9870,7 +10929,7 @@ var QuickNoteButton = (0, import_react44.memo)(function QuickNoteButton2({
9870
10929
  viewBox: "0 0 24 24",
9871
10930
  stroke: "currentColor",
9872
10931
  strokeWidth: 2,
9873
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4v16m8-8H4" })
10932
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4v16m8-8H4" })
9874
10933
  }
9875
10934
  )
9876
10935
  }
@@ -9878,10 +10937,10 @@ var QuickNoteButton = (0, import_react44.memo)(function QuickNoteButton2({
9878
10937
  });
9879
10938
 
9880
10939
  // src/components/Annotations/QuickNotePopover.tsx
9881
- var import_react45 = require("react");
10940
+ var import_react46 = require("react");
9882
10941
  init_utils();
9883
- var import_jsx_runtime31 = require("react/jsx-runtime");
9884
- var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
10942
+ var import_jsx_runtime32 = require("react/jsx-runtime");
10943
+ var QuickNotePopover = (0, import_react46.memo)(function QuickNotePopover2({
9885
10944
  visible,
9886
10945
  position,
9887
10946
  initialContent = "",
@@ -9890,21 +10949,21 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
9890
10949
  onCancel,
9891
10950
  className
9892
10951
  }) {
9893
- const [content, setContent] = (0, import_react45.useState)(initialContent);
9894
- const textareaRef = (0, import_react45.useRef)(null);
9895
- const popoverRef = (0, import_react45.useRef)(null);
9896
- const [adjustedPosition, setAdjustedPosition] = (0, import_react45.useState)(position);
9897
- (0, import_react45.useEffect)(() => {
10952
+ const [content, setContent] = (0, import_react46.useState)(initialContent);
10953
+ const textareaRef = (0, import_react46.useRef)(null);
10954
+ const popoverRef = (0, import_react46.useRef)(null);
10955
+ const [adjustedPosition, setAdjustedPosition] = (0, import_react46.useState)(position);
10956
+ (0, import_react46.useEffect)(() => {
9898
10957
  if (visible && textareaRef.current) {
9899
10958
  textareaRef.current.focus();
9900
10959
  }
9901
10960
  }, [visible]);
9902
- (0, import_react45.useEffect)(() => {
10961
+ (0, import_react46.useEffect)(() => {
9903
10962
  if (visible) {
9904
10963
  setContent(initialContent);
9905
10964
  }
9906
10965
  }, [visible, initialContent]);
9907
- (0, import_react45.useEffect)(() => {
10966
+ (0, import_react46.useEffect)(() => {
9908
10967
  if (!visible || !popoverRef.current) return;
9909
10968
  const rect = popoverRef.current.getBoundingClientRect();
9910
10969
  const padding = 10;
@@ -9923,14 +10982,14 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
9923
10982
  }
9924
10983
  setAdjustedPosition({ x, y });
9925
10984
  }, [position, visible]);
9926
- const handleSave = (0, import_react45.useCallback)(() => {
10985
+ const handleSave = (0, import_react46.useCallback)(() => {
9927
10986
  if (content.trim()) {
9928
10987
  onSave(content.trim());
9929
10988
  } else {
9930
10989
  onCancel();
9931
10990
  }
9932
10991
  }, [content, onSave, onCancel]);
9933
- const handleKeyDown = (0, import_react45.useCallback)(
10992
+ const handleKeyDown = (0, import_react46.useCallback)(
9934
10993
  (e) => {
9935
10994
  if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
9936
10995
  e.preventDefault();
@@ -9945,7 +11004,7 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
9945
11004
  if (!visible) {
9946
11005
  return null;
9947
11006
  }
9948
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
11007
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
9949
11008
  "div",
9950
11009
  {
9951
11010
  ref: popoverRef,
@@ -9964,15 +11023,15 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
9964
11023
  top: adjustedPosition.y
9965
11024
  },
9966
11025
  children: [
9967
- agentLastStatement && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "mb-2 p-2 bg-blue-50 dark:bg-blue-900/50 rounded text-xs text-blue-600 dark:text-blue-300 border border-blue-100 dark:border-blue-800", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex items-start gap-1", children: [
9968
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("svg", { className: "w-3 h-3 mt-0.5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }),
9969
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "line-clamp-2", children: [
11026
+ agentLastStatement && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "mb-2 p-2 bg-blue-50 dark:bg-blue-900/50 rounded text-xs text-blue-600 dark:text-blue-300 border border-blue-100 dark:border-blue-800", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-start gap-1", children: [
11027
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("svg", { className: "w-3 h-3 mt-0.5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }),
11028
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "line-clamp-2", children: [
9970
11029
  "AI discussed: \u201C",
9971
11030
  agentLastStatement,
9972
11031
  "\u201D"
9973
11032
  ] })
9974
11033
  ] }) }),
9975
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
11034
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
9976
11035
  "textarea",
9977
11036
  {
9978
11037
  ref: textareaRef,
@@ -9991,13 +11050,13 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
9991
11050
  )
9992
11051
  }
9993
11052
  ),
9994
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex items-center justify-between mt-2", children: [
9995
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
11053
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between mt-2", children: [
11054
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
9996
11055
  navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
9997
11056
  "+Enter to save"
9998
11057
  ] }),
9999
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex gap-2", children: [
10000
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
11058
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex gap-2", children: [
11059
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
10001
11060
  "button",
10002
11061
  {
10003
11062
  onClick: onCancel,
@@ -10010,7 +11069,7 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
10010
11069
  children: "Cancel"
10011
11070
  }
10012
11071
  ),
10013
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
11072
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
10014
11073
  "button",
10015
11074
  {
10016
11075
  onClick: handleSave,
@@ -10033,10 +11092,10 @@ var QuickNotePopover = (0, import_react45.memo)(function QuickNotePopover2({
10033
11092
  });
10034
11093
 
10035
11094
  // src/components/AskAbout/AskAboutOverlay.tsx
10036
- var import_react46 = require("react");
11095
+ var import_react47 = require("react");
10037
11096
  init_utils();
10038
- var import_jsx_runtime32 = require("react/jsx-runtime");
10039
- var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
11097
+ var import_jsx_runtime33 = require("react/jsx-runtime");
11098
+ var AskAboutOverlay = (0, import_react47.memo)(function AskAboutOverlay2({
10040
11099
  visible,
10041
11100
  progress,
10042
11101
  position,
@@ -10050,7 +11109,7 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10050
11109
  const radius = (size - strokeWidth) / 2;
10051
11110
  const circumference = 2 * Math.PI * radius;
10052
11111
  const strokeDashoffset = circumference * (1 - progress);
10053
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
11112
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
10054
11113
  "div",
10055
11114
  {
10056
11115
  className: cn(
@@ -10063,7 +11122,7 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10063
11122
  top: position.y - size / 2
10064
11123
  },
10065
11124
  children: [
10066
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
11125
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
10067
11126
  "svg",
10068
11127
  {
10069
11128
  width: size,
@@ -10071,7 +11130,7 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10071
11130
  viewBox: `0 0 ${size} ${size}`,
10072
11131
  className: "transform -rotate-90",
10073
11132
  children: [
10074
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
11133
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
10075
11134
  "circle",
10076
11135
  {
10077
11136
  cx: size / 2,
@@ -10082,7 +11141,7 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10082
11141
  strokeWidth
10083
11142
  }
10084
11143
  ),
10085
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
11144
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
10086
11145
  "circle",
10087
11146
  {
10088
11147
  cx: size / 2,
@@ -10100,12 +11159,12 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10100
11159
  ]
10101
11160
  }
10102
11161
  ),
10103
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
11162
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
10104
11163
  "div",
10105
11164
  {
10106
11165
  className: "absolute inset-0 flex items-center justify-center",
10107
11166
  style: { color: progress >= 1 ? "#22c55e" : "white" },
10108
- children: progress >= 1 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
11167
+ children: progress >= 1 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
10109
11168
  "svg",
10110
11169
  {
10111
11170
  width: "24",
@@ -10116,9 +11175,9 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10116
11175
  strokeWidth: "2",
10117
11176
  strokeLinecap: "round",
10118
11177
  strokeLinejoin: "round",
10119
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("polyline", { points: "20 6 9 17 4 12" })
11178
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("polyline", { points: "20 6 9 17 4 12" })
10120
11179
  }
10121
- ) : /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
11180
+ ) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
10122
11181
  "svg",
10123
11182
  {
10124
11183
  width: "20",
@@ -10130,9 +11189,9 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10130
11189
  strokeLinecap: "round",
10131
11190
  strokeLinejoin: "round",
10132
11191
  children: [
10133
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
10134
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
10135
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
11192
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
11193
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
11194
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
10136
11195
  ]
10137
11196
  }
10138
11197
  )
@@ -10144,10 +11203,10 @@ var AskAboutOverlay = (0, import_react46.memo)(function AskAboutOverlay2({
10144
11203
  });
10145
11204
 
10146
11205
  // src/components/AskAbout/AskAboutTrigger.tsx
10147
- var import_react47 = require("react");
11206
+ var import_react48 = require("react");
10148
11207
  init_utils();
10149
- var import_jsx_runtime33 = require("react/jsx-runtime");
10150
- var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
11208
+ var import_jsx_runtime34 = require("react/jsx-runtime");
11209
+ var AskAboutTrigger = (0, import_react48.memo)(function AskAboutTrigger2({
10151
11210
  position,
10152
11211
  onConfirm,
10153
11212
  onCancel,
@@ -10155,9 +11214,9 @@ var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
10155
11214
  autoHideDelay = 5e3,
10156
11215
  className
10157
11216
  }) {
10158
- const [adjustedPosition, setAdjustedPosition] = (0, import_react47.useState)(position);
10159
- const triggerRef = (0, import_react47.useRef)(null);
10160
- (0, import_react47.useEffect)(() => {
11217
+ const [adjustedPosition, setAdjustedPosition] = (0, import_react48.useState)(position);
11218
+ const triggerRef = (0, import_react48.useRef)(null);
11219
+ (0, import_react48.useEffect)(() => {
10161
11220
  if (!visible || !triggerRef.current) return;
10162
11221
  const rect = triggerRef.current.getBoundingClientRect();
10163
11222
  const padding = 10;
@@ -10173,19 +11232,19 @@ var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
10173
11232
  }
10174
11233
  setAdjustedPosition({ x, y });
10175
11234
  }, [position, visible]);
10176
- (0, import_react47.useEffect)(() => {
11235
+ (0, import_react48.useEffect)(() => {
10177
11236
  if (!visible || autoHideDelay === 0) return;
10178
11237
  const timer = setTimeout(onCancel, autoHideDelay);
10179
11238
  return () => clearTimeout(timer);
10180
11239
  }, [visible, autoHideDelay, onCancel]);
10181
- const handleConfirm = (0, import_react47.useCallback)(
11240
+ const handleConfirm = (0, import_react48.useCallback)(
10182
11241
  (e) => {
10183
11242
  e.stopPropagation();
10184
11243
  onConfirm();
10185
11244
  },
10186
11245
  [onConfirm]
10187
11246
  );
10188
- const handleCancel = (0, import_react47.useCallback)(
11247
+ const handleCancel = (0, import_react48.useCallback)(
10189
11248
  (e) => {
10190
11249
  e.stopPropagation();
10191
11250
  onCancel();
@@ -10195,7 +11254,7 @@ var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
10195
11254
  if (!visible) {
10196
11255
  return null;
10197
11256
  }
10198
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
11257
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
10199
11258
  "div",
10200
11259
  {
10201
11260
  ref: triggerRef,
@@ -10214,8 +11273,8 @@ var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
10214
11273
  transform: "translate(-50%, 0)"
10215
11274
  },
10216
11275
  children: [
10217
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-sm text-gray-600 dark:text-gray-300 px-2", children: "Ask about this?" }),
10218
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
11276
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-sm text-gray-600 dark:text-gray-300 px-2", children: "Ask about this?" }),
11277
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
10219
11278
  "button",
10220
11279
  {
10221
11280
  onClick: handleConfirm,
@@ -10228,7 +11287,7 @@ var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
10228
11287
  children: "Ask"
10229
11288
  }
10230
11289
  ),
10231
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
11290
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
10232
11291
  "button",
10233
11292
  {
10234
11293
  onClick: handleCancel,
@@ -10248,11 +11307,11 @@ var AskAboutTrigger = (0, import_react47.memo)(function AskAboutTrigger2({
10248
11307
  });
10249
11308
 
10250
11309
  // src/components/Minimap/Minimap.tsx
10251
- var import_react48 = require("react");
11310
+ var import_react49 = require("react");
10252
11311
  init_hooks();
10253
11312
  init_utils();
10254
- var import_jsx_runtime34 = require("react/jsx-runtime");
10255
- var PageIndicator = (0, import_react48.memo)(function PageIndicator2({
11313
+ var import_jsx_runtime35 = require("react/jsx-runtime");
11314
+ var PageIndicator = (0, import_react49.memo)(function PageIndicator2({
10256
11315
  pageNumber,
10257
11316
  status,
10258
11317
  isBookmarked,
@@ -10266,7 +11325,7 @@ var PageIndicator = (0, import_react48.memo)(function PageIndicator2({
10266
11325
  if (status === "visited") return "bg-green-400";
10267
11326
  return "bg-gray-200 dark:bg-gray-700";
10268
11327
  };
10269
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
11328
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
10270
11329
  "button",
10271
11330
  {
10272
11331
  onClick,
@@ -10282,13 +11341,13 @@ var PageIndicator = (0, import_react48.memo)(function PageIndicator2({
10282
11341
  title: `Page ${pageNumber}${isBookmarked ? " (bookmarked)" : ""}`,
10283
11342
  "aria-label": `Go to page ${pageNumber}`,
10284
11343
  children: [
10285
- isBookmarked && !compact && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full border border-white" }),
10286
- showNumber && !compact && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-medium text-white", children: pageNumber })
11344
+ isBookmarked && !compact && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full border border-white" }),
11345
+ showNumber && !compact && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-medium text-white", children: pageNumber })
10287
11346
  ]
10288
11347
  }
10289
11348
  );
10290
11349
  });
10291
- var Minimap = (0, import_react48.memo)(function Minimap2({
11350
+ var Minimap = (0, import_react49.memo)(function Minimap2({
10292
11351
  variant = "sidebar",
10293
11352
  floatingPosition = "right",
10294
11353
  maxHeight = 300,
@@ -10301,18 +11360,18 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10301
11360
  const currentPage = useViewerStore((s) => s.currentPage);
10302
11361
  const numPages = useViewerStore((s) => s.numPages);
10303
11362
  const goToPage = useViewerStore((s) => s.goToPage);
10304
- const bookmarkedPages = (0, import_react48.useMemo)(() => {
11363
+ const bookmarkedPages = (0, import_react49.useMemo)(() => {
10305
11364
  return new Set(bookmarks.map((b) => b.pageNumber));
10306
11365
  }, [bookmarks]);
10307
11366
  const compact = numPages > 50;
10308
- const handlePageClick = (0, import_react48.useCallback)(
11367
+ const handlePageClick = (0, import_react49.useCallback)(
10309
11368
  (pageNumber) => {
10310
11369
  goToPage(pageNumber);
10311
11370
  onPageClick?.(pageNumber);
10312
11371
  },
10313
11372
  [goToPage, onPageClick]
10314
11373
  );
10315
- const getPageStatus = (0, import_react48.useCallback)(
11374
+ const getPageStatus = (0, import_react49.useCallback)(
10316
11375
  (pageNumber) => {
10317
11376
  if (pageNumber === currentPage) return "current";
10318
11377
  if (bookmarkedPages.has(pageNumber)) return "bookmarked";
@@ -10321,11 +11380,11 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10321
11380
  },
10322
11381
  [currentPage, visitedPages, bookmarkedPages]
10323
11382
  );
10324
- const pageIndicators = (0, import_react48.useMemo)(() => {
11383
+ const pageIndicators = (0, import_react49.useMemo)(() => {
10325
11384
  const pages = [];
10326
11385
  for (let i = 1; i <= numPages; i++) {
10327
11386
  pages.push(
10328
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
11387
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
10329
11388
  PageIndicator,
10330
11389
  {
10331
11390
  pageNumber: i,
@@ -10346,16 +11405,16 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10346
11405
  if (numPages === 0) {
10347
11406
  return null;
10348
11407
  }
10349
- const content = /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
10350
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "mb-3", children: [
10351
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1", children: [
10352
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: "Progress" }),
10353
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { children: [
11408
+ const content = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
11409
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mb-3", children: [
11410
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1", children: [
11411
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Progress" }),
11412
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
10354
11413
  progressPercentage,
10355
11414
  "%"
10356
11415
  ] })
10357
11416
  ] }),
10358
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
11417
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
10359
11418
  "div",
10360
11419
  {
10361
11420
  className: "h-full bg-green-500 rounded-full transition-all duration-300",
@@ -10363,7 +11422,7 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10363
11422
  }
10364
11423
  ) })
10365
11424
  ] }),
10366
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
11425
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
10367
11426
  "div",
10368
11427
  {
10369
11428
  className: cn(
@@ -10374,21 +11433,21 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10374
11433
  children: pageIndicators
10375
11434
  }
10376
11435
  ),
10377
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "mt-3 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
10378
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-1", children: [
10379
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "w-2 h-2 rounded-sm bg-blue-500" }),
10380
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: "Current" })
11436
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "mt-3 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
11437
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-1", children: [
11438
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "w-2 h-2 rounded-sm bg-blue-500" }),
11439
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Current" })
10381
11440
  ] }),
10382
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-1", children: [
10383
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "w-2 h-2 rounded-sm bg-green-400" }),
10384
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: "Visited" })
11441
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-1", children: [
11442
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "w-2 h-2 rounded-sm bg-green-400" }),
11443
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Visited" })
10385
11444
  ] }),
10386
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-1", children: [
10387
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "w-2 h-2 rounded-sm bg-yellow-400" }),
10388
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: "Bookmarked" })
11445
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-1", children: [
11446
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "w-2 h-2 rounded-sm bg-yellow-400" }),
11447
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Bookmarked" })
10389
11448
  ] })
10390
11449
  ] }) }),
10391
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
11450
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
10392
11451
  visitedCount,
10393
11452
  " of ",
10394
11453
  numPages,
@@ -10396,7 +11455,7 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10396
11455
  ] })
10397
11456
  ] });
10398
11457
  if (variant === "floating") {
10399
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
11458
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
10400
11459
  "div",
10401
11460
  {
10402
11461
  className: cn(
@@ -10412,13 +11471,13 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10412
11471
  ),
10413
11472
  style: { maxHeight },
10414
11473
  children: [
10415
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 mb-2", children: "Reading Progress" }),
11474
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 mb-2", children: "Reading Progress" }),
10416
11475
  content
10417
11476
  ]
10418
11477
  }
10419
11478
  );
10420
11479
  }
10421
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
11480
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
10422
11481
  "div",
10423
11482
  {
10424
11483
  className: cn(
@@ -10432,11 +11491,251 @@ var Minimap = (0, import_react48.memo)(function Minimap2({
10432
11491
  );
10433
11492
  });
10434
11493
 
11494
+ // src/components/index.ts
11495
+ init_FloatingZoomControls2();
11496
+
11497
+ // src/components/PDFThumbnailNav/PDFThumbnailNav.tsx
11498
+ var import_react50 = require("react");
11499
+ init_hooks();
11500
+ init_utils();
11501
+ var import_jsx_runtime36 = require("react/jsx-runtime");
11502
+ var DEFAULT_WIDTH = 612;
11503
+ var DEFAULT_HEIGHT = 792;
11504
+ var PDFThumbnailNav = (0, import_react50.memo)(function PDFThumbnailNav2({
11505
+ thumbnailScale = 0.15,
11506
+ orientation = "vertical",
11507
+ maxVisible = 10,
11508
+ className,
11509
+ onThumbnailClick,
11510
+ gap = 8,
11511
+ showPageNumbers = true
11512
+ }) {
11513
+ const { document: document2, numPages, currentPage } = usePDFViewer();
11514
+ const { viewerStore } = usePDFViewerStores();
11515
+ const containerRef = (0, import_react50.useRef)(null);
11516
+ const [thumbnails, setThumbnails] = (0, import_react50.useState)(/* @__PURE__ */ new Map());
11517
+ const [visibleRange, setVisibleRange] = (0, import_react50.useState)({ start: 1, end: maxVisible });
11518
+ const renderQueueRef = (0, import_react50.useRef)(/* @__PURE__ */ new Set());
11519
+ const pageCache = (0, import_react50.useRef)(/* @__PURE__ */ new Map());
11520
+ const thumbnailWidth = Math.floor(DEFAULT_WIDTH * thumbnailScale);
11521
+ const thumbnailHeight = Math.floor(DEFAULT_HEIGHT * thumbnailScale);
11522
+ const updateVisibleRange = (0, import_react50.useCallback)(() => {
11523
+ if (!containerRef.current || numPages === 0) return;
11524
+ const container = containerRef.current;
11525
+ const isHorizontal2 = orientation === "horizontal";
11526
+ const scrollPosition = isHorizontal2 ? container.scrollLeft : container.scrollTop;
11527
+ const viewportSize = isHorizontal2 ? container.clientWidth : container.clientHeight;
11528
+ const itemSize = (isHorizontal2 ? thumbnailWidth : thumbnailHeight) + gap;
11529
+ const firstVisible = Math.max(1, Math.floor(scrollPosition / itemSize) + 1);
11530
+ const visibleCount = Math.ceil(viewportSize / itemSize) + 2;
11531
+ const lastVisible = Math.min(numPages, firstVisible + visibleCount);
11532
+ setVisibleRange({ start: firstVisible, end: lastVisible });
11533
+ }, [numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
11534
+ (0, import_react50.useEffect)(() => {
11535
+ const container = containerRef.current;
11536
+ if (!container) return;
11537
+ const handleScroll = () => {
11538
+ requestAnimationFrame(updateVisibleRange);
11539
+ };
11540
+ container.addEventListener("scroll", handleScroll, { passive: true });
11541
+ updateVisibleRange();
11542
+ return () => container.removeEventListener("scroll", handleScroll);
11543
+ }, [updateVisibleRange]);
11544
+ (0, import_react50.useEffect)(() => {
11545
+ if (!document2) {
11546
+ setThumbnails(/* @__PURE__ */ new Map());
11547
+ pageCache.current.clear();
11548
+ return;
11549
+ }
11550
+ const renderThumbnails = async () => {
11551
+ const newThumbnails = new Map(thumbnails);
11552
+ const pagesToRender = [];
11553
+ for (let i = visibleRange.start; i <= visibleRange.end; i++) {
11554
+ if (!newThumbnails.has(i) && !renderQueueRef.current.has(i)) {
11555
+ pagesToRender.push(i);
11556
+ renderQueueRef.current.add(i);
11557
+ }
11558
+ }
11559
+ for (const pageNum of pagesToRender) {
11560
+ try {
11561
+ let page = pageCache.current.get(pageNum);
11562
+ if (!page) {
11563
+ page = await document2.getPage(pageNum);
11564
+ pageCache.current.set(pageNum, page);
11565
+ }
11566
+ const viewport = page.getViewport({ scale: thumbnailScale });
11567
+ const canvas = window.document.createElement("canvas");
11568
+ canvas.width = Math.floor(viewport.width);
11569
+ canvas.height = Math.floor(viewport.height);
11570
+ const ctx = canvas.getContext("2d");
11571
+ if (ctx) {
11572
+ await page.render({
11573
+ canvasContext: ctx,
11574
+ viewport
11575
+ }).promise;
11576
+ newThumbnails.set(pageNum, {
11577
+ pageNumber: pageNum,
11578
+ canvas,
11579
+ width: canvas.width,
11580
+ height: canvas.height
11581
+ });
11582
+ }
11583
+ } catch (error) {
11584
+ console.error(`Failed to render thumbnail for page ${pageNum}:`, error);
11585
+ } finally {
11586
+ renderQueueRef.current.delete(pageNum);
11587
+ }
11588
+ }
11589
+ if (pagesToRender.length > 0) {
11590
+ setThumbnails(newThumbnails);
11591
+ }
11592
+ };
11593
+ renderThumbnails();
11594
+ }, [document2, visibleRange, thumbnailScale, thumbnails]);
11595
+ (0, import_react50.useEffect)(() => {
11596
+ if (!containerRef.current || numPages === 0) return;
11597
+ const container = containerRef.current;
11598
+ const isHorizontal2 = orientation === "horizontal";
11599
+ const itemSize = (isHorizontal2 ? thumbnailWidth : thumbnailHeight) + gap;
11600
+ const targetPosition = (currentPage - 1) * itemSize;
11601
+ const scrollPosition = isHorizontal2 ? container.scrollLeft : container.scrollTop;
11602
+ const viewportSize = isHorizontal2 ? container.clientWidth : container.clientHeight;
11603
+ if (targetPosition < scrollPosition || targetPosition + itemSize > scrollPosition + viewportSize) {
11604
+ const targetScroll = targetPosition - (viewportSize - itemSize) / 2;
11605
+ container.scrollTo({
11606
+ [isHorizontal2 ? "left" : "top"]: Math.max(0, targetScroll),
11607
+ behavior: "smooth"
11608
+ });
11609
+ }
11610
+ }, [currentPage, numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
11611
+ const handleThumbnailClick = (0, import_react50.useCallback)((pageNum) => {
11612
+ onThumbnailClick?.(pageNum);
11613
+ viewerStore.getState().requestScrollToPage(pageNum, "smooth");
11614
+ }, [onThumbnailClick, viewerStore]);
11615
+ if (!document2 || numPages === 0) {
11616
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
11617
+ "div",
11618
+ {
11619
+ className: cn(
11620
+ "pdf-thumbnail-nav",
11621
+ "flex items-center justify-center",
11622
+ "bg-gray-100 dark:bg-gray-800",
11623
+ "text-gray-500 dark:text-gray-400",
11624
+ "text-sm",
11625
+ className
11626
+ ),
11627
+ style: {
11628
+ width: orientation === "vertical" ? thumbnailWidth + 24 : "100%",
11629
+ height: orientation === "horizontal" ? thumbnailHeight + 40 : "100%"
11630
+ },
11631
+ children: "No document"
11632
+ }
11633
+ );
11634
+ }
11635
+ const isHorizontal = orientation === "horizontal";
11636
+ const totalSize = numPages * ((isHorizontal ? thumbnailWidth : thumbnailHeight) + gap) - gap;
11637
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
11638
+ "div",
11639
+ {
11640
+ ref: containerRef,
11641
+ className: cn(
11642
+ "pdf-thumbnail-nav",
11643
+ "overflow-auto",
11644
+ "bg-gray-100 dark:bg-gray-800",
11645
+ isHorizontal ? "flex-row" : "flex-col",
11646
+ className
11647
+ ),
11648
+ style: {
11649
+ ...isHorizontal ? { overflowX: "auto", overflowY: "hidden" } : { overflowX: "hidden", overflowY: "auto" }
11650
+ },
11651
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
11652
+ "div",
11653
+ {
11654
+ className: cn(
11655
+ "relative",
11656
+ isHorizontal ? "flex flex-row items-center" : "flex flex-col items-center"
11657
+ ),
11658
+ style: {
11659
+ [isHorizontal ? "width" : "height"]: totalSize,
11660
+ [isHorizontal ? "minWidth" : "minHeight"]: totalSize,
11661
+ padding: gap / 2,
11662
+ gap
11663
+ },
11664
+ children: Array.from({ length: numPages }, (_, i) => i + 1).map((pageNum) => {
11665
+ const thumbnail = thumbnails.get(pageNum);
11666
+ const isActive = pageNum === currentPage;
11667
+ const isVisible = pageNum >= visibleRange.start && pageNum <= visibleRange.end;
11668
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
11669
+ "div",
11670
+ {
11671
+ className: cn(
11672
+ "pdf-thumbnail",
11673
+ "flex-shrink-0 cursor-pointer transition-all duration-200",
11674
+ "border-2 rounded shadow-sm hover:shadow-md",
11675
+ isActive ? "border-blue-500 ring-2 ring-blue-200 dark:ring-blue-800" : "border-gray-300 dark:border-gray-600 hover:border-blue-400"
11676
+ ),
11677
+ style: {
11678
+ width: thumbnailWidth,
11679
+ height: thumbnailHeight + (showPageNumbers ? 24 : 0)
11680
+ },
11681
+ onClick: () => handleThumbnailClick(pageNum),
11682
+ role: "button",
11683
+ tabIndex: 0,
11684
+ "aria-label": `Go to page ${pageNum}`,
11685
+ "aria-current": isActive ? "page" : void 0,
11686
+ onKeyDown: (e) => {
11687
+ if (e.key === "Enter" || e.key === " ") {
11688
+ e.preventDefault();
11689
+ handleThumbnailClick(pageNum);
11690
+ }
11691
+ },
11692
+ children: [
11693
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
11694
+ "div",
11695
+ {
11696
+ className: "relative bg-white dark:bg-gray-700",
11697
+ style: {
11698
+ width: thumbnailWidth,
11699
+ height: thumbnailHeight
11700
+ },
11701
+ children: isVisible && thumbnail?.canvas ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
11702
+ "img",
11703
+ {
11704
+ src: thumbnail.canvas.toDataURL(),
11705
+ alt: `Page ${pageNum}`,
11706
+ className: "w-full h-full object-contain",
11707
+ loading: "lazy"
11708
+ }
11709
+ ) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-xs", children: pageNum })
11710
+ }
11711
+ ),
11712
+ showPageNumbers && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
11713
+ "div",
11714
+ {
11715
+ className: cn(
11716
+ "text-center text-xs py-1",
11717
+ "bg-gray-50 dark:bg-gray-700",
11718
+ isActive ? "text-blue-600 dark:text-blue-400 font-medium" : "text-gray-600 dark:text-gray-400"
11719
+ ),
11720
+ children: pageNum
11721
+ }
11722
+ )
11723
+ ]
11724
+ },
11725
+ pageNum
11726
+ );
11727
+ })
11728
+ }
11729
+ )
11730
+ }
11731
+ );
11732
+ });
11733
+
10435
11734
  // src/components/ErrorBoundary/PDFErrorBoundary.tsx
10436
- var import_react49 = require("react");
11735
+ var import_react51 = require("react");
10437
11736
  init_utils();
10438
- var import_jsx_runtime35 = require("react/jsx-runtime");
10439
- var PDFErrorBoundary = class extends import_react49.Component {
11737
+ var import_jsx_runtime37 = require("react/jsx-runtime");
11738
+ var PDFErrorBoundary = class extends import_react51.Component {
10440
11739
  constructor(props) {
10441
11740
  super(props);
10442
11741
  this.handleReset = () => {
@@ -10463,7 +11762,7 @@ var PDFErrorBoundary = class extends import_react49.Component {
10463
11762
  return fallback;
10464
11763
  }
10465
11764
  if (showDefaultUI) {
10466
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
11765
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
10467
11766
  DefaultErrorUI,
10468
11767
  {
10469
11768
  error,
@@ -10482,7 +11781,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10482
11781
  const isNetworkError = error.message.includes("fetch") || error.message.includes("network") || error.message.includes("Failed to load");
10483
11782
  let title = "Something went wrong";
10484
11783
  let description = error.message;
10485
- let icon = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
11784
+ let icon = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
10486
11785
  "path",
10487
11786
  {
10488
11787
  strokeLinecap: "round",
@@ -10494,7 +11793,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10494
11793
  if (isPDFError) {
10495
11794
  title = "Unable to load PDF";
10496
11795
  description = "The PDF file could not be loaded. It may be corrupted or in an unsupported format.";
10497
- icon = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
11796
+ icon = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
10498
11797
  "path",
10499
11798
  {
10500
11799
  strokeLinecap: "round",
@@ -10506,7 +11805,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10506
11805
  } else if (isNetworkError) {
10507
11806
  title = "Network error";
10508
11807
  description = "Unable to fetch the PDF file. Please check your internet connection and try again.";
10509
- icon = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
11808
+ icon = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
10510
11809
  "path",
10511
11810
  {
10512
11811
  strokeLinecap: "round",
@@ -10516,7 +11815,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10516
11815
  }
10517
11816
  ) });
10518
11817
  }
10519
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
11818
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
10520
11819
  "div",
10521
11820
  {
10522
11821
  className: cn(
@@ -10529,14 +11828,14 @@ function DefaultErrorUI({ error, onReset, className }) {
10529
11828
  ),
10530
11829
  children: [
10531
11830
  icon,
10532
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
10533
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
10534
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("details", { className: "mt-4 text-left max-w-md w-full", children: [
10535
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("summary", { className: "cursor-pointer text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200", children: "Technical details" }),
10536
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
11831
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
11832
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
11833
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("details", { className: "mt-4 text-left max-w-md w-full", children: [
11834
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("summary", { className: "cursor-pointer text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200", children: "Technical details" }),
11835
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
10537
11836
  ] }),
10538
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "mt-6 flex gap-3", children: [
10539
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
11837
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "mt-6 flex gap-3", children: [
11838
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
10540
11839
  "button",
10541
11840
  {
10542
11841
  onClick: onReset,
@@ -10550,7 +11849,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10550
11849
  children: "Try again"
10551
11850
  }
10552
11851
  ),
10553
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
11852
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
10554
11853
  "button",
10555
11854
  {
10556
11855
  onClick: () => window.location.reload(),
@@ -10570,7 +11869,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10570
11869
  );
10571
11870
  }
10572
11871
  function withErrorBoundary({ component, ...props }) {
10573
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(PDFErrorBoundary, { ...props, children: component });
11872
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PDFErrorBoundary, { ...props, children: component });
10574
11873
  }
10575
11874
 
10576
11875
  // src/index.ts
@@ -10594,6 +11893,7 @@ init_utils();
10594
11893
  DocumentContainer,
10595
11894
  DrawingCanvas,
10596
11895
  DualPageContainer,
11896
+ FloatingZoomControls,
10597
11897
  FocusRegionLayer,
10598
11898
  HighlightLayer,
10599
11899
  HighlightPopover,
@@ -10604,6 +11904,7 @@ init_utils();
10604
11904
  OutlinePanel,
10605
11905
  PDFErrorBoundary,
10606
11906
  PDFPage,
11907
+ PDFThumbnailNav,
10607
11908
  PDFViewer,
10608
11909
  PDFViewerClient,
10609
11910
  PDFViewerContext,
@@ -10622,9 +11923,11 @@ init_utils();
10622
11923
  ThumbnailPanel,
10623
11924
  Toolbar,
10624
11925
  VirtualizedDocumentContainer,
11926
+ applyRotation,
10625
11927
  clearHighlights,
10626
11928
  clearStudentData,
10627
11929
  cn,
11930
+ countTextOnPage,
10628
11931
  createAgentAPI,
10629
11932
  createAgentStore,
10630
11933
  createAnnotationStore,
@@ -10633,6 +11936,7 @@ init_utils();
10633
11936
  createSearchStore,
10634
11937
  createStudentStore,
10635
11938
  createViewerStore,
11939
+ doRectsIntersect,
10636
11940
  downloadAnnotationsAsJSON,
10637
11941
  downloadAnnotationsAsMarkdown,
10638
11942
  downloadFile,
@@ -10640,25 +11944,39 @@ init_utils();
10640
11944
  exportAnnotationsAsMarkdown,
10641
11945
  exportHighlightsAsJSON,
10642
11946
  exportHighlightsAsMarkdown,
11947
+ extractPageText,
11948
+ findTextInDocument,
11949
+ findTextOnPage,
10643
11950
  generateDocumentId,
10644
11951
  getAllDocumentIds,
10645
11952
  getAllStudentDataDocumentIds,
10646
11953
  getMetadata,
10647
11954
  getOutline,
10648
11955
  getPage,
11956
+ getPageText,
10649
11957
  getPageTextContent,
10650
11958
  getPluginManager,
11959
+ getRectIntersection,
11960
+ getRotatedDimensions,
10651
11961
  getStorageStats,
10652
11962
  importHighlightsFromJSON,
10653
11963
  initializePDFJS,
10654
11964
  isPDFJSInitialized,
11965
+ isPointInRect,
10655
11966
  loadDocument,
10656
11967
  loadHighlights,
10657
11968
  loadStudentData,
11969
+ mergeAdjacentRects,
11970
+ pdfToPercent,
11971
+ pdfToViewport,
10658
11972
  pdfjsLib,
11973
+ percentToPDF,
11974
+ percentToViewport,
10659
11975
  quickViewer,
11976
+ removeRotation,
10660
11977
  saveHighlights,
10661
11978
  saveStudentData,
11979
+ scaleRect,
10662
11980
  useAgentContext,
10663
11981
  useAgentStore,
10664
11982
  useAnnotationStore,
@@ -10680,6 +11998,8 @@ init_utils();
10680
11998
  useTouchGestures,
10681
11999
  useViewerStore,
10682
12000
  useZoom,
12001
+ viewportToPDF,
12002
+ viewportToPercent,
10683
12003
  withErrorBoundary
10684
12004
  });
10685
12005
  //# sourceMappingURL=index.cjs.map