@netless/window-manager 0.4.0-canary.3 → 0.4.0-canary.30

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 (88) 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 +32 -1
  7. package/README.md +2 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/StorageEvent.d.ts +8 -0
  10. package/dist/App/Storage/index.d.ts +39 -0
  11. package/dist/App/Storage/typings.d.ts +22 -0
  12. package/dist/App/Storage/utils.d.ts +5 -0
  13. package/dist/AppContext.d.ts +40 -16
  14. package/dist/AppListener.d.ts +1 -1
  15. package/dist/AppManager.d.ts +26 -12
  16. package/dist/AppProxy.d.ts +7 -8
  17. package/dist/AttributesDelegate.d.ts +2 -2
  18. package/dist/BoxManager.d.ts +6 -3
  19. package/dist/BuiltinApps.d.ts +5 -0
  20. package/dist/ContainerResizeObserver.d.ts +10 -0
  21. package/dist/Cursor/Cursor.d.ts +10 -12
  22. package/dist/Cursor/index.d.ts +6 -16
  23. package/dist/Helper.d.ts +7 -0
  24. package/dist/ReconnectRefresher.d.ts +0 -1
  25. package/dist/Register/storage.d.ts +5 -1
  26. package/dist/Utils/AppCreateQueue.d.ts +11 -0
  27. package/dist/Utils/Common.d.ts +7 -2
  28. package/dist/Utils/Reactive.d.ts +1 -1
  29. package/dist/Utils/RoomHacker.d.ts +3 -3
  30. package/dist/{MainView.d.ts → View/MainView.d.ts} +5 -6
  31. package/dist/View/ViewManager.d.ts +13 -0
  32. package/dist/constants.d.ts +4 -7
  33. package/dist/index.d.ts +36 -14
  34. package/dist/index.es.js +41 -1
  35. package/dist/index.es.js.map +1 -1
  36. package/dist/index.umd.js +41 -1
  37. package/dist/index.umd.js.map +1 -1
  38. package/dist/style.css +1 -1
  39. package/dist/typings.d.ts +3 -2
  40. package/docs/api.md +69 -6
  41. package/docs/concept.md +9 -0
  42. package/docs/replay.md +40 -0
  43. package/package.json +7 -6
  44. package/src/App/MagixEvent/index.ts +68 -0
  45. package/src/App/Storage/StorageEvent.ts +21 -0
  46. package/src/App/Storage/index.ts +289 -0
  47. package/src/App/Storage/typings.ts +23 -0
  48. package/src/App/Storage/utils.ts +17 -0
  49. package/src/AppContext.ts +69 -24
  50. package/src/AppListener.ts +15 -14
  51. package/src/AppManager.ts +261 -83
  52. package/src/AppProxy.ts +53 -64
  53. package/src/AttributesDelegate.ts +2 -2
  54. package/src/BoxManager.ts +40 -24
  55. package/src/BuiltinApps.ts +23 -0
  56. package/src/ContainerResizeObserver.ts +62 -0
  57. package/src/Cursor/Cursor.svelte +25 -21
  58. package/src/Cursor/Cursor.ts +25 -38
  59. package/src/Cursor/icons.ts +2 -0
  60. package/src/Cursor/index.ts +45 -139
  61. package/src/Helper.ts +41 -0
  62. package/src/ReconnectRefresher.ts +0 -5
  63. package/src/Register/index.ts +25 -16
  64. package/src/Register/loader.ts +2 -2
  65. package/src/Register/storage.ts +6 -1
  66. package/src/Utils/AppCreateQueue.ts +54 -0
  67. package/src/Utils/Common.ts +69 -14
  68. package/src/Utils/Reactive.ts +9 -3
  69. package/src/Utils/RoomHacker.ts +44 -14
  70. package/src/{MainView.ts → View/MainView.ts} +25 -36
  71. package/src/View/ViewManager.ts +52 -0
  72. package/src/constants.ts +5 -4
  73. package/src/image/laser-pointer-cursor.svg +17 -0
  74. package/src/index.ts +158 -99
  75. package/src/shim.d.ts +5 -0
  76. package/src/style.css +7 -1
  77. package/src/typings.ts +3 -2
  78. package/vite.config.js +8 -2
  79. package/dist/Base/Context.d.ts +0 -13
  80. package/dist/Base/index.d.ts +0 -7
  81. package/dist/Utils/CameraStore.d.ts +0 -15
  82. package/dist/ViewManager.d.ts +0 -29
  83. package/dist/sdk.d.ts +0 -14
  84. package/src/Base/Context.ts +0 -49
  85. package/src/Base/index.ts +0 -10
  86. package/src/Utils/CameraStore.ts +0 -72
  87. package/src/sdk.ts +0 -39
  88. package/src/viewManager.ts +0 -177
package/src/AppProxy.ts CHANGED
@@ -2,17 +2,13 @@ import Emittery from "emittery";
2
2
  import { AppAttributes, AppEvents, Events } from "./constants";
3
3
  import { AppContext } from "./AppContext";
4
4
  import { appRegister } from "./Register";
5
- import { autorun, ViewVisionMode } from "white-web-sdk";
6
- import { callbacks, emitter } from "./index";
5
+ import { autorun } from "white-web-sdk";
6
+ import { BoxManagerNotFoundError } from "./Utils/error";
7
+ import { debounce, get } from "lodash";
8
+ import { emitter } from "./index";
7
9
  import { Fields } from "./AttributesDelegate";
8
- import { get } from "lodash";
10
+ import { entireScenes, getScenePath, removeScenes, setScenePath, setViewFocusScenePath } from "./Utils/Common";
9
11
  import { log } from "./Utils/log";
10
- import {
11
- notifyMainViewModeChange,
12
- setScenePath,
13
- setViewFocusScenePath,
14
- setViewMode,
15
- } from "./Utils/Common";
16
12
  import type {
17
13
  AppEmitterEvent,
18
14
  AppInitState,
@@ -24,10 +20,9 @@ import type { SceneState, View, SceneDefinition } from "white-web-sdk";
24
20
  import type { AppManager } from "./AppManager";
25
21
  import type { NetlessApp } from "./typings";
26
22
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
27
- import { Base } from "./Base";
28
- import { BoxManagerNotFoundError } from "./Utils/error";
29
23
 
30
- export class AppProxy extends Base {
24
+ export class AppProxy {
25
+ public kind: string;
31
26
  public id: string;
32
27
  public scenePath?: string;
33
28
  public appEmitter: Emittery<AppEmitterEvent>;
@@ -37,8 +32,8 @@ export class AppProxy extends Base {
37
32
  private boxManager = this.manager.boxManager;
38
33
  private appProxies = this.manager.appProxies;
39
34
  private viewManager = this.manager.viewManager;
40
- private cameraStore = this.manager.cameraStore;
41
- private kind: string;
35
+ private store = this.manager.store;
36
+
42
37
  public isAddApp: boolean;
43
38
  private status: "normal" | "destroyed" = "normal";
44
39
  private stateKey: string;
@@ -47,11 +42,10 @@ export class AppProxy extends Base {
47
42
 
48
43
  constructor(
49
44
  private params: BaseInsertParams,
50
- manager: AppManager,
45
+ private manager: AppManager,
51
46
  appId: string,
52
47
  isAddApp: boolean
53
48
  ) {
54
- super(manager);
55
49
  this.kind = params.kind;
56
50
  this.id = appId;
57
51
  this.stateKey = `${this.id}_state`;
@@ -73,7 +67,7 @@ export class AppProxy extends Base {
73
67
  if (options) {
74
68
  this.scenePath = options.scenePath;
75
69
  if (this.appAttributes?.isDynamicPPT && this.scenePath) {
76
- this.scenes = this.manager.displayer.entireScenes()[this.scenePath];
70
+ this.scenes = entireScenes(this.manager.displayer)[this.scenePath];
77
71
  } else {
78
72
  this.scenes = options.scenes;
79
73
  }
@@ -98,18 +92,24 @@ export class AppProxy extends Base {
98
92
 
99
93
  public getFullScenePath(): string | undefined {
100
94
  if (this.scenePath) {
101
- return get(this.appAttributes, [Fields.FullPath], this.scenePath);
95
+ return get(this.appAttributes, [Fields.FullPath], this.getFullScenePathFromScenes());
102
96
  }
103
97
  }
104
98
 
99
+ private getFullScenePathFromScenes() {
100
+ const sceneIndex = get(this.appAttributes, ["state", "SceneIndex"], 0);
101
+ const fullPath = getScenePath(this.manager.room, this.scenePath, sceneIndex);
102
+ if (fullPath) {
103
+ this.setFullPath(fullPath);
104
+ }
105
+ return fullPath;
106
+ }
107
+
105
108
  public setFullPath(path: string) {
106
109
  this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
107
110
  }
108
111
 
109
- public async baseInsertApp(
110
- skipUpdate = false,
111
- focus?: boolean
112
- ): Promise<{ appId: string; app: NetlessApp }> {
112
+ public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
113
113
  const params = this.params;
114
114
  if (!params.kind) {
115
115
  throw new Error("[WindowManager]: kind require");
@@ -117,34 +117,27 @@ export class AppProxy extends Base {
117
117
  const appImpl = await appRegister.appClasses.get(params.kind)?.();
118
118
  const appParams = appRegister.registered.get(params.kind);
119
119
  if (appImpl) {
120
- await this.setupApp(this.id, skipUpdate, appImpl, params.options, appParams?.appOptions);
120
+ await this.setupApp(
121
+ this.id,
122
+ skipUpdate,
123
+ appImpl,
124
+ params.options,
125
+ appParams?.appOptions
126
+ );
121
127
  } else {
122
128
  throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
123
129
  }
124
- this.context.updateManagerRect();
125
- if (focus) {
126
- this.focusApp();
127
- }
130
+ this.boxManager?.updateManagerRect();
128
131
  return {
129
132
  appId: this.id,
130
133
  app: appImpl,
131
134
  };
132
135
  }
133
136
 
134
- private focusApp() {
135
- this.focusBox();
136
- this.context.switchAppToWriter(this.id);
137
- this.store.setMainViewFocusPath(this.manager.mainView);
138
- }
139
-
140
137
  public get box(): ReadonlyTeleBox | undefined {
141
138
  return this.boxManager?.getBox(this.id);
142
139
  }
143
140
 
144
- public focusBox() {
145
- this.boxManager?.focusBox({ appId: this.id });
146
- }
147
-
148
141
  private async setupApp(
149
142
  appId: string,
150
143
  skipUpdate: boolean,
@@ -172,7 +165,7 @@ export class AppProxy extends Base {
172
165
  // 延迟执行 setup, 防止初始化的属性没有更新成功
173
166
  const result = await app.setup(context);
174
167
  this.appResult = result;
175
- appRegister.notifyApp(app.kind, "created", { appId, result });
168
+ appRegister.notifyApp(this.kind, "created", { appId, result });
176
169
  this.afterSetupApp(boxInitState);
177
170
  this.fixMobileSize();
178
171
  }, 50);
@@ -184,6 +177,10 @@ export class AppProxy extends Base {
184
177
  canOperate: this.manager.canOperate,
185
178
  smartPosition: this.isAddApp,
186
179
  });
180
+ if (this.isAddApp && this.box) {
181
+ this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
182
+ this.boxManager.focusBox({ appId }, false);
183
+ }
187
184
  } catch (error: any) {
188
185
  console.error(error);
189
186
  throw new Error(`[WindowManager]: app setup error: ${error.message}`);
@@ -205,9 +202,6 @@ export class AppProxy extends Base {
205
202
 
206
203
  private afterSetupApp(boxInitState: AppInitState | undefined): void {
207
204
  if (boxInitState) {
208
- if (boxInitState.focus && this.scenePath) {
209
- this.context.switchAppToWriter(this.id);
210
- }
211
205
  if (!boxInitState?.x || !boxInitState.y) {
212
206
  this.boxManager?.setBoxInitState(this.id);
213
207
  }
@@ -226,29 +220,10 @@ export class AppProxy extends Base {
226
220
  await this.destroy(true, false, true);
227
221
  const params = this.params;
228
222
  const appProxy = new AppProxy(params, this.manager, this.id, this.isAddApp);
229
- await appProxy.baseInsertApp(true, this.store.focus === this.id);
223
+ await appProxy.baseInsertApp(true);
230
224
  this.boxManager?.updateBoxState(currentAppState);
231
225
  }
232
226
 
233
- public switchToWritable() {
234
- appRegister.notifyApp(this.kind, "focus", { appId: this.id });
235
- this.cameraStore.switchView(this.id, this.view, () => {
236
- if (this.view) {
237
- if (this.view.mode === ViewVisionMode.Writable) return;
238
- try {
239
- if (this.manager.mainView.mode === ViewVisionMode.Writable) {
240
- this.store.setMainViewFocusPath(this.manager.mainView);
241
- notifyMainViewModeChange(callbacks, ViewVisionMode.Freedom);
242
- setViewMode(this.manager.mainView, ViewVisionMode.Freedom);
243
- }
244
- setViewMode(this.view, ViewVisionMode.Writable);
245
- } catch (error) {
246
- log("switch view failed", error);
247
- }
248
- }
249
- });
250
- }
251
-
252
227
  public getAppInitState = (id: string) => {
253
228
  const attrs = this.store.getAppState(id);
254
229
  if (!attrs) return;
@@ -337,7 +312,7 @@ export class AppProxy extends Base {
337
312
  }
338
313
  });
339
314
  });
340
- this.manager.refresher?.add(this.stateKey,() => {
315
+ this.manager.refresher?.add(this.stateKey, () => {
341
316
  return autorun(() => {
342
317
  const appState = this.appAttributes?.state;
343
318
  if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
@@ -345,8 +320,20 @@ export class AppProxy extends Base {
345
320
  }
346
321
  });
347
322
  });
323
+ this.manager.refresher?.add(`${appId}-fullPath`, () => {
324
+ return autorun(() => {
325
+ const fullPath = this.appAttributes?.fullPath;
326
+ this.setFocusScenePathHandler(fullPath);
327
+ });
328
+ });
348
329
  };
349
330
 
331
+ private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
332
+ if (this.view && fullPath && fullPath !== this.view?.focusScenePath) {
333
+ setViewFocusScenePath(this.view, fullPath);
334
+ }
335
+ }, 50);
336
+
350
337
  public setScenePath(): void {
351
338
  if (!this.manager.canOperate) return;
352
339
  const fullScenePath = this.getFullScenePath();
@@ -364,7 +351,6 @@ export class AppProxy extends Base {
364
351
 
365
352
  private async createView(): Promise<View> {
366
353
  const view = await this.viewManager.createView(this.id);
367
- this.cameraStore.register(this.id, view);
368
354
  this.setViewFocusScenePath();
369
355
  return view;
370
356
  }
@@ -386,14 +372,17 @@ export class AppProxy extends Base {
386
372
  }
387
373
  if (cleanAttrs) {
388
374
  this.store.cleanAppAttributes(this.id);
375
+ if (this.scenePath) {
376
+ removeScenes(this.manager.room, this.scenePath);
377
+ }
389
378
  }
390
379
  this.appProxies.delete(this.id);
391
- this.cameraStore.unregister(this.id, this.view);
392
380
 
393
381
  this.viewManager.destroyView(this.id);
394
382
  this.manager.appStatus.delete(this.id);
395
383
  this.manager.refresher?.remove(this.id);
396
384
  this.manager.refresher?.remove(this.stateKey);
385
+ this.manager.refresher?.remove(`${this.id}-fullPath`);
397
386
  }
398
387
 
399
388
  public close(): Promise<void> {
@@ -123,7 +123,7 @@ export class AttributesDelegate {
123
123
  return this.getAppAttributes(id)?.options?.scenePath;
124
124
  }
125
125
 
126
- public getMainViewScenePath() {
126
+ public getMainViewScenePath(): string | undefined {
127
127
  return this.attributes["_mainScenePath"];
128
128
  }
129
129
 
@@ -159,7 +159,7 @@ export class AttributesDelegate {
159
159
  this.context.safeSetAttributes({ [Fields.MainViewSize]: { ...size } });
160
160
  }
161
161
 
162
- public setAppFocus(appId: string, focus: boolean) {
162
+ public setAppFocus = (appId: string, focus: boolean) => {
163
163
  if (focus) {
164
164
  this.context.safeSetAttributes({ [Fields.Focus]: appId });
165
165
  } else {
package/src/BoxManager.ts CHANGED
@@ -1,12 +1,11 @@
1
- import { AppAttributes, DEFAULT_COLLECTOR_STYLE, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
- import { debounce, maxBy } from "lodash";
1
+ import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
+ import { debounce } from "lodash";
3
+ import { emitter, WindowManager } from "./index";
3
4
  import {
4
- TELE_BOX_MANAGER_EVENT,
5
5
  TELE_BOX_STATE,
6
6
  TeleBoxCollector,
7
7
  TeleBoxManager,
8
8
  } from "@netless/telebox-insider";
9
- import { WindowManager } from "./index";
10
9
  import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
11
10
  import type {
12
11
  TeleBoxManagerUpdateConfig,
@@ -15,6 +14,7 @@ import type {
15
14
  TeleBoxManagerConfig,
16
15
  TeleBoxColorScheme,
17
16
  TeleBoxRect,
17
+ TeleBoxConfig,
18
18
  } from "@netless/telebox-insider";
19
19
  import type Emittery from "emittery";
20
20
  import type { NetlessApp } from "./typings";
@@ -57,6 +57,7 @@ export type BoxManagerContext = {
57
57
  canOperate: () => boolean;
58
58
  notifyContainerRectUpdate: (rect: TeleBoxRect) => void;
59
59
  cleanFocus: () => void;
60
+ setAppFocus: (appId: string) => void;
60
61
  };
61
62
 
62
63
  export const createBoxManager = (
@@ -74,6 +75,7 @@ export const createBoxManager = (
74
75
  notifyContainerRectUpdate: (rect: TeleBoxRect) =>
75
76
  manager.appManager?.notifyContainerRectUpdate(rect),
76
77
  cleanFocus: () => manager.appManager?.store.cleanFocus(),
78
+ setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
77
79
  callbacks,
78
80
  emitter,
79
81
  },
@@ -90,17 +92,32 @@ export class BoxManager {
90
92
  ) {
91
93
  const { emitter, callbacks } = context;
92
94
  this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
93
- this.teleBoxManager.events.on(TELE_BOX_MANAGER_EVENT.State, state => {
94
- if (state) {
95
- this.context.callbacks.emit("boxStateChange", state);
96
- this.context.emitter.emit("boxStateChange", state);
97
- }
95
+
96
+ // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
97
+ this.teleBoxManager._state$.reaction(state => {
98
+ callbacks.emit("boxStateChange", state);
99
+ emitter.emit("boxStateChange", state);
100
+ });
101
+
102
+ this.teleBoxManager._darkMode$.reaction(darkMode => {
103
+ callbacks.emit("darkModeChange", darkMode);
98
104
  });
105
+ this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
106
+ callbacks.emit("prefersColorSchemeChange", colorScheme);
107
+ });
108
+
109
+ // events.on 的值则会根据 skipUpdate 来决定是否触发回调
99
110
  this.teleBoxManager.events.on("minimized", minimized => {
100
111
  this.context.safeSetAttributes({ minimized });
101
112
  if (minimized) {
102
113
  this.context.cleanFocus();
103
114
  this.blurAllBox();
115
+ } else {
116
+ const topBox = this.getTopBox();
117
+ if (topBox) {
118
+ this.context.setAppFocus(topBox.id);
119
+ this.focusBox({ appId: topBox.id }, false);
120
+ }
104
121
  }
105
122
  });
106
123
  this.teleBoxManager.events.on("maximized", maximized => {
@@ -136,17 +153,16 @@ export class BoxManager {
136
153
  }
137
154
  }
138
155
  });
139
- this.teleBoxManager.events.on("dark_mode", darkMode => {
140
- callbacks.emit("darkModeChange", darkMode);
141
- });
142
- this.teleBoxManager.events.on("prefers_color_scheme", colorScheme => {
143
- callbacks.emit("prefersColorSchemeChange", colorScheme);
144
- });
145
156
  this.teleBoxManager.events.on("z_index", box => {
146
157
  this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
147
158
  });
159
+ emitter.on("playgroundSizeChange", this.playgroundSizeChangeListener);
148
160
  }
149
161
 
162
+ private playgroundSizeChangeListener = () => {
163
+ this.updateManagerRect();
164
+ };
165
+
150
166
  private get mainView() {
151
167
  return this.context.getMainView();
152
168
  }
@@ -251,12 +267,8 @@ export class BoxManager {
251
267
  }
252
268
 
253
269
  public setCollectorContainer(container: HTMLElement) {
254
- const styles = {
255
- ...DEFAULT_COLLECTOR_STYLE,
256
- ...this.createTeleBoxManagerConfig?.collectorStyles,
257
- };
258
270
  const collector = new TeleBoxCollector({
259
- styles
271
+ styles: this.createTeleBoxManagerConfig?.collectorStyles,
260
272
  }).mount(container);
261
273
  this.teleBoxManager.setCollector(collector);
262
274
  }
@@ -280,8 +292,7 @@ export class BoxManager {
280
292
  }
281
293
 
282
294
  public getTopBox(): ReadonlyTeleBox | undefined {
283
- const boxes = this.teleBoxManager.query();
284
- return maxBy(boxes, "zIndex");
295
+ return this.teleBoxManager.topBox;
285
296
  }
286
297
 
287
298
  public updateBoxState(state?: AppInitState): void {
@@ -358,9 +369,9 @@ export class BoxManager {
358
369
  this.teleBoxManager.updateAll(config);
359
370
  }
360
371
 
361
- public setMaximized(maximized: boolean) {
372
+ public setMaximized(maximized: boolean, skipUpdate = true): void {
362
373
  if (maximized !== this.maximized) {
363
- this.teleBoxManager.setMaximized(maximized, true);
374
+ this.teleBoxManager.setMaximized(maximized, skipUpdate);
364
375
  }
365
376
  }
366
377
 
@@ -378,6 +389,10 @@ export class BoxManager {
378
389
  }
379
390
  }
380
391
 
392
+ public updateBox(id: string, payload: TeleBoxConfig, skipUpdate = true): void {
393
+ this.teleBoxManager.update(id, payload, skipUpdate);
394
+ }
395
+
381
396
  public setReadonly(readonly: boolean) {
382
397
  this.teleBoxManager.setReadonly(readonly);
383
398
  }
@@ -391,6 +406,7 @@ export class BoxManager {
391
406
  }
392
407
 
393
408
  public destroy() {
409
+ emitter.off("playgroundSizeChange", this.playgroundSizeChangeListener);
394
410
  this.teleBoxManager.destroy();
395
411
  }
396
412
  }
@@ -0,0 +1,23 @@
1
+ import AppDocsViewer from "@netless/app-docs-viewer";
2
+ import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
3
+ import { WindowManager } from "./index";
4
+
5
+ export const setupBuiltin = () => {
6
+ if (WindowManager.debug) {
7
+ setOptions({ verbose: true });
8
+ }
9
+
10
+ WindowManager.register({
11
+ kind: AppDocsViewer.kind,
12
+ src: AppDocsViewer,
13
+ });
14
+ WindowManager.register({
15
+ kind: AppMediaPlayer.kind,
16
+ src: AppMediaPlayer,
17
+ });
18
+ };
19
+
20
+ export const BuiltinApps = {
21
+ DocsViewer: AppDocsViewer.kind as string,
22
+ MediaPlayer: AppMediaPlayer.kind as string,
23
+ };
@@ -0,0 +1,62 @@
1
+ import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
2
+ import { WindowManager } from "./index";
3
+ import type { EmitterType } from "./index";
4
+
5
+ const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
6
+
7
+ export class ContainerResizeObserver {
8
+ private containerResizeObserver?: ResizeObserver;
9
+
10
+ constructor(private emitter: EmitterType) {}
11
+
12
+ public static create(
13
+ container: HTMLElement,
14
+ sizer: HTMLElement,
15
+ wrapper: HTMLDivElement,
16
+ emitter: EmitterType
17
+ ) {
18
+ const containerResizeObserver = new ContainerResizeObserver(emitter);
19
+ containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
20
+ return containerResizeObserver;
21
+ }
22
+
23
+ public observePlaygroundSize(
24
+ container: HTMLElement,
25
+ sizer: HTMLElement,
26
+ wrapper: HTMLDivElement
27
+ ) {
28
+ this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
29
+
30
+ this.containerResizeObserver = new ResizeObserver(entries => {
31
+ const containerRect = entries[0]?.contentRect;
32
+ if (containerRect) {
33
+ this.updateSizer(containerRect, sizer, wrapper);
34
+ this.emitter.emit("playgroundSizeChange", containerRect);
35
+ }
36
+ });
37
+
38
+ this.containerResizeObserver.observe(container);
39
+ }
40
+
41
+ private updateSizer(
42
+ { width, height }: DOMRectReadOnly,
43
+ sizer: HTMLElement,
44
+ wrapper: HTMLDivElement
45
+ ) {
46
+ if (width && height) {
47
+ if (height / width > WindowManager.containerSizeRatio) {
48
+ height = width * WindowManager.containerSizeRatio;
49
+ sizer.classList.toggle("netless-window-manager-sizer-horizontal", true);
50
+ } else {
51
+ width = height / WindowManager.containerSizeRatio;
52
+ sizer.classList.toggle("netless-window-manager-sizer-horizontal", false);
53
+ }
54
+ wrapper.style.width = `${width}px`;
55
+ wrapper.style.height = `${height}px`;
56
+ }
57
+ }
58
+
59
+ public disconnect() {
60
+ this.containerResizeObserver?.disconnect();
61
+ }
62
+ }
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { isEmpty } from "lodash";
3
+ import { ApplianceNames } from "white-web-sdk";
3
4
 
4
5
  export let cursorName: string;
5
6
  export let tagName: string;
@@ -19,6 +20,7 @@
19
20
  $: hasTagName = !isEmpty(tagName);
20
21
  $: hasAvatar = !isEmpty(avatar);
21
22
  $: display = visible ? "initial" : "none";
23
+ $: isLaserPointer = appliance === ApplianceNames.laserPointer;
22
24
 
23
25
  const computedAvatarStyle = () => {
24
26
  return Object.entries({
@@ -36,28 +38,30 @@
36
38
  <div
37
39
  class="netless-window-manager-cursor-mid"
38
40
  style="transform: translateX({x}px) translateY({y}px);display: {display}"
39
- >
40
- <div class="netless-window-manager-cursor-name">
41
- <div
42
- class={theme}
43
- style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
44
- >
45
- {#if hasAvatar}
46
- <img
47
- class="netless-window-manager-cursor-selector-avatar"
48
- style={computedAvatarStyle()}
49
- src={avatar}
50
- alt="avatar"
51
- />
52
- {/if}
53
- <span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;max-width: 80px">{cursorName}</span>
54
- {#if hasTagName}
55
- <span class="netless-window-manager-cursor-tag-name" style="background-color: {cursorTagBackgroundColor}">
56
- {tagName}
57
- </span>
58
- {/if}
41
+ >
42
+ {#if !isLaserPointer}
43
+ <div class="netless-window-manager-cursor-name">
44
+ <div
45
+ class={theme}
46
+ style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
47
+ >
48
+ {#if hasAvatar}
49
+ <img
50
+ class="netless-window-manager-cursor-selector-avatar"
51
+ style={computedAvatarStyle()}
52
+ src={avatar}
53
+ alt="avatar"
54
+ />
55
+ {/if}
56
+ <span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;max-width: 80px">{cursorName}</span>
57
+ {#if hasTagName}
58
+ <span class="netless-window-manager-cursor-tag-name" style="background-color: {cursorTagBackgroundColor}">
59
+ {tagName}
60
+ </span>
61
+ {/if}
62
+ </div>
59
63
  </div>
60
- </div>
64
+ {/if}
61
65
  <div class="cursor-image-wrapper">
62
66
  <img class="netless-window-manager-cursor-{appliance}-image" {src} alt={appliance} />
63
67
  </div>