@embedpdf/plugin-thumbnail 1.5.0 → 2.0.0-next.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.
Files changed (36) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +316 -84
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/actions.d.ts +43 -0
  6. package/dist/lib/index.d.ts +5 -2
  7. package/dist/lib/reducer.d.ts +6 -0
  8. package/dist/lib/thumbnail-plugin.d.ts +19 -17
  9. package/dist/lib/types.d.ts +38 -2
  10. package/dist/preact/index.cjs +1 -1
  11. package/dist/preact/index.cjs.map +1 -1
  12. package/dist/preact/index.js +39 -18
  13. package/dist/preact/index.js.map +1 -1
  14. package/dist/react/index.cjs +1 -1
  15. package/dist/react/index.cjs.map +1 -1
  16. package/dist/react/index.js +39 -18
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/shared/components/thumbnail-img.d.ts +5 -1
  19. package/dist/shared/components/thumbnails-pane.d.ts +6 -7
  20. package/dist/shared-preact/components/thumbnail-img.d.ts +5 -1
  21. package/dist/shared-preact/components/thumbnails-pane.d.ts +6 -7
  22. package/dist/shared-react/components/thumbnail-img.d.ts +5 -1
  23. package/dist/shared-react/components/thumbnails-pane.d.ts +6 -7
  24. package/dist/svelte/components/ThumbImg.svelte.d.ts +4 -0
  25. package/dist/svelte/components/ThumbnailsPane.svelte.d.ts +4 -0
  26. package/dist/svelte/index.cjs +1 -1
  27. package/dist/svelte/index.cjs.map +1 -1
  28. package/dist/svelte/index.js +44 -25
  29. package/dist/svelte/index.js.map +1 -1
  30. package/dist/vue/components/thumbnail-img.vue.d.ts +8 -3
  31. package/dist/vue/components/thumbnails-pane.vue.d.ts +9 -2
  32. package/dist/vue/index.cjs +1 -1
  33. package/dist/vue/index.cjs.map +1 -1
  34. package/dist/vue/index.js +135 -79
  35. package/dist/vue/index.js.map +1 -1
  36. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BasePlugin, createBehaviorEmitter, createEmitter, SET_DOCUMENT, REFRESH_PAGES } from "@embedpdf/core";
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.thumbs = [];
30
- this.window = null;
31
- this.viewportH = 0;
32
- this.scrollY = 0;
33
- this.emitWindow = createBehaviorEmitter();
34
- this.refreshPages$ = createEmitter();
35
- this.taskCache = /* @__PURE__ */ new Map();
36
- this.canAutoScroll = true;
37
- this.scrollTo$ = createBehaviorEmitter();
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.refreshPages$.emit(action.payload);
46
- for (const pageIdx of action.payload) {
47
- this.taskCache.delete(pageIdx);
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(({ isChanging, targetPage }) => {
52
- this.canAutoScroll = !isChanging;
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
- /* ------------ init ------------------------------------------------ */
65
- async initialize() {
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
- onRefreshPages(fn) {
68
- return this.refreshPages$.on(fn);
179
+ onDocumentLoaded(documentId) {
180
+ this.calculateWindowState(documentId);
69
181
  }
70
- onWindow(cb) {
71
- return this.emitWindow.on(cb);
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
- onScrollTo(cb) {
74
- return this.scrollTo$.on(cb);
205
+ onRotationChanged(documentId) {
206
+ this.calculateWindowState(documentId);
75
207
  }
76
- setWindowState(state) {
77
- const core = state.core;
78
- if (!core.document) return;
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
- this.thumbs = core.document.pages.map((p) => {
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
- this.window = {
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
- if (this.viewportH > 0) {
115
- this.updateWindow(this.scrollY, this.viewportH);
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.emitWindow.emit(this.window);
299
+ this.window$.emit(documentId, window);
118
300
  }
119
301
  }
120
- /* ------------ capability ----------------------------------------- */
121
- buildCapability() {
122
- return {
123
- renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),
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 = scrollY;
131
- this.viewportH = viewportH;
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 = this.thumbs[mid];
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 < this.thumbs.length && this.thumbs[last].top < limit) last++;
146
- last = Math.min(this.thumbs.length - 1, last + BUF);
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 === this.window.start && last === this.window.end) return;
149
- this.window = {
323
+ if (start === docState.window.start && last === docState.window.end) return;
324
+ const newWindow = {
150
325
  start,
151
326
  end: last,
152
- items: this.thumbs.slice(start, last + 1),
153
- totalHeight: this.window.totalHeight
327
+ items: docState.thumbs.slice(start, last + 1),
328
+ totalHeight: docState.window.totalHeight
154
329
  };
155
- this.emitWindow.emit(this.window);
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
- /* ------------ scroll helper now in plugin ------------------------ */
158
- scrollToThumb(pageIdx) {
159
- if (!this.window) return;
160
- const item = this.thumbs[pageIdx];
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 (this.viewportH <= 0) {
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 < this.scrollY + margin + PADDING_Y;
173
- const needsDown = bottom > this.scrollY + this.viewportH - margin;
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({ top: Math.max(0, top - PADDING_Y), behavior });
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({ top: Math.max(0, bottom - this.viewportH + PADDING_Y), behavior });
361
+ this.scrollTo$.emit(id, {
362
+ top: Math.max(0, bottom - docState.viewportH + PADDING_Y),
363
+ behavior
364
+ });
178
365
  }
179
366
  }
180
- /* ------------ thumbnail raster ----------------------------------- */
181
- renderThumb(idx, dpr) {
182
- if (this.taskCache.has(idx)) return this.taskCache.get(idx);
183
- const core = this.coreState.core;
184
- const page = core.document.pages[idx];
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
- this.taskCache.set(idx, task);
198
- task.wait(ignore, () => this.taskCache.delete(idx));
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
- manifest
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;