From 84cab181207f9289695d7e05ff54defb2165b415 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 19 May 2017 17:07:59 +0200 Subject: [PATCH] Update the Console dapp (#5602) * Init Console Dapp structure * Watches and status * First REPL and display * Attaching console * Selectable autocomplete * working console // Display objects nicely * Multilines in Console Dapps * Better UI * Eval on window object * Save console gistory * Adding views * Add settings to the console dapp * Add / remove Watches * Add Snippets Cmponent * Semi Working Snippets * Working Snippets * Adding CodeMirror features * Removing old Console * Add Static folder --- js/package.json | 2 + js/src/dapps/console.js | 59 ++ .../dapps/console/Application/application.css | 65 ++ .../dapps/console/Application/application.js | 94 +++ .../console/Application/application.store.js | 42 + js/src/dapps/console/Application/index.js | 17 + .../console/Autocomplete/autocomplete.css | 55 ++ .../console/Autocomplete/autocomplete.js | 96 +++ .../Autocomplete/autocomplete.store.js | 234 ++++++ js/src/dapps/console/Autocomplete/index.js | 17 + js/src/dapps/console/Console/console.css | 58 ++ js/src/dapps/console/Console/console.js | 118 +++ js/src/dapps/console/Console/console.store.js | 126 +++ js/src/dapps/console/Console/index.js | 17 + js/src/dapps/console/Header/header.css | 51 ++ js/src/dapps/console/Header/header.js | 65 ++ js/src/dapps/console/Header/index.js | 17 + js/src/dapps/console/Input/index.js | 17 + js/src/dapps/console/Input/input.css | 46 ++ js/src/dapps/console/Input/input.js | 145 ++++ js/src/dapps/console/Input/input.store.js | 124 +++ js/src/dapps/console/Settings/index.js | 17 + js/src/dapps/console/Settings/settings.css | 32 + js/src/dapps/console/Settings/settings.js | 70 ++ .../dapps/console/Settings/settings.store.js | 71 ++ js/src/dapps/console/Snippets/index.js | 17 + js/src/dapps/console/Snippets/snippets.css | 122 +++ js/src/dapps/console/Snippets/snippets.js | 221 ++++++ .../dapps/console/Snippets/snippets.store.js | 249 ++++++ js/src/dapps/console/Watches/index.js | 17 + js/src/dapps/console/Watches/watches.css | 107 +++ js/src/dapps/console/Watches/watches.js | 186 +++++ js/src/dapps/console/Watches/watches.store.js | 148 ++++ js/src/dapps/console/codemirror.css | 36 + js/src/dapps/console/parity.js | 335 ++++++++ js/src/dapps/console/utils.js | 36 + js/src/dapps/static/.gitkeep | 0 js/src/dapps/static/console.css | 221 ------ js/src/dapps/static/console.html | 19 - js/src/dapps/static/console.js | 730 ------------------ js/src/dapps/style.css | 4 +- js/src/views/Dapps/builtin.json | 23 +- js/yarn.lock | 56 +- 43 files changed, 3189 insertions(+), 993 deletions(-) create mode 100644 js/src/dapps/console.js create mode 100644 js/src/dapps/console/Application/application.css create mode 100644 js/src/dapps/console/Application/application.js create mode 100644 js/src/dapps/console/Application/application.store.js create mode 100644 js/src/dapps/console/Application/index.js create mode 100644 js/src/dapps/console/Autocomplete/autocomplete.css create mode 100644 js/src/dapps/console/Autocomplete/autocomplete.js create mode 100644 js/src/dapps/console/Autocomplete/autocomplete.store.js create mode 100644 js/src/dapps/console/Autocomplete/index.js create mode 100644 js/src/dapps/console/Console/console.css create mode 100644 js/src/dapps/console/Console/console.js create mode 100644 js/src/dapps/console/Console/console.store.js create mode 100644 js/src/dapps/console/Console/index.js create mode 100644 js/src/dapps/console/Header/header.css create mode 100644 js/src/dapps/console/Header/header.js create mode 100644 js/src/dapps/console/Header/index.js create mode 100644 js/src/dapps/console/Input/index.js create mode 100644 js/src/dapps/console/Input/input.css create mode 100644 js/src/dapps/console/Input/input.js create mode 100644 js/src/dapps/console/Input/input.store.js create mode 100644 js/src/dapps/console/Settings/index.js create mode 100644 js/src/dapps/console/Settings/settings.css create mode 100644 js/src/dapps/console/Settings/settings.js create mode 100644 js/src/dapps/console/Settings/settings.store.js create mode 100644 js/src/dapps/console/Snippets/index.js create mode 100644 js/src/dapps/console/Snippets/snippets.css create mode 100644 js/src/dapps/console/Snippets/snippets.js create mode 100644 js/src/dapps/console/Snippets/snippets.store.js create mode 100644 js/src/dapps/console/Watches/index.js create mode 100644 js/src/dapps/console/Watches/watches.css create mode 100644 js/src/dapps/console/Watches/watches.js create mode 100644 js/src/dapps/console/Watches/watches.store.js create mode 100644 js/src/dapps/console/codemirror.css create mode 100644 js/src/dapps/console/parity.js create mode 100644 js/src/dapps/console/utils.js create mode 100644 js/src/dapps/static/.gitkeep delete mode 100755 js/src/dapps/static/console.css delete mode 100755 js/src/dapps/static/console.html delete mode 100755 js/src/dapps/static/console.js diff --git a/js/package.json b/js/package.json index 5c203a091..bcfe4f5d7 100644 --- a/js/package.json +++ b/js/package.json @@ -196,11 +196,13 @@ "react": "15.4.2", "react-ace": "4.1.0", "react-addons-css-transition-group": "15.4.2", + "react-codemirror": "^0.3.0", "react-copy-to-clipboard": "4.2.3", "react-dom": "15.4.2", "react-dropzone": "3.7.3", "react-element-to-jsx-string": "6.0.0", "react-event-listener": "0.4.1", + "react-inspector": "paritytech/react-inspector", "react-intl": "2.1.5", "react-markdown": "2.4.4", "react-portal": "3.0.0", diff --git a/js/src/dapps/console.js b/js/src/dapps/console.js new file mode 100644 index 000000000..44b6dcb9c --- /dev/null +++ b/js/src/dapps/console.js @@ -0,0 +1,59 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppContainer } from 'react-hot-loader'; + +import 'codemirror/addon/dialog/dialog'; +import 'codemirror/addon/dialog/dialog.css'; +import 'codemirror/addon/hint/javascript-hint'; +import 'codemirror/addon/hint/show-hint'; +import 'codemirror/addon/hint/show-hint.css'; +import 'codemirror/addon/search/match-highlighter'; +import 'codemirror/addon/search/search'; +import 'codemirror/addon/search/searchcursor'; +import 'codemirror/keymap/sublime'; +import 'codemirror/lib/codemirror.css'; +import 'codemirror/mode/javascript/javascript'; +// Custom codemirror style +import './console/codemirror.css'; + +import Application from './console/Application'; + +import '../../assets/fonts/Roboto/font.css'; +import '../../assets/fonts/RobotoMono/font.css'; +import './style.css'; + +ReactDOM.render( + + + , + document.querySelector('#container') +); + +if (module.hot) { + module.hot.accept('./console/Application/index.js', () => { + require('./console/Application/index.js'); + + ReactDOM.render( + + + , + document.querySelector('#container') + ); + }); +} diff --git a/js/src/dapps/console/Application/application.css b/js/src/dapps/console/Application/application.css new file mode 100644 index 000000000..eea2c030d --- /dev/null +++ b/js/src/dapps/console/Application/application.css @@ -0,0 +1,65 @@ +/* Copyright 2015-2017 Parity Technologies (UK) Ltd. +/* This file is part of Parity. +/* +/* Parity is free software: you can redistribute it and/or modify +/* it under the terms of the GNU General Public License as published by +/* the Free Software Foundation, either version 3 of the License, or +/* (at your option) any later version. +/* +/* Parity is distributed in the hope that it will be useful, +/* but WITHOUT ANY WARRANTY; without even the implied warranty of +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/* GNU General Public License for more details. +/* +/* You should have received a copy of the GNU General Public License +/* along with Parity. If not, see . +*/ + +.app { + display: flex; + flex-direction: column; + font-family: Arial, sans-serif; + font-size: 11px; + height: 100vh; + overflow: hidden; +} + +textarea, +input { + font-family: dejavu sans mono, monospace; + outline: none; +} + +code, +pre { + font-family: dejavu sans mono, monospace; + font-size: 11px; +} + +.header { + flex: 0 0 auto; +} + +.view { + display: flex; + flex: 1; + flex-direction: column; +} + +.eval { + flex: 0 1 auto; + font-family: dejavu sans mono, monospace; + overflow: auto; +} + +.input { + border-top: 1px solid #eee; + display: flex; + flex: 1 1 auto; + min-height: 50px; +} + +.status { + flex: 0 0 auto; + font-family: dejavu sans mono, monospace; +} diff --git a/js/src/dapps/console/Application/application.js b/js/src/dapps/console/Application/application.js new file mode 100644 index 000000000..5a591e710 --- /dev/null +++ b/js/src/dapps/console/Application/application.js @@ -0,0 +1,94 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; + +import { api } from '../parity'; + +import Console from '../Console'; +import Header from '../Header'; +import Input from '../Input'; +import Settings from '../Settings'; +import Snippets from '../Snippets'; +import Watches from '../Watches'; + +import ApplicationStore from './application.store'; +import WatchesStore from '../Watches/watches.store'; + +import styles from './application.css'; + +@observer +export default class Application extends Component { + application = ApplicationStore.get(); + watches = WatchesStore.get(); + + componentWillMount () { + this.watches.add('time', () => new Date()); + this.watches.add('blockNumber', api.eth.blockNumber, api); + } + + render () { + return ( +
+
+
+
+ + { this.renderView() } + +
+ +
+
+ ); + } + + renderView () { + const { view } = this.application; + + if (view === 'console') { + return ( +
+
+ +
+
+ +
+
+ ); + } + + if (view === 'settings') { + return ( +
+ +
+ ); + } + + if (view === 'snippets') { + return ( +
+ +
+ ); + } + + return null; + } +} diff --git a/js/src/dapps/console/Application/application.store.js b/js/src/dapps/console/Application/application.store.js new file mode 100644 index 000000000..c10be46c6 --- /dev/null +++ b/js/src/dapps/console/Application/application.store.js @@ -0,0 +1,42 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { action, observable } from 'mobx'; + +let instance; + +export default class ApplicationStore { + @observable view = this.views[0].id; + + views = [ + { label: 'Console', id: 'console' }, + { label: 'Snippets', id: 'snippets' }, + { label: 'Settings', id: 'settings' } + ]; + + static get () { + if (!instance) { + instance = new ApplicationStore(); + } + + return instance; + } + + @action + setView (view) { + this.view = view; + } +} diff --git a/js/src/dapps/console/Application/index.js b/js/src/dapps/console/Application/index.js new file mode 100644 index 000000000..3d8d1ca3b --- /dev/null +++ b/js/src/dapps/console/Application/index.js @@ -0,0 +1,17 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './application'; diff --git a/js/src/dapps/console/Autocomplete/autocomplete.css b/js/src/dapps/console/Autocomplete/autocomplete.css new file mode 100644 index 000000000..8d4585e7a --- /dev/null +++ b/js/src/dapps/console/Autocomplete/autocomplete.css @@ -0,0 +1,55 @@ +/* Copyright 2015-2017 Parity Technologies (UK) Ltd. +/* This file is part of Parity. +/* +/* Parity is free software: you can redistribute it and/or modify +/* it under the terms of the GNU General Public License as published by +/* the Free Software Foundation, either version 3 of the License, or +/* (at your option) any later version. +/* +/* Parity is distributed in the hope that it will be useful, +/* but WITHOUT ANY WARRANTY; without even the implied warranty of +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/* GNU General Public License for more details. +/* +/* You should have received a copy of the GNU General Public License +/* along with Parity. If not, see . +*/ + +.container { + background: #f8f8f8; + box-shadow: 0 0.125em 0.25em rgba(0, 0, 0, 0.5); + font-family: dejavu sans mono, monospace; + left: 20px; + position: absolute; + max-height: 300px; + overflow: auto; +} + +.item { + background-color: white; + padding: 0.25em 0.25em 0.25em 0.35em; + display: flex; + justify-content: space-between; + + &.selected { + background-color: rgb(64, 115, 244); + + &, + .proto { + color: white; + } + } + + &:hover { + cursor: default; + } + + &:hover:not(.selected) { + background-color: rgb(230, 236, 255); + } + + .proto { + color: gray; + margin-left: 1em; + } +} diff --git a/js/src/dapps/console/Autocomplete/autocomplete.js b/js/src/dapps/console/Autocomplete/autocomplete.js new file mode 100644 index 000000000..e2938f23d --- /dev/null +++ b/js/src/dapps/console/Autocomplete/autocomplete.js @@ -0,0 +1,96 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; + +import AutocompleteStore from './autocomplete.store'; + +import styles from './autocomplete.css'; + +@observer +export default class Autocomplete extends Component { + autocompleteStore = AutocompleteStore.get(); + + render () { + if (!this.autocompleteStore.show) { + return null; + } + + return ( +
+ { this.renderAutocompletes() } +
+ ); + } + + renderAutocompletes () { + const { selected, values } = this.autocompleteStore; + const displayedProto = {}; + + return values.map((autocomplete, index) => { + const { name, prototypeName } = autocomplete; + const onClick = () => this.handleClick(index); + const setRef = (node) => this.setRef(index, node); + + const proto = !displayedProto[prototypeName] + ? ( + + { prototypeName } + + ) + : null; + + if (!displayedProto[prototypeName]) { + displayedProto[prototypeName] = true; + } + + const classes = [ styles.item ]; + + if (index === selected) { + classes.push(styles.selected); + } + + return ( +
+ + { name } + + { proto } +
+ ); + }); + } + + handleClick = (index) => { + this.autocompleteStore.select(index); + }; + + setRef = (index, node) => { + const element = ReactDOM.findDOMNode(node); + + this.autocompleteStore.setElement(index, element); + }; +} diff --git a/js/src/dapps/console/Autocomplete/autocomplete.store.js b/js/src/dapps/console/Autocomplete/autocomplete.store.js new file mode 100644 index 000000000..82ff2f24d --- /dev/null +++ b/js/src/dapps/console/Autocomplete/autocomplete.store.js @@ -0,0 +1,234 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { action, observable } from 'mobx'; + +import { evaluate } from '../utils'; + +let instance; + +export default class AutocompleteStore { + @observable values = []; + @observable position = {}; + @observable show = false; + @observable selected = null; + + elements = {}; + inputNode = null; + lastObject = null; + lastObjectPropertyNames = []; + + static get () { + if (!instance) { + instance = new AutocompleteStore(); + } + + return instance; + } + + get hasSelected () { + return this.selected !== null; + } + + clearCache () { + this.lastObject = null; + this.lastObjectPropertyNames = null; + } + + @action + focus (offset = 1) { + if (this.values.length === 0) { + this.selected = null; + return; + } + + this.selected = this.selected === null + ? ( + offset === 1 + ? 0 + : this.values.length - 1 + ) + : (this.values.length + this.selected + offset) % (this.values.length); + + if (this.isVisible(this.selected)) { + return; + } + + const element = this.elements[this.selected]; + + if (!element) { + return; + } + + element.scrollIntoView(offset === -1); + } + + focusOnInput () { + if (!this.inputNode) { + return; + } + + this.inputNode.focus(); + } + + @action + hide () { + this.show = false; + this.selected = null; + } + + isVisible (index) { + const element = this.elements[index]; + + if (!element) { + return false; + } + + const eBoundings = element.getBoundingClientRect(); + const pBoundings = element.parentElement.getBoundingClientRect(); + + if (eBoundings.top < pBoundings.top || eBoundings.bottom > pBoundings.bottom) { + return false; + } + + return true; + } + + select (inputStore, _index = this.selected) { + const index = _index === null + ? 0 + : _index; + + if (!this.values[index]) { + console.warn(`autocomplete::select has been called on AutocompleteStore with wrong value ${index}`); + return; + } + + const { name } = this.values[index]; + const { input } = inputStore; + const objects = input.split('.'); + + objects[objects.length - 1] = name; + const nextInput = objects.join('.'); + + this.hide(); + this.focusOnInput(); + return inputStore.updateInput(nextInput, false); + } + + setElement (index, element) { + this.elements[index] = element; + } + + setInputNode (node) { + this.inputNode = node; + } + + @action + setPosition () { + if (!this.inputNode) { + return; + } + + const inputBoundings = this.inputNode.getBoundingClientRect(); + const bodyBoundings = document.body.getBoundingClientRect(); + + // display on bottom of input + if (inputBoundings.top < bodyBoundings.height / 2) { + const nextPosition = { + top: 20 + }; + + this.position = nextPosition; + return; + } + + // display on top of input + const nextPosition = { + bottom: inputBoundings.height + }; + + this.position = nextPosition; + return; + } + + @action + setValues (values) { + this.values = values; + this.selected = null; + const show = values.length > 0; + + // Reveal autocomplete + if (!this.show && show) { + this.setPosition(); + } + + this.show = show; + } + + update (input) { + if (input.length === 0) { + return this.setValues([]); + } + + const objects = input.split('.'); + const suffix = objects.pop().toLowerCase(); + const prefix = objects.join('.'); + const object = prefix.length > 0 + ? prefix + : 'window'; + + if (object !== this.lastObject) { + const evalResult = evaluate(object); + + if (evalResult.error) { + this.lastObjectProperties = []; + } else { + this.lastObjectProperties = getAllProperties(evalResult.result); + } + + this.lastObject = object; + } + + const autocompletes = this.lastObjectProperties.filter((property) => { + return property.name.toLowerCase().includes(suffix); + }); + + return this.setValues(autocompletes); + } +} + +function getAllProperties (object) { + const propertyNames = {}; + + while (object) { + const prototypeName = object && object.constructor && object.constructor.name || ''; + + Object.getOwnPropertyNames(object) + .sort() + .forEach((name) => { + if (Object.prototype.hasOwnProperty.call(propertyNames, name)) { + return; + } + + propertyNames[name] = { name, prototypeName }; + }); + + object = Object.getPrototypeOf(object); + } + + return Object.values(propertyNames); +} diff --git a/js/src/dapps/console/Autocomplete/index.js b/js/src/dapps/console/Autocomplete/index.js new file mode 100644 index 000000000..5761be0e3 --- /dev/null +++ b/js/src/dapps/console/Autocomplete/index.js @@ -0,0 +1,17 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './autocomplete'; diff --git a/js/src/dapps/console/Console/console.css b/js/src/dapps/console/Console/console.css new file mode 100644 index 000000000..a0b3db4ff --- /dev/null +++ b/js/src/dapps/console/Console/console.css @@ -0,0 +1,58 @@ +/* Copyright 2015-2017 Parity Technologies (UK) Ltd. +/* This file is part of Parity. +/* +/* Parity is free software: you can redistribute it and/or modify +/* it under the terms of the GNU General Public License as published by +/* the Free Software Foundation, either version 3 of the License, or +/* (at your option) any later version. +/* +/* Parity is distributed in the hope that it will be useful, +/* but WITHOUT ANY WARRANTY; without even the implied warranty of +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/* GNU General Public License for more details. +/* +/* You should have received a copy of the GNU General Public License +/* along with Parity. If not, see . +*/ + +.result { + border-top: 1px solid #eee; + display: flex; + font-family: dejavu sans mono, monospace; + padding: 0.35em 0.25em; + + &.error { + background-color: hsl(0, 100%, 97%); + + .text { + color: red; + } + } + + &.warn { + background-color: hsl(50, 100%, 95%); + } +} + +.type { + font-weight: bold !important; + font-size: 8pt; + padding: 0 0.5em 0 0.25em; +} + +.time { + color: gray; + padding: 0 1em 0 0.5em; +} + +.token { + white-space: pre-wrap; +} + +.text { + display: flex; +} + +.text .token:not(:first-child) { + margin-left: 0.5em; +} diff --git a/js/src/dapps/console/Console/console.js b/js/src/dapps/console/Console/console.js new file mode 100644 index 000000000..75f9713a6 --- /dev/null +++ b/js/src/dapps/console/Console/console.js @@ -0,0 +1,118 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import { ObjectInspector } from 'react-inspector'; + +import ConsoleStore from './console.store'; +import SettingsStore from '../Settings/settings.store'; + +import styles from './console.css'; + +const ICONS = { + debug: ' ', + error: '✖', + info: 'ℹ', + input: '>', + log: ' ', + result: '<', + warn: '⚠' +}; + +@observer +export default class Console extends Component { + consoleStore = ConsoleStore.get(); + settingsStore = SettingsStore.get(); + + render () { + return ( +
+ { this.renderResults() } +
+ ); + } + + renderResults () { + const { logs } = this.consoleStore; + + return logs.map((data, index) => { + const { type, timestamp } = data; + const values = this.consoleStore.logValues[index]; + const classes = [ styles.result, styles[type] ]; + + return ( +
+ + { this.renderTimestamp(timestamp) } + + { + values.map((value, valueIndex) => ( + + { this.toString(value) } + + )) + } + +
+ ); + }); + } + + renderTimestamp (timestamp) { + const { displayTimestamps } = this.settingsStore; + + if (!displayTimestamps) { + return null; + } + + return ( + + { new Date(timestamp).toISOString().slice(11, 23) } + + ); + } + + setRef = (node) => { + const element = ReactDOM.findDOMNode(node); + + this.consoleStore.setNode(element); + }; + + toString (value) { + if (typeof value === 'string') { + return value; + } + + if (value instanceof Error) { + return value.toString(); + } + + return ( + + ); + } +} diff --git a/js/src/dapps/console/Console/console.store.js b/js/src/dapps/console/Console/console.store.js new file mode 100644 index 000000000..dc2fc6db4 --- /dev/null +++ b/js/src/dapps/console/Console/console.store.js @@ -0,0 +1,126 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { action, observable } from 'mobx'; + +import AutocompleteStore from '../Autocomplete/autocomplete.store'; +import { evaluate } from '../utils'; + +let instance; + +export default class ConsoleStore { + @observable logs = []; + + autocompleteStore = AutocompleteStore.get(); + logValues = []; + node = null; + + constructor () { + this.attachConsole(); + } + + static get () { + if (!instance) { + instance = new ConsoleStore(); + } + + return instance; + } + + attachConsole () { + ['debug', 'error', 'info', 'log', 'warn'].forEach((level) => { + const old = window.console[level].bind(window.console); + + window.console[level] = (...args) => { + old(...args); + this.log({ type: level, values: args }); + }; + }); + } + + @action + clear () { + this.logs = []; + this.logValues = []; + } + + evaluate (input) { + this.log({ type: 'input', value: input }); + + setTimeout(() => { + const { result, error } = evaluate(input); + let value = error || result; + const type = error + ? 'error' + : 'result'; + + if (typeof value === 'string') { + value = `"${value}"`; + } + + if (value && typeof value === 'object' && typeof value.then === 'function') { + return value + .then((result) => { + this.log({ type: 'result', value: result }); + }) + .catch((error) => { + this.log({ type: 'error', value: error }); + }); + } + + this.log({ type, value }); + }); + } + + @action + log ({ type, value, values }) { + this.logs.push({ + type, + timestamp: Date.now() + }); + + if (values) { + this.logValues.push(values); + } else { + this.logValues.push([ value ]); + } + + this.autocompleteStore.setPosition(); + this.scroll(); + } + + setNode (node) { + this.node = node; + this.scroll(); + } + + scroll () { + if (!this.node) { + return; + } + + setTimeout(() => { + if (this.node.children.length === 0) { + return; + } + + // Scroll to the last child + this.node + .children[this.node.children.length - 1] + .scrollIntoView(false); + }, 50); + } +} diff --git a/js/src/dapps/console/Console/index.js b/js/src/dapps/console/Console/index.js new file mode 100644 index 000000000..2956b330f --- /dev/null +++ b/js/src/dapps/console/Console/index.js @@ -0,0 +1,17 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './console'; diff --git a/js/src/dapps/console/Header/header.css b/js/src/dapps/console/Header/header.css new file mode 100644 index 000000000..116de6b8c --- /dev/null +++ b/js/src/dapps/console/Header/header.css @@ -0,0 +1,51 @@ +/* Copyright 2015-2017 Parity Technologies (UK) Ltd. +/* This file is part of Parity. +/* +/* Parity is free software: you can redistribute it and/or modify +/* it under the terms of the GNU General Public License as published by +/* the Free Software Foundation, either version 3 of the License, or +/* (at your option) any later version. +/* +/* Parity is distributed in the hope that it will be useful, +/* but WITHOUT ANY WARRANTY; without even the implied warranty of +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/* GNU General Public License for more details. +/* +/* You should have received a copy of the GNU General Public License +/* along with Parity. If not, see . +*/ + +.container { + background-color: #f3f3f3; + border-bottom: 1px solid #ccc; + font-size: 12px; + padding: 0 0.5em; +} + +.tabs { + display: flex; +} + +.tab { + align-items: center; + box-sizing: border-box; + border: 1px solid transparent; + color: #333; + cursor: default; + display: flex; + height: 24px; + line-height: 15px; + margin-top: 2px; + padding: 2px 6px 2px 4px; + + &:hover, + &.active:hover { + background-color: #e5e5e5; + } + + &.active { + background-color: white; + border: 1px solid #ccc; + border-bottom: none; + } +} diff --git a/js/src/dapps/console/Header/header.js b/js/src/dapps/console/Header/header.js new file mode 100644 index 000000000..c422b8256 --- /dev/null +++ b/js/src/dapps/console/Header/header.js @@ -0,0 +1,65 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; + +import ApplicationStore from '../Application/application.store'; + +import styles from './header.css'; + +@observer +export default class Header extends Component { + application = ApplicationStore.get(); + + render () { + return ( +
+
+ { this.renderTabs() } +
+
+ ); + } + + renderTabs () { + const { view } = this.application; + + return this.application.views.map((tab) => { + const { label, id } = tab; + const classes = [ styles.tab ]; + const onClick = () => this.handleClickTab(id); + + if (id === view) { + classes.push(styles.active); + } + + return ( +
+ { label } +
+ ); + }); + } + + handleClickTab = (id) => { + this.application.setView(id); + }; +} diff --git a/js/src/dapps/console/Header/index.js b/js/src/dapps/console/Header/index.js new file mode 100644 index 000000000..aef90266f --- /dev/null +++ b/js/src/dapps/console/Header/index.js @@ -0,0 +1,17 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './header'; diff --git a/js/src/dapps/console/Input/index.js b/js/src/dapps/console/Input/index.js new file mode 100644 index 000000000..29e00f72b --- /dev/null +++ b/js/src/dapps/console/Input/index.js @@ -0,0 +1,17 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default from './input'; diff --git a/js/src/dapps/console/Input/input.css b/js/src/dapps/console/Input/input.css new file mode 100644 index 000000000..7b0c2306e --- /dev/null +++ b/js/src/dapps/console/Input/input.css @@ -0,0 +1,46 @@ +/* Copyright 2015-2017 Parity Technologies (UK) Ltd. +/* This file is part of Parity. +/* +/* Parity is free software: you can redistribute it and/or modify +/* it under the terms of the GNU General Public License as published by +/* the Free Software Foundation, either version 3 of the License, or +/* (at your option) any later version. +/* +/* Parity is distributed in the hope that it will be useful, +/* but WITHOUT ANY WARRANTY; without even the implied warranty of +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/* GNU General Public License for more details. +/* +/* You should have received a copy of the GNU General Public License +/* along with Parity. If not, see . +*/ + +.type { + color: #59f; + font-weight: bold !important; + font-size: 11px; + padding: 0 0.5em 0 0.25em; +} + +.inputContainer { + flex: 1; +} + +.input { + border: 0; + margin: 0; + padding: 0; + color: black; + height: 100%; + font-size: 11px; + resize: none; + width: 100%; +} + +.container { + border-top: 1px solid lightgray; + display: flex; + flex: 1; + padding: 0.25em; + position: relative; +} diff --git a/js/src/dapps/console/Input/input.js b/js/src/dapps/console/Input/input.js new file mode 100644 index 000000000..3263aff38 --- /dev/null +++ b/js/src/dapps/console/Input/input.js @@ -0,0 +1,145 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import keycode from 'keycode'; +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; + +import Autocomplete from '../Autocomplete'; + +import AutocompleteStore from '../Autocomplete/autocomplete.store'; +import ConsoleStore from '../Console/console.store'; +import InputStore from './input.store'; +import SettingsStore from '../Settings/settings.store'; + +import styles from './input.css'; + +@observer +export default class Input extends Component { + autocompleteStore = AutocompleteStore.get(); + consoleStore = ConsoleStore.get(); + inputStore = InputStore.get(); + settingsStore = SettingsStore.get(); + + render () { + const { input } = this.inputStore; + + return ( +
+ + > +
+