@netless/app-slide 0.2.104 → 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/dist/index.d.ts +14 -1
- package/dist/main.cjs.js +303 -302
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +3729 -2377
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +297 -296
- package/dist/main.iife.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +77 -5
package/package.json
CHANGED
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 {
|
|
@@ -113,7 +120,9 @@ export interface AppResult {
|
|
|
113
120
|
translateView: (offsetX: number, offsetY: number) => void;
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult>
|
|
123
|
+
const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
124
|
+
teardown(context: import("@netless/window-manager").AppContext): void;
|
|
125
|
+
} = {
|
|
117
126
|
kind: "Slide",
|
|
118
127
|
setup(context) {
|
|
119
128
|
console.log("[Slide] setup @ " + version);
|
|
@@ -144,6 +153,10 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
|
144
153
|
const baseScenePath = context.getInitScenePath() as string;
|
|
145
154
|
|
|
146
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;
|
|
147
160
|
|
|
148
161
|
const onPageChanged = (page: number) => {
|
|
149
162
|
const room = context.getRoom();
|
|
@@ -172,10 +185,16 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
|
172
185
|
...options,
|
|
173
186
|
onPageChanged,
|
|
174
187
|
onNavigate: options.onNavigate,
|
|
175
|
-
|
|
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
|
+
},
|
|
176
194
|
showRenderError: appOptions.showRenderError,
|
|
177
195
|
invisibleBehavior: appOptions.invisibleBehavior,
|
|
178
196
|
});
|
|
197
|
+
slideControllerRef = slideController;
|
|
179
198
|
if (useFreezer) apps.set(context.appId, slideController, box);
|
|
180
199
|
logger.setAppController(context.appId, slideController);
|
|
181
200
|
if (import.meta.env.DEV) {
|
|
@@ -247,7 +266,14 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
|
247
266
|
});
|
|
248
267
|
}
|
|
249
268
|
|
|
250
|
-
|
|
269
|
+
let disposed = false;
|
|
270
|
+
let offDestroy: (() => void) | undefined;
|
|
271
|
+
const teardown = () => {
|
|
272
|
+
if (disposed) return;
|
|
273
|
+
disposed = true;
|
|
274
|
+
const removeDestroy = offDestroy;
|
|
275
|
+
offDestroy = undefined;
|
|
276
|
+
removeDestroy?.();
|
|
251
277
|
log("[Slide] destroy", context.appId);
|
|
252
278
|
if (useFreezer) apps.delete(context.appId);
|
|
253
279
|
sideEffect.flushAll();
|
|
@@ -255,11 +281,41 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
|
255
281
|
docsViewer.destroy();
|
|
256
282
|
docsViewer = null;
|
|
257
283
|
}
|
|
258
|
-
}
|
|
284
|
+
};
|
|
285
|
+
offDestroy = context.emitter.on("destroy", teardown);
|
|
259
286
|
|
|
260
287
|
docsViewer.mount();
|
|
261
288
|
|
|
262
|
-
|
|
289
|
+
(SlideApp as any).__teardownByContext ||= new WeakMap<object, () => void>();
|
|
290
|
+
(SlideApp as any).__teardownByContext.set(context, teardown);
|
|
291
|
+
|
|
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 = {
|
|
263
319
|
onScaleChanged: (cb: (scale: number) => void) => {
|
|
264
320
|
if (!docsViewer) {
|
|
265
321
|
return () => void 0;
|
|
@@ -383,6 +439,22 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
|
383
439
|
return false;
|
|
384
440
|
},
|
|
385
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;
|
|
454
|
+
},
|
|
455
|
+
teardown(context) {
|
|
456
|
+
(SlideApp as any).__teardownByContext?.get(context)?.();
|
|
457
|
+
(SlideApp as any).__teardownByContext?.delete(context);
|
|
386
458
|
},
|
|
387
459
|
};
|
|
388
460
|
|