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