mirror of
https://github.com/xodio/xod.git
synced 2026-03-10 10:46:52 +01:00
@@ -24,7 +24,7 @@
|
||||
"doc:fz": "cd docs && make",
|
||||
"lerna": "lerna",
|
||||
"lint": "eslint packages/*/src packages/*/test packages/*/test-func --ext js,jsx --max-warnings 0",
|
||||
"start:electron": "lerna run start --scope xod-client-electron",
|
||||
"start:electron": "lerna run start --scope xod-client-electron --stream",
|
||||
"start:spectron-repl": "./tools/spectron-repl",
|
||||
"storybook": "lerna run storybook --stream --scope xod-client",
|
||||
"test": "XOD_HM_DEF=true lerna run test",
|
||||
|
||||
@@ -140,11 +140,22 @@ export const upload = R.curry(
|
||||
export const buildAndUpload = R.curry(
|
||||
(sketchFilePath, fqbn, packagesDir, librariesDir, buildDir, portName, builderToolDir) =>
|
||||
build(sketchFilePath, fqbn, packagesDir, librariesDir, buildDir, builderToolDir)
|
||||
.then((res) => {
|
||||
if (res.exitCode !== 0) {
|
||||
return Promise.reject(Object.assign(new Error(res.stderr), res));
|
||||
.then((buildResult) => {
|
||||
if (buildResult.exitCode !== 0) {
|
||||
return Promise.reject(Object.assign(new Error(buildResult.stderr), buildResult));
|
||||
}
|
||||
|
||||
return upload(sketchFilePath, fqbn, packagesDir, buildDir, portName);
|
||||
return upload(sketchFilePath, fqbn, packagesDir, buildDir, portName)
|
||||
.then((uploadResult) => {
|
||||
if (uploadResult.exitCode !== 0) {
|
||||
return Promise.reject(Object.assign(new Error(uploadResult.stderr), uploadResult));
|
||||
}
|
||||
|
||||
return {
|
||||
exitCode: uploadResult.exitCode,
|
||||
stdout: [buildResult.stdout, uploadResult.stdout].join('\n'),
|
||||
stderr: [buildResult.stderr, uploadResult.stderr].join('\n'),
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
@@ -267,8 +267,9 @@ class App extends client.App {
|
||||
/>
|
||||
<client.Editor
|
||||
size={this.state.size}
|
||||
onUploadClick={this.onUpload}
|
||||
onUploadAndDebugClick={this.onUpload}
|
||||
/>
|
||||
<client.SnackBar />
|
||||
<PopupInstallApp
|
||||
isVisible={this.state.popupInstallApp}
|
||||
onClose={this.hideInstallAppPopup}
|
||||
|
||||
@@ -192,7 +192,7 @@ const deployToArduino = ({
|
||||
return Promise.reject(Object.assign(new Error(`Upload tool exited with error code: ${result.exitCode}`), result));
|
||||
}
|
||||
sendSuccess(
|
||||
[result.stderr, result.stdout].join('\n\n'),
|
||||
[result.stdout, result.stderr].join('\n\n'),
|
||||
100
|
||||
)();
|
||||
|
||||
|
||||
@@ -25,10 +25,8 @@ export default (state, action) => {
|
||||
switch (action.type) {
|
||||
case UPLOAD:
|
||||
case UPLOAD_TO_ARDUINO: {
|
||||
if (action.meta.status === 'deleted') {
|
||||
return hideOnePopup(POPUP_ID.UPLOADING, state);
|
||||
} else if (action.meta.status !== 'failed') {
|
||||
return showOnlyPopup(POPUP_ID.UPLOADING, {}, state);
|
||||
if (action.meta.status === 'started') {
|
||||
return hideOnePopup(POPUP_ID.UPLOADING_CONFIG, state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
import R from 'ramda';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { STATUS, PopupForm } from 'xod-client';
|
||||
import { Line as ProgressBar } from 'rc-progress';
|
||||
|
||||
class PopupUploadProject extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isVisible: props.isVisible,
|
||||
};
|
||||
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
if (this.canClose()) {
|
||||
this.props.onClose(this.props.upload.id);
|
||||
}
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
switch (this.props.upload.status) {
|
||||
default:
|
||||
return 'Uploading project';
|
||||
}
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
const preStyle = {
|
||||
overflow: 'auto',
|
||||
maxWidth: 'auto',
|
||||
maxHeight: '300px',
|
||||
// allow user to select the log for bug reporting etc
|
||||
userSelect: 'initial',
|
||||
cursor: 'auto',
|
||||
};
|
||||
|
||||
const message = (this.props.upload.message) ?
|
||||
(<pre style={preStyle}>{this.props.upload.message.replace(/\r[^\n]/g, '\n')}</pre>) :
|
||||
null;
|
||||
|
||||
|
||||
const titleMessage = R.cond([
|
||||
[R.equals(STATUS.SUCCEEDED), R.always(
|
||||
<p>
|
||||
The program uploaded successfully.
|
||||
</p>
|
||||
)],
|
||||
[R.equals(STATUS.FAILED), R.always(
|
||||
<p>
|
||||
Oops! Error occured.
|
||||
</p>
|
||||
)],
|
||||
[R.T, R.always(
|
||||
<p>
|
||||
Your program is uploading onto device.<br />
|
||||
Do not unplug the device.
|
||||
</p>
|
||||
)],
|
||||
])(this.props.upload.status);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{titleMessage}
|
||||
{message}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getProgress() {
|
||||
if (this.isSucceeded()) {
|
||||
return '100';
|
||||
}
|
||||
|
||||
if (this.props.upload.percentage) {
|
||||
return this.props.upload.percentage.toString();
|
||||
}
|
||||
|
||||
return '0';
|
||||
}
|
||||
|
||||
isSucceeded() {
|
||||
return (this.props.upload.status === STATUS.SUCCEEDED);
|
||||
}
|
||||
|
||||
isFailed() {
|
||||
return (this.props.upload.status === STATUS.FAILED);
|
||||
}
|
||||
|
||||
canClose() {
|
||||
return (this.isSucceeded() || this.isFailed());
|
||||
}
|
||||
|
||||
render() {
|
||||
const title = this.getTitle();
|
||||
const message = this.getMessage();
|
||||
const progress = this.getProgress();
|
||||
const color = this.isFailed() ? '#ed5b5b' : '#81c522';
|
||||
|
||||
return (
|
||||
<PopupForm
|
||||
isVisible={this.props.isVisible}
|
||||
title={title}
|
||||
isClosable={this.canClose()}
|
||||
onClose={this.onClose}
|
||||
>
|
||||
<div className="ModalContent">
|
||||
<ProgressBar
|
||||
percent={progress}
|
||||
strokeWidth="5"
|
||||
strokeColor={color}
|
||||
strokeLinecap="square"
|
||||
trailWidth="5"
|
||||
trailColor="#373737"
|
||||
className="ProgressBar"
|
||||
/>
|
||||
</div>
|
||||
<div className="ModalContent">
|
||||
{message}
|
||||
</div>
|
||||
</PopupForm>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PopupUploadProject.propTypes = {
|
||||
upload: PropTypes.object,
|
||||
isVisible: PropTypes.bool,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
PopupUploadProject.defaultProps = {
|
||||
upload: {
|
||||
status: STATUS.STARTED,
|
||||
message: '',
|
||||
percentage: 0,
|
||||
},
|
||||
isVisible: false,
|
||||
};
|
||||
|
||||
export default PopupUploadProject;
|
||||
3
packages/xod-client-electron/src/upload/messages.js
Normal file
3
packages/xod-client-electron/src/upload/messages.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
SUCCESS: 'Uploaded successfully',
|
||||
};
|
||||
@@ -27,7 +27,6 @@ import { UPLOAD, UPLOAD_TO_ARDUINO } from '../../upload/actionTypes';
|
||||
import PopupSetWorkspace from '../../settings/components/PopupSetWorkspace';
|
||||
import PopupCreateWorkspace from '../../settings/components/PopupCreateWorkspace';
|
||||
import PopupProjectSelection from '../../projects/components/PopupProjectSelection';
|
||||
import PopupUploadProject from '../../upload/components/PopupUploadProject';
|
||||
import PopupUploadConfig from '../../upload/components/PopupUploadConfig';
|
||||
import { REDUCER_STATUS } from '../../projects/constants';
|
||||
import { SaveProgressBar } from '../components/SaveProgressBar';
|
||||
@@ -35,6 +34,7 @@ import { SaveProgressBar } from '../components/SaveProgressBar';
|
||||
import formatError from '../../shared/errorFormatter';
|
||||
import * as EVENTS from '../../shared/events';
|
||||
import * as MESSAGES from '../../shared/messages';
|
||||
import UPLOAD_MESSAGES from '../../upload/messages';
|
||||
import { createSystemMessage } from '../../shared/debuggerMessages';
|
||||
|
||||
import { subscribeAutoUpdaterEvents } from '../autoupdate';
|
||||
@@ -83,6 +83,7 @@ class App extends client.App {
|
||||
this.getSelectedBoard = this.getSelectedBoard.bind(this);
|
||||
|
||||
this.onUploadToArduinoClicked = this.onUploadToArduinoClicked.bind(this);
|
||||
this.onUploadToArduinoAndDebugClicked = this.onUploadToArduinoAndDebugClicked.bind(this);
|
||||
this.onUploadToArduino = this.onUploadToArduino.bind(this);
|
||||
this.onArduinoTargetBoardChange = this.onArduinoTargetBoardChange.bind(this);
|
||||
this.onSerialPortChange = this.onSerialPortChange.bind(this);
|
||||
@@ -188,6 +189,10 @@ class App extends client.App {
|
||||
this.props.actions.uploadToArduinoConfig();
|
||||
}
|
||||
|
||||
onUploadToArduinoAndDebugClicked() {
|
||||
this.props.actions.uploadToArduinoConfig(true);
|
||||
}
|
||||
|
||||
onUploadToArduino(board, port, cloud, debug, processActions = null) {
|
||||
const proc = (processActions !== null) ? processActions : this.props.actions.uploadToArduino();
|
||||
const eitherTProject = this.transformProjectForTranspiler(debug);
|
||||
@@ -219,6 +224,9 @@ class App extends client.App {
|
||||
}
|
||||
if (payload.success) {
|
||||
proc.success(payload.message);
|
||||
|
||||
this.props.actions.addConfirmation({ title: UPLOAD_MESSAGES.SUCCESS });
|
||||
|
||||
if (debug) {
|
||||
foldEither(
|
||||
error => this.props.actions.addError(
|
||||
@@ -633,15 +641,6 @@ class App extends client.App {
|
||||
/>
|
||||
) : null;
|
||||
}
|
||||
renderPopupUploadProcess() {
|
||||
return (this.props.popups.uploadProject) ? (
|
||||
<PopupUploadProject
|
||||
isVisible
|
||||
upload={this.props.upload}
|
||||
onClose={this.onUploadPopupClose}
|
||||
/>
|
||||
) : null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
@@ -654,10 +653,11 @@ class App extends client.App {
|
||||
<client.Editor
|
||||
size={this.state.size}
|
||||
stopDebuggerSession={() => debuggerIPC.sendStopDebuggerSession(ipcRenderer)}
|
||||
onUploadClick={this.onUploadToArduinoClicked}
|
||||
onUploadAndDebugClick={this.onUploadToArduinoAndDebugClicked}
|
||||
/>
|
||||
{this.renderPopupShowCode()}
|
||||
{this.renderPopupUploadConfig()}
|
||||
{this.renderPopupUploadProcess()}
|
||||
{this.renderPopupProjectPreferences()}
|
||||
{this.renderPopupPublishProject()}
|
||||
{this.renderPopupCreateNewProject()}
|
||||
@@ -762,7 +762,6 @@ const mapStateToProps = R.applySpec({
|
||||
switchWorkspace: client.getPopupVisibility(client.POPUP_ID.SWITCHING_WORKSPACE),
|
||||
createWorkspace: client.getPopupVisibility(client.POPUP_ID.CREATING_WORKSPACE),
|
||||
uploadToArduinoConfig: client.getPopupVisibility(client.POPUP_ID.UPLOADING_CONFIG),
|
||||
uploadProject: client.getPopupVisibility(client.POPUP_ID.UPLOADING),
|
||||
showCode: client.getPopupVisibility(client.POPUP_ID.SHOWING_CODE),
|
||||
projectPreferences: client.getPopupVisibility(
|
||||
client.POPUP_ID.EDITING_PROJECT_PREFERENCES
|
||||
@@ -789,6 +788,7 @@ const mapDispatchToProps = dispatch => ({
|
||||
switchWorkspace: settingsActions.switchWorkspace,
|
||||
openProject: client.openProject,
|
||||
saveAll: actions.saveAll,
|
||||
addConfirmation: client.addConfirmation,
|
||||
uploadToArduino: uploadActions.uploadToArduino,
|
||||
uploadToArduinoConfig: uploadActions.uploadToArduinoConfig,
|
||||
hideUploadConfigPopup: uploadActions.hideUploadConfigPopup,
|
||||
|
||||
31
packages/xod-client/src/core/assets/icons/clear-log.svg
Normal file
31
packages/xod-client/src/core/assets/icons/clear-log.svg
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="14px"
|
||||
height="14px" viewBox="0 0 14 14" enable-background="new 0 0 14 14" xml:space="preserve">
|
||||
<g id="Layer_1">
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#AAAAAA" d="M4.066,3.809l1.912,0.259l3.906,3.988L13.814,4l-3.616-3.667
|
||||
l-0.31-0.317L8.165,0.008L4.066,3.809z"/>
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M5.875,12c-0.264,0-0.512-0.103-0.699-0.283L2.004,8.618c-0.386-0.376-0.386-0.989,0-1.366L9.14,0.283
|
||||
C9.327,0.101,9.575,0,9.84,0c0.264,0,0.513,0.101,0.698,0.283l3.173,3.097c0.385,0.376,0.385,0.989,0,1.366l-7.136,6.97
|
||||
C6.388,11.897,6.14,12,5.875,12z M9.84,0.516c-0.124,0-0.24,0.047-0.327,0.131l-7.135,6.97c-0.18,0.175-0.18,0.461,0,0.638
|
||||
l3.172,3.097c0.087,0.086,0.202,0.132,0.326,0.132s0.24-0.046,0.327-0.132l7.135-6.969c0.18-0.176,0.18-0.462,0-0.637
|
||||
l-3.17-3.099C10.079,0.563,9.962,0.516,9.84,0.516z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M4.826,11.928c-0.719,0-1.245-0.025-1.524-0.256l-0.02-0.019L0.286,8.615
|
||||
c-0.382-0.373-0.382-0.986,0.003-1.363l7.137-6.969c0.385-0.377,1.012-0.377,1.398,0l3.17,3.097
|
||||
c0.388,0.376,0.388,0.989,0,1.366l-0.371-0.364c0.18-0.176,0.18-0.462,0-0.637L8.451,0.647c-0.181-0.175-0.473-0.175-0.652,0
|
||||
l-7.136,6.97c-0.18,0.175-0.18,0.461,0,0.638l2.987,3.03c0.182,0.132,0.864,0.129,1.468,0.126c0.13,0,0.263-0.002,0.398,0
|
||||
l0,0.515c-0.135,0-0.267,0-0.396,0.002C5.018,11.928,4.92,11.928,4.826,11.928z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<rect y="12" fill="#AAAAAA" width="14" height="1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
25
packages/xod-client/src/core/assets/icons/debug.svg
Normal file
25
packages/xod-client/src/core/assets/icons/debug.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="14px" height="14px" viewBox="0 0 14 14" enable-background="new 0 0 14 14" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M13.833,7.598c-0.11-0.113-0.242-0.17-0.394-0.17h-1.96V4.804l1.514-1.545
|
||||
c0.111-0.113,0.167-0.247,0.167-0.402s-0.056-0.289-0.167-0.402c-0.11-0.113-0.242-0.169-0.394-0.169s-0.283,0.057-0.394,0.169
|
||||
L10.692,4H3.308L1.793,2.455C1.683,2.342,1.552,2.286,1.4,2.286c-0.151,0-0.283,0.057-0.394,0.169
|
||||
C0.896,2.568,0.84,2.702,0.84,2.857s0.056,0.289,0.167,0.402L2.52,4.804v2.625H0.56c-0.152,0-0.283,0.057-0.394,0.17
|
||||
C0.055,7.711,0,7.845,0,8c0,0.154,0.055,0.289,0.166,0.401c0.111,0.113,0.242,0.17,0.394,0.17h1.96
|
||||
c0,0.952,0.169,1.769,0.507,2.446L1.26,13.045c-0.1,0.118-0.145,0.257-0.136,0.415c0.009,0.157,0.068,0.29,0.179,0.396
|
||||
C1.415,13.952,1.54,14,1.68,14c0.163,0,0.303-0.063,0.42-0.188l1.601-1.849l0.131,0.125c0.082,0.078,0.208,0.175,0.381,0.29
|
||||
c0.172,0.116,0.364,0.232,0.577,0.349s0.467,0.215,0.761,0.295c0.295,0.08,0.591,0.121,0.888,0.121V6.036h1.12v7.107
|
||||
c0.28,0,0.563-0.039,0.848-0.117c0.286-0.077,0.525-0.163,0.718-0.259c0.192-0.095,0.382-0.203,0.569-0.325
|
||||
c0.187-0.122,0.309-0.207,0.367-0.255s0.102-0.086,0.131-0.116l1.732,1.759c0.105,0.113,0.236,0.17,0.394,0.17
|
||||
c0.158,0,0.289-0.057,0.395-0.17c0.11-0.113,0.166-0.247,0.166-0.401c0-0.155-0.056-0.289-0.166-0.402l-1.82-1.865
|
||||
c0.391-0.709,0.587-1.572,0.587-2.59h1.959c0.152,0,0.283-0.057,0.395-0.169C13.944,8.289,14,8.155,14,8
|
||||
C14,7.845,13.944,7.711,13.833,7.598z"/>
|
||||
<path fill="#AAAAAA" d="M8.982,0.835C8.437,0.278,7.776,0,7,0S5.563,0.278,5.018,0.835S4.2,2.065,4.2,2.857h5.6
|
||||
C9.8,2.066,9.527,1.392,8.982,0.835z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
packages/xod-client/src/core/assets/icons/filter.svg
Normal file
10
packages/xod-client/src/core/assets/icons/filter.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="13px" height="13px" viewBox="0 0 13 13" enable-background="new 0 0 13 13" xml:space="preserve">
|
||||
<path fill="#AAAAAA" d="M12.945,0.359C12.839,0.12,12.658,0,12.399,0H0.598C0.341,0,0.16,0.12,0.055,0.359
|
||||
c-0.104,0.253-0.061,0.469,0.129,0.646l4.545,4.551v4.487c0,0.163,0.058,0.299,0.176,0.416l2.358,2.363
|
||||
C7.377,12.938,7.513,13,7.682,13c0.072,0,0.148-0.016,0.23-0.045c0.237-0.106,0.357-0.287,0.357-0.546V5.557l4.544-4.551
|
||||
C13.007,0.828,13.048,0.612,12.945,0.359z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 857 B |
11
packages/xod-client/src/core/assets/icons/quick-upload.svg
Normal file
11
packages/xod-client/src/core/assets/icons/quick-upload.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="14px" height="14px" viewBox="-3.5 14.5 14 14" enable-background="new -3.5 14.5 14 14" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon fill="#AAAAAA" points="7.5,20.65 4.445,20.65 4.445,14.5 -0.5,22.352 2.556,22.352 2.556,28.5 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 623 B |
@@ -32,3 +32,16 @@
|
||||
.icon-account {
|
||||
@include icon('../assets/icons/account.svg');
|
||||
}
|
||||
|
||||
.icon-quick-upload {
|
||||
@include icon('../assets/icons/quick-upload.svg');
|
||||
}
|
||||
.icon-debug {
|
||||
@include icon('../assets/icons/debug.svg');
|
||||
}
|
||||
.icon-filter {
|
||||
@include icon('../assets/icons/filter.svg');
|
||||
}
|
||||
.icon-clear-log {
|
||||
@include icon('../assets/icons/clear-log.svg');
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.Breadcrumbs {
|
||||
z-index: 9;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.CppImplementationEditors {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.debug-session-stop-button {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
top: 3px;
|
||||
right: 3px;
|
||||
|
||||
padding: 8px 15px 10px 15px;
|
||||
@@ -13,88 +13,126 @@
|
||||
}
|
||||
|
||||
.Debugger {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
bottom: 0;
|
||||
height: 196px;
|
||||
height: 220px;
|
||||
width: 100%;
|
||||
border-top: 2px solid $chrome-outlines;
|
||||
|
||||
color: $sidebar-color-text;
|
||||
|
||||
.caption {
|
||||
position: relative;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
height: 20px;
|
||||
width: 120px;
|
||||
margin-left: 2em;
|
||||
margin-top: -20px;
|
||||
|
||||
padding: 4px 8px;
|
||||
cursor: default;
|
||||
|
||||
background: $sidebar-color-bg;
|
||||
border-top-right-radius: 2px;
|
||||
border-top-left-radius: 2px;
|
||||
|
||||
.close-button {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.4em;
|
||||
line-height: 0;
|
||||
color: $sidebar-color-text;
|
||||
|
||||
&:hover {
|
||||
color: $sidebar-color-text-hover;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.status {
|
||||
font-size: $font-size-s;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
&.isCollapsed {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.clear-log-button {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 22px;
|
||||
z-index: 3;
|
||||
.titlebar {
|
||||
background-color: $coal-bright;
|
||||
|
||||
border: 0;
|
||||
background: $sidebar-color-bg;
|
||||
color: $sidebar-color-text;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
|
||||
padding: 2px 4px;
|
||||
.expander {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
color: $sidebar-color-text-hover;
|
||||
background: $sidebar-color-bg-hover;
|
||||
.title {
|
||||
margin-left: 7px;
|
||||
color: $light-grey-bright;
|
||||
}
|
||||
|
||||
.progress {
|
||||
|
||||
padding-left: 14px;
|
||||
|
||||
.progress-trail {
|
||||
width: 150px;
|
||||
height: 7px;
|
||||
border-radius: 7px;
|
||||
|
||||
background-color: $dark;
|
||||
overflow: hidden;
|
||||
|
||||
.progress-line {
|
||||
height: 7px;
|
||||
background-color: $green;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@mixin deploymentPanelButton() {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
color: #ccc;
|
||||
|
||||
&:hover {
|
||||
color: #ccc;
|
||||
background: #444;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-upload-button {
|
||||
@include deploymentPanelButton();
|
||||
@extend .icon-quick-upload;
|
||||
}
|
||||
.debug-button {
|
||||
@include deploymentPanelButton();
|
||||
@extend .icon-debug;
|
||||
}
|
||||
.filter-button {
|
||||
@include deploymentPanelButton();
|
||||
@extend .icon-filter;
|
||||
}
|
||||
.clear-log-button {
|
||||
@include deploymentPanelButton();
|
||||
@extend .icon-clear-log;
|
||||
}
|
||||
.close-button {
|
||||
@include deploymentPanelButton();
|
||||
|
||||
&::before {
|
||||
line-height: 24px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
height: 180px;
|
||||
padding: 8px;
|
||||
height: 196px;
|
||||
|
||||
background: $sidebar-color-bg;
|
||||
border-left: 1px solid $color-canvas-background;
|
||||
}
|
||||
|
||||
.log {
|
||||
max-height: 180px;
|
||||
max-height: 196px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(white, 0.35);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
@@ -142,8 +180,6 @@
|
||||
display: inline-block;
|
||||
color: $color-comment-text;
|
||||
font-family: $font-family-mono;
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
flex-shrink: 0;
|
||||
|
||||
background: $sidebar-color-bg;
|
||||
|
||||
|
||||
@@ -4,3 +4,9 @@
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Workarea-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const SHOW_DEBUGGER_PANEL = 'SHOW_DEBUGGER_PANEL';
|
||||
export const HIDE_DEBUGGER_PANEL = 'HIDE_DEBUGGER_PANEL';
|
||||
export const UPLOAD = 'UPLOAD';
|
||||
|
||||
export const TOGGLE_DEBUGGER_PANEL = 'TOGGLE_DEBUGGER_PANEL';
|
||||
|
||||
export const DEBUGGER_LOG_ADD_MESSAGES = 'DEBUGGER_LOG_ADD_MESSAGES';
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
import * as AT from './actionTypes';
|
||||
|
||||
export const showDebugger = () => ({
|
||||
type: AT.SHOW_DEBUGGER_PANEL,
|
||||
});
|
||||
|
||||
export const hideDebugger = () => ({
|
||||
type: AT.HIDE_DEBUGGER_PANEL,
|
||||
});
|
||||
|
||||
export const toggleDebugger = () => ({
|
||||
type: AT.TOGGLE_DEBUGGER_PANEL,
|
||||
});
|
||||
|
||||
export const addMessagesToDebuggerLog = message => ({
|
||||
export const addMessagesToDebuggerLog = messages => ({
|
||||
type: AT.DEBUGGER_LOG_ADD_MESSAGES,
|
||||
payload: message,
|
||||
payload: messages,
|
||||
});
|
||||
|
||||
export const clearDebuggerLog = () => ({
|
||||
|
||||
15
packages/xod-client/src/debugger/constants.js
Normal file
15
packages/xod-client/src/debugger/constants.js
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
export const UPLOAD_STATUS = {
|
||||
STARTED: 'started',
|
||||
PROGRESSED: 'progressed',
|
||||
SUCCEEDED: 'succeeded',
|
||||
FAILED: 'failed',
|
||||
};
|
||||
|
||||
export const UPLOAD_MSG_TYPE = {
|
||||
XOD: 'xod',
|
||||
LOG: 'log',
|
||||
ERROR: 'error',
|
||||
SYSTEM: 'system',
|
||||
FLASHER: 'flasher',
|
||||
};
|
||||
@@ -1,65 +1,180 @@
|
||||
import R from 'ramda';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { ContextMenuTrigger, ContextMenu, MenuItem } from 'react-contextmenu';
|
||||
import { Icon } from 'react-fa';
|
||||
import classNames from 'classnames';
|
||||
import { foldMaybe } from 'xod-func-tools';
|
||||
|
||||
import { isDebugSession } from '../selectors';
|
||||
import { getUploadProgress, isDebuggerVisible } from '../selectors';
|
||||
import { UPLOAD_MSG_TYPE } from '../constants';
|
||||
import * as DA from '../actions';
|
||||
import Log from './Log';
|
||||
|
||||
const Debugger = ({ active, actions }) => {
|
||||
const cls = classNames('Debugger', {
|
||||
'is-active': active,
|
||||
});
|
||||
|
||||
const statusIcon = (active) ? 'play' : 'stop';
|
||||
|
||||
return (
|
||||
<div className={cls}>
|
||||
<div className="caption">
|
||||
<Icon
|
||||
key="status"
|
||||
name={statusIcon}
|
||||
title="Debugs session is running"
|
||||
className="status"
|
||||
/>
|
||||
<span className="title">
|
||||
Debugger
|
||||
</span>
|
||||
<button
|
||||
className="close-button"
|
||||
onClick={actions.hideDebugger}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="clear-log-button"
|
||||
onClick={actions.clearLog}
|
||||
>
|
||||
Clear log
|
||||
</button>
|
||||
<div className="container">
|
||||
<Log />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const contextMenuAttrs = {
|
||||
className: 'contextmenu filter-button',
|
||||
};
|
||||
|
||||
const DEPLOYMENT_PANEL_FILTER_CONTEXT_MENU_ID = 'DEPLOYMENT_PANEL_FILTER_CONTEXT_MENU_ID';
|
||||
|
||||
const checkmark = active => (active ? <span className="state">✔</span> : null);
|
||||
|
||||
class Debugger extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
messageTypeFilter: {
|
||||
[UPLOAD_MSG_TYPE.FLASHER]: true,
|
||||
[UPLOAD_MSG_TYPE.XOD]: false,
|
||||
},
|
||||
};
|
||||
|
||||
this.toggleDebugMessages = this.toggleMessageType.bind(this, UPLOAD_MSG_TYPE.XOD);
|
||||
this.toggleUploadMessages = this.toggleMessageType.bind(this, UPLOAD_MSG_TYPE.FLASHER);
|
||||
}
|
||||
|
||||
toggleMessageType(type) {
|
||||
this.setState(R.over(
|
||||
R.lensPath(['messageTypeFilter', type]),
|
||||
R.not
|
||||
));
|
||||
}
|
||||
|
||||
renderControlsForExpandedState() {
|
||||
const { isExpanded, actions } = this.props;
|
||||
|
||||
if (!isExpanded) return null;
|
||||
|
||||
const { messageTypeFilter } = this.state;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<button
|
||||
className="clear-log-button"
|
||||
onClick={actions.clearLog}
|
||||
title="Clear Log"
|
||||
/>
|
||||
<ContextMenuTrigger
|
||||
id={DEPLOYMENT_PANEL_FILTER_CONTEXT_MENU_ID}
|
||||
key="contextMenuTrigger"
|
||||
renderTag="button"
|
||||
attributes={contextMenuAttrs}
|
||||
holdToDisplay={0}
|
||||
>
|
||||
<span />
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenu id={DEPLOYMENT_PANEL_FILTER_CONTEXT_MENU_ID}>
|
||||
<MenuItem onClick={this.toggleUploadMessages}>
|
||||
{checkmark(messageTypeFilter[UPLOAD_MSG_TYPE.FLASHER])}
|
||||
Upload Log
|
||||
</MenuItem>
|
||||
<MenuItem onClick={this.toggleDebugMessages}>
|
||||
{checkmark(messageTypeFilter[UPLOAD_MSG_TYPE.XOD])}
|
||||
Watched Values
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
maybeUploadProgress,
|
||||
actions,
|
||||
onUploadClick,
|
||||
onUploadAndDebugClick,
|
||||
isExpanded,
|
||||
} = this.props;
|
||||
|
||||
const uploadProgress = foldMaybe(
|
||||
null,
|
||||
progress => (
|
||||
<div className="progress-trail">
|
||||
<div
|
||||
className="progress-line"
|
||||
style={{ width: `${progress}%` }}
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-valuenow={progress}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
maybeUploadProgress
|
||||
);
|
||||
|
||||
const { messageTypeFilter } = this.state;
|
||||
const rejectedMessageTypes = R.compose(
|
||||
R.keys,
|
||||
R.filter(R.equals(false))
|
||||
)(messageTypeFilter);
|
||||
|
||||
return (
|
||||
<div className={cn('Debugger', { isCollapsed: !isExpanded })}>
|
||||
<div className="titlebar">
|
||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||
role="button"
|
||||
className="expander"
|
||||
onClick={actions.toggleDebugger}
|
||||
>
|
||||
<span className="title">
|
||||
Deployment
|
||||
</span>
|
||||
|
||||
<div className="progress">
|
||||
{uploadProgress}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.renderControlsForExpandedState()}
|
||||
|
||||
<button
|
||||
className="quick-upload-button"
|
||||
onClick={onUploadClick}
|
||||
title="Upload to Arduino"
|
||||
/>
|
||||
<button
|
||||
className="debug-button"
|
||||
onClick={onUploadAndDebugClick}
|
||||
title="Upload and Debug"
|
||||
/>
|
||||
<Icon
|
||||
Component="button"
|
||||
className="close-button"
|
||||
name={isExpanded ? 'chevron-down' : 'chevron-up'}
|
||||
onClick={actions.toggleDebugger}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isExpanded ? (
|
||||
<div className="container">
|
||||
<Log rejectedMessageTypes={rejectedMessageTypes} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Debugger.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
maybeUploadProgress: PropTypes.object.isRequired,
|
||||
isExpanded: PropTypes.bool.isRequired,
|
||||
actions: PropTypes.objectOf(PropTypes.func),
|
||||
onUploadClick: PropTypes.func.isRequired,
|
||||
onUploadAndDebugClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = R.applySpec({
|
||||
active: isDebugSession,
|
||||
maybeUploadProgress: getUploadProgress,
|
||||
isExpanded: isDebuggerVisible,
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators({
|
||||
hideDebugger: DA.hideDebugger,
|
||||
toggleDebugger: DA.toggleDebugger,
|
||||
clearLog: DA.clearDebuggerLog,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import Infinite from 'react-infinite';
|
||||
|
||||
import { getLog } from '../selectors';
|
||||
import { getLog, getUploadLog } from '../selectors';
|
||||
import SystemMessage from '../components/SystemMessage';
|
||||
import ErrorMessage from '../components/ErrorMessage';
|
||||
import LogMessage from '../components/LogMessage';
|
||||
@@ -24,23 +24,31 @@ const renderLogMessage = (messageData, idx) => R.compose(
|
||||
])
|
||||
)(messageData);
|
||||
|
||||
const Log = ({ log }) => (
|
||||
const Log = ({ log, uploadLog, rejectedMessageTypes }) => (
|
||||
<Infinite
|
||||
className="log"
|
||||
elementHeight={19}
|
||||
containerHeight={180}
|
||||
containerHeight={196}
|
||||
displayBottomUpwards
|
||||
>
|
||||
{log.map(renderLogMessage)}
|
||||
{
|
||||
uploadLog
|
||||
.concat(log)
|
||||
.filter(msg => !rejectedMessageTypes.includes(msg.type))
|
||||
.map(renderLogMessage)
|
||||
}
|
||||
</Infinite>
|
||||
);
|
||||
|
||||
Log.propTypes = {
|
||||
log: PropTypes.arrayOf(PropTypes.object),
|
||||
uploadLog: PropTypes.arrayOf(PropTypes.object),
|
||||
rejectedMessageTypes: PropTypes.arrayOf(PropTypes.string),
|
||||
};
|
||||
|
||||
const mapStateToProps = R.applySpec({
|
||||
log: getLog,
|
||||
uploadLog: getUploadLog,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {})(Log);
|
||||
|
||||
2
packages/xod-client/src/debugger/messages.js
Normal file
2
packages/xod-client/src/debugger/messages.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export const TRANSPILING = 'Transpiling...';
|
||||
export const SUCCES = 'Done!';
|
||||
@@ -2,8 +2,8 @@ import R from 'ramda';
|
||||
import { renameKeys, invertMap } from 'xod-func-tools';
|
||||
|
||||
import {
|
||||
SHOW_DEBUGGER_PANEL,
|
||||
HIDE_DEBUGGER_PANEL,
|
||||
UPLOAD,
|
||||
|
||||
TOGGLE_DEBUGGER_PANEL,
|
||||
|
||||
DEBUGGER_LOG_ADD_MESSAGES,
|
||||
@@ -13,6 +13,9 @@ import {
|
||||
DEBUG_SESSION_STOPPED,
|
||||
} from './actionTypes';
|
||||
|
||||
import { UPLOAD_STATUS, UPLOAD_MSG_TYPE } from './constants';
|
||||
import * as MSG from './messages';
|
||||
|
||||
import initialState from './state';
|
||||
|
||||
const MAX_LOG_MESSAGES = 1000;
|
||||
@@ -23,10 +26,24 @@ const MAX_LOG_MESSAGES = 1000;
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
const addToLog = R.over(R.lensProp('log'));
|
||||
const createSystemMessage = message => ({
|
||||
type: UPLOAD_MSG_TYPE.SYSTEM,
|
||||
message,
|
||||
});
|
||||
const createFlasherMessage = message => ({
|
||||
type: UPLOAD_MSG_TYPE.FLASHER,
|
||||
message,
|
||||
});
|
||||
const createErrorMessage = message => ({
|
||||
type: UPLOAD_MSG_TYPE.ERROR,
|
||||
message,
|
||||
});
|
||||
|
||||
const overDebugLog = R.over(R.lensProp('log'));
|
||||
const overUploadLog = R.over(R.lensProp('uploadLog'));
|
||||
|
||||
const addMessageToLog = R.curry(
|
||||
(message, state) => addToLog(
|
||||
(message, state) => overDebugLog(
|
||||
R.compose(
|
||||
R.takeLast(MAX_LOG_MESSAGES),
|
||||
R.append(message)
|
||||
@@ -36,7 +53,7 @@ const addMessageToLog = R.curry(
|
||||
);
|
||||
|
||||
const addMessageListToLog = R.curry(
|
||||
(messages, state) => addToLog(
|
||||
(messages, state) => overDebugLog(
|
||||
R.compose(
|
||||
R.takeLast(MAX_LOG_MESSAGES),
|
||||
R.concat(R.__, messages)
|
||||
@@ -66,7 +83,6 @@ const updateWatchNodeValues = R.curry(
|
||||
);
|
||||
|
||||
const showDebuggerPane = R.assoc('isVisible', true);
|
||||
const hideDebuggerPane = R.assoc('isVisible', false);
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
@@ -76,17 +92,71 @@ const hideDebuggerPane = R.assoc('isVisible', false);
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case SHOW_DEBUGGER_PANEL:
|
||||
return showDebuggerPane(state);
|
||||
case HIDE_DEBUGGER_PANEL:
|
||||
return hideDebuggerPane(state);
|
||||
case UPLOAD: {
|
||||
const {
|
||||
payload,
|
||||
meta: { status },
|
||||
} = action;
|
||||
|
||||
if (status === UPLOAD_STATUS.STARTED) {
|
||||
return R.compose(
|
||||
R.assoc('uploadProgress', 0),
|
||||
R.assoc('log', []),
|
||||
R.assoc('uploadLog', [
|
||||
createSystemMessage(MSG.TRANSPILING),
|
||||
])
|
||||
)(state);
|
||||
}
|
||||
if (status === UPLOAD_STATUS.PROGRESSED) {
|
||||
const { message, percentage } = payload;
|
||||
|
||||
return R.compose(
|
||||
R.assoc('uploadProgress', percentage),
|
||||
overUploadLog(R.append(createSystemMessage(message))),
|
||||
)(state);
|
||||
}
|
||||
if (status === UPLOAD_STATUS.SUCCEEDED) {
|
||||
const messages = R.compose(
|
||||
R.append(createSystemMessage(MSG.SUCCES)),
|
||||
R.map(createFlasherMessage),
|
||||
R.reject(R.isEmpty),
|
||||
R.split('\n')
|
||||
)(payload.message);
|
||||
|
||||
return R.compose(
|
||||
R.assoc('uploadProgress', null),
|
||||
overUploadLog(R.concat(R.__, messages)),
|
||||
)(state);
|
||||
}
|
||||
if (status === UPLOAD_STATUS.FAILED) {
|
||||
const messages = R.compose(
|
||||
R.map(createErrorMessage),
|
||||
R.reject(R.isEmpty),
|
||||
R.split('\n')
|
||||
)(payload.message);
|
||||
|
||||
return R.compose(
|
||||
R.assoc('uploadProgress', null),
|
||||
overUploadLog(R.concat(R.__, messages)),
|
||||
showDebuggerPane
|
||||
)(state);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
case TOGGLE_DEBUGGER_PANEL:
|
||||
return R.over(R.lensProp('isVisible'), R.not, state);
|
||||
case DEBUGGER_LOG_ADD_MESSAGES:
|
||||
case DEBUGGER_LOG_ADD_MESSAGES: {
|
||||
const showPanelOnErrorMessages = R.any(R.propEq('type', UPLOAD_MSG_TYPE.ERROR), action.payload)
|
||||
? showDebuggerPane
|
||||
: R.identity;
|
||||
|
||||
return R.compose(
|
||||
showPanelOnErrorMessages,
|
||||
addMessageListToLog(action.payload),
|
||||
updateWatchNodeValues(action.payload)
|
||||
)(state);
|
||||
}
|
||||
case DEBUGGER_LOG_CLEAR:
|
||||
return R.assoc('log', [], state);
|
||||
case DEBUG_SESSION_STARTED:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import R from 'ramda';
|
||||
import { Maybe } from 'ramda-fantasy';
|
||||
import { createSelector } from 'reselect';
|
||||
import {
|
||||
getCurrentTabId,
|
||||
@@ -24,6 +25,11 @@ export const getLog = R.compose(
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
export const getUploadLog = R.compose(
|
||||
R.prop('uploadLog'),
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
export const getDebuggerNodeIdsMap = R.compose(
|
||||
R.prop('nodeIdsMap'),
|
||||
getDebuggerState
|
||||
@@ -34,6 +40,12 @@ export const getWatchNodeValues = R.compose(
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
export const getUploadProgress = R.compose(
|
||||
Maybe,
|
||||
R.prop('uploadProgress'),
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
export const getWatchNodeValuesForCurrentPatch = createSelector(
|
||||
[getCurrentTabId, getWatchNodeValues, getBreadcrumbChunks, getBreadcrumbActiveIndex],
|
||||
(tabId, nodeValues, chunks, activeIndex) => {
|
||||
|
||||
@@ -2,6 +2,8 @@ export default {
|
||||
isVisible: false,
|
||||
isRunning: false,
|
||||
log: [],
|
||||
uploadLog: [],
|
||||
nodeIdsMap: {},
|
||||
watchNodeValues: {},
|
||||
uploadProgress: null,
|
||||
};
|
||||
|
||||
@@ -184,7 +184,6 @@ class Editor extends React.Component {
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const DebuggerContainer = (this.props.isDebuggerVisible) ? <Debugger /> : null;
|
||||
const BreadcrumbsContainer = R.pathEq(['currentTab', 'id'], DEBUGGER_TAB_ID, this.props)
|
||||
? <Breadcrumbs />
|
||||
: null;
|
||||
@@ -214,12 +213,17 @@ class Editor extends React.Component {
|
||||
onFocus={this.onWorkareaFocus}
|
||||
>
|
||||
<Tabs />
|
||||
{DebugSessionStopButton}
|
||||
{this.renderOpenedPatchTab()}
|
||||
{BreadcrumbsContainer}
|
||||
{this.renderOpenedImplementationEditorTabs()}
|
||||
{DebuggerContainer}
|
||||
<SnackBar />
|
||||
<div className="Workarea-inner">
|
||||
{DebugSessionStopButton}
|
||||
{this.renderOpenedPatchTab()}
|
||||
{BreadcrumbsContainer}
|
||||
{this.renderOpenedImplementationEditorTabs()}
|
||||
<SnackBar />
|
||||
</div>
|
||||
<Debugger
|
||||
onUploadClick={this.props.onUploadClick}
|
||||
onUploadAndDebugClick={this.props.onUploadAndDebugClick}
|
||||
/>
|
||||
</FocusTrap>
|
||||
<Sidebar
|
||||
id={SIDEBAR_IDS.RIGHT}
|
||||
@@ -245,13 +249,14 @@ Editor.propTypes = {
|
||||
implEditorTabs: PropTypes.array,
|
||||
patchesIndex: PropTypes.object,
|
||||
isHelpboxVisible: PropTypes.bool,
|
||||
isDebuggerVisible: PropTypes.bool,
|
||||
isDebugSessionRunning: PropTypes.bool,
|
||||
suggesterIsVisible: PropTypes.bool,
|
||||
suggesterPlacePosition: PropTypes.object,
|
||||
isLibSuggesterVisible: PropTypes.bool,
|
||||
defaultNodePosition: PropTypes.object.isRequired,
|
||||
stopDebuggerSession: PropTypes.func,
|
||||
onUploadClick: PropTypes.func.isRequired,
|
||||
onUploadAndDebugClick: PropTypes.func.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
updatePatchImplementation: PropTypes.func.isRequired,
|
||||
closeImplementationEditor: PropTypes.func.isRequired,
|
||||
@@ -285,7 +290,6 @@ const mapStateToProps = R.applySpec({
|
||||
suggesterPlacePosition: EditorSelectors.getSuggesterPlacePosition,
|
||||
isLibSuggesterVisible: EditorSelectors.isLibSuggesterVisible,
|
||||
isHelpboxVisible: EditorSelectors.isHelpboxVisible,
|
||||
isDebuggerVisible: DebuggerSelectors.isDebuggerVisible,
|
||||
isDebugSessionRunning: DebuggerSelectors.isDebugSession,
|
||||
defaultNodePosition: EditorSelectors.getDefaultNodePlacePosition,
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ const rawItems = {
|
||||
},
|
||||
toggleDebugger: {
|
||||
key: 'toggleDebugger',
|
||||
label: 'Toggle Debugger',
|
||||
label: 'Toggle Deployment Pane',
|
||||
command: COMMAND.TOGGLE_DEBUGGER,
|
||||
},
|
||||
toggleAccountPane: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from 'redux';
|
||||
import { Provider, connect } from 'react-redux';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import Debugger from '../src/debugger/containers/Debugger';
|
||||
import DebuggerReducer from '../src/debugger/reducer';
|
||||
@@ -57,6 +58,51 @@ const addError = (store) => {
|
||||
}]));
|
||||
};
|
||||
|
||||
const addUploadingLog = (store) => {
|
||||
store.dispatch({
|
||||
type: 'UPLOAD',
|
||||
meta: {
|
||||
status: 'started',
|
||||
},
|
||||
});
|
||||
|
||||
store.dispatch({
|
||||
type: 'UPLOAD',
|
||||
payload: {
|
||||
message: 'Project was successfully transpiled. Searching for device...',
|
||||
percentage: 10,
|
||||
id: 1,
|
||||
},
|
||||
meta: {
|
||||
status: 'progressed',
|
||||
},
|
||||
});
|
||||
|
||||
store.dispatch({
|
||||
type: 'UPLOAD',
|
||||
payload: {
|
||||
message: 'Port with connected Arduino was found. Installing toolchains...',
|
||||
percentage: 15,
|
||||
id: 1,
|
||||
},
|
||||
meta: {
|
||||
status: 'progressed',
|
||||
},
|
||||
});
|
||||
|
||||
store.dispatch({
|
||||
type: 'UPLOAD',
|
||||
payload: {
|
||||
message: 'Toolchain is installed. Uploading...',
|
||||
percentage: 30,
|
||||
id: 1,
|
||||
},
|
||||
meta: {
|
||||
status: 'progressed',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const startDebugSession = (store) => {
|
||||
store.dispatch(startDebuggerSession({
|
||||
type: 'system',
|
||||
@@ -85,7 +131,75 @@ const idle = () => {
|
||||
storiesOf('Debugger', module)
|
||||
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
||||
.add('idle', () => (
|
||||
<Debugger />
|
||||
<Debugger
|
||||
onUploadClick={action('onUploadClick')}
|
||||
onUploadAndDebugClick={action('onUploadAndDebugClick')}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const uploading = () => {
|
||||
const store = createStore();
|
||||
addUploadingLog(store);
|
||||
|
||||
storiesOf('Debugger', module)
|
||||
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
||||
.add('uploading', () => (
|
||||
<Debugger
|
||||
onUploadClick={action('onUploadClick')}
|
||||
onUploadAndDebugClick={action('onUploadAndDebugClick')}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const uploadingSuccess = () => {
|
||||
const store = createStore();
|
||||
addUploadingLog(store);
|
||||
|
||||
store.dispatch({
|
||||
type: 'UPLOAD',
|
||||
payload: {
|
||||
message: '\nConnecting to programmer: .\nFound programmer: Id = "CATERIN"; type = S\n Software Version = 1.0; No Hardware Version given.\nProgrammer supports auto addr increment.\nProgrammer supports buffered memory access with buffersize=128 bytes.\n\nProgrammer supports the following devices:\n Device code: 0x44\n\navrdude: AVR device initialized and ready to accept instructions\n\nReading | ################################################## | 100% 0.00s\n\navrdude: Device signature = 0x1e9587 (probably m32u4)\navrdude: reading input file "/Users/user/Library/Application Support/xod-client-electron/upload-temp/build/xod-arduino-sketch.cpp.hex"\navrdude: writing flash (6884 bytes):\n\nWriting | ################################################## | 100% 0.53s\n\navrdude: 6884 bytes of flash written\navrdude: verifying flash memory against /Users/user/Library/Application Support/xod-client-electron/upload-temp/build/xod-arduino-sketch.cpp.hex:\navrdude: load data flash data from input file /Users/user/Library/Application Support/xod-client-electron/upload-temp/build/xod-arduino-sketch.cpp.hex:\navrdude: input file /Users/user/Library/Application Support/xod-client-electron/upload-temp/build/xod-arduino-sketch.cpp.hex contains 6884 bytes\navrdude: reading on-chip flash data:\n\nReading | ################################################## | 100% 0.07s\n\navrdude: verifying ...\navrdude: 6884 bytes of flash verified\n\navrdude done. Thank you.\n\n\n\n',
|
||||
id: 1,
|
||||
},
|
||||
meta: {
|
||||
status: 'succeeded',
|
||||
},
|
||||
});
|
||||
|
||||
storiesOf('Debugger', module)
|
||||
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
||||
.add('uploading sussess', () => (
|
||||
<Debugger
|
||||
onUploadClick={action('onUploadClick')}
|
||||
onUploadAndDebugClick={action('onUploadAndDebugClick')}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const uploadingFail = () => {
|
||||
const store = createStore();
|
||||
addUploadingLog(store);
|
||||
|
||||
store.dispatch({
|
||||
type: 'UPLOAD',
|
||||
payload: {
|
||||
message: 'Error occured during uploading: Some horrible stuff happened',
|
||||
percentage: 30,
|
||||
id: 1,
|
||||
},
|
||||
meta: {
|
||||
status: 'failed',
|
||||
},
|
||||
});
|
||||
|
||||
storiesOf('Debugger', module)
|
||||
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
||||
.add('uploading fail', () => (
|
||||
<Debugger
|
||||
onUploadClick={action('onUploadClick')}
|
||||
onUploadAndDebugClick={action('onUploadAndDebugClick')}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
@@ -100,7 +214,10 @@ const running = () => {
|
||||
.add('running', () => (
|
||||
<div>
|
||||
<LogLength />
|
||||
<Debugger />
|
||||
<Debugger
|
||||
onUploadClick={action('onUploadClick')}
|
||||
onUploadAndDebugClick={action('onUploadAndDebugClick')}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
@@ -112,4 +229,7 @@ const running = () => {
|
||||
// =============================================================================
|
||||
|
||||
idle();
|
||||
uploading();
|
||||
uploadingSuccess();
|
||||
uploadingFail();
|
||||
running();
|
||||
|
||||
Reference in New Issue
Block a user