diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 000000000..985a708d7 --- /dev/null +++ b/README-CN.md @@ -0,0 +1,67 @@ +MQTTX Logo + +## MQTTX + +[![GitHub Release](https://img.shields.io/github/release/emqx/mqttx?color=brightgreen)](https://github.com/emqx/mqttx/releases) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667) [![Total Downloads](https://img.shields.io/github/downloads/emqx/mqttx/total.svg)](https://github.com/emqx/mqttx/releases) + +[English](./README.md) | 简体中文 + +--- + +MQTTX 是 EMQ 开源的一款跨平台 MQTT 桌面客户端,它支持 macOS, Linux, Windows。 + +MQTTX 采用了聊天界面形式,简化了页面操作逻辑,方便用户快速测试 MQTT/MQTTS 连接,及 MQTT 消息的发布与订阅。 + +## 预览 + +![mqttx-preview](./assets/mqttx-preview.png) + +## 安装 + +请从 [GitHub Releases](https://github.com/emqx/MQTTX/releases) 下载符合您的版本并安装使用。 + +## 使用 + +1. 首先创建一个 Broker + +2. 同一个 Broker 下可以创建多个客户端 + +3. 回到连接页面,选择刚才创建的 Broker 下的客户端进行连接测试 + +## 开发 + +``` shell +# 克隆项目 +git clone git@github.com:emqx/MQTTX.git + +# 安装依赖 +cd MQTTX +yarn install + +# 编译和热重载以进行开发 +yarn run electron:serve + +# 编译和压缩以构建生产版本 +yarn run electron:build +``` + +## 贡献 + +1. Fork 这个项目 + +2. 添加 upstream remote `git remote add upstream git@github.com:emqx/MQTTX.git` + +3. 本地修改代码,添加一个 commit 在您本地新的分支里 + +4. 向 upstream 仓库的 develop 分支提交一个 Pull Request,我们将会审核它 + +## 技术栈 + +- [Vue](https://vuejs.org/) + [Element](https://element.eleme.io) +- [Electron](https://electronjs.org/) +- [TypeScript](https://www.typescriptlang.org/) +- [Lowdb](https://github.com/typicode/lowdb) + +## License + +Apache License 2.0, see [LICENSE](https://github.com/emqx/MQTTX/blob/master/LICENSE). diff --git a/README.md b/README.md index 4893c53cb..f4178bc5b 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,66 @@ -# MQTTX +MQTTX Logo -Cross-platform MQTT desktop client +## MQTTX + +[![GitHub Release](https://img.shields.io/github/release/emqx/mqttx?color=brightgreen)](https://github.com/emqx/mqttx/releases) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667) [![Total Downloads](https://img.shields.io/github/downloads/emqx/mqttx/total.svg)](https://github.com/emqx/mqttx/releases) + +English | [简体中文](./README-CN.md) + +--- + +MQTTX is a cross-platform MQTT desktop client open sourced by EMQ, which supports macOS, Linux, and Windows. + +MQTTX adopts the form of chat interface, which simplifies the page operation, facilitates the user to quickly test the MQTT/MQTTS connection, publish and subscribe to MQTT messages. + +## Preview + +![mqttx-preview](./assets/mqttx-preview.png) ## Installation Download from [GitHub Releases](https://github.com/emqx/MQTTX/releases) and install it. -## Project setup +## Usage + +1. Create broker + +2. The same broker can create many clients + +3. Go back to connectons page, select the broker's client for connection testing + +## Develop ``` shell # Clone - git clone git@github.com:emqx/MQTTX.git -cd MQTTX - # Install dependencies - +cd MQTTX yarn install # Compiles and hot-reloads for development - yarn run electron:serve # Compiles and minifies for production - yarn run electron:build ``` -### Customize configuration +## Contributing + +1. Fork this repository + +2. Add upstream remote `git remote add upstream git@github.com:emqx/MQTTX.git` + +3. Modify code, add commit on new branch, push it + +4. Submit a pull request to upstream develop branch, we will review it + +## Technology Stack -See [Configuration Reference](https://cli.vuejs.org/config/). +- [Vue](https://vuejs.org/) + [Element](https://element.eleme.io) +- [Electron](https://electronjs.org/) +- [TypeScript](https://www.typescriptlang.org/) +- [Lowdb](https://github.com/typicode/lowdb) ## License diff --git a/assets/mqttx-logo.png b/assets/mqttx-logo.png new file mode 100644 index 000000000..627189329 Binary files /dev/null and b/assets/mqttx-logo.png differ diff --git a/assets/mqttx-preview.png b/assets/mqttx-preview.png new file mode 100644 index 000000000..210075d34 Binary files /dev/null and b/assets/mqttx-preview.png differ diff --git a/main/getMenuTemplate.ts b/main/getMenuTemplate.ts index 997847484..7d6eb0c8a 100644 --- a/main/getMenuTemplate.ts +++ b/main/getMenuTemplate.ts @@ -1,4 +1,4 @@ -import { shell, BrowserWindow } from 'electron' +import { app, shell, BrowserWindow } from 'electron' import updateChecker from './updateChecker' const isMac = process.platform === 'darwin' @@ -9,7 +9,7 @@ const getMenuTemplate = (win: BrowserWindow): $TSFixed => { ...(isMac ? [ { - label: 'MQTTX', + label: app.getName(), submenu: [ { role: 'about' }, { type: 'separator' }, diff --git a/main/updateChecker.ts b/main/updateChecker.ts index 3c4444f7e..e5d47520c 100644 --- a/main/updateChecker.ts +++ b/main/updateChecker.ts @@ -8,15 +8,15 @@ const downloadUrl: string = 'https://github.com/emqx/MQTTX/releases/latest' const isUpdate = (latest: string, current: string): boolean => { const latestVersion: number[] = latest.split('.').map((item) => parseInt(item, 10)) const currentVersion: number[] = current.split('.').map((item) => parseInt(item, 10)) - let isMin: boolean = false + let update: boolean = false for (let i: number = 0; i < 3; i++) { if (currentVersion[i] < latestVersion[i]) { - isMin = true + update = true } } - return isMin + return update } const updateChecker = async (isAuto: boolean = true): Promise => { diff --git a/src/App.vue b/src/App.vue index 23a450379..5bea3036c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,13 +1,30 @@ + + + diff --git a/src/assets/images/brokers.png b/src/assets/images/brokers.png index fa6fb1e95..6d0cc621b 100644 Binary files a/src/assets/images/brokers.png and b/src/assets/images/brokers.png differ diff --git a/src/assets/images/connections.png b/src/assets/images/connections.png index 76b7f2e4d..b1a77be0c 100644 Binary files a/src/assets/images/connections.png and b/src/assets/images/connections.png differ diff --git a/src/assets/images/mqttx-dark.png b/src/assets/images/mqttx-dark.png new file mode 100644 index 000000000..bbe85c328 Binary files /dev/null and b/src/assets/images/mqttx-dark.png differ diff --git a/src/assets/images/mqttx.png b/src/assets/images/mqttx-light.png similarity index 100% rename from src/assets/images/mqttx.png rename to src/assets/images/mqttx-light.png diff --git a/src/assets/scss/element/element-reset.scss b/src/assets/scss/element/element-reset.scss index 1df4e201d..333cdd796 100644 --- a/src/assets/scss/element/element-reset.scss +++ b/src/assets/scss/element/element-reset.scss @@ -11,9 +11,10 @@ background: transparent; border: 2px solid var(--color-border-bold); width: 90px; + color: var(--color-text-light); } .el-radio-button__orig-radio:checked+.el-radio-button__inner { - background-color: var(--color-bg-connection_item); + background-color: var(--color-bg-item); color: var(--color-main-green); border: 2px solid var(--color-main-green) !important; z-index: 1; @@ -25,7 +26,8 @@ /* Card */ .el-card { - border: 1px solid #E6E8F1; + border: 1px solid var(--color-border-default); + background-color: var(--color-bg-normal); } /* Form */ @@ -35,12 +37,18 @@ } .el-form-item__content { span { - color: var(--color-text-title); + color: var(--color-text-default); } } .el-select { width: 100%; } + .el-form-item.is-success .el-input__inner, + .el-form-item.is-success .el-input__inner:focus, + .el-form-item.is-success .el-textarea__inner, + .el-form-item.is-success .el-textarea__inner:focus { + border-color: var(--color-main-green); + } } /* Icon */ @@ -55,6 +63,10 @@ border: 2px solid var(--color-main-green); color: var(--color-main-green); } +.el-button.is-plain:hover, +.el-button.is-plain:focus { + background: var(--color-bg-normal); +} /* Cascader */ .el-cascader { @@ -64,13 +76,22 @@ /* Message */ .el-message-box { border-radius: 2px; + background-color: var(--color-bg-messagebox); + border-color: var(--color-border-default); + .el-message-box__title { + color: var(--color-text-title); + } + .el-message-box__content { + color: var(--color-text-default); + } } .el-message-box__btns { font-size: 14px; .el-button--small { font-size: 14px; border: none; - color: var(--color-text-default); + color: var(--color-text-light); + background: transparent; &:hover { color: var(--color-second-green); background: transparent; @@ -92,8 +113,68 @@ /* Notification */ .el-notification { border-radius: 4px; + background-color: var(--color-bg-messagebox); + border-color: var(--color-border-default); .el-notification__title { color: var(--color-text-title); font-weight: normal; } + .el-icon-success { + color: var(--color-main-green); + } +} + +/* Dialog */ +.el-dialog { + background: var(--color-bg-normal); +} + +/* Input */ +.el-input { + .el-input__inner { + background: var(--color-bg-normal); + border: 1px solid var(--color-border-default); + color: var(--color-text-default); + &::placeholder { + color: var(--color-text-light); + } + } +} +.el-textarea { + .el-textarea__inner { + background: var(--color-bg-normal); + border: 1px solid var(--color-border-default); + color: var(--color-text-default); + &::placeholder { + color: var(--color-text-light); + } + } +} + +/* Popper */ +.el-popper { + background: var(--color-bg-messagebox); + border-color: var(--color-border-default); + li { + color: var(--color-text-default); + } +} +.el-popper[x-placement^="bottom"] .popper__arrow { + border-bottom-color: var(--color-border-default); + &::after { + border-bottom-color: var(--color-bg-messagebox); + } +} +.el-select-dropdown__item.hover, +.el-select-dropdown__item:hover { + background: var(--color-bg-item); +} + +/* Cascader */ +.el-cascader-menu { + border-right: 1px solid var(--color-border-default); +} +.el-cascader-node:not(.is-disabled):hover, +.el-cascader-node:not(.is-disabled):focus { + background: var(--color-bg-item); } diff --git a/src/assets/scss/theme/dark.scss b/src/assets/scss/theme/dark.scss index e4a2c3922..8717bd259 100644 --- a/src/assets/scss/theme/dark.scss +++ b/src/assets/scss/theme/dark.scss @@ -1,19 +1,32 @@ body.dark { /* Backgroud color */ - --color-bg-primary: #747272; - --color-bg-tabs: #F8F8F8; - --color-bg-tabs_hover: #F8F8F8; + --color-bg-normal: #282828; + --color-bg-primary: #232323; + --color-bg-tabs: #484848; + --color-bg-tabs_hover: #303030; + --color-bg-item: #395449; + --color-bg-item_status: #9EA3B1; + --color-bg-input: #323232; + --color-bg-messagebox: #303133; /* Font color */ - --color-text-title: #262626; - --color-text-default: #616161; - --color-text-light: #A2A9B0; + --color-text-title: #ffffff; + --color-text-default: #d3d3d3; + --color-text-light: #a3a3a3; --color-text-tips: #B4B4B4; + --color-text-right_block: #484848; + --color-text-right_info: #959599; + --color-text-left_info: #959599; /* Accent color */ --color-main-green: #34C388; --color-second-green: #53DAA2; + --color-thrid-green: #EBF8F2; + --color-main-red: #E86AA6; + --color-second-red: #EF607C; + --color-main-grey: #323232; /* Border color */ - --color-border-default: #E6E8F1; + --color-border-default: #383838; + --color-border-bold: #969696; } diff --git a/src/assets/scss/theme/light.scss b/src/assets/scss/theme/light.scss index 62c8621ad..a914bc4d6 100644 --- a/src/assets/scss/theme/light.scss +++ b/src/assets/scss/theme/light.scss @@ -1,18 +1,20 @@ body.light { /* Backgroud color */ --color-bg-normal: #ffffff; - --color-bg-accent: #f1f1f1; --color-bg-primary: #F9FAFD; --color-bg-tabs: #F8F8F8; --color-bg-tabs_hover: #F8F8F8; - --color-bg-connection_item: #EBF8F2; - --color-bg-connection_status: #9EA3B1; + --color-bg-item: #EBF8F2; + --color-bg-item_status: #9EA3B1; + --color-bg-input: #ffffff; + --color-bg-messagebox: #ffffff; /* Font color */ --color-text-title: #262626; --color-text-default: #616161; --color-text-light: #A2A9B0; --color-text-tips: #B4B4B4; + --color-text-right_block: #34C388; --color-text-right_info: #ADE7CF; --color-text-left_info: #959599; diff --git a/src/assets/scss/theme/purple.scss b/src/assets/scss/theme/purple.scss new file mode 100644 index 000000000..9fe8e6ef5 --- /dev/null +++ b/src/assets/scss/theme/purple.scss @@ -0,0 +1,32 @@ +body.purple { + /* Backgroud color */ + --color-bg-normal: #292B33; + --color-bg-primary: #212328; + --color-bg-tabs: #414556; + --color-bg-tabs_hover: #31333F; + --color-bg-item: #31333F; + --color-bg-item_status: #9EA3B1; + --color-bg-input: #31333F; + --color-bg-messagebox: #31333F; + + /* Font color */ + --color-text-title: #ffffff; + --color-text-default: #d3d3d3; + --color-text-light: #A2A9B0; + --color-text-tips: #B4B4B4; + --color-text-right_block: #1B1D20; + --color-text-right_info: #B4B4B4; + --color-text-left_info: #959599; + + /* Accent color */ + --color-main-green: #34C388; + --color-second-green: #53DAA2; + --color-thrid-green: #EBF8F2; + --color-main-red: #E86AA6; + --color-second-red: #EF607C; + --color-main-grey: #292A33; + + /* Border color */ + --color-border-default: #40414E; + --color-border-bold: #4F5367; +} diff --git a/src/background.ts b/src/background.ts index 58d48e859..34ec2a64b 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,7 +1,7 @@ 'use strict' import { - app, protocol, BrowserWindow, ipcMain, shell, Menu, + app, protocol, BrowserWindow, ipcMain, shell, Menu, systemPreferences, } from 'electron' import { createProtocol, @@ -11,7 +11,16 @@ import db from './datastore/index' import updateChecker from '../main/updateChecker' import getMenuTemplate from '../main/getMenuTemplate' -const isDevelopment = process.env.NODE_ENV !== 'production' +interface WindowSizeModel { + width: number, + height: number, +} + +declare const __static: string + +const isDevelopment: boolean = process.env.NODE_ENV !== 'production' +const isDarkMode: boolean = systemPreferences.isDarkMode() +const isMac: boolean = process.platform === 'darwin' // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. @@ -31,22 +40,19 @@ function handleIpcMessages() { }) } -interface WindowSizeModel { - width: number, - height: number, -} - -declare const __static: string - function createWindow() { const windowSize = db.get('windowSize') + const theme = db.get<'light' | 'dark' | 'purple'>('settings.currentTheme') // Create the browser window. win = new BrowserWindow({ ...windowSize, webPreferences: { + devTools: isDevelopment, webSecurity: false, nodeIntegration: true, }, + titleBarStyle: isMac ? 'hidden' : 'default', + backgroundColor: theme === 'dark' ? '#232323' : '#ffffff', icon: `${__static}/app.ico`, }) @@ -74,7 +80,7 @@ function createWindow() { app.on('window-all-closed', () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== 'darwin') { + if (!isMac) { app.quit() } }) @@ -91,7 +97,7 @@ app.on('activate', () => { // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.on('ready', async () => { - const autoCheckUpdate = db.get('settings.autoCheck') + const autoCheckUpdate = db.get('settings.autoCheck') if (isDevelopment && !process.env.IS_TEST) { // Install Vue Devtools try { diff --git a/src/components/Ipc.vue b/src/components/Ipc.vue index a60bfcebd..c5cd85e53 100644 --- a/src/components/Ipc.vue +++ b/src/components/Ipc.vue @@ -8,7 +8,7 @@ import { Component, Vue } from 'vue-property-decorator' import { ipcRenderer } from 'electron' @Component -export default class Home extends Vue { +export default class Ipc extends Vue { private bindIpcEvents(): void { ipcRenderer.on('setting', (event: any, ...args: any[]) => { const eventType: any = args[0] diff --git a/src/components/LeftPanel.vue b/src/components/LeftPanel.vue index 6bf82f201..65552c887 100644 --- a/src/components/LeftPanel.vue +++ b/src/components/LeftPanel.vue @@ -23,10 +23,11 @@ export default class LeftPanel extends Vue {} left: 279px; z-index: 1; width: 250px; - background: #fff; + background: var(--color-bg-normal); border-radius: 0; top: 0; bottom: 0; + padding-bottom: 42px; } } .pop-enter-active { diff --git a/src/components/Leftbar.vue b/src/components/Leftbar.vue index ce32e8ce8..a7828e070 100644 --- a/src/components/Leftbar.vue +++ b/src/components/Leftbar.vue @@ -1,5 +1,5 @@