@netless/app-presentation 0.1.1 → 0.1.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.mjs CHANGED
@@ -1,11 +1,13 @@
1
- // node_modules/.pnpm/@wopjs+disposable@0.1.4/node_modules/@wopjs/disposable/dist/index.mjs
1
+ // node_modules/.pnpm/@wopjs+disposable@0.1.5/node_modules/@wopjs/disposable/dist/index.mjs
2
2
  var isAbortable = (disposable) => disposable && disposable.abortable;
3
- var invokeDispose = (disposable) => {
3
+ var dispose = (disposable) => {
4
4
  try {
5
- if (disposable.dispose) {
6
- disposable.dispose();
7
- } else {
8
- disposable();
5
+ if (disposable) {
6
+ if (disposable.dispose) {
7
+ disposable.dispose();
8
+ } else if (disposable.constructor && disposable.call && disposable.apply) {
9
+ disposable();
10
+ }
9
11
  }
10
12
  } catch (e) {
11
13
  console.error(e);
@@ -42,7 +44,7 @@ var methods = {
42
44
  flush(disposable) {
43
45
  if (disposable) {
44
46
  if (this.remove(disposable)) {
45
- invokeDispose(disposable);
47
+ dispose(disposable);
46
48
  }
47
49
  } else {
48
50
  this.dispose();
@@ -51,7 +53,7 @@ var methods = {
51
53
  };
52
54
  var disposableStore = (disposables) => {
53
55
  const disposableStore2 = () => {
54
- disposableStore2._d.forEach(invokeDispose);
56
+ disposableStore2._d.forEach(dispose);
55
57
  disposableStore2._d.clear();
56
58
  };
57
59
  disposableStore2._d = /* @__PURE__ */ new Set();
@@ -70,11 +72,9 @@ function addEventListener(target, type, listener, options) {
70
72
  }
71
73
  var listen = addEventListener;
72
74
 
73
- // node_modules/.pnpm/vanilla-lazyload@17.8.5/node_modules/vanilla-lazyload/dist/lazyload.esm.js
75
+ // node_modules/.pnpm/vanilla-lazyload@18.0.0/node_modules/vanilla-lazyload/dist/lazyload.esm.js
74
76
  var runningOnBrowser = typeof window !== "undefined";
75
77
  var isBot = runningOnBrowser && !("onscroll" in window) || typeof navigator !== "undefined" && /(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent);
76
- var supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window;
77
- var supportsClassList = runningOnBrowser && "classList" in document.createElement("p");
78
78
  var isHiDpi = runningOnBrowser && window.devicePixelRatio > 1;
79
79
  var defaultSettings = {
80
80
  elements_selector: ".lazy",
@@ -186,18 +186,22 @@ var safeCallback = (callback, arg1, arg2, arg3) => {
186
186
  callback(arg1);
187
187
  };
188
188
  var addClass = (element, className) => {
189
- if (supportsClassList) {
190
- element.classList.add(className);
189
+ if (!runningOnBrowser) {
191
190
  return;
192
191
  }
193
- element.className += (element.className ? " " : "") + className;
192
+ if (className === "") {
193
+ return;
194
+ }
195
+ element.classList.add(className);
194
196
  };
195
197
  var removeClass = (element, className) => {
196
- if (supportsClassList) {
197
- element.classList.remove(className);
198
+ if (!runningOnBrowser) {
198
199
  return;
199
200
  }
200
- element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ").replace(/^\s+/, "").replace(/\s+$/, "");
201
+ if (className === "") {
202
+ return;
203
+ }
204
+ element.classList.remove(className);
201
205
  };
202
206
  var addTempImage = (element) => {
203
207
  element.llTempImage = document.createElement("IMG");
@@ -644,7 +648,7 @@ var updateObserver = (observer, elementsToObserve) => {
644
648
  observeElements(observer, elementsToObserve);
645
649
  };
646
650
  var setObserver = (settings, instance) => {
647
- if (!supportsIntersectionObserver || shouldUseNative(settings)) {
651
+ if (shouldUseNative(settings)) {
648
652
  return;
649
653
  }
650
654
  instance._observer = new IntersectionObserver((entries) => {
@@ -693,7 +697,7 @@ LazyLoad.prototype = {
693
697
  const settings = this._settings;
694
698
  const elementsToLoad = getElementsToLoad(givenNodeset, settings);
695
699
  setToLoadCount(this, elementsToLoad.length);
696
- if (isBot || !supportsIntersectionObserver) {
700
+ if (isBot) {
697
701
  this.loadAll(elementsToLoad);
698
702
  return;
699
703
  }
@@ -783,66 +787,161 @@ function arrowRightSVG(namespace) {
783
787
  }
784
788
 
785
789
  // src/preload.ts
786
- var Preload = class {
790
+ var _Preload = class _Preload {
787
791
  constructor(pages) {
788
792
  this.pages = pages;
789
- this.head = 0;
790
- this.tail = 0;
791
- this.timer = 0;
792
- this.count = 0;
793
- this.links = new Array(pages.length);
793
+ this.loadingLinks = /* @__PURE__ */ new Map();
794
+ this.preloadMap = /* @__PURE__ */ new Map();
795
+ this.waitingLoadSet = /* @__PURE__ */ new Set();
796
+ this.waitinglinks = [];
797
+ this.touchIndex = 0;
798
+ this.preloadSize = 0;
799
+ this.preloadMap = new Map(pages.map((e, index) => [index, {
800
+ src: e.src,
801
+ state: 0 /* unloaded */
802
+ }]));
803
+ this.waitingLoadSet = new Set(pages.map((e, index) => index));
804
+ this.preloadSize = this.preloadMap.size;
794
805
  this.touch(0);
795
806
  }
796
- get done() {
797
- return this.head == 0 && this.tail == this.pages.length;
807
+ async touch(index) {
808
+ this.touchIndex = index;
809
+ const value = this.preloadMap.get(index);
810
+ let state = 0 /* unloaded */;
811
+ if (value) {
812
+ state = value.state;
813
+ if (value.state === 0 /* unloaded */) {
814
+ if (this.loadingLinks.size) {
815
+ for (const [i, link] of this.loadingLinks) {
816
+ this.destroyLink(i, link);
817
+ this.waitingLoadSet.add(i);
818
+ const v = this.preloadMap.get(i);
819
+ if (v) {
820
+ v.state = 0 /* unloaded */;
821
+ this.preloadMap.set(i, v);
822
+ }
823
+ }
824
+ for (const i of this.waitinglinks) {
825
+ this.waitingLoadSet.add(i);
826
+ }
827
+ this.waitinglinks.length = 0;
828
+ }
829
+ if (this.waitinglinks.includes(index)) {
830
+ this.waitinglinks.splice(this.waitinglinks.indexOf(index), 1);
831
+ }
832
+ state = await new Promise((resolve) => {
833
+ this.waitingLoadSet.delete(index);
834
+ this.createLink(index, resolve);
835
+ });
836
+ }
837
+ }
838
+ if (this.waitingLoadSet.size) {
839
+ this.requestAsyncCallBack(() => {
840
+ this.autoTouch();
841
+ }, 1e3);
842
+ }
843
+ return state;
798
844
  }
799
- touch(index) {
800
- if (this.done)
845
+ autoTouch() {
846
+ this.handler();
847
+ if (this.waitingLoadSet.size === 0) {
801
848
  return;
802
- this.head = this.tail = index;
803
- this.timer || (this.timer = setTimeout(this.handler.bind(this)));
849
+ }
850
+ let nextTouchDelay = 1e3;
851
+ this.touchIndex = (this.touchIndex + 1) % this.preloadSize;
852
+ const value = this.preloadMap.get(this.touchIndex);
853
+ if (value) {
854
+ switch (value.state) {
855
+ case 0 /* unloaded */:
856
+ case 3 /* error */:
857
+ if (!this.waitinglinks.includes(this.touchIndex)) {
858
+ this.waitinglinks.push(this.touchIndex);
859
+ this.waitingLoadSet.delete(this.touchIndex);
860
+ }
861
+ nextTouchDelay = 0;
862
+ break;
863
+ }
864
+ }
865
+ this.requestAsyncCallBack(() => {
866
+ this.autoTouch();
867
+ }, nextTouchDelay);
804
868
  }
805
- handler() {
806
- const { links } = this;
807
- while (this.head > 0 && links[this.head])
808
- this.head--;
809
- if (!links[this.head]) {
869
+ createLink(index, resolve) {
870
+ const value = this.preloadMap.get(index);
871
+ if (value) {
810
872
  const link = document.createElement("link");
811
- links[this.head] = link;
812
873
  link.rel = "preload";
813
874
  link.as = "image";
814
- link.href = this.pages[this.head].src;
815
- link.dataset.order = this.count + "";
875
+ link.href = value.src;
876
+ link.dataset.order = index + "";
816
877
  document.head.appendChild(link);
817
- this.count++;
878
+ link.onload = () => {
879
+ value.state = 2 /* loaded */;
880
+ this.preloadMap.set(index, value);
881
+ document.head.contains(link) && document.head.removeChild(link);
882
+ this.loadingLinks.delete(index);
883
+ resolve && resolve(2 /* loaded */);
884
+ this.autoTouch();
885
+ };
886
+ link.onerror = () => {
887
+ value.state = 3 /* error */;
888
+ this.preloadMap.set(index, value);
889
+ if (!this.waitinglinks.includes(index)) {
890
+ this.waitinglinks.push(index);
891
+ this.waitingLoadSet.delete(this.touchIndex);
892
+ }
893
+ document.head.contains(link) && document.head.removeChild(link);
894
+ this.loadingLinks.delete(index);
895
+ resolve && resolve(3 /* error */);
896
+ this.autoTouch();
897
+ };
898
+ this.loadingLinks.set(index, link);
899
+ value.state = 1 /* loading */;
900
+ this.preloadMap.set(index, value);
818
901
  }
819
- while (links[this.tail])
820
- this.tail++;
821
- if (this.tail < this.pages.length && !links[this.tail]) {
822
- const link = document.createElement("link");
823
- links[this.tail] = link;
824
- link.rel = "preload";
825
- link.as = "image";
826
- link.href = this.pages[this.tail].src;
827
- link.dataset.order = this.count + "";
828
- document.head.appendChild(link);
829
- this.count++;
902
+ }
903
+ handler() {
904
+ if (!this.waitinglinks.length) {
905
+ return;
830
906
  }
831
- this.timer = setTimeout(this.handler.bind(this), 2e3);
832
- this.count++;
833
- if (this.count >= 10) {
834
- for (const link of this.links)
835
- if (link && link.dataset.order && +link.dataset.order < this.count - 10)
836
- document.head.contains(link) && document.head.removeChild(link);
907
+ let i = this.loadingLinks.size;
908
+ while (i < _Preload.maxLinks) {
909
+ const index = this.waitinglinks.shift();
910
+ if (index !== void 0) {
911
+ this.createLink(index);
912
+ }
913
+ i++;
837
914
  }
838
915
  }
916
+ async requestAsyncCallBack(callBack, timeout) {
917
+ await new Promise(function(resolve) {
918
+ if (window.requestIdleCallback) {
919
+ requestIdleCallback(() => {
920
+ resolve(1);
921
+ }, { timeout });
922
+ } else {
923
+ setTimeout(() => {
924
+ resolve(2);
925
+ }, timeout);
926
+ }
927
+ });
928
+ callBack();
929
+ }
930
+ destroyLink(index, link) {
931
+ document.head.contains(link) && document.head.removeChild(link);
932
+ this.loadingLinks.delete(index);
933
+ }
839
934
  dispose() {
840
- clearTimeout(this.timer);
841
- for (const link of this.links)
842
- link && document.head.contains(link) && document.head.removeChild(link);
843
- this.links.length = 0;
935
+ for (const [index, link] of this.loadingLinks) {
936
+ this.destroyLink(index, link);
937
+ }
938
+ this.preloadMap.clear();
939
+ this.waitingLoadSet.clear();
940
+ this.waitinglinks.length = 0;
844
941
  }
845
942
  };
943
+ _Preload.maxLinks = 5;
944
+ var Preload = _Preload;
846
945
 
847
946
  // src/presentation.ts
848
947
  var Presentation = class {
@@ -853,6 +952,7 @@ var Presentation = class {
853
952
  this.showPreview = false;
854
953
  this.pageIndex = 0;
855
954
  this.previewLazyload = null;
955
+ this.timer = null;
856
956
  var _a;
857
957
  this.pages = config.pages;
858
958
  this.preload = new Preload(this.pages);
@@ -1055,15 +1155,22 @@ var Presentation = class {
1055
1155
  return this.pages[this.pageIndex];
1056
1156
  }
1057
1157
  updateImage() {
1058
- const page = this.page();
1059
- if (!page) {
1060
- this.image.src = "";
1061
- return;
1158
+ if (this.timer) {
1159
+ clearTimeout(this.timer);
1062
1160
  }
1063
- this.image.width = page.width;
1064
- this.image.height = page.height;
1065
- this.image.src = page.src;
1066
- this.preload.touch(this.pageIndex);
1161
+ this.timer = setTimeout(() => {
1162
+ this.timer = null;
1163
+ this.preload.touch(this.pageIndex).then(() => {
1164
+ const page = this.page();
1165
+ if (!page) {
1166
+ this.image.src = "";
1167
+ return;
1168
+ }
1169
+ this.image.width = page.width;
1170
+ this.image.height = page.height;
1171
+ this.image.src = page.src;
1172
+ });
1173
+ }, 200);
1067
1174
  }
1068
1175
  /** Returns a modified URL that points to the thumbnail image. */
1069
1176
  thumbnail(src) {
@@ -1102,7 +1209,7 @@ var createLogger = (room) => {
1102
1209
  var NetlessAppPresentation = {
1103
1210
  kind: "Presentation",
1104
1211
  setup(context) {
1105
- var _a, _b;
1212
+ var _a, _b, _c;
1106
1213
  const view = context.getView();
1107
1214
  if (!view)
1108
1215
  throw new Error("[Presentation]: no whiteboard view, make sure you have added options.scenePath in addApp()");
@@ -1134,9 +1241,10 @@ var NetlessAppPresentation = {
1134
1241
  }
1135
1242
  }
1136
1243
  }
1137
- const dispose = disposableStore();
1138
- dispose.add(() => log(`[Presentation] dispose ${context.appId}`));
1244
+ const dispose2 = disposableStore();
1245
+ dispose2.add(() => log(`[Presentation] dispose ${context.appId}`));
1139
1246
  const page$$ = context.createStorage("page", { index: 0 });
1247
+ const view$$ = context.createStorage("view", { uid: "", originX: 0, originY: 0, width: 0, height: 0 });
1140
1248
  let lastIndex = -1;
1141
1249
  const syncPage = async (index) => {
1142
1250
  if (lastIndex !== index) {
@@ -1200,19 +1308,51 @@ var NetlessAppPresentation = {
1200
1308
  width,
1201
1309
  height
1202
1310
  });
1311
+ syncViewFromRemote(true);
1203
1312
  }
1204
1313
  };
1205
- syncPage(page$$.state.index);
1206
- dispose.add(page$$.addStateChangedListener(() => syncPage(page$$.state.index)));
1207
- dispose.add(context.emitter.on("roomStateChange", (state) => {
1208
- var _a2;
1209
- if ((_a2 = state.sceneState) == null ? void 0 : _a2.scenePath.endsWith("/0")) {
1210
- syncPage(page$$.state.index);
1314
+ const me = ((_c = context.getRoom()) == null ? void 0 : _c.uid) || context.getDisplayer().observerId + "";
1315
+ let throttleSyncView = 0;
1316
+ const syncView = () => {
1317
+ if (throttleSyncView > 0)
1318
+ return;
1319
+ const { width, height } = app.page() || {};
1320
+ if (width && height && context.getIsWritable()) {
1321
+ const { camera, size } = view;
1322
+ const fixedW = Math.min(size.width, size.height * width / height);
1323
+ const fixedH = Math.min(size.height, size.width * height / width);
1324
+ const w = fixedW / camera.scale;
1325
+ const h = fixedH / camera.scale;
1326
+ const x = camera.centerX - w / 2;
1327
+ const y = camera.centerY - h / 2;
1328
+ throttleSyncView = setTimeout(() => {
1329
+ throttleSyncView = 0;
1330
+ view$$.setState({ uid: me, originX: x, originY: y, width: w, height: h });
1331
+ }, 50);
1211
1332
  }
1212
- }));
1333
+ };
1334
+ dispose2.add(() => {
1335
+ clearTimeout(throttleSyncView);
1336
+ throttleSyncView = 0;
1337
+ });
1338
+ const syncViewFromRemote = (force = false, animate = false) => {
1339
+ const { uid, originX, originY, width, height } = view$$.state;
1340
+ if ((force || uid !== me) && width > 0 && height > 0) {
1341
+ view.moveCameraToContain({
1342
+ originX,
1343
+ originY,
1344
+ width,
1345
+ height,
1346
+ animationMode: animate ? "continuous" : "immediately"
1347
+ });
1348
+ }
1349
+ };
1350
+ syncPage(page$$.state.index);
1351
+ dispose2.add(page$$.addStateChangedListener(() => syncPage(page$$.state.index)));
1352
+ dispose2.add(view$$.addStateChangedListener(() => syncViewFromRemote(false, true)));
1213
1353
  const box = context.getBox();
1214
- const app = dispose.add(createPresentation(box, pages, jumpPage, page$$, options.thumbnail));
1215
- app.contentDOM.dataset.appPresentationVersion = "0.1.1";
1354
+ const app = dispose2.add(createPresentation(box, pages, jumpPage, page$$, options.thumbnail));
1355
+ app.contentDOM.dataset.appPresentationVersion = "0.1.2";
1216
1356
  app.scaleDocsToFit = scaleDocsToFit;
1217
1357
  app.log = log;
1218
1358
  context.mountView(app.whiteboardDOM);
@@ -1220,14 +1360,31 @@ var NetlessAppPresentation = {
1220
1360
  view.disableCameraTransform = true;
1221
1361
  }
1222
1362
  scaleDocsToFit();
1223
- dispose.make(() => {
1363
+ dispose2.make(() => {
1224
1364
  view.callbacks.on("onSizeUpdated", scaleDocsToFit);
1225
1365
  return () => view.callbacks.off("onSizeUpdated", scaleDocsToFit);
1226
1366
  });
1227
- dispose.add(context.emitter.on("writableChange", (isWritable) => {
1367
+ if (options.viewport && context.isAddApp && app.page()) {
1368
+ const page = app.page();
1369
+ const viewport = typeof options.viewport === "function" ? options.viewport(page) : options.viewport;
1370
+ const fix = (i, x) => i == 0 ? i : i <= 1 ? i * x : i;
1371
+ view$$.setState({
1372
+ uid: me,
1373
+ originX: fix(viewport.x, page.width) - page.width / 2,
1374
+ originY: fix(viewport.y, page.height) - page.height / 2,
1375
+ width: fix(viewport.width, page.width),
1376
+ height: fix(viewport.height, page.height)
1377
+ });
1378
+ }
1379
+ dispose2.make(() => {
1380
+ view.callbacks.on("onCameraUpdatedByDevice", syncView);
1381
+ return () => view.callbacks.off("onCameraUpdatedByDevice", syncView);
1382
+ });
1383
+ syncViewFromRemote(true);
1384
+ dispose2.add(context.emitter.on("writableChange", (isWritable) => {
1228
1385
  app.setReadonly(!isWritable);
1229
1386
  }));
1230
- context.emitter.on("destroy", () => dispose());
1387
+ context.emitter.on("destroy", () => dispose2());
1231
1388
  const reportProgress = (progress, result) => {
1232
1389
  window.postMessage({ type: "@netless/_result_save_pdf_", appId: context.appId, progress, result }, "*");
1233
1390
  return result;
@@ -1323,7 +1480,7 @@ var NetlessAppPresentation = {
1323
1480
  const title = box.title;
1324
1481
  return reportProgress(100, { pdf: data, title });
1325
1482
  };
1326
- dispose.add(listen(window, "message", (ev) => {
1483
+ dispose2.add(listen(window, "message", (ev) => {
1327
1484
  if (ev.data && ev.data.type == "@netless/_request_save_pdf_" && ev.data.appId == context.appId) {
1328
1485
  toPdf().catch((err) => {
1329
1486
  console.warn(err);
@@ -1332,10 +1489,10 @@ var NetlessAppPresentation = {
1332
1489
  }
1333
1490
  }));
1334
1491
  const controller = { app, view, context, jumpPage, prevPage, nextPage, pageState, toPdf, log };
1335
- dispose.add(listen(window, "message", (ev) => {
1492
+ dispose2.add(listen(window, "message", (ev) => {
1336
1493
  if (ev.data === "@netless/_presentation_") {
1337
1494
  if (typeof window !== "undefined")
1338
- dispose.make(() => {
1495
+ dispose2.make(() => {
1339
1496
  const debug = window._presentation_ || (window._presentation_ = /* @__PURE__ */ new Set());
1340
1497
  debug.add(controller);
1341
1498
  return () => debug.delete(controller);
@@ -1387,7 +1544,7 @@ var install = (register, options = {}) => {
1387
1544
  };
1388
1545
 
1389
1546
  // src/index.ts
1390
- var version = "0.1.1";
1547
+ var version = "0.1.2";
1391
1548
 
1392
1549
  export { NetlessAppPresentation, Presentation, NetlessAppPresentation as default, install, style_default as styles, version };
1393
1550
  //# sourceMappingURL=index.mjs.map