@hypercard-ai/hyper-jump 0.3.1 → 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 +136 -32
- package/dist/index.css +1 -136
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +18 -20
- package/dist/index.js +43 -291
- 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
package/README.md
CHANGED
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
[](https://github.com/hypercard-ai/hyper-jump/actions/workflows/ci.yml)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
A React
|
|
7
|
+
A pluggable React document viewer built for RAG (Retrieval-Augmented Generation). Originally developed as part of [HyperCard.AI](https://hypercard.ai)'s chatbot platform, hyper-jump provides fast navigation that pairs with RAG citations to deliver an excellent document viewing experience.
|
|
8
|
+
|
|
9
|
+
Renderers are opt-in — only bundle what you use.
|
|
8
10
|
|
|
9
11
|
## Features
|
|
10
12
|
|
|
13
|
+
- **Pluggable renderers** — import only the file types you need (PDF, video, and more coming soon)
|
|
11
14
|
- Virtualized rendering via `react-window` for smooth viewing of large PDFs
|
|
12
|
-
-
|
|
15
|
+
- Unified `jump()` API for instant navigation to cited content across all file types
|
|
13
16
|
- Zoom controls with preset levels and automatic fit-to-width
|
|
14
17
|
- Responsive layout that adapts to container size
|
|
15
18
|
- Lightweight, self-contained CSS with no external styling dependencies
|
|
@@ -17,46 +20,65 @@ A React PDF viewer built for RAG (Retrieval-Augmented Generation). Originally de
|
|
|
17
20
|
## Installation
|
|
18
21
|
|
|
19
22
|
```bash
|
|
20
|
-
|
|
23
|
+
# Core + PDF renderer
|
|
24
|
+
npm install @hypercard-ai/hyper-jump react-pdf react-window
|
|
25
|
+
|
|
26
|
+
# Core + Video renderer (no extra dependencies needed)
|
|
27
|
+
npm install @hypercard-ai/hyper-jump
|
|
21
28
|
```
|
|
22
29
|
|
|
30
|
+
Renderer dependencies are optional peer dependencies — only install those needed by the renderers you use.
|
|
31
|
+
|
|
23
32
|
## Usage
|
|
24
33
|
|
|
25
34
|
```tsx
|
|
26
35
|
import { HyperJumpViewer } from "@hypercard-ai/hyper-jump";
|
|
27
|
-
import "@hypercard-ai/hyper-jump/
|
|
36
|
+
import { PdfRenderer } from "@hypercard-ai/hyper-jump/pdf";
|
|
28
37
|
|
|
29
38
|
function App() {
|
|
30
|
-
return
|
|
39
|
+
return (
|
|
40
|
+
<HyperJumpViewer
|
|
41
|
+
url="/path/to/document.pdf"
|
|
42
|
+
renderers={[PdfRenderer]}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
31
45
|
}
|
|
32
46
|
```
|
|
33
47
|
|
|
34
|
-
### Open
|
|
48
|
+
### Open at a specific position
|
|
35
49
|
|
|
36
|
-
Pass
|
|
50
|
+
Pass `initialPosition` to start at a specific location when the file loads. The meaning depends on the renderer — page index for PDF, seconds for video:
|
|
37
51
|
|
|
38
52
|
```tsx
|
|
39
|
-
|
|
53
|
+
// PDF: open at page 4 (0-indexed)
|
|
54
|
+
<HyperJumpViewer url="/doc.pdf" renderers={[PdfRenderer]} initialPosition={3} />
|
|
55
|
+
|
|
56
|
+
// Video: start at 30 seconds
|
|
57
|
+
<HyperJumpViewer url="/clip.mp4" renderers={[VideoRenderer]} initialPosition={30} />
|
|
40
58
|
```
|
|
41
59
|
|
|
42
|
-
### Jump to a
|
|
60
|
+
### Jump to a position imperatively
|
|
43
61
|
|
|
44
|
-
Use a ref to jump to any
|
|
62
|
+
Use a ref to jump to any position at any time — ideal for navigating to RAG citations. The `jump()` method works the same across all renderers:
|
|
45
63
|
|
|
46
64
|
```tsx
|
|
47
65
|
import { useRef } from "react";
|
|
48
|
-
import { HyperJumpViewer, type
|
|
49
|
-
import "@hypercard-ai/hyper-jump/
|
|
66
|
+
import { HyperJumpViewer, type HyperJumpAPI } from "@hypercard-ai/hyper-jump";
|
|
67
|
+
import { PdfRenderer } from "@hypercard-ai/hyper-jump/pdf";
|
|
50
68
|
|
|
51
69
|
function App() {
|
|
52
|
-
const viewerRef = useRef<
|
|
70
|
+
const viewerRef = useRef<HyperJumpAPI>(null);
|
|
53
71
|
|
|
54
72
|
return (
|
|
55
73
|
<>
|
|
56
|
-
<button onClick={() => viewerRef.current?.
|
|
74
|
+
<button onClick={() => viewerRef.current?.jump(5)}>
|
|
57
75
|
Go to page 6
|
|
58
76
|
</button>
|
|
59
|
-
<HyperJumpViewer
|
|
77
|
+
<HyperJumpViewer
|
|
78
|
+
url="/path/to/document.pdf"
|
|
79
|
+
renderers={[PdfRenderer]}
|
|
80
|
+
ref={viewerRef}
|
|
81
|
+
/>
|
|
60
82
|
</>
|
|
61
83
|
);
|
|
62
84
|
}
|
|
@@ -66,32 +88,114 @@ function App() {
|
|
|
66
88
|
|
|
67
89
|
### `<HyperJumpViewer />`
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
91
|
+
The core component. It detects the file type from the URL extension (or an explicit `type` prop), picks the first matching renderer, and forwards all remaining props to it.
|
|
92
|
+
|
|
93
|
+
| Prop | Type | Required | Description |
|
|
94
|
+
| --- | --- | --- | --- |
|
|
95
|
+
| `url` | `string` | Yes | URL or path to the file |
|
|
96
|
+
| `renderers` | `FileRenderer[]` | Yes | Renderers the viewer can use (first match wins) |
|
|
97
|
+
| `type` | `string` | No | Explicit file type override (e.g. `"pdf"`). If omitted, detected from URL extension |
|
|
98
|
+
| `ref` | `Ref<HyperJumpAPI>` | No | Forwarded to the active renderer for the `jump()` API |
|
|
99
|
+
| `initialPosition` | `number` | No | Position to start at when the file loads (page index for PDF, seconds for video) |
|
|
100
|
+
| `onPositionChange` | `(position: number) => void` | No | Called when the current position changes |
|
|
101
|
+
| `rendererProps` | `T` | No | Additional props forwarded to the matched renderer (generic, typed per renderer) |
|
|
102
|
+
|
|
103
|
+
### `HyperJumpAPI` (exposed via ref)
|
|
104
|
+
|
|
105
|
+
All renderers expose the same imperative API:
|
|
106
|
+
|
|
107
|
+
| Method | Description |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `jump(position: number)` | Jump to a position. Page index for PDF (0-indexed, clamped), seconds for video. |
|
|
75
110
|
|
|
76
|
-
###
|
|
111
|
+
### PDF Renderer
|
|
77
112
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
Imported from `@hypercard-ai/hyper-jump/pdf`. Requires `react-pdf` and `react-window` as peer dependencies.
|
|
114
|
+
|
|
115
|
+
#### PDF-specific props
|
|
116
|
+
|
|
117
|
+
| Prop | Type | Required | Description |
|
|
118
|
+
| --- | --- | --- | --- |
|
|
119
|
+
| `scrollBehavior` | `"auto" \| "instant" \| "smooth"` | No | Scroll behavior when navigating between pages (default: `"instant"`) |
|
|
120
|
+
|
|
121
|
+
### Video Renderer
|
|
122
|
+
|
|
123
|
+
Imported from `@hypercard-ai/hyper-jump/video`. Uses the native HTML `<video>` element — no extra dependencies required.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { HyperJumpViewer } from "@hypercard-ai/hyper-jump";
|
|
127
|
+
import { VideoRenderer } from "@hypercard-ai/hyper-jump/video";
|
|
128
|
+
import "@hypercard-ai/hyper-jump/video/styles.css";
|
|
129
|
+
|
|
130
|
+
function App() {
|
|
131
|
+
return (
|
|
132
|
+
<HyperJumpViewer
|
|
133
|
+
url="/path/to/video.mp4"
|
|
134
|
+
renderers={[VideoRenderer]}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Video-specific props
|
|
141
|
+
|
|
142
|
+
| Prop | Type | Required | Description |
|
|
143
|
+
| --- | --- | --- | --- |
|
|
144
|
+
| `autoPlay` | `boolean` | No | Whether the video should autoplay (default: `false`) |
|
|
81
145
|
|
|
82
146
|
### Exports
|
|
83
147
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
148
|
+
#### `@hypercard-ai/hyper-jump` (core)
|
|
149
|
+
|
|
150
|
+
| Export | Type | Description |
|
|
151
|
+
| --- | --- | --- |
|
|
152
|
+
| `HyperJumpViewer` | Component | The core viewer component |
|
|
153
|
+
| `HyperJumpViewerProps` | Type | Props for the viewer component |
|
|
154
|
+
| `HyperJumpAPI` | Type | Shared imperative API (`jump`) exposed via ref |
|
|
155
|
+
| `FileRenderer` | Type | Renderer descriptor interface |
|
|
156
|
+
| `RendererProps` | Type | Base props every renderer receives |
|
|
157
|
+
| `ZoomConfig` | Type | Zoom configuration interface |
|
|
158
|
+
|
|
159
|
+
#### `@hypercard-ai/hyper-jump/pdf`
|
|
160
|
+
|
|
161
|
+
| Export | Type | Description |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| `PdfRenderer` | `FileRenderer` | Renderer descriptor for PDF files |
|
|
164
|
+
| `HyperJumpPdfViewerAPI` | Type | Alias for `HyperJumpAPI` |
|
|
165
|
+
| `HyperJumpPdfViewerProps` | Type | Full props for the PDF renderer |
|
|
166
|
+
| `ScrollBehavior` | Type | Scroll behavior union type |
|
|
167
|
+
|
|
168
|
+
#### `@hypercard-ai/hyper-jump/video`
|
|
169
|
+
|
|
170
|
+
| Export | Type | Description |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| `VideoRenderer` | `FileRenderer` | Renderer descriptor for video files |
|
|
173
|
+
| `HyperJumpVideoViewerAPI` | Type | Alias for `HyperJumpAPI` |
|
|
174
|
+
| `HyperJumpVideoViewerProps` | Type | Full props for the video renderer |
|
|
175
|
+
|
|
176
|
+
### Creating a custom renderer
|
|
177
|
+
|
|
178
|
+
Implement the `FileRenderer` interface to add support for any file type:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import type { FileRenderer, RendererProps } from "@hypercard-ai/hyper-jump";
|
|
182
|
+
|
|
183
|
+
const MyVideoRenderer: FileRenderer = {
|
|
184
|
+
type: "video",
|
|
185
|
+
extensions: ["mp4", "webm", "mov"],
|
|
186
|
+
Component: ({ url, containerWidth, containerHeight, ...props }) => (
|
|
187
|
+
<video src={url} width={containerWidth} height={containerHeight} controls />
|
|
188
|
+
),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
<HyperJumpViewer url="/clip.mp4" renderers={[PdfRenderer, MyVideoRenderer]} />
|
|
192
|
+
```
|
|
90
193
|
|
|
91
194
|
## Requirements
|
|
92
195
|
|
|
93
196
|
- React 19+
|
|
94
|
-
- react-pdf 10+
|
|
197
|
+
- react-pdf 10+ (when using `PdfRenderer`)
|
|
198
|
+
- react-window 2+ (when using `PdfRenderer`)
|
|
95
199
|
|
|
96
200
|
## Development
|
|
97
201
|
|
package/dist/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* src/
|
|
1
|
+
/* src/viewer/viewer.css */
|
|
2
2
|
.hj-viewer {
|
|
3
3
|
position: relative;
|
|
4
4
|
display: flex;
|
|
@@ -8,139 +8,4 @@
|
|
|
8
8
|
width: 100%;
|
|
9
9
|
overflow: hidden;
|
|
10
10
|
}
|
|
11
|
-
.hj-viewer .react-pdf__Document {
|
|
12
|
-
height: 100%;
|
|
13
|
-
width: 100%;
|
|
14
|
-
}
|
|
15
|
-
.hj-page {
|
|
16
|
-
display: flex;
|
|
17
|
-
align-items: center;
|
|
18
|
-
justify-content: center;
|
|
19
|
-
}
|
|
20
|
-
.hj-loading {
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
justify-content: center;
|
|
24
|
-
width: 100%;
|
|
25
|
-
height: 100%;
|
|
26
|
-
background: #fff;
|
|
27
|
-
}
|
|
28
|
-
.hj-spinner {
|
|
29
|
-
width: 32px;
|
|
30
|
-
height: 32px;
|
|
31
|
-
border: 3px solid #e9ecef;
|
|
32
|
-
border-top-color: #228be6;
|
|
33
|
-
border-radius: 50%;
|
|
34
|
-
animation: hj-spin 0.6s linear infinite;
|
|
35
|
-
}
|
|
36
|
-
@keyframes hj-spin {
|
|
37
|
-
to {
|
|
38
|
-
transform: rotate(360deg);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
.hj-error {
|
|
42
|
-
display: flex;
|
|
43
|
-
align-items: center;
|
|
44
|
-
justify-content: center;
|
|
45
|
-
width: 100%;
|
|
46
|
-
height: 100%;
|
|
47
|
-
background: #fff;
|
|
48
|
-
color: #495057;
|
|
49
|
-
font-size: 14px;
|
|
50
|
-
}
|
|
51
|
-
.hj-controls {
|
|
52
|
-
position: absolute;
|
|
53
|
-
bottom: 12px;
|
|
54
|
-
left: 12px;
|
|
55
|
-
right: 12px;
|
|
56
|
-
display: flex;
|
|
57
|
-
justify-content: center;
|
|
58
|
-
align-items: center;
|
|
59
|
-
z-index: 10;
|
|
60
|
-
}
|
|
61
|
-
.hj-controls-bar {
|
|
62
|
-
display: flex;
|
|
63
|
-
align-items: center;
|
|
64
|
-
gap: 12px;
|
|
65
|
-
padding: 6px 12px;
|
|
66
|
-
background: #fff;
|
|
67
|
-
border: 1px solid #dee2e6;
|
|
68
|
-
border-radius: 8px;
|
|
69
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
70
|
-
}
|
|
71
|
-
.hj-controls-group {
|
|
72
|
-
display: flex;
|
|
73
|
-
align-items: center;
|
|
74
|
-
gap: 6px;
|
|
75
|
-
}
|
|
76
|
-
.hj-divider {
|
|
77
|
-
width: 1px;
|
|
78
|
-
height: 24px;
|
|
79
|
-
background: #dee2e6;
|
|
80
|
-
}
|
|
81
|
-
.hj-btn {
|
|
82
|
-
display: inline-flex;
|
|
83
|
-
align-items: center;
|
|
84
|
-
justify-content: center;
|
|
85
|
-
padding: 4px 12px;
|
|
86
|
-
font-size: 13px;
|
|
87
|
-
font-family: inherit;
|
|
88
|
-
line-height: 1.4;
|
|
89
|
-
border: 1px solid #dee2e6;
|
|
90
|
-
border-radius: 4px;
|
|
91
|
-
background: #fff;
|
|
92
|
-
color: #212529;
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
user-select: none;
|
|
95
|
-
white-space: nowrap;
|
|
96
|
-
}
|
|
97
|
-
.hj-btn:hover:not(:disabled) {
|
|
98
|
-
background: #f8f9fa;
|
|
99
|
-
}
|
|
100
|
-
.hj-btn:disabled {
|
|
101
|
-
opacity: 0.5;
|
|
102
|
-
cursor: not-allowed;
|
|
103
|
-
}
|
|
104
|
-
.hj-icon-btn {
|
|
105
|
-
display: inline-flex;
|
|
106
|
-
align-items: center;
|
|
107
|
-
justify-content: center;
|
|
108
|
-
width: 28px;
|
|
109
|
-
height: 28px;
|
|
110
|
-
padding: 0;
|
|
111
|
-
border: 1px solid #dee2e6;
|
|
112
|
-
border-radius: 4px;
|
|
113
|
-
background: #fff;
|
|
114
|
-
color: #212529;
|
|
115
|
-
cursor: pointer;
|
|
116
|
-
}
|
|
117
|
-
.hj-icon-btn:hover:not(:disabled) {
|
|
118
|
-
background: #f8f9fa;
|
|
119
|
-
}
|
|
120
|
-
.hj-icon-btn:disabled {
|
|
121
|
-
opacity: 0.5;
|
|
122
|
-
cursor: not-allowed;
|
|
123
|
-
}
|
|
124
|
-
.hj-page-indicator {
|
|
125
|
-
font-size: 13px;
|
|
126
|
-
min-width: 50px;
|
|
127
|
-
text-align: center;
|
|
128
|
-
color: #212529;
|
|
129
|
-
user-select: none;
|
|
130
|
-
}
|
|
131
|
-
.hj-select {
|
|
132
|
-
font-size: 13px;
|
|
133
|
-
font-family: inherit;
|
|
134
|
-
padding: 4px 8px;
|
|
135
|
-
border: 1px solid #dee2e6;
|
|
136
|
-
border-radius: 4px;
|
|
137
|
-
background: #fff;
|
|
138
|
-
color: #212529;
|
|
139
|
-
cursor: pointer;
|
|
140
|
-
min-width: 120px;
|
|
141
|
-
}
|
|
142
|
-
.hj-select:focus {
|
|
143
|
-
outline: 2px solid #228be6;
|
|
144
|
-
outline-offset: -1px;
|
|
145
|
-
}
|
|
146
11
|
/*# sourceMappingURL=index.css.map */
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/viewer/viewer.css"],"sourcesContent":["/* HyperJump Viewer */\n\n.hj-viewer {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\theight: 100%;\n\twidth: 100%;\n\toverflow: hidden;\n}\n"],"mappings":";AAEA,CAAC;AACA,YAAU;AACV,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,UAAQ;AACR,SAAO;AACP,YAAU;AACX;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
+
import { F as FileRenderer, H as HyperJumpAPI } from './types-Ce3M8ej7.js';
|
|
2
|
+
export { R as RendererProps, Z as ZoomConfig } from './types-Ce3M8ej7.js';
|
|
1
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
mode: ZoomMode;
|
|
6
|
-
value: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface HyperJumpViewerAPI {
|
|
10
|
-
/** Imperatively scroll to a page (0-indexed). Clamps to valid range. */
|
|
11
|
-
jumpToPage: (page: number) => void;
|
|
12
|
-
}
|
|
13
|
-
interface HyperJumpViewerProps {
|
|
14
|
-
/** URL of the PDF file to display */
|
|
5
|
+
interface HyperJumpViewerProps<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
6
|
+
/** URL of the file to display */
|
|
15
7
|
url: string;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
/** Ref
|
|
21
|
-
ref?: React.Ref<
|
|
8
|
+
/** Explicit file type override (e.g. "pdf", "video"). If omitted, detected from URL extension. */
|
|
9
|
+
type?: string;
|
|
10
|
+
/** Renderers that this viewer can use. The first matching renderer wins. */
|
|
11
|
+
renderers: FileRenderer[];
|
|
12
|
+
/** Ref forwarded to the active renderer (e.g. for imperative APIs like jump). */
|
|
13
|
+
ref?: React.Ref<HyperJumpAPI>;
|
|
14
|
+
/** Initial position to show when the content first loads. Meaning depends on renderer (page index for PDF, seconds for video). */
|
|
15
|
+
initialPosition?: number;
|
|
16
|
+
/** Called when the current position changes. Meaning depends on renderer (page index for PDF, seconds for video). */
|
|
17
|
+
onPositionChange?: (position: number) => void;
|
|
18
|
+
/** Additional props forwarded to the matched renderer. */
|
|
19
|
+
rendererProps?: T;
|
|
22
20
|
}
|
|
23
|
-
declare function HyperJumpViewer(props: HyperJumpViewerProps): react_jsx_runtime.JSX.Element;
|
|
21
|
+
declare function HyperJumpViewer<T extends Record<string, unknown> = Record<string, unknown>>(props: HyperJumpViewerProps<T>): react_jsx_runtime.JSX.Element;
|
|
24
22
|
|
|
25
|
-
export {
|
|
23
|
+
export { FileRenderer, HyperJumpAPI, HyperJumpViewer, type HyperJumpViewerProps };
|