@netless/app-slide 0.0.55 → 0.0.58

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/typings.d.ts CHANGED
@@ -12,3 +12,8 @@ export declare type MagixPayload = {
12
12
  type: typeof SLIDE_EVENTS.syncDispatch;
13
13
  payload: SyncEvent;
14
14
  };
15
+ export declare type MagixEvents = {
16
+ [SLIDE_EVENTS.syncDispatch]: MagixPayload;
17
+ };
18
+ export interface A {
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/app-slide",
3
- "version": "0.0.55",
3
+ "version": "0.0.58",
4
4
  "main": "dist/main.cjs.js",
5
5
  "module": "dist/main.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "devDependencies": {
13
13
  "@netless/app-shared": "^0.1.1",
14
- "@netless/slide": "^0.2.2",
14
+ "@netless/slide": "^0.2.7",
15
15
  "@types/color-string": "^1.5.2",
16
16
  "color-string": "^1.9.0",
17
17
  "side-effect-manager": "^0.1.5",
@@ -298,6 +298,9 @@ export class DocsViewer {
298
298
  $pageNumberInput.disabled = true;
299
299
  }
300
300
  this.$pageNumberInput = $pageNumberInput;
301
+ this.sideEffect.addEventListener($pageNumberInput, "focus", () => {
302
+ $pageNumberInput.select();
303
+ });
301
304
  this.sideEffect.addEventListener($pageNumberInput, "change", () => {
302
305
  if (this.readonly) {
303
306
  return;
@@ -176,7 +176,7 @@ $namespace: $slide-namespace;
176
176
  .#{$namespace}-page-number-input {
177
177
  border: none;
178
178
  outline: none;
179
- width: 1.5em;
179
+ width: 3em;
180
180
  margin: 0;
181
181
  padding: 0 2px;
182
182
  text-align: right;
@@ -8,14 +8,13 @@
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 { Event as MagixEvent } from "white-web-sdk";
12
11
  import type { AppContext, Player, Room } from "@netless/window-manager";
13
12
  import type { SyncEvent } from "@netless/slide";
14
- import type { Attributes, MagixPayload, SlideState } from "../typings";
13
+ import type { Attributes, MagixEvents, MagixPayload, SlideState } from "../typings";
15
14
 
16
15
  import { SideEffectManager } from "side-effect-manager";
17
16
  import { Slide, SLIDE_EVENTS } from "@netless/slide";
18
- import { clamp, deepClone, isObj } from "../utils/helpers";
17
+ import { clamp, deepClone } from "../utils/helpers";
19
18
  import { cachedGetBgColor } from "../utils/bgcolor";
20
19
  import { logger, log, verbose } from "../utils/logger";
21
20
  export { syncSceneWithSlide, createDocsViewerPages } from "./helpers";
@@ -29,7 +28,7 @@ export const EmptyAttributes: Attributes = {
29
28
  };
30
29
 
31
30
  export interface SlideControllerOptions {
32
- context: AppContext<Attributes>;
31
+ context: AppContext<Attributes, MagixEvents>;
33
32
  anchor: HTMLDivElement;
34
33
  onPageChanged: (page: number) => void;
35
34
  onTransitionStart: () => void;
@@ -37,11 +36,14 @@ export interface SlideControllerOptions {
37
36
  onError: (args: { error: Error }) => void;
38
37
  }
39
38
 
39
+ type MagixEventListener = Parameters<
40
+ AppContext<Attributes, MagixEvents>["addMagixEventListener"]
41
+ >[1];
42
+
40
43
  export class SlideController {
41
44
  public readonly context: SlideControllerOptions["context"];
42
45
  public readonly slide: Slide;
43
46
 
44
- private readonly channel: string;
45
47
  private readonly room?: Room;
46
48
  private readonly player?: Player;
47
49
  private readonly sideEffect = new SideEffectManager();
@@ -66,7 +68,6 @@ export class SlideController {
66
68
  this.onTransitionEnd = onTransitionEnd;
67
69
  this.onError = onError;
68
70
  this.context = context;
69
- this.channel = `channel-${context.appId}`;
70
71
  this.room = context.getRoom();
71
72
  this.player = this.room ? undefined : (context.getDisplayer() as Player);
72
73
  this.slide = this.createSlide(anchor);
@@ -137,20 +138,11 @@ export class SlideController {
137
138
  return disposer;
138
139
  });
139
140
 
140
- this.sideEffect.add(() => {
141
- context.emitter.on("seek", this.onSeeked);
142
- return () => context.emitter.off("seek", this.onSeeked);
143
- });
144
-
145
- this.sideEffect.add(() => {
146
- const displayer = context.getDisplayer();
147
- displayer.addMagixEventListener(this.channel, this.magixEventListener, {
141
+ this.sideEffect.add(() =>
142
+ context.addMagixEventListener(SLIDE_EVENTS.syncDispatch, this.magixEventListener, {
148
143
  fireSelfEventAfterCommit: true,
149
- });
150
- // here we do not pass second param -- the listener
151
- // because channel name is already unique, just make it simpler
152
- return () => displayer.removeMagixEventListener(this.channel);
153
- });
144
+ })
145
+ );
154
146
 
155
147
  slide.on(SLIDE_EVENTS.slideChange, this.onPageChanged);
156
148
  slide.on(SLIDE_EVENTS.renderStart, this.onTransitionStart);
@@ -171,25 +163,23 @@ export class SlideController {
171
163
  payload: event,
172
164
  };
173
165
  log("[Slide] dispatch", event);
174
- this.room.dispatchMagixEvent(this.channel, payload);
166
+ this.context.dispatchMagixEvent(SLIDE_EVENTS.syncDispatch, payload);
175
167
  }
176
168
  };
177
169
 
178
- private magixEventListener = (ev: MagixEvent) => {
179
- if (ev.event === this.channel && isObj(ev.payload)) {
180
- const { type, payload } = ev.payload as MagixPayload;
181
- if (type === SLIDE_EVENTS.syncDispatch) {
182
- this.syncStateOnce();
183
- log("[Slide] receive", payload);
184
- this.slide.emit(SLIDE_EVENTS.syncReceive, payload);
185
- }
170
+ private magixEventListener: MagixEventListener = ev => {
171
+ const { type, payload } = ev.payload;
172
+ if (type === SLIDE_EVENTS.syncDispatch) {
173
+ this.syncStateOnce();
174
+ log("[Slide] receive", payload);
175
+ this.slide.emit(SLIDE_EVENTS.syncReceive, payload);
186
176
  }
187
177
  };
188
178
 
189
179
  private syncStateOnce() {
190
180
  // sync state before the first event, so that they can be in the correct order
191
181
  if (this.syncStateOnceFlag) {
192
- const { state } = { ...EmptyAttributes, ...this.context.getAttributes() };
182
+ const { state } = { ...EmptyAttributes, ...this.context.storage.state };
193
183
  if (state) {
194
184
  log("[Slide] sync with state (once)", deepClone(state));
195
185
  this.slide.setSlideState(deepClone(state));
@@ -201,15 +191,7 @@ export class SlideController {
201
191
  private onStateChange = (state: SlideState) => {
202
192
  if (this.context.getIsWritable()) {
203
193
  verbose("[Slide] state change", JSON.stringify(state, null, 2));
204
- this.context.updateAttributes(["state"], state);
205
- }
206
- };
207
-
208
- private onSeeked = () => {
209
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
210
- const state = this.context.getAttributes()!.state;
211
- if (state) {
212
- this.slide.setSlideState(deepClone(state));
194
+ this.context.storage.setState({ state });
213
195
  }
214
196
  };
215
197
 
package/src/index.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import type { NetlessApp } from "@netless/window-manager";
2
2
  import type { RoomState } from "white-web-sdk";
3
+ import type { Slide } from "@netless/slide";
3
4
  import type { MountSlideOptions } from "./SlideDocsViewer";
4
- import type { Attributes } from "./typings";
5
+ import type { Attributes, MagixEvents } from "./typings";
5
6
  import type { AddHooks, FreezableSlide } from "./utils/freezer";
6
- import { useFreezer } from "./utils/freezer";
7
7
 
8
8
  import { SideEffectManager } from "side-effect-manager";
9
- import { ensureAttributes } from "@netless/app-shared";
10
9
  import {
11
10
  DefaultUrl,
12
11
  EmptyAttributes,
@@ -14,7 +13,7 @@ import {
14
13
  SlideController,
15
14
  } from "./SlideController";
16
15
  import { SlideDocsViewer } from "./SlideDocsViewer";
17
- import { apps, FreezerLength, addHooks } from "./utils/freezer";
16
+ import { apps, FreezerLength, addHooks, useFreezer } from "./utils/freezer";
18
17
  import { log, logger } from "./utils/logger";
19
18
  import styles from "./style.scss?inline";
20
19
 
@@ -27,17 +26,31 @@ export const version = __APP_VERSION__;
27
26
 
28
27
  export { DefaultUrl, apps, FreezerLength, addHooks };
29
28
 
30
- const SlideApp: NetlessApp<Attributes> = {
29
+ export interface AppOptions {
30
+ debug?: boolean;
31
+ resolution?: number;
32
+ bgColor?: string;
33
+ }
34
+
35
+ export interface Controller {
36
+ slide: () => Slide | undefined;
37
+ position: () => [page: number, pageCount: number] | undefined;
38
+ nextStep: () => boolean;
39
+ prevStep: () => boolean;
40
+ nextPage: () => boolean;
41
+ prevPage: () => boolean;
42
+ }
43
+
44
+ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, Controller> = {
31
45
  kind: "Slide",
32
46
  setup(context) {
33
47
  console.log("[Slide] setup @ " + version);
34
48
 
35
49
  if (context.getIsWritable()) {
36
- ensureAttributes(context, EmptyAttributes);
50
+ context.storage.ensureState(EmptyAttributes);
37
51
  }
38
52
 
39
- const attributes = context.getAttributes();
40
- if (!attributes?.taskId) {
53
+ if (!context.storage.state.taskId) {
41
54
  throw new Error("[Slide] no taskId");
42
55
  }
43
56
 
@@ -85,7 +98,15 @@ const SlideApp: NetlessApp<Attributes> = {
85
98
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
99
  (window as any).slideController = slideController;
87
100
  }
88
- slideController.readyPromise.then(options.onReady);
101
+ slideController.readyPromise.then(options.onReady).then(() => {
102
+ const room = context.getRoom();
103
+ let synced = false;
104
+ if (room && context.getIsWritable()) {
105
+ syncSceneWithSlide(room, context, slideController.slide, baseScenePath);
106
+ synced = true;
107
+ }
108
+ log("[Slide] page to", slideController.slide.slideState.currentSlideIndex, synced);
109
+ });
89
110
  return slideController;
90
111
  };
91
112
 
@@ -135,6 +156,54 @@ const SlideApp: NetlessApp<Attributes> = {
135
156
  });
136
157
 
137
158
  docsViewer.mount();
159
+
160
+ return {
161
+ slide: () => {
162
+ return docsViewer?.slideController?.slide;
163
+ },
164
+ nextStep: () => {
165
+ if (docsViewer && docsViewer.slideController) {
166
+ docsViewer?.slideController?.slide.nextStep();
167
+ return true;
168
+ }
169
+ return false;
170
+ },
171
+ prevStep: () => {
172
+ if (docsViewer && docsViewer.slideController) {
173
+ docsViewer?.slideController?.slide.prevStep();
174
+ return true;
175
+ }
176
+ return false;
177
+ },
178
+ position: () => {
179
+ const controller = docsViewer?.slideController;
180
+ if (controller) {
181
+ return [controller.page, controller.pageCount] as [page: number, pageCount: number];
182
+ }
183
+ },
184
+ nextPage: () => {
185
+ const controller = docsViewer?.slideController;
186
+ if (controller) {
187
+ const { page, pageCount } = controller;
188
+ if (pageCount > 0 && page < pageCount) {
189
+ controller.jumpToPage(page + 1);
190
+ return true;
191
+ }
192
+ }
193
+ return false;
194
+ },
195
+ prevPage: () => {
196
+ const controller = docsViewer?.slideController;
197
+ if (controller) {
198
+ const { page, pageCount } = controller;
199
+ if (pageCount > 0 && page > 1) {
200
+ controller.jumpToPage(page - 1);
201
+ return true;
202
+ }
203
+ }
204
+ return false;
205
+ },
206
+ };
138
207
  },
139
208
  };
140
209
 
package/src/typings.ts CHANGED
@@ -15,3 +15,9 @@ export type MagixPayload = {
15
15
  type: typeof SLIDE_EVENTS.syncDispatch;
16
16
  payload: SyncEvent;
17
17
  };
18
+
19
+ export type MagixEvents = {
20
+ [SLIDE_EVENTS.syncDispatch]: MagixPayload;
21
+ };
22
+
23
+ export interface A {}