@netless/app-slide 0.0.28 → 0.0.33
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 +1 -1
- package/dist/SlidePreviewer/index.d.ts +41 -0
- package/dist/index.d.ts +2 -0
- package/dist/main.cjs.js +221 -221
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +268 -84
- 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 +26 -34
- package/src/SlideDocsViewer/index.ts +3 -3
- package/src/SlideDocsViewer/style.scss +1 -1
- package/src/SlidePreviewer/index.ts +176 -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.33",
|
|
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
|
|
@@ -142,6 +137,11 @@ export class SlideController {
|
|
|
142
137
|
return disposer;
|
|
143
138
|
});
|
|
144
139
|
|
|
140
|
+
this.sideEffect.add(() => {
|
|
141
|
+
context.emitter.on("seek", this.onSeeked);
|
|
142
|
+
return () => context.emitter.off("seek", this.onSeeked);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
145
|
this.sideEffect.add(() => {
|
|
146
146
|
const displayer = context.getDisplayer();
|
|
147
147
|
displayer.addMagixEventListener(this.channel, this.magixEventListener, {
|
|
@@ -170,9 +170,7 @@ export class SlideController {
|
|
|
170
170
|
type: SLIDE_EVENTS.syncDispatch,
|
|
171
171
|
payload: event,
|
|
172
172
|
};
|
|
173
|
-
|
|
174
|
-
console.log("[Slide] dispatch", event);
|
|
175
|
-
}
|
|
173
|
+
log("[Slide] dispatch", event);
|
|
176
174
|
this.room.dispatchMagixEvent(this.channel, payload);
|
|
177
175
|
}
|
|
178
176
|
};
|
|
@@ -182,9 +180,7 @@ export class SlideController {
|
|
|
182
180
|
const { type, payload } = ev.payload as MagixPayload;
|
|
183
181
|
if (type === SLIDE_EVENTS.syncDispatch) {
|
|
184
182
|
this.syncStateOnce();
|
|
185
|
-
|
|
186
|
-
console.log("[Slide] receive", payload);
|
|
187
|
-
}
|
|
183
|
+
log("[Slide] receive", payload);
|
|
188
184
|
// only emit the event if the slide state is sync-ed
|
|
189
185
|
if (!this.syncStateOnceFlag) {
|
|
190
186
|
this.slide.emit(SLIDE_EVENTS.syncReceive, payload);
|
|
@@ -198,9 +194,7 @@ export class SlideController {
|
|
|
198
194
|
if (this.syncStateOnceFlag) {
|
|
199
195
|
const { state } = { ...EmptyAttributes, ...this.context.getAttributes() };
|
|
200
196
|
if (state) {
|
|
201
|
-
|
|
202
|
-
console.log("[Slide] sync with state (once)", deepClone(state));
|
|
203
|
-
}
|
|
197
|
+
log("[Slide] sync with state (once)", deepClone(state));
|
|
204
198
|
this.slide.setSlideState(deepClone(state));
|
|
205
199
|
this.syncStateOnceFlag = false;
|
|
206
200
|
}
|
|
@@ -209,13 +203,19 @@ export class SlideController {
|
|
|
209
203
|
|
|
210
204
|
private onStateChange = (state: SlideState) => {
|
|
211
205
|
if (this.context.getIsWritable()) {
|
|
212
|
-
|
|
213
|
-
console.log("[Slide] state change", JSON.stringify(state, null, 2));
|
|
214
|
-
}
|
|
206
|
+
verbose("[Slide] state change", JSON.stringify(state, null, 2));
|
|
215
207
|
this.context.updateAttributes(["state"], state);
|
|
216
208
|
}
|
|
217
209
|
};
|
|
218
210
|
|
|
211
|
+
private onSeeked = () => {
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
213
|
+
const state = this.context.getAttributes()!.state;
|
|
214
|
+
if (state) {
|
|
215
|
+
this.slide.setSlideState(deepClone(state));
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
219
|
private pollCount = 0;
|
|
220
220
|
private pollReadyState = () => {
|
|
221
221
|
if (this.ready) {
|
|
@@ -229,9 +229,7 @@ export class SlideController {
|
|
|
229
229
|
setTimeout(this.pollReadyState, 500);
|
|
230
230
|
} else {
|
|
231
231
|
this.pollCount = 0;
|
|
232
|
-
|
|
233
|
-
console.log("[Slide] renderSlide (retry after timeout)", 1);
|
|
234
|
-
}
|
|
232
|
+
log("[Slide] renderSlide (retry after timeout)", 1);
|
|
235
233
|
this.slide.renderSlide(1);
|
|
236
234
|
}
|
|
237
235
|
};
|
|
@@ -255,7 +253,7 @@ export class SlideController {
|
|
|
255
253
|
interactive: true,
|
|
256
254
|
mode: "interactive",
|
|
257
255
|
resize: true,
|
|
258
|
-
controller:
|
|
256
|
+
controller: logger.enable,
|
|
259
257
|
renderOptions: {
|
|
260
258
|
minFPS: 25,
|
|
261
259
|
maxFPS: 30,
|
|
@@ -266,7 +264,7 @@ export class SlideController {
|
|
|
266
264
|
},
|
|
267
265
|
timestamp: this.timestamp,
|
|
268
266
|
});
|
|
269
|
-
if (
|
|
267
|
+
if (import.meta.env.DEV) {
|
|
270
268
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
271
269
|
(window as any).slide = slide;
|
|
272
270
|
}
|
|
@@ -278,9 +276,7 @@ export class SlideController {
|
|
|
278
276
|
public destroy() {
|
|
279
277
|
this.sideEffect.flushAll();
|
|
280
278
|
if (!this.destroyed) {
|
|
281
|
-
|
|
282
|
-
console.log("[Slide] destroy slide (once)");
|
|
283
|
-
}
|
|
279
|
+
log("[Slide] destroy slide (once)");
|
|
284
280
|
this.slide.destroy();
|
|
285
281
|
this.destroyed = true;
|
|
286
282
|
}
|
|
@@ -318,9 +314,7 @@ export class SlideController {
|
|
|
318
314
|
|
|
319
315
|
public freeze = () => {
|
|
320
316
|
if (this.ready) {
|
|
321
|
-
|
|
322
|
-
console.log("[Slide] freeze", this.context.appId);
|
|
323
|
-
}
|
|
317
|
+
log("[Slide] freeze", this.context.appId);
|
|
324
318
|
if (this.freezePromise) {
|
|
325
319
|
this._toFreeze = 1;
|
|
326
320
|
} else if (!this.isFrozen) {
|
|
@@ -334,9 +328,7 @@ export class SlideController {
|
|
|
334
328
|
|
|
335
329
|
public unfreeze = async () => {
|
|
336
330
|
if (this.ready) {
|
|
337
|
-
|
|
338
|
-
console.log("[Slide] unfreeze", this.context.appId);
|
|
339
|
-
}
|
|
331
|
+
log("[Slide] unfreeze", this.context.appId);
|
|
340
332
|
if (this.freezePromise) {
|
|
341
333
|
this._toFreeze = -1;
|
|
342
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,176 @@
|
|
|
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
|
+
|
|
8
|
+
export interface PreviewParams {
|
|
9
|
+
container: HTMLElement;
|
|
10
|
+
taskId: string;
|
|
11
|
+
url?: string;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function previewSlide({
|
|
16
|
+
container,
|
|
17
|
+
taskId,
|
|
18
|
+
url = DefaultUrl,
|
|
19
|
+
debug = import.meta.env.DEV,
|
|
20
|
+
}: PreviewParams) {
|
|
21
|
+
if (!taskId) {
|
|
22
|
+
throw new Error("[Slide] taskId is required");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
container.style.cssText += `display:flex;flex-direction:column`;
|
|
26
|
+
const previewer = new SlidePreviewer({ target: container });
|
|
27
|
+
previewer.debug = !!debug;
|
|
28
|
+
|
|
29
|
+
previewer.mount(taskId, url);
|
|
30
|
+
|
|
31
|
+
return previewer;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (import.meta.env.DEV) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
(window as any).previewSlide = previewSlide;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class SlidePreviewer {
|
|
40
|
+
public readonly viewer: DocsViewer;
|
|
41
|
+
public readonly bgColor: string;
|
|
42
|
+
public readonly target: HTMLElement;
|
|
43
|
+
public slide: Slide | null = null;
|
|
44
|
+
public debug = import.meta.env.DEV;
|
|
45
|
+
|
|
46
|
+
public $slide!: HTMLDivElement;
|
|
47
|
+
public ready = false;
|
|
48
|
+
private resolveReady!: () => void;
|
|
49
|
+
public readonly readyPromise = new Promise<void>((resolve) => {
|
|
50
|
+
this.resolveReady = () => {
|
|
51
|
+
this.ready = true;
|
|
52
|
+
resolve();
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
public constructor(config: { target: HTMLElement }) {
|
|
57
|
+
this.target = config.target;
|
|
58
|
+
this.bgColor = cachedGetBgColor(this.target);
|
|
59
|
+
this.viewer = new DocsViewer({
|
|
60
|
+
readonly: false,
|
|
61
|
+
onNewPageIndex: this.onNewPageIndex,
|
|
62
|
+
onPlay: this.onPlay,
|
|
63
|
+
});
|
|
64
|
+
this.render();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public render() {
|
|
68
|
+
this.viewer.$content.appendChild(this.renderSlideContainer());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public mount(taskId: string, url: string) {
|
|
72
|
+
this.target.appendChild(this.viewer.$content);
|
|
73
|
+
this.target.appendChild(this.viewer.$footer);
|
|
74
|
+
|
|
75
|
+
this.slide = new Slide({
|
|
76
|
+
anchor: this.$slide,
|
|
77
|
+
interactive: false,
|
|
78
|
+
mode: "local",
|
|
79
|
+
resize: true,
|
|
80
|
+
controller: this.debug,
|
|
81
|
+
renderOptions: {
|
|
82
|
+
minFPS: 25,
|
|
83
|
+
maxFPS: 30,
|
|
84
|
+
autoFPS: true,
|
|
85
|
+
autoResolution: true,
|
|
86
|
+
transactionBgColor: this.bgColor,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.registerEventListeners();
|
|
91
|
+
|
|
92
|
+
this.slide.setResource(taskId, url);
|
|
93
|
+
this.slide.renderSlide(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected registerEventListeners() {
|
|
97
|
+
if (!this.slide) return;
|
|
98
|
+
const { slide } = this;
|
|
99
|
+
|
|
100
|
+
slide.on(SLIDE_EVENTS.slideChange, this.onPageChanged);
|
|
101
|
+
slide.on(SLIDE_EVENTS.renderStart, this.onTransitionStart);
|
|
102
|
+
slide.on(SLIDE_EVENTS.renderEnd, this.onTransitionEnd);
|
|
103
|
+
slide.on(SLIDE_EVENTS.mainSeqStepStart, this.onTransitionStart);
|
|
104
|
+
slide.on(SLIDE_EVENTS.mainSeqStepEnd, this.onTransitionEnd);
|
|
105
|
+
slide.on(SLIDE_EVENTS.renderError, this.onError);
|
|
106
|
+
slide.on(SLIDE_EVENTS.renderEnd, this.resolveReady);
|
|
107
|
+
|
|
108
|
+
this.readyPromise.then(this.refreshPages);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected onPageChanged = (page: number) => {
|
|
112
|
+
this.viewer.setPageIndex(page - 1);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
protected onTransitionStart = () => {
|
|
116
|
+
this.viewer.setPlaying();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
protected onTransitionEnd = () => {
|
|
120
|
+
this.viewer.setPaused();
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
protected onError = ({ error }: { error: Error }) => {
|
|
124
|
+
this.viewer.setPaused();
|
|
125
|
+
console.warn("[Slide] render error", error);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
private destroyed = false;
|
|
129
|
+
public destroy() {
|
|
130
|
+
if (this.slide && !this.destroyed) {
|
|
131
|
+
log("[Slide] destroy slide (once)");
|
|
132
|
+
this.slide.destroy();
|
|
133
|
+
this.destroyed = true;
|
|
134
|
+
}
|
|
135
|
+
this.viewer.destroy();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
protected refreshPages = () => {
|
|
139
|
+
if (this.slide) {
|
|
140
|
+
this.viewer.pages = createDocsViewerPages(this.slide);
|
|
141
|
+
this.viewer.setPageIndex(this.getPageIndex(this.slide.slideCount));
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
protected getPageIndex(page: number) {
|
|
146
|
+
return (page > 0 ? page : 1) - 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected renderSlideContainer(): HTMLDivElement {
|
|
150
|
+
if (!this.$slide) {
|
|
151
|
+
const $slide = document.createElement("div");
|
|
152
|
+
$slide.className = this.wrapClassName("slide");
|
|
153
|
+
$slide.dataset.appKind = "Slide";
|
|
154
|
+
this.$slide = $slide;
|
|
155
|
+
}
|
|
156
|
+
return this.$slide;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected onPlay = () => {
|
|
160
|
+
if (this.slide) {
|
|
161
|
+
this.slide.nextStep();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
protected onNewPageIndex = (index: number) => {
|
|
166
|
+
if (this.slide && this.slide.slideCount > 0) {
|
|
167
|
+
this.slide.renderSlide(clamp(index + 1, 1, this.slide.slideCount));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
protected wrapClassName(className: string) {
|
|
172
|
+
return `${this.namespace}-${className}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected namespace = "netless-app-slide";
|
|
176
|
+
}
|
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);
|