@aics/vole-app 3.2.3 → 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 (36) hide show
  1. package/LICENSE +21 -25
  2. package/es/aics-image-viewer/components/App/index.js +18 -12
  3. package/es/aics-image-viewer/components/AxisClipSliders/index.js +2 -1
  4. package/es/aics-image-viewer/components/ChannelsWidget.js +2 -2
  5. package/es/aics-image-viewer/components/ControlPanel/index.js +15 -11
  6. package/es/aics-image-viewer/components/ControlPanel/styles.css +5 -5
  7. package/es/aics-image-viewer/components/ErrorAlert/index.js +102 -65
  8. package/es/aics-image-viewer/components/ErrorAlert/styles.css +40 -7
  9. package/es/aics-image-viewer/components/StyleProvider/index.js +65 -27
  10. package/es/aics-image-viewer/components/TfEditor/index.js +8 -1
  11. package/es/aics-image-viewer/components/Toolbar/index.js +65 -7
  12. package/es/aics-image-viewer/components/Toolbar/styles.css +22 -110
  13. package/es/aics-image-viewer/components/shared/ControlPanelRow/styles.css +1 -1
  14. package/es/aics-image-viewer/components/useVolume.js +45 -40
  15. package/es/aics-image-viewer/shared/constants.js +3 -1
  16. package/es/aics-image-viewer/shared/utils/playControls.js +12 -1
  17. package/es/aics-image-viewer/shared/utils/sceneStore.js +26 -7
  18. package/es/aics-image-viewer/shared/utils/test/urlParsing.test.js +10 -2
  19. package/es/aics-image-viewer/shared/utils/urlParsing.js +17 -3
  20. package/es/aics-image-viewer/shared/utils/viewerChannelSettings.js +3 -4
  21. package/es/aics-image-viewer/state/store.js +24 -6
  22. package/es/aics-image-viewer/state/subscribers.js +22 -1
  23. package/es/aics-image-viewer/state/test/reset.test.js +3 -1
  24. package/package.json +5 -5
  25. package/type-declarations/aics-image-viewer/components/App/types.d.ts +2 -2
  26. package/type-declarations/aics-image-viewer/components/ControlPanel/index.d.ts +4 -5
  27. package/type-declarations/aics-image-viewer/components/ErrorAlert/index.d.ts +8 -5
  28. package/type-declarations/aics-image-viewer/components/Toolbar/index.d.ts +4 -0
  29. package/type-declarations/aics-image-viewer/components/useVolume.d.ts +3 -3
  30. package/type-declarations/aics-image-viewer/shared/constants.d.ts +1 -1
  31. package/type-declarations/aics-image-viewer/shared/utils/playControls.d.ts +1 -0
  32. package/type-declarations/aics-image-viewer/shared/utils/sceneStore.d.ts +3 -2
  33. package/type-declarations/aics-image-viewer/shared/utils/urlParsing.d.ts +7 -1
  34. package/type-declarations/aics-image-viewer/state/store.d.ts +2 -0
  35. package/type-declarations/aics-image-viewer/state/types.d.ts +2 -0
  36. package/LICENSE.txt +0 -26
package/LICENSE CHANGED
@@ -1,33 +1,29 @@
1
- Allen Institute Software License – This software license is the 2-clause BSD
2
- license plus clause a third clause that prohibits redistribution and use for
3
- commercial purposes without further permission.
1
+ BSD 3-Clause License
4
2
 
5
- Copyright © 2020. Allen Institute. All rights reserved.
3
+ Copyright (c) 2017-2026, Allen Institute
4
+ All rights reserved.
6
5
 
7
- Redistribution and use in source and binary forms, with or without
6
+ Redistribution and use in source and binary forms, with or without
8
7
  modification, are permitted provided that the following conditions are met:
9
8
 
10
- 1. Redistributions of source code must retain the above copyright notice, this
11
- list of conditions and the following disclaimer.
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
12
11
 
13
- 2. Redistributions in binary form must reproduce the above copyright notice,
14
- this list of conditions and the following disclaimer in the documentation
15
- and/or other materials provided with the distribution.
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
16
15
 
17
- 3. Redistributions and use for commercial purposes are not permitted without
18
- the Allen Institute’s written permission. For purposes of this license,
19
- commercial purposes are the incorporation of the Allen Institute's software
20
- into anything for which you will charge fees or other compensation or use of
21
- the software to perform a commercial service for a third party. Contact
22
- terms@alleninstitute.org for commercial licensing opportunities.
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
23
19
 
24
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
29
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -54,7 +54,8 @@ var defaultVisibleControls = {
54
54
  resetCameraButton: true,
55
55
  showAxesButton: true,
56
56
  showBoundingBoxButton: true,
57
- metadataViewer: true
57
+ metadataViewer: true,
58
+ scaleLevelControls: true
58
59
  };
59
60
  var defaultProps = {
60
61
  // rawData has a "dtype" which is expected to be "uint8", a "shape":[c,z,y,x] and a "buffer" which is a DataView
@@ -113,6 +114,7 @@ var App = function App(props) {
113
114
  var time = useViewerState(select("time"));
114
115
  var showAxes = useViewerState(select("showAxes"));
115
116
  var channelSettings = useViewerState(select("channelSettings"));
117
+ var changeViewerSetting = useViewerState(select("changeViewerSetting"));
116
118
  var changeChannelSetting = useViewerState(select("changeChannelSetting"));
117
119
  var applyColorPresets = useViewerState(select("applyColorPresets"));
118
120
  var resetToSavedState = useViewerState(select("resetToSavedViewerState"));
@@ -150,8 +152,8 @@ var App = function App(props) {
150
152
  var showError = (_props$showError = props.showError) !== null && _props$showError !== void 0 ? _props$showError : _showError;
151
153
  useEffect(function () {
152
154
  // Get notifications of loading errors which occur after the initial load, e.g. on time change or new channel load
153
- view3d.setLoadErrorHandler(function (_vol, e) {
154
- return showError(e);
155
+ view3d.setLoadErrorHandler(function (vol, e) {
156
+ return showError(e, vol);
155
157
  });
156
158
  return function () {
157
159
  return view3d.setLoadErrorHandler(undefined);
@@ -264,10 +266,12 @@ var App = function App(props) {
264
266
  _useState2 = _slicedToArray(_useState, 2),
265
267
  loadedScene = _useState2[0],
266
268
  setLoadedScene = _useState2[1];
267
- var onChangeScene = useCallback(function (image, sceneIndex) {
269
+ var onChangeScene = useCallback(function (image, sceneIndex, loadSpec) {
270
+ changeViewerSetting("time", loadSpec.time);
268
271
  onImageTitleChange === null || onImageTitleChange === void 0 || onImageTitleChange(image.imageInfo.imageInfo.name);
269
272
  setLoadedScene(sceneIndex);
270
- }, [onImageTitleChange]);
273
+ changeViewerSetting("scaleLevelIndex", image.imageInfo.multiscaleLevel);
274
+ }, [onImageTitleChange, changeViewerSetting]);
271
275
  var onChannelLoaded = useCallback(function (image, channelIndex, isInitialLoad) {
272
276
  // TODO this was once a search by name - is that still necessary or will
273
277
  // the index always be correct?
@@ -320,17 +324,17 @@ var App = function App(props) {
320
324
 
321
325
  // save the channel's new range for remapping next time
322
326
  channelRangesRef.current[channelIndex] = [thisChannel.rawMin, thisChannel.rawMax];
323
- view3d.updateLuts(image);
327
+
328
+ // Note: updateLuts and updateActiveChannels are not called here because the
329
+ // channelVersion bump (via onChannelDataLoaded) will trigger ChannelUpdater
330
+ // effects that apply LUT/color settings and call updateLuts with the final state.
324
331
  view3d.onVolumeData(image, [channelIndex]);
325
332
  if (image.channelNames[channelIndex] === maskChannelName) {
326
333
  view3d.setVolumeChannelAsMask(image, channelIndex);
327
334
  }
328
- if (image.isLoaded()) {
329
- view3d.updateActiveChannels(image);
330
- }
331
335
  }, [view3d, changeChannelSetting, maskChannelName, props.viewerChannelSettings]);
332
- var onError = useCallback(function (error) {
333
- showError(error);
336
+ var onError = useCallback(function (error, image) {
337
+ showError(error, image);
334
338
  onImageTitleChange === null || onImageTitleChange === void 0 || onImageTitleChange(undefined);
335
339
  }, [showError, onImageTitleChange]);
336
340
  var volume = useVolume(scenes, {
@@ -585,7 +589,9 @@ var App = function App(props) {
585
589
  resetCamera: resetCamera,
586
590
  downloadScreenshot: saveScreenshot,
587
591
  resetToSavedViewerState: resetToSavedViewerState,
588
- visibleControls: visibleControls
592
+ visibleControls: visibleControls,
593
+ multiscaleDims: image === null || image === void 0 ? void 0 : image.imageInfo.imageInfo.multiscaleLevelDims,
594
+ multiscaleIndex: image === null || image === void 0 ? void 0 : image.imageInfo.multiscaleLevel
589
595
  }), /*#__PURE__*/React.createElement(CellViewerCanvasWrapper, {
590
596
  view3d: view3d,
591
597
  image: image,
@@ -287,7 +287,8 @@ var AxisClipSliders = function AxisClipSliders(props) {
287
287
  onChange: function onChange(_ref6) {
288
288
  var _ref7 = _slicedToArray(_ref6, 1),
289
289
  scene = _ref7[0];
290
- return props.changeViewerSetting("scene", scene);
290
+ props.changeViewerSetting("scene", scene);
291
+ props.changeViewerSetting("useExactScaleLevel", false);
291
292
  }
292
293
  })))));
293
294
  };
@@ -139,14 +139,14 @@ var ChannelsWidget = function ChannelsWidget(props) {
139
139
  };
140
140
  var renderChannelRow = function renderChannelRow(channelIndex) {
141
141
  var channelName = channelNames[channelIndex];
142
- return channelName ? /*#__PURE__*/React.createElement(ChannelsWidgetRow, {
142
+ return /*#__PURE__*/React.createElement(ChannelsWidgetRow, {
143
143
  key: channelIndex,
144
144
  index: channelIndex,
145
145
  channelDataForChannel: channelDataChannels[channelIndex],
146
146
  name: getDisplayName(channelName, channelIndex, viewerChannelSettings),
147
147
  onColorChangeComplete: props.onColorChangeComplete,
148
148
  saveIsosurface: props.saveIsosurface
149
- }) : null;
149
+ });
150
150
  };
151
151
  var rows = channelDataChannels && Object.entries(channelGroupedByType).filter(function (_ref3) {
152
152
  var _ref4 = _slicedToArray(_ref3, 2),
@@ -1,4 +1,3 @@
1
- function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
3
2
  function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
4
3
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
@@ -38,10 +37,11 @@ function ControlPanel(props) {
38
37
  var resetToDefaultViewerState = useViewerState(select("resetToDefaultViewerState"));
39
38
  var singleChannelMode = useViewerState(select("singleChannelMode"));
40
39
  var changeViewerSetting = useViewerState(select("changeViewerSetting"));
41
- var controlPanelContainerRef = React.useRef(null);
42
- var getDropdownContainer = controlPanelContainerRef.current ? function () {
43
- return controlPanelContainerRef.current;
44
- } : undefined;
40
+ var containerRef = React.useRef(null);
41
+ var getDropdownContainer = function getDropdownContainer() {
42
+ var _containerRef$current;
43
+ return (_containerRef$current = containerRef.current) !== null && _containerRef$current !== void 0 ? _containerRef$current : document.body;
44
+ };
45
45
  var viewerChannelSettings = props.viewerChannelSettings,
46
46
  visibleControls = props.visibleControls,
47
47
  hasImage = props.hasImage;
@@ -98,12 +98,11 @@ function ControlPanel(props) {
98
98
  }, "Single channel mode"))));
99
99
  };
100
100
  var renderTab = function renderTab(thisTab, icon) {
101
- return /*#__PURE__*/React.createElement(Tooltip, _extends({
101
+ return /*#__PURE__*/React.createElement(Tooltip, {
102
102
  title: ControlTabNames[thisTab],
103
103
  placement: "right"
104
- }, !props.collapsed && {
105
- open: false
106
- }), /*#__PURE__*/React.createElement(Button, {
104
+ }, /*#__PURE__*/React.createElement(Button, {
105
+ "aria-label": ControlTabNames[thisTab],
107
106
  className: tab === thisTab ? "ant-btn-icon-only btn-tabactive" : "ant-btn-icon-only",
108
107
  onClick: function onClick() {
109
108
  return setTab(thisTab);
@@ -151,22 +150,27 @@ function ControlPanel(props) {
151
150
  onClick: resetToDefaultViewerState
152
151
  }, "Clear all settings"))));
153
152
  };
153
+ var collapseLabel = props.collapsed ? "Show panel" : "Hide panel";
154
154
  return /*#__PURE__*/React.createElement("div", {
155
155
  className: "control-panel-col-container",
156
- ref: controlPanelContainerRef
156
+ ref: containerRef
157
157
  }, /*#__PURE__*/React.createElement("div", {
158
158
  className: "control-panel-tab-col",
159
159
  style: {
160
160
  flex: "0 0 50px"
161
161
  }
162
+ }, /*#__PURE__*/React.createElement(Tooltip, {
163
+ title: collapseLabel,
164
+ placement: "right"
162
165
  }, /*#__PURE__*/React.createElement(Button, {
166
+ "aria-label": collapseLabel,
163
167
  className: "ant-btn-icon-only btn-collapse" + (props.collapsed ? " btn-collapse-collapsed" : ""),
164
168
  onClick: function onClick() {
165
169
  return props.setCollapsed(!props.collapsed);
166
170
  }
167
171
  }, /*#__PURE__*/React.createElement(ViewerIcon, {
168
172
  type: "closePanel"
169
- })), /*#__PURE__*/React.createElement("div", {
173
+ }))), /*#__PURE__*/React.createElement("div", {
170
174
  className: "tab-divider"
171
175
  }), renderTab(ControlTab.Channels, /*#__PURE__*/React.createElement(ViewerIcon, {
172
176
  type: "channels"
@@ -20,16 +20,16 @@
20
20
  }
21
21
 
22
22
  .control-panel-col-container .control-panel-tab-col .ant-btn:not([disabled]).btn-tabactive{
23
- color:var(--color-button-icon-activated-text);
24
- background-color:var(--color-button-icon-activated-bg);
25
- border-color:var(--color-button-icon-activated-outline);
23
+ color:var(--color-button-icon-active-text);
24
+ background-color:var(--color-button-icon-active-bg);
25
+ border-color:var(--color-button-icon-active-outline);
26
26
  }
27
27
 
28
28
  .control-panel-col-container .control-panel-tab-col .ant-btn:not([disabled]):hover,
29
29
  .control-panel-col-container .control-panel-tab-col .ant-btn:not([disabled]):focus-visible{
30
- color:var(--color-button-icon-activated-text);
30
+ color:var(--color-button-icon-active-text);
31
31
  background-color:var(--color-button-primary-hover-bg);
32
- border-color:var(--color-button-icon-activated-outline);
32
+ border-color:var(--color-button-icon-active-outline);
33
33
  }
34
34
 
35
35
  .control-panel-col-container .control-panel-tab-col .btn-collapse svg{
@@ -13,10 +13,11 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
13
13
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
14
14
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
15
15
  import { VolumeLoadErrorType } from "@aics/vole-core";
16
- import { RightOutlined } from "@ant-design/icons";
16
+ import { LeftOutlined, RightOutlined, WarningOutlined } from "@ant-design/icons";
17
17
  import { Alert, Button } from "antd";
18
18
  import React from "react";
19
19
  import { useConstructor } from "../../shared/utils/hooks";
20
+ import { useViewerState } from "../../state/store";
20
21
  import "./styles.css";
21
22
  var IssueLink = function IssueLink(_ref) {
22
23
  var bug = _ref.bug,
@@ -32,113 +33,149 @@ var UNKNOWN_ERROR_DESCRIPTION = /*#__PURE__*/React.createElement(React.Fragment,
32
33
  }, "send us a bug report here"), ".");
33
34
  var ERROR_TYPE_DESCRIPTIONS = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, VolumeLoadErrorType.UNKNOWN, /*#__PURE__*/React.createElement(React.Fragment, null, "An unknown error occurred while loading volume data. Check the browser console (F12) for more details. If this looks like a bug, ", /*#__PURE__*/React.createElement(IssueLink, {
34
35
  bug: true
35
- }, "send us a bug report here"), ".")), VolumeLoadErrorType.NOT_FOUND, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was unable to find any volume data at the specified location. Check that the provided URL is correct and try again.")), VolumeLoadErrorType.TOO_LARGE, /*#__PURE__*/React.createElement(React.Fragment, null, "No scale level is available for this volume which fits within our maximum GPU memory footprint. This maximum is tuned to ensure compatibility with the majority of browsers. If you're trying to load your own OME-Zarr dataset, you may be able to open this volume by including a lower scale level.")), VolumeLoadErrorType.LOAD_DATA_FAILED, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was able to find a source of volume data at the specified location, but encountered an error while trying to load it. Check that your dataset is complete and properly formatted. You can also check the browser console (F12) for more details about this error. If it looks like a problem on our end,", " ", /*#__PURE__*/React.createElement(IssueLink, {
36
+ }, "send us a bug report here"), ".")), VolumeLoadErrorType.NOT_FOUND, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was unable to find any volume data at the specified location. Check that the provided URL is correct and try again.")), VolumeLoadErrorType.TOO_LARGE, /*#__PURE__*/React.createElement(React.Fragment, null, "No resolution level is available for this volume which fits within our maximum memory footprint. This maximum is tuned to ensure compatibility with the majority of browsers. If you're trying to load your own OME-Zarr dataset, you may be able to open this volume by including a lower resolution level.")), VolumeLoadErrorType.LOAD_DATA_FAILED, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was able to find a source of volume data at the specified location, but encountered an error while trying to load it. Check that your dataset is complete and properly formatted. You can also check the browser console (F12) for more details about this error. If it looks like a problem on our end,", " ", /*#__PURE__*/React.createElement(IssueLink, {
36
37
  bug: true
37
38
  }, "start a bug report here"), ".")), VolumeLoadErrorType.INVALID_METADATA, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was unable to read all necessary information from this volume's metadata. Check that your dataset's metadata is complete and properly formatted. If you believe your data is valid and should be supported by our viewer, let us know by ", /*#__PURE__*/React.createElement(IssueLink, null, "opening a GitHub issue"), ".")), VolumeLoadErrorType.INVALID_MULTI_SOURCE_ZARR, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer is currently configured to consolidate multiple OME-Zarr datasets into a single volume, but the provided datasets can't all be matched up. Ensure that all dataset URLs are correct and that at least one equivalently-sized scale level exists in all datasets."));
38
39
  var getErrorTitle = function getErrorTitle(error) {
39
40
  var _error$toString;
40
41
  return error instanceof Error && ((_error$toString = error.toString) === null || _error$toString === void 0 ? void 0 : _error$toString.call(error)) || typeof error === "string" && error || _typeof(error) === "object" && error !== null && error.title || "Unknown error";
41
42
  };
42
- var getErrorDescription = function getErrorDescription(error) {
43
+ var getErrorDescription = function getErrorDescription(_ref2) {
43
44
  var _ERROR_TYPE_DESCRIPTI2;
45
+ var error = _ref2.error,
46
+ dims = _ref2.dims;
44
47
  var type = error.type;
45
48
  if (!type) {
46
49
  return _typeof(error) === "object" && error !== null && error.description || UNKNOWN_ERROR_DESCRIPTION;
47
50
  }
51
+ if (type === VolumeLoadErrorType.TOO_LARGE && useViewerState.getState().useExactScaleLevel) {
52
+ var dimsDetail = "";
53
+ if (Array.isArray(dims)) {
54
+ var _dims = _slicedToArray(dims, 5),
55
+ _t = _dims[0],
56
+ _c = _dims[1],
57
+ z = _dims[2],
58
+ y = _dims[3],
59
+ x = _dims[4];
60
+ dimsDetail = " (".concat(x, " x ").concat(y, " x ").concat(z, ")");
61
+ }
62
+ return /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer cannot load this resolution level", dimsDetail, " within memory limits. Try again with a smaller resolution level.");
63
+ }
48
64
  return (_ERROR_TYPE_DESCRIPTI2 = ERROR_TYPE_DESCRIPTIONS[type]) !== null && _ERROR_TYPE_DESCRIPTI2 !== void 0 ? _ERROR_TYPE_DESCRIPTI2 : UNKNOWN_ERROR_DESCRIPTION;
49
65
  };
50
- var ErrorAlert = function ErrorAlert(_ref2) {
51
- var errors = _ref2.errors,
52
- _ref2$firstErrorCount = _ref2.firstErrorCount,
53
- firstErrorCount = _ref2$firstErrorCount === void 0 ? 0 : _ref2$firstErrorCount,
54
- _afterClose = _ref2.afterClose,
55
- onSkipError = _ref2.onSkipError;
66
+ var ErrorAlert = function ErrorAlert(_ref3) {
67
+ var errors = _ref3.errors,
68
+ afterClose = _ref3.afterClose;
56
69
  var _React$useState = React.useState(false),
57
70
  _React$useState2 = _slicedToArray(_React$useState, 2),
58
71
  showDetails = _React$useState2[0],
59
72
  setShowDetails = _React$useState2[1];
60
73
  var _React$useState3 = React.useState(0),
61
74
  _React$useState4 = _slicedToArray(_React$useState3, 2),
62
- errorsSeenCount = _React$useState4[0],
63
- setErrorsSeenCount = _React$useState4[1];
64
- var error = Array.isArray(errors) ? errors[0] : errors;
65
- var errorMessage = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", null, getErrorTitle(error) + (firstErrorCount > 1 ? " (".concat(firstErrorCount, ")") : ""), " ", /*#__PURE__*/React.createElement(Button, {
75
+ errorIndex = _React$useState4[0],
76
+ setErrorIndex = _React$useState4[1];
77
+ var _React$useState5 = React.useState(false),
78
+ _React$useState6 = _slicedToArray(_React$useState5, 2),
79
+ flash = _React$useState6[0],
80
+ setFlash = _React$useState6[1];
81
+ var error = errors[errorIndex];
82
+ var infoStyle = {
83
+ display: showDetails ? undefined : "none"
84
+ };
85
+ React.useEffect(function () {
86
+ setErrorIndex(errors.length - 1);
87
+ setFlash(errors.length > 1);
88
+ window.setTimeout(function () {
89
+ return setFlash(false);
90
+ }, 100);
91
+ }, [errors]);
92
+ var cause = error.error.cause;
93
+ var msg = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
94
+ className: "error-title"
95
+ }, getErrorTitle(error.error) + (error.count > 1 ? " (".concat(error.count, ")") : ""), " ", /*#__PURE__*/React.createElement(Button, {
66
96
  type: "text",
67
97
  onClick: function onClick() {
68
98
  return setShowDetails(!showDetails);
69
99
  }
70
100
  }, showDetails ? "Show less info" : "Show more info")), /*#__PURE__*/React.createElement("div", {
71
- style: {
72
- display: showDetails ? undefined : "none"
101
+ style: infoStyle
102
+ }, getErrorDescription(error)), cause !== undefined && /*#__PURE__*/React.createElement("div", {
103
+ className: "error-cause",
104
+ style: infoStyle
105
+ }, "Caused by ", getErrorTitle(cause)));
106
+ var pageButton = undefined;
107
+ if (errors.length > 1) {
108
+ if (errorIndex === errors.length - 1) {
109
+ pageButton = /*#__PURE__*/React.createElement(Button, {
110
+ type: "text",
111
+ onClick: function onClick() {
112
+ return setErrorIndex(function (i) {
113
+ return i - 1;
114
+ });
115
+ }
116
+ }, /*#__PURE__*/React.createElement(LeftOutlined, null), " ", errors.length - 1, " previous error", errors.length > 2 ? "s" : "");
117
+ } else {
118
+ pageButton = /*#__PURE__*/React.createElement(React.Fragment, null, errorIndex > 0 && /*#__PURE__*/React.createElement(Button, {
119
+ type: "text",
120
+ onClick: function onClick() {
121
+ return setErrorIndex(function (i) {
122
+ return i - 1;
123
+ });
124
+ }
125
+ }, /*#__PURE__*/React.createElement(LeftOutlined, null)), "Error ", errorIndex + 1, " of ", errors.length, /*#__PURE__*/React.createElement(Button, {
126
+ type: "text",
127
+ onClick: function onClick() {
128
+ return setErrorIndex(function (i) {
129
+ return i + 1;
130
+ });
131
+ }
132
+ }, /*#__PURE__*/React.createElement(RightOutlined, null)));
73
133
  }
74
- }, getErrorDescription(error)));
75
- var skipErrorButton = Array.isArray(errors) && errors.length > 1 && /*#__PURE__*/React.createElement(Button, {
76
- type: "text",
77
- onClick: function onClick() {
78
- setErrorsSeenCount(function (count) {
79
- return count + 1;
80
- });
81
- onSkipError === null || onSkipError === void 0 || onSkipError();
82
- }
83
- }, "Error ", errorsSeenCount + 1, " of ", errors.length + errorsSeenCount, " ", /*#__PURE__*/React.createElement(RightOutlined, null));
134
+ }
135
+ var alertClass = flash ? "load-error-alert error-flash" : "load-error-alert";
84
136
  return /*#__PURE__*/React.createElement(Alert, {
85
137
  showIcon: true,
86
- type: "error",
87
- className: "load-error-alert",
88
- message: errorMessage,
89
138
  closable: true,
90
- afterClose: function afterClose() {
91
- setErrorsSeenCount(0);
92
- _afterClose === null || _afterClose === void 0 || _afterClose();
93
- },
94
- action: skipErrorButton
139
+ type: "error",
140
+ icon: /*#__PURE__*/React.createElement(WarningOutlined, null),
141
+ className: alertClass,
142
+ message: msg,
143
+ afterClose: afterClose,
144
+ action: pageButton
95
145
  });
96
146
  };
97
147
  export var useErrorAlert = function useErrorAlert() {
98
- var _React$useState5 = React.useState([]),
99
- _React$useState6 = _slicedToArray(_React$useState5, 2),
100
- errorList = _React$useState6[0],
101
- setErrorList = _React$useState6[1];
148
+ var _React$useState7 = React.useState([]),
149
+ _React$useState8 = _slicedToArray(_React$useState7, 2),
150
+ errors = _React$useState8[0],
151
+ setErrors = _React$useState8[1];
102
152
  // Keep track of which errors have been seen and how many times
103
153
  var seenErrors = useConstructor(function () {
104
154
  return new Map();
105
155
  });
106
- var _React$useState7 = React.useState([]),
107
- _React$useState8 = _slicedToArray(_React$useState7, 2),
108
- errorCounts = _React$useState8[0],
109
- setErrorCounts = _React$useState8[1];
110
- var addError = React.useCallback(function (error) {
156
+ var addError = React.useCallback(function (error, image) {
111
157
  var _seenErrors$get;
112
158
  var errorTitle = getErrorTitle(error);
113
159
  console.error(error instanceof Error ? error : errorTitle);
114
- var errorSeenCount = ((_seenErrors$get = seenErrors.get(errorTitle)) !== null && _seenErrors$get !== void 0 ? _seenErrors$get : 0) + 1;
115
- setErrorList(function (prev) {
116
- return [].concat(_toConsumableArray(prev), [error]);
160
+ var count = ((_seenErrors$get = seenErrors.get(errorTitle)) !== null && _seenErrors$get !== void 0 ? _seenErrors$get : 0) + 1;
161
+ var imageInfo = image === null || image === void 0 ? void 0 : image.imageInfo.imageInfo;
162
+ var dims = imageInfo && imageInfo.multiscaleLevelDims[imageInfo.multiscaleLevel].shape;
163
+ setErrors(function (prev) {
164
+ return [].concat(_toConsumableArray(prev), [{
165
+ error: error,
166
+ count: count,
167
+ dims: dims
168
+ }]);
117
169
  });
118
- setErrorCounts(function (prev) {
119
- return [].concat(_toConsumableArray(prev), [errorSeenCount]);
120
- });
121
- seenErrors.set(errorTitle, errorSeenCount);
170
+ seenErrors.set(errorTitle, count);
122
171
  }, [seenErrors]);
123
- var onSkipError = React.useCallback(function () {
124
- setErrorList(function (prev) {
125
- return prev.slice(1);
126
- });
127
- setErrorCounts(function (prev) {
128
- return prev.slice(1);
129
- });
130
- }, []);
131
172
  var afterClose = React.useCallback(function () {
132
- setErrorList([]);
133
- setErrorCounts([]);
173
+ return setErrors([]);
134
174
  }, []);
135
- var errCount = errorCounts[0];
136
- var alertComponent = errorList.length > 0 && /*#__PURE__*/React.createElement(ErrorAlert, {
137
- errors: errorList,
138
- firstErrorCount: errCount,
139
- onSkipError: onSkipError,
175
+ var alertNode = errors.length > 0 && /*#__PURE__*/React.createElement(ErrorAlert, {
176
+ errors: errors,
140
177
  afterClose: afterClose
141
178
  });
142
- return [alertComponent, addError];
179
+ return [alertNode, addError];
143
180
  };
144
181
  export default ErrorAlert;
@@ -5,21 +5,54 @@
5
5
  width:40vw;
6
6
  align-items:start;
7
7
  z-index:5000;
8
- border-color:#dc4446;
8
+ border-color:var(--color-text-error);
9
9
  padding:12px 30px;
10
+ color:var(--color-text-section);
10
11
  }
12
+
13
+ .load-error-alert.error-flash{
14
+ background-color:var(--color-text-error);
15
+ }
16
+
11
17
  .load-error-alert .ant-alert-icon,
12
18
  .load-error-alert .ant-alert-close-icon,
13
19
  .load-error-alert .ant-alert-message div{
14
20
  min-height:24px;
15
21
  }
16
- .load-error-alert .ant-btn-text{
22
+
23
+ .load-error-alert .ant-alert-close-icon{
24
+ margin-inline-start:14px;
25
+ }
26
+
27
+ .load-error-alert .ant-alert-action{
28
+ display:inline-flex;
29
+ align-items:center;
30
+ gap:4px;
31
+ }
32
+
33
+ .load-error-alert .ant-alert-action > button{
34
+ gap:4px;
35
+ border:none !important;
36
+ }
37
+
38
+ .load-error-alert .ant-btn-text,
39
+ .load-error-alert .ant-alert-action{
17
40
  color:var(--color-text-link);
18
41
  }
42
+
19
43
  .load-error-alert .ant-btn-text:hover{
20
- color:#25affe !important;
21
- }
22
- .load-error-alert .ant-alert-message div:first-child{
23
- color:var(--color-text-section);
24
- font-weight:600;
44
+ color:#25affe !important;
45
+ }
46
+
47
+ .load-error-alert .anticon{
48
+ font-size:21px;
25
49
  }
50
+
51
+ .load-error-alert .ant-alert-message > div.error-title{
52
+ font-weight:600;
53
+ }
54
+
55
+ .load-error-alert .ant-alert-message > div.error-cause{
56
+ font-style:italic;
57
+ color:var(--color-text-body);
58
+ }