@embedpdf/plugin-thumbnail 1.5.0 → 2.0.0-next.0
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.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +316 -84
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +43 -0
- package/dist/lib/index.d.ts +5 -2
- package/dist/lib/reducer.d.ts +6 -0
- package/dist/lib/thumbnail-plugin.d.ts +19 -17
- package/dist/lib/types.d.ts +38 -2
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +39 -18
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +39 -18
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/thumbnail-img.d.ts +5 -1
- package/dist/shared/components/thumbnails-pane.d.ts +6 -7
- package/dist/shared-preact/components/thumbnail-img.d.ts +5 -1
- package/dist/shared-preact/components/thumbnails-pane.d.ts +6 -7
- package/dist/shared-react/components/thumbnail-img.d.ts +5 -1
- package/dist/shared-react/components/thumbnails-pane.d.ts +6 -7
- package/dist/svelte/components/ThumbImg.svelte.d.ts +4 -0
- package/dist/svelte/components/ThumbnailsPane.svelte.d.ts +4 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +44 -25
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/components/thumbnail-img.vue.d.ts +8 -3
- package/dist/vue/components/thumbnails-pane.vue.d.ts +9 -2
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +135 -79
- package/dist/vue/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BasePlugin,
|
|
1
|
+
import { BasePlugin, createScopedEmitter, REFRESH_PAGES } from "@embedpdf/core";
|
|
2
2
|
import { ignore } from "@embedpdf/models";
|
|
3
3
|
const THUMBNAIL_PLUGIN_ID = "thumbnail";
|
|
4
4
|
const manifest = {
|
|
@@ -20,62 +20,236 @@ const manifest = {
|
|
|
20
20
|
paddingY: 0
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
+
const INIT_THUMBNAIL_STATE = "THUMBNAIL/INIT_STATE";
|
|
24
|
+
const CLEANUP_THUMBNAIL_STATE = "THUMBNAIL/CLEANUP_STATE";
|
|
25
|
+
const SET_ACTIVE_DOCUMENT = "THUMBNAIL/SET_ACTIVE_DOCUMENT";
|
|
26
|
+
const SET_WINDOW_STATE = "THUMBNAIL/SET_WINDOW_STATE";
|
|
27
|
+
const UPDATE_VIEWPORT_METRICS = "THUMBNAIL/UPDATE_VIEWPORT_METRICS";
|
|
28
|
+
function initThumbnailState(documentId, state) {
|
|
29
|
+
return { type: INIT_THUMBNAIL_STATE, payload: { documentId, state } };
|
|
30
|
+
}
|
|
31
|
+
function cleanupThumbnailState(documentId) {
|
|
32
|
+
return { type: CLEANUP_THUMBNAIL_STATE, payload: documentId };
|
|
33
|
+
}
|
|
34
|
+
function setActiveDocument(documentId) {
|
|
35
|
+
return { type: SET_ACTIVE_DOCUMENT, payload: documentId };
|
|
36
|
+
}
|
|
37
|
+
function setWindowState(documentId, window) {
|
|
38
|
+
return { type: SET_WINDOW_STATE, payload: { documentId, window } };
|
|
39
|
+
}
|
|
40
|
+
function updateViewportMetrics(documentId, scrollY, viewportH) {
|
|
41
|
+
return { type: UPDATE_VIEWPORT_METRICS, payload: { documentId, scrollY, viewportH } };
|
|
42
|
+
}
|
|
43
|
+
const initialDocumentState = {
|
|
44
|
+
thumbs: [],
|
|
45
|
+
window: null,
|
|
46
|
+
viewportH: 0,
|
|
47
|
+
scrollY: 0
|
|
48
|
+
};
|
|
49
|
+
const initialState = {
|
|
50
|
+
documents: {},
|
|
51
|
+
activeDocumentId: null
|
|
52
|
+
};
|
|
53
|
+
const thumbnailReducer = (state = initialState, action) => {
|
|
54
|
+
switch (action.type) {
|
|
55
|
+
case INIT_THUMBNAIL_STATE: {
|
|
56
|
+
const { documentId, state: docState } = action.payload;
|
|
57
|
+
return {
|
|
58
|
+
...state,
|
|
59
|
+
documents: {
|
|
60
|
+
...state.documents,
|
|
61
|
+
[documentId]: docState
|
|
62
|
+
},
|
|
63
|
+
// Set as active if no active document
|
|
64
|
+
activeDocumentId: state.activeDocumentId ?? documentId
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
case CLEANUP_THUMBNAIL_STATE: {
|
|
68
|
+
const documentId = action.payload;
|
|
69
|
+
const { [documentId]: removed, ...remainingDocs } = state.documents;
|
|
70
|
+
return {
|
|
71
|
+
...state,
|
|
72
|
+
documents: remainingDocs,
|
|
73
|
+
activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
case SET_ACTIVE_DOCUMENT: {
|
|
77
|
+
return {
|
|
78
|
+
...state,
|
|
79
|
+
activeDocumentId: action.payload
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
case SET_WINDOW_STATE: {
|
|
83
|
+
const { documentId, window } = action.payload;
|
|
84
|
+
const docState = state.documents[documentId];
|
|
85
|
+
if (!docState) return state;
|
|
86
|
+
return {
|
|
87
|
+
...state,
|
|
88
|
+
documents: {
|
|
89
|
+
...state.documents,
|
|
90
|
+
[documentId]: {
|
|
91
|
+
...docState,
|
|
92
|
+
window
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
case UPDATE_VIEWPORT_METRICS: {
|
|
98
|
+
const { documentId, scrollY, viewportH } = action.payload;
|
|
99
|
+
const docState = state.documents[documentId];
|
|
100
|
+
if (!docState) return state;
|
|
101
|
+
return {
|
|
102
|
+
...state,
|
|
103
|
+
documents: {
|
|
104
|
+
...state.documents,
|
|
105
|
+
[documentId]: {
|
|
106
|
+
...docState,
|
|
107
|
+
scrollY,
|
|
108
|
+
viewportH
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
default:
|
|
114
|
+
return state;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
23
117
|
const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
24
118
|
constructor(id, registry, cfg) {
|
|
25
119
|
var _a;
|
|
26
120
|
super(id, registry);
|
|
27
121
|
this.cfg = cfg;
|
|
28
122
|
this.scrollCapability = null;
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.
|
|
123
|
+
this.taskCaches = /* @__PURE__ */ new Map();
|
|
124
|
+
this.canAutoScroll = /* @__PURE__ */ new Map();
|
|
125
|
+
this.window$ = createScopedEmitter(
|
|
126
|
+
(documentId, window) => ({ documentId, window })
|
|
127
|
+
);
|
|
128
|
+
this.scrollTo$ = createScopedEmitter(
|
|
129
|
+
(documentId, options) => ({ documentId, options })
|
|
130
|
+
);
|
|
131
|
+
this.refreshPages$ = createScopedEmitter(
|
|
132
|
+
(documentId, pages) => ({ documentId, pages }),
|
|
133
|
+
{ cache: false }
|
|
134
|
+
);
|
|
38
135
|
this.renderCapability = this.registry.getPlugin("render").provides();
|
|
39
136
|
this.scrollCapability = ((_a = this.registry.getPlugin("scroll")) == null ? void 0 : _a.provides()) ?? null;
|
|
40
|
-
this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {
|
|
41
|
-
this.taskCache.clear();
|
|
42
|
-
this.setWindowState(state);
|
|
43
|
-
});
|
|
44
137
|
this.coreStore.onAction(REFRESH_PAGES, (action) => {
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
|
|
138
|
+
const documentId = action.payload.documentId ?? this.getActiveDocumentId();
|
|
139
|
+
const pages = action.payload.pageIndexes;
|
|
140
|
+
this.refreshPages$.emit(documentId, pages);
|
|
141
|
+
const taskCache = this.taskCaches.get(documentId);
|
|
142
|
+
if (taskCache) {
|
|
143
|
+
for (const pageIndex of pages) {
|
|
144
|
+
taskCache.delete(pageIndex);
|
|
145
|
+
}
|
|
48
146
|
}
|
|
49
147
|
});
|
|
50
148
|
if (this.scrollCapability && this.cfg.autoScroll !== false) {
|
|
51
|
-
this.scrollCapability.onPageChangeState(({
|
|
52
|
-
this.canAutoScroll
|
|
53
|
-
if (!isChanging) {
|
|
54
|
-
this.scrollToThumb(targetPage - 1);
|
|
149
|
+
this.scrollCapability.onPageChangeState(({ documentId, state }) => {
|
|
150
|
+
this.canAutoScroll.set(documentId, !state.isChanging);
|
|
151
|
+
if (!state.isChanging) {
|
|
152
|
+
this.scrollToThumb(state.targetPage - 1, documentId);
|
|
55
153
|
}
|
|
56
154
|
});
|
|
57
|
-
this.scrollCapability.onPageChange(({ pageNumber }) => {
|
|
58
|
-
if (this.canAutoScroll) {
|
|
59
|
-
this.scrollToThumb(pageNumber - 1);
|
|
155
|
+
this.scrollCapability.onPageChange(({ documentId, pageNumber }) => {
|
|
156
|
+
if (this.canAutoScroll.get(documentId) !== false) {
|
|
157
|
+
this.scrollToThumb(pageNumber - 1, documentId);
|
|
60
158
|
}
|
|
61
159
|
});
|
|
62
160
|
}
|
|
63
161
|
}
|
|
64
|
-
|
|
65
|
-
|
|
162
|
+
// ─────────────────────────────────────────────────────────
|
|
163
|
+
// Document Lifecycle Hooks (from BasePlugin)
|
|
164
|
+
// ─────────────────────────────────────────────────────────
|
|
165
|
+
onDocumentLoadingStarted(documentId) {
|
|
166
|
+
this.dispatch(
|
|
167
|
+
initThumbnailState(documentId, {
|
|
168
|
+
...initialDocumentState
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
this.taskCaches.set(documentId, /* @__PURE__ */ new Map());
|
|
172
|
+
this.canAutoScroll.set(documentId, true);
|
|
173
|
+
this.logger.debug(
|
|
174
|
+
"ThumbnailPlugin",
|
|
175
|
+
"DocumentOpened",
|
|
176
|
+
`Initialized thumbnail state for document: ${documentId}`
|
|
177
|
+
);
|
|
66
178
|
}
|
|
67
|
-
|
|
68
|
-
|
|
179
|
+
onDocumentLoaded(documentId) {
|
|
180
|
+
this.calculateWindowState(documentId);
|
|
69
181
|
}
|
|
70
|
-
|
|
71
|
-
|
|
182
|
+
onDocumentClosed(documentId) {
|
|
183
|
+
this.dispatch(cleanupThumbnailState(documentId));
|
|
184
|
+
const taskCache = this.taskCaches.get(documentId);
|
|
185
|
+
if (taskCache) {
|
|
186
|
+
taskCache.forEach((task) => {
|
|
187
|
+
task.abort({
|
|
188
|
+
code: "cancelled",
|
|
189
|
+
message: "Document closed"
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
taskCache.clear();
|
|
193
|
+
this.taskCaches.delete(documentId);
|
|
194
|
+
}
|
|
195
|
+
this.canAutoScroll.delete(documentId);
|
|
196
|
+
this.window$.clearScope(documentId);
|
|
197
|
+
this.scrollTo$.clearScope(documentId);
|
|
198
|
+
this.refreshPages$.clearScope(documentId);
|
|
199
|
+
this.logger.debug(
|
|
200
|
+
"ThumbnailPlugin",
|
|
201
|
+
"DocumentClosed",
|
|
202
|
+
`Cleaned up thumbnail state for document: ${documentId}`
|
|
203
|
+
);
|
|
72
204
|
}
|
|
73
|
-
|
|
74
|
-
|
|
205
|
+
onRotationChanged(documentId) {
|
|
206
|
+
this.calculateWindowState(documentId);
|
|
75
207
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
208
|
+
// ─────────────────────────────────────────────────────────
|
|
209
|
+
// Capability
|
|
210
|
+
// ─────────────────────────────────────────────────────────
|
|
211
|
+
buildCapability() {
|
|
212
|
+
return {
|
|
213
|
+
// Active document operations
|
|
214
|
+
scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx),
|
|
215
|
+
renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),
|
|
216
|
+
updateWindow: (scrollY, viewportH) => this.updateWindow(scrollY, viewportH),
|
|
217
|
+
getWindow: () => this.getWindow(),
|
|
218
|
+
// Document-scoped operations
|
|
219
|
+
forDocument: (documentId) => this.createThumbnailScope(documentId),
|
|
220
|
+
// Events
|
|
221
|
+
onWindow: this.window$.onGlobal,
|
|
222
|
+
onScrollTo: this.scrollTo$.onGlobal,
|
|
223
|
+
onRefreshPages: this.refreshPages$.onGlobal
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// ─────────────────────────────────────────────────────────
|
|
227
|
+
// Document Scoping
|
|
228
|
+
// ─────────────────────────────────────────────────────────
|
|
229
|
+
createThumbnailScope(documentId) {
|
|
230
|
+
return {
|
|
231
|
+
scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx, documentId),
|
|
232
|
+
renderThumb: (idx, dpr) => this.renderThumb(idx, dpr, documentId),
|
|
233
|
+
updateWindow: (scrollY, viewportH) => this.updateWindow(scrollY, viewportH, documentId),
|
|
234
|
+
getWindow: () => this.getWindow(documentId),
|
|
235
|
+
onWindow: this.window$.forScope(documentId),
|
|
236
|
+
onScrollTo: this.scrollTo$.forScope(documentId),
|
|
237
|
+
onRefreshPages: this.refreshPages$.forScope(documentId)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// ─────────────────────────────────────────────────────────
|
|
241
|
+
// State Helpers
|
|
242
|
+
// ─────────────────────────────────────────────────────────
|
|
243
|
+
getDocumentState(documentId) {
|
|
244
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
245
|
+
return this.state.documents[id] ?? null;
|
|
246
|
+
}
|
|
247
|
+
// ─────────────────────────────────────────────────────────
|
|
248
|
+
// Core Operations
|
|
249
|
+
// ─────────────────────────────────────────────────────────
|
|
250
|
+
calculateWindowState(documentId) {
|
|
251
|
+
const coreDoc = this.coreState.core.documents[documentId];
|
|
252
|
+
if (!(coreDoc == null ? void 0 : coreDoc.document)) return;
|
|
79
253
|
const OUTER_W = this.cfg.width ?? 120;
|
|
80
254
|
const L = this.cfg.labelHeight ?? 16;
|
|
81
255
|
const GAP = this.cfg.gap ?? 8;
|
|
@@ -83,7 +257,7 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
83
257
|
const PADDING_Y = this.cfg.paddingY ?? 0;
|
|
84
258
|
const INNER_W = Math.max(1, OUTER_W - 2 * P);
|
|
85
259
|
let offset = PADDING_Y;
|
|
86
|
-
|
|
260
|
+
const thumbs = coreDoc.document.pages.map((p) => {
|
|
87
261
|
const ratio = p.size.height / p.size.width;
|
|
88
262
|
const imgH = Math.round(INNER_W * ratio);
|
|
89
263
|
const wrapH = P + imgH + P + L;
|
|
@@ -99,41 +273,42 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
99
273
|
// top of the row
|
|
100
274
|
labelHeight: L,
|
|
101
275
|
padding: P
|
|
102
|
-
// NEW
|
|
103
276
|
};
|
|
104
277
|
offset += wrapH + GAP;
|
|
105
278
|
return meta;
|
|
106
279
|
});
|
|
107
|
-
|
|
280
|
+
const window = {
|
|
108
281
|
start: -1,
|
|
109
282
|
end: -1,
|
|
110
283
|
items: [],
|
|
111
284
|
totalHeight: offset - GAP + PADDING_Y
|
|
112
285
|
// Add bottom padding to total height
|
|
113
286
|
};
|
|
114
|
-
|
|
115
|
-
|
|
287
|
+
const docState = this.getDocumentState(documentId);
|
|
288
|
+
if (!docState) return;
|
|
289
|
+
this.dispatch(
|
|
290
|
+
initThumbnailState(documentId, {
|
|
291
|
+
...docState,
|
|
292
|
+
thumbs,
|
|
293
|
+
window
|
|
294
|
+
})
|
|
295
|
+
);
|
|
296
|
+
if (docState.viewportH > 0) {
|
|
297
|
+
this.updateWindow(docState.scrollY, docState.viewportH, documentId);
|
|
116
298
|
} else {
|
|
117
|
-
this.
|
|
299
|
+
this.window$.emit(documentId, window);
|
|
118
300
|
}
|
|
119
301
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx)
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
/* ------------ windowing & viewport state ------------------------- */
|
|
128
|
-
updateWindow(scrollY, viewportH) {
|
|
302
|
+
updateWindow(scrollY, viewportH, documentId) {
|
|
303
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
304
|
+
const docState = this.getDocumentState(id);
|
|
305
|
+
if (!docState || !docState.window || docState.thumbs.length === 0) return;
|
|
129
306
|
const BUF = this.cfg.buffer ?? 3;
|
|
130
|
-
this.scrollY
|
|
131
|
-
|
|
132
|
-
if (!this.window || this.thumbs.length === 0) return;
|
|
133
|
-
let low = 0, high = this.thumbs.length - 1, first = 0;
|
|
307
|
+
this.dispatch(updateViewportMetrics(id, scrollY, viewportH));
|
|
308
|
+
let low = 0, high = docState.thumbs.length - 1, first = 0;
|
|
134
309
|
while (low <= high) {
|
|
135
310
|
const mid = low + high >> 1;
|
|
136
|
-
const m =
|
|
311
|
+
const m = docState.thumbs[mid];
|
|
137
312
|
if (m.top + m.wrapperHeight < scrollY) low = mid + 1;
|
|
138
313
|
else {
|
|
139
314
|
first = mid;
|
|
@@ -142,51 +317,73 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
142
317
|
}
|
|
143
318
|
let last = first;
|
|
144
319
|
const limit = scrollY + viewportH;
|
|
145
|
-
while (last + 1 <
|
|
146
|
-
last = Math.min(
|
|
320
|
+
while (last + 1 < docState.thumbs.length && docState.thumbs[last].top < limit) last++;
|
|
321
|
+
last = Math.min(docState.thumbs.length - 1, last + BUF);
|
|
147
322
|
const start = Math.max(0, first - BUF);
|
|
148
|
-
if (start ===
|
|
149
|
-
|
|
323
|
+
if (start === docState.window.start && last === docState.window.end) return;
|
|
324
|
+
const newWindow = {
|
|
150
325
|
start,
|
|
151
326
|
end: last,
|
|
152
|
-
items:
|
|
153
|
-
totalHeight:
|
|
327
|
+
items: docState.thumbs.slice(start, last + 1),
|
|
328
|
+
totalHeight: docState.window.totalHeight
|
|
154
329
|
};
|
|
155
|
-
this.
|
|
330
|
+
this.dispatch(setWindowState(id, newWindow));
|
|
331
|
+
this.window$.emit(id, newWindow);
|
|
332
|
+
}
|
|
333
|
+
getWindow(documentId) {
|
|
334
|
+
const docState = this.getDocumentState(documentId);
|
|
335
|
+
return (docState == null ? void 0 : docState.window) ?? null;
|
|
156
336
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
337
|
+
scrollToThumb(pageIdx, documentId) {
|
|
338
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
339
|
+
const docState = this.getDocumentState(id);
|
|
340
|
+
if (!docState || !docState.window) return;
|
|
341
|
+
const item = docState.thumbs[pageIdx];
|
|
161
342
|
if (!item) return;
|
|
162
343
|
const behavior = this.cfg.scrollBehavior ?? "smooth";
|
|
163
344
|
const PADDING_Y = this.cfg.paddingY ?? 0;
|
|
164
|
-
if (
|
|
345
|
+
if (docState.viewportH <= 0) {
|
|
165
346
|
const top2 = Math.max(PADDING_Y, item.top - item.wrapperHeight);
|
|
166
|
-
this.scrollTo$.emit({ top: top2, behavior });
|
|
347
|
+
this.scrollTo$.emit(id, { top: top2, behavior });
|
|
167
348
|
return;
|
|
168
349
|
}
|
|
169
350
|
const margin = 8;
|
|
170
351
|
const top = item.top;
|
|
171
352
|
const bottom = item.top + item.wrapperHeight;
|
|
172
|
-
const needsUp = top <
|
|
173
|
-
const needsDown = bottom >
|
|
353
|
+
const needsUp = top < docState.scrollY + margin + PADDING_Y;
|
|
354
|
+
const needsDown = bottom > docState.scrollY + docState.viewportH - margin;
|
|
174
355
|
if (needsUp) {
|
|
175
|
-
this.scrollTo$.emit(
|
|
356
|
+
this.scrollTo$.emit(id, {
|
|
357
|
+
top: Math.max(0, top - PADDING_Y),
|
|
358
|
+
behavior
|
|
359
|
+
});
|
|
176
360
|
} else if (needsDown) {
|
|
177
|
-
this.scrollTo$.emit(
|
|
361
|
+
this.scrollTo$.emit(id, {
|
|
362
|
+
top: Math.max(0, bottom - docState.viewportH + PADDING_Y),
|
|
363
|
+
behavior
|
|
364
|
+
});
|
|
178
365
|
}
|
|
179
366
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
367
|
+
renderThumb(idx, dpr, documentId) {
|
|
368
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
369
|
+
const taskCache = this.taskCaches.get(id);
|
|
370
|
+
if (!taskCache) {
|
|
371
|
+
throw new Error(`Task cache not found for document: ${id}`);
|
|
372
|
+
}
|
|
373
|
+
if (taskCache.has(idx)) return taskCache.get(idx);
|
|
374
|
+
const coreDoc = this.coreState.core.documents[id];
|
|
375
|
+
if (!(coreDoc == null ? void 0 : coreDoc.document)) {
|
|
376
|
+
throw new Error(`Document not found: ${id}`);
|
|
377
|
+
}
|
|
378
|
+
const page = coreDoc.document.pages[idx];
|
|
379
|
+
if (!page) {
|
|
380
|
+
throw new Error(`Page ${idx} not found in document: ${id}`);
|
|
381
|
+
}
|
|
185
382
|
const OUTER_W = this.cfg.width ?? 120;
|
|
186
383
|
const P = this.cfg.imagePadding ?? 0;
|
|
187
384
|
const INNER_W = Math.max(1, OUTER_W - 2 * P);
|
|
188
385
|
const scale = INNER_W / page.size.width;
|
|
189
|
-
const task = this.renderCapability.renderPageRect({
|
|
386
|
+
const task = this.renderCapability.forDocument(id).renderPageRect({
|
|
190
387
|
pageIndex: idx,
|
|
191
388
|
rect: { origin: { x: 0, y: 0 }, size: page.size },
|
|
192
389
|
options: {
|
|
@@ -194,24 +391,59 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
194
391
|
dpr
|
|
195
392
|
}
|
|
196
393
|
});
|
|
197
|
-
|
|
198
|
-
task.wait(ignore, () =>
|
|
394
|
+
taskCache.set(idx, task);
|
|
395
|
+
task.wait(ignore, () => taskCache.delete(idx));
|
|
199
396
|
return task;
|
|
200
397
|
}
|
|
398
|
+
// ─────────────────────────────────────────────────────────
|
|
399
|
+
// Lifecycle
|
|
400
|
+
// ─────────────────────────────────────────────────────────
|
|
401
|
+
async initialize() {
|
|
402
|
+
this.logger.info("ThumbnailPlugin", "Initialize", "Thumbnail plugin initialized");
|
|
403
|
+
}
|
|
404
|
+
async destroy() {
|
|
405
|
+
this.window$.clear();
|
|
406
|
+
this.refreshPages$.clear();
|
|
407
|
+
this.scrollTo$.clear();
|
|
408
|
+
this.taskCaches.forEach((cache) => {
|
|
409
|
+
cache.forEach((task) => {
|
|
410
|
+
task.abort({
|
|
411
|
+
code: "cancelled",
|
|
412
|
+
message: "Plugin destroyed"
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
cache.clear();
|
|
416
|
+
});
|
|
417
|
+
this.taskCaches.clear();
|
|
418
|
+
this.canAutoScroll.clear();
|
|
419
|
+
super.destroy();
|
|
420
|
+
}
|
|
201
421
|
};
|
|
202
422
|
_ThumbnailPlugin.id = "thumbnail";
|
|
203
423
|
let ThumbnailPlugin = _ThumbnailPlugin;
|
|
204
424
|
const ThumbnailPluginPackage = {
|
|
205
425
|
manifest,
|
|
206
426
|
create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),
|
|
207
|
-
reducer:
|
|
208
|
-
|
|
209
|
-
initialState: {}
|
|
427
|
+
reducer: thumbnailReducer,
|
|
428
|
+
initialState
|
|
210
429
|
};
|
|
211
430
|
export {
|
|
431
|
+
CLEANUP_THUMBNAIL_STATE,
|
|
432
|
+
INIT_THUMBNAIL_STATE,
|
|
433
|
+
SET_ACTIVE_DOCUMENT,
|
|
434
|
+
SET_WINDOW_STATE,
|
|
212
435
|
THUMBNAIL_PLUGIN_ID,
|
|
213
436
|
ThumbnailPlugin,
|
|
214
437
|
ThumbnailPluginPackage,
|
|
215
|
-
|
|
438
|
+
UPDATE_VIEWPORT_METRICS,
|
|
439
|
+
cleanupThumbnailState,
|
|
440
|
+
initThumbnailState,
|
|
441
|
+
initialDocumentState,
|
|
442
|
+
initialState,
|
|
443
|
+
manifest,
|
|
444
|
+
setActiveDocument,
|
|
445
|
+
setWindowState,
|
|
446
|
+
thumbnailReducer,
|
|
447
|
+
updateViewportMetrics
|
|
216
448
|
};
|
|
217
449
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/lib/manifest.ts","../src/lib/thumbnail-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { ThumbnailPluginConfig } from './types';\n\nexport const THUMBNAIL_PLUGIN_ID = 'thumbnail';\n\nexport const manifest: PluginManifest<ThumbnailPluginConfig> = {\n id: THUMBNAIL_PLUGIN_ID,\n name: 'Thumbnail Plugin',\n version: '1.0.0',\n provides: ['thumbnail'],\n requires: ['render'],\n optional: ['scroll'],\n defaultConfig: {\n enabled: true,\n width: 150,\n gap: 10,\n buffer: 3,\n labelHeight: 16,\n autoScroll: true,\n scrollBehavior: 'smooth',\n imagePadding: 0,\n paddingY: 0,\n },\n};\n","import {\n BasePlugin,\n CoreState,\n createBehaviorEmitter,\n createEmitter,\n PluginRegistry,\n REFRESH_PAGES,\n SET_DOCUMENT,\n StoreState,\n Unsubscribe,\n} from '@embedpdf/core';\nimport { ScrollToOptions, ThumbMeta, ThumbnailPluginConfig, WindowState } from './types';\nimport { ThumbnailCapability } from './types';\nimport { ignore, PdfErrorReason, Task } from '@embedpdf/models';\nimport { RenderCapability, RenderPlugin } from '@embedpdf/plugin-render';\nimport { ScrollCapability, ScrollPlugin } from '@embedpdf/plugin-scroll';\n\nexport class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {\n static readonly id = 'thumbnail' as const;\n\n private renderCapability: RenderCapability;\n private scrollCapability: ScrollCapability | null = null;\n private thumbs: ThumbMeta[] = [];\n private window: WindowState | null = null;\n\n // track viewport metrics for scroll decisions\n private viewportH: number = 0;\n private scrollY: number = 0;\n\n private readonly emitWindow = createBehaviorEmitter<WindowState>();\n private readonly refreshPages$ = createEmitter<number[]>();\n private readonly taskCache = new Map<number, Task<Blob, PdfErrorReason>>();\n private canAutoScroll = true;\n // ask pane to scroll to a specific top\n private readonly scrollTo$ = createBehaviorEmitter<ScrollToOptions>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n public cfg: ThumbnailPluginConfig,\n ) {\n super(id, registry);\n\n this.renderCapability = this.registry.getPlugin<RenderPlugin>('render')!.provides();\n this.scrollCapability = this.registry.getPlugin<ScrollPlugin>('scroll')?.provides() ?? null;\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.taskCache.clear();\n this.setWindowState(state);\n });\n\n this.coreStore.onAction(REFRESH_PAGES, (action) => {\n this.refreshPages$.emit(action.payload);\n for (const pageIdx of action.payload) {\n this.taskCache.delete(pageIdx);\n }\n });\n\n // auto-scroll thumbnails when the main scroller's current page changes\n if (this.scrollCapability && this.cfg.autoScroll !== false) {\n this.scrollCapability.onPageChangeState(({ isChanging, targetPage }) => {\n this.canAutoScroll = !isChanging;\n if (!isChanging) {\n this.scrollToThumb(targetPage - 1);\n }\n });\n this.scrollCapability.onPageChange(({ pageNumber }) => {\n if (this.canAutoScroll) {\n this.scrollToThumb(pageNumber - 1);\n }\n });\n }\n }\n\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\n\n public onRefreshPages(fn: (pages: number[]) => void): Unsubscribe {\n return this.refreshPages$.on(fn);\n }\n\n public onWindow(cb: (w: WindowState) => void): Unsubscribe {\n return this.emitWindow.on(cb);\n }\n\n public onScrollTo(cb: (o: ScrollToOptions) => void): Unsubscribe {\n return this.scrollTo$.on(cb);\n }\n\n private setWindowState(state: StoreState<CoreState>) {\n const core = state.core;\n if (!core.document) return;\n\n const OUTER_W = this.cfg.width ?? 120;\n const L = this.cfg.labelHeight ?? 16;\n const GAP = this.cfg.gap ?? 8;\n const P = this.cfg.imagePadding ?? 0;\n const PADDING_Y = this.cfg.paddingY ?? 0;\n\n // Inner bitmap width cannot go below 1px\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n let offset = PADDING_Y; // Start with top padding\n this.thumbs = core.document.pages.map((p) => {\n const ratio = p.size.height / p.size.width;\n const imgH = Math.round(INNER_W * ratio);\n const wrapH = P + imgH + P + L; // padding + image + padding + label\n\n const meta: ThumbMeta = {\n pageIndex: p.index,\n width: INNER_W, // bitmap width (for <img> size)\n height: imgH, // bitmap height (for <img> size)\n wrapperHeight: wrapH, // full row height used by virtualizer\n top: offset, // top of the row\n labelHeight: L,\n padding: P, // NEW\n };\n offset += wrapH + GAP;\n return meta;\n });\n\n this.window = {\n start: -1,\n end: -1,\n items: [],\n totalHeight: offset - GAP + PADDING_Y, // Add bottom padding to total height\n };\n\n if (this.viewportH > 0) {\n this.updateWindow(this.scrollY, this.viewportH);\n } else {\n this.emitWindow.emit(this.window);\n }\n }\n\n /* ------------ capability ----------------------------------------- */\n protected buildCapability(): ThumbnailCapability {\n return {\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),\n scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx),\n };\n }\n\n /* ------------ windowing & viewport state ------------------------- */\n public updateWindow(scrollY: number, viewportH: number) {\n const BUF = this.cfg.buffer ?? 3;\n\n // remember latest viewport metrics for scroll decisions\n this.scrollY = scrollY;\n this.viewportH = viewportH;\n\n // Early return if window state hasn't been initialized yet\n if (!this.window || this.thumbs.length === 0) return;\n\n /* find first visible */\n let low = 0,\n high = this.thumbs.length - 1,\n first = 0;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const m = this.thumbs[mid];\n if (m.top + m.wrapperHeight < scrollY) low = mid + 1;\n else {\n first = mid;\n high = mid - 1;\n }\n }\n\n /* find last visible + buffer */\n let last = first;\n const limit = scrollY + viewportH;\n while (last + 1 < this.thumbs.length && this.thumbs[last].top < limit) last++;\n last = Math.min(this.thumbs.length - 1, last + BUF);\n\n const start = Math.max(0, first - BUF);\n if (start === this.window.start && last === this.window.end) return;\n\n this.window = {\n start,\n end: last,\n items: this.thumbs.slice(start, last + 1),\n totalHeight: this.window.totalHeight,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ scroll helper now in plugin ------------------------ */\n private scrollToThumb(pageIdx: number) {\n if (!this.window) return;\n const item = this.thumbs[pageIdx];\n if (!item) return;\n\n const behavior = this.cfg.scrollBehavior ?? 'smooth';\n const PADDING_Y = this.cfg.paddingY ?? 0; // Include padding in scroll calculations\n\n if (this.viewportH <= 0) {\n // Center the thumbnail in the viewport\n const top = Math.max(PADDING_Y, item.top - item.wrapperHeight);\n this.scrollTo$.emit({ top, behavior });\n return;\n }\n\n const margin = 8;\n const top = item.top;\n const bottom = item.top + item.wrapperHeight;\n\n const needsUp = top < this.scrollY + margin + PADDING_Y;\n const needsDown = bottom > this.scrollY + this.viewportH - margin;\n\n if (needsUp) {\n this.scrollTo$.emit({ top: Math.max(0, top - PADDING_Y), behavior });\n } else if (needsDown) {\n this.scrollTo$.emit({ top: Math.max(0, bottom - this.viewportH + PADDING_Y), behavior });\n }\n }\n\n /* ------------ thumbnail raster ----------------------------------- */\n private renderThumb(idx: number, dpr: number) {\n if (this.taskCache.has(idx)) return this.taskCache.get(idx)!;\n\n const core = this.coreState.core;\n const page = core.document!.pages[idx];\n\n const OUTER_W = this.cfg.width ?? 120;\n const P = this.cfg.imagePadding ?? 0;\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n const scale = INNER_W / page.size.width; // scale to inner width (excludes padding)\n\n const task = this.renderCapability.renderPageRect({\n pageIndex: idx,\n rect: { origin: { x: 0, y: 0 }, size: page.size },\n options: {\n scaleFactor: scale,\n dpr,\n },\n });\n\n this.taskCache.set(idx, task);\n task.wait(ignore, () => this.taskCache.delete(idx));\n return task;\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, THUMBNAIL_PLUGIN_ID } from './manifest';\nimport { ThumbnailPluginConfig } from './types';\nimport { ThumbnailPlugin } from './thumbnail-plugin';\n\nexport const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig> = {\n manifest,\n create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),\n reducer: () => {},\n initialState: {},\n};\n\nexport * from './thumbnail-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":["top"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/thumbnail-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { ThumbnailPluginConfig } from './types';\n\nexport const THUMBNAIL_PLUGIN_ID = 'thumbnail';\n\nexport const manifest: PluginManifest<ThumbnailPluginConfig> = {\n id: THUMBNAIL_PLUGIN_ID,\n name: 'Thumbnail Plugin',\n version: '1.0.0',\n provides: ['thumbnail'],\n requires: ['render'],\n optional: ['scroll'],\n defaultConfig: {\n enabled: true,\n width: 150,\n gap: 10,\n buffer: 3,\n labelHeight: 16,\n autoScroll: true,\n scrollBehavior: 'smooth',\n imagePadding: 0,\n paddingY: 0,\n },\n};\n","import { Action } from '@embedpdf/core';\nimport { ThumbnailDocumentState, WindowState } from './types';\n\n// Document lifecycle\nexport const INIT_THUMBNAIL_STATE = 'THUMBNAIL/INIT_STATE';\nexport const CLEANUP_THUMBNAIL_STATE = 'THUMBNAIL/CLEANUP_STATE';\nexport const SET_ACTIVE_DOCUMENT = 'THUMBNAIL/SET_ACTIVE_DOCUMENT';\n\n// Per-document operations\nexport const SET_WINDOW_STATE = 'THUMBNAIL/SET_WINDOW_STATE';\nexport const UPDATE_VIEWPORT_METRICS = 'THUMBNAIL/UPDATE_VIEWPORT_METRICS';\n\n// Document lifecycle actions\nexport interface InitThumbnailStateAction extends Action {\n type: typeof INIT_THUMBNAIL_STATE;\n payload: {\n documentId: string;\n state: ThumbnailDocumentState;\n };\n}\n\nexport interface CleanupThumbnailStateAction extends Action {\n type: typeof CLEANUP_THUMBNAIL_STATE;\n payload: string; // documentId\n}\n\nexport interface SetActiveDocumentAction extends Action {\n type: typeof SET_ACTIVE_DOCUMENT;\n payload: string | null; // documentId\n}\n\nexport interface SetWindowStateAction extends Action {\n type: typeof SET_WINDOW_STATE;\n payload: {\n documentId: string;\n window: WindowState | null;\n };\n}\n\nexport interface UpdateViewportMetricsAction extends Action {\n type: typeof UPDATE_VIEWPORT_METRICS;\n payload: {\n documentId: string;\n scrollY: number;\n viewportH: number;\n };\n}\n\nexport type ThumbnailAction =\n | InitThumbnailStateAction\n | CleanupThumbnailStateAction\n | SetActiveDocumentAction\n | SetWindowStateAction\n | UpdateViewportMetricsAction;\n\n// Action Creators\nexport function initThumbnailState(\n documentId: string,\n state: ThumbnailDocumentState,\n): InitThumbnailStateAction {\n return { type: INIT_THUMBNAIL_STATE, payload: { documentId, state } };\n}\n\nexport function cleanupThumbnailState(documentId: string): CleanupThumbnailStateAction {\n return { type: CLEANUP_THUMBNAIL_STATE, payload: documentId };\n}\n\nexport function setActiveDocument(documentId: string | null): SetActiveDocumentAction {\n return { type: SET_ACTIVE_DOCUMENT, payload: documentId };\n}\n\nexport function setWindowState(\n documentId: string,\n window: WindowState | null,\n): SetWindowStateAction {\n return { type: SET_WINDOW_STATE, payload: { documentId, window } };\n}\n\nexport function updateViewportMetrics(\n documentId: string,\n scrollY: number,\n viewportH: number,\n): UpdateViewportMetricsAction {\n return { type: UPDATE_VIEWPORT_METRICS, payload: { documentId, scrollY, viewportH } };\n}\n","import { Reducer } from '@embedpdf/core';\nimport {\n ThumbnailAction,\n INIT_THUMBNAIL_STATE,\n CLEANUP_THUMBNAIL_STATE,\n SET_ACTIVE_DOCUMENT,\n SET_WINDOW_STATE,\n UPDATE_VIEWPORT_METRICS,\n} from './actions';\nimport { ThumbnailState, ThumbnailDocumentState } from './types';\n\nexport const initialDocumentState: ThumbnailDocumentState = {\n thumbs: [],\n window: null,\n viewportH: 0,\n scrollY: 0,\n};\n\nexport const initialState: ThumbnailState = {\n documents: {},\n activeDocumentId: null,\n};\n\nexport const thumbnailReducer: Reducer<ThumbnailState, ThumbnailAction> = (\n state = initialState,\n action,\n) => {\n switch (action.type) {\n case INIT_THUMBNAIL_STATE: {\n const { documentId, state: docState } = action.payload;\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: docState,\n },\n // Set as active if no active document\n activeDocumentId: state.activeDocumentId ?? documentId,\n };\n }\n\n case CLEANUP_THUMBNAIL_STATE: {\n const documentId = action.payload;\n const { [documentId]: removed, ...remainingDocs } = state.documents;\n return {\n ...state,\n documents: remainingDocs,\n activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId,\n };\n }\n\n case SET_ACTIVE_DOCUMENT: {\n return {\n ...state,\n activeDocumentId: action.payload,\n };\n }\n\n case SET_WINDOW_STATE: {\n const { documentId, window } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n window,\n },\n },\n };\n }\n\n case UPDATE_VIEWPORT_METRICS: {\n const { documentId, scrollY, viewportH } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n scrollY,\n viewportH,\n },\n },\n };\n }\n\n default:\n return state;\n }\n};\n","import {\n BasePlugin,\n createBehaviorEmitter,\n createEmitter,\n createScopedEmitter,\n Listener,\n PluginRegistry,\n REFRESH_PAGES,\n} from '@embedpdf/core';\nimport {\n ScrollToOptions,\n ThumbMeta,\n ThumbnailPluginConfig,\n WindowState,\n ThumbnailCapability,\n ThumbnailScope,\n WindowChangeEvent,\n ScrollToEvent,\n RefreshPagesEvent,\n ThumbnailState,\n ThumbnailDocumentState,\n} from './types';\nimport { ignore, PdfErrorReason, Task } from '@embedpdf/models';\nimport { RenderCapability, RenderPlugin } from '@embedpdf/plugin-render';\nimport { ScrollCapability, ScrollPlugin } from '@embedpdf/plugin-scroll';\nimport {\n initThumbnailState,\n cleanupThumbnailState,\n setWindowState,\n updateViewportMetrics,\n ThumbnailAction,\n} from './actions';\nimport { initialDocumentState } from './reducer';\n\nexport class ThumbnailPlugin extends BasePlugin<\n ThumbnailPluginConfig,\n ThumbnailCapability,\n ThumbnailState,\n ThumbnailAction\n> {\n static readonly id = 'thumbnail' as const;\n\n private renderCapability: RenderCapability;\n private scrollCapability: ScrollCapability | null = null;\n\n // Per-document task caches\n private readonly taskCaches = new Map<string, Map<number, Task<Blob, PdfErrorReason>>>();\n\n // Per-document auto-scroll tracking\n private readonly canAutoScroll = new Map<string, boolean>();\n\n private readonly window$ = createScopedEmitter<WindowState, WindowChangeEvent, string>(\n (documentId, window) => ({ documentId, window }),\n );\n private readonly scrollTo$ = createScopedEmitter<ScrollToOptions, ScrollToEvent, string>(\n (documentId, options) => ({ documentId, options }),\n );\n private readonly refreshPages$ = createScopedEmitter<number[], RefreshPagesEvent, string>(\n (documentId, pages) => ({ documentId, pages }),\n { cache: false },\n );\n\n constructor(\n id: string,\n registry: PluginRegistry,\n public cfg: ThumbnailPluginConfig,\n ) {\n super(id, registry);\n\n this.renderCapability = this.registry.getPlugin<RenderPlugin>('render')!.provides();\n this.scrollCapability = this.registry.getPlugin<ScrollPlugin>('scroll')?.provides() ?? null;\n\n this.coreStore.onAction(REFRESH_PAGES, (action) => {\n const documentId = action.payload.documentId ?? this.getActiveDocumentId();\n const pages = action.payload.pageIndexes;\n\n this.refreshPages$.emit(documentId, pages);\n\n const taskCache = this.taskCaches.get(documentId);\n if (taskCache) {\n for (const pageIndex of pages) {\n taskCache.delete(pageIndex);\n }\n }\n });\n\n // Auto-scroll thumbnails when the main scroller's current page changes\n if (this.scrollCapability && this.cfg.autoScroll !== false) {\n this.scrollCapability.onPageChangeState(({ documentId, state }) => {\n this.canAutoScroll.set(documentId, !state.isChanging);\n if (!state.isChanging) {\n this.scrollToThumb(state.targetPage - 1, documentId);\n }\n });\n this.scrollCapability.onPageChange(({ documentId, pageNumber }) => {\n if (this.canAutoScroll.get(documentId) !== false) {\n this.scrollToThumb(pageNumber - 1, documentId);\n }\n });\n }\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Lifecycle Hooks (from BasePlugin)\n // ─────────────────────────────────────────────────────────\n\n protected override onDocumentLoadingStarted(documentId: string): void {\n // Initialize state for this document\n this.dispatch(\n initThumbnailState(documentId, {\n ...initialDocumentState,\n }),\n );\n\n // Initialize task cache\n this.taskCaches.set(documentId, new Map());\n this.canAutoScroll.set(documentId, true);\n\n this.logger.debug(\n 'ThumbnailPlugin',\n 'DocumentOpened',\n `Initialized thumbnail state for document: ${documentId}`,\n );\n }\n\n protected override onDocumentLoaded(documentId: string): void {\n // Calculate initial window state after document is fully loaded\n this.calculateWindowState(documentId);\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup state\n this.dispatch(cleanupThumbnailState(documentId));\n\n // Cleanup task cache\n const taskCache = this.taskCaches.get(documentId);\n if (taskCache) {\n taskCache.forEach((task) => {\n task.abort({\n code: 'cancelled' as any,\n message: 'Document closed',\n });\n });\n taskCache.clear();\n this.taskCaches.delete(documentId);\n }\n\n this.canAutoScroll.delete(documentId);\n this.window$.clearScope(documentId);\n this.scrollTo$.clearScope(documentId);\n this.refreshPages$.clearScope(documentId);\n this.logger.debug(\n 'ThumbnailPlugin',\n 'DocumentClosed',\n `Cleaned up thumbnail state for document: ${documentId}`,\n );\n }\n\n protected override onRotationChanged(documentId: string): void {\n // Recalculate window state when rotation changes\n this.calculateWindowState(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): ThumbnailCapability {\n return {\n // Active document operations\n scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx),\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),\n updateWindow: (scrollY, viewportH) => this.updateWindow(scrollY, viewportH),\n getWindow: () => this.getWindow(),\n\n // Document-scoped operations\n forDocument: (documentId: string) => this.createThumbnailScope(documentId),\n\n // Events\n onWindow: this.window$.onGlobal,\n onScrollTo: this.scrollTo$.onGlobal,\n onRefreshPages: this.refreshPages$.onGlobal,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createThumbnailScope(documentId: string): ThumbnailScope {\n return {\n scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx, documentId),\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr, documentId),\n updateWindow: (scrollY, viewportH) => this.updateWindow(scrollY, viewportH, documentId),\n getWindow: () => this.getWindow(documentId),\n onWindow: this.window$.forScope(documentId),\n onScrollTo: this.scrollTo$.forScope(documentId),\n onRefreshPages: this.refreshPages$.forScope(documentId),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // State Helpers\n // ─────────────────────────────────────────────────────────\n\n private getDocumentState(documentId?: string): ThumbnailDocumentState | null {\n const id = documentId ?? this.getActiveDocumentId();\n return this.state.documents[id] ?? null;\n }\n\n // ─────────────────────────────────────────────────────────\n // Core Operations\n // ─────────────────────────────────────────────────────────\n\n private calculateWindowState(documentId: string) {\n const coreDoc = this.coreState.core.documents[documentId];\n if (!coreDoc?.document) return;\n\n const OUTER_W = this.cfg.width ?? 120;\n const L = this.cfg.labelHeight ?? 16;\n const GAP = this.cfg.gap ?? 8;\n const P = this.cfg.imagePadding ?? 0;\n const PADDING_Y = this.cfg.paddingY ?? 0;\n\n // Inner bitmap width cannot go below 1px\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n let offset = PADDING_Y; // Start with top padding\n const thumbs: ThumbMeta[] = coreDoc.document.pages.map((p) => {\n const ratio = p.size.height / p.size.width;\n const imgH = Math.round(INNER_W * ratio);\n const wrapH = P + imgH + P + L; // padding + image + padding + label\n\n const meta: ThumbMeta = {\n pageIndex: p.index,\n width: INNER_W, // bitmap width (for <img> size)\n height: imgH, // bitmap height (for <img> size)\n wrapperHeight: wrapH, // full row height used by virtualizer\n top: offset, // top of the row\n labelHeight: L,\n padding: P,\n };\n offset += wrapH + GAP;\n return meta;\n });\n\n const window: WindowState = {\n start: -1,\n end: -1,\n items: [],\n totalHeight: offset - GAP + PADDING_Y, // Add bottom padding to total height\n };\n\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n // Update state with new thumbs and window\n this.dispatch(\n initThumbnailState(documentId, {\n ...docState,\n thumbs,\n window,\n }),\n );\n\n // Update window based on current viewport metrics\n if (docState.viewportH > 0) {\n this.updateWindow(docState.scrollY, docState.viewportH, documentId);\n } else {\n this.window$.emit(documentId, window);\n }\n }\n\n public updateWindow(scrollY: number, viewportH: number, documentId?: string) {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentState(id);\n if (!docState || !docState.window || docState.thumbs.length === 0) return;\n\n const BUF = this.cfg.buffer ?? 3;\n\n // Update viewport metrics\n this.dispatch(updateViewportMetrics(id, scrollY, viewportH));\n\n /* find first visible */\n let low = 0,\n high = docState.thumbs.length - 1,\n first = 0;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const m = docState.thumbs[mid];\n if (m.top + m.wrapperHeight < scrollY) low = mid + 1;\n else {\n first = mid;\n high = mid - 1;\n }\n }\n\n /* find last visible + buffer */\n let last = first;\n const limit = scrollY + viewportH;\n while (last + 1 < docState.thumbs.length && docState.thumbs[last].top < limit) last++;\n last = Math.min(docState.thumbs.length - 1, last + BUF);\n\n const start = Math.max(0, first - BUF);\n if (start === docState.window.start && last === docState.window.end) return;\n\n const newWindow: WindowState = {\n start,\n end: last,\n items: docState.thumbs.slice(start, last + 1),\n totalHeight: docState.window.totalHeight,\n };\n\n this.dispatch(setWindowState(id, newWindow));\n this.window$.emit(id, newWindow);\n }\n\n private getWindow(documentId?: string): WindowState | null {\n const docState = this.getDocumentState(documentId);\n return docState?.window ?? null;\n }\n\n private scrollToThumb(pageIdx: number, documentId?: string) {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentState(id);\n if (!docState || !docState.window) return;\n\n const item = docState.thumbs[pageIdx];\n if (!item) return;\n\n const behavior = this.cfg.scrollBehavior ?? 'smooth';\n const PADDING_Y = this.cfg.paddingY ?? 0;\n\n if (docState.viewportH <= 0) {\n // Center the thumbnail in the viewport\n const top = Math.max(PADDING_Y, item.top - item.wrapperHeight);\n this.scrollTo$.emit(id, { top, behavior });\n return;\n }\n\n const margin = 8;\n const top = item.top;\n const bottom = item.top + item.wrapperHeight;\n\n const needsUp = top < docState.scrollY + margin + PADDING_Y;\n const needsDown = bottom > docState.scrollY + docState.viewportH - margin;\n\n if (needsUp) {\n this.scrollTo$.emit(id, {\n top: Math.max(0, top - PADDING_Y),\n behavior,\n });\n } else if (needsDown) {\n this.scrollTo$.emit(id, {\n top: Math.max(0, bottom - docState.viewportH + PADDING_Y),\n behavior,\n });\n }\n }\n\n private renderThumb(idx: number, dpr: number, documentId?: string) {\n const id = documentId ?? this.getActiveDocumentId();\n const taskCache = this.taskCaches.get(id);\n if (!taskCache) {\n throw new Error(`Task cache not found for document: ${id}`);\n }\n\n if (taskCache.has(idx)) return taskCache.get(idx)!;\n\n const coreDoc = this.coreState.core.documents[id];\n if (!coreDoc?.document) {\n throw new Error(`Document not found: ${id}`);\n }\n\n const page = coreDoc.document.pages[idx];\n if (!page) {\n throw new Error(`Page ${idx} not found in document: ${id}`);\n }\n\n const OUTER_W = this.cfg.width ?? 120;\n const P = this.cfg.imagePadding ?? 0;\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n const scale = INNER_W / page.size.width;\n\n const task = this.renderCapability.forDocument(id).renderPageRect({\n pageIndex: idx,\n rect: { origin: { x: 0, y: 0 }, size: page.size },\n options: {\n scaleFactor: scale,\n dpr,\n },\n });\n\n taskCache.set(idx, task);\n task.wait(ignore, () => taskCache.delete(idx));\n return task;\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async initialize(): Promise<void> {\n this.logger.info('ThumbnailPlugin', 'Initialize', 'Thumbnail plugin initialized');\n }\n\n async destroy(): Promise<void> {\n this.window$.clear();\n this.refreshPages$.clear();\n this.scrollTo$.clear();\n\n // Cleanup all task caches\n this.taskCaches.forEach((cache) => {\n cache.forEach((task) => {\n task.abort({\n code: 'cancelled' as any,\n message: 'Plugin destroyed',\n });\n });\n cache.clear();\n });\n this.taskCaches.clear();\n this.canAutoScroll.clear();\n\n super.destroy();\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, THUMBNAIL_PLUGIN_ID } from './manifest';\nimport { ThumbnailPluginConfig, ThumbnailState } from './types';\nimport { ThumbnailPlugin } from './thumbnail-plugin';\nimport { thumbnailReducer, initialState } from './reducer';\nimport { ThumbnailAction } from './actions';\n\nexport const ThumbnailPluginPackage: PluginPackage<\n ThumbnailPlugin,\n ThumbnailPluginConfig,\n ThumbnailState,\n ThumbnailAction\n> = {\n manifest,\n create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),\n reducer: thumbnailReducer,\n initialState,\n};\n\nexport * from './thumbnail-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './actions';\nexport * from './reducer';\n"],"names":["top"],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Action } from '@embedpdf/core';
|
|
2
|
+
import { ThumbnailDocumentState, WindowState } from './types';
|
|
3
|
+
export declare const INIT_THUMBNAIL_STATE = "THUMBNAIL/INIT_STATE";
|
|
4
|
+
export declare const CLEANUP_THUMBNAIL_STATE = "THUMBNAIL/CLEANUP_STATE";
|
|
5
|
+
export declare const SET_ACTIVE_DOCUMENT = "THUMBNAIL/SET_ACTIVE_DOCUMENT";
|
|
6
|
+
export declare const SET_WINDOW_STATE = "THUMBNAIL/SET_WINDOW_STATE";
|
|
7
|
+
export declare const UPDATE_VIEWPORT_METRICS = "THUMBNAIL/UPDATE_VIEWPORT_METRICS";
|
|
8
|
+
export interface InitThumbnailStateAction extends Action {
|
|
9
|
+
type: typeof INIT_THUMBNAIL_STATE;
|
|
10
|
+
payload: {
|
|
11
|
+
documentId: string;
|
|
12
|
+
state: ThumbnailDocumentState;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface CleanupThumbnailStateAction extends Action {
|
|
16
|
+
type: typeof CLEANUP_THUMBNAIL_STATE;
|
|
17
|
+
payload: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SetActiveDocumentAction extends Action {
|
|
20
|
+
type: typeof SET_ACTIVE_DOCUMENT;
|
|
21
|
+
payload: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface SetWindowStateAction extends Action {
|
|
24
|
+
type: typeof SET_WINDOW_STATE;
|
|
25
|
+
payload: {
|
|
26
|
+
documentId: string;
|
|
27
|
+
window: WindowState | null;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface UpdateViewportMetricsAction extends Action {
|
|
31
|
+
type: typeof UPDATE_VIEWPORT_METRICS;
|
|
32
|
+
payload: {
|
|
33
|
+
documentId: string;
|
|
34
|
+
scrollY: number;
|
|
35
|
+
viewportH: number;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export type ThumbnailAction = InitThumbnailStateAction | CleanupThumbnailStateAction | SetActiveDocumentAction | SetWindowStateAction | UpdateViewportMetricsAction;
|
|
39
|
+
export declare function initThumbnailState(documentId: string, state: ThumbnailDocumentState): InitThumbnailStateAction;
|
|
40
|
+
export declare function cleanupThumbnailState(documentId: string): CleanupThumbnailStateAction;
|
|
41
|
+
export declare function setActiveDocument(documentId: string | null): SetActiveDocumentAction;
|
|
42
|
+
export declare function setWindowState(documentId: string, window: WindowState | null): SetWindowStateAction;
|
|
43
|
+
export declare function updateViewportMetrics(documentId: string, scrollY: number, viewportH: number): UpdateViewportMetricsAction;
|