@netless/window-manager 0.4.0-canary.6 → 0.4.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.
Files changed (81) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.idea/window-manager.iml +12 -0
  5. package/.vscode/settings.json +1 -0
  6. package/CHANGELOG.md +39 -2
  7. package/README.md +3 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/index.d.ts +19 -6
  10. package/dist/App/Storage/typings.d.ts +4 -0
  11. package/dist/AppContext.d.ts +39 -17
  12. package/dist/AppListener.d.ts +2 -1
  13. package/dist/AppManager.d.ts +25 -11
  14. package/dist/AppProxy.d.ts +7 -8
  15. package/dist/AttributesDelegate.d.ts +2 -2
  16. package/dist/BoxManager.d.ts +6 -3
  17. package/dist/BuiltinApps.d.ts +5 -0
  18. package/dist/ContainerResizeObserver.d.ts +10 -0
  19. package/dist/Cursor/Cursor.d.ts +10 -12
  20. package/dist/Cursor/index.d.ts +6 -16
  21. package/dist/Helper.d.ts +7 -0
  22. package/dist/Register/storage.d.ts +5 -1
  23. package/dist/Utils/AppCreateQueue.d.ts +11 -0
  24. package/dist/Utils/Common.d.ts +7 -2
  25. package/dist/Utils/RoomHacker.d.ts +3 -3
  26. package/dist/{MainView.d.ts → View/MainView.d.ts} +5 -6
  27. package/dist/View/ViewManager.d.ts +13 -0
  28. package/dist/constants.d.ts +5 -7
  29. package/dist/index.d.ts +35 -16
  30. package/dist/index.es.js +41 -1
  31. package/dist/index.es.js.map +1 -1
  32. package/dist/index.umd.js +41 -1
  33. package/dist/index.umd.js.map +1 -1
  34. package/dist/style.css +1 -1
  35. package/dist/typings.d.ts +3 -2
  36. package/docs/advanced.md +39 -0
  37. package/docs/api.md +69 -6
  38. package/docs/concept.md +9 -0
  39. package/docs/replay.md +40 -0
  40. package/package.json +7 -6
  41. package/src/App/MagixEvent/index.ts +68 -0
  42. package/src/App/Storage/index.ts +90 -44
  43. package/src/App/Storage/typings.ts +8 -0
  44. package/src/AppContext.ts +61 -25
  45. package/src/AppListener.ts +28 -16
  46. package/src/AppManager.ts +260 -81
  47. package/src/AppProxy.ts +53 -64
  48. package/src/AttributesDelegate.ts +2 -2
  49. package/src/BoxManager.ts +40 -24
  50. package/src/BuiltinApps.ts +23 -0
  51. package/src/ContainerResizeObserver.ts +62 -0
  52. package/src/Cursor/Cursor.svelte +25 -21
  53. package/src/Cursor/Cursor.ts +25 -38
  54. package/src/Cursor/icons.ts +2 -0
  55. package/src/Cursor/index.ts +45 -139
  56. package/src/Helper.ts +41 -0
  57. package/src/Register/index.ts +25 -16
  58. package/src/Register/loader.ts +2 -2
  59. package/src/Register/storage.ts +6 -1
  60. package/src/Utils/AppCreateQueue.ts +54 -0
  61. package/src/Utils/Common.ts +69 -14
  62. package/src/Utils/RoomHacker.ts +44 -14
  63. package/src/{MainView.ts → View/MainView.ts} +25 -36
  64. package/src/View/ViewManager.ts +52 -0
  65. package/src/constants.ts +6 -4
  66. package/src/image/laser-pointer-cursor.svg +17 -0
  67. package/src/index.ts +164 -99
  68. package/src/shim.d.ts +5 -0
  69. package/src/style.css +12 -1
  70. package/src/typings.ts +3 -2
  71. package/vite.config.js +8 -2
  72. package/dist/Base/Context.d.ts +0 -13
  73. package/dist/Base/index.d.ts +0 -7
  74. package/dist/Utils/CameraStore.d.ts +0 -15
  75. package/dist/ViewManager.d.ts +0 -29
  76. package/dist/sdk.d.ts +0 -14
  77. package/src/Base/Context.ts +0 -49
  78. package/src/Base/index.ts +0 -10
  79. package/src/Utils/CameraStore.ts +0 -72
  80. package/src/sdk.ts +0 -39
  81. package/src/viewManager.ts +0 -177
@@ -1,42 +1,34 @@
1
- import App from './Cursor.svelte';
2
- import { ApplianceMap } from './icons';
3
- import { ApplianceNames } from 'white-web-sdk';
4
- import { CursorState } from '../constants';
5
- import { Fields } from '../AttributesDelegate';
6
- import { get, omit } from 'lodash';
7
- import type { Position } from '../AttributesDelegate';
1
+ import App from "./Cursor.svelte";
2
+ import { ApplianceMap } from "./icons";
3
+ import { ApplianceNames } from "white-web-sdk";
4
+ import { omit } from "lodash";
5
+ import type { Position } from "../AttributesDelegate";
8
6
  import type { RoomMember } from "white-web-sdk";
9
7
  import type { CursorManager } from "./index";
10
8
  import type { SvelteComponent } from "svelte";
11
- import { Base } from '../Base';
12
- import type { AppManager } from '../AppManager';
9
+ import type { AppManager } from "../AppManager";
13
10
 
14
11
  export type Payload = {
15
- [key: string]: any
16
- }
17
-
12
+ [key: string]: any;
13
+ };
18
14
 
19
- export class Cursor extends Base {
15
+ export class Cursor {
20
16
  private member?: RoomMember;
21
17
  private timer?: number;
22
18
  private component?: SvelteComponent;
23
19
 
24
20
  constructor(
25
- manager: AppManager,
26
- addCursorChangeListener: (uid: string, callback: (position: Position, state: CursorState) => void) => void,
27
- private cursors: any,
21
+ private manager: AppManager,
28
22
  private memberId: string,
29
23
  private cursorManager: CursorManager,
30
- private wrapper?: HTMLElement,
24
+ private wrapper?: HTMLElement
31
25
  ) {
32
- super(manager);
33
- this.setMember();
26
+ this.updateMember();
34
27
  this.createCursor();
35
- addCursorChangeListener(this.memberId, this.onCursorChange);
36
28
  this.autoHidden();
37
29
  }
38
30
 
39
- private onCursorChange = (position: Position, state: CursorState) => {
31
+ public move = (position: Position) => {
40
32
  if (position.type === "main") {
41
33
  const rect = this.cursorManager.wrapperRect;
42
34
  if (this.component && rect) {
@@ -45,7 +37,6 @@ export class Cursor extends Base {
45
37
  }
46
38
  } else {
47
39
  const focusView = this.cursorManager.focusView;
48
- // TODO 可以存一个当前 focusView 的 Rect 这样只有 focus 切换的时候才调用 getBoundingClientRect
49
40
  const viewRect = focusView?.divElement?.getBoundingClientRect();
50
41
  const viewCamera = focusView?.camera;
51
42
  if (focusView && viewRect && viewCamera && this.component) {
@@ -53,10 +44,11 @@ export class Cursor extends Base {
53
44
  this.moveCursor(position, viewRect, focusView);
54
45
  }
55
46
  }
56
- if (state && state === CursorState.Leave) {
57
- this.hide();
58
- }
59
- }
47
+ };
48
+
49
+ public leave = () => {
50
+ this.hide();
51
+ };
60
52
 
61
53
  private moveCursor(cursor: Position, rect: DOMRect, view: any) {
62
54
  const { x, y, type } = cursor;
@@ -124,21 +116,12 @@ export class Cursor extends Base {
124
116
  }
125
117
  }
126
118
 
127
- public get cursorState(): CursorState | undefined {
128
- return get(this.cursors, [this.memberId, Fields.CursorState]);
129
- }
130
-
131
- public get cursorPosition(): Position | undefined {
132
- return get(this.cursors, [this.memberId, Fields.Position]);
133
- }
134
-
135
119
  private autoHidden() {
136
120
  if (this.timer) {
137
121
  clearTimeout(this.timer);
138
122
  }
139
123
  this.timer = window.setTimeout(() => {
140
124
  this.hide();
141
- this.store.updateCursorState(this.memberId, CursorState.Leave);
142
125
  }, 1000 * 10); // 10 秒钟自动隐藏
143
126
  }
144
127
 
@@ -175,9 +158,10 @@ export class Cursor extends Base {
175
158
  }
176
159
  }
177
160
 
178
- public setMember() {
179
- this.member = this.context.findMemberByUid(this.memberId);
161
+ public updateMember() {
162
+ this.member = this.manager.findMemberByUid(this.memberId);
180
163
  this.updateComponent();
164
+ return this.member;
181
165
  }
182
166
 
183
167
  private updateComponent() {
@@ -188,13 +172,16 @@ export class Cursor extends Base {
188
172
  if (this.component) {
189
173
  this.component.$destroy();
190
174
  }
191
- this.manager.refresher?.remove(this.memberId);
192
175
  this.cursorManager.cursorInstances.delete(this.memberId);
176
+ if (this.timer) {
177
+ clearTimeout(this.timer);
178
+ }
193
179
  }
194
180
 
195
181
  public hide() {
196
182
  if (this.component) {
197
183
  this.component.$set({ visible: false });
184
+ this.destroy();
198
185
  }
199
186
  }
200
187
  }
@@ -4,6 +4,7 @@ import selector from "../image/selector-cursor.png";
4
4
  import eraser from "../image/eraser-cursor.png";
5
5
  import shape from "../image/shape-cursor.svg";
6
6
  import text from "../image/text-cursor.svg";
7
+ import laser from "../image/laser-pointer-cursor.svg";
7
8
 
8
9
  export const ApplianceMap: {
9
10
  [key: string]: string;
@@ -13,4 +14,5 @@ export const ApplianceMap: {
13
14
  [ApplianceNames.eraser]: eraser,
14
15
  [ApplianceNames.shape]: shape,
15
16
  [ApplianceNames.text]: text,
17
+ [ApplianceNames.laserPointer]: laser,
16
18
  };
@@ -1,13 +1,11 @@
1
- import { autorun } from "white-web-sdk";
2
- import { Base } from "../Base";
3
- import { compact, debounce, get, uniq } from "lodash";
1
+ import { ApplianceNames } from "white-web-sdk";
4
2
  import { Cursor } from "./Cursor";
5
- import { CursorState } from "../constants";
3
+ import { CursorState, Events } from "../constants";
6
4
  import { emitter, WindowManager } from "../index";
7
- import { Fields } from "../AttributesDelegate";
8
- import { onObjectInserted } from "../Utils/Reactive";
9
5
  import { SideEffectManager } from "side-effect-manager";
10
- import type { PositionType, Position } from "../AttributesDelegate";
6
+ import { throttle } from "lodash";
7
+ import type { CursorMovePayload } from "../index";
8
+ import type { PositionType } from "../AttributesDelegate";
11
9
  import type { Point, RoomMember, View } from "white-web-sdk";
12
10
  import type { AppManager } from "../AppManager";
13
11
 
@@ -21,30 +19,47 @@ export type MoveCursorParams = {
21
19
  x: number;
22
20
  y: number;
23
21
  };
24
- export class CursorManager extends Base {
22
+ export class CursorManager {
25
23
  public containerRect?: DOMRect;
26
24
  public wrapperRect?: DOMRect;
27
25
  public cursorInstances: Map<string, Cursor> = new Map();
28
26
  public roomMembers?: readonly RoomMember[];
29
27
  private mainViewElement?: HTMLDivElement;
30
28
  private sideEffectManager = new SideEffectManager();
29
+ private store = this.manager.store;
31
30
 
32
- constructor(private appManager: AppManager) {
33
- super(appManager);
34
- this.roomMembers = this.appManager.room?.state.roomMembers;
31
+ constructor(private manager: AppManager, private enableCursor: boolean) {
32
+ this.roomMembers = this.manager.room?.state.roomMembers;
35
33
  const wrapper = WindowManager.wrapper;
36
34
  if (wrapper) {
37
35
  this.setupWrapper(wrapper);
38
36
  }
39
- emitter.on("onReconnected", () => {
40
- this.onReconnect();
37
+ emitter.on("cursorMove", payload => {
38
+ let cursorInstance = this.cursorInstances.get(payload.uid);
39
+ if (!cursorInstance) {
40
+ cursorInstance = new Cursor(this.manager, payload.uid, this, WindowManager.wrapper);
41
+ this.cursorInstances.set(payload.uid, cursorInstance);
42
+ }
43
+ if (payload.state === CursorState.Leave) {
44
+ cursorInstance.leave();
45
+ } else {
46
+ const member = cursorInstance.updateMember();
47
+ const isLaserPointer =
48
+ member?.memberState.currentApplianceName === ApplianceNames.laserPointer;
49
+ if (this.enableCursor || isLaserPointer) {
50
+ cursorInstance.move(payload.position);
51
+ }
52
+ }
53
+ });
54
+ this.sideEffectManager.add(() => {
55
+ const unsubscribe = emitter.on("playgroundSizeChange", () => {
56
+ this.updateContainerRect();
57
+ });
58
+ return unsubscribe;
41
59
  });
42
60
  }
43
61
 
44
62
  public setupWrapper(wrapper: HTMLElement) {
45
- if (this.manager.refresher?.hasReactor("cursors")) {
46
- this.destroy();
47
- }
48
63
  this.sideEffectManager.add(() => {
49
64
  wrapper.addEventListener("pointerenter", this.mouseMoveListener);
50
65
  wrapper.addEventListener("pointermove", this.mouseMoveListener);
@@ -56,77 +71,38 @@ export class CursorManager extends Base {
56
71
  };
57
72
  });
58
73
 
59
- this.initCursorAttributes();
60
74
  this.wrapperRect = wrapper.getBoundingClientRect();
61
- this.startReaction(wrapper);
62
75
  }
63
76
 
64
77
  public setMainViewDivElement(div: HTMLDivElement) {
65
78
  this.mainViewElement = div;
66
79
  }
67
80
 
68
- private startReaction(wrapper: HTMLElement) {
69
- this.manager.refresher?.add("cursors", () => {
70
- return onObjectInserted(this.cursors, () => {
71
- this.handleRoomMembersChange(wrapper);
72
- });
73
- });
74
- }
75
-
76
- private getUids = (members: readonly RoomMember[] | undefined) => {
77
- return compact(uniq(members?.map(member => member.payload?.uid)));
78
- };
79
-
80
- private handleRoomMembersChange = debounce((wrapper: HTMLElement) => {
81
- const uids = this.getUids(this.roomMembers);
82
- const cursors = Object.keys(this.cursors);
83
- if (uids?.length) {
84
- cursors.map(uid => {
85
- if (uids.includes(uid) && !this.cursorInstances.has(uid)) {
86
- if (uid === this.context.uid) {
87
- return;
88
- }
89
- const component = new Cursor(
90
- this.appManager,
91
- this.addCursorChangeListener,
92
- this.cursors,
93
- uid,
94
- this,
95
- wrapper
96
- );
97
- this.cursorInstances.set(uid, component);
98
- }
99
- });
100
- }
101
- }, 100);
102
-
103
- public get cursors() {
104
- return this.manager.attributes?.[Fields.Cursors];
105
- }
106
-
107
81
  public get boxState() {
108
82
  return this.store.getBoxState();
109
83
  }
110
84
 
111
85
  public get focusView() {
112
- return this.appManager.focusApp?.view;
86
+ return this.manager.focusApp?.view;
113
87
  }
114
88
 
115
- private mouseMoveListener = debounce((event: MouseEvent) => {
89
+ private mouseMoveListener = throttle((event: MouseEvent) => {
116
90
  this.updateCursor(this.getType(event), event.clientX, event.clientY);
117
- }, 5);
91
+ }, 16);
118
92
 
119
93
  private updateCursor(event: EventType, clientX: number, clientY: number) {
120
94
  if (this.wrapperRect && this.manager.canOperate) {
121
- const view = event.type === "main" ? this.appManager.mainView : this.focusView;
95
+ const view = event.type === "main" ? this.manager.mainView : this.focusView;
122
96
  const point = this.getPoint(view, clientX, clientY);
123
97
  if (point) {
124
- this.setNormalCursorState();
125
- this.store.updateCursor(this.context.uid, {
126
- x: point.x,
127
- y: point.y,
128
- ...event,
129
- });
98
+ this.manager.dispatchInternalEvent(Events.CursorMove, {
99
+ uid: this.manager.uid,
100
+ position: {
101
+ x: point.x,
102
+ y: point.y,
103
+ type: event.type,
104
+ },
105
+ } as CursorMovePayload);
130
106
  }
131
107
  }
132
108
  }
@@ -151,7 +127,7 @@ export class CursorManager extends Base {
151
127
  */
152
128
  private getType = (event: MouseEvent | Touch): EventType => {
153
129
  const target = event.target as HTMLElement;
154
- const focusApp = this.appManager.focusApp;
130
+ const focusApp = this.manager.focusApp;
155
131
  switch (target.parentElement) {
156
132
  case this.mainViewElement: {
157
133
  return { type: "main" };
@@ -165,25 +141,8 @@ export class CursorManager extends Base {
165
141
  }
166
142
  };
167
143
 
168
- private initCursorAttributes() {
169
- this.store.updateCursor(this.context.uid, {
170
- x: 0,
171
- y: 0,
172
- type: "main",
173
- });
174
- this.store.updateCursorState(this.context.uid, CursorState.Leave);
175
- }
176
-
177
- private setNormalCursorState() {
178
- const cursorState = this.store.getCursorState(this.context.uid);
179
- if (cursorState !== CursorState.Normal) {
180
- this.store.updateCursorState(this.context.uid, CursorState.Normal);
181
- }
182
- }
183
-
184
144
  private mouseLeaveListener = () => {
185
- this.hideCursor(this.context.uid);
186
- this.store.updateCursorState(this.context.uid, CursorState.Leave);
145
+ this.hideCursor(this.manager.uid);
187
146
  };
188
147
 
189
148
  public updateContainerRect() {
@@ -191,16 +150,6 @@ export class CursorManager extends Base {
191
150
  this.wrapperRect = WindowManager.wrapper?.getBoundingClientRect();
192
151
  }
193
152
 
194
- public setRoomMembers(members: readonly RoomMember[]) {
195
- this.roomMembers = members;
196
- this.cursorInstances.forEach(cursor => {
197
- cursor.setMember();
198
- });
199
- if (WindowManager.wrapper) {
200
- this.handleRoomMembersChange(WindowManager.wrapper);
201
- }
202
- }
203
-
204
153
  public deleteCursor(uid: string) {
205
154
  this.store.cleanCursor(uid);
206
155
  const cursor = this.cursorInstances.get(uid);
@@ -216,48 +165,6 @@ export class CursorManager extends Base {
216
165
  }
217
166
  }
218
167
 
219
- public cleanMemberAttributes(members: readonly RoomMember[]) {
220
- const uids = this.getUids(members);
221
- const needDeleteIds: string[] = [];
222
- const cursors = Object.keys(this.cursors);
223
- cursors.map(cursorId => {
224
- const index = uids.findIndex(id => id === cursorId);
225
- if (index === -1) {
226
- needDeleteIds.push(cursorId);
227
- }
228
- });
229
- needDeleteIds.forEach(uid => {
230
- this.deleteCursor(uid);
231
- });
232
- }
233
-
234
- public onReconnect() {
235
- if (this.cursorInstances.size) {
236
- this.cursorInstances.forEach(cursor => cursor.destroy());
237
- this.cursorInstances.clear();
238
- }
239
- this.roomMembers = this.appManager.room?.state.roomMembers;
240
- if (WindowManager.wrapper) {
241
- this.handleRoomMembersChange(WindowManager.wrapper);
242
- }
243
- }
244
-
245
- public addCursorChangeListener = (
246
- uid: string,
247
- callback: (position: Position, state: CursorState) => void
248
- ) => {
249
- this.manager.refresher?.add(uid, () => {
250
- const disposer = autorun(() => {
251
- const position = get(this.cursors, [uid, Fields.Position]);
252
- const state = get(this.cursors, [uid, Fields.CursorState]);
253
- if (position) {
254
- callback(position, state);
255
- }
256
- });
257
- return disposer;
258
- });
259
- };
260
-
261
168
  public destroy() {
262
169
  this.sideEffectManager.flushAll();
263
170
  if (this.cursorInstances.size) {
@@ -266,6 +173,5 @@ export class CursorManager extends Base {
266
173
  });
267
174
  this.cursorInstances.clear();
268
175
  }
269
- this.manager.refresher?.remove("cursors");
270
176
  }
271
177
  }
package/src/Helper.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { getVersionNumber } from "./Utils/Common";
2
+ import { REQUIRE_VERSION } from "./constants";
3
+ import { WhiteVersion } from "white-web-sdk";
4
+ import { WhiteWebSDKInvalidError } from "./Utils/error";
5
+ import { WindowManager } from "./index";
6
+
7
+ export const setupWrapper = (
8
+ root: HTMLElement
9
+ ): {
10
+ playground: HTMLDivElement;
11
+ wrapper: HTMLDivElement;
12
+ sizer: HTMLDivElement;
13
+ mainViewElement: HTMLDivElement;
14
+ } => {
15
+ const playground = document.createElement("div");
16
+ playground.className = "netless-window-manager-playground";
17
+
18
+ const sizer = document.createElement("div");
19
+ sizer.className = "netless-window-manager-sizer";
20
+
21
+ const wrapper = document.createElement("div");
22
+ wrapper.className = "netless-window-manager-wrapper";
23
+
24
+ const mainViewElement = document.createElement("div");
25
+ mainViewElement.className = "netless-window-manager-main-view";
26
+
27
+ playground.appendChild(sizer);
28
+ sizer.appendChild(wrapper);
29
+ wrapper.appendChild(mainViewElement);
30
+ root.appendChild(playground);
31
+ WindowManager.wrapper = wrapper;
32
+
33
+ return { playground, wrapper, sizer, mainViewElement };
34
+ };
35
+
36
+ export const checkVersion = () => {
37
+ const version = getVersionNumber(WhiteVersion);
38
+ if (version < getVersionNumber(REQUIRE_VERSION)) {
39
+ throw new WhiteWebSDKInvalidError(REQUIRE_VERSION);
40
+ }
41
+ };
@@ -10,34 +10,39 @@ class AppRegister {
10
10
 
11
11
  public async register(params: RegisterParams): Promise<void> {
12
12
  this.registered.set(params.kind, params);
13
-
14
- const srcOrAppOrFunction = params.src
15
- let downloadApp: () => Promise<NetlessApp>
16
-
13
+
14
+ const srcOrAppOrFunction = params.src;
15
+ let downloadApp: () => Promise<NetlessApp>;
16
+
17
17
  if (typeof srcOrAppOrFunction === "string") {
18
18
  downloadApp = async () => {
19
- const appClass = await loadApp(srcOrAppOrFunction, params.kind);
19
+ let appClass = (await loadApp(srcOrAppOrFunction, params.kind)) as any;
20
20
  if (appClass) {
21
- return appClass
21
+ if (appClass.__esModule) {
22
+ appClass = appClass.default;
23
+ }
24
+ return appClass;
22
25
  } else {
23
- throw new Error(`[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`);
26
+ throw new Error(
27
+ `[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`
28
+ );
24
29
  }
25
- }
30
+ };
26
31
  } else if (typeof srcOrAppOrFunction === "function") {
27
- downloadApp = srcOrAppOrFunction
32
+ downloadApp = srcOrAppOrFunction;
28
33
  } else {
29
- downloadApp = async () => srcOrAppOrFunction
34
+ downloadApp = async () => srcOrAppOrFunction;
30
35
  }
31
36
 
32
37
  this.appClasses.set(params.kind, async () => {
33
- let app = this.appClassesCache.get(params.kind)
38
+ let app = this.appClassesCache.get(params.kind);
34
39
  if (!app) {
35
- app = downloadApp()
36
- this.appClassesCache.set(params.kind, app)
40
+ app = downloadApp();
41
+ this.appClassesCache.set(params.kind, app);
37
42
  }
38
- return app
43
+ return app;
39
44
  });
40
-
45
+
41
46
  if (params.addHooks) {
42
47
  const emitter = this.createKindEmitter(params.kind);
43
48
  if (emitter) {
@@ -46,7 +51,11 @@ class AppRegister {
46
51
  }
47
52
  }
48
53
 
49
- public async notifyApp<T extends keyof RegisterEvents>(kind: string, event: T, payload: RegisterEvents[T]) {
54
+ public async notifyApp<T extends keyof RegisterEvents>(
55
+ kind: string,
56
+ event: T,
57
+ payload: RegisterEvents[T]
58
+ ) {
50
59
  const emitter = this.kindEmitters.get(kind);
51
60
  await emitter?.emit(event, payload);
52
61
  }
@@ -8,7 +8,7 @@ const TIMEOUT = 10000; // 10 秒超时
8
8
  export const getScript = async (url: string): Promise<string> => {
9
9
  const item = await getItem(url);
10
10
  if (item) {
11
- return item;
11
+ return item.sourceCode;
12
12
  } else {
13
13
  const result = await fetchWithTimeout(url, { timeout: TIMEOUT });
14
14
  const text = await result.text();
@@ -18,7 +18,7 @@ export const getScript = async (url: string): Promise<string> => {
18
18
  };
19
19
 
20
20
  export const executeScript = (text: string, appName: string): NetlessApp => {
21
- let result = Function(text + `;return ${appName}`)();
21
+ let result = Function(text + `\n;return ${appName}`)();
22
22
  if (typeof result === "undefined") {
23
23
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
24
24
  // @ts-ignore
@@ -3,6 +3,11 @@ const DatabaseName = "__WindowManagerAppCache";
3
3
  let db: IDBDatabase;
4
4
  let store: IDBObjectStore;
5
5
 
6
+ export type Item = {
7
+ kind: string;
8
+ sourceCode: string;
9
+ }
10
+
6
11
  export const initDb = async () => {
7
12
  db = await createDb();
8
13
  }
@@ -12,7 +17,7 @@ export const setItem = (key: string, val: any) => {
12
17
  return addRecord(db, { kind: key, sourceCode: val })
13
18
  };
14
19
 
15
- export const getItem = async (key: string): Promise<string | null> => {
20
+ export const getItem = async (key: string): Promise<Item | null> => {
16
21
  if (!db) return null;
17
22
  return await query(db, key);
18
23
  };
@@ -0,0 +1,54 @@
1
+ import type { AppProxy } from "../AppProxy";
2
+
3
+ export type Invoker = () => Promise<AppProxy | undefined>;
4
+
5
+ export class AppCreateQueue {
6
+ private list: Invoker[] = [];
7
+ private currentInvoker: Invoker | undefined;
8
+ private timer: number | undefined;
9
+
10
+ private initInterval() {
11
+ return setInterval(() => {
12
+ this.invoke();
13
+ }, 50);
14
+ }
15
+
16
+ public push(item: Invoker) {
17
+ this.list.push(item);
18
+ this.invoke();
19
+ if (this.timer === undefined && this.list.length > 0) {
20
+ this.timer = this.initInterval();
21
+ }
22
+ }
23
+
24
+ public invoke() {
25
+ if (this.list.length === 0) {
26
+ return;
27
+ }
28
+ if (this.currentInvoker !== undefined) {
29
+ return;
30
+ }
31
+
32
+ const item = this.list.shift();
33
+ if (item) {
34
+ this.currentInvoker = item;
35
+ item()
36
+ .then(() => {
37
+ this.currentInvoker = undefined;
38
+ if (this.list.length === 0) {
39
+ clearInterval(this.timer);
40
+ }
41
+ })
42
+ .catch(error => {
43
+ console.error(`[WindowManager]: create app error: ${error.message}`);
44
+ clearInterval(this.timer);
45
+ });
46
+ }
47
+ }
48
+
49
+ public destroy() {
50
+ if (this.timer) {
51
+ clearInterval(this.timer);
52
+ }
53
+ }
54
+ }