@design.estate/dees-catalog 6.12.0 → 7.0.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/.smartconfig.json +5 -1
- package/dist_bundle/bundle.js +24090 -1235
- package/dist_bundle/bundle.js.map +1 -0
- package/dist_bundle/chunks/U6Ua8p1giFoD1s7vrUZUdpbg/.tsbundle-artifacts.json +7 -0
- package/dist_bundle/chunks/U6Ua8p1giFoD1s7vrUZUdpbg/worker-KZQQYSUG.js +58301 -0
- package/dist_bundle/chunks/U6Ua8p1giFoD1s7vrUZUdpbg/worker-KZQQYSUG.js.map +1 -0
- package/dist_bundle/third-party-notices.md +190 -0
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/elements/00group-chart/dees-chart-area/component.js +3 -3
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +29 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.js +65 -8
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +1 -0
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/CanvasPool.d.ts +1 -0
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/CanvasPool.js +15 -1
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/PdfManager.d.ts +6 -4
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/PdfManager.js +183 -21
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/utils.d.ts +3 -0
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/utils.js +67 -1
- package/dist_ts_web/elements/00group-media/dees-pdf-viewer/component.d.ts +25 -1
- package/dist_ts_web/elements/00group-media/dees-pdf-viewer/component.js +693 -285
- package/dist_ts_web/elements/00group-media/dees-pdf-viewer/demo.js +7 -7
- package/dist_ts_web/elements/00group-media/dees-thumbnail-pdf/component.d.ts +20 -1
- package/dist_ts_web/elements/00group-media/dees-thumbnail-pdf/component.js +379 -116
- package/dist_ts_web/elements/00group-media/dees-thumbnail-pdf/demo.js +2 -2
- package/dist_ts_web/elements/00group-media/dees-thumbnail-shared/DeesThumbnailBase.d.ts +1 -0
- package/dist_ts_web/elements/00group-media/dees-thumbnail-shared/DeesThumbnailBase.js +7 -1
- package/package.json +5 -4
- package/readme.hints.md +26 -19
- package/readme.md +104 -53
- package/third-party-notices.md +190 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-chart/dees-chart-area/component.ts +2 -2
- package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +13 -0
- package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.ts +51 -6
- package/ts_web/elements/00group-harness/interfaces.ts +2 -0
- package/ts_web/elements/00group-media/dees-pdf-shared/CanvasPool.ts +15 -1
- package/ts_web/elements/00group-media/dees-pdf-shared/PdfManager.ts +214 -24
- package/ts_web/elements/00group-media/dees-pdf-shared/utils.ts +77 -1
- package/ts_web/elements/00group-media/dees-pdf-viewer/component.ts +774 -327
- package/ts_web/elements/00group-media/dees-pdf-viewer/demo.ts +7 -7
- package/ts_web/elements/00group-media/dees-thumbnail-pdf/component.ts +427 -128
- package/ts_web/elements/00group-media/dees-thumbnail-pdf/demo.ts +1 -1
- package/ts_web/elements/00group-media/dees-thumbnail-shared/DeesThumbnailBase.ts +5 -0
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { property, state, html, customElement, type TemplateResult, type CSSResult } from '@design.estate/dees-element';
|
|
2
|
+
import type { PDFDocumentProxy, RenderTask } from 'pdfjs-dist';
|
|
2
3
|
import { DeesThumbnailBase } from '../dees-thumbnail-shared/DeesThumbnailBase.js';
|
|
3
4
|
import { thumbnailBaseStyles } from '../dees-thumbnail-shared/styles.js';
|
|
4
5
|
import { PdfManager } from '../dees-pdf-shared/PdfManager.js';
|
|
5
6
|
import { CanvasPool, type PooledCanvas } from '../dees-pdf-shared/CanvasPool.js';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
throttle,
|
|
9
|
+
formatFileSize,
|
|
10
|
+
raceWithAbort,
|
|
11
|
+
settlePromisesWithin,
|
|
12
|
+
waitForAnimationFrame,
|
|
13
|
+
} from '../dees-pdf-shared/utils.js';
|
|
7
14
|
import { tilePdfStyles } from './styles.js';
|
|
8
15
|
import { demo as demoFunc } from './demo.js';
|
|
9
16
|
|
|
@@ -13,6 +20,13 @@ declare global {
|
|
|
13
20
|
}
|
|
14
21
|
}
|
|
15
22
|
|
|
23
|
+
interface ILoadOperation {
|
|
24
|
+
key: string;
|
|
25
|
+
promise: Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const pipelineSettlementGraceMs = 2_500;
|
|
29
|
+
|
|
16
30
|
@customElement('dees-thumbnail-pdf')
|
|
17
31
|
export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
18
32
|
public static demo = demoFunc;
|
|
@@ -41,8 +55,21 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
41
55
|
accessor fileSize: number = 0;
|
|
42
56
|
|
|
43
57
|
private renderPagesTask: Promise<void> | null = null;
|
|
58
|
+
private renderPagesRequestPromise: Promise<void> | null = null;
|
|
59
|
+
private currentPageRenderTask: RenderTask | null = null;
|
|
44
60
|
private renderPagesQueued: boolean = false;
|
|
45
|
-
private
|
|
61
|
+
private renderQueueEpoch = 0;
|
|
62
|
+
private pdfDocument: PDFDocumentProxy | null = null;
|
|
63
|
+
private pdfLoadPromise: Promise<PDFDocumentProxy> | null = null;
|
|
64
|
+
private loadAbortController: AbortController | null = null;
|
|
65
|
+
private loadOperation: ILoadOperation | null = null;
|
|
66
|
+
private loadGeneration = 0;
|
|
67
|
+
private connectionEpoch = 0;
|
|
68
|
+
private cleanupPromise: Promise<void> | null = null;
|
|
69
|
+
private renderFailurePromise: Promise<void> | null = null;
|
|
70
|
+
private renderAdmissionOpen = false;
|
|
71
|
+
private renderGeneration = 0;
|
|
72
|
+
private documentUsePipelines = new Set<Promise<void>>();
|
|
46
73
|
private canvases: PooledCanvas[] = [];
|
|
47
74
|
private resizeObserver?: ResizeObserver;
|
|
48
75
|
private stackElement: HTMLElement | null = null;
|
|
@@ -90,8 +117,8 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
90
117
|
}
|
|
91
118
|
|
|
92
119
|
protected onBecameVisible(): void {
|
|
93
|
-
if (!this.rendered && this.pdfUrl) {
|
|
94
|
-
this.
|
|
120
|
+
if (this.isConnected && !this.rendered && this.pdfUrl) {
|
|
121
|
+
void this.schedulePdfLoad(this.connectionEpoch);
|
|
95
122
|
}
|
|
96
123
|
}
|
|
97
124
|
|
|
@@ -103,7 +130,6 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
103
130
|
this.isHovering = false;
|
|
104
131
|
if (this.currentPreviewPage !== 1) {
|
|
105
132
|
this.currentPreviewPage = 1;
|
|
106
|
-
void this.scheduleRenderPages();
|
|
107
133
|
}
|
|
108
134
|
}
|
|
109
135
|
|
|
@@ -119,171 +145,376 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
119
145
|
|
|
120
146
|
if (newPage !== this.currentPreviewPage) {
|
|
121
147
|
this.currentPreviewPage = newPage;
|
|
122
|
-
void this.scheduleRenderPages();
|
|
123
148
|
}
|
|
124
149
|
}
|
|
125
150
|
|
|
126
151
|
public async connectedCallback(): Promise<void> {
|
|
152
|
+
const connectionEpoch = ++this.connectionEpoch;
|
|
127
153
|
await super.connectedCallback();
|
|
128
154
|
await this.updateComplete;
|
|
155
|
+
if (!this.isConnected || connectionEpoch !== this.connectionEpoch) return;
|
|
129
156
|
this.cacheElements();
|
|
130
157
|
this.setupResizeObserver();
|
|
158
|
+
if (
|
|
159
|
+
this.pdfUrl
|
|
160
|
+
&& this.isVisible
|
|
161
|
+
) {
|
|
162
|
+
const operationKey = `${connectionEpoch}:${this.pdfUrl}`;
|
|
163
|
+
if (this.loadOperation?.key === operationKey) {
|
|
164
|
+
await this.loadOperation.promise;
|
|
165
|
+
} else if (connectionEpoch > 1 || !this.pdfDocument) {
|
|
166
|
+
await this.schedulePdfLoad(connectionEpoch);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
131
169
|
}
|
|
132
170
|
|
|
133
171
|
public async disconnectedCallback(): Promise<void> {
|
|
172
|
+
this.connectionEpoch += 1;
|
|
173
|
+
this.loadGeneration += 1;
|
|
174
|
+
this.renderAdmissionOpen = false;
|
|
175
|
+
this.loadAbortController?.abort();
|
|
176
|
+
const cleanupPromise = this.cleanup();
|
|
134
177
|
await super.disconnectedCallback();
|
|
135
|
-
|
|
136
|
-
this.resizeObserver?.disconnect();
|
|
137
|
-
this.resizeObserver = undefined;
|
|
178
|
+
await cleanupPromise;
|
|
138
179
|
}
|
|
139
180
|
|
|
140
|
-
private
|
|
141
|
-
|
|
181
|
+
private schedulePdfLoad(connectionEpochArg: number): Promise<void> {
|
|
182
|
+
const url = this.pdfUrl;
|
|
183
|
+
const key = `${connectionEpochArg}:${url}`;
|
|
184
|
+
if (this.loadOperation?.key === key) {
|
|
185
|
+
return this.loadOperation.promise;
|
|
186
|
+
}
|
|
187
|
+
const generation = ++this.loadGeneration;
|
|
188
|
+
const operation: ILoadOperation = {
|
|
189
|
+
key,
|
|
190
|
+
promise: Promise.resolve(),
|
|
191
|
+
};
|
|
192
|
+
operation.promise = this.performPdfLoad(generation, connectionEpochArg, url)
|
|
193
|
+
.finally(() => {
|
|
194
|
+
if (this.loadOperation === operation) {
|
|
195
|
+
this.loadOperation = null;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
this.loadOperation = operation;
|
|
199
|
+
return operation.promise;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private isCurrentLoad(generationArg: number, connectionEpochArg: number, urlArg: string): boolean {
|
|
203
|
+
return this.isConnected
|
|
204
|
+
&& generationArg === this.loadGeneration
|
|
205
|
+
&& connectionEpochArg === this.connectionEpoch
|
|
206
|
+
&& urlArg === this.pdfUrl;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private async performPdfLoad(
|
|
210
|
+
generationArg: number,
|
|
211
|
+
connectionEpochArg: number,
|
|
212
|
+
urlArg: string,
|
|
213
|
+
): Promise<void> {
|
|
214
|
+
await this.cleanup();
|
|
215
|
+
if (
|
|
216
|
+
!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)
|
|
217
|
+
|| !urlArg
|
|
218
|
+
|| !this.isVisible
|
|
219
|
+
) return;
|
|
142
220
|
|
|
143
221
|
this.loading = true;
|
|
144
222
|
this.error = false;
|
|
145
|
-
|
|
223
|
+
const loadStartedAt = performance.now();
|
|
224
|
+
const abortController = new AbortController();
|
|
225
|
+
this.loadAbortController = abortController;
|
|
226
|
+
const { signal } = abortController;
|
|
146
227
|
|
|
147
228
|
try {
|
|
148
|
-
|
|
149
|
-
this.
|
|
150
|
-
|
|
151
|
-
|
|
229
|
+
const pdfLoadPromise = PdfManager.loadDocument(urlArg, signal);
|
|
230
|
+
this.pdfLoadPromise = pdfLoadPromise;
|
|
231
|
+
let pdfDocument: PDFDocumentProxy;
|
|
152
232
|
try {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
233
|
+
pdfDocument = await pdfLoadPromise;
|
|
234
|
+
} finally {
|
|
235
|
+
if (this.pdfLoadPromise === pdfLoadPromise) {
|
|
236
|
+
this.pdfLoadPromise = null;
|
|
237
|
+
}
|
|
157
238
|
}
|
|
158
|
-
this.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
this.
|
|
239
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) || signal.aborted) {
|
|
240
|
+
await PdfManager.releaseDocument(pdfDocument);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
this.pdfDocument = pdfDocument;
|
|
244
|
+
this.renderAdmissionOpen = true;
|
|
245
|
+
const documentUsePipeline = (async () => {
|
|
246
|
+
this.pageCount = pdfDocument.numPages;
|
|
247
|
+
this.currentPreviewPage = 1;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const downloadInfo = await raceWithAbort(pdfDocument.getDownloadInfo(), signal);
|
|
251
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
252
|
+
this.fileSize = downloadInfo.length;
|
|
253
|
+
} catch {
|
|
254
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
255
|
+
}
|
|
256
|
+
this.loadedPdfUrl = urlArg;
|
|
257
|
+
|
|
258
|
+
this.loading = false;
|
|
259
|
+
await this.updateComplete;
|
|
260
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
261
|
+
this.cacheElements();
|
|
262
|
+
this.setupResizeObserver();
|
|
263
|
+
|
|
264
|
+
await this.scheduleRenderPages();
|
|
265
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
266
|
+
this.rendered = true;
|
|
267
|
+
|
|
268
|
+
const duration = performance.now() - loadStartedAt;
|
|
269
|
+
console.log(`PDF tile rendered in ${duration}ms`);
|
|
270
|
+
})();
|
|
271
|
+
this.documentUsePipelines.add(documentUsePipeline);
|
|
272
|
+
try {
|
|
273
|
+
await documentUsePipeline;
|
|
274
|
+
} finally {
|
|
275
|
+
this.documentUsePipelines.delete(documentUsePipeline);
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) && !signal.aborted) {
|
|
279
|
+
await this.handleDocumentFailure(
|
|
280
|
+
error,
|
|
281
|
+
generationArg,
|
|
282
|
+
connectionEpochArg,
|
|
283
|
+
urlArg,
|
|
284
|
+
this.pdfDocument,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
163
289
|
|
|
164
|
-
|
|
165
|
-
|
|
290
|
+
private requestRenderPages(): void {
|
|
291
|
+
const requestPromise = this.scheduleRenderPages();
|
|
292
|
+
if (this.renderPagesRequestPromise === requestPromise) return;
|
|
293
|
+
this.renderPagesRequestPromise = requestPromise;
|
|
294
|
+
const generation = this.loadGeneration;
|
|
295
|
+
const connectionEpoch = this.connectionEpoch;
|
|
296
|
+
const url = this.pdfUrl;
|
|
297
|
+
const pdfDocument = this.pdfDocument;
|
|
298
|
+
void requestPromise.catch(async (error) => {
|
|
299
|
+
await this.handleDocumentFailure(
|
|
300
|
+
error,
|
|
301
|
+
generation,
|
|
302
|
+
connectionEpoch,
|
|
303
|
+
url,
|
|
304
|
+
pdfDocument,
|
|
305
|
+
);
|
|
306
|
+
}).finally(() => {
|
|
307
|
+
if (this.renderPagesRequestPromise === requestPromise) {
|
|
308
|
+
this.renderPagesRequestPromise = null;
|
|
309
|
+
}
|
|
310
|
+
}).catch(() => {});
|
|
311
|
+
}
|
|
166
312
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
313
|
+
private handleDocumentFailure(
|
|
314
|
+
errorArg: unknown,
|
|
315
|
+
generationArg: number,
|
|
316
|
+
connectionEpochArg: number,
|
|
317
|
+
urlArg: string,
|
|
318
|
+
pdfDocumentArg: PDFDocumentProxy | null,
|
|
319
|
+
): Promise<void> {
|
|
320
|
+
if (
|
|
321
|
+
errorArg instanceof Error
|
|
322
|
+
&& ['AbortError', 'AbortException', 'RenderingCancelledException'].includes(errorArg.name)
|
|
323
|
+
) {
|
|
324
|
+
return Promise.resolve();
|
|
173
325
|
}
|
|
326
|
+
if (
|
|
327
|
+
!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)
|
|
328
|
+
|| (pdfDocumentArg && pdfDocumentArg !== this.pdfDocument)
|
|
329
|
+
) {
|
|
330
|
+
return Promise.resolve();
|
|
331
|
+
}
|
|
332
|
+
if (this.renderFailurePromise) return this.renderFailurePromise;
|
|
333
|
+
|
|
334
|
+
this.renderAdmissionOpen = false;
|
|
335
|
+
this.loadAbortController?.abort();
|
|
336
|
+
console.error('Failed to load or render PDF thumbnail:', errorArg);
|
|
337
|
+
const failurePromise = (async () => {
|
|
338
|
+
await this.cleanup();
|
|
339
|
+
if (
|
|
340
|
+
this.isConnected
|
|
341
|
+
&& generationArg === this.loadGeneration
|
|
342
|
+
&& connectionEpochArg === this.connectionEpoch
|
|
343
|
+
&& urlArg === this.pdfUrl
|
|
344
|
+
) {
|
|
345
|
+
this.error = true;
|
|
346
|
+
this.loading = false;
|
|
347
|
+
}
|
|
348
|
+
})();
|
|
349
|
+
this.renderFailurePromise = failurePromise;
|
|
350
|
+
void failurePromise.finally(() => {
|
|
351
|
+
if (this.renderFailurePromise === failurePromise) {
|
|
352
|
+
this.renderFailurePromise = null;
|
|
353
|
+
}
|
|
354
|
+
}).catch(() => {});
|
|
355
|
+
return failurePromise;
|
|
174
356
|
}
|
|
175
357
|
|
|
176
358
|
private scheduleRenderPages(): Promise<void> {
|
|
177
|
-
if (!this.pdfDocument) {
|
|
359
|
+
if (!this.renderAdmissionOpen || !this.pdfDocument) {
|
|
178
360
|
return Promise.resolve();
|
|
179
361
|
}
|
|
180
362
|
|
|
181
363
|
if (this.renderPagesTask) {
|
|
364
|
+
this.renderGeneration += 1;
|
|
182
365
|
this.renderPagesQueued = true;
|
|
366
|
+
try {
|
|
367
|
+
this.currentPageRenderTask?.cancel();
|
|
368
|
+
} catch {
|
|
369
|
+
// The active task remains part of the render queue settlement.
|
|
370
|
+
}
|
|
183
371
|
return this.renderPagesTask;
|
|
184
372
|
}
|
|
185
373
|
|
|
186
|
-
this.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
})().finally(() => {
|
|
193
|
-
this.renderPagesTask = null;
|
|
194
|
-
if (this.renderPagesQueued) {
|
|
374
|
+
this.renderGeneration += 1;
|
|
375
|
+
const renderQueueEpoch = ++this.renderQueueEpoch;
|
|
376
|
+
const renderPagesTask = (async () => {
|
|
377
|
+
do {
|
|
378
|
+
if (renderQueueEpoch !== this.renderQueueEpoch) return;
|
|
195
379
|
this.renderPagesQueued = false;
|
|
196
|
-
|
|
380
|
+
const renderGeneration = this.renderGeneration;
|
|
381
|
+
await this.performRenderPages(renderGeneration);
|
|
382
|
+
} while (
|
|
383
|
+
renderQueueEpoch === this.renderQueueEpoch
|
|
384
|
+
&& this.renderPagesQueued
|
|
385
|
+
&& this.renderAdmissionOpen
|
|
386
|
+
&& this.pdfDocument
|
|
387
|
+
);
|
|
388
|
+
})();
|
|
389
|
+
this.renderPagesTask = renderPagesTask;
|
|
390
|
+
void renderPagesTask.finally(() => {
|
|
391
|
+
if (this.renderPagesTask === renderPagesTask) {
|
|
392
|
+
this.renderPagesTask = null;
|
|
197
393
|
}
|
|
198
|
-
});
|
|
394
|
+
}).catch(() => {});
|
|
199
395
|
|
|
200
|
-
return
|
|
396
|
+
return renderPagesTask;
|
|
201
397
|
}
|
|
202
398
|
|
|
203
|
-
private async performRenderPages(): Promise<void> {
|
|
204
|
-
|
|
399
|
+
private async performRenderPages(renderGenerationArg: number): Promise<void> {
|
|
400
|
+
const pdfDocument = this.pdfDocument;
|
|
401
|
+
const signal = this.loadAbortController?.signal;
|
|
402
|
+
if (!this.renderAdmissionOpen || !pdfDocument || !signal) return;
|
|
403
|
+
|
|
404
|
+
const isCurrentRender = (): boolean => (
|
|
405
|
+
this.isConnected
|
|
406
|
+
&& this.renderAdmissionOpen
|
|
407
|
+
&& !signal.aborted
|
|
408
|
+
&& renderGenerationArg === this.renderGeneration
|
|
409
|
+
&& pdfDocument === this.pdfDocument
|
|
410
|
+
);
|
|
205
411
|
|
|
206
|
-
await
|
|
412
|
+
await waitForAnimationFrame();
|
|
413
|
+
if (!isCurrentRender()) return;
|
|
207
414
|
|
|
208
415
|
const canvas = this.shadowRoot?.querySelector('.preview-canvas') as HTMLCanvasElement;
|
|
209
|
-
if (!canvas)
|
|
416
|
+
if (!canvas) throw new Error('PDF thumbnail preview canvas is unavailable');
|
|
210
417
|
|
|
211
418
|
this.clearCanvases();
|
|
212
419
|
this.cacheElements();
|
|
213
420
|
|
|
214
421
|
const { availableWidth, availableHeight } = this.getAvailableSize();
|
|
215
422
|
|
|
423
|
+
let pooledCanvas: PooledCanvas | undefined;
|
|
424
|
+
let pooledCanvasRenderSettled = true;
|
|
216
425
|
try {
|
|
217
426
|
const pageNum = this.currentPreviewPage;
|
|
218
|
-
const page = await
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const a4PortraitRatio = 1.414;
|
|
224
|
-
const a4LandscapeRatio = 0.707;
|
|
225
|
-
const letterPortraitRatio = 1.294;
|
|
226
|
-
const letterLandscapeRatio = 0.773;
|
|
227
|
-
const tolerance = 0.05;
|
|
228
|
-
|
|
229
|
-
const isA4Portrait = Math.abs(aspectRatio - a4PortraitRatio) < (a4PortraitRatio * tolerance);
|
|
230
|
-
const isA4Landscape = Math.abs(aspectRatio - a4LandscapeRatio) < (a4LandscapeRatio * tolerance);
|
|
231
|
-
const isLetterPortrait = Math.abs(aspectRatio - letterPortraitRatio) < (letterPortraitRatio * tolerance);
|
|
232
|
-
const isLetterLandscape = Math.abs(aspectRatio - letterLandscapeRatio) < (letterLandscapeRatio * tolerance);
|
|
233
|
-
|
|
234
|
-
this.isA4Format = isA4Portrait || isA4Landscape || isLetterPortrait || isLetterLandscape;
|
|
235
|
-
|
|
236
|
-
const adjustedWidth = this.isA4Format ? availableWidth : availableWidth - 24;
|
|
237
|
-
const adjustedHeight = this.isA4Format ? availableHeight : availableHeight - 24;
|
|
238
|
-
|
|
239
|
-
const scaleX = adjustedWidth > 0 ? adjustedWidth / initialViewport.width : 0;
|
|
240
|
-
const scaleY = adjustedHeight > 0 ? adjustedHeight / initialViewport.height : 0;
|
|
241
|
-
const baseScale = Math.min(scaleX || 0.5, scaleY || scaleX || 0.5);
|
|
242
|
-
const renderScale = Math.min(baseScale * 2, 3.0);
|
|
243
|
-
|
|
244
|
-
if (!Number.isFinite(renderScale) || renderScale <= 0) {
|
|
245
|
-
page.cleanup?.();
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
427
|
+
const page = await raceWithAbort(pdfDocument.getPage(pageNum), signal);
|
|
428
|
+
try {
|
|
429
|
+
if (!isCurrentRender()) return;
|
|
430
|
+
const initialViewport = page.getViewport({ scale: 1 });
|
|
431
|
+
const aspectRatio = initialViewport.height / initialViewport.width;
|
|
248
432
|
|
|
249
|
-
|
|
433
|
+
const a4PortraitRatio = 1.414;
|
|
434
|
+
const a4LandscapeRatio = 0.707;
|
|
435
|
+
const letterPortraitRatio = 1.294;
|
|
436
|
+
const letterLandscapeRatio = 0.773;
|
|
437
|
+
const tolerance = 0.05;
|
|
250
438
|
|
|
251
|
-
|
|
252
|
-
|
|
439
|
+
const isA4Portrait = Math.abs(aspectRatio - a4PortraitRatio) < (a4PortraitRatio * tolerance);
|
|
440
|
+
const isA4Landscape = Math.abs(aspectRatio - a4LandscapeRatio) < (a4LandscapeRatio * tolerance);
|
|
441
|
+
const isLetterPortrait = Math.abs(aspectRatio - letterPortraitRatio) < (letterPortraitRatio * tolerance);
|
|
442
|
+
const isLetterLandscape = Math.abs(aspectRatio - letterLandscapeRatio) < (letterLandscapeRatio * tolerance);
|
|
253
443
|
|
|
254
|
-
|
|
255
|
-
canvasContext: pooledCanvas.ctx,
|
|
256
|
-
viewport: viewport,
|
|
257
|
-
};
|
|
444
|
+
this.isA4Format = isA4Portrait || isA4Landscape || isLetterPortrait || isLetterLandscape;
|
|
258
445
|
|
|
259
|
-
|
|
446
|
+
const adjustedWidth = this.isA4Format ? availableWidth : availableWidth - 24;
|
|
447
|
+
const adjustedHeight = this.isA4Format ? availableHeight : availableHeight - 24;
|
|
260
448
|
|
|
261
|
-
|
|
262
|
-
|
|
449
|
+
const scaleX = adjustedWidth > 0 ? adjustedWidth / initialViewport.width : 0;
|
|
450
|
+
const scaleY = adjustedHeight > 0 ? adjustedHeight / initialViewport.height : 0;
|
|
451
|
+
const baseScale = Math.min(scaleX || 0.5, scaleY || scaleX || 0.5);
|
|
452
|
+
const renderScale = Math.min(baseScale * 2, 3.0);
|
|
263
453
|
|
|
264
|
-
|
|
265
|
-
|
|
454
|
+
if (!Number.isFinite(renderScale) || renderScale <= 0) {
|
|
455
|
+
throw new Error('PDF thumbnail render scale is invalid');
|
|
456
|
+
}
|
|
266
457
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
458
|
+
const viewport = page.getViewport({ scale: renderScale });
|
|
459
|
+
pooledCanvas = CanvasPool.acquire(viewport.width, viewport.height);
|
|
460
|
+
this.canvases.push(pooledCanvas);
|
|
461
|
+
|
|
462
|
+
const renderTask = page.render({
|
|
463
|
+
canvasContext: pooledCanvas.ctx,
|
|
464
|
+
viewport,
|
|
465
|
+
});
|
|
466
|
+
this.currentPageRenderTask = renderTask;
|
|
467
|
+
pooledCanvasRenderSettled = false;
|
|
468
|
+
const trackedRenderPromise = renderTask.promise.finally(() => {
|
|
469
|
+
pooledCanvasRenderSettled = true;
|
|
470
|
+
});
|
|
471
|
+
try {
|
|
472
|
+
await raceWithAbort(trackedRenderPromise, signal);
|
|
473
|
+
} finally {
|
|
474
|
+
if (this.currentPageRenderTask === renderTask) {
|
|
475
|
+
this.currentPageRenderTask = null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!isCurrentRender()) return;
|
|
479
|
+
|
|
480
|
+
canvas.width = viewport.width;
|
|
481
|
+
canvas.height = viewport.height;
|
|
482
|
+
|
|
483
|
+
const displayWidth = adjustedWidth;
|
|
484
|
+
const displayHeight = (viewport.height / viewport.width) * adjustedWidth;
|
|
485
|
+
|
|
486
|
+
if (displayHeight > adjustedHeight) {
|
|
487
|
+
const altDisplayHeight = adjustedHeight;
|
|
488
|
+
const altDisplayWidth = (viewport.width / viewport.height) * adjustedHeight;
|
|
489
|
+
canvas.style.width = `${altDisplayWidth}px`;
|
|
490
|
+
canvas.style.height = `${altDisplayHeight}px`;
|
|
491
|
+
} else {
|
|
492
|
+
canvas.style.width = `${displayWidth}px`;
|
|
493
|
+
canvas.style.height = `${displayHeight}px`;
|
|
494
|
+
}
|
|
276
495
|
|
|
277
|
-
|
|
278
|
-
|
|
496
|
+
const ctx = canvas.getContext('2d');
|
|
497
|
+
if (!ctx) throw new Error('PDF thumbnail preview context is unavailable');
|
|
279
498
|
ctx.imageSmoothingEnabled = true;
|
|
280
499
|
ctx.imageSmoothingQuality = 'high';
|
|
281
500
|
ctx.drawImage(pooledCanvas.canvas, 0, 0);
|
|
501
|
+
} finally {
|
|
502
|
+
page.cleanup();
|
|
503
|
+
}
|
|
504
|
+
} catch (error: unknown) {
|
|
505
|
+
if (
|
|
506
|
+
error instanceof Error
|
|
507
|
+
&& ['AbortError', 'AbortException', 'RenderingCancelledException'].includes(error.name)
|
|
508
|
+
) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
throw error;
|
|
512
|
+
} finally {
|
|
513
|
+
if (pooledCanvas && pooledCanvasRenderSettled) {
|
|
514
|
+
const canvasIndex = this.canvases.indexOf(pooledCanvas);
|
|
515
|
+
if (canvasIndex >= 0) this.canvases.splice(canvasIndex, 1);
|
|
516
|
+
CanvasPool.release(pooledCanvas);
|
|
282
517
|
}
|
|
283
|
-
|
|
284
|
-
page.cleanup();
|
|
285
|
-
} catch (error) {
|
|
286
|
-
console.error(`Failed to render page ${this.currentPreviewPage}:`, error);
|
|
287
518
|
}
|
|
288
519
|
}
|
|
289
520
|
|
|
@@ -294,15 +525,89 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
294
525
|
this.canvases = [];
|
|
295
526
|
}
|
|
296
527
|
|
|
297
|
-
private
|
|
298
|
-
this.
|
|
528
|
+
private discardCanvases(): void {
|
|
529
|
+
for (const pooledCanvas of this.canvases) {
|
|
530
|
+
CanvasPool.discard(pooledCanvas);
|
|
531
|
+
}
|
|
532
|
+
this.canvases = [];
|
|
533
|
+
}
|
|
299
534
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
this.
|
|
535
|
+
private cleanup(): Promise<void> {
|
|
536
|
+
if (this.cleanupPromise) {
|
|
537
|
+
return this.cleanupPromise;
|
|
303
538
|
}
|
|
539
|
+
const cleanupPromise = this.performCleanup();
|
|
540
|
+
this.cleanupPromise = cleanupPromise;
|
|
541
|
+
void cleanupPromise.finally(() => {
|
|
542
|
+
if (this.cleanupPromise === cleanupPromise) {
|
|
543
|
+
this.cleanupPromise = null;
|
|
544
|
+
}
|
|
545
|
+
}).catch(() => {});
|
|
546
|
+
return cleanupPromise;
|
|
547
|
+
}
|
|
304
548
|
|
|
549
|
+
private async performCleanup(): Promise<void> {
|
|
550
|
+
this.renderAdmissionOpen = false;
|
|
551
|
+
this.renderGeneration += 1;
|
|
552
|
+
this.renderQueueEpoch += 1;
|
|
553
|
+
this.resizeObserver?.disconnect();
|
|
554
|
+
this.resizeObserver = undefined;
|
|
555
|
+
const abortController = this.loadAbortController;
|
|
556
|
+
this.loadAbortController = null;
|
|
557
|
+
abortController?.abort();
|
|
558
|
+
const loadOperation = this.loadOperation;
|
|
559
|
+
const pdfLoadPromise = this.pdfLoadPromise;
|
|
560
|
+
const renderPagesTask = this.renderPagesTask;
|
|
561
|
+
const currentPageRenderTask = this.currentPageRenderTask;
|
|
562
|
+
const documentUsePipelines = [...this.documentUsePipelines];
|
|
305
563
|
this.renderPagesQueued = false;
|
|
564
|
+
try {
|
|
565
|
+
currentPageRenderTask?.cancel();
|
|
566
|
+
} catch {
|
|
567
|
+
// The tracked render promise still participates in settlement below.
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const pdfDocument = this.pdfDocument;
|
|
571
|
+
this.pdfDocument = null;
|
|
572
|
+
const releasePromise = pdfDocument
|
|
573
|
+
? PdfManager.releaseDocument(pdfDocument).catch((error) => {
|
|
574
|
+
console.error('Failed to release PDF tile document:', error);
|
|
575
|
+
})
|
|
576
|
+
: null;
|
|
577
|
+
|
|
578
|
+
const pipelinesSettled = await settlePromisesWithin([
|
|
579
|
+
...(pdfLoadPromise ? [pdfLoadPromise] : []),
|
|
580
|
+
...documentUsePipelines,
|
|
581
|
+
...(renderPagesTask ? [renderPagesTask] : []),
|
|
582
|
+
...(currentPageRenderTask ? [currentPageRenderTask.promise] : []),
|
|
583
|
+
...(releasePromise ? [releasePromise] : []),
|
|
584
|
+
], pipelineSettlementGraceMs);
|
|
585
|
+
if (this.pdfLoadPromise === pdfLoadPromise) {
|
|
586
|
+
this.pdfLoadPromise = null;
|
|
587
|
+
}
|
|
588
|
+
if (this.loadOperation === loadOperation) {
|
|
589
|
+
this.loadOperation = null;
|
|
590
|
+
}
|
|
591
|
+
if (this.renderPagesTask === renderPagesTask) {
|
|
592
|
+
this.renderPagesTask = null;
|
|
593
|
+
}
|
|
594
|
+
if (this.currentPageRenderTask === currentPageRenderTask) {
|
|
595
|
+
this.currentPageRenderTask = null;
|
|
596
|
+
}
|
|
597
|
+
this.documentUsePipelines.clear();
|
|
598
|
+
|
|
599
|
+
if (pipelinesSettled) {
|
|
600
|
+
this.clearCanvases();
|
|
601
|
+
} else {
|
|
602
|
+
this.discardCanvases();
|
|
603
|
+
}
|
|
604
|
+
const previewCanvas = this.shadowRoot?.querySelector<HTMLCanvasElement>('.preview-canvas');
|
|
605
|
+
if (previewCanvas) {
|
|
606
|
+
previewCanvas.width = 0;
|
|
607
|
+
previewCanvas.height = 0;
|
|
608
|
+
previewCanvas.style.removeProperty('width');
|
|
609
|
+
previewCanvas.style.removeProperty('height');
|
|
610
|
+
}
|
|
306
611
|
this.pageCount = 0;
|
|
307
612
|
this.currentPreviewPage = 1;
|
|
308
613
|
this.isHovering = false;
|
|
@@ -312,28 +617,22 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
312
617
|
this.rendered = false;
|
|
313
618
|
this.loading = false;
|
|
314
619
|
this.error = false;
|
|
620
|
+
this.fileSize = 0;
|
|
315
621
|
}
|
|
316
622
|
|
|
317
623
|
public async updated(changedProperties: Map<PropertyKey, unknown>): Promise<void> {
|
|
318
624
|
super.updated(changedProperties);
|
|
319
625
|
|
|
320
|
-
if (changedProperties.has('pdfUrl') && this.
|
|
321
|
-
|
|
322
|
-
if (previousUrl) {
|
|
323
|
-
PdfManager.releaseDocument(previousUrl);
|
|
324
|
-
}
|
|
325
|
-
this.cleanup();
|
|
326
|
-
this.rendered = false;
|
|
327
|
-
this.currentPreviewPage = 1;
|
|
328
|
-
|
|
329
|
-
const rect = this.getBoundingClientRect();
|
|
330
|
-
if (rect.top < window.innerHeight && rect.bottom > 0) {
|
|
331
|
-
this.loadAndRenderPreview();
|
|
332
|
-
}
|
|
626
|
+
if (changedProperties.has('pdfUrl') && this.isConnected) {
|
|
627
|
+
await this.schedulePdfLoad(this.connectionEpoch);
|
|
333
628
|
}
|
|
334
629
|
|
|
335
|
-
if (
|
|
336
|
-
|
|
630
|
+
if (
|
|
631
|
+
changedProperties.has('currentPreviewPage')
|
|
632
|
+
&& this.renderAdmissionOpen
|
|
633
|
+
&& this.rendered
|
|
634
|
+
) {
|
|
635
|
+
this.requestRenderPages();
|
|
337
636
|
}
|
|
338
637
|
}
|
|
339
638
|
|
|
@@ -409,7 +708,7 @@ export class DeesThumbnailPdf extends DeesThumbnailBase {
|
|
|
409
708
|
|
|
410
709
|
this.resizeObserver = new ResizeObserver(() => {
|
|
411
710
|
if (this.rendered && this.pdfDocument && !this.loading) {
|
|
412
|
-
|
|
711
|
+
this.requestRenderPages();
|
|
413
712
|
}
|
|
414
713
|
});
|
|
415
714
|
|