Compare commits

..
Author SHA1 Message Date
Thomas Nordquist 8318215776 Update readme 2019-02-18 23:48:12 +01:00
Thomas Nordquist cf3ad3b91f Prepare alpha release 2019-02-18 23:10:42 +01:00
Thomas Nordquist 6e17fc06b3 Fix asset upload 2019-02-18 23:07:42 +01:00
Thomas Nordquist 1655ee6e13 Fix about window icon 2019-02-18 22:52:06 +01:00
Thomas Nordquist 4a6b31bc4c Fix tests 2019-02-18 22:48:04 +01:00
Thomas Nordquist 8b18689456 Add about window 2019-02-18 22:35:21 +01:00
Thomas Nordquist b81e9b2b79 Remove empty topics from tree 2019-02-18 22:21:09 +01:00
Thomas Nordquist 8acf6fe27c Merge branch 'master' into allow-recursive-topic-deletion 2019-02-18 21:31:24 +01:00
Thomas Nordquist ff599c470a WiP 2019-02-18 19:58:17 +01:00
Thomas Nordquist ca90f4d32d Clean up 2019-02-18 16:36:53 +01:00
Thomas Nordquist 9c6846a281 Fix disconnect unsubscribe 2019-02-18 16:36:41 +01:00
Thomas Nordquist 50be3e8b0b WiP 2019-02-18 16:02:21 +01:00
29 changed files with 252 additions and 524 deletions
-6
View File
@@ -1,6 +0,0 @@
# next release
- Magnify app content with "Ctrl +", "Ctrl -", "Ctrl 0"
- Add message diff view
- Make user interactions more predictable
# 0.2.0
+19 -23
View File
@@ -1,46 +1,42 @@
# [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?branch=master)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
[![Build_Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
### Version
### Version 0.1.3
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
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?
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.
## 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)
The app is available for 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 ([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)) and 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)).
## 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/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 >=30k topics and >=100.000 messages per minute.
So don't worry if you got >=10k topics and >=100k messages per minute.
One can also subscribe to custom topics insted of the "root wildcard #", therefore limiting the amount of messages.
Future releases will allow for a more granular selection of subscribed topics to handle
## Develop
PRs and issues are welcome
+18 -23
View File
@@ -1,47 +1,42 @@
# [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?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)
[![Build_Status](https://travis-ci.org/thomasnordquist/MQTT-Explorer.svg)](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
### Version {{ version }}
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
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?
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.
## 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)
The app is available for Windows ({{windowsTargets}}), Linux ({{linuxTargets}}) and Mac ({{macTargets}}).
## 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/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 >=30k topics and >=100.000 messages per minute.
So don't worry if you got >=10k topics and >=100k messages per minute.
One can also subscribe to custom topics insted of the "root wildcard #", therefore limiting the amount of messages.
Future releases will allow for a more granular selection of subscribed topics to handle
## Develop
PRs and issues are welcome
+1 -4
View File
@@ -12,9 +12,7 @@
"@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",
@@ -28,7 +26,6 @@
"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",
@@ -37,10 +34,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,5 +1,4 @@
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
@@ -1,82 +0,0 @@
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={classes.list}>
<div className={this.props.classes.list}>
{this.renderSubscriptions()}
</div>
</List>
@@ -79,7 +79,6 @@ 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,17 +71,16 @@ 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: `calc((100vh - ${connectionHeight}) / 2) auto 0 auto`,
margin: '13vw auto 0 auto',
minWidth: '800px',
maxWidth: '850px',
height: connectionHeight,
height: '440px',
outline: 'none' as 'none',
display: 'flex' as 'flex',
},
+4 -5
View File
@@ -6,7 +6,6 @@ import { Theme, withStyles } from '@material-ui/core/styles'
interface HistoryItem {
title: JSX.Element | string
value: string | any
selected?: boolean
}
interface Props {
@@ -29,18 +28,18 @@ class MessageHistory extends React.Component<Props, State> {
}
public renderHistory() {
const style = (element: HistoryItem) => ({
backgroundColor: element.selected ? 'rgba(120, 120, 120, 0.6)' : 'rgba(80, 80, 80, 0.6)',
const style = {
backgroundColor: '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(element)}
style={style}
onClick={(event: React.MouseEvent) => this.props.onClick && this.props.onClick(index, event.target)}
>
<div><i>{element.title}</i></div>
@@ -11,7 +11,6 @@ const throttle = require('lodash.throttle')
interface Props {
node?: q.TreeNode<TopicViewModel>
selected?: q.Message
onSelect: (message: q.Message) => void
}
@@ -53,7 +52,6 @@ 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)))
@@ -81,10 +79,8 @@ class MessageHistory extends React.Component<Props, State> {
}
private displayMessage = (index: number, eventTarget: EventTarget) => {
const message = this.props.node && this.props.node.messageHistory.toArray().reverse()[index]
if (message) {
this.props.onSelect(message)
}
const message = this.props.node && this.props.node.messageHistory.toArray()[index]
this.props.onSelect(message)
}
}
+18 -2
View File
@@ -165,10 +165,14 @@ class Sidebar extends React.Component<Props, State> {
{this.messageMetaInfo()}
<div ref={this.valueRef}>
<React.Suspense fallback={<div>Loading...</div>}>
<ValueRenderer node={this.props.node} compareWith={this.state.compareMessage} />
<ReactResizeDetector handleWidth={true} onResize={this.valueRenderWidthChange} />
<ValueRenderer message={this.props.node && this.props.node.message} />
</React.Suspense>
</div>
<div><MessageHistory onSelect={this.handleMessageHistorySelect} selected={this.state.compareMessage} node={this.props.node} /></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>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel defaultExpanded={true}>
@@ -191,6 +195,18 @@ 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
+34 -71
View File
@@ -1,102 +1,65 @@
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 * as q from '../../../../backend/src/Model'
import { Theme, withTheme } from '@material-ui/core/styles'
import { default as ReactJson } from 'react-json-view'
interface Props {
node?: q.TreeNode<any>,
compareWith?: q.Message
message?: q.Message
theme: Theme
}
interface State {
width: number
}
class ValueRenderer extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { width: 0 }
}
class ValueRenderer extends React.Component<Props, {}> {
public render() {
if (!this.props.node) {
return null
}
return (
<div style={{ padding: '8px 0px 8px 8px' }}>
<ReactResizeDetector handleWidth={true} onResize={this.updateWidth} />
{this.renderValue()}
</div>
)
return <div style={{ padding: '8px 0px 8px 8px' }}>{this.renderValue()}</div>
}
public renderValue() {
const { node } = this.props
if (!node || !node.message) {
return <span key="empty" />
const { message } = this.props
if (!message) {
return null
}
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, compareMessage.value)
return this.renderRawValue(message.value)
}
if (typeof json === 'string') {
return this.renderRawValue(message.value, compareMessage.value)
return this.renderRawValue(message.value)
} else if (typeof json === 'number') {
return this.renderRawValue(message.value, compareMessage.value)
return this.renderRawValue(message.value)
} else if (typeof json === 'boolean') {
return this.renderRawValue(message.value, compareMessage.value)
return this.renderRawValue(message.value)
} else {
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)
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}
/>
)
}
}
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
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,
}
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)
return <pre style={style}><code>{value}</code></pre>
}
}
+19 -32
View File
@@ -29,9 +29,6 @@ const styles = (theme: Theme) => {
cursor: 'pointer',
marginTop: '-1px',
},
subnodes: {
marginLeft: theme.spacing(1.5),
},
selected: {
backgroundColor: 'rgba(170, 170, 170, 0.55)',
},
@@ -190,28 +187,23 @@ 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>
<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
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>
)
}
@@ -220,12 +212,6 @@ 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)
@@ -240,9 +226,10 @@ class TreeNode extends React.Component<Props, State> {
this.setState({ mouseOver: hover })
}, 45)
private toggleCollapsed = (event: React.MouseEvent) => {
private didClickNode = (event: React.MouseEvent) => {
event.stopPropagation()
this.toggle()
this.didSelectTopic()
}
private renderNodes() {
+14 -6
View File
@@ -11,19 +11,29 @@ 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} onClick={this.props.toggleCollapsed}>{this.renderExpander()}</span>
{this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
<span className={classes.expander}>{this.renderExpander()}</span> {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
</span>
)
}
@@ -71,8 +81,6 @@ const styles = (theme: Theme) => ({
},
expander: {
color: theme.palette.type === 'light' ? '#222' : '#eee',
cursor: 'pointer' as 'pointer',
paddingRight: theme.spacing(0.25),
},
title: {
borderRadius: '4px',
@@ -80,7 +88,7 @@ const styles = (theme: Theme) => ({
display: 'inline-block' as 'inline-block',
whiteSpace: 'nowrap' as 'nowrap',
padding: '1px 4px 0px 4px',
height: '14px',
height: '16px',
margin: '1px 0px 2px 0px',
},
collapsedSubnodes: {
+63 -54
View File
@@ -111,11 +111,6 @@
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"
@@ -129,11 +124,6 @@
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"
@@ -622,6 +612,11 @@ 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"
@@ -1028,15 +1023,6 @@ 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"
@@ -1616,11 +1602,6 @@ 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"
@@ -1664,11 +1645,6 @@ 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"
@@ -2188,7 +2164,14 @@ faye-websocket@~0.11.1:
dependencies:
websocket-driver ">=0.5.1"
fbjs@^0.8.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:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
@@ -2275,6 +2258,14 @@ 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"
@@ -2468,13 +2459,6 @@ 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"
@@ -3357,11 +3341,21 @@ 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"
@@ -4276,13 +4270,6 @@ 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"
@@ -4388,6 +4375,11 @@ 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"
@@ -4479,6 +4471,16 @@ 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"
@@ -4508,6 +4510,16 @@ 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"
@@ -4562,6 +4574,13 @@ 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"
@@ -4915,11 +4934,6 @@ 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"
@@ -5428,11 +5442,6 @@ 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
@@ -1,32 +0,0 @@
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: T[] = []
private items: (any)[] = []
private start: number = 0
private end: number = 0
+6 -12
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,18 +21,13 @@ 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
@@ -61,19 +56,18 @@ export class ConnectionManager {
})
}
public removeConnection(conenctionId: string) {
const connection = this.connections[conenctionId]
public removeConnection(hash: string) {
const connection = this.connections[hash]
if (connection) {
backendEvents.unsubscribeAll(makePublishEvent(conenctionId))
connection.disconnect()
delete this.connections[conenctionId]
delete this.connections[hash]
connection.stateMachine.onUpdate.removeAllListeners()
}
}
public closeAllConnections() {
Object.keys(this.connections)
.forEach(conenctionId => this.removeConnection(conenctionId))
.forEach(hash => this.removeConnection(hash))
}
}
+4 -12
View File
@@ -1,15 +1,15 @@
{
"name": "MQTT-Explorer",
"version": "0.2.0",
"version": "0.2.0-alpha",
"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": "ts-node scripts/prepare-release.ts",
"prepare-release": "./scripts/prepare-release.sh",
"package": "ts-node package.ts",
"ui-test": "./scripts/uiTests.sh",
"upload-video-artifacts": "./scripts/uploadVideoAsset.ts ui-test.mp4 ui-test.gif",
@@ -32,12 +32,6 @@
"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",
@@ -50,7 +44,6 @@
"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",
@@ -61,8 +54,7 @@
"axios": "^0.18.0",
"chai": "^4.2.0",
"electron": "^4.0.2",
"electron-builder": "^20.38.5",
"fs-extra": "^7.0.1",
"electron-builder": "^20.38.4",
"mime": "^2.4.0",
"mocha": "^5.2.0",
"mustache": "^3.0.1",
+1 -13
View File
@@ -30,16 +30,6 @@ 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,
@@ -51,13 +41,11 @@ 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
@@ -0,0 +1,26 @@
#!/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
@@ -1,66 +0,0 @@
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()
+5 -7
View File
@@ -1,7 +1,5 @@
#!/bin/bash
DIMENSIONS="1280x720"
ffmpeg -s:v $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv app2.mp4
ffmpeg -s:v 1024x700 -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
@@ -11,16 +9,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 $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -ss $END_OF_BLACK app.mp4
ffmpeg -s:v 1024x700 -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24.yuv -ss $END_OF_BLACK app.mp4
# Generate gif palette
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
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
# Create 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
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
# Clean up
rm ffmpeg_info palette*.png qrawvideorgb24.yuv
mv app.mp4 ui-test.mp4
mv app720.gif ui-test.gif
mv app1024.gif ui-test.gif
+1 -1
View File
@@ -9,7 +9,7 @@ function finish {
trap finish EXIT
DIMENSIONS="1280x720"
DIMENSIONS="1024x700"
SCR=99
# Start new window manager
Xvfb :$SCR -screen 0 "$DIMENSIONS"x24 -ac &
+1 -41
View File
@@ -8,6 +8,7 @@ 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',
@@ -74,50 +75,9 @@ 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 runningUiTestOnCi = Boolean(process.argv.find(arg => arg === '--runningUiTestOnCi'))
const isFullscreen = Boolean(process.argv.find(arg => arg === '--fullscreen'))
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: runningUiTestOnCi ? 1280 : 1024,
height: runningUiTestOnCi ? 720 : 700,
width: 1024,
height: 700,
show: false,
webPreferences: {
nodeIntegration: true,
@@ -48,7 +48,7 @@ function createWindow() {
mainWindow.once('ready-to-show', () => {
if (mainWindow) {
runningUiTestOnCi && mainWindow.setFullScreen(true)
isFullscreen && 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 runningUiTestOnCi = os.platform() === 'darwin' ? [] : ['--runningUiTestOnCi']
const fullscreen = os.platform() === 'darwin' ? [] : ['--fullscreen']
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(runningUiTestOnCi),
args: [`--app=${__dirname}/../../..`, '--force-device-scale-factor=1', '--no-sandbox', '--disable-dev-shm-usage', '--disable-extensions'].concat(fullscreen),
},
windowTypes: ['app', 'webview'],
},
@@ -45,12 +45,12 @@ async function doStuff() {
await sleep(1000)
await sleep(1000)
await showText('Topics overview', 2000, browser, 'top')
await showText('Overview of topics', 2000, browser, 'top')
await sleep(2000)
await showText('Indicate topic updates', 2000, browser, 'bottom')
await showText('Indicates which topics change', 2000, browser, 'bottom')
await sleep(3000)
await showText('Plot topic history', 2000, browser)
await showText('Plot topics', 2000, browser)
await showNumericPlot(browser)
await sleep(2000)
+2 -9
View File
@@ -119,13 +119,6 @@
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"
@@ -1068,7 +1061,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.5:
electron-builder@^20.38.4:
version "20.38.5"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.38.5.tgz#31b3913a68b4911afd4cfc7bcd2522c5808040cd"
integrity sha512-p88IDHhH2J4hA6KwRBJY+OfVZuFtFIShY3Uh/TwYAfbX0v1RhKZytuGdO8sty2zcWxDYX74xDBv+X9oN6qEIRQ==
@@ -1467,7 +1460,7 @@ fs-extra@^4.0.1:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^7.0.0, fs-extra@^7.0.1:
fs-extra@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==