@netless/window-manager 0.4.0-canary.5 → 0.4.0-canary.9

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 (57) hide show
  1. package/dist/App/Storage/StorageEvent.d.ts +8 -0
  2. package/dist/App/Storage/index.d.ts +26 -0
  3. package/dist/App/Storage/typings.d.ts +21 -0
  4. package/dist/App/Storage/utils.d.ts +5 -0
  5. package/dist/AppContext.d.ts +3 -1
  6. package/dist/AppListener.d.ts +0 -1
  7. package/dist/AppManager.d.ts +5 -5
  8. package/dist/AppProxy.d.ts +2 -3
  9. package/dist/Base/Context.d.ts +0 -1
  10. package/dist/BoxManager.d.ts +2 -1
  11. package/dist/BuiltinApps.d.ts +6 -0
  12. package/dist/ContainerResizeObserver.d.ts +10 -0
  13. package/dist/Helper.d.ts +6 -0
  14. package/dist/Utils/Common.d.ts +3 -1
  15. package/dist/Utils/Reactive.d.ts +1 -1
  16. package/dist/{MainView.d.ts → View/MainView.d.ts} +2 -4
  17. package/dist/View/ViewManager.d.ts +13 -0
  18. package/dist/constants.d.ts +1 -6
  19. package/dist/index.d.ts +5 -10
  20. package/dist/index.es.js +1 -1
  21. package/dist/index.es.js.map +1 -1
  22. package/dist/index.umd.js +1 -1
  23. package/dist/index.umd.js.map +1 -1
  24. package/dist/style.css +1 -1
  25. package/dist/typings.d.ts +1 -0
  26. package/package.json +3 -3
  27. package/src/App/Storage/StorageEvent.ts +21 -0
  28. package/src/App/Storage/index.ts +243 -0
  29. package/src/App/Storage/typings.ts +21 -0
  30. package/src/App/Storage/utils.ts +17 -0
  31. package/src/AppContext.ts +10 -2
  32. package/src/AppListener.ts +1 -8
  33. package/src/AppManager.ts +45 -27
  34. package/src/AppProxy.ts +14 -36
  35. package/src/Base/Context.ts +0 -4
  36. package/src/BoxManager.ts +9 -7
  37. package/src/BuiltinApps.ts +24 -0
  38. package/src/ContainerResizeObserver.ts +62 -0
  39. package/src/Helper.ts +30 -0
  40. package/src/ReconnectRefresher.ts +0 -1
  41. package/src/Utils/Common.ts +35 -13
  42. package/src/Utils/Reactive.ts +9 -3
  43. package/src/Utils/RoomHacker.ts +15 -0
  44. package/src/{MainView.ts → View/MainView.ts} +7 -25
  45. package/src/View/ViewManager.ts +53 -0
  46. package/src/constants.ts +1 -3
  47. package/src/index.ts +19 -71
  48. package/src/shim.d.ts +4 -0
  49. package/src/style.css +6 -0
  50. package/src/typings.ts +1 -0
  51. package/vite.config.js +4 -1
  52. package/dist/Utils/CameraStore.d.ts +0 -15
  53. package/dist/ViewManager.d.ts +0 -29
  54. package/dist/sdk.d.ts +0 -14
  55. package/src/Utils/CameraStore.ts +0 -72
  56. package/src/sdk.ts +0 -39
  57. package/src/viewManager.ts +0 -177
package/src/index.ts CHANGED
@@ -1,9 +1,8 @@
1
- import AppDocsViewer from "@netless/app-docs-viewer";
2
- import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
3
1
  import Emittery from "emittery";
4
2
  import pRetry from "p-retry";
5
3
  import { AppManager } from "./AppManager";
6
4
  import { appRegister } from "./Register";
5
+ import { ContainerResizeObserver } from "./ContainerResizeObserver";
7
6
  import { createBoxManager } from "./BoxManager";
8
7
  import { CursorManager } from "./Cursor";
9
8
  import { DEFAULT_CONTAINER_RATIO, Events, REQUIRE_VERSION } from "./constants";
@@ -11,12 +10,12 @@ import { Fields } from "./AttributesDelegate";
11
10
  import { initDb } from "./Register/storage";
12
11
  import { isNull, isObject } from "lodash";
13
12
  import { log } from "./Utils/log";
13
+ import { ReconnectRefresher } from "./ReconnectRefresher";
14
14
  import { replaceRoomFunction } from "./Utils/RoomHacker";
15
- import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
16
- import { setupWrapper } from "./ViewManager";
15
+ import { setupBuiltin } from "./BuiltinApps";
16
+ import { setupWrapper } from "./Helper";
17
17
  import "./style.css";
18
18
  import "@netless/telebox-insider/dist/style.css";
19
- import "@netless/app-docs-viewer/dist/style.css";
20
19
  import {
21
20
  addEmitterOnceListener,
22
21
  ensureValidScenePath,
@@ -59,9 +58,6 @@ import type { AppListeners } from "./AppListener";
59
58
  import type { NetlessApp, RegisterParams } from "./typings";
60
59
  import type { TeleBoxColorScheme, TeleBoxState } from "@netless/telebox-insider";
61
60
  import type { AppProxy } from "./AppProxy";
62
- import { ReconnectRefresher } from "./ReconnectRefresher";
63
-
64
- const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
65
61
 
66
62
  export type WindowMangerAttributes = {
67
63
  modelValue?: string;
@@ -153,6 +149,8 @@ export type PublicEvent = {
153
149
  darkModeChange: boolean;
154
150
  prefersColorSchemeChange: TeleBoxColorScheme;
155
151
  cameraStateChange: CameraState;
152
+ mainViewScenePathChange: string;
153
+ mainViewSceneIndexChange: number;
156
154
  };
157
155
 
158
156
  export type MountParams = {
@@ -186,7 +184,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
186
184
  public static containerSizeRatio = DEFAULT_CONTAINER_RATIO;
187
185
  private static isCreated = false;
188
186
 
189
- public version = "0.4.0-canary.5";
187
+ public version = __APP_VERSION__;
190
188
 
191
189
  public appListeners?: AppListeners;
192
190
 
@@ -200,6 +198,8 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
200
198
  private boxManager?: BoxManager;
201
199
  private static params?: MountParams;
202
200
 
201
+ private containerResizeObserver?: ContainerResizeObserver;
202
+
203
203
  constructor(context: InvisiblePluginContext) {
204
204
  super(context);
205
205
  WindowManager.displayer = context.displayer;
@@ -225,9 +225,6 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
225
225
  }
226
226
  let manager = await this.initManager(room);
227
227
  this.debug = Boolean(debug);
228
- if (this.debug) {
229
- setOptions({ verbose: true });
230
- }
231
228
  log("Already insert room", manager);
232
229
 
233
230
  if (isRoom(this.displayer)) {
@@ -321,7 +318,12 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
321
318
  style.textContent = overwriteStyles;
322
319
  playground.appendChild(style);
323
320
  }
324
- manager.observePlaygroundSize(playground, sizer, wrapper);
321
+ manager.containerResizeObserver = ContainerResizeObserver.create(
322
+ playground,
323
+ sizer,
324
+ wrapper,
325
+ emitter
326
+ );
325
327
  WindowManager.wrapper = wrapper;
326
328
  return mainViewElement;
327
329
  }
@@ -479,10 +481,8 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
479
481
  * 设置所有 app 的 readonly 模式
480
482
  */
481
483
  public setReadonly(readonly: boolean): void {
482
- if (this.room?.isWritable) {
483
- this.readonly = readonly;
484
- this.appManager?.boxManager?.setReadonly(readonly);
485
- }
484
+ this.readonly = readonly;
485
+ this.boxManager?.setReadonly(readonly);
486
486
  }
487
487
 
488
488
  /**
@@ -702,62 +702,10 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
702
702
  }
703
703
  }
704
704
  }
705
-
706
- private containerResizeObserver?: ResizeObserver;
707
-
708
- private observePlaygroundSize(
709
- container: HTMLElement,
710
- sizer: HTMLElement,
711
- wrapper: HTMLDivElement
712
- ) {
713
- this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
714
-
715
- this.containerResizeObserver = new ResizeObserver(entries => {
716
- const containerRect = entries[0]?.contentRect;
717
- if (containerRect) {
718
- this.updateSizer(containerRect, sizer, wrapper);
719
- this.cursorManager?.updateContainerRect();
720
- this.boxManager?.updateManagerRect();
721
- emitter.emit("playgroundSizeChange", containerRect);
722
- }
723
- });
724
-
725
- this.containerResizeObserver.observe(container);
726
- }
727
-
728
- private updateSizer(
729
- { width, height }: DOMRectReadOnly,
730
- sizer: HTMLElement,
731
- wrapper: HTMLDivElement
732
- ) {
733
- if (width && height) {
734
- if (height / width > WindowManager.containerSizeRatio) {
735
- height = width * WindowManager.containerSizeRatio;
736
- sizer.classList.toggle("netless-window-manager-sizer-horizontal", true);
737
- } else {
738
- width = height / WindowManager.containerSizeRatio;
739
- sizer.classList.toggle("netless-window-manager-sizer-horizontal", false);
740
- }
741
- wrapper.style.width = `${width}px`;
742
- wrapper.style.height = `${height}px`;
743
- }
744
- }
745
705
  }
746
706
 
747
- WindowManager.register({
748
- kind: AppDocsViewer.kind,
749
- src: AppDocsViewer,
750
- });
751
- WindowManager.register({
752
- kind: AppMediaPlayer.kind,
753
- src: AppMediaPlayer,
754
- });
755
-
756
- export const BuiltinApps = {
757
- DocsViewer: AppDocsViewer.kind as string,
758
- MediaPlayer: AppMediaPlayer.kind as string,
759
- };
707
+ setupBuiltin();
760
708
 
761
709
  export * from "./typings";
762
710
 
763
- export { WhiteWindowSDK } from "./sdk";
711
+ export { BuiltinApps } from "./BuiltinApps";
package/src/shim.d.ts CHANGED
@@ -4,3 +4,7 @@ declare module "*.svelte" {
4
4
  const app: SvelteComponent;
5
5
  export default app;
6
6
  }
7
+
8
+ declare global {
9
+ const __APP_VERSION__: string;
10
+ }
package/src/style.css CHANGED
@@ -167,3 +167,9 @@
167
167
  display: flex;
168
168
  justify-content: center;
169
169
  }
170
+
171
+ .telebox-collector {
172
+ position: absolute;
173
+ right: 10px;
174
+ bottom: 15px;
175
+ }
package/src/typings.ts CHANGED
@@ -76,3 +76,4 @@ export type AppListenerKeys = keyof AppEmitterEvent;
76
76
  export type { AppContext } from "./AppContext";
77
77
  export type { ReadonlyTeleBox, TeleBoxRect };
78
78
  export type { SceneState, SceneDefinition, View, AnimationMode, Displayer, Room, Player };
79
+ export type { Storage, StorageStateChangedEvent, StorageStateChangedListener } from "./App/Storage";
package/vite.config.js CHANGED
@@ -1,13 +1,16 @@
1
1
  import path from "path";
2
2
  import { defineConfig } from "vite";
3
3
  import { svelte } from "@sveltejs/vite-plugin-svelte";
4
- import { dependencies ,peerDependencies } from "./package.json"
4
+ import { dependencies ,peerDependencies, version } from "./package.json"
5
5
 
6
6
 
7
7
  export default defineConfig(({ mode }) => {
8
8
  const isProd = mode === "production";
9
9
 
10
10
  return {
11
+ define: {
12
+ __APP_VERSION__: JSON.stringify(version),
13
+ },
11
14
  plugins: [
12
15
  svelte({
13
16
  emitCss: false,
@@ -1,15 +0,0 @@
1
- import type { Camera, View } from "white-web-sdk";
2
- export declare class CameraStore {
3
- private cameras;
4
- private listeners;
5
- setCamera(id: string, camera: Camera): void;
6
- getCamera(id: string): Camera | undefined;
7
- deleteCamera(id: string): void;
8
- recoverCamera(id: string, view?: View): void;
9
- register(id: string, view: View): void;
10
- unregister(id: string, view?: View): void;
11
- private onListener;
12
- private offListener;
13
- switchView(id: string, view: View | undefined, callback: () => void): Promise<void>;
14
- private getOrCreateListener;
15
- }
@@ -1,29 +0,0 @@
1
- import { Base } from "./Base";
2
- import type { View, Displayer } from "white-web-sdk";
3
- import type { AppManager } from "./AppManager";
4
- export declare class ViewManager extends Base {
5
- private views;
6
- private timer?;
7
- private appTimer?;
8
- private mainViewProxy;
9
- private displayer;
10
- constructor(manager: AppManager);
11
- get currentScenePath(): string;
12
- get mainView(): View;
13
- createView(appId: string): View;
14
- destroyView(appId: string): void;
15
- private releaseView;
16
- getView(appId: string): View | undefined;
17
- switchMainViewToWriter(): Promise<boolean> | undefined;
18
- freedomAllViews(): void;
19
- switchAppToWriter(id: string): void;
20
- destroy(): void;
21
- }
22
- export declare const createView: (displayer: Displayer) => View;
23
- export declare const setDefaultCameraBound: (view: View) => void;
24
- export declare const setupWrapper: (root: HTMLElement) => {
25
- playground: HTMLDivElement;
26
- wrapper: HTMLDivElement;
27
- sizer: HTMLDivElement;
28
- mainViewElement: HTMLDivElement;
29
- };
package/dist/sdk.d.ts DELETED
@@ -1,14 +0,0 @@
1
- import { WindowManager } from './index';
2
- import type { MountParams } from "./index";
3
- import type { WhiteWebSdkConfiguration, JoinRoomParams } from "white-web-sdk";
4
- declare type WhiteWindowSDKConfiguration = Omit<WhiteWebSdkConfiguration, "useMobXState">;
5
- declare type WindowJoinRoomParams = {
6
- joinRoomParams: Omit<JoinRoomParams, "useMultiViews" | "disableMagixEventDispatchLimit">;
7
- mountParams: Omit<MountParams, "room">;
8
- };
9
- export declare class WhiteWindowSDK {
10
- private sdk;
11
- constructor(params: WhiteWindowSDKConfiguration);
12
- mount(params: WindowJoinRoomParams): Promise<WindowManager>;
13
- }
14
- export {};
@@ -1,72 +0,0 @@
1
- import { AnimationMode } from "white-web-sdk";
2
- import type { Camera, View } from "white-web-sdk";
3
-
4
- export class CameraStore {
5
- private cameras: Map<string, Camera> = new Map();
6
- private listeners: Map<string, any> = new Map();
7
-
8
- public setCamera(id: string, camera: Camera) {
9
- this.cameras.set(id, camera);
10
- }
11
-
12
- public getCamera(id: string) {
13
- return this.cameras.get(id);
14
- }
15
-
16
- public deleteCamera(id: string) {
17
- this.cameras.delete(id);
18
- }
19
-
20
- public recoverCamera(id: string, view?: View) {
21
- const camera = this.cameras.get(id);
22
- if (camera && view) {
23
- view.moveCamera({
24
- ...camera,
25
- animationMode: AnimationMode.Immediately,
26
- });
27
- }
28
- }
29
-
30
- public register(id: string, view: View) {
31
- this.onListener(id, view);
32
- this.setCamera(id, view.camera);
33
- }
34
-
35
- public unregister(id: string, view?: View) {
36
- if (view) {
37
- this.offListener(id, view);
38
- }
39
- this.listeners.delete(id);
40
- this.deleteCamera(id);
41
- }
42
-
43
- private onListener = (id: string, view: View) => {
44
- view.callbacks.on("onCameraUpdated", this.getOrCreateListener(id));
45
- };
46
-
47
- private offListener = (id: string, view: View) => {
48
- view.callbacks.off("onCameraUpdated", this.getOrCreateListener(id));
49
- };
50
-
51
- public async switchView(id: string, view: View | undefined, callback: () => void) {
52
- if (view) {
53
- this.offListener(id, view);
54
- await callback();
55
- this.recoverCamera(id, view);
56
- this.onListener(id, view);
57
- }
58
- }
59
-
60
- private getOrCreateListener(id: string) {
61
- let listener = this.listeners.get(id);
62
- if (listener) {
63
- return listener;
64
- } else {
65
- listener = (camera: Camera) => {
66
- this.setCamera(id, camera);
67
- };
68
- this.listeners.set(id, listener);
69
- return listener;
70
- }
71
- }
72
- }
package/src/sdk.ts DELETED
@@ -1,39 +0,0 @@
1
- import { isBoolean } from 'lodash';
2
- import { WhiteWebSdk } from 'white-web-sdk';
3
- import { WindowManager } from './index';
4
- import type { MountParams } from "./index";
5
- import type { WhiteWebSdkConfiguration, JoinRoomParams } from "white-web-sdk";
6
-
7
-
8
-
9
- type WhiteWindowSDKConfiguration = Omit<WhiteWebSdkConfiguration, "useMobXState">
10
- type WindowJoinRoomParams = {
11
- joinRoomParams: Omit<JoinRoomParams, "useMultiViews" | "disableMagixEventDispatchLimit">,
12
- mountParams: Omit<MountParams, "room">,
13
- }
14
-
15
- export class WhiteWindowSDK {
16
- private sdk: WhiteWebSdk;
17
-
18
- constructor(params: WhiteWindowSDKConfiguration) {
19
- this.sdk = new WhiteWebSdk({ ...params, useMobXState: true });
20
- }
21
-
22
- public async mount(params: WindowJoinRoomParams): Promise<WindowManager> {
23
- const invisiblePlugins = params.joinRoomParams.invisiblePlugins || [];
24
- const room = await this.sdk.joinRoom({
25
- ...params.joinRoomParams,
26
- useMultiViews: true,
27
- invisiblePlugins: [...invisiblePlugins, WindowManager],
28
- disableMagixEventDispatchLimit: true,
29
- });
30
- const manager = await WindowManager.mount({
31
- room,
32
- ...params.mountParams
33
- });
34
- if (isBoolean(params.joinRoomParams.disableCameraTransform)) {
35
- manager.mainView.disableCameraTransform = params.joinRoomParams.disableCameraTransform;
36
- }
37
- return manager;
38
- }
39
- }
@@ -1,177 +0,0 @@
1
- import { Base } from "./Base";
2
- import { callbacks, WindowManager } from "./index";
3
- import { reaction, ViewVisionMode } from "white-web-sdk";
4
- import { SET_SCENEPATH_DELAY } from "./constants";
5
- import { notifyMainViewModeChange, setScenePath, setViewMode } from "./Utils/Common";
6
- import type { View, Displayer } from "white-web-sdk";
7
- import type { AppManager } from "./AppManager";
8
-
9
- export class ViewManager extends Base {
10
- private views: Map<string, View> = new Map();
11
- private timer?: number;
12
- private appTimer?: number;
13
-
14
- private mainViewProxy = this.manager.mainViewProxy;
15
- private displayer = this.manager.displayer;
16
-
17
- constructor(manager: AppManager) {
18
- super(manager);
19
- setTimeout(() => {
20
- // 延迟初始化 focus 的 reaction
21
- this.manager.refresher?.add("focus", () => {
22
- return reaction(
23
- () => this.store.focus,
24
- focus => {
25
- if (focus) {
26
- this.switchAppToWriter(focus);
27
- } else {
28
- this.switchMainViewToWriter();
29
- this.context.blurFocusBox();
30
- }
31
- },
32
- { fireImmediately: true }
33
- );
34
- });
35
- }, 100);
36
- }
37
-
38
- public get currentScenePath(): string {
39
- return this.displayer.state.sceneState.scenePath;
40
- }
41
-
42
- public get mainView(): View {
43
- return this.mainViewProxy.view;
44
- }
45
-
46
- public createView(appId: string): View {
47
- const view = createView(this.displayer);
48
- setViewMode(view, ViewVisionMode.Freedom);
49
- this.views.set(appId, view);
50
- return view;
51
- }
52
-
53
- public destroyView(appId: string): void {
54
- const view = this.views.get(appId);
55
- if (view) {
56
- this.releaseView(view);
57
- this.views.delete(appId);
58
- }
59
- }
60
-
61
- private releaseView(view: View) {
62
- if (!(view as any).didRelease) {
63
- view.release();
64
- }
65
- }
66
-
67
- public getView(appId: string): View | undefined {
68
- return this.views.get(appId);
69
- }
70
-
71
- public switchMainViewToWriter(): Promise<boolean> | undefined {
72
- if (this.timer) {
73
- clearTimeout(this.timer);
74
- }
75
- if (this.mainView.mode === ViewVisionMode.Writable) return;
76
- this.freedomAllViews();
77
- return new Promise((resolve, reject) => {
78
- this.timer = window.setTimeout(() => {
79
- try {
80
- const mainViewScenePath = this.store.getMainViewScenePath();
81
- if (mainViewScenePath) {
82
- this.freedomAllViews();
83
- setScenePath(this.manager.room, mainViewScenePath);
84
- this.mainViewProxy.switchViewModeToWriter();
85
- }
86
- resolve(true);
87
- } catch (error) {
88
- reject(error);
89
- }
90
- }, SET_SCENEPATH_DELAY);
91
- });
92
- }
93
-
94
- public freedomAllViews(): void {
95
- this.manager.appProxies.forEach(appProxy => {
96
- appProxy.setViewFocusScenePath();
97
- if (appProxy.view) {
98
- setViewMode(appProxy.view, ViewVisionMode.Freedom);
99
- }
100
- });
101
- if (this.mainView.mode === ViewVisionMode.Writable) {
102
- notifyMainViewModeChange(callbacks, ViewVisionMode.Freedom);
103
- setViewMode(this.mainView, ViewVisionMode.Freedom);
104
- }
105
- if (!this.mainView.focusScenePath) {
106
- this.store.setMainViewFocusPath(this.mainView);
107
- }
108
- }
109
-
110
- public switchAppToWriter(id: string): void {
111
- if (this.appTimer) {
112
- clearTimeout(this.appTimer);
113
- }
114
- this.freedomAllViews();
115
- // 为了同步端不闪烁, 需要给 room setScenePath 一个延迟
116
- this.appTimer = setTimeout(() => {
117
- const appProxy = this.manager.appProxies.get(id);
118
- if (appProxy) {
119
- if (this.manager.boxManager?.minimized) return;
120
- appProxy.setScenePath();
121
- appProxy.switchToWritable();
122
- appProxy.focusBox();
123
- }
124
- }, SET_SCENEPATH_DELAY);
125
- }
126
-
127
- public destroy(): void {
128
- this.mainViewProxy.removeMainViewListener();
129
- if (WindowManager.wrapper) {
130
- WindowManager.wrapper.parentNode?.removeChild(WindowManager.wrapper);
131
- WindowManager.wrapper = undefined;
132
- }
133
- this.releaseView(this.mainView);
134
- }
135
- }
136
-
137
- export const createView = (displayer: Displayer): View => {
138
- const view = displayer.views.createView();
139
- setDefaultCameraBound(view);
140
- return view;
141
- };
142
-
143
- export const setDefaultCameraBound = (view: View) => {
144
- view.setCameraBound({
145
- maxContentMode: () => 10,
146
- minContentMode: () => 0.1,
147
- });
148
- };
149
-
150
- export const setupWrapper = (
151
- root: HTMLElement
152
- ): {
153
- playground: HTMLDivElement;
154
- wrapper: HTMLDivElement;
155
- sizer: HTMLDivElement;
156
- mainViewElement: HTMLDivElement;
157
- } => {
158
- const playground = document.createElement("div");
159
- playground.className = "netless-window-manager-playground";
160
-
161
- const sizer = document.createElement("div");
162
- sizer.className = "netless-window-manager-sizer";
163
-
164
- const wrapper = document.createElement("div");
165
- wrapper.className = "netless-window-manager-wrapper";
166
-
167
- const mainViewElement = document.createElement("div");
168
- mainViewElement.className = "netless-window-manager-main-view";
169
-
170
- playground.appendChild(sizer);
171
- sizer.appendChild(wrapper);
172
- wrapper.appendChild(mainViewElement);
173
- root.appendChild(playground);
174
- WindowManager.wrapper = wrapper;
175
-
176
- return { playground, wrapper, sizer, mainViewElement };
177
- };