@netless/app-presentation 0.1.13-beta.3 → 0.1.13-beta.4
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 +2 -2
- package/dist/index.global.js +62 -15
- package/dist/index.js +62 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/app-presentation.ts +55 -11
- package/src/setup-ready.ts +9 -2
package/src/app-presentation.ts
CHANGED
|
@@ -99,8 +99,8 @@ export interface PresentationAppOptions {
|
|
|
99
99
|
/** useClipView is used to set the presentation use clip view, it will be used in the presentation, and the presentation will be use clip view when the app is initialized */
|
|
100
100
|
useClipView?: boolean;
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
102
|
+
* First-image warning threshold in ms. Default: 5_000. New lazy hosts
|
|
103
|
+
* await real readiness; legacy/eager hosts resolve on timeout.
|
|
104
104
|
*/
|
|
105
105
|
setupReadyTimeout?: number;
|
|
106
106
|
/**
|
|
@@ -269,6 +269,29 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
269
269
|
// fails, so we don't fight the SDK's background onError retry.
|
|
270
270
|
let blurEpisode = false;
|
|
271
271
|
let reDegradeEnabled = false;
|
|
272
|
+
// Duplicate focus(false) deliveries are unavoidable (multiple sources in
|
|
273
|
+
// the host). Preview-resource clearing is the only non-idempotent DOM op
|
|
274
|
+
// in the blur path, so gate it on this flag until focus restores it.
|
|
275
|
+
let previewSuspended = false;
|
|
276
|
+
// A freshly-created runtime has active resources even if setup completed
|
|
277
|
+
// after the box already lost focus. Track applied resource state instead of
|
|
278
|
+
// reading box.focus, and ignore repeated signals from the host.
|
|
279
|
+
let appliedFocusState = true;
|
|
280
|
+
let logicalFocused = true;
|
|
281
|
+
const activityBlocked = (): boolean => {
|
|
282
|
+
const wm = context.getWindowManager() as any;
|
|
283
|
+
if (wm?.lazySetupInMaximizedMode !== true) return false;
|
|
284
|
+
const host = (context as any).getRuntimeActivity?.();
|
|
285
|
+
// Use the host's complete activity snapshot when available, rather than
|
|
286
|
+
// combining it with a local boxState from a different restore revision.
|
|
287
|
+
if (host) return document.visibilityState === "hidden" || !host.active;
|
|
288
|
+
return (
|
|
289
|
+
document.visibilityState === "hidden" ||
|
|
290
|
+
wm.boxState === "minimized" ||
|
|
291
|
+
wm.attributes?.minimized === true ||
|
|
292
|
+
context.getBoxStatus() === "minimized"
|
|
293
|
+
);
|
|
294
|
+
};
|
|
272
295
|
let boxBlurred = box.$box.classList.contains(boxBlurClass);
|
|
273
296
|
|
|
274
297
|
const setBackgroundSuspended = (suspended: boolean): void => {
|
|
@@ -284,7 +307,7 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
284
307
|
const thumbnailURL = page.thumbnail;
|
|
285
308
|
const img = findBackgroundImage(view);
|
|
286
309
|
if (!thumbnailURL || !hasUsableThumbnail(page)) {
|
|
287
|
-
if (boxBlurred) {
|
|
310
|
+
if (boxBlurred || activityBlocked()) {
|
|
288
311
|
setBackgroundSuspended(true);
|
|
289
312
|
if (
|
|
290
313
|
img &&
|
|
@@ -311,8 +334,11 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
311
334
|
};
|
|
312
335
|
|
|
313
336
|
const suspendCachedResources = (): void => {
|
|
314
|
-
if (!blurEpisode || !boxBlurred) return;
|
|
315
|
-
|
|
337
|
+
if (!blurEpisode || (!boxBlurred && !activityBlocked())) return;
|
|
338
|
+
if (!previewSuspended) {
|
|
339
|
+
previewSuspended = true;
|
|
340
|
+
app.suspendPreviewResources();
|
|
341
|
+
}
|
|
316
342
|
const page = pages[pageIndex$.value];
|
|
317
343
|
if (!page || !hasUsableThumbnail(page) || reDegradeEnabled) {
|
|
318
344
|
degrade();
|
|
@@ -323,6 +349,7 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
323
349
|
if (!blurEpisode) return;
|
|
324
350
|
blurEpisode = false;
|
|
325
351
|
reDegradeEnabled = false;
|
|
352
|
+
previewSuspended = false;
|
|
326
353
|
restore();
|
|
327
354
|
app.preload.resume(pageIndex$.value);
|
|
328
355
|
};
|
|
@@ -361,25 +388,36 @@ export const setupBlurThumbnailDegradation = (config: {
|
|
|
361
388
|
reDegradeEnabled = false;
|
|
362
389
|
};
|
|
363
390
|
|
|
364
|
-
const
|
|
391
|
+
const applyActivity = (): void => {
|
|
392
|
+
const lazy = (context.getWindowManager() as any)?.lazySetupInMaximizedMode === true;
|
|
393
|
+
const isFocused = (!lazy || logicalFocused) && !activityBlocked();
|
|
394
|
+
if (appliedFocusState === isFocused) return;
|
|
395
|
+
if (!isFocused && !isBlurDegradationAllowed(context) && !activityBlocked()) return;
|
|
365
396
|
if (isFocused) {
|
|
366
397
|
restoreFocusedResources();
|
|
367
398
|
} else {
|
|
368
|
-
if (!isBlurDegradationAllowed(context)) return;
|
|
369
399
|
blurEpisode = true;
|
|
370
400
|
reDegradeEnabled = true;
|
|
371
401
|
degrade();
|
|
372
402
|
app.preload.pause();
|
|
373
403
|
suspendCachedResources();
|
|
374
404
|
}
|
|
405
|
+
appliedFocusState = isFocused;
|
|
406
|
+
};
|
|
407
|
+
const offFocus = context.emitter.on("focus", (isFocused: boolean) => {
|
|
408
|
+
logicalFocused = isFocused;
|
|
409
|
+
applyActivity();
|
|
375
410
|
});
|
|
376
411
|
dispose.add(offFocus);
|
|
412
|
+
dispose.add((context.emitter as any).on("runtimeActivity", applyActivity));
|
|
413
|
+
document.addEventListener("visibilitychange", applyActivity);
|
|
414
|
+
dispose.add(() => document.removeEventListener("visibilitychange", applyActivity));
|
|
377
415
|
|
|
378
416
|
const updateBoxBlurred = (blurred: boolean): void => {
|
|
379
417
|
if (boxBlurred === blurred) return;
|
|
380
418
|
boxBlurred = blurred;
|
|
381
419
|
if (blurred) suspendCachedResources();
|
|
382
|
-
else restoreFocusedResources();
|
|
420
|
+
else if (logicalFocused && !activityBlocked()) restoreFocusedResources();
|
|
383
421
|
};
|
|
384
422
|
const onBoxBlur = (): void => updateBoxBlurred(true);
|
|
385
423
|
const onBoxFocus = (): void => updateBoxBlurred(false);
|
|
@@ -1488,10 +1526,16 @@ export const NetlessAppPresentation: NetlessApp<
|
|
|
1488
1526
|
// page image is actually visible (or the bounded wait gives up).
|
|
1489
1527
|
const result = await waitForCurrentPageImage(
|
|
1490
1528
|
() => findBackgroundImage(view), setupReadyTimeout, () => disposed,
|
|
1529
|
+
{
|
|
1530
|
+
waitForReady: () => Boolean((context as any).waitForActualSetupReady),
|
|
1531
|
+
onTimeout: () => warn(`[Presentation] setup page image still pending after ${setupReadyTimeout}ms`),
|
|
1532
|
+
},
|
|
1491
1533
|
);
|
|
1492
|
-
if (
|
|
1493
|
-
|
|
1494
|
-
|
|
1534
|
+
if (disposed) throw new Error("[Presentation] disposed before setup ready");
|
|
1535
|
+
if (result === "error" && (context as any).waitForActualSetupReady) {
|
|
1536
|
+
throw new Error("[Presentation] setup page image failed to load");
|
|
1537
|
+
}
|
|
1538
|
+
if (result === "error" && !disposed) {
|
|
1495
1539
|
warn("[Presentation] setup page image failed to load; continuing setup");
|
|
1496
1540
|
}
|
|
1497
1541
|
return controller;
|
package/src/setup-ready.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export type SetupImageResult = "loaded" | "error" | "timeout" | "disposed";
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** New lazy hosts await actual readiness; legacy hosts retain the bounded wait. */
|
|
4
4
|
export const waitForCurrentPageImage = (
|
|
5
5
|
getImage: () => HTMLImageElement | null,
|
|
6
6
|
timeoutMs: number,
|
|
7
7
|
isDisposed: () => boolean,
|
|
8
|
+
options?: { waitForReady: () => boolean; onTimeout: () => void },
|
|
8
9
|
): Promise<SetupImageResult> => {
|
|
9
10
|
return new Promise(resolve => {
|
|
10
11
|
let settled = false;
|
|
12
|
+
let timedOut = false;
|
|
11
13
|
let pollTimer: number | undefined;
|
|
12
14
|
const settle = (result: SetupImageResult) => {
|
|
13
15
|
if (settled) return;
|
|
@@ -16,12 +18,17 @@ export const waitForCurrentPageImage = (
|
|
|
16
18
|
if (pollTimer !== undefined) window.clearInterval(pollTimer);
|
|
17
19
|
resolve(result);
|
|
18
20
|
};
|
|
19
|
-
const timeoutTimer = window.setTimeout(() =>
|
|
21
|
+
const timeoutTimer = window.setTimeout(() => {
|
|
22
|
+
timedOut = true;
|
|
23
|
+
options?.onTimeout();
|
|
24
|
+
if (!options?.waitForReady()) settle("timeout");
|
|
25
|
+
}, timeoutMs);
|
|
20
26
|
const check = () => {
|
|
21
27
|
if (isDisposed()) {
|
|
22
28
|
settle("disposed");
|
|
23
29
|
return;
|
|
24
30
|
}
|
|
31
|
+
if (timedOut && !options?.waitForReady()) { settle("timeout"); return; }
|
|
25
32
|
const img = getImage();
|
|
26
33
|
if (img?.complete) {
|
|
27
34
|
if (img.naturalWidth > 0) {
|