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.js CHANGED
@@ -310,6 +310,9 @@ var init_highlight_storage = __esm({
310
310
 
311
311
  // src/store/viewer-store.ts
312
312
  import { createStore } from "zustand/vanilla";
313
+ function generateRequestId() {
314
+ return `scroll-${Date.now()}-${++requestCounter}`;
315
+ }
313
316
  function createViewerStore(initialOverrides = {}) {
314
317
  return createStore()((set, get) => ({
315
318
  ...initialState,
@@ -362,6 +365,46 @@ function createViewerStore(initialOverrides = {}) {
362
365
  set({ currentPage: currentPage - 1 });
363
366
  }
364
367
  },
368
+ // Scroll coordination actions
369
+ requestScrollToPage: (page, behavior = "smooth") => {
370
+ const { numPages } = get();
371
+ const validPage = Math.max(1, Math.min(page, numPages));
372
+ return new Promise((resolve) => {
373
+ const requestId = generateRequestId();
374
+ const timeoutId = setTimeout(() => {
375
+ const callback = scrollCallbacks.get(requestId);
376
+ if (callback) {
377
+ scrollCallbacks.delete(requestId);
378
+ callback.resolve();
379
+ }
380
+ const currentRequest = get().scrollToPageRequest;
381
+ if (currentRequest?.requestId === requestId) {
382
+ set({ scrollToPageRequest: null });
383
+ }
384
+ }, SCROLL_TIMEOUT_MS);
385
+ scrollCallbacks.set(requestId, { resolve, timeoutId });
386
+ set({
387
+ currentPage: validPage,
388
+ scrollToPageRequest: {
389
+ page: validPage,
390
+ requestId,
391
+ behavior
392
+ }
393
+ });
394
+ });
395
+ },
396
+ completeScrollRequest: (requestId) => {
397
+ const callback = scrollCallbacks.get(requestId);
398
+ if (callback) {
399
+ clearTimeout(callback.timeoutId);
400
+ scrollCallbacks.delete(requestId);
401
+ callback.resolve();
402
+ }
403
+ const currentRequest = get().scrollToPageRequest;
404
+ if (currentRequest?.requestId === requestId) {
405
+ set({ scrollToPageRequest: null });
406
+ }
407
+ },
365
408
  // Zoom actions
366
409
  setScale: (scale) => {
367
410
  const clampedScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
@@ -428,13 +471,16 @@ function createViewerStore(initialOverrides = {}) {
428
471
  }
429
472
  }));
430
473
  }
431
- var ZOOM_LEVELS, MIN_SCALE, MAX_SCALE, initialState;
474
+ var ZOOM_LEVELS, MIN_SCALE, MAX_SCALE, SCROLL_TIMEOUT_MS, scrollCallbacks, requestCounter, initialState;
432
475
  var init_viewer_store = __esm({
433
476
  "src/store/viewer-store.ts"() {
434
477
  "use strict";
435
478
  ZOOM_LEVELS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
436
479
  MIN_SCALE = 0.1;
437
480
  MAX_SCALE = 10;
481
+ SCROLL_TIMEOUT_MS = 3e3;
482
+ scrollCallbacks = /* @__PURE__ */ new Map();
483
+ requestCounter = 0;
438
484
  initialState = {
439
485
  // Document state
440
486
  document: null,
@@ -445,6 +491,8 @@ var init_viewer_store = __esm({
445
491
  currentPage: 1,
446
492
  scale: 1,
447
493
  rotation: 0,
494
+ // Scroll coordination
495
+ scrollToPageRequest: null,
448
496
  // UI state
449
497
  viewMode: "single",
450
498
  scrollMode: "single",
@@ -1253,6 +1301,252 @@ var init_agent_api = __esm({
1253
1301
  }
1254
1302
  });
1255
1303
 
1304
+ // src/utils/text-search.ts
1305
+ async function extractPageText(document2, pageNumber) {
1306
+ const page = await document2.getPage(pageNumber);
1307
+ const textContent = await page.getTextContent();
1308
+ const viewport = page.getViewport({ scale: 1 });
1309
+ let fullText = "";
1310
+ const charPositions = [];
1311
+ for (const item of textContent.items) {
1312
+ if ("str" in item && item.str) {
1313
+ const tx = item.transform;
1314
+ const x = tx[4];
1315
+ const y = viewport.height - tx[5];
1316
+ const width = item.width ?? 0;
1317
+ const height = item.height ?? 12;
1318
+ const charWidth = item.str.length > 0 ? width / item.str.length : width;
1319
+ for (let i = 0; i < item.str.length; i++) {
1320
+ charPositions.push({
1321
+ char: item.str[i],
1322
+ rect: {
1323
+ x: x + i * charWidth,
1324
+ y: y - height,
1325
+ width: charWidth,
1326
+ height
1327
+ }
1328
+ });
1329
+ }
1330
+ fullText += item.str;
1331
+ }
1332
+ }
1333
+ return { fullText, charPositions };
1334
+ }
1335
+ async function findTextOnPage(document2, pageNumber, query, options = {}) {
1336
+ const { caseSensitive = false, wholeWord = false } = options;
1337
+ if (!query || pageNumber < 1 || pageNumber > document2.numPages) {
1338
+ return [];
1339
+ }
1340
+ const { fullText, charPositions } = await extractPageText(document2, pageNumber);
1341
+ const matches = [];
1342
+ const searchText = caseSensitive ? query : query.toLowerCase();
1343
+ const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
1344
+ let startIndex = 0;
1345
+ while (true) {
1346
+ const matchIndex = textToSearch.indexOf(searchText, startIndex);
1347
+ if (matchIndex === -1) break;
1348
+ if (wholeWord) {
1349
+ const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
1350
+ const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
1351
+ if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
1352
+ startIndex = matchIndex + 1;
1353
+ continue;
1354
+ }
1355
+ }
1356
+ const matchRects = [];
1357
+ for (let i = matchIndex; i < matchIndex + query.length && i < charPositions.length; i++) {
1358
+ matchRects.push(charPositions[i].rect);
1359
+ }
1360
+ const mergedRects = mergeAdjacentRects(matchRects);
1361
+ matches.push({
1362
+ text: fullText.substring(matchIndex, matchIndex + query.length),
1363
+ rects: mergedRects,
1364
+ pageNumber,
1365
+ startIndex: matchIndex
1366
+ });
1367
+ startIndex = matchIndex + 1;
1368
+ }
1369
+ return matches;
1370
+ }
1371
+ async function findTextInDocument(document2, query, options = {}) {
1372
+ const { pageRange, ...findOptions } = options;
1373
+ const pagesToSearch = pageRange ?? Array.from({ length: document2.numPages }, (_, i) => i + 1);
1374
+ const allMatches = [];
1375
+ for (const pageNum of pagesToSearch) {
1376
+ if (pageNum < 1 || pageNum > document2.numPages) continue;
1377
+ try {
1378
+ const matches = await findTextOnPage(document2, pageNum, query, findOptions);
1379
+ allMatches.push(...matches);
1380
+ } catch {
1381
+ }
1382
+ }
1383
+ return allMatches;
1384
+ }
1385
+ function mergeAdjacentRects(rects) {
1386
+ if (rects.length === 0) return [];
1387
+ const sorted = [...rects].sort((a, b) => a.y - b.y || a.x - b.x);
1388
+ const merged = [];
1389
+ let current = { ...sorted[0] };
1390
+ for (let i = 1; i < sorted.length; i++) {
1391
+ const rect = sorted[i];
1392
+ if (Math.abs(rect.y - current.y) < 2 && rect.x <= current.x + current.width + 2) {
1393
+ const newRight = Math.max(current.x + current.width, rect.x + rect.width);
1394
+ current.width = newRight - current.x;
1395
+ current.height = Math.max(current.height, rect.height);
1396
+ } else {
1397
+ merged.push(current);
1398
+ current = { ...rect };
1399
+ }
1400
+ }
1401
+ merged.push(current);
1402
+ return merged;
1403
+ }
1404
+ async function getPageText(document2, pageNumber) {
1405
+ if (pageNumber < 1 || pageNumber > document2.numPages) {
1406
+ return "";
1407
+ }
1408
+ const page = await document2.getPage(pageNumber);
1409
+ const textContent = await page.getTextContent();
1410
+ return textContent.items.filter((item) => "str" in item).map((item) => item.str).join("");
1411
+ }
1412
+ async function countTextOnPage(document2, pageNumber, query, options = {}) {
1413
+ const { caseSensitive = false, wholeWord = false } = options;
1414
+ if (!query || pageNumber < 1 || pageNumber > document2.numPages) {
1415
+ return 0;
1416
+ }
1417
+ const text = await getPageText(document2, pageNumber);
1418
+ const searchText = caseSensitive ? query : query.toLowerCase();
1419
+ const textToSearch = caseSensitive ? text : text.toLowerCase();
1420
+ let count = 0;
1421
+ let startIndex = 0;
1422
+ while (true) {
1423
+ const matchIndex = textToSearch.indexOf(searchText, startIndex);
1424
+ if (matchIndex === -1) break;
1425
+ if (wholeWord) {
1426
+ const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
1427
+ const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
1428
+ if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
1429
+ startIndex = matchIndex + 1;
1430
+ continue;
1431
+ }
1432
+ }
1433
+ count++;
1434
+ startIndex = matchIndex + 1;
1435
+ }
1436
+ return count;
1437
+ }
1438
+ var init_text_search = __esm({
1439
+ "src/utils/text-search.ts"() {
1440
+ "use strict";
1441
+ }
1442
+ });
1443
+
1444
+ // src/utils/coordinates.ts
1445
+ function pdfToViewport(x, y, scale, pageHeight) {
1446
+ return {
1447
+ x: x * scale,
1448
+ y: (pageHeight - y) * scale
1449
+ };
1450
+ }
1451
+ function viewportToPDF(x, y, scale, pageHeight) {
1452
+ return {
1453
+ x: x / scale,
1454
+ y: pageHeight - y / scale
1455
+ };
1456
+ }
1457
+ function percentToPDF(xPercent, yPercent, pageWidth, pageHeight) {
1458
+ return {
1459
+ x: xPercent / 100 * pageWidth,
1460
+ y: yPercent / 100 * pageHeight
1461
+ };
1462
+ }
1463
+ function pdfToPercent(x, y, pageWidth, pageHeight) {
1464
+ return {
1465
+ x: x / pageWidth * 100,
1466
+ y: y / pageHeight * 100
1467
+ };
1468
+ }
1469
+ function percentToViewport(xPercent, yPercent, pageWidth, pageHeight, scale) {
1470
+ return {
1471
+ x: xPercent / 100 * pageWidth * scale,
1472
+ y: yPercent / 100 * pageHeight * scale
1473
+ };
1474
+ }
1475
+ function viewportToPercent(x, y, pageWidth, pageHeight, scale) {
1476
+ return {
1477
+ x: x / (pageWidth * scale) * 100,
1478
+ y: y / (pageHeight * scale) * 100
1479
+ };
1480
+ }
1481
+ function applyRotation(x, y, rotation, pageWidth, pageHeight) {
1482
+ const normalizedRotation = (rotation % 360 + 360) % 360;
1483
+ switch (normalizedRotation) {
1484
+ case 90:
1485
+ return { x: y, y: pageWidth - x };
1486
+ case 180:
1487
+ return { x: pageWidth - x, y: pageHeight - y };
1488
+ case 270:
1489
+ return { x: pageHeight - y, y: x };
1490
+ default:
1491
+ return { x, y };
1492
+ }
1493
+ }
1494
+ function removeRotation(x, y, rotation, pageWidth, pageHeight) {
1495
+ const normalizedRotation = (rotation % 360 + 360) % 360;
1496
+ switch (normalizedRotation) {
1497
+ case 90:
1498
+ return { x: pageWidth - y, y: x };
1499
+ case 180:
1500
+ return { x: pageWidth - x, y: pageHeight - y };
1501
+ case 270:
1502
+ return { x: y, y: pageHeight - x };
1503
+ default:
1504
+ return { x, y };
1505
+ }
1506
+ }
1507
+ function getRotatedDimensions(width, height, rotation) {
1508
+ const normalizedRotation = (rotation % 360 + 360) % 360;
1509
+ if (normalizedRotation === 90 || normalizedRotation === 270) {
1510
+ return { width: height, height: width };
1511
+ }
1512
+ return { width, height };
1513
+ }
1514
+ function scaleRect(rect, fromScale, toScale) {
1515
+ const ratio = toScale / fromScale;
1516
+ return {
1517
+ x: rect.x * ratio,
1518
+ y: rect.y * ratio,
1519
+ width: rect.width * ratio,
1520
+ height: rect.height * ratio
1521
+ };
1522
+ }
1523
+ function isPointInRect(point, rect) {
1524
+ return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
1525
+ }
1526
+ function doRectsIntersect(rectA, rectB) {
1527
+ 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);
1528
+ }
1529
+ function getRectIntersection(rectA, rectB) {
1530
+ const x = Math.max(rectA.x, rectB.x);
1531
+ const y = Math.max(rectA.y, rectB.y);
1532
+ const right = Math.min(rectA.x + rectA.width, rectB.x + rectB.width);
1533
+ const bottom = Math.min(rectA.y + rectA.height, rectB.y + rectB.height);
1534
+ if (right <= x || bottom <= y) {
1535
+ return null;
1536
+ }
1537
+ return {
1538
+ x,
1539
+ y,
1540
+ width: right - x,
1541
+ height: bottom - y
1542
+ };
1543
+ }
1544
+ var init_coordinates = __esm({
1545
+ "src/utils/coordinates.ts"() {
1546
+ "use strict";
1547
+ }
1548
+ });
1549
+
1256
1550
  // src/utils/index.ts
1257
1551
  var init_utils = __esm({
1258
1552
  "src/utils/index.ts"() {
@@ -1265,6 +1559,8 @@ var init_utils = __esm({
1265
1559
  init_export_annotations();
1266
1560
  init_student_storage();
1267
1561
  init_agent_api();
1562
+ init_text_search();
1563
+ init_coordinates();
1268
1564
  }
1269
1565
  });
1270
1566
 
@@ -1361,6 +1657,12 @@ function createSearchStore(initialOverrides = {}) {
1361
1657
  set({ currentResultIndex: index });
1362
1658
  }
1363
1659
  },
1660
+ setCaseSensitive: (value) => {
1661
+ set({ caseSensitive: value });
1662
+ },
1663
+ setWholeWord: (value) => {
1664
+ set({ wholeWord: value });
1665
+ },
1364
1666
  toggleCaseSensitive: () => {
1365
1667
  set((state) => ({ caseSensitive: !state.caseSensitive }));
1366
1668
  },
@@ -7883,6 +8185,8 @@ var init_DocumentContainer = __esm({
7883
8185
  nextPage,
7884
8186
  previousPage
7885
8187
  } = usePDFViewer();
8188
+ const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
8189
+ const { viewerStore } = usePDFViewerStores();
7886
8190
  const [currentPageObj, setCurrentPageObj] = useState18(null);
7887
8191
  const [isLoadingPage, setIsLoadingPage] = useState18(false);
7888
8192
  const containerRef = useRef16(null);
@@ -7948,6 +8252,11 @@ var init_DocumentContainer = __esm({
7948
8252
  const page = await document2.getPage(currentPage);
7949
8253
  if (!cancelled && document2 === documentRef.current) {
7950
8254
  setCurrentPageObj(page);
8255
+ if (scrollToPageRequest && scrollToPageRequest.page === currentPage) {
8256
+ requestAnimationFrame(() => {
8257
+ viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
8258
+ });
8259
+ }
7951
8260
  }
7952
8261
  } catch (error) {
7953
8262
  if (!cancelled) {
@@ -7966,7 +8275,7 @@ var init_DocumentContainer = __esm({
7966
8275
  return () => {
7967
8276
  cancelled = true;
7968
8277
  };
7969
- }, [document2, currentPage]);
8278
+ }, [document2, currentPage, scrollToPageRequest, viewerStore]);
7970
8279
  const getPageElement = useCallback29(() => {
7971
8280
  return containerRef.current?.querySelector(`[data-page-number="${currentPage}"]`);
7972
8281
  }, [currentPage]);
@@ -8108,6 +8417,8 @@ var init_VirtualizedDocumentContainer = __esm({
8108
8417
  nextPage,
8109
8418
  previousPage
8110
8419
  } = usePDFViewer();
8420
+ const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
8421
+ const { viewerStore } = usePDFViewerStores();
8111
8422
  const containerRef = useRef17(null);
8112
8423
  const scrollContainerRef = useRef17(null);
8113
8424
  const documentRef = useRef17(null);
@@ -8245,6 +8556,45 @@ var init_VirtualizedDocumentContainer = __esm({
8245
8556
  loadPages();
8246
8557
  }, [document2, visiblePages, pageObjects]);
8247
8558
  useEffect20(() => {
8559
+ if (!scrollToPageRequest || !scrollContainerRef.current || pageInfos.length === 0) return;
8560
+ const { page, requestId, behavior } = scrollToPageRequest;
8561
+ const pageInfo = pageInfos.find((p) => p.pageNumber === page);
8562
+ if (!pageInfo) {
8563
+ viewerStore.getState().completeScrollRequest(requestId);
8564
+ return;
8565
+ }
8566
+ const container = scrollContainerRef.current;
8567
+ const targetScroll = pageInfo.top - pageGap;
8568
+ const scrollTop = container.scrollTop;
8569
+ const viewportHeight = container.clientHeight;
8570
+ const isVisible = targetScroll >= scrollTop && pageInfo.top + pageInfo.height <= scrollTop + viewportHeight;
8571
+ if (isVisible) {
8572
+ viewerStore.getState().completeScrollRequest(requestId);
8573
+ return;
8574
+ }
8575
+ container.scrollTo({
8576
+ top: targetScroll,
8577
+ behavior
8578
+ });
8579
+ if (behavior === "instant") {
8580
+ requestAnimationFrame(() => {
8581
+ viewerStore.getState().completeScrollRequest(requestId);
8582
+ });
8583
+ } else {
8584
+ let scrollEndTimeout;
8585
+ const handleScrollEnd = () => {
8586
+ clearTimeout(scrollEndTimeout);
8587
+ scrollEndTimeout = setTimeout(() => {
8588
+ container.removeEventListener("scroll", handleScrollEnd);
8589
+ viewerStore.getState().completeScrollRequest(requestId);
8590
+ }, 100);
8591
+ };
8592
+ container.addEventListener("scroll", handleScrollEnd, { passive: true });
8593
+ handleScrollEnd();
8594
+ }
8595
+ }, [scrollToPageRequest, pageInfos, pageGap, viewerStore]);
8596
+ useEffect20(() => {
8597
+ if (scrollToPageRequest) return;
8248
8598
  if (!scrollContainerRef.current || pageInfos.length === 0) return;
8249
8599
  const pageInfo = pageInfos.find((p) => p.pageNumber === currentPage);
8250
8600
  if (pageInfo) {
@@ -8259,7 +8609,7 @@ var init_VirtualizedDocumentContainer = __esm({
8259
8609
  });
8260
8610
  }
8261
8611
  }
8262
- }, [currentPage, pageInfos, pageGap]);
8612
+ }, [currentPage, pageInfos, pageGap, scrollToPageRequest]);
8263
8613
  const handlePinchZoom = useCallback30(
8264
8614
  (pinchScale) => {
8265
8615
  const newScale = Math.max(0.25, Math.min(4, baseScaleRef.current * pinchScale));
@@ -8466,6 +8816,8 @@ var init_DualPageContainer = __esm({
8466
8816
  setScale,
8467
8817
  goToPage
8468
8818
  } = usePDFViewer();
8819
+ const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
8820
+ const { viewerStore } = usePDFViewerStores();
8469
8821
  const containerRef = useRef18(null);
8470
8822
  const documentRef = useRef18(null);
8471
8823
  const baseScaleRef = useRef18(scale);
@@ -8551,6 +8903,14 @@ var init_DualPageContainer = __esm({
8551
8903
  if (!cancelled) {
8552
8904
  setLeftPage(left);
8553
8905
  setRightPage(right);
8906
+ if (scrollToPageRequest) {
8907
+ const requestedPage = scrollToPageRequest.page;
8908
+ if (requestedPage === spread2.left || requestedPage === spread2.right) {
8909
+ requestAnimationFrame(() => {
8910
+ viewerStore.getState().completeScrollRequest(scrollToPageRequest.requestId);
8911
+ });
8912
+ }
8913
+ }
8554
8914
  }
8555
8915
  } catch (error) {
8556
8916
  if (!cancelled) {
@@ -8566,7 +8926,7 @@ var init_DualPageContainer = __esm({
8566
8926
  return () => {
8567
8927
  cancelled = true;
8568
8928
  };
8569
- }, [document2, currentPage, getSpreadPages]);
8929
+ }, [document2, currentPage, getSpreadPages, scrollToPageRequest, viewerStore]);
8570
8930
  const goToPreviousSpread = useCallback31(() => {
8571
8931
  const spread2 = getSpreadPages(currentPage);
8572
8932
  const leftmostPage = spread2.left || spread2.right || currentPage;
@@ -8743,13 +9103,159 @@ var init_DualPageContainer = __esm({
8743
9103
  }
8744
9104
  });
8745
9105
 
9106
+ // src/components/FloatingZoomControls/FloatingZoomControls.tsx
9107
+ import { memo as memo24, useCallback as useCallback32 } from "react";
9108
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
9109
+ var FloatingZoomControls;
9110
+ var init_FloatingZoomControls = __esm({
9111
+ "src/components/FloatingZoomControls/FloatingZoomControls.tsx"() {
9112
+ "use strict";
9113
+ init_hooks();
9114
+ init_utils();
9115
+ FloatingZoomControls = memo24(function FloatingZoomControls2({
9116
+ position = "bottom-right",
9117
+ className,
9118
+ showFitToWidth = true,
9119
+ showFitToPage = false,
9120
+ showZoomLevel = true
9121
+ }) {
9122
+ const { viewerStore } = usePDFViewerStores();
9123
+ const scale = useViewerStore((s) => s.scale);
9124
+ const document2 = useViewerStore((s) => s.document);
9125
+ const handleZoomIn = useCallback32(() => {
9126
+ const currentScale = viewerStore.getState().scale;
9127
+ const newScale = Math.min(4, currentScale + 0.05);
9128
+ viewerStore.getState().setScale(newScale);
9129
+ }, [viewerStore]);
9130
+ const handleZoomOut = useCallback32(() => {
9131
+ const currentScale = viewerStore.getState().scale;
9132
+ const newScale = Math.max(0.1, currentScale - 0.05);
9133
+ viewerStore.getState().setScale(newScale);
9134
+ }, [viewerStore]);
9135
+ const handleFitToWidth = useCallback32(() => {
9136
+ viewerStore.getState().setScale(1);
9137
+ }, [viewerStore]);
9138
+ const handleFitToPage = useCallback32(() => {
9139
+ viewerStore.getState().setScale(0.75);
9140
+ }, [viewerStore]);
9141
+ if (!document2) return null;
9142
+ const positionClasses = {
9143
+ "bottom-right": "bottom-4 right-4",
9144
+ "bottom-left": "bottom-4 left-4",
9145
+ "top-right": "top-4 right-4",
9146
+ "top-left": "top-4 left-4"
9147
+ };
9148
+ const zoomPercentage = Math.round(scale * 100);
9149
+ return /* @__PURE__ */ jsxs21(
9150
+ "div",
9151
+ {
9152
+ className: cn(
9153
+ "fixed z-50 flex items-center gap-1",
9154
+ "bg-white dark:bg-gray-800 rounded-lg shadow-lg",
9155
+ "border border-gray-200 dark:border-gray-700",
9156
+ "p-1",
9157
+ positionClasses[position],
9158
+ className
9159
+ ),
9160
+ children: [
9161
+ /* @__PURE__ */ jsx25(
9162
+ "button",
9163
+ {
9164
+ onClick: handleZoomOut,
9165
+ className: cn(
9166
+ "w-8 h-8 flex items-center justify-center rounded",
9167
+ "text-gray-700 dark:text-gray-300",
9168
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9169
+ "transition-colors",
9170
+ "disabled:opacity-50 disabled:cursor-not-allowed"
9171
+ ),
9172
+ disabled: scale <= 0.25,
9173
+ title: "Zoom Out",
9174
+ "aria-label": "Zoom Out",
9175
+ children: /* @__PURE__ */ jsx25("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx25("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) })
9176
+ }
9177
+ ),
9178
+ showZoomLevel && /* @__PURE__ */ jsxs21("span", { className: "min-w-[48px] text-center text-sm font-medium text-gray-700 dark:text-gray-300", children: [
9179
+ zoomPercentage,
9180
+ "%"
9181
+ ] }),
9182
+ /* @__PURE__ */ jsx25(
9183
+ "button",
9184
+ {
9185
+ onClick: handleZoomIn,
9186
+ className: cn(
9187
+ "w-8 h-8 flex items-center justify-center rounded",
9188
+ "text-gray-700 dark:text-gray-300",
9189
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9190
+ "transition-colors",
9191
+ "disabled:opacity-50 disabled:cursor-not-allowed"
9192
+ ),
9193
+ disabled: scale >= 4,
9194
+ title: "Zoom In",
9195
+ "aria-label": "Zoom In",
9196
+ children: /* @__PURE__ */ jsx25("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx25("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) })
9197
+ }
9198
+ ),
9199
+ (showFitToWidth || showFitToPage) && /* @__PURE__ */ jsx25("div", { className: "w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1" }),
9200
+ showFitToWidth && /* @__PURE__ */ jsx25(
9201
+ "button",
9202
+ {
9203
+ onClick: handleFitToWidth,
9204
+ className: cn(
9205
+ "w-8 h-8 flex items-center justify-center rounded",
9206
+ "text-gray-700 dark:text-gray-300",
9207
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9208
+ "transition-colors"
9209
+ ),
9210
+ title: "Fit to Width",
9211
+ "aria-label": "Fit to Width",
9212
+ children: /* @__PURE__ */ jsx25("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx25("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" }) })
9213
+ }
9214
+ ),
9215
+ showFitToPage && /* @__PURE__ */ jsx25(
9216
+ "button",
9217
+ {
9218
+ onClick: handleFitToPage,
9219
+ className: cn(
9220
+ "w-8 h-8 flex items-center justify-center rounded",
9221
+ "text-gray-700 dark:text-gray-300",
9222
+ "hover:bg-gray-100 dark:hover:bg-gray-700",
9223
+ "transition-colors"
9224
+ ),
9225
+ title: "Fit to Page",
9226
+ "aria-label": "Fit to Page",
9227
+ children: /* @__PURE__ */ jsx25("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx25("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" }) })
9228
+ }
9229
+ )
9230
+ ]
9231
+ }
9232
+ );
9233
+ });
9234
+ }
9235
+ });
9236
+
9237
+ // src/components/FloatingZoomControls/index.ts
9238
+ var init_FloatingZoomControls2 = __esm({
9239
+ "src/components/FloatingZoomControls/index.ts"() {
9240
+ "use strict";
9241
+ init_FloatingZoomControls();
9242
+ }
9243
+ });
9244
+
8746
9245
  // src/components/PDFViewer/PDFViewerClient.tsx
8747
9246
  var PDFViewerClient_exports = {};
8748
9247
  __export(PDFViewerClient_exports, {
8749
9248
  PDFViewerClient: () => PDFViewerClient
8750
9249
  });
8751
- import { useEffect as useEffect22, useCallback as useCallback32, memo as memo24, useRef as useRef19, useState as useState21 } from "react";
8752
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
9250
+ import {
9251
+ useEffect as useEffect22,
9252
+ useCallback as useCallback33,
9253
+ memo as memo25,
9254
+ useRef as useRef19,
9255
+ useState as useState21,
9256
+ forwardRef
9257
+ } from "react";
9258
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
8753
9259
  function getSrcIdentifier(src) {
8754
9260
  if (typeof src === "string") {
8755
9261
  return src;
@@ -8761,7 +9267,26 @@ function getSrcIdentifier(src) {
8761
9267
  const last = Array.from(data.slice(-4)).map((b) => b.toString(16).padStart(2, "0")).join("");
8762
9268
  return `binary:${len}:${first}:${last}`;
8763
9269
  }
8764
- var PDFViewerInner, PDFViewerClient;
9270
+ function mergeRects2(rects) {
9271
+ if (rects.length === 0) return [];
9272
+ const sorted = [...rects].sort((a, b) => a.y - b.y || a.x - b.x);
9273
+ const merged = [];
9274
+ let current = { ...sorted[0] };
9275
+ for (let i = 1; i < sorted.length; i++) {
9276
+ const rect = sorted[i];
9277
+ if (Math.abs(rect.y - current.y) < 2 && rect.x <= current.x + current.width + 2) {
9278
+ const newRight = Math.max(current.x + current.width, rect.x + rect.width);
9279
+ current.width = newRight - current.x;
9280
+ current.height = Math.max(current.height, rect.height);
9281
+ } else {
9282
+ merged.push(current);
9283
+ current = { ...rect };
9284
+ }
9285
+ }
9286
+ merged.push(current);
9287
+ return merged;
9288
+ }
9289
+ var PDFViewerInner, PDFViewerInnerWithRef, PDFViewerClient;
8765
9290
  var init_PDFViewerClient = __esm({
8766
9291
  "src/components/PDFViewer/PDFViewerClient.tsx"() {
8767
9292
  "use strict";
@@ -8773,33 +9298,61 @@ var init_PDFViewerClient = __esm({
8773
9298
  init_DocumentContainer();
8774
9299
  init_ContinuousScrollContainer();
8775
9300
  init_DualPageContainer();
9301
+ init_FloatingZoomControls2();
8776
9302
  init_utils();
8777
- PDFViewerInner = memo24(function PDFViewerInner2({
9303
+ PDFViewerInner = memo25(function PDFViewerInner2({
8778
9304
  src,
8779
9305
  initialPage = 1,
8780
- initialScale = 1,
9306
+ page: controlledPage,
9307
+ initialScale = "auto",
8781
9308
  showToolbar = true,
8782
9309
  showSidebar = true,
8783
9310
  showAnnotationToolbar = false,
9311
+ showFloatingZoom = true,
8784
9312
  viewMode = "single",
8785
9313
  onDocumentLoad,
8786
9314
  onPageChange,
8787
9315
  onScaleChange,
9316
+ onZoomChange,
8788
9317
  onError,
9318
+ onPageRenderStart,
9319
+ onPageRenderComplete,
9320
+ onHighlightAdded,
9321
+ onHighlightRemoved,
9322
+ onAnnotationAdded,
8789
9323
  workerSrc,
8790
- className
9324
+ className,
9325
+ loadingComponent,
9326
+ errorComponent,
9327
+ onReady
8791
9328
  }) {
8792
- const { viewerStore } = usePDFViewerStores();
9329
+ const { viewerStore, annotationStore, searchStore } = usePDFViewerStores();
8793
9330
  const mountedRef = useRef19(true);
8794
9331
  const [, setLoadState] = useState21("idle");
8795
9332
  const onDocumentLoadRef = useRef19(onDocumentLoad);
8796
9333
  const onErrorRef = useRef19(onError);
8797
9334
  const onPageChangeRef = useRef19(onPageChange);
8798
9335
  const onScaleChangeRef = useRef19(onScaleChange);
9336
+ const onZoomChangeRef = useRef19(onZoomChange);
9337
+ const onPageRenderStartRef = useRef19(onPageRenderStart);
9338
+ const onPageRenderCompleteRef = useRef19(onPageRenderComplete);
9339
+ const onHighlightAddedRef = useRef19(onHighlightAdded);
9340
+ const onHighlightRemovedRef = useRef19(onHighlightRemoved);
9341
+ const onAnnotationAddedRef = useRef19(onAnnotationAdded);
9342
+ const onReadyRef = useRef19(onReady);
8799
9343
  onDocumentLoadRef.current = onDocumentLoad;
8800
9344
  onErrorRef.current = onError;
8801
9345
  onPageChangeRef.current = onPageChange;
8802
9346
  onScaleChangeRef.current = onScaleChange;
9347
+ onZoomChangeRef.current = onZoomChange;
9348
+ onPageRenderStartRef.current = onPageRenderStart;
9349
+ onPageRenderCompleteRef.current = onPageRenderComplete;
9350
+ onHighlightAddedRef.current = onHighlightAdded;
9351
+ onHighlightRemovedRef.current = onHighlightRemoved;
9352
+ onAnnotationAddedRef.current = onAnnotationAdded;
9353
+ onReadyRef.current = onReady;
9354
+ const isControlled = controlledPage !== void 0;
9355
+ const prevControlledPageRef = useRef19(controlledPage);
8803
9356
  const srcIdRef = useRef19(null);
8804
9357
  const currentPage = useViewerStore((s) => s.currentPage);
8805
9358
  const scale = useViewerStore((s) => s.scale);
@@ -8808,7 +9361,452 @@ var init_PDFViewerClient = __esm({
8808
9361
  const error = useViewerStore((s) => s.error);
8809
9362
  const sidebarOpen = useViewerStore((s) => s.sidebarOpen);
8810
9363
  const srcId = getSrcIdentifier(src);
8811
- const handleRetry = useCallback32(() => {
9364
+ const handleRef = useRef19(null);
9365
+ useEffect22(() => {
9366
+ const handle = {
9367
+ // ==================== Text Highlighting ====================
9368
+ highlightText: async (text, options) => {
9369
+ const doc = viewerStore.getState().document;
9370
+ if (!doc) return [];
9371
+ const color = options?.color ?? "yellow";
9372
+ const targetPage = options?.page;
9373
+ const caseSensitive = options?.caseSensitive ?? false;
9374
+ const scrollTo = options?.scrollTo ?? true;
9375
+ const highlightIds = [];
9376
+ const searchText = caseSensitive ? text : text.toLowerCase();
9377
+ const pagesToSearch = targetPage ? [targetPage] : Array.from({ length: doc.numPages }, (_, i) => i + 1);
9378
+ for (const pageNum of pagesToSearch) {
9379
+ try {
9380
+ const page = await doc.getPage(pageNum);
9381
+ const textContent = await page.getTextContent();
9382
+ const viewport = page.getViewport({ scale: 1 });
9383
+ let fullText = "";
9384
+ const charPositions = [];
9385
+ for (const item of textContent.items) {
9386
+ if ("str" in item && item.str) {
9387
+ const tx = item.transform;
9388
+ const x = tx[4];
9389
+ const y = viewport.height - tx[5];
9390
+ const width = item.width ?? 0;
9391
+ const height = item.height ?? 12;
9392
+ const charWidth = item.str.length > 0 ? width / item.str.length : width;
9393
+ for (let i = 0; i < item.str.length; i++) {
9394
+ charPositions.push({
9395
+ char: item.str[i],
9396
+ rect: {
9397
+ x: x + i * charWidth,
9398
+ y: y - height,
9399
+ width: charWidth,
9400
+ height
9401
+ }
9402
+ });
9403
+ }
9404
+ fullText += item.str;
9405
+ }
9406
+ }
9407
+ const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
9408
+ let startIndex = 0;
9409
+ while (true) {
9410
+ const matchIndex = textToSearch.indexOf(searchText, startIndex);
9411
+ if (matchIndex === -1) break;
9412
+ const matchRects = [];
9413
+ for (let i = matchIndex; i < matchIndex + text.length && i < charPositions.length; i++) {
9414
+ matchRects.push(charPositions[i].rect);
9415
+ }
9416
+ const mergedRects = mergeRects2(matchRects);
9417
+ const highlight = annotationStore.getState().addHighlight({
9418
+ pageNumber: pageNum,
9419
+ rects: mergedRects,
9420
+ color,
9421
+ text: fullText.substring(matchIndex, matchIndex + text.length)
9422
+ });
9423
+ highlightIds.push(highlight.id);
9424
+ startIndex = matchIndex + 1;
9425
+ }
9426
+ } catch {
9427
+ }
9428
+ }
9429
+ if (scrollTo && highlightIds.length > 0) {
9430
+ const firstHighlight = annotationStore.getState().highlights.find((h) => h.id === highlightIds[0]);
9431
+ if (firstHighlight) {
9432
+ viewerStore.getState().goToPage(firstHighlight.pageNumber);
9433
+ }
9434
+ }
9435
+ return highlightIds;
9436
+ },
9437
+ removeHighlight: (id) => {
9438
+ annotationStore.getState().removeHighlight(id);
9439
+ },
9440
+ clearHighlights: () => {
9441
+ const highlights = annotationStore.getState().highlights;
9442
+ for (const h of highlights) {
9443
+ annotationStore.getState().removeHighlight(h.id);
9444
+ }
9445
+ },
9446
+ // ==================== Annotations ====================
9447
+ drawRect: (options) => {
9448
+ const annotation = annotationStore.getState().addAnnotation({
9449
+ type: "shape",
9450
+ shapeType: "rect",
9451
+ pageNumber: options.page,
9452
+ x: options.x,
9453
+ y: options.y,
9454
+ width: options.width,
9455
+ height: options.height,
9456
+ color: options.color ?? "blue",
9457
+ strokeWidth: options.strokeWidth ?? 2
9458
+ });
9459
+ return annotation.id;
9460
+ },
9461
+ drawCircle: (options) => {
9462
+ const annotation = annotationStore.getState().addAnnotation({
9463
+ type: "shape",
9464
+ shapeType: "circle",
9465
+ pageNumber: options.page,
9466
+ x: options.x,
9467
+ y: options.y,
9468
+ width: options.radius * 2,
9469
+ height: options.radius * 2,
9470
+ color: options.color ?? "blue",
9471
+ strokeWidth: options.strokeWidth ?? 2
9472
+ });
9473
+ return annotation.id;
9474
+ },
9475
+ addNote: (options) => {
9476
+ const annotation = annotationStore.getState().addAnnotation({
9477
+ type: "note",
9478
+ pageNumber: options.page,
9479
+ x: options.x,
9480
+ y: options.y,
9481
+ content: options.content,
9482
+ color: options.color ?? "yellow"
9483
+ });
9484
+ return annotation.id;
9485
+ },
9486
+ removeAnnotation: (id) => {
9487
+ annotationStore.getState().removeAnnotation(id);
9488
+ },
9489
+ clearAnnotations: () => {
9490
+ const annotations = annotationStore.getState().annotations;
9491
+ for (const a of annotations) {
9492
+ annotationStore.getState().removeAnnotation(a.id);
9493
+ }
9494
+ },
9495
+ // ==================== Navigation ====================
9496
+ goToPage: async (page, options) => {
9497
+ const behavior = options?.behavior ?? "smooth";
9498
+ await viewerStore.getState().requestScrollToPage(page, behavior);
9499
+ },
9500
+ nextPage: () => {
9501
+ viewerStore.getState().nextPage();
9502
+ },
9503
+ previousPage: () => {
9504
+ viewerStore.getState().previousPage();
9505
+ },
9506
+ getCurrentPage: () => {
9507
+ return viewerStore.getState().currentPage;
9508
+ },
9509
+ getNumPages: () => {
9510
+ return viewerStore.getState().numPages;
9511
+ },
9512
+ // ==================== Zoom ====================
9513
+ setZoom: (scale2) => {
9514
+ viewerStore.getState().setScale(scale2);
9515
+ },
9516
+ getZoom: () => {
9517
+ return viewerStore.getState().scale;
9518
+ },
9519
+ zoomIn: () => {
9520
+ viewerStore.getState().zoomIn();
9521
+ },
9522
+ zoomOut: () => {
9523
+ viewerStore.getState().zoomOut();
9524
+ },
9525
+ // ==================== Search ====================
9526
+ search: async (query, options) => {
9527
+ const doc = viewerStore.getState().document;
9528
+ if (!doc) return [];
9529
+ searchStore.getState().setQuery(query);
9530
+ if (options?.caseSensitive !== void 0) {
9531
+ searchStore.getState().setCaseSensitive(options.caseSensitive);
9532
+ }
9533
+ if (options?.wholeWord !== void 0) {
9534
+ searchStore.getState().setWholeWord(options.wholeWord);
9535
+ }
9536
+ await searchStore.getState().search(doc);
9537
+ return searchStore.getState().results;
9538
+ },
9539
+ nextSearchResult: () => {
9540
+ searchStore.getState().nextResult();
9541
+ const results = searchStore.getState().results;
9542
+ const index = searchStore.getState().currentResultIndex;
9543
+ if (results[index]) {
9544
+ viewerStore.getState().goToPage(results[index].pageNumber);
9545
+ }
9546
+ },
9547
+ previousSearchResult: () => {
9548
+ searchStore.getState().previousResult();
9549
+ const results = searchStore.getState().results;
9550
+ const index = searchStore.getState().currentResultIndex;
9551
+ if (results[index]) {
9552
+ viewerStore.getState().goToPage(results[index].pageNumber);
9553
+ }
9554
+ },
9555
+ clearSearch: () => {
9556
+ searchStore.getState().clearSearch();
9557
+ },
9558
+ // ==================== Combined Search & Highlight ====================
9559
+ searchAndHighlight: async (query, options) => {
9560
+ const doc = viewerStore.getState().document;
9561
+ if (!doc) {
9562
+ return { matchCount: 0, highlightIds: [], matches: [] };
9563
+ }
9564
+ const color = options?.color ?? "yellow";
9565
+ const caseSensitive = options?.caseSensitive ?? false;
9566
+ const wholeWord = options?.wholeWord ?? false;
9567
+ const scrollToFirst = options?.scrollToFirst ?? true;
9568
+ const clearPrevious = options?.clearPrevious ?? true;
9569
+ if (clearPrevious) {
9570
+ const existingHighlights = annotationStore.getState().highlights;
9571
+ for (const h of existingHighlights) {
9572
+ if (h.source === "search") {
9573
+ annotationStore.getState().removeHighlight(h.id);
9574
+ }
9575
+ }
9576
+ }
9577
+ let pagesToSearch;
9578
+ if (options?.pageRange) {
9579
+ if (Array.isArray(options.pageRange)) {
9580
+ pagesToSearch = options.pageRange;
9581
+ } else {
9582
+ const { start, end } = options.pageRange;
9583
+ pagesToSearch = Array.from({ length: end - start + 1 }, (_, i) => start + i);
9584
+ }
9585
+ } else {
9586
+ pagesToSearch = Array.from({ length: doc.numPages }, (_, i) => i + 1);
9587
+ }
9588
+ const result = {
9589
+ matchCount: 0,
9590
+ highlightIds: [],
9591
+ matches: []
9592
+ };
9593
+ const searchText = caseSensitive ? query : query.toLowerCase();
9594
+ for (const pageNum of pagesToSearch) {
9595
+ if (pageNum < 1 || pageNum > doc.numPages) continue;
9596
+ try {
9597
+ const page = await doc.getPage(pageNum);
9598
+ const textContent = await page.getTextContent();
9599
+ const viewport = page.getViewport({ scale: 1 });
9600
+ let fullText = "";
9601
+ const charPositions = [];
9602
+ for (const item of textContent.items) {
9603
+ if ("str" in item && item.str) {
9604
+ const tx = item.transform;
9605
+ const x = tx[4];
9606
+ const y = viewport.height - tx[5];
9607
+ const width = item.width ?? 0;
9608
+ const height = item.height ?? 12;
9609
+ const charWidth = item.str.length > 0 ? width / item.str.length : width;
9610
+ for (let i = 0; i < item.str.length; i++) {
9611
+ charPositions.push({
9612
+ char: item.str[i],
9613
+ rect: {
9614
+ x: x + i * charWidth,
9615
+ y: y - height,
9616
+ width: charWidth,
9617
+ height
9618
+ }
9619
+ });
9620
+ }
9621
+ fullText += item.str;
9622
+ }
9623
+ }
9624
+ const textToSearch = caseSensitive ? fullText : fullText.toLowerCase();
9625
+ let startIndex = 0;
9626
+ while (true) {
9627
+ let matchIndex = textToSearch.indexOf(searchText, startIndex);
9628
+ if (matchIndex === -1) break;
9629
+ if (wholeWord) {
9630
+ const beforeChar = matchIndex > 0 ? textToSearch[matchIndex - 1] : " ";
9631
+ const afterChar = matchIndex + query.length < textToSearch.length ? textToSearch[matchIndex + query.length] : " ";
9632
+ if (/\w/.test(beforeChar) || /\w/.test(afterChar)) {
9633
+ startIndex = matchIndex + 1;
9634
+ continue;
9635
+ }
9636
+ }
9637
+ const matchRects = [];
9638
+ for (let i = matchIndex; i < matchIndex + query.length && i < charPositions.length; i++) {
9639
+ matchRects.push(charPositions[i].rect);
9640
+ }
9641
+ const mergedRects = mergeRects2(matchRects);
9642
+ const highlight = annotationStore.getState().addHighlight({
9643
+ pageNumber: pageNum,
9644
+ rects: mergedRects,
9645
+ color,
9646
+ text: fullText.substring(matchIndex, matchIndex + query.length),
9647
+ source: "search"
9648
+ });
9649
+ result.matchCount++;
9650
+ result.highlightIds.push(highlight.id);
9651
+ result.matches.push({
9652
+ pageNumber: pageNum,
9653
+ text: fullText.substring(matchIndex, matchIndex + query.length),
9654
+ highlightId: highlight.id,
9655
+ rects: mergedRects
9656
+ });
9657
+ startIndex = matchIndex + 1;
9658
+ }
9659
+ } catch {
9660
+ }
9661
+ }
9662
+ if (scrollToFirst && result.matches.length > 0) {
9663
+ const firstMatch = result.matches[0];
9664
+ await viewerStore.getState().requestScrollToPage(firstMatch.pageNumber, "smooth");
9665
+ }
9666
+ return result;
9667
+ },
9668
+ // ==================== Agent Tools ====================
9669
+ agentTools: {
9670
+ navigateToPage: async (page) => {
9671
+ try {
9672
+ const { currentPage: currentPage2, numPages } = viewerStore.getState();
9673
+ if (numPages === 0) {
9674
+ return {
9675
+ success: false,
9676
+ error: { code: "NO_DOCUMENT", message: "No document is loaded" }
9677
+ };
9678
+ }
9679
+ if (page < 1 || page > numPages) {
9680
+ return {
9681
+ success: false,
9682
+ error: { code: "INVALID_PAGE", message: `Page ${page} is out of range (1-${numPages})` }
9683
+ };
9684
+ }
9685
+ const previousPage = currentPage2;
9686
+ await viewerStore.getState().requestScrollToPage(page, "smooth");
9687
+ return {
9688
+ success: true,
9689
+ data: { previousPage, currentPage: page }
9690
+ };
9691
+ } catch (err) {
9692
+ return {
9693
+ success: false,
9694
+ error: { code: "NAVIGATION_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9695
+ };
9696
+ }
9697
+ },
9698
+ highlightText: async (text, options) => {
9699
+ try {
9700
+ const highlightIds = await handle.highlightText(text, options);
9701
+ return {
9702
+ success: true,
9703
+ data: { matchCount: highlightIds.length, highlightIds }
9704
+ };
9705
+ } catch (err) {
9706
+ return {
9707
+ success: false,
9708
+ error: { code: "HIGHLIGHT_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9709
+ };
9710
+ }
9711
+ },
9712
+ getPageContent: async (page) => {
9713
+ try {
9714
+ const doc = viewerStore.getState().document;
9715
+ if (!doc) {
9716
+ return {
9717
+ success: false,
9718
+ error: { code: "NO_DOCUMENT", message: "No document is loaded" }
9719
+ };
9720
+ }
9721
+ if (page < 1 || page > doc.numPages) {
9722
+ return {
9723
+ success: false,
9724
+ error: { code: "INVALID_PAGE", message: `Page ${page} is out of range (1-${doc.numPages})` }
9725
+ };
9726
+ }
9727
+ const pageObj = await doc.getPage(page);
9728
+ const textContent = await pageObj.getTextContent();
9729
+ const text = textContent.items.filter((item) => "str" in item).map((item) => item.str).join("");
9730
+ return {
9731
+ success: true,
9732
+ data: { text }
9733
+ };
9734
+ } catch (err) {
9735
+ return {
9736
+ success: false,
9737
+ error: { code: "CONTENT_FETCH_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9738
+ };
9739
+ }
9740
+ },
9741
+ clearAllVisuals: async () => {
9742
+ try {
9743
+ const highlights = annotationStore.getState().highlights;
9744
+ for (const h of highlights) {
9745
+ annotationStore.getState().removeHighlight(h.id);
9746
+ }
9747
+ const annotations = annotationStore.getState().annotations;
9748
+ for (const a of annotations) {
9749
+ annotationStore.getState().removeAnnotation(a.id);
9750
+ }
9751
+ return { success: true };
9752
+ } catch (err) {
9753
+ return {
9754
+ success: false,
9755
+ error: { code: "CLEAR_FAILED", message: err instanceof Error ? err.message : "Unknown error" }
9756
+ };
9757
+ }
9758
+ }
9759
+ },
9760
+ // ==================== Coordinate Helpers ====================
9761
+ coordinates: {
9762
+ getPageDimensions: (page) => {
9763
+ const doc = viewerStore.getState().document;
9764
+ if (!doc || page < 1 || page > doc.numPages) {
9765
+ return null;
9766
+ }
9767
+ try {
9768
+ return {
9769
+ width: 612,
9770
+ // Default US Letter width
9771
+ height: 792,
9772
+ // Default US Letter height
9773
+ rotation: viewerStore.getState().rotation
9774
+ };
9775
+ } catch {
9776
+ return null;
9777
+ }
9778
+ },
9779
+ percentToPixels: (xPercent, yPercent, page) => {
9780
+ const dimensions = handle.coordinates.getPageDimensions(page);
9781
+ if (!dimensions) return null;
9782
+ const scale2 = viewerStore.getState().scale;
9783
+ return {
9784
+ x: xPercent / 100 * dimensions.width * scale2,
9785
+ y: yPercent / 100 * dimensions.height * scale2
9786
+ };
9787
+ },
9788
+ pixelsToPercent: (x, y, page) => {
9789
+ const dimensions = handle.coordinates.getPageDimensions(page);
9790
+ if (!dimensions) return null;
9791
+ const scale2 = viewerStore.getState().scale;
9792
+ return {
9793
+ x: x / (dimensions.width * scale2) * 100,
9794
+ y: y / (dimensions.height * scale2) * 100
9795
+ };
9796
+ }
9797
+ },
9798
+ // ==================== Document ====================
9799
+ getDocument: () => {
9800
+ return viewerStore.getState().document;
9801
+ },
9802
+ isLoaded: () => {
9803
+ return viewerStore.getState().document !== null;
9804
+ }
9805
+ };
9806
+ handleRef.current = handle;
9807
+ onReadyRef.current?.(handle);
9808
+ }, [viewerStore, annotationStore, searchStore]);
9809
+ const handleRetry = useCallback33(() => {
8812
9810
  srcIdRef.current = null;
8813
9811
  viewerStore.getState().setError(null);
8814
9812
  setLoadState("idle");
@@ -8846,8 +9844,12 @@ var init_PDFViewerClient = __esm({
8846
9844
  if (initialPage !== 1) {
8847
9845
  viewerStore.getState().goToPage(initialPage);
8848
9846
  }
8849
- if (typeof initialScale === "number" && initialScale !== 1) {
9847
+ if (typeof initialScale === "number") {
8850
9848
  viewerStore.getState().setScale(initialScale);
9849
+ } else if (initialScale === "auto" || initialScale === "page-width") {
9850
+ viewerStore.getState().setScale(1);
9851
+ } else if (initialScale === "page-fit") {
9852
+ viewerStore.getState().setScale(0.75);
8851
9853
  }
8852
9854
  onDocumentLoadRef.current?.({ document: document2, numPages });
8853
9855
  } else {
@@ -8879,11 +9881,37 @@ var init_PDFViewerClient = __esm({
8879
9881
  if (prevScaleRef.current !== scale) {
8880
9882
  prevScaleRef.current = scale;
8881
9883
  onScaleChangeRef.current?.(scale);
9884
+ onZoomChangeRef.current?.(scale);
8882
9885
  }
8883
9886
  }, [scale]);
9887
+ useEffect22(() => {
9888
+ if (!isControlled || controlledPage === void 0) return;
9889
+ if (prevControlledPageRef.current === controlledPage) return;
9890
+ prevControlledPageRef.current = controlledPage;
9891
+ const { numPages, currentPage: currentPage2 } = viewerStore.getState();
9892
+ if (numPages > 0 && controlledPage !== currentPage2) {
9893
+ viewerStore.getState().requestScrollToPage(controlledPage, "smooth");
9894
+ }
9895
+ }, [controlledPage, isControlled, viewerStore]);
8884
9896
  const themeClass = theme === "dark" ? "dark" : "";
8885
9897
  if (error) {
8886
- return /* @__PURE__ */ jsx25(
9898
+ if (errorComponent) {
9899
+ const errorContent = typeof errorComponent === "function" ? errorComponent(error, handleRetry) : errorComponent;
9900
+ return /* @__PURE__ */ jsx26(
9901
+ "div",
9902
+ {
9903
+ className: cn(
9904
+ "pdf-viewer pdf-viewer-error",
9905
+ "flex flex-col h-full",
9906
+ "bg-white dark:bg-gray-900",
9907
+ themeClass,
9908
+ className
9909
+ ),
9910
+ children: errorContent
9911
+ }
9912
+ );
9913
+ }
9914
+ return /* @__PURE__ */ jsx26(
8887
9915
  "div",
8888
9916
  {
8889
9917
  className: cn(
@@ -8893,10 +9921,10 @@ var init_PDFViewerClient = __esm({
8893
9921
  themeClass,
8894
9922
  className
8895
9923
  ),
8896
- children: /* @__PURE__ */ jsx25("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs21("div", { className: "text-center p-8", children: [
8897
- /* @__PURE__ */ jsx25("div", { className: "text-red-500 text-lg font-semibold mb-2", children: "Failed to load PDF" }),
8898
- /* @__PURE__ */ jsx25("div", { className: "text-gray-500 text-sm", children: error.message }),
8899
- /* @__PURE__ */ jsx25(
9924
+ children: /* @__PURE__ */ jsx26("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs22("div", { className: "text-center p-8", children: [
9925
+ /* @__PURE__ */ jsx26("div", { className: "text-red-500 text-lg font-semibold mb-2", children: "Failed to load PDF" }),
9926
+ /* @__PURE__ */ jsx26("div", { className: "text-gray-500 text-sm", children: error.message }),
9927
+ /* @__PURE__ */ jsx26(
8900
9928
  "button",
8901
9929
  {
8902
9930
  onClick: handleRetry,
@@ -8911,56 +9939,73 @@ var init_PDFViewerClient = __esm({
8911
9939
  const renderContainer = () => {
8912
9940
  switch (viewMode) {
8913
9941
  case "continuous":
8914
- return /* @__PURE__ */ jsx25(ContinuousScrollContainer, {});
9942
+ return /* @__PURE__ */ jsx26(ContinuousScrollContainer, {});
8915
9943
  case "dual":
8916
- return /* @__PURE__ */ jsx25(DualPageContainer, {});
9944
+ return /* @__PURE__ */ jsx26(DualPageContainer, {});
8917
9945
  case "single":
8918
9946
  default:
8919
- return /* @__PURE__ */ jsx25(DocumentContainer, {});
9947
+ return /* @__PURE__ */ jsx26(DocumentContainer, {});
8920
9948
  }
8921
9949
  };
8922
- return /* @__PURE__ */ jsxs21(
9950
+ return /* @__PURE__ */ jsxs22(
8923
9951
  "div",
8924
9952
  {
8925
9953
  className: cn(
8926
9954
  "pdf-viewer",
8927
- "flex flex-col h-full",
9955
+ "flex flex-col h-full relative",
8928
9956
  "bg-white dark:bg-gray-900",
8929
9957
  "text-gray-900 dark:text-gray-100",
8930
9958
  themeClass,
8931
9959
  className
8932
9960
  ),
8933
9961
  children: [
8934
- showToolbar && /* @__PURE__ */ jsx25(Toolbar, {}),
8935
- showAnnotationToolbar && /* @__PURE__ */ jsx25(AnnotationToolbar, {}),
8936
- /* @__PURE__ */ jsxs21("div", { className: "flex flex-1 overflow-hidden", children: [
8937
- showSidebar && sidebarOpen && /* @__PURE__ */ jsx25(Sidebar, {}),
9962
+ showToolbar && /* @__PURE__ */ jsx26(Toolbar, {}),
9963
+ showAnnotationToolbar && /* @__PURE__ */ jsx26(AnnotationToolbar, {}),
9964
+ /* @__PURE__ */ jsxs22("div", { className: "flex flex-1 overflow-hidden", children: [
9965
+ showSidebar && sidebarOpen && /* @__PURE__ */ jsx26(Sidebar, {}),
8938
9966
  renderContainer()
8939
9967
  ] }),
8940
- isLoading && /* @__PURE__ */ jsx25("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-900/80", children: /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center", children: [
8941
- /* @__PURE__ */ jsx25("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
8942
- /* @__PURE__ */ jsx25("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF..." })
9968
+ showFloatingZoom && /* @__PURE__ */ jsx26(FloatingZoomControls, { position: "bottom-right" }),
9969
+ isLoading && /* @__PURE__ */ jsx26("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 dark:bg-gray-900/80", children: loadingComponent ?? /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center", children: [
9970
+ /* @__PURE__ */ jsx26("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
9971
+ /* @__PURE__ */ jsx26("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF..." })
8943
9972
  ] }) })
8944
9973
  ]
8945
9974
  }
8946
9975
  );
8947
9976
  });
8948
- PDFViewerClient = memo24(function PDFViewerClient2(props) {
8949
- return /* @__PURE__ */ jsx25(
8950
- PDFViewerProvider,
8951
- {
8952
- theme: props.theme,
8953
- defaultSidebarPanel: props.defaultSidebarPanel,
8954
- children: /* @__PURE__ */ jsx25(PDFViewerInner, { ...props })
8955
- }
8956
- );
8957
- });
9977
+ PDFViewerInnerWithRef = forwardRef(
9978
+ function PDFViewerInnerWithRef2(props, ref) {
9979
+ const handleRef = useRef19(null);
9980
+ const handleReady = useCallback33((handle) => {
9981
+ handleRef.current = handle;
9982
+ if (typeof ref === "function") {
9983
+ ref(handle);
9984
+ } else if (ref) {
9985
+ ref.current = handle;
9986
+ }
9987
+ }, [ref]);
9988
+ return /* @__PURE__ */ jsx26(PDFViewerInner, { ...props, onReady: handleReady });
9989
+ }
9990
+ );
9991
+ PDFViewerClient = memo25(
9992
+ forwardRef(function PDFViewerClient2(props, ref) {
9993
+ return /* @__PURE__ */ jsx26(
9994
+ PDFViewerProvider,
9995
+ {
9996
+ theme: props.theme,
9997
+ defaultSidebarPanel: props.defaultSidebarPanel,
9998
+ children: /* @__PURE__ */ jsx26(PDFViewerInnerWithRef, { ref, ...props })
9999
+ }
10000
+ );
10001
+ })
10002
+ );
8958
10003
  }
8959
10004
  });
8960
10005
 
8961
10006
  // src/components/PDFViewer/PDFViewer.tsx
8962
- import { lazy, Suspense, memo as memo25 } from "react";
8963
- import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
10007
+ import { lazy, Suspense, memo as memo26 } from "react";
10008
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
8964
10009
  var PDFViewerClient3, PDFViewerLoading, PDFViewer;
8965
10010
  var init_PDFViewer = __esm({
8966
10011
  "src/components/PDFViewer/PDFViewer.tsx"() {
@@ -8969,10 +10014,10 @@ var init_PDFViewer = __esm({
8969
10014
  PDFViewerClient3 = lazy(
8970
10015
  () => Promise.resolve().then(() => (init_PDFViewerClient(), PDFViewerClient_exports)).then((mod) => ({ default: mod.PDFViewerClient }))
8971
10016
  );
8972
- PDFViewerLoading = memo25(function PDFViewerLoading2({
10017
+ PDFViewerLoading = memo26(function PDFViewerLoading2({
8973
10018
  className
8974
10019
  }) {
8975
- return /* @__PURE__ */ jsx26(
10020
+ return /* @__PURE__ */ jsx27(
8976
10021
  "div",
8977
10022
  {
8978
10023
  className: cn(
@@ -8981,18 +10026,18 @@ var init_PDFViewer = __esm({
8981
10026
  "bg-white dark:bg-gray-900",
8982
10027
  className
8983
10028
  ),
8984
- children: /* @__PURE__ */ jsx26("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center", children: [
8985
- /* @__PURE__ */ jsx26("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
8986
- /* @__PURE__ */ jsx26("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF viewer..." })
10029
+ children: /* @__PURE__ */ jsx27("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs23("div", { className: "flex flex-col items-center", children: [
10030
+ /* @__PURE__ */ jsx27("div", { className: "w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" }),
10031
+ /* @__PURE__ */ jsx27("div", { className: "mt-2 text-sm text-gray-500", children: "Loading PDF viewer..." })
8987
10032
  ] }) })
8988
10033
  }
8989
10034
  );
8990
10035
  });
8991
- PDFViewer = memo25(function PDFViewer2(props) {
10036
+ PDFViewer = memo26(function PDFViewer2(props) {
8992
10037
  if (typeof window === "undefined") {
8993
- return /* @__PURE__ */ jsx26(PDFViewerLoading, { className: props.className });
10038
+ return /* @__PURE__ */ jsx27(PDFViewerLoading, { className: props.className });
8994
10039
  }
8995
- return /* @__PURE__ */ jsx26(Suspense, { fallback: /* @__PURE__ */ jsx26(PDFViewerLoading, { className: props.className }), children: /* @__PURE__ */ jsx26(PDFViewerClient3, { ...props }) });
10040
+ return /* @__PURE__ */ jsx27(Suspense, { fallback: /* @__PURE__ */ jsx27(PDFViewerLoading, { className: props.className }), children: /* @__PURE__ */ jsx27(PDFViewerClient3, { ...props }) });
8996
10041
  });
8997
10042
  }
8998
10043
  });
@@ -9021,8 +10066,8 @@ init_AnnotationToolbar2();
9021
10066
 
9022
10067
  // src/components/Annotations/StickyNote.tsx
9023
10068
  init_utils();
9024
- import { memo as memo26, useState as useState22, useRef as useRef20, useEffect as useEffect23, useCallback as useCallback33 } from "react";
9025
- import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
10069
+ import { memo as memo27, useState as useState22, useRef as useRef20, useEffect as useEffect23, useCallback as useCallback34 } from "react";
10070
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
9026
10071
  var NOTE_COLORS = [
9027
10072
  "#fef08a",
9028
10073
  // yellow
@@ -9035,7 +10080,7 @@ var NOTE_COLORS = [
9035
10080
  "#fed7aa"
9036
10081
  // orange
9037
10082
  ];
9038
- var StickyNote = memo26(function StickyNote2({
10083
+ var StickyNote = memo27(function StickyNote2({
9039
10084
  note,
9040
10085
  scale,
9041
10086
  isSelected,
@@ -9061,24 +10106,24 @@ var StickyNote = memo26(function StickyNote2({
9061
10106
  textareaRef.current.select();
9062
10107
  }
9063
10108
  }, [isEditing]);
9064
- const handleClick = useCallback33((e) => {
10109
+ const handleClick = useCallback34((e) => {
9065
10110
  e.stopPropagation();
9066
10111
  onSelect?.();
9067
10112
  if (!isExpanded) {
9068
10113
  setIsExpanded(true);
9069
10114
  }
9070
10115
  }, [isExpanded, onSelect]);
9071
- const handleDoubleClick = useCallback33((e) => {
10116
+ const handleDoubleClick = useCallback34((e) => {
9072
10117
  e.stopPropagation();
9073
10118
  onStartEdit?.();
9074
10119
  }, [onStartEdit]);
9075
- const handleBlur = useCallback33(() => {
10120
+ const handleBlur = useCallback34(() => {
9076
10121
  if (isEditing && localContent !== note.content) {
9077
10122
  onUpdate?.({ content: localContent });
9078
10123
  }
9079
10124
  onEndEdit?.();
9080
10125
  }, [isEditing, localContent, note.content, onUpdate, onEndEdit]);
9081
- const handleKeyDown = useCallback33((e) => {
10126
+ const handleKeyDown = useCallback34((e) => {
9082
10127
  if (e.key === "Escape") {
9083
10128
  setLocalContent(note.content);
9084
10129
  onEndEdit?.();
@@ -9086,16 +10131,16 @@ var StickyNote = memo26(function StickyNote2({
9086
10131
  handleBlur();
9087
10132
  }
9088
10133
  }, [note.content, onEndEdit, handleBlur]);
9089
- const handleColorChange = useCallback33((color) => {
10134
+ const handleColorChange = useCallback34((color) => {
9090
10135
  onUpdate?.({ color });
9091
10136
  }, [onUpdate]);
9092
- const handleCollapse = useCallback33((e) => {
10137
+ const handleCollapse = useCallback34((e) => {
9093
10138
  e.stopPropagation();
9094
10139
  setIsExpanded(false);
9095
10140
  onEndEdit?.();
9096
10141
  }, [onEndEdit]);
9097
10142
  if (!isExpanded) {
9098
- return /* @__PURE__ */ jsx27(
10143
+ return /* @__PURE__ */ jsx28(
9099
10144
  "div",
9100
10145
  {
9101
10146
  ref: noteRef,
@@ -9116,14 +10161,14 @@ var StickyNote = memo26(function StickyNote2({
9116
10161
  onMouseDown: onDragStart,
9117
10162
  onTouchStart: onDragStart,
9118
10163
  title: note.content || "Empty note",
9119
- children: /* @__PURE__ */ jsx27(
10164
+ children: /* @__PURE__ */ jsx28(
9120
10165
  "svg",
9121
10166
  {
9122
10167
  className: "w-4 h-4 opacity-70",
9123
10168
  fill: "currentColor",
9124
10169
  viewBox: "0 0 20 20",
9125
10170
  style: { color: "#333" },
9126
- children: /* @__PURE__ */ jsx27(
10171
+ children: /* @__PURE__ */ jsx28(
9127
10172
  "path",
9128
10173
  {
9129
10174
  fillRule: "evenodd",
@@ -9136,7 +10181,7 @@ var StickyNote = memo26(function StickyNote2({
9136
10181
  }
9137
10182
  );
9138
10183
  }
9139
- return /* @__PURE__ */ jsxs23(
10184
+ return /* @__PURE__ */ jsxs24(
9140
10185
  "div",
9141
10186
  {
9142
10187
  ref: noteRef,
@@ -9154,14 +10199,14 @@ var StickyNote = memo26(function StickyNote2({
9154
10199
  },
9155
10200
  onClick: handleClick,
9156
10201
  children: [
9157
- /* @__PURE__ */ jsxs23(
10202
+ /* @__PURE__ */ jsxs24(
9158
10203
  "div",
9159
10204
  {
9160
10205
  className: "flex items-center justify-between px-2 py-1 border-b border-black/10 cursor-move",
9161
10206
  onMouseDown: onDragStart,
9162
10207
  onTouchStart: onDragStart,
9163
10208
  children: [
9164
- /* @__PURE__ */ jsx27("div", { className: "flex gap-1", children: NOTE_COLORS.map((color) => /* @__PURE__ */ jsx27(
10209
+ /* @__PURE__ */ jsx28("div", { className: "flex gap-1", children: NOTE_COLORS.map((color) => /* @__PURE__ */ jsx28(
9165
10210
  "button",
9166
10211
  {
9167
10212
  className: cn(
@@ -9178,8 +10223,8 @@ var StickyNote = memo26(function StickyNote2({
9178
10223
  },
9179
10224
  color
9180
10225
  )) }),
9181
- /* @__PURE__ */ jsxs23("div", { className: "flex gap-1", children: [
9182
- /* @__PURE__ */ jsx27(
10226
+ /* @__PURE__ */ jsxs24("div", { className: "flex gap-1", children: [
10227
+ /* @__PURE__ */ jsx28(
9183
10228
  "button",
9184
10229
  {
9185
10230
  className: "p-0.5 hover:bg-black/10 rounded",
@@ -9188,23 +10233,23 @@ var StickyNote = memo26(function StickyNote2({
9188
10233
  onDelete?.();
9189
10234
  },
9190
10235
  title: "Delete note",
9191
- children: /* @__PURE__ */ jsx27("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx27("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" }) })
10236
+ children: /* @__PURE__ */ jsx28("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx28("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" }) })
9192
10237
  }
9193
10238
  ),
9194
- /* @__PURE__ */ jsx27(
10239
+ /* @__PURE__ */ jsx28(
9195
10240
  "button",
9196
10241
  {
9197
10242
  className: "p-0.5 hover:bg-black/10 rounded",
9198
10243
  onClick: handleCollapse,
9199
10244
  title: "Collapse note",
9200
- children: /* @__PURE__ */ jsx27("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx27("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
10245
+ children: /* @__PURE__ */ jsx28("svg", { className: "w-3.5 h-3.5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx28("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
9201
10246
  }
9202
10247
  )
9203
10248
  ] })
9204
10249
  ]
9205
10250
  }
9206
10251
  ),
9207
- /* @__PURE__ */ jsx27("div", { className: "p-2", children: isEditing ? /* @__PURE__ */ jsx27(
10252
+ /* @__PURE__ */ jsx28("div", { className: "p-2", children: isEditing ? /* @__PURE__ */ jsx28(
9208
10253
  "textarea",
9209
10254
  {
9210
10255
  ref: textareaRef,
@@ -9219,7 +10264,7 @@ var StickyNote = memo26(function StickyNote2({
9219
10264
  onKeyDown: handleKeyDown,
9220
10265
  placeholder: "Enter note..."
9221
10266
  }
9222
- ) : /* @__PURE__ */ jsx27(
10267
+ ) : /* @__PURE__ */ jsx28(
9223
10268
  "div",
9224
10269
  {
9225
10270
  className: cn(
@@ -9230,7 +10275,7 @@ var StickyNote = memo26(function StickyNote2({
9230
10275
  children: note.content || "Double-click to edit..."
9231
10276
  }
9232
10277
  ) }),
9233
- /* @__PURE__ */ jsx27("div", { className: "px-2 pb-1 text-[10px] text-gray-500", children: new Date(note.updatedAt).toLocaleDateString() })
10278
+ /* @__PURE__ */ jsx28("div", { className: "px-2 pb-1 text-[10px] text-gray-500", children: new Date(note.updatedAt).toLocaleDateString() })
9234
10279
  ]
9235
10280
  }
9236
10281
  );
@@ -9238,8 +10283,8 @@ var StickyNote = memo26(function StickyNote2({
9238
10283
 
9239
10284
  // src/components/Annotations/DrawingCanvas.tsx
9240
10285
  init_utils();
9241
- import { memo as memo27, useRef as useRef21, useCallback as useCallback34, useState as useState23 } from "react";
9242
- import { jsx as jsx28 } from "react/jsx-runtime";
10286
+ import { memo as memo28, useRef as useRef21, useCallback as useCallback35, useState as useState23 } from "react";
10287
+ import { jsx as jsx29 } from "react/jsx-runtime";
9243
10288
  function pointsToSvgPath(points) {
9244
10289
  if (points.length === 0) return "";
9245
10290
  if (points.length === 1) {
@@ -9277,7 +10322,7 @@ function simplifyPath(points, tolerance = 1) {
9277
10322
  result.push(points[points.length - 1]);
9278
10323
  return result;
9279
10324
  }
9280
- var DrawingCanvas = memo27(function DrawingCanvas2({
10325
+ var DrawingCanvas = memo28(function DrawingCanvas2({
9281
10326
  width,
9282
10327
  height,
9283
10328
  scale,
@@ -9290,7 +10335,7 @@ var DrawingCanvas = memo27(function DrawingCanvas2({
9290
10335
  const svgRef = useRef21(null);
9291
10336
  const [isDrawing, setIsDrawing] = useState23(false);
9292
10337
  const [currentPath, setCurrentPath] = useState23([]);
9293
- const getPoint = useCallback34((e) => {
10338
+ const getPoint = useCallback35((e) => {
9294
10339
  if (!svgRef.current) return null;
9295
10340
  const svg = svgRef.current;
9296
10341
  const rect = svg.getBoundingClientRect();
@@ -9310,7 +10355,7 @@ var DrawingCanvas = memo27(function DrawingCanvas2({
9310
10355
  y: (clientY - rect.top) / scale
9311
10356
  };
9312
10357
  }, [scale]);
9313
- const handleStart = useCallback34((e) => {
10358
+ const handleStart = useCallback35((e) => {
9314
10359
  if (!isActive) return;
9315
10360
  const point = getPoint(e);
9316
10361
  if (point) {
@@ -9318,14 +10363,14 @@ var DrawingCanvas = memo27(function DrawingCanvas2({
9318
10363
  setCurrentPath([point]);
9319
10364
  }
9320
10365
  }, [isActive, getPoint]);
9321
- const handleMove = useCallback34((e) => {
10366
+ const handleMove = useCallback35((e) => {
9322
10367
  if (!isDrawing || !isActive) return;
9323
10368
  const point = getPoint(e);
9324
10369
  if (point) {
9325
10370
  setCurrentPath((prev) => [...prev, point]);
9326
10371
  }
9327
10372
  }, [isDrawing, isActive, getPoint]);
9328
- const handleEnd = useCallback34(() => {
10373
+ const handleEnd = useCallback35(() => {
9329
10374
  if (!isDrawing) return;
9330
10375
  setIsDrawing(false);
9331
10376
  if (currentPath.length >= 2) {
@@ -9334,7 +10379,7 @@ var DrawingCanvas = memo27(function DrawingCanvas2({
9334
10379
  }
9335
10380
  setCurrentPath([]);
9336
10381
  }, [isDrawing, currentPath, onDrawingComplete]);
9337
- return /* @__PURE__ */ jsx28(
10382
+ return /* @__PURE__ */ jsx29(
9338
10383
  "svg",
9339
10384
  {
9340
10385
  ref: svgRef,
@@ -9354,7 +10399,7 @@ var DrawingCanvas = memo27(function DrawingCanvas2({
9354
10399
  onTouchStart: handleStart,
9355
10400
  onTouchMove: handleMove,
9356
10401
  onTouchEnd: handleEnd,
9357
- children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ jsx28(
10402
+ children: isDrawing && currentPath.length > 0 && /* @__PURE__ */ jsx29(
9358
10403
  "path",
9359
10404
  {
9360
10405
  d: pointsToSvgPath(currentPath),
@@ -9372,9 +10417,9 @@ var DrawingCanvas = memo27(function DrawingCanvas2({
9372
10417
 
9373
10418
  // src/components/Annotations/ShapeRenderer.tsx
9374
10419
  init_utils();
9375
- import { memo as memo28, useCallback as useCallback35, useState as useState24, useRef as useRef22 } from "react";
9376
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
9377
- var ShapeRenderer = memo28(function ShapeRenderer2({
10420
+ import { memo as memo29, useCallback as useCallback36, useState as useState24, useRef as useRef22 } from "react";
10421
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
10422
+ var ShapeRenderer = memo29(function ShapeRenderer2({
9378
10423
  shape,
9379
10424
  scale,
9380
10425
  isSelected,
@@ -9395,7 +10440,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9395
10440
  const scaledWidth = width * scale;
9396
10441
  const scaledHeight = height * scale;
9397
10442
  const scaledStroke = strokeWidth * scale;
9398
- const getResizeHandles = useCallback35(() => {
10443
+ const getResizeHandles = useCallback36(() => {
9399
10444
  const handleSize = 8;
9400
10445
  const half = handleSize / 2;
9401
10446
  return [
@@ -9409,7 +10454,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9409
10454
  { position: "w", cursor: "ew-resize", x: scaledX - half, y: scaledY + scaledHeight / 2 - half }
9410
10455
  ];
9411
10456
  }, [scaledX, scaledY, scaledWidth, scaledHeight]);
9412
- const handleMouseDown = useCallback35((e, handle) => {
10457
+ const handleMouseDown = useCallback36((e, handle) => {
9413
10458
  e.stopPropagation();
9414
10459
  onSelect?.();
9415
10460
  if (!isEditing) return;
@@ -9485,7 +10530,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9485
10530
  document.addEventListener("mousemove", handleMouseMove);
9486
10531
  document.addEventListener("mouseup", handleMouseUp);
9487
10532
  }, [isEditing, x, y, width, height, scale, onSelect, onUpdate]);
9488
- const renderShape2 = useCallback35(() => {
10533
+ const renderShape2 = useCallback36(() => {
9489
10534
  const commonProps = {
9490
10535
  stroke: color,
9491
10536
  strokeWidth: scaledStroke,
@@ -9497,7 +10542,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9497
10542
  };
9498
10543
  switch (shapeType) {
9499
10544
  case "rect":
9500
- return /* @__PURE__ */ jsx29(
10545
+ return /* @__PURE__ */ jsx30(
9501
10546
  "rect",
9502
10547
  {
9503
10548
  x: scaledX,
@@ -9508,7 +10553,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9508
10553
  }
9509
10554
  );
9510
10555
  case "circle":
9511
- return /* @__PURE__ */ jsx29(
10556
+ return /* @__PURE__ */ jsx30(
9512
10557
  "ellipse",
9513
10558
  {
9514
10559
  cx: scaledX + scaledWidth / 2,
@@ -9519,7 +10564,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9519
10564
  }
9520
10565
  );
9521
10566
  case "line":
9522
- return /* @__PURE__ */ jsx29(
10567
+ return /* @__PURE__ */ jsx30(
9523
10568
  "line",
9524
10569
  {
9525
10570
  x1: scaledX,
@@ -9539,22 +10584,22 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9539
10584
  const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
9540
10585
  const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
9541
10586
  const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
9542
- return /* @__PURE__ */ jsxs24("g", { children: [
9543
- /* @__PURE__ */ jsx29("line", { x1: scaledX, y1: scaledY, x2: endX, y2: endY, ...commonProps }),
9544
- /* @__PURE__ */ jsx29("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
9545
- /* @__PURE__ */ jsx29("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
10587
+ return /* @__PURE__ */ jsxs25("g", { children: [
10588
+ /* @__PURE__ */ jsx30("line", { x1: scaledX, y1: scaledY, x2: endX, y2: endY, ...commonProps }),
10589
+ /* @__PURE__ */ jsx30("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
10590
+ /* @__PURE__ */ jsx30("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
9546
10591
  ] });
9547
10592
  default:
9548
10593
  return null;
9549
10594
  }
9550
10595
  }, [shapeType, scaledX, scaledY, scaledWidth, scaledHeight, color, scaledStroke, isSelected]);
9551
- return /* @__PURE__ */ jsxs24(
10596
+ return /* @__PURE__ */ jsxs25(
9552
10597
  "g",
9553
10598
  {
9554
10599
  className: cn("shape-renderer", className),
9555
10600
  onMouseDown: (e) => handleMouseDown(e),
9556
10601
  children: [
9557
- /* @__PURE__ */ jsx29(
10602
+ /* @__PURE__ */ jsx30(
9558
10603
  "rect",
9559
10604
  {
9560
10605
  x: scaledX - 5,
@@ -9567,7 +10612,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9567
10612
  }
9568
10613
  ),
9569
10614
  renderShape2(),
9570
- isSelected && /* @__PURE__ */ jsx29(
10615
+ isSelected && /* @__PURE__ */ jsx30(
9571
10616
  "rect",
9572
10617
  {
9573
10618
  x: scaledX - 2,
@@ -9580,7 +10625,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9580
10625
  strokeDasharray: "4 2"
9581
10626
  }
9582
10627
  ),
9583
- isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ jsx29(
10628
+ isSelected && isEditing && getResizeHandles().map((handle) => /* @__PURE__ */ jsx30(
9584
10629
  "rect",
9585
10630
  {
9586
10631
  x: handle.x,
@@ -9600,7 +10645,7 @@ var ShapeRenderer = memo28(function ShapeRenderer2({
9600
10645
  }
9601
10646
  );
9602
10647
  });
9603
- var ShapePreview = memo28(function ShapePreview2({
10648
+ var ShapePreview = memo29(function ShapePreview2({
9604
10649
  shapeType,
9605
10650
  startPoint,
9606
10651
  endPoint,
@@ -9621,9 +10666,9 @@ var ShapePreview = memo28(function ShapePreview2({
9621
10666
  };
9622
10667
  switch (shapeType) {
9623
10668
  case "rect":
9624
- return /* @__PURE__ */ jsx29("rect", { x, y, width, height, ...commonProps });
10669
+ return /* @__PURE__ */ jsx30("rect", { x, y, width, height, ...commonProps });
9625
10670
  case "circle":
9626
- return /* @__PURE__ */ jsx29(
10671
+ return /* @__PURE__ */ jsx30(
9627
10672
  "ellipse",
9628
10673
  {
9629
10674
  cx: x + width / 2,
@@ -9634,7 +10679,7 @@ var ShapePreview = memo28(function ShapePreview2({
9634
10679
  }
9635
10680
  );
9636
10681
  case "line":
9637
- return /* @__PURE__ */ jsx29(
10682
+ return /* @__PURE__ */ jsx30(
9638
10683
  "line",
9639
10684
  {
9640
10685
  x1: startPoint.x * scale,
@@ -9656,8 +10701,8 @@ var ShapePreview = memo28(function ShapePreview2({
9656
10701
  const arrow1Y = endY - arrowLength * Math.sin(angle - arrowAngle);
9657
10702
  const arrow2X = endX - arrowLength * Math.cos(angle + arrowAngle);
9658
10703
  const arrow2Y = endY - arrowLength * Math.sin(angle + arrowAngle);
9659
- return /* @__PURE__ */ jsxs24("g", { children: [
9660
- /* @__PURE__ */ jsx29(
10704
+ return /* @__PURE__ */ jsxs25("g", { children: [
10705
+ /* @__PURE__ */ jsx30(
9661
10706
  "line",
9662
10707
  {
9663
10708
  x1: startPoint.x * scale,
@@ -9667,8 +10712,8 @@ var ShapePreview = memo28(function ShapePreview2({
9667
10712
  ...commonProps
9668
10713
  }
9669
10714
  ),
9670
- /* @__PURE__ */ jsx29("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
9671
- /* @__PURE__ */ jsx29("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
10715
+ /* @__PURE__ */ jsx30("line", { x1: endX, y1: endY, x2: arrow1X, y2: arrow1Y, ...commonProps }),
10716
+ /* @__PURE__ */ jsx30("line", { x1: endX, y1: endY, x2: arrow2X, y2: arrow2Y, ...commonProps })
9672
10717
  ] });
9673
10718
  default:
9674
10719
  return null;
@@ -9677,9 +10722,9 @@ var ShapePreview = memo28(function ShapePreview2({
9677
10722
 
9678
10723
  // src/components/Annotations/QuickNoteButton.tsx
9679
10724
  init_utils();
9680
- import { memo as memo29, useCallback as useCallback36, useState as useState25 } from "react";
9681
- import { jsx as jsx30 } from "react/jsx-runtime";
9682
- var QuickNoteButton = memo29(function QuickNoteButton2({
10725
+ import { memo as memo30, useCallback as useCallback37, useState as useState25 } from "react";
10726
+ import { jsx as jsx31 } from "react/jsx-runtime";
10727
+ var QuickNoteButton = memo30(function QuickNoteButton2({
9683
10728
  pageNumber,
9684
10729
  scale,
9685
10730
  position = "top-right",
@@ -9688,7 +10733,7 @@ var QuickNoteButton = memo29(function QuickNoteButton2({
9688
10733
  visible = true
9689
10734
  }) {
9690
10735
  const [isHovered, setIsHovered] = useState25(false);
9691
- const handleClick = useCallback36(
10736
+ const handleClick = useCallback37(
9692
10737
  (e) => {
9693
10738
  e.stopPropagation();
9694
10739
  const x = position === "top-right" ? 80 : 80;
@@ -9700,7 +10745,7 @@ var QuickNoteButton = memo29(function QuickNoteButton2({
9700
10745
  if (!visible) {
9701
10746
  return null;
9702
10747
  }
9703
- return /* @__PURE__ */ jsx30(
10748
+ return /* @__PURE__ */ jsx31(
9704
10749
  "button",
9705
10750
  {
9706
10751
  onClick: handleClick,
@@ -9722,7 +10767,7 @@ var QuickNoteButton = memo29(function QuickNoteButton2({
9722
10767
  ),
9723
10768
  title: "Add quick note",
9724
10769
  "aria-label": "Add quick note",
9725
- children: /* @__PURE__ */ jsx30(
10770
+ children: /* @__PURE__ */ jsx31(
9726
10771
  "svg",
9727
10772
  {
9728
10773
  className: "w-4 h-4 text-yellow-900",
@@ -9730,7 +10775,7 @@ var QuickNoteButton = memo29(function QuickNoteButton2({
9730
10775
  viewBox: "0 0 24 24",
9731
10776
  stroke: "currentColor",
9732
10777
  strokeWidth: 2,
9733
- children: /* @__PURE__ */ jsx30("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4v16m8-8H4" })
10778
+ children: /* @__PURE__ */ jsx31("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4v16m8-8H4" })
9734
10779
  }
9735
10780
  )
9736
10781
  }
@@ -9739,9 +10784,9 @@ var QuickNoteButton = memo29(function QuickNoteButton2({
9739
10784
 
9740
10785
  // src/components/Annotations/QuickNotePopover.tsx
9741
10786
  init_utils();
9742
- import { memo as memo30, useCallback as useCallback37, useState as useState26, useRef as useRef23, useEffect as useEffect24 } from "react";
9743
- import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
9744
- var QuickNotePopover = memo30(function QuickNotePopover2({
10787
+ import { memo as memo31, useCallback as useCallback38, useState as useState26, useRef as useRef23, useEffect as useEffect24 } from "react";
10788
+ import { jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
10789
+ var QuickNotePopover = memo31(function QuickNotePopover2({
9745
10790
  visible,
9746
10791
  position,
9747
10792
  initialContent = "",
@@ -9783,14 +10828,14 @@ var QuickNotePopover = memo30(function QuickNotePopover2({
9783
10828
  }
9784
10829
  setAdjustedPosition({ x, y });
9785
10830
  }, [position, visible]);
9786
- const handleSave = useCallback37(() => {
10831
+ const handleSave = useCallback38(() => {
9787
10832
  if (content.trim()) {
9788
10833
  onSave(content.trim());
9789
10834
  } else {
9790
10835
  onCancel();
9791
10836
  }
9792
10837
  }, [content, onSave, onCancel]);
9793
- const handleKeyDown = useCallback37(
10838
+ const handleKeyDown = useCallback38(
9794
10839
  (e) => {
9795
10840
  if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
9796
10841
  e.preventDefault();
@@ -9805,7 +10850,7 @@ var QuickNotePopover = memo30(function QuickNotePopover2({
9805
10850
  if (!visible) {
9806
10851
  return null;
9807
10852
  }
9808
- return /* @__PURE__ */ jsxs25(
10853
+ return /* @__PURE__ */ jsxs26(
9809
10854
  "div",
9810
10855
  {
9811
10856
  ref: popoverRef,
@@ -9824,15 +10869,15 @@ var QuickNotePopover = memo30(function QuickNotePopover2({
9824
10869
  top: adjustedPosition.y
9825
10870
  },
9826
10871
  children: [
9827
- agentLastStatement && /* @__PURE__ */ jsx31("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__ */ jsxs25("div", { className: "flex items-start gap-1", children: [
9828
- /* @__PURE__ */ jsx31("svg", { className: "w-3 h-3 mt-0.5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx31("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" }) }),
9829
- /* @__PURE__ */ jsxs25("span", { className: "line-clamp-2", children: [
10872
+ agentLastStatement && /* @__PURE__ */ jsx32("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__ */ jsxs26("div", { className: "flex items-start gap-1", children: [
10873
+ /* @__PURE__ */ jsx32("svg", { className: "w-3 h-3 mt-0.5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx32("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" }) }),
10874
+ /* @__PURE__ */ jsxs26("span", { className: "line-clamp-2", children: [
9830
10875
  "AI discussed: \u201C",
9831
10876
  agentLastStatement,
9832
10877
  "\u201D"
9833
10878
  ] })
9834
10879
  ] }) }),
9835
- /* @__PURE__ */ jsx31(
10880
+ /* @__PURE__ */ jsx32(
9836
10881
  "textarea",
9837
10882
  {
9838
10883
  ref: textareaRef,
@@ -9851,13 +10896,13 @@ var QuickNotePopover = memo30(function QuickNotePopover2({
9851
10896
  )
9852
10897
  }
9853
10898
  ),
9854
- /* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between mt-2", children: [
9855
- /* @__PURE__ */ jsxs25("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
10899
+ /* @__PURE__ */ jsxs26("div", { className: "flex items-center justify-between mt-2", children: [
10900
+ /* @__PURE__ */ jsxs26("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
9856
10901
  navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
9857
10902
  "+Enter to save"
9858
10903
  ] }),
9859
- /* @__PURE__ */ jsxs25("div", { className: "flex gap-2", children: [
9860
- /* @__PURE__ */ jsx31(
10904
+ /* @__PURE__ */ jsxs26("div", { className: "flex gap-2", children: [
10905
+ /* @__PURE__ */ jsx32(
9861
10906
  "button",
9862
10907
  {
9863
10908
  onClick: onCancel,
@@ -9870,7 +10915,7 @@ var QuickNotePopover = memo30(function QuickNotePopover2({
9870
10915
  children: "Cancel"
9871
10916
  }
9872
10917
  ),
9873
- /* @__PURE__ */ jsx31(
10918
+ /* @__PURE__ */ jsx32(
9874
10919
  "button",
9875
10920
  {
9876
10921
  onClick: handleSave,
@@ -9894,9 +10939,9 @@ var QuickNotePopover = memo30(function QuickNotePopover2({
9894
10939
 
9895
10940
  // src/components/AskAbout/AskAboutOverlay.tsx
9896
10941
  init_utils();
9897
- import { memo as memo31 } from "react";
9898
- import { jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
9899
- var AskAboutOverlay = memo31(function AskAboutOverlay2({
10942
+ import { memo as memo32 } from "react";
10943
+ import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
10944
+ var AskAboutOverlay = memo32(function AskAboutOverlay2({
9900
10945
  visible,
9901
10946
  progress,
9902
10947
  position,
@@ -9910,7 +10955,7 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9910
10955
  const radius = (size - strokeWidth) / 2;
9911
10956
  const circumference = 2 * Math.PI * radius;
9912
10957
  const strokeDashoffset = circumference * (1 - progress);
9913
- return /* @__PURE__ */ jsxs26(
10958
+ return /* @__PURE__ */ jsxs27(
9914
10959
  "div",
9915
10960
  {
9916
10961
  className: cn(
@@ -9923,7 +10968,7 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9923
10968
  top: position.y - size / 2
9924
10969
  },
9925
10970
  children: [
9926
- /* @__PURE__ */ jsxs26(
10971
+ /* @__PURE__ */ jsxs27(
9927
10972
  "svg",
9928
10973
  {
9929
10974
  width: size,
@@ -9931,7 +10976,7 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9931
10976
  viewBox: `0 0 ${size} ${size}`,
9932
10977
  className: "transform -rotate-90",
9933
10978
  children: [
9934
- /* @__PURE__ */ jsx32(
10979
+ /* @__PURE__ */ jsx33(
9935
10980
  "circle",
9936
10981
  {
9937
10982
  cx: size / 2,
@@ -9942,7 +10987,7 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9942
10987
  strokeWidth
9943
10988
  }
9944
10989
  ),
9945
- /* @__PURE__ */ jsx32(
10990
+ /* @__PURE__ */ jsx33(
9946
10991
  "circle",
9947
10992
  {
9948
10993
  cx: size / 2,
@@ -9960,12 +11005,12 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9960
11005
  ]
9961
11006
  }
9962
11007
  ),
9963
- /* @__PURE__ */ jsx32(
11008
+ /* @__PURE__ */ jsx33(
9964
11009
  "div",
9965
11010
  {
9966
11011
  className: "absolute inset-0 flex items-center justify-center",
9967
11012
  style: { color: progress >= 1 ? "#22c55e" : "white" },
9968
- children: progress >= 1 ? /* @__PURE__ */ jsx32(
11013
+ children: progress >= 1 ? /* @__PURE__ */ jsx33(
9969
11014
  "svg",
9970
11015
  {
9971
11016
  width: "24",
@@ -9976,9 +11021,9 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9976
11021
  strokeWidth: "2",
9977
11022
  strokeLinecap: "round",
9978
11023
  strokeLinejoin: "round",
9979
- children: /* @__PURE__ */ jsx32("polyline", { points: "20 6 9 17 4 12" })
11024
+ children: /* @__PURE__ */ jsx33("polyline", { points: "20 6 9 17 4 12" })
9980
11025
  }
9981
- ) : /* @__PURE__ */ jsxs26(
11026
+ ) : /* @__PURE__ */ jsxs27(
9982
11027
  "svg",
9983
11028
  {
9984
11029
  width: "20",
@@ -9990,9 +11035,9 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
9990
11035
  strokeLinecap: "round",
9991
11036
  strokeLinejoin: "round",
9992
11037
  children: [
9993
- /* @__PURE__ */ jsx32("circle", { cx: "12", cy: "12", r: "10" }),
9994
- /* @__PURE__ */ jsx32("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
9995
- /* @__PURE__ */ jsx32("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
11038
+ /* @__PURE__ */ jsx33("circle", { cx: "12", cy: "12", r: "10" }),
11039
+ /* @__PURE__ */ jsx33("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
11040
+ /* @__PURE__ */ jsx33("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
9996
11041
  ]
9997
11042
  }
9998
11043
  )
@@ -10005,9 +11050,9 @@ var AskAboutOverlay = memo31(function AskAboutOverlay2({
10005
11050
 
10006
11051
  // src/components/AskAbout/AskAboutTrigger.tsx
10007
11052
  init_utils();
10008
- import { memo as memo32, useCallback as useCallback38, useState as useState27, useRef as useRef24, useEffect as useEffect25 } from "react";
10009
- import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
10010
- var AskAboutTrigger = memo32(function AskAboutTrigger2({
11053
+ import { memo as memo33, useCallback as useCallback39, useState as useState27, useRef as useRef24, useEffect as useEffect25 } from "react";
11054
+ import { jsx as jsx34, jsxs as jsxs28 } from "react/jsx-runtime";
11055
+ var AskAboutTrigger = memo33(function AskAboutTrigger2({
10011
11056
  position,
10012
11057
  onConfirm,
10013
11058
  onCancel,
@@ -10038,14 +11083,14 @@ var AskAboutTrigger = memo32(function AskAboutTrigger2({
10038
11083
  const timer = setTimeout(onCancel, autoHideDelay);
10039
11084
  return () => clearTimeout(timer);
10040
11085
  }, [visible, autoHideDelay, onCancel]);
10041
- const handleConfirm = useCallback38(
11086
+ const handleConfirm = useCallback39(
10042
11087
  (e) => {
10043
11088
  e.stopPropagation();
10044
11089
  onConfirm();
10045
11090
  },
10046
11091
  [onConfirm]
10047
11092
  );
10048
- const handleCancel = useCallback38(
11093
+ const handleCancel = useCallback39(
10049
11094
  (e) => {
10050
11095
  e.stopPropagation();
10051
11096
  onCancel();
@@ -10055,7 +11100,7 @@ var AskAboutTrigger = memo32(function AskAboutTrigger2({
10055
11100
  if (!visible) {
10056
11101
  return null;
10057
11102
  }
10058
- return /* @__PURE__ */ jsxs27(
11103
+ return /* @__PURE__ */ jsxs28(
10059
11104
  "div",
10060
11105
  {
10061
11106
  ref: triggerRef,
@@ -10074,8 +11119,8 @@ var AskAboutTrigger = memo32(function AskAboutTrigger2({
10074
11119
  transform: "translate(-50%, 0)"
10075
11120
  },
10076
11121
  children: [
10077
- /* @__PURE__ */ jsx33("span", { className: "text-sm text-gray-600 dark:text-gray-300 px-2", children: "Ask about this?" }),
10078
- /* @__PURE__ */ jsx33(
11122
+ /* @__PURE__ */ jsx34("span", { className: "text-sm text-gray-600 dark:text-gray-300 px-2", children: "Ask about this?" }),
11123
+ /* @__PURE__ */ jsx34(
10079
11124
  "button",
10080
11125
  {
10081
11126
  onClick: handleConfirm,
@@ -10088,7 +11133,7 @@ var AskAboutTrigger = memo32(function AskAboutTrigger2({
10088
11133
  children: "Ask"
10089
11134
  }
10090
11135
  ),
10091
- /* @__PURE__ */ jsx33(
11136
+ /* @__PURE__ */ jsx34(
10092
11137
  "button",
10093
11138
  {
10094
11139
  onClick: handleCancel,
@@ -10110,9 +11155,9 @@ var AskAboutTrigger = memo32(function AskAboutTrigger2({
10110
11155
  // src/components/Minimap/Minimap.tsx
10111
11156
  init_hooks();
10112
11157
  init_utils();
10113
- import { memo as memo33, useMemo as useMemo14, useCallback as useCallback39 } from "react";
10114
- import { Fragment as Fragment3, jsx as jsx34, jsxs as jsxs28 } from "react/jsx-runtime";
10115
- var PageIndicator = memo33(function PageIndicator2({
11158
+ import { memo as memo34, useMemo as useMemo14, useCallback as useCallback40 } from "react";
11159
+ import { Fragment as Fragment3, jsx as jsx35, jsxs as jsxs29 } from "react/jsx-runtime";
11160
+ var PageIndicator = memo34(function PageIndicator2({
10116
11161
  pageNumber,
10117
11162
  status,
10118
11163
  isBookmarked,
@@ -10126,7 +11171,7 @@ var PageIndicator = memo33(function PageIndicator2({
10126
11171
  if (status === "visited") return "bg-green-400";
10127
11172
  return "bg-gray-200 dark:bg-gray-700";
10128
11173
  };
10129
- return /* @__PURE__ */ jsxs28(
11174
+ return /* @__PURE__ */ jsxs29(
10130
11175
  "button",
10131
11176
  {
10132
11177
  onClick,
@@ -10142,13 +11187,13 @@ var PageIndicator = memo33(function PageIndicator2({
10142
11187
  title: `Page ${pageNumber}${isBookmarked ? " (bookmarked)" : ""}`,
10143
11188
  "aria-label": `Go to page ${pageNumber}`,
10144
11189
  children: [
10145
- isBookmarked && !compact && /* @__PURE__ */ jsx34("div", { className: "absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full border border-white" }),
10146
- showNumber && !compact && /* @__PURE__ */ jsx34("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-medium text-white", children: pageNumber })
11190
+ isBookmarked && !compact && /* @__PURE__ */ jsx35("div", { className: "absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full border border-white" }),
11191
+ showNumber && !compact && /* @__PURE__ */ jsx35("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-medium text-white", children: pageNumber })
10147
11192
  ]
10148
11193
  }
10149
11194
  );
10150
11195
  });
10151
- var Minimap = memo33(function Minimap2({
11196
+ var Minimap = memo34(function Minimap2({
10152
11197
  variant = "sidebar",
10153
11198
  floatingPosition = "right",
10154
11199
  maxHeight = 300,
@@ -10165,14 +11210,14 @@ var Minimap = memo33(function Minimap2({
10165
11210
  return new Set(bookmarks.map((b) => b.pageNumber));
10166
11211
  }, [bookmarks]);
10167
11212
  const compact = numPages > 50;
10168
- const handlePageClick = useCallback39(
11213
+ const handlePageClick = useCallback40(
10169
11214
  (pageNumber) => {
10170
11215
  goToPage(pageNumber);
10171
11216
  onPageClick?.(pageNumber);
10172
11217
  },
10173
11218
  [goToPage, onPageClick]
10174
11219
  );
10175
- const getPageStatus = useCallback39(
11220
+ const getPageStatus = useCallback40(
10176
11221
  (pageNumber) => {
10177
11222
  if (pageNumber === currentPage) return "current";
10178
11223
  if (bookmarkedPages.has(pageNumber)) return "bookmarked";
@@ -10185,7 +11230,7 @@ var Minimap = memo33(function Minimap2({
10185
11230
  const pages = [];
10186
11231
  for (let i = 1; i <= numPages; i++) {
10187
11232
  pages.push(
10188
- /* @__PURE__ */ jsx34(
11233
+ /* @__PURE__ */ jsx35(
10189
11234
  PageIndicator,
10190
11235
  {
10191
11236
  pageNumber: i,
@@ -10206,16 +11251,16 @@ var Minimap = memo33(function Minimap2({
10206
11251
  if (numPages === 0) {
10207
11252
  return null;
10208
11253
  }
10209
- const content = /* @__PURE__ */ jsxs28(Fragment3, { children: [
10210
- /* @__PURE__ */ jsxs28("div", { className: "mb-3", children: [
10211
- /* @__PURE__ */ jsxs28("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1", children: [
10212
- /* @__PURE__ */ jsx34("span", { children: "Progress" }),
10213
- /* @__PURE__ */ jsxs28("span", { children: [
11254
+ const content = /* @__PURE__ */ jsxs29(Fragment3, { children: [
11255
+ /* @__PURE__ */ jsxs29("div", { className: "mb-3", children: [
11256
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-1", children: [
11257
+ /* @__PURE__ */ jsx35("span", { children: "Progress" }),
11258
+ /* @__PURE__ */ jsxs29("span", { children: [
10214
11259
  progressPercentage,
10215
11260
  "%"
10216
11261
  ] })
10217
11262
  ] }),
10218
- /* @__PURE__ */ jsx34("div", { className: "h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx34(
11263
+ /* @__PURE__ */ jsx35("div", { className: "h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx35(
10219
11264
  "div",
10220
11265
  {
10221
11266
  className: "h-full bg-green-500 rounded-full transition-all duration-300",
@@ -10223,7 +11268,7 @@ var Minimap = memo33(function Minimap2({
10223
11268
  }
10224
11269
  ) })
10225
11270
  ] }),
10226
- /* @__PURE__ */ jsx34(
11271
+ /* @__PURE__ */ jsx35(
10227
11272
  "div",
10228
11273
  {
10229
11274
  className: cn(
@@ -10234,21 +11279,21 @@ var Minimap = memo33(function Minimap2({
10234
11279
  children: pageIndicators
10235
11280
  }
10236
11281
  ),
10237
- /* @__PURE__ */ jsx34("div", { className: "mt-3 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxs28("div", { className: "flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
10238
- /* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1", children: [
10239
- /* @__PURE__ */ jsx34("div", { className: "w-2 h-2 rounded-sm bg-blue-500" }),
10240
- /* @__PURE__ */ jsx34("span", { children: "Current" })
11282
+ /* @__PURE__ */ jsx35("div", { className: "mt-3 pt-2 border-t border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxs29("div", { className: "flex flex-wrap gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
11283
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-1", children: [
11284
+ /* @__PURE__ */ jsx35("div", { className: "w-2 h-2 rounded-sm bg-blue-500" }),
11285
+ /* @__PURE__ */ jsx35("span", { children: "Current" })
10241
11286
  ] }),
10242
- /* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1", children: [
10243
- /* @__PURE__ */ jsx34("div", { className: "w-2 h-2 rounded-sm bg-green-400" }),
10244
- /* @__PURE__ */ jsx34("span", { children: "Visited" })
11287
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-1", children: [
11288
+ /* @__PURE__ */ jsx35("div", { className: "w-2 h-2 rounded-sm bg-green-400" }),
11289
+ /* @__PURE__ */ jsx35("span", { children: "Visited" })
10245
11290
  ] }),
10246
- /* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1", children: [
10247
- /* @__PURE__ */ jsx34("div", { className: "w-2 h-2 rounded-sm bg-yellow-400" }),
10248
- /* @__PURE__ */ jsx34("span", { children: "Bookmarked" })
11291
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-1", children: [
11292
+ /* @__PURE__ */ jsx35("div", { className: "w-2 h-2 rounded-sm bg-yellow-400" }),
11293
+ /* @__PURE__ */ jsx35("span", { children: "Bookmarked" })
10249
11294
  ] })
10250
11295
  ] }) }),
10251
- /* @__PURE__ */ jsxs28("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
11296
+ /* @__PURE__ */ jsxs29("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
10252
11297
  visitedCount,
10253
11298
  " of ",
10254
11299
  numPages,
@@ -10256,7 +11301,7 @@ var Minimap = memo33(function Minimap2({
10256
11301
  ] })
10257
11302
  ] });
10258
11303
  if (variant === "floating") {
10259
- return /* @__PURE__ */ jsxs28(
11304
+ return /* @__PURE__ */ jsxs29(
10260
11305
  "div",
10261
11306
  {
10262
11307
  className: cn(
@@ -10272,13 +11317,13 @@ var Minimap = memo33(function Minimap2({
10272
11317
  ),
10273
11318
  style: { maxHeight },
10274
11319
  children: [
10275
- /* @__PURE__ */ jsx34("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 mb-2", children: "Reading Progress" }),
11320
+ /* @__PURE__ */ jsx35("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 mb-2", children: "Reading Progress" }),
10276
11321
  content
10277
11322
  ]
10278
11323
  }
10279
11324
  );
10280
11325
  }
10281
- return /* @__PURE__ */ jsx34(
11326
+ return /* @__PURE__ */ jsx35(
10282
11327
  "div",
10283
11328
  {
10284
11329
  className: cn(
@@ -10292,10 +11337,250 @@ var Minimap = memo33(function Minimap2({
10292
11337
  );
10293
11338
  });
10294
11339
 
11340
+ // src/components/index.ts
11341
+ init_FloatingZoomControls2();
11342
+
11343
+ // src/components/PDFThumbnailNav/PDFThumbnailNav.tsx
11344
+ init_hooks();
11345
+ init_utils();
11346
+ import { memo as memo35, useEffect as useEffect26, useState as useState28, useRef as useRef25, useCallback as useCallback41 } from "react";
11347
+ import { jsx as jsx36, jsxs as jsxs30 } from "react/jsx-runtime";
11348
+ var DEFAULT_WIDTH = 612;
11349
+ var DEFAULT_HEIGHT = 792;
11350
+ var PDFThumbnailNav = memo35(function PDFThumbnailNav2({
11351
+ thumbnailScale = 0.15,
11352
+ orientation = "vertical",
11353
+ maxVisible = 10,
11354
+ className,
11355
+ onThumbnailClick,
11356
+ gap = 8,
11357
+ showPageNumbers = true
11358
+ }) {
11359
+ const { document: document2, numPages, currentPage } = usePDFViewer();
11360
+ const { viewerStore } = usePDFViewerStores();
11361
+ const containerRef = useRef25(null);
11362
+ const [thumbnails, setThumbnails] = useState28(/* @__PURE__ */ new Map());
11363
+ const [visibleRange, setVisibleRange] = useState28({ start: 1, end: maxVisible });
11364
+ const renderQueueRef = useRef25(/* @__PURE__ */ new Set());
11365
+ const pageCache = useRef25(/* @__PURE__ */ new Map());
11366
+ const thumbnailWidth = Math.floor(DEFAULT_WIDTH * thumbnailScale);
11367
+ const thumbnailHeight = Math.floor(DEFAULT_HEIGHT * thumbnailScale);
11368
+ const updateVisibleRange = useCallback41(() => {
11369
+ if (!containerRef.current || numPages === 0) return;
11370
+ const container = containerRef.current;
11371
+ const isHorizontal2 = orientation === "horizontal";
11372
+ const scrollPosition = isHorizontal2 ? container.scrollLeft : container.scrollTop;
11373
+ const viewportSize = isHorizontal2 ? container.clientWidth : container.clientHeight;
11374
+ const itemSize = (isHorizontal2 ? thumbnailWidth : thumbnailHeight) + gap;
11375
+ const firstVisible = Math.max(1, Math.floor(scrollPosition / itemSize) + 1);
11376
+ const visibleCount = Math.ceil(viewportSize / itemSize) + 2;
11377
+ const lastVisible = Math.min(numPages, firstVisible + visibleCount);
11378
+ setVisibleRange({ start: firstVisible, end: lastVisible });
11379
+ }, [numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
11380
+ useEffect26(() => {
11381
+ const container = containerRef.current;
11382
+ if (!container) return;
11383
+ const handleScroll = () => {
11384
+ requestAnimationFrame(updateVisibleRange);
11385
+ };
11386
+ container.addEventListener("scroll", handleScroll, { passive: true });
11387
+ updateVisibleRange();
11388
+ return () => container.removeEventListener("scroll", handleScroll);
11389
+ }, [updateVisibleRange]);
11390
+ useEffect26(() => {
11391
+ if (!document2) {
11392
+ setThumbnails(/* @__PURE__ */ new Map());
11393
+ pageCache.current.clear();
11394
+ return;
11395
+ }
11396
+ const renderThumbnails = async () => {
11397
+ const newThumbnails = new Map(thumbnails);
11398
+ const pagesToRender = [];
11399
+ for (let i = visibleRange.start; i <= visibleRange.end; i++) {
11400
+ if (!newThumbnails.has(i) && !renderQueueRef.current.has(i)) {
11401
+ pagesToRender.push(i);
11402
+ renderQueueRef.current.add(i);
11403
+ }
11404
+ }
11405
+ for (const pageNum of pagesToRender) {
11406
+ try {
11407
+ let page = pageCache.current.get(pageNum);
11408
+ if (!page) {
11409
+ page = await document2.getPage(pageNum);
11410
+ pageCache.current.set(pageNum, page);
11411
+ }
11412
+ const viewport = page.getViewport({ scale: thumbnailScale });
11413
+ const canvas = window.document.createElement("canvas");
11414
+ canvas.width = Math.floor(viewport.width);
11415
+ canvas.height = Math.floor(viewport.height);
11416
+ const ctx = canvas.getContext("2d");
11417
+ if (ctx) {
11418
+ await page.render({
11419
+ canvasContext: ctx,
11420
+ viewport
11421
+ }).promise;
11422
+ newThumbnails.set(pageNum, {
11423
+ pageNumber: pageNum,
11424
+ canvas,
11425
+ width: canvas.width,
11426
+ height: canvas.height
11427
+ });
11428
+ }
11429
+ } catch (error) {
11430
+ console.error(`Failed to render thumbnail for page ${pageNum}:`, error);
11431
+ } finally {
11432
+ renderQueueRef.current.delete(pageNum);
11433
+ }
11434
+ }
11435
+ if (pagesToRender.length > 0) {
11436
+ setThumbnails(newThumbnails);
11437
+ }
11438
+ };
11439
+ renderThumbnails();
11440
+ }, [document2, visibleRange, thumbnailScale, thumbnails]);
11441
+ useEffect26(() => {
11442
+ if (!containerRef.current || numPages === 0) return;
11443
+ const container = containerRef.current;
11444
+ const isHorizontal2 = orientation === "horizontal";
11445
+ const itemSize = (isHorizontal2 ? thumbnailWidth : thumbnailHeight) + gap;
11446
+ const targetPosition = (currentPage - 1) * itemSize;
11447
+ const scrollPosition = isHorizontal2 ? container.scrollLeft : container.scrollTop;
11448
+ const viewportSize = isHorizontal2 ? container.clientWidth : container.clientHeight;
11449
+ if (targetPosition < scrollPosition || targetPosition + itemSize > scrollPosition + viewportSize) {
11450
+ const targetScroll = targetPosition - (viewportSize - itemSize) / 2;
11451
+ container.scrollTo({
11452
+ [isHorizontal2 ? "left" : "top"]: Math.max(0, targetScroll),
11453
+ behavior: "smooth"
11454
+ });
11455
+ }
11456
+ }, [currentPage, numPages, orientation, thumbnailWidth, thumbnailHeight, gap]);
11457
+ const handleThumbnailClick = useCallback41((pageNum) => {
11458
+ onThumbnailClick?.(pageNum);
11459
+ viewerStore.getState().requestScrollToPage(pageNum, "smooth");
11460
+ }, [onThumbnailClick, viewerStore]);
11461
+ if (!document2 || numPages === 0) {
11462
+ return /* @__PURE__ */ jsx36(
11463
+ "div",
11464
+ {
11465
+ className: cn(
11466
+ "pdf-thumbnail-nav",
11467
+ "flex items-center justify-center",
11468
+ "bg-gray-100 dark:bg-gray-800",
11469
+ "text-gray-500 dark:text-gray-400",
11470
+ "text-sm",
11471
+ className
11472
+ ),
11473
+ style: {
11474
+ width: orientation === "vertical" ? thumbnailWidth + 24 : "100%",
11475
+ height: orientation === "horizontal" ? thumbnailHeight + 40 : "100%"
11476
+ },
11477
+ children: "No document"
11478
+ }
11479
+ );
11480
+ }
11481
+ const isHorizontal = orientation === "horizontal";
11482
+ const totalSize = numPages * ((isHorizontal ? thumbnailWidth : thumbnailHeight) + gap) - gap;
11483
+ return /* @__PURE__ */ jsx36(
11484
+ "div",
11485
+ {
11486
+ ref: containerRef,
11487
+ className: cn(
11488
+ "pdf-thumbnail-nav",
11489
+ "overflow-auto",
11490
+ "bg-gray-100 dark:bg-gray-800",
11491
+ isHorizontal ? "flex-row" : "flex-col",
11492
+ className
11493
+ ),
11494
+ style: {
11495
+ ...isHorizontal ? { overflowX: "auto", overflowY: "hidden" } : { overflowX: "hidden", overflowY: "auto" }
11496
+ },
11497
+ children: /* @__PURE__ */ jsx36(
11498
+ "div",
11499
+ {
11500
+ className: cn(
11501
+ "relative",
11502
+ isHorizontal ? "flex flex-row items-center" : "flex flex-col items-center"
11503
+ ),
11504
+ style: {
11505
+ [isHorizontal ? "width" : "height"]: totalSize,
11506
+ [isHorizontal ? "minWidth" : "minHeight"]: totalSize,
11507
+ padding: gap / 2,
11508
+ gap
11509
+ },
11510
+ children: Array.from({ length: numPages }, (_, i) => i + 1).map((pageNum) => {
11511
+ const thumbnail = thumbnails.get(pageNum);
11512
+ const isActive = pageNum === currentPage;
11513
+ const isVisible = pageNum >= visibleRange.start && pageNum <= visibleRange.end;
11514
+ return /* @__PURE__ */ jsxs30(
11515
+ "div",
11516
+ {
11517
+ className: cn(
11518
+ "pdf-thumbnail",
11519
+ "flex-shrink-0 cursor-pointer transition-all duration-200",
11520
+ "border-2 rounded shadow-sm hover:shadow-md",
11521
+ isActive ? "border-blue-500 ring-2 ring-blue-200 dark:ring-blue-800" : "border-gray-300 dark:border-gray-600 hover:border-blue-400"
11522
+ ),
11523
+ style: {
11524
+ width: thumbnailWidth,
11525
+ height: thumbnailHeight + (showPageNumbers ? 24 : 0)
11526
+ },
11527
+ onClick: () => handleThumbnailClick(pageNum),
11528
+ role: "button",
11529
+ tabIndex: 0,
11530
+ "aria-label": `Go to page ${pageNum}`,
11531
+ "aria-current": isActive ? "page" : void 0,
11532
+ onKeyDown: (e) => {
11533
+ if (e.key === "Enter" || e.key === " ") {
11534
+ e.preventDefault();
11535
+ handleThumbnailClick(pageNum);
11536
+ }
11537
+ },
11538
+ children: [
11539
+ /* @__PURE__ */ jsx36(
11540
+ "div",
11541
+ {
11542
+ className: "relative bg-white dark:bg-gray-700",
11543
+ style: {
11544
+ width: thumbnailWidth,
11545
+ height: thumbnailHeight
11546
+ },
11547
+ children: isVisible && thumbnail?.canvas ? /* @__PURE__ */ jsx36(
11548
+ "img",
11549
+ {
11550
+ src: thumbnail.canvas.toDataURL(),
11551
+ alt: `Page ${pageNum}`,
11552
+ className: "w-full h-full object-contain",
11553
+ loading: "lazy"
11554
+ }
11555
+ ) : /* @__PURE__ */ jsx36("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-xs", children: pageNum })
11556
+ }
11557
+ ),
11558
+ showPageNumbers && /* @__PURE__ */ jsx36(
11559
+ "div",
11560
+ {
11561
+ className: cn(
11562
+ "text-center text-xs py-1",
11563
+ "bg-gray-50 dark:bg-gray-700",
11564
+ isActive ? "text-blue-600 dark:text-blue-400 font-medium" : "text-gray-600 dark:text-gray-400"
11565
+ ),
11566
+ children: pageNum
11567
+ }
11568
+ )
11569
+ ]
11570
+ },
11571
+ pageNum
11572
+ );
11573
+ })
11574
+ }
11575
+ )
11576
+ }
11577
+ );
11578
+ });
11579
+
10295
11580
  // src/components/ErrorBoundary/PDFErrorBoundary.tsx
10296
11581
  init_utils();
10297
11582
  import { Component } from "react";
10298
- import { jsx as jsx35, jsxs as jsxs29 } from "react/jsx-runtime";
11583
+ import { jsx as jsx37, jsxs as jsxs31 } from "react/jsx-runtime";
10299
11584
  var PDFErrorBoundary = class extends Component {
10300
11585
  constructor(props) {
10301
11586
  super(props);
@@ -10323,7 +11608,7 @@ var PDFErrorBoundary = class extends Component {
10323
11608
  return fallback;
10324
11609
  }
10325
11610
  if (showDefaultUI) {
10326
- return /* @__PURE__ */ jsx35(
11611
+ return /* @__PURE__ */ jsx37(
10327
11612
  DefaultErrorUI,
10328
11613
  {
10329
11614
  error,
@@ -10342,7 +11627,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10342
11627
  const isNetworkError = error.message.includes("fetch") || error.message.includes("network") || error.message.includes("Failed to load");
10343
11628
  let title = "Something went wrong";
10344
11629
  let description = error.message;
10345
- let icon = /* @__PURE__ */ jsx35("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx35(
11630
+ let icon = /* @__PURE__ */ jsx37("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx37(
10346
11631
  "path",
10347
11632
  {
10348
11633
  strokeLinecap: "round",
@@ -10354,7 +11639,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10354
11639
  if (isPDFError) {
10355
11640
  title = "Unable to load PDF";
10356
11641
  description = "The PDF file could not be loaded. It may be corrupted or in an unsupported format.";
10357
- icon = /* @__PURE__ */ jsx35("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx35(
11642
+ icon = /* @__PURE__ */ jsx37("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx37(
10358
11643
  "path",
10359
11644
  {
10360
11645
  strokeLinecap: "round",
@@ -10366,7 +11651,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10366
11651
  } else if (isNetworkError) {
10367
11652
  title = "Network error";
10368
11653
  description = "Unable to fetch the PDF file. Please check your internet connection and try again.";
10369
- icon = /* @__PURE__ */ jsx35("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx35(
11654
+ icon = /* @__PURE__ */ jsx37("svg", { className: "w-12 h-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx37(
10370
11655
  "path",
10371
11656
  {
10372
11657
  strokeLinecap: "round",
@@ -10376,7 +11661,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10376
11661
  }
10377
11662
  ) });
10378
11663
  }
10379
- return /* @__PURE__ */ jsxs29(
11664
+ return /* @__PURE__ */ jsxs31(
10380
11665
  "div",
10381
11666
  {
10382
11667
  className: cn(
@@ -10389,14 +11674,14 @@ function DefaultErrorUI({ error, onReset, className }) {
10389
11674
  ),
10390
11675
  children: [
10391
11676
  icon,
10392
- /* @__PURE__ */ jsx35("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
10393
- /* @__PURE__ */ jsx35("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
10394
- /* @__PURE__ */ jsxs29("details", { className: "mt-4 text-left max-w-md w-full", children: [
10395
- /* @__PURE__ */ jsx35("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" }),
10396
- /* @__PURE__ */ jsx35("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
11677
+ /* @__PURE__ */ jsx37("h2", { className: "mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100", children: title }),
11678
+ /* @__PURE__ */ jsx37("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 max-w-md", children: description }),
11679
+ /* @__PURE__ */ jsxs31("details", { className: "mt-4 text-left max-w-md w-full", children: [
11680
+ /* @__PURE__ */ jsx37("summary", { className: "cursor-pointer text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200", children: "Technical details" }),
11681
+ /* @__PURE__ */ jsx37("pre", { className: "mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto", children: error.stack || error.message })
10397
11682
  ] }),
10398
- /* @__PURE__ */ jsxs29("div", { className: "mt-6 flex gap-3", children: [
10399
- /* @__PURE__ */ jsx35(
11683
+ /* @__PURE__ */ jsxs31("div", { className: "mt-6 flex gap-3", children: [
11684
+ /* @__PURE__ */ jsx37(
10400
11685
  "button",
10401
11686
  {
10402
11687
  onClick: onReset,
@@ -10410,7 +11695,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10410
11695
  children: "Try again"
10411
11696
  }
10412
11697
  ),
10413
- /* @__PURE__ */ jsx35(
11698
+ /* @__PURE__ */ jsx37(
10414
11699
  "button",
10415
11700
  {
10416
11701
  onClick: () => window.location.reload(),
@@ -10430,7 +11715,7 @@ function DefaultErrorUI({ error, onReset, className }) {
10430
11715
  );
10431
11716
  }
10432
11717
  function withErrorBoundary({ component, ...props }) {
10433
- return /* @__PURE__ */ jsx35(PDFErrorBoundary, { ...props, children: component });
11718
+ return /* @__PURE__ */ jsx37(PDFErrorBoundary, { ...props, children: component });
10434
11719
  }
10435
11720
 
10436
11721
  // src/index.ts
@@ -10453,6 +11738,7 @@ export {
10453
11738
  DocumentContainer,
10454
11739
  DrawingCanvas,
10455
11740
  DualPageContainer,
11741
+ FloatingZoomControls,
10456
11742
  FocusRegionLayer,
10457
11743
  HighlightLayer,
10458
11744
  HighlightPopover,
@@ -10463,6 +11749,7 @@ export {
10463
11749
  OutlinePanel,
10464
11750
  PDFErrorBoundary,
10465
11751
  PDFPage,
11752
+ PDFThumbnailNav,
10466
11753
  PDFViewer,
10467
11754
  PDFViewerClient,
10468
11755
  PDFViewerContext,
@@ -10481,9 +11768,11 @@ export {
10481
11768
  ThumbnailPanel,
10482
11769
  Toolbar,
10483
11770
  VirtualizedDocumentContainer,
11771
+ applyRotation,
10484
11772
  clearHighlights,
10485
11773
  clearStudentData,
10486
11774
  cn,
11775
+ countTextOnPage,
10487
11776
  createAgentAPI,
10488
11777
  createAgentStore,
10489
11778
  createAnnotationStore,
@@ -10492,6 +11781,7 @@ export {
10492
11781
  createSearchStore,
10493
11782
  createStudentStore,
10494
11783
  createViewerStore,
11784
+ doRectsIntersect,
10495
11785
  downloadAnnotationsAsJSON,
10496
11786
  downloadAnnotationsAsMarkdown,
10497
11787
  downloadFile,
@@ -10499,25 +11789,39 @@ export {
10499
11789
  exportAnnotationsAsMarkdown,
10500
11790
  exportHighlightsAsJSON,
10501
11791
  exportHighlightsAsMarkdown,
11792
+ extractPageText,
11793
+ findTextInDocument,
11794
+ findTextOnPage,
10502
11795
  generateDocumentId,
10503
11796
  getAllDocumentIds,
10504
11797
  getAllStudentDataDocumentIds,
10505
11798
  getMetadata,
10506
11799
  getOutline,
10507
11800
  getPage,
11801
+ getPageText,
10508
11802
  getPageTextContent,
10509
11803
  getPluginManager,
11804
+ getRectIntersection,
11805
+ getRotatedDimensions,
10510
11806
  getStorageStats,
10511
11807
  importHighlightsFromJSON,
10512
11808
  initializePDFJS,
10513
11809
  isPDFJSInitialized,
11810
+ isPointInRect,
10514
11811
  loadDocument,
10515
11812
  loadHighlights,
10516
11813
  loadStudentData,
11814
+ mergeAdjacentRects,
11815
+ pdfToPercent,
11816
+ pdfToViewport,
10517
11817
  pdfjsLib,
11818
+ percentToPDF,
11819
+ percentToViewport,
10518
11820
  quickViewer,
11821
+ removeRotation,
10519
11822
  saveHighlights,
10520
11823
  saveStudentData,
11824
+ scaleRect,
10521
11825
  useAgentContext,
10522
11826
  useAgentStore,
10523
11827
  useAnnotationStore,
@@ -10539,6 +11843,8 @@ export {
10539
11843
  useTouchGestures,
10540
11844
  useViewerStore,
10541
11845
  useZoom,
11846
+ viewportToPDF,
11847
+ viewportToPercent,
10542
11848
  withErrorBoundary
10543
11849
  };
10544
11850
  //# sourceMappingURL=index.js.map