Compare commits

..
Author SHA1 Message Date
Thomas Nordquist 0ead83463c Add app veyor build badge 2019-02-26 23:43:00 +01:00
Thomas Nordquist 2d8253ef31 WiP 2019-02-26 23:38:46 +01:00
Thomas Nordquist d4a709f0d5 build only appx 2019-02-26 11:56:34 +01:00
Thomas Nordquist d090197fe5 WiP 2019-02-26 11:43:27 +01:00
Thomas Nordquist 63cdc53075 Remove publisher 2019-02-25 18:12:53 +01:00
Thomas Nordquist dca35c1ed7 Merge branch 'master' into add-appveyor-support 2019-02-25 18:11:50 +01:00
Thomas Nordquist 63530a41ac Fix topic selector xpath 2019-02-25 17:58:24 +01:00
Thomas Nordquist d3bf5e87e1 Clean up dependencies 2019-02-25 17:30:41 +01:00
Thomas Nordquist 10c6c3cecd Add changelog 2019-02-25 17:08:41 +01:00
Thomas Nordquist 4d8fa72491 Refactor 2019-02-25 17:07:49 +01:00
Thomas Nordquist b9c0a66948 Refactor value rendering 2019-02-25 17:07:29 +01:00
Thomas Nordquist 6b859d31e8 Fix loading client id conenction setting
fixes #71
2019-02-25 16:40:43 +01:00
Thomas Nordquist 4fd328e716 Add zoom menu and shortcuts 2019-02-25 13:52:07 +01:00
Thomas Nordquist b606f14836 Clean imports & modules 2019-02-25 12:04:41 +01:00
Thomas Nordquist ad49c5c819 Update diff view 2019-02-25 11:57:44 +01:00
Thomas Nordquist 749a591465 Fix message diffing 2019-02-24 11:46:35 +01:00
Thomas Nordquist a1b1c92648 Remove mouse-over to select topics 2019-02-24 11:38:00 +01:00
Thomas Nordquist 85c6c4ebbc Fix message history comparison selection 2019-02-23 23:26:52 +01:00
Thomas Nordquist de7c9479c9 Highlight selected history entry 2019-02-23 23:16:47 +01:00
Thomas Nordquist 138f51eb39 Add diff view for received messages 2019-02-23 22:49:42 +01:00
Thomas Nordquist 107ca83882 Remove obsolete import 2019-02-23 22:35:20 +01:00
Thomas Nordquist 839ad531ba Fix RingBuffer array type 2019-02-23 22:35:03 +01:00
Thomas Nordquist 174eb0c767 Fix leaking connections 2019-02-23 22:34:32 +01:00
Thomas Nordquist 953422dcd4 Show travis-ci master build status 2019-02-22 12:08:36 +01:00
Thomas Nordquist 4c58d4af69 WiP 2019-02-21 17:42:18 +01:00
Thomas Nordquist 766a283b01 WiP 2019-02-21 12:17:14 +01:00
Thomas Nordquist 870043bde6 WiP 2019-02-21 11:59:22 +01:00
Thomas Nordquist d2fea71bd5 Rewrite rpepare-release in typescript 2019-02-21 11:58:41 +01:00
Thomas Nordquist 208c379898 WiP 2019-02-21 11:27:07 +01:00
Thomas Nordquist 55e3c827c4 WiP 2019-02-21 10:44:33 +01:00
Thomas Nordquist a4015141c3 Fix zoomFactor 2019-02-21 10:42:51 +01:00
Thomas Nordquist 8ac285846a WiP 2019-02-21 09:17:06 +01:00
Thomas Nordquist 2cb4084ed6 WiP 2019-02-21 09:15:34 +01:00
Thomas Nordquist 0bcae7912c WiP 2019-02-21 09:09:49 +01:00
Thomas Nordquist 1868a08237 WiP 2019-02-21 09:08:16 +01:00
Thomas Nordquist ba919c19fa WiP 2019-02-21 08:59:38 +01:00
Thomas Nordquist 701477f32e WiP 2019-02-21 08:56:00 +01:00
Thomas Nordquist fc59c46121 WiP 2019-02-21 08:52:24 +01:00
Thomas Nordquist eb67c69fad WiP 2019-02-21 08:51:19 +01:00
Thomas Nordquist ca3bd0b22b Fix connection window location 2019-02-20 17:09:47 +01:00
Thomas Nordquist 797d11d58d Update video captions 2019-02-20 15:57:32 +01:00
Thomas Nordquist 8281a852ab Change ui test resolution 2019-02-20 15:54:38 +01:00
Thomas Nordquist e5c3f207d9 Update gif image scale 2019-02-20 15:49:13 +01:00
Thomas Nordquist 2efc8f4dda Record videos in 720p 2019-02-20 15:40:35 +01:00
Thomas Nordquist 60a06d8c82 Update Readme & Add snap store link 2019-02-20 15:35:51 +01:00
Thomas Nordquist 857500f9c9 Update electron builder 2019-02-20 11:27:07 +01:00
Thomas Nordquist 396e6985ce Update readme 2019-02-19 00:33:21 +01:00
Thomas Nordquist 6d75f29f03 Update readme 2019-02-19 00:09:25 +01:00
Thomas Nordquist f8cdf0837e Prepare alpha release 2019-02-18 23:48:58 +01:00
Thomas Nordquist 5bb96233d1 Fix asset upload 2019-02-18 23:48:58 +01:00
Thomas Nordquist f3ff6cde7b Fix about window icon 2019-02-18 23:48:58 +01:00
Thomas Nordquist ec88f9822d Fix tests 2019-02-18 23:48:58 +01:00
Thomas Nordquist 235e81826b Add about window 2019-02-18 23:48:58 +01:00
Thomas Nordquist 615ec17b96 Remove empty topics from tree 2019-02-18 23:48:58 +01:00
Thomas Nordquist ddc801fe93 WiP 2019-02-18 23:48:58 +01:00
Thomas Nordquist 24c6b7e7b3 Clean up 2019-02-18 23:48:58 +01:00
Thomas Nordquist 55ea381b3b WiP 2019-02-18 23:48:58 +01:00
29 changed files with 526 additions and 255 deletions
+6
View File
@@ -0,0 +1,6 @@
# next release
- Magnify app content with "Ctrl +", "Ctrl -", "Ctrl 0"
- Add message diff view
- Make user interactions more predictable
# 0.2.0
+24 -20
View File
@@ -1,42 +1,46 @@
# [MQTT Explorer](https://thomasnordquist.github.io/MQTT-Explorer/)
[![Downloads](https://img.shields.io/github/release/thomasnordquist/mqtt-explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Downloads](https://img.shields.io/github/downloads/thomasnordquist/mqtt-explorer/total.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Build_Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
[![Build_Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg?branch=master)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
### Version 0.1.3
### Version
See the whole picture of your broker.
- What topics are active?
- Which retained topics are abandoned?
- Where is this new device / service I connected to the broker?
A MQTT Tool to get a quick overview of your MQTT topics and integrate new devices/services more efficiently.
- Visualize topics and topic activity
- Delete retained topics
- Search/filter topics
- Delete topic recursively
- Publish topics
- Plot numeric topics
- Keep a history of each topic
MQTT-Explorer strives to be THE swiss-army-knife tool.
The perfect tool to integrate new services, IoT devices in your network.
The hierarchical view allows for a quick understanding what is going on on your broker.
The app is available for Windows ([portable](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-0.1.3.exe), [installer](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-Setup-0.1.3.exe)), Linux ([AppImage](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-0.1.3-x86_64.AppImage)) and Mac ([dmg](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.1.3/MQTT-Explorer-0.1.3.dmg)).
## Download
| Platform | | Downloads |
|:----------:|:-------------:|:------:|
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | [portable](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.2.0/MQTT-Explorer-0.2.0.exe), [installer](https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v0.2.0/MQTT-Explorer-Setup-0.2.0.exe) |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | [AppImage](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.2.0&#x2F;MQTT-Explorer-0.2.0-x86_64.AppImage)<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![ubuntu](https://user-images.githubusercontent.com/7721625/51445401-a5306e00-1d04-11e9-9b9b-20e196b82142.png) | Ubuntu | [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/mqtt-explorer)<br>[Ubuntu Store](snap://mqtt-explorer) |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | [dmg](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.2.0&#x2F;MQTT-Explorer-0.2.0.dmg) |
[More Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
## Screenshots
![screen1](https://user-images.githubusercontent.com/7721625/51770198-6c6a0d80-20e5-11e9-94d5-a0174634253c.png)
## Download
| Platform | | Downloads |
|:----------:|:-------------:|:------:|
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | [portable](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-0.1.3.exe), [installer](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-Setup-0.1.3.exe) |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | [AppImage](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-0.1.3-x86_64.AppImage)<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | [dmg](https:&#x2F;&#x2F;github.com&#x2F;thomasnordquist&#x2F;MQTT-Explorer&#x2F;releases&#x2F;download&#x2F;v0.1.3&#x2F;MQTT-Explorer-0.1.3.dmg) |
[More Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
## Demo
![screencast](https://user-images.githubusercontent.com/7721625/51978075-5bc0eb00-2489-11e9-953a-0965b154557b.gif)
![screencast](https://user-images.githubusercontent.com/7721625/52979302-3f073b80-33d5-11e9-9953-b70ebb349439.gif)
## Performance
This App is optimized to handle thousands of topics and at least hundreds of thousands messages per minute.
So don't worry if you got >=10k topics and >=100k messages per minute.
So don't worry if you got >=30k topics and >=100.000 messages per minute.
Future releases will allow for a more granular selection of subscribed topics to handle
One can also subscribe to custom topics insted of the "root wildcard #", therefore limiting the amount of messages.
## Develop
PRs and issues are welcome
+24 -20
View File
@@ -1,43 +1,47 @@
# [MQTT Explorer](https://thomasnordquist.github.io/MQTT-Explorer/)
[![Downloads](https://img.shields.io/github/release/thomasnordquist/mqtt-explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Downloads](https://img.shields.io/github/downloads/thomasnordquist/mqtt-explorer/total.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
[![Build_Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
![](https://img.shields.io/github/commits-since/thomasnordquist/mqtt-explorer/latest.svg?style=flat)
[![Build_Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg?branch=master)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
[![Build status](https://ci.appveyor.com/api/projects/status/c35tkm29rm4m5364/branch/master?svg=true)](https://ci.appveyor.com/project/thomasnordquist/mqtt-explorer/branch/master)
### Version {{ version }}
See the whole picture of your broker.
- What topics are active?
- Which retained topics are abandoned?
- Where is this new device / service I connected to the broker?
A MQTT Tool to get a quick overview of your MQTT topics and integrate new devices/services more efficiently.
- Visualize topics and topic activity
- Delete retained topics
- Search/filter topics
- Delete topic recursively
- Publish topics
- Plot numeric topics
- Keep a history of each topic
MQTT-Explorer strives to be THE swiss-army-knife tool.
The perfect tool to integrate new services, IoT devices in your network.
The hierarchical view allows for a quick understanding what is going on on your broker.
The app is available for Windows ({{windowsTargets}}), Linux ({{linuxTargets}}) and Mac ({{macTargets}}).
## Download
| Platform | | Downloads |
|:----------:|:-------------:|:------:|
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | {{windowsTargets}} |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | {{linuxTargets}}<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![ubuntu](https://user-images.githubusercontent.com/7721625/51445401-a5306e00-1d04-11e9-9b9b-20e196b82142.png) | Ubuntu | [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/mqtt-explorer)<br>[Ubuntu Store](snap://mqtt-explorer) |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | {{macTargets}} |
[More Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
## Screenshots
![screen1](https://user-images.githubusercontent.com/7721625/51770198-6c6a0d80-20e5-11e9-94d5-a0174634253c.png)
## Download
| Platform | | Downloads |
|:----------:|:-------------:|:------:|
| ![windows](https://user-images.githubusercontent.com/7721625/51445407-b4172080-1d04-11e9-8c70-d8413d1d6d8b.png) | Windows | {{windowsTargets}} |
| ![linux](https://user-images.githubusercontent.com/7721625/51445392-947ff800-1d04-11e9-8c7f-a30efb755651.png) | Linux | {{linuxTargets}}<br>*Run AppImage:<br>Make it executable and double-click it.* |
| ![mac](https://user-images.githubusercontent.com/7721625/51445390-921d9e00-1d04-11e9-8339-351469ef20ae.png) | Mac | {{macTargets}} |
[More Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
## Demo
![screencast](https://user-images.githubusercontent.com/7721625/51978075-5bc0eb00-2489-11e9-953a-0965b154557b.gif)
![screencast](https://user-images.githubusercontent.com/7721625/52979302-3f073b80-33d5-11e9-9953-b70ebb349439.gif)
## Performance
This App is optimized to handle thousands of topics and at least hundreds of thousands messages per minute.
So don't worry if you got >=10k topics and >=100k messages per minute.
So don't worry if you got >=30k topics and >=100.000 messages per minute.
Future releases will allow for a more granular selection of subscribed topics to handle
One can also subscribe to custom topics insted of the "root wildcard #", therefore limiting the amount of messages.
## Develop
PRs and issues are welcome
+4 -1
View File
@@ -12,7 +12,9 @@
"@material-ui/core": "^4.0.0-alpha.0",
"@material-ui/icons": "^3.0.1",
"@material-ui/styles": "^3.0.0-alpha.8",
"@types/diff": "^4.0.1",
"@types/node": "^10.12.18",
"@types/prismjs": "^1.9.1",
"@types/react": "^16.7.18",
"@types/react-dom": "^16.0.11",
"@types/react-redux": "^6.0.12",
@@ -26,6 +28,7 @@
"compare-versions": "^3.4.0",
"copy-text-to-clipboard": "^1.0.4",
"css-loader": "^2.1.0",
"diff": "^4.0.1",
"electron-nucleus": "^1.11.0",
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist",
"html-webpack-plugin": "^4.0.0-beta.5",
@@ -34,10 +37,10 @@
"lodash.throttle": "^4.1.1",
"moving-average": "^1.0.0",
"number-abbreviate": "^2.0.0",
"prismjs": "^1.15.0",
"react": "16.8",
"react-ace": "^6.3.2",
"react-dom": "^16.7.0",
"react-json-view": "^1.19.1",
"react-redux": "^6.0.0",
"react-resize-detector": "^3.4.0",
"react-split-pane": "^0.1.85",
+1
View File
@@ -1,4 +1,5 @@
import * as React from 'react'
import CodeDiff from './components/CodeDiff'
import ConnectionSetup from './components/ConnectionSetup/ConnectionSetup'
import CssBaseline from '@material-ui/core/CssBaseline'
import ErrorBoundary from './ErrorBoundary'
+82
View File
@@ -0,0 +1,82 @@
import * as diff from 'diff'
import * as Prism from 'prismjs'
import * as React from 'react'
import { Theme, withStyles } from '@material-ui/core'
import 'prismjs/components/prism-json'
import 'prismjs/themes/prism-tomorrow.css'
interface Props {
previous: string
current: string
language?: 'json'
classes: any
}
class CodeDiff extends React.Component<Props, {}> {
constructor(props: Props) {
super(props)
}
public render() {
const changes = diff.diffLines(this.props.previous, this.props.current)
const styledLines = Prism.highlight(this.props.current, Prism.languages.json).split('\n')
let lineNumber = 0
const code = changes.map((change, key) => {
const hasStyledCode = change.removed !== true
const changedLines = change.count || 0
if (hasStyledCode && this.props.language === 'json') {
const html = styledLines.slice(lineNumber, lineNumber + changedLines).join('\n')
lineNumber += changedLines
return <div key={key}><span className={this.cssClassForChange(change)} dangerouslySetInnerHTML={{ __html: html }} /></div>
}
return <div key={key}><span className={this.cssClassForChange(change)}>{change.value}</span></div>
})
return (
<pre className={`language-json ${this.props.classes.codeBlock}`}>
{code}
</pre>
)
}
private cssClassForChange(change: diff.Change) {
if (change.added === true) {
return this.props.classes.addition
}
if (change.removed === true) {
return this.props.classes.deletion
}
return this.props.classes.noChange
}
}
const style = (theme: Theme) => {
const baseStyle = {
width: '100%',
}
return {
codeBlock: {
fontSize: '12px',
maxHeight: '200px',
},
noChange: {
...baseStyle,
},
deletion: {
...baseStyle,
backgroundColor: 'rgba(255, 10, 10, 0.3)',
},
addition: {
...baseStyle,
backgroundColor: 'rgba(10, 255, 10, 0.3)',
},
}
}
export default withStyles(style)(CodeDiff)
@@ -69,7 +69,7 @@ class ConnectionSettings extends React.Component<Props, State> {
className={classes.topicList}
component="nav"
>
<div className={this.props.classes.list}>
<div className={classes.list}>
{this.renderSubscriptions()}
</div>
</List>
@@ -79,6 +79,7 @@ class ConnectionSettings extends React.Component<Props, State> {
className={classes.fullWidth}
label="MQTT Client ID"
margin="normal"
value={this.props.connection.clientId}
onChange={this.handleChange('clientId')}
/>
</Grid>
@@ -71,16 +71,17 @@ class ConnectionSetup extends React.Component<Props, {}> {
}
}
const connectionHeight = '440px'
const styles = (theme: Theme) => ({
title: {
color: theme.palette.text.primary,
whiteSpace: 'nowrap' as 'nowrap',
},
root: {
margin: '13vw auto 0 auto',
margin: `calc((100vh - ${connectionHeight}) / 2) auto 0 auto`,
minWidth: '800px',
maxWidth: '850px',
height: '440px',
height: connectionHeight,
outline: 'none' as 'none',
display: 'flex' as 'flex',
},
+5 -4
View File
@@ -6,6 +6,7 @@ import { Theme, withStyles } from '@material-ui/core/styles'
interface HistoryItem {
title: JSX.Element | string
value: string | any
selected?: boolean
}
interface Props {
@@ -28,18 +29,18 @@ class MessageHistory extends React.Component<Props, State> {
}
public renderHistory() {
const style = {
backgroundColor: 'rgba(80, 80, 80, 0.6)',
const style = (element: HistoryItem) => ({
backgroundColor: element.selected ? 'rgba(120, 120, 120, 0.6)' : 'rgba(80, 80, 80, 0.6)',
margin: '8px',
padding: '8px 8px 0 8px',
cursor: this.props.onClick ? 'pointer' : 'inherit',
}
})
const messageStyle: React.CSSProperties = { textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }
const elements = this.props.items.map((element, index) => (
<div
key={index}
style={style}
style={style(element)}
onClick={(event: React.MouseEvent) => this.props.onClick && this.props.onClick(index, event.target)}
>
<div><i>{element.title}</i></div>
@@ -11,6 +11,7 @@ const throttle = require('lodash.throttle')
interface Props {
node?: q.TreeNode<TopicViewModel>
selected?: q.Message
onSelect: (message: q.Message) => void
}
@@ -52,6 +53,7 @@ class MessageHistory extends React.Component<Props, State> {
const historyElements = history.reverse().map(message => ({
title: <DateFormatter date={message.received} />,
value: message.value,
selected: message && message === this.props.selected,
}))
const numericMessages = history.filter(message => !isNaN(parseFloat(message.value)))
@@ -79,8 +81,10 @@ class MessageHistory extends React.Component<Props, State> {
}
private displayMessage = (index: number, eventTarget: EventTarget) => {
const message = this.props.node && this.props.node.messageHistory.toArray()[index]
this.props.onSelect(message)
const message = this.props.node && this.props.node.messageHistory.toArray().reverse()[index]
if (message) {
this.props.onSelect(message)
}
}
}
+2 -18
View File
@@ -165,14 +165,10 @@ class Sidebar extends React.Component<Props, State> {
{this.messageMetaInfo()}
<div ref={this.valueRef}>
<React.Suspense fallback={<div>Loading...</div>}>
<ReactResizeDetector handleWidth={true} onResize={this.valueRenderWidthChange} />
<ValueRenderer message={this.props.node && this.props.node.message} />
<ValueRenderer node={this.props.node} compareWith={this.state.compareMessage} />
</React.Suspense>
</div>
<div><MessageHistory onSelect={this.handleMessageHistorySelect} node={this.props.node} /></div>
<Popper open={Boolean(this.state.compareMessage)} anchorEl={this.valueRef.current} placement="left" transition={true}>
{this.showValueComparison}
</Popper>
<div><MessageHistory onSelect={this.handleMessageHistorySelect} selected={this.state.compareMessage} node={this.props.node} /></div>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel defaultExpanded={true}>
@@ -195,18 +191,6 @@ class Sidebar extends React.Component<Props, State> {
)
}
private valueRenderWidthChange = (width: number) => {
this.setState({ valueRenderWidth: width })
}
private showValueComparison = (a: any) => (
<Fade {...a.TransitionProps} timeout={350}>
<Paper style={{ maxWidth: this.state.valueRenderWidth }}>
<ValueRenderer message={this.state.compareMessage} />
</Paper>
</Fade>
)
private messageMetaInfo() {
if (!this.props.node || !this.props.node.message || !this.props.node.mqttMessage) {
return null
+71 -34
View File
@@ -1,65 +1,102 @@
import * as React from 'react'
import * as q from '../../../../backend/src/Model'
import * as React from 'react'
import CodeDiff from '../CodeDiff'
import { default as ReactResizeDetector } from 'react-resize-detector'
import { Theme, withTheme } from '@material-ui/core/styles'
import { default as ReactJson } from 'react-json-view'
interface Props {
message?: q.Message
node?: q.TreeNode<any>,
compareWith?: q.Message
theme: Theme
}
class ValueRenderer extends React.Component<Props, {}> {
interface State {
width: number
}
class ValueRenderer extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { width: 0 }
}
public render() {
return <div style={{ padding: '8px 0px 8px 8px' }}>{this.renderValue()}</div>
if (!this.props.node) {
return null
}
return (
<div style={{ padding: '8px 0px 8px 8px' }}>
<ReactResizeDetector handleWidth={true} onResize={this.updateWidth} />
{this.renderValue()}
</div>
)
}
public renderValue() {
const { message } = this.props
if (!message) {
return null
const { node } = this.props
if (!node || !node.message) {
return <span key="empty" />
}
const message = node.message
const previousMessages = node.messageHistory.toArray()
const previousMessage = previousMessages[previousMessages.length - 2]
const compareMessage = this.props.compareWith || previousMessage || message
let json
try {
json = JSON.parse(message.value)
} catch (error) {
return this.renderRawValue(message.value)
return this.renderRawValue(message.value, compareMessage.value)
}
if (typeof json === 'string') {
return this.renderRawValue(message.value)
return this.renderRawValue(message.value, compareMessage.value)
} else if (typeof json === 'number') {
return this.renderRawValue(message.value)
return this.renderRawValue(message.value, compareMessage.value)
} else if (typeof json === 'boolean') {
return this.renderRawValue(message.value)
return this.renderRawValue(message.value, compareMessage.value)
} else {
const theme = (this.props.theme.palette.type === 'dark') ? 'monokai' : 'bright:inverted'
return (
<ReactJson
style={{ width: '100%', overflowY: 'scroll', wordBreak: 'break-all' }}
src={json}
name={null}
theme={theme}
/>
)
const current = this.messageToPrettyJson(message) || message.value
const compare = this.messageToPrettyJson(compareMessage) || compareMessage.value
const language = current && compare ? 'json' : undefined
return this.renderDiff(current, compare, language)
}
}
private renderRawValue(value: string) {
const style: React.CSSProperties = {
padding: '8px 12px 8px 12px',
backgroundColor: 'rgba(100, 100, 100, 0.55)',
wordBreak: 'break-all',
width: '100%',
overflow: 'auto hidden',
display: 'block',
lineHeight: '1.2em',
color: this.props.theme.palette.text.primary,
private renderDiff(current: string = '', previous: string = '', language?: 'json') {
return (
<CodeDiff
previous={previous}
current={current}
language={language}
/>
)
}
private messageToPrettyJson(message?: q.Message): string | undefined {
if (!message || !message.value) {
return undefined
}
return <pre style={style}><code>{value}</code></pre>
try {
const json = JSON.parse(message.value)
return JSON.stringify(json, undefined, ' ')
} catch {
return undefined
}
}
private updateWidth = (width: number) => {
if (width !== this.state.width) {
this.setState({ width })
}
}
private renderRawValue(value: string, compare: string) {
return this.renderDiff(value, compare)
}
}
+32 -19
View File
@@ -29,6 +29,9 @@ const styles = (theme: Theme) => {
cursor: 'pointer',
marginTop: '-1px',
},
subnodes: {
marginLeft: theme.spacing(1.5),
},
selected: {
backgroundColor: 'rgba(170, 170, 170, 0.55)',
},
@@ -187,23 +190,28 @@ class TreeNode extends React.Component<Props, State> {
const highlightClass = this.state.selected ? this.props.classes.selected : (this.state.mouseOver ? this.props.classes.hover : '')
return (
<div
key={this.props.treeNode.hash()}
className={`${classes.node} ${this.props.className}`}
onClick={this.didClickNode}
onMouseOver={this.mouseOver}
onMouseOut={this.mouseOut}
ref={this.nodeRef}
>
<TreeNodeTitle
style={animation}
collapsed={this.collapsed()}
treeNode={this.props.treeNode}
name={this.props.name}
didSelectNode={this.didSelectTopic}
className={highlightClass}
/>
{this.renderNodes()}
<div>
<div
key={this.props.treeNode.hash()}
className={`${classes.node} ${this.props.className}`}
onMouseOver={this.mouseOver}
onMouseOut={this.mouseOut}
onClick={this.didClickTitle}
ref={this.nodeRef}
>
<TreeNodeTitle
style={animation}
toggleCollapsed={this.toggleCollapsed}
didSelectNode={this.didSelectTopic}
collapsed={this.collapsed()}
treeNode={this.props.treeNode}
name={this.props.name}
className={highlightClass}
/>
</div>
<div className={classes.subnodes}>
{this.renderNodes()}
</div>
</div>
)
}
@@ -212,6 +220,12 @@ class TreeNode extends React.Component<Props, State> {
this.props.didSelectTopic(this.props.treeNode)
}
private didClickTitle = (event: React.MouseEvent) => {
event.preventDefault()
this.props.didSelectTopic(this.props.treeNode)
this.toggle()
}
private mouseOver = (event: React.MouseEvent) => {
event.stopPropagation()
this.setHover(true)
@@ -226,10 +240,9 @@ class TreeNode extends React.Component<Props, State> {
this.setState({ mouseOver: hover })
}, 45)
private didClickNode = (event: React.MouseEvent) => {
private toggleCollapsed = (event: React.MouseEvent) => {
event.stopPropagation()
this.toggle()
this.didSelectTopic()
}
private renderNodes() {
+6 -14
View File
@@ -11,29 +11,19 @@ export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
collapsed?: boolean | undefined
classes: any
didSelectNode: any
toggleCollapsed: any
}
class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
private mouseOver = (event: React.MouseEvent) => {
event.preventDefault()
this.selectTopic()
}
private selectTopic = debounce(() => {
if (this.props.treeNode.message) {
this.props.didSelectNode(this.props.treeNode)
}
}, 5)
public render() {
const { classes, treeNode, style, className } = this.props
return (
<span
className={`${classes.title} ${className}`}
onMouseOver={treeNode.message ? this.mouseOver : undefined}
style={style}
>
<span className={classes.expander}>{this.renderExpander()}</span> {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
<span className={classes.expander} onClick={this.props.toggleCollapsed}>{this.renderExpander()}</span>
{this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
</span>
)
}
@@ -81,6 +71,8 @@ const styles = (theme: Theme) => ({
},
expander: {
color: theme.palette.type === 'light' ? '#222' : '#eee',
cursor: 'pointer' as 'pointer',
paddingRight: theme.spacing(0.25),
},
title: {
borderRadius: '4px',
@@ -88,7 +80,7 @@ const styles = (theme: Theme) => ({
display: 'inline-block' as 'inline-block',
whiteSpace: 'nowrap' as 'nowrap',
padding: '1px 4px 0px 4px',
height: '16px',
height: '14px',
margin: '1px 0px 2px 0px',
},
collapsedSubnodes: {
+54 -63
View File
@@ -111,6 +111,11 @@
prop-types "^15.6.0"
react-is "^16.8.0"
"@types/diff@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/diff/-/diff-4.0.1.tgz#471950ff97e2205ab02404bd7ac960fac074d886"
integrity sha512-BFob98KGKIqjcJjbmO/g+daJinDOtSUs27e0NTirODSUHYhUUAVXmq/nKIrmRFsOHrOWj5mncvByrzyw1X1PNQ==
"@types/jss@^9.5.6":
version "9.5.7"
resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.7.tgz#fa57a6d0b38a3abef8a425e3eb6a53495cb9d5a0"
@@ -124,6 +129,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
"@types/prismjs@^1.9.1":
version "1.9.1"
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.9.1.tgz#e63b1ed46f184046615dd87b978f902c9f328649"
integrity sha512-6TjOUrpDM2yjyPE/bdenDqxfoR4E8ve7coEdtiKg5IMBsjqTGsIN7HF5dpaNg/Ab8VumNeRXI8EjQsyBYWPeNA==
"@types/prop-types@*":
version "15.5.8"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce"
@@ -612,11 +622,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base16@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
@@ -1023,6 +1028,15 @@ clean-css@4.2.x:
dependencies:
source-map "~0.6.0"
clipboard@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d"
integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==
dependencies:
good-listener "^1.2.2"
select "^1.1.2"
tiny-emitter "^2.0.0"
cliui@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
@@ -1602,6 +1616,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
delegate@^3.1.2:
version "3.2.0"
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -1645,6 +1664,11 @@ diff-match-patch@^1.0.4:
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1"
integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg==
diff@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff"
integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -2164,14 +2188,7 @@ faye-websocket@~0.11.1:
dependencies:
websocket-driver ">=0.5.1"
fbemitter@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-2.1.1.tgz#523e14fdaf5248805bb02f62efc33be703f51865"
integrity sha1-Uj4U/a9SSIBbsC9i78M75wP1GGU=
dependencies:
fbjs "^0.8.4"
fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.4:
fbjs@^0.8.1:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
@@ -2258,14 +2275,6 @@ flush-write-stream@^1.0.0:
inherits "^2.0.1"
readable-stream "^2.0.4"
flux@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/flux/-/flux-3.1.3.tgz#d23bed515a79a22d933ab53ab4ada19d05b2f08a"
integrity sha1-0jvtUVp5oi2TOrU6tK2hnQWy8Io=
dependencies:
fbemitter "^2.0.0"
fbjs "^0.8.0"
follow-redirects@^1.0.0, follow-redirects@^1.3.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb"
@@ -2459,6 +2468,13 @@ globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
good-listener@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
dependencies:
delegate "^3.1.2"
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
@@ -3341,21 +3357,11 @@ lodash-es@^4.17.11:
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==
lodash.curry@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
integrity sha1-JI42By7ekGUB11lmIAqG2riyMXA=
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.flow@^3.3.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@@ -4270,6 +4276,13 @@ pretty-error@^2.1.1:
renderkid "^2.0.1"
utila "~0.4"
prismjs@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9"
integrity sha512-Lf2JrFYx8FanHrjoV5oL8YHCclLQgbJcVZR+gikGGMqz6ub5QVWDTM6YIwm3BuPxM/LOV+rKns3LssXNLIf+DA==
optionalDependencies:
clipboard "^2.0.0"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -4375,11 +4388,6 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
pure-color@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=
qs@6.5.2, qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@@ -4471,16 +4479,6 @@ react-ace@^6.3.2:
lodash.isequal "^4.5.0"
prop-types "^15.6.2"
react-base16-styling@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c"
integrity sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=
dependencies:
base16 "^1.0.0"
lodash.curry "^4.0.1"
lodash.flow "^3.3.0"
pure-color "^1.2.0"
react-dom@^16.6.3, react-dom@^16.7.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8"
@@ -4510,16 +4508,6 @@ react-is@^16.8.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.2.tgz#09891d324cad1cb0c1f2d91f70a71a4bee34df0f"
integrity sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q==
react-json-view@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.19.1.tgz#95d8e59e024f08a25e5dc8f076ae304eed97cf5c"
integrity sha512-u5e0XDLIs9Rj43vWkKvwL8G3JzvXSl6etuS5G42a8klMohZuYFQzSN6ri+/GiBptDqlrXPTdExJVU7x9rrlXhg==
dependencies:
flux "^3.1.3"
react-base16-styling "^0.6.0"
react-lifecycles-compat "^3.0.4"
react-textarea-autosize "^6.1.0"
react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@@ -4574,13 +4562,6 @@ react-style-proptype@^3.0.0:
dependencies:
prop-types "^15.5.4"
react-textarea-autosize@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz#df91387f8a8f22020b77e3833c09829d706a09a5"
integrity sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==
dependencies:
prop-types "^15.6.0"
react-transition-group@^2.2.1:
version "2.5.3"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.3.tgz#26de363cab19e5c88ae5dbae105c706cf953bb92"
@@ -4934,6 +4915,11 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
select@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
selfsigned@^1.9.1:
version "1.10.4"
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd"
@@ -5442,6 +5428,11 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
tiny-emitter@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
tiny-lr@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab"
+32
View File
@@ -0,0 +1,32 @@
image: Visual Studio 2017
platform: x64
cache:
- node_modules
- '%LOCALAPPDATA%\electron\Cache'
- '%LOCALAPPDATA%\electron-builder\cache'
install:
- ps: Install-Product node 10
build_script:
- yarn
- yarn build
test_script:
- yarn test
before_deploy:
- yarn prepare-release
- yarn package appx
deploy:
- provider: Environment
name: production
on:
branch: master # only this will work
APPVEYOR_REPO_TAG: true # condition will never be evaluated
artifacts:
- path: 'build\clean\build\MQTT-Explorer*.appx'
+1 -1
View File
@@ -6,7 +6,7 @@ export class RingBuffer<T extends Lengthwise> {
private capacity: number
private maxItems: number
private usage: number = 0
private items: (any)[] = []
private items: T[] = []
private start: number = 0
private end: number = 0
+12 -6
View File
@@ -11,8 +11,8 @@ import {
removeConnection,
updateAvailable,
} from '../../events'
import { DataSource, MqttSource } from './DataSource'
import ConfigStorage from './ConfigStorage'
import { UpdateInfo } from 'builder-util-runtime'
export class ConnectionManager {
@@ -21,13 +21,18 @@ export class ConnectionManager {
public manageConnections() {
backendEvents.subscribe(addMqttConnectionEvent, this.handleConnectionRequest)
backendEvents.subscribe(removeConnection, (connectionId: string) => {
backendEvents.unsubscribeAll(makePublishEvent(connectionId))
this.removeConnection(connectionId)
})
}
private handleConnectionRequest = (event: AddMqttConnection) => {
const connectionId = event.id
// Prevent double connections when reloading
if (this.connections[connectionId]) {
this.removeConnection(connectionId)
}
const options = event.options
const connection = new MqttSource()
this.connections[connectionId] = connection
@@ -56,18 +61,19 @@ export class ConnectionManager {
})
}
public removeConnection(hash: string) {
const connection = this.connections[hash]
public removeConnection(conenctionId: string) {
const connection = this.connections[conenctionId]
if (connection) {
backendEvents.unsubscribeAll(makePublishEvent(conenctionId))
connection.disconnect()
delete this.connections[hash]
delete this.connections[conenctionId]
connection.stateMachine.onUpdate.removeAllListeners()
}
}
public closeAllConnections() {
Object.keys(this.connections)
.forEach(hash => this.removeConnection(hash))
.forEach(conenctionId => this.removeConnection(conenctionId))
}
}
+12 -4
View File
@@ -1,15 +1,15 @@
{
"name": "MQTT-Explorer",
"version": "0.2.0-alpha",
"version": "0.2.0",
"description": "Explore your message queues",
"main": "dist/src/electron.js",
"scripts": {
"start": "electron .",
"test": "yarn run test-backend",
"install": "cd app; yarn; cd ..",
"install": "cd app && yarn && cd ..",
"build": "tsc && cd app && yarn run build && cd ..",
"test-backend": "cd backend && yarn run test && cd ..",
"prepare-release": "./scripts/prepare-release.sh",
"prepare-release": "ts-node scripts/prepare-release.ts",
"package": "ts-node package.ts",
"ui-test": "./scripts/uiTests.sh",
"upload-video-artifacts": "./scripts/uploadVideoAsset.ts ui-test.mp4 ui-test.gif",
@@ -32,6 +32,12 @@
"category": "Development",
"maintainer": "Thomas Nordquist"
},
"appx": {
"applicationId": "mqttexplorer",
"identityName": "51031thomas.nordquist.MQTT-Explorer",
"publisherDisplayName": "Thomas Nordquist",
"publisher": "CN=0A6DE643-4AA2-4FF2-9729-6935C9ED8C13"
},
"directories": {
"app": "./",
"buildResources": "res",
@@ -44,6 +50,7 @@
"license": "ISC",
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/fs-extra": "^5.0.5",
"@types/lowdb": "^1.0.6",
"@types/mime": "^2.0.0",
"@types/mocha": "^5.2.5",
@@ -54,7 +61,8 @@
"axios": "^0.18.0",
"chai": "^4.2.0",
"electron": "^4.0.2",
"electron-builder": "^20.38.4",
"electron-builder": "^20.38.5",
"fs-extra": "^7.0.1",
"mime": "^2.4.0",
"mocha": "^5.2.0",
"mustache": "^3.0.1",
+13 -1
View File
@@ -30,6 +30,16 @@ const win: builder.CliOptions = {
publish: 'always',
}
const winAppx: builder.CliOptions = {
x64: true,
ia32: true,
armv7l: false,
arm64: false,
win: ['appx'],
projectDir: './build/clean',
publish: 'never',
}
const mac: builder.CliOptions = {
x64: true,
ia32: true,
@@ -41,11 +51,13 @@ const mac: builder.CliOptions = {
}
async function executeBuild() {
console.log(process.argv[2])
switch (process.argv[2]) {
case 'win':
await builder.build(win)
break
case 'appx':
await builder.build(winAppx)
break
case 'linux':
await builder.build(linux)
break
-26
View File
@@ -1,26 +0,0 @@
#!/bin/bash
set -e
ORIGINAL_DIR=`pwd`
DIR=build/clean
rm -rf "$DIR" || echo "Directory did not exist"
mkdir -p "$DIR"
git clone .git "$DIR"
cd $DIR
# App
cd app
yarn
cd ..
# Build
yarn
yarn build
rm -rf node_modules
yarn install --production
rm -rf app/node_modules
cd "$ORIGINAL_DIR"
+66
View File
@@ -0,0 +1,66 @@
import * as fs from 'fs-extra'
import * as path from 'path'
import { spawn, ChildProcess } from 'child_process'
import { chdir } from 'process'
async function exec(cmd: string, args: string[] = []) {
const child = spawn(cmd, args, { shell: true })
redirectOutputFor(child)
await waitFor(child)
}
function redirectOutputFor(child: ChildProcess) {
const printStdout = (data: Buffer) => {
process.stdout.write(data.toString())
}
const printStderr = (data: Buffer) => {
process.stderr.write(data.toString())
}
child.stdout.on('data', printStdout)
child.stderr.on('data', printStderr)
child.once('close', () => {
child.stdout.off('data', printStdout)
child.stderr.off('data', printStderr)
})
}
async function waitFor(child: ChildProcess) {
return new Promise((resolve) => {
child.once('close', () => resolve())
})
}
const targetDir = path.join('build', 'clean')
async function prepareRelease() {
const originalDir = __dirname
await fs.remove(targetDir)
await fs.mkdirp(targetDir)
// Create fresh clone of the local git repo
await exec('git', ['clone', '.git', targetDir])
// Enter git repo
chdir(targetDir)
// Install app dependencies
chdir('app')
await exec('yarn')
chdir('..')
// Install electron dependencies
await exec('yarn')
// Build App and Electron backend
await exec('yarn', ['build'])
// Clean up
await fs.remove('node_modules')
await exec('yarn', ['install', '--production'])
await fs.remove(path.join('app', 'node_modules'))
chdir(originalDir)
}
prepareRelease()
+7 -5
View File
@@ -1,5 +1,7 @@
#!/bin/bash
ffmpeg -s:v 1024x700 -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv app2.mp4
DIMENSIONS="1280x720"
ffmpeg -s:v $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv app2.mp4
# The video starts with a few blank frames, we want to know when the stop
ffprobe -f lavfi -i "movie=app2.mp4,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet > ffmpeg_info
@@ -9,16 +11,16 @@ END_OF_BLACK=`cat ffmpeg_info | grep end | head -n1 | cut -d'=' -f2`
END_OF_BLACK=`awk "BEGIN {print $END_OF_BLACK+0.8; exit}"`
# Trim black frames at start
ffmpeg -s:v 1024x700 -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -ss $END_OF_BLACK app.mp4
ffmpeg -s:v $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -ss $END_OF_BLACK app.mp4
# Generate gif palette
ffmpeg -y -s:v 1024x700 -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -vf fps=10,scale=1024:-1:flags=lanczos,palettegen palette1024.png
ffmpeg -y -s:v $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -vf fps=10,scale=1280:-1:flags=lanczos,palettegen palette1024.png
# Create gif
ffmpeg -s:v 1024x700 -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -i palette1024.png -ss $END_OF_BLACK -filter_complex "fps=10,scale=1024:-1:flags=lanczos[x];[x][1:v]paletteuse" app1024.gif
ffmpeg -s:v $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -i palette1024.png -ss $END_OF_BLACK -filter_complex "fps=10,scale=1280:-1:flags=lanczos[x];[x][1:v]paletteuse" app720.gif
# Clean up
rm ffmpeg_info palette*.png qrawvideorgb24.yuv
mv app.mp4 ui-test.mp4
mv app1024.gif ui-test.gif
mv app720.gif ui-test.gif
+1 -1
View File
@@ -9,7 +9,7 @@ function finish {
trap finish EXIT
DIMENSIONS="1024x700"
DIMENSIONS="1280x720"
SCR=99
# Start new window manager
Xvfb :$SCR -screen 0 "$DIMENSIONS"x24 -ac &
+41 -1
View File
@@ -8,7 +8,6 @@ const applicationMenu = {
{
label: 'About Application',
click: () => {
console.log(path.join(__dirname, 'icon.png'))
openAboutWindow({
icon_path: path.join(__dirname, '..', '..', 'icon.png'),
license: 'AGPL-3.0',
@@ -75,9 +74,50 @@ const editMenu = {
],
}
const viewMenu = {
label: 'View',
submenu: [
{
label: 'Default size',
accelerator: 'CmdOrCtrl+0',
click: () => {
const window = BrowserWindow.getFocusedWindow()
if (window) {
window.webContents.setZoomFactor(1)
}
},
},
{
label: 'Increase size',
accelerator: 'CmdOrCtrl+Plus',
click: () => {
const window = BrowserWindow.getFocusedWindow()
if (window) {
window.webContents.getZoomFactor((zoom) => {
window.webContents.setZoomFactor(Math.min(zoom + 0.1, 2.0))
})
}
},
},
{
label: 'Reduce size',
accelerator: 'CmdOrCtrl+-',
click: () => {
const window = BrowserWindow.getFocusedWindow()
if (window) {
window.webContents.getZoomFactor((zoom) => {
window.webContents.setZoomFactor(Math.max(zoom - 0.1, 0.5))
})
}
},
},
],
}
const template = [
applicationMenu,
editMenu,
viewMenu,
]
export const menuTemplate = Menu.buildFromTemplate(template)
+4 -4
View File
@@ -15,7 +15,7 @@ if (!isDev) {
}
const isDebugEnabled = Boolean(process.argv.find(arg => arg === 'debug'))
const isFullscreen = Boolean(process.argv.find(arg => arg === '--fullscreen'))
const runningUiTestOnCi = Boolean(process.argv.find(arg => arg === '--runningUiTestOnCi'))
require('electron-debug')({ enabled: isDebugEnabled })
@@ -36,8 +36,8 @@ function createWindow() {
const iconPath = path.join(__dirname, 'icon.png')
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1024,
height: 700,
width: runningUiTestOnCi ? 1280 : 1024,
height: runningUiTestOnCi ? 720 : 700,
show: false,
webPreferences: {
nodeIntegration: true,
@@ -48,7 +48,7 @@ function createWindow() {
mainWindow.once('ready-to-show', () => {
if (mainWindow) {
isFullscreen && mainWindow.setFullScreen(true)
runningUiTestOnCi && mainWindow.setFullScreen(true)
mainWindow.show()
}
})
+1 -1
View File
@@ -24,6 +24,6 @@ async function topicMatches(topics: string[], browser: Browser<void>) {
}
function topicSelector(topics: string[]) {
const suffix = topics.map(topic => `*[contains(text(), "${topic}")]`).join('/../..//')
const suffix = topics.map(topic => `*[contains(text(), "${topic}")]`).join('/../../..//')
return `//${suffix}`
}
+5 -5
View File
@@ -21,7 +21,7 @@ import { showMenu } from './scenarios/showMenu'
import { createFakeMousePointer, sleep, showText, hideText, clickOnHistory } from './util'
const binary = os.platform() === 'darwin' ? 'Electron.app/Contents/MacOS/Electron' : 'electron'
const fullscreen = os.platform() === 'darwin' ? [] : ['--fullscreen']
const runningUiTestOnCi = os.platform() === 'darwin' ? [] : ['--runningUiTestOnCi']
const options = {
host: 'localhost', // Use localhost as chrome driver server
@@ -30,7 +30,7 @@ const options = {
browserName: 'electron',
chromeOptions: {
binary: `${__dirname}/../../../node_modules/electron/dist/${binary}`,
args: [`--app=${__dirname}/../../..`, '--force-device-scale-factor=1', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions'].concat(fullscreen),
args: [`--app=${__dirname}/../../..`, '--force-device-scale-factor=1', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions'].concat(runningUiTestOnCi),
},
windowTypes: ['app', 'webview'],
},
@@ -45,12 +45,12 @@ async function doStuff() {
await sleep(1000)
await sleep(1000)
await showText('Overview of topics', 2000, browser, 'top')
await showText('Topics overview', 2000, browser, 'top')
await sleep(2000)
await showText('Indicates which topics change', 2000, browser, 'bottom')
await showText('Indicate topic updates', 2000, browser, 'bottom')
await sleep(3000)
await showText('Plot topics', 2000, browser)
await showText('Plot topic history', 2000, browser)
await showNumericPlot(browser)
await sleep(2000)
+9 -2
View File
@@ -119,6 +119,13 @@
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
"@types/fs-extra@^5.0.5":
version "5.0.5"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b"
integrity sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==
dependencies:
"@types/node" "*"
"@types/lodash@*":
version "4.14.121"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.121.tgz#9327e20d49b95fc2bf983fc2f045b2c6effc80b9"
@@ -1061,7 +1068,7 @@ ejs@~2.5.6:
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
integrity sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==
electron-builder@^20.38.4:
electron-builder@^20.38.5:
version "20.38.5"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.38.5.tgz#31b3913a68b4911afd4cfc7bcd2522c5808040cd"
integrity sha512-p88IDHhH2J4hA6KwRBJY+OfVZuFtFIShY3Uh/TwYAfbX0v1RhKZytuGdO8sty2zcWxDYX74xDBv+X9oN6qEIRQ==
@@ -1460,7 +1467,7 @@ fs-extra@^4.0.1:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^7.0.0:
fs-extra@^7.0.0, fs-extra@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==