@netless/window-manager 1.0.0-canary.9 → 1.0.1

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 (132) 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 -40
  5. package/dist/index.js +62 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/{index.es.js → index.mjs} +7954 -5445
  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 +126 -113
  12. package/docs/app-context.md +248 -209
  13. package/docs/basic.md +25 -26
  14. package/docs/camera.md +21 -21
  15. package/docs/cn/advanced.md +137 -0
  16. package/docs/cn/api.md +311 -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 +105 -73
  35. package/src/App/AppPageStateImpl.ts +6 -25
  36. package/src/App/AppProxy.ts +41 -166
  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 +88 -77
  45. package/src/AttributesDelegate.ts +42 -22
  46. package/src/BoxEmitter.ts +12 -6
  47. package/src/BoxManager.ts +128 -108
  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/index.ts +1 -1
  58. package/src/PageState.ts +6 -5
  59. package/src/ReconnectRefresher.ts +9 -4
  60. package/src/RedoUndo.ts +3 -3
  61. package/src/Register/index.ts +22 -17
  62. package/src/Register/loader.ts +26 -22
  63. package/src/Register/storage.ts +13 -13
  64. package/src/Utils/Common.ts +18 -14
  65. package/src/Utils/Reactive.ts +26 -25
  66. package/src/Utils/RoomHacker.ts +4 -4
  67. package/src/Utils/error.ts +0 -1
  68. package/src/View/IframeBridge.ts +680 -0
  69. package/src/View/MainView.ts +127 -53
  70. package/src/callback.ts +21 -1
  71. package/src/constants.ts +0 -2
  72. package/src/image/pencil-eraser-1.svg +3 -0
  73. package/src/image/pencil-eraser-2.svg +3 -0
  74. package/src/image/pencil-eraser-3.svg +3 -0
  75. package/src/index.ts +220 -83
  76. package/src/style.css +27 -10
  77. package/src/typings.ts +20 -10
  78. package/.prettierignore +0 -7
  79. package/.prettierrc.json +0 -9
  80. package/CHANGELOG.md +0 -196
  81. package/__mocks__/white-web-sdk.ts +0 -50
  82. package/dist/App/AppContext.d.ts +0 -76
  83. package/dist/App/AppPageStateImpl.d.ts +0 -21
  84. package/dist/App/AppProxy.d.ts +0 -86
  85. package/dist/App/AppViewSync.d.ts +0 -11
  86. package/dist/App/MagixEvent/index.d.ts +0 -29
  87. package/dist/App/Storage/StorageEvent.d.ts +0 -8
  88. package/dist/App/Storage/index.d.ts +0 -39
  89. package/dist/App/Storage/typings.d.ts +0 -22
  90. package/dist/App/Storage/utils.d.ts +0 -5
  91. package/dist/App/WhiteboardView.d.ts +0 -22
  92. package/dist/App/index.d.ts +0 -3
  93. package/dist/AppListener.d.ts +0 -21
  94. package/dist/AppManager.d.ts +0 -107
  95. package/dist/AttributesDelegate.d.ts +0 -80
  96. package/dist/BoxEmitter.d.ts +0 -34
  97. package/dist/BoxManager.d.ts +0 -99
  98. package/dist/BuiltinApps.d.ts +0 -5
  99. package/dist/Cursor/Cursor.d.ts +0 -39
  100. package/dist/Cursor/icons.d.ts +0 -3
  101. package/dist/Cursor/index.d.ts +0 -46
  102. package/dist/Helper.d.ts +0 -17
  103. package/dist/InternalEmitter.d.ts +0 -39
  104. package/dist/Page/PageController.d.ts +0 -21
  105. package/dist/Page/index.d.ts +0 -3
  106. package/dist/PageState.d.ts +0 -9
  107. package/dist/ReconnectRefresher.d.ts +0 -24
  108. package/dist/RedoUndo.d.ts +0 -18
  109. package/dist/Register/index.d.ts +0 -28
  110. package/dist/Register/loader.d.ts +0 -4
  111. package/dist/Register/storage.d.ts +0 -8
  112. package/dist/Utils/AppCreateQueue.d.ts +0 -15
  113. package/dist/Utils/Common.d.ts +0 -23
  114. package/dist/Utils/Reactive.d.ts +0 -6
  115. package/dist/Utils/RoomHacker.d.ts +0 -3
  116. package/dist/Utils/error.d.ts +0 -27
  117. package/dist/Utils/log.d.ts +0 -1
  118. package/dist/View/CameraSynchronizer.d.ts +0 -16
  119. package/dist/View/MainView.d.ts +0 -47
  120. package/dist/View/ViewManager.d.ts +0 -13
  121. package/dist/callback.d.ts +0 -24
  122. package/dist/constants.d.ts +0 -49
  123. package/dist/index.cjs.js +0 -46
  124. package/dist/index.umd.js +0 -46
  125. package/dist/typings.d.ts +0 -82
  126. package/jest.config.js +0 -27
  127. package/pnpm-lock.yaml +0 -6302
  128. package/src/App/AppViewSync.ts +0 -68
  129. package/src/App/WhiteboardView.ts +0 -83
  130. package/src/View/CameraSynchronizer.ts +0 -56
  131. package/vite.config.js +0 -51
  132. /package/docs/{qickstart.md → cn/quickstart.md} +0 -0
package/src/index.ts CHANGED
@@ -2,11 +2,12 @@ import pRetry from "p-retry";
2
2
  import { AppManager } from "./AppManager";
3
3
  import { appRegister } from "./Register";
4
4
  import { callbacks } from "./callback";
5
- import { checkVersion, setupWrapper } from "./Helper";
5
+ import { checkVersion, createInvisiblePlugin, setupWrapper } from "./Helper";
6
+ import { ContainerResizeObserver } from "./ContainerResizeObserver";
6
7
  import { createBoxManager } from "./BoxManager";
7
8
  import { CursorManager } from "./Cursor";
8
9
  import { DEFAULT_CONTAINER_RATIO, Events, INIT_DIR, ROOT_DIR } from "./constants";
9
- import { emitter } from "./InternalEmitter";
10
+ import { internalEmitter } from "./InternalEmitter";
10
11
  import { Fields } from "./AttributesDelegate";
11
12
  import { initDb } from "./Register/storage";
12
13
  import { InvisiblePlugin, isPlayer, isRoom, RoomPhase, ViewMode } from "white-web-sdk";
@@ -29,7 +30,7 @@ import {
29
30
  } from "./Utils/Common";
30
31
  import type { TELE_BOX_STATE, BoxManager } from "./BoxManager";
31
32
  import * as Errors from "./Utils/error";
32
- import type { Apps, Position , ICamera, ISize } from "./AttributesDelegate";
33
+ import type { Apps, Position } from "./AttributesDelegate";
33
34
  import type {
34
35
  Displayer,
35
36
  SceneDefinition,
@@ -54,6 +55,9 @@ import type { PublicEvent } from "./callback";
54
55
  import type Emittery from "emittery";
55
56
  import type { PageController, AddPageParams, PageState } from "./Page";
56
57
  import { boxEmitter } from "./BoxEmitter";
58
+ import { IframeBridge } from "./View/IframeBridge";
59
+ import { setOptions } from "@netless/app-media-player";
60
+ export * from "./View/IframeBridge";
57
61
 
58
62
  export type WindowMangerAttributes = {
59
63
  modelValue?: string;
@@ -104,8 +108,6 @@ export type AppSyncAttributes = {
104
108
  isDynamicPPT?: boolean;
105
109
  fullPath?: string;
106
110
  createdAt?: number;
107
- camera?: ICamera;
108
- size?: ISize;
109
111
  };
110
112
 
111
113
  export type AppInitState = {
@@ -124,33 +126,53 @@ export type AppInitState = {
124
126
 
125
127
  export type CursorMovePayload = { uid: string; state?: "leave"; position: Position };
126
128
 
129
+ export type CursorOptions = {
130
+ /**
131
+ * If `"custom"`, it will render the pencil / eraser cursor as a circle and shapes cursor as a cross.
132
+ *
133
+ * @default "default"
134
+ */
135
+ style?: "default" | "custom";
136
+ };
137
+
127
138
  export type MountParams = {
128
139
  room: Room | Player;
129
140
  container?: HTMLElement;
130
141
  /** 白板高宽比例, 默认为 9 / 16 */
131
142
  containerSizeRatio?: number;
132
- /** 是否高亮显示同步区域, 默认为 true */
133
- highlightStage?: boolean;
143
+ /** @deprecated 显示 PS 透明背景,默认 true */
144
+ chessboard?: boolean;
134
145
  collectorContainer?: HTMLElement;
135
146
  collectorStyles?: Partial<CSSStyleDeclaration>;
136
147
  overwriteStyles?: string;
137
148
  cursor?: boolean;
149
+ cursorOptions?: CursorOptions;
138
150
  debug?: boolean;
139
151
  disableCameraTransform?: boolean;
140
152
  prefersColorScheme?: TeleBoxColorScheme;
141
153
  applianceIcons?: ApplianceIcons;
154
+ fullscreen?: boolean;
155
+ polling?: boolean;
156
+ supportAppliancePlugin?: boolean;
142
157
  };
143
158
 
144
- export const reconnectRefresher = new ReconnectRefresher({ emitter });
159
+ export const reconnectRefresher = new ReconnectRefresher({ emitter: internalEmitter });
145
160
 
146
- export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> implements PageController {
161
+ export class WindowManager
162
+ extends InvisiblePlugin<WindowMangerAttributes, any>
163
+ implements PageController
164
+ {
147
165
  public static kind = "WindowManager";
148
166
  public static displayer: Displayer;
167
+ public static wrapper?: HTMLElement;
168
+ public static sizer?: HTMLElement;
149
169
  public static playground?: HTMLElement;
150
170
  public static container?: HTMLElement;
151
171
  public static debug = false;
152
172
  public static containerSizeRatio = DEFAULT_CONTAINER_RATIO;
173
+ public static supportAppliancePlugin?: boolean;
153
174
  private static isCreated = false;
175
+ private static _resolve = (_manager: WindowManager) => void 0;
154
176
 
155
177
  public version = __APP_VERSION__;
156
178
  public dependencies = __APP_DEPENDENCIES__;
@@ -164,10 +186,14 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
164
186
  public viewMode = ViewMode.Broadcaster;
165
187
  public isReplay = isPlayer(this.displayer);
166
188
  private _pageState?: PageStateImpl;
189
+ private _fullscreen?: boolean;
190
+ private _cursorUIDs: string[] = [];
191
+ private _cursorUIDsStyleDOM?: HTMLStyleElement;
167
192
 
168
193
  private boxManager?: BoxManager;
169
194
  private static params?: MountParams;
170
195
 
196
+ private containerResizeObserver?: ContainerResizeObserver;
171
197
  public containerSizeRatio = WindowManager.containerSizeRatio;
172
198
 
173
199
  constructor(context: InvisiblePluginContext) {
@@ -176,9 +202,14 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
176
202
  (window as any).NETLESS_DEPS = __APP_DEPENDENCIES__;
177
203
  }
178
204
 
205
+ public static onCreate(manager: WindowManager) {
206
+ WindowManager._resolve(manager);
207
+ }
208
+
179
209
  public static async mount(params: MountParams): Promise<WindowManager> {
180
210
  const room = params.room;
181
211
  WindowManager.container = params.container;
212
+ WindowManager.supportAppliancePlugin = params.supportAppliancePlugin;
182
213
  const containerSizeRatio = params.containerSizeRatio;
183
214
  const debug = params.debug;
184
215
 
@@ -202,6 +233,9 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
202
233
  }
203
234
 
204
235
  this.debug = Boolean(debug);
236
+ if (this.debug) {
237
+ setOptions({ verbose: true });
238
+ }
205
239
  log("Already insert room", manager);
206
240
 
207
241
  if (isRoom(this.displayer)) {
@@ -211,13 +245,14 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
211
245
  } else {
212
246
  await pRetry(
213
247
  async count => {
214
- manager = (await room.getInvisiblePlugin(WindowManager.kind)) as WindowManager;
248
+ manager = room.getInvisiblePlugin(WindowManager.kind) as WindowManager;
215
249
  if (!manager) {
216
250
  log(`manager is empty. retrying ${count}`);
217
251
  throw new Error();
218
252
  }
219
253
  },
220
- { retries: 10 }
254
+ // 1s, 2s, 4s, 5s, 5s, 5s, 5s, 5s, 5s
255
+ { retries: 10, maxTimeout: 5000 } as any
221
256
  );
222
257
  }
223
258
 
@@ -230,26 +265,26 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
230
265
  }
231
266
  await manager.ensureAttributes();
232
267
 
268
+ manager._fullscreen = params.fullscreen;
233
269
  manager.appManager = new AppManager(manager);
270
+ manager.appManager.polling = params.polling || false;
234
271
  manager._pageState = new PageStateImpl(manager.appManager);
235
- manager.cursorManager = new CursorManager(manager.appManager, Boolean(cursor), params.applianceIcons);
272
+ manager.cursorManager = new CursorManager(
273
+ manager.appManager,
274
+ Boolean(cursor),
275
+ params.cursorOptions,
276
+ params.applianceIcons
277
+ );
236
278
  if (containerSizeRatio) {
237
279
  manager.containerSizeRatio = containerSizeRatio;
238
280
  }
239
- manager.boxManager = createBoxManager(manager, callbacks, emitter, boxEmitter, {
240
- collectorContainer: params.collectorContainer,
241
- collectorStyles: params.collectorStyles,
242
- prefersColorScheme: params.prefersColorScheme,
243
- stageRatio: params.containerSizeRatio,
244
- highlightStage: params.highlightStage
245
- });
246
- manager.appManager?.setBoxManager(manager.boxManager);
281
+
247
282
  if (params.container) {
248
283
  manager.bindContainer(params.container);
249
284
  }
250
285
 
251
286
  replaceRoomFunction(room, manager);
252
- emitter.emit("onCreated");
287
+ internalEmitter.emit("onCreated");
253
288
  WindowManager.isCreated = true;
254
289
  try {
255
290
  await initDb();
@@ -260,48 +295,44 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
260
295
  return manager;
261
296
  }
262
297
 
263
- private static async initManager(room: Room): Promise<WindowManager> {
264
- let manager = room.getInvisiblePlugin(WindowManager.kind) as WindowManager;
265
- if (!manager) {
266
- if (isRoom(room)) {
267
- if (room.isWritable === false) {
268
- try {
269
- await room.setWritable(true);
270
- } catch (error) {
271
- throw new Error("[WindowManger]: room must be switched to be writable");
272
- }
273
- manager = (await room.createInvisiblePlugin(
274
- WindowManager,
275
- {}
276
- )) as WindowManager;
277
- manager.ensureAttributes();
278
- await wait(500);
279
- await room.setWritable(false);
280
- } else {
281
- manager = (await room.createInvisiblePlugin(
282
- WindowManager,
283
- {}
284
- )) as WindowManager;
285
- }
286
- }
287
- }
288
- return manager;
298
+ private static initManager(room: Room): Promise<WindowManager | undefined> {
299
+ return createInvisiblePlugin(room);
289
300
  }
290
301
 
291
302
  private static initContainer(
303
+ manager: WindowManager,
292
304
  container: HTMLElement,
293
- overwriteStyles: string | undefined
305
+ params: {
306
+ chessboard?: boolean;
307
+ overwriteStyles?: string;
308
+ fullscreen?: boolean;
309
+ }
294
310
  ) {
311
+ const { chessboard, overwriteStyles, fullscreen } = params;
295
312
  if (!WindowManager.container) {
296
313
  WindowManager.container = container;
297
314
  }
298
- const { playground, mainViewElement } = setupWrapper(container);
315
+ const { playground, wrapper, sizer, mainViewElement } = setupWrapper(container);
299
316
  WindowManager.playground = playground;
317
+ if (chessboard) {
318
+ sizer.classList.add("netless-window-manager-chess-sizer");
319
+ }
320
+ if (fullscreen) {
321
+ sizer.classList.add("netless-window-manager-fullscreen");
322
+ }
300
323
  if (overwriteStyles) {
301
324
  const style = document.createElement("style");
302
325
  style.textContent = overwriteStyles;
303
326
  playground.appendChild(style);
304
327
  }
328
+ manager.containerResizeObserver = ContainerResizeObserver.create(
329
+ playground,
330
+ sizer,
331
+ wrapper,
332
+ internalEmitter
333
+ );
334
+ WindowManager.wrapper = wrapper;
335
+ WindowManager.sizer = sizer;
305
336
  return mainViewElement;
306
337
  }
307
338
 
@@ -320,20 +351,24 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
320
351
  } else {
321
352
  if (WindowManager.params) {
322
353
  const params = WindowManager.params;
323
- const mainViewElement = WindowManager.initContainer(
324
- container,
325
- params.overwriteStyles
326
- );
327
- if (this.boxManager && WindowManager.playground) {
328
- this.boxManager.setRoot(WindowManager.playground);
354
+ const mainViewElement = WindowManager.initContainer(this, container, params);
355
+ if (this.boxManager) {
356
+ this.boxManager.destroy();
329
357
  }
358
+ const boxManager = createBoxManager(this, callbacks, internalEmitter, boxEmitter, {
359
+ collectorContainer: params.collectorContainer,
360
+ collectorStyles: params.collectorStyles,
361
+ prefersColorScheme: params.prefersColorScheme,
362
+ });
363
+ this.boxManager = boxManager;
364
+ this.appManager?.setBoxManager(boxManager);
330
365
  this.bindMainView(mainViewElement, params.disableCameraTransform);
331
- if (WindowManager.playground) {
332
- this.cursorManager?.setupWrapper(WindowManager.playground);
366
+ if (WindowManager.wrapper) {
367
+ this.cursorManager?.setupWrapper(WindowManager.wrapper);
333
368
  }
334
369
  }
335
370
  }
336
- emitter.emit("updateManagerRect");
371
+ internalEmitter.emit("updateManagerRect");
337
372
  this.appManager?.refresh();
338
373
  this.appManager?.resetMaximized();
339
374
  this.appManager?.resetMinimized();
@@ -343,7 +378,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
343
378
 
344
379
  public bindCollectorContainer(container: HTMLElement) {
345
380
  if (WindowManager.isCreated && this.boxManager) {
346
- this.boxManager.setCollector(container);
381
+ this.boxManager.setCollectorContainer(container);
347
382
  } else {
348
383
  if (WindowManager.params) {
349
384
  WindowManager.params.collectorContainer = container;
@@ -354,9 +389,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
354
389
  /**
355
390
  * 注册插件
356
391
  */
357
- public static register<AppOptions = any, SetupResult = any, Attributes = any>(
358
- params: RegisterParams<AppOptions, SetupResult, Attributes>
359
- ): Promise<void> {
392
+ public static register(params: RegisterParams<any, any, any>): Promise<void> {
360
393
  return appRegister.register(params);
361
394
  }
362
395
 
@@ -375,7 +408,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
375
408
  // 移除根目录时需要做一些异步的释放操作 addApp 需要等待释放完成才可以继续添加
376
409
  if (this.appManager.rootDirRemoving) {
377
410
  return new Promise((resolve, reject) => {
378
- emitter.once("rootDirRemoved").then(async () => {
411
+ internalEmitter.once("rootDirRemoved").then(async () => {
379
412
  try {
380
413
  const appId = await this._addApp(params);
381
414
  resolve(appId);
@@ -397,6 +430,9 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
397
430
  if (!params.kind || typeof params.kind !== "string") {
398
431
  throw new Errors.ParamsInvalidError();
399
432
  }
433
+ if (params.src && typeof params.src === "string") {
434
+ appRegister.register({ kind: params.kind, src: params.src });
435
+ }
400
436
  const appImpl = await appRegister.appClasses.get(params.kind)?.();
401
437
  if (appImpl && appImpl.config?.singleton) {
402
438
  if (this.appManager.appProxies.has(params.kind)) {
@@ -429,11 +465,12 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
429
465
  for (const appId of apps) {
430
466
  const appScenePath = appManager.store.getAppScenePath(appId);
431
467
  if (appScenePath && appScenePath === scenePath) {
432
- console.warn(`[WindowManager]: ScenePath ${scenePath} Already opened`);
468
+ console.warn(`[WindowManager]: ScenePath "${scenePath}" already opened`);
433
469
  if (this.boxManager) {
434
470
  const topBox = this.boxManager.getTopBox();
435
471
  if (topBox) {
436
472
  this.boxManager.setZIndex(appId, topBox.zIndex + 1, false);
473
+ this.boxManager.focusBox({ appId }, false);
437
474
  }
438
475
  }
439
476
  return;
@@ -506,15 +543,16 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
506
543
  }
507
544
 
508
545
  public async jumpPage(index: number): Promise<boolean> {
509
- if (!this.appManager) {
510
- return false;
511
- }
512
- if (index < 0 || index >= this.pageState.length) {
513
- console.warn(`[WindowManager]: index ${index} out of range`);
546
+ if (this.appManager) {
547
+ if (index < 0 || index >= this.pageState.length) {
548
+ console.warn(`[WindowManager]: index ${index} out of range`);
549
+ return false;
550
+ }
551
+ await this.appManager.setMainViewSceneIndex(index);
552
+ return true;
553
+ } else {
514
554
  return false;
515
555
  }
516
- await this.appManager.setMainViewSceneIndex(index);
517
- return true;
518
556
  }
519
557
 
520
558
  public async addPage(params?: AddPageParams): Promise<void> {
@@ -546,7 +584,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
546
584
  console.warn(`[WindowManager]: index ${index} out of range`);
547
585
  return false;
548
586
  }
549
- return this.appManager.removeSceneByIndex(needRemoveIndex);;
587
+ return this.appManager.removeSceneByIndex(needRemoveIndex);
550
588
  } else {
551
589
  return false;
552
590
  }
@@ -572,7 +610,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
572
610
  public setReadonly(readonly: boolean): void {
573
611
  this.readonly = readonly;
574
612
  this.boxManager?.setReadonly(readonly);
575
- emitter.emit("setReadonly", readonly);
613
+ internalEmitter.emit("setReadonly", readonly);
576
614
  }
577
615
 
578
616
  /**
@@ -589,12 +627,24 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
589
627
  addEmitterOnceListener(`destroy-${kind}`, listener);
590
628
  }
591
629
 
630
+ /**
631
+ * app 本地自定义事件回调
632
+ *
633
+ * 返回一个用于撤销此监听的函数
634
+ */
635
+ public onAppEvent(
636
+ kind: string,
637
+ listener: (args: { kind: string; appId: string; type: string; value: any }) => void
638
+ ): () => void {
639
+ return internalEmitter.on(`custom-${kind}` as any, listener);
640
+ }
641
+
592
642
  /**
593
643
  * 设置 ViewMode
594
644
  */
595
645
  public setViewMode(mode: ViewMode): void {
596
- if (mode === ViewMode.Broadcaster) {
597
- if (this.canOperate) {
646
+ if (mode === ViewMode.Broadcaster || mode === ViewMode.Follower) {
647
+ if (this.canOperate && mode === ViewMode.Broadcaster) {
598
648
  this.appManager?.mainViewProxy.setCameraAndSize();
599
649
  }
600
650
  this.appManager?.mainViewProxy.start();
@@ -603,6 +653,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
603
653
  this.appManager?.mainViewProxy.stop();
604
654
  }
605
655
  this.viewMode = mode;
656
+ this.appManager?.mainViewProxy.setViewMode(mode);
606
657
  }
607
658
 
608
659
  public setBoxState(boxState: TeleBoxState): void {
@@ -634,6 +685,35 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
634
685
  this.boxManager?.setMinimized(minimized, false);
635
686
  }
636
687
 
688
+ public setFullscreen(fullscreen: boolean): void {
689
+ if (this._fullscreen !== fullscreen) {
690
+ this._fullscreen = fullscreen;
691
+ WindowManager.sizer?.classList.toggle("netless-window-manager-fullscreen", fullscreen);
692
+ callbacks.emit("fullscreenChange", fullscreen);
693
+ }
694
+ }
695
+
696
+ public get cursorUIDs(): string[] {
697
+ return this._cursorUIDs;
698
+ }
699
+
700
+ public setCursorUIDs(cursorUIDs?: string[] | null): void {
701
+ this._cursorUIDs = cursorUIDs || [];
702
+ if (this._cursorUIDs.length === 0) {
703
+ this._cursorUIDsStyleDOM?.remove();
704
+ } else {
705
+ if (!this._cursorUIDsStyleDOM) {
706
+ this._cursorUIDsStyleDOM = document.createElement("style");
707
+ }
708
+ WindowManager.playground?.appendChild(this._cursorUIDsStyleDOM);
709
+ let style = "[data-cursor-uid] { display: none }";
710
+ for (const uid of this._cursorUIDs) {
711
+ style += `\n[data-cursor-uid="${uid}"] { display: flex }`;
712
+ }
713
+ this._cursorUIDsStyleDOM.textContent = style;
714
+ }
715
+ }
716
+
637
717
  public get mainView(): View {
638
718
  if (this.appManager) {
639
719
  return this.appManager.mainViewProxy.view;
@@ -690,6 +770,27 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
690
770
  return this.appManager?.focusApp?.view || this.mainView;
691
771
  }
692
772
 
773
+ public get polling(): boolean {
774
+ return this.appManager?.polling || false;
775
+ }
776
+
777
+ public set polling(b: boolean) {
778
+ if (this.appManager) {
779
+ this.appManager.polling = b;
780
+ }
781
+ }
782
+
783
+ public get cursorStyle(): "default" | "custom" {
784
+ return this.cursorManager?.style || "default";
785
+ }
786
+
787
+ public set cursorStyle(value: "default" | "custom") {
788
+ if (!this.cursorManager) {
789
+ throw new Error("[WindowManager]: cursor is not enabled, please set { cursor: true }.");
790
+ }
791
+ this.cursorManager.style = value;
792
+ }
793
+
693
794
  public get mainViewSceneIndex(): number {
694
795
  return this._pageState?.index || 0;
695
796
  }
@@ -735,6 +836,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
735
836
  }
736
837
  }
737
838
 
839
+ public get fullscreen(): boolean {
840
+ return Boolean(this._fullscreen);
841
+ }
842
+
738
843
  /**
739
844
  * 查询所有的 App
740
845
  */
@@ -756,6 +861,18 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
756
861
  return this.appManager?.closeApp(appId);
757
862
  }
758
863
 
864
+ /**
865
+ * 切换 focus 到指定的 app, 并且把这个 app 放到最前面
866
+ */
867
+ public focusApp(appId: string) {
868
+ const box = this.boxManager?.getBox(appId);
869
+ if (box) {
870
+ this.boxManager?.focusBox({ appId }, false);
871
+ // 1.0 版本这里会有正式的 api
872
+ (this.boxManager?.teleBoxManager as any).makeBoxTop(box, false);
873
+ }
874
+ }
875
+
759
876
  public moveCamera(
760
877
  camera: Partial<Camera> & { animationMode?: AnimationMode | undefined }
761
878
  ): void {
@@ -799,14 +916,19 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
799
916
  }
800
917
 
801
918
  private _destroy() {
919
+ this.containerResizeObserver?.disconnect();
802
920
  this.appManager?.destroy();
803
921
  this.cursorManager?.destroy();
804
922
  WindowManager.container = undefined;
923
+ WindowManager.wrapper = undefined;
924
+ WindowManager.sizer = undefined;
805
925
  WindowManager.isCreated = false;
806
926
  if (WindowManager.playground) {
807
927
  WindowManager.playground.parentNode?.removeChild(WindowManager.playground);
808
928
  }
809
929
  WindowManager.params = undefined;
930
+ this._iframeBridge?.destroy();
931
+ this._iframeBridge = undefined;
810
932
  log("Destroyed");
811
933
  }
812
934
 
@@ -849,6 +971,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
849
971
  }
850
972
 
851
973
  public cleanCurrentScene(): void {
974
+ log("clean current scene");
852
975
  this.focusedView?.cleanCurrentScene();
853
976
  }
854
977
 
@@ -897,26 +1020,28 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
897
1020
  }
898
1021
 
899
1022
  public refresh() {
900
- this._refresh();
901
- this.appManager?.dispatchInternalEvent(Events.Refresh);
1023
+ this._refresh();
1024
+ this.appManager?.dispatchInternalEvent(Events.Refresh);
902
1025
  }
903
-
904
- /** @inner */
1026
+
1027
+ /** @internal */
905
1028
  public _refresh() {
906
1029
  this.appManager?.mainViewProxy.rebind();
907
1030
  if (WindowManager.container) {
908
1031
  this.bindContainer(WindowManager.container);
909
1032
  }
910
- this.appManager?.refresher?.refresh();
1033
+ this.appManager?.refresher.refresh();
911
1034
  }
912
1035
 
913
1036
  public setContainerSizeRatio(ratio: number) {
914
- if (!isNumber(ratio)) {
915
- throw new Error(`[WindowManager]: updateContainerSizeRatio error, ratio must be a number. but got ${ratio}`);
1037
+ if (!isNumber(ratio) || !(ratio > 0)) {
1038
+ throw new Error(
1039
+ `[WindowManager]: updateContainerSizeRatio error, ratio must be a positive number. but got ${ratio}`
1040
+ );
916
1041
  }
917
1042
  WindowManager.containerSizeRatio = ratio;
918
1043
  this.containerSizeRatio = ratio;
919
- emitter.emit("containerSizeRatioUpdate", ratio);
1044
+ internalEmitter.emit("containerSizeRatioUpdate", ratio);
920
1045
  }
921
1046
 
922
1047
  private isDynamicPPT(scenes: SceneDefinition[]) {
@@ -944,7 +1069,19 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
944
1069
  if (!this.attributes[Fields.Registered]) {
945
1070
  this.safeSetAttributes({ [Fields.Registered]: {} });
946
1071
  }
1072
+ if (!this.attributes[Fields.IframeBridge]) {
1073
+ this.safeSetAttributes({ [Fields.IframeBridge]: {} });
1074
+ }
1075
+ }
1076
+ }
1077
+
1078
+ private _iframeBridge?: IframeBridge;
1079
+ public getIframeBridge() {
1080
+ if (!this.appManager) {
1081
+ throw new Error("[WindowManager]: should call getIframeBridge() after await mount()");
947
1082
  }
1083
+ this._iframeBridge || (this._iframeBridge = new IframeBridge(this, this.appManager));
1084
+ return this._iframeBridge;
948
1085
  }
949
1086
  }
950
1087
 
package/src/style.css CHANGED
@@ -53,7 +53,6 @@
53
53
  height: 100%;
54
54
  }
55
55
 
56
-
57
56
  .netless-window-manager-cursor-pencil-image {
58
57
  width: 26px;
59
58
  height: 26px;
@@ -122,9 +121,10 @@
122
121
  left: 0;
123
122
  top: 0;
124
123
  will-change: transform;
125
- transition: transform 0.1s;
124
+ transition: transform 0.12s;
126
125
  transform-origin: 0 0;
127
126
  user-select: none;
127
+ pointer-events: none;
128
128
  }
129
129
 
130
130
  .netless-window-manager-cursor-pencil-offset {
@@ -158,6 +158,20 @@
158
158
  margin-top: 3px;
159
159
  }
160
160
 
161
+ .netless-window-manager-cursor-pencilEraser-image {
162
+ margin-left: -22px;
163
+ margin-top: 3px;
164
+ }
165
+
166
+ .netless-window-manager-laserPointer-pencilEraser-offset {
167
+ margin-left: -18px;
168
+ }
169
+
170
+ .netless-window-manager-pencilEraser-3-offset {
171
+ margin-top: -14px;
172
+ margin-left: -6px;
173
+ }
174
+
161
175
  .netless-window-manager-cursor-name {
162
176
  width: 100%;
163
177
  height: 48px;
@@ -179,11 +193,14 @@
179
193
  bottom: 15px;
180
194
  }
181
195
 
182
- .window-manager-view-wrapper {
183
- z-index: 5000;
184
- width: 100%;
185
- height: 100%;
186
- position: absolute;
187
- left: 0;
188
- top: 0;
189
- }
196
+ .netless-iframe-brdige-hidden {
197
+ display: none;
198
+ }
199
+
200
+ .netless-window-manager-fullscreen .telebox-titlebar,
201
+ .netless-window-manager-fullscreen .telebox-max-titlebar-maximized,
202
+ .netless-window-manager-fullscreen .netless-app-slide-footer,
203
+ .netless-window-manager-fullscreen .telebox-footer-wrap,
204
+ .netless-window-manager-fullscreen .telebox-titlebar-wrap {
205
+ display: none;
206
+ }