@netless/app-slide 0.1.5 → 0.2.1

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.
@@ -1,18 +1,30 @@
1
+ import type { AppContext, Displayer } from "@netless/window-manager";
1
2
  import type { SlideController } from "../SlideController";
2
3
  declare type DebugMessage = {
3
4
  slide: boolean | "__instance" | "__debug";
4
5
  };
6
+ declare type RoomLogger = {
7
+ debug: (...args: any) => void;
8
+ info: (...args: any) => void;
9
+ warn: (...args: any) => void;
10
+ };
5
11
  declare class Logger {
6
12
  enable: boolean;
7
13
  apps: Record<string, {
8
- context?: any;
9
- controller?: SlideController;
14
+ context?: AppContext | null;
15
+ controller?: SlideController | null;
10
16
  }>;
17
+ setAppContext(appId: string, context: AppContext | null): void;
18
+ setAppController(appId: string, controller: SlideController | null): void;
19
+ deleteApp(appId: string): void;
11
20
  level: "debug" | "verbose";
21
+ roomLogger: RoomLogger | null;
12
22
  constructor(enable: boolean);
13
- log(...args: Parameters<Console["log"]>): void;
14
- verbose(...args: Parameters<Console["log"]>): void;
15
- dispose(appId: string): void;
23
+ initialize(): void;
24
+ dispose(): void;
25
+ log(...args: any[]): void;
26
+ verbose(...args: any[]): void;
27
+ warn(...args: any[]): void;
16
28
  /**
17
29
  * @example
18
30
  * window.postMessage({ slide: '__debug' })
@@ -21,6 +33,7 @@ declare class Logger {
21
33
  _onMessage: (ev: MessageEvent<DebugMessage> | CustomEvent<DebugMessage>) => void;
22
34
  }
23
35
  export declare const logger: Logger;
24
- export declare const log: (...args: Parameters<Console["log"]>) => void;
25
- export declare const verbose: (...args: Parameters<Console["log"]>) => void;
36
+ export declare const log: (...args: any[]) => void;
37
+ export declare const verbose: (...args: any[]) => void;
38
+ export declare const setRoomLogger: (displayer: Displayer) => void;
26
39
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/app-slide",
3
- "version": "0.1.5",
3
+ "version": "0.2.1",
4
4
  "main": "dist/main.cjs.js",
5
5
  "module": "dist/main.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,17 +11,16 @@
11
11
  ],
12
12
  "devDependencies": {
13
13
  "@netless/app-shared": "^0.1.1",
14
- "@netless/slide": "^0.2.10",
14
+ "@netless/slide": "^0.3.6",
15
15
  "@types/color-string": "^1.5.2",
16
16
  "color-string": "^1.9.0",
17
17
  "side-effect-manager": "^0.1.5",
18
- "vanilla-lazyload": "^17.6.1"
18
+ "vanilla-lazyload": "^17.8.2"
19
19
  },
20
20
  "scripts": {
21
21
  "types": "cross-env NODE_ENV=production tsc --declaration --emitDeclarationOnly --outDir dist",
22
22
  "build": "vite build && npm run types",
23
23
  "build:dev": "vite build --mode development && npm run types",
24
24
  "cleanup": "rimraf ./dist"
25
- },
26
- "readme": "## @netless/app-slide\n\nNetless App for viewing animated slides, in our new engine.\n\n[中文](./README-zh.md)\n\n### Usage\n\n```ts\nimport type { Attributes as SlideAttributes } from \"@netless/app-slide\";\n\n// 1. register before joining room\nWindowManager.register({\n kind: \"Slide\",\n appOptions: {\n // turn on to show debug controller\n debug: false,\n },\n src: async () => {\n const app = await import(\"@netless/app-slide\");\n return app.default ?? app;\n },\n});\n\n// 2. when joined room, add ppt to whiteboard\nmanager.addApp({\n kind: \"Slide\",\n options: {\n scenePath: `/ppt/${uuid}`, // [1]\n title: \"a.pptx\",\n },\n attributes: {\n taskId: \"1234567...\", // [2]\n url: \"https://convertcdn.netless.link/dynamicConvert\", // [3]\n } as SlideAttributes,\n});\n```\n\nParameters:\n\n1. (**required**) `scenePath`\n\n Which scene to be put on top of slides.\n\n2. (**required**) `taskId`\n\n The [PPT conversion](https://developer.netless.link/server-en/home/server-conversion) task id.\n\n3. (optional) `url`\n\n The base url of ppt resources, `https://convertcdn.netless.link/dynamicConvert` by default.\n\n### Preview\n\n```ts\nimport { previewSlide } from \"@netless/app-slide\";\n\nconst previewer = previewSlide({\n container: document.getElementById(\"preview\"),\n taskId: \"1234567...\",\n});\n\npreviewer.destroy();\n```\n\n### License\n\nMIT @ [netless](https://github.com/netless-io)\n"
25
+ }
27
26
  }
@@ -8,7 +8,7 @@
8
8
  // 4. automatically re-create scenes to sync strokes, a view must be existing
9
9
  // 5. pages information are loaded dynamically by the slide package
10
10
 
11
- import type { AppContext, Player, Room } from "@netless/window-manager";
11
+ import type { AppContext, Player, Room, Displayer } from "@netless/window-manager";
12
12
  import type { SyncEvent } from "@netless/slide";
13
13
  import type { Attributes, MagixEvents, MagixPayload, SlideState } from "../typings";
14
14
  import type { AppOptions } from "..";
@@ -17,7 +17,7 @@ import { SideEffectManager } from "side-effect-manager";
17
17
  import { Slide, SLIDE_EVENTS } from "@netless/slide";
18
18
  import { clamp } from "../utils/helpers";
19
19
  import { cachedGetBgColor } from "../utils/bgcolor";
20
- import { logger, log, verbose } from "../utils/logger";
20
+ import { logger, log, verbose, setRoomLogger } from "../utils/logger";
21
21
  export { syncSceneWithSlide, createDocsViewerPages } from "./helpers";
22
22
 
23
23
  export const DefaultUrl = "https://convertcdn.netless.link/dynamicConvert";
@@ -71,10 +71,13 @@ export class SlideController {
71
71
  this.onTransitionStart = onTransitionStart;
72
72
  this.onTransitionEnd = onTransitionEnd;
73
73
  this.onError = onError;
74
+
74
75
  this.context = context;
75
76
  this.room = context.getRoom();
76
77
  this.player = this.room ? undefined : (context.getDisplayer() as Player);
78
+ setRoomLogger((this.room || this.player) as Displayer);
77
79
  this.slide = this.createSlide(anchor);
80
+
78
81
  // the adder does not need to sync state
79
82
  this.syncStateOnceFlag = !this.context.isAddApp;
80
83
  this.visible = document.visibilityState === "visible";
@@ -85,11 +88,16 @@ export class SlideController {
85
88
  public ready = false;
86
89
  private resolveReady!: () => void;
87
90
  public readonly readyPromise = new Promise<void>(resolve => {
88
- this.resolveReady = () =>
89
- setTimeout(() => {
90
- this.ready = true;
91
- resolve();
92
- }, 1000);
91
+ this.resolveReady = () => {
92
+ if (this.ready) {
93
+ log("[Slide] render end");
94
+ } else {
95
+ setTimeout(() => {
96
+ this.ready = true;
97
+ resolve();
98
+ }, 1000);
99
+ }
100
+ };
93
101
  });
94
102
 
95
103
  public jumpToPage(page: number) {
@@ -113,7 +121,7 @@ export class SlideController {
113
121
  slide.setResource(taskId, url || DefaultUrl);
114
122
  if (state) {
115
123
  // if we already have state, try restore from it
116
- log("[Slide] init with state", state);
124
+ log("[Slide] init with state", JSON.stringify(state));
117
125
  this.syncStateOnceFlag = false;
118
126
  slide.setSlideState(state);
119
127
  } else if (context.isAddApp) {
@@ -171,7 +179,7 @@ export class SlideController {
171
179
  type: SLIDE_EVENTS.syncDispatch,
172
180
  payload: event,
173
181
  };
174
- log("[Slide] dispatch", event);
182
+ verbose("[Slide] dispatch", JSON.stringify(event));
175
183
  this.context.dispatchMagixEvent(SLIDE_EVENTS.syncDispatch, payload);
176
184
  }
177
185
  };
@@ -180,7 +188,7 @@ export class SlideController {
180
188
  const { type, payload } = ev.payload;
181
189
  if (type === SLIDE_EVENTS.syncDispatch) {
182
190
  this.syncStateOnce();
183
- log("[Slide] receive", payload);
191
+ verbose("[Slide] receive", JSON.stringify(payload));
184
192
  this.slide.emit(SLIDE_EVENTS.syncReceive, payload);
185
193
  }
186
194
  };
@@ -193,7 +201,7 @@ export class SlideController {
193
201
  }
194
202
  const { state } = this.context.storage.state;
195
203
  if (state) {
196
- log("[Slide] sync with state (once)", state);
204
+ log("[Slide] sync with state (once)", JSON.stringify(state));
197
205
  this.slide.setSlideState(state);
198
206
  this.syncStateOnceFlag = false;
199
207
  }
@@ -220,7 +228,7 @@ export class SlideController {
220
228
  setTimeout(this.pollReadyState, 500);
221
229
  } else {
222
230
  this.pollCount = 0;
223
- log("[Slide] renderSlide (retry after timeout)", 1);
231
+ log("[Slide] renderSlide 1 (retry after timeout)");
224
232
  this.slide.renderSlide(1);
225
233
  }
226
234
  };
@@ -4,6 +4,7 @@ import type { SlideController, SlideControllerOptions } from "../SlideController
4
4
  import { SideEffectManager } from "side-effect-manager";
5
5
  import { createDocsViewerPages } from "../SlideController";
6
6
  import { DocsViewer } from "../DocsViewer";
7
+ import { logger } from "../utils/logger";
7
8
 
8
9
  export const ClickThroughAppliances = new Set(["clicker"]);
9
10
 
@@ -119,7 +120,7 @@ export class SlideDocsViewer {
119
120
 
120
121
  protected onError = ({ error }: { error: Error }) => {
121
122
  this.viewer.setPaused();
122
- console.warn("[Slide] render error", error);
123
+ logger.warn("[Slide] render error", error);
123
124
  };
124
125
 
125
126
  protected refreshPages = () => {
@@ -4,7 +4,6 @@ import { DocsViewer } from "../DocsViewer";
4
4
  import { createDocsViewerPages, DefaultUrl } from "../SlideController";
5
5
  import { cachedGetBgColor } from "../utils/bgcolor";
6
6
  import { clamp } from "../utils/helpers";
7
- import { log } from "../utils/logger";
8
7
  import style from "../style.scss?inline";
9
8
 
10
9
  export interface PreviewParams {
@@ -180,7 +179,6 @@ export class SlidePreviewer {
180
179
  public destroy() {
181
180
  this.sideEffect.flushAll();
182
181
  if (this.slide && !this.destroyed) {
183
- log("[Slide] destroy slide (once)");
184
182
  this.slide.destroy();
185
183
  this.destroyed = true;
186
184
  }
package/src/index.ts CHANGED
@@ -50,6 +50,7 @@ export interface Controller {
50
50
  prevStep: () => boolean;
51
51
  nextPage: () => boolean;
52
52
  prevPage: () => boolean;
53
+ jumpToPage: (page: number) => boolean;
53
54
  }
54
55
 
55
56
  const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
@@ -92,7 +93,7 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
92
93
  syncSceneWithSlide(room, context, docsViewer.slideController.slide, baseScenePath);
93
94
  synced = true;
94
95
  }
95
- log("[Slide] page to", page, synced);
96
+ log("[Slide] page to", page, synced ? "(synced)" : "");
96
97
  docsViewer.viewer.setPageIndex(page - 1);
97
98
  }
98
99
  };
@@ -104,7 +105,7 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
104
105
  onPageChanged,
105
106
  });
106
107
  if (useFreezer) apps.set(context.appId, slideController);
107
- (logger.apps[context.appId] ||= {}).controller = slideController;
108
+ logger.setAppController(context.appId, slideController);
108
109
  if (import.meta.env.DEV) {
109
110
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
111
  (window as any).slideController = slideController;
@@ -116,7 +117,8 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
116
117
  syncSceneWithSlide(room, context, slideController.slide, baseScenePath);
117
118
  synced = true;
118
119
  }
119
- log("[Slide] page to", slideController.slide.slideState.currentSlideIndex, synced);
120
+ const page = slideController.slide.slideState.currentSlideIndex;
121
+ log("[Slide] page to", page, synced ? "(synced)" : "", "(on ready)");
120
122
  });
121
123
  return slideController;
122
124
  };
@@ -137,10 +139,10 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
137
139
  const sideEffect = new SideEffectManager();
138
140
 
139
141
  sideEffect.add(() => {
140
- (logger.apps[context.appId] ||= {}).context = context;
142
+ logger.setAppContext(context.appId, context);
141
143
  logger.enable = context.getAppOptions()?.debug || import.meta.env.DEV;
142
144
  logger.level = import.meta.env.DEV ? "verbose" : "debug";
143
- return () => logger.dispose(context.appId);
145
+ return () => logger.deleteApp(context.appId);
144
146
  });
145
147
 
146
148
  if (room) {
@@ -157,7 +159,7 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
157
159
  }
158
160
 
159
161
  context.emitter.on("destroy", () => {
160
- log("[Slide]: destroy");
162
+ log("[Slide] destroy", context.appId);
161
163
  if (useFreezer) apps.delete(context.appId);
162
164
  sideEffect.flushAll();
163
165
  if (docsViewer) {
@@ -214,6 +216,17 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
214
216
  }
215
217
  return false;
216
218
  },
219
+ jumpToPage: (newPage: number) => {
220
+ const controller = docsViewer?.slideController;
221
+ if (controller) {
222
+ const { page, pageCount } = controller;
223
+ if (pageCount > 0 && page > 0 && page <= pageCount) {
224
+ controller.jumpToPage(newPage);
225
+ return true;
226
+ }
227
+ }
228
+ return false;
229
+ },
217
230
  };
218
231
  },
219
232
  };
@@ -9,22 +9,26 @@ export interface FreezableSlide {
9
9
  export let useFreezer = false;
10
10
  export const FreezerLength = 2;
11
11
 
12
+ const inspect = (arr: string[]) => {
13
+ return "[" + arr + "]";
14
+ };
15
+
12
16
  export const apps = {
13
17
  map: new Map<string, FreezableSlide>(),
14
18
  queue: [] as string[],
15
19
  validateQueue() {
16
- log("[Slide] freezer: validate", this.queue);
20
+ log("[Slide] freezer: validate", inspect(this.queue));
17
21
  while (this.queue.length > FreezerLength) {
18
22
  const appId = this.queue.pop() as string;
19
23
  const slide = this.map.get(appId);
20
24
  if (slide) {
21
- log("[Slide] freezer: validate-freeze", appId, this.queue);
25
+ log("[Slide] freezer: validate-freeze", appId, inspect(this.queue));
22
26
  slide.freeze();
23
27
  }
24
28
  }
25
29
  },
26
30
  set(appId: string, slide: FreezableSlide) {
27
- log("[Slide] freezer: add", appId, this.queue);
31
+ log("[Slide] freezer: add", appId, inspect(this.queue));
28
32
  this.map.set(appId, slide);
29
33
  if (!this.queue.includes(appId)) {
30
34
  this.queue.unshift(appId);
@@ -32,9 +36,9 @@ export const apps = {
32
36
  this.validateQueue();
33
37
  },
34
38
  delete(appId: string) {
35
- log("[Slide] freezer: delete", appId, this.queue);
36
39
  this.map.delete(appId);
37
40
  this.queue = this.queue.filter(id => id !== appId);
41
+ log("[Slide] freezer: delete", appId, inspect(this.queue));
38
42
  },
39
43
  focus(appId: string) {
40
44
  const slide = this.map.get(appId);
@@ -44,7 +48,7 @@ export const apps = {
44
48
  }
45
49
  this.queue.unshift(appId);
46
50
  this.validateQueue();
47
- log("[Slide] freezer: focus", appId, this.queue);
51
+ log("[Slide] freezer: focus", appId, inspect(this.queue));
48
52
  if (slide) {
49
53
  slide.unfreeze();
50
54
  }
@@ -1,30 +1,76 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { AppContext, Displayer } from "@netless/window-manager";
1
3
  import type { SlideController } from "../SlideController";
2
4
  import { isObj } from "./helpers";
3
5
 
4
6
  type DebugMessage = { slide: boolean | "__instance" | "__debug" };
5
7
 
8
+ type RoomLogger = {
9
+ debug: (...args: any) => void;
10
+ info: (...args: any) => void;
11
+ warn: (...args: any) => void;
12
+ };
13
+
6
14
  class Logger {
7
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
- public apps: Record<string, { context?: any; controller?: SlideController }> = {};
15
+ public apps: Record<
16
+ string,
17
+ { context?: AppContext | null; controller?: SlideController | null }
18
+ > = {};
19
+
20
+ setAppContext(appId: string, context: AppContext | null) {
21
+ (this.apps[appId] ||= {}).context = context;
22
+ this.log(`[Slide] new ${appId}`);
23
+ }
24
+
25
+ setAppController(appId: string, controller: SlideController | null) {
26
+ (this.apps[appId] ||= {}).controller = controller;
27
+ }
28
+
29
+ deleteApp(appId: string) {
30
+ delete this.apps[appId];
31
+ this.log(`[Slide] delete ${appId}`);
32
+ }
33
+
9
34
  public level: "debug" | "verbose" = import.meta.env.DEV ? "verbose" : "debug";
35
+
36
+ public roomLogger: RoomLogger | null = null;
37
+
10
38
  constructor(public enable: boolean) {
39
+ this.initialize();
40
+ }
41
+
42
+ initialize() {
11
43
  window.addEventListener("message", this._onMessage);
12
44
  }
13
- log(...args: Parameters<Console["log"]>) {
14
- if (this.enable) {
45
+
46
+ dispose() {
47
+ window.removeEventListener("message", this._onMessage);
48
+ }
49
+
50
+ log(...args: any[]) {
51
+ if (this.roomLogger) {
52
+ this.roomLogger.info(...args);
53
+ } else if (this.enable) {
15
54
  console.log(...args);
16
55
  }
17
56
  }
18
- verbose(...args: Parameters<Console["log"]>) {
19
- if (this.enable && this.level === "verbose") {
57
+
58
+ verbose(...args: any[]) {
59
+ if (this.roomLogger) {
60
+ this.roomLogger.debug(...args);
61
+ } else if (this.enable && this.level === "verbose") {
20
62
  console.log(...args);
21
63
  }
22
64
  }
23
- dispose(appId: string) {
24
- this.enable = false;
25
- delete this.apps[appId];
26
- window.removeEventListener("message", this._onMessage);
65
+
66
+ warn(...args: any[]) {
67
+ if (this.roomLogger) {
68
+ this.roomLogger.warn(...args);
69
+ } else {
70
+ console.warn(...args);
71
+ }
27
72
  }
73
+
28
74
  /**
29
75
  * @example
30
76
  * window.postMessage({ slide: '__debug' })
@@ -52,6 +98,9 @@ class Logger {
52
98
  };
53
99
  }
54
100
 
55
- export const logger = new Logger(import.meta.env.DEV);
56
- export const log = logger.log.bind(logger);
57
- export const verbose = logger.verbose.bind(logger);
101
+ export const logger = /** @__PURE__ */ new Logger(import.meta.env.DEV);
102
+ export const log = /** @__PURE__ */ logger.log.bind(logger);
103
+ export const verbose = /** @__PURE__ */ logger.verbose.bind(logger);
104
+ export const setRoomLogger = (displayer: Displayer) => {
105
+ logger.roomLogger = (displayer as any).logger;
106
+ };