@netless/app-presentation 0.1.13-beta.4 → 0.1.13
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.d.ts +11 -1
- package/dist/index.global.js +45 -35
- package/dist/index.js +45 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/app-presentation.ts +33 -41
- package/src/presentation.ts +8 -1
package/src/app-presentation.ts
CHANGED
|
@@ -263,12 +263,11 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
263
263
|
const { context, view, pages, pageIndex$, app, dispose } = config;
|
|
264
264
|
const box = context.getBox();
|
|
265
265
|
const boxBlurClass = box.wrapClassName("blur");
|
|
266
|
-
//
|
|
267
|
-
// without a thumbnail don't end it, so a later page degrades again), while
|
|
268
|
-
// `reDegradeEnabled` only turns off when the degraded thumbnail itself
|
|
269
|
-
// fails, so we don't fight the SDK's background onError retry.
|
|
266
|
+
// A failed thumbnail is pinned only for its page during this blur episode.
|
|
270
267
|
let blurEpisode = false;
|
|
271
|
-
let
|
|
268
|
+
let failedThumbnailPageIndex: number | null = null;
|
|
269
|
+
const canReDegrade = (): boolean =>
|
|
270
|
+
blurEpisode && failedThumbnailPageIndex !== pageIndex$.value;
|
|
272
271
|
// Duplicate focus(false) deliveries are unavoidable (multiple sources in
|
|
273
272
|
// the host). Preview-resource clearing is the only non-idempotent DOM op
|
|
274
273
|
// in the blur path, so gate it on this flag until focus restores it.
|
|
@@ -340,7 +339,7 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
340
339
|
app.suspendPreviewResources();
|
|
341
340
|
}
|
|
342
341
|
const page = pages[pageIndex$.value];
|
|
343
|
-
if (!page || !hasUsableThumbnail(page) ||
|
|
342
|
+
if (!page || !hasUsableThumbnail(page) || canReDegrade()) {
|
|
344
343
|
degrade();
|
|
345
344
|
}
|
|
346
345
|
};
|
|
@@ -348,7 +347,7 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
348
347
|
const restoreFocusedResources = (): void => {
|
|
349
348
|
if (!blurEpisode) return;
|
|
350
349
|
blurEpisode = false;
|
|
351
|
-
|
|
350
|
+
failedThumbnailPageIndex = null;
|
|
352
351
|
previewSuspended = false;
|
|
353
352
|
restore();
|
|
354
353
|
app.preload.resume(pageIndex$.value);
|
|
@@ -374,18 +373,23 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
374
373
|
};
|
|
375
374
|
|
|
376
375
|
const onImgLoad = (ev: Event): void => {
|
|
377
|
-
if (!
|
|
376
|
+
if (!canReDegrade()) return;
|
|
378
377
|
if (!ownsTarget(ev.target)) return;
|
|
379
378
|
degrade();
|
|
380
379
|
};
|
|
381
380
|
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
// still force-restores afterwards.
|
|
381
|
+
// Stop re-degrading this page when its thumbnail fails, so the SDK's
|
|
382
|
+
// onError retry can settle on the full image. Other pages remain eligible.
|
|
385
383
|
const onImgError = (ev: Event): void => {
|
|
386
|
-
if (!
|
|
384
|
+
if (!canReDegrade()) return;
|
|
387
385
|
if (!ownsTarget(ev.target)) return;
|
|
388
|
-
|
|
386
|
+
const thumbnailURL = pages[pageIndex$.value]?.thumbnail;
|
|
387
|
+
if (
|
|
388
|
+
thumbnailURL &&
|
|
389
|
+
resolveImageUrl(ev.target.src) === resolveImageUrl(thumbnailURL)
|
|
390
|
+
) {
|
|
391
|
+
failedThumbnailPageIndex = pageIndex$.value;
|
|
392
|
+
}
|
|
389
393
|
};
|
|
390
394
|
|
|
391
395
|
const applyActivity = (): void => {
|
|
@@ -397,7 +401,7 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
397
401
|
restoreFocusedResources();
|
|
398
402
|
} else {
|
|
399
403
|
blurEpisode = true;
|
|
400
|
-
|
|
404
|
+
failedThumbnailPageIndex = null;
|
|
401
405
|
degrade();
|
|
402
406
|
app.preload.pause();
|
|
403
407
|
suspendCachedResources();
|
|
@@ -447,27 +451,16 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
447
451
|
});
|
|
448
452
|
};
|
|
449
453
|
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
454
|
+
const getLogger = (context: AppContext) =>
|
|
455
|
+
(context.getWindowManager() as any)?.Logger ?? (context.getRoom() as any)?.logger;
|
|
456
|
+
|
|
457
|
+
const createLogger = (context: AppContext): Logger => {
|
|
458
|
+
return (...args) => getLogger(context)?.info?.(...args);
|
|
456
459
|
};
|
|
457
460
|
|
|
458
|
-
const createDiagnosticLogger = (
|
|
461
|
+
export const createDiagnosticLogger = (
|
|
459
462
|
context: AppContext,
|
|
460
463
|
): PresentationDiagnosticLogger => {
|
|
461
|
-
const createAppLogger = (context as any).createLogger;
|
|
462
|
-
if (typeof createAppLogger === "function") {
|
|
463
|
-
return createAppLogger.call(context, "camera", {
|
|
464
|
-
debounceTime: 300,
|
|
465
|
-
maxWaitTime: 2000,
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// Compatibility fallback for WindowManager versions without AppContext.createLogger().
|
|
470
|
-
const roomLogger = (context.getRoom() as any)?.logger;
|
|
471
464
|
const prefix = `[Presentation][${context.appId}][camera]`;
|
|
472
465
|
const emit = (
|
|
473
466
|
level: "info" | "warn" | "error",
|
|
@@ -475,6 +468,7 @@ const createDiagnosticLogger = (
|
|
|
475
468
|
...data: unknown[]
|
|
476
469
|
) => {
|
|
477
470
|
try {
|
|
471
|
+
const roomLogger = getLogger(context);
|
|
478
472
|
const printer = roomLogger?.[level];
|
|
479
473
|
if (typeof printer === "function")
|
|
480
474
|
printer.call(roomLogger, `${prefix}[${event}]`, ...data);
|
|
@@ -554,10 +548,8 @@ export const NetlessAppPresentation: NetlessApp<
|
|
|
554
548
|
|
|
555
549
|
const options = context.getAppOptions() || {};
|
|
556
550
|
const room = context.getRoom();
|
|
557
|
-
const log = options.log || createLogger(
|
|
558
|
-
const
|
|
559
|
-
const warn: Logger = (...data) =>
|
|
560
|
-
roomLogger?.warn ? roomLogger.warn(...data) : log(...data);
|
|
551
|
+
const log = options.log || createLogger(context);
|
|
552
|
+
const warn: Logger = (...data) => getLogger(context)?.warn?.(...data);
|
|
561
553
|
const configuredOriginSize = context.storage.state.originSize;
|
|
562
554
|
const originSize = isValidSize(configuredOriginSize)
|
|
563
555
|
? {
|
|
@@ -927,6 +919,7 @@ export const NetlessAppPresentation: NetlessApp<
|
|
|
927
919
|
view,
|
|
928
920
|
options.thumbnail,
|
|
929
921
|
options.useClipView,
|
|
922
|
+
error => getLogger(context)?.error?.("[Presentation] thumbnail URL parsing failed", context.appId, error),
|
|
930
923
|
),
|
|
931
924
|
);
|
|
932
925
|
app.contentDOM.dataset.appPresentationVersion = __VERSION__;
|
|
@@ -1522,8 +1515,8 @@ export const NetlessAppPresentation: NetlessApp<
|
|
|
1522
1515
|
// `SetupResult | Promise<SetupResult>` declaration is the minimum version.
|
|
1523
1516
|
const setupReadyTimeout = options.setupReadyTimeout ?? DEFAULT_SETUP_READY_TIMEOUT;
|
|
1524
1517
|
return prepareScenesPromise.then(async () => {
|
|
1525
|
-
//
|
|
1526
|
-
//
|
|
1518
|
+
// Wait for the current page image or its first failure. The SDK owns
|
|
1519
|
+
// background-image retries, so a failed request must not fail setup.
|
|
1527
1520
|
const result = await waitForCurrentPageImage(
|
|
1528
1521
|
() => findBackgroundImage(view), setupReadyTimeout, () => disposed,
|
|
1529
1522
|
{
|
|
@@ -1532,10 +1525,7 @@ export const NetlessAppPresentation: NetlessApp<
|
|
|
1532
1525
|
},
|
|
1533
1526
|
);
|
|
1534
1527
|
if (disposed) throw new Error("[Presentation] disposed before setup ready");
|
|
1535
|
-
if (result === "error"
|
|
1536
|
-
throw new Error("[Presentation] setup page image failed to load");
|
|
1537
|
-
}
|
|
1538
|
-
if (result === "error" && !disposed) {
|
|
1528
|
+
if (result === "error") {
|
|
1539
1529
|
warn("[Presentation] setup page image failed to load; continuing setup");
|
|
1540
1530
|
}
|
|
1541
1531
|
return controller;
|
|
@@ -1597,6 +1587,7 @@ function createPresentation(
|
|
|
1597
1587
|
view: View,
|
|
1598
1588
|
thumbnail?: (src: string) => string,
|
|
1599
1589
|
useClipView?: boolean,
|
|
1590
|
+
onThumbnailError?: (error: unknown) => void,
|
|
1600
1591
|
): AppPresentation {
|
|
1601
1592
|
box.mountStyles(styles);
|
|
1602
1593
|
|
|
@@ -1605,6 +1596,7 @@ function createPresentation(
|
|
|
1605
1596
|
readonly: box.readonly,
|
|
1606
1597
|
jumpPage,
|
|
1607
1598
|
thumbnail,
|
|
1599
|
+
onThumbnailError,
|
|
1608
1600
|
});
|
|
1609
1601
|
app.box = box;
|
|
1610
1602
|
box.mountContent(app.contentDOM);
|
package/src/presentation.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface PresentationConfig {
|
|
|
18
18
|
readonly pages: PresentationPage[]
|
|
19
19
|
readonly readonly?: boolean
|
|
20
20
|
readonly thumbnail?: (src: string) => string
|
|
21
|
+
readonly onThumbnailError?: (error: unknown) => void
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -59,6 +60,7 @@ export class Presentation implements IDisposable<void> {
|
|
|
59
60
|
readonly dispose = disposableStore()
|
|
60
61
|
readonly pages: PresentationPage[]
|
|
61
62
|
readonly preload: Preload
|
|
63
|
+
readonly onThumbnailError?: (error: unknown) => void
|
|
62
64
|
|
|
63
65
|
dom: Element | DocumentFragment
|
|
64
66
|
contentDOM: HTMLDivElement
|
|
@@ -81,6 +83,7 @@ export class Presentation implements IDisposable<void> {
|
|
|
81
83
|
constructor(config: PresentationConfig) {
|
|
82
84
|
this.pages = config.pages
|
|
83
85
|
this.preload = new Preload(this.pages)
|
|
86
|
+
this.onThumbnailError = config.onThumbnailError
|
|
84
87
|
if (config.thumbnail) this.thumbnail = config.thumbnail
|
|
85
88
|
this.dispose.add(this.preload)
|
|
86
89
|
this.readonly = config.readonly ?? false
|
|
@@ -329,7 +332,11 @@ export class Presentation implements IDisposable<void> {
|
|
|
329
332
|
url.searchParams.set('x-oss-process', 'image/resize,l_50')
|
|
330
333
|
return url.toString()
|
|
331
334
|
} catch (err) {
|
|
332
|
-
|
|
335
|
+
try {
|
|
336
|
+
this.onThumbnailError?.(err)
|
|
337
|
+
} catch {
|
|
338
|
+
// A diagnostic failure must not change the thumbnail fallback.
|
|
339
|
+
}
|
|
333
340
|
return src
|
|
334
341
|
}
|
|
335
342
|
}
|