@netless/fastboard 0.0.1 → 0.0.5

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 (43) hide show
  1. package/README.md +8 -10
  2. package/dist/index.cjs.js +4 -4
  3. package/dist/index.cjs.js.map +1 -1
  4. package/dist/index.es.js +478 -168
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/svelte.cjs.js +2 -0
  7. package/dist/svelte.cjs.js.map +1 -0
  8. package/dist/svelte.es.js +31 -0
  9. package/dist/svelte.es.js.map +1 -0
  10. package/dist/vue.cjs.js +2 -0
  11. package/dist/vue.cjs.js.map +1 -0
  12. package/dist/vue.es.js +42 -0
  13. package/dist/vue.es.js.map +1 -0
  14. package/package.json +25 -41
  15. package/src/WhiteboardApp.ts +39 -3
  16. package/src/behaviors/style.ts +1 -1
  17. package/src/components/PlayerControl/PlayerControl.scss +145 -0
  18. package/src/components/PlayerControl/PlayerControl.tsx +158 -0
  19. package/src/components/PlayerControl/components/Button.tsx +55 -0
  20. package/src/components/PlayerControl/hooks.ts +95 -0
  21. package/src/components/PlayerControl/icons/Loading.tsx +13 -0
  22. package/src/components/PlayerControl/icons/Pause.tsx +13 -0
  23. package/src/components/PlayerControl/icons/Play.tsx +13 -0
  24. package/src/components/PlayerControl/icons/index.ts +10 -0
  25. package/src/components/PlayerControl/index.ts +1 -0
  26. package/src/components/Root.tsx +1 -2
  27. package/src/components/Toolbar/Content.tsx +28 -15
  28. package/src/components/Toolbar/components/ColorBox.tsx +3 -3
  29. package/src/components/Toolbar/components/UpDownButtons.tsx +7 -10
  30. package/src/components/Toolbar/hooks.ts +1 -1
  31. package/src/components/ZoomControl.tsx +1 -1
  32. package/src/hooks.ts +18 -60
  33. package/src/i18n/en.json +2 -1
  34. package/src/i18n/zh-CN.json +2 -1
  35. package/src/index.ts +9 -2
  36. package/src/internal/Instance.tsx +40 -19
  37. package/src/internal/helpers.ts +15 -0
  38. package/src/internal/index.ts +1 -0
  39. package/src/internal/mount-whiteboard.ts +13 -5
  40. package/src/style.scss +1 -0
  41. package/src/svelte.ts +45 -0
  42. package/src/vue.ts +74 -0
  43. package/src/helpers/index.ts +0 -18
@@ -0,0 +1,95 @@
1
+ import type { DependencyList } from "react";
2
+ import type { Player } from "white-web-sdk";
3
+
4
+ import { useCallback, useEffect, useRef, useState } from "react";
5
+ import { PlayerPhase } from "white-web-sdk";
6
+
7
+ const EMPTY_ARRAY: DependencyList = [];
8
+
9
+ function useForceUpdate() {
10
+ const [, forceUpdate_] = useState({});
11
+ // eslint-disable-next-line react-hooks/exhaustive-deps
12
+ return useCallback(() => forceUpdate_({}), EMPTY_ARRAY);
13
+ }
14
+
15
+ function useLastValue<T>(value: T) {
16
+ const ref = useRef<T>(value);
17
+ useEffect(() => {
18
+ ref.current = value;
19
+ }, [value]);
20
+ return ref.current;
21
+ }
22
+
23
+ export function usePlayer(player?: Player | null) {
24
+ const togglePlay = useCallback(() => {
25
+ if (player) {
26
+ switch (player.phase) {
27
+ case PlayerPhase.WaitingFirstFrame:
28
+ case PlayerPhase.Pause:
29
+ case PlayerPhase.Ended: {
30
+ player.play();
31
+ break;
32
+ }
33
+ case PlayerPhase.Playing: {
34
+ player.pause();
35
+ break;
36
+ }
37
+ }
38
+ }
39
+ }, [player]);
40
+
41
+ const seekToProgressTime = useCallback(
42
+ (time: number) => {
43
+ if (player) {
44
+ player.seekToProgressTime(time);
45
+ }
46
+ },
47
+ [player]
48
+ );
49
+
50
+ const lastPlayer = useLastValue(player);
51
+
52
+ const forceUpdate = useForceUpdate();
53
+
54
+ const setSpeed = useCallback(
55
+ (speed: number) => {
56
+ if (player) {
57
+ player.playbackSpeed = speed;
58
+ forceUpdate();
59
+ }
60
+ },
61
+ [forceUpdate, player]
62
+ );
63
+
64
+ useEffect(() => {
65
+ if (!lastPlayer && player) {
66
+ forceUpdate();
67
+ }
68
+ }, [forceUpdate, lastPlayer, player]);
69
+
70
+ useEffect(() => {
71
+ if (player) {
72
+ player.callbacks.on("onPhaseChanged", forceUpdate);
73
+ player.callbacks.on("onProgressTimeChanged", forceUpdate);
74
+ return () => {
75
+ player.callbacks.off("onPhaseChanged", forceUpdate);
76
+ player.callbacks.off("onProgressTimeChanged", forceUpdate);
77
+ };
78
+ }
79
+ }, [forceUpdate, player]);
80
+
81
+ const phase = player ? player.phase : PlayerPhase.WaitingFirstFrame;
82
+ const currentTime = player ? player.progressTime : 0;
83
+ const totalTime = player ? player.timeDuration : 0;
84
+ const speed = player ? player.playbackSpeed : 1;
85
+
86
+ return {
87
+ phase,
88
+ currentTime,
89
+ totalTime,
90
+ speed,
91
+ setSpeed,
92
+ togglePlay,
93
+ seekToProgressTime,
94
+ };
95
+ }
@@ -0,0 +1,13 @@
1
+ import type { IconProps } from "../../../types";
2
+
3
+ import React from "react";
4
+ import { getStroke } from "../../../theme";
5
+
6
+ export const Loading = (props: IconProps) => {
7
+ const stroke = getStroke(props);
8
+ return (
9
+ <svg viewBox="0 0 24 24">
10
+ <path d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z" fill={stroke}></path>
11
+ </svg>
12
+ );
13
+ };
@@ -0,0 +1,13 @@
1
+ import type { IconProps } from "../../../types";
2
+
3
+ import React from "react";
4
+ import { getStroke } from "../../../theme";
5
+
6
+ export const Pause = (props: IconProps) => {
7
+ const stroke = getStroke(props);
8
+ return (
9
+ <svg viewBox="0 0 24 24">
10
+ <path d="M14 19h4V5h-4M6 19h4V5H6v14z" fill={stroke}></path>
11
+ </svg>
12
+ );
13
+ };
@@ -0,0 +1,13 @@
1
+ import type { IconProps } from "../../../types";
2
+
3
+ import React from "react";
4
+ import { getStroke } from "../../../theme";
5
+
6
+ export const Play = (props: IconProps) => {
7
+ const stroke = getStroke(props);
8
+ return (
9
+ <svg viewBox="0 0 24 24">
10
+ <path d="M8 5.14v14l11-7l-11-7z" fill={stroke}></path>
11
+ </svg>
12
+ );
13
+ };
@@ -0,0 +1,10 @@
1
+ import { memo } from "react";
2
+ import { Loading } from "./Loading";
3
+ import { Pause } from "./Pause";
4
+ import { Play } from "./Play";
5
+
6
+ export const Icons = {
7
+ Play: memo(Play),
8
+ Pause: memo(Pause),
9
+ Loading: memo(Loading),
10
+ };
@@ -0,0 +1 @@
1
+ export { PlayerControl, name, type PlayerControlProps } from "./PlayerControl";
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback, useState } from "react";
2
2
 
3
- import { Lock } from "../internal/helpers";
4
- import { Instance } from "../internal";
3
+ import { Lock, Instance } from "../internal";
5
4
  import { Toolbar } from "./Toolbar";
6
5
  import { RedoUndo } from "./RedoUndo";
7
6
  import { ZoomControl } from "./ZoomControl";
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
 
3
- import { useInstance } from "../../internal";
4
- import { clamp } from "../../helpers";
3
+ import { useInstance, clamp } from "../../internal";
5
4
  import { name } from "./Toolbar";
6
5
  import { ItemHeight, ItemsCount, MaxHeight, MinHeight } from "./const";
7
6
  import { DownButton, UpButton } from "./components/UpDownButtons";
@@ -16,42 +15,54 @@ import { PencilButton } from "./components/PencilButton";
16
15
  import { TextButton } from "./components/TextButton";
17
16
  import { ShapesButton } from "./components/ShapesButton";
18
17
 
19
- export interface ContentProps {
20
- padding?: number;
21
- }
22
-
23
- export function Content({ padding = 16 }: ContentProps) {
18
+ export function Content() {
24
19
  const app = useInstance();
25
20
  const ref = useRef<HTMLDivElement>(null);
21
+ const [scrollTop, setScrollTop] = useState(0);
26
22
  const [parentHeight, setParentHeight] = useState(0);
27
23
 
24
+ const hasAppButton = app?.config.toolbar?.apps?.enable ?? true;
28
25
  const needScroll = parentHeight < ItemHeight * ItemsCount + 48;
29
26
  const sectionHeight = clamp(
30
27
  parentHeight - 48 * (needScroll ? 3 : 1),
31
28
  MinHeight,
32
29
  MaxHeight
33
30
  );
31
+ const scrollBuffer = Math.max(parentHeight - sectionHeight - 1, 0);
32
+ const disableScrollUp = scrollTop === 0;
33
+ const disableScrollDown = scrollTop === scrollBuffer;
34
+
35
+ const scrollTo = useCallback(
36
+ (height: number) => {
37
+ setScrollTop(clamp(scrollTop + height, 0, scrollBuffer));
38
+ },
39
+ [scrollBuffer, scrollTop]
40
+ );
34
41
 
35
- const scrollTo = useCallback((height: number) => {
42
+ useEffect(() => {
36
43
  if (ref.current) {
37
- ref.current.scrollTop = ref.current.scrollTop + height;
44
+ ref.current.scrollTop = scrollTop;
38
45
  }
39
- }, []);
46
+ }, [scrollTop]);
40
47
 
41
48
  useEffect(() => {
42
49
  const container = ref.current?.parentElement?.parentElement;
43
50
  if (container) {
51
+ const { paddingTop, paddingBottom } = getComputedStyle(container);
52
+ const padding = parseInt(paddingTop) + parseInt(paddingBottom) || 0;
44
53
  const resizeObserver = new ResizeObserver(() => {
45
- setParentHeight(container.getBoundingClientRect().height - padding * 2);
54
+ setParentHeight(container.getBoundingClientRect().height - padding);
46
55
  });
47
56
  resizeObserver.observe(container);
48
57
  return () => resizeObserver.disconnect();
49
58
  }
50
- }, [padding]);
59
+ }, []);
51
60
 
52
61
  return (
53
62
  <>
54
- {needScroll && <UpButton scrollTo={scrollTo} />}
63
+ {needScroll && (
64
+ <UpButton scrollTo={scrollTo} disabled={disableScrollUp} />
65
+ )}
55
66
  <div
56
67
  ref={ref}
57
68
  className={`${name}-section`}
@@ -67,14 +78,16 @@ export function Content({ padding = 16 }: ContentProps) {
67
78
  <ShapesButton />
68
79
  <EraserButton />
69
80
  <CleanButton />
70
- {(app?.config.toolbar?.apps?.enable ?? true) && (
81
+ {hasAppButton && (
71
82
  <AppsButton
72
83
  content={app?.config.toolbar?.apps?.content}
73
84
  onClick={app?.config.toolbar?.apps?.onClick}
74
85
  />
75
86
  )}
76
87
  </div>
77
- {needScroll && <DownButton scrollTo={scrollTo} />}
88
+ {needScroll && (
89
+ <DownButton scrollTo={scrollTo} disabled={disableScrollDown} />
90
+ )}
78
91
  </>
79
92
  );
80
93
  }
@@ -1,9 +1,9 @@
1
1
  import type { Color } from "white-web-sdk";
2
2
 
3
3
  import clsx from "clsx";
4
- import React from "react";
5
- import { useContext } from "react";
6
- import { isEqualArray } from "../../../helpers";
4
+ import React, { useContext } from "react";
5
+
6
+ import { isEqualArray } from "../../../internal";
7
7
  import { ToolbarContext } from "../Toolbar";
8
8
 
9
9
  const colors: Record<string, Color> = {
@@ -8,18 +8,17 @@ import { ToolbarContext } from "../Toolbar";
8
8
  import { ItemHeight } from "../const";
9
9
 
10
10
  export interface UpButtonProps {
11
+ disabled: boolean;
11
12
  scrollTo: (height: number) => void;
12
13
  }
13
14
 
14
- export function UpButton({ scrollTo }: UpButtonProps) {
15
- const { theme, icons, writable } = useContext(ToolbarContext);
15
+ export function UpButton({ disabled, scrollTo }: UpButtonProps) {
16
+ const { theme, icons } = useContext(ToolbarContext);
16
17
  const scrollUp = useCallback(() => scrollTo(-ItemHeight), [scrollTo]);
17
18
 
18
- const disabled = !writable;
19
-
20
19
  return (
21
20
  <>
22
- <Button content="Up" onClick={scrollUp}>
21
+ <Button content="Up" disabled={disabled} onClick={scrollUp}>
23
22
  <Icon
24
23
  fallback={<Icons.Up theme={theme} />}
25
24
  src={disabled ? icons?.upIconDisable : icons?.upIcon}
@@ -31,16 +30,14 @@ export function UpButton({ scrollTo }: UpButtonProps) {
31
30
  );
32
31
  }
33
32
 
34
- export function DownButton({ scrollTo }: UpButtonProps) {
35
- const { theme, icons, writable } = useContext(ToolbarContext);
33
+ export function DownButton({ disabled, scrollTo }: UpButtonProps) {
34
+ const { theme, icons } = useContext(ToolbarContext);
36
35
  const scrollDown = useCallback(() => scrollTo(ItemHeight), [scrollTo]);
37
36
 
38
- const disabled = !writable;
39
-
40
37
  return (
41
38
  <>
42
39
  <CutLine />
43
- <Button content="Down" onClick={scrollDown}>
40
+ <Button content="Down" disabled={disabled} onClick={scrollDown}>
44
41
  <Icon
45
42
  fallback={<Icons.Down theme={theme} />}
46
43
  src={disabled ? icons?.downIconDisable : icons?.downIcon}
@@ -8,7 +8,7 @@ import type {
8
8
  } from "white-web-sdk";
9
9
 
10
10
  import { useCallback, useEffect, useState } from "react";
11
- import { noop } from "../../internal/helpers";
11
+ import { noop } from "../../internal";
12
12
 
13
13
  export function useWritable(room?: Room | null) {
14
14
  const [value, setValue] = useState(false);
@@ -5,7 +5,7 @@ import clsx from "clsx";
5
5
  import React, { useCallback, useEffect, useState } from "react";
6
6
  import Tippy from "@tippyjs/react";
7
7
 
8
- import { clamp } from "../helpers";
8
+ import { clamp } from "../internal";
9
9
  import { TopOffset } from "../theme";
10
10
  import { Icon } from "../icons";
11
11
  import { Minus } from "../icons/Minus";
package/src/hooks.ts CHANGED
@@ -1,10 +1,9 @@
1
- import type { WhiteboardApp } from "./WhiteboardApp";
2
- import type { FastBoardConfig } from "./index";
1
+ import type { WhiteboardApp, WhiteboardAppConfig } from "./WhiteboardApp";
3
2
 
4
- import { useEffect, useRef, useState } from "react";
3
+ import { useEffect, useState } from "react";
5
4
  import { createWhiteboardApp } from "./index";
6
5
 
7
- const SECRET_DEV_KEY = Symbol("fastboard working state");
6
+ export type FastBoardConfig = WhiteboardAppConfig;
8
7
 
9
8
  /**
10
9
  * @example
@@ -24,65 +23,24 @@ export function useFastboard(config: FastBoardConfig): readonly [
24
23
  const [app, setApp] = useState<WhiteboardApp | null>(null);
25
24
  const [currentTarget, ref] = useState<HTMLDivElement | null>(null);
26
25
 
27
- interface WorkingState {
28
- app: WhiteboardApp | undefined;
29
- type: "creating" | "disposing" | undefined;
30
- next: HTMLDivElement | null | undefined;
31
- }
32
-
33
- const working = useRef<WorkingState>({
34
- app: undefined,
35
- type: undefined,
36
- next: undefined,
37
- });
38
-
39
26
  useEffect(() => {
40
- // == HOT RELOAD ==
41
- if (process.env.NODE_ENV !== "production") {
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- const exist = (window as any)[SECRET_DEV_KEY];
44
- if (exist) {
45
- working.current = exist;
46
- } else {
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
- (window as any)[SECRET_DEV_KEY] = working.current;
49
- }
50
- }
27
+ let isMounted = true;
28
+ const promise = createWhiteboardApp(config).then(app => {
29
+ if (isMounted) setApp(app);
30
+ });
31
+ return () => {
32
+ isMounted = false;
33
+ promise.then(() => app?.dispose());
34
+ };
35
+ // ignore config and app change
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, []);
51
38
 
52
- if (working.current.type) {
53
- working.current.next = currentTarget;
54
- return;
39
+ useEffect(() => {
40
+ if (app) {
41
+ app.bindElement(currentTarget);
55
42
  }
56
-
57
- (async () => {
58
- let target: WorkingState["next"] = currentTarget;
59
-
60
- do {
61
- if (target) {
62
- working.current.type = "creating";
63
- if (working.current.app) {
64
- await working.current.app.dispose();
65
- }
66
- working.current.app = await createWhiteboardApp({
67
- ...config,
68
- target: target,
69
- });
70
- setApp(working.current.app);
71
- } else if (target === null) {
72
- working.current.type = "disposing";
73
- if (working.current.app) {
74
- await working.current.app.dispose();
75
- working.current.app = undefined;
76
- }
77
- setApp(null);
78
- }
79
- working.current.type = undefined;
80
-
81
- target = working.current.next;
82
- working.current.next = undefined;
83
- } while (target !== undefined);
84
- })();
85
- }, [config, currentTarget]);
43
+ }, [app, currentTarget]);
86
44
 
87
45
  return Object.assign([app, ref] as const, { app, ref });
88
46
  }
package/src/i18n/en.json CHANGED
@@ -25,6 +25,7 @@
25
25
  "speechBalloon": "Speech Balloon",
26
26
  "rectangle": "Rectangle",
27
27
  "ellipse": "Ellipse",
28
- "straight": "Straight"
28
+ "straight": "Straight",
29
+ "speed": "Speed"
29
30
  }
30
31
  }
@@ -26,6 +26,7 @@
26
26
  "speechBalloon": "气球",
27
27
  "rectangle": "矩形",
28
28
  "ellipse": "椭圆",
29
- "straight": "直线"
29
+ "straight": "直线",
30
+ "speed": "速度"
30
31
  }
31
32
  }
package/src/index.ts CHANGED
@@ -10,13 +10,20 @@ export { PageControl, type PageControlProps } from "./components/PageControl";
10
10
  export { RedoUndo, type RedoUndoProps } from "./components/RedoUndo";
11
11
  export { Toolbar, type ToolbarProps } from "./components/Toolbar";
12
12
  export { ZoomControl, type ZoomControlProps } from "./components/ZoomControl";
13
+ export {
14
+ PlayerControl,
15
+ type PlayerControlProps,
16
+ } from "./components/PlayerControl";
13
17
  export * from "./WhiteboardApp";
14
18
  export * from "./hooks";
15
19
 
16
20
  export const register = WindowManager.register.bind(WindowManager);
17
21
 
18
- export type FastBoardConfig = Omit<WhiteboardAppConfig, "target">;
19
-
22
+ /**
23
+ * @example
24
+ * let app = await createWhiteboardApp(config)
25
+ * app.bindElement(el)
26
+ */
20
27
  export async function createWhiteboardApp(
21
28
  config: WhiteboardAppConfig
22
29
  ): Promise<WhiteboardApp> {
@@ -37,7 +37,6 @@ export type InsertDocsParams = InsertDocsStatic | InsertDocsDynamic;
37
37
  export type Language = "zh-CN" | "en-US";
38
38
 
39
39
  export interface WhiteboardAppConfig {
40
- readonly target: HTMLElement;
41
40
  readonly sdkConfig: SdkConfig;
42
41
  readonly joinRoom: JoinRoom;
43
42
  readonly managerConfig?: Omit<ManagerConfig, "container">;
@@ -63,7 +62,6 @@ export class Instance {
63
62
 
64
63
  readonly config: WhiteboardAppConfig;
65
64
 
66
- target: HTMLElement | null = null;
67
65
  sdk: WhiteWebSdk | null = null;
68
66
  room: Room | null = null;
69
67
  manager: WindowManager | null = null;
@@ -84,14 +82,41 @@ export class Instance {
84
82
  }
85
83
 
86
84
  constructor(config: WhiteboardAppConfig) {
87
- this.target = config.target;
88
85
  this.config = config;
89
86
  this.refreshReadyPromise();
87
+ this.initialize();
88
+ }
89
+
90
+ private _target: HTMLElement | null = null;
91
+
92
+ async initialize() {
93
+ const essentials = await mountWhiteboard(
94
+ this.config.sdkConfig,
95
+ this.config.joinRoom,
96
+ this.config.managerConfig || {},
97
+ this.config.language || "en-US"
98
+ );
99
+ this.accept(essentials);
100
+ this.resolveReady();
101
+ }
102
+
103
+ get target(): HTMLElement | null {
104
+ return this._target;
105
+ }
106
+
107
+ set target(value: HTMLElement | null) {
108
+ if (this._target && value) {
109
+ ReactDOM.unmountComponentAtNode(this._target);
110
+ }
111
+ this._target = value;
90
112
  this.forceUpdate();
91
113
  }
92
114
 
93
- forceUpdate() {
94
- ReactDOM.render(<Root instance={this} />, this.target);
115
+ async forceUpdate() {
116
+ await this.readyPromise;
117
+ if (this.target) {
118
+ ReactDOM.render(<Root instance={this} />, this.target);
119
+ }
95
120
  }
96
121
 
97
122
  accept({ sdk, room, manager, i18n }: AcceptParams) {
@@ -112,19 +137,11 @@ export class Instance {
112
137
  }
113
138
  }
114
139
 
115
- async mount(container: HTMLElement) {
116
- if (this.room) {
117
- console.warn("[WhiteboardApp] already mounted");
118
- return;
140
+ async mount(node: HTMLElement) {
141
+ await this.readyPromise;
142
+ if (this.manager) {
143
+ this.manager.bindContainer(node);
119
144
  }
120
- const essentials = await mountWhiteboard(
121
- this.config.sdkConfig,
122
- this.config.joinRoom,
123
- { ...this.config.managerConfig, container },
124
- this.config.language
125
- );
126
- this.accept(essentials);
127
- this.resolveReady();
128
145
  }
129
146
 
130
147
  async unmount() {
@@ -203,8 +220,12 @@ export class Instance {
203
220
  });
204
221
  }
205
222
 
206
- changeLanguage(language: Language) {
207
- return this.i18n?.changeLanguage(language);
223
+ async changeLanguage(language: Language) {
224
+ try {
225
+ await this.i18n?.changeLanguage(language);
226
+ } finally {
227
+ await this.forceUpdate();
228
+ }
208
229
  }
209
230
  }
210
231
 
@@ -2,6 +2,21 @@ export function noop() {
2
2
  return;
3
3
  }
4
4
 
5
+ export function applyStyles(css: string) {
6
+ const el = document.createElement("style");
7
+ el.appendChild(document.createTextNode(css));
8
+ document.head.appendChild(el);
9
+ return el;
10
+ }
11
+
12
+ export function clamp(value: number, min: number, max: number) {
13
+ return value < min ? min : value > max ? max : value;
14
+ }
15
+
16
+ export function isEqualArray<T>(a: T[], b: T[]) {
17
+ return a.length === b.length && a.every((e, i) => e === b[i]);
18
+ }
19
+
5
20
  export type TaskFn = () => Promise<void> | void;
6
21
 
7
22
  export class Lock {
@@ -1,2 +1,3 @@
1
+ export * from "./helpers";
1
2
  export * from "./mount-whiteboard";
2
3
  export * from "./Instance";
@@ -1,5 +1,9 @@
1
1
  import type { MountParams } from "@netless/window-manager";
2
- import type { JoinRoomParams, WhiteWebSdkConfiguration } from "white-web-sdk";
2
+ import type {
3
+ JoinRoomParams,
4
+ RoomCallbacks,
5
+ WhiteWebSdkConfiguration,
6
+ } from "white-web-sdk";
3
7
  import type { Essentials, Language } from "./instance";
4
8
 
5
9
  import { WindowManager } from "@netless/window-manager";
@@ -13,7 +17,7 @@ export type SdkConfig = Omit<
13
17
  export type JoinRoom = Omit<
14
18
  JoinRoomParams,
15
19
  "useMultiViews" | "disableMagixEventDispatchLimit"
16
- >;
20
+ > & { callbacks?: Partial<RoomCallbacks> };
17
21
  export type ManagerConfig = Omit<MountParams, "room">;
18
22
 
19
23
  function ensureWindowManager(joinRoom: JoinRoom) {
@@ -45,7 +49,7 @@ export async function mountWhiteboard(
45
49
  sdkConfig: SdkConfig,
46
50
  joinRoom: JoinRoom,
47
51
  managerConfig: ManagerConfig,
48
- language?: Language
52
+ language: Language
49
53
  ): Promise<Essentials> {
50
54
  const sdk = new WhiteWebSdk({
51
55
  ...sdkConfig,
@@ -53,7 +57,10 @@ export async function mountWhiteboard(
53
57
  });
54
58
 
55
59
  ensureWindowManager(joinRoom);
56
- const room = await sdk.joinRoom({
60
+ joinRoom = { ...joinRoom };
61
+ const callbacks = joinRoom.callbacks;
62
+ delete joinRoom.callbacks;
63
+ const joinRoomParams: JoinRoomParams = {
57
64
  floatBar: true,
58
65
  hotKeys: {
59
66
  ...DefaultHotKeys,
@@ -63,7 +70,8 @@ export async function mountWhiteboard(
63
70
  useMultiViews: true,
64
71
  disableNewPencil: false,
65
72
  disableMagixEventDispatchLimit: true,
66
- });
73
+ };
74
+ const room = await sdk.joinRoom(joinRoomParams, callbacks);
67
75
 
68
76
  const manager = await WindowManager.mount({
69
77
  cursor: true,
package/src/style.scss CHANGED
@@ -7,6 +7,7 @@
7
7
  @import "./components/PageControl.scss";
8
8
  @import "./components/ZoomControl.scss";
9
9
  @import "./components/Toolbar/Toolbar.scss";
10
+ @import "./components/PlayerControl/PlayerControl.scss";
10
11
 
11
12
  .tippy-box.fastboard-tip {
12
13
  color: #eee;