@netless/window-manager 1.0.19 → 1.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "Multi-window mode for Netless Whiteboard",
5
5
  "author": "l1shen <lishen1635@gmail.com> (https://github.com/l1shen)",
6
6
  "license": "MIT",
@@ -1,4 +1,5 @@
1
1
  import type { View } from "white-web-sdk";
2
+ import { BOX_SIZE_SETTLE_DELAY } from "../constants";
2
3
 
3
4
  export type AppBoxSize = { width: number; height: number };
4
5
  export type AppBoxSizeChange = AppBoxSize & { appId: string };
@@ -15,7 +16,6 @@ type ViewWithRefreshSize = View & {
15
16
  };
16
17
 
17
18
  const BOX_SIZE_EPSILON = 0.5;
18
- const BOX_SIZE_SETTLE_DELAY = 650;
19
19
  const BOX_SIZE_CONFIRM_DELAY = 100;
20
20
 
21
21
  const sizesEqual = (left: AppBoxSize | undefined, right: AppBoxSize): boolean =>
@@ -36,6 +36,8 @@ import { callbacks } from "../callback";
36
36
  import { getExtendClass } from "../Utils/extendClass";
37
37
  import { AppBoxSizeSynchronizer } from "./AppBoxSizeSynchronizer";
38
38
 
39
+ const APP_SETUP_WATCHDOG_TIMEOUT = 10_000;
40
+
39
41
  export type AppEmitter = Emittery<AppEmitterEvent>;
40
42
 
41
43
  export class AppProxy implements PageRemoveService {
@@ -58,6 +60,7 @@ export class AppProxy implements PageRemoveService {
58
60
  private _pageState: AppPageStateImpl;
59
61
  private _prevFullPath: string | undefined;
60
62
  private boxSizeSynchronizer: AppBoxSizeSynchronizer;
63
+ private setupWatchdogTimer?: ReturnType<typeof setTimeout>;
61
64
 
62
65
  public appResult?: NetlessApp<any>;
63
66
  public appContext?: AppContext<any, any>;
@@ -246,7 +249,9 @@ export class AppProxy implements PageRemoveService {
246
249
  this.Logger.info(
247
250
  `[WindowManager]: setup app ${this.kind}, appId: ${appId}`
248
251
  );
249
- const result = await app.setup(context);
252
+ const setupResult = await this.runAppSetup(appId, app, context);
253
+ if (!setupResult.succeeded) return;
254
+ const { result } = setupResult;
250
255
  this.appResult = result;
251
256
  this.scheduleBoxSizeSync();
252
257
  appRegister.notifyApp(this.kind, "created", { appId, result });
@@ -286,6 +291,39 @@ export class AppProxy implements PageRemoveService {
286
291
  }
287
292
  }
288
293
 
294
+ private async runAppSetup(
295
+ appId: string,
296
+ app: NetlessApp,
297
+ context: AppContext<any, any>
298
+ ): Promise<{ succeeded: true; result: any } | { succeeded: false }> {
299
+ this.clearSetupWatchdog();
300
+ this.setupWatchdogTimer = setTimeout(() => {
301
+ this.setupWatchdogTimer = undefined;
302
+ if (this.status === "destroyed") return;
303
+ this.Logger?.warn(
304
+ `[WindowManager]: hydrate timeout, stage: app setup, kind: ${this.kind}, appId: ${appId}, timeout: ${APP_SETUP_WATCHDOG_TIMEOUT}ms`
305
+ );
306
+ }, APP_SETUP_WATCHDOG_TIMEOUT);
307
+ try {
308
+ return { succeeded: true, result: await app.setup(context) };
309
+ } catch (error) {
310
+ const message = error instanceof Error ? error.message : String(error);
311
+ this.Logger?.error(
312
+ `[WindowManager]: app setup error, kind: ${this.kind}, appId: ${appId}, status: ${this.status}, error: ${message}`
313
+ );
314
+ return { succeeded: false };
315
+ } finally {
316
+ this.clearSetupWatchdog();
317
+ }
318
+ }
319
+
320
+ private clearSetupWatchdog(): void {
321
+ if (this.setupWatchdogTimer != null) {
322
+ clearTimeout(this.setupWatchdogTimer);
323
+ this.setupWatchdogTimer = undefined;
324
+ }
325
+ }
326
+
289
327
  // 兼容移动端创建时会出现 PPT 不适配的问题
290
328
  private fixMobileSize() {
291
329
  const box = this.boxManager?.getBox(this.id);
@@ -564,6 +602,7 @@ export class AppProxy implements PageRemoveService {
564
602
  ) {
565
603
  if (this.status === "destroyed") return;
566
604
  this.status = "destroyed";
605
+ this.clearSetupWatchdog();
567
606
  this.boxSizeSynchronizer.destroy();
568
607
  try {
569
608
  await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
@@ -2,25 +2,58 @@ import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observe
2
2
  import { WindowManager } from "./index";
3
3
  import type { EmitterType } from "./InternalEmitter";
4
4
  import type { UnsubscribeFn } from "emittery";
5
- import { LocalConsole } from "./Utils/log";
5
+ import type { Logger } from "white-web-sdk";
6
+ import { CONTAINER_STATE_LOG_DEBOUNCE } from "./constants";
7
+ import { ArgusLog, LocalConsole } from "./Utils/log";
6
8
 
7
9
  const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
8
10
 
11
+ export type ContainerInternalState = {
12
+ mainViewElement?: HTMLElement;
13
+ mainViewSize?: Pick<DOMRectReadOnly, "width" | "height">;
14
+ teleBoxContainerRect?: Pick<DOMRectReadOnly, "width" | "height">;
15
+ mainViewDidRelease?: boolean;
16
+ containerSizeRatio?: number;
17
+ };
18
+
9
19
  export class ContainerResizeObserver {
10
20
  private containerResizeObserver?: ResizeObserver;
11
21
  private disposer?: UnsubscribeFn;
12
22
 
13
23
  private updateSizerLocalConsole = new LocalConsole("updateSizer", 100);
24
+ private containerStateArgusLog?: ArgusLog;
25
+ private containerStateLogTimer?: ReturnType<typeof setTimeout>;
26
+ private pendingContainerState?: {
27
+ origin?: string;
28
+ observedSize?: Pick<DOMRectReadOnly, "width" | "height">;
29
+ };
30
+ private container?: HTMLElement;
31
+ private sizer?: HTMLElement;
32
+ private wrapper?: HTMLDivElement;
14
33
 
15
- constructor(private emitter: EmitterType) {}
34
+ constructor(
35
+ private emitter: EmitterType,
36
+ logger?: Logger,
37
+ private getInternalState?: () => ContainerInternalState
38
+ ) {
39
+ if (logger) {
40
+ this.containerStateArgusLog = new ArgusLog(logger, "containerState");
41
+ }
42
+ }
16
43
 
17
44
  public static create(
18
45
  container: HTMLElement,
19
46
  sizer: HTMLElement,
20
47
  wrapper: HTMLDivElement,
21
- emitter: EmitterType
48
+ emitter: EmitterType,
49
+ logger?: Logger,
50
+ getInternalState?: () => ContainerInternalState
22
51
  ) {
23
- const containerResizeObserver = new ContainerResizeObserver(emitter);
52
+ const containerResizeObserver = new ContainerResizeObserver(
53
+ emitter,
54
+ logger,
55
+ getInternalState
56
+ );
24
57
  containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
25
58
  return containerResizeObserver;
26
59
  }
@@ -30,6 +63,9 @@ export class ContainerResizeObserver {
30
63
  sizer: HTMLElement,
31
64
  wrapper: HTMLDivElement
32
65
  ) {
66
+ this.container = container;
67
+ this.sizer = sizer;
68
+ this.wrapper = wrapper;
33
69
  this.updateSizer(
34
70
  container.getBoundingClientRect(),
35
71
  sizer,
@@ -60,6 +96,7 @@ export class ContainerResizeObserver {
60
96
  wrapper: HTMLDivElement,
61
97
  origin?: string
62
98
  ) {
99
+ const observedSize = { width, height };
63
100
  if (width && height) {
64
101
  if (height / width > WindowManager.containerSizeRatio) {
65
102
  height = width * WindowManager.containerSizeRatio;
@@ -82,12 +119,105 @@ export class ContainerResizeObserver {
82
119
  origin,
83
120
  });
84
121
  }
122
+ this.scheduleContainerStateLog(origin, observedSize);
123
+ }
124
+
125
+ public logCurrentState(origin: string): void {
126
+ this.scheduleContainerStateLog(origin);
127
+ }
128
+
129
+ private scheduleContainerStateLog(
130
+ origin?: string,
131
+ observedSize?: Pick<DOMRectReadOnly, "width" | "height">
132
+ ): void {
133
+ if (!this.containerStateArgusLog || !this.container || !this.sizer || !this.wrapper) return;
134
+
135
+ this.pendingContainerState = {
136
+ origin,
137
+ observedSize: observedSize
138
+ ? { width: observedSize.width, height: observedSize.height }
139
+ : undefined,
140
+ };
141
+ if (this.containerStateLogTimer != null) {
142
+ clearTimeout(this.containerStateLogTimer);
143
+ }
144
+ this.containerStateLogTimer = setTimeout(
145
+ this.flushContainerStateLog,
146
+ CONTAINER_STATE_LOG_DEBOUNCE
147
+ );
148
+ }
149
+
150
+ private flushContainerStateLog = (): void => {
151
+ this.containerStateLogTimer = undefined;
152
+ const pendingState = this.pendingContainerState;
153
+ this.pendingContainerState = undefined;
154
+ if (
155
+ !pendingState ||
156
+ !this.containerStateArgusLog ||
157
+ !this.container ||
158
+ !this.sizer ||
159
+ !this.wrapper
160
+ ) {
161
+ return;
162
+ }
163
+
164
+ const internalState = this.getInternalState?.();
165
+ const root = this.container.parentElement;
166
+ const rootRect = root?.getBoundingClientRect();
167
+ const playgroundRect = this.container.getBoundingClientRect();
168
+ const sizerRect = this.sizer.getBoundingClientRect();
169
+ const wrapperRect = this.wrapper.getBoundingClientRect();
170
+ const mainViewRect = internalState?.mainViewElement?.getBoundingClientRect();
171
+ const rootStyle = root ? window.getComputedStyle(root) : undefined;
172
+ this.containerStateArgusLog.log(
173
+ `origin: ${pendingState.origin || "unknown"}, connected: ${Boolean(
174
+ root?.isConnected
175
+ )}, ` +
176
+ `observed: ${this.formatSize(
177
+ pendingState.observedSize || playgroundRect
178
+ )}, root: ${this.formatSize(rootRect)}, playground: ${this.formatSize(
179
+ playgroundRect
180
+ )}, sizer: ${this.formatSize(sizerRect)}, wrapper: ${this.formatSize(
181
+ wrapperRect
182
+ )}, mainViewDOM: ${this.formatSize(mainViewRect)}, mainViewSize: ${this.formatSize(
183
+ internalState?.mainViewSize
184
+ )}, teleBoxRect: ${this.formatSize(
185
+ internalState?.teleBoxContainerRect
186
+ )}, didRelease: ${internalState?.mainViewDidRelease ?? "unknown"}, ratio: ${
187
+ internalState?.containerSizeRatio ?? "unknown"
188
+ }, display: ${rootStyle?.display || "unknown"}, visibility: ${
189
+ rootStyle?.visibility || "unknown"
190
+ }, clientRects: ${root?.getClientRects().length || 0}, inViewport: ${
191
+ rootRect
192
+ ? rootRect.bottom > 0 &&
193
+ rootRect.right > 0 &&
194
+ rootRect.top < window.innerHeight &&
195
+ rootRect.left < window.innerWidth
196
+ : false
197
+ }`
198
+ );
199
+ };
200
+
201
+ private formatSize(rect?: Pick<DOMRectReadOnly, "width" | "height">): string {
202
+ if (!rect) return "unknown";
203
+ return `${Math.round(rect.width * 100) / 100}x${Math.round(rect.height * 100) / 100}`;
85
204
  }
86
205
 
87
206
  public disconnect() {
88
207
  this.updateSizerLocalConsole.destroy();
208
+ if (this.containerStateLogTimer != null) {
209
+ clearTimeout(this.containerStateLogTimer);
210
+ this.containerStateLogTimer = undefined;
211
+ }
212
+ this.pendingContainerState = undefined;
213
+ this.containerStateArgusLog?.destroy();
214
+ this.containerStateArgusLog = undefined;
89
215
  this.containerResizeObserver?.disconnect();
90
216
  this.disposer?.();
91
217
  this.disposer = undefined;
218
+ this.container = undefined;
219
+ this.sizer = undefined;
220
+ this.wrapper = undefined;
221
+ this.getInternalState = undefined;
92
222
  }
93
223
  }
package/src/constants.ts CHANGED
@@ -57,3 +57,7 @@ export const ROOT_DIR = "/";
57
57
  export const INIT_DIR = "/init";
58
58
 
59
59
  export const SETUP_APP_DELAY = 50;
60
+
61
+ export const BOX_SIZE_SETTLE_DELAY = 650;
62
+
63
+ export const CONTAINER_STATE_LOG_DEBOUNCE = 1000;
package/src/index.ts CHANGED
@@ -528,7 +528,18 @@ export class WindowManager
528
528
  playground,
529
529
  sizer,
530
530
  wrapper,
531
- internalEmitter
531
+ internalEmitter,
532
+ manager.Logger,
533
+ () => {
534
+ const mainView = manager.appManager?.mainViewProxy.view;
535
+ return {
536
+ mainViewElement: mainView?.divElement,
537
+ mainViewSize: mainView?.size,
538
+ teleBoxContainerRect: manager.boxManager?.teleBoxManager.containerRect,
539
+ mainViewDidRelease: Boolean((mainView as any)?.didRelease),
540
+ containerSizeRatio: manager.containerSizeRatio,
541
+ };
542
+ }
532
543
  );
533
544
  WindowManager.wrapper = wrapper;
534
545
  WindowManager.sizer = sizer;
@@ -577,6 +588,7 @@ export class WindowManager
577
588
  this.appManager?.resetMinimized();
578
589
  this.appManager?.displayerWritableListener(!this.room.isWritable);
579
590
  WindowManager.container = container;
591
+ this.containerResizeObserver?.logCurrentState("bindContainer");
580
592
  this.extendPluginManager?.refreshContainer(container);
581
593
  }
582
594