@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/README.md +135 -32
- package/dist/index.css +1 -139
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +18 -23
- package/dist/index.js +40 -306
- package/dist/index.js.map +1 -1
- package/dist/pdf/index.css +148 -0
- package/dist/pdf/index.css.map +1 -0
- package/dist/pdf/index.d.ts +12 -0
- package/dist/pdf/index.js +315 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/types-Ce3M8ej7.d.ts +31 -0
- package/dist/video/index.css +18 -0
- package/dist/video/index.css.map +1 -0
- package/dist/video/index.d.ts +11 -0
- package/dist/video/index.js +48 -0
- package/dist/video/index.js.map +1 -0
- package/package.json +25 -8
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// src/pdf/pdf-viewer.tsx
|
|
2
|
+
import {
|
|
3
|
+
forwardRef,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState
|
|
10
|
+
} from "react";
|
|
11
|
+
import { Document, pdfjs } from "react-pdf";
|
|
12
|
+
import "react-pdf/dist/Page/AnnotationLayer.css";
|
|
13
|
+
import "react-pdf/dist/Page/TextLayer.css";
|
|
14
|
+
import { List } from "react-window";
|
|
15
|
+
|
|
16
|
+
// src/pdf/controls.tsx
|
|
17
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
|
+
var ZOOM_OPTIONS = [
|
|
19
|
+
{ label: "Auto Width", value: "automatic" },
|
|
20
|
+
{ label: "50%", value: "0.5" },
|
|
21
|
+
{ label: "75%", value: "0.75" },
|
|
22
|
+
{ label: "100%", value: "1" },
|
|
23
|
+
{ label: "125%", value: "1.25" },
|
|
24
|
+
{ label: "150%", value: "1.5" },
|
|
25
|
+
{ label: "200%", value: "2" },
|
|
26
|
+
{ label: "300%", value: "3" },
|
|
27
|
+
{ label: "400%", value: "4" }
|
|
28
|
+
];
|
|
29
|
+
function ChevronLeft() {
|
|
30
|
+
return /* @__PURE__ */ jsxs(
|
|
31
|
+
"svg",
|
|
32
|
+
{
|
|
33
|
+
width: "16",
|
|
34
|
+
height: "16",
|
|
35
|
+
viewBox: "0 0 24 24",
|
|
36
|
+
fill: "none",
|
|
37
|
+
stroke: "currentColor",
|
|
38
|
+
strokeWidth: "2",
|
|
39
|
+
strokeLinecap: "round",
|
|
40
|
+
strokeLinejoin: "round",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx("title", { children: "Previous Page" }),
|
|
43
|
+
/* @__PURE__ */ jsx("path", { d: "M15 18l-6-6 6-6" })
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function ChevronRight() {
|
|
49
|
+
return /* @__PURE__ */ jsxs(
|
|
50
|
+
"svg",
|
|
51
|
+
{
|
|
52
|
+
width: "16",
|
|
53
|
+
height: "16",
|
|
54
|
+
viewBox: "0 0 24 24",
|
|
55
|
+
fill: "none",
|
|
56
|
+
stroke: "currentColor",
|
|
57
|
+
strokeWidth: "2",
|
|
58
|
+
strokeLinecap: "round",
|
|
59
|
+
strokeLinejoin: "round",
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsx("title", { children: "Next Page" }),
|
|
62
|
+
/* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6 6" })
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
function PDFViewerControls(props) {
|
|
68
|
+
const {
|
|
69
|
+
onChangeZoom,
|
|
70
|
+
pageIndex,
|
|
71
|
+
numPages,
|
|
72
|
+
zoomConfig,
|
|
73
|
+
onNextPage,
|
|
74
|
+
onPrevPage
|
|
75
|
+
} = props;
|
|
76
|
+
const zoomValue = zoomConfig.mode === "automatic" || zoomConfig.mode === "page-width" ? zoomConfig.mode : zoomConfig.value.toString();
|
|
77
|
+
return /* @__PURE__ */ jsx("div", { className: "hj-controls", children: /* @__PURE__ */ jsxs("div", { className: "hj-controls-bar", children: [
|
|
78
|
+
/* @__PURE__ */ jsxs("div", { className: "hj-controls-group", children: [
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
"button",
|
|
81
|
+
{
|
|
82
|
+
type: "button",
|
|
83
|
+
className: "hj-icon-btn",
|
|
84
|
+
onClick: onPrevPage,
|
|
85
|
+
disabled: pageIndex <= 0,
|
|
86
|
+
"aria-label": "Previous Page",
|
|
87
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {})
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsxs("span", { className: "hj-page-indicator", children: [
|
|
91
|
+
pageIndex + 1,
|
|
92
|
+
" / ",
|
|
93
|
+
numPages
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
type: "button",
|
|
99
|
+
className: "hj-icon-btn",
|
|
100
|
+
onClick: onNextPage,
|
|
101
|
+
disabled: pageIndex >= numPages - 1,
|
|
102
|
+
"aria-label": "Next Page",
|
|
103
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {})
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
] }),
|
|
107
|
+
/* @__PURE__ */ jsx("div", { className: "hj-divider" }),
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
"select",
|
|
110
|
+
{
|
|
111
|
+
className: "hj-select",
|
|
112
|
+
value: zoomValue,
|
|
113
|
+
onChange: (e) => onChangeZoom(e.target.value),
|
|
114
|
+
"aria-label": "Zoom Level",
|
|
115
|
+
children: ZOOM_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
] }) });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/pdf/error-page.tsx
|
|
122
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
123
|
+
function PDFErrorPage() {
|
|
124
|
+
return /* @__PURE__ */ jsx2("div", { className: "hj-error", children: "Error loading file" });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/pdf/loading-page.tsx
|
|
128
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
129
|
+
function PDFLoadingPage() {
|
|
130
|
+
return /* @__PURE__ */ jsx3("div", { className: "hj-loading", children: /* @__PURE__ */ jsx3("div", { className: "hj-spinner" }) });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/pdf/renderer.tsx
|
|
134
|
+
import { Page } from "react-pdf";
|
|
135
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
136
|
+
function PDFPageRenderer(props) {
|
|
137
|
+
const { index, style, scale } = props;
|
|
138
|
+
return /* @__PURE__ */ jsx4("div", { className: "hj-page", style, children: /* @__PURE__ */ jsx4(Page, { pageIndex: index, scale, loading: PDFLoadingPage }) });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/pdf/constants.ts
|
|
142
|
+
var PAGE_HEIGHT = 842;
|
|
143
|
+
var PAGE_WIDTH = 595;
|
|
144
|
+
|
|
145
|
+
// src/pdf/utils.ts
|
|
146
|
+
async function getPageDimensions(document, scale) {
|
|
147
|
+
const dims = [];
|
|
148
|
+
for (let i = 1; i <= document.numPages; i++) {
|
|
149
|
+
try {
|
|
150
|
+
const page = await document.getPage(i);
|
|
151
|
+
const viewport = page.getViewport({ scale });
|
|
152
|
+
const { height, width } = viewport;
|
|
153
|
+
dims.push({ height, width });
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error("Failed to get page dimensions", error);
|
|
156
|
+
dims.push({
|
|
157
|
+
width: PAGE_WIDTH,
|
|
158
|
+
height: PAGE_HEIGHT
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return dims;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/pdf/pdf-viewer.tsx
|
|
166
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
167
|
+
pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
168
|
+
var PAGE_MARGIN = 12;
|
|
169
|
+
var PdfViewerComponent = forwardRef(function PdfViewerComponent2(props, ref) {
|
|
170
|
+
const {
|
|
171
|
+
url,
|
|
172
|
+
initialPosition,
|
|
173
|
+
onPositionChange,
|
|
174
|
+
scrollBehavior = "instant"
|
|
175
|
+
} = props;
|
|
176
|
+
const [document, setDocument] = useState();
|
|
177
|
+
const [pageIndex, setPageIndex] = useState(0);
|
|
178
|
+
const [pageDimensions, setPageDimensions] = useState([]);
|
|
179
|
+
const [zoomConfig, setZoomConfig] = useState({
|
|
180
|
+
mode: "automatic",
|
|
181
|
+
value: 1
|
|
182
|
+
});
|
|
183
|
+
const scrollPageRef = useRef(0);
|
|
184
|
+
const listRef = useRef(null);
|
|
185
|
+
const numPages = useMemo(() => {
|
|
186
|
+
return document?.numPages || 0;
|
|
187
|
+
}, [document]);
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (document) {
|
|
190
|
+
getPageDimensions(document, zoomConfig.value).then((value) => {
|
|
191
|
+
setPageDimensions(value);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}, [document, zoomConfig]);
|
|
195
|
+
const scrollToPage = useCallback(
|
|
196
|
+
(target) => {
|
|
197
|
+
if (numPages === 0 || pageDimensions.length !== numPages) return;
|
|
198
|
+
const clamped = Math.max(0, Math.min(Math.floor(target), numPages - 1));
|
|
199
|
+
listRef.current?.scrollToRow({
|
|
200
|
+
index: clamped,
|
|
201
|
+
align: "start",
|
|
202
|
+
behavior: scrollBehavior
|
|
203
|
+
});
|
|
204
|
+
setPageIndex(clamped);
|
|
205
|
+
},
|
|
206
|
+
[numPages, pageDimensions, scrollBehavior]
|
|
207
|
+
);
|
|
208
|
+
useImperativeHandle(ref, () => ({ jump: scrollToPage }), [scrollToPage]);
|
|
209
|
+
const hasAppliedInitialPosition = useRef(false);
|
|
210
|
+
const onLoadSuccess = useCallback((response) => {
|
|
211
|
+
hasAppliedInitialPosition.current = false;
|
|
212
|
+
setDocument(response);
|
|
213
|
+
}, []);
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
if (!hasAppliedInitialPosition.current && initialPosition !== void 0 && pageDimensions.length === numPages && numPages > 0) {
|
|
216
|
+
hasAppliedInitialPosition.current = true;
|
|
217
|
+
const id = requestAnimationFrame(() => scrollToPage(initialPosition));
|
|
218
|
+
return () => cancelAnimationFrame(id);
|
|
219
|
+
}
|
|
220
|
+
}, [initialPosition, pageDimensions, numPages, scrollToPage]);
|
|
221
|
+
const file = useMemo(() => {
|
|
222
|
+
return { url };
|
|
223
|
+
}, [url]);
|
|
224
|
+
const onPrevPage = useCallback(() => {
|
|
225
|
+
if (pageIndex > 0) {
|
|
226
|
+
const newPageIndex = pageIndex - 1;
|
|
227
|
+
listRef.current?.scrollToRow({
|
|
228
|
+
index: newPageIndex,
|
|
229
|
+
align: "start",
|
|
230
|
+
behavior: scrollBehavior
|
|
231
|
+
});
|
|
232
|
+
setPageIndex(newPageIndex);
|
|
233
|
+
}
|
|
234
|
+
}, [pageIndex, scrollBehavior]);
|
|
235
|
+
const onNextPage = useCallback(() => {
|
|
236
|
+
if (pageIndex < numPages - 1) {
|
|
237
|
+
const newPageIndex = pageIndex + 1;
|
|
238
|
+
listRef.current?.scrollToRow({
|
|
239
|
+
index: newPageIndex,
|
|
240
|
+
align: "start",
|
|
241
|
+
behavior: scrollBehavior
|
|
242
|
+
});
|
|
243
|
+
setPageIndex(newPageIndex);
|
|
244
|
+
}
|
|
245
|
+
}, [pageIndex, numPages, scrollBehavior]);
|
|
246
|
+
const onChangeZoom = useCallback((value) => {
|
|
247
|
+
if (value === "automatic") {
|
|
248
|
+
setZoomConfig({ mode: "automatic", value: 1 });
|
|
249
|
+
} else {
|
|
250
|
+
setZoomConfig({ mode: "manual", value: Number.parseFloat(value) });
|
|
251
|
+
}
|
|
252
|
+
}, []);
|
|
253
|
+
const getItemSize = useCallback(
|
|
254
|
+
(index) => {
|
|
255
|
+
if (pageDimensions[index]) {
|
|
256
|
+
return pageDimensions[index].height + PAGE_MARGIN;
|
|
257
|
+
}
|
|
258
|
+
return 0;
|
|
259
|
+
},
|
|
260
|
+
[pageDimensions]
|
|
261
|
+
);
|
|
262
|
+
const onRowsRendered = useCallback(
|
|
263
|
+
(visibleRows) => {
|
|
264
|
+
const prev = scrollPageRef.current;
|
|
265
|
+
scrollPageRef.current = visibleRows.startIndex;
|
|
266
|
+
if (visibleRows.startIndex !== prev) {
|
|
267
|
+
setPageIndex(visibleRows.startIndex);
|
|
268
|
+
onPositionChange?.(visibleRows.startIndex);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
[onPositionChange]
|
|
272
|
+
);
|
|
273
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
274
|
+
file ? /* @__PURE__ */ jsx5(
|
|
275
|
+
Document,
|
|
276
|
+
{
|
|
277
|
+
file,
|
|
278
|
+
onLoadSuccess,
|
|
279
|
+
error: PDFErrorPage,
|
|
280
|
+
loading: PDFLoadingPage,
|
|
281
|
+
children: pageDimensions.length > 0 && pageDimensions.length === numPages && /* @__PURE__ */ jsx5(
|
|
282
|
+
List,
|
|
283
|
+
{
|
|
284
|
+
listRef,
|
|
285
|
+
rowCount: numPages,
|
|
286
|
+
rowHeight: getItemSize,
|
|
287
|
+
onRowsRendered,
|
|
288
|
+
rowProps: { scale: zoomConfig.value },
|
|
289
|
+
rowComponent: PDFPageRenderer
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
) : /* @__PURE__ */ jsx5(PDFLoadingPage, {}),
|
|
294
|
+
/* @__PURE__ */ jsx5(
|
|
295
|
+
PDFViewerControls,
|
|
296
|
+
{
|
|
297
|
+
pageIndex,
|
|
298
|
+
numPages,
|
|
299
|
+
onPrevPage,
|
|
300
|
+
onNextPage,
|
|
301
|
+
zoomConfig,
|
|
302
|
+
onChangeZoom
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
] });
|
|
306
|
+
});
|
|
307
|
+
var PdfRenderer = {
|
|
308
|
+
type: "pdf",
|
|
309
|
+
extensions: ["pdf"],
|
|
310
|
+
Component: PdfViewerComponent
|
|
311
|
+
};
|
|
312
|
+
export {
|
|
313
|
+
PdfRenderer
|
|
314
|
+
};
|
|
315
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pdf/pdf-viewer.tsx","../../src/pdf/controls.tsx","../../src/pdf/error-page.tsx","../../src/pdf/loading-page.tsx","../../src/pdf/renderer.tsx","../../src/pdf/constants.ts","../../src/pdf/utils.ts"],"sourcesContent":["import type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport {\n\tforwardRef,\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 type {\n\tFileRenderer,\n\tHyperJumpAPI,\n\tRendererProps,\n\tZoomConfig,\n} from \"../lib/types\";\nimport PDFViewerControls from \"./controls\";\nimport PDFErrorPage from \"./error-page\";\nimport PDFLoadingPage from \"./loading-page\";\nimport \"./pdf-viewer.css\";\nimport PDFPageRenderer from \"./renderer\";\nimport { getPageDimensions } from \"./utils\";\n\npdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;\n\nconst PAGE_MARGIN = 12;\n\nexport type HyperJumpPdfViewerAPI = HyperJumpAPI;\n\nexport type ScrollBehavior = \"auto\" | \"instant\" | \"smooth\";\n\nexport interface HyperJumpPdfViewerProps extends RendererProps {\n\t/** Scroll behavior when navigating between pages (default: \"instant\") */\n\tscrollBehavior?: ScrollBehavior;\n}\n\nconst PdfViewerComponent = forwardRef<\n\tHyperJumpPdfViewerAPI,\n\tHyperJumpPdfViewerProps\n>(function PdfViewerComponent(props, ref) {\n\tconst {\n\t\turl,\n\t\tinitialPosition,\n\t\tonPositionChange,\n\t\tscrollBehavior = \"instant\",\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 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, () => ({ jump: scrollToPage }), [scrollToPage]);\n\n\tconst hasAppliedInitialPosition = useRef(false);\n\n\tconst onLoadSuccess: OnDocumentLoadSuccess = useCallback((response) => {\n\t\thasAppliedInitialPosition.current = false;\n\t\tsetDocument(response);\n\t}, []);\n\n\t// Scroll to initialPosition 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!hasAppliedInitialPosition.current &&\n\t\t\tinitialPosition !== undefined &&\n\t\t\tpageDimensions.length === numPages &&\n\t\t\tnumPages > 0\n\t\t) {\n\t\t\thasAppliedInitialPosition.current = true;\n\t\t\tconst id = requestAnimationFrame(() => scrollToPage(initialPosition));\n\t\t\treturn () => cancelAnimationFrame(id);\n\t\t}\n\t}, [initialPosition, 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\tonPositionChange?.(visibleRows.startIndex);\n\t\t\t}\n\t\t},\n\t\t[onPositionChange],\n\t);\n\n\treturn (\n\t\t<>\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</>\n\t);\n});\n\n/** Renderer descriptor for PDF files. Pass this to HyperJumpViewer's `renderers` prop. */\nexport const PdfRenderer: FileRenderer = {\n\ttype: \"pdf\",\n\textensions: [\"pdf\"],\n\tComponent: PdfViewerComponent as React.ComponentType<\n\t\tRendererProps & Record<string, unknown>\n\t>,\n};\n","import \"./controls.css\";\nimport type { ZoomConfig } from \"../lib/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","import \"./error-page.css\";\n\nexport default function PDFErrorPage() {\n\treturn <div className=\"hj-error\">Error loading file</div>;\n}\n","import \"./loading-page.css\";\n\nexport 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\";\nimport \"./renderer.css\";\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","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":["jsx","jsx","jsx","jsx","jsxs","PdfViewerComponent"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type ZoomMode = "automatic" | "page-width" | "manual";
|
|
2
|
+
interface ZoomConfig {
|
|
3
|
+
mode: ZoomMode;
|
|
4
|
+
value: number;
|
|
5
|
+
}
|
|
6
|
+
/** Props that the core viewer passes to every renderer. */
|
|
7
|
+
interface RendererProps {
|
|
8
|
+
url: string;
|
|
9
|
+
containerWidth: number;
|
|
10
|
+
containerHeight: number;
|
|
11
|
+
/** Initial position to show when the content first loads. Meaning depends on renderer (page index for PDF, seconds for video). */
|
|
12
|
+
initialPosition?: number;
|
|
13
|
+
/** Called when the current position changes. Meaning depends on renderer (page index for PDF, seconds for video). */
|
|
14
|
+
onPositionChange?: (position: number) => void;
|
|
15
|
+
}
|
|
16
|
+
/** Imperative API shared by all renderers. Exposed via ref on HyperJumpViewer. */
|
|
17
|
+
interface HyperJumpAPI {
|
|
18
|
+
/** Jump to a position. Meaning depends on renderer (page index for PDF, seconds for video). */
|
|
19
|
+
jump: (position: number) => void;
|
|
20
|
+
}
|
|
21
|
+
/** A renderer descriptor that tells the core viewer how to handle a file type. */
|
|
22
|
+
interface FileRenderer {
|
|
23
|
+
/** Unique identifier for this renderer type (e.g. "pdf", "video"). */
|
|
24
|
+
type: string;
|
|
25
|
+
/** File extensions this renderer handles, without dots (e.g. ["pdf"]). */
|
|
26
|
+
extensions: string[];
|
|
27
|
+
/** The component that renders the file. */
|
|
28
|
+
Component: React.ComponentType<RendererProps & Record<string, unknown>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type { FileRenderer as F, HyperJumpAPI as H, RendererProps as R, ZoomConfig as Z };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* src/video/video-viewer.css */
|
|
2
|
+
.hj-video {
|
|
3
|
+
position: relative;
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
height: 100%;
|
|
8
|
+
width: 100%;
|
|
9
|
+
padding: 16px;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
}
|
|
13
|
+
.hj-video video {
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
max-height: 100%;
|
|
16
|
+
object-fit: contain;
|
|
17
|
+
}
|
|
18
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/video/video-viewer.css"],"sourcesContent":[".hj-video {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\theight: 100%;\n\twidth: 100%;\n\tpadding: 16px;\n\tbox-sizing: border-box;\n\toverflow: hidden;\n}\n\n.hj-video video {\n\tmax-width: 100%;\n\tmax-height: 100%;\n\tobject-fit: contain;\n}\n"],"mappings":";AAAA,CAAC;AACA,YAAU;AACV,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,UAAQ;AACR,SAAO;AACP,WAAS;AACT,cAAY;AACZ,YAAU;AACX;AAEA,CAZC,SAYS;AACT,aAAW;AACX,cAAY;AACZ,cAAY;AACb;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { H as HyperJumpAPI, R as RendererProps, F as FileRenderer } from '../types-Ce3M8ej7.js';
|
|
2
|
+
|
|
3
|
+
type HyperJumpVideoViewerAPI = HyperJumpAPI;
|
|
4
|
+
interface HyperJumpVideoViewerProps extends RendererProps {
|
|
5
|
+
/** Whether the video should autoplay (default: false). */
|
|
6
|
+
autoPlay?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Renderer descriptor for video files. Pass this to HyperJumpViewer's `renderers` prop. */
|
|
9
|
+
declare const VideoRenderer: FileRenderer;
|
|
10
|
+
|
|
11
|
+
export { type HyperJumpVideoViewerAPI, type HyperJumpVideoViewerProps, VideoRenderer };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/video/video-viewer.tsx
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
var VideoViewerComponent = forwardRef(function VideoViewerComponent2(props, ref) {
|
|
5
|
+
const { url, autoPlay = false, initialPosition, onPositionChange } = props;
|
|
6
|
+
const videoRef = useRef(null);
|
|
7
|
+
const hasAppliedInitialPosition = useRef(false);
|
|
8
|
+
useImperativeHandle(
|
|
9
|
+
ref,
|
|
10
|
+
() => ({
|
|
11
|
+
jump: (position) => {
|
|
12
|
+
if (videoRef.current) {
|
|
13
|
+
videoRef.current.currentTime = position;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}),
|
|
17
|
+
[]
|
|
18
|
+
);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!hasAppliedInitialPosition.current && initialPosition !== void 0 && videoRef.current) {
|
|
21
|
+
hasAppliedInitialPosition.current = true;
|
|
22
|
+
videoRef.current.currentTime = initialPosition;
|
|
23
|
+
}
|
|
24
|
+
}, [initialPosition]);
|
|
25
|
+
return /* @__PURE__ */ jsx("div", { className: "hj-video", children: /* @__PURE__ */ jsx(
|
|
26
|
+
"video",
|
|
27
|
+
{
|
|
28
|
+
ref: videoRef,
|
|
29
|
+
src: url,
|
|
30
|
+
autoPlay,
|
|
31
|
+
controls: true,
|
|
32
|
+
onTimeUpdate: () => {
|
|
33
|
+
if (videoRef.current) {
|
|
34
|
+
onPositionChange?.(videoRef.current.currentTime);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
) });
|
|
39
|
+
});
|
|
40
|
+
var VideoRenderer = {
|
|
41
|
+
type: "video",
|
|
42
|
+
extensions: ["mp4", "mov", "webm", "ogg"],
|
|
43
|
+
Component: VideoViewerComponent
|
|
44
|
+
};
|
|
45
|
+
export {
|
|
46
|
+
VideoRenderer
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/video/video-viewer.tsx"],"sourcesContent":["import { forwardRef, useEffect, useImperativeHandle, useRef } from \"react\";\nimport type { FileRenderer, HyperJumpAPI, RendererProps } from \"../lib/types\";\nimport \"./video-viewer.css\";\n\nexport type HyperJumpVideoViewerAPI = HyperJumpAPI;\n\nexport interface HyperJumpVideoViewerProps extends RendererProps {\n\t/** Whether the video should autoplay (default: false). */\n\tautoPlay?: boolean;\n}\n\nconst VideoViewerComponent = forwardRef<\n\tHyperJumpVideoViewerAPI,\n\tHyperJumpVideoViewerProps\n>(function VideoViewerComponent(props, ref) {\n\tconst { url, autoPlay = false, initialPosition, onPositionChange } = props;\n\tconst videoRef = useRef<HTMLVideoElement>(null);\n\tconst hasAppliedInitialPosition = useRef(false);\n\n\tuseImperativeHandle(\n\t\tref,\n\t\t() => ({\n\t\t\tjump: (position: number) => {\n\t\t\t\tif (videoRef.current) {\n\t\t\t\t\tvideoRef.current.currentTime = position;\n\t\t\t\t}\n\t\t\t},\n\t\t}),\n\t\t[],\n\t);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!hasAppliedInitialPosition.current &&\n\t\t\tinitialPosition !== undefined &&\n\t\t\tvideoRef.current\n\t\t) {\n\t\t\thasAppliedInitialPosition.current = true;\n\t\t\tvideoRef.current.currentTime = initialPosition;\n\t\t}\n\t}, [initialPosition]);\n\n\treturn (\n\t\t<div className=\"hj-video\">\n\t\t\t{/* biome-ignore lint/a11y/useMediaCaption: captions are the consumer's responsibility */}\n\t\t\t<video\n\t\t\t\tref={videoRef}\n\t\t\t\tsrc={url}\n\t\t\t\tautoPlay={autoPlay}\n\t\t\t\tcontrols\n\t\t\t\tonTimeUpdate={() => {\n\t\t\t\t\tif (videoRef.current) {\n\t\t\t\t\t\tonPositionChange?.(videoRef.current.currentTime);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t);\n});\n\n/** Renderer descriptor for video files. Pass this to HyperJumpViewer's `renderers` prop. */\nexport const VideoRenderer: FileRenderer = {\n\ttype: \"video\",\n\textensions: [\"mp4\", \"mov\", \"webm\", \"ogg\"],\n\tComponent: VideoViewerComponent as React.ComponentType<\n\t\tRendererProps & Record<string, unknown>\n\t>,\n};\n"],"mappings":";AAAA,SAAS,YAAY,WAAW,qBAAqB,cAAc;AA6ChE;AAlCH,IAAM,uBAAuB,WAG3B,SAASA,sBAAqB,OAAO,KAAK;AAC3C,QAAM,EAAE,KAAK,WAAW,OAAO,iBAAiB,iBAAiB,IAAI;AACrE,QAAM,WAAW,OAAyB,IAAI;AAC9C,QAAM,4BAA4B,OAAO,KAAK;AAE9C;AAAA,IACC;AAAA,IACA,OAAO;AAAA,MACN,MAAM,CAAC,aAAqB;AAC3B,YAAI,SAAS,SAAS;AACrB,mBAAS,QAAQ,cAAc;AAAA,QAChC;AAAA,MACD;AAAA,IACD;AAAA,IACA,CAAC;AAAA,EACF;AAEA,YAAU,MAAM;AACf,QACC,CAAC,0BAA0B,WAC3B,oBAAoB,UACpB,SAAS,SACR;AACD,gCAA0B,UAAU;AACpC,eAAS,QAAQ,cAAc;AAAA,IAChC;AAAA,EACD,GAAG,CAAC,eAAe,CAAC;AAEpB,SACC,oBAAC,SAAI,WAAU,YAEd;AAAA,IAAC;AAAA;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL;AAAA,MACA,UAAQ;AAAA,MACR,cAAc,MAAM;AACnB,YAAI,SAAS,SAAS;AACrB,6BAAmB,SAAS,QAAQ,WAAW;AAAA,QAChD;AAAA,MACD;AAAA;AAAA,EACD,GACD;AAEF,CAAC;AAGM,IAAM,gBAA8B;AAAA,EAC1C,MAAM;AAAA,EACN,YAAY,CAAC,OAAO,OAAO,QAAQ,KAAK;AAAA,EACxC,WAAW;AAGZ;","names":["VideoViewerComponent"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypercard-ai/hyper-jump",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Document viewer built for RAG",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "HyperCard AI",
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"pdf",
|
|
17
|
+
"video",
|
|
17
18
|
"viewer",
|
|
18
19
|
"react",
|
|
19
20
|
"rag",
|
|
20
|
-
"virtualized"
|
|
21
|
-
"react-pdf"
|
|
21
|
+
"virtualized"
|
|
22
22
|
],
|
|
23
23
|
"type": "module",
|
|
24
24
|
"module": "./dist/index.js",
|
|
@@ -28,7 +28,17 @@
|
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"default": "./dist/index.js"
|
|
30
30
|
},
|
|
31
|
-
"./
|
|
31
|
+
"./pdf": {
|
|
32
|
+
"types": "./dist/pdf/index.d.ts",
|
|
33
|
+
"default": "./dist/pdf/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./styles.css": "./dist/index.css",
|
|
36
|
+
"./pdf/styles.css": "./dist/pdf/index.css",
|
|
37
|
+
"./video": {
|
|
38
|
+
"types": "./dist/video/index.d.ts",
|
|
39
|
+
"default": "./dist/video/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./video/styles.css": "./dist/video/index.css"
|
|
32
42
|
},
|
|
33
43
|
"files": [
|
|
34
44
|
"dist",
|
|
@@ -56,7 +66,16 @@
|
|
|
56
66
|
"peerDependencies": {
|
|
57
67
|
"react": ">=19.0.0",
|
|
58
68
|
"react-dom": ">=19.0.0",
|
|
59
|
-
"react-pdf": ">=10.0.0"
|
|
69
|
+
"react-pdf": ">=10.0.0",
|
|
70
|
+
"react-window": ">=2.0.0"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"react-pdf": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"react-window": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
60
79
|
},
|
|
61
80
|
"devDependencies": {
|
|
62
81
|
"@biomejs/biome": "2.3.15",
|
|
@@ -70,12 +89,10 @@
|
|
|
70
89
|
"react": "^19.2.4",
|
|
71
90
|
"react-dom": "^19.2.4",
|
|
72
91
|
"react-pdf": "10.3.0",
|
|
92
|
+
"react-window": "^2.2.7",
|
|
73
93
|
"storybook": "^10.2.8",
|
|
74
94
|
"tsup": "^8.5.1",
|
|
75
95
|
"typescript": "^5.9.3",
|
|
76
96
|
"vitest": "^4.0.18"
|
|
77
|
-
},
|
|
78
|
-
"dependencies": {
|
|
79
|
-
"react-window": "^2.2.7"
|
|
80
97
|
}
|
|
81
98
|
}
|