@aics/vole-app 3.3.0 → 3.3.1

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 (27) hide show
  1. package/LICENSE +21 -25
  2. package/es/aics-image-viewer/components/App/index.js +10 -9
  3. package/es/aics-image-viewer/components/ChannelsWidget.js +2 -2
  4. package/es/aics-image-viewer/components/ControlPanel/index.js +15 -11
  5. package/es/aics-image-viewer/components/ControlPanel/styles.css +5 -5
  6. package/es/aics-image-viewer/components/ErrorAlert/index.js +91 -64
  7. package/es/aics-image-viewer/components/ErrorAlert/styles.css +33 -4
  8. package/es/aics-image-viewer/components/StyleProvider/index.js +42 -22
  9. package/es/aics-image-viewer/components/TfEditor/index.js +8 -1
  10. package/es/aics-image-viewer/components/Toolbar/index.js +33 -12
  11. package/es/aics-image-viewer/components/Toolbar/styles.css +21 -130
  12. package/es/aics-image-viewer/components/shared/ControlPanelRow/styles.css +1 -1
  13. package/es/aics-image-viewer/components/useVolume.js +3 -0
  14. package/es/aics-image-viewer/shared/constants.js +1 -1
  15. package/es/aics-image-viewer/shared/utils/playControls.js +12 -1
  16. package/es/aics-image-viewer/shared/utils/sceneStore.js +8 -0
  17. package/es/aics-image-viewer/shared/utils/viewerChannelSettings.js +3 -4
  18. package/package.json +5 -5
  19. package/type-declarations/aics-image-viewer/components/App/types.d.ts +2 -2
  20. package/type-declarations/aics-image-viewer/components/ControlPanel/index.d.ts +4 -5
  21. package/type-declarations/aics-image-viewer/components/ErrorAlert/index.d.ts +8 -5
  22. package/type-declarations/aics-image-viewer/components/Toolbar/index.d.ts +2 -1
  23. package/type-declarations/aics-image-viewer/components/useVolume.d.ts +1 -1
  24. package/type-declarations/aics-image-viewer/shared/constants.d.ts +1 -1
  25. package/type-declarations/aics-image-viewer/shared/utils/playControls.d.ts +1 -0
  26. package/type-declarations/aics-image-viewer/shared/utils/sceneStore.d.ts +2 -1
  27. package/LICENSE.txt +0 -26
@@ -317,6 +317,7 @@ var TfEditor = function TfEditor(props) {
317
317
  colorPickerPosition = _useState8[0],
318
318
  setColorPickerPosition = _useState8[1];
319
319
  var lastColorRef = useRef(TFEDITOR_DEFAULT_COLOR);
320
+ var dropdownContainerRef = useRef(null);
320
321
  var svgRef = useRef(null); // need access to SVG element to measure mouse position
321
322
 
322
323
  var histogram = props.channelData.histogram;
@@ -765,7 +766,9 @@ var TfEditor = function TfEditor(props) {
765
766
  }
766
767
  })))), props.volumeEnabled && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h4", null, "Volume settings"), /*#__PURE__*/React.createElement("div", {
767
768
  className: "tf-editor-control-row"
768
- }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Dropdown, {
769
+ }, /*#__PURE__*/React.createElement("div", {
770
+ ref: dropdownContainerRef
771
+ }, /*#__PURE__*/React.createElement(Dropdown, {
769
772
  trigger: ["click"],
770
773
  menu: {
771
774
  items: [{
@@ -785,6 +788,10 @@ var TfEditor = function TfEditor(props) {
785
788
  var key = _ref5.key;
786
789
  return applyTFGenerator(key);
787
790
  }
791
+ },
792
+ getPopupContainer: function getPopupContainer() {
793
+ var _dropdownContainerRef;
794
+ return (_dropdownContainerRef = dropdownContainerRef.current) !== null && _dropdownContainerRef !== void 0 ? _dropdownContainerRef : document.body;
788
795
  }
789
796
  }, /*#__PURE__*/React.createElement(Button, {
790
797
  size: "small"
@@ -4,8 +4,6 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
4
4
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
7
- // TODO this really ought to be exported at the top level...
8
-
9
7
  import { ReloadOutlined } from "@ant-design/icons";
10
8
  import { Button, Radio, Select, Tooltip } from "antd";
11
9
  import { debounce } from "lodash";
@@ -35,6 +33,7 @@ var Toolbar = function Toolbar(props) {
35
33
  var leftRef = React.useRef(null);
36
34
  var rightRef = React.useRef(null);
37
35
  var centerRef = React.useRef(null);
36
+ var resizeObserver = React.useRef();
38
37
  var _React$useState = React.useState(false),
39
38
  _React$useState2 = _slicedToArray(_React$useState, 2),
40
39
  scrollMode = _React$useState2[0],
@@ -65,7 +64,7 @@ var Toolbar = function Toolbar(props) {
65
64
  return;
66
65
  }
67
66
  setScrollBtnLeft(barEl.scrollLeft > 0);
68
- setScrollBtnRight(barEl.scrollLeft < barEl.scrollWidth - barEl.clientWidth);
67
+ setScrollBtnRight(Math.ceil(barEl.scrollLeft) < barEl.scrollWidth - barEl.clientWidth);
69
68
  }, []);
70
69
 
71
70
  // This is effectively a `useCallback` - it memoizes a function - but since we're feeding the whole function into
@@ -93,15 +92,30 @@ var Toolbar = function Toolbar(props) {
93
92
  return leftRect.right > centerRect.left || centerRect.right > rightRect.left;
94
93
  }
95
94
  });
96
- checkScrollBtnVisible();
95
+ window.setTimeout(checkScrollBtnVisible, 0);
97
96
  }, RESIZE_DEBOUNCE_DELAY);
98
97
  }, [checkScrollBtnVisible]);
99
98
  React.useEffect(function () {
100
99
  // Make sure `checkSize` is run once on mount
101
100
  checkSize();
101
+ if (resizeObserver.current !== undefined) {
102
+ resizeObserver.current.disconnect();
103
+ }
104
+ resizeObserver.current = new ResizeObserver(checkSize);
105
+ if (leftRef.current !== null) {
106
+ resizeObserver.current.observe(leftRef.current);
107
+ }
108
+ if (centerRef.current !== null) {
109
+ resizeObserver.current.observe(centerRef.current);
110
+ }
111
+ if (rightRef.current !== null) {
112
+ resizeObserver.current.observe(rightRef.current);
113
+ }
102
114
  window.addEventListener("resize", checkSize);
103
115
  return function () {
104
- return window.removeEventListener("resize", checkSize);
116
+ var _resizeObserver$curre;
117
+ (_resizeObserver$curre = resizeObserver.current) === null || _resizeObserver$curre === void 0 || _resizeObserver$curre.disconnect();
118
+ window.removeEventListener("resize", checkSize);
105
119
  };
106
120
  }, [checkSize]);
107
121
  var scrollX = function scrollX(amount) {
@@ -126,7 +140,8 @@ var Toolbar = function Toolbar(props) {
126
140
  var classForToggleBtn = function classForToggleBtn(active) {
127
141
  return "ant-btn-icon-only btn-borderless" + (active ? " btn-active" : "");
128
142
  };
129
- var visibleControls = props.visibleControls;
143
+ var visibleControls = props.visibleControls,
144
+ multiscaleDims = props.multiscaleDims;
130
145
  var twoDMode = viewMode !== ViewMode.threeD;
131
146
  var renderGroup1 = visibleControls.viewModeRadioButtons || visibleControls.resetCameraButton || visibleControls.autoRotateButton;
132
147
  var renderGroup4 = visibleControls.showAxesButton || visibleControls.showBoundingBoxButton;
@@ -214,8 +229,9 @@ var Toolbar = function Toolbar(props) {
214
229
  }, "Full field"))), /*#__PURE__*/React.createElement("div", {
215
230
  className: "viewer-toolbar-group"
216
231
  }, /*#__PURE__*/React.createElement(Select, {
217
- className: "select-render-setting",
218
- popupClassName: "viewer-toolbar-dropdown",
232
+ style: {
233
+ minWidth: 120
234
+ },
219
235
  value: renderMode,
220
236
  onChange: function onChange(value) {
221
237
  return changeViewerSetting("renderMode", value);
@@ -249,9 +265,13 @@ var Toolbar = function Toolbar(props) {
249
265
  onClick: toggleBoundingBox
250
266
  }, /*#__PURE__*/React.createElement(ViewerIcon, {
251
267
  type: "boundingBox"
252
- })))), props.multiscaleDims !== undefined && props.multiscaleDims.length > 1 && /*#__PURE__*/React.createElement("div", {
268
+ })))), multiscaleDims !== undefined && multiscaleDims.length > 1 && visibleControls.scaleLevelControls && /*#__PURE__*/React.createElement("div", {
253
269
  className: "viewer-toolbar-group"
254
- }, /*#__PURE__*/React.createElement("span", null, "Resolution"), /*#__PURE__*/React.createElement(Radio.Group, {
270
+ }, /*#__PURE__*/React.createElement("span", {
271
+ style: {
272
+ color: "var(--color-button-tertiary-text)"
273
+ }
274
+ }, "Resolution"), /*#__PURE__*/React.createElement(Radio.Group, {
255
275
  value: useExactScaleLevel,
256
276
  onChange: function onChange(e) {
257
277
  return changeViewerSetting("useExactScaleLevel", e.target.value);
@@ -261,7 +281,8 @@ var Toolbar = function Toolbar(props) {
261
281
  }, "Auto"), /*#__PURE__*/React.createElement(Radio.Button, {
262
282
  value: true
263
283
  }, "Manual")), /*#__PURE__*/React.createElement(Select, {
264
- className: "select-render-setting",
284
+ className: "select-resolution",
285
+ getPopupContainer: getPopupContainer,
265
286
  style: {
266
287
  minWidth: 150
267
288
  },
@@ -270,7 +291,7 @@ var Toolbar = function Toolbar(props) {
270
291
  return changeViewerSetting("scaleLevelIndex", value);
271
292
  },
272
293
  disabled: !useExactScaleLevel
273
- }, props.multiscaleDims.map(function (dims, idx) {
294
+ }, multiscaleDims.map(function (dims, idx) {
274
295
  var _dims$shape = _slicedToArray(dims.shape, 5),
275
296
  _t = _dims$shape[0],
276
297
  _c = _dims$shape[1],
@@ -108,155 +108,46 @@
108
108
  margin-right:6px;
109
109
  }
110
110
 
111
+ .viewer-toolbar .btn-borderless,
112
+ .viewer-toolbar .ant-select.ant-select-outlined .ant-select-selector,
113
+ .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-checked){
114
+ background-color:var(--color-toolbar-button-bg);
115
+ }
116
+
111
117
  .viewer-toolbar .ant-btn-icon-only{
112
118
  padding-bottom:4px;
113
119
  padding-left:1px;
114
120
  }
115
121
 
116
122
  .viewer-toolbar .btn-borderless{
117
- background-color:var(--color-toolbar-button-bg);
118
123
  border-color:transparent;
119
124
  color:var(--color-text-body);
120
125
  }
121
126
 
122
127
  .viewer-toolbar .btn-active{
123
- color:var(--color-button-icon-activated-text);
124
- background-color:var(--color-button-icon-activated-bg);
125
- border:1px solid var(--color-button-icon-activated-outline);
128
+ color:var(--color-button-icon-active-text);
129
+ background-color:var(--color-button-icon-active-bg);
130
+ border:1px solid var(--color-button-icon-active-outline);
126
131
  }
127
132
 
128
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled){
129
- background-color:black;
130
- box-shadow:-1px 0px 0px 0px transparent;
131
- transition-property:color, background-color, border-color, box-shadow;
132
- }
133
-
134
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled).ant-radio-button-wrapper-checked,
135
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):hover{
136
- outline:none;
137
- color:white;
138
- border-color:#aeacae;
139
- }
140
-
141
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled).ant-radio-button-wrapper-checked:not(:first-child), .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):hover:not(:first-child){
142
- box-shadow:-1px 0px 0px 0px #aeacae;
143
- }
144
-
145
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled).ant-radio-button-wrapper-checked{
146
- background-color:#4b4b4b;
147
- }
148
-
149
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):hover{
150
- background-color:#aa88ed;
151
- color:white;
152
- }
133
+ .viewer-toolbar{
134
+ --color-button-radio-hover-outline:var(--color-button-tertiary-active-outline);
135
+ --color-button-radio-hover-text:var(--color-button-icon-active-text);
136
+ }
153
137
 
154
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):focus-within{
155
- outline:none;
156
- }
138
+ .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):hover,
139
+ .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):has(:focus-visible){
140
+ background-color:var(--color-button-tertiary-hover-text);
141
+ }
157
142
 
158
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled)::before{
159
- content:none;
143
+ .viewer-toolbar .ant-select.select-resolution > .ant-select-selector{
144
+ text-align:left;
160
145
  }
161
146
 
162
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting{
163
- min-width:120px;
164
- }
165
-
166
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting .ant-select-selection-item,
167
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting .ant-select-arrow{
147
+ .viewer-toolbar .ant-select.select-resolution .ant-select-selection-item{
168
148
  color:var(--color-button-tertiary-text);
169
- transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
170
149
  }
171
150
 
172
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-item,
173
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-search{
174
- padding-inline-end:0;
175
- }
176
-
177
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-item{
178
- color:var(--color-button-icon-disabled-text);
179
- }
180
-
181
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-item:hover{
182
- color:var(--color-button-icon-disabled-text);
183
- }
184
-
185
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selector{
186
- border-color:var(--color-button-icon-disabled-text);
187
- }
188
-
189
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-arrow{
151
+ .viewer-toolbar .ant-select.select-resolution.ant-select-disabled .ant-select-arrow{
190
152
  display:none;
191
153
  }
192
-
193
- .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting .ant-select-selector{
194
- border-color:#aeacae;
195
- box-shadow:none;
196
- background-color:var(--color-toolbar-button-bg);
197
- }
198
-
199
- .viewer-toolbar .ant-select.select-render-setting:hover:not(:disabled) .ant-select-selector,
200
- .viewer-toolbar .ant-select.select-render-setting:hover:not(:disabled) .ant-select-arrow,
201
- .viewer-toolbar .ant-select.select-render-setting:hover:not(:disabled) .ant-select-selection-item,
202
- .viewer-toolbar .ant-select.select-render-setting:focus-visible:not(:disabled) .ant-select-selector,
203
- .viewer-toolbar .ant-select.select-render-setting:focus-visible:not(:disabled) .ant-select-arrow,
204
- .viewer-toolbar .ant-select.select-render-setting:focus-visible:not(:disabled) .ant-select-selection-item,
205
- .viewer-toolbar.viewer-toolbar.viewer-toolbar .ant-select-open .ant-select-selector,
206
- .viewer-toolbar.viewer-toolbar.viewer-toolbar .ant-select-open .ant-select-arrow,
207
- .viewer-toolbar.viewer-toolbar.viewer-toolbar .ant-select-open .ant-select-selection-item{
208
- color:var(--color-button-tertiary-hover-text);
209
- outline-color:var(--color-button-tertiary-hover-outline);
210
- }
211
-
212
- .viewer-toolbar-dropdown .ant-select-dropdown-menu-item-active{
213
- transition-property:background-color, color;
214
- }
215
-
216
- .viewer-toolbar-dropdown .ant-select-dropdown-menu-item-disabled,
217
- .viewer-toolbar-dropdown .ant-select-dropdown-menu-item-disabled:hover{
218
- color:#6e6e6e;
219
- }
220
-
221
- .viewer-toolbar-dropdown .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled){
222
- background-color:transparent;
223
- color:white;
224
- }
225
-
226
- .viewer-toolbar-dropdown .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled).ant-select-dropdown-menu-item-selected{
227
- background-color:#4b4b4b;
228
- }
229
-
230
- .viewer-toolbar-dropdown .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled){
231
- background-color:#f6f0ff;
232
- color:#4b4b4b;
233
- }
234
-
235
- @supports selector(:has(:focus-visible)){
236
- .viewer-toolbar .ant-select-selection:focus-visible,
237
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):has(:focus-visible),
238
- .viewer-toolbar .btn-borderless:focus-visible{
239
- background-color:#aa88ed !important;
240
- color:white !important;
241
- }
242
-
243
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):has(:focus-visible){
244
- outline:none;
245
- border-color:#aeacae;
246
- }
247
-
248
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):has(:focus-visible):not(:first-child){
249
- box-shadow:-1px 0px 0px 0px #aeacae;
250
- }
251
- }
252
-
253
- @supports not (selector(:has(:focus-visible))){
254
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):focus-within{
255
- outline:none;
256
- border-color:#aeacae;
257
- }
258
-
259
- .viewer-toolbar .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-disabled):focus-within:not(:first-child){
260
- box-shadow:-1px 0px 0px 0px #aeacae;
261
- }
262
- }
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  .control-panel-col .control-panel-row .ant-btn-text{
28
- color:var(--color-button-icon-activated-text);
28
+ color:var(--color-button-icon-active-text);
29
29
  transition:color 0.3s;
30
30
  }
31
31
 
@@ -110,6 +110,9 @@ var useVolume = function useVolume(scenePaths, options) {
110
110
  setPlayingAxis(axis);
111
111
  // prioritize prefetching along the playing axis
112
112
  sceneLoader.setPrefetchPriority(axis ? [AXIS_TO_LOADER_PRIORITY[axis]] : []);
113
+ sceneLoader.updateFetchOptions({
114
+ onlyPriorityDirections: isPlaying
115
+ });
113
116
  // sync multichannel loading so we don't show loaded channels one at a time
114
117
  sceneLoader.syncMultichannelLoading(isPlaying);
115
118
  if (image) {
@@ -31,7 +31,7 @@ export var TFEDITOR_DEFAULT_COLOR = [255, 255, 255];
31
31
  export var TFEDITOR_MAX_BIN = 255;
32
32
  export var CACHE_MAX_SIZE = 1000000000;
33
33
  export var QUEUE_MAX_SIZE = 10;
34
- export var QUEUE_MAX_LOW_PRIORITY_SIZE = 4;
34
+ export var QUEUE_MAX_LOW_PRIORITY_SIZE = 6;
35
35
 
36
36
  /** Maps an axis the user can "play" through to the direction to prefetch while playing that axis */
37
37
  export var AXIS_TO_LOADER_PRIORITY = {
@@ -13,6 +13,7 @@ var PlayControls = /*#__PURE__*/function () {
13
13
  _defineProperty(this, "playWaitingForLoad", false);
14
14
  _defineProperty(this, "playHolding", false);
15
15
  _defineProperty(this, "playTimeoutId", 0);
16
+ _defineProperty(this, "lastStepTime", 0);
16
17
  }
17
18
  return _createClass(PlayControls, [{
18
19
  key: "setPlayingAxis",
@@ -28,11 +29,18 @@ var PlayControls = /*#__PURE__*/function () {
28
29
  if (!this.playingAxis || this.playHolding || !this.stepAxis) {
29
30
  return;
30
31
  }
31
- // If the volume is not loaded, wait for it to load before continuing
32
32
  if (!((_this$getVolumeIsLoad = this.getVolumeIsLoaded) !== null && _this$getVolumeIsLoad !== void 0 && _this$getVolumeIsLoad.call(this))) {
33
33
  this.playWaitingForLoad = true;
34
34
  return;
35
35
  }
36
+
37
+ // Enforce minimum interval between frame *presentations* (not requests).
38
+ // lastStepTime is set in onImageLoaded when a frame's data arrives.
39
+ var delay = PLAY_STEP_INTERVAL_MS - (performance.now() - this.lastStepTime);
40
+ if (delay > 0) {
41
+ this.playTimeoutId = window.setTimeout(this.playStep.bind(this), delay);
42
+ return;
43
+ }
36
44
  this.stepAxis(this.playingAxis);
37
45
  this.playTimeoutId = window.setTimeout(this.playStep.bind(this), PLAY_STEP_INTERVAL_MS);
38
46
  }
@@ -41,6 +49,9 @@ var PlayControls = /*#__PURE__*/function () {
41
49
  }, {
42
50
  key: "onImageLoaded",
43
51
  value: function onImageLoaded() {
52
+ // Record when the frame was presented, so scheduleNextStep enforces
53
+ // PLAY_STEP_INTERVAL_MS between presentations (not between requests).
54
+ this.lastStepTime = performance.now();
44
55
  if (this.playWaitingForLoad) {
45
56
  this.playWaitingForLoad = false;
46
57
  this.playStep();
@@ -158,6 +158,14 @@ var SceneStore = /*#__PURE__*/function () {
158
158
  currentLoader.setPrefetchPriority(priority);
159
159
  }
160
160
  }
161
+ }, {
162
+ key: "updateFetchOptions",
163
+ value: function updateFetchOptions(fetchOptions) {
164
+ var currentLoader = this.loaders[this.currentScene];
165
+ if (currentLoader) {
166
+ currentLoader.updateFetchOptions(fetchOptions);
167
+ }
168
+ }
161
169
  }]);
162
170
  }();
163
171
  export { SceneStore as default };
@@ -87,12 +87,11 @@ export function findFirstChannelMatch(channel, channelIndex, settings) {
87
87
  }
88
88
  export function getDisplayName(name, index, settings) {
89
89
  if (settings) {
90
+ var _c$name;
90
91
  var c = findFirstChannelMatch(name, index, settings);
91
- if (c) {
92
- return c.name || name;
93
- }
92
+ return (_c$name = c === null || c === void 0 ? void 0 : c.name) !== null && _c$name !== void 0 ? _c$name : name;
94
93
  }
95
- return name;
94
+ return name !== null && name !== void 0 ? name : index.toString();
96
95
  }
97
96
  export function makeChannelIndexGrouping(channels, settings) {
98
97
  if (!channels) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aics/vole-app",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "web view of cell volume images",
5
5
  "main": "es/index.js",
6
6
  "module": "es/index.js",
@@ -25,6 +25,7 @@
25
25
  "gh-build-nightly": "webpack --config webpack.dev.js --env env=production basename=/vole-app/",
26
26
  "gh-build-release": "webpack --config webpack.dev.js --env env=production basename=/website-3d-cell-viewer-release/",
27
27
  "s3-build": "webpack --config webpack.dev.js --env env=production",
28
+ "release-build": "webpack --config webpack.dev.js --env env=production",
28
29
  "docker-build": "webpack --config webpack.common.js --env env=production",
29
30
  "dev": "webpack --config webpack.dev.js --env env=dev",
30
31
  "lint": "eslint --config ./.eslintrc --ignore-path ./.eslintignore --ext .jsx --ext .js --ext .ts --ext .tsx ./",
@@ -43,9 +44,9 @@
43
44
  "checks": "npm run lint && npm run typeCheck && npm run test"
44
45
  },
45
46
  "author": "Megan Riel-Mehan",
46
- "license": "ISC",
47
+ "license": "BSD-3-Clause",
47
48
  "dependencies": {
48
- "@aics/vole-core": "^4.8.2",
49
+ "@aics/vole-core": "^4.9.1",
49
50
  "@ant-design/icons": "^5.2.5",
50
51
  "@fortawesome/fontawesome-svg-core": "^6.5.2",
51
52
  "@fortawesome/free-solid-svg-icons": "^6.5.2",
@@ -65,8 +66,7 @@
65
66
  "peerDependencies": {
66
67
  "antd": "^5.16.2",
67
68
  "react": "18.x",
68
- "react-dom": "18.x",
69
- "three": "^0.171.0"
69
+ "react-dom": "18.x"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@babel/cli": "^7.14.5",
@@ -5,7 +5,7 @@ import type { ViewerChannelSettings } from "../../shared/utils/viewerChannelSett
5
5
  import type { ViewerState } from "../../state/types";
6
6
  /** `typeof useEffect`, but the effect handler takes a `Volume` as an argument */
7
7
  export type UseImageEffectType = (effect: (image: Volume) => void | (() => void), deps: React.DependencyList) => void;
8
- type ControlNames = "alphaMaskSlider" | "autoRotateButton" | "axisClipSliders" | "brightnessSlider" | "backgroundColorPicker" | "boundingBoxColorPicker" | "colorPresetsDropdown" | "densitySlider" | "levelsSliders" | "interpolationControl" | "saveSurfaceButtons" | "fovCellSwitchControls" | "viewModeRadioButtons" | "resetCameraButton" | "showAxesButton" | "showBoundingBoxButton" | "metadataViewer";
8
+ type ControlNames = "alphaMaskSlider" | "autoRotateButton" | "axisClipSliders" | "brightnessSlider" | "backgroundColorPicker" | "boundingBoxColorPicker" | "colorPresetsDropdown" | "densitySlider" | "levelsSliders" | "interpolationControl" | "saveSurfaceButtons" | "fovCellSwitchControls" | "viewModeRadioButtons" | "resetCameraButton" | "showAxesButton" | "showBoundingBoxButton" | "metadataViewer" | "scaleLevelControls";
9
9
  /** Show/hide different elements of the UI */
10
10
  export type ControlVisibilityFlags = {
11
11
  [K in ControlNames]: boolean;
@@ -41,7 +41,7 @@ export interface AppProps {
41
41
  view3dRef?: MutableRefObject<View3d | null>;
42
42
  metadataFormatter?: (metadata: MetadataRecord) => MetadataRecord;
43
43
  onControlPanelToggle?: (collapsed: boolean) => void;
44
- showError?: (error: any) => void;
44
+ showError?: (error: unknown, image?: Volume) => void;
45
45
  onImageTitleChange?: (title: string | undefined) => void;
46
46
  }
47
47
  export {};
@@ -1,11 +1,10 @@
1
1
  import React from "react";
2
2
  import type { MetadataRecord } from "../../shared/types";
3
- import ChannelsWidget from "../ChannelsWidget";
4
- import CustomizeWidget, { type CustomizeWidgetProps } from "../CustomizeWidget";
5
- import GlobalVolumeControls, { type GlobalVolumeControlsProps } from "../GlobalVolumeControls";
3
+ import { type ChannelsWidgetProps } from "../ChannelsWidget";
4
+ import { type CustomizeWidgetProps } from "../CustomizeWidget";
5
+ import { type GlobalVolumeControlsProps } from "../GlobalVolumeControls";
6
6
  import "./styles.css";
7
- type PropsOf<T> = T extends React.ComponentType<infer P> ? P : never;
8
- interface ControlPanelProps extends PropsOf<typeof ChannelsWidget>, PropsOf<typeof GlobalVolumeControls>, PropsOf<typeof CustomizeWidget> {
7
+ interface ControlPanelProps extends ChannelsWidgetProps, GlobalVolumeControlsProps, CustomizeWidgetProps {
9
8
  hasImage: boolean;
10
9
  visibleControls: GlobalVolumeControlsProps["visibleControls"] & CustomizeWidgetProps["visibleControls"] & {
11
10
  colorPresetsDropdown: boolean;
@@ -1,16 +1,19 @@
1
+ import { type Volume } from "@aics/vole-core";
1
2
  import React from "react";
2
3
  import "./styles.css";
3
4
  export type ErrorAlertDescription = {
4
5
  title: string;
5
6
  description: React.ReactNode;
6
7
  };
8
+ type ErrorInfo = {
9
+ error: unknown;
10
+ count: number;
11
+ dims?: [number, number, number, number, number];
12
+ };
7
13
  export type ErrorAlertProps = {
8
- errors: unknown;
9
- /** The number of times we've seen an error of the type that is currently being displayed before */
10
- firstErrorCount?: number;
14
+ errors: ErrorInfo[];
11
15
  afterClose?: () => void;
12
- onSkipError?: () => void;
13
16
  };
14
17
  declare const ErrorAlert: React.FC<ErrorAlertProps>;
15
- export declare const useErrorAlert: () => [React.ReactNode, (error: unknown) => void];
18
+ export declare const useErrorAlert: () => [React.ReactNode, (error: unknown, image?: Volume) => void];
16
19
  export default ErrorAlert;
@@ -1,4 +1,4 @@
1
- import type { VolumeDims } from "@aics/vole-core/es/types/VolumeDims";
1
+ import type { VolumeDims } from "@aics/vole-core";
2
2
  import React from "react";
3
3
  import "./styles.css";
4
4
  type ToolbarProps = {
@@ -19,6 +19,7 @@ type ToolbarProps = {
19
19
  resetCameraButton: boolean;
20
20
  showAxesButton: boolean;
21
21
  showBoundingBoxButton: boolean;
22
+ scaleLevelControls: boolean;
22
23
  };
23
24
  };
24
25
  declare const Toolbar: React.FC<ToolbarProps>;
@@ -11,7 +11,7 @@ export type UseVolumeOptions = {
11
11
  /** Callback for when a single channel of the volume has loaded. */
12
12
  onChannelLoaded?: (image: Volume, channelIndex: number, isInitialLoad: boolean) => void;
13
13
  /** Callback for when image loading encounters an error. */
14
- onError?: (error: unknown) => void;
14
+ onError?: (error: unknown, image?: Volume) => void;
15
15
  /** The name of a channel which should be treated as a mask rather than as viewable data. */
16
16
  maskChannelName?: string;
17
17
  };
@@ -10,7 +10,7 @@ export declare const TFEDITOR_DEFAULT_COLOR: ColorArray;
10
10
  export declare const TFEDITOR_MAX_BIN = 255;
11
11
  export declare const CACHE_MAX_SIZE = 1000000000;
12
12
  export declare const QUEUE_MAX_SIZE = 10;
13
- export declare const QUEUE_MAX_LOW_PRIORITY_SIZE = 4;
13
+ export declare const QUEUE_MAX_LOW_PRIORITY_SIZE = 6;
14
14
  /** Maps an axis the user can "play" through to the direction to prefetch while playing that axis */
15
15
  export declare const AXIS_TO_LOADER_PRIORITY: Record<AxisName | "t", PrefetchDirection>;
16
16
  /** Maps channel data types to their minimum and maximum values */
@@ -5,6 +5,7 @@ export default class PlayControls {
5
5
  playWaitingForLoad: boolean;
6
6
  playHolding: boolean;
7
7
  playTimeoutId: number;
8
+ private lastStepTime;
8
9
  getVolumeIsLoaded?: () => boolean;
9
10
  stepAxis?: (axis: PlayAxisName) => void;
10
11
  onPlayingAxisChanged?: (axis: PlayAxisName | null) => void;
@@ -1,4 +1,4 @@
1
- import type { LoadSpec, PrefetchDirection, RawArrayLoaderOptions, Volume, VolumeLoaderContext } from "@aics/vole-core";
1
+ import type { LoadSpec, PrefetchDirection, RawArrayLoaderOptions, Volume, VolumeLoaderContext, ZarrLoaderFetchOptions } from "@aics/vole-core";
2
2
  import type { ThreadableVolumeLoader } from "@aics/vole-core/es/types/loaders/IVolumeLoader";
3
3
  export type LoadSceneOptions = {
4
4
  onCreateScene?: (volume: Volume, sceneIndex: number, loadSpec: LoadSpec) => void;
@@ -18,4 +18,5 @@ export default class SceneStore {
18
18
  createVolume(scene: number, loadSpec: LoadSpec, onChannelLoaded?: (volume: Volume, channelIndex: number) => void): Promise<Volume>;
19
19
  syncMultichannelLoading(sync: boolean): void;
20
20
  setPrefetchPriority(priority: PrefetchDirection[]): void;
21
+ updateFetchOptions(fetchOptions: Partial<ZarrLoaderFetchOptions>): void;
21
22
  }
package/LICENSE.txt DELETED
@@ -1,26 +0,0 @@
1
- Allen Institute Software License – This software license is the 2-clause BSD license
2
- plus clause a third clause that prohibits redistribution for commercial purposes without further permission.
3
-
4
- Copyright © 2017. Allen Institute. All rights reserved.
5
-
6
- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7
- following conditions are met:
8
-
9
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
10
- following disclaimer.
11
-
12
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13
- following disclaimer in the documentation and/or other materials provided with the distribution.
14
-
15
- 3. Redistributions for commercial purposes are not permitted without the Allen Institute’s written permission.
16
- For purposes of this license, commercial purposes is the incorporation of the Allen Institute's software into
17
- anything for which you will charge fees or other compensation. Contact terms@alleninstitute.org for commercial
18
- licensing opportunities.
19
-
20
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26
- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.