@netless/window-manager 1.0.0-canary.53 → 1.0.0-canary.55

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.
Files changed (44) hide show
  1. package/dist/index.cjs.js +375 -444
  2. package/dist/index.es.js +421 -490
  3. package/dist/index.umd.js +375 -445
  4. package/dist/src/App/AppContext.d.ts +8 -8
  5. package/dist/src/AppManager.d.ts +5 -1
  6. package/dist/src/Cursor/index.d.ts +1 -0
  7. package/dist/src/Utils/Reactive.d.ts +1 -1
  8. package/dist/src/View/CameraSynchronizer.d.ts +3 -2
  9. package/dist/src/View/ScrollMode.d.ts +32 -0
  10. package/dist/src/View/ViewSync.d.ts +3 -2
  11. package/dist/src/callback.d.ts +3 -0
  12. package/dist/src/constants.d.ts +2 -0
  13. package/dist/src/index.d.ts +21 -11
  14. package/dist/src/storage.d.ts +7 -0
  15. package/dist/src/typings.d.ts +5 -4
  16. package/dist/style.css +2 -1
  17. package/docs/api.md +10 -0
  18. package/package.json +4 -3
  19. package/pnpm-lock.yaml +28 -73
  20. package/src/App/AppContext.ts +19 -8
  21. package/src/App/WhiteboardView.ts +4 -2
  22. package/src/AppListener.ts +1 -10
  23. package/src/AppManager.ts +18 -1
  24. package/src/Cursor/index.ts +6 -2
  25. package/src/Utils/Reactive.ts +2 -1
  26. package/src/View/CameraSynchronizer.ts +33 -22
  27. package/src/View/MainView.ts +1 -0
  28. package/src/View/ScrollMode.ts +219 -0
  29. package/src/View/ViewSync.ts +24 -16
  30. package/src/callback.ts +3 -0
  31. package/src/constants.ts +3 -0
  32. package/src/index.ts +56 -63
  33. package/src/storage.ts +15 -0
  34. package/src/style.css +1 -1
  35. package/src/typings.ts +6 -3
  36. package/vite.config.js +1 -1
  37. package/dist/src/App/Storage/StorageEvent.d.ts +0 -8
  38. package/dist/src/App/Storage/index.d.ts +0 -39
  39. package/dist/src/App/Storage/typings.d.ts +0 -22
  40. package/dist/src/App/Storage/utils.d.ts +0 -5
  41. package/src/App/Storage/StorageEvent.ts +0 -21
  42. package/src/App/Storage/index.ts +0 -295
  43. package/src/App/Storage/typings.ts +0 -23
  44. package/src/App/Storage/utils.ts +0 -17
package/dist/index.es.js CHANGED
@@ -31,11 +31,12 @@ var __objRest = (source, exclude) => {
31
31
  };
32
32
  import pRetry from "p-retry";
33
33
  import Emittery from "emittery";
34
- import { debounce, throttle, isEqual, pick, isObject, has, get, size, mapValues, noop as noop$1, isBoolean, isNumber, omitBy, isUndefined, isInteger, orderBy, isEmpty, omit, isFunction, isNull } from "lodash";
35
- import { ScenePathType, AnimationMode, UpdateEventKind, listenUpdated, unlistenUpdated, reaction, toJS, WhiteVersion, autorun, listenDisposed, unlistenDisposed, ViewMode, isPlayer, isRoom, ApplianceNames, RoomPhase, InvisiblePlugin } from "white-web-sdk";
34
+ import { debounce, pick, isBoolean, isNumber, throttle, isEqual, isEmpty, get, omitBy, isUndefined, isObject, isInteger, orderBy, omit, isFunction, isNull } from "lodash";
35
+ import { ScenePathType, toJS, WhiteVersion, autorun, reaction, listenUpdated, unlistenUpdated, listenDisposed, unlistenDisposed, AnimationMode, ViewMode, UpdateEventKind, isPlayer, isRoom, ApplianceNames, RoomPhase, InvisiblePlugin } from "white-web-sdk";
36
36
  import { v4 } from "uuid";
37
- import { genUID, SideEffectManager } from "side-effect-manager";
38
- import { Val, combine, ValManager, withReadonlyValueEnhancer, withValueEnhancer, derive } from "value-enhancer";
37
+ import { Storage } from "@netless/synced-store";
38
+ import { Val, derive, combine, ValManager, withReadonlyValueEnhancer, withValueEnhancer } from "value-enhancer";
39
+ import { SideEffectManager, genUID } from "side-effect-manager";
39
40
  import { ResizeObserver as ResizeObserver$2 } from "@juggle/resize-observer";
40
41
  var Events = /* @__PURE__ */ ((Events2) => {
41
42
  Events2["AppMove"] = "AppMove";
@@ -95,6 +96,8 @@ const ROOT_DIR = "/";
95
96
  const INIT_DIR = "/init";
96
97
  const SETUP_APP_DELAY = 50;
97
98
  const MAX_PAGE_SIZE = 500;
99
+ const SCROLL_MODE_BASE_WIDTH = 1600;
100
+ const SCROLL_MODE_BASE_HEIGHT = SCROLL_MODE_BASE_WIDTH * 3;
98
101
  const callbacks = new Emittery();
99
102
  class AppCreateQueue {
100
103
  constructor() {
@@ -515,67 +518,6 @@ const isRootDirPage = (scenePath) => {
515
518
  }, 0);
516
519
  return delimiterCount === 1;
517
520
  };
518
- class CameraSynchronizer {
519
- constructor(saveCamera) {
520
- this.saveCamera = saveCamera;
521
- this.setRect = (rect, updateCamera = true) => {
522
- this.rect = rect;
523
- if (this.remoteCamera && this.remoteSize && updateCamera) {
524
- this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
525
- }
526
- };
527
- this.onRemoteUpdate = throttle((camera, size2) => {
528
- this.remoteCamera = camera;
529
- this.remoteSize = size2;
530
- if (this.remoteSize && this.rect) {
531
- const nextScale = camera.scale * computedMinScale(size2, this.rect);
532
- const config = {
533
- scale: nextScale
534
- };
535
- if (camera.centerX !== null) {
536
- config.centerX = camera.centerX;
537
- }
538
- if (camera.centerY !== null) {
539
- config.centerY = camera.centerY;
540
- }
541
- console.trace("moveCamera");
542
- this.moveCamera(config);
543
- }
544
- }, 10);
545
- }
546
- setView(view) {
547
- this.view = view;
548
- }
549
- onRemoteSizeUpdate(size2) {
550
- var _a;
551
- this.remoteSize = size2;
552
- const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size2, ["width", "height"]));
553
- if (this.rect && this.remoteCamera && needMoveCamera) {
554
- if (!this.view)
555
- return;
556
- const currentCamera = this.view.camera;
557
- (_a = this.view) == null ? void 0 : _a.moveCameraToContain({
558
- width: size2.width,
559
- height: size2.height,
560
- originX: currentCamera.centerX - size2.width / 2,
561
- originY: currentCamera.centerY - size2.height / 2
562
- });
563
- }
564
- }
565
- onLocalCameraUpdate(camera) {
566
- this.saveCamera(camera);
567
- this.remoteCamera = camera;
568
- }
569
- moveCamera(camera) {
570
- var _a;
571
- (_a = this.view) == null ? void 0 : _a.moveCamera(__spreadProps(__spreadValues({}, camera), { animationMode: AnimationMode.Immediately }));
572
- }
573
- }
574
- const computedMinScale = (remoteSize, currentSize) => {
575
- const wScale = currentSize.width / remoteSize.width;
576
- const hScale = currentSize.height / remoteSize.height;
577
- return Math.min(wScale, hScale);
578
- };
579
521
  class AppListeners {
580
522
  constructor(manager) {
581
523
  this.manager = manager;
@@ -676,16 +618,7 @@ class AppListeners {
676
618
  }
677
619
  };
678
620
  this.moveCameraHandler = (payload) => {
679
- var _a;
680
- const cameraPayload = payload;
681
- if (payload.scale) {
682
- const remoteSize = this.manager.mainViewProxy.size$.value;
683
- const currentSize = (_a = this.manager.boxManager) == null ? void 0 : _a.stageRect;
684
- if (remoteSize && currentSize) {
685
- cameraPayload.scale = payload.scale * computedMinScale(remoteSize, currentSize);
686
- }
687
- }
688
- this.manager.mainView.moveCamera(cameraPayload);
621
+ this.manager.mainView.moveCamera(payload);
689
622
  };
690
623
  this.moveCameraToContainHandler = (payload) => {
691
624
  this.manager.mainView.moveCameraToContain(payload);
@@ -753,292 +686,6 @@ class BindContainerRoomPhaseInvalidError extends Error {
753
686
  this.message = "[WindowManager]: room phase only Connected can be bindContainer";
754
687
  }
755
688
  }
756
- const onObjectByEvent = (event) => {
757
- return (object, func) => {
758
- if (object === void 0)
759
- return;
760
- if (listenUpdated) {
761
- const listener = (events) => {
762
- const kinds = events.map((e) => e.kind);
763
- if (kinds.includes(event)) {
764
- func();
765
- }
766
- };
767
- listenUpdated(object, listener);
768
- func();
769
- return () => unlistenUpdated(object, listener);
770
- } else {
771
- return reaction(() => object, () => {
772
- func();
773
- }, {
774
- fireImmediately: true
775
- });
776
- }
777
- };
778
- };
779
- const safeListenPropsUpdated = (getProps, callback, onDestroyed) => {
780
- let disposeListenUpdated = null;
781
- const disposeReaction = reaction(getProps, () => {
782
- if (disposeListenUpdated) {
783
- disposeListenUpdated();
784
- disposeListenUpdated = null;
785
- }
786
- const props = getProps();
787
- if (isObject(props)) {
788
- disposeListenUpdated = () => unlistenUpdated(props, callback);
789
- listenUpdated(props, callback);
790
- } else {
791
- onDestroyed == null ? void 0 : onDestroyed(props);
792
- }
793
- }, { fireImmediately: true });
794
- return () => {
795
- disposeListenUpdated == null ? void 0 : disposeListenUpdated();
796
- disposeReaction();
797
- };
798
- };
799
- const onObjectRemoved = onObjectByEvent(UpdateEventKind.Removed);
800
- onObjectByEvent(UpdateEventKind.Inserted);
801
- const plainObjectKeys = Object.keys;
802
- function isRef(e) {
803
- return Boolean(has(e, "__isRef"));
804
- }
805
- function makeRef(v) {
806
- return { k: genUID(), v, __isRef: true };
807
- }
808
- class StorageEvent {
809
- constructor() {
810
- this.listeners = /* @__PURE__ */ new Set();
811
- }
812
- get length() {
813
- return this.listeners.size;
814
- }
815
- dispatch(message) {
816
- this.listeners.forEach((callback) => callback(message));
817
- }
818
- addListener(listener) {
819
- this.listeners.add(listener);
820
- }
821
- removeListener(listener) {
822
- this.listeners.delete(listener);
823
- }
824
- }
825
- const STORAGE_NS = "_WM-STORAGE_";
826
- class Storage {
827
- constructor(context, id, defaultState) {
828
- this._sideEffect = new SideEffectManager();
829
- this._destroyed = false;
830
- this._refMap = /* @__PURE__ */ new WeakMap();
831
- this._lastValue = /* @__PURE__ */ new Map();
832
- this.onStateChanged = new StorageEvent();
833
- if (defaultState && !isObject(defaultState)) {
834
- throw new Error(`Default state for Storage ${id} is not an object.`);
835
- }
836
- this._context = context;
837
- this.id = id || null;
838
- this._state = {};
839
- const rawState = this._getRawState(this._state);
840
- if (this._context.isWritable) {
841
- if (this.id === null) {
842
- if (context.isAddApp && defaultState) {
843
- this.setState(defaultState);
844
- }
845
- } else {
846
- if (rawState === this._state || !isObject(rawState)) {
847
- if (!get(this._context.getAttributes(), [STORAGE_NS])) {
848
- this._context.updateAttributes([STORAGE_NS], {});
849
- }
850
- this._context.updateAttributes([STORAGE_NS, this.id], this._state);
851
- if (defaultState) {
852
- this.setState(defaultState);
853
- }
854
- }
855
- }
856
- }
857
- plainObjectKeys(rawState).forEach((key) => {
858
- if (this.id === null && key === STORAGE_NS) {
859
- return;
860
- }
861
- try {
862
- const rawValue = isObject(rawState[key]) ? JSON.parse(JSON.stringify(rawState[key])) : rawState[key];
863
- if (isRef(rawValue)) {
864
- this._state[key] = rawValue.v;
865
- if (isObject(rawValue.v)) {
866
- this._refMap.set(rawValue.v, rawValue);
867
- }
868
- } else {
869
- this._state[key] = rawValue;
870
- }
871
- } catch (e) {
872
- console.error(e);
873
- }
874
- });
875
- this._sideEffect.addDisposer(safeListenPropsUpdated(() => this.id === null ? context.getAttributes() : get(context.getAttributes(), [STORAGE_NS, this.id]), this._updateProperties.bind(this), this.destroy.bind(this)));
876
- }
877
- get state() {
878
- if (this._destroyed) {
879
- console.warn(`Accessing state on destroyed Storage "${this.id}"`);
880
- }
881
- return this._state;
882
- }
883
- addStateChangedListener(handler) {
884
- this.onStateChanged.addListener(handler);
885
- return () => this.onStateChanged.removeListener(handler);
886
- }
887
- ensureState(state) {
888
- return this.setState(plainObjectKeys(state).reduce((payload, key) => {
889
- if (!has(this._state, key)) {
890
- payload[key] = state[key];
891
- }
892
- return payload;
893
- }, {}));
894
- }
895
- setState(state) {
896
- if (this._destroyed) {
897
- console.error(new Error(`Cannot call setState on destroyed Storage "${this.id}".`));
898
- return;
899
- }
900
- if (!this._context.isWritable) {
901
- console.error(new Error(`Cannot setState on Storage "${this.id}" without writable access`), state);
902
- return;
903
- }
904
- const keys = plainObjectKeys(state);
905
- if (keys.length > 0) {
906
- keys.forEach((key) => {
907
- const value = state[key];
908
- if (value === this._state[key]) {
909
- return;
910
- }
911
- if (value === void 0) {
912
- this._lastValue.set(key, this._state[key]);
913
- delete this._state[key];
914
- this._setRawState(key, value);
915
- } else {
916
- this._lastValue.set(key, this._state[key]);
917
- this._state[key] = value;
918
- let payload = value;
919
- if (isObject(value)) {
920
- let refValue = this._refMap.get(value);
921
- if (!refValue) {
922
- refValue = makeRef(value);
923
- this._refMap.set(value, refValue);
924
- }
925
- payload = refValue;
926
- }
927
- this._setRawState(key, payload);
928
- }
929
- });
930
- }
931
- }
932
- emptyStorage() {
933
- if (size(this._state) <= 0) {
934
- return;
935
- }
936
- if (this._destroyed) {
937
- console.error(new Error(`Cannot empty destroyed Storage "${this.id}".`));
938
- return;
939
- }
940
- if (!this._context.isWritable) {
941
- console.error(new Error(`Cannot empty Storage "${this.id}" without writable access.`));
942
- return;
943
- }
944
- this.setState(mapValues(this._state, noop$1));
945
- }
946
- deleteStorage() {
947
- if (this.id === null) {
948
- throw new Error(`Cannot delete main Storage`);
949
- }
950
- if (!this._context.isWritable) {
951
- console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
952
- return;
953
- }
954
- this.destroy();
955
- this._context.updateAttributes([STORAGE_NS, this.id], void 0);
956
- }
957
- get destroyed() {
958
- return this._destroyed;
959
- }
960
- destroy() {
961
- this._destroyed = true;
962
- this._sideEffect.flushAll();
963
- }
964
- _getRawState(defaultValue) {
965
- var _a;
966
- if (this.id === null) {
967
- return (_a = this._context.getAttributes()) != null ? _a : defaultValue;
968
- } else {
969
- return get(this._context.getAttributes(), [STORAGE_NS, this.id], defaultValue);
970
- }
971
- }
972
- _setRawState(key, value) {
973
- if (this.id === null) {
974
- if (key === STORAGE_NS) {
975
- throw new Error(`Cannot set attribute internal filed "${STORAGE_NS}"`);
976
- }
977
- return this._context.updateAttributes([key], value);
978
- } else {
979
- return this._context.updateAttributes([STORAGE_NS, this.id, key], value);
980
- }
981
- }
982
- _updateProperties(actions) {
983
- var _a;
984
- if (this._destroyed) {
985
- console.error(new Error(`Cannot call _updateProperties on destroyed Storage "${this.id}".`));
986
- return;
987
- }
988
- if (actions.length > 0) {
989
- const diffs = {};
990
- for (let i = 0; i < actions.length; i++) {
991
- try {
992
- const action = actions[i];
993
- const key = action.key;
994
- if (this.id === null && key === STORAGE_NS) {
995
- continue;
996
- }
997
- const value = isObject(action.value) ? JSON.parse(JSON.stringify(action.value)) : action.value;
998
- let oldValue;
999
- if (this._lastValue.has(key)) {
1000
- oldValue = this._lastValue.get(key);
1001
- this._lastValue.delete(key);
1002
- }
1003
- switch (action.kind) {
1004
- case 2: {
1005
- if (has(this._state, key)) {
1006
- oldValue = this._state[key];
1007
- delete this._state[key];
1008
- }
1009
- diffs[key] = { oldValue };
1010
- break;
1011
- }
1012
- default: {
1013
- let newValue = value;
1014
- if (isRef(value)) {
1015
- const { k: k2, v } = value;
1016
- const curValue = this._state[key];
1017
- if (isObject(curValue) && ((_a = this._refMap.get(curValue)) == null ? void 0 : _a.k) === k2) {
1018
- newValue = curValue;
1019
- } else {
1020
- newValue = v;
1021
- if (isObject(v)) {
1022
- this._refMap.set(v, value);
1023
- }
1024
- }
1025
- }
1026
- if (newValue !== this._state[key]) {
1027
- oldValue = this._state[key];
1028
- this._state[key] = newValue;
1029
- }
1030
- diffs[key] = { newValue, oldValue };
1031
- break;
1032
- }
1033
- }
1034
- } catch (e) {
1035
- console.error(e);
1036
- }
1037
- }
1038
- this.onStateChanged.dispatch(diffs);
1039
- }
1040
- }
1041
- }
1042
689
  class WhiteBoardView {
1043
690
  constructor(view, appContext, appProxy, ensureSize) {
1044
691
  this.view = view;
@@ -1067,11 +714,12 @@ class WhiteBoardView {
1067
714
  const scenePath = this.appProxy.scenePath;
1068
715
  if (!scenePath)
1069
716
  return;
717
+ const scenes = Array.isArray(scene) ? scene : [scene || {}];
1070
718
  if (after) {
1071
719
  const nextIndex = this.pageState.index + 1;
1072
- putScenes(this.appContext.room, scenePath, [scene || {}], nextIndex);
720
+ putScenes(this.appContext.room, scenePath, scenes, nextIndex);
1073
721
  } else {
1074
- putScenes(this.appContext.room, scenePath, [scene || {}]);
722
+ putScenes(this.appContext.room, scenePath, scenes);
1075
723
  }
1076
724
  };
1077
725
  this.removePage = async (index2) => {
@@ -1100,9 +748,9 @@ class WhiteBoardView {
1100
748
  camera$.setValue(pickCamera(camera));
1101
749
  }
1102
750
  }),
1103
- appProxy.size$.subscribe((size2) => {
1104
- if (size2) {
1105
- baseRect$.setValue(pick(size2, ["width", "height"]));
751
+ appProxy.size$.subscribe((size) => {
752
+ if (size) {
753
+ baseRect$.setValue(pick(size, ["width", "height"]));
1106
754
  }
1107
755
  })
1108
756
  ]);
@@ -1235,18 +883,18 @@ class AppContext {
1235
883
  this.appProxy.whiteBoardViewCreated$.setValue(true);
1236
884
  return this.whiteBoardView;
1237
885
  };
1238
- this.ensurePageSize = (size2) => {
886
+ this.ensurePageSize = (size) => {
1239
887
  var _a;
1240
- if (!isNumber(size2))
888
+ if (!isNumber(size))
1241
889
  return;
1242
890
  if (!this.appProxy.scenePath)
1243
891
  return;
1244
- if (this.appProxy.pageState.length >= size2)
892
+ if (this.appProxy.pageState.length >= size)
1245
893
  return;
1246
- if (size2 <= 0 || size2 >= MAX_PAGE_SIZE) {
1247
- throw Error(`[WindowManager]: size ${size2} muse be in range [1, ${MAX_PAGE_SIZE}]`);
894
+ if (size <= 0 || size >= MAX_PAGE_SIZE) {
895
+ throw Error(`[WindowManager]: size ${size} muse be in range [1, ${MAX_PAGE_SIZE}]`);
1248
896
  }
1249
- const needInsert = size2 - this.appProxy.pageState.length;
897
+ const needInsert = size - this.appProxy.pageState.length;
1250
898
  const scenes = new Array(needInsert).fill({});
1251
899
  (_a = this.room) == null ? void 0 : _a.putScenes(this.appProxy.scenePath, scenes);
1252
900
  };
@@ -1271,10 +919,20 @@ class AppContext {
1271
919
  this.getAppOptions = () => {
1272
920
  return typeof this.appOptions === "function" ? this.appOptions() : this.appOptions;
1273
921
  };
1274
- this.createStorage = (storeId, defaultState) => {
1275
- const storage = new Storage(this, storeId, defaultState);
922
+ this.createStorage = (namespace, defaultState) => {
923
+ const isWritable$ = new Val(this.isWritable);
924
+ const plugin$ = new Val(this.manager.windowManger);
925
+ const storage = new Storage({
926
+ plugin$,
927
+ isWritable$,
928
+ namespace,
929
+ defaultState
930
+ });
931
+ this.emitter.on("writableChange", (writable) => {
932
+ isWritable$.setValue(writable);
933
+ });
1276
934
  this.emitter.on("destroy", () => {
1277
- storage.destroy();
935
+ storage.disconnect();
1278
936
  });
1279
937
  return storage;
1280
938
  };
@@ -1337,7 +995,7 @@ class AppContext {
1337
995
  }
1338
996
  get storage() {
1339
997
  if (!this._storage) {
1340
- this._storage = new Storage(this);
998
+ this._storage = this.createStorage(this.appId, this.getAttributes());
1341
999
  }
1342
1000
  return this._storage;
1343
1001
  }
@@ -1395,6 +1053,77 @@ class AppPageStateImpl {
1395
1053
  (_a = this.sceneNode) == null ? void 0 : _a.dispose();
1396
1054
  }
1397
1055
  }
1056
+ class CameraSynchronizer {
1057
+ constructor(saveCamera) {
1058
+ this.saveCamera = saveCamera;
1059
+ this.scale = 1;
1060
+ this.setRect = (rect, updateCamera = true) => {
1061
+ this.rect = rect;
1062
+ if (this.remoteCamera && this.remoteSize && updateCamera) {
1063
+ this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
1064
+ }
1065
+ };
1066
+ this.onRemoteUpdate = throttle((camera, size) => {
1067
+ this.remoteCamera = camera;
1068
+ this.remoteSize = size;
1069
+ if (this.remoteSize && this.rect) {
1070
+ requestAnimationFrame(() => {
1071
+ this.moveCameraToContian(size);
1072
+ this.moveCamera(camera);
1073
+ });
1074
+ }
1075
+ }, 32);
1076
+ }
1077
+ setView(view) {
1078
+ this.view = view;
1079
+ }
1080
+ onRemoteSizeUpdate(size) {
1081
+ var _a;
1082
+ this.remoteSize = size;
1083
+ const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size, ["width", "height"]));
1084
+ if (this.rect && this.remoteCamera && needMoveCamera) {
1085
+ if (!this.view)
1086
+ return;
1087
+ const currentCamera = this.view.camera;
1088
+ (_a = this.view) == null ? void 0 : _a.moveCameraToContain({
1089
+ width: size.width,
1090
+ height: size.height,
1091
+ originX: currentCamera.centerX - size.width / 2,
1092
+ originY: currentCamera.centerY - size.height / 2
1093
+ });
1094
+ }
1095
+ }
1096
+ onLocalCameraUpdate(camera) {
1097
+ this.saveCamera(camera);
1098
+ this.remoteCamera = camera;
1099
+ }
1100
+ moveCameraToContian(size) {
1101
+ if (!isEmpty(size) && this.view) {
1102
+ this.view.moveCameraToContain({
1103
+ width: size.width,
1104
+ height: size.height,
1105
+ originX: -size.width / 2,
1106
+ originY: -size.height / 2,
1107
+ animationMode: AnimationMode.Immediately
1108
+ });
1109
+ this.scale = this.view.camera.scale;
1110
+ }
1111
+ }
1112
+ moveCamera(camera) {
1113
+ if (!isEmpty(camera) && this.view && camera.centerX && camera.centerY) {
1114
+ if (isEqual(camera, this.view.camera))
1115
+ return;
1116
+ const { centerX, centerY, scale: scale2 } = camera;
1117
+ const needScale = scale2 * (this.scale || 1);
1118
+ this.view.moveCamera({
1119
+ centerX,
1120
+ centerY,
1121
+ scale: needScale,
1122
+ animationMode: AnimationMode.Immediately
1123
+ });
1124
+ }
1125
+ }
1126
+ }
1398
1127
  class ViewSync {
1399
1128
  constructor(context) {
1400
1129
  this.context = context;
@@ -1412,8 +1141,9 @@ class ViewSync {
1412
1141
  };
1413
1142
  this.subscribeView = () => {
1414
1143
  return this.context.view$.subscribe((view) => {
1144
+ var _a;
1415
1145
  const currentCamera = this.context.camera$.value;
1416
- if (currentCamera && this.context.size$.value) {
1146
+ if (currentCamera && this.context.size$.value && ((_a = this.needRecoverCamera$) == null ? void 0 : _a.value)) {
1417
1147
  view == null ? void 0 : view.moveCamera({
1418
1148
  scale: 1,
1419
1149
  animationMode: AnimationMode.Immediately
@@ -1427,16 +1157,16 @@ class ViewSync {
1427
1157
  return this.context.camera$.subscribe((camera, skipUpdate) => {
1428
1158
  if (skipUpdate)
1429
1159
  return;
1430
- const size2 = this.context.size$.value;
1431
- if (camera && size2) {
1432
- this.synchronizer.onRemoteUpdate(camera, size2);
1160
+ const size = this.context.size$.value;
1161
+ if (camera && size) {
1162
+ this.synchronizer.onRemoteUpdate(camera, size);
1433
1163
  }
1434
1164
  });
1435
1165
  };
1436
1166
  this.subscribeSize = () => {
1437
- return this.context.size$.subscribe((size2) => {
1438
- if (size2 && this.isBroadcastMode) {
1439
- this.synchronizer.onRemoteSizeUpdate(size2);
1167
+ return this.context.size$.subscribe((size) => {
1168
+ if (size && this.isBroadcastMode) {
1169
+ this.synchronizer.onRemoteSizeUpdate(size);
1440
1170
  }
1441
1171
  });
1442
1172
  };
@@ -1462,10 +1192,13 @@ class ViewSync {
1462
1192
  return;
1463
1193
  if (!this.isBroadcastMode)
1464
1194
  return;
1465
- if (this.context.size$.value && this.context.stageRect$.value) {
1466
- const diffScale = computedMinScale(this.context.size$.value, this.context.stageRect$.value);
1467
- const remoteScale = camera.scale / diffScale;
1468
- this.synchronizer.onLocalCameraUpdate(__spreadProps(__spreadValues({}, camera), { scale: remoteScale, id: this.context.uid }));
1195
+ const { size$, stageRect$, view$ } = this.context;
1196
+ if (size$.value && stageRect$.value && view$.value) {
1197
+ this.synchronizer.onLocalCameraUpdate(__spreadProps(__spreadValues({}, camera), { id: this.context.uid }));
1198
+ const newSize = __spreadProps(__spreadValues({}, view$.value.size), { id: this.context.uid });
1199
+ if (!isEqual(size$.value, newSize)) {
1200
+ this.context.storeSize(newSize);
1201
+ }
1469
1202
  }
1470
1203
  };
1471
1204
  this.synchronizer = this.createSynchronizer();
@@ -1476,10 +1209,14 @@ class ViewSync {
1476
1209
  this.subscribeSize(),
1477
1210
  this.subscribeStageRect()
1478
1211
  ]);
1212
+ if (context.viewMode$) {
1213
+ this.needRecoverCamera$ = derive(context.viewMode$, (mode) => mode !== "scroll");
1214
+ }
1479
1215
  const camera$size$ = combine([this.context.camera$, this.context.size$]);
1480
- camera$size$.reaction(([camera, size2]) => {
1481
- if (camera && size2) {
1482
- this.synchronizer.onRemoteUpdate(camera, size2);
1216
+ camera$size$.reaction(([camera, size]) => {
1217
+ var _a;
1218
+ if (camera && size && ((_a = this.needRecoverCamera$) == null ? void 0 : _a.value)) {
1219
+ this.synchronizer.onRemoteUpdate(camera, size);
1483
1220
  camera$size$.destroy();
1484
1221
  }
1485
1222
  });
@@ -1632,13 +1369,13 @@ class AttributesDelegate {
1632
1369
  setMainViewCamera(camera) {
1633
1370
  this.context.safeSetAttributes({ ["mainViewCamera"]: __spreadValues({}, camera) });
1634
1371
  }
1635
- setMainViewSize(size2) {
1636
- this.context.safeSetAttributes({ ["mainViewSize"]: __spreadValues({}, size2) });
1372
+ setMainViewSize(size) {
1373
+ this.context.safeSetAttributes({ ["mainViewSize"]: __spreadValues({}, size) });
1637
1374
  }
1638
- setMainViewCameraAndSize(camera, size2) {
1375
+ setMainViewCameraAndSize(camera, size) {
1639
1376
  this.context.safeSetAttributes({
1640
1377
  ["mainViewCamera"]: __spreadValues({}, camera),
1641
- ["mainViewSize"]: __spreadValues({}, size2)
1378
+ ["mainViewSize"]: __spreadValues({}, size)
1642
1379
  });
1643
1380
  }
1644
1381
  updateCursor(uid, position) {
@@ -1798,8 +1535,8 @@ class AppProxy {
1798
1535
  this.storeCamera = (camera) => {
1799
1536
  this.store.updateAppAttributes(this.id, Fields.Camera, camera);
1800
1537
  };
1801
- this.storeSize = (size2) => {
1802
- this.store.updateAppAttributes(this.id, Fields.Size, size2);
1538
+ this.storeSize = (size) => {
1539
+ this.store.updateAppAttributes(this.id, Fields.Size, size);
1803
1540
  };
1804
1541
  this.updateSize = (width, height) => {
1805
1542
  const iSize = {
@@ -1835,9 +1572,9 @@ class AppProxy {
1835
1572
  this.sideEffectManager.add(() => this.manager.refresher.add(`${this.id}-size`, () => reaction(() => {
1836
1573
  var _a2;
1837
1574
  return (_a2 = this.appAttributes) == null ? void 0 : _a2.size;
1838
- }, (size2) => {
1839
- if (size2) {
1840
- const rawSize = toJS(size2);
1575
+ }, (size) => {
1576
+ if (size) {
1577
+ const rawSize = toJS(size);
1841
1578
  if (!isEqual(rawSize, this.size$.value)) {
1842
1579
  this.size$.setValue(rawSize);
1843
1580
  }
@@ -2374,8 +2111,8 @@ class MainViewProxy {
2374
2111
  this.storeCamera = (camera) => {
2375
2112
  this.store.setMainViewCamera(camera);
2376
2113
  };
2377
- this.storeSize = (size2) => {
2378
- this.store.setMainViewSize(size2);
2114
+ this.storeSize = (size) => {
2115
+ this.store.setMainViewSize(size);
2379
2116
  };
2380
2117
  this.cameraReaction = () => {
2381
2118
  return reaction(() => this.mainViewCamera, (camera) => {
@@ -2388,9 +2125,9 @@ class MainViewProxy {
2388
2125
  }, { fireImmediately: true });
2389
2126
  };
2390
2127
  this.sizeReaction = () => {
2391
- return reaction(() => this.mainViewSize, (size2) => {
2392
- if (size2) {
2393
- const rawSize = toJS(size2);
2128
+ return reaction(() => this.mainViewSize, (size) => {
2129
+ if (size) {
2130
+ const rawSize = toJS(size);
2394
2131
  if (!isEqual(rawSize, this.size$.value)) {
2395
2132
  this.size$.setValue(rawSize);
2396
2133
  }
@@ -2400,8 +2137,8 @@ class MainViewProxy {
2400
2137
  this.mainViewClickListener = () => {
2401
2138
  this.mainViewClickHandler();
2402
2139
  };
2403
- this.setMainViewSize = debounce((size2) => {
2404
- this.store.setMainViewSize(__spreadProps(__spreadValues({}, size2), { id: this.manager.uid }));
2140
+ this.setMainViewSize = debounce((size) => {
2141
+ this.store.setMainViewSize(__spreadProps(__spreadValues({}, size), { id: this.manager.uid }));
2405
2142
  }, 50);
2406
2143
  this.onCameraOrSizeUpdated = () => {
2407
2144
  callbacks.emit("cameraStateChange", this.cameraState);
@@ -2432,9 +2169,9 @@ class MainViewProxy {
2432
2169
  callbacks.emit("baseCameraChange", camera);
2433
2170
  }
2434
2171
  });
2435
- this.size$.reaction((size2) => {
2436
- if (size2) {
2437
- callbacks.emit("baseSizeChange", size2);
2172
+ this.size$.reaction((size) => {
2173
+ if (size) {
2174
+ callbacks.emit("baseSizeChange", size);
2438
2175
  }
2439
2176
  });
2440
2177
  }
@@ -2559,6 +2296,51 @@ class MainViewProxy {
2559
2296
  this.sideEffectManager.flushAll();
2560
2297
  }
2561
2298
  }
2299
+ const onObjectByEvent = (event) => {
2300
+ return (object, func) => {
2301
+ if (object === void 0)
2302
+ return;
2303
+ if (listenUpdated) {
2304
+ const listener = (events) => {
2305
+ const kinds = events.map((e) => e.kind);
2306
+ if (kinds.includes(event)) {
2307
+ func();
2308
+ }
2309
+ };
2310
+ listenUpdated(object, listener);
2311
+ func();
2312
+ return () => unlistenUpdated(object, listener);
2313
+ } else {
2314
+ return reaction(() => object, () => {
2315
+ func();
2316
+ }, {
2317
+ fireImmediately: true
2318
+ });
2319
+ }
2320
+ };
2321
+ };
2322
+ const safeListenPropsUpdated = (getProps, callback, onDestroyed) => {
2323
+ let disposeListenUpdated = null;
2324
+ const disposeReaction = reaction(getProps, () => {
2325
+ if (disposeListenUpdated) {
2326
+ disposeListenUpdated();
2327
+ disposeListenUpdated = null;
2328
+ }
2329
+ const props = getProps();
2330
+ if (isObject(props)) {
2331
+ disposeListenUpdated = () => unlistenUpdated(props, callback);
2332
+ listenUpdated(props, callback);
2333
+ } else {
2334
+ onDestroyed == null ? void 0 : onDestroyed(props);
2335
+ }
2336
+ }, { fireImmediately: true });
2337
+ return () => {
2338
+ disposeListenUpdated == null ? void 0 : disposeListenUpdated();
2339
+ disposeReaction();
2340
+ };
2341
+ };
2342
+ const onObjectRemoved = onObjectByEvent(UpdateEventKind.Removed);
2343
+ onObjectByEvent(UpdateEventKind.Inserted);
2562
2344
  class RedoUndo {
2563
2345
  constructor(context) {
2564
2346
  this.context = context;
@@ -2618,24 +2400,176 @@ class RedoUndo {
2618
2400
  this.disposePrevFocusViewRedoUndoListeners(this.context.focus());
2619
2401
  }
2620
2402
  }
2403
+ const createScrollStorage = (manager) => {
2404
+ return new Storage({
2405
+ plugin$: new Val(manager.windowManger),
2406
+ isWritable$: manager.isWritable$,
2407
+ namespace: "scrollStorage",
2408
+ defaultState: { scrollTop: 0 }
2409
+ });
2410
+ };
2411
+ function clamp$2(x2, min, max) {
2412
+ return x2 < min ? min : x2 > max ? max : x2;
2413
+ }
2414
+ class ScrollMode {
2415
+ constructor(manager) {
2416
+ var _a;
2417
+ this.manager = manager;
2418
+ this.sideEffect = new SideEffectManager();
2419
+ this.baseWidth = SCROLL_MODE_BASE_WIDTH;
2420
+ this.baseHeight = SCROLL_MODE_BASE_HEIGHT;
2421
+ this.initScroll = () => {
2422
+ const halfWbHeight = this._size$.value.height / 2 / this._scale$.value;
2423
+ const scrollTop = this._scrollTop$.value;
2424
+ this._scrollTop$.setValue(clamp$2(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight) - 0.01);
2425
+ };
2426
+ this.getWhiteboardElement = (root) => {
2427
+ const className = ".netless-window-manager-main-view";
2428
+ return root && root.querySelector(className);
2429
+ };
2430
+ this.onWheel = (ev) => {
2431
+ var _a2;
2432
+ const target = ev.target;
2433
+ if (this.manager.canOperate && ((_a2 = this._whiteboard$.value) == null ? void 0 : _a2.contains(target))) {
2434
+ ev.preventDefault();
2435
+ ev.stopPropagation();
2436
+ const dy = ev.deltaY || 0;
2437
+ const { width } = this._size$.value;
2438
+ if (dy && width > 0) {
2439
+ const halfWbHeight = this._size$.value.height / 2 / this._scale$.value;
2440
+ const scrollTop = this._scrollTop$.value + dy / this._scale$.value;
2441
+ this.scrollStorage.setState({
2442
+ scrollTop: clamp$2(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight)
2443
+ });
2444
+ callbacks.emit("userScroll");
2445
+ }
2446
+ }
2447
+ };
2448
+ this._root$ = new Val(null);
2449
+ this._mainView$ = new Val(this.manager.mainView);
2450
+ this._mainView$.value.disableCameraTransform = true;
2451
+ if ((_a = manager.scrollBaseSize$) == null ? void 0 : _a.value) {
2452
+ this.baseWidth = manager.scrollBaseSize$.value.width;
2453
+ this.baseHeight = manager.scrollBaseSize$.value.height;
2454
+ }
2455
+ this.scrollStorage = createScrollStorage(manager);
2456
+ const scrollTop$ = new Val(this.scrollStorage.state.scrollTop);
2457
+ this._scrollTop$ = scrollTop$;
2458
+ this.sideEffect.push(this.scrollStorage.on("stateChanged", () => {
2459
+ this._scrollTop$.setValue(this.scrollStorage.state.scrollTop);
2460
+ }));
2461
+ const size$ = new Val({ width: 0, height: 0 }, { compare: (a, b2) => a.width === b2.width && a.height === b2.height });
2462
+ this._size$ = size$;
2463
+ this.sideEffect.add(() => {
2464
+ const onSizeUpdated = size$.setValue.bind(size$);
2465
+ onSizeUpdated(this._mainView$.value.size);
2466
+ this._mainView$.value.callbacks.on("onSizeUpdated", onSizeUpdated);
2467
+ return () => this._mainView$.value.callbacks.off("onSizeUpdated", onSizeUpdated);
2468
+ });
2469
+ this.sideEffect.add(() => {
2470
+ const onCameraUpdated = (camera) => {
2471
+ const halfWbHeight = size$.value.height / 2 / scale$.value;
2472
+ const scrollTop = camera.centerY;
2473
+ this.scrollStorage.setState({
2474
+ scrollTop: clamp$2(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight)
2475
+ });
2476
+ callbacks.emit("userScroll");
2477
+ };
2478
+ this._mainView$.value.callbacks.on("onCameraUpdatedByDevice", onCameraUpdated);
2479
+ return () => this._mainView$.value.callbacks.off("onCameraUpdatedByDevice", onCameraUpdated);
2480
+ });
2481
+ const scale$ = derive(size$, (size) => size.width / this.baseWidth);
2482
+ this._scale$ = scale$;
2483
+ const page$ = new Val(0);
2484
+ this.sideEffect.push(combine([scrollTop$, size$, scale$]).subscribe(([scrollTop, size, scale2]) => {
2485
+ if (scale2 > 0) {
2486
+ const wbHeight = size.height / scale2;
2487
+ page$.setValue(Math.max(scrollTop / wbHeight - 0.5, 0));
2488
+ }
2489
+ }));
2490
+ this._page$ = page$;
2491
+ this.sideEffect.push(combine([scrollTop$, scale$]).subscribe(([scrollTop, scale2]) => {
2492
+ this.updateBound(scrollTop, size$.value, scale2);
2493
+ }));
2494
+ this.sideEffect.push(size$.reaction(() => {
2495
+ this.updateScroll(scrollTop$.value);
2496
+ }));
2497
+ const whiteboard$ = derive(this._root$, this.getWhiteboardElement);
2498
+ this._whiteboard$ = whiteboard$;
2499
+ this.sideEffect.push(whiteboard$.reaction((el) => {
2500
+ if (el == null ? void 0 : el.parentElement) {
2501
+ this.sideEffect.addEventListener(el.parentElement, "wheel", this.onWheel, { capture: true, passive: false }, "wheel");
2502
+ }
2503
+ }));
2504
+ const maxScrollPage$ = combine([this._size$, this._scale$], ([size, scale2]) => {
2505
+ const halfWbHeight = size.height / 2 / scale2;
2506
+ return (this.baseHeight - halfWbHeight) / halfWbHeight / 2 - 0.51;
2507
+ });
2508
+ this.scrollState$ = combine([this._scrollTop$, this._page$, maxScrollPage$], ([scrollTop, page, maxScrollPage]) => {
2509
+ return {
2510
+ scrollTop,
2511
+ page,
2512
+ maxScrollPage
2513
+ };
2514
+ });
2515
+ this.updateScroll(scrollTop$.value);
2516
+ this.sideEffect.push(this.scrollState$.subscribe((state) => callbacks.emit("scrollStateChange", state)));
2517
+ this.initScroll();
2518
+ }
2519
+ setRoot(root) {
2520
+ this._root$.setValue(root);
2521
+ }
2522
+ updateScroll(scrollTop) {
2523
+ this._mainView$.value.moveCamera({
2524
+ centerY: scrollTop,
2525
+ animationMode: AnimationMode.Immediately
2526
+ });
2527
+ }
2528
+ updateBound(scrollTop, { height }, scale2) {
2529
+ if (scale2 > 0) {
2530
+ this._mainView$.value.moveCameraToContain({
2531
+ originX: 0,
2532
+ originY: scrollTop - height / scale2 / 2,
2533
+ width: this.baseWidth,
2534
+ height: height / scale2,
2535
+ animationMode: AnimationMode.Immediately
2536
+ });
2537
+ this._mainView$.value.setCameraBound({
2538
+ damping: 1,
2539
+ maxContentMode: () => scale2,
2540
+ minContentMode: () => scale2,
2541
+ centerX: this.baseWidth / 2,
2542
+ centerY: this.baseHeight / 2,
2543
+ width: this.baseWidth,
2544
+ height: this.baseHeight
2545
+ });
2546
+ }
2547
+ }
2548
+ dispose() {
2549
+ this.sideEffect.flushAll();
2550
+ }
2551
+ }
2621
2552
  class AppManager {
2622
2553
  constructor(windowManger) {
2554
+ var _a;
2623
2555
  this.windowManger = windowManger;
2624
2556
  this.appProxies = /* @__PURE__ */ new Map();
2625
2557
  this.appStatus = /* @__PURE__ */ new Map();
2626
2558
  this.store = store;
2627
2559
  this.isReplay = this.windowManger.isReplay;
2628
2560
  this.mainViewScenesLength = 0;
2561
+ this.scrollBaseSize$ = new Val(null);
2629
2562
  this.callbacksNode = null;
2630
2563
  this.appCreateQueue = new AppCreateQueue();
2631
2564
  this.sceneIndex$ = new Val(void 0);
2632
2565
  this.focused$ = new Val(void 0);
2633
2566
  this.members$ = new Val([]);
2567
+ this.isWritable$ = new Val(Boolean((_a = this.room) == null ? void 0 : _a.isWritable));
2634
2568
  this.sideEffectManager = new SideEffectManager();
2635
2569
  this.sceneState = null;
2636
2570
  this.rootDirRemoving = false;
2637
2571
  this.onRemoveScenes = async (params) => {
2638
- var _a, _b;
2572
+ var _a2, _b;
2639
2573
  const { scenePath } = params;
2640
2574
  if (scenePath === ROOT_DIR) {
2641
2575
  await this.onRootDirRemoved();
@@ -2644,7 +2578,7 @@ class AppManager {
2644
2578
  }
2645
2579
  if (isRootDirPage(scenePath)) {
2646
2580
  let nextIndex = this.mainView.focusSceneIndex || 0;
2647
- let sceneName = (_a = this.callbacksNode) == null ? void 0 : _a.scenes[nextIndex];
2581
+ let sceneName = (_a2 = this.callbacksNode) == null ? void 0 : _a2.scenes[nextIndex];
2648
2582
  if (!sceneName) {
2649
2583
  nextIndex = 0;
2650
2584
  sceneName = (_b = this.callbacksNode) == null ? void 0 : _b.scenes[nextIndex];
@@ -2695,11 +2629,11 @@ class AppManager {
2695
2629
  }
2696
2630
  };
2697
2631
  this.removeSceneByIndex = async (index2) => {
2698
- var _a;
2632
+ var _a2;
2699
2633
  const nextIndex = calculateNextIndex(index2, this.windowManger.pageState);
2700
2634
  this.setSceneIndexWithoutSync(nextIndex);
2701
2635
  this.dispatchInternalEvent(Events.SetAppFocusIndex, { type: "main", index: nextIndex });
2702
- const scene = (_a = this.callbacksNode) == null ? void 0 : _a.scenes[index2];
2636
+ const scene = (_a2 = this.callbacksNode) == null ? void 0 : _a2.scenes[index2];
2703
2637
  setTimeout(() => {
2704
2638
  if (scene) {
2705
2639
  removeScenes(this.room, `${ROOT_DIR}${scene}`, index2);
@@ -2717,8 +2651,8 @@ class AppManager {
2717
2651
  });
2718
2652
  };
2719
2653
  this.setSceneIndexWithoutSync = (index2) => {
2720
- var _a;
2721
- const sceneName = (_a = this.callbacksNode) == null ? void 0 : _a.scenes[index2];
2654
+ var _a2;
2655
+ const sceneName = (_a2 = this.callbacksNode) == null ? void 0 : _a2.scenes[index2];
2722
2656
  if (sceneName) {
2723
2657
  this.mainViewProxy.setFocusScenePath(`${ROOT_DIR}${sceneName}`);
2724
2658
  }
@@ -2789,8 +2723,8 @@ class AppManager {
2789
2723
  });
2790
2724
  };
2791
2725
  this.addAppCloseListener = () => {
2792
- var _a;
2793
- (_a = this.refresher) == null ? void 0 : _a.add("appsClose", () => {
2726
+ var _a2;
2727
+ (_a2 = this.refresher) == null ? void 0 : _a2.add("appsClose", () => {
2794
2728
  return onObjectRemoved(this.attributes.apps, () => {
2795
2729
  this.onAppDelete(this.attributes.apps);
2796
2730
  });
@@ -2807,13 +2741,13 @@ class AppManager {
2807
2741
  }
2808
2742
  };
2809
2743
  this.onFocusChange = (focused) => {
2810
- var _a;
2744
+ var _a2;
2811
2745
  if (this.focused$.value !== focused) {
2812
2746
  callbacks.emit("focusedChange", focused);
2813
2747
  emitter.emit("focusedChange", { focused, prev: this.focused$.value });
2814
2748
  this.focused$.setValue(focused);
2815
2749
  if (focused !== void 0) {
2816
- (_a = this.boxManager) == null ? void 0 : _a.focusBox({ appId: focused });
2750
+ (_a2 = this.boxManager) == null ? void 0 : _a2.focusBox({ appId: focused });
2817
2751
  setTimeout(() => {
2818
2752
  const appProxy = this.appProxies.get(focused);
2819
2753
  if (appProxy) {
@@ -2839,14 +2773,14 @@ class AppManager {
2839
2773
  });
2840
2774
  };
2841
2775
  this.onMinimized = (minimized) => {
2842
- var _a, _b;
2843
- if (((_a = this.boxManager) == null ? void 0 : _a.minimized) !== minimized) {
2776
+ var _a2, _b;
2777
+ if (((_a2 = this.boxManager) == null ? void 0 : _a2.minimized) !== minimized) {
2844
2778
  if (minimized === true) {
2845
2779
  (_b = this.boxManager) == null ? void 0 : _b.blurAllBox();
2846
2780
  }
2847
2781
  setTimeout(() => {
2848
- var _a2;
2849
- (_a2 = this.boxManager) == null ? void 0 : _a2.setMinimized(Boolean(minimized));
2782
+ var _a3;
2783
+ (_a3 = this.boxManager) == null ? void 0 : _a3.setMinimized(Boolean(minimized));
2850
2784
  }, 0);
2851
2785
  }
2852
2786
  };
@@ -2886,11 +2820,11 @@ class AppManager {
2886
2820
  }
2887
2821
  };
2888
2822
  this.displayerWritableListener = (isReadonly) => {
2889
- var _a, _b;
2823
+ var _a2, _b;
2890
2824
  const isWritable = !isReadonly;
2891
2825
  const isManualWritable = this.windowManger.readonly === void 0 || !this.windowManger.readonly;
2892
2826
  if (this.windowManger.readonly === void 0) {
2893
- (_a = this.boxManager) == null ? void 0 : _a.setReadonly(isReadonly);
2827
+ (_a2 = this.boxManager) == null ? void 0 : _a2.setReadonly(isReadonly);
2894
2828
  } else {
2895
2829
  (_b = this.boxManager) == null ? void 0 : _b.setReadonly(!(isWritable && isManualWritable));
2896
2830
  }
@@ -2903,6 +2837,7 @@ class AppManager {
2903
2837
  }
2904
2838
  }
2905
2839
  emitter.emit("writableChange", isWritable);
2840
+ this.isWritable$.setValue(isWritable);
2906
2841
  };
2907
2842
  this.updateSceneIndex = () => {
2908
2843
  const scenePath = this.store.getMainViewScenePath();
@@ -2935,10 +2870,10 @@ class AppManager {
2935
2870
  this.refresher.setContext({ emitter });
2936
2871
  this.sideEffectManager.add(() => {
2937
2872
  return () => {
2938
- var _a, _b, _c;
2873
+ var _a2, _b, _c;
2939
2874
  this.appCreateQueue.destroy();
2940
2875
  this.mainViewProxy.destroy();
2941
- (_a = this.refresher) == null ? void 0 : _a.destroy();
2876
+ (_a2 = this.refresher) == null ? void 0 : _a2.destroy();
2942
2877
  this.viewManager.destroy();
2943
2878
  (_b = this.boxManager) == null ? void 0 : _b.destroy();
2944
2879
  (_c = this.callbacksNode) == null ? void 0 : _c.dispose();
@@ -2957,6 +2892,16 @@ class AppManager {
2957
2892
  this.safeUpdateAttributes([Fields.Registered, payload.kind], payload);
2958
2893
  });
2959
2894
  this.members$.setValue(serializeRoomMembers(this.displayer.state.roomMembers));
2895
+ emitter.on("mainViewMounted", () => {
2896
+ this.windowManger.viewMode$.subscribe((viewMode) => {
2897
+ const playground = this.windowManger.playground$.value;
2898
+ if (viewMode === "scroll" && playground) {
2899
+ const scrollMode = new ScrollMode(this);
2900
+ this.scrollMode = scrollMode;
2901
+ scrollMode.setRoot(playground);
2902
+ }
2903
+ });
2904
+ });
2960
2905
  }
2961
2906
  async onRootDirRemoved(needClose = true) {
2962
2907
  await this.setMainViewScenePath(INIT_DIR);
@@ -3997,8 +3942,8 @@ var progressToPixels = function(progress, length) {
3997
3942
  return px.transform(progress * length);
3998
3943
  };
3999
3944
  var unmeasured = { x: 0, y: 0, width: 0, height: 0 };
4000
- function calcOrigin(origin, offset, size2) {
4001
- return typeof origin === "string" ? origin : px.transform(offset + size2 * origin);
3945
+ function calcOrigin(origin, offset, size) {
3946
+ return typeof origin === "string" ? origin : px.transform(offset + size * origin);
4002
3947
  }
4003
3948
  function calculateSVGTransformOrigin(dimensions, originX, originY) {
4004
3949
  return calcOrigin(originX, dimensions.x, dimensions.width) + " " + calcOrigin(originY, dimensions.y, dimensions.height);
@@ -4475,9 +4420,9 @@ class TeleBox {
4475
4420
  }, skipUpdate);
4476
4421
  }));
4477
4422
  const intrinsicCoord$ = new Val({ x: x2, y: y2 }, { compare: shallowequal });
4478
- const pxIntrinsicSize$ = combine([intrinsicSize$, managerStageRect$], ([size2, managerStageRect]) => ({
4479
- width: managerStageRect.width * size2.width,
4480
- height: managerStageRect.height * size2.height
4423
+ const pxIntrinsicSize$ = combine([intrinsicSize$, managerStageRect$], ([size, managerStageRect]) => ({
4424
+ width: managerStageRect.width * size.width,
4425
+ height: managerStageRect.height * size.height
4481
4426
  }), { compare: shallowequal });
4482
4427
  const pxIntrinsicCoord$ = combine([intrinsicCoord$, managerStageRect$], ([intrinsicCoord, managerStageRect]) => ({
4483
4428
  x: intrinsicCoord.x * managerStageRect.width,
@@ -6894,7 +6839,7 @@ class CursorManager {
6894
6839
  this.initCursorInstance = (uid) => {
6895
6840
  let cursorInstance = this.cursorInstances.get(uid);
6896
6841
  if (!cursorInstance) {
6897
- cursorInstance = new Cursor(this.manager, uid, this, WindowManager.playground);
6842
+ cursorInstance = new Cursor(this.manager, uid, this, this.playground$.value);
6898
6843
  this.cursorInstances.set(uid, cursorInstance);
6899
6844
  }
6900
6845
  return cursorInstance;
@@ -6937,7 +6882,7 @@ class CursorManager {
6937
6882
  this.hideCursor(this.manager.uid);
6938
6883
  };
6939
6884
  this.roomMembers = (_a = this.manager.room) == null ? void 0 : _a.state.roomMembers;
6940
- const playground = WindowManager.playground;
6885
+ const playground = this.playground$.value;
6941
6886
  if (playground) {
6942
6887
  this.setupWrapper(playground);
6943
6888
  }
@@ -6951,6 +6896,9 @@ class CursorManager {
6951
6896
  this.applianceIcons = __spreadValues(__spreadValues({}, ApplianceMap), applianceIcons);
6952
6897
  }
6953
6898
  }
6899
+ get playground$() {
6900
+ return this.manager.windowManger.playground$;
6901
+ }
6954
6902
  canMoveCursor(member) {
6955
6903
  const isLaserPointer = (member == null ? void 0 : member.memberState.currentApplianceName) === ApplianceNames.laserPointer;
6956
6904
  return this.enableCursor || isLaserPointer;
@@ -11688,70 +11636,35 @@ const reconnectRefresher = new ReconnectRefresher({ emitter });
11688
11636
  const _WindowManager = class extends InvisiblePlugin {
11689
11637
  constructor(context) {
11690
11638
  super(context);
11691
- this.version = "1.0.0-canary.53";
11692
- this.dependencies = { "dependencies": { "@juggle/resize-observer": "^3.3.1", "@netless/telebox-insider": "1.0.0-alpha.37", "emittery": "^0.11.0", "lodash": "^4.17.21", "p-retry": "^4.6.2", "side-effect-manager": "^1.1.1", "uuid": "^7.0.3", "value-enhancer": "^1.3.2" }, "peerDependencies": { "white-web-sdk": "^2.16.0" }, "devDependencies": { "@netless/app-docs-viewer": "^0.3.3", "@netless/app-plyr": "0.2.2", "@playwright/test": "^1.23.2", "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-url": "^6.1.0", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.49", "@tsconfig/svelte": "^2.0.1", "@types/debug": "^4.1.7", "@types/lodash": "^4.14.182", "@types/lodash-es": "^4.17.6", "@types/node": "^18.0.3", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^4.30.0", "@typescript-eslint/parser": "^4.30.0", "@vitest/ui": "^0.14.2", "cypress": "^8.7.0", "dotenv": "^10.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^3.2.0", "jsdom": "^19.0.0", "less": "^4.1.3", "prettier": "^2.3.2", "prettier-plugin-svelte": "^2.4.0", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-styles": "^3.14.1", "svelte": "^3.42.4", "typescript": "^4.5.5", "vite": "^2.5.3", "vite-plugin-dts": "^1.2.1", "vitest": "^0.18.0", "white-web-sdk": "2.16.26" } };
11639
+ this.version = "1.0.0-canary.54";
11640
+ this.dependencies = { "dependencies": { "@juggle/resize-observer": "^3.3.1", "@netless/synced-store": "^2.0.7", "@netless/telebox-insider": "1.0.0-alpha.37", "emittery": "^0.11.0", "lodash": "^4.17.21", "p-retry": "^4.6.2", "side-effect-manager": "^1.2.1", "uuid": "^7.0.3", "value-enhancer": "^1.3.2" }, "peerDependencies": { "white-web-sdk": "^2.16.0" }, "devDependencies": { "@netless/app-docs-viewer": "^0.3.3", "@netless/app-plyr": "0.2.2", "@playwright/test": "^1.23.2", "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-url": "^6.1.0", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.49", "@tsconfig/svelte": "^2.0.1", "@types/debug": "^4.1.7", "@types/lodash": "^4.14.182", "@types/lodash-es": "^4.17.6", "@types/node": "^18.0.3", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^4.30.0", "@typescript-eslint/parser": "^4.30.0", "@vitest/ui": "^0.14.2", "cypress": "^8.7.0", "dotenv": "^10.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^3.2.0", "jsdom": "^19.0.0", "less": "^4.1.3", "prettier": "^2.3.2", "prettier-plugin-svelte": "^2.4.0", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-styles": "^3.14.1", "svelte": "^3.42.4", "typescript": "^4.5.5", "vite": "^2.5.3", "vite-plugin-dts": "^1.2.1", "vitest": "^0.18.0", "white-web-sdk": "^2.16.35" } };
11693
11641
  this.emitter = callbacks;
11694
11642
  this.viewMode = ViewMode.Broadcaster;
11695
11643
  this.viewMode$ = new Val(ViewMode.Broadcaster);
11644
+ this.playground$ = new Val(void 0);
11696
11645
  this.isReplay = isPlayer(this.displayer);
11697
- this.cameraUpdating = 0;
11698
- this.nextCamera = null;
11699
11646
  this.containerSizeRatio = _WindowManager.containerSizeRatio;
11700
11647
  this.moveCamera = (camera) => {
11701
11648
  var _a;
11649
+ const pureCamera = omit(camera, ["animationMode"]);
11702
11650
  const mainViewCamera = __spreadValues({}, this.mainView.camera);
11703
- const nextCamera = __spreadValues(__spreadValues({}, mainViewCamera), camera);
11704
- if (isEqual(nextCamera, mainViewCamera))
11705
- return;
11706
- if (!this.appManager)
11651
+ if (isEqual(__spreadValues(__spreadValues({}, mainViewCamera), pureCamera), mainViewCamera))
11707
11652
  return;
11708
- if (camera.animationMode === AnimationMode.Immediately) {
11709
- this.appManager.mainViewProxy.storeCamera(__spreadValues({
11710
- id: this.appManager.uid
11711
- }, nextCamera));
11712
- } else {
11713
- const remoteCamera = this.appManager.mainViewProxy.size$.value;
11714
- const currentSize = (_a = this.boxManager) == null ? void 0 : _a.stageRect;
11715
- let nextScale;
11716
- if (camera.scale && remoteCamera && currentSize) {
11717
- nextScale = camera.scale * computedMinScale(remoteCamera, currentSize);
11718
- }
11719
- if (nextScale) {
11720
- this.mainView.moveCamera(__spreadProps(__spreadValues({}, camera), {
11721
- scale: nextScale
11722
- }));
11723
- } else {
11724
- this.mainView.moveCamera(camera);
11725
- }
11726
- this.appManager.dispatchInternalEvent(Events.MoveCamera, camera);
11727
- this.mainView.callbacks.off("onCameraUpdated", this.onCameraUpdated);
11728
- clearTimeout(this.cameraUpdating);
11729
- this.cameraUpdating = 0;
11730
- this.mainView.callbacks.on("onCameraUpdated", this.onCameraUpdated);
11731
- if (nextScale) {
11732
- this.nextCamera = nextCamera;
11733
- }
11734
- }
11735
- };
11736
- this.onCameraUpdated = () => {
11737
- if (this.cameraUpdating) {
11738
- clearTimeout(this.cameraUpdating);
11739
- this.cameraUpdating = 0;
11740
- }
11741
- this.cameraUpdating = setTimeout(() => {
11742
- this.mainView.callbacks.off("onCameraUpdated", this.onCameraUpdated);
11743
- clearTimeout(this.cameraUpdating);
11744
- this.cameraUpdating = 0;
11745
- if (!this.appManager || !this.nextCamera)
11746
- return;
11747
- this.appManager.mainViewProxy.storeCamera(__spreadValues({
11748
- id: this.appManager.uid
11749
- }, this.nextCamera));
11750
- this.nextCamera = null;
11751
- }, 50);
11653
+ this.debouncedStoreCamera();
11654
+ this.mainView.moveCamera(camera);
11655
+ (_a = this.appManager) == null ? void 0 : _a.dispatchInternalEvent(Events.MoveCamera, camera);
11656
+ };
11657
+ this.debouncedStoreCamera = () => {
11658
+ const storeCamera = debounce(() => {
11659
+ var _a, _b;
11660
+ (_a = this.appManager) == null ? void 0 : _a.mainViewProxy.storeCurrentCamera();
11661
+ (_b = this.appManager) == null ? void 0 : _b.mainViewProxy.storeCurrentSize();
11662
+ this.mainView.callbacks.off("onCameraUpdated", storeCamera);
11663
+ }, 200);
11664
+ this.mainView.callbacks.on("onCameraUpdated", storeCamera);
11752
11665
  };
11753
11666
  _WindowManager.displayer = context.displayer;
11754
- window.NETLESS_DEPS = { "dependencies": { "@juggle/resize-observer": "^3.3.1", "@netless/telebox-insider": "1.0.0-alpha.37", "emittery": "^0.11.0", "lodash": "^4.17.21", "p-retry": "^4.6.2", "side-effect-manager": "^1.1.1", "uuid": "^7.0.3", "value-enhancer": "^1.3.2" }, "peerDependencies": { "white-web-sdk": "^2.16.0" }, "devDependencies": { "@netless/app-docs-viewer": "^0.3.3", "@netless/app-plyr": "0.2.2", "@playwright/test": "^1.23.2", "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-url": "^6.1.0", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.49", "@tsconfig/svelte": "^2.0.1", "@types/debug": "^4.1.7", "@types/lodash": "^4.14.182", "@types/lodash-es": "^4.17.6", "@types/node": "^18.0.3", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^4.30.0", "@typescript-eslint/parser": "^4.30.0", "@vitest/ui": "^0.14.2", "cypress": "^8.7.0", "dotenv": "^10.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^3.2.0", "jsdom": "^19.0.0", "less": "^4.1.3", "prettier": "^2.3.2", "prettier-plugin-svelte": "^2.4.0", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-styles": "^3.14.1", "svelte": "^3.42.4", "typescript": "^4.5.5", "vite": "^2.5.3", "vite-plugin-dts": "^1.2.1", "vitest": "^0.18.0", "white-web-sdk": "2.16.26" } };
11667
+ window.NETLESS_DEPS = { "dependencies": { "@juggle/resize-observer": "^3.3.1", "@netless/synced-store": "^2.0.7", "@netless/telebox-insider": "1.0.0-alpha.37", "emittery": "^0.11.0", "lodash": "^4.17.21", "p-retry": "^4.6.2", "side-effect-manager": "^1.2.1", "uuid": "^7.0.3", "value-enhancer": "^1.3.2" }, "peerDependencies": { "white-web-sdk": "^2.16.0" }, "devDependencies": { "@netless/app-docs-viewer": "^0.3.3", "@netless/app-plyr": "0.2.2", "@playwright/test": "^1.23.2", "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-url": "^6.1.0", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.49", "@tsconfig/svelte": "^2.0.1", "@types/debug": "^4.1.7", "@types/lodash": "^4.14.182", "@types/lodash-es": "^4.17.6", "@types/node": "^18.0.3", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^4.30.0", "@typescript-eslint/parser": "^4.30.0", "@vitest/ui": "^0.14.2", "cypress": "^8.7.0", "dotenv": "^10.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^3.2.0", "jsdom": "^19.0.0", "less": "^4.1.3", "prettier": "^2.3.2", "prettier-plugin-svelte": "^2.4.0", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-styles": "^3.14.1", "svelte": "^3.42.4", "typescript": "^4.5.5", "vite": "^2.5.3", "vite-plugin-dts": "^1.2.1", "vitest": "^0.18.0", "white-web-sdk": "^2.16.35" } };
11755
11668
  }
11756
11669
  static async mount(params) {
11757
11670
  var _a;
@@ -11798,6 +11711,9 @@ const _WindowManager = class extends InvisiblePlugin {
11798
11711
  }
11799
11712
  manager.containerSizeRatio = _WindowManager.containerSizeRatio;
11800
11713
  await manager.ensureAttributes();
11714
+ if (params.viewMode) {
11715
+ manager.viewMode$.setValue(params.viewMode);
11716
+ }
11801
11717
  manager.appManager = new AppManager(manager);
11802
11718
  manager._pageState = new PageStateImpl(manager.appManager);
11803
11719
  manager.cursorManager = new CursorManager(manager.appManager, Boolean(cursor), params.applianceIcons);
@@ -11815,6 +11731,9 @@ const _WindowManager = class extends InvisiblePlugin {
11815
11731
  if (params.container) {
11816
11732
  manager.bindContainer(params.container);
11817
11733
  }
11734
+ if (params.scrollModeWidth && params.scrollModeHeight) {
11735
+ manager.appManager.scrollBaseSize$.setValue({ width: params.scrollModeWidth, height: params.scrollModeHeight });
11736
+ }
11818
11737
  replaceRoomFunction(room, manager);
11819
11738
  emitter.emit("onCreated");
11820
11739
  _WindowManager.isCreated = true;
@@ -11883,6 +11802,7 @@ const _WindowManager = class extends InvisiblePlugin {
11883
11802
  this.bindMainView(mainViewElement, params.disableCameraTransform);
11884
11803
  if (_WindowManager.playground) {
11885
11804
  (_b = this.cursorManager) == null ? void 0 : _b.setupWrapper(_WindowManager.playground);
11805
+ this.playground$.setValue(_WindowManager.playground);
11886
11806
  }
11887
11807
  }
11888
11808
  }
@@ -12098,10 +12018,11 @@ const _WindowManager = class extends InvisiblePlugin {
12098
12018
  if (mode === ViewMode.Broadcaster) {
12099
12019
  if (this.canOperate) {
12100
12020
  mainViewProxy == null ? void 0 : mainViewProxy.storeCurrentCamera();
12021
+ mainViewProxy == null ? void 0 : mainViewProxy.storeCurrentSize();
12101
12022
  }
12102
12023
  mainViewProxy == null ? void 0 : mainViewProxy.start();
12103
12024
  }
12104
- if (mode === ViewMode.Freedom) {
12025
+ if (mode === ViewMode.Freedom || mode === "scroll") {
12105
12026
  mainViewProxy == null ? void 0 : mainViewProxy.stop();
12106
12027
  }
12107
12028
  this.viewMode = mode;
@@ -12252,6 +12173,10 @@ const _WindowManager = class extends InvisiblePlugin {
12252
12173
  throw new AppManagerNotInitError();
12253
12174
  }
12254
12175
  }
12176
+ get scrollState() {
12177
+ var _a, _b;
12178
+ return (_b = (_a = this.appManager) == null ? void 0 : _a.scrollMode) == null ? void 0 : _b.scrollState$.value;
12179
+ }
12255
12180
  get teleboxManager() {
12256
12181
  if (!this.boxManager) {
12257
12182
  throw new BoxManagerNotInitializeError();
@@ -12270,6 +12195,12 @@ const _WindowManager = class extends InvisiblePlugin {
12270
12195
  var _a;
12271
12196
  return (_a = this.appManager) == null ? void 0 : _a.closeApp(appId);
12272
12197
  }
12198
+ moveCameraToContain(rectangle) {
12199
+ var _a;
12200
+ this.debouncedStoreCamera();
12201
+ this.mainView.moveCameraToContain(rectangle);
12202
+ (_a = this.appManager) == null ? void 0 : _a.dispatchInternalEvent(Events.MoveCameraToContain, rectangle);
12203
+ }
12273
12204
  convertToPointInWorld(point) {
12274
12205
  return this.mainView.convertToPointInWorld(point);
12275
12206
  }
@@ -12406,9 +12337,9 @@ const _WindowManager = class extends InvisiblePlugin {
12406
12337
  var _a;
12407
12338
  (_a = this.boxManager) == null ? void 0 : _a.teleBoxManager.setStageStyle(style2);
12408
12339
  }
12409
- setBaseSize(size2) {
12340
+ setBaseSize(size) {
12410
12341
  var _a;
12411
- (_a = this.appManager) == null ? void 0 : _a.mainViewProxy.setMainViewSize(size2);
12342
+ (_a = this.appManager) == null ? void 0 : _a.mainViewProxy.setMainViewSize(size);
12412
12343
  setTimeout(() => {
12413
12344
  if (!this.appManager || !this.appManager.mainViewProxy.camera$.value)
12414
12345
  return;