@netless/window-manager 1.0.0-canary.33 → 1.0.0-canary.36

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.
@@ -7,13 +7,14 @@ import {
7
7
  reaction,
8
8
  unlistenDisposed,
9
9
  unlistenUpdated,
10
- toJS,
10
+ toJS
11
11
  } from "white-web-sdk";
12
12
  import type {
13
13
  Room,
14
14
  SceneDefinition,
15
15
  View,
16
- EventListener as WhiteEventListener
16
+ EventListener as WhiteEventListener,
17
+ Player
17
18
  } from "white-web-sdk";
18
19
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
19
20
  import type Emittery from "emittery";
@@ -91,6 +92,15 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
91
92
  return this.appProxy.view;
92
93
  };
93
94
 
95
+ public get now(): number {
96
+ if (this.isReplay) {
97
+ const player = this.displayer as Player;
98
+ return player.beginTimestamp + player.progressTime;
99
+ } else {
100
+ return (this.displayer as Room).calibrationTimestamp;
101
+ }
102
+ }
103
+
94
104
  public createWhiteBoardView = (params?: CreateWhiteBoardViewParams): WhiteBoardView => {
95
105
  if (this.whiteBoardView) {
96
106
  return this.whiteBoardView;
@@ -2,9 +2,10 @@ import { callbacks } from "./callback";
2
2
  import { emitter } from "./InternalEmitter";
3
3
  import { Events, MagixEventName } from "./constants";
4
4
  import { setViewFocusScenePath } from "./Utils/Common";
5
- import type { Event } from "white-web-sdk";
5
+ import type { AnimationMode, Camera, Event, Rectangle } from "white-web-sdk";
6
6
  import type { AppManager } from "./AppManager";
7
7
  import type { TeleBoxState } from "@netless/telebox-insider";
8
+ import { computedMinScale } from "./View/CameraSynchronizer";
8
9
 
9
10
  type SetAppFocusIndex = {
10
11
  type: "main" | "app";
@@ -73,6 +74,14 @@ export class AppListeners {
73
74
  this.manager.attributesUpdateCallback(this.manager.attributes.apps);
74
75
  break;
75
76
  }
77
+ case Events.MoveCamera: {
78
+ this.moveCameraHandler(data.payload);
79
+ break;
80
+ }
81
+ case Events.MoveCameraToContain: {
82
+ this.moveCameraToContainHandler(data.payload);
83
+ break;
84
+ }
76
85
  default:
77
86
  break;
78
87
  }
@@ -125,4 +134,20 @@ export class AppListeners {
125
134
  }
126
135
  }
127
136
  }
137
+
138
+ private moveCameraHandler = (payload: Camera) => {
139
+ const cameraPayload = payload;
140
+ if (payload.scale) {
141
+ const remoteSize = this.manager.mainViewProxy.size$.value;
142
+ const currentSize = this.manager.boxManager?.stageRect;
143
+ if (remoteSize && currentSize) {
144
+ cameraPayload.scale = payload.scale * computedMinScale(remoteSize, currentSize);
145
+ }
146
+ }
147
+ this.manager.mainView.moveCamera(cameraPayload);
148
+ }
149
+
150
+ private moveCameraToContainHandler = (payload: Rectangle & { animationMode?: AnimationMode }) => {
151
+ this.manager.mainView.moveCameraToContain(payload);
152
+ }
128
153
  }
package/src/BoxManager.ts CHANGED
@@ -15,7 +15,7 @@ import type {
15
15
  TeleBoxRect,
16
16
  TeleBoxConfig,
17
17
  TeleBoxFullscreen
18
- } from "@netless/telebox-insider";
18
+ , TeleBoxManagerThemeConfig } from "@netless/telebox-insider";
19
19
  import type Emittery from "emittery";
20
20
  import type { NetlessApp } from "./typings";
21
21
  import type { View } from "white-web-sdk";
@@ -53,6 +53,9 @@ export type CreateTeleBoxManagerConfig = {
53
53
  containerStyle?: string;
54
54
  stageStyle?: string;
55
55
  fullscreen?: TeleBoxFullscreen;
56
+ defaultBoxBodyStyle?: string | null;
57
+ defaultBoxStageStyle?: string | null;
58
+ theme?: TeleBoxManagerThemeConfig;
56
59
  };
57
60
 
58
61
  export type BoxManagerContext = {
@@ -268,6 +271,18 @@ export class BoxManager {
268
271
  initManagerState.fullscreen = createTeleBoxManagerConfig.fullscreen;
269
272
  }
270
273
 
274
+ if (createTeleBoxManagerConfig?.defaultBoxBodyStyle !== undefined) {
275
+ initManagerState.defaultBoxBodyStyle = createTeleBoxManagerConfig.defaultBoxBodyStyle;
276
+ }
277
+
278
+ if (createTeleBoxManagerConfig?.defaultBoxStageStyle !== undefined) {
279
+ initManagerState.defaultBoxStageStyle = createTeleBoxManagerConfig.defaultBoxStageStyle;
280
+ }
281
+
282
+ if (createTeleBoxManagerConfig?.theme) {
283
+ initManagerState.theme = createTeleBoxManagerConfig.theme;
284
+ }
285
+
271
286
  const manager = new TeleBoxManager(initManagerState);
272
287
  if (this.teleBoxManager) {
273
288
  this.teleBoxManager.destroy();
@@ -1,28 +1,24 @@
1
1
  import AppDocsViewer from "@netless/app-docs-viewer";
2
- import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
2
+ import Plyr from "@netless/app-plyr";
3
3
  import { WindowManager } from "./index";
4
4
 
5
5
  export const setupBuiltin = () => {
6
- if (WindowManager.debug) {
7
- setOptions({ verbose: true });
8
- }
9
-
10
6
  WindowManager.register({
11
7
  kind: AppDocsViewer.kind,
12
8
  src: AppDocsViewer,
13
9
  });
14
10
  WindowManager.register({
15
- kind: AppMediaPlayer.kind,
16
- src: AppMediaPlayer,
11
+ kind: Plyr.kind,
12
+ src: Plyr,
17
13
  });
18
14
  };
19
15
 
20
16
  export const BuiltinApps = {
21
17
  DocsViewer: AppDocsViewer.kind as string,
22
- MediaPlayer: AppMediaPlayer.kind as string,
18
+ MediaPlayer: Plyr.kind as string,
23
19
  };
24
20
 
25
21
  export const BuiltinAppsMap = {
26
22
  [BuiltinApps.DocsViewer]: AppDocsViewer,
27
- [BuiltinApps.MediaPlayer]: AppMediaPlayer,
23
+ [BuiltinApps.MediaPlayer]: Plyr,
28
24
  }
@@ -1,7 +1,7 @@
1
1
  import { AnimationMode } from "white-web-sdk";
2
2
  import { isEqual, pick, throttle } from "lodash";
3
3
  import type { TeleBoxRect } from "@netless/telebox-insider";
4
- import type { Camera, View } from "white-web-sdk";
4
+ import type { Camera, View, Size } from "white-web-sdk";
5
5
  import type { ICamera, ISize } from "../AttributesDelegate";
6
6
 
7
7
  export type SaveCamera = (camera: ICamera) => void;
@@ -30,12 +30,9 @@ export class CameraSynchronizer {
30
30
  this.remoteCamera = camera;
31
31
  this.remoteSize = size;
32
32
  if (this.remoteSize && this.rect) {
33
- const wScale = this.rect.width / size.width;
34
- const hScale = this.rect.height / size.height;
35
- const nextScale = camera.scale * Math.min(wScale, hScale);
36
- const config: Partial<Camera> & { animationMode: AnimationMode } = {
33
+ const nextScale = camera.scale * computedMinScale(size, this.rect);
34
+ const config: Partial<Camera> = {
37
35
  scale: nextScale,
38
- animationMode: AnimationMode.Continuous,
39
36
  }
40
37
  if (camera.centerX !== null) {
41
38
  config.centerX = camera.centerX;
@@ -65,6 +62,12 @@ export class CameraSynchronizer {
65
62
  }
66
63
 
67
64
  private moveCamera(camera: Partial<Camera>) {
68
- this.view?.moveCamera({ ...camera, animationMode: AnimationMode.Continuous });
65
+ this.view?.moveCamera({ ...camera, animationMode: AnimationMode.Immediately });
69
66
  }
70
67
  }
68
+
69
+ export const computedMinScale = (remoteSize: Size, currentSize: Size) => {
70
+ const wScale = currentSize.width / remoteSize.width;
71
+ const hScale = currentSize.height / remoteSize.height;
72
+ return Math.min(wScale, hScale);
73
+ }
@@ -49,6 +49,16 @@ export class MainViewProxy {
49
49
  }
50
50
  }
51
51
  }));
52
+ this.camera$.reaction(camera => {
53
+ if (camera) {
54
+ callbacks.emit("baseCameraChange", camera);
55
+ }
56
+ });
57
+ this.size$.reaction(size => {
58
+ if (size) {
59
+ callbacks.emit("baseSizeChange", size);
60
+ }
61
+ });
52
62
  }
53
63
 
54
64
  public createViewSync = () => {
@@ -1,7 +1,6 @@
1
- import { ViewMode, AnimationMode } from "white-web-sdk";
2
- import { CameraSynchronizer } from "./CameraSynchronizer";
1
+ import { ViewMode, AnimationMode, Size } from "white-web-sdk";
2
+ import { CameraSynchronizer, computedMinScale } from "./CameraSynchronizer";
3
3
  import { combine } from "value-enhancer";
4
- import { isEqual } from "lodash";
5
4
  import { SideEffectManager } from "side-effect-manager";
6
5
  import type { Camera, View } from "white-web-sdk";
7
6
  import type { Val, ReadonlyVal } from "value-enhancer";
@@ -94,13 +93,11 @@ export class ViewSync {
94
93
 
95
94
  private onCameraUpdatedByDevice = (camera: Camera) => {
96
95
  if (!camera) return;
97
- this.synchronizer.onLocalCameraUpdate({ ...camera, id: this.context.uid });
98
- const stage = this.context.stageRect$.value;
99
- if (stage) {
100
- const size = { width: stage.width, height: stage.height, id: this.context.uid };
101
- if (!isEqual(size, this.context.size$.value)) {
102
- this.context.storeSize(size);
103
- }
96
+ if (this.context.size$.value && this.context.stageRect$.value) {
97
+ // 始终以远端的 size 为标准, 设置到 attributes 时根据尺寸的大小还原回去
98
+ const diffScale = computedMinScale(this.context.size$.value, this.context.stageRect$.value);
99
+ const remoteScale = camera.scale / diffScale;
100
+ this.synchronizer.onLocalCameraUpdate({ ...camera, scale: remoteScale, id: this.context.uid });
104
101
  }
105
102
  };
106
103
 
package/src/callback.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import Emittery from "emittery";
2
2
  import type { TeleBoxColorScheme, TELE_BOX_STATE } from "@netless/telebox-insider";
3
- import type { CameraState, SceneState, ViewVisionMode } from "white-web-sdk";
3
+ import type { Camera, CameraState, SceneState, Size, ViewVisionMode } from "white-web-sdk";
4
4
  import type { LoadAppEvent } from "./Register";
5
5
  import type { PageState } from "./Page";
6
+ import { ICamera, ISize } from "./AttributesDelegate";
6
7
 
7
8
  export type PublicEvent = {
8
9
  mainViewModeChange: ViewVisionMode;
@@ -21,6 +22,8 @@ export type PublicEvent = {
21
22
  sceneStateChange: SceneState;
22
23
  pageStateChange: PageState;
23
24
  appClose: { appId: string; kind: string, error?: Error };
25
+ baseCameraChange: ICamera;
26
+ baseSizeChange: ISize;
24
27
  };
25
28
 
26
29
  export type CallbacksType = Emittery<PublicEvent>;
package/src/constants.ts CHANGED
@@ -15,6 +15,8 @@ export enum Events {
15
15
  Refresh = "Refresh",
16
16
  InitMainViewCamera = "InitMainViewCamera",
17
17
  InvokeAttributesUpdateCallback = "InvokeAttributesUpdateCallback",
18
+ MoveCamera = "MoveCamera",
19
+ MoveCameraToContain = "moveCameraToContain",
18
20
  }
19
21
 
20
22
  export const MagixEventName = "__WindowManger";
package/src/index.ts CHANGED
@@ -9,14 +9,13 @@ import { DEFAULT_CONTAINER_RATIO, Events, INIT_DIR, ROOT_DIR } from "./constants
9
9
  import { emitter } from "./InternalEmitter";
10
10
  import { Fields } from "./AttributesDelegate";
11
11
  import { initDb } from "./Register/storage";
12
- import { InvisiblePlugin, isPlayer, isRoom, RoomPhase, ViewMode } from "white-web-sdk";
13
- import { isEqual, isNull, isObject, isNumber } from "lodash";
12
+ import { AnimationMode, InvisiblePlugin, isPlayer, isRoom, RoomPhase, ViewMode } from "white-web-sdk";
13
+ import { isEqual, isNull, isObject, isNumber, isEmpty } from "lodash";
14
14
  import { log } from "./Utils/log";
15
15
  import { PageStateImpl } from "./PageState";
16
16
  import { ReconnectRefresher } from "./ReconnectRefresher";
17
17
  import { replaceRoomFunction } from "./Utils/RoomHacker";
18
18
  import { setupBuiltin } from "./BuiltinApps";
19
- import "video.js/dist/video-js.css";
20
19
  import "./style.css";
21
20
  import "@netless/telebox-insider/dist/style.css";
22
21
  import {
@@ -45,14 +44,17 @@ import type {
45
44
  Player,
46
45
  ImageInformation,
47
46
  SceneState,
48
- Rectangle} from "white-web-sdk";
47
+ Rectangle,
48
+ Size
49
+ } from "white-web-sdk";
49
50
  import type { AppListeners } from "./AppListener";
50
51
  import type { ApplianceIcons, NetlessApp, RegisterParams } from "./typings";
51
- import type { TeleBoxColorScheme, TeleBoxFullscreen, TeleBoxState } from "@netless/telebox-insider";
52
+ import type { TeleBoxColorScheme, TeleBoxFullscreen, TeleBoxManagerThemeConfig, TeleBoxState } from "@netless/telebox-insider";
52
53
  import type { AppProxy } from "./App";
53
54
  import type { PublicEvent } from "./callback";
54
55
  import type Emittery from "emittery";
55
56
  import type { PageController, AddPageParams, PageState } from "./Page";
57
+ import { computedMinScale } from "./View/CameraSynchronizer";
56
58
 
57
59
  export type WindowMangerAttributes = {
58
60
  modelValue?: string;
@@ -136,8 +138,6 @@ export type MountParams = {
136
138
  containerSizeRatio?: number;
137
139
  /** @deprecated */
138
140
  chessboard?: boolean;
139
- /** 是否高亮显示同步区域, 默认为 true */
140
- highlightStage?: boolean;
141
141
  collectorContainer?: HTMLElement;
142
142
  collectorStyles?: Partial<CSSStyleDeclaration>;
143
143
  overwriteStyles?: string;
@@ -149,6 +149,12 @@ export type MountParams = {
149
149
  prefersColorScheme?: TeleBoxColorScheme;
150
150
  applianceIcons?: ApplianceIcons;
151
151
  fullscreen?: TeleBoxFullscreen;
152
+ /** Custom `style` attribute value for content area of all boxes. Can be overwritten by box. */
153
+ defaultBoxBodyStyle?: string | null;
154
+ /** Custom `style` attribute value for stage area of all boxes. Can be overwritten by box. */
155
+ defaultBoxStageStyle?: string | null;
156
+ /** Theme variable */
157
+ theme?: TeleBoxManagerThemeConfig;
152
158
  };
153
159
 
154
160
  export const reconnectRefresher = new ReconnectRefresher({ emitter });
@@ -663,17 +669,17 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
663
669
  }
664
670
  }
665
671
 
666
- public get baseCamera$() {
672
+ public get baseCamera() {
667
673
  if (this.appManager) {
668
- return this.appManager.mainViewProxy.camera$;
674
+ return this.appManager.mainViewProxy.camera$.value;
669
675
  } else {
670
676
  throw new Errors.AppManagerNotInitError();
671
677
  }
672
678
  }
673
679
 
674
- public get baseSize$() {
680
+ public get baseSize() {
675
681
  if (this.appManager) {
676
- return this.appManager.mainViewProxy.size$;
682
+ return this.appManager.mainViewProxy.size$.value;
677
683
  } else {
678
684
  throw new Errors.AppManagerNotInitError();
679
685
  }
@@ -793,30 +799,86 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
793
799
  return this.appManager?.closeApp(appId);
794
800
  }
795
801
 
796
- public moveCamera(camera: Partial<Camera> ): void {
802
+ public moveCamera(camera: Partial<Camera> & { animationMode?: AnimationMode } ): void {
797
803
  const mainViewCamera = { ...this.mainView.camera };
798
804
  const nextCamera = { ...mainViewCamera, ...camera };
799
805
  if (isEqual(nextCamera, mainViewCamera)) return;
800
806
  if (!this.appManager) return;
801
- this.appManager.mainViewProxy.storeCamera({
802
- id: this.appManager.uid,
803
- ...nextCamera
804
- });
807
+ if (camera.animationMode === AnimationMode.Immediately) {
808
+ this.appManager.mainViewProxy.storeCamera({
809
+ id: this.appManager.uid,
810
+ ...nextCamera
811
+ });
812
+ } else {
813
+ const remoteCamera = this.appManager.mainViewProxy.size$.value;
814
+ const currentSize = this.boxManager?.stageRect;
815
+ let nextScale;
816
+ if (camera.scale && remoteCamera && currentSize) {
817
+ nextScale = camera.scale * computedMinScale(remoteCamera, currentSize);
818
+ }
819
+ if (nextScale) {
820
+ this.mainView.moveCamera({
821
+ ...camera,
822
+ scale: nextScale,
823
+ });
824
+ } else {
825
+ this.mainView.moveCamera(camera);
826
+ }
827
+ this.appManager.dispatchInternalEvent(Events.MoveCamera, camera);
828
+ setTimeout(() => {
829
+ if (!this.appManager) return;
830
+ this.appManager.mainViewProxy.storeCamera({
831
+ id: this.appManager.uid,
832
+ ...nextCamera
833
+ });
834
+ }, 200);
835
+ }
805
836
  }
806
837
 
807
- public moveCameraToContain(rectangle: Rectangle): void {
838
+ public moveCameraToContain(rectangle: Rectangle & { animationMode?: AnimationMode }): void {
808
839
  if (!this.appManager) return;
809
- const mainViewSize = this.appManager.mainViewProxy.size$.value;
810
- if (mainViewSize) {
811
- const wScale = mainViewSize.width / rectangle.width;
812
- const hScale = mainViewSize.height / rectangle.height;
813
- const nextScale = Math.min(wScale, hScale);
814
- this.appManager.mainViewProxy.storeCamera({
840
+ const camera: Partial<Camera> = {};
841
+ if (isNumber(rectangle.originX)) {
842
+ camera.centerX = rectangle.originX;
843
+ }
844
+ if (isNumber(rectangle.originY)) {
845
+ camera.centerY = rectangle.originY;
846
+ }
847
+ if (rectangle.animationMode === AnimationMode.Immediately) {
848
+ this.appManager.mainViewProxy.storeSize({
815
849
  id: this.appManager.uid,
816
- scale: nextScale,
817
- centerX: rectangle.originX,
818
- centerY: rectangle.originY,
850
+ width: rectangle.width,
851
+ height: rectangle.height,
819
852
  });
853
+ this.mainView.moveCameraToContain(rectangle);
854
+ if (!isEmpty(camera) && this.appManager.mainViewProxy.camera$.value) {
855
+ this.appManager.mainViewProxy.storeCamera({
856
+ ...this.appManager.mainViewProxy.camera$.value,
857
+ id: this.appManager.uid,
858
+ centerX: this.mainView.camera.centerX,
859
+ centerY: this.mainView.camera.centerY
860
+ });
861
+ }
862
+ } else {
863
+ this.appManager.dispatchInternalEvent(Events.MoveCameraToContain, rectangle);
864
+ this.mainView.moveCameraToContain(rectangle);
865
+ setTimeout(() => {
866
+ if (!this.appManager) return;
867
+ this.appManager.mainViewProxy.storeSize({
868
+ id: this.appManager.uid,
869
+ width: rectangle.width,
870
+ height: rectangle.height,
871
+ });
872
+
873
+ if (!isEmpty(camera) && this.appManager.mainViewProxy.camera$.value) {
874
+ this.appManager.mainViewProxy.storeCamera({
875
+ ...this.appManager.mainViewProxy.camera$.value,
876
+ id: this.appManager.uid,
877
+ centerX: this.mainView.camera.centerX,
878
+ centerY: this.mainView.camera.centerY
879
+ });
880
+ }
881
+ }, 200);
820
882
  }
821
883
  }
822
884
 
@@ -969,6 +1031,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
969
1031
  this.boxManager?.teleBoxManager.setStageStyle(style);
970
1032
  }
971
1033
 
1034
+ public setBaseSize(size: Size) {
1035
+ this.appManager?.mainViewProxy.setMainViewSize(size);
1036
+ }
1037
+
972
1038
  public createPPTHandler() {
973
1039
  return {
974
1040
  onPageJumpTo: (_pptUUID: string, index: number) => {
package/vite.config.js CHANGED
@@ -15,7 +15,9 @@ export default defineConfig(({ mode }) => {
15
15
  inline: [
16
16
  "@juggle/resize-observer"
17
17
  ]
18
- }
18
+ },
19
+ setupFiles: "./test/setup.ts",
20
+ include: ["test/**/*.test.ts"],
19
21
  },
20
22
  define: {
21
23
  __APP_VERSION__: JSON.stringify(version),