@netless/app-presentation 0.1.12 → 0.1.13-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.js CHANGED
@@ -1151,6 +1151,9 @@ var _Preload = class _Preload {
1151
1151
  // 当前触摸的索引
1152
1152
  this.touchIndex = 0;
1153
1153
  this.preloadSize = 0;
1154
+ // 暂停后不再创建预加载链接(app 失焦降级时调用);已插入的 link 会被移除,
1155
+ // 挂起的 idle 回调重新进入 touch() 时也会被跳过
1156
+ this.paused = false;
1154
1157
  this.preloadMap = new Map(pages.map((e, index) => [index, {
1155
1158
  src: e.src,
1156
1159
  state: 0 /* unloaded */
@@ -1158,7 +1161,21 @@ var _Preload = class _Preload {
1158
1161
  this.preloadSize = this.preloadMap.size;
1159
1162
  this.touch(0, true);
1160
1163
  }
1164
+ pause() {
1165
+ if (this.paused)
1166
+ return;
1167
+ this.paused = true;
1168
+ this.destroySomeLink();
1169
+ }
1170
+ resume(index) {
1171
+ if (!this.paused)
1172
+ return;
1173
+ this.paused = false;
1174
+ this.touch(index != null ? index : this.touchIndex, true);
1175
+ }
1161
1176
  touch(index, force = false) {
1177
+ if (this.paused)
1178
+ return;
1162
1179
  if (index >= this.preloadSize) {
1163
1180
  this.touchIndex = 0;
1164
1181
  } else {
@@ -1957,6 +1974,138 @@ var ppt2page = (ppt, name, originSize) => {
1957
1974
  const size = fitPageSizeToOrigin(ppt, originSize);
1958
1975
  return __spreadProps(__spreadValues({}, size), { src: ppt.src, thumbnail: ppt.previewURL, name });
1959
1976
  };
1977
+ var findBackgroundImage = (view) => {
1978
+ const container = view.divElement;
1979
+ if (!container || typeof container.querySelector !== "function")
1980
+ return null;
1981
+ return container.querySelector('img[alt="background"]');
1982
+ };
1983
+ var isBlurDegradationAllowed = (context) => {
1984
+ const boxStatus = context.getBoxStatus();
1985
+ if (boxStatus) {
1986
+ return boxStatus !== "normal";
1987
+ }
1988
+ const boxState = context.getWindowManager().boxState;
1989
+ return boxState !== void 0 && boxState !== "normal";
1990
+ };
1991
+ var DEFAULT_SETUP_READY_TIMEOUT = 5e3;
1992
+ var waitForCurrentPageImage = (view, timeoutMs, isDisposed) => {
1993
+ return new Promise((resolve) => {
1994
+ let settled = false;
1995
+ let pollTimer;
1996
+ const settle = (loaded) => {
1997
+ if (settled)
1998
+ return;
1999
+ settled = true;
2000
+ window.clearTimeout(timeoutTimer);
2001
+ if (pollTimer !== void 0)
2002
+ window.clearInterval(pollTimer);
2003
+ resolve(loaded);
2004
+ };
2005
+ const timeoutTimer = window.setTimeout(() => settle(false), timeoutMs);
2006
+ const check = () => {
2007
+ if (isDisposed()) {
2008
+ settle(false);
2009
+ return;
2010
+ }
2011
+ const img = findBackgroundImage(view);
2012
+ if (img && img.complete && img.naturalWidth > 0) {
2013
+ settle(true);
2014
+ }
2015
+ };
2016
+ check();
2017
+ if (!settled) {
2018
+ pollTimer = window.setInterval(check, 100);
2019
+ }
2020
+ });
2021
+ };
2022
+ var resolveImageUrl = (url) => {
2023
+ try {
2024
+ return new URL(url, window.location.href).href;
2025
+ } catch (e) {
2026
+ return url;
2027
+ }
2028
+ };
2029
+ var setupBlurThumbnailDegradation = (config) => {
2030
+ const { context, view, pages, pageIndex$, app, dispose: dispose2 } = config;
2031
+ let blurEpisode = false;
2032
+ let reDegradeEnabled = false;
2033
+ const degrade = () => {
2034
+ const page = pages[pageIndex$.value];
2035
+ if (!(page == null ? void 0 : page.thumbnail))
2036
+ return;
2037
+ const img = findBackgroundImage(view);
2038
+ if (img && resolveImageUrl(img.src) !== resolveImageUrl(page.thumbnail)) {
2039
+ img.src = page.thumbnail;
2040
+ }
2041
+ };
2042
+ const restore2 = () => {
2043
+ const page = pages[pageIndex$.value];
2044
+ const img = findBackgroundImage(view);
2045
+ if (page && img && resolveImageUrl(img.src) !== resolveImageUrl(page.src)) {
2046
+ img.src = page.src;
2047
+ }
2048
+ };
2049
+ const isBackgroundImg = (target) => {
2050
+ var _a;
2051
+ const el = target;
2052
+ return Boolean(
2053
+ el && el.tagName === "IMG" && ((_a = el.matches) == null ? void 0 : _a.call(el, 'img[alt="background"]'))
2054
+ );
2055
+ };
2056
+ const onImgLoad = (ev) => {
2057
+ if (!blurEpisode || !reDegradeEnabled)
2058
+ return;
2059
+ if (!isBackgroundImg(ev.target))
2060
+ return;
2061
+ degrade();
2062
+ };
2063
+ const onImgError = (ev) => {
2064
+ if (!blurEpisode || !reDegradeEnabled)
2065
+ return;
2066
+ if (!isBackgroundImg(ev.target))
2067
+ return;
2068
+ reDegradeEnabled = false;
2069
+ };
2070
+ const offFocus = context.emitter.on("focus", (isFocused) => {
2071
+ if (isFocused) {
2072
+ if (blurEpisode) {
2073
+ blurEpisode = false;
2074
+ reDegradeEnabled = false;
2075
+ restore2();
2076
+ app.preload.resume(pageIndex$.value);
2077
+ }
2078
+ } else {
2079
+ if (!isBlurDegradationAllowed(context))
2080
+ return;
2081
+ blurEpisode = true;
2082
+ reDegradeEnabled = true;
2083
+ degrade();
2084
+ app.preload.pause();
2085
+ }
2086
+ });
2087
+ dispose2.add(offFocus);
2088
+ const viewEl = view.divElement;
2089
+ if (viewEl && typeof viewEl.addEventListener === "function") {
2090
+ viewEl.addEventListener("load", onImgLoad, true);
2091
+ viewEl.addEventListener("error", onImgError, true);
2092
+ dispose2.add(() => {
2093
+ viewEl.removeEventListener("load", onImgLoad, true);
2094
+ viewEl.removeEventListener("error", onImgError, true);
2095
+ });
2096
+ } else {
2097
+ warnOnceMissingViewContainer(view);
2098
+ }
2099
+ };
2100
+ var degradedWarnedViews = /* @__PURE__ */ new WeakSet();
2101
+ var warnOnceMissingViewContainer = (view) => {
2102
+ if (degradedWarnedViews.has(view))
2103
+ return;
2104
+ degradedWarnedViews.add(view);
2105
+ console.warn(
2106
+ "[Presentation]: blur thumbnail degradation runs without re-degrade coverage (view container unavailable)"
2107
+ );
2108
+ };
1960
2109
  var createLogger = (room) => {
1961
2110
  if (room && room.logger) {
1962
2111
  return (...args) => room.logger.info(...args);
@@ -1968,7 +2117,10 @@ var createDiagnosticLogger = (context) => {
1968
2117
  var _a;
1969
2118
  const createAppLogger = context.createLogger;
1970
2119
  if (typeof createAppLogger === "function") {
1971
- return createAppLogger.call(context, "camera", { debounceTime: 300, maxWaitTime: 2e3 });
2120
+ return createAppLogger.call(context, "camera", {
2121
+ debounceTime: 300,
2122
+ maxWaitTime: 2e3
2123
+ });
1972
2124
  }
1973
2125
  const roomLogger = (_a = context.getRoom()) == null ? void 0 : _a.logger;
1974
2126
  const prefix = `[Presentation][${context.appId}][camera]`;
@@ -2025,40 +2177,60 @@ var scenesEqual = (scenes1, scenes2) => {
2025
2177
  return scene.name === scene2.name && ((_a = scene.ppt) == null ? void 0 : _a.width) === ((_b = scene2.ppt) == null ? void 0 : _b.width) && ((_c = scene.ppt) == null ? void 0 : _c.height) === ((_d = scene2.ppt) == null ? void 0 : _d.height) && ((_e = scene.ppt) == null ? void 0 : _e.src) === ((_f = scene2.ppt) == null ? void 0 : _f.src);
2026
2178
  });
2027
2179
  };
2180
+ var teardownByContext = /* @__PURE__ */ new WeakMap();
2028
2181
  var NetlessAppPresentation = {
2029
2182
  kind: "Presentation",
2030
2183
  setup(context) {
2031
- var _a, _b, _c;
2184
+ var _a, _b, _c, _d;
2032
2185
  const diagnosticLogger = createDiagnosticLogger(context);
2033
2186
  const view = context.getView();
2034
2187
  if (!view)
2035
- throw new Error("[Presentation]: no whiteboard view, make sure you have added options.scenePath in addApp()");
2188
+ throw new Error(
2189
+ "[Presentation]: no whiteboard view, make sure you have added options.scenePath in addApp()"
2190
+ );
2036
2191
  const options = context.getAppOptions() || {};
2037
2192
  const room = context.getRoom();
2038
2193
  const log = options.log || createLogger(room);
2039
2194
  const roomLogger = room == null ? void 0 : room.logger;
2040
2195
  const warn = (...data) => (roomLogger == null ? void 0 : roomLogger.warn) ? roomLogger.warn(...data) : log(...data);
2041
2196
  const configuredOriginSize = context.storage.state.originSize;
2042
- const originSize = isValidSize(configuredOriginSize) ? { width: configuredOriginSize.width, height: configuredOriginSize.height } : void 0;
2197
+ const originSize = isValidSize(configuredOriginSize) ? {
2198
+ width: configuredOriginSize.width,
2199
+ height: configuredOriginSize.height
2200
+ } : void 0;
2043
2201
  if (configuredOriginSize != null && !originSize) {
2044
- warn(`[Presentation] originSize should contain finite positive width and height, got ${JSON.stringify(configuredOriginSize)}`);
2202
+ warn(
2203
+ `[Presentation] originSize should contain finite positive width and height, got ${JSON.stringify(configuredOriginSize)}`
2204
+ );
2045
2205
  }
2046
2206
  const pages = (_a = context.getScenes()) == null ? void 0 : _a.map(({ ppt, name }) => ppt2page(ppt, name, originSize)).filter(Boolean);
2047
2207
  if (!pages || pages.length === 0)
2048
- throw new Error("[Presentation]: empty scenes, make sure you have added options.scenes in addApp()");
2208
+ throw new Error(
2209
+ "[Presentation]: empty scenes, make sure you have added options.scenes in addApp()"
2210
+ );
2049
2211
  if (pages[0].src.startsWith("ppt"))
2050
- throw new Error("[Presentation]: legacy dynamic PPT is unsupported, please use the projector converter and @netless/slide to render it");
2212
+ throw new Error(
2213
+ "[Presentation]: legacy dynamic PPT is unsupported, please use the projector converter and @netless/slide to render it"
2214
+ );
2051
2215
  const scenePath = context.getInitScenePath();
2052
2216
  let maxCameraScale = (_b = options.maxCameraScale) != null ? _b : 3;
2053
2217
  if (!(Number.isFinite(maxCameraScale) && maxCameraScale > 0)) {
2054
- warn(`[Presentation] maxCameraScale should be a positive number, got ${options.maxCameraScale}`);
2218
+ warn(
2219
+ `[Presentation] maxCameraScale should be a positive number, got ${options.maxCameraScale}`
2220
+ );
2055
2221
  maxCameraScale = 3;
2056
2222
  }
2057
2223
  log(`[Presentation] new ${context.appId}`);
2058
2224
  const dispose2 = disposableStore();
2059
2225
  dispose2.add(() => log(`[Presentation] dispose ${context.appId}`));
2060
2226
  dispose2.add(() => diagnosticLogger.flush());
2061
- const view$$ = context.createStorage("view", { uid: "", originX: 0, originY: 0, width: 0, height: 0 });
2227
+ const view$$ = context.createStorage("view", {
2228
+ uid: "",
2229
+ originX: 0,
2230
+ originY: 0,
2231
+ width: 0,
2232
+ height: 0
2233
+ });
2062
2234
  const _addScenePathListener = (name, listener) => {
2063
2235
  const windowManger = context.manager.windowManger;
2064
2236
  windowManger.emitter.on(name, listener);
@@ -2080,13 +2252,16 @@ var NetlessAppPresentation = {
2080
2252
  let pageIndex = getPageIndex(view);
2081
2253
  const pageIndex$ = readable(pageIndex, (set) => {
2082
2254
  set(pageIndex);
2083
- return _addScenePathListener("onAppScenePathChange", (payload) => {
2084
- const { appId } = payload;
2085
- if (appId === context.appId) {
2086
- const _pageIndex = getPageIndex(payload.view);
2087
- set(_pageIndex);
2255
+ return _addScenePathListener(
2256
+ "onAppScenePathChange",
2257
+ (payload) => {
2258
+ const { appId } = payload;
2259
+ if (appId === context.appId) {
2260
+ const _pageIndex = getPageIndex(payload.view);
2261
+ set(_pageIndex);
2262
+ }
2088
2263
  }
2089
- });
2264
+ );
2090
2265
  });
2091
2266
  dispose2.add(() => {
2092
2267
  pageIndex$.dispose();
@@ -2103,7 +2278,12 @@ var NetlessAppPresentation = {
2103
2278
  const p = pages[index];
2104
2279
  const name = (_a2 = p.name) != null ? _a2 : String(index + 1);
2105
2280
  if (!scenes.some((scene) => scene.name === name)) {
2106
- await context.addPage({ scene: { name, ppt: { width: p.width, height: p.height, src: p.src } } });
2281
+ await context.addPage({
2282
+ scene: {
2283
+ name,
2284
+ ppt: { width: p.width, height: p.height, src: p.src }
2285
+ }
2286
+ });
2107
2287
  }
2108
2288
  if (logger) {
2109
2289
  logger.info(`[Presentation] syncPage ${scenePath}/${name}`);
@@ -2115,15 +2295,21 @@ var NetlessAppPresentation = {
2115
2295
  if (!context.isAddApp)
2116
2296
  return;
2117
2297
  if (pages.length > 100)
2118
- warn(`[Presentation]: too many pages (${pages.length}), may cause performance issues`);
2298
+ warn(
2299
+ `[Presentation]: too many pages (${pages.length}), may cause performance issues`
2300
+ );
2119
2301
  if (!room || !room.isWritable)
2120
2302
  return;
2121
2303
  if (pageIndex$.value < 0 || pageIndex$.value >= pages.length) {
2122
- throw new Error(`[Presentation] Invalid page index: ${pageIndex$.value}, scenes length: ${pages.length}`);
2304
+ throw new Error(
2305
+ `[Presentation] Invalid page index: ${pageIndex$.value}, scenes length: ${pages.length}`
2306
+ );
2123
2307
  }
2124
2308
  const scenes = room.entireScenes()[scenePath];
2125
2309
  if (!scenes || !scenes[pageIndex$.value]) {
2126
- throw new Error(`[Presentation]: no initial scene found at ${scenePath}, page index: ${pageIndex$.value}`);
2310
+ throw new Error(
2311
+ `[Presentation]: no initial scene found at ${scenePath}, page index: ${pageIndex$.value}`
2312
+ );
2127
2313
  }
2128
2314
  const { name, ppt } = scenes[pageIndex$.value];
2129
2315
  const nextScenes = pages.map((page, index) => {
@@ -2152,16 +2338,22 @@ var NetlessAppPresentation = {
2152
2338
  const prepareScenesPromise = prepareScenes();
2153
2339
  const canJumpPage = (index) => {
2154
2340
  if (!context.getIsWritable()) {
2155
- warn("[Presentation]: no permission, make sure you have test room.isWritable");
2341
+ warn(
2342
+ "[Presentation]: no permission, make sure you have test room.isWritable"
2343
+ );
2156
2344
  return false;
2157
2345
  }
2158
2346
  if (!(0 <= index && index < pages.length)) {
2159
- warn(`[Presentation]: page ${index + 1} out of bounds [1, ${pages.length}]`);
2347
+ warn(
2348
+ `[Presentation]: page ${index + 1} out of bounds [1, ${pages.length}]`
2349
+ );
2160
2350
  return false;
2161
2351
  }
2162
2352
  const scenes = context.getDisplayer().entireScenes()[scenePath];
2163
2353
  if (!scenes) {
2164
- warn(`[Presentation]: no scenes found at ${scenePath}, make sure you have added options.scenePath in addApp()`);
2354
+ warn(
2355
+ `[Presentation]: no scenes found at ${scenePath}, make sure you have added options.scenePath in addApp()`
2356
+ );
2165
2357
  return false;
2166
2358
  }
2167
2359
  return true;
@@ -2267,13 +2459,20 @@ var NetlessAppPresentation = {
2267
2459
  try {
2268
2460
  const { camera, size } = view;
2269
2461
  const referenceSize = getCameraReferenceSize(originSize, page);
2270
- const viewport = cameraToSharedViewport(camera, size, referenceSize);
2462
+ const viewport = cameraToSharedViewport(
2463
+ camera,
2464
+ size,
2465
+ referenceSize
2466
+ );
2271
2467
  if (viewport)
2272
2468
  view$$.setState(__spreadValues({ uid: me }, viewport));
2273
2469
  if (pendingMoveCameraRequest) {
2274
2470
  diagnosticLogger.debouncedInfo(
2275
2471
  "moveCamera",
2276
- getCameraDiagnosticState("moveCamera", pendingMoveCameraRequest)
2472
+ getCameraDiagnosticState(
2473
+ "moveCamera",
2474
+ pendingMoveCameraRequest
2475
+ )
2277
2476
  );
2278
2477
  pendingMoveCameraRequest = void 0;
2279
2478
  }
@@ -2281,7 +2480,10 @@ var NetlessAppPresentation = {
2281
2480
  diagnosticLogger.error(
2282
2481
  "syncView.failed",
2283
2482
  error,
2284
- getCameraDiagnosticState("moveCamera", pendingMoveCameraRequest)
2483
+ getCameraDiagnosticState(
2484
+ "moveCamera",
2485
+ pendingMoveCameraRequest
2486
+ )
2285
2487
  );
2286
2488
  pendingMoveCameraRequest = void 0;
2287
2489
  }
@@ -2306,17 +2508,32 @@ var NetlessAppPresentation = {
2306
2508
  });
2307
2509
  }
2308
2510
  };
2309
- dispose2.add(view$$.addStateChangedListener(() => syncViewFromRemote(false, true)));
2511
+ dispose2.add(
2512
+ view$$.addStateChangedListener(() => syncViewFromRemote(false, true))
2513
+ );
2310
2514
  const getPageSize = () => {
2311
2515
  const { width, height } = app.page() || {};
2312
2516
  return { width: width || 0, height: height || 0 };
2313
2517
  };
2314
2518
  const box = context.getBox();
2315
- const app = dispose2.add(createPresentation(box, pages, jumpPage, pageIndex$, view, options.thumbnail, options.useClipView));
2316
- app.contentDOM.dataset.appPresentationVersion = "0.1.12";
2519
+ const app = dispose2.add(
2520
+ createPresentation(
2521
+ box,
2522
+ pages,
2523
+ jumpPage,
2524
+ pageIndex$,
2525
+ view,
2526
+ options.thumbnail,
2527
+ options.useClipView
2528
+ )
2529
+ );
2530
+ app.contentDOM.dataset.appPresentationVersion = "0.1.13-beta.1";
2317
2531
  app.scaleDocsToFit = scaleDocsToFit;
2318
2532
  app.log = log;
2319
2533
  app.warn = warn;
2534
+ if (pageIndex$.value !== 0) {
2535
+ app.preload.touch(pageIndex$.value, true);
2536
+ }
2320
2537
  const getCameraDiagnosticState = (reason, requestedCamera) => {
2321
2538
  const page = app.page();
2322
2539
  const pageSize = page && isValidSize(page) ? { width: page.width, height: page.height } : void 0;
@@ -2360,19 +2577,21 @@ var NetlessAppPresentation = {
2360
2577
  };
2361
2578
  if (originSize) {
2362
2579
  let previousPageIndex = pageIndex$.value;
2363
- dispose2.add(pageIndex$.subscribe((nextPageIndex) => {
2364
- if (nextPageIndex === previousPageIndex)
2365
- return;
2366
- previousPageIndex = nextPageIndex;
2367
- scaleDocsToFit();
2368
- }));
2580
+ dispose2.add(
2581
+ pageIndex$.subscribe((nextPageIndex) => {
2582
+ if (nextPageIndex === previousPageIndex)
2583
+ return;
2584
+ previousPageIndex = nextPageIndex;
2585
+ scaleDocsToFit();
2586
+ })
2587
+ );
2369
2588
  }
2370
2589
  if (options.justDocsViewReadonly) {
2371
2590
  app.setDocsViewReadonly(true);
2372
2591
  }
2373
2592
  const goToPageByClick = () => {
2374
- var _a2, _b2, _c2, _d;
2375
- const currentApplianceName = (_d = (_c2 = (_b2 = (_a2 = context.getRoom()) == null ? void 0 : _a2.state) == null ? void 0 : _b2.memberState) == null ? void 0 : _c2.currentApplianceName) != null ? _d : "";
2593
+ var _a2, _b2, _c2, _d2;
2594
+ const currentApplianceName = (_d2 = (_c2 = (_b2 = (_a2 = context.getRoom()) == null ? void 0 : _a2.state) == null ? void 0 : _b2.memberState) == null ? void 0 : _c2.currentApplianceName) != null ? _d2 : "";
2376
2595
  if (!app.readonly && currentApplianceName === "clicker") {
2377
2596
  nextPage();
2378
2597
  }
@@ -2414,9 +2633,14 @@ var NetlessAppPresentation = {
2414
2633
  });
2415
2634
  syncViewFromRemote(true);
2416
2635
  reportInitializedCamera("setup");
2417
- dispose2.add(context.emitter.on("writableChange", (isWritable) => {
2418
- app.setReadonly(!isWritable);
2419
- }));
2636
+ dispose2.add(
2637
+ context.emitter.on("writableChange", (isWritable) => {
2638
+ app.setReadonly(!isWritable);
2639
+ })
2640
+ );
2641
+ if (!options.disableBlurThumbnailDegradation) {
2642
+ setupBlurThumbnailDegradation({ context, view, pages, pageIndex$, app, dispose: dispose2 });
2643
+ }
2420
2644
  const getOriginScale = () => {
2421
2645
  const page = app.page();
2422
2646
  if (!page || !isValidSize(page))
@@ -2438,29 +2662,53 @@ var NetlessAppPresentation = {
2438
2662
  img.crossOrigin = "Anonymous";
2439
2663
  await new Promise((resolve, reject) => {
2440
2664
  img.onload = () => resolve();
2441
- img.onerror = () => reject(new Error(
2442
- `[Presentation]: failed to load screenshot page image: ${safeResourceLocation(src)}`
2443
- ));
2665
+ img.onerror = () => reject(
2666
+ new Error(
2667
+ `[Presentation]: failed to load screenshot page image: ${safeResourceLocation(src)}`
2668
+ )
2669
+ );
2444
2670
  img.src = src;
2445
2671
  });
2446
- _context.drawImage(img, 0, 0, width, height, 0, 0, _width || width, _height || height);
2672
+ _context.drawImage(
2673
+ img,
2674
+ 0,
2675
+ 0,
2676
+ width,
2677
+ height,
2678
+ 0,
2679
+ 0,
2680
+ _width || width,
2681
+ _height || height
2682
+ );
2447
2683
  const currentScenePath = view.focusScenePath;
2448
2684
  if (!currentScenePath) {
2449
2685
  throw new Error("[Presentation]: current scene path not found");
2450
2686
  }
2451
2687
  const windowManger = context.getWindowManager();
2452
2688
  if (windowManger._appliancePlugin) {
2453
- await windowManger._appliancePlugin.screenshotToCanvasAsync(_context, currentScenePath, _width || width, _height || height, {
2454
- centerX: 0,
2455
- centerY: 0,
2456
- scale: _width && _height ? Math.min(_width / width, _height / height) : 1
2457
- });
2689
+ await windowManger._appliancePlugin.screenshotToCanvasAsync(
2690
+ _context,
2691
+ currentScenePath,
2692
+ _width || width,
2693
+ _height || height,
2694
+ {
2695
+ centerX: 0,
2696
+ centerY: 0,
2697
+ scale: _width && _height ? Math.min(_width / width, _height / height) : 1
2698
+ }
2699
+ );
2458
2700
  } else {
2459
- await view.screenshotToCanvasAsync(_context, currentScenePath, _width || width, _height || height, {
2460
- centerX: 0,
2461
- centerY: 0,
2462
- scale: _width && _height ? Math.min(_width / width, _height / height) : 1
2463
- });
2701
+ await view.screenshotToCanvasAsync(
2702
+ _context,
2703
+ currentScenePath,
2704
+ _width || width,
2705
+ _height || height,
2706
+ {
2707
+ centerX: 0,
2708
+ centerY: 0,
2709
+ scale: _width && _height ? Math.min(_width / width, _height / height) : 1
2710
+ }
2711
+ );
2464
2712
  }
2465
2713
  };
2466
2714
  const screenshotCurrentPageAsync = async (_context, _width, _height) => {
@@ -2478,21 +2726,27 @@ var NetlessAppPresentation = {
2478
2726
  let scrollbar;
2479
2727
  if (options.useScrollbar) {
2480
2728
  dispose2.make(() => {
2481
- scrollbar = new Scrollbar(app.contentDOM, {
2482
- appId: context.appId,
2483
- getPageSize,
2484
- getOriginScale,
2485
- syncView,
2486
- getWritable: () => context.getIsWritable(),
2487
- scrollbarEventCallback: options.scrollbarEventCallback
2488
- }, view);
2729
+ scrollbar = new Scrollbar(
2730
+ app.contentDOM,
2731
+ {
2732
+ appId: context.appId,
2733
+ getPageSize,
2734
+ getOriginScale,
2735
+ syncView,
2736
+ getWritable: () => context.getIsWritable(),
2737
+ scrollbarEventCallback: options.scrollbarEventCallback
2738
+ },
2739
+ view
2740
+ );
2489
2741
  return () => scrollbar == null ? void 0 : scrollbar.destroy();
2490
2742
  });
2491
2743
  }
2492
2744
  const moveCamera = (camera) => {
2493
2745
  try {
2494
2746
  if (!context.getIsWritable()) {
2495
- throw new Error("[Presentation]: moveCamera must be called in writable room");
2747
+ throw new Error(
2748
+ "[Presentation]: moveCamera must be called in writable room"
2749
+ );
2496
2750
  }
2497
2751
  pendingMoveCameraRequest = __spreadValues({}, camera);
2498
2752
  if (scrollbar) {
@@ -2513,9 +2767,29 @@ var NetlessAppPresentation = {
2513
2767
  throw error;
2514
2768
  }
2515
2769
  };
2516
- context.emitter.on("destroy", () => dispose2());
2770
+ let disposed = false;
2771
+ let offDestroy;
2772
+ const teardown = () => {
2773
+ if (disposed)
2774
+ return;
2775
+ disposed = true;
2776
+ const removeDestroy = offDestroy;
2777
+ offDestroy = void 0;
2778
+ removeDestroy == null ? void 0 : removeDestroy();
2779
+ dispose2();
2780
+ };
2781
+ offDestroy = context.emitter.on("destroy", teardown);
2782
+ teardownByContext.set(context, teardown);
2517
2783
  const reportProgress = (progress, result) => {
2518
- window.postMessage({ type: "@netless/_result_save_pdf_", appId: context.appId, progress, result }, "*");
2784
+ window.postMessage(
2785
+ {
2786
+ type: "@netless/_result_save_pdf_",
2787
+ appId: context.appId,
2788
+ progress,
2789
+ result
2790
+ },
2791
+ "*"
2792
+ );
2519
2793
  return result;
2520
2794
  };
2521
2795
  const base64url = async (url) => {
@@ -2555,7 +2829,9 @@ var NetlessAppPresentation = {
2555
2829
  }
2556
2830
  const scenes = context.getDisplayer().entireScenes()[scenePath];
2557
2831
  if (!scenes) {
2558
- throw new Error(`[Presentation]: no scenes found while exporting PDF: ${scenePath}`);
2832
+ throw new Error(
2833
+ `[Presentation]: no scenes found while exporting PDF: ${scenePath}`
2834
+ );
2559
2835
  }
2560
2836
  const stage_canvas = document.createElement("canvas");
2561
2837
  stage_canvas.width = pdfWidth;
@@ -2580,20 +2856,32 @@ var NetlessAppPresentation = {
2580
2856
  const img = document.createElement("img");
2581
2857
  await new Promise((resolve, reject) => {
2582
2858
  img.onload = () => resolve();
2583
- img.onerror = () => reject(new Error(
2584
- `[Presentation]: failed to load PDF page image, page index: ${index}`
2585
- ));
2859
+ img.onerror = () => reject(
2860
+ new Error(
2861
+ `[Presentation]: failed to load PDF page image, page index: ${index}`
2862
+ )
2863
+ );
2586
2864
  img.src = url;
2587
2865
  });
2588
2866
  stage.drawImage(img, 0, 0, width2, height2);
2589
2867
  wb.clearRect(0, 0, pdfWidth, pdfHeight);
2590
2868
  const name = (_a2 = p.name) != null ? _a2 : String(index + 1);
2591
2869
  if (scenes.some((scene) => scene.name == name)) {
2592
- const camera = { centerX: 0, centerY: 0, scale: Math.min(wb_canvas.width / width2, wb_canvas.height / height2) };
2870
+ const camera = {
2871
+ centerX: 0,
2872
+ centerY: 0,
2873
+ scale: Math.min(wb_canvas.width / width2, wb_canvas.height / height2)
2874
+ };
2593
2875
  const sPath = `${scenePath}/${name}`;
2594
2876
  const windowManger = context.getWindowManager();
2595
2877
  if (windowManger._appliancePlugin) {
2596
- await windowManger._appliancePlugin.screenshotToCanvasAsync(wb, sPath, wb_canvas.width, wb_canvas.height, camera);
2878
+ await windowManger._appliancePlugin.screenshotToCanvasAsync(
2879
+ wb,
2880
+ sPath,
2881
+ wb_canvas.width,
2882
+ wb_canvas.height,
2883
+ camera
2884
+ );
2597
2885
  } else {
2598
2886
  await view.screenshotToCanvasAsync(
2599
2887
  wb,
@@ -2640,14 +2928,20 @@ var NetlessAppPresentation = {
2640
2928
  throw error;
2641
2929
  }
2642
2930
  };
2643
- dispose2.add(listen(window, "message", (ev) => {
2644
- if (ev.data && ev.data.type == "@netless/_request_save_pdf_" && ev.data.appId == context.appId) {
2645
- toPdf().catch((err) => {
2646
- warn(err);
2647
- reportProgress(100, null);
2648
- });
2649
- }
2650
- }));
2931
+ dispose2.add(
2932
+ listen(
2933
+ window,
2934
+ "message",
2935
+ (ev) => {
2936
+ if (ev.data && ev.data.type == "@netless/_request_save_pdf_" && ev.data.appId == context.appId) {
2937
+ toPdf().catch((err) => {
2938
+ warn(err);
2939
+ reportProgress(100, null);
2940
+ });
2941
+ }
2942
+ }
2943
+ )
2944
+ );
2651
2945
  const setDocsViewReadonly = (bol) => {
2652
2946
  app.setDocsViewReadonly(bol);
2653
2947
  };
@@ -2658,19 +2952,57 @@ var NetlessAppPresentation = {
2658
2952
  scrollbar.setReadonly(bol);
2659
2953
  }
2660
2954
  };
2661
- const controller = { app, view, context, jumpPage, prevPage, nextPage, jumpPageAsync, prevPageAsync, nextPageAsync, pageState, toPdf, log, setDocsViewReadonly, setReadonly, moveCamera, getOriginScale, getScale, getPageSize, screenshotCurrentPageAsync };
2662
- dispose2.add(listen(window, "message", (ev) => {
2663
- if (ev.data === "@netless/_presentation_") {
2664
- if (typeof window !== "undefined")
2665
- dispose2.make(() => {
2666
- const debug = window._presentation_ || (window._presentation_ = /* @__PURE__ */ new Set());
2667
- debug.add(controller);
2668
- return () => debug.delete(controller);
2669
- });
2670
- console.log(controller);
2671
- }
2672
- }));
2673
- return prepareScenesPromise.then(() => controller);
2955
+ const controller = {
2956
+ app,
2957
+ view,
2958
+ context,
2959
+ jumpPage,
2960
+ prevPage,
2961
+ nextPage,
2962
+ jumpPageAsync,
2963
+ prevPageAsync,
2964
+ nextPageAsync,
2965
+ pageState,
2966
+ toPdf,
2967
+ log,
2968
+ setDocsViewReadonly,
2969
+ setReadonly,
2970
+ moveCamera,
2971
+ getOriginScale,
2972
+ getScale,
2973
+ getPageSize,
2974
+ screenshotCurrentPageAsync
2975
+ };
2976
+ dispose2.add(
2977
+ listen(
2978
+ window,
2979
+ "message",
2980
+ (ev) => {
2981
+ if (ev.data === "@netless/_presentation_") {
2982
+ if (typeof window !== "undefined")
2983
+ dispose2.make(() => {
2984
+ const debug = window._presentation_ || (window._presentation_ = /* @__PURE__ */ new Set());
2985
+ debug.add(controller);
2986
+ return () => debug.delete(controller);
2987
+ });
2988
+ console.log(controller);
2989
+ }
2990
+ }
2991
+ )
2992
+ );
2993
+ const setupReadyTimeout = (_d = options.setupReadyTimeout) != null ? _d : DEFAULT_SETUP_READY_TIMEOUT;
2994
+ return prepareScenesPromise.then(async () => {
2995
+ const loaded = await waitForCurrentPageImage(view, setupReadyTimeout, () => disposed);
2996
+ if (!loaded) {
2997
+ warn(`[Presentation] setup ready wait timed out after ${setupReadyTimeout}ms`);
2998
+ }
2999
+ return controller;
3000
+ });
3001
+ },
3002
+ teardown(context) {
3003
+ var _a;
3004
+ (_a = teardownByContext.get(context)) == null ? void 0 : _a();
3005
+ teardownByContext.delete(context);
2674
3006
  }
2675
3007
  };
2676
3008
  var AppPresentation = class extends Presentation {
@@ -2690,19 +3022,29 @@ var AppPresentation = class extends Presentation {
2690
3022
  if (0 <= index && index < this.pages.length) {
2691
3023
  this.jumpPage(index);
2692
3024
  } else {
2693
- (_a = this.warn) == null ? void 0 : _a.call(this, `[Presentation]: page index ${index} out of bounds [0, ${this.pages.length - 1}]`);
3025
+ (_a = this.warn) == null ? void 0 : _a.call(
3026
+ this,
3027
+ `[Presentation]: page index ${index} out of bounds [0, ${this.pages.length - 1}]`
3028
+ );
2694
3029
  }
2695
3030
  }
2696
3031
  };
2697
3032
  function createPresentation(box, pages, jumpPage, pageIndex$, view, thumbnail, useClipView) {
2698
3033
  box.mountStyles(style_default);
2699
- const app = new AppPresentation({ pages, readonly: box.readonly, jumpPage, thumbnail });
3034
+ const app = new AppPresentation({
3035
+ pages,
3036
+ readonly: box.readonly,
3037
+ jumpPage,
3038
+ thumbnail
3039
+ });
2700
3040
  app.box = box;
2701
3041
  box.mountContent(app.contentDOM);
2702
3042
  box.mountFooter(app.footerDOM);
2703
- app.dispose.add(pageIndex$.subscribe((pageIndex) => {
2704
- app.setPageIndex(pageIndex);
2705
- }));
3043
+ app.dispose.add(
3044
+ pageIndex$.subscribe((pageIndex) => {
3045
+ app.setPageIndex(pageIndex);
3046
+ })
3047
+ );
2706
3048
  if (useClipView && view) {
2707
3049
  const onCameraUpdatedEffectForMaskView = (0, import_debounce2.default)(() => {
2708
3050
  const { width: pageWidth, height: pageHeight } = app.page() || {};
@@ -2711,8 +3053,12 @@ function createPresentation(box, pages, jumpPage, pageIndex$, view, thumbnail, u
2711
3053
  }
2712
3054
  const { scale } = view.camera;
2713
3055
  const { width, height } = view.size;
2714
- const pageRatioX = Math.round((1 - pageWidth * scale / width) / 2 * 100);
2715
- const pageRatioY = Math.round((1 - pageHeight * scale / height) / 2 * 100);
3056
+ const pageRatioX = Math.round(
3057
+ (1 - pageWidth * scale / width) / 2 * 100
3058
+ );
3059
+ const pageRatioY = Math.round(
3060
+ (1 - pageHeight * scale / height) / 2 * 100
3061
+ );
2716
3062
  app.whiteboardDOM.style.clipPath = `inset(${pageRatioY}% ${pageRatioX}%)`;
2717
3063
  }, 50);
2718
3064
  onCameraUpdatedEffectForMaskView();
@@ -2737,7 +3083,7 @@ var install = (register, options = {}) => {
2737
3083
  };
2738
3084
 
2739
3085
  // src/index.ts
2740
- var version = "0.1.12";
3086
+ var version = "0.1.13-beta.1";
2741
3087
 
2742
3088
  exports.NetlessAppPresentation = NetlessAppPresentation;
2743
3089
  exports.Presentation = Presentation;