@netless/app-slide 0.0.29 → 0.0.34
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/README.md +14 -1
- package/dist/DocsViewer/index.d.ts +1 -5
- package/dist/SlideController/index.d.ts +0 -1
- package/dist/SlidePreviewer/index.d.ts +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/main.cjs.js +140 -140
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +232 -71
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +143 -143
- package/dist/main.iife.js.map +1 -1
- package/dist/utils/logger.d.ts +20 -0
- package/package.json +4 -4
- package/src/DocsViewer/index.ts +1 -10
- package/src/SlideController/index.ts +15 -36
- package/src/SlideDocsViewer/index.ts +3 -3
- package/src/SlidePreviewer/index.ts +184 -0
- package/src/index.ts +20 -5
- package/src/utils/bgcolor.ts +2 -3
- package/src/utils/freezer.ts +6 -15
- package/src/utils/logger.ts +39 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SlideController } from "../SlideController";
|
|
2
|
+
declare class Logger {
|
|
3
|
+
enable: boolean;
|
|
4
|
+
apps: Record<string, {
|
|
5
|
+
context?: any;
|
|
6
|
+
controller?: SlideController;
|
|
7
|
+
}>;
|
|
8
|
+
level: "debug" | "verbose";
|
|
9
|
+
constructor(enable: boolean);
|
|
10
|
+
log(...args: Parameters<Console["log"]>): void;
|
|
11
|
+
verbose(...args: Parameters<Console["log"]>): void;
|
|
12
|
+
dispose(appId: string): void;
|
|
13
|
+
_onMessage: (ev: MessageEvent<{
|
|
14
|
+
slide: boolean | "__instance";
|
|
15
|
+
}>) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const logger: Logger;
|
|
18
|
+
export declare const log: (...args: Parameters<Console["log"]>) => void;
|
|
19
|
+
export declare const verbose: (...args: Parameters<Console["log"]>) => void;
|
|
20
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/app-slide",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"main": "dist/main.cjs.js",
|
|
5
5
|
"module": "dist/main.es.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
],
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@netless/app-shared": "^0.1.1",
|
|
14
|
-
"@netless/slide": "^0.1.
|
|
15
|
-
"@types/color-string": "^1.5.
|
|
16
|
-
"color-string": "^1.
|
|
14
|
+
"@netless/slide": "^0.1.37",
|
|
15
|
+
"@types/color-string": "^1.5.2",
|
|
16
|
+
"color-string": "^1.8.2",
|
|
17
17
|
"side-effect-manager": "^0.1.5",
|
|
18
18
|
"vanilla-lazyload": "^17.5.0"
|
|
19
19
|
},
|
package/src/DocsViewer/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { arrowRightSVG } from "./icons/arrow-right";
|
|
|
4
4
|
import { playSVG } from "./icons/play";
|
|
5
5
|
import { pauseSVG } from "./icons/pause";
|
|
6
6
|
|
|
7
|
-
import type { ReadonlyTeleBox } from "@netless/window-manager";
|
|
8
7
|
import type { ILazyLoadInstance } from "vanilla-lazyload";
|
|
9
8
|
import LazyLoad from "vanilla-lazyload";
|
|
10
9
|
import { SideEffectManager } from "side-effect-manager";
|
|
@@ -18,15 +17,13 @@ export interface DocsViewerPage {
|
|
|
18
17
|
|
|
19
18
|
export interface DocsViewerConfig {
|
|
20
19
|
readonly: boolean;
|
|
21
|
-
box: ReadonlyTeleBox;
|
|
22
20
|
onNewPageIndex: (index: number) => void;
|
|
23
21
|
onPlay?: () => void;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
export class DocsViewer {
|
|
27
|
-
public constructor({ readonly,
|
|
25
|
+
public constructor({ readonly, onNewPageIndex, onPlay }: DocsViewerConfig) {
|
|
28
26
|
this.readonly = readonly;
|
|
29
|
-
this.box = box;
|
|
30
27
|
this.onNewPageIndex = onNewPageIndex;
|
|
31
28
|
this.onPlay = onPlay;
|
|
32
29
|
|
|
@@ -34,7 +31,6 @@ export class DocsViewer {
|
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
protected readonly: boolean;
|
|
37
|
-
protected box: ReadonlyTeleBox;
|
|
38
34
|
protected onNewPageIndex: (index: number) => void;
|
|
39
35
|
protected onPlay?: () => void;
|
|
40
36
|
|
|
@@ -62,11 +58,6 @@ export class DocsViewer {
|
|
|
62
58
|
|
|
63
59
|
public pageIndex = 0;
|
|
64
60
|
|
|
65
|
-
public mount(): void {
|
|
66
|
-
this.box.mountContent(this.$content);
|
|
67
|
-
this.box.mountFooter(this.$footer);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
61
|
public unmount(): void {
|
|
71
62
|
this.$content.remove();
|
|
72
63
|
this.$footer.remove();
|
|
@@ -17,6 +17,7 @@ import { SideEffectManager } from "side-effect-manager";
|
|
|
17
17
|
import { Slide, SLIDE_EVENTS } from "@netless/slide";
|
|
18
18
|
import { clamp, deepClone, isObj } from "../utils/helpers";
|
|
19
19
|
import { cachedGetBgColor } from "../utils/bgcolor";
|
|
20
|
+
import { logger, log, verbose } from "../utils/logger";
|
|
20
21
|
export { syncSceneWithSlide, createDocsViewerPages } from "./helpers";
|
|
21
22
|
|
|
22
23
|
export const DefaultUrl = "https://convertcdn.netless.link/dynamicConvert";
|
|
@@ -39,7 +40,6 @@ export interface SlideControllerOptions {
|
|
|
39
40
|
export class SlideController {
|
|
40
41
|
public readonly context: SlideControllerOptions["context"];
|
|
41
42
|
public readonly slide: Slide;
|
|
42
|
-
public readonly debug: boolean;
|
|
43
43
|
|
|
44
44
|
private readonly channel: string;
|
|
45
45
|
private readonly room?: Room;
|
|
@@ -69,7 +69,6 @@ export class SlideController {
|
|
|
69
69
|
this.channel = `channel-${context.appId}`;
|
|
70
70
|
this.room = context.getRoom();
|
|
71
71
|
this.player = this.room ? undefined : (context.getDisplayer() as Player);
|
|
72
|
-
this.debug = import.meta.env.DEV || !!context.getAppOptions()?.debug;
|
|
73
72
|
this.slide = this.createSlide(anchor);
|
|
74
73
|
// the adder does not need to sync state
|
|
75
74
|
this.syncStateOnceFlag = !this.context.isAddApp;
|
|
@@ -104,16 +103,12 @@ export class SlideController {
|
|
|
104
103
|
slide.setResource(taskId, url || DefaultUrl);
|
|
105
104
|
if (state) {
|
|
106
105
|
// if we already have state, try restore from it
|
|
107
|
-
|
|
108
|
-
console.log("[Slide] init with state", deepClone(state));
|
|
109
|
-
}
|
|
106
|
+
log("[Slide] init with state", deepClone(state));
|
|
110
107
|
this.syncStateOnceFlag = false;
|
|
111
108
|
slide.setSlideState(deepClone(state));
|
|
112
109
|
} else if (context.isAddApp) {
|
|
113
110
|
// otherwise, maybe this slide is just added, let the adder kick start first render
|
|
114
|
-
|
|
115
|
-
console.log("[Slide] init by renderSlide", 1);
|
|
116
|
-
}
|
|
111
|
+
log("[Slide] init by renderSlide", 1);
|
|
117
112
|
slide.renderSlide(1);
|
|
118
113
|
}
|
|
119
114
|
// there's still some risk that the adder is left and no first render
|
|
@@ -175,9 +170,7 @@ export class SlideController {
|
|
|
175
170
|
type: SLIDE_EVENTS.syncDispatch,
|
|
176
171
|
payload: event,
|
|
177
172
|
};
|
|
178
|
-
|
|
179
|
-
console.log("[Slide] dispatch", event);
|
|
180
|
-
}
|
|
173
|
+
log("[Slide] dispatch", event);
|
|
181
174
|
this.room.dispatchMagixEvent(this.channel, payload);
|
|
182
175
|
}
|
|
183
176
|
};
|
|
@@ -187,9 +180,7 @@ export class SlideController {
|
|
|
187
180
|
const { type, payload } = ev.payload as MagixPayload;
|
|
188
181
|
if (type === SLIDE_EVENTS.syncDispatch) {
|
|
189
182
|
this.syncStateOnce();
|
|
190
|
-
|
|
191
|
-
console.log("[Slide] receive", payload);
|
|
192
|
-
}
|
|
183
|
+
log("[Slide] receive", payload);
|
|
193
184
|
// only emit the event if the slide state is sync-ed
|
|
194
185
|
if (!this.syncStateOnceFlag) {
|
|
195
186
|
this.slide.emit(SLIDE_EVENTS.syncReceive, payload);
|
|
@@ -203,9 +194,7 @@ export class SlideController {
|
|
|
203
194
|
if (this.syncStateOnceFlag) {
|
|
204
195
|
const { state } = { ...EmptyAttributes, ...this.context.getAttributes() };
|
|
205
196
|
if (state) {
|
|
206
|
-
|
|
207
|
-
console.log("[Slide] sync with state (once)", deepClone(state));
|
|
208
|
-
}
|
|
197
|
+
log("[Slide] sync with state (once)", deepClone(state));
|
|
209
198
|
this.slide.setSlideState(deepClone(state));
|
|
210
199
|
this.syncStateOnceFlag = false;
|
|
211
200
|
}
|
|
@@ -214,20 +203,18 @@ export class SlideController {
|
|
|
214
203
|
|
|
215
204
|
private onStateChange = (state: SlideState) => {
|
|
216
205
|
if (this.context.getIsWritable()) {
|
|
217
|
-
|
|
218
|
-
console.log("[Slide] state change", JSON.stringify(state, null, 2));
|
|
219
|
-
}
|
|
206
|
+
verbose("[Slide] state change", JSON.stringify(state, null, 2));
|
|
220
207
|
this.context.updateAttributes(["state"], state);
|
|
221
208
|
}
|
|
222
209
|
};
|
|
223
210
|
|
|
224
211
|
private onSeeked = () => {
|
|
225
212
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
226
|
-
const state = this.context.getAttributes()!.state
|
|
213
|
+
const state = this.context.getAttributes()!.state;
|
|
227
214
|
if (state) {
|
|
228
215
|
this.slide.setSlideState(deepClone(state));
|
|
229
216
|
}
|
|
230
|
-
}
|
|
217
|
+
};
|
|
231
218
|
|
|
232
219
|
private pollCount = 0;
|
|
233
220
|
private pollReadyState = () => {
|
|
@@ -242,9 +229,7 @@ export class SlideController {
|
|
|
242
229
|
setTimeout(this.pollReadyState, 500);
|
|
243
230
|
} else {
|
|
244
231
|
this.pollCount = 0;
|
|
245
|
-
|
|
246
|
-
console.log("[Slide] renderSlide (retry after timeout)", 1);
|
|
247
|
-
}
|
|
232
|
+
log("[Slide] renderSlide (retry after timeout)", 1);
|
|
248
233
|
this.slide.renderSlide(1);
|
|
249
234
|
}
|
|
250
235
|
};
|
|
@@ -268,7 +253,7 @@ export class SlideController {
|
|
|
268
253
|
interactive: true,
|
|
269
254
|
mode: "interactive",
|
|
270
255
|
resize: true,
|
|
271
|
-
controller:
|
|
256
|
+
controller: logger.enable,
|
|
272
257
|
renderOptions: {
|
|
273
258
|
minFPS: 25,
|
|
274
259
|
maxFPS: 30,
|
|
@@ -279,7 +264,7 @@ export class SlideController {
|
|
|
279
264
|
},
|
|
280
265
|
timestamp: this.timestamp,
|
|
281
266
|
});
|
|
282
|
-
if (
|
|
267
|
+
if (import.meta.env.DEV) {
|
|
283
268
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
284
269
|
(window as any).slide = slide;
|
|
285
270
|
}
|
|
@@ -291,9 +276,7 @@ export class SlideController {
|
|
|
291
276
|
public destroy() {
|
|
292
277
|
this.sideEffect.flushAll();
|
|
293
278
|
if (!this.destroyed) {
|
|
294
|
-
|
|
295
|
-
console.log("[Slide] destroy slide (once)");
|
|
296
|
-
}
|
|
279
|
+
log("[Slide] destroy slide (once)");
|
|
297
280
|
this.slide.destroy();
|
|
298
281
|
this.destroyed = true;
|
|
299
282
|
}
|
|
@@ -331,9 +314,7 @@ export class SlideController {
|
|
|
331
314
|
|
|
332
315
|
public freeze = () => {
|
|
333
316
|
if (this.ready) {
|
|
334
|
-
|
|
335
|
-
console.log("[Slide] freeze", this.context.appId);
|
|
336
|
-
}
|
|
317
|
+
log("[Slide] freeze", this.context.appId);
|
|
337
318
|
if (this.freezePromise) {
|
|
338
319
|
this._toFreeze = 1;
|
|
339
320
|
} else if (!this.isFrozen) {
|
|
@@ -347,9 +328,7 @@ export class SlideController {
|
|
|
347
328
|
|
|
348
329
|
public unfreeze = async () => {
|
|
349
330
|
if (this.ready) {
|
|
350
|
-
|
|
351
|
-
console.log("[Slide] unfreeze", this.context.appId);
|
|
352
|
-
}
|
|
331
|
+
log("[Slide] unfreeze", this.context.appId);
|
|
353
332
|
if (this.freezePromise) {
|
|
354
333
|
this._toFreeze = -1;
|
|
355
334
|
} else if (this.isFrozen) {
|
|
@@ -5,7 +5,7 @@ import { SideEffectManager } from "side-effect-manager";
|
|
|
5
5
|
import { createDocsViewerPages } from "../SlideController";
|
|
6
6
|
import { DocsViewer } from "../DocsViewer";
|
|
7
7
|
|
|
8
|
-
export const ClickThroughAppliances = new Set(["clicker"
|
|
8
|
+
export const ClickThroughAppliances = new Set(["clicker"]);
|
|
9
9
|
|
|
10
10
|
export type MountSlideOptions = Omit<SlideControllerOptions, "context" | "onPageChanged"> & {
|
|
11
11
|
onReady: () => void;
|
|
@@ -35,7 +35,6 @@ export class SlideDocsViewer {
|
|
|
35
35
|
this.mountWhiteboard = mountWhiteboard;
|
|
36
36
|
|
|
37
37
|
this.viewer = new DocsViewer({
|
|
38
|
-
box,
|
|
39
38
|
readonly: box.readonly,
|
|
40
39
|
onNewPageIndex: this.onNewPageIndex,
|
|
41
40
|
onPlay: this.onPlay,
|
|
@@ -98,7 +97,8 @@ export class SlideDocsViewer {
|
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
public mount() {
|
|
101
|
-
this.
|
|
100
|
+
this.box.mountContent(this.viewer.$content);
|
|
101
|
+
this.box.mountFooter(this.viewer.$footer);
|
|
102
102
|
|
|
103
103
|
this.slideController = this.mountSlideController({
|
|
104
104
|
anchor: this.$slide,
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Slide, SLIDE_EVENTS } from "@netless/slide";
|
|
2
|
+
import { DocsViewer } from "../DocsViewer";
|
|
3
|
+
import { createDocsViewerPages, DefaultUrl } from "../SlideController";
|
|
4
|
+
import { cachedGetBgColor } from "../utils/bgcolor";
|
|
5
|
+
import { clamp } from "../utils/helpers";
|
|
6
|
+
import { log } from "../utils/logger";
|
|
7
|
+
import style from "../style.scss?inline";
|
|
8
|
+
|
|
9
|
+
export interface PreviewParams {
|
|
10
|
+
container: HTMLElement;
|
|
11
|
+
taskId: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function previewSlide({
|
|
17
|
+
container,
|
|
18
|
+
taskId,
|
|
19
|
+
url = DefaultUrl,
|
|
20
|
+
debug = import.meta.env.DEV,
|
|
21
|
+
}: PreviewParams) {
|
|
22
|
+
if (!taskId) {
|
|
23
|
+
throw new Error("[Slide] taskId is required");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
container.style.cssText += `display:flex;flex-direction:column`;
|
|
27
|
+
const previewer = new SlidePreviewer({ target: container });
|
|
28
|
+
previewer.debug = !!debug;
|
|
29
|
+
|
|
30
|
+
previewer.mount(taskId, url);
|
|
31
|
+
|
|
32
|
+
return previewer;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (import.meta.env.DEV) {
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
(window as any).previewSlide = previewSlide;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class SlidePreviewer {
|
|
41
|
+
public readonly viewer: DocsViewer;
|
|
42
|
+
public readonly bgColor: string;
|
|
43
|
+
public readonly target: HTMLElement;
|
|
44
|
+
public slide: Slide | null = null;
|
|
45
|
+
public debug = import.meta.env.DEV;
|
|
46
|
+
|
|
47
|
+
public $slide!: HTMLDivElement;
|
|
48
|
+
public ready = false;
|
|
49
|
+
private resolveReady!: () => void;
|
|
50
|
+
public readonly readyPromise = new Promise<void>(resolve => {
|
|
51
|
+
this.resolveReady = () => {
|
|
52
|
+
this.ready = true;
|
|
53
|
+
resolve();
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
public constructor(config: { target: HTMLElement }) {
|
|
58
|
+
this.target = config.target;
|
|
59
|
+
this.bgColor = cachedGetBgColor(this.target);
|
|
60
|
+
this.viewer = new DocsViewer({
|
|
61
|
+
readonly: false,
|
|
62
|
+
onNewPageIndex: this.onNewPageIndex,
|
|
63
|
+
onPlay: this.onPlay,
|
|
64
|
+
});
|
|
65
|
+
this.render();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public render() {
|
|
69
|
+
this.viewer.$content.appendChild(this.renderSlideContainer());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public mount(taskId: string, url: string) {
|
|
73
|
+
this.target.appendChild(this.renderStyle());
|
|
74
|
+
this.target.appendChild(this.viewer.$content);
|
|
75
|
+
this.target.appendChild(this.viewer.$footer);
|
|
76
|
+
|
|
77
|
+
this.slide = new Slide({
|
|
78
|
+
anchor: this.$slide,
|
|
79
|
+
interactive: false,
|
|
80
|
+
mode: "local",
|
|
81
|
+
resize: true,
|
|
82
|
+
controller: this.debug,
|
|
83
|
+
renderOptions: {
|
|
84
|
+
minFPS: 25,
|
|
85
|
+
maxFPS: 30,
|
|
86
|
+
autoFPS: true,
|
|
87
|
+
autoResolution: true,
|
|
88
|
+
transactionBgColor: this.bgColor,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.registerEventListeners();
|
|
93
|
+
|
|
94
|
+
this.slide.setResource(taskId, url);
|
|
95
|
+
this.slide.renderSlide(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected renderStyle(): HTMLElement {
|
|
99
|
+
const element = document.createElement("style");
|
|
100
|
+
element.appendChild(document.createTextNode(style));
|
|
101
|
+
return element;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
protected registerEventListeners() {
|
|
105
|
+
if (!this.slide) return;
|
|
106
|
+
const { slide } = this;
|
|
107
|
+
|
|
108
|
+
slide.on(SLIDE_EVENTS.slideChange, this.onPageChanged);
|
|
109
|
+
slide.on(SLIDE_EVENTS.renderStart, this.onTransitionStart);
|
|
110
|
+
slide.on(SLIDE_EVENTS.renderEnd, this.onTransitionEnd);
|
|
111
|
+
slide.on(SLIDE_EVENTS.mainSeqStepStart, this.onTransitionStart);
|
|
112
|
+
slide.on(SLIDE_EVENTS.mainSeqStepEnd, this.onTransitionEnd);
|
|
113
|
+
slide.on(SLIDE_EVENTS.renderError, this.onError);
|
|
114
|
+
slide.on(SLIDE_EVENTS.renderEnd, this.resolveReady);
|
|
115
|
+
|
|
116
|
+
this.readyPromise.then(this.refreshPages);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected onPageChanged = (page: number) => {
|
|
120
|
+
this.viewer.setPageIndex(page - 1);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
protected onTransitionStart = () => {
|
|
124
|
+
this.viewer.setPlaying();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
protected onTransitionEnd = () => {
|
|
128
|
+
this.viewer.setPaused();
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
protected onError = ({ error }: { error: Error }) => {
|
|
132
|
+
this.viewer.setPaused();
|
|
133
|
+
console.warn("[Slide] render error", error);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
private destroyed = false;
|
|
137
|
+
public destroy() {
|
|
138
|
+
if (this.slide && !this.destroyed) {
|
|
139
|
+
log("[Slide] destroy slide (once)");
|
|
140
|
+
this.slide.destroy();
|
|
141
|
+
this.destroyed = true;
|
|
142
|
+
}
|
|
143
|
+
this.viewer.destroy();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected refreshPages = () => {
|
|
147
|
+
if (this.slide) {
|
|
148
|
+
this.viewer.pages = createDocsViewerPages(this.slide);
|
|
149
|
+
this.viewer.setPageIndex(this.getPageIndex(this.slide.slideCount));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
protected getPageIndex(page: number) {
|
|
154
|
+
return (page > 0 ? page : 1) - 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
protected renderSlideContainer(): HTMLDivElement {
|
|
158
|
+
if (!this.$slide) {
|
|
159
|
+
const $slide = document.createElement("div");
|
|
160
|
+
$slide.className = this.wrapClassName("slide");
|
|
161
|
+
$slide.dataset.appKind = "Slide";
|
|
162
|
+
this.$slide = $slide;
|
|
163
|
+
}
|
|
164
|
+
return this.$slide;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
protected onPlay = () => {
|
|
168
|
+
if (this.slide) {
|
|
169
|
+
this.slide.nextStep();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
protected onNewPageIndex = (index: number) => {
|
|
174
|
+
if (this.slide && this.slide.slideCount > 0) {
|
|
175
|
+
this.slide.renderSlide(clamp(index + 1, 1, this.slide.slideCount));
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
protected wrapClassName(className: string) {
|
|
180
|
+
return `${this.namespace}-${className}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
protected namespace = "netless-app-slide";
|
|
184
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,8 +14,12 @@ import {
|
|
|
14
14
|
} from "./SlideController";
|
|
15
15
|
import { SlideDocsViewer } from "./SlideDocsViewer";
|
|
16
16
|
import { apps, FreezerLength, addHooks } from "./utils/freezer";
|
|
17
|
+
import { log, logger } from "./utils/logger";
|
|
17
18
|
import styles from "./style.scss?inline";
|
|
18
19
|
|
|
20
|
+
export type { PreviewParams } from "./SlidePreviewer";
|
|
21
|
+
export { SlidePreviewer, default as previewSlide } from "./SlidePreviewer";
|
|
22
|
+
|
|
19
23
|
export type { Attributes, AddHooks, FreezableSlide };
|
|
20
24
|
|
|
21
25
|
export { DefaultUrl, apps, FreezerLength, addHooks };
|
|
@@ -48,11 +52,13 @@ const SlideApp: NetlessApp<Attributes> = {
|
|
|
48
52
|
|
|
49
53
|
const onPageChanged = (page: number) => {
|
|
50
54
|
const room = context.getRoom();
|
|
51
|
-
if (docsViewer && docsViewer.slideController
|
|
52
|
-
|
|
53
|
-
if (context.
|
|
54
|
-
|
|
55
|
+
if (docsViewer && docsViewer.slideController) {
|
|
56
|
+
let synced = false;
|
|
57
|
+
if (room && context.getIsWritable()) {
|
|
58
|
+
syncSceneWithSlide(room, context, docsViewer.slideController.slide, baseScenePath);
|
|
59
|
+
synced = true;
|
|
55
60
|
}
|
|
61
|
+
log("[Slide] page to", page, synced);
|
|
56
62
|
docsViewer.viewer.setPageIndex(page - 1);
|
|
57
63
|
}
|
|
58
64
|
};
|
|
@@ -64,6 +70,7 @@ const SlideApp: NetlessApp<Attributes> = {
|
|
|
64
70
|
onPageChanged,
|
|
65
71
|
});
|
|
66
72
|
apps.set(context.appId, slideController);
|
|
73
|
+
(logger.apps[context.appId] ||= {}).controller = slideController;
|
|
67
74
|
if (import.meta.env.DEV) {
|
|
68
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
76
|
(window as any).slideController = slideController;
|
|
@@ -86,6 +93,14 @@ const SlideApp: NetlessApp<Attributes> = {
|
|
|
86
93
|
|
|
87
94
|
const room = context.getRoom();
|
|
88
95
|
const sideEffect = new SideEffectManager();
|
|
96
|
+
|
|
97
|
+
sideEffect.add(() => {
|
|
98
|
+
(logger.apps[context.appId] ||= {}).context = context;
|
|
99
|
+
logger.enable = context.getAppOptions()?.debug || import.meta.env.DEV;
|
|
100
|
+
logger.level = import.meta.env.DEV ? "verbose" : "debug";
|
|
101
|
+
return () => logger.dispose(context.appId);
|
|
102
|
+
});
|
|
103
|
+
|
|
89
104
|
if (room) {
|
|
90
105
|
docsViewer.toggleClickThrough(room.state.memberState.currentApplianceName);
|
|
91
106
|
sideEffect.add(() => {
|
|
@@ -100,7 +115,7 @@ const SlideApp: NetlessApp<Attributes> = {
|
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
context.emitter.on("destroy", () => {
|
|
103
|
-
|
|
118
|
+
log("[Slide]: destroy");
|
|
104
119
|
apps.delete(context.appId);
|
|
105
120
|
sideEffect.flushAll();
|
|
106
121
|
if (docsViewer) {
|
package/src/utils/bgcolor.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ColorString from "color-string";
|
|
2
|
+
import { log } from "./logger";
|
|
2
3
|
|
|
3
4
|
export function guessBgColor(el: HTMLElement): string {
|
|
4
5
|
try {
|
|
@@ -38,9 +39,7 @@ let cachedBgColor = "";
|
|
|
38
39
|
export function cachedGetBgColor(el: HTMLElement): string {
|
|
39
40
|
if (!cachedBgColor) {
|
|
40
41
|
cachedBgColor = toHex(guessBgColor(el));
|
|
41
|
-
|
|
42
|
-
console.log("[Slide] guess bg color", cachedBgColor);
|
|
43
|
-
}
|
|
42
|
+
log("[Slide] guess bg color", cachedBgColor);
|
|
44
43
|
}
|
|
45
44
|
return cachedBgColor;
|
|
46
45
|
}
|
package/src/utils/freezer.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RegisterParams } from "@netless/window-manager";
|
|
2
|
+
import { log } from "./logger";
|
|
2
3
|
|
|
3
4
|
export interface FreezableSlide {
|
|
4
5
|
freeze: () => void;
|
|
@@ -11,24 +12,18 @@ export const apps = {
|
|
|
11
12
|
map: new Map<string, FreezableSlide>(),
|
|
12
13
|
queue: [] as string[],
|
|
13
14
|
validateQueue() {
|
|
14
|
-
|
|
15
|
-
console.log("[Slide] freezer: validate", this.queue);
|
|
16
|
-
}
|
|
15
|
+
log("[Slide] freezer: validate", this.queue);
|
|
17
16
|
while (this.queue.length > FreezerLength) {
|
|
18
17
|
const appId = this.queue.pop() as string;
|
|
19
18
|
const slide = this.map.get(appId);
|
|
20
19
|
if (slide) {
|
|
21
|
-
|
|
22
|
-
console.log("[Slide] freezer: validate-freeze", appId, this.queue);
|
|
23
|
-
}
|
|
20
|
+
log("[Slide] freezer: validate-freeze", appId, this.queue);
|
|
24
21
|
slide.freeze();
|
|
25
22
|
}
|
|
26
23
|
}
|
|
27
24
|
},
|
|
28
25
|
set(appId: string, slide: FreezableSlide) {
|
|
29
|
-
|
|
30
|
-
console.log("[Slide] freezer: add", appId, this.queue);
|
|
31
|
-
}
|
|
26
|
+
log("[Slide] freezer: add", appId, this.queue);
|
|
32
27
|
this.map.set(appId, slide);
|
|
33
28
|
if (!this.queue.includes(appId)) {
|
|
34
29
|
this.queue.unshift(appId);
|
|
@@ -36,9 +31,7 @@ export const apps = {
|
|
|
36
31
|
this.validateQueue();
|
|
37
32
|
},
|
|
38
33
|
delete(appId: string) {
|
|
39
|
-
|
|
40
|
-
console.log("[Slide] freezer: delete", appId, this.queue);
|
|
41
|
-
}
|
|
34
|
+
log("[Slide] freezer: delete", appId, this.queue);
|
|
42
35
|
this.map.delete(appId);
|
|
43
36
|
this.queue = this.queue.filter(id => id !== appId);
|
|
44
37
|
},
|
|
@@ -50,9 +43,7 @@ export const apps = {
|
|
|
50
43
|
}
|
|
51
44
|
this.queue.unshift(appId);
|
|
52
45
|
this.validateQueue();
|
|
53
|
-
|
|
54
|
-
console.log("[Slide] freezer: focus", appId, this.queue);
|
|
55
|
-
}
|
|
46
|
+
log("[Slide] freezer: focus", appId, this.queue);
|
|
56
47
|
if (slide) {
|
|
57
48
|
slide.unfreeze();
|
|
58
49
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SlideController } from "../SlideController";
|
|
2
|
+
import { isObj } from "./helpers";
|
|
3
|
+
|
|
4
|
+
class Logger {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
public apps: Record<string, { context?: any; controller?: SlideController }> = {};
|
|
7
|
+
public level: "debug" | "verbose" = import.meta.env.DEV ? "verbose" : "debug";
|
|
8
|
+
constructor(public enable: boolean) {
|
|
9
|
+
window.addEventListener("message", this._onMessage);
|
|
10
|
+
}
|
|
11
|
+
log(...args: Parameters<Console["log"]>) {
|
|
12
|
+
if (this.enable) {
|
|
13
|
+
console.log(...args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
verbose(...args: Parameters<Console["log"]>) {
|
|
17
|
+
if (this.enable && this.level === "verbose") {
|
|
18
|
+
console.log(...args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
dispose(appId: string) {
|
|
22
|
+
this.enable = false;
|
|
23
|
+
delete this.apps[appId];
|
|
24
|
+
window.removeEventListener("message", this._onMessage);
|
|
25
|
+
}
|
|
26
|
+
_onMessage = (ev: MessageEvent<{ slide: boolean | "__instance" }>) => {
|
|
27
|
+
if (isObj(ev.data)) {
|
|
28
|
+
if (typeof ev.data.slide === "boolean") {
|
|
29
|
+
this.enable = ev.data.slide;
|
|
30
|
+
} else if (ev.data.slide === "__instance") {
|
|
31
|
+
console.log(this);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const logger = new Logger(import.meta.env.DEV);
|
|
38
|
+
export const log = logger.log.bind(logger);
|
|
39
|
+
export const verbose = logger.verbose.bind(logger);
|