@netless/window-manager 1.0.0-canary.8 → 1.0.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 (135) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +90 -64
  3. package/README.zh-cn.md +224 -0
  4. package/dist/index.d.ts +1133 -39
  5. package/dist/index.js +62 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/{index.es.js → index.mjs} +8393 -5913
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/style.css +1 -1
  10. package/docs/advanced.md +55 -55
  11. package/docs/api.md +124 -113
  12. package/docs/app-context.md +248 -209
  13. package/docs/basic.md +25 -26
  14. package/docs/camera.md +19 -20
  15. package/docs/cn/advanced.md +137 -0
  16. package/docs/cn/api.md +309 -0
  17. package/docs/cn/app-context.md +369 -0
  18. package/docs/cn/basic.md +64 -0
  19. package/docs/cn/camera.md +53 -0
  20. package/docs/cn/concept.md +9 -0
  21. package/docs/cn/custom-max-bar.md +31 -0
  22. package/docs/cn/develop-app.md +94 -0
  23. package/docs/cn/export-pdf.md +48 -0
  24. package/docs/cn/migrate.md +60 -0
  25. package/docs/cn/replay.md +40 -0
  26. package/docs/concept.md +6 -5
  27. package/docs/custom-max-bar.md +31 -0
  28. package/docs/develop-app.md +22 -19
  29. package/docs/export-pdf.md +48 -0
  30. package/docs/migrate.md +25 -27
  31. package/docs/quickstart.md +50 -0
  32. package/docs/replay.md +20 -20
  33. package/package.json +32 -22
  34. package/src/App/AppContext.ts +104 -71
  35. package/src/App/AppPageStateImpl.ts +6 -25
  36. package/src/App/AppProxy.ts +42 -147
  37. package/src/App/MagixEvent/index.ts +38 -38
  38. package/src/App/Storage/StorageEvent.ts +13 -13
  39. package/src/App/Storage/index.ts +269 -245
  40. package/src/App/Storage/typings.ts +4 -2
  41. package/src/App/Storage/utils.ts +3 -3
  42. package/src/App/index.ts +0 -1
  43. package/src/AppListener.ts +8 -8
  44. package/src/AppManager.ts +84 -75
  45. package/src/AttributesDelegate.ts +42 -22
  46. package/src/BoxEmitter.ts +12 -6
  47. package/src/BoxManager.ts +122 -106
  48. package/src/ContainerResizeObserver.ts +75 -0
  49. package/src/Cursor/Cursor.svelte +16 -5
  50. package/src/Cursor/Cursor.svelte.d.ts +21 -0
  51. package/src/Cursor/Cursor.ts +77 -13
  52. package/src/Cursor/icons.ts +6 -0
  53. package/src/Cursor/icons2.ts +66 -0
  54. package/src/Cursor/index.ts +127 -26
  55. package/src/Helper.ts +94 -14
  56. package/src/InternalEmitter.ts +2 -7
  57. package/src/Page/PageController.ts +1 -0
  58. package/src/Page/index.ts +1 -1
  59. package/src/PageState.ts +6 -5
  60. package/src/ReconnectRefresher.ts +9 -4
  61. package/src/RedoUndo.ts +3 -3
  62. package/src/Register/index.ts +22 -17
  63. package/src/Register/loader.ts +26 -22
  64. package/src/Register/storage.ts +13 -13
  65. package/src/Utils/Common.ts +18 -14
  66. package/src/Utils/Reactive.ts +26 -25
  67. package/src/Utils/RoomHacker.ts +4 -4
  68. package/src/Utils/error.ts +0 -1
  69. package/src/View/IframeBridge.ts +680 -0
  70. package/src/View/MainView.ts +114 -53
  71. package/src/callback.ts +21 -1
  72. package/src/constants.ts +0 -2
  73. package/src/image/pencil-eraser-1.svg +3 -0
  74. package/src/image/pencil-eraser-2.svg +3 -0
  75. package/src/image/pencil-eraser-3.svg +3 -0
  76. package/src/index.ts +224 -74
  77. package/src/style.css +27 -10
  78. package/src/typings.ts +20 -10
  79. package/.prettierignore +0 -7
  80. package/.prettierrc.json +0 -9
  81. package/CHANGELOG.md +0 -196
  82. package/__mocks__/white-web-sdk.ts +0 -50
  83. package/dist/App/AppContext.d.ts +0 -76
  84. package/dist/App/AppPageStateImpl.d.ts +0 -21
  85. package/dist/App/AppProxy.d.ts +0 -81
  86. package/dist/App/AppViewSync.d.ts +0 -11
  87. package/dist/App/MagixEvent/index.d.ts +0 -29
  88. package/dist/App/Storage/StorageEvent.d.ts +0 -8
  89. package/dist/App/Storage/index.d.ts +0 -39
  90. package/dist/App/Storage/typings.d.ts +0 -22
  91. package/dist/App/Storage/utils.d.ts +0 -5
  92. package/dist/App/WhiteboardView.d.ts +0 -22
  93. package/dist/App/index.d.ts +0 -3
  94. package/dist/AppListener.d.ts +0 -21
  95. package/dist/AppManager.d.ts +0 -107
  96. package/dist/AttributesDelegate.d.ts +0 -80
  97. package/dist/BoxEmitter.d.ts +0 -34
  98. package/dist/BoxManager.d.ts +0 -100
  99. package/dist/BuiltinApps.d.ts +0 -5
  100. package/dist/Cursor/Cursor.d.ts +0 -39
  101. package/dist/Cursor/icons.d.ts +0 -3
  102. package/dist/Cursor/index.d.ts +0 -46
  103. package/dist/Helper.d.ts +0 -17
  104. package/dist/InternalEmitter.d.ts +0 -39
  105. package/dist/Page/PageController.d.ts +0 -20
  106. package/dist/Page/index.d.ts +0 -3
  107. package/dist/PageState.d.ts +0 -9
  108. package/dist/ReconnectRefresher.d.ts +0 -24
  109. package/dist/RedoUndo.d.ts +0 -18
  110. package/dist/Register/index.d.ts +0 -28
  111. package/dist/Register/loader.d.ts +0 -4
  112. package/dist/Register/storage.d.ts +0 -8
  113. package/dist/Utils/AppCreateQueue.d.ts +0 -15
  114. package/dist/Utils/Common.d.ts +0 -23
  115. package/dist/Utils/Reactive.d.ts +0 -6
  116. package/dist/Utils/RoomHacker.d.ts +0 -3
  117. package/dist/Utils/error.d.ts +0 -27
  118. package/dist/Utils/log.d.ts +0 -1
  119. package/dist/View/CameraSynchronizer.d.ts +0 -17
  120. package/dist/View/MainView.d.ts +0 -48
  121. package/dist/View/ViewManager.d.ts +0 -13
  122. package/dist/View/ViewSync.d.ts +0 -7
  123. package/dist/callback.d.ts +0 -24
  124. package/dist/constants.d.ts +0 -49
  125. package/dist/index.cjs.js +0 -46
  126. package/dist/index.umd.js +0 -46
  127. package/dist/typings.d.ts +0 -82
  128. package/jest.config.js +0 -27
  129. package/pnpm-lock.yaml +0 -6302
  130. package/src/App/AppViewSync.ts +0 -68
  131. package/src/App/WhiteboardView.ts +0 -83
  132. package/src/View/CameraSynchronizer.ts +0 -73
  133. package/src/View/ViewSync.ts +0 -10
  134. package/vite.config.js +0 -51
  135. /package/docs/{qickstart.md → cn/quickstart.md} +0 -0
package/src/BoxManager.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
2
  import { debounce } from "lodash";
3
- import { TELE_BOX_STATE, TeleBoxManager } from "@netless/telebox-insider";
3
+ import { TELE_BOX_STATE, TeleBoxCollector, TeleBoxManager } from "@netless/telebox-insider";
4
4
  import { WindowManager } from "./index";
5
5
  import type { BoxEmitterType } from "./BoxEmitter";
6
6
  import type { AddAppOptions, AppInitState } from "./index";
@@ -18,7 +18,6 @@ import type { NetlessApp } from "./typings";
18
18
  import type { View } from "white-web-sdk";
19
19
  import type { CallbacksType } from "./callback";
20
20
  import type { EmitterType } from "./InternalEmitter";
21
- import { SideEffectManager } from "side-effect-manager";
22
21
 
23
22
  export { TELE_BOX_STATE };
24
23
 
@@ -46,7 +45,6 @@ export type CreateTeleBoxManagerConfig = {
46
45
  collectorContainer?: HTMLElement;
47
46
  collectorStyles?: Partial<CSSStyleDeclaration>;
48
47
  prefersColorScheme?: TeleBoxColorScheme;
49
- stageRatio?: number;
50
48
  };
51
49
 
52
50
  export type BoxManagerContext = {
@@ -81,7 +79,7 @@ export const createBoxManager = (
81
79
  setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
82
80
  callbacks,
83
81
  emitter,
84
- boxEmitter
82
+ boxEmitter,
85
83
  },
86
84
  options
87
85
  );
@@ -89,93 +87,91 @@ export const createBoxManager = (
89
87
 
90
88
  export class BoxManager {
91
89
  public teleBoxManager: TeleBoxManager;
92
- protected sideEffectManager: SideEffectManager;
93
90
 
94
91
  constructor(
95
92
  private context: BoxManagerContext,
96
- createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
93
+ private createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
97
94
  ) {
98
- this.sideEffectManager = new SideEffectManager();
99
95
  const { emitter, callbacks, boxEmitter } = context;
100
96
  this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
101
- this.sideEffectManager.add(() => [
102
- // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
103
- this.teleBoxManager._state$.reaction(state => {
104
- callbacks.emit("boxStateChange", state);
105
- emitter.emit("boxStateChange", state);
106
- }),
107
- this.teleBoxManager._darkMode$.reaction(darkMode => {
108
- callbacks.emit("darkModeChange", darkMode);
109
- }),
110
- this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
111
- callbacks.emit("prefersColorSchemeChange", colorScheme);
112
- }),
113
- this.teleBoxManager._minimized$.reaction((minimized, skipUpdate) => {
114
- if (skipUpdate) {
115
- return;
116
- }
117
- this.context.safeSetAttributes({ minimized });
118
- if (minimized) {
119
- this.context.cleanFocus();
120
- this.blurAllBox();
121
- } else {
122
- const topBox = this.getTopBox();
123
- if (topBox) {
124
- this.context.setAppFocus(topBox.id);
125
- this.focusBox({ appId: topBox.id }, false);
126
- }
127
- }
128
- }),
129
- this.teleBoxManager._maximized$.reaction((maximized, skipUpdate) => {
130
- if (skipUpdate) {
131
- return;
97
+
98
+ // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
99
+ this.teleBoxManager._state$.reaction(state => {
100
+ callbacks.emit("boxStateChange", state);
101
+ emitter.emit("boxStateChange", state);
102
+ });
103
+
104
+ this.teleBoxManager._darkMode$.reaction(darkMode => {
105
+ callbacks.emit("darkModeChange", darkMode);
106
+ });
107
+ this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
108
+ callbacks.emit("prefersColorSchemeChange", colorScheme);
109
+ });
110
+
111
+ // ppt 在最小化后刷新恢复正常大小,拿不到正确的宽高,需要手动触发一下窗口的 resize
112
+ this.teleBoxManager._minimized$.reaction(minimized => {
113
+ if (!minimized) {
114
+ setTimeout(() => {
115
+ const offset = 0.001 * (Math.random() > 0.5 ? 1 : -1);
116
+ this.teleBoxManager.boxes.forEach(box => {
117
+ box.resize(box.intrinsicWidth + offset, box.intrinsicHeight + offset, true);
118
+ });
119
+ }, 400);
120
+ }
121
+ });
122
+
123
+ // events.on 的值则会根据 skipUpdate 来决定是否触发回调
124
+ this.teleBoxManager.events.on("minimized", minimized => {
125
+ this.context.safeSetAttributes({ minimized });
126
+ if (minimized) {
127
+ this.context.cleanFocus();
128
+ this.blurAllBox();
129
+ } else {
130
+ const topBox = this.getTopBox();
131
+ if (topBox) {
132
+ this.context.setAppFocus(topBox.id);
133
+ this.focusBox({ appId: topBox.id }, false);
132
134
  }
133
- this.context.safeSetAttributes({ maximized });
134
- }),
135
- this.teleBoxManager.events.on("removed", boxes => {
136
- boxes.forEach(box => {
137
- boxEmitter.emit("close", { appId: box.id });
135
+ }
136
+ });
137
+ this.teleBoxManager.events.on("maximized", maximized => {
138
+ this.context.safeSetAttributes({ maximized });
139
+ });
140
+ this.teleBoxManager.events.on("removed", boxes => {
141
+ boxes.forEach(box => {
142
+ boxEmitter.emit("close", { appId: box.id });
143
+ });
144
+ });
145
+ this.teleBoxManager.events.on(
146
+ "intrinsic_move",
147
+ debounce((box: ReadonlyTeleBox): void => {
148
+ boxEmitter.emit("move", { appId: box.id, x: box.intrinsicX, y: box.intrinsicY });
149
+ }, 50)
150
+ );
151
+ this.teleBoxManager.events.on(
152
+ "intrinsic_resize",
153
+ debounce((box: ReadonlyTeleBox): void => {
154
+ boxEmitter.emit("resize", {
155
+ appId: box.id,
156
+ width: box.intrinsicWidth,
157
+ height: box.intrinsicHeight,
138
158
  });
139
- }),
140
- this.teleBoxManager.events.on(
141
- "intrinsic_move",
142
- debounce((box: ReadonlyTeleBox): void => {
143
- boxEmitter.emit("move", { appId: box.id, x: box.intrinsicX, y: box.intrinsicY });
144
- }, 50)
145
- ),
146
- this.teleBoxManager.events.on(
147
- "intrinsic_resize",
148
- debounce((box: ReadonlyTeleBox): void => {
149
- boxEmitter.emit("resize", {
150
- appId: box.id,
151
- width: box.intrinsicWidth,
152
- height: box.intrinsicHeight,
153
- });
154
- }, 200)
155
- ),
156
- this.teleBoxManager.events.on("focused", box => {
157
- if (box) {
158
- if (this.canOperate) {
159
- boxEmitter.emit("focus", { appId: box.id });
160
- } else {
161
- this.teleBoxManager.blurBox(box.id);
162
- }
159
+ }, 200)
160
+ );
161
+ this.teleBoxManager.events.on("focused", box => {
162
+ if (box) {
163
+ if (this.canOperate) {
164
+ boxEmitter.emit("focus", { appId: box.id });
165
+ } else {
166
+ this.teleBoxManager.blurBox(box.id);
163
167
  }
164
- }),
165
- this.teleBoxManager.events.on("z_index", box => {
166
- this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
167
- }),
168
- this.teleBoxManager._stageRect$.subscribe(stage => {
169
- emitter.emit("playgroundSizeChange", stage);
170
- this.context.notifyContainerRectUpdate(stage);
171
- }),
172
- emitter.on("writableChange", isWritable => {
173
- this.teleBoxManager.setHighlightStage(isWritable);
174
- }),
175
- emitter.on("containerSizeRatioUpdate", ratio => {
176
- this.teleBoxManager._stageRatio$.setValue(ratio);
177
- }),
178
- ]);
168
+ }
169
+ });
170
+ this.teleBoxManager.events.on("z_index", box => {
171
+ this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
172
+ });
173
+ emitter.on("playgroundSizeChange", () => this.updateManagerRect());
174
+ emitter.on("updateManagerRect", () => this.updateManagerRect());
179
175
  }
180
176
 
181
177
  private get mainView() {
@@ -210,16 +206,12 @@ export class BoxManager {
210
206
  return this.teleBoxManager.boxes.length;
211
207
  }
212
208
 
213
- public get stageRect() {
214
- return this.teleBoxManager.stageRect;
215
- }
216
-
217
- public createBox(params: CreateBoxParams): ReadonlyTeleBox | undefined {
209
+ public createBox(params: CreateBoxParams): void {
218
210
  if (!this.teleBoxManager) return;
219
211
  let { minwidth = MIN_WIDTH, minheight = MIN_HEIGHT } = params.app.config ?? {};
220
212
  const { width, height } = params.app.config ?? {};
221
213
  const title = params.options?.title || params.appId;
222
- const rect = this.teleBoxManager.rootRect;
214
+ const rect = this.teleBoxManager.containerRect;
223
215
 
224
216
  if (minwidth > 1) {
225
217
  minwidth = minwidth / rect.width;
@@ -237,18 +229,38 @@ export class BoxManager {
237
229
  height,
238
230
  id: params.appId,
239
231
  };
240
- const box = this.teleBoxManager.create(createBoxConfig, params.smartPosition);
232
+ this.teleBoxManager.create(createBoxConfig, params.smartPosition);
241
233
  this.context.emitter.emit(`${params.appId}${Events.WindowCreated}` as any);
242
- return box;
234
+ }
235
+
236
+ public setBoxInitState(appId: string): void {
237
+ const box = this.teleBoxManager.queryOne({ id: appId });
238
+ if (box) {
239
+ if (box.state === TELE_BOX_STATE.Maximized) {
240
+ this.context.boxEmitter.emit("resize", {
241
+ appId: appId,
242
+ x: box.x,
243
+ y: box.y,
244
+ width: box.intrinsicWidth,
245
+ height: box.intrinsicHeight,
246
+ });
247
+ }
248
+ }
243
249
  }
244
250
 
245
251
  public setupBoxManager(
246
252
  createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
247
253
  ): TeleBoxManager {
248
- const root = WindowManager.playground;
254
+ const root = WindowManager.wrapper ? WindowManager.wrapper : document.body;
255
+ const rect = root.getBoundingClientRect();
249
256
  const initManagerState: TeleBoxManagerConfig = {
250
- stageRatio: createTeleBoxManagerConfig?.stageRatio,
251
257
  root: root,
258
+ containerRect: {
259
+ x: 0,
260
+ y: 0,
261
+ width: rect.width,
262
+ height: rect.height,
263
+ },
252
264
  fence: false,
253
265
  prefersColorScheme: createTeleBoxManagerConfig?.prefersColorScheme,
254
266
  };
@@ -258,16 +270,20 @@ export class BoxManager {
258
270
  this.teleBoxManager.destroy();
259
271
  }
260
272
  this.teleBoxManager = manager;
261
- const container = createTeleBoxManagerConfig?.collectorContainer;
273
+ const container = createTeleBoxManagerConfig?.collectorContainer || WindowManager.wrapper;
262
274
  if (container) {
263
- this.teleBoxManager.collector.set$collector(container);
264
- }
265
- if (createTeleBoxManagerConfig?.collectorStyles) {
266
- this.teleBoxManager.collector.setStyles(createTeleBoxManagerConfig.collectorStyles);
275
+ this.setCollectorContainer(container);
267
276
  }
268
277
  return manager;
269
278
  }
270
279
 
280
+ public setCollectorContainer(container: HTMLElement) {
281
+ const collector = new TeleBoxCollector({
282
+ styles: this.createTeleBoxManagerConfig?.collectorStyles,
283
+ }).mount(container);
284
+ this.teleBoxManager.setCollector(collector);
285
+ }
286
+
271
287
  public getBox(appId: string): ReadonlyTeleBox | undefined {
272
288
  return this.teleBoxManager.queryOne({ id: appId });
273
289
  }
@@ -320,6 +336,15 @@ export class BoxManager {
320
336
  }
321
337
  }
322
338
 
339
+ public updateManagerRect(): void {
340
+ const rect = this.mainView.divElement?.getBoundingClientRect();
341
+ if (rect && rect.width > 0 && rect.height > 0) {
342
+ const containerRect = { x: 0, y: 0, width: rect.width, height: rect.height };
343
+ this.teleBoxManager.setContainerRect(containerRect);
344
+ this.context.notifyContainerRectUpdate(this.teleBoxManager.containerRect);
345
+ }
346
+ }
347
+
323
348
  public moveBox({ appId, x, y }: MoveBoxParams): void {
324
349
  this.teleBoxManager.update(appId, { x, y }, true);
325
350
  }
@@ -391,16 +416,7 @@ export class BoxManager {
391
416
  this.teleBoxManager.update(id, { zIndex }, skipUpdate);
392
417
  }
393
418
 
394
- public setRoot(root: HTMLElement) {
395
- this.teleBoxManager._root$.setValue(root);
396
- }
397
-
398
- public setCollector(collector: HTMLElement) {
399
- this.teleBoxManager.collector.set$collector(collector);
400
- }
401
-
402
419
  public destroy() {
403
- this.sideEffectManager.flushAll();
404
420
  this.teleBoxManager.destroy();
405
421
  }
406
422
  }
@@ -0,0 +1,75 @@
1
+ import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
2
+ import { isFunction } from "lodash";
3
+ import { WindowManager } from "./index";
4
+ import type { EmitterType } from "./InternalEmitter";
5
+ import type { UnsubscribeFn } from "emittery";
6
+
7
+ const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
8
+
9
+ export class ContainerResizeObserver {
10
+ private containerResizeObserver?: ResizeObserver;
11
+ private disposer?: UnsubscribeFn;
12
+
13
+ constructor(private emitter: EmitterType) {}
14
+
15
+ public static create(
16
+ container: HTMLElement,
17
+ sizer: HTMLElement,
18
+ wrapper: HTMLDivElement,
19
+ emitter: EmitterType
20
+ ) {
21
+ const containerResizeObserver = new ContainerResizeObserver(emitter);
22
+ containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
23
+ return containerResizeObserver;
24
+ }
25
+
26
+ public observePlaygroundSize(
27
+ container: HTMLElement,
28
+ sizer: HTMLElement,
29
+ wrapper: HTMLDivElement
30
+ ) {
31
+ this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
32
+
33
+ this.containerResizeObserver = new ResizeObserver(entries => {
34
+ const containerRect = entries[0]?.contentRect;
35
+ if (containerRect) {
36
+ this.updateSizer(containerRect, sizer, wrapper);
37
+ this.emitter.emit("playgroundSizeChange", containerRect);
38
+ }
39
+ });
40
+
41
+ this.disposer = this.emitter.on("containerSizeRatioUpdate", () => {
42
+ const containerRect = container.getBoundingClientRect();
43
+ this.updateSizer(containerRect, sizer, wrapper);
44
+ this.emitter.emit("playgroundSizeChange", containerRect);
45
+ });
46
+
47
+ this.containerResizeObserver.observe(container);
48
+ }
49
+
50
+ public updateSizer(
51
+ { width, height }: DOMRectReadOnly,
52
+ sizer: HTMLElement,
53
+ wrapper: HTMLDivElement
54
+ ) {
55
+ if (width && height) {
56
+ if (height / width > WindowManager.containerSizeRatio) {
57
+ height = width * WindowManager.containerSizeRatio;
58
+ sizer.classList.toggle("netless-window-manager-sizer-horizontal", true);
59
+ } else {
60
+ width = height / WindowManager.containerSizeRatio;
61
+ sizer.classList.toggle("netless-window-manager-sizer-horizontal", false);
62
+ }
63
+ wrapper.style.width = `${width}px`;
64
+ wrapper.style.height = `${height}px`;
65
+ }
66
+ }
67
+
68
+ public disconnect() {
69
+ this.containerResizeObserver?.disconnect();
70
+ if (isFunction(this.disposer)) {
71
+ this.disposer();
72
+ this.disposer = undefined;
73
+ }
74
+ }
75
+ }
@@ -2,6 +2,7 @@
2
2
  import { isEmpty } from "lodash";
3
3
  import { ApplianceNames } from "white-web-sdk";
4
4
 
5
+ export let uid: string;
5
6
  export let cursorName: string;
6
7
  export let tagName: string | undefined;
7
8
  export let backgroundColor: string;
@@ -15,12 +16,17 @@
15
16
  export let color: string;
16
17
  export let cursorTagBackgroundColor: string;
17
18
  export let opacity: number;
19
+ export let pencilEraserSize: number | undefined;
20
+ export let custom: boolean | undefined;
18
21
 
19
22
  $: hasName = !isEmpty(cursorName);
20
23
  $: hasTagName = !isEmpty(tagName);
21
24
  $: hasAvatar = !isEmpty(avatar);
22
- $: display = visible ? "initial" : "none";
25
+ $: display = visible ? "" : "none";
23
26
  $: isLaserPointer = appliance === ApplianceNames.laserPointer;
27
+ $: isLaserPointerPencilEraser = isLaserPointer || appliance === ApplianceNames.pencilEraser;
28
+ $: offset = isLaserPointerPencilEraser ? "netless-window-manager-laserPointer-pencilEraser-offset" : "";
29
+ $: pencilEraserSize3ImageOffset = pencilEraserSize === 3 ? "netless-window-manager-pencilEraser-3-offset" : "";
24
30
 
25
31
  const computedAvatarStyle = () => {
26
32
  return Object.entries({
@@ -36,11 +42,12 @@
36
42
  </script>
37
43
 
38
44
  <div
39
- class="netless-window-manager-cursor-mid"
45
+ class={"netless-window-manager-cursor-mid" + (custom ? " netless-window-manager-cursor-custom" : "")}
40
46
  style="transform: translateX({x}px) translateY({y}px);display: {display}"
41
- >
47
+ data-cursor-uid={uid}
48
+ >
42
49
  {#if !isLaserPointer}
43
- <div class="netless-window-manager-cursor-name">
50
+ <div class="netless-window-manager-cursor-name {offset} {pencilEraserSize3ImageOffset}">
44
51
  <div
45
52
  class={theme}
46
53
  style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
@@ -63,6 +70,10 @@
63
70
  </div>
64
71
  {/if}
65
72
  <div class="cursor-image-wrapper">
66
- <img class="netless-window-manager-cursor-{appliance}-image" {src} alt={appliance} />
73
+ <img
74
+ class="netless-window-manager-cursor-{appliance}-image {pencilEraserSize3ImageOffset}"
75
+ {src}
76
+ alt={appliance}
77
+ />
67
78
  </div>
68
79
  </div>
@@ -0,0 +1,21 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+
3
+ declare class Cursor extends SvelteComponentTyped<{
4
+ readonly cursorName: string;
5
+ readonly tagName?: string;
6
+ readonly backgroundColor: string;
7
+ readonly appliance: string;
8
+ readonly x: number;
9
+ readonly y: number;
10
+ readonly src?: string;
11
+ readonly visible: boolean;
12
+ readonly avatar: string;
13
+ readonly theme: string;
14
+ readonly color: string;
15
+ readonly cursorTagBackgroundColor: string;
16
+ readonly opacity: number;
17
+ readonly pencilEraserSize?: number;
18
+ readonly custom?: boolean;
19
+ }> {}
20
+
21
+ export default Cursor;
@@ -1,12 +1,15 @@
1
- import App from "./Cursor.svelte";
2
- import { ApplianceNames } from "white-web-sdk";
3
- import { findMemberByUid } from "../Helper";
4
- import { omit } from "lodash";
5
- import type { Position } from "../AttributesDelegate";
6
1
  import type { RoomMember } from "white-web-sdk";
7
- import type { CursorManager } from "./index";
8
- import type { SvelteComponent } from "svelte";
2
+ import type { CursorOptions } from "../index";
9
3
  import type { AppManager } from "../AppManager";
4
+ import type { Position } from "../AttributesDelegate";
5
+ import type { CursorManager } from "./index";
6
+
7
+ import { omit } from "lodash";
8
+ import { ApplianceNames } from "white-web-sdk";
9
+
10
+ import { findMemberByUid } from "../Helper";
11
+ import App from "./Cursor.svelte";
12
+ import { remoteIcon } from "./icons2";
10
13
 
11
14
  export type Payload = {
12
15
  [key: string]: any;
@@ -15,7 +18,8 @@ export type Payload = {
15
18
  export class Cursor {
16
19
  private member?: RoomMember;
17
20
  private timer?: number;
18
- private component?: SvelteComponent;
21
+ private component?: App;
22
+ private style: CursorOptions["style"] & string = "default";
19
23
 
20
24
  constructor(
21
25
  private manager: AppManager,
@@ -26,6 +30,7 @@ export class Cursor {
26
30
  this.updateMember();
27
31
  this.createCursor();
28
32
  this.autoHidden();
33
+ this.setStyle(cursorManager.style);
29
34
  }
30
35
 
31
36
  public move = (position: Position) => {
@@ -46,6 +51,16 @@ export class Cursor {
46
51
  }
47
52
  };
48
53
 
54
+ public setStyle = (style: typeof this.style) => {
55
+ this.style = style;
56
+ if (this.component) {
57
+ this.component.$set({
58
+ src: this.getIcon(),
59
+ custom: this.isCustomIcon(),
60
+ });
61
+ }
62
+ };
63
+
49
64
  public leave = () => {
50
65
  this.hide();
51
66
  };
@@ -56,6 +71,10 @@ export class Cursor {
56
71
  if (point) {
57
72
  let translateX = point.x - 2;
58
73
  let translateY = point.y - 18;
74
+ if (this.isCustomIcon()) {
75
+ translateX -= 11;
76
+ translateY += 4;
77
+ }
59
78
  if (type === "app") {
60
79
  const wrapperRect = this.cursorManager.wrapperRect;
61
80
  if (wrapperRect) {
@@ -80,6 +99,11 @@ export class Cursor {
80
99
  return `rgb(${rgb})`;
81
100
  }
82
101
 
102
+ public get memberColorHex(): string {
103
+ const [r, g, b] = this.member?.memberState?.strokeColor || [236, 52, 85];
104
+ return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
105
+ }
106
+
83
107
  private get payload(): Payload | undefined {
84
108
  return this.member?.payload;
85
109
  }
@@ -140,11 +164,13 @@ export class Cursor {
140
164
 
141
165
  private initProps() {
142
166
  return {
167
+ uid: this.memberId,
143
168
  x: 0,
144
169
  y: 0,
145
170
  appliance: this.memberApplianceName as string,
146
171
  avatar: this.memberAvatar,
147
172
  src: this.getIcon(),
173
+ custom: this.isCustomIcon(),
148
174
  visible: false,
149
175
  backgroundColor: this.memberColor,
150
176
  cursorName: this.memberCursorName,
@@ -153,15 +179,53 @@ export class Cursor {
153
179
  cursorTagBackgroundColor: this.memberCursorTagBackgroundColor,
154
180
  opacity: this.memberOpacity,
155
181
  tagName: this.memberTagName,
182
+ pencilEraserSize: this.member?.memberState.pencilEraserSize,
156
183
  };
157
184
  }
158
185
 
159
- private getIcon() {
160
- if (this.member) {
161
- const icons = this.cursorManager.applianceIcons;
162
- const applianceSrc = icons[this.memberApplianceName || ApplianceNames.shape];
163
- return applianceSrc || icons[ApplianceNames.shape];
186
+ private getIcon(): string | undefined {
187
+ if (!this.member) return;
188
+
189
+ const { memberApplianceName, memberColorHex } = this;
190
+ const { userApplianceIcons, applianceIcons } = this.cursorManager;
191
+
192
+ let iconsKey: string | undefined = this.memberApplianceName;
193
+ if (iconsKey === ApplianceNames.pencilEraser) {
194
+ iconsKey = `${iconsKey}${this.member?.memberState.pencilEraserSize || 1}`;
195
+ }
196
+
197
+ const userApplianceSrc = iconsKey && userApplianceIcons[iconsKey];
198
+ if (userApplianceSrc) return userApplianceSrc;
199
+
200
+ if (this.style === "custom" && memberApplianceName) {
201
+ const customApplianceSrc = remoteIcon(memberApplianceName, memberColorHex);
202
+ if (customApplianceSrc) return customApplianceSrc;
203
+ }
204
+
205
+ const applianceSrc = applianceIcons[iconsKey || ApplianceNames.shape];
206
+ return applianceSrc || applianceIcons[ApplianceNames.shape];
207
+ }
208
+
209
+ private isCustomIcon(): boolean {
210
+ if (!this.member) return false;
211
+
212
+ const { memberApplianceName, memberColorHex } = this;
213
+ const { userApplianceIcons } = this.cursorManager;
214
+
215
+ let iconsKey: string | undefined = this.memberApplianceName;
216
+ if (iconsKey === ApplianceNames.pencilEraser) {
217
+ iconsKey = `${iconsKey}${this.member?.memberState.pencilEraserSize || 1}`;
164
218
  }
219
+
220
+ const userApplianceSrc = iconsKey && userApplianceIcons[iconsKey];
221
+ if (userApplianceSrc) return false;
222
+
223
+ if (this.style === "custom" && memberApplianceName) {
224
+ const customApplianceSrc = remoteIcon(memberApplianceName, memberColorHex);
225
+ if (customApplianceSrc) return true;
226
+ }
227
+
228
+ return false;
165
229
  }
166
230
 
167
231
  public updateMember() {
@@ -5,6 +5,9 @@ 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
7
  import laser from "../image/laser-pointer-cursor.svg";
8
+ import pencilEraser1 from "../image/pencil-eraser-1.svg";
9
+ import pencilEraser2 from "../image/pencil-eraser-2.svg";
10
+ import pencilEraser3 from "../image/pencil-eraser-3.svg";
8
11
 
9
12
  export const ApplianceMap: {
10
13
  [key: string]: string;
@@ -15,4 +18,7 @@ export const ApplianceMap: {
15
18
  [ApplianceNames.shape]: shape,
16
19
  [ApplianceNames.text]: text,
17
20
  [ApplianceNames.laserPointer]: laser,
21
+ ["pencilEraser1"]: pencilEraser1,
22
+ ["pencilEraser2"]: pencilEraser2,
23
+ ["pencilEraser3"]: pencilEraser3,
18
24
  };