@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/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
- /** 显示 PS 透明背景,默认 true */
143
+ /** @deprecated 显示 PS 透明背景,默认 true */
133
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,25 +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
- });
245
- manager.appManager?.setBoxManager(manager.boxManager);
281
+
246
282
  if (params.container) {
247
283
  manager.bindContainer(params.container);
248
284
  }
249
285
 
250
286
  replaceRoomFunction(room, manager);
251
- emitter.emit("onCreated");
287
+ internalEmitter.emit("onCreated");
252
288
  WindowManager.isCreated = true;
253
289
  try {
254
290
  await initDb();
@@ -259,48 +295,44 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
259
295
  return manager;
260
296
  }
261
297
 
262
- private static async initManager(room: Room): Promise<WindowManager> {
263
- let manager = room.getInvisiblePlugin(WindowManager.kind) as WindowManager;
264
- if (!manager) {
265
- if (isRoom(room)) {
266
- if (room.isWritable === false) {
267
- try {
268
- await room.setWritable(true);
269
- } catch (error) {
270
- throw new Error("[WindowManger]: room must be switched to be writable");
271
- }
272
- manager = (await room.createInvisiblePlugin(
273
- WindowManager,
274
- {}
275
- )) as WindowManager;
276
- manager.ensureAttributes();
277
- await wait(500);
278
- await room.setWritable(false);
279
- } else {
280
- manager = (await room.createInvisiblePlugin(
281
- WindowManager,
282
- {}
283
- )) as WindowManager;
284
- }
285
- }
286
- }
287
- return manager;
298
+ private static initManager(room: Room): Promise<WindowManager | undefined> {
299
+ return createInvisiblePlugin(room);
288
300
  }
289
301
 
290
302
  private static initContainer(
303
+ manager: WindowManager,
291
304
  container: HTMLElement,
292
- overwriteStyles: string | undefined
305
+ params: {
306
+ chessboard?: boolean;
307
+ overwriteStyles?: string;
308
+ fullscreen?: boolean;
309
+ }
293
310
  ) {
311
+ const { chessboard, overwriteStyles, fullscreen } = params;
294
312
  if (!WindowManager.container) {
295
313
  WindowManager.container = container;
296
314
  }
297
- const { playground, mainViewElement } = setupWrapper(container);
315
+ const { playground, wrapper, sizer, mainViewElement } = setupWrapper(container);
298
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
+ }
299
323
  if (overwriteStyles) {
300
324
  const style = document.createElement("style");
301
325
  style.textContent = overwriteStyles;
302
326
  playground.appendChild(style);
303
327
  }
328
+ manager.containerResizeObserver = ContainerResizeObserver.create(
329
+ playground,
330
+ sizer,
331
+ wrapper,
332
+ internalEmitter
333
+ );
334
+ WindowManager.wrapper = wrapper;
335
+ WindowManager.sizer = sizer;
304
336
  return mainViewElement;
305
337
  }
306
338
 
@@ -319,20 +351,24 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
319
351
  } else {
320
352
  if (WindowManager.params) {
321
353
  const params = WindowManager.params;
322
- const mainViewElement = WindowManager.initContainer(
323
- container,
324
- params.overwriteStyles
325
- );
326
- if (this.boxManager && WindowManager.playground) {
327
- this.boxManager.setRoot(WindowManager.playground);
354
+ const mainViewElement = WindowManager.initContainer(this, container, params);
355
+ if (this.boxManager) {
356
+ this.boxManager.destroy();
328
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);
329
365
  this.bindMainView(mainViewElement, params.disableCameraTransform);
330
- if (WindowManager.playground) {
331
- this.cursorManager?.setupWrapper(WindowManager.playground);
366
+ if (WindowManager.wrapper) {
367
+ this.cursorManager?.setupWrapper(WindowManager.wrapper);
332
368
  }
333
369
  }
334
370
  }
335
- emitter.emit("updateManagerRect");
371
+ internalEmitter.emit("updateManagerRect");
336
372
  this.appManager?.refresh();
337
373
  this.appManager?.resetMaximized();
338
374
  this.appManager?.resetMinimized();
@@ -342,7 +378,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
342
378
 
343
379
  public bindCollectorContainer(container: HTMLElement) {
344
380
  if (WindowManager.isCreated && this.boxManager) {
345
- this.boxManager.setCollector(container);
381
+ this.boxManager.setCollectorContainer(container);
346
382
  } else {
347
383
  if (WindowManager.params) {
348
384
  WindowManager.params.collectorContainer = container;
@@ -353,9 +389,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
353
389
  /**
354
390
  * 注册插件
355
391
  */
356
- public static register<AppOptions = any, SetupResult = any, Attributes = any>(
357
- params: RegisterParams<AppOptions, SetupResult, Attributes>
358
- ): Promise<void> {
392
+ public static register(params: RegisterParams<any, any, any>): Promise<void> {
359
393
  return appRegister.register(params);
360
394
  }
361
395
 
@@ -374,7 +408,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
374
408
  // 移除根目录时需要做一些异步的释放操作 addApp 需要等待释放完成才可以继续添加
375
409
  if (this.appManager.rootDirRemoving) {
376
410
  return new Promise((resolve, reject) => {
377
- emitter.once("rootDirRemoved").then(async () => {
411
+ internalEmitter.once("rootDirRemoved").then(async () => {
378
412
  try {
379
413
  const appId = await this._addApp(params);
380
414
  resolve(appId);
@@ -396,6 +430,9 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
396
430
  if (!params.kind || typeof params.kind !== "string") {
397
431
  throw new Errors.ParamsInvalidError();
398
432
  }
433
+ if (params.src && typeof params.src === "string") {
434
+ appRegister.register({ kind: params.kind, src: params.src });
435
+ }
399
436
  const appImpl = await appRegister.appClasses.get(params.kind)?.();
400
437
  if (appImpl && appImpl.config?.singleton) {
401
438
  if (this.appManager.appProxies.has(params.kind)) {
@@ -428,11 +465,12 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
428
465
  for (const appId of apps) {
429
466
  const appScenePath = appManager.store.getAppScenePath(appId);
430
467
  if (appScenePath && appScenePath === scenePath) {
431
- console.warn(`[WindowManager]: ScenePath ${scenePath} Already opened`);
468
+ console.warn(`[WindowManager]: ScenePath "${scenePath}" already opened`);
432
469
  if (this.boxManager) {
433
470
  const topBox = this.boxManager.getTopBox();
434
471
  if (topBox) {
435
472
  this.boxManager.setZIndex(appId, topBox.zIndex + 1, false);
473
+ this.boxManager.focusBox({ appId }, false);
436
474
  }
437
475
  }
438
476
  return;
@@ -504,6 +542,19 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
504
542
  }
505
543
  }
506
544
 
545
+ public async jumpPage(index: number): Promise<boolean> {
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 {
554
+ return false;
555
+ }
556
+ }
557
+
507
558
  public async addPage(params?: AddPageParams): Promise<void> {
508
559
  if (this.appManager) {
509
560
  const after = params?.after;
@@ -533,7 +584,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
533
584
  console.warn(`[WindowManager]: index ${index} out of range`);
534
585
  return false;
535
586
  }
536
- return this.appManager.removeSceneByIndex(needRemoveIndex);;
587
+ return this.appManager.removeSceneByIndex(needRemoveIndex);
537
588
  } else {
538
589
  return false;
539
590
  }
@@ -559,7 +610,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
559
610
  public setReadonly(readonly: boolean): void {
560
611
  this.readonly = readonly;
561
612
  this.boxManager?.setReadonly(readonly);
562
- emitter.emit("setReadonly", readonly);
613
+ internalEmitter.emit("setReadonly", readonly);
563
614
  }
564
615
 
565
616
  /**
@@ -576,12 +627,24 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
576
627
  addEmitterOnceListener(`destroy-${kind}`, listener);
577
628
  }
578
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
+
579
642
  /**
580
643
  * 设置 ViewMode
581
644
  */
582
645
  public setViewMode(mode: ViewMode): void {
583
- if (mode === ViewMode.Broadcaster) {
584
- if (this.canOperate) {
646
+ if (mode === ViewMode.Broadcaster || mode === ViewMode.Follower) {
647
+ if (this.canOperate && mode === ViewMode.Broadcaster) {
585
648
  this.appManager?.mainViewProxy.setCameraAndSize();
586
649
  }
587
650
  this.appManager?.mainViewProxy.start();
@@ -590,6 +653,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
590
653
  this.appManager?.mainViewProxy.stop();
591
654
  }
592
655
  this.viewMode = mode;
656
+ this.appManager?.mainViewProxy.setViewMode(mode);
593
657
  }
594
658
 
595
659
  public setBoxState(boxState: TeleBoxState): void {
@@ -621,6 +685,35 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
621
685
  this.boxManager?.setMinimized(minimized, false);
622
686
  }
623
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
+
624
717
  public get mainView(): View {
625
718
  if (this.appManager) {
626
719
  return this.appManager.mainViewProxy.view;
@@ -677,6 +770,27 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
677
770
  return this.appManager?.focusApp?.view || this.mainView;
678
771
  }
679
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
+
680
794
  public get mainViewSceneIndex(): number {
681
795
  return this._pageState?.index || 0;
682
796
  }
@@ -722,6 +836,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
722
836
  }
723
837
  }
724
838
 
839
+ public get fullscreen(): boolean {
840
+ return Boolean(this._fullscreen);
841
+ }
842
+
725
843
  /**
726
844
  * 查询所有的 App
727
845
  */
@@ -743,6 +861,18 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
743
861
  return this.appManager?.closeApp(appId);
744
862
  }
745
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
+
746
876
  public moveCamera(
747
877
  camera: Partial<Camera> & { animationMode?: AnimationMode | undefined }
748
878
  ): void {
@@ -786,14 +916,19 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
786
916
  }
787
917
 
788
918
  private _destroy() {
919
+ this.containerResizeObserver?.disconnect();
789
920
  this.appManager?.destroy();
790
921
  this.cursorManager?.destroy();
791
922
  WindowManager.container = undefined;
923
+ WindowManager.wrapper = undefined;
924
+ WindowManager.sizer = undefined;
792
925
  WindowManager.isCreated = false;
793
926
  if (WindowManager.playground) {
794
927
  WindowManager.playground.parentNode?.removeChild(WindowManager.playground);
795
928
  }
796
929
  WindowManager.params = undefined;
930
+ this._iframeBridge?.destroy();
931
+ this._iframeBridge = undefined;
797
932
  log("Destroyed");
798
933
  }
799
934
 
@@ -836,6 +971,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
836
971
  }
837
972
 
838
973
  public cleanCurrentScene(): void {
974
+ log("clean current scene");
839
975
  this.focusedView?.cleanCurrentScene();
840
976
  }
841
977
 
@@ -884,26 +1020,28 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
884
1020
  }
885
1021
 
886
1022
  public refresh() {
887
- this._refresh();
888
- this.appManager?.dispatchInternalEvent(Events.Refresh);
1023
+ this._refresh();
1024
+ this.appManager?.dispatchInternalEvent(Events.Refresh);
889
1025
  }
890
-
891
- /** @inner */
1026
+
1027
+ /** @internal */
892
1028
  public _refresh() {
893
1029
  this.appManager?.mainViewProxy.rebind();
894
1030
  if (WindowManager.container) {
895
1031
  this.bindContainer(WindowManager.container);
896
1032
  }
897
- this.appManager?.refresher?.refresh();
1033
+ this.appManager?.refresher.refresh();
898
1034
  }
899
1035
 
900
1036
  public setContainerSizeRatio(ratio: number) {
901
- if (!isNumber(ratio)) {
902
- 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
+ );
903
1041
  }
904
1042
  WindowManager.containerSizeRatio = ratio;
905
1043
  this.containerSizeRatio = ratio;
906
- emitter.emit("containerSizeRatioUpdate", ratio);
1044
+ internalEmitter.emit("containerSizeRatioUpdate", ratio);
907
1045
  }
908
1046
 
909
1047
  private isDynamicPPT(scenes: SceneDefinition[]) {
@@ -931,7 +1069,19 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> imple
931
1069
  if (!this.attributes[Fields.Registered]) {
932
1070
  this.safeSetAttributes({ [Fields.Registered]: {} });
933
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()");
934
1082
  }
1083
+ this._iframeBridge || (this._iframeBridge = new IframeBridge(this, this.appManager));
1084
+ return this._iframeBridge;
935
1085
  }
936
1086
  }
937
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
+ }