captide
Version:
Get millions of financial documents into your AI app 🚀
869 lines (850 loc) • 47.5 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
// Initial state for the context
var initialState = {
document: null,
isLoading: false,
isOpen: false,
zoomLevel: 0.7
};
var DocumentViewerContext = React.createContext(undefined);
DocumentViewerContext.displayName = 'DocumentViewerContext';
var DocumentViewerProvider = function (_a) {
var children = _a.children, providedFetchFn = _a.fetchDocumentFn;
var _b = React.useState(initialState), state = _b[0], setState = _b[1];
var fetchDocumentFnRef = React.useRef(null);
React.useEffect(function () {
if (providedFetchFn) {
fetchDocumentFnRef.current = providedFetchFn;
}
}, [providedFetchFn]);
var updateDocumentViewer = React.useCallback(function (updates) {
setState(function (prev) { return (__assign(__assign({}, prev), updates)); });
}, []);
var setDocument = React.useCallback(function (document) {
setState(function (prev) { return (__assign(__assign({}, prev), { document: document })); });
}, []);
var setFetchDocumentFn = React.useCallback(function (fn) {
fetchDocumentFnRef.current = fn;
}, []);
var openViewer = React.useCallback(function () {
setState(function (prev) { return (__assign(__assign({}, prev), { isOpen: true })); });
}, []);
var closeViewer = React.useCallback(function () {
setState(function (prev) { return (__assign(__assign({}, prev), { isOpen: false, document: null, pageNumber: undefined, citationSnippet: undefined })); });
}, []);
var loadDocument = React.useCallback(function (documentId, pageNumber, citationSnippet) { return __awaiter(void 0, void 0, void 0, function () {
var fetchFn, response, documentWithId_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
setState(function (prev) { return (__assign(__assign({}, prev), { isLoading: true, document: null, isOpen: true, pageNumber: pageNumber, citationSnippet: citationSnippet })); });
fetchFn = fetchDocumentFnRef.current || providedFetchFn;
if (!fetchFn) {
setState(function (prev) { return (__assign(__assign({}, prev), { isLoading: false })); });
throw new Error('No fetchDocumentFn provided. Cannot load document.');
}
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, fetchFn(documentId)];
case 2:
response = _b.sent();
documentWithId_1 = __assign(__assign({}, response), { documentId: documentId });
setState(function (prev) { return (__assign(__assign({}, prev), { document: documentWithId_1, isLoading: false })); });
return [3 /*break*/, 4];
case 3:
_b.sent();
setState(function (prev) { return (__assign(__assign({}, prev), { isLoading: false })); });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); }, [providedFetchFn]);
var setZoomLevel = React.useCallback(function (level) {
setState(function (prev) { return (__assign(__assign({}, prev), { zoomLevel: level })); });
}, []);
var zoomIn = React.useCallback(function () {
setState(function (prev) { return (__assign(__assign({}, prev), { zoomLevel: prev.zoomLevel + 0.1 })); });
}, []);
var zoomOut = React.useCallback(function () {
setState(function (prev) { return (__assign(__assign({}, prev), { zoomLevel: Math.max(0.1, prev.zoomLevel - 0.1) })); });
}, []);
var resetZoom = React.useCallback(function () {
setState(function (prev) { return (__assign(__assign({}, prev), { zoomLevel: 1.0 })); });
}, []);
var contextValue = __assign(__assign({}, state), { updateDocumentViewer: updateDocumentViewer, setDocument: setDocument, loadDocument: loadDocument, setFetchDocumentFn: setFetchDocumentFn, openViewer: openViewer, closeViewer: closeViewer, setZoomLevel: setZoomLevel, zoomIn: zoomIn, zoomOut: zoomOut, resetZoom: resetZoom });
return (React__default["default"].createElement(DocumentViewerContext.Provider, { value: contextValue }, children));
};
var useDocumentViewer = function () {
var context = React.useContext(DocumentViewerContext);
if (!context) {
throw new Error('useDocumentViewer must be used within a DocumentViewerProvider');
}
return context;
};
/**
* PDF Highlighting Utility
*
* Provides functionality to find and highlight text in PDF documents using PDF.js
*/
/**
* Normalize text for comparison (remove spaces, dollar signs, make lowercase)
*/
var normalizeText = function (str) {
return str
.toLowerCase()
.replace(/\s+/g, '') // Remove all whitespace
.replace(/\$/g, '') // Remove dollar signs
.trim();
};
/**
* Extract text content from a PDF page
*/
var extractTextFromPage = function (pageView) { return __awaiter(void 0, void 0, void 0, function () {
var textContent, textItems, fullText;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
if (!(pageView === null || pageView === void 0 ? void 0 : pageView.pdfPage))
return [2 /*return*/, null];
return [4 /*yield*/, pageView.pdfPage.getTextContent()];
case 1:
textContent = _a.sent();
textItems = textContent.items.map(function (item) { return item.str; });
fullText = textItems.join(' ');
return [2 /*return*/, { textItems: textItems, fullText: fullText, textContent: textContent }];
case 2:
_a.sent();
return [2 /*return*/, null];
case 3: return [2 /*return*/];
}
});
}); };
/**
* Find text in PDF and return matching text items with coordinates
*/
var findTextInPDF = function (searchText, pdfViewerInstance, targetPage) { return __awaiter(void 0, void 0, void 0, function () {
var pagesToSearch, _i, pagesToSearch_1, pageNum, pageView, pageTextData, normalizedPageText, normalizedSearch, matchIndex, matchingTextItems, normalizedPos, i, textItem, normalizedItemText, itemStart, itemEnd;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!searchText || !pdfViewerInstance || !pdfViewerInstance.pagesCount)
return [2 /*return*/, null];
pagesToSearch = targetPage
? [targetPage]
: Array.from({ length: pdfViewerInstance.pagesCount }, function (_, i) { return i + 1; });
_i = 0, pagesToSearch_1 = pagesToSearch;
_a.label = 1;
case 1:
if (!(_i < pagesToSearch_1.length)) return [3 /*break*/, 4];
pageNum = pagesToSearch_1[_i];
pageView = pdfViewerInstance.getPageView(pageNum - 1);
return [4 /*yield*/, extractTextFromPage(pageView)];
case 2:
pageTextData = _a.sent();
if (!pageTextData)
return [3 /*break*/, 3];
normalizedPageText = normalizeText(pageTextData.fullText);
normalizedSearch = normalizeText(searchText);
matchIndex = normalizedPageText.indexOf(normalizedSearch);
if (matchIndex !== -1) {
matchingTextItems = [];
normalizedPos = 0;
for (i = 0; i < pageTextData.textContent.items.length; i++) {
textItem = pageTextData.textContent.items[i];
normalizedItemText = normalizeText(textItem.str);
itemStart = normalizedPos;
itemEnd = normalizedPos + normalizedItemText.length;
// Check if this text item overlaps with our match
if (itemStart < matchIndex + normalizedSearch.length && itemEnd > matchIndex) {
matchingTextItems.push({
index: i,
item: textItem,
start: Math.max(itemStart, matchIndex),
end: Math.min(itemEnd, matchIndex + normalizedSearch.length)
});
}
normalizedPos += normalizedItemText.length;
}
if (matchingTextItems.length > 0) {
return [2 /*return*/, {
page: pageNum,
textItems: matchingTextItems,
textContent: pageTextData.textContent
}];
}
}
_a.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/, null];
}
});
}); };
/**
* Create a rectangle highlight overlay on a PDF page
*/
var createRectangleHighlight = function (searchText, pdfViewerInstance, targetPage, currentHighlight) { return __awaiter(void 0, void 0, void 0, function () {
var result, pageNumber;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!searchText || !pdfViewerInstance || !pdfViewerInstance.pagesCount)
return [2 /*return*/, null];
// Check if we already have a highlight for the same text and page
if (currentHighlight &&
currentHighlight.text === searchText &&
currentHighlight.page === (targetPage || pdfViewerInstance.currentPageNumber)) {
return [2 /*return*/, currentHighlight];
}
return [4 /*yield*/, findTextInPDF(searchText, pdfViewerInstance, targetPage)];
case 1:
result = _a.sent();
if (!result)
return [2 /*return*/, null];
// Navigate to the page if needed
if (result.page !== pdfViewerInstance.currentPageNumber) {
try {
pageNumber = Number(result.page);
if (pageNumber >= 1 && pageNumber <= pdfViewerInstance.pagesCount) {
pdfViewerInstance.currentPageNumber = pageNumber;
}
}
catch (err) {
console.warn('Failed to navigate to page for highlighting:', result.page, err);
}
}
return [2 /*return*/, new Promise(function (resolve) {
(function () { return __awaiter(void 0, void 0, void 0, function () {
var currentPageView, pageDiv, minX, minY, maxX, maxY, hasValidCoordinates, _i, _a, matchItem, item, x, y, width, height, viewport, _b, x1, y1, _c, x2, y2, highlightElement, padding, offsetX, offsetY, highlight;
return __generator(this, function (_d) {
currentPageView = pdfViewerInstance.getPageView(result.page - 1);
if (!(currentPageView === null || currentPageView === void 0 ? void 0 : currentPageView.div)) {
resolve(null);
return [2 /*return*/];
}
pageDiv = currentPageView.div;
minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
hasValidCoordinates = false;
for (_i = 0, _a = result.textItems; _i < _a.length; _i++) {
matchItem = _a[_i];
item = matchItem.item;
if (item.transform && item.width && item.height) {
x = item.transform[4];
y = item.transform[5];
width = item.width;
height = item.height;
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x + width);
maxY = Math.max(maxY, y + height);
hasValidCoordinates = true;
}
}
if (!hasValidCoordinates) {
resolve(null);
return [2 /*return*/];
}
viewport = currentPageView.viewport;
_b = viewport.convertToViewportPoint(minX, minY), x1 = _b[0], y1 = _b[1];
_c = viewport.convertToViewportPoint(maxX, maxY), x2 = _c[0], y2 = _c[1];
highlightElement = document.createElement('div');
highlightElement.className = 'pdf-rectangle-highlight';
padding = 4;
offsetX = 9;
offsetY = 5;
highlightElement.style.left = "".concat(Math.min(x1, x2) - padding - offsetX, "px");
highlightElement.style.top = "".concat(Math.min(y1, y2) - padding - offsetY, "px");
highlightElement.style.width = "".concat(Math.abs(x2 - x1) + padding * 2, "px");
highlightElement.style.height = "".concat(Math.abs(y2 - y1) + padding * 2, "px");
// Add to page
pageDiv.style.position = 'relative';
pageDiv.appendChild(highlightElement);
highlight = {
element: highlightElement,
page: result.page,
text: searchText
};
resolve(highlight);
return [2 /*return*/];
});
}); })();
})];
}
});
}); };
/**
* Remove a highlight element from the DOM
*/
var removeHighlight = function (highlight) {
if (highlight === null || highlight === void 0 ? void 0 : highlight.element) {
highlight.element.remove();
}
};
var Loader = function (_a) {
var _b = _a.className, className = _b === void 0 ? '' : _b, style = _a.style;
return (React__default["default"].createElement("div", { className: "flex flex-col items-center justify-center w-full h-full ".concat(className), style: style },
React__default["default"].createElement("div", { style: {
width: '40px',
height: '40px',
border: '3px solid #f3f3f3',
borderTop: '3px solid #475569',
borderRadius: '50%',
animation: 'spin 1s linear infinite'
} }),
React__default["default"].createElement("style", null, "\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n ")));
};
/**
* A simple icon-only download button component
*/
var DownloadButton = function (_a) {
var onClick = _a.onClick, _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.className, className = _c === void 0 ? '' : _c;
var buttonStyle = __assign({ width: '32px', height: '32px', padding: '8px', borderRadius: '4px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(255, 255, 255, 0.9)', color: '#475569', border: '1px solid rgba(203, 213, 225, 0.5)', boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)', transition: 'background-color 0.2s ease' }, style);
// Handle hover states
var handleMouseOver = function (e) {
e.currentTarget.style.backgroundColor = '#f8fafc';
};
var handleMouseOut = function (e) {
e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
};
return (React__default["default"].createElement("button", { onClick: onClick, style: buttonStyle, className: className, onMouseOver: handleMouseOver, onMouseOut: handleMouseOut, title: "Download PDF" },
React__default["default"].createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
React__default["default"].createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
React__default["default"].createElement("polyline", { points: "7 10 12 15 17 10" }),
React__default["default"].createElement("line", { x1: "12", y1: "15", x2: "12", y2: "3" }))));
};
// Simple placeholder for SSR
var PDFPlaceholder = function (_a) {
var _b = _a.className, className = _b === void 0 ? 'w-full h-full' : _b, style = _a.style;
return (React__default["default"].createElement("div", { className: className, style: __assign(__assign({}, style), { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '20px', color: '#666' }) }, "PDF viewer loading..."));
};
var DocumentViewer = function (_a) {
var _b = _a.className, className = _b === void 0 ? 'w-full h-full' : _b, style = _a.style;
var _c = useDocumentViewer(), pdfDocument = _c.document, isLoading = _c.isLoading, zoomLevel = _c.zoomLevel, zoomIn = _c.zoomIn, zoomOut = _c.zoomOut, resetZoom = _c.resetZoom, pageNumber = _c.pageNumber, citationSnippet = _c.citationSnippet;
var containerRef = React.useRef(null);
var viewerContainerRef = React.useRef(null);
var _d = React.useState(null), error = _d[0], setError = _d[1];
var _e = React.useState(0), numPages = _e[0], setNumPages = _e[1];
var _f = React.useState(1), currentPage = _f[0], setCurrentPage = _f[1];
var _g = React.useState(false), pdfJsLoaded = _g[0], setPdfJsLoaded = _g[1];
var _h = React.useState(null), viewer = _h[0], setViewer = _h[1];
var _j = React.useState(null), currentHighlight = _j[0], setCurrentHighlight = _j[1];
// Only run in browser
var isBrowser = typeof window !== 'undefined';
// Add highlighting styles
if (typeof window !== 'undefined' && !document.getElementById('pdf-rectangle-highlight-style')) {
var style_1 = document.createElement('style');
style_1.id = 'pdf-rectangle-highlight-style';
style_1.textContent = "\n .pdf-rectangle-highlight {\n position: absolute !important;\n background: rgba(255, 235, 59, 0.3) !important;\n border: 2px solid #fdcb6e !important;\n border-radius: 3px !important;\n pointer-events: none !important;\n z-index: 1000 !important;\n box-shadow: 0 2px 8px rgba(253, 203, 110, 0.3) !important;\n }\n ";
document.head.appendChild(style_1);
}
// Remove current highlight
var removeCurrentHighlight = React.useCallback(function () {
if (currentHighlight) {
removeHighlight(currentHighlight);
setCurrentHighlight(null);
}
}, [currentHighlight]);
// Handle download functionality
var handleDownload = function () {
if (!(pdfDocument === null || pdfDocument === void 0 ? void 0 : pdfDocument.originalFileUrl))
return;
fetch(pdfDocument.originalFileUrl)
.then(function (response) {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.blob();
})
.then(function (blob) {
var blobUrl = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = blobUrl;
var filename = 'document.pdf';
try {
var urlObj = new URL(pdfDocument.originalFileUrl);
var pathParts = urlObj.pathname.split('/');
var potentialFilename = pathParts[pathParts.length - 1];
if (potentialFilename && potentialFilename.includes('.pdf')) {
filename = decodeURIComponent(potentialFilename.split('?')[0]);
}
}
catch (e) {
// Fall back to default name
}
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(blobUrl);
document.body.removeChild(a);
})
.catch(function (error) {
window.open(pdfDocument.originalFileUrl, '_blank');
});
};
// Handle keyboard shortcuts
React.useEffect(function () {
var el = containerRef.current;
if (!el)
return;
var handler = function (e) {
if (e.ctrlKey || e.metaKey) {
switch (e.key) {
case '=':
case '+':
e.preventDefault();
zoomIn();
break;
case '-':
e.preventDefault();
zoomOut();
break;
case '0':
e.preventDefault();
resetZoom();
break;
}
}
};
el.addEventListener('keydown', handler);
return function () { return el.removeEventListener('keydown', handler); };
}, [zoomIn, zoomOut, resetZoom]);
// Handle wheel zoom
React.useEffect(function () {
var el = containerRef.current;
if (!el)
return;
var handler = function (e) {
if (e.ctrlKey) {
e.preventDefault();
e.deltaY < 0 ? zoomIn() : zoomOut();
}
};
el.addEventListener('wheel', handler, { passive: false });
return function () { return el.removeEventListener('wheel', handler); };
}, [zoomIn, zoomOut]);
// Initialize PDF.js in browser
React.useEffect(function () {
if (!isBrowser)
return;
var mounted = true;
var loadPdfJs = function () { return __awaiter(void 0, void 0, void 0, function () {
var pdfjsLib, GlobalWorkerOptions, workerUrl, preloadWorker, link, customStyles, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
return [4 /*yield*/, Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('pdfjs-dist')); })];
case 1:
pdfjsLib = _a.sent();
return [4 /*yield*/, Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('pdfjs-dist')); })];
case 2:
GlobalWorkerOptions = (_a.sent()).GlobalWorkerOptions;
workerUrl = "https://cdn.jsdelivr.net/npm/pdfjs-dist@".concat(pdfjsLib.version, "/build/pdf.worker.min.mjs");
GlobalWorkerOptions.workerSrc = workerUrl;
// Pre-fetch the worker
try {
preloadWorker = document.createElement('link');
preloadWorker.rel = 'preload';
preloadWorker.as = 'script';
preloadWorker.href = workerUrl;
document.head.appendChild(preloadWorker);
}
catch (e) {
// Ignore preload errors
}
// Load viewer CSS if not already loaded
if (!document.getElementById('pdfjs-viewer-styles')) {
link = document.createElement('link');
link.id = 'pdfjs-viewer-styles';
link.rel = 'stylesheet';
link.href = "https://cdn.jsdelivr.net/npm/pdfjs-dist@".concat(pdfjsLib.version, "/web/pdf_viewer.css");
document.head.appendChild(link);
customStyles = document.createElement('style');
customStyles.id = 'pdf-custom-styles';
customStyles.textContent = "\n .pdf-container {\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: auto;\n }\n .pdfViewer .page {\n margin: 15px auto;\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\n }\n .pdfViewer .page.highlighted {\n box-shadow: 0 0 15px 5px rgba(255, 235, 59, 0.5);\n }\n ";
document.head.appendChild(customStyles);
}
if (mounted) {
setPdfJsLoaded(true);
}
return [3 /*break*/, 4];
case 3:
err_1 = _a.sent();
if (mounted) {
setError("Failed to load PDF.js: ".concat(err_1 instanceof Error ? err_1.message : String(err_1)));
}
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); };
loadPdfJs();
return function () {
mounted = false;
};
}, [isBrowser]);
// Load and render PDF when document changes and PDF.js is loaded
React.useEffect(function () {
if (!isBrowser || !(pdfDocument === null || pdfDocument === void 0 ? void 0 : pdfDocument.originalFileUrl) || !pdfJsLoaded || !viewerContainerRef.current)
return;
var mounted = true;
var pdfViewerInstance = null;
var pdfDocumentInstance = null;
var eventBusInstance = null;
var loadAndRenderPdf = function () { return __awaiter(void 0, void 0, void 0, function () {
var pdfjsLib, viewerModule, viewerContainer, viewerElement, pdfLinkService, loadingTask, err_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 4, , 5]);
setError(null);
return [4 /*yield*/, Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('pdfjs-dist')); })];
case 1:
pdfjsLib = _a.sent();
return [4 /*yield*/, Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('pdfjs-dist/web/pdf_viewer.mjs')); })];
case 2:
viewerModule = _a.sent();
if (!mounted || !viewerContainerRef.current)
return [2 /*return*/];
// Clear previous viewer
viewerContainerRef.current.innerHTML = '';
viewerContainer = document.createElement('div');
viewerContainer.className = 'pdf-container';
viewerElement = document.createElement('div');
viewerElement.className = 'pdfViewer';
viewerContainer.appendChild(viewerElement);
viewerContainerRef.current.appendChild(viewerContainer);
// Create event bus
eventBusInstance = new viewerModule.EventBus();
pdfLinkService = new viewerModule.PDFLinkService({
eventBus: eventBusInstance,
});
// Create viewer
pdfViewerInstance = new viewerModule.PDFViewer({
container: viewerContainer,
viewer: viewerElement,
eventBus: eventBusInstance,
linkService: pdfLinkService,
textLayerMode: 2,
removePageBorders: false,
});
pdfLinkService.setViewer(pdfViewerInstance);
// Set up event listeners
eventBusInstance.on('pagesinit', function () {
// Set initial zoom level
if (pdfViewerInstance && zoomLevel !== undefined) {
if (typeof zoomLevel === 'string') {
pdfViewerInstance.currentScaleValue = zoomLevel;
}
else {
pdfViewerInstance.currentScale = zoomLevel;
}
}
// Navigate to specific page if pageNumber is provided
if (typeof pageNumber === 'number' && pdfViewerInstance && mounted) {
var pageNum_1 = pageNumber;
if (pageNum_1 >= 1 && pageNum_1 <= pdfViewerInstance.pagesCount) {
try {
pdfViewerInstance.currentPageNumber = Number(pageNum_1);
// Highlight the page after a short delay
setTimeout(function () {
var _a;
if (mounted && pdfViewerInstance) {
var pageDiv = (_a = pdfViewerInstance.getPageView(pageNum_1 - 1)) === null || _a === void 0 ? void 0 : _a.div;
if (pageDiv) {
pageDiv.classList.add('highlighted');
pageDiv.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
}, 200);
}
catch (err) {
console.warn('Failed to navigate to page:', pageNum_1, err);
}
}
}
});
eventBusInstance.on('pagechanging', function (evt) {
if (mounted) {
var pageNumber_1 = parseInt(evt.pageNumber, 10) || 1;
setCurrentPage(pageNumber_1);
}
});
loadingTask = pdfjsLib.getDocument({
url: pdfDocument.originalFileUrl,
withCredentials: false,
cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@".concat(pdfjsLib.version, "/cmaps/"),
cMapPacked: true,
});
return [4 /*yield*/, loadingTask.promise];
case 3:
pdfDocumentInstance = _a.sent();
if (!mounted || !pdfViewerInstance)
return [2 /*return*/];
// Set the document in the viewer
pdfViewerInstance.setDocument(pdfDocumentInstance);
pdfLinkService.setDocument(pdfDocumentInstance);
setNumPages(pdfDocumentInstance.numPages);
setViewer(pdfViewerInstance);
// Set zoom after document is loaded - with delay to ensure pages are ready
if (pdfViewerInstance && zoomLevel !== undefined && pdfViewerInstance.pagesCount > 0) {
setTimeout(function () {
if (pdfViewerInstance && mounted && pdfViewerInstance.pagesCount > 0) {
try {
if (typeof zoomLevel === 'string') {
pdfViewerInstance.currentScaleValue = zoomLevel;
}
else {
pdfViewerInstance.currentScale = zoomLevel;
}
}
catch (err) {
// Silently handle zoom setting errors
console.warn('Failed to set zoom level:', err);
}
}
}, 100);
}
return [3 /*break*/, 5];
case 4:
err_2 = _a.sent();
if (mounted) {
setError("Failed to load or render PDF: ".concat(err_2 instanceof Error ? err_2.message : String(err_2)));
}
return [3 /*break*/, 5];
case 5: return [2 /*return*/];
}
});
}); };
loadAndRenderPdf();
return function () {
mounted = false;
// Clean up
if (eventBusInstance) {
eventBusInstance.off('pagesinit');
eventBusInstance.off('pagechanging');
}
if (pdfDocumentInstance) {
pdfDocumentInstance.destroy();
}
};
}, [pdfDocument === null || pdfDocument === void 0 ? void 0 : pdfDocument.originalFileUrl, pdfJsLoaded, zoomLevel, isBrowser, pageNumber]);
// Handle text highlighting when citationSnippet changes
React.useEffect(function () {
if (citationSnippet && viewer && !isLoading) {
// Check if we already have a highlight for this exact text
if (currentHighlight && currentHighlight.text === citationSnippet) {
return;
}
var targetPage_1;
if (typeof pageNumber === 'number') {
targetPage_1 = pageNumber;
}
(function () { return __awaiter(void 0, void 0, void 0, function () {
var newHighlight;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, createRectangleHighlight(citationSnippet, viewer, targetPage_1, currentHighlight)];
case 1:
newHighlight = _a.sent();
if (newHighlight) {
removeCurrentHighlight();
setCurrentHighlight(newHighlight);
}
return [2 /*return*/];
}
});
}); })();
}
}, [citationSnippet, viewer, isLoading, pageNumber, currentHighlight, removeCurrentHighlight]);
// Clean up highlight when citationSnippet becomes null
React.useEffect(function () {
if (!citationSnippet && currentHighlight) {
removeCurrentHighlight();
}
}, [citationSnippet, currentHighlight, removeCurrentHighlight]);
// Handle pageNumber changes after viewer is loaded
React.useEffect(function () {
if (typeof pageNumber === 'number' && viewer && !isLoading && numPages > 0) {
var pageNum_2 = pageNumber;
// Validate page number is within bounds
if (pageNum_2 >= 1 && pageNum_2 <= viewer.pagesCount) {
try {
viewer.currentPageNumber = Number(pageNum_2);
setTimeout(function () {
var _a;
if (viewer) {
var pageDiv = (_a = viewer.getPageView(pageNum_2 - 1)) === null || _a === void 0 ? void 0 : _a.div;
if (pageDiv) {
pageDiv.classList.add('highlighted');
pageDiv.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
}, 200);
}
catch (err) {
console.warn('Failed to navigate to page:', pageNum_2, err);
}
}
}
}, [pageNumber, viewer, isLoading, numPages]);
// Update zoom level when it changes
React.useEffect(function () {
if (viewer && zoomLevel !== undefined && viewer.pagesCount > 0) {
try {
if (typeof zoomLevel === 'string') {
viewer.currentScaleValue = zoomLevel;
}
else {
viewer.currentScale = zoomLevel;
}
}
catch (err) {
// Silently handle zoom setting errors
console.warn('Failed to set zoom level:', err);
}
}
}, [viewer, zoomLevel]);
// Clean up highlight on unmount
React.useEffect(function () {
return function () {
if (currentHighlight) {
removeHighlight(currentHighlight);
}
};
}, [currentHighlight]);
// Render for SSR
if (!isBrowser) {
return React__default["default"].createElement(PDFPlaceholder, { className: className, style: style });
}
if (!pdfDocument || isLoading) {
return React__default["default"].createElement(Loader, null);
}
return (React__default["default"].createElement("div", { ref: containerRef, className: "relative ".concat(className), style: style, tabIndex: 0 },
numPages > 0 && (React__default["default"].createElement("div", { className: "absolute z-20", style: { top: '8px', left: '8px' } },
React__default["default"].createElement("div", { className: "h-8 px-3 flex items-center justify-center text-sm bg-white/90 backdrop-blur-sm text-gray-700 font-medium rounded-md shadow-sm border border-gray-200/50" },
"Page ",
currentPage,
" of ",
numPages))),
React__default["default"].createElement("div", { className: "absolute z-20", style: { top: '8px', right: '16px' } },
React__default["default"].createElement("div", { className: "flex items-center space-x-2" },
React__default["default"].createElement("button", { onClick: zoomOut, className: "w-8 h-8 flex items-center justify-center bg-white/90 backdrop-blur-sm text-gray-700 rounded-md shadow-sm border border-gray-200/50 hover:bg-gray-50 transition-colors font-medium cursor-pointer", title: "Zoom out (Ctrl+-)" }, "-"),
React__default["default"].createElement("button", { onClick: zoomIn, className: "w-8 h-8 flex items-center justify-center bg-white/90 backdrop-blur-sm text-gray-700 rounded-md shadow-sm border border-gray-200/50 hover:bg-gray-50 transition-colors font-medium cursor-pointer", title: "Zoom in (Ctrl+=)" }, "+"),
React__default["default"].createElement(DownloadButton, { onClick: handleDownload }))),
React__default["default"].createElement("div", { ref: viewerContainerRef, className: "w-full h-full", style: {
opacity: isLoading ? 0 : 1,
transition: 'opacity 0.3s ease'
} }),
isLoading && (React__default["default"].createElement("div", { className: "absolute inset-0" },
React__default["default"].createElement(Loader, null))),
error && !isLoading && (React__default["default"].createElement("div", { className: "absolute inset-0 flex flex-col items-center justify-center p-4", style: {
backgroundColor: 'white'
} },
React__default["default"].createElement("div", { style: { marginBottom: '20px', color: '#dc2626' } },
React__default["default"].createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
React__default["default"].createElement("circle", { cx: "12", cy: "12", r: "10" }),
React__default["default"].createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
React__default["default"].createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" }))),
React__default["default"].createElement("h2", { style: { margin: '0 0 10px', fontSize: '20px', color: '#333' } }, "Failed to Load PDF"),
React__default["default"].createElement("p", { style: { margin: '0 0 20px', color: '#666', textAlign: 'center' } }, error),
React__default["default"].createElement("button", { className: "mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600", onClick: function () { return window.location.reload(); } }, "Reload Page"),
React__default["default"].createElement("div", { className: "mt-4 ml-2" },
React__default["default"].createElement(DownloadButton, { onClick: handleDownload }))))));
};
exports.DocumentViewer = DocumentViewer;
exports.DocumentViewerProvider = DocumentViewerProvider;
exports.useDocumentViewer = useDocumentViewer;
//# sourceMappingURL=index.js.map