@netless/app-presentation 0.1.13-beta.1 → 0.1.13-beta.3

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
@@ -1154,6 +1154,7 @@ var _Preload = class _Preload {
1154
1154
  // 暂停后不再创建预加载链接(app 失焦降级时调用);已插入的 link 会被移除,
1155
1155
  // 挂起的 idle 回调重新进入 touch() 时也会被跳过
1156
1156
  this.paused = false;
1157
+ this.disposed = false;
1157
1158
  this.preloadMap = new Map(pages.map((e, index) => [index, {
1158
1159
  src: e.src,
1159
1160
  state: 0 /* unloaded */
@@ -1168,13 +1169,13 @@ var _Preload = class _Preload {
1168
1169
  this.destroySomeLink();
1169
1170
  }
1170
1171
  resume(index) {
1171
- if (!this.paused)
1172
+ if (!this.paused || this.disposed)
1172
1173
  return;
1173
1174
  this.paused = false;
1174
1175
  this.touch(index != null ? index : this.touchIndex, true);
1175
1176
  }
1176
1177
  touch(index, force = false) {
1177
- if (this.paused)
1178
+ if (this.paused || this.disposed)
1178
1179
  return;
1179
1180
  if (index >= this.preloadSize) {
1180
1181
  this.touchIndex = 0;
@@ -1182,11 +1183,11 @@ var _Preload = class _Preload {
1182
1183
  this.touchIndex = index;
1183
1184
  }
1184
1185
  const value = this.preloadMap.get(this.touchIndex);
1185
- if (value && value.state === 0 /* unloaded */) {
1186
+ if (value && (value.state === 0 /* unloaded */ || force && value.state === 2 /* error */)) {
1186
1187
  this.createLink(this.touchIndex, force);
1187
1188
  }
1188
1189
  if (this.loadingLinks.size < _Preload.maxLinks) {
1189
- const willLoad = [...this.preloadMap.entries()].find(([i, e]) => i > this.touchIndex && e.state !== 1 /* loaded */);
1190
+ const willLoad = [...this.preloadMap.entries()].find(([i, e]) => i > this.touchIndex && e.state === 0 /* unloaded */);
1190
1191
  if (willLoad) {
1191
1192
  this.requestAsyncCallBack(() => {
1192
1193
  this.touch(willLoad[0], false);
@@ -1219,21 +1220,27 @@ var _Preload = class _Preload {
1219
1220
  linkDom.as = "image";
1220
1221
  linkDom.href = value.src;
1221
1222
  linkDom.dataset.order = index + "";
1222
- document.head.appendChild(linkDom);
1223
1223
  linkDom.onload = () => {
1224
+ var _a;
1225
+ if (((_a = this.loadingLinks.get(index)) == null ? void 0 : _a.link) !== linkDom)
1226
+ return;
1224
1227
  const value2 = this.preloadMap.get(index);
1225
1228
  if (value2) {
1226
1229
  value2.state = 1 /* loaded */;
1227
1230
  this.preloadMap.set(index, value2);
1228
- document.head.contains(linkDom) && document.head.removeChild(linkDom);
1229
- this.loadingLinks.delete(index);
1231
+ this.removeLink(index, linkDom);
1230
1232
  this.touch(this.touchIndex + 1, false);
1231
1233
  }
1232
1234
  };
1233
1235
  linkDom.onerror = () => {
1236
+ var _a;
1237
+ if (((_a = this.loadingLinks.get(index)) == null ? void 0 : _a.link) !== linkDom)
1238
+ return;
1234
1239
  const value2 = this.preloadMap.get(index);
1235
- if (value2 == null ? void 0 : value2.src) {
1236
- linkDom.href = value2.src;
1240
+ if (value2) {
1241
+ value2.state = 2 /* error */;
1242
+ this.removeLink(index, linkDom);
1243
+ this.touch(this.touchIndex + 1, false);
1237
1244
  }
1238
1245
  };
1239
1246
  this.loadingLinks.set(index, {
@@ -1242,17 +1249,23 @@ var _Preload = class _Preload {
1242
1249
  });
1243
1250
  value.state = 0 /* unloaded */;
1244
1251
  this.preloadMap.set(index, value);
1252
+ document.head.appendChild(linkDom);
1245
1253
  return 0 /* unloaded */;
1246
1254
  }
1247
1255
  return 2 /* error */;
1248
1256
  }
1257
+ removeLink(index, link) {
1258
+ link.onload = null;
1259
+ link.onerror = null;
1260
+ document.head.contains(link) && document.head.removeChild(link);
1261
+ this.loadingLinks.delete(index);
1262
+ }
1249
1263
  destroySomeLink(excludeIndex) {
1250
1264
  for (const [index, { link }] of this.loadingLinks) {
1251
1265
  if (excludeIndex !== void 0 && index === excludeIndex) {
1252
1266
  continue;
1253
1267
  }
1254
- document.head.contains(link) && document.head.removeChild(link);
1255
- this.loadingLinks.delete(index);
1268
+ this.removeLink(index, link);
1256
1269
  }
1257
1270
  }
1258
1271
  async requestAsyncCallBack(callBack, timeout) {
@@ -1270,6 +1283,7 @@ var _Preload = class _Preload {
1270
1283
  callBack();
1271
1284
  }
1272
1285
  dispose() {
1286
+ this.disposed = true;
1273
1287
  this.destroySomeLink();
1274
1288
  this.preloadMap.clear();
1275
1289
  }
@@ -1317,10 +1331,7 @@ var Presentation = class {
1317
1331
  if (this.initialized)
1318
1332
  return;
1319
1333
  this.initialized = true;
1320
- this.dispose.add(() => {
1321
- var _a2;
1322
- return (_a2 = this.previewLazyload) == null ? void 0 : _a2.destroy();
1323
- });
1334
+ this.dispose.add(() => this.destroyPreviewLazyload());
1324
1335
  this.contentDOM.classList.toggle(this.c("readonly"), this.readonly);
1325
1336
  this.dom.appendChild(this.contentDOM);
1326
1337
  const previewMask = this.contentDOM.appendChild(document.createElement("div"));
@@ -1473,6 +1484,17 @@ var Presentation = class {
1473
1484
  }
1474
1485
  }
1475
1486
  }
1487
+ /** Release preview images while this presentation stays in the runtime cache. */
1488
+ suspendPreviewResources() {
1489
+ this.togglePreview(false);
1490
+ this.destroyPreviewLazyload();
1491
+ this.previewDOM.querySelectorAll("img").forEach((img) => {
1492
+ img.removeAttribute("src");
1493
+ img.removeAttribute("srcset");
1494
+ img.removeAttribute("sizes");
1495
+ LazyLoad.resetStatus(img);
1496
+ });
1497
+ }
1476
1498
  // [origin] means the cause:
1477
1499
  // - navigation: user clicked on the left / right button on footer
1478
1500
  // - input: user filled the bottom-right page number input box
@@ -1507,6 +1529,11 @@ var Presentation = class {
1507
1529
  return src;
1508
1530
  }
1509
1531
  }
1532
+ destroyPreviewLazyload() {
1533
+ var _a;
1534
+ (_a = this.previewLazyload) == null ? void 0 : _a.destroy();
1535
+ this.previewLazyload = null;
1536
+ }
1510
1537
  c(className) {
1511
1538
  return `${this.namespace}-${className}`;
1512
1539
  }
@@ -1519,7 +1546,7 @@ var Presentation = class {
1519
1546
  };
1520
1547
 
1521
1548
  // src/style.scss?inline
1522
- var style_default = ".netless-app-presentation{height:100%;display:flex;flex-flow:column nowrap}.netless-app-presentation-content{position:relative;height:100%;overflow:hidden}.netless-app-presentation-preview-mask{display:none;position:absolute;z-index:200;top:0;left:0;width:100%;height:100%}.netless-app-presentation-preview{display:flex;flex-direction:column;align-items:center;position:absolute;z-index:300;top:0;left:0;width:33%;max-width:200px;height:100%;padding-top:10px;transform:translate(-100%);background:#ededf0e6;box-shadow:inset -1px 0 #0000001c;transition:transform .4s;overflow:auto;overflow-y:scroll}.netless-app-presentation-preview-active .netless-app-presentation-preview-mask{display:block}.netless-app-presentation-preview-active .netless-app-presentation-preview{transform:translate(0)}.netless-app-presentation-preview-page{position:relative;display:block;width:55%;margin-bottom:10px;font-size:0;color:#0000;outline:none;border:7px solid rgba(0,0,0,0);border-radius:4px;transition:border-color .3s;user-select:none}.netless-app-presentation-preview-page:hover,.netless-app-presentation-preview-page.netless-app-presentation-preview-page-active{border-color:#444e601a}.netless-app-presentation-preview-page>img{width:100%;height:auto;box-sizing:border-box;border:1px solid rgba(0,0,0,.5);border-radius:1px;background-color:#fff;box-shadow:0 2px 8px #0000004d}.netless-app-presentation-preview-page-name{position:absolute;top:1px;left:-10px;transform:translate(-100%);text-align:right;font-size:12px;color:#5f5f5f;user-select:none}.netless-app-presentation-footer{box-sizing:border-box;height:26px;display:flex;align-items:center;padding:0 16px;border-top:1px solid #eeeef7;color:#191919}.netless-app-presentation-float-footer{width:100%;min-height:26px;position:absolute;left:0;bottom:0;z-index:2000;background:#f9f9fce6;transition:opacity .4s}.netless-app-presentation-footer-btn{box-sizing:border-box;width:26px;height:26px;font-size:0;margin:0;padding:3px;border:none;border-radius:1px;outline:none;color:currentColor;background:#0000;transition:background .4s;cursor:pointer;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.netless-app-presentation-footer-btn:hover{background:#ededf0e6}@media (hover: none){.netless-app-presentation-footer-btn:hover{background:#0000!important}}.netless-app-presentation-footer-btn>svg{width:100%;height:100%}.netless-app-presentation-footer-btn>svg:nth-of-type(2){display:none}.netless-app-presentation-footer-btn.netless-app-presentation-footer-btn-playing>svg:nth-of-type(1){display:none}.netless-app-presentation-footer-btn.netless-app-presentation-footer-btn-playing>svg:nth-of-type(2){display:initial}.netless-app-presentation-footer-btn~.netless-app-presentation-footer-btn{margin-left:15px}.netless-app-presentation-page-jumps{flex:1;display:flex;justify-content:center;align-items:center}.netless-app-presentation-page-number{margin-left:auto;font-size:13px;user-select:none;white-space:nowrap;word-break:keep-all}.netless-app-presentation-page-number-input{border:none;outline:none;width:3em;margin:0;padding:0 2px;text-align:right;font-size:13px;line-height:1;font-weight:400;font-family:inherit;border-radius:2px;color:currentColor;background:#0000;transition:background .4s;user-select:text;-webkit-tap-highlight-color:rgba(0,0,0,0)}.netless-app-presentation-page-number-input:hover,.netless-app-presentation-page-number-input:focus,.netless-app-presentation-page-number-input:active{background:#fff;box-shadow:#63636333 0 2px 8px}.netless-app-presentation-readonly.netless-app-presentation-footer{display:none}.netless-app-presentation-wb-view{position:absolute;top:0;left:0;width:100%;height:100%;z-index:100;overflow:hidden;transition:opacity .2s}.netless-app-presentation-image{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.netless-app-presentation-image>img{max-width:100%;max-height:100%;object-fit:contain}.telebox-color-scheme-dark .netless-app-presentation-page-number-input{color:#a6a6a8}.telebox-color-scheme-dark .netless-app-presentation-page-number-input:active,.telebox-color-scheme-dark .netless-app-presentation-page-number-input:focus,.telebox-color-scheme-dark .netless-app-presentation-page-number-input:hover{color:#222}.telebox-color-scheme-dark .netless-app-presentation-footer{color:#a6a6a8;background:#2d2d33;border-top:none}.telebox-color-scheme-dark .netless-app-presentation-footer-btn:hover{background:#212126}.telebox-color-scheme-dark .netless-app-presentation-preview{background:#323232e6}.netless-app-presentation-scrollbar-container{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:101}.netless-app-presentation-scrollbar-container-x,.netless-app-presentation-scrollbar-container-y{position:absolute;pointer-events:none;display:flex;justify-content:center;box-sizing:border-box;padding:0}.netless-app-presentation-scrollbar-container-x{bottom:0;left:0;height:6px;width:100%;padding-right:6px}.netless-app-presentation-scrollbar-container-y{top:0;right:0;width:6px;height:100%;flex-direction:column;padding-bottom:6px}.netless-app-presentation-scrollbar-x,.netless-app-presentation-scrollbar-y{border-radius:2px;background-color:#1b1f4d4d;pointer-events:auto;cursor:default}.netless-app-presentation-scrollbar-x{height:6px}.netless-app-presentation-scrollbar-y{width:6px}";
1549
+ var style_default = ".netless-app-presentation{height:100%;display:flex;flex-flow:column nowrap}.netless-app-presentation-content{position:relative;height:100%;overflow:hidden}.netless-app-presentation-background-suspended .background{display:none!important}.netless-app-presentation-preview-mask{display:none;position:absolute;z-index:200;top:0;left:0;width:100%;height:100%}.netless-app-presentation-preview{display:flex;flex-direction:column;align-items:center;position:absolute;z-index:300;top:0;left:0;width:33%;max-width:200px;height:100%;padding-top:10px;transform:translate(-100%);background:#ededf0e6;box-shadow:inset -1px 0 #0000001c;transition:transform .4s;overflow:auto;overflow-y:scroll}.netless-app-presentation-preview-active .netless-app-presentation-preview-mask{display:block}.netless-app-presentation-preview-active .netless-app-presentation-preview{transform:translate(0)}.netless-app-presentation-preview-page{position:relative;display:block;width:55%;margin-bottom:10px;font-size:0;color:#0000;outline:none;border:7px solid rgba(0,0,0,0);border-radius:4px;transition:border-color .3s;user-select:none}.netless-app-presentation-preview-page:hover,.netless-app-presentation-preview-page.netless-app-presentation-preview-page-active{border-color:#444e601a}.netless-app-presentation-preview-page>img{width:100%;height:auto;box-sizing:border-box;border:1px solid rgba(0,0,0,.5);border-radius:1px;background-color:#fff;box-shadow:0 2px 8px #0000004d}.netless-app-presentation-preview-page-name{position:absolute;top:1px;left:-10px;transform:translate(-100%);text-align:right;font-size:12px;color:#5f5f5f;user-select:none}.netless-app-presentation-footer{box-sizing:border-box;height:26px;display:flex;align-items:center;padding:0 16px;border-top:1px solid #eeeef7;color:#191919}.netless-app-presentation-float-footer{width:100%;min-height:26px;position:absolute;left:0;bottom:0;z-index:2000;background:#f9f9fce6;transition:opacity .4s}.netless-app-presentation-footer-btn{box-sizing:border-box;width:26px;height:26px;font-size:0;margin:0;padding:3px;border:none;border-radius:1px;outline:none;color:currentColor;background:#0000;transition:background .4s;cursor:pointer;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.netless-app-presentation-footer-btn:hover{background:#ededf0e6}@media (hover: none){.netless-app-presentation-footer-btn:hover{background:#0000!important}}.netless-app-presentation-footer-btn>svg{width:100%;height:100%}.netless-app-presentation-footer-btn>svg:nth-of-type(2){display:none}.netless-app-presentation-footer-btn.netless-app-presentation-footer-btn-playing>svg:nth-of-type(1){display:none}.netless-app-presentation-footer-btn.netless-app-presentation-footer-btn-playing>svg:nth-of-type(2){display:initial}.netless-app-presentation-footer-btn~.netless-app-presentation-footer-btn{margin-left:15px}.netless-app-presentation-page-jumps{flex:1;display:flex;justify-content:center;align-items:center}.netless-app-presentation-page-number{margin-left:auto;font-size:13px;user-select:none;white-space:nowrap;word-break:keep-all}.netless-app-presentation-page-number-input{border:none;outline:none;width:3em;margin:0;padding:0 2px;text-align:right;font-size:13px;line-height:1;font-weight:400;font-family:inherit;border-radius:2px;color:currentColor;background:#0000;transition:background .4s;user-select:text;-webkit-tap-highlight-color:rgba(0,0,0,0)}.netless-app-presentation-page-number-input:hover,.netless-app-presentation-page-number-input:focus,.netless-app-presentation-page-number-input:active{background:#fff;box-shadow:#63636333 0 2px 8px}.netless-app-presentation-readonly.netless-app-presentation-footer{display:none}.netless-app-presentation-wb-view{position:absolute;top:0;left:0;width:100%;height:100%;z-index:100;overflow:hidden;transition:opacity .2s}.netless-app-presentation-image{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.netless-app-presentation-image>img{max-width:100%;max-height:100%;object-fit:contain}.telebox-color-scheme-dark .netless-app-presentation-page-number-input{color:#a6a6a8}.telebox-color-scheme-dark .netless-app-presentation-page-number-input:active,.telebox-color-scheme-dark .netless-app-presentation-page-number-input:focus,.telebox-color-scheme-dark .netless-app-presentation-page-number-input:hover{color:#222}.telebox-color-scheme-dark .netless-app-presentation-footer{color:#a6a6a8;background:#2d2d33;border-top:none}.telebox-color-scheme-dark .netless-app-presentation-footer-btn:hover{background:#212126}.telebox-color-scheme-dark .netless-app-presentation-preview{background:#323232e6}.netless-app-presentation-scrollbar-container{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:101}.netless-app-presentation-scrollbar-container-x,.netless-app-presentation-scrollbar-container-y{position:absolute;pointer-events:none;display:flex;justify-content:center;box-sizing:border-box;padding:0}.netless-app-presentation-scrollbar-container-x{bottom:0;left:0;height:6px;width:100%;padding-right:6px}.netless-app-presentation-scrollbar-container-y{top:0;right:0;width:6px;height:100%;flex-direction:column;padding-bottom:6px}.netless-app-presentation-scrollbar-x,.netless-app-presentation-scrollbar-y{border-radius:2px;background-color:#1b1f4d4d;pointer-events:auto;cursor:default}.netless-app-presentation-scrollbar-x{height:6px}.netless-app-presentation-scrollbar-y{width:6px}";
1523
1550
 
1524
1551
  // src/store.ts
1525
1552
  function noop() {
@@ -1965,6 +1992,66 @@ function cameraToSharedViewport(camera, viewSize, referenceSize) {
1965
1992
  };
1966
1993
  }
1967
1994
 
1995
+ // src/runtime-lifecycle.ts
1996
+ var createPresentationRuntimeTeardown = (box, dispose2) => {
1997
+ let tornDown = false;
1998
+ return () => {
1999
+ var _a, _b, _c;
2000
+ if (tornDown)
2001
+ return;
2002
+ tornDown = true;
2003
+ try {
2004
+ dispose2();
2005
+ } finally {
2006
+ try {
2007
+ (_a = box.unmountContent) == null ? void 0 : _a.call(box);
2008
+ } finally {
2009
+ try {
2010
+ (_b = box.unmountFooter) == null ? void 0 : _b.call(box);
2011
+ } finally {
2012
+ (_c = box.unmountStyles) == null ? void 0 : _c.call(box);
2013
+ }
2014
+ }
2015
+ }
2016
+ };
2017
+ };
2018
+
2019
+ // src/setup-ready.ts
2020
+ var waitForCurrentPageImage = (getImage, timeoutMs, isDisposed) => {
2021
+ return new Promise((resolve) => {
2022
+ let settled = false;
2023
+ let pollTimer;
2024
+ const settle = (result) => {
2025
+ if (settled)
2026
+ return;
2027
+ settled = true;
2028
+ window.clearTimeout(timeoutTimer);
2029
+ if (pollTimer !== void 0)
2030
+ window.clearInterval(pollTimer);
2031
+ resolve(result);
2032
+ };
2033
+ const timeoutTimer = window.setTimeout(() => settle("timeout"), timeoutMs);
2034
+ const check = () => {
2035
+ var _a, _b;
2036
+ if (isDisposed()) {
2037
+ settle("disposed");
2038
+ return;
2039
+ }
2040
+ const img = getImage();
2041
+ if (img == null ? void 0 : img.complete) {
2042
+ if (img.naturalWidth > 0) {
2043
+ settle("loaded");
2044
+ } else if (img.currentSrc || ((_a = img.getAttribute("src")) == null ? void 0 : _a.trim()) || ((_b = img.getAttribute("srcset")) == null ? void 0 : _b.trim())) {
2045
+ settle("error");
2046
+ }
2047
+ }
2048
+ };
2049
+ check();
2050
+ if (!settled)
2051
+ pollTimer = window.setInterval(check, 100);
2052
+ });
2053
+ };
2054
+
1968
2055
  // src/app-presentation.ts
1969
2056
  var import_debounce2 = __toESM(require_debounce());
1970
2057
  var emptySceneName = "$$empty$$";
@@ -1981,6 +2068,10 @@ var findBackgroundImage = (view) => {
1981
2068
  return container.querySelector('img[alt="background"]');
1982
2069
  };
1983
2070
  var isBlurDegradationAllowed = (context) => {
2071
+ var _a;
2072
+ if (((_a = context.getWindowManager()) == null ? void 0 : _a.lazySetupInMaximizedMode) !== true) {
2073
+ return false;
2074
+ }
1984
2075
  const boxStatus = context.getBoxStatus();
1985
2076
  if (boxStatus) {
1986
2077
  return boxStatus !== "normal";
@@ -1989,36 +2080,6 @@ var isBlurDegradationAllowed = (context) => {
1989
2080
  return boxState !== void 0 && boxState !== "normal";
1990
2081
  };
1991
2082
  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
2083
  var resolveImageUrl = (url) => {
2023
2084
  try {
2024
2085
  return new URL(url, window.location.href).href;
@@ -2026,17 +2087,55 @@ var resolveImageUrl = (url) => {
2026
2087
  return url;
2027
2088
  }
2028
2089
  };
2090
+ var TRANSPARENT_BACKGROUND = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
2091
+ var BACKGROUND_SUSPENDED_CLASS = "netless-app-presentation-background-suspended";
2092
+ var hasUsableThumbnail = (page) => {
2093
+ const thumbnailURL = page.thumbnail;
2094
+ if (!thumbnailURL)
2095
+ return false;
2096
+ try {
2097
+ const source = new URL(page.src, window.location.href);
2098
+ const thumbnail = new URL(thumbnailURL, window.location.href);
2099
+ const sameResourcePath = source.origin === thumbnail.origin && source.pathname === thumbnail.pathname;
2100
+ if (/\.gif$/i.test(source.pathname) && sameResourcePath)
2101
+ return false;
2102
+ } catch (e) {
2103
+ if (/\.gif(?:$|[?#])/i.test(page.src))
2104
+ return false;
2105
+ }
2106
+ return resolveImageUrl(thumbnailURL) !== resolveImageUrl(page.src);
2107
+ };
2029
2108
  var setupBlurThumbnailDegradation = (config) => {
2030
2109
  const { context, view, pages, pageIndex$, app, dispose: dispose2 } = config;
2110
+ const box = context.getBox();
2111
+ const boxBlurClass = box.wrapClassName("blur");
2031
2112
  let blurEpisode = false;
2032
2113
  let reDegradeEnabled = false;
2114
+ let boxBlurred = box.$box.classList.contains(boxBlurClass);
2115
+ const setBackgroundSuspended = (suspended) => {
2116
+ app.whiteboardDOM.classList.toggle(
2117
+ BACKGROUND_SUSPENDED_CLASS,
2118
+ suspended
2119
+ );
2120
+ };
2033
2121
  const degrade = () => {
2034
2122
  const page = pages[pageIndex$.value];
2035
- if (!(page == null ? void 0 : page.thumbnail))
2123
+ if (!page)
2036
2124
  return;
2125
+ const thumbnailURL = page.thumbnail;
2037
2126
  const img = findBackgroundImage(view);
2038
- if (img && resolveImageUrl(img.src) !== resolveImageUrl(page.thumbnail)) {
2039
- img.src = page.thumbnail;
2127
+ if (!thumbnailURL || !hasUsableThumbnail(page)) {
2128
+ if (boxBlurred) {
2129
+ setBackgroundSuspended(true);
2130
+ if (img && resolveImageUrl(img.src) !== resolveImageUrl(TRANSPARENT_BACKGROUND)) {
2131
+ img.src = TRANSPARENT_BACKGROUND;
2132
+ }
2133
+ }
2134
+ return;
2135
+ }
2136
+ setBackgroundSuspended(false);
2137
+ if (img && resolveImageUrl(img.src) !== resolveImageUrl(thumbnailURL)) {
2138
+ img.src = thumbnailURL;
2040
2139
  }
2041
2140
  };
2042
2141
  const restore2 = () => {
@@ -2045,6 +2144,24 @@ var setupBlurThumbnailDegradation = (config) => {
2045
2144
  if (page && img && resolveImageUrl(img.src) !== resolveImageUrl(page.src)) {
2046
2145
  img.src = page.src;
2047
2146
  }
2147
+ setBackgroundSuspended(false);
2148
+ };
2149
+ const suspendCachedResources = () => {
2150
+ if (!blurEpisode || !boxBlurred)
2151
+ return;
2152
+ app.suspendPreviewResources();
2153
+ const page = pages[pageIndex$.value];
2154
+ if (!page || !hasUsableThumbnail(page) || reDegradeEnabled) {
2155
+ degrade();
2156
+ }
2157
+ };
2158
+ const restoreFocusedResources = () => {
2159
+ if (!blurEpisode)
2160
+ return;
2161
+ blurEpisode = false;
2162
+ reDegradeEnabled = false;
2163
+ restore2();
2164
+ app.preload.resume(pageIndex$.value);
2048
2165
  };
2049
2166
  const isBackgroundImg = (target) => {
2050
2167
  var _a;
@@ -2053,28 +2170,29 @@ var setupBlurThumbnailDegradation = (config) => {
2053
2170
  el && el.tagName === "IMG" && ((_a = el.matches) == null ? void 0 : _a.call(el, 'img[alt="background"]'))
2054
2171
  );
2055
2172
  };
2173
+ const ownsTarget = (target) => {
2174
+ if (!isBackgroundImg(target))
2175
+ return false;
2176
+ const el = view.divElement;
2177
+ return !!(el && el.contains(target));
2178
+ };
2056
2179
  const onImgLoad = (ev) => {
2057
2180
  if (!blurEpisode || !reDegradeEnabled)
2058
2181
  return;
2059
- if (!isBackgroundImg(ev.target))
2182
+ if (!ownsTarget(ev.target))
2060
2183
  return;
2061
2184
  degrade();
2062
2185
  };
2063
2186
  const onImgError = (ev) => {
2064
2187
  if (!blurEpisode || !reDegradeEnabled)
2065
2188
  return;
2066
- if (!isBackgroundImg(ev.target))
2189
+ if (!ownsTarget(ev.target))
2067
2190
  return;
2068
2191
  reDegradeEnabled = false;
2069
2192
  };
2070
2193
  const offFocus = context.emitter.on("focus", (isFocused) => {
2071
2194
  if (isFocused) {
2072
- if (blurEpisode) {
2073
- blurEpisode = false;
2074
- reDegradeEnabled = false;
2075
- restore2();
2076
- app.preload.resume(pageIndex$.value);
2077
- }
2195
+ restoreFocusedResources();
2078
2196
  } else {
2079
2197
  if (!isBlurDegradationAllowed(context))
2080
2198
  return;
@@ -2082,29 +2200,41 @@ var setupBlurThumbnailDegradation = (config) => {
2082
2200
  reDegradeEnabled = true;
2083
2201
  degrade();
2084
2202
  app.preload.pause();
2203
+ suspendCachedResources();
2085
2204
  }
2086
2205
  });
2087
2206
  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
- );
2207
+ const updateBoxBlurred = (blurred) => {
2208
+ if (boxBlurred === blurred)
2209
+ return;
2210
+ boxBlurred = blurred;
2211
+ if (blurred)
2212
+ suspendCachedResources();
2213
+ else
2214
+ restoreFocusedResources();
2215
+ };
2216
+ const onBoxBlur = () => updateBoxBlurred(true);
2217
+ const onBoxFocus = () => updateBoxBlurred(false);
2218
+ box.events.on("blur", onBoxBlur);
2219
+ box.events.on("focus", onBoxFocus);
2220
+ dispose2.add(() => {
2221
+ box.events.off("blur", onBoxBlur);
2222
+ box.events.off("focus", onBoxFocus);
2223
+ });
2224
+ const boxClassObserver = new MutationObserver(() => {
2225
+ updateBoxBlurred(box.$box.classList.contains(boxBlurClass));
2226
+ });
2227
+ boxClassObserver.observe(box.$box, {
2228
+ attributes: true,
2229
+ attributeFilter: ["class"]
2230
+ });
2231
+ dispose2.add(() => boxClassObserver.disconnect());
2232
+ document.addEventListener("load", onImgLoad, true);
2233
+ document.addEventListener("error", onImgError, true);
2234
+ dispose2.add(() => {
2235
+ document.removeEventListener("load", onImgLoad, true);
2236
+ document.removeEventListener("error", onImgError, true);
2237
+ });
2108
2238
  };
2109
2239
  var createLogger = (room) => {
2110
2240
  if (room && room.logger) {
@@ -2527,7 +2657,7 @@ var NetlessAppPresentation = {
2527
2657
  options.useClipView
2528
2658
  )
2529
2659
  );
2530
- app.contentDOM.dataset.appPresentationVersion = "0.1.13-beta.1";
2660
+ app.contentDOM.dataset.appPresentationVersion = "0.1.13-beta.3";
2531
2661
  app.scaleDocsToFit = scaleDocsToFit;
2532
2662
  app.log = log;
2533
2663
  app.warn = warn;
@@ -2769,6 +2899,10 @@ var NetlessAppPresentation = {
2769
2899
  };
2770
2900
  let disposed = false;
2771
2901
  let offDestroy;
2902
+ const teardownRuntime = createPresentationRuntimeTeardown(
2903
+ box,
2904
+ dispose2
2905
+ );
2772
2906
  const teardown = () => {
2773
2907
  if (disposed)
2774
2908
  return;
@@ -2776,7 +2910,7 @@ var NetlessAppPresentation = {
2776
2910
  const removeDestroy = offDestroy;
2777
2911
  offDestroy = void 0;
2778
2912
  removeDestroy == null ? void 0 : removeDestroy();
2779
- dispose2();
2913
+ teardownRuntime();
2780
2914
  };
2781
2915
  offDestroy = context.emitter.on("destroy", teardown);
2782
2916
  teardownByContext.set(context, teardown);
@@ -2992,9 +3126,15 @@ var NetlessAppPresentation = {
2992
3126
  );
2993
3127
  const setupReadyTimeout = (_d = options.setupReadyTimeout) != null ? _d : DEFAULT_SETUP_READY_TIMEOUT;
2994
3128
  return prepareScenesPromise.then(async () => {
2995
- const loaded = await waitForCurrentPageImage(view, setupReadyTimeout, () => disposed);
2996
- if (!loaded) {
3129
+ const result = await waitForCurrentPageImage(
3130
+ () => findBackgroundImage(view),
3131
+ setupReadyTimeout,
3132
+ () => disposed
3133
+ );
3134
+ if (result === "timeout" && !disposed) {
2997
3135
  warn(`[Presentation] setup ready wait timed out after ${setupReadyTimeout}ms`);
3136
+ } else if (result === "error" && !disposed) {
3137
+ warn("[Presentation] setup page image failed to load; continuing setup");
2998
3138
  }
2999
3139
  return controller;
3000
3140
  });
@@ -3083,12 +3223,13 @@ var install = (register, options = {}) => {
3083
3223
  };
3084
3224
 
3085
3225
  // src/index.ts
3086
- var version = "0.1.13-beta.1";
3226
+ var version = "0.1.13-beta.3";
3087
3227
 
3088
3228
  exports.NetlessAppPresentation = NetlessAppPresentation;
3089
3229
  exports.Presentation = Presentation;
3090
3230
  exports.Scrollbar = Scrollbar;
3091
3231
  exports.default = NetlessAppPresentation;
3092
3232
  exports.install = install;
3233
+ exports.setupBlurThumbnailDegradation = setupBlurThumbnailDegradation;
3093
3234
  exports.version = version;
3094
3235
  //# sourceMappingURL=index.js.map