Add volumes
This commit is contained in:
parent
fa26db923c
commit
f756bc9626
actions
components
graph
input
label
number-input
preferences
slider
volume-slider
reducers
store
utils
yarn.lock
|
@ -21,5 +21,15 @@ module.exports = createActionCreators({
|
|||
KILL_SOURCE_OUTPUT_BY_INDEX: sourceOutputIndex => ({ sourceOutputIndex }),
|
||||
|
||||
UNLOAD_MODULE_BY_INDEX: moduleIndex => ({ moduleIndex }),
|
||||
|
||||
SET_SINK_VOLUMES: (index, channelVolumes) => ({ index, channelVolumes }),
|
||||
SET_SOURCE_VOLUMES: (index, channelVolumes) => ({ index, channelVolumes }),
|
||||
SET_SINK_INPUT_VOLUMES: (index, channelVolumes) => ({ index, channelVolumes }),
|
||||
SET_SOURCE_OUTPUT_VOLUMES: (index, channelVolumes) => ({ index, channelVolumes }),
|
||||
|
||||
SET_SINK_CHANNEL_VOLUME: (index, channelIndex, volume) => ({ index, channelIndex, volume }),
|
||||
SET_SOURCE_CHANNEL_VOLUME: (index, channelIndex, volume) => ({ index, channelIndex, volume }),
|
||||
SET_SINK_INPUT_CHANNEL_VOLUME: (index, channelIndex, volume) => ({ index, channelIndex, volume }),
|
||||
SET_SOURCE_OUTPUT_CHANNEL_VOLUME: (index, channelIndex, volume) => ({ index, channelIndex, volume }),
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ const r = require('r-dom');
|
|||
|
||||
const {
|
||||
GraphView: GraphViewBase,
|
||||
Node: NodeBase,
|
||||
Edge: EdgeBase,
|
||||
GraphUtils,
|
||||
} = require('react-digraph');
|
||||
|
@ -18,8 +19,11 @@ class GraphView extends GraphViewBase {
|
|||
_super_handleNodeMove: this.handleNodeMove,
|
||||
handleNodeMove: this.constructor.prototype.handleNodeMove.bind(this),
|
||||
|
||||
_super_getEdgeComponent: this.handleNodeMove,
|
||||
_super_getEdgeComponent: this.getEdgeComponent,
|
||||
getEdgeComponent: this.constructor.prototype.getEdgeComponent.bind(this),
|
||||
|
||||
_super_getNodeComponent: this.getNodeComponent,
|
||||
getNodeComponent: this.constructor.prototype.getNodeComponent.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,6 +49,29 @@ class GraphView extends GraphViewBase {
|
|||
super.componentDidUpdate(prevProps, prevState);
|
||||
}
|
||||
|
||||
getNodeComponent(id, node) {
|
||||
const { nodeTypes, nodeSubtypes, nodeSize, renderNode, renderNodeText, nodeKey } = this.props;
|
||||
return r(Node, {
|
||||
key: id,
|
||||
id,
|
||||
data: node,
|
||||
nodeTypes,
|
||||
nodeSize,
|
||||
nodeKey,
|
||||
nodeSubtypes,
|
||||
onNodeMouseEnter: this.handleNodeMouseEnter,
|
||||
onNodeMouseLeave: this.handleNodeMouseLeave,
|
||||
onNodeMove: this.handleNodeMove,
|
||||
onNodeUpdate: this.handleNodeUpdate,
|
||||
onNodeSelected: this.handleNodeSelected,
|
||||
renderNode,
|
||||
renderNodeText,
|
||||
isSelected: this.state.selectedNodeObj.node === node,
|
||||
layoutEngine: this.layoutEngine,
|
||||
viewWrapperElem: this.viewWrapper.current,
|
||||
});
|
||||
}
|
||||
|
||||
handleNodeMove(position, nodeId, shiftKey) {
|
||||
this._super_handleNodeMove(position, nodeId, shiftKey);
|
||||
if (this.props.onNodeMove) {
|
||||
|
@ -52,7 +79,7 @@ class GraphView extends GraphViewBase {
|
|||
}
|
||||
}
|
||||
|
||||
getEdgeComponent(edge) {
|
||||
getEdgeComponent(edge, nodeMoving) {
|
||||
if (!this.props.renderEdge) {
|
||||
return this._super_getEdgeComponent(edge);
|
||||
}
|
||||
|
@ -74,13 +101,46 @@ class GraphView extends GraphViewBase {
|
|||
targetNode: targetNode || targetPosition,
|
||||
nodeKey,
|
||||
isSelected: selected,
|
||||
nodeMoving,
|
||||
renderEdgeText,
|
||||
});
|
||||
}
|
||||
|
||||
syncRenderEdge(edge, nodeMoving = false) {
|
||||
if (!edge.source) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idVar = edge.target ? `${edge.source}-${edge.target}` : 'custom';
|
||||
const id = `edge-${idVar}`;
|
||||
const element = this.getEdgeComponent(edge, nodeMoving);
|
||||
this.renderEdge(id, element, edge, nodeMoving);
|
||||
|
||||
if (this.isEdgeSelected(edge)) {
|
||||
const container = document.getElementById(`${id}-container`);
|
||||
container.parentNode.appendChild(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const size = 120;
|
||||
|
||||
class Node extends NodeBase {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
Object.assign(this, {
|
||||
_super_handleDragEnd: this.handleDragEnd,
|
||||
handleDragEnd: this.constructor.prototype.handleDragEnd.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
handleDragEnd(...args) {
|
||||
this.oldSibling = null;
|
||||
return this._super_handleDragEnd(...args);
|
||||
}
|
||||
}
|
||||
|
||||
EdgeBase.calculateOffset = function (nodeSize, source, target) {
|
||||
const arrowVector = math.matrix([ target.x - source.x, target.y - source.y ]);
|
||||
const offsetLength = Math.max(0, Math.min((0.75 * size), (math.norm(arrowVector) / 2) - 40));
|
||||
|
@ -112,10 +172,6 @@ class Edge extends EdgeBase {
|
|||
className: 'edge-path',
|
||||
d: this.getPathDescription(data) || undefined,
|
||||
}),
|
||||
this.props.renderEdgeText && r(this.props.renderEdgeText, {
|
||||
data,
|
||||
transform: this.getEdgeHandleTranslation(),
|
||||
}),
|
||||
]),
|
||||
r.g({
|
||||
className: 'edge-mouse-handler',
|
||||
|
@ -128,6 +184,11 @@ class Edge extends EdgeBase {
|
|||
'data-target': data.target,
|
||||
d: this.getPathDescription(data) || undefined,
|
||||
}),
|
||||
this.props.renderEdgeText && !this.props.nodeMoving && r(this.props.renderEdgeText, {
|
||||
data,
|
||||
transform: this.getEdgeHandleTranslation(),
|
||||
selected: this.props.isSelected,
|
||||
}),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ const {
|
|||
map,
|
||||
values,
|
||||
flatten,
|
||||
memoizeWith,
|
||||
path,
|
||||
filter,
|
||||
forEach,
|
||||
merge,
|
||||
repeat,
|
||||
} = require('ramda');
|
||||
|
||||
const React = require('react');
|
||||
|
@ -18,6 +18,7 @@ const { connect } = require('react-redux');
|
|||
const { bindActionCreators } = require('redux');
|
||||
|
||||
const d = require('../../utils/d');
|
||||
const memoize = require('../../utils/memoize');
|
||||
|
||||
const {
|
||||
pulse: pulseActions,
|
||||
|
@ -30,6 +31,8 @@ const {
|
|||
PA_VOLUME_NORM,
|
||||
} = require('../../constants/pulse');
|
||||
|
||||
const VolumeSlider = require('../../components/volume-slider');
|
||||
|
||||
const {
|
||||
GraphView,
|
||||
} = require('./satellites-graph');
|
||||
|
@ -38,18 +41,8 @@ const {
|
|||
Edge,
|
||||
} = require('./base');
|
||||
|
||||
const weakmapId_ = new WeakMap();
|
||||
const weakmapId = o => {
|
||||
if (!weakmapId_.has(o)) {
|
||||
weakmapId_.set(o, String(Math.random()));
|
||||
}
|
||||
return weakmapId_.get(o);
|
||||
};
|
||||
|
||||
const dgoToPai = new WeakMap();
|
||||
|
||||
const memoize = memoizeWith(weakmapId);
|
||||
|
||||
const key = pao => `${pao.type}-${pao.index}`;
|
||||
|
||||
const sourceKey = pai => {
|
||||
|
@ -72,12 +65,12 @@ const paoToNode = memoize(pao => ({
|
|||
type: pao.type,
|
||||
}));
|
||||
|
||||
const paiToEdge = memoize(pai => ({
|
||||
id: key(pai),
|
||||
source: sourceKey(pai),
|
||||
target: targetKey(pai),
|
||||
index: pai.index,
|
||||
type: pai.type,
|
||||
const paoToEdge = memoize(pao => ({
|
||||
id: key(pao),
|
||||
source: sourceKey(pao),
|
||||
target: targetKey(pao),
|
||||
index: pao.index,
|
||||
type: pao.type,
|
||||
}));
|
||||
|
||||
const getPaiIcon = memoize(pai => {
|
||||
|
@ -211,12 +204,26 @@ const renderNode = (nodeRef, data, key, selected, hovered) => r({
|
|||
hovered,
|
||||
});
|
||||
|
||||
const getVolumesForThumbnail = ({ pai, state }) => {
|
||||
const { lockChannelsTogether } = state.preferences;
|
||||
let volumes = (pai && pai.channelVolumes) || [];
|
||||
if (lockChannelsTogether) {
|
||||
if (volumes.every(v => v === volumes[0])) {
|
||||
volumes = [
|
||||
volumes.reduce((a, b) => Math.max(a, b)),
|
||||
];
|
||||
}
|
||||
}
|
||||
return volumes;
|
||||
};
|
||||
|
||||
const VolumeThumbnail = ({ pai, state }) => {
|
||||
if (state.preferences.hideVolumeThumbnails) {
|
||||
return r(React.Fragment);
|
||||
}
|
||||
const { baseVolume } = pai;
|
||||
|
||||
const volumes = (pai && pai.channelVolumes) || [];
|
||||
const volumes = getVolumesForThumbnail({ pai, state });
|
||||
const muted = !pai || pai.muted;
|
||||
|
||||
const step = size / 32;
|
||||
|
@ -229,6 +236,7 @@ const VolumeThumbnail = ({ pai, state }) => {
|
|||
'volume-thumbnail': true,
|
||||
'volume-thumbnail-muted': muted,
|
||||
},
|
||||
height: (2 * padding) + height,
|
||||
}, [
|
||||
r.line({
|
||||
className: 'volume-thumbnail-ruler-line',
|
||||
|
@ -238,6 +246,14 @@ const VolumeThumbnail = ({ pai, state }) => {
|
|||
y2: padding + height,
|
||||
}),
|
||||
|
||||
baseVolume && r.line({
|
||||
className: 'volume-thumbnail-ruler-line',
|
||||
x1: padding + ((baseVolume / PA_VOLUME_NORM) * width),
|
||||
x2: padding + ((baseVolume / PA_VOLUME_NORM) * width),
|
||||
y1: padding,
|
||||
y2: padding + height,
|
||||
}),
|
||||
|
||||
r.line({
|
||||
className: 'volume-thumbnail-ruler-line',
|
||||
x1: padding + width,
|
||||
|
@ -256,6 +272,63 @@ const VolumeThumbnail = ({ pai, state }) => {
|
|||
]);
|
||||
};
|
||||
|
||||
const getVolumes = ({ pai, state }) => {
|
||||
const { lockChannelsTogether } = state.preferences;
|
||||
let volumes = (pai && pai.channelVolumes) || [];
|
||||
if (lockChannelsTogether) {
|
||||
volumes = [
|
||||
volumes.reduce((a, b) => Math.max(a, b)),
|
||||
];
|
||||
}
|
||||
return { volumes, lockChannelsTogether };
|
||||
};
|
||||
|
||||
const VolumeControls = ({ pai, state }) => {
|
||||
const { maxVolume } = state.preferences;
|
||||
const { volumes, lockChannelsTogether } = getVolumes({ pai, state });
|
||||
const baseVolume = pai && pai.baseVolume;
|
||||
const muted = !pai || pai.muted;
|
||||
|
||||
return r.div({
|
||||
className: 'volume-controls',
|
||||
}, [
|
||||
...volumes.map((v, channelIndex) => r(VolumeSlider, {
|
||||
muted,
|
||||
baseVolume,
|
||||
normVolume: PA_VOLUME_NORM,
|
||||
maxVolume: PA_VOLUME_NORM * maxVolume,
|
||||
value: v,
|
||||
onChange: v => {
|
||||
if (pai.type === 'sink') {
|
||||
if (lockChannelsTogether) {
|
||||
state.setSinkVolumes(pai.index, repeat(v, pai.sampleSpec.channels));
|
||||
} else {
|
||||
state.setSinkChannelVolume(pai.index, channelIndex, v);
|
||||
}
|
||||
} else if (pai.type === 'source') {
|
||||
if (lockChannelsTogether) {
|
||||
state.setSourceVolumes(pai.index, repeat(v, pai.sampleSpec.channels));
|
||||
} else {
|
||||
state.setSourceChannelVolume(pai.index, channelIndex, v);
|
||||
}
|
||||
} else if (pai.type === 'sinkInput') {
|
||||
if (lockChannelsTogether) {
|
||||
state.setSinkInputVolumes(pai.index, repeat(v, pai.sampleSpec.channels));
|
||||
} else {
|
||||
state.setSinkInputChannelVolume(pai.index, channelIndex, v);
|
||||
}
|
||||
} else if (pai.type === 'sourceOutput') {
|
||||
if (lockChannelsTogether) {
|
||||
state.setSourceOutputVolumes(pai.index, repeat(v, pai.sampleSpec.channels));
|
||||
} else {
|
||||
state.setSourceOutputChannelVolume(pai.index, channelIndex, v);
|
||||
}
|
||||
}
|
||||
},
|
||||
})),
|
||||
]);
|
||||
};
|
||||
|
||||
const DebugText = ({ dgo, pai, state }) => r.div({
|
||||
style: {
|
||||
fontSize: '50%',
|
||||
|
@ -265,21 +338,23 @@ const DebugText = ({ dgo, pai, state }) => r.div({
|
|||
JSON.stringify(pai, null, 2),
|
||||
] : []);
|
||||
|
||||
const SinkText = ({ dgo, pai, state }) => r.div([
|
||||
const SinkText = ({ dgo, pai, state, selected }) => r.div([
|
||||
r.div({
|
||||
className: 'node-name',
|
||||
title: pai.name,
|
||||
}, pai.description),
|
||||
r(VolumeThumbnail, { pai, state }),
|
||||
!selected && r(VolumeThumbnail, { pai, state }),
|
||||
selected && r(VolumeControls, { pai, state }),
|
||||
r(DebugText, { dgo, pai, state }),
|
||||
]);
|
||||
|
||||
const SourceText = ({ dgo, pai, state }) => r.div([
|
||||
const SourceText = ({ dgo, pai, state, selected }) => r.div([
|
||||
r.div({
|
||||
className: 'node-name',
|
||||
title: pai.name,
|
||||
}, pai.description),
|
||||
r(VolumeThumbnail, { pai, state }),
|
||||
!selected && r(VolumeThumbnail, { pai, state }),
|
||||
selected && r(VolumeControls, { pai, state }),
|
||||
r(DebugText, { dgo, pai, state }),
|
||||
]);
|
||||
|
||||
|
@ -299,7 +374,7 @@ const ModuleText = ({ dgo, pai, state }) => r.div([
|
|||
r(DebugText, { dgo, pai, state }),
|
||||
]);
|
||||
|
||||
const renderNodeText = state => dgo => r('foreignObject', {
|
||||
const renderNodeText = state => (dgo, i, selected) => r('foreignObject', {
|
||||
x: -s2,
|
||||
y: -s2,
|
||||
}, r.div({
|
||||
|
@ -319,6 +394,7 @@ const renderNodeText = state => dgo => r('foreignObject', {
|
|||
dgo,
|
||||
pai: dgoToPai.get(dgo),
|
||||
state,
|
||||
selected,
|
||||
})));
|
||||
|
||||
const renderEdge = props => r(Edge, {
|
||||
|
@ -328,7 +404,7 @@ const renderEdge = props => r(Edge, {
|
|||
...props,
|
||||
});
|
||||
|
||||
const renderEdgeText = state => ({ data: dgo, transform }) => {
|
||||
const renderEdgeText = state => ({ data: dgo, transform, selected }) => {
|
||||
const pai = dgo.type && getPaiByTypeAndIndex(dgo.type, dgo.index)({ pulse: state });
|
||||
|
||||
return r('foreignObject', {
|
||||
|
@ -340,7 +416,8 @@ const renderEdgeText = state => ({ data: dgo, transform }) => {
|
|||
height: size,
|
||||
},
|
||||
}, [
|
||||
pai && r(VolumeThumbnail, { pai, state }),
|
||||
pai && (!selected) && r(VolumeThumbnail, { pai, state }),
|
||||
pai && selected && r(VolumeControls, { pai, state }),
|
||||
r(DebugText, { dgo, pai, state }),
|
||||
]));
|
||||
};
|
||||
|
@ -444,9 +521,9 @@ class Graph extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
let edges = map(paiToEdge, flatten(map(values, [
|
||||
this.props.infos.sinkInputs,
|
||||
this.props.infos.sourceOutputs,
|
||||
let edges = map(paoToEdge, flatten(map(values, [
|
||||
this.props.objects.sinkInputs,
|
||||
this.props.objects.sourceOutputs,
|
||||
])));
|
||||
|
||||
const connectedNodeKeys = new Set();
|
||||
|
|
|
@ -5,9 +5,6 @@ const {
|
|||
prop,
|
||||
groupBy,
|
||||
flatten,
|
||||
addIndex,
|
||||
mapObjIndexed,
|
||||
values,
|
||||
} = require('ramda');
|
||||
|
||||
const React = require('react');
|
||||
|
@ -16,11 +13,39 @@ const r = require('r-dom');
|
|||
|
||||
const plusMinus = require('../../utils/plus-minus');
|
||||
|
||||
const memoize = require('../../utils/memoize');
|
||||
|
||||
const {
|
||||
GraphView: GraphViewBase,
|
||||
} = require('./base');
|
||||
|
||||
const mapIndexed = addIndex(map);
|
||||
const originalEdgeToSatelliteNode = edge => ({
|
||||
id: `${edge.target}__satellite__${edge.id}`,
|
||||
edge: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
type: 'satellite',
|
||||
});
|
||||
|
||||
const originalEdgeAndSatelliteNodeToSatelliteEdge = (edge, satelliteNode) => {
|
||||
const satelliteEdge = {
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: satelliteNode.id,
|
||||
originalTarget: edge.target,
|
||||
index: edge.index,
|
||||
type: edge.type,
|
||||
};
|
||||
|
||||
satelliteEdgeToOriginalEdge.set(satelliteEdge, edge);
|
||||
return satelliteEdge;
|
||||
};
|
||||
|
||||
const originalEdgeToSatellites = memoize(edge => {
|
||||
const satelliteNode = originalEdgeToSatelliteNode(edge);
|
||||
const satelliteEdge = originalEdgeAndSatelliteNodeToSatelliteEdge(edge, satelliteNode);
|
||||
return { satelliteEdge, satelliteNode };
|
||||
});
|
||||
|
||||
const Satellite = () => r(React.Fragment);
|
||||
|
||||
|
@ -33,8 +58,10 @@ class GraphView extends React.Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
edgesByTargetNodeKey: {},
|
||||
originalEdgesByTargetNodeKey: {},
|
||||
satelliteNodesByTargetNodeKey: {},
|
||||
satelliteEdges: [],
|
||||
selected: null,
|
||||
};
|
||||
|
||||
this.graph = React.createRef();
|
||||
|
@ -48,23 +75,41 @@ class GraphView extends React.Component {
|
|||
renderNode: this.renderNode.bind(this),
|
||||
renderNodeText: this.renderNodeText.bind(this),
|
||||
|
||||
renderEdge: this.renderEdge.bind(this),
|
||||
renderEdgeText: this.renderEdgeText.bind(this),
|
||||
|
||||
afterRenderEdge: this.afterRenderEdge.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props) {
|
||||
const { nodeKey, edgeKey } = props;
|
||||
const originalEdgesByTargetNodeKey = groupBy(prop('target'), props.edges);
|
||||
|
||||
const edgesByTargetNodeKey = groupBy(prop('target'), props.edges);
|
||||
const satelliteNodesByTargetNodeKey = map(map(edge => ({
|
||||
[nodeKey]: `${edge.target}__satellite__${edge[edgeKey]}`,
|
||||
edge: edge[edgeKey],
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
type: 'satellite',
|
||||
})), edgesByTargetNodeKey);
|
||||
let { selected } = props;
|
||||
|
||||
return { edgesByTargetNodeKey, satelliteNodesByTargetNodeKey };
|
||||
const satelliteEdges = [];
|
||||
|
||||
const satelliteNodesByTargetNodeKey = map(edges => map(edge => {
|
||||
const {
|
||||
satelliteNode,
|
||||
satelliteEdge,
|
||||
} = originalEdgeToSatellites(edge);
|
||||
|
||||
if (edge === selected) {
|
||||
selected = satelliteEdge;
|
||||
}
|
||||
|
||||
satelliteEdges.push(satelliteEdge);
|
||||
|
||||
return satelliteNode;
|
||||
}, edges), originalEdgesByTargetNodeKey);
|
||||
|
||||
return {
|
||||
originalEdgesByTargetNodeKey,
|
||||
satelliteNodesByTargetNodeKey,
|
||||
satelliteEdges,
|
||||
selected,
|
||||
};
|
||||
}
|
||||
|
||||
static repositionSatellites(position, satelliteNodes) {
|
||||
|
@ -119,6 +164,14 @@ class GraphView extends React.Component {
|
|||
return r(React.Fragment);
|
||||
}
|
||||
|
||||
renderEdge(...args) {
|
||||
return this.props.renderEdge(...args);
|
||||
}
|
||||
|
||||
renderEdgeText(...args) {
|
||||
return this.props.renderEdgeText(...args);
|
||||
}
|
||||
|
||||
afterRenderEdge(id, element, edge, edgeContainer) {
|
||||
const originalEdge = satelliteEdgeToOriginalEdge.get(edge);
|
||||
this.props.afterRenderEdge(id, element, originalEdge || edge, edgeContainer);
|
||||
|
@ -126,7 +179,11 @@ class GraphView extends React.Component {
|
|||
|
||||
render() {
|
||||
const { nodeKey } = this.props;
|
||||
const { edgesByTargetNodeKey, satelliteNodesByTargetNodeKey } = this.state;
|
||||
const {
|
||||
satelliteNodesByTargetNodeKey,
|
||||
satelliteEdges: edges,
|
||||
selected,
|
||||
} = this.state;
|
||||
|
||||
const nodes = flatten(map(node => {
|
||||
const satelliteNodes = satelliteNodesByTargetNodeKey[node[nodeKey]] || [];
|
||||
|
@ -134,26 +191,6 @@ class GraphView extends React.Component {
|
|||
return satelliteNodes.concat(node);
|
||||
}, this.props.nodes));
|
||||
|
||||
let { selected } = this.props;
|
||||
|
||||
const edges = flatten(values(mapObjIndexed((edges, target) => mapIndexed((edge, i) => {
|
||||
const satelliteEdge = {
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: satelliteNodesByTargetNodeKey[target][i][nodeKey],
|
||||
originalTarget: edge.target,
|
||||
index: edge.index,
|
||||
type: edge.type,
|
||||
};
|
||||
|
||||
if (edge === selected) {
|
||||
selected = satelliteEdge;
|
||||
}
|
||||
|
||||
satelliteEdgeToOriginalEdge.set(satelliteEdge, edge);
|
||||
return satelliteEdge;
|
||||
}, edges), edgesByTargetNodeKey)));
|
||||
|
||||
return r(GraphViewBase, {
|
||||
...this.props,
|
||||
|
||||
|
@ -172,6 +209,9 @@ class GraphView extends React.Component {
|
|||
renderNode: this.renderNode,
|
||||
renderNodeText: this.renderNodeText,
|
||||
|
||||
renderEdge: this.renderEdge,
|
||||
renderEdgeText: this.renderEdgeText,
|
||||
|
||||
afterRenderEdge: this.props.afterRenderEdge && this.afterRenderEdge,
|
||||
});
|
||||
}
|
||||
|
|
7
components/input/index.js
Normal file
7
components/input/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
const r = require('r-dom');
|
||||
|
||||
module.exports = props => r.input({
|
||||
className: 'input',
|
||||
...props,
|
||||
}, props.children);
|
6
components/label/index.js
Normal file
6
components/label/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
const r = require('r-dom');
|
||||
|
||||
module.exports = props => r.label({
|
||||
className: 'label',
|
||||
}, props.children);
|
10
components/number-input/index.js
Normal file
10
components/number-input/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
const r = require('r-dom');
|
||||
|
||||
const Label = require('../label');
|
||||
const Input = require('../input');
|
||||
|
||||
module.exports = props => r(Label, [
|
||||
...[].concat(props.children),
|
||||
r(Input, props),
|
||||
]);
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
const {
|
||||
pick,
|
||||
defaultTo,
|
||||
} = require('ramda');
|
||||
|
||||
const r = require('r-dom');
|
||||
|
@ -14,6 +15,7 @@ const { preferences: preferencesActions } = require('../../actions');
|
|||
|
||||
const Button = require('../button');
|
||||
const Checkbox = require('../checkbox');
|
||||
const NumberInput = require('../number-input');
|
||||
|
||||
const Preferences = withStateHandlers(
|
||||
{
|
||||
|
@ -84,6 +86,24 @@ const Preferences = withStateHandlers(
|
|||
}, 'Hide volume thumbnails'),
|
||||
]),
|
||||
|
||||
r.div([
|
||||
r(Checkbox, {
|
||||
checked: props.preferences.lockChannelsTogether,
|
||||
onChange: () => props.actions.toggle('lockChannelsTogether'),
|
||||
}, 'Lock channels together'),
|
||||
]),
|
||||
|
||||
r.div([
|
||||
r(NumberInput, {
|
||||
type: 'number',
|
||||
value: defaultTo(150, Math.round(props.preferences.maxVolume * 100)),
|
||||
onChange: e => {
|
||||
const v = defaultTo(150, Math.max(0, parseInt(e.target.value, 10)));
|
||||
props.actions.set({ maxVolume: v / 100 });
|
||||
},
|
||||
}, 'Maximum volume: '),
|
||||
]),
|
||||
|
||||
r.div([
|
||||
r(Checkbox, {
|
||||
checked: props.preferences.showDebugInfo,
|
||||
|
|
4
components/slider/index.js
Normal file
4
components/slider/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
module.exports = class Slider {
|
||||
|
||||
};
|
167
components/volume-slider/index.js
Normal file
167
components/volume-slider/index.js
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* global document */
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const r = require('r-dom');
|
||||
|
||||
const width = 300;
|
||||
const height = 18;
|
||||
|
||||
const clamp = x => Math.min(
|
||||
width - (height / 2),
|
||||
Math.max(
|
||||
(height / 2),
|
||||
x,
|
||||
),
|
||||
);
|
||||
|
||||
const vol2pix = (v, maxVolume) => (v / maxVolume) * (width - height);
|
||||
const pix2vol = (x, maxVolume) => (x * maxVolume) / (width - height);
|
||||
|
||||
module.exports = class VolumeSlider extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.svg = React.createRef();
|
||||
|
||||
this.state = {
|
||||
draggingX: null,
|
||||
};
|
||||
|
||||
Object.assign(this, {
|
||||
handlePointerDown: this.handlePointerDown.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.svg.current.addEventListener('pointerdown', this.handlePointerDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.svg.current.removeEventListener('pointerdown', this.handlePointerDown);
|
||||
}
|
||||
|
||||
handlePointerDown(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const originX = e.clientX - e.offsetX;
|
||||
|
||||
const move = e => {
|
||||
if (this.state.draggingX !== null) {
|
||||
this.setState({
|
||||
draggingX: clamp(e.clientX - originX),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const up = e => {
|
||||
this.setState({
|
||||
draggingX: null,
|
||||
});
|
||||
|
||||
document.removeEventListener('pointermove', move);
|
||||
document.removeEventListener('pointerup', up);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const click = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
document.removeEventListener('click', click, true);
|
||||
};
|
||||
|
||||
document.addEventListener('pointermove', move);
|
||||
document.addEventListener('pointerup', up);
|
||||
document.addEventListener('click', click, true);
|
||||
|
||||
this.setState({
|
||||
draggingX: clamp(e.offsetX),
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { draggingX } = this.state;
|
||||
const { maxVolume } = this.props;
|
||||
|
||||
if (draggingX === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetValue = Math.floor(pix2vol(draggingX - (height / 2), maxVolume));
|
||||
|
||||
this.props.onChange(targetValue);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
muted,
|
||||
baseVolume,
|
||||
normVolume,
|
||||
maxVolume,
|
||||
value,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
draggingX,
|
||||
} = this.state;
|
||||
|
||||
const x = draggingX === null ?
|
||||
((height / 2) + vol2pix(value, maxVolume)) :
|
||||
draggingX;
|
||||
|
||||
const baseX = (height / 2) + vol2pix(baseVolume, maxVolume);
|
||||
const normX = (height / 2) + vol2pix(normVolume, maxVolume);
|
||||
|
||||
return r.svg({
|
||||
ref: this.svg,
|
||||
classSet: {
|
||||
'volume-slider': true,
|
||||
'volume-slider-muted': muted,
|
||||
},
|
||||
width,
|
||||
height,
|
||||
}, [
|
||||
baseVolume && r.line({
|
||||
className: 'volume-slider-base-mark',
|
||||
x1: baseX,
|
||||
x2: baseX,
|
||||
y1: 0,
|
||||
y2: height,
|
||||
}),
|
||||
|
||||
r.line({
|
||||
className: 'volume-slider-norm-mark',
|
||||
x1: normX,
|
||||
x2: normX,
|
||||
y1: 0,
|
||||
y2: height,
|
||||
}),
|
||||
|
||||
r.line({
|
||||
className: 'volume-slider-bg',
|
||||
x1: height / 2,
|
||||
x2: width - (height / 2),
|
||||
y1: height / 2,
|
||||
y2: height / 2,
|
||||
}),
|
||||
|
||||
r.line({
|
||||
className: 'volume-slider-fill',
|
||||
x1: height / 2,
|
||||
x2: x,
|
||||
y1: height / 2,
|
||||
y2: height / 2,
|
||||
}),
|
||||
|
||||
r.circle({
|
||||
className: 'volume-slider-handle',
|
||||
cx: x,
|
||||
cy: height / 2,
|
||||
r: (height - 2) / 2,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
};
|
76
index.css
76
index.css
|
@ -33,12 +33,30 @@ button:active {
|
|||
top: 1px;
|
||||
}
|
||||
|
||||
.label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
user-select: none;
|
||||
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.input {
|
||||
background: var(--themeUnfocusedBaseColor);
|
||||
color: var(--themeUnfocusedFgColor);
|
||||
border: 1px solid var(--borders);
|
||||
padding: 4px;
|
||||
}
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--themeSelectedBgColor);
|
||||
}
|
||||
.input[type="number"] {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.view-wrapper .graph {
|
||||
background: var(--themeBaseColor);
|
||||
}
|
||||
|
@ -63,6 +81,10 @@ button:active {
|
|||
color: var(--themeSelectedFgColor);
|
||||
}
|
||||
|
||||
.view-wrapper .edge-container:hover .edge {
|
||||
stroke: var(--themeSelectedBgColor);
|
||||
}
|
||||
|
||||
.view-wrapper .graph .edge.selected {
|
||||
stroke: var(--themeSelectedBgColor);
|
||||
}
|
||||
|
@ -90,6 +112,7 @@ button:active {
|
|||
|
||||
#edge-custom-container .edge-path {
|
||||
marker-end: none;
|
||||
stroke: var(--themeSelectedBgColor);
|
||||
}
|
||||
|
||||
.view-wrapper .graph .edge {
|
||||
|
@ -100,6 +123,16 @@ button:active {
|
|||
fill: var(--borders);
|
||||
}
|
||||
|
||||
.view-wrapper .edge-mouse-handler.edge-mouse-handler {
|
||||
opacity: 1;
|
||||
}
|
||||
.view-wrapper .edge-mouse-handler .edge-overlay-path {
|
||||
opacity: 0;
|
||||
}
|
||||
.view-wrapper .edge-mouse-handler .edge-text {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.preferences {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
@ -172,3 +205,46 @@ button:active {
|
|||
.edge-text {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.volume-controls {
|
||||
background: var(--themeBgColor);
|
||||
border: 1px solid var(--borders);
|
||||
|
||||
pointer-events: initial;
|
||||
padding: 2px;
|
||||
|
||||
width: 308px;
|
||||
|
||||
margin-left: -50%;
|
||||
}
|
||||
|
||||
.volume-controls:hover {
|
||||
border-color: var(--themeSelectedBgColor);
|
||||
}
|
||||
|
||||
.volume-slider-norm-mark, .volume-slider-base-mark {
|
||||
stroke: var(--borders);
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.volume-slider-bg {
|
||||
stroke: var(--borders);
|
||||
stroke-width: 6px;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.volume-slider-fill {
|
||||
stroke: var(--successColor);
|
||||
stroke-width: 6px;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.volume-slider-handle {
|
||||
fill: var(--themeBgColor);
|
||||
stroke: var(--borders);
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.volume-slider-handle:hover {
|
||||
stroke: var(--themeSelectedBgColor);
|
||||
}
|
||||
|
|
8
index.js
8
index.js
|
@ -10,4 +10,12 @@ app.on('ready', () => {
|
|||
win.setAutoHideMenuBar(true);
|
||||
win.setMenuBarVisibility(false);
|
||||
win.loadFile('index.html');
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const { default: installExtension, REACT_DEVELOPER_TOOLS } = require('electron-devtools-installer');
|
||||
|
||||
installExtension(REACT_DEVELOPER_TOOLS)
|
||||
.then(name => console.log(`Added Extension: ${name}`))
|
||||
.catch(error => console.error(error));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"devDependencies": {
|
||||
"ava": "^0.25.0",
|
||||
"electron": "^3.0.8",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"eslint-config-xo-overrides": "^1.1.2",
|
||||
"remotedev-server": "^0.2.6",
|
||||
"uws": "^99.0.0",
|
||||
|
@ -20,7 +21,7 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@futpib/paclient": "^0.0.3",
|
||||
"@futpib/paclient": "^0.0.5",
|
||||
"bluebird": "^3.5.3",
|
||||
"camelcase": "^5.0.0",
|
||||
"electron-store": "^2.0.0",
|
||||
|
|
|
@ -17,6 +17,9 @@ const initialState = {
|
|||
hidePulseaudioApps: true,
|
||||
|
||||
hideVolumeThumbnails: false,
|
||||
lockChannelsTogether: true,
|
||||
|
||||
maxVolume: 1.5,
|
||||
|
||||
showDebugInfo: false,
|
||||
};
|
||||
|
|
|
@ -5,6 +5,8 @@ const {
|
|||
omit,
|
||||
fromPairs,
|
||||
map,
|
||||
pick,
|
||||
equals,
|
||||
} = require('ramda');
|
||||
|
||||
const { combineReducers } = require('redux');
|
||||
|
@ -33,6 +35,9 @@ const reducer = combineReducers({
|
|||
if (payload.type !== type) {
|
||||
return state;
|
||||
}
|
||||
if (payload.type === 'sinkInput' || payload.type === 'sourceOutput') {
|
||||
return state;
|
||||
}
|
||||
return merge(state, {
|
||||
[payload.index]: payload,
|
||||
});
|
||||
|
@ -43,6 +48,32 @@ const reducer = combineReducers({
|
|||
}
|
||||
return omit([ payload.index ], state);
|
||||
},
|
||||
[pulse.info]: (state, { payload }) => {
|
||||
if (payload.type !== type) {
|
||||
return state;
|
||||
}
|
||||
if (payload.type === 'sinkInput' || payload.type === 'sourceOutput') {
|
||||
const newPao = pick([
|
||||
'type',
|
||||
'index',
|
||||
'moduleIndex',
|
||||
'clientIndex',
|
||||
'sinkIndex',
|
||||
'sourceIndex',
|
||||
], payload);
|
||||
|
||||
const oldPao = state[payload.index];
|
||||
|
||||
if (equals(newPao, oldPao)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return merge(state, {
|
||||
[newPao.index]: newPao,
|
||||
});
|
||||
}
|
||||
return state;
|
||||
},
|
||||
[pulse.close]: () => initialState.objects[key],
|
||||
}, initialState.objects[key]) ], things))),
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ const { pulse: pulseActions } = require('../actions');
|
|||
|
||||
const { things } = require('../constants/pulse');
|
||||
|
||||
const { getPaiByTypeAndIndex } = require('../selectors');
|
||||
|
||||
function getFnFromType(type) {
|
||||
let fn;
|
||||
switch (type) {
|
||||
|
@ -29,6 +31,23 @@ function getFnFromType(type) {
|
|||
return 'get' + fn[0].toUpperCase() + fn.slice(1);
|
||||
}
|
||||
|
||||
function setSinkChannelVolume(pa, store, index, channelIndex, volume, cb) {
|
||||
const pai = getPaiByTypeAndIndex('sink', index)(store.getState());
|
||||
pa.setSinkVolumes(index, pai.channelVolumes.map((v, i) => i === channelIndex ? volume : v), cb);
|
||||
}
|
||||
function setSourceChannelVolume(pa, store, index, channelIndex, volume, cb) {
|
||||
const pai = getPaiByTypeAndIndex('source', index)(store.getState());
|
||||
pa.setSourceVolumes(index, pai.channelVolumes.map((v, i) => i === channelIndex ? volume : v), cb);
|
||||
}
|
||||
function setSinkInputChannelVolume(pa, store, index, channelIndex, volume, cb) {
|
||||
const pai = getPaiByTypeAndIndex('sinkInput', index)(store.getState());
|
||||
pa.setSinkInputVolumesByIndex(index, pai.channelVolumes.map((v, i) => i === channelIndex ? volume : v), cb);
|
||||
}
|
||||
function setSourceOutputChannelVolume(pa, store, index, channelIndex, volume, cb) {
|
||||
const pai = getPaiByTypeAndIndex('sourceOutput', index)(store.getState());
|
||||
pa.setSourceOutputVolumesByIndex(index, pai.channelVolumes.map((v, i) => i === channelIndex ? volume : v), cb);
|
||||
}
|
||||
|
||||
module.exports = store => {
|
||||
const pa = new PAClient();
|
||||
|
||||
|
@ -128,6 +147,37 @@ module.exports = store => {
|
|||
pa.unloadModuleByIndex(moduleIndex, rethrow);
|
||||
return state;
|
||||
},
|
||||
|
||||
[pulseActions.setSinkVolumes]: (state, { payload: { index, channelVolumes } }) => {
|
||||
pa.setSinkVolumes(index, channelVolumes, rethrow);
|
||||
return state;
|
||||
},
|
||||
[pulseActions.setSourceVolumes]: (state, { payload: { index, channelVolumes } }) => {
|
||||
pa.setSourceVolumes(index, channelVolumes, rethrow);
|
||||
return state;
|
||||
},
|
||||
[pulseActions.setSinkInputVolumes]: (state, { payload: { index, channelVolumes } }) => {
|
||||
pa.setSinkInputVolumesByIndex(index, channelVolumes, rethrow);
|
||||
return state;
|
||||
},
|
||||
[pulseActions.setSourceOutputVolumes]: (state, { payload: { index, channelVolumes } }) => {
|
||||
pa.setSourceOutputVolumesByIndex(index, channelVolumes, rethrow);
|
||||
return state;
|
||||
},
|
||||
|
||||
[pulseActions.setSinkChannelVolume]: (state, { payload: { index, channelIndex, volume } }) => {
|
||||
return setSinkChannelVolume(pa, store, index, channelIndex, volume, rethrow);
|
||||
},
|
||||
[pulseActions.setSourceChannelVolume]: (state, { payload: { index, channelIndex, volume } }) => {
|
||||
return setSourceChannelVolume(pa, store, index, channelIndex, volume, rethrow);
|
||||
},
|
||||
[pulseActions.setSinkInputChannelVolume]: (state, { payload: { index, channelIndex, volume } }) => {
|
||||
return setSinkInputChannelVolume(pa, store, index, channelIndex, volume, rethrow);
|
||||
},
|
||||
[pulseActions.setSourceOutputChannelVolume]: (state, { payload: { index, channelIndex, volume } }) => {
|
||||
return setSourceOutputChannelVolume(pa, store, index, channelIndex, volume, rethrow);
|
||||
},
|
||||
|
||||
}, null);
|
||||
|
||||
return next => action => {
|
||||
|
|
16
utils/memoize.js
Normal file
16
utils/memoize.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
const {
|
||||
memoizeWith,
|
||||
} = require('ramda');
|
||||
|
||||
const weakmapId_ = new WeakMap();
|
||||
const weakmapId = o => {
|
||||
if (!weakmapId_.has(o)) {
|
||||
weakmapId_.set(o, String(Math.random()));
|
||||
}
|
||||
return weakmapId_.get(o);
|
||||
};
|
||||
|
||||
const memoize = memoizeWith(weakmapId);
|
||||
|
||||
module.exports = memoize;
|
30
yarn.lock
30
yarn.lock
|
@ -2,6 +2,11 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"7zip@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30"
|
||||
integrity sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA=
|
||||
|
||||
"@ava/babel-plugin-throws-helper@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ava/babel-plugin-throws-helper/-/babel-plugin-throws-helper-2.0.0.tgz#2fc1fe3c211a71071a4eca7b8f7af5842cd1ae7c"
|
||||
|
@ -79,10 +84,10 @@
|
|||
dependencies:
|
||||
arrify "^1.0.1"
|
||||
|
||||
"@futpib/paclient@^0.0.3":
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@futpib/paclient/-/paclient-0.0.3.tgz#54c10ac6d811c5104b66a9a4985809955c8ffee6"
|
||||
integrity sha512-9OuBDQRb9U55y3Xu89tZV+oiwt2ghgTsSAqM+SAySoLCBt0yG5LaB9PCV+bxkBy6aY6bvvPuNrYd88hL57gaxQ==
|
||||
"@futpib/paclient@^0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@futpib/paclient/-/paclient-0.0.5.tgz#0de89ee7175e3de994bc298ddb1e461aa3007543"
|
||||
integrity sha512-49jeRSEOXto3MntDj2Dzm4t7U9M0X41seqF+T/xwFRYk/pBUKdiXHuofNFJvn4rwM7P4BVjxU6fRpCOEAxH/VA==
|
||||
|
||||
"@ladjs/time-require@^0.1.4":
|
||||
version "0.1.4"
|
||||
|
@ -1409,6 +1414,11 @@ cross-spawn@^6.0.5:
|
|||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-unzip@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cross-unzip/-/cross-unzip-0.0.2.tgz#5183bc47a09559befcf98cc4657964999359372f"
|
||||
integrity sha1-UYO8R6CVWb78+YzEZXlkmZNZNy8=
|
||||
|
||||
crypto-random-string@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
|
||||
|
@ -1899,6 +1909,16 @@ ejs@^2.4.1:
|
|||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
|
||||
integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
|
||||
|
||||
electron-devtools-installer@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-2.2.4.tgz#261a50337e37121d338b966f07922eb4939a8763"
|
||||
integrity sha512-b5kcM3hmUqn64+RUcHjjr8ZMpHS2WJ5YO0pnG9+P/RTdx46of/JrEjuciHWux6pE+On6ynWhHJF53j/EDJN0PA==
|
||||
dependencies:
|
||||
"7zip" "0.0.6"
|
||||
cross-unzip "0.0.2"
|
||||
rimraf "^2.5.2"
|
||||
semver "^5.3.0"
|
||||
|
||||
electron-download@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8"
|
||||
|
@ -5485,7 +5505,7 @@ ret@~0.1.10:
|
|||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||
|
||||
rimraf@^2.2.8, rimraf@^2.6.1:
|
||||
rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.6.1:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
||||
integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==
|
||||
|
|
Loading…
Reference in New Issue
Block a user