@netless/window-manager 1.0.0-canary.5 → 1.0.0-canary.50

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 (95) hide show
  1. package/README.md +30 -6
  2. package/dist/index.cjs.js +12477 -34
  3. package/dist/index.es.js +6839 -10495
  4. package/dist/index.umd.js +12485 -46
  5. package/dist/{App → src/App}/AppContext.d.ts +12 -7
  6. package/dist/{App → src/App}/AppPageStateImpl.d.ts +0 -0
  7. package/dist/{App → src/App}/AppProxy.d.ts +29 -11
  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 +1 -0
  15. package/dist/src/App/type.d.ts +21 -0
  16. package/dist/{AppListener.d.ts → src/AppListener.d.ts} +2 -2
  17. package/dist/{AppManager.d.ts → src/AppManager.d.ts} +6 -5
  18. package/dist/{AttributesDelegate.d.ts → src/AttributesDelegate.d.ts} +5 -2
  19. package/dist/{BoxEmitter.d.ts → src/BoxEmitter.d.ts} +0 -0
  20. package/dist/{BoxManager.d.ts → src/BoxManager.d.ts} +12 -6
  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 +2 -1
  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} +0 -0
  31. package/dist/{RedoUndo.d.ts → src/RedoUndo.d.ts} +0 -0
  32. package/dist/{Register → src/Register}/index.d.ts +4 -2
  33. package/dist/{Register → src/Register}/loader.d.ts +1 -1
  34. package/dist/src/Register/storage.d.ts +11 -0
  35. package/dist/{Utils → src/Utils}/AppCreateQueue.d.ts +0 -0
  36. package/dist/{Utils → src/Utils}/Common.d.ts +0 -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 +1 -1
  40. package/dist/{Utils → src/Utils}/log.d.ts +0 -0
  41. package/dist/src/View/CameraSynchronizer.d.ts +19 -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 +29 -0
  45. package/dist/{callback.d.ts → src/callback.d.ts} +10 -1
  46. package/dist/{constants.d.ts → src/constants.d.ts} +10 -5
  47. package/dist/src/image.d.ts +19 -0
  48. package/dist/{index.d.ts → src/index.d.ts} +49 -14
  49. package/dist/src/shim.d.ts +11 -0
  50. package/dist/{typings.d.ts → src/typings.d.ts} +12 -3
  51. package/dist/style.css +795 -1
  52. package/docs/app-context.md +155 -27
  53. package/docs/mirgrate-to-1.0.md +68 -0
  54. package/package.json +23 -19
  55. package/playwright.config.ts +29 -0
  56. package/pnpm-lock.yaml +3078 -4412
  57. package/src/App/AppContext.ts +62 -29
  58. package/src/App/AppProxy.ts +235 -113
  59. package/src/App/WhiteboardView.ts +34 -12
  60. package/src/App/index.ts +1 -0
  61. package/src/App/type.ts +22 -0
  62. package/src/AppListener.ts +30 -21
  63. package/src/AppManager.ts +66 -43
  64. package/src/AttributesDelegate.ts +6 -3
  65. package/src/BoxManager.ts +76 -38
  66. package/src/BuiltinApps.ts +9 -8
  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 +2 -1
  72. package/src/PageState.ts +1 -1
  73. package/src/ReconnectRefresher.ts +6 -2
  74. package/src/Register/index.ts +36 -14
  75. package/src/Register/loader.ts +20 -9
  76. package/src/Register/storage.ts +26 -5
  77. package/src/Utils/Common.ts +3 -0
  78. package/src/Utils/Reactive.ts +27 -26
  79. package/src/Utils/RoomHacker.ts +3 -0
  80. package/src/Utils/error.ts +2 -2
  81. package/src/View/CameraSynchronizer.ts +41 -38
  82. package/src/View/MainView.ts +116 -75
  83. package/src/View/ViewSync.ts +123 -6
  84. package/src/callback.ts +6 -1
  85. package/src/constants.ts +8 -3
  86. package/src/index.ts +201 -63
  87. package/src/style.css +3 -46
  88. package/src/typings.ts +14 -3
  89. package/vite.config.js +12 -7
  90. package/dist/App/AppViewSync.d.ts +0 -11
  91. package/dist/App/WhiteboardView.d.ts +0 -21
  92. package/dist/Register/storage.d.ts +0 -8
  93. package/dist/View/CameraSynchronizer.d.ts +0 -17
  94. package/dist/View/ViewSync.d.ts +0 -7
  95. package/src/App/AppViewSync.ts +0 -69
@@ -1,19 +1,20 @@
1
1
  import { callbacks } from "../callback";
2
2
  import { getItem, setItem } from "./storage";
3
3
  import type { NetlessApp } from "../typings";
4
+ import { appRegister } from ".";
4
5
 
5
6
  const Prefix = "NetlessApp";
6
7
 
7
8
  const TIMEOUT = 10000; // 下载 script 10 秒超时
8
9
 
9
- export const getScript = async (url: string): Promise<string> => {
10
- const item = await getItem(url);
10
+ export const getScript = async (kind: string, url: string): Promise<string> => {
11
+ const item = await getItem(kind);
11
12
  if (item) {
12
13
  return item.sourceCode;
13
14
  } else {
14
15
  const result = await fetchWithTimeout(url, { timeout: TIMEOUT });
15
16
  const text = await result.text();
16
- await setItem(url, text);
17
+ await setItem(kind, url, text);
17
18
  return text;
18
19
  }
19
20
  };
@@ -28,6 +29,15 @@ export const executeScript = (text: string, appName: string): NetlessApp => {
28
29
  return result;
29
30
  };
30
31
 
32
+ const emitSuccess = (kind: string, url: string) => {
33
+ callbacks.emit("loadApp", { kind, status: "success" });
34
+ appRegister.downloaded.set(kind, url);
35
+ };
36
+
37
+ const emitFailed = (kind: string, reason: string) => {
38
+ callbacks.emit("loadApp", { kind, status: "failed", reason, });
39
+ };
40
+
31
41
  export const loadApp = async (
32
42
  url: string,
33
43
  key: string,
@@ -36,14 +46,14 @@ export const loadApp = async (
36
46
  const appName = name || Prefix + key;
37
47
  callbacks.emit("loadApp", { kind: key, status: "start" });
38
48
  try {
39
- const text = await getScript(url);
49
+ const text = await getScript(key, url);
40
50
  if (!text || text.length === 0) {
41
- callbacks.emit("loadApp", { kind: key, status: "failed", reason: "script is empty." });
51
+ emitFailed(key, "script is empty");
42
52
  return;
43
53
  }
44
54
  try {
45
55
  const result = executeScript(text, appName);
46
- callbacks.emit("loadApp", { kind: key, status: "success" });
56
+ emitSuccess(key, url);
47
57
  return result;
48
58
  } catch (error: any) {
49
59
  if (error.message.includes("Can only have one anonymous define call per script file")) {
@@ -54,16 +64,17 @@ export const loadApp = async (
54
64
  delete define.amd;
55
65
  }
56
66
  const result = executeScript(text, appName);
57
- callbacks.emit("loadApp", { kind: key, status: "success" });
67
+ emitSuccess(key, url);
58
68
  return result;
59
69
  }
60
- callbacks.emit("loadApp", { kind: key, status: "failed", reason: error.message });
70
+ emitFailed(key, error.message);
61
71
  }
62
72
  } catch (error: any) {
63
- callbacks.emit("loadApp", { kind: key, status: "failed", reason: error.message });
73
+ emitFailed(key, error.message);
64
74
  }
65
75
  };
66
76
 
77
+
67
78
  async function fetchWithTimeout(resource: string, options: RequestInit & { timeout: number }) {
68
79
  const { timeout = 10000 } = options;
69
80
 
@@ -1,3 +1,5 @@
1
+ import type { AppRegister } from "./index";
2
+
1
3
  const DatabaseName = "__WindowManagerAppCache";
2
4
 
3
5
  let db: IDBDatabase;
@@ -5,21 +7,26 @@ let store: IDBObjectStore;
5
7
 
6
8
  export type Item = {
7
9
  kind: string;
10
+ url: string;
8
11
  sourceCode: string;
9
12
  }
10
13
 
11
- export const initDb = async () => {
14
+ export const initDb = async (appRegister: AppRegister) => {
12
15
  db = await createDb();
16
+ const items = await queryAll(db);
17
+ items.forEach(item => {
18
+ appRegister.downloaded.set(item.kind, item.url);
19
+ });
13
20
  }
14
21
 
15
- export const setItem = (key: string, val: any) => {
22
+ export const setItem = (kind: string, url: string, val: any) => {
16
23
  if (!db) return;
17
- return addRecord(db, { kind: key, sourceCode: val })
24
+ return addRecord(db, { kind, url, sourceCode: val })
18
25
  };
19
26
 
20
- export const getItem = async (key: string): Promise<Item | null> => {
27
+ export const getItem = async (kind: string): Promise<Item | null> => {
21
28
  if (!db) return null;
22
- return await query(db, key);
29
+ return await query(db, kind);
23
30
  };
24
31
 
25
32
  export const removeItem = (key: string) => {
@@ -27,6 +34,11 @@ export const removeItem = (key: string) => {
27
34
  return deleteRecord(db, key);
28
35
  };
29
36
 
37
+ export const getAll = () => {
38
+ if (!db) return;
39
+ return queryAll(db);
40
+ }
41
+
30
42
  function createDb(): Promise<IDBDatabase> {
31
43
  return new Promise((resolve, reject) => {
32
44
  const request = indexedDB.open(DatabaseName, 2);
@@ -64,6 +76,15 @@ function query<T>(db: IDBDatabase, val: string): Promise<T | null> {
64
76
  })
65
77
  }
66
78
 
79
+ function queryAll(db: IDBDatabase): Promise<Item[]> {
80
+ return new Promise((resolve, reject) => {
81
+ const index = db.transaction(["apps"]).objectStore("apps").index("kind");
82
+ const request = index.getAll();
83
+ request.onerror = e => reject(e);
84
+ request.onsuccess = () => resolve(request.result);
85
+ });
86
+ }
87
+
67
88
  function addRecord(db: IDBDatabase, payload: any): Promise<void> {
68
89
  return new Promise((resolve, reject) => {
69
90
  const request = db.transaction(["apps"], "readwrite").objectStore("apps").add(payload);
@@ -17,6 +17,9 @@ export const genAppId = async (kind: string) => {
17
17
  };
18
18
 
19
19
  export const setViewFocusScenePath = (view: View, focusScenePath: string) => {
20
+ if ((view as any).didRelease) {
21
+ return;
22
+ }
20
23
  if (view.focusScenePath !== focusScenePath) {
21
24
  view.focusScenePath = focusScenePath;
22
25
  return view;
@@ -1,6 +1,6 @@
1
- import { listenUpdated, unlistenUpdated, reaction, UpdateEventKind } from "white-web-sdk";
2
- import type { AkkoObjectUpdatedProperty , AkkoObjectUpdatedListener } from "white-web-sdk";
3
1
  import { isObject } from "lodash";
2
+ import { listenUpdated, reaction, unlistenUpdated, UpdateEventKind } from "white-web-sdk";
3
+ import type { AkkoObjectUpdatedProperty, AkkoObjectUpdatedListener } from "white-web-sdk";
4
4
 
5
5
  // 兼容 13 和 14 版本 SDK
6
6
  export const onObjectByEvent = (event: UpdateEventKind) => {
@@ -12,7 +12,7 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
12
12
  if (kinds.includes(event)) {
13
13
  func();
14
14
  }
15
- }
15
+ };
16
16
  listenUpdated(object, listener);
17
17
  func();
18
18
  return () => unlistenUpdated(object, listener);
@@ -21,43 +21,44 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
21
21
  () => object,
22
22
  () => {
23
23
  func();
24
- }, {
24
+ },
25
+ {
25
26
  fireImmediately: true,
26
27
  }
27
- )
28
+ );
28
29
  }
29
- }
30
- }
30
+ };
31
+ };
31
32
 
32
33
  export const safeListenPropsUpdated = <T>(
33
34
  getProps: () => T,
34
35
  callback: AkkoObjectUpdatedListener<T>,
35
36
  onDestroyed?: (props: unknown) => void
36
- ) => {
37
+ ) => {
37
38
  let disposeListenUpdated: (() => void) | null = null;
38
39
  const disposeReaction = reaction(
39
- getProps,
40
- () => {
41
- if (disposeListenUpdated) {
42
- disposeListenUpdated();
43
- disposeListenUpdated = null;
44
- }
45
- const props = getProps();
46
- if (isObject(props)) {
47
- disposeListenUpdated = () => unlistenUpdated(props, callback);
48
- listenUpdated(props, callback);
49
- } else {
50
- onDestroyed?.(props);
51
- }
52
- },
53
- { fireImmediately: true }
40
+ getProps,
41
+ () => {
42
+ if (disposeListenUpdated) {
43
+ disposeListenUpdated();
44
+ disposeListenUpdated = null;
45
+ }
46
+ const props = getProps();
47
+ if (isObject(props)) {
48
+ disposeListenUpdated = () => unlistenUpdated(props, callback);
49
+ listenUpdated(props, callback);
50
+ } else {
51
+ onDestroyed?.(props);
52
+ }
53
+ },
54
+ { fireImmediately: true }
54
55
  );
55
56
 
56
57
  return () => {
57
- disposeListenUpdated?.();
58
- disposeReaction();
58
+ disposeListenUpdated?.();
59
+ disposeReaction();
59
60
  };
60
- }
61
+ };
61
62
 
62
63
  export const onObjectRemoved = onObjectByEvent(UpdateEventKind.Removed);
63
64
  export const onObjectInserted = onObjectByEvent(UpdateEventKind.Inserted);
@@ -56,6 +56,9 @@ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager)
56
56
  room.lockImages = (...args) => manager.lockImages(...args);
57
57
 
58
58
  delegateRemoveScenes(room, manager);
59
+ if (!(room as any).dynamicPpt.slideStateAdapter.pptHandler) {
60
+ (room as any).dynamicPpt.slideStateAdapter.pptHandler = manager.createPPTHandler();
61
+ }
59
62
  }
60
63
  };
61
64
 
@@ -31,8 +31,8 @@ export class InvalidScenePath extends Error {
31
31
  override message = `[WindowManager]: ScenePath should start with "/"`;
32
32
  }
33
33
 
34
- export class BoxManagerNotFoundError extends Error {
35
- override message = "[WindowManager]: boxManager not found";
34
+ export class BoxManagerNotInitializeError extends Error {
35
+ override message = "[WindowManager]: boxManager need initialize";
36
36
  }
37
37
 
38
38
  export class BindContainerRoomPhaseInvalidError extends Error {
@@ -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
4
  import type { Camera, View, Size } from "white-web-sdk";
5
- import type { ISize } from "../AttributesDelegate";
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?: ISize;
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,48 +26,51 @@ export class CameraSynchronizer {
26
26
  }
27
27
 
28
28
  // 远端 Camera 或者 size 更新
29
- public onRemoteUpdate = throttle((camera: Camera, size: ISize) => {
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 nextScale = camera.scale * computedMinScale(size, this.rect);
34
+ const config: Partial<Camera> = {
43
35
  scale: nextScale,
44
- animationMode: AnimationMode.Immediately,
45
- });
46
- // TODO 直接调用 moveCamera 依然会出现 camera 错误的情况,这里暂时加一个 delay 保证 camera 是对的, 后续需要 SDK 进行修改
47
- delay(moveCamera, 50);
36
+ }
37
+ if (camera.centerX !== null) {
38
+ config.centerX = camera.centerX;
39
+ }
40
+ if (camera.centerY !== null) {
41
+ config.centerY = camera.centerY;
42
+ }
43
+ this.moveCamera(config);
48
44
  }
49
- }, 50);
45
+ }, 10);
50
46
 
47
+ public onRemoteSizeUpdate(size: ISize) {
48
+ this.remoteSize = size;
49
+ const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size, ["width", "height"]));
50
+ if (this.rect && this.remoteCamera && needMoveCamera) {
51
+ if (!this.view) return;
52
+ const currentCamera = this.view.camera;
53
+ this.view?.moveCameraToContain({
54
+ width: size.width,
55
+ height: size.height,
56
+ originX: currentCamera.centerX - (size.width / 2),
57
+ originY: currentCamera.centerY - (size.height / 2),
58
+ });
59
+ }
60
+ }
51
61
 
52
- public onLocalCameraUpdate(camera: Camera) {
62
+ public onLocalCameraUpdate(camera: ICamera) {
53
63
  this.saveCamera(camera);
54
64
  this.remoteCamera = camera;
55
65
  }
56
66
 
57
- // 本地 Size 更新, 先匹配 camera 到新的 size 然后再发送 camera 数据到远端
58
- public onLocalSizeUpdate = (size: Size) => {
59
- if (this.rect && this.view) {
60
- let scale: number;
61
- if (size.width < size.height) {
62
- scale = this.rect.width / size.width;
63
- } else {
64
- scale = this.rect.height / size.height;
65
- }
66
- const nextScale = this.view.camera.scale / scale;
67
- this.view.moveCamera({
68
- scale: nextScale,
69
- animationMode: AnimationMode.Immediately
70
- });
71
- }
67
+ private moveCamera(camera: Partial<Camera>) {
68
+ this.view?.moveCamera({ ...camera, animationMode: AnimationMode.Immediately });
72
69
  }
73
70
  }
71
+
72
+ export const computedMinScale = (remoteSize: Size, currentSize: Size) => {
73
+ const wScale = currentSize.width / remoteSize.width;
74
+ const hScale = currentSize.height / remoteSize.height;
75
+ return Math.min(wScale, hScale);
76
+ }