@netless/window-manager 1.0.0-canary.3 → 1.0.0-canary.32

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 (87) hide show
  1. package/__mocks__/white-web-sdk.ts +10 -1
  2. package/dist/index.cjs.js +120 -12
  3. package/dist/index.es.js +2284 -954
  4. package/dist/index.umd.js +120 -12
  5. package/dist/{App → src/App}/AppContext.d.ts +12 -8
  6. package/dist/{App → src/App}/AppPageStateImpl.d.ts +0 -0
  7. package/dist/{App → src/App}/AppProxy.d.ts +36 -7
  8. package/dist/{App → src/App}/MagixEvent/index.d.ts +0 -0
  9. package/dist/{App → src/App}/Storage/StorageEvent.d.ts +0 -0
  10. package/dist/{App → src/App}/Storage/index.d.ts +0 -0
  11. package/dist/{App → src/App}/Storage/typings.d.ts +0 -0
  12. package/dist/{App → src/App}/Storage/utils.d.ts +0 -0
  13. package/dist/src/App/WhiteboardView.d.ts +27 -0
  14. package/dist/{App → src/App}/index.d.ts +2 -1
  15. package/dist/src/App/type.d.ts +21 -0
  16. package/dist/{AppListener.d.ts → src/AppListener.d.ts} +0 -2
  17. package/dist/{AppManager.d.ts → src/AppManager.d.ts} +7 -6
  18. package/dist/{AttributesDelegate.d.ts → src/AttributesDelegate.d.ts} +11 -16
  19. package/dist/{BoxEmitter.d.ts → src/BoxEmitter.d.ts} +0 -0
  20. package/dist/{BoxManager.d.ts → src/BoxManager.d.ts} +10 -7
  21. package/dist/{BuiltinApps.d.ts → src/BuiltinApps.d.ts} +3 -0
  22. package/dist/{Cursor → src/Cursor}/Cursor.d.ts +0 -0
  23. package/dist/{Cursor → src/Cursor}/icons.d.ts +0 -0
  24. package/dist/{Cursor → src/Cursor}/index.d.ts +3 -3
  25. package/dist/{Helper.d.ts → src/Helper.d.ts} +4 -8
  26. package/dist/{InternalEmitter.d.ts → src/InternalEmitter.d.ts} +3 -4
  27. package/dist/{Page → src/Page}/PageController.d.ts +1 -0
  28. package/dist/{Page → src/Page}/index.d.ts +0 -0
  29. package/dist/{PageState.d.ts → src/PageState.d.ts} +0 -0
  30. package/dist/{ReconnectRefresher.d.ts → src/ReconnectRefresher.d.ts} +1 -1
  31. package/dist/{RedoUndo.d.ts → src/RedoUndo.d.ts} +0 -0
  32. package/dist/{Register → src/Register}/index.d.ts +0 -0
  33. package/dist/{Register → src/Register}/loader.d.ts +0 -0
  34. package/dist/{Register → src/Register}/storage.d.ts +0 -0
  35. package/dist/{Utils → src/Utils}/AppCreateQueue.d.ts +0 -0
  36. package/dist/{Utils → src/Utils}/Common.d.ts +1 -0
  37. package/dist/{Utils → src/Utils}/Reactive.d.ts +0 -0
  38. package/dist/{Utils → src/Utils}/RoomHacker.d.ts +0 -0
  39. package/dist/{Utils → src/Utils}/error.d.ts +0 -0
  40. package/dist/{Utils → src/Utils}/log.d.ts +0 -0
  41. package/dist/src/View/CameraSynchronizer.d.ts +18 -0
  42. package/dist/{View → src/View}/MainView.d.ts +18 -7
  43. package/dist/{View → src/View}/ViewManager.d.ts +0 -0
  44. package/dist/src/View/ViewSync.d.ts +24 -0
  45. package/dist/{callback.d.ts → src/callback.d.ts} +5 -0
  46. package/dist/{constants.d.ts → src/constants.d.ts} +8 -5
  47. package/dist/src/image.d.ts +19 -0
  48. package/dist/{index.d.ts → src/index.d.ts} +40 -14
  49. package/dist/src/shim.d.ts +11 -0
  50. package/dist/{typings.d.ts → src/typings.d.ts} +8 -2
  51. package/dist/style.css +1 -1
  52. package/docs/app-context.md +157 -25
  53. package/docs/mirgrate-to-1.0.md +28 -0
  54. package/package.json +12 -7
  55. package/playwright.config.ts +29 -0
  56. package/pnpm-lock.yaml +517 -35
  57. package/src/App/AppContext.ts +50 -28
  58. package/src/App/AppProxy.ts +266 -80
  59. package/src/App/{WhiteBoardView.ts → WhiteboardView.ts} +38 -7
  60. package/src/App/index.ts +2 -1
  61. package/src/App/type.ts +22 -0
  62. package/src/AppListener.ts +5 -21
  63. package/src/AppManager.ts +56 -43
  64. package/src/AttributesDelegate.ts +19 -19
  65. package/src/BoxManager.ts +60 -40
  66. package/src/BuiltinApps.ts +5 -0
  67. package/src/Cursor/Cursor.ts +7 -3
  68. package/src/Cursor/index.ts +7 -8
  69. package/src/Helper.ts +25 -7
  70. package/src/InternalEmitter.ts +3 -4
  71. package/src/Page/PageController.ts +1 -0
  72. package/src/PageState.ts +1 -1
  73. package/src/ReconnectRefresher.ts +7 -2
  74. package/src/Utils/Common.ts +9 -0
  75. package/src/Utils/Reactive.ts +27 -26
  76. package/src/Utils/RoomHacker.ts +3 -0
  77. package/src/View/CameraSynchronizer.ts +37 -34
  78. package/src/View/MainView.ts +108 -81
  79. package/src/View/ViewSync.ts +110 -0
  80. package/src/callback.ts +1 -0
  81. package/src/constants.ts +6 -3
  82. package/src/index.ts +141 -57
  83. package/src/style.css +3 -46
  84. package/src/typings.ts +8 -2
  85. package/vite.config.js +5 -3
  86. package/dist/App/WhiteBoardView.d.ts +0 -18
  87. package/dist/View/CameraSynchronizer.d.ts +0 -17
@@ -1,20 +1,20 @@
1
1
  import { AnimationMode } from "white-web-sdk";
2
- import { delay, throttle } from "lodash";
2
+ import { isEqual, pick, throttle } from "lodash";
3
3
  import type { TeleBoxRect } from "@netless/telebox-insider";
4
- import type { Camera, View, Size } from "white-web-sdk";
5
- import type { MainViewSize } from "../AttributesDelegate";
4
+ import type { Camera, View } from "white-web-sdk";
5
+ import type { ICamera, ISize } from "../AttributesDelegate";
6
6
 
7
- export type SaveCamera = (camera: Camera) => void;
7
+ export type SaveCamera = (camera: ICamera) => void;
8
8
 
9
9
  export class CameraSynchronizer {
10
- protected remoteCamera?: Camera;
11
- protected remoteSize?: MainViewSize;
10
+ public remoteCamera?: ICamera;
11
+ public remoteSize?: ISize;
12
12
  protected rect?: TeleBoxRect;
13
13
  protected view?: View;
14
14
 
15
15
  constructor(protected saveCamera: SaveCamera) {}
16
16
 
17
- public setRect(rect: TeleBoxRect) {
17
+ public setRect = (rect: TeleBoxRect) => {
18
18
  this.rect = rect;
19
19
  if (this.remoteCamera && this.remoteSize) {
20
20
  this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
@@ -26,42 +26,45 @@ export class CameraSynchronizer {
26
26
  }
27
27
 
28
28
  // 远端 Camera 或者 size 更新
29
- public onRemoteUpdate = throttle((camera: Camera, size: MainViewSize) => {
29
+ public onRemoteUpdate = throttle((camera: ICamera, size: ISize) => {
30
30
  this.remoteCamera = camera;
31
31
  this.remoteSize = size;
32
32
  if (this.remoteSize && this.rect) {
33
- let scale: number;
34
- if (size.width < size.height) {
35
- scale = this.rect.width / size.width;
36
- } else {
37
- scale = this.rect.height / size.height;
38
- }
39
- const nextScale = camera.scale * scale;
40
- const moveCamera = () => this.view?.moveCamera({
41
- centerX: camera.centerX,
42
- centerY: camera.centerY,
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 } = {
43
37
  scale: nextScale,
44
- animationMode: AnimationMode.Immediately,
45
- });
46
- // TODO 直接调用 moveCamera 依然会出现 camera 错误的情况,这里暂时加一个 delay 保证 camera 是对的, 后续需要 SDK 进行修改
47
- delay(moveCamera, 10);
38
+ animationMode: AnimationMode.Continuous,
39
+ }
40
+ if (camera.centerX !== null) {
41
+ config.centerX = camera.centerX;
42
+ }
43
+ if (camera.centerY !== null) {
44
+ config.centerY = camera.centerY;
45
+ }
46
+ this.moveCamera(config);
48
47
  }
49
- }, 50);
48
+ }, 10);
50
49
 
50
+ public onRemoteSizeUpdate(size: ISize) {
51
+ this.remoteSize = size;
52
+ const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size, ["width", "height"]));
53
+ if (this.rect && this.remoteCamera && needMoveCamera) {
54
+ const scale = this.rect.width / size.width;
55
+ const nextScale = this.remoteCamera.scale * scale;
56
+ this.moveCamera({
57
+ scale: nextScale,
58
+ })
59
+ }
60
+ }
51
61
 
52
- public onLocalCameraUpdate(camera: Camera) {
62
+ public onLocalCameraUpdate(camera: ICamera) {
53
63
  this.saveCamera(camera);
64
+ this.remoteCamera = camera;
54
65
  }
55
66
 
56
- // 本地 Size 更新, 先匹配 camera 到新的 size 然后再发送 camera 数据到远端
57
- public onLocalSizeUpdate(size: Size) {
58
- if (this.rect && this.view) {
59
- const scale = this.rect.width / size.width;
60
- const nextScale = this.view.camera.scale * scale;
61
- this.view.moveCamera({
62
- scale: nextScale,
63
- animationMode: AnimationMode.Immediately
64
- });
65
- }
67
+ private moveCamera(camera: Partial<Camera>) {
68
+ this.view?.moveCamera({ ...camera, animationMode: AnimationMode.Continuous });
66
69
  }
67
70
  }
@@ -1,73 +1,96 @@
1
- import { reaction } from "white-web-sdk";
2
1
  import { callbacks } from "../callback";
3
2
  import { createView } from "./ViewManager";
4
3
  import { debounce, get, isEqual } from "lodash";
5
4
  import { emitter } from "../InternalEmitter";
6
5
  import { Events } from "../constants";
7
6
  import { Fields } from "../AttributesDelegate";
8
- import { setViewFocusScenePath } from "../Utils/Common";
7
+ import { AnimationMode, reaction, toJS } from "white-web-sdk";
8
+ import { releaseView, setScenePath, setViewFocusScenePath } from "../Utils/Common";
9
9
  import { SideEffectManager } from "side-effect-manager";
10
- import type { Camera, Size, View } from "white-web-sdk";
10
+ import { Val } from "value-enhancer";
11
+ import { ViewSync } from "./ViewSync";
12
+ import type { ICamera, ISize } from "../AttributesDelegate";
13
+ import type { Size, View } from "white-web-sdk";
11
14
  import type { AppManager } from "../AppManager";
12
- import { CameraSynchronizer } from "./CameraSynchronizer";
13
15
 
14
16
  export class MainViewProxy {
15
17
  private started = false;
16
18
  private mainViewIsAddListener = false;
17
19
  private mainView: View;
18
20
  private store = this.manager.store;
19
- private synchronizer: CameraSynchronizer;
20
21
 
21
22
  private sideEffectManager = new SideEffectManager();
22
23
 
24
+ public camera$ = new Val<ICamera | undefined>(undefined);
25
+ public size$ = new Val<ISize | undefined>(undefined);
26
+ public view$ = new Val<View | undefined>(undefined);
27
+
28
+ public viewSync?: ViewSync;
29
+
23
30
  constructor(private manager: AppManager) {
24
- this.synchronizer = new CameraSynchronizer(
25
- camera => this.store.setMainViewCamera({ ...camera, id: this.manager.uid })
26
- );
27
31
  this.mainView = this.createMainView();
28
- this.moveCameraSizeByAttributes();
29
32
  emitter.once("mainViewMounted").then(() => {
30
33
  this.addMainViewListener();
31
34
  this.start();
32
35
  this.ensureCameraAndSize();
33
36
  this.startListenWritableChange();
34
37
  });
35
- this.sideEffectManager.add(() => {
36
- return emitter.on("containerSizeRatioUpdate", this.onUpdateContainerSizeRatio);
37
- });
38
- this.sideEffectManager.add(() => {
39
- return emitter.on("startReconnect", () => {
40
- if (!(this.mainView as any).didRelease) {
41
- this.mainView.release();
38
+ this.sideEffectManager.add(() => [
39
+ emitter.on("startReconnect", () => {
40
+ releaseView(this.mainView);
41
+ }),
42
+ ]);
43
+ this.createViewSync();
44
+ this.sideEffectManager.add(() => emitter.on("focusedChange", ({ focused }) => {
45
+ if (focused === undefined) {
46
+ const scenePath = this.store.getMainViewScenePath();
47
+ if (scenePath) {
48
+ setScenePath(this.manager.room, scenePath);
42
49
  }
50
+ }
51
+ }));
52
+ }
53
+
54
+ public createViewSync = () => {
55
+ if (this.manager.boxManager && !this.viewSync) {
56
+ this.viewSync = new ViewSync({
57
+ uid: this.manager.uid,
58
+ view$: this.view$,
59
+ camera$: this.camera$,
60
+ size$: this.size$,
61
+ stageRect$: this.manager.boxManager?.stageRect$,
62
+ viewMode$: this.manager.windowManger.viewMode$,
63
+ storeCamera: this.storeCamera,
64
+ storeSize: this.storeSize,
43
65
  });
44
- });
45
- const rect = this.manager.boxManager?.stageRect;
46
- if (rect) {
47
- this.synchronizer.setRect(rect);
48
66
  }
49
- this.sideEffectManager.add(() => {
50
- return emitter.on("playgroundSizeChange", rect => {
51
- this.synchronizer.setRect(rect);
52
- this.synchronizer.onLocalSizeUpdate(rect);
53
- });
54
- });
55
- }
67
+ };
56
68
 
57
69
  private startListenWritableChange = () => {
58
- this.sideEffectManager.add(() => {
59
- return emitter.on("writableChange", isWritable => {
70
+ this.sideEffectManager.add(() =>
71
+ emitter.on("writableChange", isWritable => {
60
72
  if (isWritable) {
61
73
  this.ensureCameraAndSize();
62
74
  }
63
- });
64
- });
75
+ })
76
+ );
65
77
  };
66
78
 
67
79
  public ensureCameraAndSize() {
68
80
  if (!this.mainViewCamera || !this.mainViewSize) {
69
81
  this.manager.dispatchInternalEvent(Events.InitMainViewCamera);
70
- this.setCameraAndSize();
82
+ this.storeCamera({
83
+ id: this.manager.uid,
84
+ ...this.view.camera
85
+ });
86
+ const stageRect = this.manager.boxManager?.stageRect;
87
+ if (stageRect && !this.mainViewSize) {
88
+ this.storeSize({
89
+ id: this.manager.uid,
90
+ width: stageRect.width,
91
+ height: stageRect.height
92
+ });
93
+ }
71
94
  }
72
95
  }
73
96
 
@@ -83,55 +106,73 @@ export class MainViewProxy {
83
106
  return get(this.view, ["didRelease"]);
84
107
  }
85
108
 
86
- private moveCameraSizeByAttributes() {
87
- this.synchronizer.onRemoteUpdate(this.mainViewCamera, this.mainViewSize);
88
- }
89
-
90
109
  public start() {
91
110
  if (this.started) return;
92
- this.sizeChangeHandler(this.mainViewSize);
93
111
  this.addCameraListener();
94
112
  this.addCameraReaction();
95
113
  this.started = true;
96
114
  }
97
115
 
98
116
  public addCameraReaction = () => {
99
- this.manager.refresher?.add(Fields.MainViewCamera, this.cameraReaction);
117
+ this.manager.refresher.add(Fields.MainViewCamera, this.cameraReaction);
118
+ this.manager.refresher.add(Fields.MainViewSize, this.sizeReaction);
100
119
  };
101
120
 
102
- public setCameraAndSize(): void {
103
- const stageSize = this.getStageSize();
104
- if (stageSize) {
105
- const camera = { ...this.mainView.camera, id: this.manager.uid };
106
- const size = { ...stageSize, id: this.manager.uid };
107
- this.store.setMainViewCameraAndSize(camera, size);
121
+ public storeCurrentCamera = () => {
122
+ const iCamera = this.view.camera;
123
+ this.storeCamera({
124
+ id: this.manager.uid,
125
+ ...iCamera
126
+ });
127
+ }
128
+
129
+ public storeCurrentSize = () => {
130
+ const rect = this.manager.boxManager?.stageRect;
131
+ if (rect) {
132
+ this.storeSize({
133
+ id: this.manager.uid,
134
+ width: rect.width,
135
+ height: rect.height
136
+ });
108
137
  }
109
138
  }
110
139
 
140
+ public storeCamera = (camera: ICamera) => {
141
+ this.store.setMainViewCamera(camera);
142
+ };
143
+
144
+ public storeSize = (size: ISize) => {
145
+ this.store.setMainViewSize(size);
146
+ };
147
+
111
148
  private cameraReaction = () => {
112
149
  return reaction(
113
150
  () => this.mainViewCamera,
114
151
  camera => {
115
- if (camera && camera.id !== this.manager.uid) {
116
- this.synchronizer.onRemoteUpdate(camera, this.mainViewSize);
152
+ if (camera) {
153
+ const rawCamera = toJS(camera);
154
+ if (!isEqual(rawCamera, this.camera$.value)) {
155
+ this.camera$.setValue(rawCamera);
156
+ }
117
157
  }
118
158
  },
119
159
  { fireImmediately: true }
120
160
  );
121
161
  };
122
162
 
123
- public sizeChangeHandler = debounce((size: Size) => {
124
- if (size) {
125
- this.synchronizer.onLocalSizeUpdate(size);
126
- }
127
- }, 30);
128
-
129
- public onUpdateContainerSizeRatio = () => {
130
- const size = this.store.getMainViewSize();
131
- this.sizeChangeHandler(size);
132
- if (size.id === this.manager.uid) {
133
- this.setCameraAndSize();
134
- }
163
+ private sizeReaction = () => {
164
+ return reaction(
165
+ () => this.mainViewSize,
166
+ size => {
167
+ if (size) {
168
+ const rawSize = toJS(size);
169
+ if (!isEqual(rawSize, this.size$.value)) {
170
+ this.size$.setValue(rawSize);
171
+ }
172
+ }
173
+ },
174
+ { fireImmediately: true }
175
+ );
135
176
  };
136
177
 
137
178
  public get view(): View {
@@ -148,7 +189,7 @@ export class MainViewProxy {
148
189
  if (mainViewScenePath) {
149
190
  setViewFocusScenePath(mainView, mainViewScenePath);
150
191
  }
151
- this.synchronizer.setView(mainView);
192
+ this.view$.setValue(mainView);
152
193
  return mainView;
153
194
  }
154
195
 
@@ -170,33 +211,18 @@ export class MainViewProxy {
170
211
  public rebind(): void {
171
212
  const divElement = this.mainView.divElement;
172
213
  const disableCameraTransform = this.mainView.disableCameraTransform;
214
+ const camera = { ...this.mainView.camera };
173
215
  this.stop();
174
- if (!this.didRelease) {
175
- this.mainView.release();
176
- }
216
+ releaseView(this.mainView);
177
217
  this.removeMainViewListener();
178
218
  this.mainView = this.createMainView();
179
219
  this.mainView.disableCameraTransform = disableCameraTransform;
180
220
  this.mainView.divElement = divElement;
221
+ this.mainView.moveCamera({ ...camera, animationMode: AnimationMode.Immediately });
181
222
  this.addMainViewListener();
182
223
  this.start();
183
224
  }
184
225
 
185
- private onCameraUpdatedByDevice = (camera: Camera) => {
186
- this.synchronizer.onLocalCameraUpdate(camera);
187
- const size = this.getStageSize();
188
- if (size && !isEqual(size, this.mainViewSize)) {
189
- this.setMainViewSize(size);
190
- }
191
- };
192
-
193
- private getStageSize(): Size | undefined {
194
- const stage = this.manager.boxManager?.stageRect;
195
- if (stage) {
196
- return { width: stage.width, height: stage.height };
197
- }
198
- }
199
-
200
226
  public addMainViewListener(): void {
201
227
  if (this.mainViewIsAddListener) return;
202
228
  if (this.view.divElement) {
@@ -229,13 +255,11 @@ export class MainViewProxy {
229
255
  }, 50);
230
256
 
231
257
  private addCameraListener() {
232
- this.view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
233
258
  this.view.callbacks.on("onCameraUpdated", this.onCameraOrSizeUpdated);
234
259
  this.view.callbacks.on("onSizeUpdated", this.onCameraOrSizeUpdated);
235
260
  }
236
261
 
237
262
  private removeCameraListener() {
238
- this.view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
239
263
  this.view.callbacks.off("onCameraUpdated", this.onCameraOrSizeUpdated);
240
264
  this.view.callbacks.off("onSizeUpdated", this.onCameraOrSizeUpdated);
241
265
  }
@@ -246,12 +270,15 @@ export class MainViewProxy {
246
270
 
247
271
  public stop() {
248
272
  this.removeCameraListener();
249
- this.manager.refresher?.remove(Fields.MainViewCamera);
250
- this.manager.refresher?.remove(Fields.MainViewSize);
273
+ this.manager.refresher.remove(Fields.MainViewCamera);
274
+ this.manager.refresher.remove(Fields.MainViewSize);
251
275
  this.started = false;
252
276
  }
253
277
 
254
278
  public destroy() {
279
+ this.camera$.destroy();
280
+ this.size$.destroy();
281
+ this.view$.destroy();
255
282
  this.removeMainViewListener();
256
283
  this.stop();
257
284
  this.sideEffectManager.flushAll();
@@ -0,0 +1,110 @@
1
+ import { ViewMode, AnimationMode } from "white-web-sdk";
2
+ import { CameraSynchronizer } from "./CameraSynchronizer";
3
+ import { combine } from "value-enhancer";
4
+ import { isEqual } from "lodash";
5
+ import { SideEffectManager } from "side-effect-manager";
6
+ import type { Camera, View } from "white-web-sdk";
7
+ import type { Val, ReadonlyVal } from "value-enhancer";
8
+ import type { ICamera, ISize } from "../AttributesDelegate";
9
+ import type { TeleBoxRect } from "@netless/telebox-insider";
10
+
11
+ export type ViewSyncContext = {
12
+ uid: string;
13
+ // 远端 camera
14
+ camera$: Val<ICamera | undefined, boolean>;
15
+ // 远端 size
16
+ size$: Val<ISize | undefined>;
17
+
18
+ stageRect$: ReadonlyVal<TeleBoxRect>;
19
+
20
+ viewMode$?: Val<ViewMode>;
21
+
22
+ storeCamera: (camera: ICamera) => void;
23
+
24
+ storeSize: (size: ISize) => void;
25
+
26
+ view$: Val<View | undefined>;
27
+ };
28
+
29
+ export class ViewSync {
30
+ private sem = new SideEffectManager();
31
+ private synchronizer: CameraSynchronizer;
32
+
33
+ constructor(private context: ViewSyncContext) {
34
+ this.synchronizer = new CameraSynchronizer((camera: ICamera) => {
35
+ this.context.camera$.setValue(camera, true);
36
+ const notStoreCamera =
37
+ this.context.viewMode$ && this.context.viewMode$.value === ViewMode.Freedom;
38
+ if (notStoreCamera) {
39
+ return;
40
+ } else {
41
+ this.context.storeCamera(camera);
42
+ }
43
+ });
44
+ this.bindView(this.context.view$.value);
45
+ this.sem.add(() => [
46
+ this.context.view$.subscribe(view => {
47
+ const currentCamera = this.context.camera$.value;
48
+ if (currentCamera && this.context.size$.value) {
49
+ view?.moveCamera({
50
+ scale: 1,
51
+ animationMode: AnimationMode.Immediately,
52
+ });
53
+ this.synchronizer.onRemoteUpdate(currentCamera, this.context.size$.value);
54
+ }
55
+
56
+ this.bindView(view);
57
+ }),
58
+ this.context.camera$.subscribe((camera, skipUpdate) => {
59
+ const size = this.context.size$.value;
60
+ if (camera && size && !skipUpdate) {
61
+ this.synchronizer.onRemoteUpdate(camera, size);
62
+ }
63
+ }),
64
+ this.context.size$.subscribe(size => {
65
+ if (size) {
66
+ this.synchronizer.onRemoteSizeUpdate(size);
67
+ }
68
+ }),
69
+ this.context.stageRect$.subscribe(rect => {
70
+ if (rect) {
71
+ this.synchronizer.setRect(rect);
72
+ }
73
+ })
74
+ ]);
75
+ const camera$size$ = combine([this.context.camera$, this.context.size$]);
76
+ camera$size$.reaction(([camera, size]) => {
77
+ if (camera && size) {
78
+ this.synchronizer.onRemoteUpdate(camera, size);
79
+ camera$size$.destroy();
80
+ }
81
+ });
82
+ }
83
+
84
+ public bindView = (view?: View) => {
85
+ if (!view) return;
86
+ this.synchronizer.setView(view);
87
+ this.sem.flush("view");
88
+ this.sem.add(() => {
89
+ view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
90
+ return () =>
91
+ view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
92
+ }, "view");
93
+ };
94
+
95
+ private onCameraUpdatedByDevice = (camera: Camera) => {
96
+ 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
+ }
104
+ }
105
+ };
106
+
107
+ public destroy() {
108
+ this.sem.flushAll();
109
+ }
110
+ }
package/src/callback.ts CHANGED
@@ -20,6 +20,7 @@ export type PublicEvent = {
20
20
  ready: undefined; // 所有 APP 创建完毕时触发
21
21
  sceneStateChange: SceneState;
22
22
  pageStateChange: PageState;
23
+ appClose: { appId: string; kind: string, error?: Error };
23
24
  };
24
25
 
25
26
  export type CallbacksType = Emittery<PublicEvent>;
package/src/constants.ts CHANGED
@@ -5,18 +5,16 @@ export enum Events {
5
5
  AppBoxStateChange = "AppBoxStateChange",
6
6
  GetAttributes = "GetAttributes",
7
7
  UpdateWindowManagerWrapper = "UpdateWindowManagerWrapper",
8
- InitReplay = "InitReplay",
9
8
  WindowCreated = "WindowCreated",
10
9
  SetMainViewScenePath = "SetMainViewScenePath",
11
10
  SetMainViewSceneIndex = "SetMainViewSceneIndex",
12
11
  SetAppFocusIndex = "SetAppFocusIndex",
13
12
  SwitchViewsToFreedom = "SwitchViewsToFreedom",
14
- MoveCamera = "MoveCamera",
15
- MoveCameraToContain = "MoveCameraToContain",
16
13
  CursorMove = "CursorMove",
17
14
  RootDirRemoved = "RootDirRemoved",
18
15
  Refresh = "Refresh",
19
16
  InitMainViewCamera = "InitMainViewCamera",
17
+ InvokeAttributesUpdateCallback = "InvokeAttributesUpdateCallback",
20
18
  }
21
19
 
22
20
  export const MagixEventName = "__WindowManger";
@@ -27,6 +25,11 @@ export enum AppAttributes {
27
25
  Position = "position",
28
26
  SceneIndex = "SceneIndex",
29
27
  ZIndex = "zIndex",
28
+ Visible = "visible",
29
+ Ratio = "ratio",
30
+ StageRatio = "stageRatio",
31
+ Draggable = "draggable",
32
+ Resizable = "resizable",
30
33
  }
31
34
 
32
35
  export enum AppEvents {