@netless/window-manager 0.4.10 → 0.4.11-canary.2

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.
package/docs/advanced.md CHANGED
@@ -4,6 +4,7 @@
4
4
  - [撤销重做](#redo-undo)
5
5
  - [清屏](#clean-current-scene)
6
6
  - [判断是否打开某种 APP](#has-kind)
7
+ - [页面控制器](#page-control)
7
8
 
8
9
 
9
10
  <h3 id="redo-undo">撤销重做</h3>
@@ -64,3 +65,26 @@ manager.emitter.on("ready", () => { // ready 事件在所有 app 创建完成后
64
65
  const hasSlide = apps.some(app => app.kind === "Slide"); // 判断已经打开的 APP 中是否有 Slide
65
66
  });
66
67
  ```
68
+
69
+ <br>
70
+
71
+ <h3 id="page-control">页面控制器</h3>
72
+
73
+ `manager` 提供了一个 `pageState` 来获取当前的 index 和总页数
74
+
75
+ ```ts
76
+ manager.pageState.index // 当前的 index
77
+ manager.pageState.length // 总页数
78
+
79
+ manager.emitter.on("pageStateChange", state => {
80
+ // 当前 index 变化和总页数变化会触发此事件
81
+ });
82
+ ```
83
+
84
+ 上一页/下一页/添加一页
85
+
86
+ ```ts
87
+ manager.nextPage()
88
+ manager.prevPage()
89
+ manager.addPage()
90
+ ```
package/docs/api.md CHANGED
@@ -229,6 +229,7 @@ manager.addPage({ scene: { name: "page2" } }) // 传入 page 信息
229
229
  | canRedoSteps | number | | 当前 focus 的 view 可以重做的步数 |
230
230
  | canRedoSteps | number | | 当前 focus 的 view 可以撤销的步数 |
231
231
  | sceneState | SceneState | | 兼容原本 SDK 的 sceneState 属性, 只对 mainView 生效 |
232
+ | pageState | PageState | | 组合 mainView 的 index 和 scenes 的修改 |
232
233
 
233
234
  <br>
234
235
 
@@ -253,6 +254,7 @@ manager.callbacks.on(events, listener)
253
254
  | loadApp | LoadAppEvent | | 加载远程APP 事件 |
254
255
  | ready | undefined | | 当所有 APP 创建完毕时触发 |
255
256
  | sceneStateChange | SceneState | | 当 sceneState 修改时触发 |
257
+ | pageStateChange | PageState | | |
256
258
 
257
259
  ```ts
258
260
  type LoadAppEvent = {
@@ -260,4 +262,11 @@ type LoadAppEvent = {
260
262
  status: "start" | "success" | "failed";
261
263
  reason?: string;
262
264
  }
265
+ ```
266
+
267
+ ```ts
268
+ type PageState = {
269
+ index: number;
270
+ length: number;
271
+ }
263
272
  ```
@@ -69,10 +69,14 @@ const Counter: NetlessApp<{ count: number }> = {
69
69
  decButton.addEventListener("click", decButtonOnClick);
70
70
  $content.appendChild(decButton);
71
71
 
72
- const event1Disposer = context.addMagixEventListener(`${context.appId}_event1`, msg => {
72
+ // 监听事件
73
+ const event1Disposer = context.addMagixEventListener("event1", msg => {
73
74
  console.log("event1", msg);
74
75
  });
75
76
 
77
+ // 向打开 app 的其他人发送消息
78
+ context.dispatchMagixEvent("event1", { count: 10 });
79
+
76
80
  // 应用销毁时, 注意清理掉监听器
77
81
  context.emitter.on("destroy", () => {
78
82
  incButton.removeEventListener("click", incButtonOnClick);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "0.4.10",
3
+ "version": "0.4.11-canary.2",
4
4
  "description": "",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
@@ -31,7 +31,7 @@
31
31
  "video.js": ">=7"
32
32
  },
33
33
  "devDependencies": {
34
- "@netless/app-docs-viewer": "^0.2.6",
34
+ "@netless/app-docs-viewer": "^0.2.8",
35
35
  "@netless/app-media-player": "0.1.0-beta.5",
36
36
  "@rollup/plugin-commonjs": "^20.0.0",
37
37
  "@rollup/plugin-node-resolve": "^13.0.4",
package/src/AppContext.ts CHANGED
@@ -165,7 +165,8 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
165
165
  /** Dispatch events to other clients (and self). */
166
166
  public dispatchMagixEvent: MagixEventDispatcher<TMagixEventPayloads> = (...args) => {
167
167
  // can't dispatch events on replay mode
168
- return this.manager.room?.dispatchMagixEvent(...args);
168
+ const appScopeEvent = `${this.appId}:${args[0]}`;
169
+ return this.manager.room?.dispatchMagixEvent(appScopeEvent, args[1]);
169
170
  };
170
171
 
171
172
  /** Listen to events from others clients (and self messages). */
@@ -174,9 +175,17 @@ export class AppContext<TAttributes = any, TMagixEventPayloads = any, TAppOption
174
175
  handler,
175
176
  options
176
177
  ) => {
177
- this.manager.displayer.addMagixEventListener(event, handler as WhiteEventListener, options);
178
+ const appScopeEvent = `${this.appId}:${event}`;
179
+ this.manager.displayer.addMagixEventListener(
180
+ appScopeEvent,
181
+ handler as WhiteEventListener,
182
+ options
183
+ );
178
184
  return () =>
179
- this.manager.displayer.removeMagixEventListener(event, handler as WhiteEventListener);
185
+ this.manager.displayer.removeMagixEventListener(
186
+ appScopeEvent,
187
+ handler as WhiteEventListener
188
+ );
180
189
  };
181
190
 
182
191
  /** Remove a Magix event listener. */
package/src/AppManager.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AppAttributes, AppStatus, Events, MagixEventName, ROOT_DIR } from "./constants";
1
+ import { AppAttributes, AppStatus, Events, INIT_DIR, MagixEventName, ROOT_DIR } from "./constants";
2
2
  import { AppCreateQueue } from "./Utils/AppCreateQueue";
3
3
  import { AppListeners } from "./AppListener";
4
4
  import { AppProxy } from "./AppProxy";
@@ -6,6 +6,7 @@ import { appRegister } from "./Register";
6
6
  import { autorun, isPlayer, isRoom, ScenePathType } from "white-web-sdk";
7
7
  import { callbacks } from "./callback";
8
8
  import { emitter } from "./InternalEmitter";
9
+ import { Fields, store } from "./AttributesDelegate";
9
10
  import { get, isInteger, orderBy } from "lodash";
10
11
  import { log } from "./Utils/log";
11
12
  import { MainViewProxy } from "./View/MainView";
@@ -13,8 +14,8 @@ import { onObjectRemoved, safeListenPropsUpdated } from "./Utils/Reactive";
13
14
  import { reconnectRefresher, WindowManager } from "./index";
14
15
  import { RedoUndo } from "./RedoUndo";
15
16
  import { SideEffectManager } from "side-effect-manager";
16
- import { store } from "./AttributesDelegate";
17
17
  import { ViewManager } from "./View/ViewManager";
18
+ import type { SyncRegisterAppPayload } from "./Register";
18
19
  import type { EmitterEvent } from "./InternalEmitter";
19
20
  import {
20
21
  entireScenes,
@@ -34,6 +35,7 @@ import type {
34
35
  SceneState,
35
36
  } from "white-web-sdk";
36
37
  import type { AddAppParams, BaseInsertParams, TeleBoxRect } from "./index";
38
+
37
39
  export class AppManager {
38
40
  public displayer: Displayer;
39
41
  public viewManager: ViewManager;
@@ -97,23 +99,30 @@ export class AppManager {
97
99
  emitter.on("setReadonly", this.onReadonlyChanged);
98
100
 
99
101
  this.createRootDirScenesCallback();
102
+
103
+ appRegister.setSyncRegisterApp(payload => {
104
+ this.safeUpdateAttributes([Fields.Registered, payload.kind], payload);
105
+ });
100
106
  }
101
107
 
102
108
  private onRemoveScenes = (scenePath: string) => {
109
+ // 如果移除根目录就把 scenePath 设置为初始值
103
110
  if (scenePath === ROOT_DIR) {
104
- this.setMainViewScenePath(ROOT_DIR);
111
+ this.setMainViewScenePath(INIT_DIR);
105
112
  this.createRootDirScenesCallback();
106
113
  this.onRootDirRemoved();
107
114
  emitter.emit("rootDirRemoved");
108
115
  return;
109
116
  }
117
+ // 如果移除的 path 跟 MainViewScenePath 相同就取当前目录的当前 index
110
118
  const mainViewScenePath = this.store.getMainViewScenePath();
111
119
  if (this.room && mainViewScenePath) {
112
120
  if (mainViewScenePath === scenePath) {
113
- this.setMainViewScenePath(ROOT_DIR);
121
+ const nextPath = this.callbacksNode?.scenes[this.store.getMainViewSceneIndex()];
122
+ this.setMainViewScenePath(`/${nextPath}` || INIT_DIR);
114
123
  }
115
124
  }
116
- }
125
+ };
117
126
 
118
127
  /**
119
128
  * 根目录被删除时所有的 scene 都会被删除.
@@ -133,7 +142,7 @@ export class AppManager {
133
142
  this.appProxies.forEach(appProxy => {
134
143
  appProxy.emitAppIsWritableChange();
135
144
  });
136
- }
145
+ };
137
146
 
138
147
  private onPlayerSeek = (time: number) => {
139
148
  this.appProxies.forEach(appProxy => {
@@ -141,7 +150,7 @@ export class AppManager {
141
150
  });
142
151
  this.attributesUpdateCallback(this.attributes.apps);
143
152
  this.onAppDelete(this.attributes.apps);
144
- }
153
+ };
145
154
 
146
155
  private createRootDirScenesCallback = () => {
147
156
  let isRecreate = false;
@@ -157,7 +166,7 @@ export class AppManager {
157
166
  this.updateSceneState(this.callbacksNode);
158
167
  this.mainViewScenesLength = this.callbacksNode.scenes.length;
159
168
  if (isRecreate) {
160
- callbacks.emit("mainViewScenesLengthChange", this.callbacksNode.scenes.length);
169
+ this.emitMainViewScenesChange(this.callbacksNode.scenes.length);
161
170
  }
162
171
  }
163
172
  };
@@ -165,7 +174,12 @@ export class AppManager {
165
174
  private onSceneChange = (node: ScenesCallbacksNode) => {
166
175
  this.mainViewScenesLength = node.scenes.length;
167
176
  this.updateSceneState(node);
168
- callbacks.emit("mainViewScenesLengthChange", this.mainViewScenesLength);
177
+ this.emitMainViewScenesChange(this.mainViewScenesLength);
178
+ };
179
+
180
+ private emitMainViewScenesChange = (length: number) => {
181
+ callbacks.emit("mainViewScenesLengthChange", length);
182
+ emitter.emit("changePageState");
169
183
  };
170
184
 
171
185
  private updateSceneState = (node: ScenesCallbacksNode) => {
@@ -250,14 +264,7 @@ export class AppManager {
250
264
  this.refresher?.add("minimized", () => {
251
265
  return autorun(() => {
252
266
  const minimized = this.attributes.minimized;
253
- if (this.boxManager?.minimized !== minimized) {
254
- if (minimized === true) {
255
- this.boxManager?.blurAllBox();
256
- }
257
- setTimeout(() => {
258
- this.boxManager?.setMinimized(Boolean(minimized));
259
- }, 0);
260
- }
267
+ this.onMinimized(minimized);
261
268
  });
262
269
  });
263
270
  this.refresher?.add("mainViewIndex", () => {
@@ -265,6 +272,7 @@ export class AppManager {
265
272
  const mainSceneIndex = get(this.attributes, "_mainSceneIndex");
266
273
  if (mainSceneIndex !== undefined && this._prevSceneIndex !== mainSceneIndex) {
267
274
  callbacks.emit("mainViewSceneIndexChange", mainSceneIndex);
275
+ emitter.emit("changePageState");
268
276
  if (this.callbacksNode) {
269
277
  this.updateSceneState(this.callbacksNode);
270
278
  }
@@ -292,13 +300,16 @@ export class AppManager {
292
300
  }
293
301
  });
294
302
  });
303
+ this.refresher?.add("registeredChange", () => {
304
+ return autorun(() => {
305
+ const registered = get(this.attributes, Fields.Registered);
306
+ this.onRegisteredChange(registered);
307
+ });
308
+ });
295
309
  if (!this.attributes.apps || Object.keys(this.attributes.apps).length === 0) {
296
310
  const mainScenePath = this.store.getMainViewScenePath();
297
311
  if (!mainScenePath) return;
298
- const sceneState = this.displayer.state.sceneState;
299
- if (sceneState.scenePath !== mainScenePath) {
300
- setScenePath(this.room, mainScenePath);
301
- }
312
+ this.resetScenePath(mainScenePath);
302
313
  }
303
314
  this.displayerWritableListener(!this.room?.isWritable);
304
315
  this.displayer.callbacks.on("onEnableWriteNowChanged", this.displayerWritableListener);
@@ -362,6 +373,30 @@ export class AppManager {
362
373
  }
363
374
  }
364
375
 
376
+ private onRegisteredChange = (registered: Record<string, SyncRegisterAppPayload>) => {
377
+ if (!registered) return;
378
+ Object.entries(registered).forEach(([kind, payload]) => {
379
+ if (!appRegister.appClasses.has(kind)) {
380
+ appRegister.register({
381
+ kind,
382
+ src: payload.src,
383
+ name: payload.name,
384
+ });
385
+ }
386
+ });
387
+ };
388
+
389
+ private onMinimized = (minimized: boolean | undefined) => {
390
+ if (this.boxManager?.minimized !== minimized) {
391
+ if (minimized === true) {
392
+ this.boxManager?.blurAllBox();
393
+ }
394
+ setTimeout(() => {
395
+ this.boxManager?.setMinimized(Boolean(minimized));
396
+ }, 0);
397
+ }
398
+ };
399
+
365
400
  public refresh() {
366
401
  this.attributesUpdateCallback(this.attributes.apps);
367
402
  }
@@ -405,6 +440,13 @@ export class AppManager {
405
440
  }
406
441
  }
407
442
 
443
+ private resetScenePath(scenePath: string) {
444
+ const sceneState = this.displayer.state.sceneState;
445
+ if (sceneState.scenePath !== scenePath) {
446
+ setScenePath(this.room, scenePath);
447
+ }
448
+ }
449
+
408
450
  public async addApp(params: AddAppParams, isDynamicPPT: boolean): Promise<string | undefined> {
409
451
  log("addApp", params);
410
452
  const { appId, needFocus } = await this.beforeAddApp(params, isDynamicPPT);
@@ -17,6 +17,7 @@ export enum Fields {
17
17
  Position = "position",
18
18
  CursorState = "cursorState",
19
19
  FullPath = "fullPath",
20
+ Registered = "registered",
20
21
  }
21
22
 
22
23
  export type Apps = {
@@ -22,6 +22,7 @@ export type EmitterEvent = {
22
22
  focusedChange: { focused: string | undefined; prev: string | undefined };
23
23
  rootDirRemoved: undefined;
24
24
  setReadonly: boolean;
25
+ changePageState: undefined;
25
26
  };
26
27
 
27
28
  export type EmitterType = Emittery<EmitterEvent>;
@@ -0,0 +1,31 @@
1
+ import type { AppManager } from "./AppManager";
2
+ import { callbacks } from "./callback";
3
+ import { emitter } from "./InternalEmitter";
4
+
5
+ export type PageState = {
6
+ index: number;
7
+ length: number;
8
+ }
9
+
10
+ export class PageStateImpl {
11
+ constructor(private manager: AppManager) {
12
+ emitter.on("changePageState", () => {
13
+ callbacks.emit("pageStateChange", this.toObject())
14
+ });
15
+ };
16
+
17
+ public get index(): number {
18
+ return this.manager?.store.getMainViewSceneIndex() || 0;
19
+ }
20
+
21
+ public get length(): number {
22
+ return this.manager?.mainViewScenesLength || 0;
23
+ }
24
+
25
+ public toObject(): PageState {
26
+ return {
27
+ index: this.index,
28
+ length: this.length
29
+ }
30
+ }
31
+ }
@@ -8,12 +8,25 @@ export type LoadAppEvent = {
8
8
  reason?: string;
9
9
  };
10
10
 
11
+ export type SyncRegisterAppPayload = { kind: string, src: string, name: string | undefined };
12
+ export type SyncRegisterApp = (payload: SyncRegisterAppPayload) => void;
13
+
11
14
  class AppRegister {
12
15
  public kindEmitters: Map<string, Emittery<RegisterEvents>> = new Map();
13
16
  public registered: Map<string, RegisterParams> = new Map();
14
17
  public appClassesCache: Map<string, Promise<NetlessApp>> = new Map();
15
18
  public appClasses: Map<string, () => Promise<NetlessApp>> = new Map();
16
19
 
20
+ private syncRegisterApp: SyncRegisterApp | null = null;
21
+
22
+ public setSyncRegisterApp(fn: SyncRegisterApp) {
23
+ this.syncRegisterApp = fn;
24
+ }
25
+
26
+ public onSyncRegisterAppChange = (payload: SyncRegisterAppPayload) => {
27
+ this.register({ kind: payload.kind, src: payload.src });
28
+ }
29
+
17
30
  public async register(params: RegisterParams): Promise<void> {
18
31
  this.appClassesCache.delete(params.kind);
19
32
  this.registered.set(params.kind, params);
@@ -23,7 +36,7 @@ class AppRegister {
23
36
 
24
37
  if (typeof srcOrAppOrFunction === "string") {
25
38
  downloadApp = async () => {
26
- let appClass = (await loadApp(srcOrAppOrFunction, params.kind)) as any;
39
+ let appClass = (await loadApp(srcOrAppOrFunction, params.kind, params.name)) as any;
27
40
  if (appClass) {
28
41
  if (appClass.__esModule) {
29
42
  appClass = appClass.default;
@@ -35,6 +48,9 @@ class AppRegister {
35
48
  );
36
49
  }
37
50
  };
51
+ if (this.syncRegisterApp) {
52
+ this.syncRegisterApp({ kind: params.kind, src: srcOrAppOrFunction, name: params.name });
53
+ }
38
54
  } else if (typeof srcOrAppOrFunction === "function") {
39
55
  downloadApp = srcOrAppOrFunction;
40
56
  } else {
@@ -58,6 +74,17 @@ class AppRegister {
58
74
  }
59
75
  }
60
76
 
77
+ public unregister(kind: string) {
78
+ this.appClasses.delete(kind);
79
+ this.appClassesCache.delete(kind);
80
+ this.registered.delete(kind);
81
+ const kindEmitter = this.kindEmitters.get(kind);
82
+ if (kindEmitter) {
83
+ kindEmitter.clearListeners();
84
+ this.kindEmitters.delete(kind);
85
+ }
86
+ }
87
+
61
88
  public async notifyApp<T extends keyof RegisterEvents>(
62
89
  kind: string,
63
90
  event: T,
@@ -33,7 +33,8 @@ export const setViewSceneIndex = (view: View, index: number) => {
33
33
  export const setScenePath = (room: Room | undefined, scenePath: string) => {
34
34
  if (room && room.isWritable) {
35
35
  if (room.state.sceneState.scenePath !== scenePath) {
36
- room.setScenePath(scenePath);
36
+ const nextScenePath = scenePath === "/" ? "" : scenePath;
37
+ room.setScenePath(nextScenePath);
37
38
  }
38
39
  }
39
40
  };
package/src/callback.ts CHANGED
@@ -2,6 +2,7 @@ import Emittery from "emittery";
2
2
  import type { TeleBoxColorScheme, TELE_BOX_STATE } from "@netless/telebox-insider";
3
3
  import type { CameraState, SceneState, ViewVisionMode } from "white-web-sdk";
4
4
  import type { LoadAppEvent } from "./Register";
5
+ import type { PageState } from "./PageState";
5
6
 
6
7
  export type PublicEvent = {
7
8
  mainViewModeChange: ViewVisionMode;
@@ -18,6 +19,7 @@ export type PublicEvent = {
18
19
  loadApp: LoadAppEvent;
19
20
  ready: undefined; // 所有 APP 创建完毕时触发
20
21
  sceneStateChange: SceneState;
22
+ pageStateChange: PageState;
21
23
  };
22
24
 
23
25
  export type CallbacksType = Emittery<PublicEvent>;
package/src/constants.ts CHANGED
@@ -49,3 +49,4 @@ export const SET_SCENEPATH_DELAY = 100; // 设置 scenePath 的延迟事件
49
49
  export const DEFAULT_CONTAINER_RATIO = 9 / 16;
50
50
 
51
51
  export const ROOT_DIR = "/";
52
+ export const INIT_DIR = "/init";
package/src/index.ts CHANGED
@@ -6,13 +6,14 @@ import { checkVersion, setupWrapper } from "./Helper";
6
6
  import { ContainerResizeObserver } from "./ContainerResizeObserver";
7
7
  import { createBoxManager } from "./BoxManager";
8
8
  import { CursorManager } from "./Cursor";
9
- import { DEFAULT_CONTAINER_RATIO, Events, ROOT_DIR } from "./constants";
9
+ import { DEFAULT_CONTAINER_RATIO, Events, INIT_DIR, ROOT_DIR } from "./constants";
10
10
  import { emitter } from "./InternalEmitter";
11
11
  import { Fields } from "./AttributesDelegate";
12
12
  import { initDb } from "./Register/storage";
13
13
  import { InvisiblePlugin, isPlayer, isRoom, RoomPhase, ViewMode } from "white-web-sdk";
14
14
  import { isEqual, isNull, isObject, omit } from "lodash";
15
15
  import { log } from "./Utils/log";
16
+ import { PageStateImpl } from "./PageState";
16
17
  import { ReconnectRefresher } from "./ReconnectRefresher";
17
18
  import { replaceRoomFunction } from "./Utils/RoomHacker";
18
19
  import { setupBuiltin } from "./BuiltinApps";
@@ -56,6 +57,7 @@ import type { TeleBoxColorScheme, TeleBoxState } from "@netless/telebox-insider"
56
57
  import type { AppProxy } from "./AppProxy";
57
58
  import type { PublicEvent } from "./Callback";
58
59
  import type Emittery from "emittery";
60
+ import type { PageState } from "./PageState";
59
61
 
60
62
  export type WindowMangerAttributes = {
61
63
  modelValue?: string;
@@ -168,6 +170,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
168
170
  public cursorManager?: CursorManager;
169
171
  public viewMode = ViewMode.Broadcaster;
170
172
  public isReplay = isPlayer(this.displayer);
173
+ private _pageState?: PageStateImpl;
171
174
 
172
175
  private boxManager?: BoxManager;
173
176
  private static params?: MountParams;
@@ -235,6 +238,7 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
235
238
  await manager.ensureAttributes();
236
239
 
237
240
  manager.appManager = new AppManager(manager);
241
+ manager._pageState = new PageStateImpl(manager.appManager);
238
242
  manager.cursorManager = new CursorManager(manager.appManager, Boolean(cursor));
239
243
 
240
244
  if (params.container) {
@@ -368,6 +372,13 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
368
372
  return appRegister.register(params);
369
373
  }
370
374
 
375
+ /**
376
+ * 注销插件
377
+ */
378
+ public static unregister(kind: string) {
379
+ return appRegister.unregister(kind);
380
+ }
381
+
371
382
  /**
372
383
  * 创建一个 app 至白板
373
384
  */
@@ -671,6 +682,14 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
671
682
  }
672
683
  }
673
684
 
685
+ public get pageState(): PageState {
686
+ if (this._pageState) {
687
+ return this._pageState.toObject();
688
+ } else {
689
+ throw new AppManagerNotInitError();
690
+ }
691
+ }
692
+
674
693
  /**
675
694
  * 查询所有的 App
676
695
  */
@@ -851,11 +870,14 @@ export class WindowManager extends InvisiblePlugin<WindowMangerAttributes> {
851
870
  this.safeSetAttributes({ [Fields.Cursors]: {} });
852
871
  }
853
872
  if (!this.attributes["_mainScenePath"]) {
854
- this.safeSetAttributes({ _mainScenePath: ROOT_DIR });
873
+ this.safeSetAttributes({ _mainScenePath: INIT_DIR });
855
874
  }
856
875
  if (!this.attributes["_mainSceneIndex"]) {
857
876
  this.safeSetAttributes({ _mainSceneIndex: 0 });
858
877
  }
878
+ if (!this.attributes[Fields.Registered]) {
879
+ this.safeSetAttributes({ [Fields.Registered]: {} });
880
+ }
859
881
  }
860
882
  }
861
883
  }
@@ -865,4 +887,4 @@ setupBuiltin();
865
887
  export * from "./typings";
866
888
 
867
889
  export { BuiltinApps } from "./BuiltinApps";
868
- export type { PublicEvent } from "./callback";
890
+ export type { PublicEvent } from "./callback";