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