@netless/window-manager 1.0.13-test.9 → 1.0.14
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/dist/index.d.ts +61 -4
- package/dist/index.js +14 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7513 -7167
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/App/AppProxy.ts +6 -6
- package/src/AppListener.ts +0 -1
- package/src/AppManager.ts +13 -16
- package/src/AttributesDelegate.ts +0 -7
- package/src/BoxManager.ts +0 -1
- package/src/BuiltinApps.ts +9 -6
- package/src/ContainerResizeObserver.ts +18 -14
- package/src/InternalEmitter.ts +1 -0
- package/src/Utils/Common.ts +0 -1
- package/src/Utils/Reactive.ts +2 -2
- package/src/Utils/RoomHacker.ts +15 -1
- package/src/Utils/attributesLogStringify.ts +78 -0
- package/src/Utils/log.ts +228 -0
- package/src/View/MainView.ts +138 -13
- package/src/index.ts +19 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/window-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Multi-window mode for Netless Whiteboard",
|
|
5
5
|
"author": "l1shen <lishen1635@gmail.com> (https://github.com/l1shen)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"jspdf": "2.5.1",
|
|
26
|
-
"white-web-sdk": "^2.16.
|
|
26
|
+
"white-web-sdk": "^2.16.54"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"jspdf": {
|
|
@@ -72,6 +72,6 @@
|
|
|
72
72
|
"typescript": "^4.5.5",
|
|
73
73
|
"vite": "^2.9.9",
|
|
74
74
|
"vitest": "^0.14.1",
|
|
75
|
-
"white-web-sdk": "^2.16.
|
|
75
|
+
"white-web-sdk": "^2.16.54"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/App/AppProxy.ts
CHANGED
|
@@ -149,7 +149,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
149
149
|
): Promise<{ appId: string; app: NetlessApp }> {
|
|
150
150
|
const params = this.params;
|
|
151
151
|
if (!params.kind) {
|
|
152
|
-
this.Logger
|
|
152
|
+
this.Logger && this.Logger.error(`[WindowManager]: kind require`);
|
|
153
153
|
throw new Error("[WindowManager]: kind require");
|
|
154
154
|
}
|
|
155
155
|
const appImpl = await appRegister.appClasses.get(params.kind)?.();
|
|
@@ -167,7 +167,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
167
167
|
params.isDragContent
|
|
168
168
|
);
|
|
169
169
|
} else {
|
|
170
|
-
this.Logger
|
|
170
|
+
this.Logger && this.Logger.error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
|
|
171
171
|
throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
|
|
172
172
|
}
|
|
173
173
|
internalEmitter.emit("updateManagerRect");
|
|
@@ -216,7 +216,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
216
216
|
}
|
|
217
217
|
setTimeout(async () => {
|
|
218
218
|
// 延迟执行 setup, 防止初始化的属性没有更新成功
|
|
219
|
-
this.Logger
|
|
219
|
+
this.Logger && this.Logger.info(`[WindowManager]: setup app ${this.kind}, appId: ${appId}`);
|
|
220
220
|
const result = await app.setup(context);
|
|
221
221
|
this.appResult = result;
|
|
222
222
|
appRegister.notifyApp(this.kind, "created", { appId, result });
|
|
@@ -251,7 +251,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
251
251
|
this.boxManager.focusBox({ appId }, false);
|
|
252
252
|
}
|
|
253
253
|
} catch (error: any) {
|
|
254
|
-
this.Logger
|
|
254
|
+
this.Logger && this.Logger.error(`[WindowManager]: app setup error: ${error.message}`);
|
|
255
255
|
throw new Error(`[WindowManager]: app setup error: ${error.message}`);
|
|
256
256
|
}
|
|
257
257
|
}
|
|
@@ -538,7 +538,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
538
538
|
await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
|
|
539
539
|
await this.appEmitter.emit("destroy", { error });
|
|
540
540
|
} catch (error) {
|
|
541
|
-
this.Logger
|
|
541
|
+
this.Logger && this.Logger.error(`[WindowManager]: notifyApp error: ${error.message}`);
|
|
542
542
|
console.error("[WindowManager]: notifyApp error", error.message, error.stack);
|
|
543
543
|
}
|
|
544
544
|
this.appEmitter.clearListeners();
|
|
@@ -561,7 +561,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
561
561
|
this.manager.refresher.remove(this.stateKey);
|
|
562
562
|
this.manager.refresher.remove(`${this.id}-fullPath`);
|
|
563
563
|
this._prevFullPath = undefined;
|
|
564
|
-
this.Logger
|
|
564
|
+
this.Logger && this.Logger.info(`[WindowManager]: destroy app ${this.kind} appId: ${this.id}`);
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
public close(): Promise<void> {
|
package/src/AppListener.ts
CHANGED
package/src/AppManager.ts
CHANGED
|
@@ -133,7 +133,6 @@ export class AppManager {
|
|
|
133
133
|
this.createRootDirScenesCallback();
|
|
134
134
|
|
|
135
135
|
appRegister.setSyncRegisterApp(payload => {
|
|
136
|
-
this.Logger?.info(`[WindowManager] syncRegisterApp ${JSON.stringify(payload)}`);
|
|
137
136
|
this.safeUpdateAttributes([Fields.Registered, payload.kind], payload);
|
|
138
137
|
});
|
|
139
138
|
}
|
|
@@ -146,7 +145,7 @@ export class AppManager {
|
|
|
146
145
|
const { scenePath } = params;
|
|
147
146
|
// 如果移除根目录就把 scenePath 设置为初始值
|
|
148
147
|
if (scenePath === ROOT_DIR) {
|
|
149
|
-
console.log("[window-manager] onRemoveScenes
|
|
148
|
+
console.log("[window-manager] onRemoveScenes ROOT_DIR");
|
|
150
149
|
await this.onRootDirRemoved();
|
|
151
150
|
this.dispatchInternalEvent(Events.RootDirRemoved);
|
|
152
151
|
return;
|
|
@@ -159,7 +158,7 @@ export class AppManager {
|
|
|
159
158
|
sceneName = this.callbacksNode?.scenes[nextIndex];
|
|
160
159
|
}
|
|
161
160
|
if (sceneName) {
|
|
162
|
-
console.log(
|
|
161
|
+
console.log(`[window-manager] onRemoveScenes setMainViewScenePath ${ROOT_DIR}${sceneName}`);
|
|
163
162
|
this.setMainViewScenePath(`${ROOT_DIR}${sceneName}`);
|
|
164
163
|
}
|
|
165
164
|
await this.setMainViewSceneIndex(nextIndex);
|
|
@@ -605,7 +604,7 @@ export class AppManager {
|
|
|
605
604
|
try {
|
|
606
605
|
const appAttributes = this.attributes[id];
|
|
607
606
|
if (!appAttributes) {
|
|
608
|
-
this.Logger
|
|
607
|
+
this.Logger && this.Logger.error(
|
|
609
608
|
`[WindowManager]: appAttributes is undefined, appId: ${id}`
|
|
610
609
|
);
|
|
611
610
|
throw new Error("appAttributes is undefined");
|
|
@@ -713,11 +712,12 @@ export class AppManager {
|
|
|
713
712
|
callbacks.emit("onMainViewMounted", mainView);
|
|
714
713
|
const hasRoot = this.hasRoot(mainView.divElement);
|
|
715
714
|
const rect = this.getRectByDivElement(mainView.divElement);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
715
|
+
let log = `[window-manager] bindMainView hasRoot:${hasRoot}, rect:${JSON.stringify(rect)}, outerHeight:${window.outerHeight}, outerWidth:${window.outerWidth}`;
|
|
716
|
+
const visualViewport = window.visualViewport;
|
|
717
|
+
if (visualViewport) {
|
|
718
|
+
log += `, visualViewportWidth:${visualViewport.width}, visualViewportHeight:${visualViewport.height}, visualViewportOffsetLeft:${visualViewport.offsetLeft}, visualViewportOffsetTop:${visualViewport.offsetTop}`;
|
|
719
|
+
}
|
|
720
|
+
console.log(log);
|
|
721
721
|
}
|
|
722
722
|
|
|
723
723
|
private hasRoot(divElement: HTMLDivElement){
|
|
@@ -818,7 +818,7 @@ export class AppManager {
|
|
|
818
818
|
return appProxy;
|
|
819
819
|
} else {
|
|
820
820
|
this.appStatus.delete(appId);
|
|
821
|
-
this.Logger
|
|
821
|
+
this.Logger && this.Logger.error(`[WindowManager]: initialize AppProxy failed, appId: ${appId}`);
|
|
822
822
|
throw new Error("[WindowManger]: initialize AppProxy failed");
|
|
823
823
|
}
|
|
824
824
|
}
|
|
@@ -858,11 +858,11 @@ export class AppManager {
|
|
|
858
858
|
const scenePathType = this.displayer.scenePathType(scenePath);
|
|
859
859
|
const sceneDir = parseSceneDir(scenePath);
|
|
860
860
|
if (sceneDir !== ROOT_DIR) {
|
|
861
|
-
this.Logger
|
|
861
|
+
this.Logger && this.Logger.error(`[WindowManager]: main view scenePath must in root dir "/"`);
|
|
862
862
|
throw new Error(`[WindowManager]: main view scenePath must in root dir "/"`);
|
|
863
863
|
}
|
|
864
864
|
if (scenePathType === ScenePathType.None) {
|
|
865
|
-
this.Logger
|
|
865
|
+
this.Logger && this.Logger.error(`[WindowManager]: ${scenePath} not valid scene`);
|
|
866
866
|
throw new Error(`[WindowManager]: ${scenePath} not valid scene`);
|
|
867
867
|
} else if (scenePathType === ScenePathType.Page) {
|
|
868
868
|
await this._setMainViewScenePath(scenePath);
|
|
@@ -879,7 +879,6 @@ export class AppManager {
|
|
|
879
879
|
private async _setMainViewScenePath(scenePath: string) {
|
|
880
880
|
const success = this.setMainViewFocusPath(scenePath);
|
|
881
881
|
if (success) {
|
|
882
|
-
console.log("[window-manager] _setMainViewScenePath ", scenePath);
|
|
883
882
|
this.safeSetAttributes({ _mainScenePath: scenePath });
|
|
884
883
|
this.store.setMainViewFocusPath(this.mainView);
|
|
885
884
|
this.updateSceneIndex();
|
|
@@ -896,7 +895,6 @@ export class AppManager {
|
|
|
896
895
|
const pageName = scenePath.replace(sceneDir, "").replace("/", "");
|
|
897
896
|
const index = scenes.findIndex(scene => scene.name === pageName);
|
|
898
897
|
if (isInteger(index) && index >= 0) {
|
|
899
|
-
console.log("[window-manager] updateSceneIndex ", index);
|
|
900
898
|
this.safeSetAttributes({ _mainSceneIndex: index });
|
|
901
899
|
}
|
|
902
900
|
}
|
|
@@ -915,14 +913,13 @@ export class AppManager {
|
|
|
915
913
|
this.dispatchSetMainViewScenePath(scenePath);
|
|
916
914
|
}
|
|
917
915
|
} else {
|
|
918
|
-
this.Logger
|
|
916
|
+
this.Logger && this.Logger.error(`[WindowManager]: ${index} not valid index`);
|
|
919
917
|
throw new Error(`[WindowManager]: ${index} not valid index`);
|
|
920
918
|
}
|
|
921
919
|
}
|
|
922
920
|
}
|
|
923
921
|
|
|
924
922
|
private dispatchSetMainViewScenePath(scenePath: string): void {
|
|
925
|
-
console.log("[window-manager] dispatchSetMainViewScenePath ", JSON.stringify(scenePath));
|
|
926
923
|
this.dispatchInternalEvent(Events.SetMainViewScenePath, { nextScenePath: scenePath });
|
|
927
924
|
callbacks.emit("mainViewScenePathChange", scenePath);
|
|
928
925
|
// 兼容 15 的 SDK, 需要 room 的当前 ScenePath
|
|
@@ -7,7 +7,6 @@ import type { Cursor } from "./Cursor/Cursor";
|
|
|
7
7
|
import { getExtendClass } from "./Utils/extendClass";
|
|
8
8
|
import type { ExtendClass } from "./Utils/extendClass";
|
|
9
9
|
import type { NotMinimizedBoxState, TeleBoxState } from "@netless/telebox-insider";
|
|
10
|
-
import { LocalConsole } from "./Utils/log";
|
|
11
10
|
|
|
12
11
|
export enum Fields {
|
|
13
12
|
Apps = "apps",
|
|
@@ -55,7 +54,6 @@ export type ISize = Size & { id: string };
|
|
|
55
54
|
|
|
56
55
|
export class AttributesDelegate {
|
|
57
56
|
static readonly kind = "AttributesDelegate";
|
|
58
|
-
private setMainViewCameraConsole = new LocalConsole("setMainViewCamera", 50);
|
|
59
57
|
constructor(private context: StoreContext) {}
|
|
60
58
|
|
|
61
59
|
public setContext(context: StoreContext) {
|
|
@@ -196,12 +194,10 @@ export class AttributesDelegate {
|
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
public setMainViewScenePath(scenePath: string) {
|
|
199
|
-
console.log("[window-manager] setMainViewScenePath ", scenePath);
|
|
200
197
|
this.context.safeSetAttributes({ _mainScenePath: scenePath });
|
|
201
198
|
}
|
|
202
199
|
|
|
203
200
|
public setMainViewSceneIndex(index: number) {
|
|
204
|
-
console.log("[window-manager] setMainViewSceneIndex ", index);
|
|
205
201
|
this.context.safeSetAttributes({ _mainSceneIndex: index });
|
|
206
202
|
}
|
|
207
203
|
|
|
@@ -214,19 +210,16 @@ export class AttributesDelegate {
|
|
|
214
210
|
}
|
|
215
211
|
|
|
216
212
|
public setMainViewCamera(camera: ICamera) {
|
|
217
|
-
this.setMainViewCameraConsole.log(JSON.stringify(camera));
|
|
218
213
|
this.context.safeSetAttributes({ [Fields.MainViewCamera]: { ...camera } });
|
|
219
214
|
}
|
|
220
215
|
|
|
221
216
|
public setMainViewSize(size: ISize) {
|
|
222
217
|
if (size.width === 0 || size.height === 0) return;
|
|
223
|
-
console.log("[window-manager] setMainViewSize ", JSON.stringify(size));
|
|
224
218
|
this.context.safeSetAttributes({ [Fields.MainViewSize]: { ...size } });
|
|
225
219
|
}
|
|
226
220
|
|
|
227
221
|
public setMainViewCameraAndSize(camera: ICamera, size: ISize) {
|
|
228
222
|
if (size.width === 0 || size.height === 0) return;
|
|
229
|
-
console.log("[window-manager] setMainViewCameraAndSize ", JSON.stringify(camera), JSON.stringify(size));
|
|
230
223
|
this.context.safeSetAttributes({
|
|
231
224
|
[Fields.MainViewCamera]: { ...camera },
|
|
232
225
|
[Fields.MainViewSize]: { ...size },
|
package/src/BoxManager.ts
CHANGED
|
@@ -420,7 +420,6 @@ export class BoxManager {
|
|
|
420
420
|
const rect = this.mainView.divElement?.getBoundingClientRect();
|
|
421
421
|
if (rect && rect.width > 0 && rect.height > 0) {
|
|
422
422
|
const containerRect = { x: 0, y: 0, width: rect.width, height: rect.height };
|
|
423
|
-
console.log("[window-manager] updateManagerRect" + JSON.stringify(containerRect) + "mainView" + this.mainView.size);
|
|
424
423
|
this.teleBoxManager.setContainerRect(containerRect);
|
|
425
424
|
this.context.notifyContainerRectUpdate(this.teleBoxManager.containerRect);
|
|
426
425
|
}
|
package/src/BuiltinApps.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import AppDocsViewer from "@netless/app-docs-viewer";
|
|
2
|
-
import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
|
|
3
2
|
import { WindowManager } from "./index";
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
const loadAppMediaPlayer = async () => {
|
|
5
|
+
const mod = await import("@netless/app-media-player");
|
|
6
6
|
if (WindowManager.debug) {
|
|
7
|
-
setOptions({ verbose: true });
|
|
7
|
+
mod.setOptions({ verbose: true });
|
|
8
8
|
}
|
|
9
|
+
return (mod.default || mod) as any;
|
|
10
|
+
};
|
|
9
11
|
|
|
12
|
+
export const setupBuiltin = () => {
|
|
10
13
|
WindowManager.register({
|
|
11
14
|
kind: AppDocsViewer.kind,
|
|
12
15
|
src: AppDocsViewer,
|
|
13
16
|
});
|
|
14
17
|
WindowManager.register({
|
|
15
|
-
kind:
|
|
16
|
-
src:
|
|
18
|
+
kind: "MediaPlayer",
|
|
19
|
+
src: loadAppMediaPlayer,
|
|
17
20
|
});
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
export const BuiltinApps = {
|
|
21
24
|
DocsViewer: AppDocsViewer.kind as string,
|
|
22
|
-
MediaPlayer:
|
|
25
|
+
MediaPlayer: "MediaPlayer" as string,
|
|
23
26
|
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
|
|
2
|
-
import { isFunction } from "lodash";
|
|
3
2
|
import { WindowManager } from "./index";
|
|
4
3
|
import type { EmitterType } from "./InternalEmitter";
|
|
5
4
|
import type { UnsubscribeFn } from "emittery";
|
|
5
|
+
import { LocalConsole } from "./Utils/log";
|
|
6
6
|
|
|
7
7
|
const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
|
|
8
8
|
|
|
9
9
|
export class ContainerResizeObserver {
|
|
10
10
|
private containerResizeObserver?: ResizeObserver;
|
|
11
11
|
private disposer?: UnsubscribeFn;
|
|
12
|
+
|
|
13
|
+
private updateSizerLocalConsole = new LocalConsole("updateSizer", 100);
|
|
12
14
|
|
|
13
15
|
constructor(private emitter: EmitterType) {}
|
|
14
16
|
|
|
@@ -28,22 +30,19 @@ export class ContainerResizeObserver {
|
|
|
28
30
|
sizer: HTMLElement,
|
|
29
31
|
wrapper: HTMLDivElement
|
|
30
32
|
) {
|
|
31
|
-
|
|
32
|
-
this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
|
|
33
|
+
this.updateSizer(container.getBoundingClientRect(), sizer, wrapper, 'observePlaygroundSize');
|
|
33
34
|
|
|
34
35
|
this.containerResizeObserver = new ResizeObserver(entries => {
|
|
35
36
|
const containerRect = entries[0]?.contentRect;
|
|
36
37
|
if (containerRect) {
|
|
37
|
-
this.updateSizer(containerRect, sizer, wrapper);
|
|
38
|
-
console.log(`[window-manager] containerResizeObserver ${JSON.stringify(containerRect)}`);
|
|
38
|
+
this.updateSizer(containerRect, sizer, wrapper, 'containerResizeObserver');
|
|
39
39
|
this.emitter.emit("playgroundSizeChange", containerRect);
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
this.disposer = this.emitter.on("containerSizeRatioUpdate", () => {
|
|
44
44
|
const containerRect = container.getBoundingClientRect();
|
|
45
|
-
|
|
46
|
-
this.updateSizer(containerRect, sizer, wrapper);
|
|
45
|
+
this.updateSizer(containerRect, sizer, wrapper, 'containerSizeRatioUpdate');
|
|
47
46
|
this.emitter.emit("playgroundSizeChange", containerRect);
|
|
48
47
|
});
|
|
49
48
|
|
|
@@ -53,7 +52,8 @@ export class ContainerResizeObserver {
|
|
|
53
52
|
public updateSizer(
|
|
54
53
|
{ width, height }: DOMRectReadOnly,
|
|
55
54
|
sizer: HTMLElement,
|
|
56
|
-
wrapper: HTMLDivElement
|
|
55
|
+
wrapper: HTMLDivElement,
|
|
56
|
+
origin?: string
|
|
57
57
|
) {
|
|
58
58
|
if (width && height) {
|
|
59
59
|
if (height / width > WindowManager.containerSizeRatio) {
|
|
@@ -65,16 +65,20 @@ export class ContainerResizeObserver {
|
|
|
65
65
|
}
|
|
66
66
|
wrapper.style.width = `${width}px`;
|
|
67
67
|
wrapper.style.height = `${height}px`;
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
69
|
+
this.updateSizerLocalConsole.log(`from ${origin}, traget size: ${JSON.stringify({ width, height })}, wrapperRect: ${wrapperRect.width} ${wrapperRect.height}`);
|
|
70
|
+
this.emitter.emit("wrapperRectChange", {
|
|
71
|
+
width: wrapperRect.width,
|
|
72
|
+
height: wrapperRect.height,
|
|
73
|
+
origin,
|
|
74
|
+
});
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
public disconnect() {
|
|
79
|
+
this.updateSizerLocalConsole.destroy();
|
|
74
80
|
this.containerResizeObserver?.disconnect();
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.disposer = undefined;
|
|
78
|
-
}
|
|
81
|
+
this.disposer?.();
|
|
82
|
+
this.disposer = undefined;
|
|
79
83
|
}
|
|
80
84
|
}
|
package/src/InternalEmitter.ts
CHANGED
|
@@ -29,6 +29,7 @@ export type EmitterEvent = {
|
|
|
29
29
|
changePageState: undefined;
|
|
30
30
|
writableChange: boolean;
|
|
31
31
|
containerSizeRatioUpdate: number;
|
|
32
|
+
wrapperRectChange: { width: number; height: number; origin?: string };
|
|
32
33
|
boxesStatusChange: Map<string, TeleBoxState>;
|
|
33
34
|
lastNotMinimizedBoxesStatusChange: Map<string, NotMinimizedBoxState>;
|
|
34
35
|
};
|
package/src/Utils/Common.ts
CHANGED
|
@@ -34,7 +34,6 @@ export const setScenePath = (room: Room | undefined, scenePath: string) => {
|
|
|
34
34
|
if (room && room.isWritable) {
|
|
35
35
|
if (room.state.sceneState.scenePath !== scenePath) {
|
|
36
36
|
const nextScenePath = scenePath === "/" ? "" : scenePath;
|
|
37
|
-
console.log("[window-manager] real setScenePath for current room ", nextScenePath);
|
|
38
37
|
room.setScenePath(nextScenePath);
|
|
39
38
|
}
|
|
40
39
|
}
|
package/src/Utils/Reactive.ts
CHANGED
|
@@ -30,8 +30,8 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
|
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export const safeListenPropsUpdated = <T
|
|
34
|
-
getProps: () => T,
|
|
33
|
+
export const safeListenPropsUpdated = <T extends Record<string, unknown>>(
|
|
34
|
+
getProps: () => T | null | undefined,
|
|
35
35
|
callback: AkkoObjectUpdatedListener<T>,
|
|
36
36
|
onDestroyed?: (props: unknown) => void
|
|
37
37
|
) => {
|
package/src/Utils/RoomHacker.ts
CHANGED
|
@@ -37,10 +37,24 @@ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager)
|
|
|
37
37
|
room.scalePptToFit = (...args) => {
|
|
38
38
|
_scalePptToFit.call(room, ...args);
|
|
39
39
|
if (manager.appManager?.mainViewProxy) {
|
|
40
|
-
console.log("[window-manager] scalePptToFit " + JSON.stringify(args));
|
|
41
40
|
manager.appManager.mainViewProxy.setCameraAndSize();
|
|
42
41
|
}
|
|
43
42
|
};
|
|
43
|
+
const _putScenes = room.putScenes;
|
|
44
|
+
room.putScenes = (...args) => {
|
|
45
|
+
const [path, scenes] = args;
|
|
46
|
+
const currentScenePath = manager.mainView.focusScenePath;
|
|
47
|
+
if (currentScenePath && path && scenes) {
|
|
48
|
+
console.log("[window-manager] putScenes " + JSON.stringify(args));
|
|
49
|
+
for (const scene of scenes) {
|
|
50
|
+
if (`${path}${scene.name}` === currentScenePath) {
|
|
51
|
+
console.error(`[window-manager] putScenes: scene name can not be the same as the current scene path: ${currentScenePath}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return _putScenes.call(room, ...args);
|
|
57
|
+
};
|
|
44
58
|
room.moveCamera = (camera: Camera) => manager.moveCamera(camera);
|
|
45
59
|
room.moveCameraToContain = (...args) => manager.moveCameraToContain(...args);
|
|
46
60
|
room.convertToPointInWorld = (...args) => manager.mainView.convertToPointInWorld(...args);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** 合法标识符形式的 key 省略引号,形如 `{aaa:undefined}` */
|
|
2
|
+
function formatAttributesLogObjectKey(key: string): string {
|
|
3
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* attributes 调试日志:对象/数组会写成近似 JS 字面量(保留 `undefined`、数组空洞),避免 `[object Object]`;
|
|
8
|
+
* 并处理 BigInt、循环引用等。
|
|
9
|
+
*/
|
|
10
|
+
export function stringifyForAttributesLog(value: unknown, seen?: WeakSet<object>): string {
|
|
11
|
+
if (value === undefined) {
|
|
12
|
+
return "undefined";
|
|
13
|
+
}
|
|
14
|
+
if (value === null) {
|
|
15
|
+
return "null";
|
|
16
|
+
}
|
|
17
|
+
const t = typeof value;
|
|
18
|
+
if (t === "bigint") {
|
|
19
|
+
return `${value}n`;
|
|
20
|
+
}
|
|
21
|
+
if (t === "symbol") {
|
|
22
|
+
return String(value);
|
|
23
|
+
}
|
|
24
|
+
if (t === "function") {
|
|
25
|
+
const fn = value as (...args: unknown[]) => unknown;
|
|
26
|
+
return `[Function ${fn.name || "anonymous"}]`;
|
|
27
|
+
}
|
|
28
|
+
if (t !== "object") {
|
|
29
|
+
return t === "string" ? JSON.stringify(value as string) : String(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const obj = value as object;
|
|
33
|
+
if (seen?.has(obj)) {
|
|
34
|
+
return "[Circular]";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const nextSeen = seen ?? new WeakSet<object>();
|
|
38
|
+
nextSeen.add(obj);
|
|
39
|
+
try {
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
return `[${Array.from(value as unknown[], (item) =>
|
|
42
|
+
stringifyForAttributesLog(item, nextSeen),
|
|
43
|
+
).join(",")}]`;
|
|
44
|
+
}
|
|
45
|
+
if (value instanceof Date) {
|
|
46
|
+
return JSON.stringify(value.toISOString());
|
|
47
|
+
}
|
|
48
|
+
if (value instanceof RegExp) {
|
|
49
|
+
return String(value);
|
|
50
|
+
}
|
|
51
|
+
const keys = Object.keys(value as object);
|
|
52
|
+
const pairs = keys.map((k) => {
|
|
53
|
+
let v: unknown;
|
|
54
|
+
try {
|
|
55
|
+
v = (value as Record<string, unknown>)[k];
|
|
56
|
+
} catch {
|
|
57
|
+
return `${formatAttributesLogObjectKey(k)}:[Threw]`;
|
|
58
|
+
}
|
|
59
|
+
return `${formatAttributesLogObjectKey(k)}:${stringifyForAttributesLog(v, nextSeen)}`;
|
|
60
|
+
});
|
|
61
|
+
return `{${pairs.join(",")}}`;
|
|
62
|
+
} catch {
|
|
63
|
+
return "[Unserializable]";
|
|
64
|
+
} finally {
|
|
65
|
+
nextSeen.delete(obj);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 仅一层 key 合并:可作为 attributes 片段的「普通对象」(非数组、Date 等) */
|
|
70
|
+
export function isShallowMergeAttributesRecord(value: unknown): value is Record<string, unknown> {
|
|
71
|
+
return (
|
|
72
|
+
value !== null &&
|
|
73
|
+
typeof value === "object" &&
|
|
74
|
+
!Array.isArray(value) &&
|
|
75
|
+
!(value instanceof Date) &&
|
|
76
|
+
!(value instanceof RegExp)
|
|
77
|
+
);
|
|
78
|
+
}
|