@aics/vole-app 3.5.0 → 3.6.0

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 (38) hide show
  1. package/es/aics-image-viewer/components/App/index.js +10 -0
  2. package/es/aics-image-viewer/components/ChannelsWidget.js +3 -1
  3. package/es/aics-image-viewer/components/ChannelsWidgetRow.js +29 -16
  4. package/es/aics-image-viewer/components/ControlPanel/CopySettingsButton.js +177 -147
  5. package/es/aics-image-viewer/components/ControlPanel/index.js +4 -1
  6. package/es/aics-image-viewer/components/Toolbar/styles.css +0 -10
  7. package/es/aics-image-viewer/components/ViewerCanvasWrapper/index.js +2 -1
  8. package/es/aics-image-viewer/components/dimension_sliders/AxisClipSliders.js +21 -8
  9. package/es/aics-image-viewer/components/dimension_sliders/RotationSliders.js +5 -7
  10. package/es/aics-image-viewer/components/dimension_sliders/SliderRows.js +8 -6
  11. package/es/aics-image-viewer/components/shared/ControlPanelRow/index.js +4 -2
  12. package/es/aics-image-viewer/components/shared/ControlPanelRow/styles.css +4 -3
  13. package/es/aics-image-viewer/components/useVolume.js +58 -24
  14. package/es/aics-image-viewer/shared/constants.js +24 -0
  15. package/es/aics-image-viewer/shared/utils/parseSnapshot.js +28 -2
  16. package/es/aics-image-viewer/shared/utils/parseUrl.js +41 -139
  17. package/es/aics-image-viewer/shared/utils/test/camera.test.js +1 -1
  18. package/es/aics-image-viewer/shared/utils/test/parseUrl.test.js +20 -20
  19. package/es/aics-image-viewer/shared/utils/test/storage.test.js +2 -2
  20. package/es/aics-image-viewer/shared/utils/test/viewerChannelSettings.test.js +1 -1
  21. package/es/aics-image-viewer/state/test/deserialize.test.js +1 -1
  22. package/es/aics-image-viewer/state/test/reset.test.js +1 -1
  23. package/es/aics-image-viewer/state/test/serialize.test.js +1 -0
  24. package/package.json +26 -44
  25. package/type-declarations/aics-image-viewer/components/ChannelsWidget.d.ts +3 -0
  26. package/type-declarations/aics-image-viewer/components/ChannelsWidgetRow.d.ts +3 -0
  27. package/type-declarations/aics-image-viewer/components/ControlPanel/CopySettingsButton.d.ts +2 -0
  28. package/type-declarations/aics-image-viewer/components/ViewerCanvasWrapper/index.d.ts +1 -0
  29. package/type-declarations/aics-image-viewer/components/dimension_sliders/AxisClipSliders.d.ts +1 -0
  30. package/type-declarations/aics-image-viewer/components/dimension_sliders/SliderRows.d.ts +1 -2
  31. package/type-declarations/aics-image-viewer/components/shared/ControlPanelRow/index.d.ts +1 -0
  32. package/type-declarations/aics-image-viewer/components/useVolume.d.ts +3 -0
  33. package/type-declarations/aics-image-viewer/shared/constants.d.ts +1 -0
  34. package/type-declarations/aics-image-viewer/shared/utils/parseSnapshot.d.ts +2 -0
  35. package/type-declarations/aics-image-viewer/shared/utils/parseUrl.d.ts +1 -8
  36. package/es/Globals.d.js +0 -0
  37. package/es/aics-image-viewer/shared/utils/firebase/index.js +0 -109
  38. package/type-declarations/aics-image-viewer/shared/utils/firebase/index.d.ts +0 -69
@@ -25,13 +25,25 @@ export var AxisClipSliders = function AxisClipSliders(props) {
25
25
  changeViewerSetting("region", _defineProperty({}, axis, [start, end]));
26
26
  };
27
27
  var updateSlice = function updateSlice(axis, slice) {
28
+ var isScrubbing = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
28
29
  pauseOnInput(axis);
30
+ if (isScrubbing) {
31
+ props.setScrubbingAxis(axis);
32
+ }
29
33
  props.changeViewerSetting("slice", _defineProperty({}, axis, slice / props.numSlices[axis]));
30
34
  };
31
35
  var updateTime = function updateTime(time) {
36
+ var isScrubbing = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
32
37
  pauseOnInput("t");
38
+ if (isScrubbing) {
39
+ props.setScrubbingAxis("t");
40
+ }
33
41
  props.changeViewerSetting("time", time);
34
42
  };
43
+ var endScrub = function endScrub() {
44
+ props.setScrubbingAxis(null);
45
+ props.playControls.endHold();
46
+ };
35
47
 
36
48
  // Pause when view mode or volume size has changed
37
49
  useEffect(function () {
@@ -54,20 +66,20 @@ export var AxisClipSliders = function AxisClipSliders(props) {
54
66
  label: axis.toUpperCase(),
55
67
  val: Math.round(props.slices[axis] * numSlices),
56
68
  max: numSlices,
69
+ onSlide: function onSlide(val) {
70
+ return updateSlice(axis, val, true);
71
+ },
57
72
  onChange: function onChange(val) {
58
73
  return updateSlice(axis, val);
59
74
  },
60
75
  onStart: function onStart() {
61
76
  return props.playControls.startHold(axis);
62
77
  },
63
- onEnd: function onEnd() {
64
- return props.playControls.endHold();
65
- },
78
+ onEnd: endScrub,
66
79
  playing: props.playingAxis === axis,
67
80
  onTogglePlayback: function onTogglePlayback(willPlay) {
68
81
  return handlePlayPause(axis, willPlay);
69
- },
70
- updateWhileSliding: numSlices === numSlicesLoaded
82
+ }
71
83
  }));
72
84
  };
73
85
  var create3dAxisSlider = function create3dAxisSlider(axis) {
@@ -119,15 +131,16 @@ export var AxisClipSliders = function AxisClipSliders(props) {
119
131
  onTogglePlayback: function onTogglePlayback(willPlay) {
120
132
  return handlePlayPause("t", willPlay);
121
133
  },
134
+ onSlide: function onSlide(time) {
135
+ return updateTime(time, true);
136
+ },
122
137
  onChange: function onChange(time) {
123
138
  return updateTime(time);
124
139
  },
125
140
  onStart: function onStart() {
126
141
  return props.playControls.startHold("t");
127
142
  },
128
- onEnd: function onEnd() {
129
- return props.playControls.endHold();
130
- }
143
+ onEnd: endScrub
131
144
  })))), props.numScenes > 1 && /*#__PURE__*/React.createElement("span", {
132
145
  className: "slider-group"
133
146
  }, /*#__PURE__*/React.createElement("span", {
@@ -100,21 +100,19 @@ export var RotationSliders = function RotationSliders(_ref) {
100
100
 
101
101
  // Set up event listeners on mount.
102
102
  useEffect(function () {
103
- // TODO this is *extremely brittle*: setting this clears away any other listener for render events.
104
- // At time of writing, `setOnRenderCallback` appears not to be used anywhere else in either this package or
105
- // vole-core, but that could change at any time.
106
- // Ideally `View3d` would have an `addEventListener`/`removeEventListener` model.
107
- view3d.setOnRenderCallback(function () {
103
+ var renderListener = function renderListener() {
108
104
  if (!hasControlRef.current) {
109
105
  setRotation(getRotationAngles(view3d.getCameraState()));
110
106
  }
111
- });
107
+ };
108
+ view3d.addEventListener("render", renderListener);
112
109
  var clickListener = function clickListener() {
113
110
  return hasControlRef.current = false;
114
111
  };
115
112
  view3d.getDOMElement().addEventListener("mousedown", clickListener);
116
113
  return function () {
117
- return view3d.getDOMElement().removeEventListener("mousedown", clickListener);
114
+ view3d.removeEventListener("render", renderListener);
115
+ view3d.getDOMElement().removeEventListener("mousedown", clickListener);
118
116
  };
119
117
  }, [view3d]);
120
118
  var createRotateSlider = function createRotateSlider(axis) {
@@ -116,6 +116,7 @@ export var IndexSliderRow = function IndexSliderRow(props) {
116
116
  /** Wrapper around `SliderRow` that adds a play button and accounts for the case where not all of an axis is loaded. */
117
117
  export var PlaySliderRow = function PlaySliderRow(props) {
118
118
  var onChange = props.onChange,
119
+ onSlide = props.onSlide,
119
120
  onStart = props.onStart,
120
121
  onEnd = props.onEnd;
121
122
  // In partially-loaded axes, stores the displayed value of the slider while the user is sliding it
@@ -131,9 +132,10 @@ export var PlaySliderRow = function PlaySliderRow(props) {
131
132
  var wrappedOnChange = useCallback(function (val) {
132
133
  return onChange === null || onChange === void 0 ? void 0 : onChange(val);
133
134
  }, [onChange]);
134
- var wrappedSetValReadout = useCallback(function (val) {
135
- return setValReadout(val);
136
- }, []);
135
+ var wrappedOnSlide = useCallback(function (val) {
136
+ setValReadout(val);
137
+ onSlide === null || onSlide === void 0 || onSlide(val);
138
+ }, [onSlide]);
137
139
  var wrappedOnStart = useCallback(function () {
138
140
  setValReadout(props.val);
139
141
  setSliderHeld(true);
@@ -146,10 +148,10 @@ export var PlaySliderRow = function PlaySliderRow(props) {
146
148
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(IndexSliderRow, {
147
149
  label: props.label,
148
150
  val: props.val,
149
- valReadout: props.updateWhileSliding || !sliderHeld ? undefined : valReadout,
151
+ valReadout: !sliderHeld ? undefined : valReadout,
150
152
  max: props.max,
151
- onSlide: props.updateWhileSliding ? wrappedOnChange : wrappedSetValReadout,
152
- onChange: props.updateWhileSliding ? undefined : wrappedOnChange,
153
+ onSlide: wrappedOnSlide,
154
+ onChange: wrappedOnChange,
153
155
  onStart: wrappedOnStart,
154
156
  onEnd: wrappedOnEnd
155
157
  }), props.max > 1 && /*#__PURE__*/React.createElement(Tooltip, {
@@ -31,7 +31,7 @@ var ControlPanelRow = function ControlPanelRow(inputProps) {
31
31
  var style = {
32
32
  "--control-panel-row-vertical-margin": props.verticalMargin + "px"
33
33
  };
34
- var createTitle = props.title !== undefined || props.showColorPicker;
34
+ var createTitle = props.title !== undefined || props.icon !== undefined || props.showColorPicker;
35
35
  var baseClass = "control-panel-row".concat(props.highlight ? " control-panel-row-highlight" : "");
36
36
  var className = props.className ? "".concat(baseClass, " ").concat(props.className) : baseClass;
37
37
  return /*#__PURE__*/React.createElement("div", {
@@ -40,7 +40,9 @@ var ControlPanelRow = function ControlPanelRow(inputProps) {
40
40
  onClick: props.onClick
41
41
  }, createTitle && /*#__PURE__*/React.createElement("div", {
42
42
  className: "control-panel-row-title"
43
- }, props.showColorPicker && /*#__PURE__*/React.createElement(ColorPicker, _extends({
43
+ }, props.icon && /*#__PURE__*/React.createElement("div", {
44
+ className: "control-panel-row-icon"
45
+ }, props.icon), props.showColorPicker && /*#__PURE__*/React.createElement(ColorPicker, _extends({
44
46
  disableAlpha: true,
45
47
  width: 18
46
48
  }, colorPickerProps)), props.title && /*#__PURE__*/React.createElement("h4", null, props.title)), props.children);
@@ -16,8 +16,10 @@
16
16
  flex-grow:1;
17
17
  }
18
18
 
19
- .control-panel-col .control-panel-row .control-panel-row-title .color-picker{
20
- margin-right:16px;
19
+ .control-panel-col .control-panel-row .control-panel-row-title .color-picker,
20
+ .control-panel-col .control-panel-row .control-panel-row-title .control-panel-row-icon{
21
+ margin-right:10px;
22
+ font-size:12px;
21
23
  }
22
24
 
23
25
  .control-panel-col .control-panel-row .control-panel-row-title h4{
@@ -58,7 +60,6 @@
58
60
  width:100%;
59
61
  border-bottom:1px solid var(--color-controlpanel-border);
60
62
  }
61
-
62
63
  .control-panel-col .ant-collapse-item:not(:last-child) .control-panel-row:last-child::after{
63
64
  border-bottom:none;
64
65
  }
@@ -39,7 +39,7 @@ export var ImageLoadStatus = /*#__PURE__*/function (ImageLoadStatus) {
39
39
  return ImageLoadStatus;
40
40
  }({});
41
41
  var LoadType = /*#__PURE__*/function (LoadType) {
42
- LoadType[LoadType["TIME"] = 0] = "TIME";
42
+ LoadType[LoadType["RELOAD"] = 0] = "RELOAD";
43
43
  LoadType[LoadType["SCENE"] = 1] = "SCENE";
44
44
  return LoadType;
45
45
  }(LoadType || {}); // Used by `channelVersions` (see below)
@@ -84,6 +84,7 @@ var useVolume = function useVolume(scenePaths, options) {
84
84
  var onChannelLoadedRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onChannelLoaded);
85
85
  var onChangeSceneRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onChangeScene);
86
86
  var onCreateImageRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onCreateImage);
87
+ var onScrubRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onScrub);
87
88
  var maskChannelName = options === null || options === void 0 ? void 0 : options.maskChannelName;
88
89
 
89
90
  // set up our big objects: the image, its loading infrastructure, and controls for playback
@@ -104,6 +105,10 @@ var useVolume = function useVolume(scenePaths, options) {
104
105
  _useState4 = _slicedToArray(_useState3, 2),
105
106
  playingAxis = _useState4[0],
106
107
  setPlayingAxis = _useState4[1];
108
+ var _useState5 = useState(null),
109
+ _useState6 = _slicedToArray(_useState5, 2),
110
+ scrubbingAxis = _useState6[0],
111
+ setScrubbingAxis = _useState6[1];
107
112
  useEffect(function () {
108
113
  playControls.onPlayingAxisChanged = function (axis) {
109
114
  var isPlaying = axis !== null;
@@ -113,36 +118,24 @@ var useVolume = function useVolume(scenePaths, options) {
113
118
  sceneLoader.updateFetchOptions({
114
119
  onlyPriorityDirections: isPlaying
115
120
  });
116
- // sync multichannel loading so we don't show loaded channels one at a time
117
- sceneLoader.syncMultichannelLoading(isPlaying);
118
- if (image) {
119
- // If we're playing and entire axis is not in memory (T always, Z likely), downlevel to speed things up
120
- var _image$imageInfo = image.imageInfo,
121
- volumeSize = _image$imageInfo.volumeSize,
122
- subregionSize = _image$imageInfo.subregionSize;
123
- var shouldDownlevel = isPlaying && (axis === "t" || volumeSize[axis] !== subregionSize[axis]);
124
- image.updateRequiredData({
125
- scaleLevelBias: shouldDownlevel ? 1 : 0
126
- });
127
- }
128
121
  };
129
122
  }, [sceneLoader, playControls, image]);
130
123
 
131
124
  // track which channels have been loaded
132
- var _useState5 = useState([]),
133
- _useState6 = _slicedToArray(_useState5, 2),
134
- channelVersions = _useState6[0],
135
- _setChannelVersions = _useState6[1];
125
+ var _useState7 = useState([]),
126
+ _useState8 = _slicedToArray(_useState7, 2),
127
+ channelVersions = _useState8[0],
128
+ _setChannelVersions = _useState8[1];
136
129
  var _useRefWithSetter = useRefWithSetter(_setChannelVersions, channelVersions),
137
130
  _useRefWithSetter2 = _slicedToArray(_useRefWithSetter, 2),
138
131
  channelVersionsRef = _useRefWithSetter2[0],
139
132
  setChannelVersions = _useRefWithSetter2[1];
140
133
 
141
134
  // Some extra items for tracking load status
142
- var _useState7 = useState(false),
143
- _useState8 = _slicedToArray(_useState7, 2),
144
- loadThrewError = _useState8[0],
145
- setLoadThrewError = _useState8[1];
135
+ var _useState9 = useState(false),
136
+ _useState0 = _slicedToArray(_useState9, 2),
137
+ loadThrewError = _useState0[0],
138
+ setLoadThrewError = _useState0[1];
146
139
  var inInitialLoadRef = useRef(true);
147
140
 
148
141
  // derive whether the image is loaded from whether any and/or all channels are loaded
@@ -178,11 +171,41 @@ var useVolume = function useVolume(scenePaths, options) {
178
171
  );
179
172
  }));
180
173
  }, [channelVersionsRef, setChannelVersions]);
174
+ useEffect(function () {
175
+ if (!image) return;
176
+ var onLoadStart = function onLoadStart() {
177
+ return setIsLoading(LoadType.RELOAD);
178
+ };
179
+ image.addEventListener("loadStart", onLoadStart);
180
+ return function () {
181
+ return image.removeEventListener("loadStart", onLoadStart);
182
+ };
183
+ }, [image, setIsLoading]);
181
184
  var onError = useCallback(function (e) {
182
185
  setLoadThrewError(true);
183
186
  onErrorRef(e);
184
187
  throw e;
185
188
  }, [onErrorRef]);
189
+ var scaleLevelBias = useMemo(function () {
190
+ if (!image) return 0;
191
+
192
+ // If we're playing/scrubbing and entire axis is not in memory, downlevel to speed things up
193
+ var _image$imageInfo = image.imageInfo,
194
+ volumeSize = _image$imageInfo.volumeSize,
195
+ subregionSize = _image$imageInfo.subregionSize,
196
+ numMultiscaleLevels = _image$imageInfo.numMultiscaleLevels;
197
+ var axis = scrubbingAxis !== null && scrubbingAxis !== void 0 ? scrubbingAxis : playingAxis;
198
+ if (axis && (axis === "t" || volumeSize[axis] !== subregionSize[axis])) {
199
+ var isPlayback = scrubbingAxis !== null;
200
+ return isPlayback ? numMultiscaleLevels : 1;
201
+ }
202
+ return 0;
203
+ }, [image, playingAxis, scrubbingAxis]);
204
+ useEffect(function () {
205
+ image === null || image === void 0 || image.updateRequiredData({
206
+ scaleLevelBias: scaleLevelBias
207
+ })["catch"](onError);
208
+ }, [scaleLevelBias, image, onError]);
186
209
 
187
210
  // channelGroupedByType groups channel indexes by their category.
188
211
  // It depends on `viewerChannelSettings` and the channel names in the current image.
@@ -310,9 +333,8 @@ var useVolume = function useVolume(scenePaths, options) {
310
333
  var setTime = useCallback(function (view3d, time) {
311
334
  if (image && !inInitialLoadRef.current) {
312
335
  view3d.setTime(image, time)["catch"](onError);
313
- setIsLoading(LoadType.TIME);
314
336
  }
315
- }, [image, onError, setIsLoading, inInitialLoadRef]);
337
+ }, [image, onError, inInitialLoadRef]);
316
338
  var setScene = useCallback(function (scene) {
317
339
  if (image && !inInitialLoadRef.current) {
318
340
  var onCreateScene = function onCreateScene(volume, sceneIndex, loadSpec) {
@@ -335,6 +357,17 @@ var useVolume = function useVolume(scenePaths, options) {
335
357
  setIsLoading(LoadType.SCENE);
336
358
  }
337
359
  }, [image, sceneLoader, onError, setIsLoading, setChannelStateForNewImage, channelVersionsRef, setChannelVersions, onChangeSceneRef]);
360
+ useEffect(function () {
361
+ if (image && !inInitialLoadRef.current) {
362
+ // When playing, wait for all channels to load before displaying
363
+ // When scrubbing, show channels as they arrive, for speed
364
+ sceneLoader.syncMultichannelLoading(scrubbingAxis === null && playingAxis !== null);
365
+ }
366
+ }, [image, scrubbingAxis, playingAxis, sceneLoader]);
367
+ useEffect(function () {
368
+ if (!image) return;
369
+ onScrubRef(scrubbingAxis !== null, image);
370
+ }, [onScrubRef, scrubbingAxis, image]);
338
371
  return useMemo(function () {
339
372
  return {
340
373
  image: image,
@@ -342,10 +375,11 @@ var useVolume = function useVolume(scenePaths, options) {
342
375
  imageLoadStatus: imageLoadStatus,
343
376
  setTime: setTime,
344
377
  setScene: setScene,
378
+ setScrubbingAxis: setScrubbingAxis,
345
379
  playControls: playControls,
346
380
  playingAxis: playingAxis,
347
381
  channelGroupedByType: channelGroupedByType
348
382
  };
349
- }, [image, channelVersions, imageLoadStatus, setTime, setScene, playControls, playingAxis, channelGroupedByType]);
383
+ }, [image, channelVersions, imageLoadStatus, setTime, setScene, setScrubbingAxis, playControls, playingAxis, channelGroupedByType]);
350
384
  };
351
385
  export default useVolume;
@@ -4,6 +4,30 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
4
4
  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); }
5
5
  import { PrefetchDirection } from "@aics/vole-core";
6
6
  import { ImageType, RenderMode, ViewMode } from "../state/types";
7
+ /**
8
+ * Gets the current vole-app version safely across the different app and library
9
+ * build environments.
10
+ */
11
+ function getBuildVersion() {
12
+ // Defined as global constants when built by Vite as an app
13
+ if (typeof VITE_VOLEAPP_VERSION === "string" && VITE_VOLEAPP_VERSION.length > 0) {
14
+ return VITE_VOLEAPP_VERSION;
15
+ }
16
+ // Injected at build time when built by Babel as a library
17
+ try {
18
+ return "3.6.0";
19
+ } catch (_unused) {
20
+ return undefined;
21
+ }
22
+ }
23
+ var buildVersion = getBuildVersion();
24
+ if (buildVersion === undefined) {
25
+ console.error("Failed to determine VOLEAPP_VERSION; a default fallback will be used instead. This is likely due to a build environment misconfiguration.");
26
+ }
27
+ // 3.5.0 is the first version where app versions are serialized to channel
28
+ // state, and is used as the fallback.
29
+ export var VOLEAPP_VERSION = buildVersion !== null && buildVersion !== void 0 ? buildVersion : "3.5.0";
30
+
7
31
  // Add all exported constants here to prevent circular dependencies
8
32
  export var
9
33
  // Control panel will automatically close if viewport is less than this width
@@ -14,6 +14,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
14
14
  import { snapshotToChannelState, snapshotToViewerChannelSetting } from "../../state/deserialize";
15
15
  import { channelStateToSnapshot } from "../../state/serialize";
16
16
  import { cloneChannelState } from "../../state/util";
17
+ import { VOLEAPP_VERSION } from "../constants";
17
18
 
18
19
  /**
19
20
  * A message sent from an external application after this app was opened,
@@ -89,6 +90,30 @@ export var channelStatesToSnapshot = function channelStatesToSnapshot(channelSta
89
90
  };
90
91
  };
91
92
 
93
+ /** Converts a single `ChannelState` to a compact JSON representation that can be stringified into the clipboard. */
94
+ export var singleChannelStateToSnapshot = function singleChannelStateToSnapshot(channelState, excludeKeys) {
95
+ var setting = cloneChannelState(channelState);
96
+ if (excludeKeys !== undefined) {
97
+ var _iterator3 = _createForOfIteratorHelper(excludeKeys),
98
+ _step3;
99
+ try {
100
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
101
+ var key = _step3.value;
102
+ delete setting[key];
103
+ }
104
+ } catch (err) {
105
+ _iterator3.e(err);
106
+ } finally {
107
+ _iterator3.f();
108
+ }
109
+ }
110
+ var channels = _defineProperty({}, setting.name, channelStateToSnapshot(setting, false));
111
+ return {
112
+ version: VOLEAPP_VERSION,
113
+ channels: channels
114
+ };
115
+ };
116
+
92
117
  /** Converts a compacted set of `ChannelState`s from the clipboard into a record of channel names and their states. */
93
118
  export var snapshotToChannelStates = function snapshotToChannelStates(serialized) {
94
119
  var result = {};
@@ -142,12 +167,13 @@ export function viewerMessageToParams(message, propUrl) {
142
167
  // get metadata
143
168
  var meta = message.meta;
144
169
  var metadata = meta && scenes.map(function (scene) {
145
- if (Array.isArray(scene)) {
170
+ if (Array.isArray(scene) && scene.length > 1) {
146
171
  // can't handle multi-source scenes (yet)
147
172
  console.warn("Can't apply metadata to multi-source scene", scene);
148
173
  return undefined;
149
174
  }
150
- return meta[scene];
175
+ var sceneString = Array.isArray(scene) ? scene[0] : scene;
176
+ return meta[sceneString];
151
177
  });
152
178
  return {
153
179
  imageUrl: imageUrl,