@relevaince/document-viewer 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +78 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,28 +20,11 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
DocumentViewer: () => DocumentViewer,
|
|
24
|
-
getFileType: () => getFileType
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
|
|
28
|
-
// src/utils/getFileType.ts
|
|
29
|
-
function getFileType(url) {
|
|
30
|
-
const clean = url.split("?")[0].split("#")[0].toLowerCase();
|
|
31
|
-
if (clean.endsWith(".pdf")) return "pdf";
|
|
32
|
-
if (clean.endsWith(".docx") || clean.endsWith(".doc")) return "docx";
|
|
33
|
-
return "unknown";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
23
|
// src/components/renderers/PdfRenderer.tsx
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
var DEFAULT_WORKER_URL = "https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs";
|
|
24
|
+
var PdfRenderer_exports = {};
|
|
25
|
+
__export(PdfRenderer_exports, {
|
|
26
|
+
PdfRenderer: () => PdfRenderer
|
|
27
|
+
});
|
|
42
28
|
function bboxToHighlight(target) {
|
|
43
29
|
if (!target.bbox) return null;
|
|
44
30
|
const page = target.page ?? 1;
|
|
@@ -64,7 +50,7 @@ function bboxToHighlight(target) {
|
|
|
64
50
|
}
|
|
65
51
|
function CitationHighlightRenderer() {
|
|
66
52
|
const { highlight, isScrolledTo } = (0, import_react_pdf_highlighter_extended.useHighlightContainerContext)();
|
|
67
|
-
return /* @__PURE__ */ (0,
|
|
53
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
68
54
|
import_react_pdf_highlighter_extended.AreaHighlight,
|
|
69
55
|
{
|
|
70
56
|
highlight,
|
|
@@ -102,12 +88,12 @@ function PdfRenderer({
|
|
|
102
88
|
const handleUtilsRef = (0, import_react.useCallback)((utils) => {
|
|
103
89
|
highlighterRef.current = utils;
|
|
104
90
|
}, []);
|
|
105
|
-
return /* @__PURE__ */ (0,
|
|
91
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: "100%", height: "100%", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
106
92
|
import_react_pdf_highlighter_extended.PdfLoader,
|
|
107
93
|
{
|
|
108
94
|
document: target.url,
|
|
109
95
|
workerSrc: workerUrl ?? DEFAULT_WORKER_URL,
|
|
110
|
-
beforeLoad: () => loadingComponent ?? /* @__PURE__ */ (0,
|
|
96
|
+
beforeLoad: () => loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
111
97
|
"div",
|
|
112
98
|
{
|
|
113
99
|
style: {
|
|
@@ -125,7 +111,7 @@ function PdfRenderer({
|
|
|
125
111
|
),
|
|
126
112
|
errorMessage: (error) => {
|
|
127
113
|
onLoadError?.(error);
|
|
128
|
-
return errorComponent ?? /* @__PURE__ */ (0,
|
|
114
|
+
return errorComponent ?? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
129
115
|
"div",
|
|
130
116
|
{
|
|
131
117
|
style: {
|
|
@@ -147,7 +133,7 @@ function PdfRenderer({
|
|
|
147
133
|
loadNotifiedRef.current = true;
|
|
148
134
|
onLoadSuccess?.();
|
|
149
135
|
}
|
|
150
|
-
return /* @__PURE__ */ (0,
|
|
136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
151
137
|
import_react_pdf_highlighter_extended.PdfHighlighter,
|
|
152
138
|
{
|
|
153
139
|
pdfDocument,
|
|
@@ -157,21 +143,51 @@ function PdfRenderer({
|
|
|
157
143
|
width: "100%",
|
|
158
144
|
height: "100%"
|
|
159
145
|
},
|
|
160
|
-
children: /* @__PURE__ */ (0,
|
|
146
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CitationHighlightRenderer, {})
|
|
161
147
|
}
|
|
162
148
|
);
|
|
163
149
|
}
|
|
164
150
|
}
|
|
165
151
|
) });
|
|
166
152
|
}
|
|
153
|
+
var import_react, import_react_pdf_highlighter_extended, import_jsx_runtime2, CITATION_ID, DEFAULT_WORKER_URL;
|
|
154
|
+
var init_PdfRenderer = __esm({
|
|
155
|
+
"src/components/renderers/PdfRenderer.tsx"() {
|
|
156
|
+
"use strict";
|
|
157
|
+
import_react = require("react");
|
|
158
|
+
import_react_pdf_highlighter_extended = require("react-pdf-highlighter-extended");
|
|
159
|
+
import_jsx_runtime2 = require("react/jsx-runtime");
|
|
160
|
+
CITATION_ID = "citation-highlight";
|
|
161
|
+
DEFAULT_WORKER_URL = "https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs";
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// src/index.ts
|
|
166
|
+
var index_exports = {};
|
|
167
|
+
__export(index_exports, {
|
|
168
|
+
DocumentViewer: () => DocumentViewer,
|
|
169
|
+
getFileType: () => getFileType
|
|
170
|
+
});
|
|
171
|
+
module.exports = __toCommonJS(index_exports);
|
|
172
|
+
|
|
173
|
+
// src/components/DocumentViewer.tsx
|
|
174
|
+
var import_react2 = require("react");
|
|
175
|
+
|
|
176
|
+
// src/utils/getFileType.ts
|
|
177
|
+
function getFileType(url) {
|
|
178
|
+
const clean = url.split("?")[0].split("#")[0].toLowerCase();
|
|
179
|
+
if (clean.endsWith(".pdf")) return "pdf";
|
|
180
|
+
if (clean.endsWith(".docx") || clean.endsWith(".doc")) return "docx";
|
|
181
|
+
return "unknown";
|
|
182
|
+
}
|
|
167
183
|
|
|
168
184
|
// src/components/renderers/DocxRenderer.tsx
|
|
169
|
-
var
|
|
185
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
170
186
|
function DocxRenderer({ target }) {
|
|
171
187
|
const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
|
|
172
188
|
target.url
|
|
173
189
|
)}`;
|
|
174
|
-
return /* @__PURE__ */ (0,
|
|
190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
175
191
|
"iframe",
|
|
176
192
|
{
|
|
177
193
|
src: officeUrl,
|
|
@@ -189,6 +205,20 @@ function DocxRenderer({ target }) {
|
|
|
189
205
|
|
|
190
206
|
// src/components/DocumentViewer.tsx
|
|
191
207
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
208
|
+
var LazyPdfRenderer = (0, import_react2.lazy)(
|
|
209
|
+
() => Promise.resolve().then(() => (init_PdfRenderer(), PdfRenderer_exports)).then((mod) => ({
|
|
210
|
+
default: mod.PdfRenderer
|
|
211
|
+
}))
|
|
212
|
+
);
|
|
213
|
+
function ClientOnly({
|
|
214
|
+
children,
|
|
215
|
+
fallback
|
|
216
|
+
}) {
|
|
217
|
+
const [mounted, setMounted] = (0, import_react2.useState)(false);
|
|
218
|
+
(0, import_react2.useEffect)(() => setMounted(true), []);
|
|
219
|
+
if (!mounted) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback ?? null });
|
|
220
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
|
|
221
|
+
}
|
|
192
222
|
function DocumentViewer({
|
|
193
223
|
target,
|
|
194
224
|
pdfWorkerUrl,
|
|
@@ -209,9 +239,25 @@ function DocumentViewer({
|
|
|
209
239
|
borderRadius: 8,
|
|
210
240
|
background: "#fafafa"
|
|
211
241
|
};
|
|
242
|
+
const loadingFallback = loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
243
|
+
"div",
|
|
244
|
+
{
|
|
245
|
+
style: {
|
|
246
|
+
display: "flex",
|
|
247
|
+
alignItems: "center",
|
|
248
|
+
justifyContent: "center",
|
|
249
|
+
width: "100%",
|
|
250
|
+
height: "100%",
|
|
251
|
+
color: "#888",
|
|
252
|
+
fontSize: 14,
|
|
253
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
254
|
+
},
|
|
255
|
+
children: "Loading\u2026"
|
|
256
|
+
}
|
|
257
|
+
);
|
|
212
258
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: wrapperStyle, children: [
|
|
213
|
-
type === "pdf" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
214
|
-
|
|
259
|
+
type === "pdf" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ClientOnly, { fallback: loadingFallback, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react2.Suspense, { fallback: loadingFallback, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
260
|
+
LazyPdfRenderer,
|
|
215
261
|
{
|
|
216
262
|
target,
|
|
217
263
|
workerUrl: pdfWorkerUrl,
|
|
@@ -220,7 +266,7 @@ function DocumentViewer({
|
|
|
220
266
|
onLoadSuccess,
|
|
221
267
|
onLoadError
|
|
222
268
|
}
|
|
223
|
-
),
|
|
269
|
+
) }) }),
|
|
224
270
|
type === "docx" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DocxRenderer, { target }),
|
|
225
271
|
type === "unknown" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
226
272
|
"div",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/utils/getFileType.ts","../src/components/renderers/PdfRenderer.tsx","../src/components/renderers/DocxRenderer.tsx","../src/components/DocumentViewer.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Components */\n/* ------------------------------------------------------------------ */\nexport { DocumentViewer } from \"./components/DocumentViewer\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\nexport type { ViewerTarget, BBox } from \"./types/ViewerTarget\";\nexport type { DocumentViewerProps } from \"./types/DocumentViewerProps\";\n\n/* ------------------------------------------------------------------ */\n/* Utilities */\n/* ------------------------------------------------------------------ */\nexport { getFileType } from \"./utils/getFileType\";\n","/**\n * Infer document type from URL extension.\n * Strips query-strings and fragments before matching.\n */\nexport function getFileType(url: string): \"pdf\" | \"docx\" | \"unknown\" {\n const clean = url.split(\"?\")[0].split(\"#\")[0].toLowerCase();\n\n if (clean.endsWith(\".pdf\")) return \"pdf\";\n if (clean.endsWith(\".docx\") || clean.endsWith(\".doc\")) return \"docx\";\n\n return \"unknown\";\n}\n","import React, { useRef, useEffect, useMemo, useCallback } from \"react\";\nimport {\n PdfLoader,\n PdfHighlighter,\n AreaHighlight,\n useHighlightContainerContext,\n} from \"react-pdf-highlighter-extended\";\nimport type {\n Highlight,\n ScaledPosition,\n PdfHighlighterUtils,\n} from \"react-pdf-highlighter-extended\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/* ------------------------------------------------------------------ */\n/* Constants */\n/* ------------------------------------------------------------------ */\n\nconst CITATION_ID = \"citation-highlight\";\n\nconst DEFAULT_WORKER_URL =\n \"https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs\";\n\n/* ------------------------------------------------------------------ */\n/* BBox → Highlight conversion */\n/* ------------------------------------------------------------------ */\n\n/**\n * Convert our normalised (0-1) BBox into the Scaled coordinate system\n * used by react-pdf-highlighter-extended.\n *\n * The library internally computes:\n * viewportX = (viewportWidth × scaled.x1) / scaled.width\n *\n * So `scaled.x1 / scaled.width` must equal the normalised value.\n * We simply store the 0-1 value directly and set width / height to 1.\n */\nfunction bboxToHighlight(target: ViewerTarget): Highlight | null {\n if (!target.bbox) return null;\n\n const page = target.page ?? 1;\n const { x1, y1, x2, y2 } = target.bbox;\n\n const scaledRect = {\n x1,\n y1,\n x2,\n y2,\n width: 1,\n height: 1,\n pageNumber: page,\n };\n\n const position: ScaledPosition = {\n boundingRect: scaledRect,\n rects: [scaledRect],\n };\n\n return {\n id: CITATION_ID,\n type: \"area\",\n position,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Highlight child renderer */\n/* ------------------------------------------------------------------ */\n\n/**\n * Rendered once per highlight by PdfHighlighter's internal loop.\n * Uses context to access the highlight's viewport position.\n */\nfunction CitationHighlightRenderer() {\n const { highlight, isScrolledTo } = useHighlightContainerContext();\n\n return (\n <AreaHighlight\n highlight={highlight}\n isScrolledTo={isScrolledTo}\n style={{\n background: isScrolledTo\n ? \"rgba(255, 171, 0, 0.35)\"\n : \"rgba(255, 171, 0, 0.2)\",\n border: \"1.5px solid rgba(255, 171, 0, 0.6)\",\n borderRadius: 2,\n transition: \"background 200ms ease\",\n }}\n />\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* PdfRenderer */\n/* ------------------------------------------------------------------ */\n\ninterface PdfRendererProps {\n target: ViewerTarget;\n workerUrl?: string;\n loadingComponent?: React.ReactNode;\n errorComponent?: React.ReactNode;\n onLoadSuccess?: () => void;\n onLoadError?: (err: Error) => void;\n}\n\nexport function PdfRenderer({\n target,\n workerUrl,\n loadingComponent,\n errorComponent,\n onLoadSuccess,\n onLoadError,\n}: PdfRendererProps) {\n const highlighterRef = useRef<PdfHighlighterUtils | null>(null);\n const loadNotifiedRef = useRef(false);\n\n /* Build highlight array from target bbox */\n const highlights = useMemo(() => {\n const h = bboxToHighlight(target);\n return h ? [h] : [];\n }, [target]);\n\n /* Scroll to the citation highlight once the viewer is ready */\n useEffect(() => {\n if (highlights.length === 0) return;\n\n const timer = setTimeout(() => {\n highlighterRef.current?.scrollToHighlight(highlights[0]);\n }, 400);\n\n return () => clearTimeout(timer);\n }, [highlights]);\n\n /* Stable callback so we don't re-trigger PdfHighlighter renders */\n const handleUtilsRef = useCallback((utils: PdfHighlighterUtils) => {\n highlighterRef.current = utils;\n }, []);\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", position: \"relative\" }}>\n <PdfLoader\n document={target.url}\n workerSrc={workerUrl ?? DEFAULT_WORKER_URL}\n beforeLoad={() =>\n loadingComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Loading PDF…\n </div>\n )\n }\n errorMessage={(error) => {\n onLoadError?.(error);\n return (\n errorComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#ef4444\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Failed to load PDF\n </div>\n )\n );\n }}\n >\n {(pdfDocument) => {\n /* Notify once */\n if (!loadNotifiedRef.current) {\n loadNotifiedRef.current = true;\n onLoadSuccess?.();\n }\n\n return (\n <PdfHighlighter\n pdfDocument={pdfDocument}\n highlights={highlights}\n utilsRef={handleUtilsRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n }}\n >\n <CitationHighlightRenderer />\n </PdfHighlighter>\n );\n }}\n </PdfLoader>\n </div>\n );\n}\n","import React from \"react\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/**\n * Renders DOCX / DOC files via the free Microsoft Office Online embed viewer.\n * The document URL must be publicly accessible.\n */\nexport function DocxRenderer({ target }: { target: ViewerTarget }) {\n const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(\n target.url\n )}`;\n\n return (\n <iframe\n src={officeUrl}\n title={target.title ?? \"Document preview\"}\n style={{\n width: \"100%\",\n height: \"100%\",\n border: \"none\",\n borderRadius: 6,\n background: \"#fff\",\n }}\n />\n );\n}\n","import React from \"react\";\nimport type { DocumentViewerProps } from \"../types/DocumentViewerProps\";\nimport { getFileType } from \"../utils/getFileType\";\nimport { PdfRenderer } from \"./renderers/PdfRenderer\";\nimport { DocxRenderer } from \"./renderers/DocxRenderer\";\n\n/**\n * Top-level component that routes to the correct renderer\n * based on the target's file type.\n *\n * ```tsx\n * <DocumentViewer\n * target={{\n * url: \"https://example.com/report.pdf\",\n * page: 3,\n * bbox: { x1: 0.1, y1: 0.2, x2: 0.5, y2: 0.3 },\n * }}\n * />\n * ```\n */\nexport function DocumentViewer({\n target,\n pdfWorkerUrl,\n loadingComponent,\n errorComponent,\n onLoadStart,\n onLoadSuccess,\n onLoadError,\n className,\n}: DocumentViewerProps) {\n if (!target) return null;\n\n const type = target.fileType ?? getFileType(target.url);\n\n const wrapperStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n position: \"relative\",\n overflow: \"hidden\",\n borderRadius: 8,\n background: \"#fafafa\",\n };\n\n return (\n <div className={className} style={wrapperStyle}>\n {type === \"pdf\" && (\n <PdfRenderer\n target={target}\n workerUrl={pdfWorkerUrl}\n loadingComponent={loadingComponent}\n errorComponent={errorComponent}\n onLoadSuccess={onLoadSuccess}\n onLoadError={onLoadError}\n />\n )}\n\n {type === \"docx\" && <DocxRenderer target={target} />}\n\n {type === \"unknown\" && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Unsupported file type\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/renderers/PdfRenderer.tsx","../src/index.ts","../src/components/DocumentViewer.tsx","../src/utils/getFileType.ts","../src/components/renderers/DocxRenderer.tsx"],"sourcesContent":["import React, { useRef, useEffect, useMemo, useCallback } from \"react\";\nimport {\n PdfLoader,\n PdfHighlighter,\n AreaHighlight,\n useHighlightContainerContext,\n} from \"react-pdf-highlighter-extended\";\nimport type {\n Highlight,\n ScaledPosition,\n PdfHighlighterUtils,\n} from \"react-pdf-highlighter-extended\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/* ------------------------------------------------------------------ */\n/* Constants */\n/* ------------------------------------------------------------------ */\n\nconst CITATION_ID = \"citation-highlight\";\n\nconst DEFAULT_WORKER_URL =\n \"https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs\";\n\n/* ------------------------------------------------------------------ */\n/* BBox → Highlight conversion */\n/* ------------------------------------------------------------------ */\n\n/**\n * Convert our normalised (0-1) BBox into the Scaled coordinate system\n * used by react-pdf-highlighter-extended.\n *\n * The library internally computes:\n * viewportX = (viewportWidth × scaled.x1) / scaled.width\n *\n * So `scaled.x1 / scaled.width` must equal the normalised value.\n * We simply store the 0-1 value directly and set width / height to 1.\n */\nfunction bboxToHighlight(target: ViewerTarget): Highlight | null {\n if (!target.bbox) return null;\n\n const page = target.page ?? 1;\n const { x1, y1, x2, y2 } = target.bbox;\n\n const scaledRect = {\n x1,\n y1,\n x2,\n y2,\n width: 1,\n height: 1,\n pageNumber: page,\n };\n\n const position: ScaledPosition = {\n boundingRect: scaledRect,\n rects: [scaledRect],\n };\n\n return {\n id: CITATION_ID,\n type: \"area\",\n position,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Highlight child renderer */\n/* ------------------------------------------------------------------ */\n\n/**\n * Rendered once per highlight by PdfHighlighter's internal loop.\n * Uses context to access the highlight's viewport position.\n */\nfunction CitationHighlightRenderer() {\n const { highlight, isScrolledTo } = useHighlightContainerContext();\n\n return (\n <AreaHighlight\n highlight={highlight}\n isScrolledTo={isScrolledTo}\n style={{\n background: isScrolledTo\n ? \"rgba(255, 171, 0, 0.35)\"\n : \"rgba(255, 171, 0, 0.2)\",\n border: \"1.5px solid rgba(255, 171, 0, 0.6)\",\n borderRadius: 2,\n transition: \"background 200ms ease\",\n }}\n />\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* PdfRenderer */\n/* ------------------------------------------------------------------ */\n\ninterface PdfRendererProps {\n target: ViewerTarget;\n workerUrl?: string;\n loadingComponent?: React.ReactNode;\n errorComponent?: React.ReactNode;\n onLoadSuccess?: () => void;\n onLoadError?: (err: Error) => void;\n}\n\nexport function PdfRenderer({\n target,\n workerUrl,\n loadingComponent,\n errorComponent,\n onLoadSuccess,\n onLoadError,\n}: PdfRendererProps) {\n const highlighterRef = useRef<PdfHighlighterUtils | null>(null);\n const loadNotifiedRef = useRef(false);\n\n /* Build highlight array from target bbox */\n const highlights = useMemo(() => {\n const h = bboxToHighlight(target);\n return h ? [h] : [];\n }, [target]);\n\n /* Scroll to the citation highlight once the viewer is ready */\n useEffect(() => {\n if (highlights.length === 0) return;\n\n const timer = setTimeout(() => {\n highlighterRef.current?.scrollToHighlight(highlights[0]);\n }, 400);\n\n return () => clearTimeout(timer);\n }, [highlights]);\n\n /* Stable callback so we don't re-trigger PdfHighlighter renders */\n const handleUtilsRef = useCallback((utils: PdfHighlighterUtils) => {\n highlighterRef.current = utils;\n }, []);\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", position: \"relative\" }}>\n <PdfLoader\n document={target.url}\n workerSrc={workerUrl ?? DEFAULT_WORKER_URL}\n beforeLoad={() =>\n loadingComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Loading PDF…\n </div>\n )\n }\n errorMessage={(error) => {\n onLoadError?.(error);\n return (\n errorComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#ef4444\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Failed to load PDF\n </div>\n )\n );\n }}\n >\n {(pdfDocument) => {\n /* Notify once */\n if (!loadNotifiedRef.current) {\n loadNotifiedRef.current = true;\n onLoadSuccess?.();\n }\n\n return (\n <PdfHighlighter\n pdfDocument={pdfDocument}\n highlights={highlights}\n utilsRef={handleUtilsRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n }}\n >\n <CitationHighlightRenderer />\n </PdfHighlighter>\n );\n }}\n </PdfLoader>\n </div>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* Components */\n/* ------------------------------------------------------------------ */\nexport { DocumentViewer } from \"./components/DocumentViewer\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\nexport type { ViewerTarget, BBox } from \"./types/ViewerTarget\";\nexport type { DocumentViewerProps } from \"./types/DocumentViewerProps\";\n\n/* ------------------------------------------------------------------ */\n/* Utilities */\n/* ------------------------------------------------------------------ */\nexport { getFileType } from \"./utils/getFileType\";\n","import React, { lazy, Suspense, useState, useEffect } from \"react\";\nimport type { DocumentViewerProps } from \"../types/DocumentViewerProps\";\nimport { getFileType } from \"../utils/getFileType\";\nimport { DocxRenderer } from \"./renderers/DocxRenderer\";\n\n/* ------------------------------------------------------------------ */\n/* Lazy-load PdfRenderer so pdfjs-dist never runs on the server. */\n/* pdfjs-dist references `window` / `document` at the module level, */\n/* which crashes during Next.js SSR. */\n/* ------------------------------------------------------------------ */\n\nconst LazyPdfRenderer = lazy(() =>\n import(\"./renderers/PdfRenderer\").then((mod) => ({\n default: mod.PdfRenderer,\n }))\n);\n\n/**\n * Tiny wrapper that defers children until after first client-side mount.\n * Prevents any browser-only code from executing during SSR.\n */\nfunction ClientOnly({\n children,\n fallback,\n}: {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n}) {\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n if (!mounted) return <>{fallback ?? null}</>;\n return <>{children}</>;\n}\n\n/**\n * Top-level component that routes to the correct renderer\n * based on the target's file type.\n *\n * ```tsx\n * <DocumentViewer\n * target={{\n * url: \"https://example.com/report.pdf\",\n * page: 3,\n * bbox: { x1: 0.1, y1: 0.2, x2: 0.5, y2: 0.3 },\n * }}\n * />\n * ```\n */\nexport function DocumentViewer({\n target,\n pdfWorkerUrl,\n loadingComponent,\n errorComponent,\n onLoadStart,\n onLoadSuccess,\n onLoadError,\n className,\n}: DocumentViewerProps) {\n if (!target) return null;\n\n const type = target.fileType ?? getFileType(target.url);\n\n const wrapperStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n position: \"relative\",\n overflow: \"hidden\",\n borderRadius: 8,\n background: \"#fafafa\",\n };\n\n const loadingFallback = loadingComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Loading…\n </div>\n );\n\n return (\n <div className={className} style={wrapperStyle}>\n {type === \"pdf\" && (\n <ClientOnly fallback={loadingFallback}>\n <Suspense fallback={loadingFallback}>\n <LazyPdfRenderer\n target={target}\n workerUrl={pdfWorkerUrl}\n loadingComponent={loadingComponent}\n errorComponent={errorComponent}\n onLoadSuccess={onLoadSuccess}\n onLoadError={onLoadError}\n />\n </Suspense>\n </ClientOnly>\n )}\n\n {type === \"docx\" && <DocxRenderer target={target} />}\n\n {type === \"unknown\" && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Unsupported file type\n </div>\n )}\n </div>\n );\n}\n","/**\n * Infer document type from URL extension.\n * Strips query-strings and fragments before matching.\n */\nexport function getFileType(url: string): \"pdf\" | \"docx\" | \"unknown\" {\n const clean = url.split(\"?\")[0].split(\"#\")[0].toLowerCase();\n\n if (clean.endsWith(\".pdf\")) return \"pdf\";\n if (clean.endsWith(\".docx\") || clean.endsWith(\".doc\")) return \"docx\";\n\n return \"unknown\";\n}\n","import React from \"react\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/**\n * Renders DOCX / DOC files via the free Microsoft Office Online embed viewer.\n * The document URL must be publicly accessible.\n */\nexport function DocxRenderer({ target }: { target: ViewerTarget }) {\n const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(\n target.url\n )}`;\n\n return (\n <iframe\n src={officeUrl}\n title={target.title ?? \"Document preview\"}\n style={{\n width: \"100%\",\n height: \"100%\",\n border: \"none\",\n borderRadius: 6,\n background: \"#fff\",\n }}\n />\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_react","import_jsx_runtime"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
8
10
|
|
|
9
11
|
// src/components/renderers/PdfRenderer.tsx
|
|
12
|
+
var PdfRenderer_exports = {};
|
|
13
|
+
__export(PdfRenderer_exports, {
|
|
14
|
+
PdfRenderer: () => PdfRenderer
|
|
15
|
+
});
|
|
10
16
|
import { useRef, useEffect, useMemo, useCallback } from "react";
|
|
11
17
|
import {
|
|
12
18
|
PdfLoader,
|
|
@@ -14,9 +20,7 @@ import {
|
|
|
14
20
|
AreaHighlight,
|
|
15
21
|
useHighlightContainerContext
|
|
16
22
|
} from "react-pdf-highlighter-extended";
|
|
17
|
-
import { jsx } from "react/jsx-runtime";
|
|
18
|
-
var CITATION_ID = "citation-highlight";
|
|
19
|
-
var DEFAULT_WORKER_URL = "https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs";
|
|
23
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
20
24
|
function bboxToHighlight(target) {
|
|
21
25
|
if (!target.bbox) return null;
|
|
22
26
|
const page = target.page ?? 1;
|
|
@@ -42,7 +46,7 @@ function bboxToHighlight(target) {
|
|
|
42
46
|
}
|
|
43
47
|
function CitationHighlightRenderer() {
|
|
44
48
|
const { highlight, isScrolledTo } = useHighlightContainerContext();
|
|
45
|
-
return /* @__PURE__ */
|
|
49
|
+
return /* @__PURE__ */ jsx2(
|
|
46
50
|
AreaHighlight,
|
|
47
51
|
{
|
|
48
52
|
highlight,
|
|
@@ -80,12 +84,12 @@ function PdfRenderer({
|
|
|
80
84
|
const handleUtilsRef = useCallback((utils) => {
|
|
81
85
|
highlighterRef.current = utils;
|
|
82
86
|
}, []);
|
|
83
|
-
return /* @__PURE__ */
|
|
87
|
+
return /* @__PURE__ */ jsx2("div", { style: { width: "100%", height: "100%", position: "relative" }, children: /* @__PURE__ */ jsx2(
|
|
84
88
|
PdfLoader,
|
|
85
89
|
{
|
|
86
90
|
document: target.url,
|
|
87
91
|
workerSrc: workerUrl ?? DEFAULT_WORKER_URL,
|
|
88
|
-
beforeLoad: () => loadingComponent ?? /* @__PURE__ */
|
|
92
|
+
beforeLoad: () => loadingComponent ?? /* @__PURE__ */ jsx2(
|
|
89
93
|
"div",
|
|
90
94
|
{
|
|
91
95
|
style: {
|
|
@@ -103,7 +107,7 @@ function PdfRenderer({
|
|
|
103
107
|
),
|
|
104
108
|
errorMessage: (error) => {
|
|
105
109
|
onLoadError?.(error);
|
|
106
|
-
return errorComponent ?? /* @__PURE__ */
|
|
110
|
+
return errorComponent ?? /* @__PURE__ */ jsx2(
|
|
107
111
|
"div",
|
|
108
112
|
{
|
|
109
113
|
style: {
|
|
@@ -125,7 +129,7 @@ function PdfRenderer({
|
|
|
125
129
|
loadNotifiedRef.current = true;
|
|
126
130
|
onLoadSuccess?.();
|
|
127
131
|
}
|
|
128
|
-
return /* @__PURE__ */
|
|
132
|
+
return /* @__PURE__ */ jsx2(
|
|
129
133
|
PdfHighlighter,
|
|
130
134
|
{
|
|
131
135
|
pdfDocument,
|
|
@@ -135,21 +139,40 @@ function PdfRenderer({
|
|
|
135
139
|
width: "100%",
|
|
136
140
|
height: "100%"
|
|
137
141
|
},
|
|
138
|
-
children: /* @__PURE__ */
|
|
142
|
+
children: /* @__PURE__ */ jsx2(CitationHighlightRenderer, {})
|
|
139
143
|
}
|
|
140
144
|
);
|
|
141
145
|
}
|
|
142
146
|
}
|
|
143
147
|
) });
|
|
144
148
|
}
|
|
149
|
+
var CITATION_ID, DEFAULT_WORKER_URL;
|
|
150
|
+
var init_PdfRenderer = __esm({
|
|
151
|
+
"src/components/renderers/PdfRenderer.tsx"() {
|
|
152
|
+
"use strict";
|
|
153
|
+
CITATION_ID = "citation-highlight";
|
|
154
|
+
DEFAULT_WORKER_URL = "https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs";
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// src/components/DocumentViewer.tsx
|
|
159
|
+
import { lazy, Suspense, useState, useEffect as useEffect2 } from "react";
|
|
160
|
+
|
|
161
|
+
// src/utils/getFileType.ts
|
|
162
|
+
function getFileType(url) {
|
|
163
|
+
const clean = url.split("?")[0].split("#")[0].toLowerCase();
|
|
164
|
+
if (clean.endsWith(".pdf")) return "pdf";
|
|
165
|
+
if (clean.endsWith(".docx") || clean.endsWith(".doc")) return "docx";
|
|
166
|
+
return "unknown";
|
|
167
|
+
}
|
|
145
168
|
|
|
146
169
|
// src/components/renderers/DocxRenderer.tsx
|
|
147
|
-
import { jsx
|
|
170
|
+
import { jsx } from "react/jsx-runtime";
|
|
148
171
|
function DocxRenderer({ target }) {
|
|
149
172
|
const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
|
|
150
173
|
target.url
|
|
151
174
|
)}`;
|
|
152
|
-
return /* @__PURE__ */
|
|
175
|
+
return /* @__PURE__ */ jsx(
|
|
153
176
|
"iframe",
|
|
154
177
|
{
|
|
155
178
|
src: officeUrl,
|
|
@@ -166,7 +189,21 @@ function DocxRenderer({ target }) {
|
|
|
166
189
|
}
|
|
167
190
|
|
|
168
191
|
// src/components/DocumentViewer.tsx
|
|
169
|
-
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
192
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
193
|
+
var LazyPdfRenderer = lazy(
|
|
194
|
+
() => Promise.resolve().then(() => (init_PdfRenderer(), PdfRenderer_exports)).then((mod) => ({
|
|
195
|
+
default: mod.PdfRenderer
|
|
196
|
+
}))
|
|
197
|
+
);
|
|
198
|
+
function ClientOnly({
|
|
199
|
+
children,
|
|
200
|
+
fallback
|
|
201
|
+
}) {
|
|
202
|
+
const [mounted, setMounted] = useState(false);
|
|
203
|
+
useEffect2(() => setMounted(true), []);
|
|
204
|
+
if (!mounted) return /* @__PURE__ */ jsx3(Fragment, { children: fallback ?? null });
|
|
205
|
+
return /* @__PURE__ */ jsx3(Fragment, { children });
|
|
206
|
+
}
|
|
170
207
|
function DocumentViewer({
|
|
171
208
|
target,
|
|
172
209
|
pdfWorkerUrl,
|
|
@@ -187,9 +224,25 @@ function DocumentViewer({
|
|
|
187
224
|
borderRadius: 8,
|
|
188
225
|
background: "#fafafa"
|
|
189
226
|
};
|
|
227
|
+
const loadingFallback = loadingComponent ?? /* @__PURE__ */ jsx3(
|
|
228
|
+
"div",
|
|
229
|
+
{
|
|
230
|
+
style: {
|
|
231
|
+
display: "flex",
|
|
232
|
+
alignItems: "center",
|
|
233
|
+
justifyContent: "center",
|
|
234
|
+
width: "100%",
|
|
235
|
+
height: "100%",
|
|
236
|
+
color: "#888",
|
|
237
|
+
fontSize: 14,
|
|
238
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
239
|
+
},
|
|
240
|
+
children: "Loading\u2026"
|
|
241
|
+
}
|
|
242
|
+
);
|
|
190
243
|
return /* @__PURE__ */ jsxs("div", { className, style: wrapperStyle, children: [
|
|
191
|
-
type === "pdf" && /* @__PURE__ */ jsx3(
|
|
192
|
-
|
|
244
|
+
type === "pdf" && /* @__PURE__ */ jsx3(ClientOnly, { fallback: loadingFallback, children: /* @__PURE__ */ jsx3(Suspense, { fallback: loadingFallback, children: /* @__PURE__ */ jsx3(
|
|
245
|
+
LazyPdfRenderer,
|
|
193
246
|
{
|
|
194
247
|
target,
|
|
195
248
|
workerUrl: pdfWorkerUrl,
|
|
@@ -198,7 +251,7 @@ function DocumentViewer({
|
|
|
198
251
|
onLoadSuccess,
|
|
199
252
|
onLoadError
|
|
200
253
|
}
|
|
201
|
-
),
|
|
254
|
+
) }) }),
|
|
202
255
|
type === "docx" && /* @__PURE__ */ jsx3(DocxRenderer, { target }),
|
|
203
256
|
type === "unknown" && /* @__PURE__ */ jsx3(
|
|
204
257
|
"div",
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/getFileType.ts","../src/components/renderers/PdfRenderer.tsx","../src/components/renderers/DocxRenderer.tsx","../src/components/DocumentViewer.tsx"],"sourcesContent":["/**\n * Infer document type from URL extension.\n * Strips query-strings and fragments before matching.\n */\nexport function getFileType(url: string): \"pdf\" | \"docx\" | \"unknown\" {\n const clean = url.split(\"?\")[0].split(\"#\")[0].toLowerCase();\n\n if (clean.endsWith(\".pdf\")) return \"pdf\";\n if (clean.endsWith(\".docx\") || clean.endsWith(\".doc\")) return \"docx\";\n\n return \"unknown\";\n}\n","import React, { useRef, useEffect, useMemo, useCallback } from \"react\";\nimport {\n PdfLoader,\n PdfHighlighter,\n AreaHighlight,\n useHighlightContainerContext,\n} from \"react-pdf-highlighter-extended\";\nimport type {\n Highlight,\n ScaledPosition,\n PdfHighlighterUtils,\n} from \"react-pdf-highlighter-extended\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/* ------------------------------------------------------------------ */\n/* Constants */\n/* ------------------------------------------------------------------ */\n\nconst CITATION_ID = \"citation-highlight\";\n\nconst DEFAULT_WORKER_URL =\n \"https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs\";\n\n/* ------------------------------------------------------------------ */\n/* BBox → Highlight conversion */\n/* ------------------------------------------------------------------ */\n\n/**\n * Convert our normalised (0-1) BBox into the Scaled coordinate system\n * used by react-pdf-highlighter-extended.\n *\n * The library internally computes:\n * viewportX = (viewportWidth × scaled.x1) / scaled.width\n *\n * So `scaled.x1 / scaled.width` must equal the normalised value.\n * We simply store the 0-1 value directly and set width / height to 1.\n */\nfunction bboxToHighlight(target: ViewerTarget): Highlight | null {\n if (!target.bbox) return null;\n\n const page = target.page ?? 1;\n const { x1, y1, x2, y2 } = target.bbox;\n\n const scaledRect = {\n x1,\n y1,\n x2,\n y2,\n width: 1,\n height: 1,\n pageNumber: page,\n };\n\n const position: ScaledPosition = {\n boundingRect: scaledRect,\n rects: [scaledRect],\n };\n\n return {\n id: CITATION_ID,\n type: \"area\",\n position,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Highlight child renderer */\n/* ------------------------------------------------------------------ */\n\n/**\n * Rendered once per highlight by PdfHighlighter's internal loop.\n * Uses context to access the highlight's viewport position.\n */\nfunction CitationHighlightRenderer() {\n const { highlight, isScrolledTo } = useHighlightContainerContext();\n\n return (\n <AreaHighlight\n highlight={highlight}\n isScrolledTo={isScrolledTo}\n style={{\n background: isScrolledTo\n ? \"rgba(255, 171, 0, 0.35)\"\n : \"rgba(255, 171, 0, 0.2)\",\n border: \"1.5px solid rgba(255, 171, 0, 0.6)\",\n borderRadius: 2,\n transition: \"background 200ms ease\",\n }}\n />\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* PdfRenderer */\n/* ------------------------------------------------------------------ */\n\ninterface PdfRendererProps {\n target: ViewerTarget;\n workerUrl?: string;\n loadingComponent?: React.ReactNode;\n errorComponent?: React.ReactNode;\n onLoadSuccess?: () => void;\n onLoadError?: (err: Error) => void;\n}\n\nexport function PdfRenderer({\n target,\n workerUrl,\n loadingComponent,\n errorComponent,\n onLoadSuccess,\n onLoadError,\n}: PdfRendererProps) {\n const highlighterRef = useRef<PdfHighlighterUtils | null>(null);\n const loadNotifiedRef = useRef(false);\n\n /* Build highlight array from target bbox */\n const highlights = useMemo(() => {\n const h = bboxToHighlight(target);\n return h ? [h] : [];\n }, [target]);\n\n /* Scroll to the citation highlight once the viewer is ready */\n useEffect(() => {\n if (highlights.length === 0) return;\n\n const timer = setTimeout(() => {\n highlighterRef.current?.scrollToHighlight(highlights[0]);\n }, 400);\n\n return () => clearTimeout(timer);\n }, [highlights]);\n\n /* Stable callback so we don't re-trigger PdfHighlighter renders */\n const handleUtilsRef = useCallback((utils: PdfHighlighterUtils) => {\n highlighterRef.current = utils;\n }, []);\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", position: \"relative\" }}>\n <PdfLoader\n document={target.url}\n workerSrc={workerUrl ?? DEFAULT_WORKER_URL}\n beforeLoad={() =>\n loadingComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Loading PDF…\n </div>\n )\n }\n errorMessage={(error) => {\n onLoadError?.(error);\n return (\n errorComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#ef4444\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Failed to load PDF\n </div>\n )\n );\n }}\n >\n {(pdfDocument) => {\n /* Notify once */\n if (!loadNotifiedRef.current) {\n loadNotifiedRef.current = true;\n onLoadSuccess?.();\n }\n\n return (\n <PdfHighlighter\n pdfDocument={pdfDocument}\n highlights={highlights}\n utilsRef={handleUtilsRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n }}\n >\n <CitationHighlightRenderer />\n </PdfHighlighter>\n );\n }}\n </PdfLoader>\n </div>\n );\n}\n","import React from \"react\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/**\n * Renders DOCX / DOC files via the free Microsoft Office Online embed viewer.\n * The document URL must be publicly accessible.\n */\nexport function DocxRenderer({ target }: { target: ViewerTarget }) {\n const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(\n target.url\n )}`;\n\n return (\n <iframe\n src={officeUrl}\n title={target.title ?? \"Document preview\"}\n style={{\n width: \"100%\",\n height: \"100%\",\n border: \"none\",\n borderRadius: 6,\n background: \"#fff\",\n }}\n />\n );\n}\n","import React from \"react\";\nimport type { DocumentViewerProps } from \"../types/DocumentViewerProps\";\nimport { getFileType } from \"../utils/getFileType\";\nimport { PdfRenderer } from \"./renderers/PdfRenderer\";\nimport { DocxRenderer } from \"./renderers/DocxRenderer\";\n\n/**\n * Top-level component that routes to the correct renderer\n * based on the target's file type.\n *\n * ```tsx\n * <DocumentViewer\n * target={{\n * url: \"https://example.com/report.pdf\",\n * page: 3,\n * bbox: { x1: 0.1, y1: 0.2, x2: 0.5, y2: 0.3 },\n * }}\n * />\n * ```\n */\nexport function DocumentViewer({\n target,\n pdfWorkerUrl,\n loadingComponent,\n errorComponent,\n onLoadStart,\n onLoadSuccess,\n onLoadError,\n className,\n}: DocumentViewerProps) {\n if (!target) return null;\n\n const type = target.fileType ?? getFileType(target.url);\n\n const wrapperStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n position: \"relative\",\n overflow: \"hidden\",\n borderRadius: 8,\n background: \"#fafafa\",\n };\n\n return (\n <div className={className} style={wrapperStyle}>\n {type === \"pdf\" && (\n <PdfRenderer\n target={target}\n workerUrl={pdfWorkerUrl}\n loadingComponent={loadingComponent}\n errorComponent={errorComponent}\n onLoadSuccess={onLoadSuccess}\n onLoadError={onLoadError}\n />\n )}\n\n {type === \"docx\" && <DocxRenderer target={target} />}\n\n {type === \"unknown\" && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Unsupported file type\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/renderers/PdfRenderer.tsx","../src/components/DocumentViewer.tsx","../src/utils/getFileType.ts","../src/components/renderers/DocxRenderer.tsx"],"sourcesContent":["import React, { useRef, useEffect, useMemo, useCallback } from \"react\";\nimport {\n PdfLoader,\n PdfHighlighter,\n AreaHighlight,\n useHighlightContainerContext,\n} from \"react-pdf-highlighter-extended\";\nimport type {\n Highlight,\n ScaledPosition,\n PdfHighlighterUtils,\n} from \"react-pdf-highlighter-extended\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/* ------------------------------------------------------------------ */\n/* Constants */\n/* ------------------------------------------------------------------ */\n\nconst CITATION_ID = \"citation-highlight\";\n\nconst DEFAULT_WORKER_URL =\n \"https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs\";\n\n/* ------------------------------------------------------------------ */\n/* BBox → Highlight conversion */\n/* ------------------------------------------------------------------ */\n\n/**\n * Convert our normalised (0-1) BBox into the Scaled coordinate system\n * used by react-pdf-highlighter-extended.\n *\n * The library internally computes:\n * viewportX = (viewportWidth × scaled.x1) / scaled.width\n *\n * So `scaled.x1 / scaled.width` must equal the normalised value.\n * We simply store the 0-1 value directly and set width / height to 1.\n */\nfunction bboxToHighlight(target: ViewerTarget): Highlight | null {\n if (!target.bbox) return null;\n\n const page = target.page ?? 1;\n const { x1, y1, x2, y2 } = target.bbox;\n\n const scaledRect = {\n x1,\n y1,\n x2,\n y2,\n width: 1,\n height: 1,\n pageNumber: page,\n };\n\n const position: ScaledPosition = {\n boundingRect: scaledRect,\n rects: [scaledRect],\n };\n\n return {\n id: CITATION_ID,\n type: \"area\",\n position,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Highlight child renderer */\n/* ------------------------------------------------------------------ */\n\n/**\n * Rendered once per highlight by PdfHighlighter's internal loop.\n * Uses context to access the highlight's viewport position.\n */\nfunction CitationHighlightRenderer() {\n const { highlight, isScrolledTo } = useHighlightContainerContext();\n\n return (\n <AreaHighlight\n highlight={highlight}\n isScrolledTo={isScrolledTo}\n style={{\n background: isScrolledTo\n ? \"rgba(255, 171, 0, 0.35)\"\n : \"rgba(255, 171, 0, 0.2)\",\n border: \"1.5px solid rgba(255, 171, 0, 0.6)\",\n borderRadius: 2,\n transition: \"background 200ms ease\",\n }}\n />\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* PdfRenderer */\n/* ------------------------------------------------------------------ */\n\ninterface PdfRendererProps {\n target: ViewerTarget;\n workerUrl?: string;\n loadingComponent?: React.ReactNode;\n errorComponent?: React.ReactNode;\n onLoadSuccess?: () => void;\n onLoadError?: (err: Error) => void;\n}\n\nexport function PdfRenderer({\n target,\n workerUrl,\n loadingComponent,\n errorComponent,\n onLoadSuccess,\n onLoadError,\n}: PdfRendererProps) {\n const highlighterRef = useRef<PdfHighlighterUtils | null>(null);\n const loadNotifiedRef = useRef(false);\n\n /* Build highlight array from target bbox */\n const highlights = useMemo(() => {\n const h = bboxToHighlight(target);\n return h ? [h] : [];\n }, [target]);\n\n /* Scroll to the citation highlight once the viewer is ready */\n useEffect(() => {\n if (highlights.length === 0) return;\n\n const timer = setTimeout(() => {\n highlighterRef.current?.scrollToHighlight(highlights[0]);\n }, 400);\n\n return () => clearTimeout(timer);\n }, [highlights]);\n\n /* Stable callback so we don't re-trigger PdfHighlighter renders */\n const handleUtilsRef = useCallback((utils: PdfHighlighterUtils) => {\n highlighterRef.current = utils;\n }, []);\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", position: \"relative\" }}>\n <PdfLoader\n document={target.url}\n workerSrc={workerUrl ?? DEFAULT_WORKER_URL}\n beforeLoad={() =>\n loadingComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Loading PDF…\n </div>\n )\n }\n errorMessage={(error) => {\n onLoadError?.(error);\n return (\n errorComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#ef4444\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Failed to load PDF\n </div>\n )\n );\n }}\n >\n {(pdfDocument) => {\n /* Notify once */\n if (!loadNotifiedRef.current) {\n loadNotifiedRef.current = true;\n onLoadSuccess?.();\n }\n\n return (\n <PdfHighlighter\n pdfDocument={pdfDocument}\n highlights={highlights}\n utilsRef={handleUtilsRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n }}\n >\n <CitationHighlightRenderer />\n </PdfHighlighter>\n );\n }}\n </PdfLoader>\n </div>\n );\n}\n","import React, { lazy, Suspense, useState, useEffect } from \"react\";\nimport type { DocumentViewerProps } from \"../types/DocumentViewerProps\";\nimport { getFileType } from \"../utils/getFileType\";\nimport { DocxRenderer } from \"./renderers/DocxRenderer\";\n\n/* ------------------------------------------------------------------ */\n/* Lazy-load PdfRenderer so pdfjs-dist never runs on the server. */\n/* pdfjs-dist references `window` / `document` at the module level, */\n/* which crashes during Next.js SSR. */\n/* ------------------------------------------------------------------ */\n\nconst LazyPdfRenderer = lazy(() =>\n import(\"./renderers/PdfRenderer\").then((mod) => ({\n default: mod.PdfRenderer,\n }))\n);\n\n/**\n * Tiny wrapper that defers children until after first client-side mount.\n * Prevents any browser-only code from executing during SSR.\n */\nfunction ClientOnly({\n children,\n fallback,\n}: {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n}) {\n const [mounted, setMounted] = useState(false);\n useEffect(() => setMounted(true), []);\n if (!mounted) return <>{fallback ?? null}</>;\n return <>{children}</>;\n}\n\n/**\n * Top-level component that routes to the correct renderer\n * based on the target's file type.\n *\n * ```tsx\n * <DocumentViewer\n * target={{\n * url: \"https://example.com/report.pdf\",\n * page: 3,\n * bbox: { x1: 0.1, y1: 0.2, x2: 0.5, y2: 0.3 },\n * }}\n * />\n * ```\n */\nexport function DocumentViewer({\n target,\n pdfWorkerUrl,\n loadingComponent,\n errorComponent,\n onLoadStart,\n onLoadSuccess,\n onLoadError,\n className,\n}: DocumentViewerProps) {\n if (!target) return null;\n\n const type = target.fileType ?? getFileType(target.url);\n\n const wrapperStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n position: \"relative\",\n overflow: \"hidden\",\n borderRadius: 8,\n background: \"#fafafa\",\n };\n\n const loadingFallback = loadingComponent ?? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Loading…\n </div>\n );\n\n return (\n <div className={className} style={wrapperStyle}>\n {type === \"pdf\" && (\n <ClientOnly fallback={loadingFallback}>\n <Suspense fallback={loadingFallback}>\n <LazyPdfRenderer\n target={target}\n workerUrl={pdfWorkerUrl}\n loadingComponent={loadingComponent}\n errorComponent={errorComponent}\n onLoadSuccess={onLoadSuccess}\n onLoadError={onLoadError}\n />\n </Suspense>\n </ClientOnly>\n )}\n\n {type === \"docx\" && <DocxRenderer target={target} />}\n\n {type === \"unknown\" && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"#888\",\n fontSize: 14,\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n }}\n >\n Unsupported file type\n </div>\n )}\n </div>\n );\n}\n","/**\n * Infer document type from URL extension.\n * Strips query-strings and fragments before matching.\n */\nexport function getFileType(url: string): \"pdf\" | \"docx\" | \"unknown\" {\n const clean = url.split(\"?\")[0].split(\"#\")[0].toLowerCase();\n\n if (clean.endsWith(\".pdf\")) return \"pdf\";\n if (clean.endsWith(\".docx\") || clean.endsWith(\".doc\")) return \"docx\";\n\n return \"unknown\";\n}\n","import React from \"react\";\nimport type { ViewerTarget } from \"../../types/ViewerTarget\";\n\n/**\n * Renders DOCX / DOC files via the free Microsoft Office Online embed viewer.\n * The document URL must be publicly accessible.\n */\nexport function DocxRenderer({ target }: { target: ViewerTarget }) {\n const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(\n target.url\n )}`;\n\n return (\n <iframe\n src={officeUrl}\n title={target.title ?? \"Document preview\"}\n style={{\n width: \"100%\",\n height: \"100%\",\n border: \"none\",\n borderRadius: 6,\n background: \"#fff\",\n }}\n />\n );\n}\n"],"mappings":"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","names":["jsx","useEffect","jsx","useEffect"]}
|