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