@netless/app-slide 0.3.0-canary.2 → 0.3.0-canary.21

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.
Files changed (47) hide show
  1. package/README-zh.md +17 -7
  2. package/README.md +4 -7
  3. package/dist/Logger.d.ts +12 -0
  4. package/dist/Refrigerator.d.ts +3 -1
  5. package/dist/SlideViewer/footer.d.ts +12 -0
  6. package/dist/{SlideViewer.d.ts → SlideViewer/index.d.ts} +33 -20
  7. package/dist/SlideViewer/sidebar.d.ts +12 -0
  8. package/dist/SlideViewer/typings.d.ts +18 -0
  9. package/dist/SlideViewer/utils.d.ts +7 -0
  10. package/dist/connect.d.ts +14 -0
  11. package/dist/constants.d.ts +1 -1
  12. package/dist/icons/index.d.ts +5 -0
  13. package/dist/icons/save.d.ts +1 -0
  14. package/dist/icons/spinner.d.ts +1 -0
  15. package/dist/index.d.ts +7 -6
  16. package/dist/main.js +3 -0
  17. package/dist/main.js.map +1 -0
  18. package/dist/main.mjs +1406 -0
  19. package/dist/main.mjs.map +1 -0
  20. package/dist/preview.d.ts +5 -0
  21. package/dist/typings.d.ts +9 -18
  22. package/dist/utils.d.ts +12 -0
  23. package/package.json +12 -8
  24. package/src/Logger.ts +38 -0
  25. package/src/Refrigerator.ts +11 -1
  26. package/src/SlideViewer/footer.ts +169 -0
  27. package/src/SlideViewer/index.ts +329 -0
  28. package/src/SlideViewer/sidebar.ts +140 -0
  29. package/src/SlideViewer/typings.ts +21 -0
  30. package/src/SlideViewer/utils.ts +37 -0
  31. package/src/connect.ts +152 -0
  32. package/src/constants.ts +1 -1
  33. package/src/icons/index.ts +5 -0
  34. package/src/icons/save.ts +18 -0
  35. package/src/icons/spinner.ts +37 -0
  36. package/src/index.ts +64 -192
  37. package/src/preview.ts +21 -0
  38. package/src/style.scss +67 -16
  39. package/src/typings.ts +18 -16
  40. package/src/utils.ts +90 -0
  41. package/dist/main.cjs.js +0 -6363
  42. package/dist/main.cjs.js.map +0 -1
  43. package/dist/main.es.js +0 -38214
  44. package/dist/main.es.js.map +0 -1
  45. package/dist/main.iife.js +0 -6363
  46. package/dist/main.iife.js.map +0 -1
  47. package/src/SlideViewer.ts +0 -557
package/src/connect.ts ADDED
@@ -0,0 +1,152 @@
1
+ import type { AppContext, Storage, WhiteBoardView } from "@netless/window-manager";
2
+ import type { SideEffectManager } from "side-effect-manager";
3
+ import type { SlideViewer } from "./SlideViewer";
4
+ import type { Attributes } from "./typings";
5
+
6
+ import { get, wrap_class } from "./utils";
7
+
8
+ export interface ConnectParams {
9
+ context: AppContext;
10
+ storage: Storage<Attributes>;
11
+ viewer: SlideViewer;
12
+ sideEffect: SideEffectManager;
13
+ logger: { info: (msg: string) => void };
14
+ }
15
+
16
+ export function connect({ context, storage, viewer, sideEffect, logger }: ConnectParams) {
17
+ const log = logger.info.bind(logger);
18
+
19
+ let whiteboard: WhiteBoardView | null = null;
20
+
21
+ viewer.prepare(({ width, height, slideCount }) => {
22
+ if (context.destroyed) return;
23
+ width && context.box.setStageRatio(height / width);
24
+ whiteboard = context.createWhiteBoardView({ size: slideCount });
25
+ whiteboard.setBaseRect({ width, height });
26
+
27
+ viewer._slideTitle = context.box.title;
28
+ viewer._getWhiteSnapshot = (
29
+ index: number,
30
+ canvas: HTMLCanvasElement,
31
+ ctx: CanvasRenderingContext2D
32
+ ) => {
33
+ if (!whiteboard) {
34
+ return null;
35
+ }
36
+ const scenePath = whiteboard.pageState.pages[index - 1];
37
+ const { width, height } = whiteboard.view.size;
38
+ canvas.width = width;
39
+ canvas.height = height;
40
+ whiteboard.view.screenshotToCanvas(ctx, scenePath, width, height, whiteboard.view.camera);
41
+ };
42
+
43
+ kick_start();
44
+ });
45
+
46
+ const { slide } = viewer;
47
+
48
+ function dump_state(state: unknown = storage.state.state) {
49
+ return JSON.stringify(state);
50
+ }
51
+
52
+ function kick_start() {
53
+ if (storage.state.state) {
54
+ log("[Slide] restore " + dump_state());
55
+ slide.setSlideState(storage.state.state);
56
+ } else if (context.isAddApp) {
57
+ log("[Slide] kick start");
58
+ slide.renderSlide(1);
59
+ } else {
60
+ log("[Slide] wait for restore");
61
+ const timeoutID = sideEffect.add(() => {
62
+ const timer = setInterval(() => {
63
+ if (!storage.state.state) {
64
+ log("[Slide] timeout");
65
+ slide.renderSlide(1);
66
+ }
67
+ }, 15 * 1000);
68
+ return () => clearInterval(timer);
69
+ });
70
+ const disposerID = sideEffect.push(
71
+ storage.on("stateChanged", () => {
72
+ if (storage.state.state) {
73
+ log("[Slide] restore " + dump_state());
74
+ slide.setSlideState(storage.state.state);
75
+ sideEffect.flush(disposerID);
76
+ sideEffect.flush(timeoutID);
77
+ }
78
+ })
79
+ );
80
+ }
81
+ }
82
+
83
+ // setup sync event
84
+ slide.on("stateChange", () => {
85
+ if (storage.isWritable) {
86
+ // log("[Slide] save state " + dump_state(slide.slideState));
87
+ storage.setState({ state: slide.slideState });
88
+ }
89
+ });
90
+ slide.on("syncDispatch", payload => {
91
+ if (context.isWritable) {
92
+ log("[Slide] dispatch " + dump_state(get(payload, "uuid")));
93
+ context.dispatchMagixEvent("syncDispatch", payload);
94
+ }
95
+ });
96
+ sideEffect.push(
97
+ context.addMagixEventListener(
98
+ "syncDispatch",
99
+ ({ payload }) => {
100
+ log("[Slide] receive " + dump_state(get(payload, "uuid")));
101
+ slide.emit("syncReceive", payload);
102
+ },
103
+ { fireSelfEventAfterCommit: true }
104
+ )
105
+ );
106
+
107
+ // save state
108
+ slide.on("slideChange", page => {
109
+ if (context.isWritable && whiteboard) {
110
+ log("[Slide] page to " + page);
111
+ whiteboard.jumpPage(page - 1);
112
+ }
113
+ });
114
+
115
+ // handle ui events
116
+ function disable_whiteboard() {
117
+ if (whiteboard && whiteboard.view.divElement) {
118
+ whiteboard.view.divElement.classList.add(wrap_class("wb-view-hidden"));
119
+ }
120
+ }
121
+
122
+ function enable_whiteboard() {
123
+ if (whiteboard && whiteboard.view.divElement) {
124
+ whiteboard.view.divElement.classList.remove(wrap_class("wb-view-hidden"));
125
+ }
126
+ }
127
+
128
+ viewer.setReadonly(!context.isWritable);
129
+ sideEffect.push([
130
+ context.emitter.on("writableChange", () => viewer.setReadonly(!context.isWritable)),
131
+ viewer.events.on("renderStart", disable_whiteboard),
132
+ viewer.events.on("renderEnd", enable_whiteboard),
133
+ ]);
134
+
135
+ const isEditable = (el: EventTarget | null) => {
136
+ if (!el) return false;
137
+ const tagName = (el as HTMLElement).tagName;
138
+ return tagName === "INPUT" || tagName === "TEXTAREA" || tagName === "SELECT";
139
+ };
140
+
141
+ sideEffect.addEventListener(window, "keydown", ev => {
142
+ if (!ev || (ev.target as HTMLElement).className === "telebox-quarantine-outer") return;
143
+ if (context.box.focus && !context.box.readonly && !isEditable(ev.target)) {
144
+ if (ev.key === "ArrowUp" || ev.key === "ArrowLeft") {
145
+ viewer.slide.prevStep();
146
+ }
147
+ if (ev.key === "ArrowRight" || ev.key === "ArrowDown") {
148
+ viewer.slide.nextStep();
149
+ }
150
+ }
151
+ });
152
+ }
package/src/constants.ts CHANGED
@@ -1,3 +1,3 @@
1
- export const namespace = "netless-app-slide";
1
+ export const class_name = "netless-app-slide";
2
2
 
3
3
  export const DefaultUrl = "https://convertcdn.netless.link/dynamicConvert";
@@ -0,0 +1,5 @@
1
+ export { sidebarSVG } from "./sidebar";
2
+ export { arrowLeftSVG } from "./arrow-left";
3
+ export { playSVG } from "./play";
4
+ export { pauseSVG } from "./pause";
5
+ export { arrowRightSVG } from "./arrow-right";
@@ -0,0 +1,18 @@
1
+ export function saveSVG(namespace: string): SVGElement {
2
+ const NS = "http://www.w3.org/2000/svg";
3
+
4
+ const $svg = document.createElementNS(NS, "svg");
5
+ $svg.setAttribute("class", `${namespace}-footer-icon-save`);
6
+ $svg.setAttribute("viewBox", "0 0 448 512");
7
+
8
+ const $path = document.createElementNS(NS, "path");
9
+ $path.setAttribute("fill", "currentColor");
10
+ $path.setAttribute(
11
+ "d",
12
+ "M224 256c-35.2 0-64 28.8-64 64c0 35.2 28.8 64 64 64c35.2 0 64-28.8 64-64C288 284.8 259.2 256 224 256zM433.1 129.1l-83.9-83.9C341.1 37.06 328.8 32 316.1 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h320c35.35 0 64-28.65 64-64V163.9C448 151.2 442.9 138.9 433.1 129.1zM128 80h144V160H128V80zM400 416c0 8.836-7.164 16-16 16H64c-8.836 0-16-7.164-16-16V96c0-8.838 7.164-16 16-16h16v104c0 13.25 10.75 24 24 24h192C309.3 208 320 197.3 320 184V83.88l78.25 78.25C399.4 163.2 400 164.8 400 166.3V416z"
13
+ );
14
+
15
+ $svg.appendChild($path);
16
+
17
+ return $svg;
18
+ }
@@ -0,0 +1,37 @@
1
+ export function spinnerSVG(namespace: string): SVGElement {
2
+ const NS = "http://www.w3.org/2000/svg";
3
+
4
+ const $svg = document.createElementNS(NS, "svg");
5
+ $svg.setAttribute("class", `${namespace}-footer-icon-spinner`);
6
+ $svg.setAttribute("viewBox", "0 0 512 512");
7
+
8
+ const $path = document.createElementNS(NS, "path");
9
+ $path.setAttribute("fill", "currentColor");
10
+ $path.setAttribute(
11
+ "d",
12
+ "M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"
13
+ );
14
+
15
+ const $animate = document.createElementNS(NS, "animateTransform");
16
+ $animate.setAttribute("attributeName", "transform");
17
+ $animate.setAttribute("attributeType", "xml");
18
+ $animate.setAttribute("type", "rotate");
19
+ $animate.setAttribute("from", "0 256 256");
20
+ $animate.setAttribute("to", "360 256 256");
21
+ $animate.setAttribute("begin", "0s");
22
+ $animate.setAttribute("dur", "2s");
23
+ $animate.setAttribute("fill", "freeze");
24
+ $animate.setAttribute("repeatCount", "indefinite");
25
+ $path.appendChild($animate);
26
+
27
+ const $progress = document.createElementNS(NS, "text");
28
+ $progress.setAttribute("x", "125");
29
+ $progress.setAttribute("y", "345");
30
+ $progress.setAttribute("data-id", "progress");
31
+ $progress.style.fontSize = "246px";
32
+ $progress.textContent = "20";
33
+ $svg.appendChild($progress);
34
+ $svg.appendChild($path);
35
+
36
+ return $svg;
37
+ }
package/src/index.ts CHANGED
@@ -1,31 +1,45 @@
1
- import type { AppContext, NetlessApp, Player, WhiteBoardView } from "@netless/window-manager";
2
- import type { AddHooks, AppOptions, Attributes, MagixEvents, SlideState } from "./typings";
1
+ import type { NetlessApp } from "@netless/window-manager";
2
+ import type { ISlideConfig } from "@netless/slide";
3
3
  import type { SlideViewerOptions } from "./SlideViewer";
4
+ import type { AddHooks, AppOptions, Attributes, MagixEvents, SlideState } from "./typings";
4
5
 
5
6
  export type { SlideState, AddHooks, AppOptions, Attributes, MagixEvents, SlideViewerOptions };
6
7
 
7
- import ColorString from "color-string";
8
+ import { Slide } from "@netless/slide";
8
9
  import { SideEffectManager } from "side-effect-manager";
10
+
11
+ import { DefaultUrl } from "./constants";
12
+ import { addHooks, refrigerator } from "./Refrigerator";
9
13
  import { SlideViewer } from "./SlideViewer";
10
- import { refrigerator, addHooks } from "./Refrigerator";
14
+ import { Logger } from "./Logger";
15
+ import { connect } from "./connect";
16
+ import { previewSlide } from "./preview";
17
+ import { make_bg_color, make_timestamp } from "./utils";
11
18
 
12
- export { SlideViewer, refrigerator, addHooks };
19
+ export { Slide, SlideViewer, refrigerator, addHooks, DefaultUrl, previewSlide };
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ export const usePlugin = /* @__PURE__ */ Slide.usePlugin.bind(Slide) as (plugin: any) => void;
13
23
 
14
24
  export const version = __APP_VERSION__;
15
25
 
16
26
  const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, void> = {
17
27
  kind: "Slide",
28
+ config: { enableShadowDOM: false },
18
29
  setup(context) {
19
- console.log("[Slide] setup @ " + __APP_VERSION__);
30
+ console.log("[Slide] setup @ " + __APP_VERSION__ + " (" + context.appId + ")");
31
+
32
+ const logger = new Logger(context);
33
+ logger.info("[Slide] setup @ " + __APP_VERSION__);
20
34
 
21
- if (!context.storage.state.taskId) {
35
+ if (!context.attributes.taskId) {
22
36
  throw new Error("[Slide] no taskId");
23
37
  }
24
38
 
25
- const options = context.getAppOptions() || {};
26
- const debug = Boolean(options.debug);
27
- const logger = make_logger(context);
28
- logger.info("[Slide] setup @ " + __APP_VERSION__);
39
+ const storage = context.createStorage("slide", context.attributes);
40
+
41
+ const appOptions = context.getAppOptions() || {};
42
+ const renderOptions = appOptions.renderOptions || {};
29
43
 
30
44
  const sideEffect = new SideEffectManager();
31
45
 
@@ -33,213 +47,71 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, void> = {
33
47
  const viewer = new SlideViewer({
34
48
  interactive: true,
35
49
  mode: "interactive",
36
- resize: true,
37
50
  enableGlobalClick: true,
38
- controller: debug,
39
51
  timestamp: make_timestamp(context),
40
- logger,
52
+ logger: appOptions.logger || logger,
53
+ whiteTracker: (context.displayer as unknown as { tracker: ISlideConfig["whiteTracker"] }).tracker,
41
54
  renderOptions: {
42
- minFPS: options.minFPS || 25,
43
- maxFPS: options.maxFPS || 30,
44
- autoFPS: options.autoFPS ?? true,
45
- autoResolution: options.autoResolution ?? true,
46
- resolution: options.resolution,
47
- transactionBgColor: options.bgColor || make_bg_color(context.box.$content),
55
+ minFPS: renderOptions.minFPS || 25,
56
+ maxFPS: renderOptions.maxFPS || 30,
57
+ autoFPS: renderOptions.autoFPS ?? true,
58
+ autoResolution: renderOptions.autoResolution ?? true,
59
+ resolution: renderOptions.resolution,
60
+ maxResolutionLevel: renderOptions.maxResolutionLevel,
61
+ transactionBgColor: renderOptions.transactionBgColor || make_bg_color(context.box.$content),
48
62
  },
49
-
50
- taskId: context.storage.state.taskId,
51
- url: context.storage.state.url,
63
+ rtcAudio: appOptions.rtcAudio,
64
+ useLocalCache: appOptions.useLocalCache,
65
+ resourceTimeout: appOptions.resourceTimeout,
66
+ loaderDelegate: appOptions.loaderDelegate,
67
+ navigatorDelegate: appOptions.navigatorDelegate,
68
+ fixedFrameSize: appOptions.fixedFrameSize,
69
+ taskId: storage.state.taskId,
70
+ url: storage.state.url,
52
71
  });
53
72
 
54
73
  // For debugging.
55
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
- (viewer as any).context = context;
74
+ Object.assign(viewer, { context, logger });
57
75
 
58
- sideEffect.addDisposer(refrigerator.set(context.appId, viewer));
76
+ sideEffect.push(refrigerator.set(context.appId, viewer, context.box));
59
77
 
60
78
  if (import.meta.env.DEV) {
61
79
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
80
  (window as any).slideViewer = viewer;
63
81
  }
64
82
 
83
+ // Log more
84
+ sideEffect.push(viewer.events.on("freeze", () => logger.info("[Slide] freeze")));
85
+ sideEffect.push(viewer.events.on("unfreeze", () => logger.info("[Slide] unfreeze")));
86
+ sideEffect.push(
87
+ viewer.events.on("prepareError", error =>
88
+ logger.info("[Slide] fetch slide info failed: " + error.message)
89
+ )
90
+ );
91
+ sideEffect.push(
92
+ viewer.events.on("renderError", ({ error, index }) =>
93
+ logger.info("[Slide] render failed " + `[${index}]: ${error.message}`)
94
+ )
95
+ );
96
+
65
97
  // Mount UI
66
98
  context.box.mountStyles(SlideViewer.styles);
67
99
  context.box.mountStage(viewer.$slide);
68
100
  context.box.mountFooter(viewer.$footer);
69
101
  context.box.mountContent(viewer.$content);
70
- sideEffect.addDisposer(() => viewer.destroy());
102
+ sideEffect.push(() => viewer.destroy());
71
103
 
72
104
  // Connect UI with Netless App
73
- connect({ context, viewer, sideEffect, logger });
105
+ connect({ context, storage, viewer, sideEffect, logger });
74
106
 
75
- onUnmount(context, () => {
107
+ context.emitter.on("destroy", () => {
76
108
  logger.info("[Slide] destroy");
109
+ logger.destroy();
77
110
  sideEffect.flushAll();
78
111
  });
112
+
113
+ return viewer;
79
114
  },
80
115
  };
81
116
 
82
117
  export default SlideApp;
83
-
84
- export function previewSlide(options: SlideViewerOptions & { container: HTMLElement }) {
85
- const { container } = options;
86
- container.style.cssText = "display:flex;flex-direction:column";
87
- const viewer = new SlideViewer(options);
88
- container.appendChild(document.createElement("style")).textContent = SlideViewer.styles;
89
- container.appendChild(viewer.$content);
90
- container.appendChild(viewer.$footer);
91
- viewer.prepare(() => viewer.slide.renderSlide(1));
92
- return viewer;
93
- }
94
-
95
- function make_timestamp(context: AppContext): () => number {
96
- const room = context.room;
97
- const player = context.displayer as Player;
98
- if (room) {
99
- return () => room.calibrationTimestamp;
100
- } else if (player) {
101
- return () => player.beginTimestamp + player.progressTime;
102
- } else {
103
- return () => Date.now();
104
- }
105
- }
106
-
107
- function make_logger(context: AppContext): { info: (msg: string) => void } {
108
- if (import.meta.env.DEV) {
109
- return { info: console.debug.bind(console) };
110
- }
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
- return (context.displayer as any).logger;
113
- }
114
-
115
- function make_bg_color(el: HTMLElement): string {
116
- try {
117
- let bg = window.getComputedStyle(el).backgroundColor;
118
- if (bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") {
119
- bg = hex(bg);
120
- console.log("[Slide] guess bg color:", bg);
121
- return bg;
122
- }
123
- if (el.parentElement) {
124
- return make_bg_color(el.parentElement);
125
- }
126
- } catch {
127
- // ignored
128
- }
129
- return "#ffffff";
130
- }
131
-
132
- // https://github.com/Qix-/color-convert/blob/8dfdbbc6b46fa6a305bf394d942cc1b08e23fca5/conversions.js#L616
133
- function hex(color: string): string {
134
- const result = ColorString.get(color);
135
- if (result && result.model === "rgb") {
136
- const args = result.value;
137
-
138
- const integer =
139
- ((Math.round(args[0]) & 0xff) << 16) +
140
- ((Math.round(args[1]) & 0xff) << 8) +
141
- (Math.round(args[2]) & 0xff);
142
-
143
- const string = integer.toString(16);
144
- return "#" + "000000".substring(string.length) + string;
145
- } else {
146
- return color;
147
- }
148
- }
149
-
150
- function onUnmount(context: AppContext, callback: () => void) {
151
- context.emitter.on("destroy", callback);
152
- }
153
-
154
- interface ConnectParams {
155
- context: AppContext;
156
- viewer: SlideViewer;
157
- sideEffect: SideEffectManager;
158
- logger: { info: (msg: string) => void };
159
- }
160
-
161
- function connect({ context, viewer, sideEffect, logger }: ConnectParams) {
162
- const log = import.meta.env.DEV ? console.log.bind(console) : logger.info.bind(logger);
163
-
164
- let whiteboard: WhiteBoardView | null = null;
165
-
166
- viewer.prepare(({ width, height, slideCount }) => {
167
- if (context.destroyed) return;
168
- width && context.box.setStageRatio(height / width);
169
- whiteboard = context.createWhiteBoardView(slideCount);
170
- kick_start();
171
- });
172
-
173
- const { slide } = viewer;
174
-
175
- function kick_start() {
176
- if (context.storage.state.state) {
177
- log("[Slide] restore", { ...context.storage.state.state });
178
- slide.setSlideState(context.storage.state.state);
179
- } else if (context.isAddApp) {
180
- log("[Slide] kick start");
181
- slide.renderSlide(1);
182
- } else {
183
- log("[Slide] wait for restore");
184
- const timeoutID = sideEffect.add(() => {
185
- const timer = setInterval(() => {
186
- if (!context.storage.state.state) {
187
- log("[Slide] timeout");
188
- slide.renderSlide(1);
189
- }
190
- }, 15 * 1000);
191
- return () => clearInterval(timer);
192
- });
193
- const disposerID = sideEffect.addDisposer(
194
- context.storage.addStateChangedListener(() => {
195
- if (context.storage.state.state) {
196
- log("[Slide] restore", { ...context.storage.state.state });
197
- slide.setSlideState(context.storage.state.state);
198
- sideEffect.flush(disposerID);
199
- sideEffect.flush(timeoutID);
200
- }
201
- })
202
- );
203
- }
204
- }
205
-
206
- // setup sync event
207
- slide.on("stateChange", () => {
208
- if (context.isWritable) {
209
- log("[Slide] save state", { ...slide.slideState });
210
- context.storage.setState({ state: slide.slideState });
211
- }
212
- });
213
- slide.on("syncDispatch", payload => {
214
- if (context.isWritable) {
215
- log("[Slide] dispatch", { ...payload });
216
- context.dispatchMagixEvent("syncDispatch", payload);
217
- }
218
- });
219
- sideEffect.addDisposer(
220
- context.addMagixEventListener(
221
- "syncDispatch",
222
- ({ payload }) => {
223
- log("[Slide] receive", { ...payload });
224
- slide.emit("syncReceive", payload);
225
- },
226
- { fireSelfEventAfterCommit: true }
227
- )
228
- );
229
-
230
- // save state
231
- slide.on("slideChange", page => {
232
- if (context.isWritable && whiteboard) {
233
- log("[Slide] page to", page);
234
- whiteboard.jumpPage(page - 1);
235
- }
236
- });
237
-
238
- // handle ui events
239
- viewer.setReadonly(!context.isWritable);
240
- sideEffect.addDisposer(
241
- context.emitter.on("writableChange", w => {
242
- viewer.setReadonly(!w);
243
- })
244
- );
245
- }
package/src/preview.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { SlideViewerOptions } from "./SlideViewer";
2
+
3
+ import { SlideViewer } from "./SlideViewer";
4
+ import { hc } from "./utils";
5
+
6
+ export function previewSlide(options: SlideViewerOptions & { container: HTMLElement }) {
7
+ const { container, ...slideOptions } = options;
8
+ const wrapper = hc("div", "preview-wrapper");
9
+ const viewer = new SlideViewer(slideOptions);
10
+ wrapper.appendChild(document.createElement("style")).textContent = SlideViewer.styles;
11
+ wrapper.appendChild(viewer.$content);
12
+ wrapper.appendChild(viewer.$footer);
13
+ container.appendChild(wrapper);
14
+ viewer.prepare(() => viewer.slide.renderSlide(1));
15
+ return viewer;
16
+ }
17
+
18
+ if (import.meta.env.DEV) {
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ (window as any).previewSlide = previewSlide;
21
+ }