@netless/app-slide 0.2.106-beta.3 → 0.2.106-beta.5
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 +9 -9
- package/dist/main.cjs.js +201 -201
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +240 -139
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +201 -201
- package/dist/main.iife.js.map +1 -1
- package/package.json +2 -2
- package/src/SlideController/index.ts +101 -15
- package/src/SlideDocsViewer/index.ts +3 -0
- package/src/index.ts +18 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/app-slide",
|
|
3
|
-
"version": "0.2.106-beta.
|
|
3
|
+
"version": "0.2.106-beta.5",
|
|
4
4
|
"main": "dist/main.cjs.js",
|
|
5
5
|
"module": "dist/main.es.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@juggle/resize-observer": "^3.3.1",
|
|
21
|
-
"@netless/slide": "1.4.
|
|
21
|
+
"@netless/slide": "1.4.62-beta.1",
|
|
22
22
|
"@types/color-string": "^1.5.2",
|
|
23
23
|
"color-string": "^1.9.1",
|
|
24
24
|
"jspdf": "2.5.1",
|
|
@@ -56,6 +56,48 @@ const noop = function noop() {
|
|
|
56
56
|
// do nothing
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
type SlideWebGLTask = {
|
|
60
|
+
label: string;
|
|
61
|
+
run: () => void | Promise<void>;
|
|
62
|
+
resolve: () => void;
|
|
63
|
+
reject: (error: unknown) => void;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const slideWebGLTasks: SlideWebGLTask[] = [];
|
|
67
|
+
let slideWebGLRunning = false;
|
|
68
|
+
|
|
69
|
+
function runNextSlideWebGLTask(): void {
|
|
70
|
+
if (slideWebGLRunning) return;
|
|
71
|
+
const task = slideWebGLTasks.shift();
|
|
72
|
+
if (!task) return;
|
|
73
|
+
slideWebGLRunning = true;
|
|
74
|
+
const startedAt = performance.now();
|
|
75
|
+
log("[Slide][player-queue] start", task.label, "pending", slideWebGLTasks.length);
|
|
76
|
+
void Promise.resolve().then(task.run).then(() => {
|
|
77
|
+
log("[Slide][player-queue] done", task.label, "ms", Math.round(performance.now() - startedAt));
|
|
78
|
+
task.resolve();
|
|
79
|
+
}, error => {
|
|
80
|
+
log("[Slide][player-queue] failed", task.label, error);
|
|
81
|
+
task.reject(error);
|
|
82
|
+
}).finally(() => {
|
|
83
|
+
slideWebGLRunning = false;
|
|
84
|
+
runNextSlideWebGLTask();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function enqueueSlideWebGLTransition(
|
|
89
|
+
run: () => void | Promise<void>,
|
|
90
|
+
priority = false,
|
|
91
|
+
label = "unspecified"
|
|
92
|
+
): Promise<void> {
|
|
93
|
+
return new Promise<void>((resolve, reject) => {
|
|
94
|
+
const task = { label, run, resolve, reject };
|
|
95
|
+
if (priority) slideWebGLTasks.unshift(task);
|
|
96
|
+
else slideWebGLTasks.push(task);
|
|
97
|
+
runNextSlideWebGLTask();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
59
101
|
type MagixEventListener = Parameters<
|
|
60
102
|
AppContext<Attributes, MagixEvents>["addMagixEventListener"]
|
|
61
103
|
>[1];
|
|
@@ -364,13 +406,30 @@ export class SlideControllerBase {
|
|
|
364
406
|
|
|
365
407
|
protected destroyed = false;
|
|
366
408
|
protected destroyPromise?: Promise<void>;
|
|
409
|
+
private resolveDestroyedSignal!: () => void;
|
|
410
|
+
private readonly destroyedSignal = new Promise<void>(resolve => {
|
|
411
|
+
this.resolveDestroyedSignal = resolve;
|
|
412
|
+
});
|
|
367
413
|
|
|
368
414
|
public destroy(): Promise<void> {
|
|
369
415
|
this.sideEffect.flushAll();
|
|
370
416
|
if (!this.destroyed) {
|
|
371
417
|
log("[Slide] destroy slide (once)");
|
|
372
418
|
this.destroyed = true;
|
|
373
|
-
this.destroyPromise = Promise
|
|
419
|
+
this.destroyPromise = enqueueSlideWebGLTransition(() => new Promise<void>((resolve, reject) => {
|
|
420
|
+
try {
|
|
421
|
+
(this.slide.destroy as (
|
|
422
|
+
onPlayerDestroyed?: () => void,
|
|
423
|
+
onError?: (error: unknown) => void
|
|
424
|
+
) => void).call(this.slide, () => {
|
|
425
|
+
log("[Slide][player] destroy callback", this.context.appId);
|
|
426
|
+
resolve();
|
|
427
|
+
}, reject);
|
|
428
|
+
} catch (error) {
|
|
429
|
+
reject(error);
|
|
430
|
+
}
|
|
431
|
+
}), true, `destroy:${this.context.appId}`);
|
|
432
|
+
this.resolveDestroyedSignal();
|
|
374
433
|
}
|
|
375
434
|
return this.destroyPromise ?? Promise.resolve();
|
|
376
435
|
}
|
|
@@ -445,7 +504,20 @@ export class SlideControllerBase {
|
|
|
445
504
|
if (this.ready) {
|
|
446
505
|
log("[Slide] freeze", this.context.appId);
|
|
447
506
|
if (this.invisibleBehavior === "frozen") {
|
|
448
|
-
await
|
|
507
|
+
await enqueueSlideWebGLTransition(() => {
|
|
508
|
+
if (this.destroyed) return Promise.resolve();
|
|
509
|
+
const destroyed = new Promise<void>((resolve, reject) => {
|
|
510
|
+
const result = (this.slide.frozen as (
|
|
511
|
+
onPlayerDestroyed?: () => void,
|
|
512
|
+
onError?: (error: unknown) => void
|
|
513
|
+
) => void | Promise<void>).call(this.slide, () => {
|
|
514
|
+
log("[Slide][player] frozen callback", this.context.appId);
|
|
515
|
+
resolve();
|
|
516
|
+
}, reject);
|
|
517
|
+
if (result) void result.then(resolve, reject);
|
|
518
|
+
});
|
|
519
|
+
return Promise.race([destroyed, this.destroyedSignal]);
|
|
520
|
+
}, false, `freeze:${this.context.appId}`);
|
|
449
521
|
} else {
|
|
450
522
|
this.slide.pause();
|
|
451
523
|
}
|
|
@@ -469,25 +541,39 @@ export class SlideControllerBase {
|
|
|
469
541
|
this.isFrozen = false;
|
|
470
542
|
try {
|
|
471
543
|
if (this.ready) {
|
|
472
|
-
let isNeedSyncState = false;
|
|
473
544
|
log("[Slide] unfreeze", this.context.appId);
|
|
474
545
|
if (this.invisibleBehavior === "frozen") {
|
|
475
|
-
await
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
546
|
+
await enqueueSlideWebGLTransition(() => {
|
|
547
|
+
if (this.destroyed) return Promise.resolve();
|
|
548
|
+
const created = new Promise<void>((resolve, reject) => {
|
|
549
|
+
const result = (this.slide.release as (
|
|
550
|
+
onRestored?: () => void,
|
|
551
|
+
onPlayerCreated?: () => void,
|
|
552
|
+
onError?: (error: unknown) => void
|
|
553
|
+
) => void | Promise<void>).call(this.slide, () => {
|
|
554
|
+
log("[Slide][player] restored callback", this.context.appId);
|
|
555
|
+
if (this.isLazySetupMode() && !this.shouldBeActive()) return;
|
|
556
|
+
this.slide.notifyFrameResize();
|
|
557
|
+
const state = this.context.storage.state.state;
|
|
558
|
+
if (state) {
|
|
559
|
+
log("[Slide] sync storage", JSON.stringify(state));
|
|
560
|
+
void this.slide.setSlideState(state).catch(error => {
|
|
561
|
+
this.resourceStateUnknown = true;
|
|
562
|
+
logger.warn("[Slide] storage sync after release failed", this.context.appId, error);
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}, () => {
|
|
566
|
+
log("[Slide][player] created callback", this.context.appId);
|
|
567
|
+
resolve();
|
|
568
|
+
}, reject);
|
|
569
|
+
if (result) void result.then(resolve, reject);
|
|
570
|
+
});
|
|
571
|
+
return Promise.race([created, this.destroyedSignal]);
|
|
572
|
+
}, false, `release:${this.context.appId}`);
|
|
480
573
|
} else {
|
|
481
574
|
this.slide.resume();
|
|
482
575
|
this.slide.notifyFrameResize();
|
|
483
576
|
}
|
|
484
|
-
if (isNeedSyncState && (!this.isLazySetupMode() || this.shouldBeActive())) {
|
|
485
|
-
const state = this.context.storage.state.state;
|
|
486
|
-
if (state) {
|
|
487
|
-
log("[Slide] sync storage", JSON.stringify(state));
|
|
488
|
-
await this.slide.setSlideState(state);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
577
|
} else {
|
|
492
578
|
this._toFreeze = -1;
|
|
493
579
|
}
|
|
@@ -435,6 +435,7 @@ export class SlideDocsViewer {
|
|
|
435
435
|
};
|
|
436
436
|
|
|
437
437
|
protected onRenderStart = () => {
|
|
438
|
+
logger.log("[Slide][notes] hide", this.appId, "scene", this.whiteboardView.focusScenePath);
|
|
438
439
|
this.$whiteboardView.classList.add(this.wrapClassName("wb-view-hidden"));
|
|
439
440
|
this.viewer.setPlaying();
|
|
440
441
|
};
|
|
@@ -447,11 +448,13 @@ export class SlideDocsViewer {
|
|
|
447
448
|
|
|
448
449
|
public onPageChanged = () => {
|
|
449
450
|
clearTimeout(this._onPageChangedTimer);
|
|
451
|
+
logger.log("[Slide][notes] show scheduled", this.appId, "scene", this.whiteboardView.focusScenePath);
|
|
450
452
|
this._onPageChangedTimer = setTimeout(this._onPageChanged, 200) as unknown as number;
|
|
451
453
|
};
|
|
452
454
|
|
|
453
455
|
protected _onPageChangedTimer = 0;
|
|
454
456
|
protected _onPageChanged = () => {
|
|
457
|
+
logger.log("[Slide][notes] show", this.appId, "scene", this.whiteboardView.focusScenePath);
|
|
455
458
|
this.$overlay.style.opacity = "";
|
|
456
459
|
this.$whiteboardView.classList.remove(this.wrapClassName("wb-view-hidden"));
|
|
457
460
|
};
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
syncSceneWithSlide,
|
|
14
14
|
SlideController,
|
|
15
15
|
SlideControllerBase,
|
|
16
|
+
enqueueSlideWebGLTransition,
|
|
16
17
|
} from "./SlideController";
|
|
17
18
|
import { SlideDocsViewer } from "./SlideDocsViewer";
|
|
18
19
|
import { apps, FreezerLength, addHooks, useFreezer } from "./utils/freezer";
|
|
@@ -126,9 +127,7 @@ export interface AppResult {
|
|
|
126
127
|
playPptMedia: () => void;
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult>
|
|
130
|
-
teardown(context: import("@netless/window-manager").AppContext): Promise<void>;
|
|
131
|
-
} = {
|
|
130
|
+
const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
132
131
|
kind: "Slide",
|
|
133
132
|
setup(context) {
|
|
134
133
|
console.log("[Slide] setup @ " + version);
|
|
@@ -225,6 +224,8 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
225
224
|
const page = slideController.slide.slideState.currentSlideIndex;
|
|
226
225
|
log("[Slide] page to", page, synced ? "(synced)" : "", "(on ready)");
|
|
227
226
|
slideController.slide.on("renderEnd", options.onRenderEnd);
|
|
227
|
+
}).catch(error => {
|
|
228
|
+
if (!disposed) logger.warn("[Slide] ready callback failed", context.appId, error);
|
|
228
229
|
});
|
|
229
230
|
return slideController;
|
|
230
231
|
};
|
|
@@ -329,10 +330,9 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
329
330
|
};
|
|
330
331
|
offDestroy = context.emitter.on("destroy", teardown);
|
|
331
332
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
(SlideApp as any).__teardownByContext.set(context, teardown);
|
|
333
|
+
const mounting = enqueueSlideWebGLTransition(() => {
|
|
334
|
+
if (!disposed) docsViewer?.mount();
|
|
335
|
+
}, false, `setup:${context.appId}`);
|
|
336
336
|
|
|
337
337
|
// Resolve once the SlideController finished its first render (renderEnd).
|
|
338
338
|
// New lazy hosts retain the real completion after the warning threshold.
|
|
@@ -357,7 +357,10 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
357
357
|
// the poll only catches teardown and pre-ready render failures.
|
|
358
358
|
slideControllerRef?.readyPromise.then(
|
|
359
359
|
() => settle(!firstRenderFailed),
|
|
360
|
-
() =>
|
|
360
|
+
() => {
|
|
361
|
+
firstRenderFailed = true;
|
|
362
|
+
settle(false);
|
|
363
|
+
}
|
|
361
364
|
);
|
|
362
365
|
const pollTimer = window.setInterval(() => {
|
|
363
366
|
if (disposed || firstRenderFailed) settle(false);
|
|
@@ -502,7 +505,10 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
502
505
|
};
|
|
503
506
|
|
|
504
507
|
const setupReadyTimeout = appOptions?.setupReadyTimeout ?? DEFAULT_SLIDE_SETUP_READY_TIMEOUT;
|
|
505
|
-
return
|
|
508
|
+
return mounting.then(() => {
|
|
509
|
+
if (disposed) throw new Error("[Slide] disposed before first render");
|
|
510
|
+
return waitForFirstRender(setupReadyTimeout);
|
|
511
|
+
}).then(async () => {
|
|
506
512
|
if (disposed) throw new Error("[Slide] disposed before first render");
|
|
507
513
|
if (firstRenderFailed) {
|
|
508
514
|
throw new Error("[Slide] first render failed before ready");
|
|
@@ -511,12 +517,11 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
511
517
|
log("[Slide] setup ready wait timed out, slide keeps loading in background");
|
|
512
518
|
}
|
|
513
519
|
return appResult;
|
|
520
|
+
}).catch(async error => {
|
|
521
|
+
if (!disposed) await teardown();
|
|
522
|
+
throw error;
|
|
514
523
|
}) as unknown as AppResult;
|
|
515
524
|
},
|
|
516
|
-
async teardown(context) {
|
|
517
|
-
await (SlideApp as any).__teardownByContext?.get(context)?.();
|
|
518
|
-
(SlideApp as any).__teardownByContext?.delete(context);
|
|
519
|
-
},
|
|
520
525
|
};
|
|
521
526
|
|
|
522
527
|
export default SlideApp;
|