@netless/app-slide 0.2.105-beta.0 → 0.2.105-beta.1

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/app-slide",
3
- "version": "0.2.105-beta.0",
3
+ "version": "0.2.105-beta.1",
4
4
  "main": "dist/main.cjs.js",
5
5
  "module": "dist/main.es.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -37,6 +37,8 @@ export const usePlugin: (plugin: any) => any = /* @__PURE__ */ Slide.usePlugin.b
37
37
 
38
38
  export const version = __APP_VERSION__;
39
39
 
40
+ const DEFAULT_SLIDE_SETUP_READY_TIMEOUT = 5_000;
41
+
40
42
  export { DefaultUrl, apps, FreezerLength, addHooks, useFreezer, log, logger };
41
43
 
42
44
  export { setFreezerLength, getFreezerLength, onCreated, onDestroyed } from "./utils/freezer";
@@ -87,6 +89,11 @@ export interface AppOptions
87
89
  enableScale?: boolean;
88
90
  resourceMaxRetries?: number;
89
91
  onResourceMaxRetries: (url: string, error: Error) => void;
92
+ /**
93
+ * Max time (ms) `setup()` waits for the first `renderEnd` before resolving
94
+ * anyway (the slide keeps loading in background). Default: 5_000.
95
+ */
96
+ setupReadyTimeout?: number;
90
97
  }
91
98
 
92
99
  export interface ILogger {
@@ -146,6 +153,10 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
146
153
  const baseScenePath = context.getInitScenePath() as string;
147
154
 
148
155
  let docsViewer: SlideDocsViewer | null = null;
156
+ // Serial setup queue support: capture the controller so setup() can await
157
+ // its first render before WindowManager starts the next app's setup.
158
+ let slideControllerRef: SlideController | null | undefined;
159
+ let firstRenderFailed = false;
149
160
 
150
161
  const onPageChanged = (page: number) => {
151
162
  const room = context.getRoom();
@@ -174,10 +185,16 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
174
185
  ...options,
175
186
  onPageChanged,
176
187
  onNavigate: options.onNavigate,
177
- onRenderError: appOptions.onRenderError,
188
+ // Wrap the user callback: a render failure before the first
189
+ // renderEnd also fails the setup wait (serial setup queue).
190
+ onRenderError: (error, pageIndex) => {
191
+ firstRenderFailed = true;
192
+ appOptions.onRenderError?.(error, pageIndex);
193
+ },
178
194
  showRenderError: appOptions.showRenderError,
179
195
  invisibleBehavior: appOptions.invisibleBehavior,
180
196
  });
197
+ slideControllerRef = slideController;
181
198
  if (useFreezer) apps.set(context.appId, slideController, box);
182
199
  logger.setAppController(context.appId, slideController);
183
200
  if (import.meta.env.DEV) {
@@ -272,7 +289,33 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
272
289
  (SlideApp as any).__teardownByContext ||= new WeakMap<object, () => void>();
273
290
  (SlideApp as any).__teardownByContext.set(context, teardown);
274
291
 
275
- return {
292
+ // Resolve once the SlideController finished its first render (renderEnd).
293
+ // `false` means timeout or teardown; the slide then keeps loading in the
294
+ // background without blocking WindowManager's serial setup queue.
295
+ const waitForFirstRender = (timeoutMs: number): Promise<boolean> =>
296
+ new Promise<boolean>(resolve => {
297
+ let settled = false;
298
+ let pollTimer: number | undefined;
299
+ const settle = (loaded: boolean) => {
300
+ if (settled) return;
301
+ settled = true;
302
+ window.clearTimeout(timeoutTimer);
303
+ if (pollTimer !== undefined) window.clearInterval(pollTimer);
304
+ resolve(loaded);
305
+ };
306
+ const timeoutTimer = window.setTimeout(() => settle(false), timeoutMs);
307
+ // Success path resolves straight from readyPromise (no polling lag);
308
+ // the poll only catches teardown and pre-ready render failures.
309
+ slideControllerRef?.readyPromise.then(
310
+ () => settle(!firstRenderFailed),
311
+ () => settle(false),
312
+ );
313
+ pollTimer = window.setInterval(() => {
314
+ if (disposed || firstRenderFailed) settle(false);
315
+ }, 100);
316
+ });
317
+
318
+ const appResult: AppResult = {
276
319
  onScaleChanged: (cb: (scale: number) => void) => {
277
320
  if (!docsViewer) {
278
321
  return () => void 0;
@@ -396,6 +439,18 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
396
439
  return false;
397
440
  },
398
441
  };
442
+
443
+ const setupReadyTimeout =
444
+ appOptions?.setupReadyTimeout ?? DEFAULT_SLIDE_SETUP_READY_TIMEOUT;
445
+ return waitForFirstRender(setupReadyTimeout).then(() => {
446
+ if (firstRenderFailed) {
447
+ throw new Error("[Slide] first render failed before ready");
448
+ }
449
+ if (!disposed && !slideControllerRef?.ready) {
450
+ log("[Slide] setup ready wait timed out, slide keeps loading in background");
451
+ }
452
+ return appResult;
453
+ }) as unknown as AppResult;
399
454
  },
400
455
  teardown(context) {
401
456
  (SlideApp as any).__teardownByContext?.get(context)?.();