@hypercard-ai/hyper-jump 0.3.2 → 1.0.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 CHANGED
@@ -1,145 +1,7 @@
1
- import "./index.css";
1
+ // src/viewer/viewer.tsx
2
+ import { useMemo } from "react";
2
3
 
3
- // src/viewer.tsx
4
- import {
5
- useCallback as useCallback2,
6
- useEffect,
7
- useImperativeHandle,
8
- useMemo,
9
- useRef as useRef2,
10
- useState as useState2
11
- } from "react";
12
- import { Document, pdfjs } from "react-pdf";
13
- import "react-pdf/dist/Page/AnnotationLayer.css";
14
- import "react-pdf/dist/Page/TextLayer.css";
15
- import { List } from "react-window";
16
-
17
- // src/controls.tsx
18
- import { jsx, jsxs } from "react/jsx-runtime";
19
- var ZOOM_OPTIONS = [
20
- { label: "Auto Width", value: "automatic" },
21
- { label: "50%", value: "0.5" },
22
- { label: "75%", value: "0.75" },
23
- { label: "100%", value: "1" },
24
- { label: "125%", value: "1.25" },
25
- { label: "150%", value: "1.5" },
26
- { label: "200%", value: "2" },
27
- { label: "300%", value: "3" },
28
- { label: "400%", value: "4" }
29
- ];
30
- function ChevronLeft() {
31
- return /* @__PURE__ */ jsxs(
32
- "svg",
33
- {
34
- width: "16",
35
- height: "16",
36
- viewBox: "0 0 24 24",
37
- fill: "none",
38
- stroke: "currentColor",
39
- strokeWidth: "2",
40
- strokeLinecap: "round",
41
- strokeLinejoin: "round",
42
- children: [
43
- /* @__PURE__ */ jsx("title", { children: "Previous Page" }),
44
- /* @__PURE__ */ jsx("path", { d: "M15 18l-6-6 6-6" })
45
- ]
46
- }
47
- );
48
- }
49
- function ChevronRight() {
50
- return /* @__PURE__ */ jsxs(
51
- "svg",
52
- {
53
- width: "16",
54
- height: "16",
55
- viewBox: "0 0 24 24",
56
- fill: "none",
57
- stroke: "currentColor",
58
- strokeWidth: "2",
59
- strokeLinecap: "round",
60
- strokeLinejoin: "round",
61
- children: [
62
- /* @__PURE__ */ jsx("title", { children: "Next Page" }),
63
- /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6 6" })
64
- ]
65
- }
66
- );
67
- }
68
- function PDFViewerControls(props) {
69
- const {
70
- onChangeZoom,
71
- pageIndex,
72
- numPages,
73
- zoomConfig,
74
- onNextPage,
75
- onPrevPage
76
- } = props;
77
- const zoomValue = zoomConfig.mode === "automatic" || zoomConfig.mode === "page-width" ? zoomConfig.mode : zoomConfig.value.toString();
78
- return /* @__PURE__ */ jsx("div", { className: "hj-controls", children: /* @__PURE__ */ jsxs("div", { className: "hj-controls-bar", children: [
79
- /* @__PURE__ */ jsxs("div", { className: "hj-controls-group", children: [
80
- /* @__PURE__ */ jsx(
81
- "button",
82
- {
83
- type: "button",
84
- className: "hj-icon-btn",
85
- onClick: onPrevPage,
86
- disabled: pageIndex <= 0,
87
- "aria-label": "Previous Page",
88
- children: /* @__PURE__ */ jsx(ChevronLeft, {})
89
- }
90
- ),
91
- /* @__PURE__ */ jsxs("span", { className: "hj-page-indicator", children: [
92
- pageIndex + 1,
93
- " / ",
94
- numPages
95
- ] }),
96
- /* @__PURE__ */ jsx(
97
- "button",
98
- {
99
- type: "button",
100
- className: "hj-icon-btn",
101
- onClick: onNextPage,
102
- disabled: pageIndex >= numPages - 1,
103
- "aria-label": "Next Page",
104
- children: /* @__PURE__ */ jsx(ChevronRight, {})
105
- }
106
- )
107
- ] }),
108
- /* @__PURE__ */ jsx("div", { className: "hj-divider" }),
109
- /* @__PURE__ */ jsx(
110
- "select",
111
- {
112
- className: "hj-select",
113
- value: zoomValue,
114
- onChange: (e) => onChangeZoom(e.target.value),
115
- "aria-label": "Zoom Level",
116
- children: ZOOM_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
117
- }
118
- )
119
- ] }) });
120
- }
121
-
122
- // src/error-page.tsx
123
- import { jsx as jsx2 } from "react/jsx-runtime";
124
- function PDFErrorPage() {
125
- return /* @__PURE__ */ jsx2("div", { className: "hj-error", children: "Error loading file" });
126
- }
127
-
128
- // src/loading-page.tsx
129
- import { jsx as jsx3 } from "react/jsx-runtime";
130
- function PDFLoadingPage() {
131
- return /* @__PURE__ */ jsx3("div", { className: "hj-loading", children: /* @__PURE__ */ jsx3("div", { className: "hj-spinner" }) });
132
- }
133
-
134
- // src/renderer.tsx
135
- import { Page } from "react-pdf";
136
- import { jsx as jsx4 } from "react/jsx-runtime";
137
- function PDFPageRenderer(props) {
138
- const { index, style, scale } = props;
139
- return /* @__PURE__ */ jsx4("div", { className: "hj-page", style, children: /* @__PURE__ */ jsx4(Page, { pageIndex: index, scale, loading: PDFLoadingPage }) });
140
- }
141
-
142
- // src/use-element-size.ts
4
+ // src/lib/use-element-size.ts
143
5
  import { useCallback, useRef, useState } from "react";
144
6
  function useElementSize() {
145
7
  const [size, setSize] = useState({ width: 0, height: 0 });
@@ -164,178 +26,50 @@ function useElementSize() {
164
26
  return { ref, width: size.width, height: size.height };
165
27
  }
166
28
 
167
- // src/constants.ts
168
- var PAGE_HEIGHT = 842;
169
- var PAGE_WIDTH = 595;
170
-
171
- // src/utils.ts
172
- async function getPageDimensions(document, scale) {
173
- const dims = [];
174
- for (let i = 1; i <= document.numPages; i++) {
175
- try {
176
- const page = await document.getPage(i);
177
- const viewport = page.getViewport({ scale });
178
- const { height, width } = viewport;
179
- dims.push({ height, width });
180
- } catch (error) {
181
- console.error("Failed to get page dimensions", error);
182
- dims.push({
183
- width: PAGE_WIDTH,
184
- height: PAGE_HEIGHT
185
- });
186
- }
29
+ // src/viewer/viewer.tsx
30
+ import { jsx } from "react/jsx-runtime";
31
+ function getExtension(url) {
32
+ try {
33
+ const pathname = new URL(url, "https://placeholder.com").pathname;
34
+ const dot = pathname.lastIndexOf(".");
35
+ if (dot === -1) return null;
36
+ return pathname.slice(dot + 1).toLowerCase();
37
+ } catch {
38
+ return null;
187
39
  }
188
- return dims;
189
40
  }
190
-
191
- // src/viewer.tsx
192
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
193
- pdfjs.GlobalWorkerOptions.workerSrc = new URL(
194
- "pdfjs-dist/build/pdf.worker.min.mjs",
195
- import.meta.url
196
- ).toString();
197
- var PAGE_MARGIN = 12;
198
41
  function HyperJumpViewer(props) {
199
42
  const {
200
43
  url,
201
- initialPage,
202
- onPageChange,
203
- scrollBehavior = "instant",
204
- ref
44
+ type,
45
+ renderers,
46
+ ref,
47
+ initialPosition,
48
+ onPositionChange,
49
+ rendererProps
205
50
  } = props;
206
- const [document, setDocument] = useState2();
207
- const [pageIndex, setPageIndex] = useState2(0);
208
- const [pageDimensions, setPageDimensions] = useState2([]);
209
- const [zoomConfig, setZoomConfig] = useState2({
210
- mode: "automatic",
211
- value: 1
212
- });
213
- const scrollPageRef = useRef2(0);
214
- const { ref: containerRef } = useElementSize();
215
- const listRef = useRef2(null);
216
- const numPages = useMemo(() => {
217
- return document?.numPages || 0;
218
- }, [document]);
219
- useEffect(() => {
220
- if (document) {
221
- getPageDimensions(document, zoomConfig.value).then((value) => {
222
- setPageDimensions(value);
223
- });
224
- }
225
- }, [document, zoomConfig]);
226
- const scrollToPage = useCallback2(
227
- (target) => {
228
- if (numPages === 0 || pageDimensions.length !== numPages) return;
229
- const clamped = Math.max(0, Math.min(Math.floor(target), numPages - 1));
230
- listRef.current?.scrollToRow({
231
- index: clamped,
232
- align: "start",
233
- behavior: scrollBehavior
234
- });
235
- setPageIndex(clamped);
236
- },
237
- [numPages, pageDimensions, scrollBehavior]
238
- );
239
- useImperativeHandle(ref, () => ({ jumpToPage: scrollToPage }), [
240
- scrollToPage
241
- ]);
242
- const hasAppliedInitialPage = useRef2(false);
243
- const onLoadSuccess = useCallback2((response) => {
244
- hasAppliedInitialPage.current = false;
245
- setDocument(response);
246
- }, []);
247
- useEffect(() => {
248
- if (!hasAppliedInitialPage.current && initialPage !== void 0 && pageDimensions.length === numPages && numPages > 0) {
249
- hasAppliedInitialPage.current = true;
250
- const id = requestAnimationFrame(() => scrollToPage(initialPage));
251
- return () => cancelAnimationFrame(id);
252
- }
253
- }, [initialPage, pageDimensions, numPages, scrollToPage]);
254
- const file = useMemo(() => {
255
- return { url };
256
- }, [url]);
257
- const onPrevPage = useCallback2(() => {
258
- if (pageIndex > 0) {
259
- const newPageIndex = pageIndex - 1;
260
- listRef.current?.scrollToRow({
261
- index: newPageIndex,
262
- align: "start",
263
- behavior: scrollBehavior
264
- });
265
- setPageIndex(newPageIndex);
266
- }
267
- }, [pageIndex, scrollBehavior]);
268
- const onNextPage = useCallback2(() => {
269
- if (pageIndex < numPages - 1) {
270
- const newPageIndex = pageIndex + 1;
271
- listRef.current?.scrollToRow({
272
- index: newPageIndex,
273
- align: "start",
274
- behavior: scrollBehavior
275
- });
276
- setPageIndex(newPageIndex);
277
- }
278
- }, [pageIndex, numPages, scrollBehavior]);
279
- const onChangeZoom = useCallback2((value) => {
280
- if (value === "automatic") {
281
- setZoomConfig({ mode: "automatic", value: 1 });
282
- } else {
283
- setZoomConfig({ mode: "manual", value: Number.parseFloat(value) });
51
+ const {
52
+ ref: containerRef,
53
+ width: containerWidth,
54
+ height: containerHeight
55
+ } = useElementSize();
56
+ const renderer = useMemo(() => {
57
+ const ext = type ?? getExtension(url);
58
+ if (!ext) return null;
59
+ return renderers.find((r) => r.type === ext || r.extensions.includes(ext));
60
+ }, [url, type, renderers]);
61
+ return /* @__PURE__ */ jsx("div", { className: "hj-viewer", ref: containerRef, children: renderer ? /* @__PURE__ */ jsx(
62
+ renderer.Component,
63
+ {
64
+ ref,
65
+ url,
66
+ containerWidth,
67
+ containerHeight,
68
+ initialPosition,
69
+ onPositionChange,
70
+ ...rendererProps
284
71
  }
285
- }, []);
286
- const getItemSize = useCallback2(
287
- (index) => {
288
- if (pageDimensions[index]) {
289
- return pageDimensions[index].height + PAGE_MARGIN;
290
- }
291
- return 0;
292
- },
293
- [pageDimensions]
294
- );
295
- const onRowsRendered = useCallback2(
296
- (visibleRows) => {
297
- const prev = scrollPageRef.current;
298
- scrollPageRef.current = visibleRows.startIndex;
299
- if (visibleRows.startIndex !== prev) {
300
- setPageIndex(visibleRows.startIndex);
301
- onPageChange?.(visibleRows.startIndex);
302
- }
303
- },
304
- [onPageChange]
305
- );
306
- return /* @__PURE__ */ jsxs2("div", { className: "hj-viewer", ref: containerRef, children: [
307
- file ? /* @__PURE__ */ jsx5(
308
- Document,
309
- {
310
- file,
311
- onLoadSuccess,
312
- error: PDFErrorPage,
313
- loading: PDFLoadingPage,
314
- children: pageDimensions.length > 0 && pageDimensions.length === numPages && /* @__PURE__ */ jsx5(
315
- List,
316
- {
317
- listRef,
318
- rowCount: numPages,
319
- rowHeight: getItemSize,
320
- onRowsRendered,
321
- rowProps: { scale: zoomConfig.value },
322
- rowComponent: PDFPageRenderer
323
- }
324
- )
325
- }
326
- ) : /* @__PURE__ */ jsx5(PDFLoadingPage, {}),
327
- /* @__PURE__ */ jsx5(
328
- PDFViewerControls,
329
- {
330
- pageIndex,
331
- numPages,
332
- onPrevPage,
333
- onNextPage,
334
- zoomConfig,
335
- onChangeZoom
336
- }
337
- )
338
- ] });
72
+ ) : /* @__PURE__ */ jsx("div", { className: "hj-error", children: "Unsupported file type" }) });
339
73
  }
340
74
  export {
341
75
  HyperJumpViewer
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/viewer.tsx","../src/controls.tsx","../src/error-page.tsx","../src/loading-page.tsx","../src/renderer.tsx","../src/use-element-size.ts","../src/constants.ts","../src/utils.ts"],"sourcesContent":["import type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport {\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { Document, pdfjs } from \"react-pdf\";\nimport \"react-pdf/dist/Page/AnnotationLayer.css\";\nimport \"react-pdf/dist/Page/TextLayer.css\";\nimport type { OnDocumentLoadSuccess } from \"react-pdf/dist/shared/types.js\";\nimport { List, type ListImperativeAPI } from \"react-window\";\nimport PDFViewerControls from \"./controls\";\nimport PDFErrorPage from \"./error-page\";\nimport PDFLoadingPage from \"./loading-page\";\nimport PDFPageRenderer from \"./renderer\";\nimport \"./styles.css\";\nimport type { ZoomConfig } from \"./types\";\nimport { useElementSize } from \"./use-element-size\";\nimport { getPageDimensions } from \"./utils\";\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n\t\"pdfjs-dist/build/pdf.worker.min.mjs\",\n\timport.meta.url,\n).toString();\n\nconst PAGE_MARGIN = 12;\n\nexport interface HyperJumpViewerAPI {\n\t/** Imperatively scroll to a page (0-indexed). Clamps to valid range. */\n\tjumpToPage: (page: number) => void;\n}\n\nexport type ScrollBehavior = \"auto\" | \"instant\" | \"smooth\";\n\nexport interface HyperJumpViewerProps {\n\t/** URL of the PDF file to display */\n\turl: string;\n\t/** Page to show when the document first loads (0-indexed) */\n\tinitialPage?: number;\n\t/** Called when the visible page changes (0-indexed) */\n\tonPageChange?: (page: number) => void;\n\t/** Scroll behavior when navigating between pages (default: \"instant\") */\n\tscrollBehavior?: ScrollBehavior;\n\t/** Ref exposing imperative jumpToPage method */\n\tref?: React.Ref<HyperJumpViewerAPI>;\n}\n\nexport function HyperJumpViewer(props: HyperJumpViewerProps) {\n\tconst {\n\t\turl,\n\t\tinitialPage,\n\t\tonPageChange,\n\t\tscrollBehavior = \"instant\",\n\t\tref,\n\t} = props;\n\tconst [document, setDocument] = useState<PDFDocumentProxy>();\n\tconst [pageIndex, setPageIndex] = useState(0);\n\tconst [pageDimensions, setPageDimensions] = useState<\n\t\t{ width: number; height: number }[]\n\t>([]);\n\tconst [zoomConfig, setZoomConfig] = useState<ZoomConfig>({\n\t\tmode: \"automatic\",\n\t\tvalue: 1,\n\t});\n\tconst scrollPageRef = useRef(0);\n\n\tconst { ref: containerRef } = useElementSize();\n\tconst listRef = useRef<ListImperativeAPI>(null);\n\n\tconst numPages = useMemo(() => {\n\t\treturn document?.numPages || 0;\n\t}, [document]);\n\n\tuseEffect(() => {\n\t\tif (document) {\n\t\t\tgetPageDimensions(document, zoomConfig.value).then((value) => {\n\t\t\t\tsetPageDimensions(value);\n\t\t\t});\n\t\t}\n\t}, [document, zoomConfig]);\n\n\tconst scrollToPage = useCallback(\n\t\t(target: number) => {\n\t\t\tif (numPages === 0 || pageDimensions.length !== numPages) return;\n\t\t\tconst clamped = Math.max(0, Math.min(Math.floor(target), numPages - 1));\n\t\t\tlistRef.current?.scrollToRow({\n\t\t\t\tindex: clamped,\n\t\t\t\talign: \"start\",\n\t\t\t\tbehavior: scrollBehavior,\n\t\t\t});\n\t\t\tsetPageIndex(clamped);\n\t\t},\n\t\t[numPages, pageDimensions, scrollBehavior],\n\t);\n\n\tuseImperativeHandle(ref, () => ({ jumpToPage: scrollToPage }), [\n\t\tscrollToPage,\n\t]);\n\n\tconst hasAppliedInitialPage = useRef(false);\n\n\tconst onLoadSuccess: OnDocumentLoadSuccess = useCallback((response) => {\n\t\thasAppliedInitialPage.current = false;\n\t\tsetDocument(response);\n\t}, []);\n\n\t// Scroll to initialPage once when dimensions are first available.\n\t// Deferred by a frame so the List's scroll container is fully initialized.\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!hasAppliedInitialPage.current &&\n\t\t\tinitialPage !== undefined &&\n\t\t\tpageDimensions.length === numPages &&\n\t\t\tnumPages > 0\n\t\t) {\n\t\t\thasAppliedInitialPage.current = true;\n\t\t\tconst id = requestAnimationFrame(() => scrollToPage(initialPage));\n\t\t\treturn () => cancelAnimationFrame(id);\n\t\t}\n\t}, [initialPage, pageDimensions, numPages, scrollToPage]);\n\n\tconst file = useMemo(() => {\n\t\treturn { url };\n\t}, [url]);\n\n\tconst onPrevPage = useCallback(() => {\n\t\tif (pageIndex > 0) {\n\t\t\tconst newPageIndex = pageIndex - 1;\n\t\t\tlistRef.current?.scrollToRow({\n\t\t\t\tindex: newPageIndex,\n\t\t\t\talign: \"start\",\n\t\t\t\tbehavior: scrollBehavior,\n\t\t\t});\n\t\t\tsetPageIndex(newPageIndex);\n\t\t}\n\t}, [pageIndex, scrollBehavior]);\n\n\tconst onNextPage = useCallback(() => {\n\t\tif (pageIndex < numPages - 1) {\n\t\t\tconst newPageIndex = pageIndex + 1;\n\t\t\tlistRef.current?.scrollToRow({\n\t\t\t\tindex: newPageIndex,\n\t\t\t\talign: \"start\",\n\t\t\t\tbehavior: scrollBehavior,\n\t\t\t});\n\t\t\tsetPageIndex(newPageIndex);\n\t\t}\n\t}, [pageIndex, numPages, scrollBehavior]);\n\n\tconst onChangeZoom = useCallback((value: string) => {\n\t\tif (value === \"automatic\") {\n\t\t\tsetZoomConfig({ mode: \"automatic\", value: 1 });\n\t\t} else {\n\t\t\tsetZoomConfig({ mode: \"manual\", value: Number.parseFloat(value) });\n\t\t}\n\t}, []);\n\n\tconst getItemSize = useCallback(\n\t\t(index: number) => {\n\t\t\tif (pageDimensions[index]) {\n\t\t\t\treturn pageDimensions[index].height + PAGE_MARGIN;\n\t\t\t}\n\t\t\treturn 0;\n\t\t},\n\t\t[pageDimensions],\n\t);\n\n\tconst onRowsRendered = useCallback(\n\t\t(visibleRows: { startIndex: number; stopIndex: number }) => {\n\t\t\tconst prev = scrollPageRef.current;\n\t\t\tscrollPageRef.current = visibleRows.startIndex;\n\t\t\tif (visibleRows.startIndex !== prev) {\n\t\t\t\tsetPageIndex(visibleRows.startIndex);\n\t\t\t\tonPageChange?.(visibleRows.startIndex);\n\t\t\t}\n\t\t},\n\t\t[onPageChange],\n\t);\n\n\treturn (\n\t\t<div className=\"hj-viewer\" ref={containerRef}>\n\t\t\t{file ? (\n\t\t\t\t<Document\n\t\t\t\t\tfile={file}\n\t\t\t\t\tonLoadSuccess={onLoadSuccess}\n\t\t\t\t\terror={PDFErrorPage}\n\t\t\t\t\tloading={PDFLoadingPage}\n\t\t\t\t>\n\t\t\t\t\t{pageDimensions.length > 0 && pageDimensions.length === numPages && (\n\t\t\t\t\t\t<List\n\t\t\t\t\t\t\tlistRef={listRef}\n\t\t\t\t\t\t\trowCount={numPages}\n\t\t\t\t\t\t\trowHeight={getItemSize}\n\t\t\t\t\t\t\tonRowsRendered={onRowsRendered}\n\t\t\t\t\t\t\trowProps={{ scale: zoomConfig.value }}\n\t\t\t\t\t\t\trowComponent={PDFPageRenderer}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</Document>\n\t\t\t) : (\n\t\t\t\t<PDFLoadingPage />\n\t\t\t)}\n\t\t\t<PDFViewerControls\n\t\t\t\tpageIndex={pageIndex}\n\t\t\t\tnumPages={numPages}\n\t\t\t\tonPrevPage={onPrevPage}\n\t\t\t\tonNextPage={onNextPage}\n\t\t\t\tzoomConfig={zoomConfig}\n\t\t\t\tonChangeZoom={onChangeZoom}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","import type { ZoomConfig } from \"./types\";\n\nconst ZOOM_OPTIONS = [\n\t{ label: \"Auto Width\", value: \"automatic\" },\n\t{ label: \"50%\", value: \"0.5\" },\n\t{ label: \"75%\", value: \"0.75\" },\n\t{ label: \"100%\", value: \"1\" },\n\t{ label: \"125%\", value: \"1.25\" },\n\t{ label: \"150%\", value: \"1.5\" },\n\t{ label: \"200%\", value: \"2\" },\n\t{ label: \"300%\", value: \"3\" },\n\t{ label: \"400%\", value: \"4\" },\n];\n\ninterface IProps {\n\tonChangeZoom(value: string): void;\n\tpageIndex: number;\n\tnumPages: number;\n\tzoomConfig: ZoomConfig;\n\tonNextPage(): void;\n\tonPrevPage(): void;\n}\n\nfunction ChevronLeft() {\n\treturn (\n\t\t<svg\n\t\t\twidth=\"16\"\n\t\t\theight=\"16\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t>\n\t\t\t<title>Previous Page</title>\n\t\t\t<path d=\"M15 18l-6-6 6-6\" />\n\t\t</svg>\n\t);\n}\n\nfunction ChevronRight() {\n\treturn (\n\t\t<svg\n\t\t\twidth=\"16\"\n\t\t\theight=\"16\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t>\n\t\t\t<title>Next Page</title>\n\t\t\t<path d=\"M9 18l6-6-6 6\" />\n\t\t</svg>\n\t);\n}\n\nexport default function PDFViewerControls(props: IProps) {\n\tconst {\n\t\tonChangeZoom,\n\t\tpageIndex,\n\t\tnumPages,\n\t\tzoomConfig,\n\t\tonNextPage,\n\t\tonPrevPage,\n\t} = props;\n\n\tconst zoomValue =\n\t\tzoomConfig.mode === \"automatic\" || zoomConfig.mode === \"page-width\"\n\t\t\t? zoomConfig.mode\n\t\t\t: zoomConfig.value.toString();\n\n\treturn (\n\t\t<div className=\"hj-controls\">\n\t\t\t<div className=\"hj-controls-bar\">\n\t\t\t\t<div className=\"hj-controls-group\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"hj-icon-btn\"\n\t\t\t\t\t\tonClick={onPrevPage}\n\t\t\t\t\t\tdisabled={pageIndex <= 0}\n\t\t\t\t\t\taria-label=\"Previous Page\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<ChevronLeft />\n\t\t\t\t\t</button>\n\t\t\t\t\t<span className=\"hj-page-indicator\">\n\t\t\t\t\t\t{pageIndex + 1} / {numPages}\n\t\t\t\t\t</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"hj-icon-btn\"\n\t\t\t\t\t\tonClick={onNextPage}\n\t\t\t\t\t\tdisabled={pageIndex >= numPages - 1}\n\t\t\t\t\t\taria-label=\"Next Page\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<ChevronRight />\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"hj-divider\" />\n\t\t\t\t<select\n\t\t\t\t\tclassName=\"hj-select\"\n\t\t\t\t\tvalue={zoomValue}\n\t\t\t\t\tonChange={(e) => onChangeZoom(e.target.value)}\n\t\t\t\t\taria-label=\"Zoom Level\"\n\t\t\t\t>\n\t\t\t\t\t{ZOOM_OPTIONS.map((opt) => (\n\t\t\t\t\t\t<option key={opt.value} value={opt.value}>\n\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","export default function PDFErrorPage() {\n\treturn <div className=\"hj-error\">Error loading file</div>;\n}\n","export default function PDFLoadingPage() {\n\treturn (\n\t\t<div className=\"hj-loading\">\n\t\t\t<div className=\"hj-spinner\" />\n\t\t</div>\n\t);\n}\n","import { Page } from \"react-pdf\";\nimport type { RowComponentProps } from \"react-window\";\nimport PDFLoadingPage from \"./loading-page\";\n\ninterface RowProps {\n\tscale: number;\n}\n\nexport default function PDFPageRenderer(props: RowComponentProps<RowProps>) {\n\tconst { index, style, scale } = props;\n\treturn (\n\t\t<div className=\"hj-page\" style={style}>\n\t\t\t<Page pageIndex={index} scale={scale} loading={PDFLoadingPage} />\n\t\t</div>\n\t);\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nexport function useElementSize<T extends HTMLElement = HTMLDivElement>() {\n\tconst [size, setSize] = useState({ width: 0, height: 0 });\n\tconst observerRef = useRef<ResizeObserver | null>(null);\n\n\tconst ref = useCallback((node: T | null) => {\n\t\tif (observerRef.current) {\n\t\t\tobserverRef.current.disconnect();\n\t\t\tobserverRef.current = null;\n\t\t}\n\n\t\tif (node) {\n\t\t\tconst observer = new ResizeObserver(([entry]) => {\n\t\t\t\tconst { width, height } = entry.contentRect;\n\t\t\t\tsetSize((prev) => {\n\t\t\t\t\tif (prev.width === width && prev.height === height) return prev;\n\t\t\t\t\treturn { width, height };\n\t\t\t\t});\n\t\t\t});\n\t\t\tobserver.observe(node);\n\t\t\tobserverRef.current = observer;\n\t\t}\n\t}, []);\n\n\treturn { ref, width: size.width, height: size.height };\n}\n","export const PAGE_HEIGHT = 842;\nexport const PAGE_WIDTH = 595;\n","import type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport { PAGE_HEIGHT, PAGE_WIDTH } from \"./constants\";\n\nexport async function getPageDimensions(\n\tdocument: PDFDocumentProxy,\n\tscale: number,\n) {\n\tconst dims = [];\n\tfor (let i = 1; i <= document.numPages; i++) {\n\t\ttry {\n\t\t\tconst page = await document.getPage(i);\n\t\t\tconst viewport = page.getViewport({ scale });\n\t\t\tconst { height, width } = viewport;\n\t\t\tdims.push({ height, width });\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to get page dimensions\", error);\n\t\t\tdims.push({\n\t\t\t\twidth: PAGE_WIDTH,\n\t\t\t\theight: PAGE_HEIGHT,\n\t\t\t});\n\t\t}\n\t}\n\treturn dims;\n}\n"],"mappings":"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","names":["useCallback","useRef","useState","jsx","jsx","jsx","jsx","jsxs","useState","useRef","useCallback"]}
1
+ {"version":3,"sources":["../src/viewer/viewer.tsx","../src/lib/use-element-size.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { FileRenderer, HyperJumpAPI } from \"../lib/types\";\nimport { useElementSize } from \"../lib/use-element-size\";\nimport \"./viewer.css\";\n\nexport interface HyperJumpViewerProps<\n\tT extends Record<string, unknown> = Record<string, unknown>,\n> {\n\t/** URL of the file to display */\n\turl: string;\n\t/** Explicit file type override (e.g. \"pdf\", \"video\"). If omitted, detected from URL extension. */\n\ttype?: string;\n\t/** Renderers that this viewer can use. The first matching renderer wins. */\n\trenderers: FileRenderer[];\n\t/** Ref forwarded to the active renderer (e.g. for imperative APIs like jump). */\n\tref?: React.Ref<HyperJumpAPI>;\n\t/** Initial position to show when the content first loads. Meaning depends on renderer (page index for PDF, seconds for video). */\n\tinitialPosition?: number;\n\t/** Called when the current position changes. Meaning depends on renderer (page index for PDF, seconds for video). */\n\tonPositionChange?: (position: number) => void;\n\t/** Additional props forwarded to the matched renderer. */\n\trendererProps?: T;\n}\n\nfunction getExtension(url: string): string | null {\n\ttry {\n\t\tconst pathname = new URL(url, \"https://placeholder.com\").pathname;\n\t\tconst dot = pathname.lastIndexOf(\".\");\n\t\tif (dot === -1) return null;\n\t\treturn pathname.slice(dot + 1).toLowerCase();\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function HyperJumpViewer<\n\tT extends Record<string, unknown> = Record<string, unknown>,\n>(props: HyperJumpViewerProps<T>) {\n\tconst {\n\t\turl,\n\t\ttype,\n\t\trenderers,\n\t\tref,\n\t\tinitialPosition,\n\t\tonPositionChange,\n\t\trendererProps,\n\t} = props;\n\n\tconst {\n\t\tref: containerRef,\n\t\twidth: containerWidth,\n\t\theight: containerHeight,\n\t} = useElementSize();\n\n\tconst renderer = useMemo(() => {\n\t\tconst ext = type ?? getExtension(url);\n\t\tif (!ext) return null;\n\t\treturn renderers.find((r) => r.type === ext || r.extensions.includes(ext));\n\t}, [url, type, renderers]);\n\n\treturn (\n\t\t<div className=\"hj-viewer\" ref={containerRef}>\n\t\t\t{renderer ? (\n\t\t\t\t<renderer.Component\n\t\t\t\t\tref={ref}\n\t\t\t\t\turl={url}\n\t\t\t\t\tcontainerWidth={containerWidth}\n\t\t\t\t\tcontainerHeight={containerHeight}\n\t\t\t\t\tinitialPosition={initialPosition}\n\t\t\t\t\tonPositionChange={onPositionChange}\n\t\t\t\t\t{...rendererProps}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"hj-error\">Unsupported file type</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nexport function useElementSize<T extends HTMLElement = HTMLDivElement>() {\n\tconst [size, setSize] = useState({ width: 0, height: 0 });\n\tconst observerRef = useRef<ResizeObserver | null>(null);\n\n\tconst ref = useCallback((node: T | null) => {\n\t\tif (observerRef.current) {\n\t\t\tobserverRef.current.disconnect();\n\t\t\tobserverRef.current = null;\n\t\t}\n\n\t\tif (node) {\n\t\t\tconst observer = new ResizeObserver(([entry]) => {\n\t\t\t\tconst { width, height } = entry.contentRect;\n\t\t\t\tsetSize((prev) => {\n\t\t\t\t\tif (prev.width === width && prev.height === height) return prev;\n\t\t\t\t\treturn { width, height };\n\t\t\t\t});\n\t\t\t});\n\t\t\tobserver.observe(node);\n\t\t\tobserverRef.current = observer;\n\t\t}\n\t}, []);\n\n\treturn { ref, width: size.width, height: size.height };\n}\n"],"mappings":";AAAA,SAAS,eAAe;;;ACAxB,SAAS,aAAa,QAAQ,gBAAgB;AAEvC,SAAS,iBAAyD;AACxE,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE,OAAO,GAAG,QAAQ,EAAE,CAAC;AACxD,QAAM,cAAc,OAA8B,IAAI;AAEtD,QAAM,MAAM,YAAY,CAAC,SAAmB;AAC3C,QAAI,YAAY,SAAS;AACxB,kBAAY,QAAQ,WAAW;AAC/B,kBAAY,UAAU;AAAA,IACvB;AAEA,QAAI,MAAM;AACT,YAAM,WAAW,IAAI,eAAe,CAAC,CAAC,KAAK,MAAM;AAChD,cAAM,EAAE,OAAO,OAAO,IAAI,MAAM;AAChC,gBAAQ,CAAC,SAAS;AACjB,cAAI,KAAK,UAAU,SAAS,KAAK,WAAW,OAAQ,QAAO;AAC3D,iBAAO,EAAE,OAAO,OAAO;AAAA,QACxB,CAAC;AAAA,MACF,CAAC;AACD,eAAS,QAAQ,IAAI;AACrB,kBAAY,UAAU;AAAA,IACvB;AAAA,EACD,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AACtD;;;ADqCI;AAvCJ,SAAS,aAAa,KAA4B;AACjD,MAAI;AACH,UAAM,WAAW,IAAI,IAAI,KAAK,yBAAyB,EAAE;AACzD,UAAM,MAAM,SAAS,YAAY,GAAG;AACpC,QAAI,QAAQ,GAAI,QAAO;AACvB,WAAO,SAAS,MAAM,MAAM,CAAC,EAAE,YAAY;AAAA,EAC5C,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEO,SAAS,gBAEd,OAAgC;AACjC,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI;AAEJ,QAAM;AAAA,IACL,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EACT,IAAI,eAAe;AAEnB,QAAM,WAAW,QAAQ,MAAM;AAC9B,UAAM,MAAM,QAAQ,aAAa,GAAG;AACpC,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,WAAW,SAAS,GAAG,CAAC;AAAA,EAC1E,GAAG,CAAC,KAAK,MAAM,SAAS,CAAC;AAEzB,SACC,oBAAC,SAAI,WAAU,aAAY,KAAK,cAC9B,qBACA;AAAA,IAAC,SAAS;AAAA,IAAT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA;AAAA,EACL,IAEA,oBAAC,SAAI,WAAU,YAAW,mCAAqB,GAEjD;AAEF;","names":[]}
@@ -0,0 +1,148 @@
1
+ /* src/pdf/controls.css */
2
+ .hj-controls {
3
+ position: absolute;
4
+ bottom: 12px;
5
+ left: 12px;
6
+ right: 12px;
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ z-index: 10;
11
+ }
12
+ .hj-controls-bar {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: 12px;
16
+ padding: 6px 12px;
17
+ background: #fff;
18
+ border: 1px solid #dee2e6;
19
+ border-radius: 8px;
20
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
21
+ }
22
+ .hj-controls-group {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 6px;
26
+ }
27
+ .hj-divider {
28
+ width: 1px;
29
+ height: 24px;
30
+ background: #dee2e6;
31
+ }
32
+ .hj-btn {
33
+ display: inline-flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ padding: 4px 12px;
37
+ font-size: 13px;
38
+ font-family: inherit;
39
+ line-height: 1.4;
40
+ border: 1px solid #dee2e6;
41
+ border-radius: 4px;
42
+ background: #fff;
43
+ color: #212529;
44
+ cursor: pointer;
45
+ user-select: none;
46
+ white-space: nowrap;
47
+ }
48
+ .hj-btn:hover:not(:disabled) {
49
+ background: #f8f9fa;
50
+ }
51
+ .hj-btn:disabled {
52
+ opacity: 0.5;
53
+ cursor: not-allowed;
54
+ }
55
+ .hj-icon-btn {
56
+ display: inline-flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ width: 28px;
60
+ height: 28px;
61
+ padding: 0;
62
+ border: 1px solid #dee2e6;
63
+ border-radius: 4px;
64
+ background: #fff;
65
+ color: #212529;
66
+ cursor: pointer;
67
+ }
68
+ .hj-icon-btn:hover:not(:disabled) {
69
+ background: #f8f9fa;
70
+ }
71
+ .hj-icon-btn:disabled {
72
+ opacity: 0.5;
73
+ cursor: not-allowed;
74
+ }
75
+ .hj-page-indicator {
76
+ font-size: 13px;
77
+ min-width: 50px;
78
+ text-align: center;
79
+ color: #212529;
80
+ user-select: none;
81
+ }
82
+ .hj-select {
83
+ font-size: 13px;
84
+ font-family: inherit;
85
+ padding: 4px 8px;
86
+ border: 1px solid #dee2e6;
87
+ border-radius: 4px;
88
+ background: #fff;
89
+ color: #212529;
90
+ cursor: pointer;
91
+ min-width: 120px;
92
+ }
93
+ .hj-select:focus {
94
+ outline: 2px solid #228be6;
95
+ outline-offset: -1px;
96
+ }
97
+
98
+ /* src/pdf/error-page.css */
99
+ .hj-error {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ width: 100%;
104
+ height: 100%;
105
+ font-size: 14px;
106
+ }
107
+
108
+ /* src/pdf/loading-page.css */
109
+ .hj-loading {
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ width: 100%;
114
+ height: 100%;
115
+ }
116
+ .hj-spinner {
117
+ width: 32px;
118
+ height: 32px;
119
+ border: 3px solid #e9ecef;
120
+ border-top-color: #228be6;
121
+ border-radius: 50%;
122
+ animation: hj-spin 0.6s linear infinite;
123
+ }
124
+ @keyframes hj-spin {
125
+ to {
126
+ transform: rotate(360deg);
127
+ }
128
+ }
129
+
130
+ /* src/pdf/pdf-viewer.css */
131
+ .hj-viewer .react-pdf__Document {
132
+ height: 100%;
133
+ width: 100%;
134
+ }
135
+ .hj-viewer .react-pdf__message {
136
+ height: 100%;
137
+ }
138
+
139
+ /* src/pdf/renderer.css */
140
+ .hj-page {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: center;
144
+ }
145
+ .hj-page .react-pdf__Page {
146
+ height: 100%;
147
+ }
148
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/pdf/controls.css","../../src/pdf/error-page.css","../../src/pdf/loading-page.css","../../src/pdf/pdf-viewer.css","../../src/pdf/renderer.css"],"sourcesContent":["/* Controls */\n\n.hj-controls {\n\tposition: absolute;\n\tbottom: 12px;\n\tleft: 12px;\n\tright: 12px;\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tz-index: 10;\n}\n\n.hj-controls-bar {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 6px 12px;\n\tbackground: #fff;\n\tborder: 1px solid #dee2e6;\n\tborder-radius: 8px;\n\tbox-shadow:\n\t\t0 1px 3px rgba(0, 0, 0, 0.1),\n\t\t0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n.hj-controls-group {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.hj-divider {\n\twidth: 1px;\n\theight: 24px;\n\tbackground: #dee2e6;\n}\n\n.hj-btn {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 4px 12px;\n\tfont-size: 13px;\n\tfont-family: inherit;\n\tline-height: 1.4;\n\tborder: 1px solid #dee2e6;\n\tborder-radius: 4px;\n\tbackground: #fff;\n\tcolor: #212529;\n\tcursor: pointer;\n\tuser-select: none;\n\twhite-space: nowrap;\n}\n\n.hj-btn:hover:not(:disabled) {\n\tbackground: #f8f9fa;\n}\n\n.hj-btn:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n.hj-icon-btn {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 28px;\n\theight: 28px;\n\tpadding: 0;\n\tborder: 1px solid #dee2e6;\n\tborder-radius: 4px;\n\tbackground: #fff;\n\tcolor: #212529;\n\tcursor: pointer;\n}\n\n.hj-icon-btn:hover:not(:disabled) {\n\tbackground: #f8f9fa;\n}\n\n.hj-icon-btn:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n.hj-page-indicator {\n\tfont-size: 13px;\n\tmin-width: 50px;\n\ttext-align: center;\n\tcolor: #212529;\n\tuser-select: none;\n}\n\n.hj-select {\n\tfont-size: 13px;\n\tfont-family: inherit;\n\tpadding: 4px 8px;\n\tborder: 1px solid #dee2e6;\n\tborder-radius: 4px;\n\tbackground: #fff;\n\tcolor: #212529;\n\tcursor: pointer;\n\tmin-width: 120px;\n}\n\n.hj-select:focus {\n\toutline: 2px solid #228be6;\n\toutline-offset: -1px;\n}\n","/* Error page */\n\n.hj-error {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 100%;\n\theight: 100%;\n\tfont-size: 14px;\n}\n","/* Loading page */\n\n.hj-loading {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 100%;\n\theight: 100%;\n}\n\n.hj-spinner {\n\twidth: 32px;\n\theight: 32px;\n\tborder: 3px solid #e9ecef;\n\tborder-top-color: #228be6;\n\tborder-radius: 50%;\n\tanimation: hj-spin 0.6s linear infinite;\n}\n\n@keyframes hj-spin {\n\tto {\n\t\ttransform: rotate(360deg);\n\t}\n}\n","/* PDF Viewer — react-pdf integration styles */\n\n.hj-viewer .react-pdf__Document {\n\theight: 100%;\n\twidth: 100%;\n}\n\n.hj-viewer .react-pdf__message {\n\theight: 100%;\n}\n","/* Page renderer */\n\n.hj-page {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\n\n.hj-page .react-pdf__Page {\n\theight: 100%;\n}\n"],"mappings":";AAEA,CAAC;AACA,YAAU;AACV,UAAQ;AACR,QAAM;AACN,SAAO;AACP,WAAS;AACT,mBAAiB;AACjB,eAAa;AACb,WAAS;AACV;AAEA,CAAC;AACA,WAAS;AACT,eAAa;AACb,OAAK;AACL,WAAS,IAAI;AACb,cAAY;AACZ,UAAQ,IAAI,MAAM;AAClB,iBAAe;AACf,cACC,EAAE,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAC5B,EAAE,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAC1B;AAEA,CAAC;AACA,WAAS;AACT,eAAa;AACb,OAAK;AACN;AAEA,CAAC;AACA,SAAO;AACP,UAAQ;AACR,cAAY;AACb;AAEA,CAAC;AACA,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,WAAS,IAAI;AACb,aAAW;AACX,eAAa;AACb,eAAa;AACb,UAAQ,IAAI,MAAM;AAClB,iBAAe;AACf,cAAY;AACZ,SAAO;AACP,UAAQ;AACR,eAAa;AACb,eAAa;AACd;AAEA,CAjBC,MAiBM,MAAM,KAAK;AACjB,cAAY;AACb;AAEA,CArBC,MAqBM;AACN,WAAS;AACT,UAAQ;AACT;AAEA,CAAC;AACA,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,WAAS;AACT,UAAQ,IAAI,MAAM;AAClB,iBAAe;AACf,cAAY;AACZ,SAAO;AACP,UAAQ;AACT;AAEA,CAdC,WAcW,MAAM,KAAK;AACtB,cAAY;AACb;AAEA,CAlBC,WAkBW;AACX,WAAS;AACT,UAAQ;AACT;AAEA,CAAC;AACA,aAAW;AACX,aAAW;AACX,cAAY;AACZ,SAAO;AACP,eAAa;AACd;AAEA,CAAC;AACA,aAAW;AACX,eAAa;AACb,WAAS,IAAI;AACb,UAAQ,IAAI,MAAM;AAClB,iBAAe;AACf,cAAY;AACZ,SAAO;AACP,UAAQ;AACR,aAAW;AACZ;AAEA,CAZC,SAYS;AACT,WAAS,IAAI,MAAM;AACnB,kBAAgB;AACjB;;;AC5GA,CAAC;AACA,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,aAAW;AACZ;;;ACPA,CAAC;AACA,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACT;AAEA,CAAC;AACA,SAAO;AACP,UAAQ;AACR,UAAQ,IAAI,MAAM;AAClB,oBAAkB;AAClB,iBAAe;AACf,aAAW,QAAQ,KAAK,OAAO;AAChC;AAEA,WAHY;AAIX;AACC,eAAW,OAAO;AACnB;AACD;;;ACrBA,CAAC,UAAU,CAAC;AACX,UAAQ;AACR,SAAO;AACR;AAEA,CALC,UAKU,CAAC;AACX,UAAQ;AACT;;;ACPA,CAAC;AACA,WAAS;AACT,eAAa;AACb,mBAAiB;AAClB;AAEA,CANC,QAMQ,CAAC;AACT,UAAQ;AACT;","names":[]}
@@ -0,0 +1,12 @@
1
+ import { H as HyperJumpAPI, R as RendererProps, F as FileRenderer } from '../types-Ce3M8ej7.js';
2
+
3
+ type HyperJumpPdfViewerAPI = HyperJumpAPI;
4
+ type ScrollBehavior = "auto" | "instant" | "smooth";
5
+ interface HyperJumpPdfViewerProps extends RendererProps {
6
+ /** Scroll behavior when navigating between pages (default: "instant") */
7
+ scrollBehavior?: ScrollBehavior;
8
+ }
9
+ /** Renderer descriptor for PDF files. Pass this to HyperJumpViewer's `renderers` prop. */
10
+ declare const PdfRenderer: FileRenderer;
11
+
12
+ export { type HyperJumpPdfViewerAPI, type HyperJumpPdfViewerProps, PdfRenderer, type ScrollBehavior };