@netless/app-slide 0.0.54 → 0.0.57

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.54",
3
+ "version": "0.0.57",
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.1",
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,10 +8,9 @@
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";
@@ -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,12 +163,12 @@ 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)) {
170
+ private magixEventListener: MagixEventListener = ev => {
171
+ if (ev.event === SLIDE_EVENTS.syncDispatch && isObj(ev.payload)) {
180
172
  const { type, payload } = ev.payload as MagixPayload;
181
173
  if (type === SLIDE_EVENTS.syncDispatch) {
182
174
  this.syncStateOnce();
@@ -189,7 +181,7 @@ export class SlideController {
189
181
  private syncStateOnce() {
190
182
  // sync state before the first event, so that they can be in the correct order
191
183
  if (this.syncStateOnceFlag) {
192
- const { state } = { ...EmptyAttributes, ...this.context.getAttributes() };
184
+ const { state } = { ...EmptyAttributes, ...this.context.storage.state };
193
185
  if (state) {
194
186
  log("[Slide] sync with state (once)", deepClone(state));
195
187
  this.slide.setSlideState(deepClone(state));
@@ -201,15 +193,7 @@ export class SlideController {
201
193
  private onStateChange = (state: SlideState) => {
202
194
  if (this.context.getIsWritable()) {
203
195
  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));
196
+ this.context.storage.setState({ state });
213
197
  }
214
198
  };
215
199
 
@@ -251,6 +235,7 @@ export class SlideController {
251
235
  mode: "interactive",
252
236
  resize: true,
253
237
  controller: logger.enable,
238
+ enableGlobalClick: true,
254
239
  renderOptions: {
255
240
  minFPS: 25,
256
241
  maxFPS: 30,
@@ -122,6 +122,7 @@ export class SlidePreviewer {
122
122
  mode: "local",
123
123
  resize: true,
124
124
  controller: this.debug,
125
+ enableGlobalClick: true,
125
126
  renderOptions: {
126
127
  minFPS: 25,
127
128
  maxFPS: 30,
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 {}