@netless/window-manager 0.4.0-canary.2 → 0.4.0-canary.20

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 (82) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.idea/window-manager.iml +12 -0
  5. package/.vscode/settings.json +1 -0
  6. package/CHANGELOG.md +29 -1
  7. package/README.md +1 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/StorageEvent.d.ts +8 -0
  10. package/dist/App/Storage/index.d.ts +39 -0
  11. package/dist/App/Storage/typings.d.ts +22 -0
  12. package/dist/App/Storage/utils.d.ts +5 -0
  13. package/dist/AppContext.d.ts +40 -16
  14. package/dist/AppListener.d.ts +1 -1
  15. package/dist/AppManager.d.ts +15 -11
  16. package/dist/AppProxy.d.ts +6 -5
  17. package/dist/AttributesDelegate.d.ts +2 -2
  18. package/dist/BoxManager.d.ts +6 -3
  19. package/dist/BuiltinApps.d.ts +5 -0
  20. package/dist/ContainerResizeObserver.d.ts +10 -0
  21. package/dist/Cursor/Cursor.d.ts +8 -11
  22. package/dist/Cursor/index.d.ts +5 -16
  23. package/dist/Helper.d.ts +6 -0
  24. package/dist/ReconnectRefresher.d.ts +0 -1
  25. package/dist/Register/storage.d.ts +5 -1
  26. package/dist/Utils/Common.d.ts +7 -2
  27. package/dist/Utils/Reactive.d.ts +1 -1
  28. package/dist/Utils/RoomHacker.d.ts +1 -1
  29. package/dist/{MainView.d.ts → View/MainView.d.ts} +5 -6
  30. package/dist/View/ViewManager.d.ts +13 -0
  31. package/dist/constants.d.ts +3 -7
  32. package/dist/index.d.ts +25 -10
  33. package/dist/index.es.js +41 -1
  34. package/dist/index.es.js.map +1 -1
  35. package/dist/index.umd.js +41 -1
  36. package/dist/index.umd.js.map +1 -1
  37. package/dist/style.css +1 -1
  38. package/dist/typings.d.ts +3 -2
  39. package/docs/api.md +36 -6
  40. package/docs/concept.md +9 -0
  41. package/package.json +7 -6
  42. package/src/App/MagixEvent/index.ts +68 -0
  43. package/src/App/Storage/StorageEvent.ts +21 -0
  44. package/src/App/Storage/index.ts +289 -0
  45. package/src/App/Storage/typings.ts +23 -0
  46. package/src/App/Storage/utils.ts +17 -0
  47. package/src/AppContext.ts +66 -24
  48. package/src/AppListener.ts +15 -14
  49. package/src/AppManager.ts +141 -63
  50. package/src/AppProxy.ts +50 -52
  51. package/src/AttributesDelegate.ts +2 -2
  52. package/src/BoxManager.ts +40 -24
  53. package/src/BuiltinApps.ts +23 -0
  54. package/src/ContainerResizeObserver.ts +62 -0
  55. package/src/Cursor/Cursor.ts +22 -36
  56. package/src/Cursor/index.ts +33 -139
  57. package/src/Helper.ts +30 -0
  58. package/src/ReconnectRefresher.ts +0 -5
  59. package/src/Register/index.ts +25 -16
  60. package/src/Register/loader.ts +1 -1
  61. package/src/Register/storage.ts +6 -1
  62. package/src/Utils/Common.ts +66 -13
  63. package/src/Utils/Reactive.ts +9 -3
  64. package/src/Utils/RoomHacker.ts +42 -13
  65. package/src/{MainView.ts → View/MainView.ts} +25 -36
  66. package/src/View/ViewManager.ts +52 -0
  67. package/src/constants.ts +3 -4
  68. package/src/index.ts +96 -72
  69. package/src/shim.d.ts +5 -0
  70. package/src/style.css +7 -1
  71. package/src/typings.ts +3 -2
  72. package/vite.config.js +8 -2
  73. package/dist/Base/Context.d.ts +0 -13
  74. package/dist/Base/index.d.ts +0 -7
  75. package/dist/Utils/CameraStore.d.ts +0 -15
  76. package/dist/ViewManager.d.ts +0 -29
  77. package/dist/sdk.d.ts +0 -14
  78. package/src/Base/Context.ts +0 -49
  79. package/src/Base/index.ts +0 -10
  80. package/src/Utils/CameraStore.ts +0 -72
  81. package/src/sdk.ts +0 -39
  82. package/src/viewManager.ts +0 -177
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .netless-window-manager-playground{width:100%;height:100%;position:relative;z-index:1;overflow:hidden;user-select:none}.netless-window-manager-sizer{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;overflow:hidden;display:flex}.netless-window-manager-sizer-horizontal{flex-direction:column}.netless-window-manager-sizer:before,.netless-window-manager-sizer:after{flex:1;content:"";display:block}.netless-window-manager-chess-sizer:before,.netless-window-manager-chess-sizer:after{background-image:linear-gradient(45deg,#b0b0b0 25%,transparent 25%),linear-gradient(-45deg,#b0b0b0 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#b0b0b0 75%),linear-gradient(-45deg,transparent 75%,#b0b0b0 75%);background-color:#fff;background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px}.netless-window-manager-wrapper{position:relative;z-index:1;width:100%;height:100%;overflow:hidden}.netless-window-manager-main-view{width:100%;height:100%}.netless-window-manager-cursor-pencil-image{width:26px;height:26px}.netless-window-manager-cursor-eraser-image{width:26px;height:26px}.netless-window-manager-cursor-selector-image{width:24px;height:24px}.netless-window-manager-cursor-selector-avatar{border-radius:50%;border-style:solid;border-width:2px;border-color:#fff;margin-bottom:2px}.netless-window-manager-cursor-selector-avatar img{width:12px}.netless-window-manager-cursor-inner{border-radius:4px;display:flex;align-items:center;justify-content:center;flex-direction:row;padding-left:4px;padding-right:4px;font-size:12px}.netless-window-manager-cursor-inner-mellow{height:32px;border-radius:16px;display:flex;align-items:center;justify-content:center;flex-direction:row;padding-left:16px;padding-right:16px}.netless-window-manager-cursor-tag-name{font-size:12px;margin-left:4px;padding:2px 8px;border-radius:4px}.netless-window-manager-cursor-mid{display:flex;flex-direction:column;align-items:center;justify-content:center;position:absolute;width:26px;height:26px;z-index:2147483647;left:0;top:0;will-change:transform;transition:transform .05s;transform-origin:0 0;user-select:none}.netless-window-manager-cursor-pencil-offset{margin-left:-20px}.netless-window-manager-cursor-selector-offset{margin-left:-22px;margin-top:56px}.netless-window-manager-cursor-text-offset{margin-left:-30px;margin-top:18px}.netless-window-manager-cursor-shape-offset{display:flex;flex-direction:column;align-items:center;justify-content:center;position:absolute;width:180px;height:64px;margin-left:-30px;margin-top:12px}.netless-window-manager-cursor-name{width:100%;height:48px;display:flex;align-items:center;justify-content:center;position:absolute;top:-40px}.cursor-image-wrapper{display:flex;justify-content:center}.tele-fancy-scrollbar{overscroll-behavior:contain;overflow:auto;overflow-y:scroll;overflow-y:overlay;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;scrollbar-width:auto}.tele-fancy-scrollbar::-webkit-scrollbar{height:8px;width:8px}.tele-fancy-scrollbar::-webkit-scrollbar-track{background-color:transparent}.tele-fancy-scrollbar::-webkit-scrollbar-thumb{background-color:#444e601a;background-color:transparent;border-radius:4px;transition:background-color .4s}.tele-fancy-scrollbar:hover::-webkit-scrollbar-thumb{background-color:#444e601a}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:hover{background-color:#444e6033}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:active{background-color:#444e6033}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:vertical{min-height:50px}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:horizontal{min-width:50px}.telebox-box{position:absolute;top:0;left:0;z-index:100;will-change:transform;transition:width .4s cubic-bezier(.4,.9,.71,1.02),height .4s cubic-bezier(.55,.82,.63,.95),opacity .6s cubic-bezier(.7,0,.84,0),transform .4s ease}.telebox-box-main{position:relative;width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden;background:#f9f9fc;box-shadow:0 4px 10px #2f419226;border-radius:6px;border:1px solid #e3e3ec}.telebox-titlebar-wrap{flex-shrink:0;position:relative;z-index:1}.telebox-content-wrap{flex:1;width:100%;overflow:hidden;display:flex;justify-content:center;align-items:center}.telebox-content{width:100%;height:100%;position:relative}.telebox-footer-wrap{flex-shrink:0;display:flex;flex-direction:column}.telebox-footer-wrap:before{content:"";display:block;flex:1}.telebox-resize-handle{position:absolute;z-index:2147483647}.telebox-n{width:100%;height:5px;left:0;top:-5px;cursor:n-resize}.telebox-s{width:100%;height:5px;left:0;bottom:-5px;cursor:s-resize}.telebox-w{width:5px;height:100%;left:-5px;top:0;cursor:w-resize}.telebox-e{width:5px;height:100%;right:-5px;top:0;cursor:e-resize}.telebox-nw{width:15px;height:15px;top:-5px;left:-5px;cursor:nw-resize}.telebox-ne{width:15px;height:15px;top:-5px;right:-5px;cursor:ne-resize}.telebox-se{width:15px;height:15px;bottom:-5px;right:-5px;cursor:se-resize}.telebox-sw{width:15px;height:15px;bottom:-5px;left:-5px;cursor:sw-resize}.telebox-track-mask{position:fixed;top:0;left:0;z-index:2147483647;width:100%;height:100%;background:rgba(0,0,0,.0001);cursor:move}.telebox-cursor-n{cursor:n-resize}.telebox-cursor-s{cursor:s-resize}.telebox-cursor-w{cursor:w-resize}.telebox-cursor-e{cursor:e-resize}.telebox-cursor-nw{cursor:nw-resize}.telebox-cursor-ne{cursor:ne-resize}.telebox-cursor-se{cursor:se-resize}.telebox-cursor-sw{cursor:sw-resize}.telebox-maximized .telebox-resize-handles,.telebox-no-resize .telebox-resize-handles{display:none}.telebox-maximized{box-shadow:none;transition:none}.telebox-minimized{will-change:transform;transition:width 50ms cubic-bezier(.4,.9,.71,1.02),height 50ms cubic-bezier(.55,.82,.63,.95),opacity .6s cubic-bezier(.7,0,.84,0),transform .6s ease;opacity:0;pointer-events:none;user-select:none}.telebox-transforming{will-change:transform;transition:opacity .6s cubic-bezier(.7,0,.84,0)}.telebox-readonly .telebox-resize-handle{cursor:initial!important;pointer-events:none!important}.telebox-color-scheme-dark .telebox-box-main{color:#e9e9e9;background:#212126;border-color:#43434d}.telebox-titlebar{box-sizing:border-box;height:26px;display:flex;align-items:center;padding:0 16px;background:#fff;user-select:none;border-bottom:1px solid #eeeef7}.telebox-title{overflow:hidden;margin:0 24px 0 0;padding:0;font-size:14px;font-weight:400;font-family:PingFangSC-Regular,PingFang SC;white-space:nowrap;word-break:keep-all;text-overflow:ellipsis;color:#191919}.telebox-titlebar-btns{white-space:nowrap;word-break:keep-all;margin-left:auto;font-size:0}.telebox-titlebar-btn{width:22px;height:22px;padding:0;outline:0;border:none;background:0 0;cursor:pointer}.telebox-titlebar-btn~.telebox-titlebar-btn{margin-left:10px}.telebox-titlebar-btn-icon{width:22px;height:22px}.telebox-readonly .telebox-titlebar-btn{cursor:not-allowed}.telebox-titlebar-icon-minimize{background:center/cover no-repeat url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjggMjgiPgogICAgPGRlZnM+CiAgICAgICAgPHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjh2MjhIMHoiIC8+CiAgICA8L2RlZnM+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj4KICAgICAgICAgICAgPHVzZSB4bGluazpocmVmPSIjYSIgLz4KICAgICAgICA8L21hc2s+CiAgICAgICAgPHBhdGggZmlsbD0iI0E3QTdDQSIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNOSAxM2gxMHYxLjZIOXoiIG1hc2s9InVybCgjYikiIC8+CiAgICA8L2c+Cjwvc3ZnPgo=)}.telebox-titlebar-icon-maximize{background:center/cover no-repeat url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjggMjgiPgogICAgPGRlZnM+CiAgICAgICAgPHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjh2MjhIMHoiIC8+CiAgICA8L2RlZnM+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj4KICAgICAgICAgICAgPHVzZSB4bGluazpocmVmPSIjYSIgLz4KICAgICAgICA8L21hc2s+CiAgICAgICAgPGcgZmlsbD0iI0E3QTdDQSIgZmlsbC1ydWxlPSJub256ZXJvIiBtYXNrPSJ1cmwoI2IpIj4KICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik0yMC40ODEgMTcuMWgxLjJ2NC41ODFIMTcuMXYtMS4yaDMuMzgxVjE3LjF6bS0xNC4xOTA1LS4wMDloMS4ydjMuMzgxaDMuMzgwOXYxLjJoLTQuNTgxdi00LjU4MXpNMTcuMSA2LjE5MDVoNC41ODF2NC41ODA5aC0xLjJ2LTMuMzgxSDE3LjF2LTEuMnptLTEwLjcwMDguMTA4N2g0Ljc5ODV2MS4ySDcuNTk5MnYzLjU5ODVoLTEuMlY2LjI5OTJ6IiAvPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+Cg==)}.telebox-titlebar-icon-maximize.is-active{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjggMjgiPgogICAgPGRlZnM+CiAgICAgICAgPHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjZ2MjZIMHoiIC8+CiAgICAgICAgPHBhdGggaWQ9ImMiIGQ9Ik0yNi44NjkgMEwyOCAxLjEzMVYyNi44N0wyNi44NjkgMjhIMS4xM0wwIDI2Ljg3VjEuMTMxTDEuMTMgMHoiIC8+CiAgICA8L2RlZnM+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEgMSkiPgogICAgICAgICAgICA8bWFzayBpZD0iYiIgZmlsbD0iI2ZmZiI+CiAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNhIiAvPgogICAgICAgICAgICA8L21hc2s+CiAgICAgICAgICAgIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0tNC42NDI5LTQuNjQyOWgzNS4yODU4djM1LjI4NThILTQuNjQyOXoiIG1hc2s9InVybCgjYikiIC8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8bWFzayBpZD0iZCIgZmlsbD0iI2ZmZiI+CiAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNjIiAvPgogICAgICAgICAgICA8L21hc2s+CiAgICAgICAgICAgIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0tMTcuNTE2OCAxNEwxNC0xNy41MTY4IDQ1LjUxNjggMTQgMTQgNDUuNTE2OHoiIG1hc2s9InVybCgjZCkiIC8+CiAgICAgICAgPC9nPgogICAgICAgIDxwYXRoIHN0cm9rZT0iI0E3QTdDQSIgc3Ryb2tlLXdpZHRoPSIxLjIiCiAgICAgICAgICAgIGQ9Ik0xMC4wODg2IDIxLjQ4NjV2LTMuNjk2Nkg2LjM5Mk0yMS4zODU1IDEwLjE4OTVoLTMuNjk2NlY2LjQ5M00yMS40MDIgMTcuNzk4M2gtMy42OTY2djMuNjk2Nk0xMC4yNTAzIDYuMTQ5OHYzLjg5ODVINi4zNTE3IiAvPgogICAgPC9nPgo8L3N2Zz4K)}.telebox-titlebar-icon-close{background:center/cover no-repeat url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyOCAyOCI+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iI0E3QTdDQSIgc3Ryb2tlLXdpZHRoPSIxLjQiPgogICAgICAgIDxwYXRoIGQ9Ik04LjM1MyAyMC4zMzIxTDIwLjMzMiA4LjM1M00yMC4zMzIyIDIwLjMzMjFMOC4zNTMgOC4zNTMiIC8+CiAgICA8L2c+Cjwvc3ZnPgo=)}.telebox-color-scheme-dark .telebox-titlebar{color:#e9e9e9;background:#43434d;border-bottom:none}.telebox-collector{visibility:hidden;display:block;position:absolute;z-index:200;width:40px;height:40px;margin:0;padding:0;border:none;outline:0;font-size:0;border-radius:50%;background:#fff;box-shadow:0 2px 6px #2f419226;cursor:pointer;user-select:none;pointer-events:none;background-repeat:no-repeat;background-size:18px 16px;background-position:center}.telebox-collector-visible{visibility:visible;pointer-events:initial}.telebox-collector-readonly{cursor:not-allowed}.telebox-color-scheme-dark.telebox-collector{background-color:#43434d}.telebox-max-titlebar{display:none;position:absolute;top:0;left:0;z-index:50000;user-select:none}.telebox-max-titlebar-maximized{display:flex}.telebox-titles{flex:1;height:100%;margin:0 16px 0 -16px;overflow-y:hidden;overflow-x:scroll;overflow-x:overlay;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;scrollbar-width:auto}.telebox-titles::-webkit-scrollbar{height:8px;width:8px}.telebox-titles::-webkit-scrollbar-track{background-color:transparent}.telebox-titles::-webkit-scrollbar-thumb{background-color:#eeeef7cc;background-color:transparent;border-radius:4px;transition:background-color .4s}.telebox-titles:hover::-webkit-scrollbar-thumb{background-color:#eeeef7cc}.telebox-titles::-webkit-scrollbar-thumb:hover{background-color:#eeeef7}.telebox-titles::-webkit-scrollbar-thumb:active{background-color:#eeeef7}.telebox-titles::-webkit-scrollbar-thumb:vertical{min-height:50px}.telebox-titles::-webkit-scrollbar-thumb:horizontal{min-width:50px}.telebox-titles-content{height:100%;display:flex;flex-wrap:nowrap;align-items:center;padding:0}.telebox-titles-tab{overflow:hidden;max-width:182px;min-width:50px;padding:0 26px 0 16px;outline:0;font-size:13px;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;text-overflow:ellipsis;white-space:nowrap;word-break:keep-all;border:none;border-right:1px solid #e5e5f0;color:#7b88a0;background:0 0;cursor:pointer}.telebox-titles-tab~.telebox-titles-tab{margin-left:2px}.telebox-titles-tab-focus{color:#357bf6}.telebox-readonly .telebox-titles-tab{cursor:not-allowed}.telebox-color-scheme-dark{color-scheme:dark}.telebox-color-scheme-dark.telebox-titlebar{color:#e9e9e9;background:#43434d;border-bottom:none}.telebox-color-scheme-dark .telebox-titles-tab{border-right-color:#7b88a0}.telebox-color-scheme-dark .telebox-title{color:#e9e9e9}
1
+ .netless-window-manager-playground{width:100%;height:100%;position:relative;z-index:1;overflow:hidden;user-select:none}.netless-window-manager-sizer{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;overflow:hidden;display:flex}.netless-window-manager-sizer-horizontal{flex-direction:column}.netless-window-manager-sizer:before,.netless-window-manager-sizer:after{flex:1;content:"";display:block}.netless-window-manager-chess-sizer:before,.netless-window-manager-chess-sizer:after{background-image:linear-gradient(45deg,#b0b0b0 25%,transparent 25%),linear-gradient(-45deg,#b0b0b0 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#b0b0b0 75%),linear-gradient(-45deg,transparent 75%,#b0b0b0 75%);background-color:#fff;background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0px}.netless-window-manager-wrapper{position:relative;z-index:1;width:100%;height:100%;overflow:hidden}.netless-window-manager-main-view{width:100%;height:100%}.netless-window-manager-cursor-pencil-image{width:26px;height:26px}.netless-window-manager-cursor-eraser-image{width:26px;height:26px}.netless-window-manager-cursor-selector-image{width:24px;height:24px}.netless-window-manager-cursor-selector-avatar{border-radius:50%;border-style:solid;border-width:2px;border-color:#fff;margin-bottom:2px}.netless-window-manager-cursor-selector-avatar img{width:12px}.netless-window-manager-cursor-inner{border-radius:4px;display:flex;align-items:center;justify-content:center;flex-direction:row;padding-left:4px;padding-right:4px;font-size:12px}.netless-window-manager-cursor-inner-mellow{height:32px;border-radius:16px;display:flex;align-items:center;justify-content:center;flex-direction:row;padding-left:16px;padding-right:16px}.netless-window-manager-cursor-tag-name{font-size:12px;margin-left:4px;padding:2px 8px;border-radius:4px}.netless-window-manager-cursor-mid{display:flex;flex-direction:column;align-items:center;justify-content:center;position:absolute;width:26px;height:26px;z-index:2147483647;left:0;top:0;will-change:transform;transition:transform .1s;transform-origin:0 0;user-select:none}.netless-window-manager-cursor-pencil-offset{margin-left:-20px}.netless-window-manager-cursor-selector-offset{margin-left:-22px;margin-top:56px}.netless-window-manager-cursor-text-offset{margin-left:-30px;margin-top:18px}.netless-window-manager-cursor-shape-offset{display:flex;flex-direction:column;align-items:center;justify-content:center;position:absolute;width:180px;height:64px;margin-left:-30px;margin-top:12px}.netless-window-manager-cursor-name{width:100%;height:48px;display:flex;align-items:center;justify-content:center;position:absolute;top:-40px}.cursor-image-wrapper{display:flex;justify-content:center}.telebox-collector{position:absolute;right:10px;bottom:15px}.tele-fancy-scrollbar{overscroll-behavior:contain;overflow:auto;overflow-y:scroll;overflow-y:overlay;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;scrollbar-width:auto}.tele-fancy-scrollbar::-webkit-scrollbar{height:8px;width:8px}.tele-fancy-scrollbar::-webkit-scrollbar-track{background-color:transparent}.tele-fancy-scrollbar::-webkit-scrollbar-thumb{background-color:#444e601a;background-color:transparent;border-radius:4px;transition:background-color .4s}.tele-fancy-scrollbar:hover::-webkit-scrollbar-thumb{background-color:#444e601a}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:hover{background-color:#444e6033}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:active{background-color:#444e6033}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:vertical{min-height:50px}.tele-fancy-scrollbar::-webkit-scrollbar-thumb:horizontal{min-width:50px}.telebox-box{position:absolute;top:0;left:0;z-index:100;transition:width .4s cubic-bezier(.4,.9,.71,1.02),height .4s cubic-bezier(.55,.82,.63,.95),opacity .6s cubic-bezier(.7,0,.84,0),transform .4s ease}.telebox-box-main{position:relative;width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden;background:#f9f9fc;box-shadow:0 4px 10px #2f419226;border-radius:6px;border:1px solid #e3e3ec}.telebox-titlebar-wrap{flex-shrink:0;position:relative;z-index:1}.telebox-content-wrap{flex:1;width:100%;overflow:hidden;display:flex;justify-content:center;align-items:center}.telebox-content{width:100%;height:100%;position:relative}.telebox-footer-wrap{flex-shrink:0;display:flex;flex-direction:column}.telebox-footer-wrap:before{content:"";display:block;flex:1}.telebox-resize-handle{position:absolute;z-index:2147483647}.telebox-n{width:100%;height:5px;left:0;top:-5px;cursor:n-resize}.telebox-s{width:100%;height:5px;left:0;bottom:-5px;cursor:s-resize}.telebox-w{width:5px;height:100%;left:-5px;top:0;cursor:w-resize}.telebox-e{width:5px;height:100%;right:-5px;top:0;cursor:e-resize}.telebox-nw{width:15px;height:15px;top:-5px;left:-5px;cursor:nw-resize}.telebox-ne{width:15px;height:15px;top:-5px;right:-5px;cursor:ne-resize}.telebox-se{width:15px;height:15px;bottom:-5px;right:-5px;cursor:se-resize}.telebox-sw{width:15px;height:15px;bottom:-5px;left:-5px;cursor:sw-resize}.telebox-track-mask{position:fixed;top:0;left:0;z-index:2147483647;width:100%;height:100%;background:rgba(0,0,0,.0001);cursor:move}.telebox-cursor-n{cursor:n-resize}.telebox-cursor-s{cursor:s-resize}.telebox-cursor-w{cursor:w-resize}.telebox-cursor-e{cursor:e-resize}.telebox-cursor-nw{cursor:nw-resize}.telebox-cursor-ne{cursor:ne-resize}.telebox-cursor-se{cursor:se-resize}.telebox-cursor-sw{cursor:sw-resize}.telebox-maximized .telebox-resize-handles,.telebox-no-resize .telebox-resize-handles{display:none}.telebox-maximized{box-shadow:none;transition:none}.telebox-minimized{transition:width 50ms cubic-bezier(.4,.9,.71,1.02),height 50ms cubic-bezier(.55,.82,.63,.95),opacity .6s cubic-bezier(.7,0,.84,0),transform .6s ease;opacity:0;pointer-events:none;user-select:none}.telebox-transforming{will-change:transform;transition:opacity .6s cubic-bezier(.7,0,.84,0)}.telebox-readonly .telebox-resize-handle{cursor:initial!important;pointer-events:none!important}.telebox-color-scheme-dark .telebox-box-main{color:#e9e9e9;background:#212126;border-color:#43434d}.telebox-titlebar{box-sizing:border-box;height:26px;display:flex;align-items:center;padding:0 16px;background:#fff;user-select:none;border-bottom:1px solid #eeeef7;touch-action:manipulation}.telebox-title-area{overflow:hidden;position:relative;height:100%;flex:1;display:flex;align-items:center}.telebox-title{overflow:hidden;margin:0;padding:0;font-size:14px;font-weight:400;font-family:PingFangSC-Regular,PingFang SC;white-space:nowrap;word-break:keep-all;text-overflow:ellipsis;color:#191919}.telebox-drag-area{position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;z-index:10}.telebox-titlebar-btns{white-space:nowrap;word-break:keep-all;margin-left:auto;font-size:0}.telebox-titlebar-btn{width:22px;height:22px;padding:0;outline:0;border:none;background:0 0;cursor:pointer}.telebox-titlebar-btn~.telebox-titlebar-btn{margin-left:10px}.telebox-titlebar-btn-icon{width:22px;height:22px}.telebox-readonly .telebox-titlebar-btn{cursor:not-allowed}.telebox-titlebar-icon-minimize{background:center/cover no-repeat url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjggMjgiPgogICAgPGRlZnM+CiAgICAgICAgPHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjh2MjhIMHoiIC8+CiAgICA8L2RlZnM+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj4KICAgICAgICAgICAgPHVzZSB4bGluazpocmVmPSIjYSIgLz4KICAgICAgICA8L21hc2s+CiAgICAgICAgPHBhdGggZmlsbD0iI0E3QTdDQSIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNOSAxM2gxMHYxLjZIOXoiIG1hc2s9InVybCgjYikiIC8+CiAgICA8L2c+Cjwvc3ZnPgo=)}.telebox-titlebar-icon-maximize{background:center/cover no-repeat url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjggMjgiPgogICAgPGRlZnM+CiAgICAgICAgPHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjh2MjhIMHoiIC8+CiAgICA8L2RlZnM+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj4KICAgICAgICAgICAgPHVzZSB4bGluazpocmVmPSIjYSIgLz4KICAgICAgICA8L21hc2s+CiAgICAgICAgPGcgZmlsbD0iI0E3QTdDQSIgZmlsbC1ydWxlPSJub256ZXJvIiBtYXNrPSJ1cmwoI2IpIj4KICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik0yMC40ODEgMTcuMWgxLjJ2NC41ODFIMTcuMXYtMS4yaDMuMzgxVjE3LjF6bS0xNC4xOTA1LS4wMDloMS4ydjMuMzgxaDMuMzgwOXYxLjJoLTQuNTgxdi00LjU4MXpNMTcuMSA2LjE5MDVoNC41ODF2NC41ODA5aC0xLjJ2LTMuMzgxSDE3LjF2LTEuMnptLTEwLjcwMDguMTA4N2g0Ljc5ODV2MS4ySDcuNTk5MnYzLjU5ODVoLTEuMlY2LjI5OTJ6IiAvPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+Cg==)}.telebox-titlebar-icon-maximize.is-active{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjggMjgiPgogICAgPGRlZnM+CiAgICAgICAgPHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjZ2MjZIMHoiIC8+CiAgICAgICAgPHBhdGggaWQ9ImMiIGQ9Ik0yNi44NjkgMEwyOCAxLjEzMVYyNi44N0wyNi44NjkgMjhIMS4xM0wwIDI2Ljg3VjEuMTMxTDEuMTMgMHoiIC8+CiAgICA8L2RlZnM+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEgMSkiPgogICAgICAgICAgICA8bWFzayBpZD0iYiIgZmlsbD0iI2ZmZiI+CiAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNhIiAvPgogICAgICAgICAgICA8L21hc2s+CiAgICAgICAgICAgIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0tNC42NDI5LTQuNjQyOWgzNS4yODU4djM1LjI4NThILTQuNjQyOXoiIG1hc2s9InVybCgjYikiIC8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8bWFzayBpZD0iZCIgZmlsbD0iI2ZmZiI+CiAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNjIiAvPgogICAgICAgICAgICA8L21hc2s+CiAgICAgICAgICAgIDxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0tMTcuNTE2OCAxNEwxNC0xNy41MTY4IDQ1LjUxNjggMTQgMTQgNDUuNTE2OHoiIG1hc2s9InVybCgjZCkiIC8+CiAgICAgICAgPC9nPgogICAgICAgIDxwYXRoIHN0cm9rZT0iI0E3QTdDQSIgc3Ryb2tlLXdpZHRoPSIxLjIiCiAgICAgICAgICAgIGQ9Ik0xMC4wODg2IDIxLjQ4NjV2LTMuNjk2Nkg2LjM5Mk0yMS4zODU1IDEwLjE4OTVoLTMuNjk2NlY2LjQ5M00yMS40MDIgMTcuNzk4M2gtMy42OTY2djMuNjk2Nk0xMC4yNTAzIDYuMTQ5OHYzLjg5ODVINi4zNTE3IiAvPgogICAgPC9nPgo8L3N2Zz4K)}.telebox-titlebar-icon-close{background:center/cover no-repeat url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyOCAyOCI+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iI0E3QTdDQSIgc3Ryb2tlLXdpZHRoPSIxLjQiPgogICAgICAgIDxwYXRoIGQ9Ik04LjM1MyAyMC4zMzIxTDIwLjMzMiA4LjM1M00yMC4zMzIyIDIwLjMzMjFMOC4zNTMgOC4zNTMiIC8+CiAgICA8L2c+Cjwvc3ZnPgo=)}.telebox-color-scheme-dark .telebox-titlebar{color:#e9e9e9;background:#43434d;border-bottom:none}.telebox-collector{visibility:hidden;display:block;position:absolute;z-index:200;width:40px;height:40px;margin:0;padding:0;border:none;outline:0;font-size:0;border-radius:50%;background:#fff;box-shadow:0 2px 6px #2f419226;cursor:pointer;user-select:none;pointer-events:none;background-repeat:no-repeat;background-size:18px 16px;background-position:center}.telebox-collector-visible{visibility:visible;pointer-events:initial}.telebox-collector-readonly{cursor:not-allowed}.telebox-color-scheme-dark.telebox-collector{background-color:#43434d}.telebox-max-titlebar{display:none;position:absolute;top:0;left:0;z-index:50000;user-select:none}.telebox-max-titlebar .telebox-drag-area{height:100%;min-width:16px;position:static;margin:0;flex-grow:1;flex-shrink:0}.telebox-max-titlebar-maximized{display:flex}.telebox-titles{height:100%;margin:0;overflow-y:hidden;overflow-x:scroll;overflow-x:overlay;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;scrollbar-width:auto}.telebox-titles::-webkit-scrollbar{height:8px;width:8px}.telebox-titles::-webkit-scrollbar-track{background-color:transparent}.telebox-titles::-webkit-scrollbar-thumb{background-color:#eeeef7cc;background-color:transparent;border-radius:4px;transition:background-color .4s}.telebox-titles:hover::-webkit-scrollbar-thumb{background-color:#eeeef7cc}.telebox-titles::-webkit-scrollbar-thumb:hover{background-color:#eeeef7}.telebox-titles::-webkit-scrollbar-thumb:active{background-color:#eeeef7}.telebox-titles::-webkit-scrollbar-thumb:vertical{min-height:50px}.telebox-titles::-webkit-scrollbar-thumb:horizontal{min-width:50px}.telebox-titles-content{height:100%;display:flex;flex-wrap:nowrap;align-items:center;padding:0}.telebox-titles-tab{height:100%;overflow:hidden;max-width:182px;min-width:50px;padding:0 26px 0 16px;outline:0;font-size:13px;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;text-overflow:ellipsis;white-space:nowrap;word-break:keep-all;border:none;border-right:1px solid #e5e5f0;color:#7b88a0;background:0 0;cursor:pointer;user-select:none}.telebox-titles-tab-focus{color:#357bf6}.telebox-readonly .telebox-titles-tab{cursor:not-allowed}.telebox-color-scheme-dark{color-scheme:dark}.telebox-color-scheme-dark.telebox-titlebar{color:#e9e9e9;background:#43434d;border-bottom:none}.telebox-color-scheme-dark .telebox-titles-tab{border-right-color:#7b88a0}.telebox-color-scheme-dark .telebox-title{color:#e9e9e9}
package/dist/typings.d.ts CHANGED
@@ -2,7 +2,7 @@ import type Emittery from "emittery";
2
2
  import type { AnimationMode, Displayer, DisplayerState, Player, Room, SceneDefinition, SceneState, View } from "white-web-sdk";
3
3
  import type { AppContext } from "./AppContext";
4
4
  import type { ReadonlyTeleBox, TeleBoxRect } from "@netless/telebox-insider";
5
- export interface NetlessApp<Attributes = any, SetupResult = any, AppOptions = any> {
5
+ export interface NetlessApp<Attributes = any, MagixEventPayloads = any, AppOptions = any, SetupResult = any> {
6
6
  kind: string;
7
7
  config?: {
8
8
  /** Box width relative to whiteboard. 0~1. Default 0.5. */
@@ -16,7 +16,7 @@ export interface NetlessApp<Attributes = any, SetupResult = any, AppOptions = an
16
16
  /** App only single instance. */
17
17
  singleton?: boolean;
18
18
  };
19
- setup: (context: AppContext<Attributes, AppOptions>) => SetupResult;
19
+ setup: (context: AppContext<Attributes, MagixEventPayloads, AppOptions>) => SetupResult;
20
20
  }
21
21
  export declare type AppEmitterEvent<T = any> = {
22
22
  /**
@@ -70,3 +70,4 @@ export declare type AppListenerKeys = keyof AppEmitterEvent;
70
70
  export type { AppContext } from "./AppContext";
71
71
  export type { ReadonlyTeleBox, TeleBoxRect };
72
72
  export type { SceneState, SceneDefinition, View, AnimationMode, Displayer, Room, Player };
73
+ export type { Storage, StorageStateChangedEvent, StorageStateChangedListener } from "./App/Storage";
package/docs/api.md CHANGED
@@ -9,9 +9,13 @@
9
9
  - [实例方法](#instance-methods)
10
10
  - [`addApp`](#addApp)
11
11
  - [`closeApp`](#closeApp)
12
+ - [`setMainViewSceneIndex`](#setMainViewSceneIndex)
13
+ - [`setBoxState`](#setBoxState)
12
14
  - [实例属性](#prototypes)
13
15
  - [事件回调](#events)
14
16
 
17
+ <br>
18
+
15
19
  <h2 id="static-methods">静态方法</h2>
16
20
 
17
21
  <h3 id="mount">WindowManager.mount</h3>
@@ -42,6 +46,7 @@ const manager = await WindowManager.mount(
42
46
  | prefersColorScheme | [optional] string | light | auto, light, dark |
43
47
  | debug | [optional] boolean | false | 打印日志信息
44
48
 
49
+
45
50
  <h3 id="register">WindowManager.register</h3>
46
51
 
47
52
  > 注册 `APP` 到 `WindowManager`
@@ -81,6 +86,8 @@ WindowManager.setContainer(document.getElementById("container"));
81
86
  WindowManager.setCollectorContainer(document.getElementById("collector-container"));
82
87
  ```
83
88
 
89
+ <br>
90
+
84
91
  <h2 id="instance-methods">实例方法</h2>
85
92
 
86
93
  <h3 id="addApp">addApp</h3>
@@ -105,15 +112,36 @@ const appId = await manager.addApp({
105
112
  manager.closeApp(appId)
106
113
  ```
107
114
 
115
+ <h3 id="setMainViewSceneIndex">setMainViewSceneIndex</h3>
116
+
117
+ > 设置主白板的 `SceneIndex`
118
+
119
+ ```ts
120
+ manager.setMainViewSceneIndex(1)
121
+ ```
122
+
123
+ <h3 id="setBoxState">setBoxState</h3>
124
+
125
+ > 设置当前的 `boxState`
126
+
127
+ ```ts
128
+ manager.setBoxState("normal") // boxState: normal | maximized | minimized
129
+ ```
130
+
131
+ <br>
132
+
108
133
  <h2 id="prototypes">实例属性</h2>
109
134
 
110
- | name | type | default | desc |
111
- | ------------------ | ------- | ------- | ------ |
112
- | mainView | View | | 主白板 |
113
- | boxState | string | | 当前窗口状态 |
114
- | darkMode | boolean | | 黑夜模式 |
115
- | prefersColorScheme | string | | 颜色主题 |
135
+ | name | type | default | desc |
136
+ | ------------------ | ------- | ------- | ----------------- |
137
+ | mainView | View | | 主白板 |
138
+ | mainViewSceneIndex | number | | 当前主白板的 SceneIndex |
139
+ | boxState | string | | 当前窗口状态 |
140
+ | darkMode | boolean | | 黑夜模式 |
141
+ | prefersColorScheme | string | | 颜色主题 |
142
+ | focused | string | | focus 的 app |
116
143
 
144
+ <br>
117
145
 
118
146
  <h2 id="events">事件回调</h2>
119
147
 
@@ -124,7 +152,9 @@ manager.callbacks.on(events, listener)
124
152
  | name | type | default | desc |
125
153
  | ------------------------ | -------------- | ------- | -------------------------- |
126
154
  | mainViewModeChange | ViewVisionMode | | |
155
+ | mainViewSceneIndexChange | index: number | | |
127
156
  | boxStateChange | string | | normal,minimized,maximized |
128
157
  | darkModeChange | boolean | | |
129
158
  | prefersColorSchemeChange | string | | auto,light,dark |
130
159
  | cameraStateChange | CameraState | | |
160
+ | focusedChange | string, undefined | | 当前 focus 的 appId,主白板时为 undefined |
@@ -0,0 +1,9 @@
1
+ # 概念
2
+
3
+ ## 同步区域
4
+
5
+ 在不同分辨率的设备上,想要看到相同的区域和窗口,我们就需要在所有设备保持一个相同的比例。
6
+
7
+ 所以 `WindowManager` 有一个 `containerSizeRatio` 的选项来配置白板的宽高比,默认为 `9 / 16`
8
+
9
+ 如果外层给到 `WindowManager` 宽高并不是完美适配这个宽高比的话, `WindowManger` 会自动在内部算出一个适配这个比例的最大宽高,然后填充上去,这时在内部就会有一些区域不能操作
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@netless/window-manager",
3
- "version": "0.4.0-canary.2",
3
+ "version": "0.4.0-canary.20",
4
4
  "description": "",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
7
7
  "types": "dist/index.d.ts",
8
+ "repository": "netless-io/window-manager",
8
9
  "scripts": {
9
10
  "prettier": "prettier --write .",
10
11
  "build": "vite build && yarn type-gen",
@@ -18,13 +19,10 @@
18
19
  "license": "ISC",
19
20
  "peerDependencies": {
20
21
  "video.js": ">=7",
21
- "white-web-sdk": "^2.13.16"
22
+ "white-web-sdk": "^2.16.0"
22
23
  },
23
24
  "dependencies": {
24
25
  "@juggle/resize-observer": "^3.3.1",
25
- "@netless/app-docs-viewer": "0.1.26",
26
- "@netless/app-media-player": "0.1.0-beta.5",
27
- "@netless/telebox-insider": "0.2.17",
28
26
  "emittery": "^0.9.2",
29
27
  "lodash": "^4.17.21",
30
28
  "p-retry": "^4.6.1",
@@ -33,6 +31,9 @@
33
31
  "video.js": ">=7"
34
32
  },
35
33
  "devDependencies": {
34
+ "@netless/app-docs-viewer": "^0.2.5",
35
+ "@netless/app-media-player": "0.1.0-beta.5",
36
+ "@netless/telebox-insider": "0.2.21",
36
37
  "@rollup/plugin-commonjs": "^20.0.0",
37
38
  "@rollup/plugin-node-resolve": "^13.0.4",
38
39
  "@rollup/plugin-url": "^6.1.0",
@@ -57,6 +58,6 @@
57
58
  "typescript": "^4.3.5",
58
59
  "video.js": "^7.14.3",
59
60
  "vite": "^2.5.3",
60
- "white-web-sdk": "^2.15.12"
61
+ "white-web-sdk": "^2.16.1"
61
62
  }
62
63
  }
@@ -0,0 +1,68 @@
1
+ import type {
2
+ MagixEventListenerOptions as WhiteMagixListenerOptions,
3
+ Event as WhiteEvent,
4
+ EventPhase as WhiteEventPhase,
5
+ Scope as WhiteScope,
6
+ } from "white-web-sdk";
7
+
8
+ export interface MagixEventListenerOptions extends WhiteMagixListenerOptions {
9
+ /**
10
+ * Rapid emitted callbacks will be slowed down to this interval (in ms).
11
+ */
12
+ fireInterval?: number;
13
+ /**
14
+ * If `true`, sent events will reach self-listeners after committed to server.
15
+ * Otherwise the events will reach self-listeners immediately.
16
+ */
17
+ fireSelfEventAfterCommit?: boolean;
18
+ }
19
+
20
+ export interface MagixEventMessage<
21
+ TPayloads = any,
22
+ TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
23
+ > extends Omit<WhiteEvent, "scope" | "phase"> {
24
+ /** Event name */
25
+ event: TEvent;
26
+ /** Event Payload */
27
+ payload: TPayloads[TEvent];
28
+ /** Whiteboard ID of the client who dispatched the event. It will be AdminObserverId for system events. */
29
+ authorId: number;
30
+ scope: `${WhiteScope}`;
31
+ phase: `${WhiteEventPhase}`;
32
+ }
33
+
34
+ export type MagixEventTypes<TPayloads = any> = Extract<keyof TPayloads, string>;
35
+
36
+ export type MagixEventPayload<
37
+ TPayloads = any,
38
+ TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
39
+ > = TPayloads[TEvent];
40
+
41
+ export type MagixEventDispatcher<TPayloads = any> = <
42
+ TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
43
+ >(
44
+ event: TEvent,
45
+ payload: TPayloads[TEvent]
46
+ ) => void;
47
+
48
+ export type MagixEventHandler<
49
+ TPayloads = any,
50
+ TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
51
+ > = (message: MagixEventMessage<TPayloads, TEvent>) => void;
52
+
53
+ export type MagixEventListenerDisposer = () => void
54
+
55
+ export type MagixEventAddListener<TPayloads = any> = <
56
+ TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
57
+ >(
58
+ event: TEvent,
59
+ handler: MagixEventHandler<TPayloads, TEvent>,
60
+ options?: MagixEventListenerOptions | undefined
61
+ ) => MagixEventListenerDisposer;
62
+
63
+ export type MagixEventRemoveListener<TPayloads = any> = <
64
+ TEvent extends MagixEventTypes<TPayloads> = MagixEventTypes<TPayloads>
65
+ >(
66
+ event: TEvent,
67
+ handler?: MagixEventHandler<TPayloads, TEvent>
68
+ ) => void;
@@ -0,0 +1,21 @@
1
+ export type StorageEventListener<T> = (event: T) => void;
2
+
3
+ export class StorageEvent<TMessage> {
4
+ listeners = new Set<StorageEventListener<TMessage>>();
5
+
6
+ get length(): number {
7
+ return this.listeners.size;
8
+ }
9
+
10
+ dispatch(message: TMessage): void {
11
+ this.listeners.forEach(callback => callback(message));
12
+ }
13
+
14
+ addListener(listener: StorageEventListener<TMessage>): void {
15
+ this.listeners.add(listener);
16
+ }
17
+
18
+ removeListener(listener: StorageEventListener<TMessage>): void {
19
+ this.listeners.delete(listener);
20
+ }
21
+ }
@@ -0,0 +1,289 @@
1
+ import type { AkkoObjectUpdatedProperty } from "white-web-sdk";
2
+ import { get, has, mapValues, isObject, size, noop } from "lodash";
3
+ import { SideEffectManager } from "side-effect-manager";
4
+ import type { AppContext } from "../../AppContext";
5
+ import { safeListenPropsUpdated } from "../../Utils/Reactive";
6
+ import { isRef, makeRef, plainObjectKeys } from "./utils";
7
+ import type { Diff, MaybeRefValue, RefValue, StorageStateChangedEvent, StorageStateChangedListener, StorageStateChangedListenerDisposer } from "./typings";
8
+ import { StorageEvent } from "./StorageEvent";
9
+
10
+ export * from './typings';
11
+
12
+ export const STORAGE_NS = "_WM-STORAGE_";
13
+
14
+ export class Storage<TState extends Record<string, any> = any> implements Storage<TState> {
15
+ readonly id: string | null;
16
+
17
+ private readonly _context: AppContext;
18
+ private readonly _sideEffect = new SideEffectManager();
19
+ private _state: TState;
20
+ private _destroyed = false;
21
+
22
+ private _refMap = new WeakMap<any, RefValue>();
23
+
24
+ /**
25
+ * `setState` alters local state immediately before sending to server. This will cache the old value for onStateChanged diffing.
26
+ */
27
+ private _lastValue = new Map<string | number | symbol, TState[Extract<keyof TState, string>]>();
28
+
29
+ constructor(context: AppContext, id?: string, defaultState?: TState) {
30
+ if (defaultState && !isObject(defaultState)) {
31
+ throw new Error(`Default state for Storage ${id} is not an object.`);
32
+ }
33
+
34
+ this._context = context;
35
+ this.id = id || null;
36
+
37
+ this._state = {} as TState;
38
+ const rawState = this._getRawState(this._state);
39
+
40
+ if (this.id !== null && this._context.getIsWritable()) {
41
+ if (rawState === this._state || !isObject(rawState)) {
42
+ if (!get(this._context.getAttributes(), [STORAGE_NS])) {
43
+ this._context.updateAttributes([STORAGE_NS], {});
44
+ }
45
+ this._context.updateAttributes([STORAGE_NS, this.id], this._state);
46
+ }
47
+ if (defaultState) {
48
+ this.setState(defaultState);
49
+ }
50
+ }
51
+
52
+ // strip mobx
53
+ plainObjectKeys(rawState).forEach(key => {
54
+ if (this.id === null && key === STORAGE_NS) {
55
+ return;
56
+ }
57
+ try {
58
+ const rawValue = isObject(rawState[key]) ? JSON.parse(JSON.stringify(rawState[key])) : rawState[key];
59
+ if (isRef<TState[Extract<keyof TState, string>]>(rawValue)) {
60
+ this._state[key] = rawValue.v;
61
+ if (isObject(rawValue.v)) {
62
+ this._refMap.set(rawValue.v, rawValue);
63
+ }
64
+ } else {
65
+ this._state[key] = rawValue;
66
+ }
67
+ } catch (e) {
68
+ console.error(e);
69
+ }
70
+ });
71
+
72
+ this._sideEffect.addDisposer(
73
+ safeListenPropsUpdated(
74
+ () => this.id === null ? context.getAttributes() : get(context.getAttributes(), [STORAGE_NS, this.id]),
75
+ this._updateProperties.bind(this),
76
+ this.destroy.bind(this)
77
+ )
78
+ );
79
+ }
80
+
81
+ get state(): Readonly<TState> {
82
+ if (this._destroyed) {
83
+ console.warn(`Accessing state on destroyed Storage "${this.id}"`)
84
+ }
85
+ return this._state;
86
+ }
87
+
88
+ readonly onStateChanged = new StorageEvent<StorageStateChangedEvent<TState>>();
89
+
90
+ addStateChangedListener(handler: StorageStateChangedListener<TState>): StorageStateChangedListenerDisposer {
91
+ this.onStateChanged.addListener(handler);
92
+ return () => this.onStateChanged.removeListener(handler);
93
+ }
94
+
95
+ ensureState(state: Partial<TState>): void {
96
+ return this.setState(
97
+ plainObjectKeys(state).reduce((payload, key) => {
98
+ if (!has(this._state, key)) {
99
+ payload[key] = state[key];
100
+ }
101
+ return payload;
102
+ }, {} as Partial<TState>)
103
+ );
104
+ }
105
+
106
+ setState(state: Partial<TState>): void {
107
+ if (this._destroyed) {
108
+ console.error(new Error(`Cannot call setState on destroyed Storage "${this.id}".`));
109
+ return;
110
+ }
111
+
112
+ if (!this._context.getIsWritable()) {
113
+ console.error(new Error(`Cannot setState on Storage "${this.id}" without writable access`), state);
114
+ return;
115
+ }
116
+
117
+ const keys = plainObjectKeys(state);
118
+ if (keys.length > 0) {
119
+ keys.forEach(key => {
120
+ const value = state[key];
121
+ if (value === this._state[key]) {
122
+ return;
123
+ }
124
+
125
+ if (value === void 0) {
126
+ this._lastValue.set(key, this._state[key]);
127
+ delete this._state[key];
128
+ this._setRawState(key, value);
129
+ } else {
130
+ this._lastValue.set(key, this._state[key]);
131
+ this._state[key] = value as TState[Extract<keyof TState, string>];
132
+
133
+ let payload: MaybeRefValue<typeof value> = value;
134
+ if (isObject(value)) {
135
+ let refValue = this._refMap.get(value);
136
+ if (!refValue) {
137
+ refValue = makeRef(value);
138
+ this._refMap.set(value, refValue);
139
+ }
140
+ payload = refValue;
141
+ }
142
+
143
+ this._setRawState(key, payload)
144
+ }
145
+ });
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Empty storage data.
151
+ */
152
+ emptyStorage(): void {
153
+ if (size(this._state) <= 0) {
154
+ return;
155
+ }
156
+
157
+ if (this._destroyed) {
158
+ console.error(new Error(`Cannot empty destroyed Storage "${this.id}".`));
159
+ return;
160
+ }
161
+
162
+ if (!this._context.getIsWritable()) {
163
+ console.error(new Error(`Cannot empty Storage "${this.id}" without writable access.`));
164
+ return;
165
+ }
166
+
167
+ this.setState(mapValues(this._state, noop as () => undefined));
168
+ }
169
+
170
+ /**
171
+ * Delete storage index with all of its data and destroy the Storage instance.
172
+ */
173
+ deleteStorage(): void {
174
+ if (this.id === null) {
175
+ throw new Error(`Cannot delete main Storage`);
176
+ }
177
+
178
+ if (!this._context.getIsWritable()) {
179
+ console.error(new Error(`Cannot delete Storage "${this.id}" without writable access.`));
180
+ return;
181
+ }
182
+
183
+ this.destroy();
184
+
185
+ this._context.updateAttributes([STORAGE_NS, this.id], void 0);
186
+ }
187
+
188
+ get destroyed(): boolean {
189
+ return this._destroyed;
190
+ }
191
+
192
+ /**
193
+ * Destroy the Storage instance. The data will be kept.
194
+ */
195
+ destroy() {
196
+ this._destroyed = true;
197
+ this._sideEffect.flushAll();
198
+ }
199
+
200
+ private _getRawState(): TState | undefined
201
+ private _getRawState(defaultValue: TState): TState
202
+ private _getRawState(defaultValue?: TState): TState | undefined {
203
+ if (this.id === null) {
204
+ return get(this._context.getAttributes(), [], defaultValue);
205
+ } else {
206
+ return get(this._context.getAttributes(), [STORAGE_NS, this.id], defaultValue);
207
+ }
208
+ }
209
+
210
+ private _setRawState(key: string, value: any): void {
211
+ if (this.id === null) {
212
+ if (key === STORAGE_NS) {
213
+ throw new Error(`Cannot set attribute internal filed "${STORAGE_NS}"`)
214
+ }
215
+ return this._context.updateAttributes([key], value);
216
+ } else {
217
+ return this._context.updateAttributes([STORAGE_NS, this.id, key], value);
218
+ }
219
+ }
220
+
221
+ private _updateProperties(actions: ReadonlyArray<AkkoObjectUpdatedProperty<TState, string>>): void {
222
+ if (this._destroyed) {
223
+ console.error(new Error(`Cannot call _updateProperties on destroyed Storage "${this.id}".`));
224
+ return;
225
+ }
226
+
227
+ if (actions.length > 0) {
228
+ const diffs: Diff<TState> = {};
229
+
230
+ for (let i = 0; i < actions.length; i++) {
231
+ try {
232
+ const action = actions[i]
233
+ const key = action.key as Extract<keyof TState, string>;
234
+
235
+ if (this.id === null && key === STORAGE_NS) {
236
+ continue
237
+ }
238
+
239
+ const value = isObject(action.value) ? JSON.parse(JSON.stringify(action.value)) : action.value;
240
+ let oldValue: TState[Extract<keyof TState, string>] | undefined;
241
+ if (this._lastValue.has(key)) {
242
+ oldValue = this._lastValue.get(key);
243
+ this._lastValue.delete(key);
244
+ }
245
+
246
+ switch (action.kind) {
247
+ case 2: {
248
+ // Removed
249
+ if (has(this._state, key)) {
250
+ oldValue = this._state[key];
251
+ delete this._state[key];
252
+ }
253
+ diffs[key] = { oldValue };
254
+ break;
255
+ }
256
+ default: {
257
+ let newValue = value;
258
+
259
+ if (isRef<TState[Extract<keyof TState, string>]>(value)) {
260
+ const { k, v } = value;
261
+ const curValue = this._state[key];
262
+ if (isObject(curValue) && this._refMap.get(curValue)?.k === k) {
263
+ newValue = curValue;
264
+ } else {
265
+ newValue = v;
266
+ if (isObject(v)) {
267
+ this._refMap.set(v, value);
268
+ }
269
+ }
270
+ }
271
+
272
+ if (newValue !== this._state[key]) {
273
+ oldValue = this._state[key];
274
+ this._state[key] = newValue;
275
+ }
276
+
277
+ diffs[key] = { newValue, oldValue };
278
+ break;
279
+ }
280
+ }
281
+ } catch (e) {
282
+ console.error(e)
283
+ }
284
+ }
285
+
286
+ this.onStateChanged.dispatch(diffs);
287
+ }
288
+ }
289
+ }
@@ -0,0 +1,23 @@
1
+ import type { StorageEventListener } from "./StorageEvent";
2
+
3
+ export type RefValue<TValue = any> = { k: string; v: TValue; __isRef: true };
4
+
5
+ export type ExtractRawValue<TValue> = TValue extends RefValue<infer TRefValue> ? TRefValue : TValue;
6
+
7
+ export type AutoRefValue<TValue> = RefValue<ExtractRawValue<TValue>>;
8
+
9
+ export type MaybeRefValue<TValue> = TValue | AutoRefValue<TValue>;
10
+
11
+ export type DiffOne<T> = { oldValue?: T; newValue?: T };
12
+
13
+ export type Diff<T> = { [K in keyof T]?: DiffOne<T[K]> };
14
+
15
+ export type StorageOnSetStatePayload<TState = unknown> = {
16
+ [K in keyof TState]?: MaybeRefValue<TState[K]>;
17
+ };
18
+
19
+ export type StorageStateChangedEvent<TState = any> = Diff<TState>;
20
+
21
+ export type StorageStateChangedListener<TState = any> = StorageEventListener<StorageStateChangedEvent<TState>>;
22
+
23
+ export type StorageStateChangedListenerDisposer = () => void;
@@ -0,0 +1,17 @@
1
+ import { has } from "lodash";
2
+ import { genUID } from "side-effect-manager";
3
+ import type { AutoRefValue, ExtractRawValue, RefValue } from "./typings";
4
+
5
+ export const plainObjectKeys = Object.keys as <T>(o: T) => Array<Extract<keyof T, string>>;
6
+
7
+ export function isRef<TValue = unknown>(e: unknown): e is RefValue<TValue> {
8
+ return Boolean(has(e, '__isRef'));
9
+ }
10
+
11
+ export function makeRef<TValue>(v: TValue): RefValue<TValue> {
12
+ return { k: genUID(), v, __isRef: true };
13
+ }
14
+
15
+ export function makeAutoRef<TValue>(v: TValue): AutoRefValue<TValue> {
16
+ return isRef<ExtractRawValue<TValue>>(v) ? v : makeRef(v as ExtractRawValue<TValue>);
17
+ }