@netless/app-slide 0.2.2 → 0.3.0-canary.0
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-zh.md +1 -4
- package/README.md +1 -4
- package/dist/Refrigerator.d.ts +13 -0
- package/dist/SlideViewer.d.ts +63 -0
- package/dist/constants.d.ts +2 -0
- package/dist/{DocsViewer/icons → icons}/arrow-left.d.ts +0 -0
- package/dist/{DocsViewer/icons → icons}/arrow-right.d.ts +0 -0
- package/dist/{DocsViewer/icons → icons}/pause.d.ts +0 -0
- package/dist/{DocsViewer/icons → icons}/play.d.ts +0 -0
- package/dist/{DocsViewer/icons → icons}/sidebar.d.ts +0 -0
- package/dist/index.d.ts +10 -35
- package/dist/main.cjs.js +177 -177
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +1901 -2479
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +254 -254
- package/dist/main.iife.js.map +1 -1
- package/dist/typings.d.ts +21 -9
- package/dist/utils.d.ts +1 -0
- package/package.json +2 -2
- package/src/Refrigerator.ts +66 -0
- package/src/SlideViewer.ts +543 -0
- package/src/constants.ts +3 -0
- package/src/{DocsViewer/icons → icons}/arrow-left.ts +0 -0
- package/src/{DocsViewer/icons → icons}/arrow-right.ts +0 -0
- package/src/{DocsViewer/icons → icons}/pause.ts +0 -0
- package/src/{DocsViewer/icons → icons}/play.ts +0 -0
- package/src/{DocsViewer/icons → icons}/sidebar.ts +0 -0
- package/src/index.ts +210 -205
- package/src/style.scss +245 -5
- package/src/typings.ts +23 -10
- package/src/utils.ts +3 -0
- package/dist/DocsViewer/index.d.ts +0 -53
- package/dist/SlideController/helpers.d.ts +0 -6
- package/dist/SlideController/index.d.ts +0 -60
- package/dist/SlideDocsViewer/index.d.ts +0 -45
- package/dist/SlidePreviewer/index.d.ts +0 -52
- package/dist/utils/bgcolor.d.ts +0 -3
- package/dist/utils/freezer.d.ts +0 -22
- package/dist/utils/helpers.d.ts +0 -6
- package/dist/utils/logger.d.ts +0 -39
- package/src/DocsViewer/icons/arrow-left.svg +0 -4
- package/src/DocsViewer/icons/arrow-right.svg +0 -4
- package/src/DocsViewer/icons/pause.svg +0 -4
- package/src/DocsViewer/icons/play.svg +0 -4
- package/src/DocsViewer/icons/sidebar.svg +0 -4
- package/src/DocsViewer/index.ts +0 -368
- package/src/DocsViewer/style.scss +0 -228
- package/src/DocsViewer/variables.scss +0 -1
- package/src/SlideController/helpers.ts +0 -48
- package/src/SlideController/index.ts +0 -356
- package/src/SlideDocsViewer/index.ts +0 -210
- package/src/SlideDocsViewer/style.scss +0 -11
- package/src/SlidePreviewer/index.ts +0 -226
- package/src/utils/bgcolor.ts +0 -45
- package/src/utils/freezer.ts +0 -75
- package/src/utils/helpers.ts +0 -26
- package/src/utils/logger.ts +0 -106
package/src/index.ts
CHANGED
|
@@ -1,234 +1,239 @@
|
|
|
1
|
-
import type { NetlessApp } from "@netless/window-manager";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type { MountSlideOptions } from "./SlideDocsViewer";
|
|
5
|
-
import type { Attributes, MagixEvents } from "./typings";
|
|
6
|
-
import type { AddHooks, FreezableSlide } from "./utils/freezer";
|
|
1
|
+
import type { AppContext, NetlessApp, Player, WhiteBoardView } from "@netless/window-manager";
|
|
2
|
+
import type { AddHooks, AppOptions, Attributes, MagixEvents, SlideState } from "./typings";
|
|
3
|
+
import type { SlideViewerOptions } from "./SlideViewer";
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
DefaultUrl,
|
|
11
|
-
EmptyAttributes,
|
|
12
|
-
syncSceneWithSlide,
|
|
13
|
-
SlideController,
|
|
14
|
-
} from "./SlideController";
|
|
15
|
-
import { SlideDocsViewer } from "./SlideDocsViewer";
|
|
16
|
-
import { apps, FreezerLength, addHooks, useFreezer } from "./utils/freezer";
|
|
17
|
-
import { log, logger } from "./utils/logger";
|
|
18
|
-
import styles from "./style.scss?inline";
|
|
19
|
-
|
|
20
|
-
export type { PreviewParams } from "./SlidePreviewer";
|
|
21
|
-
export { SlidePreviewer, default as previewSlide } from "./SlidePreviewer";
|
|
22
|
-
|
|
23
|
-
export type { Attributes, AddHooks, FreezableSlide };
|
|
5
|
+
export type { SlideState, AddHooks, AppOptions, Attributes, MagixEvents, SlideViewerOptions };
|
|
24
6
|
|
|
25
|
-
|
|
7
|
+
import ColorString from "color-string";
|
|
8
|
+
import { SideEffectManager } from "side-effect-manager";
|
|
9
|
+
import { SlideViewer } from "./SlideViewer";
|
|
10
|
+
import { refrigerator, addHooks } from "./Refrigerator";
|
|
11
|
+
import { noop } from "./utils";
|
|
26
12
|
|
|
27
|
-
export {
|
|
28
|
-
|
|
29
|
-
export interface AppOptions {
|
|
30
|
-
/** show debug controller */
|
|
31
|
-
debug?: boolean;
|
|
32
|
-
/** scale */
|
|
33
|
-
resolution?: number;
|
|
34
|
-
/** background color for slide animations */
|
|
35
|
-
bgColor?: string;
|
|
36
|
-
/** minimal fps @default 25 */
|
|
37
|
-
minFPS?: number;
|
|
38
|
-
/** maximal fps @default 30 */
|
|
39
|
-
maxFPS?: number;
|
|
40
|
-
/** automatically decrease fps @default true */
|
|
41
|
-
autoFPS?: boolean;
|
|
42
|
-
/** whether to re-scale automatically @default true */
|
|
43
|
-
autoResolution?: boolean;
|
|
44
|
-
}
|
|
13
|
+
export { SlideViewer, refrigerator, addHooks };
|
|
45
14
|
|
|
46
|
-
export
|
|
47
|
-
slide: () => Slide | undefined;
|
|
48
|
-
position: () => [page: number, pageCount: number] | undefined;
|
|
49
|
-
nextStep: () => boolean;
|
|
50
|
-
prevStep: () => boolean;
|
|
51
|
-
nextPage: () => boolean;
|
|
52
|
-
prevPage: () => boolean;
|
|
53
|
-
jumpToPage: (page: number) => boolean;
|
|
54
|
-
}
|
|
15
|
+
export const version = __APP_VERSION__;
|
|
55
16
|
|
|
56
|
-
const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions,
|
|
17
|
+
const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, void> = {
|
|
57
18
|
kind: "Slide",
|
|
58
19
|
setup(context) {
|
|
59
|
-
console.log("[Slide] setup @ " +
|
|
60
|
-
|
|
61
|
-
if (context.getIsWritable()) {
|
|
62
|
-
context.storage.ensureState(EmptyAttributes);
|
|
63
|
-
}
|
|
20
|
+
console.log("[Slide] setup @ " + __APP_VERSION__);
|
|
64
21
|
|
|
65
22
|
if (!context.storage.state.taskId) {
|
|
66
23
|
throw new Error("[Slide] no taskId");
|
|
67
24
|
}
|
|
68
25
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
throw new Error("[Slide] no view, please set scenePath on addApp()");
|
|
72
|
-
}
|
|
73
|
-
view.disableCameraTransform = true;
|
|
74
|
-
|
|
75
|
-
const box = context.getBox();
|
|
76
|
-
box.mountStyles(styles);
|
|
77
|
-
try {
|
|
78
|
-
box.$content.dataset.appSlideVersion = version;
|
|
79
|
-
} catch {
|
|
80
|
-
// ignore
|
|
81
|
-
}
|
|
26
|
+
const options = context.getAppOptions() || {};
|
|
27
|
+
const debug = Boolean(options.debug);
|
|
82
28
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
onPageChanged,
|
|
106
|
-
});
|
|
107
|
-
if (useFreezer) apps.set(context.appId, slideController);
|
|
108
|
-
logger.setAppController(context.appId, slideController);
|
|
109
|
-
if (import.meta.env.DEV) {
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
-
(window as any).slideController = slideController;
|
|
112
|
-
}
|
|
113
|
-
slideController.readyPromise.then(options.onReady).then(() => {
|
|
114
|
-
const room = context.getRoom();
|
|
115
|
-
let synced = false;
|
|
116
|
-
if (room && context.getIsWritable()) {
|
|
117
|
-
syncSceneWithSlide(room, context, slideController.slide, baseScenePath);
|
|
118
|
-
synced = true;
|
|
119
|
-
}
|
|
120
|
-
const page = slideController.slide.slideState.currentSlideIndex;
|
|
121
|
-
log("[Slide] page to", page, synced ? "(synced)" : "", "(on ready)");
|
|
122
|
-
});
|
|
123
|
-
return slideController;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
docsViewer = new SlideDocsViewer({
|
|
127
|
-
box,
|
|
128
|
-
view,
|
|
129
|
-
mountSlideController,
|
|
130
|
-
mountWhiteboard: context.mountView.bind(context),
|
|
29
|
+
const sideEffect = new SideEffectManager();
|
|
30
|
+
|
|
31
|
+
// create ui
|
|
32
|
+
const viewer = new SlideViewer({
|
|
33
|
+
interactive: true,
|
|
34
|
+
mode: "interactive",
|
|
35
|
+
resize: true,
|
|
36
|
+
enableGlobalClick: true,
|
|
37
|
+
controller: debug,
|
|
38
|
+
timestamp: make_timestamp(context),
|
|
39
|
+
logger: make_logger(context),
|
|
40
|
+
renderOptions: {
|
|
41
|
+
minFPS: options.minFPS || 25,
|
|
42
|
+
maxFPS: options.maxFPS || 30,
|
|
43
|
+
autoFPS: options.autoFPS ?? true,
|
|
44
|
+
autoResolution: options.autoResolution ?? true,
|
|
45
|
+
resolution: options.resolution,
|
|
46
|
+
transactionBgColor: options.bgColor || make_bg_color(context.box.$content),
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
taskId: context.storage.state.taskId,
|
|
50
|
+
url: context.storage.state.url,
|
|
131
51
|
});
|
|
132
52
|
|
|
53
|
+
// For debugging.
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
(viewer as any).context = context;
|
|
56
|
+
|
|
57
|
+
sideEffect.addDisposer(refrigerator.set(context.appId, viewer));
|
|
58
|
+
|
|
133
59
|
if (import.meta.env.DEV) {
|
|
134
60
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
135
|
-
(window as any).
|
|
61
|
+
(window as any).slideViewer = viewer;
|
|
136
62
|
}
|
|
137
63
|
|
|
138
|
-
|
|
139
|
-
|
|
64
|
+
// mount ui
|
|
65
|
+
context.box.mountStyles(SlideViewer.styles);
|
|
66
|
+
context.box.mountStage(viewer.$slide);
|
|
67
|
+
context.box.mountFooter(viewer.$footer);
|
|
68
|
+
context.box.mountContent(viewer.$content);
|
|
69
|
+
sideEffect.addDisposer(() => viewer.destroy());
|
|
140
70
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
71
|
+
// connect ui with netless app
|
|
72
|
+
connect({ context, viewer, sideEffect, debug });
|
|
73
|
+
|
|
74
|
+
onUnmount(context, () => {
|
|
75
|
+
console.log("[Slide] destroy");
|
|
76
|
+
sideEffect.flushAll();
|
|
146
77
|
});
|
|
78
|
+
},
|
|
79
|
+
};
|
|
147
80
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
81
|
+
export default SlideApp;
|
|
82
|
+
|
|
83
|
+
export function previewSlide(options: SlideViewerOptions & { container: HTMLElement }) {
|
|
84
|
+
const { container } = options;
|
|
85
|
+
const viewer = new SlideViewer(options);
|
|
86
|
+
container.appendChild(viewer.$content);
|
|
87
|
+
return viewer;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function make_timestamp(context: AppContext): () => number {
|
|
91
|
+
const room = context.room;
|
|
92
|
+
const player = context.displayer as Player;
|
|
93
|
+
if (room) {
|
|
94
|
+
return () => room.calibrationTimestamp;
|
|
95
|
+
} else if (player) {
|
|
96
|
+
return () => player.beginTimestamp + player.progressTime;
|
|
97
|
+
} else {
|
|
98
|
+
return () => Date.now();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function make_logger(context: AppContext) {
|
|
103
|
+
if (import.meta.env.DEV) {
|
|
104
|
+
return { info: console.debug.bind(console) };
|
|
105
|
+
}
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
return (context.displayer as any).logger;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function make_bg_color(el: HTMLElement): string {
|
|
111
|
+
try {
|
|
112
|
+
let bg = window.getComputedStyle(el).backgroundColor;
|
|
113
|
+
if (bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") {
|
|
114
|
+
bg = hex(bg);
|
|
115
|
+
console.log("[Slide] guess bg color:", bg);
|
|
116
|
+
return bg;
|
|
159
117
|
}
|
|
118
|
+
if (el.parentElement) {
|
|
119
|
+
return make_bg_color(el.parentElement);
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// ignored
|
|
123
|
+
}
|
|
124
|
+
return "#ffffff";
|
|
125
|
+
}
|
|
160
126
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
127
|
+
// https://github.com/Qix-/color-convert/blob/8dfdbbc6b46fa6a305bf394d942cc1b08e23fca5/conversions.js#L616
|
|
128
|
+
function hex(color: string): string {
|
|
129
|
+
const result = ColorString.get(color);
|
|
130
|
+
if (result && result.model === "rgb") {
|
|
131
|
+
const args = result.value;
|
|
132
|
+
|
|
133
|
+
const integer =
|
|
134
|
+
((Math.round(args[0]) & 0xff) << 16) +
|
|
135
|
+
((Math.round(args[1]) & 0xff) << 8) +
|
|
136
|
+
(Math.round(args[2]) & 0xff);
|
|
137
|
+
|
|
138
|
+
const string = integer.toString(16);
|
|
139
|
+
return "#" + "000000".substring(string.length) + string;
|
|
140
|
+
} else {
|
|
141
|
+
return color;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
170
144
|
|
|
171
|
-
|
|
145
|
+
function onUnmount(context: AppContext, callback: () => void) {
|
|
146
|
+
context.emitter.on("destroy", callback);
|
|
147
|
+
}
|
|
172
148
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
prevPage: () => {
|
|
209
|
-
const controller = docsViewer?.slideController;
|
|
210
|
-
if (controller) {
|
|
211
|
-
const { page, pageCount } = controller;
|
|
212
|
-
if (pageCount > 0 && page > 1) {
|
|
213
|
-
controller.jumpToPage(page - 1);
|
|
214
|
-
return true;
|
|
149
|
+
interface ConnectParams {
|
|
150
|
+
context: AppContext;
|
|
151
|
+
viewer: SlideViewer;
|
|
152
|
+
sideEffect: SideEffectManager;
|
|
153
|
+
debug: boolean;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function connect({ context, viewer, sideEffect, debug }: ConnectParams) {
|
|
157
|
+
const log = debug ? console.log.bind(console) : noop;
|
|
158
|
+
|
|
159
|
+
let whiteboard: WhiteBoardView | null = null;
|
|
160
|
+
|
|
161
|
+
viewer.prepare(({ width, height, slideCount }) => {
|
|
162
|
+
if (context.destroyed) return;
|
|
163
|
+
width && context.box.setStageRatio(height / width);
|
|
164
|
+
whiteboard = context.createWhiteBoardView(slideCount);
|
|
165
|
+
kick_start();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const { slide } = viewer;
|
|
169
|
+
|
|
170
|
+
function kick_start() {
|
|
171
|
+
if (context.storage.state.state) {
|
|
172
|
+
log("[Slide] restore", { ...context.storage.state.state });
|
|
173
|
+
slide.setSlideState(context.storage.state.state);
|
|
174
|
+
} else if (context.isAddApp) {
|
|
175
|
+
log("[Slide] kick start");
|
|
176
|
+
slide.renderSlide(1);
|
|
177
|
+
} else {
|
|
178
|
+
log("[Slide] wait for restore");
|
|
179
|
+
const timeoutID = sideEffect.add(() => {
|
|
180
|
+
const timer = setInterval(() => {
|
|
181
|
+
if (!context.storage.state.state) {
|
|
182
|
+
log("[Slide] timeout");
|
|
183
|
+
slide.renderSlide(1);
|
|
215
184
|
}
|
|
216
|
-
}
|
|
217
|
-
return
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
185
|
+
}, 15 * 1000);
|
|
186
|
+
return () => clearInterval(timer);
|
|
187
|
+
});
|
|
188
|
+
const disposerID = sideEffect.addDisposer(
|
|
189
|
+
context.storage.addStateChangedListener(() => {
|
|
190
|
+
if (context.storage.state.state) {
|
|
191
|
+
log("[Slide] restore", { ...context.storage.state.state });
|
|
192
|
+
slide.setSlideState(context.storage.state.state);
|
|
193
|
+
sideEffect.flush(disposerID);
|
|
194
|
+
sideEffect.flush(timeoutID);
|
|
226
195
|
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
},
|
|
232
|
-
};
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
233
200
|
|
|
234
|
-
|
|
201
|
+
// setup sync event
|
|
202
|
+
slide.on("stateChange", () => {
|
|
203
|
+
if (context.isWritable) {
|
|
204
|
+
log("[Slide] save state", { ...slide.slideState });
|
|
205
|
+
context.storage.setState({ state: slide.slideState });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
slide.on("syncDispatch", payload => {
|
|
209
|
+
if (context.isWritable) {
|
|
210
|
+
log("[Slide] dispatch", { ...payload });
|
|
211
|
+
context.dispatchMagixEvent("syncDispatch", payload);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
sideEffect.addDisposer(
|
|
215
|
+
context.addMagixEventListener(
|
|
216
|
+
"syncDispatch",
|
|
217
|
+
({ payload }) => {
|
|
218
|
+
log("[Slide] receive", { ...payload });
|
|
219
|
+
slide.emit("syncReceive", payload);
|
|
220
|
+
},
|
|
221
|
+
{ fireSelfEventAfterCommit: true }
|
|
222
|
+
)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// save state
|
|
226
|
+
slide.on("slideChange", page => {
|
|
227
|
+
log("[Slide] page to", page);
|
|
228
|
+
whiteboard && whiteboard.jumpPage(page - 1);
|
|
229
|
+
viewer.setPage(page);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// handle ui events
|
|
233
|
+
viewer.setReadonly(!context.isWritable);
|
|
234
|
+
sideEffect.addDisposer(
|
|
235
|
+
context.emitter.on("writableChange", w => {
|
|
236
|
+
viewer.setReadonly(!w);
|
|
237
|
+
})
|
|
238
|
+
);
|
|
239
|
+
}
|