@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/src/Utils/log.ts CHANGED
@@ -1,5 +1,30 @@
1
+ import type { Logger } from "white-web-sdk";
2
+ import { isShallowMergeAttributesRecord, stringifyForAttributesLog } from "./attributesLogStringify";
1
3
  import { WindowManager } from "../index";
2
4
 
5
+ /** ArgusLog 经 `logger.info` 上报的单条字符串上限(含前缀) */
6
+ const ARGUS_LOG_INFO_MAX_LENGTH = 1500;
7
+
8
+ function truncateArgusLogInfoMessage(message: string): string {
9
+ if (message.length <= ARGUS_LOG_INFO_MAX_LENGTH) {
10
+ return message;
11
+ }
12
+ const ellipsis = "…";
13
+ return message.slice(0, ARGUS_LOG_INFO_MAX_LENGTH - ellipsis.length) + ellipsis;
14
+ }
15
+
16
+ function keysPathEqual(a: string[], b: string[]): boolean {
17
+ if (a.length !== b.length) {
18
+ return false;
19
+ }
20
+ for (let i = 0; i < a.length; i++) {
21
+ if (a[i] !== b[i]) {
22
+ return false;
23
+ }
24
+ }
25
+ return true;
26
+ }
27
+
3
28
  export const log = (...args: any[]): void => {
4
29
  if (WindowManager.debug) {
5
30
  console.log(`[WindowManager]:`, ...args);
@@ -41,4 +66,207 @@ export class LocalConsole {
41
66
  }
42
67
  console.log(`[window-manager][${this.name}]: ${args.join(", ")}`);
43
68
  }
69
+
70
+ /**
71
+ * 销毁:清除 debounce 定时器与未输出的暂存参数。
72
+ * 持有 LocalConsole 的类在销毁时应调用。
73
+ */
74
+ destroy(): void {
75
+ if (this.flushTimer != null) {
76
+ clearTimeout(this.flushTimer);
77
+ this.flushTimer = null;
78
+ }
79
+ this.pendingArgs = null;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * 按 `[WindowManager][tagName]` 前缀输出。
85
+ * 若传入 `debounceTime`(毫秒):窗口内多次 `log` 不立即输出,只在连续停止调用满 `debounceTime` 后输出**最后一次**的参数(尾部 debounce)。
86
+ */
87
+ export class ArgusLog {
88
+ private pendingArgs: unknown[] | null = null;
89
+ private flushTimer: ReturnType<typeof setTimeout> | null = null;
90
+
91
+ /** debounce 窗口内按一层 key 合并;同 key 后者覆盖;非普通对象则整段待输出被本次值替换 */
92
+ private pendingShallowMerge:
93
+ | { kind: "record"; label: string; data: Record<string, unknown> }
94
+ | { kind: "atom"; label: string; value: unknown }
95
+ | null = null;
96
+ private shallowMergeTimer: ReturnType<typeof setTimeout> | null = null;
97
+
98
+ /** debounce 窗口内 safeUpdateAttributes:同 keys 数组则只更新 value,否则追加一段,flush 时拼成一条 */
99
+ private pendingUpdateSegments: { keys: string[]; value: unknown }[] | null = null;
100
+ private updateMergeTimer: ReturnType<typeof setTimeout> | null = null;
101
+
102
+ constructor(
103
+ private readonly logger: Logger,
104
+ private readonly name: string,
105
+ private readonly debounceTime?: number,
106
+ ) {}
107
+
108
+ private emitInfo(message: string): void {
109
+ this.logger.info(truncateArgusLogInfoMessage(message));
110
+ }
111
+
112
+ private flush(): void {
113
+ this.flushTimer = null;
114
+ const args = this.pendingArgs;
115
+ this.pendingArgs = null;
116
+ if (args === null) {
117
+ return;
118
+ }
119
+ this.emitInfo(`[WindowManager][${this.name}]: ${args.join(", ")}`);
120
+ }
121
+
122
+ private flushShallowMerge(): void {
123
+ this.shallowMergeTimer = null;
124
+ const p = this.pendingShallowMerge;
125
+ this.pendingShallowMerge = null;
126
+ if (p === null) {
127
+ return;
128
+ }
129
+ const body =
130
+ p.kind === "record" ? stringifyForAttributesLog(p.data) : stringifyForAttributesLog(p.value);
131
+ this.emitInfo(`[WindowManager][${this.name}]: ${p.label} ${body}`);
132
+ // 输出后释放合并对象引用,避免长时间持有 attributes 快照
133
+ if (p.kind === "record") {
134
+ for (const k of Object.keys(p.data)) {
135
+ delete p.data[k];
136
+ }
137
+ }
138
+ }
139
+
140
+ log(...args: unknown[]): void {
141
+ const ms = this.debounceTime;
142
+ if (ms != null && ms > 0) {
143
+ this.pendingArgs = args;
144
+ if (this.flushTimer != null) {
145
+ clearTimeout(this.flushTimer);
146
+ }
147
+ this.flushTimer = setTimeout(() => this.flush(), ms);
148
+ return;
149
+ }
150
+ this.emitInfo(`[WindowManager][${this.name}]: ${args.join(", ")}`);
151
+ }
152
+
153
+ /**
154
+ * 带 debounce 时:窗口内多次调用会把「一层 key」合并进同一条日志(不同 key 并存,同 key 取最后一次)。
155
+ * `payload` 为普通对象时做浅合并;否则视为原子值,覆盖当前待合并状态(丢弃此前累积的对象 key)。
156
+ * 无 debounce 或时间为 0 时立即输出。
157
+ */
158
+ logDebouncedShallowMerge(label: string, payload: unknown): void {
159
+ const ms = this.debounceTime;
160
+ const debounced = ms != null && ms > 0;
161
+
162
+ const emit = (text: string): void => {
163
+ this.emitInfo(`[WindowManager][${this.name}]: ${label} ${text}`);
164
+ };
165
+
166
+ if (!debounced) {
167
+ emit(stringifyForAttributesLog(payload));
168
+ return;
169
+ }
170
+
171
+ if (this.shallowMergeTimer != null) {
172
+ clearTimeout(this.shallowMergeTimer);
173
+ this.shallowMergeTimer = null;
174
+ }
175
+
176
+ if (isShallowMergeAttributesRecord(payload)) {
177
+ if (this.pendingShallowMerge?.kind === "record") {
178
+ this.pendingShallowMerge = {
179
+ kind: "record",
180
+ label,
181
+ data: { ...this.pendingShallowMerge.data, ...payload },
182
+ };
183
+ } else {
184
+ this.pendingShallowMerge = { kind: "record", label, data: { ...payload } };
185
+ }
186
+ } else {
187
+ this.pendingShallowMerge = { kind: "atom", label, value: payload };
188
+ }
189
+
190
+ this.shallowMergeTimer = setTimeout(() => this.flushShallowMerge(), ms);
191
+ }
192
+
193
+ private flushUpdateAttributesMerge(): void {
194
+ this.updateMergeTimer = null;
195
+ const segments = this.pendingUpdateSegments;
196
+ this.pendingUpdateSegments = null;
197
+ if (segments === null || segments.length === 0) {
198
+ return;
199
+ }
200
+ const parts = segments.map(
201
+ (s) => `${s.keys.join(", ")} ${stringifyForAttributesLog(s.value)}`,
202
+ );
203
+ this.emitInfo(`[WindowManager][${this.name}]: safeUpdateAttributes ${parts.join(" | ")}`);
204
+ for (const s of segments) {
205
+ s.keys.length = 0;
206
+ s.value = undefined;
207
+ }
208
+ segments.length = 0;
209
+ }
210
+
211
+ /**
212
+ * 带 debounce 时:连续调用若 `keys` 与上一段完全相同则覆盖该段的 `value`;否则追加一段。
213
+ * flush 时输出一条日志,多段用 ` | ` 连接。
214
+ */
215
+ logDebouncedUpdateAttributes(keys: string[], value: unknown): void {
216
+ const ms = this.debounceTime;
217
+ const debounced = ms != null && ms > 0;
218
+ const keysCopy = [...keys];
219
+
220
+ if (!debounced) {
221
+ this.emitInfo(
222
+ `[WindowManager][${this.name}]: safeUpdateAttributes ${keysCopy.join(", ")} ${stringifyForAttributesLog(value)}`,
223
+ );
224
+ return;
225
+ }
226
+
227
+ if (this.updateMergeTimer != null) {
228
+ clearTimeout(this.updateMergeTimer);
229
+ this.updateMergeTimer = null;
230
+ }
231
+
232
+ if (this.pendingUpdateSegments === null || this.pendingUpdateSegments.length === 0) {
233
+ this.pendingUpdateSegments = [{ keys: keysCopy, value }];
234
+ } else {
235
+ const last = this.pendingUpdateSegments[this.pendingUpdateSegments.length - 1];
236
+ if (keysPathEqual(last.keys, keysCopy)) {
237
+ last.value = value;
238
+ } else {
239
+ this.pendingUpdateSegments.push({ keys: keysCopy, value });
240
+ }
241
+ }
242
+
243
+ this.updateMergeTimer = setTimeout(() => this.flushUpdateAttributesMerge(), ms);
244
+ }
245
+
246
+ /**
247
+ * 销毁:清除所有 `setTimeout` debounce 定时器,并丢弃尚未输出的暂存日志(不补打日志)。
248
+ * WindowManager 销毁时应调用,避免泄漏与销毁后仍触发 `logger.info`。
249
+ */
250
+ destroy(): void {
251
+ if (this.flushTimer != null) {
252
+ clearTimeout(this.flushTimer);
253
+ this.flushTimer = null;
254
+ }
255
+ if (this.shallowMergeTimer != null) {
256
+ clearTimeout(this.shallowMergeTimer);
257
+ this.shallowMergeTimer = null;
258
+ }
259
+ if (this.updateMergeTimer != null) {
260
+ clearTimeout(this.updateMergeTimer);
261
+ this.updateMergeTimer = null;
262
+ }
263
+ this.pendingArgs = null;
264
+ this.pendingShallowMerge = null;
265
+ this.pendingUpdateSegments = null;
266
+ }
267
+
268
+ /** 与 `destroy()` 相同,保留旧名以兼容 */
269
+ dispose(): void {
270
+ this.destroy();
271
+ }
44
272
  }
@@ -3,7 +3,7 @@ import { callbacks } from "../callback";
3
3
  import { createView } from "./ViewManager";
4
4
  import { debounce, get, isEmpty, isEqual } from "lodash";
5
5
  import { internalEmitter } from "../InternalEmitter";
6
- import { Fields } from "../AttributesDelegate";
6
+ import { Fields, type MainViewCamera } from "../AttributesDelegate";
7
7
  import { setViewFocusScenePath } from "../Utils/Common";
8
8
  import { SideEffectManager } from "side-effect-manager";
9
9
  import type { Camera, Room, Size, View } from "white-web-sdk";
@@ -11,7 +11,14 @@ import type { AppManager } from "../AppManager";
11
11
  import { Events } from "../constants";
12
12
  import { LocalConsole } from "../Utils/log";
13
13
 
14
- (window as any).___local_log = (window as any).___local_log || new Set();
14
+ type MainViewScreenLike = {
15
+ refreshSize?: (width: number, height: number) => void;
16
+ resizeObserver?: {
17
+ disconnect?: () => void;
18
+ observe?: (target: Element) => void;
19
+ };
20
+ };
21
+
15
22
  export class MainViewProxy {
16
23
  /** Refresh the view's camera in an interval of 1.5s. */
17
24
  public polling = false;
@@ -19,13 +26,19 @@ export class MainViewProxy {
19
26
  private scale?: number;
20
27
  private started = false;
21
28
  private mainViewIsAddListener = false;
29
+ private isForcingMainViewDivElement = false;
30
+ private wrapperRectWorkaroundFrame = 0;
31
+ private pendingWrapperRectChange?: { width: number; height: number; origin?: string };
22
32
  private mainView: View;
23
33
  private store = this.manager.store;
24
34
  private viewMode = this.manager.windowManger.viewMode;
25
35
 
26
36
  private sideEffectManager = new SideEffectManager();
27
37
 
28
- private playgroundSizeChangeListenerLocalConsole = new LocalConsole("playgroundSizeChangeListener", 30);
38
+ private playgroundSizeChangeListenerLocalConsole = new LocalConsole("playgroundSizeChangeListener", 100);
39
+ private sizeUpdatedLocalConsole = new LocalConsole("sizeUpdated", 100);
40
+ private cameraUpdatedLocalConsole = new LocalConsole("cameraUpdated", 100);
41
+ private cameraReactionLocalConsole = new LocalConsole("cameraReaction", 100);
29
42
 
30
43
  constructor(private manager: AppManager) {
31
44
  this.mainView = this.createMainView();
@@ -54,6 +67,9 @@ export class MainViewProxy {
54
67
  this.sideEffectManager.add(() => {
55
68
  return internalEmitter.on("containerSizeRatioUpdate", this.onUpdateContainerSizeRatio);
56
69
  });
70
+ this.sideEffectManager.add(() => {
71
+ return internalEmitter.on("wrapperRectChange", this.onWrapperRectChange);
72
+ });
57
73
  this.sideEffectManager.add(() => {
58
74
  return internalEmitter.on("startReconnect", () => {
59
75
  if (!this.didRelease) {
@@ -109,14 +125,105 @@ export class MainViewProxy {
109
125
  this.moveCamera(this.mainViewCamera);
110
126
  }
111
127
 
128
+ private onWrapperRectChange = (payload: { width: number; height: number; origin?: string }) => {
129
+ this.pendingWrapperRectChange = payload;
130
+ if (this.wrapperRectWorkaroundFrame) {
131
+ cancelAnimationFrame(this.wrapperRectWorkaroundFrame);
132
+ }
133
+ this.wrapperRectWorkaroundFrame = requestAnimationFrame(this.runWrapperRectWorkaround);
134
+ };
135
+
136
+ private runWrapperRectWorkaround = () => {
137
+ this.wrapperRectWorkaroundFrame = 0;
138
+ const payload = this.pendingWrapperRectChange;
139
+ const element = this.mainView.divElement;
140
+ this.pendingWrapperRectChange = undefined;
141
+ if (!payload || !element) return;
142
+
143
+ const rect = element.getBoundingClientRect();
144
+ const observedSize = { width: rect.width, height: rect.height };
145
+ const wrapperMatchesDom =
146
+ Math.abs(payload.width - observedSize.width) <= 0.5 &&
147
+ Math.abs(payload.height - observedSize.height) <= 0.5;
148
+ const viewIsStale =
149
+ Math.abs(this.mainView.size.width - observedSize.width) > 0.5 ||
150
+ Math.abs(this.mainView.size.height - observedSize.height) > 0.5;
151
+
152
+ if (wrapperMatchesDom && viewIsStale) {
153
+ this.forceSyncMainViewDivElement(
154
+ `wrapperRectChange:${payload.origin || "unknown"}`,
155
+ observedSize,
156
+ element
157
+ );
158
+ }
159
+ };
160
+
161
+ private forceSyncMainViewDivElement(
162
+ reason: string,
163
+ observedSize: Pick<Size, "width" | "height">,
164
+ element: HTMLDivElement
165
+ ) {
166
+ const { width: viewWidth, height: viewHeight } = this.mainView.size;
167
+ let targetElement = element;
168
+ if (
169
+ Math.abs(viewWidth - observedSize.width) <= 0.5 &&
170
+ Math.abs(viewHeight - observedSize.height) <= 0.5
171
+ ) {
172
+ return;
173
+ }
174
+ if (this.isForcingMainViewDivElement) {
175
+ console.log("[window-manager] skipForceSyncMainViewDivElement " + JSON.stringify({
176
+ reason,
177
+ observedSize,
178
+ viewSize: this.mainView.size,
179
+ }));
180
+ return;
181
+ }
182
+ this.isForcingMainViewDivElement = true;
183
+ try {
184
+ const mainView = this.mainView as View & { screen?: MainViewScreenLike };
185
+ const screen = mainView.screen;
186
+ const resizeObserver = screen?.resizeObserver;
187
+ if (typeof screen?.refreshSize === "function") {
188
+ console.log(
189
+ "[window-manager] forceSyncMainViewDivElement observerReset " +
190
+ JSON.stringify({
191
+ reason,
192
+ viewSize: this.mainView.size,
193
+ observedSize,
194
+ })
195
+ );
196
+ // Reset the observer queue so we sync against the current DOM box,
197
+ // not a stale ResizeObserver entry from a rapid resize burst.
198
+ resizeObserver?.disconnect?.();
199
+ screen.refreshSize(observedSize.width, observedSize.height);
200
+ resizeObserver?.observe?.(element);
201
+ }
202
+ } finally {
203
+ queueMicrotask(() => {
204
+ const rect = targetElement.getBoundingClientRect();
205
+ console.log("[window-manager] forceSyncMainViewDivElementResult " + JSON.stringify({
206
+ reason,
207
+ viewSize: this.mainView.size,
208
+ rect: { width: rect.width, height: rect.height },
209
+ }));
210
+ this.isForcingMainViewDivElement = false;
211
+ });
212
+ }
213
+ }
214
+
112
215
  public start() {
113
- console.log("[window-manager] start " + JSON.stringify(this.mainViewSize));
216
+ console.log("[window-manager] start attributes size:" + JSON.stringify(this.mainViewSize));
114
217
  this.sizeChangeHandler(this.mainViewSize);
115
218
  if (this.started) return;
116
219
  this.addCameraListener();
117
220
  this.addCameraReaction();
118
221
  if (this.manager.room) this.syncMainView(this.manager.room);
119
222
  this.started = true;
223
+ if(this.mainView.focusScenePath) {
224
+ this.manager.windowManger.onMainViewScenePathChangeHandler(this.mainView.focusScenePath);
225
+ }
226
+ console.log("[window-manager] start end mainView size:" + JSON.stringify(this.mainView.size));
120
227
  }
121
228
 
122
229
  public addCameraReaction = () => {
@@ -132,11 +239,11 @@ export class MainViewProxy {
132
239
  private cameraReaction = () => {
133
240
  return reaction(
134
241
  () => this.mainViewCamera,
135
- camera => {
242
+ (camera: MainViewCamera | undefined) => {
136
243
  if (camera && camera.id !== this.manager.uid) {
137
- console.log("[window-manager] cameraReaction " + JSON.stringify(camera) + JSON.stringify(this.mainViewSize));
138
244
  this.moveCameraToContian(this.mainViewSize);
139
245
  this.moveCamera(camera);
246
+ this.cameraReactionLocalConsole.log(`camera: ${JSON.stringify(camera)}, current size: ${JSON.stringify(this.mainViewSize)}`);
140
247
  }
141
248
  },
142
249
  { fireImmediately: true }
@@ -147,7 +254,7 @@ export class MainViewProxy {
147
254
  if (size) {
148
255
  this.moveCameraToContian(size);
149
256
  this.moveCamera(this.mainViewCamera);
150
- console.log("[window-manager] sizeChangeHandler current size and camera" + JSON.stringify(size) + JSON.stringify(this.mainViewCamera) +
257
+ console.log("[window-manager] sizeChangeHandler current size and camera" + JSON.stringify(size) + JSON.stringify(this.mainViewCamera) +
151
258
  JSON.stringify(this.mainView.camera) + JSON.stringify(this.mainView.size));
152
259
  }
153
260
  this.ensureMainViewSize();
@@ -248,18 +355,18 @@ export class MainViewProxy {
248
355
 
249
356
  private addCameraListener() {
250
357
  this.view.callbacks.on("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
251
- this.view.callbacks.on("onCameraUpdated", this.onCameraOrSizeUpdated);
252
- this.view.callbacks.on("onSizeUpdated", this.onCameraOrSizeUpdated);
358
+ this.view.callbacks.on("onCameraUpdated", this.onCameraUpdated);
359
+ this.view.callbacks.on("onSizeUpdated", this.onSizeUpdated);
253
360
  }
254
361
 
255
362
  private removeCameraListener() {
256
363
  this.view.callbacks.off("onCameraUpdatedByDevice", this.onCameraUpdatedByDevice);
257
- this.view.callbacks.off("onCameraUpdated", this.onCameraOrSizeUpdated);
258
- this.view.callbacks.off("onSizeUpdated", this.onCameraOrSizeUpdated);
364
+ this.view.callbacks.off("onCameraUpdated", this.onCameraUpdated);
365
+ this.view.callbacks.off("onSizeUpdated", this.onSizeUpdated);
259
366
  }
260
367
 
261
368
  private _syncMainViewTimer = 0;
262
- private onCameraOrSizeUpdated = () => {
369
+ private handleCameraOrSizeUpdated = () => {
263
370
  callbacks.emit("cameraStateChange", this.cameraState);
264
371
  // sdk >= 2.16.43 的 syncMainView() 可以写入当前 main view 的 camera, 以修复复制粘贴元素的位置
265
372
  // 注意到这个操作会发送信令,应当避免频繁调用
@@ -270,6 +377,16 @@ export class MainViewProxy {
270
377
  this.ensureMainViewSize();
271
378
  };
272
379
 
380
+ private onCameraUpdated = (camera: Camera) => {
381
+ this.cameraUpdatedLocalConsole.log(JSON.stringify(camera));
382
+ this.handleCameraOrSizeUpdated();
383
+ };
384
+
385
+ private onSizeUpdated = (size: Size) => {
386
+ this.sizeUpdatedLocalConsole.log(JSON.stringify(size));
387
+ this.handleCameraOrSizeUpdated();
388
+ };
389
+
273
390
  private ensureMainViewSize() {
274
391
  if (
275
392
  (!this.mainViewSize ||
@@ -284,7 +401,6 @@ export class MainViewProxy {
284
401
 
285
402
  private syncMainView = (room: Room) => {
286
403
  if (room.isWritable) {
287
- console.log("[window-manager] syncMainView ");
288
404
  room.syncMainView(this.mainView);
289
405
  }
290
406
  };
@@ -328,6 +444,15 @@ export class MainViewProxy {
328
444
  };
329
445
 
330
446
  public destroy() {
447
+ console.log("[window-manager] destroy ");
448
+ if (this.wrapperRectWorkaroundFrame) {
449
+ cancelAnimationFrame(this.wrapperRectWorkaroundFrame);
450
+ this.wrapperRectWorkaroundFrame = 0;
451
+ }
452
+ this.playgroundSizeChangeListenerLocalConsole.destroy();
453
+ this.sizeUpdatedLocalConsole.destroy();
454
+ this.cameraUpdatedLocalConsole.destroy();
455
+ this.cameraReactionLocalConsole.destroy();
331
456
  this.removeMainViewListener();
332
457
  this.stop();
333
458
  this.sideEffectManager.flushAll();
package/src/index.ts CHANGED
@@ -12,7 +12,7 @@ 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, isNumber } from "lodash";
15
- import { log } from "./Utils/log";
15
+ import { ArgusLog, log } from "./Utils/log";
16
16
  import { PageStateImpl } from "./PageState";
17
17
  import { ReconnectRefresher } from "./ReconnectRefresher";
18
18
  import { replaceRoomFunction } from "./Utils/RoomHacker";
@@ -245,6 +245,8 @@ export class WindowManager
245
245
 
246
246
  private _roomLogger?: Logger;
247
247
 
248
+ public attributesDeboundceLog?: ArgusLog;
249
+
248
250
  get Logger(): Logger | undefined {
249
251
  return this._roomLogger;
250
252
  }
@@ -253,16 +255,9 @@ export class WindowManager
253
255
  super(context);
254
256
  WindowManager.displayer = context.displayer;
255
257
  (window as any).NETLESS_DEPS = __APP_DEPENDENCIES__;
256
- this.visibleStateListener();
257
- document.addEventListener("visibilitychange", this.visibleStateListener);
258
258
  this.emitter.on('mainViewScenePathChange', this.onMainViewScenePathChangeHandler)
259
259
  }
260
260
 
261
-
262
- private visibleStateListener = () => {
263
- console.log("[window-manager] visibleStateListener isVisible:" + !document.hidden);
264
- }
265
-
266
261
  public static onCreate(manager: WindowManager) {
267
262
  WindowManager._resolve(manager);
268
263
  }
@@ -294,6 +289,7 @@ export class WindowManager
294
289
  manager = await this.initManager(room);
295
290
  if (manager) {
296
291
  manager._roomLogger = (room as unknown as { logger: Logger }).logger;
292
+ manager.attributesDeboundceLog = new ArgusLog(manager._roomLogger, "attributes", 300);
297
293
  if (WindowManager.registered.size > 0) {
298
294
  manager._roomLogger.info(
299
295
  `[WindowManager] registered apps: ${JSON.stringify(
@@ -403,16 +399,17 @@ export class WindowManager
403
399
  if (mainViewElement) {
404
400
  const backgroundImage = mainViewElement.querySelector('.background img');
405
401
  if (backgroundImage) {
406
- // todo 获取到 back ground image 的 rect情况以及css情况是否可见
407
402
  const backgroundImageRect = backgroundImage?.getBoundingClientRect();
408
403
  const backgroundImageCSS = window.getComputedStyle(backgroundImage);
409
404
  const backgroundImageVisible = backgroundImageRect?.width > 0 && backgroundImageRect?.height > 0 && backgroundImageCSS.display !== 'none';
410
- console.log("[window-manager] backgroundImageVisible" + backgroundImageVisible);
405
+ const camera = this.mainView.camera;
406
+ console.log("[window-manager] backgroundImageVisible:" + backgroundImageVisible + " camera:" + JSON.stringify(camera));
411
407
  return;
412
408
  }
413
- console.log("[window-manager] onMainViewScenePathChange" + scenePath + 'backgroundImageVisible is not found');
409
+ console.log("[window-manager] onMainViewScenePathChange scenePath:" + scenePath + ' backgroundImageVisible is not found');
410
+ return;
414
411
  }
415
- console.log("[window-manager] onMainViewScenePathChange" + scenePath + 'mainViewElement is not found');
412
+ console.log("[window-manager] onMainViewScenePathChange scenePath:" + scenePath + ' mainViewElement is not found');
416
413
  }
417
414
 
418
415
  private static initManager(room: Room): Promise<WindowManager | undefined> {
@@ -1047,7 +1044,6 @@ export class WindowManager
1047
1044
  animationMode?: AnimationMode;
1048
1045
  }>
1049
1046
  ): void {
1050
- console.log("[window-manager] moveCameraToContain" + JSON.stringify(rectangle));
1051
1047
  this.mainView.moveCameraToContain(rectangle);
1052
1048
  setTimeout(() => {
1053
1049
  this.appManager?.mainViewProxy.setCameraAndSize();
@@ -1071,6 +1067,8 @@ export class WindowManager
1071
1067
  }
1072
1068
 
1073
1069
  private _destroy() {
1070
+ this.attributesDeboundceLog?.destroy();
1071
+ this.attributesDeboundceLog = undefined;
1074
1072
  this.containerResizeObserver?.disconnect();
1075
1073
  this.appManager?.destroy();
1076
1074
  this.cursorManager?.destroy();
@@ -1083,7 +1081,6 @@ export class WindowManager
1083
1081
  WindowManager.playground.parentNode?.removeChild(WindowManager.playground);
1084
1082
  }
1085
1083
  WindowManager.params = undefined;
1086
- document.removeEventListener("visibilitychange", this.visibleStateListener);
1087
1084
  this.emitter.off('mainViewScenePathChange', this.onMainViewScenePathChangeHandler);
1088
1085
  this._iframeBridge?.destroy();
1089
1086
  this._iframeBridge = undefined;
@@ -1115,12 +1112,18 @@ export class WindowManager
1115
1112
  public safeSetAttributes(attributes: any): void {
1116
1113
  if (this.canOperate) {
1117
1114
  this.setAttributes(attributes);
1115
+ if (this.attributesDeboundceLog) {
1116
+ this.attributesDeboundceLog.logDebouncedShallowMerge("safeSetAttributes", attributes);
1117
+ }
1118
1118
  }
1119
1119
  }
1120
1120
 
1121
1121
  public safeUpdateAttributes(keys: string[], value: any): void {
1122
1122
  if (this.canOperate) {
1123
1123
  this.updateAttributes(keys, value);
1124
+ if (this.attributesDeboundceLog) {
1125
+ this.attributesDeboundceLog.logDebouncedUpdateAttributes(keys, value);
1126
+ }
1124
1127
  }
1125
1128
  }
1126
1129
 
@@ -1131,6 +1134,7 @@ export class WindowManager
1131
1134
  public cleanCurrentScene(): void {
1132
1135
  log("clean current scene");
1133
1136
  this.focusedView?.cleanCurrentScene();
1137
+ this.Logger && this.Logger.info(`[WindowManager]: cleanCurrentScene ${this.focusedView?.focusScenePath}`);
1134
1138
  }
1135
1139
 
1136
1140
  public redo(): number {
@@ -1143,6 +1147,7 @@ export class WindowManager
1143
1147
 
1144
1148
  public delete(): void {
1145
1149
  this.focusedView?.delete();
1150
+ this.Logger && this.Logger.info(`[WindowManager]: delete ${this.focusedView?.focusScenePath}`);
1146
1151
  }
1147
1152
 
1148
1153
  public copy(): void {