@micromag/viewer 0.3.161 → 0.3.162

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 (3) hide show
  1. package/es/index.js +52 -107
  2. package/lib/index.js +51 -106
  3. package/package.json +9 -9
package/es/index.js CHANGED
@@ -21,7 +21,7 @@ import { Helmet } from 'react-helmet';
21
21
  import { useIntl, FormattedMessage } from 'react-intl';
22
22
  import EventEmitter from 'wolfy87-eventemitter';
23
23
  import { Button, Close, ScreenPreview, Screen, Meta, FontFaces } from '@micromag/core/components';
24
- import { useDocumentEvent, useDimensionObserver, useTrackEvent, useMediaDuration, useMediaCurrentTime, useParsedStory, useLoadedFonts, useTrackScreenView, useScreenSizeFromElement, useFullscreen } from '@micromag/core/hooks';
24
+ import { useDocumentEvent, useDimensionObserver, useTrackEvent, useMediaProgress, useParsedStory, useLoadedFonts, useTrackScreenView, useScreenSizeFromElement, useFullscreen } from '@micromag/core/hooks';
25
25
  import { getStyleFromColor, getStyleFromText, getColorAsString, getDeviceScreens } from '@micromag/core/utils';
26
26
  import { useSpring, config } from '@react-spring/core';
27
27
  import { animated } from '@react-spring/web';
@@ -1396,8 +1396,6 @@ var styles$2 = {"track":"micromag-viewer-partials-seek-bar-track","progress":"mi
1396
1396
 
1397
1397
  var propTypes$5 = {
1398
1398
  media: PropTypes.node,
1399
- currentTime: PropTypes.number,
1400
- duration: PropTypes.number,
1401
1399
  playing: PropTypes.bool,
1402
1400
  backgroundColor: PropTypes.string,
1403
1401
  progressColor: PropTypes.string,
@@ -1410,8 +1408,6 @@ var propTypes$5 = {
1410
1408
  };
1411
1409
  var defaultProps$5 = {
1412
1410
  media: null,
1413
- currentTime: null,
1414
- duration: null,
1415
1411
  playing: false,
1416
1412
  backgroundColor: null,
1417
1413
  progressColor: null,
@@ -1427,8 +1423,6 @@ var SeekBar = function SeekBar(_ref) {
1427
1423
  var _ref3;
1428
1424
 
1429
1425
  var media = _ref.media,
1430
- currentTime = _ref.currentTime,
1431
- duration = _ref.duration,
1432
1426
  playing = _ref.playing,
1433
1427
  backgroundColor = _ref.backgroundColor,
1434
1428
  progressColor = _ref.progressColor,
@@ -1439,86 +1433,45 @@ var SeekBar = function SeekBar(_ref) {
1439
1433
  className = _ref.className,
1440
1434
  withSeekHead = _ref.withSeekHead;
1441
1435
  var intl = useIntl();
1442
-
1443
- var _useSpring = useSpring(function () {
1444
- return {
1445
- x: currentTime !== null && duration !== null ? currentTime / duration : 0,
1446
- config: {
1447
- duration: 0
1448
- }
1449
- };
1450
- }),
1451
- _useSpring2 = _slicedToArray(_useSpring, 2),
1452
- springProps = _useSpring2[0],
1453
- setSpringProps = _useSpring2[1];
1454
-
1455
- var lastMediaRef = useRef(media);
1456
- var mediaChanged = lastMediaRef.current !== media;
1457
- lastMediaRef.current = media;
1458
-
1459
- var _useDimensionObserver = useDimensionObserver(),
1460
- elRef = _useDimensionObserver.ref,
1461
- _useDimensionObserver2 = _useDimensionObserver.width,
1462
- elWidth = _useDimensionObserver2 === void 0 ? null : _useDimensionObserver2;
1463
-
1464
- useEffect(function () {
1465
- if (currentTime === null || duration === null) {
1436
+ var progress = useMediaProgress(media, {
1437
+ disabled: !playing
1438
+ });
1439
+ var onDrag = useCallback(function (_ref2) {
1440
+ var _ref2$xy = _slicedToArray(_ref2.xy, 1),
1441
+ x = _ref2$xy[0],
1442
+ elapsedTime = _ref2.elapsedTime,
1443
+ active = _ref2.active,
1444
+ tap = _ref2.tap,
1445
+ currentTarget = _ref2.currentTarget;
1446
+
1447
+ if (!active && elapsedTime > 300) {
1466
1448
  return;
1467
1449
  }
1468
1450
 
1469
- var progress = duration > 0 ? currentTime / duration : 0;
1470
- setSpringProps.start({
1471
- reset: true,
1472
- immediate: !playing || mediaChanged,
1473
- from: {
1474
- x: progress
1475
- },
1476
- to: {
1477
- x: playing ? 1 : progress
1478
- },
1479
- config: {
1480
- duration: (duration - currentTime) * 1000
1481
- }
1482
- });
1483
- }, [playing, duration, currentTime, mediaChanged, setSpringProps]); // User events
1451
+ var _currentTarget$getBou = currentTarget.getBoundingClientRect(),
1452
+ elX = _currentTarget$getBou.left,
1453
+ elWidth = _currentTarget$getBou.width;
1484
1454
 
1485
- var seekFromX = useCallback(function (x) {
1486
- var elX = elRef.current.getBoundingClientRect().left;
1487
- var progress = Math.max(0, Math.min(1, (x - elX) / elWidth));
1488
- setSpringProps.start({
1489
- immediate: true,
1490
- to: {
1491
- x: progress
1492
- }
1493
- });
1455
+ var newProgress = Math.max(0, Math.min(1, (x - elX) / elWidth));
1494
1456
 
1495
- if (onSeek !== null && duration !== null) {
1496
- onSeek(progress * duration);
1457
+ if (onSeek !== null) {
1458
+ onSeek(newProgress, tap);
1497
1459
  }
1498
- }, [duration, playing, onSeek]);
1499
- var bind = useGesture({
1500
- onDrag: function onDrag(_ref2) {
1501
- var _ref2$xy = _slicedToArray(_ref2.xy, 1),
1502
- x = _ref2$xy[0],
1503
- elapsedTime = _ref2.elapsedTime,
1504
- active = _ref2.active;
1505
-
1506
- if (!active && elapsedTime > 300) {
1507
- return;
1508
- }
1509
-
1510
- seekFromX(x);
1511
- },
1512
- onPointerDown: function onPointerDown() {
1513
- if (onSeekStart !== null) {
1514
- onSeekStart();
1515
- }
1516
- },
1517
- onPointerUp: function onPointerUp() {
1518
- if (onSeekEnd !== null) {
1519
- onSeekEnd();
1520
- }
1460
+ }, [onSeek]);
1461
+ var onDragStart = useCallback(function () {
1462
+ if (onSeekStart !== null) {
1463
+ onSeekStart();
1521
1464
  }
1465
+ }, [onSeekStart]);
1466
+ var onDragEnd = useCallback(function () {
1467
+ if (onSeekEnd !== null) {
1468
+ onSeekEnd();
1469
+ }
1470
+ }, [onSeekEnd]);
1471
+ var bind = useGesture({
1472
+ onDrag: onDrag,
1473
+ onDragStart: onDragStart,
1474
+ onDragEnd: onDragEnd
1522
1475
  }, {
1523
1476
  drag: {
1524
1477
  axis: 'x',
@@ -1534,25 +1487,19 @@ var SeekBar = function SeekBar(_ref) {
1534
1487
  style: {
1535
1488
  backgroundColor: backgroundColor
1536
1489
  }
1537
- }, /*#__PURE__*/React.createElement(animated.div, {
1490
+ }, /*#__PURE__*/React.createElement("div", {
1538
1491
  className: styles$2.playHead,
1539
1492
  style: {
1540
- left: springProps.x.to(function (x) {
1541
- return "".concat(x * 100, "%");
1542
- }),
1493
+ left: "".concat(progress * 100, "%"),
1543
1494
  backgroundColor: progressColor
1544
1495
  }
1545
- }), /*#__PURE__*/React.createElement(animated.div, {
1496
+ }), /*#__PURE__*/React.createElement("div", {
1546
1497
  className: styles$2.progress,
1547
1498
  style: {
1548
- transform: springProps.x.to(function (x) {
1549
- return "scaleX(".concat(x, ")");
1550
- }),
1499
+ transform: "scaleX(".concat(progress, ")"),
1551
1500
  backgroundColor: progressColor
1552
1501
  }
1553
- })), /*#__PURE__*/React.createElement("button", Object.assign({
1554
- ref: elRef
1555
- }, bind(), {
1502
+ })), /*#__PURE__*/React.createElement("button", Object.assign({}, bind(), {
1556
1503
  type: "button",
1557
1504
  className: styles$2.track,
1558
1505
  title: intl.formatMessage({
@@ -1607,12 +1554,6 @@ function PlaybackControls(_ref) {
1607
1554
  controlsTheme = _usePlaybackContext.controlsTheme,
1608
1555
  showControls = _usePlaybackContext.showControls;
1609
1556
 
1610
- var duration = useMediaDuration(mediaElement);
1611
- var currentTime = useMediaCurrentTime(mediaElement, {
1612
- disabled: !playing,
1613
- updateInterval: 100
1614
- });
1615
-
1616
1557
  var _useState = useState(null),
1617
1558
  _useState2 = _slicedToArray(_useState, 2),
1618
1559
  customControlsTheme = _useState2[0],
@@ -1665,20 +1606,25 @@ function PlaybackControls(_ref) {
1665
1606
  }, [setMuted, controlsVisible, showControls]);
1666
1607
  var onSeekStart = useCallback(function () {
1667
1608
  setWasPlaying(playing);
1668
- if (playing) setPlaying(false);
1669
- }, [playing, setWasPlaying]);
1670
- var onSeek = useCallback(function (time) {
1609
+
1610
+ if (playing) {
1611
+ setPlaying(false);
1612
+ }
1613
+ }, [playing, setWasPlaying, setPlaying]);
1614
+ var onSeek = useCallback(function (progress) {
1671
1615
  if (mediaElement !== null) {
1672
- mediaElement.currentTime = time;
1616
+ mediaElement.currentTime = progress * mediaElement.duration;
1673
1617
  }
1674
1618
 
1675
1619
  if (!controlsVisible && controls) {
1676
1620
  showControls();
1677
1621
  }
1678
- }, [mediaElement, controlsVisible, controls, setWasPlaying, playing, setPlaying, showControls]);
1622
+ }, [mediaElement, controlsVisible, controls, showControls]);
1679
1623
  var onSeekEnd = useCallback(function () {
1680
- if (wasPlaying) setPlaying(true);
1681
- }, [playing, setPlaying, wasPlaying]);
1624
+ if (wasPlaying) {
1625
+ setPlaying(true);
1626
+ }
1627
+ }, [setPlaying, wasPlaying]);
1682
1628
  var mediaHasAudio = mediaElement !== null && (hasAudio === null || hasAudio === true);
1683
1629
 
1684
1630
  var _ref3 = customControlsTheme || {},
@@ -1716,10 +1662,8 @@ function PlaybackControls(_ref) {
1716
1662
  icon: playing ? faPause : faPlay
1717
1663
  })), /*#__PURE__*/React.createElement(SeekBar, {
1718
1664
  className: styles$1.seekBar,
1719
- duration: duration,
1720
- currentTime: currentTime,
1721
- playing: playing,
1722
1665
  media: mediaElement,
1666
+ playing: playing,
1723
1667
  onSeek: onSeek,
1724
1668
  onSeekStart: onSeekStart,
1725
1669
  onSeekEnd: onSeekEnd,
@@ -2316,6 +2260,7 @@ var Viewer = function Viewer(_ref) {
2316
2260
  }, /*#__PURE__*/React.createElement(ScreenSizeProvider, {
2317
2261
  size: screenSize
2318
2262
  }, /*#__PURE__*/React.createElement(ViewerProvider, {
2263
+ containerRef: containerRef,
2319
2264
  events: eventsManager,
2320
2265
  menuVisible: menuVisible,
2321
2266
  menuOverScreen: menuOverScreen,
package/lib/index.js CHANGED
@@ -1416,8 +1416,6 @@ var styles$2 = {"track":"micromag-viewer-partials-seek-bar-track","progress":"mi
1416
1416
 
1417
1417
  var propTypes$5 = {
1418
1418
  media: PropTypes__default["default"].node,
1419
- currentTime: PropTypes__default["default"].number,
1420
- duration: PropTypes__default["default"].number,
1421
1419
  playing: PropTypes__default["default"].bool,
1422
1420
  backgroundColor: PropTypes__default["default"].string,
1423
1421
  progressColor: PropTypes__default["default"].string,
@@ -1430,8 +1428,6 @@ var propTypes$5 = {
1430
1428
  };
1431
1429
  var defaultProps$5 = {
1432
1430
  media: null,
1433
- currentTime: null,
1434
- duration: null,
1435
1431
  playing: false,
1436
1432
  backgroundColor: null,
1437
1433
  progressColor: null,
@@ -1447,8 +1443,6 @@ var SeekBar = function SeekBar(_ref) {
1447
1443
  var _ref3;
1448
1444
 
1449
1445
  var media = _ref.media,
1450
- currentTime = _ref.currentTime,
1451
- duration = _ref.duration,
1452
1446
  playing = _ref.playing,
1453
1447
  backgroundColor = _ref.backgroundColor,
1454
1448
  progressColor = _ref.progressColor,
@@ -1459,86 +1453,45 @@ var SeekBar = function SeekBar(_ref) {
1459
1453
  className = _ref.className,
1460
1454
  withSeekHead = _ref.withSeekHead;
1461
1455
  var intl = reactIntl.useIntl();
1462
-
1463
- var _useSpring = core.useSpring(function () {
1464
- return {
1465
- x: currentTime !== null && duration !== null ? currentTime / duration : 0,
1466
- config: {
1467
- duration: 0
1468
- }
1469
- };
1470
- }),
1471
- _useSpring2 = _slicedToArray__default["default"](_useSpring, 2),
1472
- springProps = _useSpring2[0],
1473
- setSpringProps = _useSpring2[1];
1474
-
1475
- var lastMediaRef = React.useRef(media);
1476
- var mediaChanged = lastMediaRef.current !== media;
1477
- lastMediaRef.current = media;
1478
-
1479
- var _useDimensionObserver = hooks.useDimensionObserver(),
1480
- elRef = _useDimensionObserver.ref,
1481
- _useDimensionObserver2 = _useDimensionObserver.width,
1482
- elWidth = _useDimensionObserver2 === void 0 ? null : _useDimensionObserver2;
1483
-
1484
- React.useEffect(function () {
1485
- if (currentTime === null || duration === null) {
1456
+ var progress = hooks.useMediaProgress(media, {
1457
+ disabled: !playing
1458
+ });
1459
+ var onDrag = React.useCallback(function (_ref2) {
1460
+ var _ref2$xy = _slicedToArray__default["default"](_ref2.xy, 1),
1461
+ x = _ref2$xy[0],
1462
+ elapsedTime = _ref2.elapsedTime,
1463
+ active = _ref2.active,
1464
+ tap = _ref2.tap,
1465
+ currentTarget = _ref2.currentTarget;
1466
+
1467
+ if (!active && elapsedTime > 300) {
1486
1468
  return;
1487
1469
  }
1488
1470
 
1489
- var progress = duration > 0 ? currentTime / duration : 0;
1490
- setSpringProps.start({
1491
- reset: true,
1492
- immediate: !playing || mediaChanged,
1493
- from: {
1494
- x: progress
1495
- },
1496
- to: {
1497
- x: playing ? 1 : progress
1498
- },
1499
- config: {
1500
- duration: (duration - currentTime) * 1000
1501
- }
1502
- });
1503
- }, [playing, duration, currentTime, mediaChanged, setSpringProps]); // User events
1471
+ var _currentTarget$getBou = currentTarget.getBoundingClientRect(),
1472
+ elX = _currentTarget$getBou.left,
1473
+ elWidth = _currentTarget$getBou.width;
1504
1474
 
1505
- var seekFromX = React.useCallback(function (x) {
1506
- var elX = elRef.current.getBoundingClientRect().left;
1507
- var progress = Math.max(0, Math.min(1, (x - elX) / elWidth));
1508
- setSpringProps.start({
1509
- immediate: true,
1510
- to: {
1511
- x: progress
1512
- }
1513
- });
1475
+ var newProgress = Math.max(0, Math.min(1, (x - elX) / elWidth));
1514
1476
 
1515
- if (onSeek !== null && duration !== null) {
1516
- onSeek(progress * duration);
1477
+ if (onSeek !== null) {
1478
+ onSeek(newProgress, tap);
1517
1479
  }
1518
- }, [duration, playing, onSeek]);
1519
- var bind = react.useGesture({
1520
- onDrag: function onDrag(_ref2) {
1521
- var _ref2$xy = _slicedToArray__default["default"](_ref2.xy, 1),
1522
- x = _ref2$xy[0],
1523
- elapsedTime = _ref2.elapsedTime,
1524
- active = _ref2.active;
1525
-
1526
- if (!active && elapsedTime > 300) {
1527
- return;
1528
- }
1529
-
1530
- seekFromX(x);
1531
- },
1532
- onPointerDown: function onPointerDown() {
1533
- if (onSeekStart !== null) {
1534
- onSeekStart();
1535
- }
1536
- },
1537
- onPointerUp: function onPointerUp() {
1538
- if (onSeekEnd !== null) {
1539
- onSeekEnd();
1540
- }
1480
+ }, [onSeek]);
1481
+ var onDragStart = React.useCallback(function () {
1482
+ if (onSeekStart !== null) {
1483
+ onSeekStart();
1541
1484
  }
1485
+ }, [onSeekStart]);
1486
+ var onDragEnd = React.useCallback(function () {
1487
+ if (onSeekEnd !== null) {
1488
+ onSeekEnd();
1489
+ }
1490
+ }, [onSeekEnd]);
1491
+ var bind = react.useGesture({
1492
+ onDrag: onDrag,
1493
+ onDragStart: onDragStart,
1494
+ onDragEnd: onDragEnd
1542
1495
  }, {
1543
1496
  drag: {
1544
1497
  axis: 'x',
@@ -1554,25 +1507,19 @@ var SeekBar = function SeekBar(_ref) {
1554
1507
  style: {
1555
1508
  backgroundColor: backgroundColor
1556
1509
  }
1557
- }, /*#__PURE__*/React__default["default"].createElement(web.animated.div, {
1510
+ }, /*#__PURE__*/React__default["default"].createElement("div", {
1558
1511
  className: styles$2.playHead,
1559
1512
  style: {
1560
- left: springProps.x.to(function (x) {
1561
- return "".concat(x * 100, "%");
1562
- }),
1513
+ left: "".concat(progress * 100, "%"),
1563
1514
  backgroundColor: progressColor
1564
1515
  }
1565
- }), /*#__PURE__*/React__default["default"].createElement(web.animated.div, {
1516
+ }), /*#__PURE__*/React__default["default"].createElement("div", {
1566
1517
  className: styles$2.progress,
1567
1518
  style: {
1568
- transform: springProps.x.to(function (x) {
1569
- return "scaleX(".concat(x, ")");
1570
- }),
1519
+ transform: "scaleX(".concat(progress, ")"),
1571
1520
  backgroundColor: progressColor
1572
1521
  }
1573
- })), /*#__PURE__*/React__default["default"].createElement("button", Object.assign({
1574
- ref: elRef
1575
- }, bind(), {
1522
+ })), /*#__PURE__*/React__default["default"].createElement("button", Object.assign({}, bind(), {
1576
1523
  type: "button",
1577
1524
  className: styles$2.track,
1578
1525
  title: intl.formatMessage({
@@ -1627,12 +1574,6 @@ function PlaybackControls(_ref) {
1627
1574
  controlsTheme = _usePlaybackContext.controlsTheme,
1628
1575
  showControls = _usePlaybackContext.showControls;
1629
1576
 
1630
- var duration = hooks.useMediaDuration(mediaElement);
1631
- var currentTime = hooks.useMediaCurrentTime(mediaElement, {
1632
- disabled: !playing,
1633
- updateInterval: 100
1634
- });
1635
-
1636
1577
  var _useState = React.useState(null),
1637
1578
  _useState2 = _slicedToArray__default["default"](_useState, 2),
1638
1579
  customControlsTheme = _useState2[0],
@@ -1685,20 +1626,25 @@ function PlaybackControls(_ref) {
1685
1626
  }, [setMuted, controlsVisible, showControls]);
1686
1627
  var onSeekStart = React.useCallback(function () {
1687
1628
  setWasPlaying(playing);
1688
- if (playing) setPlaying(false);
1689
- }, [playing, setWasPlaying]);
1690
- var onSeek = React.useCallback(function (time) {
1629
+
1630
+ if (playing) {
1631
+ setPlaying(false);
1632
+ }
1633
+ }, [playing, setWasPlaying, setPlaying]);
1634
+ var onSeek = React.useCallback(function (progress) {
1691
1635
  if (mediaElement !== null) {
1692
- mediaElement.currentTime = time;
1636
+ mediaElement.currentTime = progress * mediaElement.duration;
1693
1637
  }
1694
1638
 
1695
1639
  if (!controlsVisible && controls) {
1696
1640
  showControls();
1697
1641
  }
1698
- }, [mediaElement, controlsVisible, controls, setWasPlaying, playing, setPlaying, showControls]);
1642
+ }, [mediaElement, controlsVisible, controls, showControls]);
1699
1643
  var onSeekEnd = React.useCallback(function () {
1700
- if (wasPlaying) setPlaying(true);
1701
- }, [playing, setPlaying, wasPlaying]);
1644
+ if (wasPlaying) {
1645
+ setPlaying(true);
1646
+ }
1647
+ }, [setPlaying, wasPlaying]);
1702
1648
  var mediaHasAudio = mediaElement !== null && (hasAudio === null || hasAudio === true);
1703
1649
 
1704
1650
  var _ref3 = customControlsTheme || {},
@@ -1736,10 +1682,8 @@ function PlaybackControls(_ref) {
1736
1682
  icon: playing ? faPause.faPause : faPlay.faPlay
1737
1683
  })), /*#__PURE__*/React__default["default"].createElement(SeekBar, {
1738
1684
  className: styles$1.seekBar,
1739
- duration: duration,
1740
- currentTime: currentTime,
1741
- playing: playing,
1742
1685
  media: mediaElement,
1686
+ playing: playing,
1743
1687
  onSeek: onSeek,
1744
1688
  onSeekStart: onSeekStart,
1745
1689
  onSeekEnd: onSeekEnd,
@@ -2336,6 +2280,7 @@ var Viewer = function Viewer(_ref) {
2336
2280
  }, /*#__PURE__*/React__default["default"].createElement(contexts.ScreenSizeProvider, {
2337
2281
  size: screenSize
2338
2282
  }, /*#__PURE__*/React__default["default"].createElement(contexts.ViewerProvider, {
2283
+ containerRef: containerRef,
2339
2284
  events: eventsManager,
2340
2285
  menuVisible: menuVisible,
2341
2286
  menuOverScreen: menuOverScreen,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micromag/viewer",
3
- "version": "0.3.161",
3
+ "version": "0.3.162",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "javascript"
@@ -59,13 +59,13 @@
59
59
  "@fortawesome/fontawesome-svg-core": "^1.2.32",
60
60
  "@fortawesome/free-solid-svg-icons": "^5.15.1",
61
61
  "@fortawesome/react-fontawesome": "^0.1.13",
62
- "@micromag/core": "^0.3.161",
63
- "@micromag/element-scroll": "^0.3.161",
64
- "@micromag/element-share-options": "^0.3.161",
65
- "@micromag/elements": "^0.3.161",
66
- "@micromag/fields": "^0.3.161",
67
- "@micromag/intl": "^0.3.161",
68
- "@micromag/screens": "^0.3.161",
62
+ "@micromag/core": "^0.3.162",
63
+ "@micromag/element-scroll": "^0.3.162",
64
+ "@micromag/element-share-options": "^0.3.162",
65
+ "@micromag/elements": "^0.3.162",
66
+ "@micromag/fields": "^0.3.162",
67
+ "@micromag/intl": "^0.3.162",
68
+ "@micromag/screens": "^0.3.162",
69
69
  "@react-spring/core": "^9.1.1",
70
70
  "@react-spring/web": "^9.1.1",
71
71
  "@use-gesture/react": "^10.2.4",
@@ -83,5 +83,5 @@
83
83
  "publishConfig": {
84
84
  "access": "public"
85
85
  },
86
- "gitHead": "badf4dd6125aa3169f3d01afc712be5b8181d898"
86
+ "gitHead": "ffd4deb967b364580d292f0e02af777abac02967"
87
87
  }