first commit
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
.DS_Store
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Webpack
|
||||
.webpack/
|
||||
|
||||
# Electron-Forge
|
||||
out/
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Main Process",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"args": [
|
||||
"."
|
||||
],
|
||||
"outputCapture": "std"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 - present Xen Kuo
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,80 @@
|
|||
# comNG -- 串口助手
|
||||
|
||||
`comNG` 是一款具有现代化 UI 设计并且功能强大的串口助手软件。
|
||||
|
||||
## 介绍
|
||||
|
||||
comNG 区别于其他串口助手的地方在于其强大的 “现场数据分析“ 能力。简单来说就是 comNG 提供的多种功能以帮助用户更方便的分析打印输出文本。这些功能包括:
|
||||
|
||||
- 内建的 comNGLang 高亮语法
|
||||
- 内建的手动文本高亮功能(类似于 notepad++的 Style Token)
|
||||
- 搜索文本高亮 (类似于 vscode 的搜索文本高亮)
|
||||
- 选择文本高亮 (类似于 vscode 的选择文本高亮)
|
||||
- 基于文本内容的中断功能,全新的功能
|
||||
- 日志文档的签名(时间和姓名)
|
||||
- 跨平台:Windows, Mac OS 以及 Ubuntu 等 Linux 系统
|
||||
|
||||
另外还包含一些串口助手通用的功能:
|
||||
|
||||
- Modem 信号指示和控制
|
||||
- 自定义波特率
|
||||
- 十六进制接收
|
||||
- 接收时间戳
|
||||
- 发送文本
|
||||
- 流控
|
||||
- 文件保存和打开,支持拖动
|
||||
|
||||
当然还有一些不支持的功能,比如:
|
||||
|
||||
- 十六进制发送
|
||||
- 文件发送
|
||||
- 抓取至文件
|
||||
|
||||
## 用户界面
|
||||
|
||||
![image](/image/preview.jpg)
|
||||
|
||||
## 使用方法
|
||||
|
||||
下载对应系统的安装文件,安装,然后就应该可以正常使用了。对于 Linux 系统,可能对串口设备文件执行以下命令:
|
||||
|
||||
`sudo chmod 666 /dev/ttyS1`
|
||||
|
||||
记得把 `ttyS1` 替换为你的串口设备文件。
|
||||
|
||||
## 开发
|
||||
|
||||
### 克隆代码
|
||||
|
||||
```
|
||||
git clone git@gitee.com:xenkuo/comNG.git
|
||||
```
|
||||
|
||||
### 安装依赖文件
|
||||
|
||||
```
|
||||
cd comNG
|
||||
yarn
|
||||
```
|
||||
|
||||
Windows 下安装 node 和 electron 比较麻烦,建议使用以下 `.npmrc` 文件配置:
|
||||
|
||||
```
|
||||
registry=https://registry.npm.taobao.org
|
||||
electron_mirror=https://cdn.npm.taobao.org/dist/electron/
|
||||
electron_custom_dir=7.1.11
|
||||
```
|
||||
|
||||
Windows 下安装 native 编译工具更麻烦,建议多试试,因为我现在在其他 Windows 上也安装不成功了。。。
|
||||
|
||||
### 编译
|
||||
|
||||
```
|
||||
code .
|
||||
yarn start
|
||||
yarn make
|
||||
```
|
||||
|
||||
## Licence
|
||||
|
||||
comNG is [MIT](https://opensource.org/licenses/MIT) licensed and all it's dependencies are MIT licensed.
|
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 145 KiB |
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"name": "comNG",
|
||||
"productName": "comNG",
|
||||
"version": "1.0.4",
|
||||
"description": "My Electron application description",
|
||||
"repository": "https://github.com/xenkuo/comNG",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"rebuild": "electron-rebuild -f -w serialport",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "echo \"No linting configured\""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "Xen",
|
||||
"email": "xenkuo@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"forge": {
|
||||
"packagerConfig": {
|
||||
"asar": true,
|
||||
"ignore": [
|
||||
"node_modules/monaco-editor/(dev|esm|min-maps|monaco.d.ts)",
|
||||
"node_modules/materialize-css/(extras|js|sass)",
|
||||
"node_modules/material-design-icons/(?!iconfont)"
|
||||
],
|
||||
"icon": "image/logo"
|
||||
},
|
||||
"makers": [
|
||||
{
|
||||
"name": "@electron-forge/maker-squirrel",
|
||||
"config": {
|
||||
"authors": "Xen",
|
||||
"setupIcon": "image/logo.ico"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-dmg",
|
||||
"config": {
|
||||
"icon": "./image/logo.icns",
|
||||
"background": "./image/dmg-background.png",
|
||||
"format": "ULFO"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-deb",
|
||||
"config": {
|
||||
"options": {
|
||||
"maintainer": "Xen",
|
||||
"icon": "./image/logo.png",
|
||||
"mimeType": [
|
||||
"cnl",
|
||||
"text"
|
||||
],
|
||||
"section": "utils",
|
||||
"categories": [
|
||||
"Utilify"
|
||||
],
|
||||
"homepage": "https://xenkuo.github.io",
|
||||
"description": "comNG is a serialport tool from next generation!"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"publishers": [
|
||||
{
|
||||
"name": "@electron-forge/publisher-github",
|
||||
"config": {
|
||||
"repository": {
|
||||
"owner": "xenkuo",
|
||||
"name": "comNG"
|
||||
},
|
||||
"draft": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-localshortcut": "^3.2.1",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-store": "^5.1.0",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"materialize-css": "^1.0.0-rc.2",
|
||||
"monaco-editor": "^0.20.0",
|
||||
"serialport": "^8.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "6.0.0-beta.47",
|
||||
"@electron-forge/maker-deb": "6.0.0-beta.47",
|
||||
"@electron-forge/maker-dmg": "^6.0.0-beta.49",
|
||||
"@electron-forge/maker-squirrel": "6.0.0-beta.47",
|
||||
"@electron-forge/publisher-github": "^6.0.0-beta.50",
|
||||
"electron": "7.1.11",
|
||||
"electron-rebuild": "^1.10.0",
|
||||
"eslint": "^6.8.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,548 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable no-undef */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const amdLoader = require("../node_modules/monaco-editor/min/vs/loader.js");
|
||||
const { dialog } = require("electron").remote;
|
||||
|
||||
const amdRequire = amdLoader.require;
|
||||
const hexmodeUnitWidth = 8;
|
||||
|
||||
const decoMod = 7;
|
||||
const decoTable = [
|
||||
{ style: "hl-red", color: "#ff8a80" },
|
||||
{ style: "hl-orange", color: "#ffd180" },
|
||||
{ style: "hl-yellow", color: "#ffff8d" },
|
||||
{ style: "hl-green", color: "#b9f6ca" },
|
||||
{ style: "hl-blue", color: "8dd8ff" },
|
||||
{ style: "hl-indigo", color: "8c9eff" },
|
||||
{ style: "hl-purple", color: "ea80fc" },
|
||||
];
|
||||
|
||||
var editor;
|
||||
var hexmodeIndex = 0;
|
||||
var hexmodeUnitIndex = 0;
|
||||
var breakpointHit = false;
|
||||
var breakpointAfterLines = 0;
|
||||
var breakpointBuff = [];
|
||||
var half_line = false;
|
||||
var decoIndex = 0;
|
||||
|
||||
function uriFromPath(_path) {
|
||||
var pathName = path.resolve(_path).replace(/\\/g, "/");
|
||||
if (pathName.length > 0 && pathName.charAt(0) !== "/") {
|
||||
pathName = "/" + pathName;
|
||||
}
|
||||
return encodeURI("file://" + pathName);
|
||||
}
|
||||
|
||||
function decoGet() {
|
||||
return decoTable[decoIndex++ % decoMod];
|
||||
}
|
||||
|
||||
function decoApply(m, text) {
|
||||
let matches = m.findMatches(
|
||||
text,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?",
|
||||
false
|
||||
);
|
||||
let decoration = decoGet();
|
||||
|
||||
console.log(decoration.style, decoration.color);
|
||||
|
||||
for (let i of matches) {
|
||||
let range = i.range;
|
||||
|
||||
m.deltaDecorations(
|
||||
[],
|
||||
[
|
||||
{
|
||||
range: range,
|
||||
options: {
|
||||
className: decoration.style,
|
||||
overviewRuler: {
|
||||
color: decoration.color,
|
||||
position: 4, // position right
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function decoRemove(m, text) {
|
||||
let matches = m.findMatches(
|
||||
text,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?",
|
||||
false
|
||||
);
|
||||
|
||||
for (let match of matches) {
|
||||
let decos = m.getDecorationsInRange(match.range);
|
||||
|
||||
// super word remove decoration will cause sub word decoration to 1
|
||||
for (let deco of decos) {
|
||||
m.deltaDecorations([deco.id], []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function highlightToggle() {
|
||||
console.log("highligh toggle");
|
||||
let decoExpectedLength = 1;
|
||||
let m = editor.getModel();
|
||||
let range = editor.getSelection();
|
||||
let text = m.getValueInRange(range);
|
||||
if (text === "") {
|
||||
let word = m.getWordAtPosition(editor.getPosition());
|
||||
text = word.word;
|
||||
range.startColumn = word.startColumn;
|
||||
range.endColumn = word.endColumn;
|
||||
decoExpectedLength = 2;
|
||||
}
|
||||
|
||||
if (text === "") return;
|
||||
|
||||
let decos = m.getDecorationsInRange(range);
|
||||
if (decos.length === decoExpectedLength) {
|
||||
decoApply(m, text);
|
||||
} else {
|
||||
decoRemove(m, text);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function openFile() {
|
||||
dialog
|
||||
.showOpenDialog({
|
||||
properties: ["openFile"],
|
||||
filters: [{ name: "comNG log", extensions: ["cnl", "txt"] }],
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.canceled === false) {
|
||||
const file = result.filePaths[0];
|
||||
const text = fs.readFileSync(file).toString();
|
||||
editor.getModel().setValue(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveToFile() {
|
||||
let fileName = new Date(+new Date() + 8 * 3600 * 1000);
|
||||
fileName = "Log-" + fileName.toISOString();
|
||||
fileName = fileName.replace(/[.|:]/g, "-");
|
||||
if (config.advance.sign.switch === true && config.advance.sign.name !== "")
|
||||
fileName += "-" + config.advance.sign.name;
|
||||
|
||||
dialog
|
||||
.showSaveDialog({
|
||||
properties: ["createDirectory"],
|
||||
defaultPath: fileName,
|
||||
filters: [{ name: "comNG Log", extensions: ["cnl"] }],
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.canceled === false) {
|
||||
const file = result.filePath;
|
||||
const text = editor.getModel().getValue();
|
||||
fs.writeFileSync(file, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getTimestamp() {
|
||||
const t = new Date();
|
||||
|
||||
return (
|
||||
t.toLocaleTimeString().split(" ")[0] +
|
||||
":" +
|
||||
t.getMilliseconds().toString().padStart(3, 0) +
|
||||
" "
|
||||
);
|
||||
}
|
||||
|
||||
function editorAppend(text) {
|
||||
const lineCount = editor.getModel().getLineCount();
|
||||
const lastLineLength = editor.getModel().getLineMaxColumn(lineCount);
|
||||
|
||||
const range = new monaco.Range(
|
||||
lineCount,
|
||||
lastLineLength,
|
||||
lineCount,
|
||||
lastLineLength
|
||||
);
|
||||
|
||||
editor.getModel().applyEdits([
|
||||
{
|
||||
forceMoveMarkers: true,
|
||||
range: range,
|
||||
text: text.toString(),
|
||||
},
|
||||
]);
|
||||
|
||||
editor.revealLine(editor.getModel().getLineCount());
|
||||
}
|
||||
|
||||
function buff2hex(buff) {
|
||||
return Array.prototype.map
|
||||
.call(new Uint8Array(buff), (x) => ("00" + x.toString(16)).slice(-2))
|
||||
.join("");
|
||||
}
|
||||
|
||||
function showHex(buff) {
|
||||
let hexBuff = buff2hex(buff);
|
||||
|
||||
while (hexBuff.length !== 0) {
|
||||
let len = hexmodeUnitWidth - hexmodeIndex;
|
||||
if (hexBuff.length < len) len = hexBuff.length;
|
||||
let line = hexBuff.slice(0, len);
|
||||
|
||||
hexmodeIndex = (hexmodeIndex + len) % hexmodeUnitWidth;
|
||||
if (hexmodeIndex === 0) {
|
||||
hexmodeUnitIndex++;
|
||||
if (hexmodeUnitIndex === 4) {
|
||||
hexmodeUnitIndex = 0;
|
||||
line += "\n";
|
||||
} else {
|
||||
line += " ";
|
||||
}
|
||||
}
|
||||
editorAppend(line);
|
||||
|
||||
hexBuff = hexBuff.slice(len, hexBuff.length);
|
||||
}
|
||||
}
|
||||
|
||||
function breakpointProcess(line) {
|
||||
if (breakpointHit === false) {
|
||||
let bpLine = line;
|
||||
|
||||
if (breakpointBuff.length !== 0) {
|
||||
bpLine = Buffer.concat(
|
||||
[breakpointBuff, line],
|
||||
line.length + breakpointBuff.length
|
||||
);
|
||||
breakpointBuff = [];
|
||||
}
|
||||
|
||||
if (bpLine.includes(config.advance.breakpoint.onText) === true) {
|
||||
breakpointHit = true;
|
||||
breakpointAfterLines = 0;
|
||||
}
|
||||
} else {
|
||||
breakpointAfterLines++;
|
||||
if (breakpointAfterLines >= config.advance.breakpoint.afterLines) {
|
||||
breakpointHit = false;
|
||||
breakpointAfterLines = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function processSerialData(buff) {
|
||||
if (config.general.hexmode === true) {
|
||||
showHex(buff);
|
||||
} else {
|
||||
let index = -1;
|
||||
// console.log(buff)
|
||||
// console.log(buff.toString())
|
||||
while ((index = buff.indexOf("\n")) !== -1) {
|
||||
let line = buff.slice(0, index + 1);
|
||||
// console.log(line)
|
||||
|
||||
if (half_line === true) {
|
||||
editorAppend(line);
|
||||
half_line = false;
|
||||
} else {
|
||||
let timestamp = "";
|
||||
|
||||
if (config.general.timestamp === true) timestamp = getTimestamp();
|
||||
editorAppend(timestamp + line);
|
||||
}
|
||||
buff = buff.slice(index + 1, buff.length);
|
||||
// console.log(buff)
|
||||
|
||||
if (config.advance.breakpoint.switch === true) {
|
||||
if (breakpointProcess(line) === true) {
|
||||
buff = [];
|
||||
serialClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buff.length !== 0) {
|
||||
if (half_line === false) {
|
||||
let timestamp = "";
|
||||
|
||||
if (config.general.timestamp === true) timestamp = getTimestamp();
|
||||
editorAppend(timestamp + buff);
|
||||
half_line = true;
|
||||
} else {
|
||||
editorAppend(buff);
|
||||
}
|
||||
}
|
||||
if (config.advance.breakpoint.switch === true) {
|
||||
breakpointBuff = buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amdRequire.config({
|
||||
// eslint-disable-next-line no-undef
|
||||
baseUrl: uriFromPath(
|
||||
path.join(__dirname, "../node_modules/monaco-editor/min")
|
||||
),
|
||||
});
|
||||
|
||||
// workaround monaco-css not understanding the environment
|
||||
self.module = undefined;
|
||||
|
||||
amdRequire(["vs/editor/editor.main"], function () {
|
||||
monaco.languages.register({
|
||||
id: "comNGLang",
|
||||
});
|
||||
monaco.languages.setMonarchTokensProvider("comNGLang", {
|
||||
defaultToken: "",
|
||||
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/^\[?[f|F][a|A][t|T][a|A][l|L]\]?\s.*/, "fatal"],
|
||||
[/\s+\[?[f|F][a|A][t|T][a|A][l|L]\]?\s+/, "fatal"],
|
||||
[/^\[?F\]?\s.*/, "fatal"],
|
||||
[/\s+\[?F\]?\s+/, "fatal"],
|
||||
[/^\[?[e|E][r|R][r|R][o|O][r|R]\]?\s.*/, "error"],
|
||||
[/\s+\[?[e|E][r|R][r|R][o|O][r|R]\]?\s+/, "error"],
|
||||
[/^\[?E\]?\s.*/, "error"],
|
||||
[/\s+\[?E\]?\s+/, "error"],
|
||||
[/^\[?[w|W][a|A][r|R][n|N]\]?\s.*/, "warn"],
|
||||
[/\s+\[?[w|W][a|A][r|R][n|N]\]?\s+/, "warn"],
|
||||
[/^\[?W\]?\s.*/, "warn"],
|
||||
[/\s+\[?W\]?\s+/, "warn"],
|
||||
[/^\[?[i|I][n|N][f|F][o|O]\]?\s.*/, "info"],
|
||||
[/\s+\[?[i|I][n|N][f|F][o|O]\]?\s+/, "info"],
|
||||
[/^\[?I\]?\s.*/, "info"],
|
||||
[/\s+\[?I\]?\s+/, "info"],
|
||||
[/^\[?[t|T][r|R][a|A][c|C][e|E]\]?\s.*/, "trace"],
|
||||
[/\s+\[?[t|T][r|R][a|A][c|C][e|E]\]?\s+/, "trace"],
|
||||
[/^\[?T\]?\s.*/, "trace"],
|
||||
[/\s+\[?T\]?\s+/, "trace"],
|
||||
[/^\[?[d|D][e|E][b|B][u|U][g|G]\]?\s.*/, "debug"],
|
||||
[/\s+\[?[d|D][e|E][b|B][u|U][g|G]\]?\s+/, "debug"],
|
||||
[/^\[?D\]?\s.*/, "debug"],
|
||||
[/\s+\[?D\]?\s+/, "debug"],
|
||||
|
||||
[/\[\d;\d{2}m/, "useless"],
|
||||
[/\[\dm/, "useless"],
|
||||
|
||||
[/[{}()[\]]/, "bracket"],
|
||||
[/^\d{1,2}:\d{2}:\d{2}:\d{1,3}/, "timestamp"],
|
||||
[/\d{1,4}(-|\/|\.|:)\d{1,2}\1\d{1,4}/, "time"],
|
||||
[
|
||||
/(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)(-|\/|\.|:)(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\2(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\2(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)/,
|
||||
"ip",
|
||||
],
|
||||
[
|
||||
/[0-9a-fA-F]{2}(-|\/|\.|:)[0-9a-fA-F]{2}\1[0-9a-fA-F]{2}\1[0-9a-fA-F]{2}\1[0-9a-fA-F]{2}\1[0-9a-fA-F]{2}/,
|
||||
"mac",
|
||||
],
|
||||
[/\d*\.\d+([eE][-+]?\d+)?/, "number"],
|
||||
[/0[xX][0-9a-fA-F]+/, "number"],
|
||||
[/[0-9a-fA-F]{4,}/, "number"],
|
||||
[/\d+/, "number"],
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// Define a new theme that contains only rules that match this language
|
||||
monaco.editor.defineTheme("comNGTheme", {
|
||||
base: "vs",
|
||||
inherit: false,
|
||||
colors: {
|
||||
"editor.background": "#fafafa",
|
||||
"scrollbarSlider.background": "#fafafa",
|
||||
},
|
||||
rules: [
|
||||
{ token: "number", foreground: "2e7d32" },
|
||||
{ token: "bracket", foreground: "ff9800" },
|
||||
{ token: "timestamp", foreground: "009688" },
|
||||
{ token: "time", foreground: "2196f3" },
|
||||
{ token: "ip", foreground: "03a9f4" },
|
||||
{ token: "mac", foreground: "00bcd4" },
|
||||
{ token: "fatal", foreground: "e91e63" },
|
||||
{ token: "error", foreground: "f44336" },
|
||||
{ token: "warn", foreground: "ff9800" },
|
||||
{ token: "info", foreground: "9e9e9e" },
|
||||
{ token: "trace", foreground: "9e9d24" },
|
||||
{ token: "debug", foreground: "2e7d32" },
|
||||
{ token: "useless", foreground: "cecece" },
|
||||
],
|
||||
});
|
||||
|
||||
editor = monaco.editor.create(document.getElementById("editor-area"), {
|
||||
theme: "comNGTheme",
|
||||
language: "comNGLang",
|
||||
automaticLayout: true,
|
||||
readOnly: true,
|
||||
folding: false,
|
||||
fontFamily: config.general.fontFamily,
|
||||
fontSize: config.general.fontSize,
|
||||
overviewRulerBorder: false,
|
||||
scrollBeyondLastLine: false,
|
||||
smoothScrolling: true,
|
||||
mouseWheelZoom: true, // combined with Ctrl
|
||||
wordWrap: "on",
|
||||
wordWrapBreakAfterCharacters: "",
|
||||
wordWrapBreakBeforeCharacters: "",
|
||||
lineNumbersMinChars: 3,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
scrollbar: {
|
||||
vertical: "auto",
|
||||
useShadows: false,
|
||||
verticalScrollbarSize: 10,
|
||||
},
|
||||
});
|
||||
|
||||
let editorConfig = {
|
||||
brackets: [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
['"', '"'],
|
||||
["'", "'"],
|
||||
],
|
||||
};
|
||||
monaco.languages.setLanguageConfiguration("comNGLang", editorConfig);
|
||||
|
||||
editor.addAction({
|
||||
id: "highlight-toggle",
|
||||
label: "Highlight Toggle",
|
||||
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_E],
|
||||
precondition: null,
|
||||
keybindingContext: null,
|
||||
contextMenuGroupId: "9_cutcopypaste",
|
||||
contextMenuOrder: 1.5,
|
||||
run: highlightToggle,
|
||||
});
|
||||
|
||||
// editor.addAction({
|
||||
// id: "open-file",
|
||||
// label: "Open File...",
|
||||
// keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_O],
|
||||
// precondition: null,
|
||||
// keybindingContext: null,
|
||||
// contextMenuGroupId: "9_cutcopypaste",
|
||||
// contextMenuOrder: 1.5,
|
||||
// run: dummy,
|
||||
// });
|
||||
|
||||
// editor.addAction({
|
||||
// id: "save-to-file",
|
||||
// label: "Save To File...",
|
||||
// keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S],
|
||||
// precondition: null,
|
||||
// keybindingContext: null,
|
||||
// contextMenuGroupId: "9_cutcopypaste",
|
||||
// contextMenuOrder: 1.5,
|
||||
// run: dummy,
|
||||
// });
|
||||
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd + monaco.KeyCode.KEY_W, () => {
|
||||
// Do nothing but prevent default action: close window
|
||||
});
|
||||
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd + monaco.KeyCode.KEY_X, () => {
|
||||
// Do nothing but prevent default action: close window
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("clear-btn").onclick = () => {
|
||||
let value = "";
|
||||
|
||||
if (config.advance.sign.switch === true) {
|
||||
value = "Captured at " + new Date().toLocaleString() + " with comNG";
|
||||
if (config.advance.sign.name !== "")
|
||||
value += " by " + config.advance.sign.name + ".";
|
||||
value += "\n";
|
||||
}
|
||||
hexmodeIndex = 0;
|
||||
editor.getModel().setValue(value);
|
||||
};
|
||||
|
||||
document.getElementById("editor-font-family").onblur = (e) => {
|
||||
let font = e.target.value.trim();
|
||||
|
||||
if (font === "")
|
||||
font =
|
||||
"Consolas, 'SF Mono', Menlo, 'Lucida Console', 'Courier New', monospace";
|
||||
editor.updateOptions({ fontFamily: font });
|
||||
configUpdate("general.fontFamily", font);
|
||||
};
|
||||
|
||||
document.getElementById("editor-font-size").onblur = (e) => {
|
||||
let size = e.target.value.trim();
|
||||
if (size === "") size = 12;
|
||||
|
||||
editor.updateOptions({ fontSize: size });
|
||||
configUpdate("general.fontSize", size);
|
||||
};
|
||||
|
||||
document.getElementById("breakpoint-switch").onclick = (e) => {
|
||||
if (e.target.checked === true) {
|
||||
if (config.advance.breakpoint.onText.length === 0) {
|
||||
toast("Error: Breakpoint on-text cant be empty");
|
||||
e.target.checked = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
configUpdate("advance.breakpoint.switch", e.target.checked);
|
||||
breakpointHit = false;
|
||||
breakpointAfterLines = 0;
|
||||
};
|
||||
|
||||
document.getElementById("breakpoint-on-text").onblur = (e) => {
|
||||
configUpdate("advance.breakpoint.onText", e.target.value);
|
||||
};
|
||||
|
||||
document.getElementById("breakpoint-after-lines").onblur = (e) => {
|
||||
let lines = parseInt(e.target.value);
|
||||
|
||||
if (isNaN(lines) === true) lines = 5;
|
||||
configUpdate("advance.breakpoint.afterLines", lines);
|
||||
};
|
||||
|
||||
document.getElementById("editor-area").ondragover = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
document.getElementById("editor-area").ondragleave = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
document.getElementById("editor-area").ondragend = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
document.getElementById("editor-area").ondrop = (e) => {
|
||||
console.log("ondrop");
|
||||
e.preventDefault();
|
||||
|
||||
let f = e.dataTransfer.files[0];
|
||||
|
||||
f.text().then((text) => {
|
||||
editor.getModel().setValue(text);
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
|
@ -0,0 +1,297 @@
|
|||
html {
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
body {
|
||||
border: 1px solid #26a69a;
|
||||
height: 100%;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
:root {
|
||||
--menu-height: 200px;
|
||||
--bar-height: 53px;
|
||||
--trans-btn-height: 20px;
|
||||
--modem-btn-height: 10px;
|
||||
--bar-color-head: #fafafa;
|
||||
--bar-color-middle: #fafafa;
|
||||
--bar-color-tail: #fafafa;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav {
|
||||
-webkit-app-region: drag;
|
||||
background-color: #fafafa;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-top: 14px;
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
border-left: 4px solid #26a69a;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.head-li {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.tiny-btn {
|
||||
-webkit-app-region: no-drag;
|
||||
margin: 5px !important;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.no-head {
|
||||
margin-top: 12px;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.select-wrapper input.select-dropdown {
|
||||
padding-left: 14px;
|
||||
padding-right: 20px;
|
||||
box-sizing: border-box;
|
||||
text-overflow: ellipsis;
|
||||
border-radius: 12px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
z-index: inherit;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.dropdown-content li {
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.dropdown-content li > a,
|
||||
.dropdown-content li > span {
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.tabs .tab {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.tabs .tab a {
|
||||
color: #26a69a;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
/* border-radius: 6px 6px 6px 6px; */
|
||||
}
|
||||
|
||||
.tabs .indicator {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.tabs .tab a:hover,
|
||||
.tabs .tab a.active,
|
||||
.tabs .tab a:focus.active {
|
||||
color: #26a69a;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.config-row {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.config-key {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
line-height: 42px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.label-row {
|
||||
margin-bottom: 0px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.label-key {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.toast {
|
||||
background-color: #fafafa;
|
||||
color: #26a69a;
|
||||
margin-bottom: 254px;
|
||||
margin-left: 150px;
|
||||
max-width: 300px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#logo {
|
||||
zoom: 80%;
|
||||
height: 20px !important;
|
||||
line-height: 24px !important;
|
||||
margin-left: 6px;
|
||||
margin-top: 2px;
|
||||
color: #26a69a;
|
||||
}
|
||||
|
||||
#menu-area {
|
||||
height: var(--menu-height);
|
||||
width: calc(100%);
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
#menu-tabs-row {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#menu-tabs {
|
||||
background-color: #e0f2f1;
|
||||
}
|
||||
|
||||
#bar-area {
|
||||
width: calc(100% - 2px);
|
||||
height: var(--bar-height);
|
||||
position: fixed;
|
||||
bottom: 1px;
|
||||
margin-bottom: 0px;
|
||||
line-height: 54px;
|
||||
background: -webkit-linear-gradient(
|
||||
60deg,
|
||||
var(--bar-color-head),
|
||||
var(--bar-color-middle),
|
||||
var(--bar-color-tail)
|
||||
);
|
||||
}
|
||||
|
||||
#modem-signal-bar {
|
||||
height: var(--modem-btn-height);
|
||||
/* background-color: aqua; */
|
||||
position: fixed;
|
||||
bottom: var(--bar-height) - var(--modem-btn-height);
|
||||
}
|
||||
|
||||
.modem-btn {
|
||||
position: sticky;
|
||||
bottom: var(--bar-height) - var(--modem-btn-height);
|
||||
font-size: var(--modem-btn-height);
|
||||
height: var(--modem-btn-height) !important;
|
||||
line-height: var(--modem-btn-height) !important;
|
||||
border-radius: 0px 0px var(--modem-btn-height) var(--modem-btn-height);
|
||||
}
|
||||
|
||||
#trans-log-area {
|
||||
font-size: 12px;
|
||||
max-height: 136px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#trans-eof-row {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#trans-send-btn,
|
||||
#trans-log-btn {
|
||||
margin-top: 16px;
|
||||
|
||||
height: var(--trans-btn-height) !important;
|
||||
line-height: var(--trans-btn-height) !important;
|
||||
border-radius: calc(var(--trans-btn-height) / 2);
|
||||
}
|
||||
|
||||
#color-panel {
|
||||
height: 43px;
|
||||
line-height: 43px;
|
||||
}
|
||||
|
||||
#licence-row {
|
||||
margin-top: 6px;
|
||||
}
|
||||
#licence-key {
|
||||
height: 24px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#refresh-btn {
|
||||
transform: scale(0.6);
|
||||
transform-origin: left;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
#clear-btn {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
#baud-input,
|
||||
#path-input {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
|
||||
.hl-red {
|
||||
background-color: #ff8a80 !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hl-orange {
|
||||
background-color: #ffd180 !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hl-yellow {
|
||||
background-color: #ffff8d !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hl-green {
|
||||
background-color: #b9f6ca !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hl-blue {
|
||||
background-color: #80d8ff !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hl-indigo {
|
||||
background-color: #8c9eff !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.hl-purple {
|
||||
background-color: #ea80fc !important;
|
||||
border-radius: 4px;
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--Import Google Icon Font-->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../node_modules/material-design-icons/iconfont/material-icons.css"
|
||||
/>
|
||||
<!--Import materialize.css-->
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="../node_modules/materialize-css/dist/css/materialize.css"
|
||||
media="screen,projection"
|
||||
/>
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self' 'unsafe-inline'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://api.github.com/repos/xenkuo/comNG/releases/latest"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="z-depth-0" id="nav-area">
|
||||
<div class="nav-wrapper" id="header">
|
||||
<i class="material-icons left" id="logo">vpn_key</i>
|
||||
|
||||
<ul class="right">
|
||||
<li class="head-li">
|
||||
<a id="min-btn" class="waves-effect btn-floating tiny-btn grey"></a>
|
||||
</li>
|
||||
<li class="head-li">
|
||||
<a
|
||||
id="max-btn"
|
||||
class="waves-effect btn-floating tiny-btn white"
|
||||
></a>
|
||||
</li>
|
||||
<li class="head-li">
|
||||
<a
|
||||
id="close-btn"
|
||||
class="waves-effect btn-floating tiny-btn red"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="editor-area"></div>
|
||||
|
||||
<div id="menu-area" hidden>
|
||||
<div class="row">
|
||||
<div class="col s12" id="menu-tabs-row">
|
||||
<ul class="tabs" id="menu-tabs">
|
||||
<li class="tab col s3">
|
||||
<a class="active" href="#general-tab">general</a>
|
||||
</li>
|
||||
<li class="tab col s3"><a href="#transmit-tab">transmit</a></li>
|
||||
<li class="tab col s3"><a href="#advance-tab">advance</a></li>
|
||||
<li class="tab col s3"><a href="#about-tab">about</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="general-tab" class="col s12">
|
||||
<div class="col s6">
|
||||
<div class="row config-row">
|
||||
<div class="col s5 config-key">
|
||||
<p>Hex Mode</p>
|
||||
</div>
|
||||
<div class="col s7 config-item">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Off
|
||||
<input type="checkbox" id="hexmode-switch" />
|
||||
<span class="lever"></span>
|
||||
On
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="col s5 config-key">
|
||||
<p>Timestamp</p>
|
||||
</div>
|
||||
<div class="col s7 config-item">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Off
|
||||
<input type="checkbox" id="timestamp-switch" />
|
||||
<span class="lever"></span>
|
||||
On
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="col s5 config-key">
|
||||
<p>Modem Signal</p>
|
||||
</div>
|
||||
<div class="col s7 config-item">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Off
|
||||
<input type="checkbox" id="modem-signal-switch" />
|
||||
<span class="lever"></span>
|
||||
On
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="col s7 config-key">
|
||||
<p>Customized Baud Rate</p>
|
||||
</div>
|
||||
<div class="input-field col s3 config-item">
|
||||
<input
|
||||
id="customized"
|
||||
value="9600"
|
||||
type="text"
|
||||
class="validate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s2 no-head">
|
||||
<div class="input-field col s12">
|
||||
<select id="databits-select">
|
||||
<option value="1" selected>8</option>
|
||||
<option value="2">7</option>
|
||||
<option value="3">6</option>
|
||||
<option value="4">5</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s12">
|
||||
<select id="parity-select">
|
||||
<option value="1" selected>None</option>
|
||||
<option value="2">Even</option>
|
||||
<option value="3">Odd</option>
|
||||
<option value="4">Mark</option>
|
||||
<option value="5">Space</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s12">
|
||||
<select id="stopbits-select">
|
||||
<option value="1" selected>1</option>
|
||||
<option value="2">2</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s12">
|
||||
<select id="flowcontrol-select">
|
||||
<option value="1" selected>None</option>
|
||||
<option value="2">RTSCTS</option>
|
||||
<option value="3">XON</option>
|
||||
<option value="4">XOFF</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s1 no-head"></div>
|
||||
<div class="col s3 no-head">
|
||||
<div class="input-field col s12 config-item">
|
||||
<input
|
||||
id="editor-font-family"
|
||||
placeholder="Font Family"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-field col s12 config-item">
|
||||
<input
|
||||
id="editor-font-size"
|
||||
placeholder="Font Size"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="transmit-tab" class="col s12">
|
||||
<div class="col s7">
|
||||
<div class="row config-row">
|
||||
<div class="input-field col s8">
|
||||
<input value="AT" id="trans-data" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="col s4">
|
||||
<a class="btn-small waves-effect" id="trans-send-btn"
|
||||
><i class="material-icons">send</i></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="col s1 config-key">
|
||||
<p>EOF</p>
|
||||
</div>
|
||||
<div class="input-field col s4" id="trans-eof-row">
|
||||
<select id="trans-eof-select">
|
||||
<option value="1" selected>CRLF</option>
|
||||
<option value="2">LF</option>
|
||||
<option value="3">CR</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="col s2 config-key">
|
||||
<p>Repeat</p>
|
||||
</div>
|
||||
<div class="col s5 config-item">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Off
|
||||
<input type="checkbox" id="trans-repeat-switch" />
|
||||
<span class="lever"></span>
|
||||
On
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s2 config-key">
|
||||
<p>Interval</p>
|
||||
</div>
|
||||
<div class="input-field col s3 config-item">
|
||||
<input
|
||||
id="trans-repeat-interval"
|
||||
placeholder="ms"
|
||||
value="1000"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s5">
|
||||
<!-- <p id="trans-log-p">transmit log</p> -->
|
||||
<div class="row" id="trans-log-row">
|
||||
<div class="input-field col s9">
|
||||
<textarea
|
||||
id="trans-log-area"
|
||||
class="materialize-textarea"
|
||||
placeholder="History"
|
||||
readonly
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="col s3 right-btn">
|
||||
<a class="btn-small waves-effect red" id="trans-log-btn"
|
||||
><i class="material-icons">clear</i></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="advance-tab" class="col s12">
|
||||
<div class="col s4">
|
||||
<blockquote>Sign</blockquote>
|
||||
<div class="divider"></div>
|
||||
<div class="row config-row">
|
||||
<div class="col s12 config-item center">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Off
|
||||
<input type="checkbox" id="sign-switch" />
|
||||
<span class="lever"></span>
|
||||
On
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="input-field col s12">
|
||||
<input placeholder="Name" type="text" id="sign-name" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s4">
|
||||
<blockquote>Breakpoint</blockquote>
|
||||
<div class="divider"></div>
|
||||
<div class="row config-row">
|
||||
<div class="col s12 config-item center">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Off
|
||||
<input type="checkbox" id="breakpoint-switch" />
|
||||
<span class="lever"></span>
|
||||
On
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="input-field col s12">
|
||||
<input
|
||||
value="Error"
|
||||
id="breakpoint-on-text"
|
||||
type="text"
|
||||
maxlength="16"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row config-row">
|
||||
<div class="input-field col s12">
|
||||
<input value="5" id="breakpoint-after-lines" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s4">
|
||||
<blockquote>Bar Color</blockquote>
|
||||
<div class="divider"></div>
|
||||
<div class="center" id="color-panel">
|
||||
<input value="#ffba3a" id="bar-color-head" type="color" />
|
||||
<input value="#ff8a80" id="bar-color-middle" type="color" />
|
||||
<input value="#80d8ff" id="bar-color-tail" type="color" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="about-tab" class="col s12">
|
||||
<div class="row label-row">
|
||||
<div class="col s3 label-key">
|
||||
<p>Version</p>
|
||||
</div>
|
||||
<div class="col s3">
|
||||
<p id="app-version"></p>
|
||||
</div>
|
||||
<div class="col s3 label-key">
|
||||
<p>Licence</p>
|
||||
</div>
|
||||
<div class="col s3">
|
||||
<p>MIT</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row label-row">
|
||||
<div class="col s3 label-key">
|
||||
<p>Issue</p>
|
||||
</div>
|
||||
<div class="col s9">
|
||||
<p>
|
||||
<a href="https://github.com/xenkuo/comNG/issues" id="issue"
|
||||
>https://github.com/xenkuo/comNG/issues</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row label-row">
|
||||
<div class="col s3 label-key">
|
||||
<p>Introduction</p>
|
||||
</div>
|
||||
<div class="col s9">
|
||||
<p>
|
||||
<a
|
||||
href="https://xenkuo.github.io/2019-08-01-comNG"
|
||||
id="introduction"
|
||||
>comNG: a modern and powerful COM tool</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row label-row">
|
||||
<div class="col s3 label-key">
|
||||
<p>Highlight Syntax</p>
|
||||
</div>
|
||||
<div class="col s9">
|
||||
<p>
|
||||
<a
|
||||
href="https://xenkuo.github.io/2019-08-03-comNGLang"
|
||||
id="comnglang"
|
||||
>https://xenkuo.github.io/2019-08-03-comNGLang</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="bar-area">
|
||||
<div class="col s12" id="modem-signal-bar" hidden>
|
||||
<div class="col s1"></div>
|
||||
<a class="btn-small modem-btn col s1 disabled" id="cts-btn">CTS</a>
|
||||
<div class="col s1"></div>
|
||||
<a class="btn-small modem-btn col s1 disabled" id="dsr-btn">DSR</a>
|
||||
<div class="col s1"></div>
|
||||
<a class="btn-small modem-btn col s1 disabled" id="dcd-btn">DCD</a>
|
||||
<div class="col s1"></div>
|
||||
<div class="col s1"></div>
|
||||
|
||||
<a
|
||||
class="btn-small waves-effect z-depth-0 modem-btn col s1"
|
||||
id="rts-btn"
|
||||
>RTS</a
|
||||
>
|
||||
<div class="col s1"></div>
|
||||
<a
|
||||
class="btn-small waves-effect z-depth-0 modem-btn col s1"
|
||||
id="dtr-btn"
|
||||
>DTR</a
|
||||
>
|
||||
</div>
|
||||
<div class="col s2">
|
||||
<a id="menu-btn" class="waves-effect btn-floating btn-small"
|
||||
><i class="material-icons">menu</i></a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col s10">
|
||||
<div class="input-field col s3" id="baud-input">
|
||||
<div class="container">
|
||||
<select id="baud-select">
|
||||
<option value="1">9600</option>
|
||||
<option value="2">19200</option>
|
||||
<option value="3" selected>115200</option>
|
||||
<option value="4">921600</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s5" id="path-input">
|
||||
<select id="path-select">
|
||||
<option value="0">Select port ...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col s1">
|
||||
<a class="waves-effect btn-floating" id="refresh-btn"
|
||||
><i class="material-icons">refresh</i></a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col s1">
|
||||
<a class="waves-effect btn-floating btn-small red" id="clear-btn"
|
||||
><i class="material-icons">delete</i></a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="switch col s2">
|
||||
<label>
|
||||
<input type="checkbox" id="port-switch" />
|
||||
<span class="lever"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- You can also require other files to run in this process -->
|
||||
<script src="editor.js"></script>
|
||||
<script src="serialport.js"></script>
|
||||
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,536 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable no-undef */
|
||||
const { remote, shell, ipcRenderer, clipboard } = require("electron");
|
||||
const Store = require("electron-store");
|
||||
const appVersion = remote.app.getVersion();
|
||||
const appUpdaterUrl =
|
||||
"https://api.github.com/repos/xenkuo/comNG/releases/latest";
|
||||
|
||||
var M = require("materialize-css");
|
||||
M.AutoInit();
|
||||
|
||||
var config;
|
||||
var barHeight;
|
||||
var menuHeight;
|
||||
const store = new Store({
|
||||
projectVersion: appVersion,
|
||||
migrations: {
|
||||
"1.0.3": (store) => {
|
||||
store.delete("desert");
|
||||
store.delete("about");
|
||||
store.set("window.width", 600);
|
||||
store.set("window.height", 640);
|
||||
store.set("window.widthBefore", 600);
|
||||
store.set("window.heightBefore", 640);
|
||||
store.set("window.xBefore", 0);
|
||||
store.set("window.yBefore", 0);
|
||||
|
||||
store.set("menu.hidden", true);
|
||||
store.set("menu.tab", "general");
|
||||
|
||||
store.set("baudIndex", 2);
|
||||
store.set("pathIndex", 0);
|
||||
|
||||
store.set("general.hexmode", false);
|
||||
store.set("general.timestamp", false);
|
||||
store.set("general.customized", 9600);
|
||||
store.set("general.databitsIndex", 0);
|
||||
store.set("general.parityIndex", 0);
|
||||
store.set("general.stopbitsIndex", 0);
|
||||
store.set("general.flowcontrolIndex", 0);
|
||||
|
||||
store.set("transmit.eof", "\r\n");
|
||||
|
||||
store.set("advance.sign.switch", false);
|
||||
store.set("advance.sign.name", "");
|
||||
store.set("advance.breakpoint.switch", false);
|
||||
store.set("advance.breakpoint.onText", "Error");
|
||||
store.set("advance.breakpoint.afterLines", 5);
|
||||
store.set("advance.barColor.head", "#fafafa");
|
||||
store.set("advance.barColor.middle", "#fafafa");
|
||||
store.set("advance.barColor.tail", "#26a69a");
|
||||
},
|
||||
"1.0.4": (store) => {
|
||||
store.set("general.modemSignal", false);
|
||||
store.set(
|
||||
"general.fontFamily",
|
||||
"Consolas, 'SF Mono', Menlo, 'Lucida Console', 'Courier New', monospace"
|
||||
);
|
||||
store.set("general.fontSize", 12);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function configUpdate(key, value) {
|
||||
let keyArray = key.split(".");
|
||||
|
||||
if (keyArray.length === 1) {
|
||||
config[keyArray[0]] = value;
|
||||
} else if (keyArray.length === 2) {
|
||||
config[keyArray[0]][keyArray[1]] = value;
|
||||
} else if (keyArray.length === 3) {
|
||||
config[keyArray[0]][keyArray[1]][keyArray[2]] = value;
|
||||
} else {
|
||||
console.error("config key structure error");
|
||||
}
|
||||
|
||||
store.set(key, value);
|
||||
}
|
||||
|
||||
ipcRenderer.on("main-cmd", (event, arg) => {
|
||||
console.log(arg);
|
||||
switch (arg) {
|
||||
case "Clear": {
|
||||
let text = editor.getModel().getValue();
|
||||
clipboard.writeText(text);
|
||||
editor.getModel().setValue("");
|
||||
break;
|
||||
}
|
||||
case "Switch":
|
||||
document.getElementById("port-switch").click();
|
||||
break;
|
||||
case "Open":
|
||||
openFile();
|
||||
break;
|
||||
case "Save":
|
||||
saveToFile();
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown cmds");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
window.onload = () => {
|
||||
config = store.store;
|
||||
document.getElementById("menu-area").hidden = config.menu.hidden;
|
||||
|
||||
barHeight = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--bar-height")
|
||||
.trim()
|
||||
.split("px")[0];
|
||||
barHeight = parseInt(barHeight);
|
||||
menuHeight = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--menu-height")
|
||||
.trim()
|
||||
.split("px")[0];
|
||||
menuHeight = parseInt(menuHeight);
|
||||
|
||||
let nav = document.getElementById("nav-area");
|
||||
let bar = document.getElementById("bar-area");
|
||||
let menu = document.getElementById("menu-area");
|
||||
let editor = document.getElementById("editor-area");
|
||||
|
||||
editor.style.height =
|
||||
window.innerHeight -
|
||||
nav.offsetHeight -
|
||||
bar.offsetHeight -
|
||||
menu.offsetHeight +
|
||||
"px";
|
||||
|
||||
M.Tabs.getInstance(document.getElementById("menu-tabs")).select(
|
||||
config.menu.tab
|
||||
);
|
||||
|
||||
let baudSelect = document.getElementById("baud-select");
|
||||
baudSelect.options[0].text = config.general.customized;
|
||||
baudSelect.selectedIndex = config.baudIndex;
|
||||
M.FormSelect.init(baudSelect);
|
||||
|
||||
portUpdate();
|
||||
|
||||
document.getElementById("hexmode-switch").checked = config.general.hexmode;
|
||||
document.getElementById("timestamp-switch").checked =
|
||||
config.general.timestamp;
|
||||
document.getElementById("modem-signal-switch").checked =
|
||||
config.general.modemSignal;
|
||||
if (config.general.modemSignal === true) {
|
||||
document.getElementById("modem-signal-bar").hidden = false;
|
||||
} else {
|
||||
document.getElementById("modem-signal-bar").hidden = true;
|
||||
}
|
||||
document.getElementById("customized").value = config.general.customized;
|
||||
|
||||
let databits = document.getElementById("databits-select");
|
||||
databits.selectedIndex = config.general.databitsIndex;
|
||||
M.FormSelect.init(databits);
|
||||
let parity = document.getElementById("parity-select");
|
||||
parity.selectedIndex = config.general.parityIndex;
|
||||
M.FormSelect.init(parity);
|
||||
let stopbits = document.getElementById("stopbits-select");
|
||||
stopbits.selectedIndex = config.general.stopbitsIndex;
|
||||
M.FormSelect.init(stopbits);
|
||||
let flowcontrol = document.getElementById("flowcontrol-select");
|
||||
flowcontrol.selectedIndex = config.general.flowcontrolIndex;
|
||||
M.FormSelect.init(flowcontrol);
|
||||
|
||||
document.getElementById("editor-font-family").value =
|
||||
config.general.fontFamily;
|
||||
document.getElementById("editor-font-size").value = config.general.fontSize;
|
||||
|
||||
document.getElementById("breakpoint-switch").checked =
|
||||
config.advance.breakpoint.switch;
|
||||
document.getElementById("breakpoint-on-text").value =
|
||||
config.advance.breakpoint.onText;
|
||||
document.getElementById("breakpoint-after-lines").value =
|
||||
config.advance.breakpoint.afterLines;
|
||||
|
||||
document.getElementById("sign-switch").checked = config.advance.sign.switch;
|
||||
document.getElementById("sign-name").value = config.advance.sign.name;
|
||||
|
||||
document.getElementById("bar-color-head").value =
|
||||
config.advance.barColor.head;
|
||||
document.getElementById("bar-color-middle").value =
|
||||
config.advance.barColor.middle;
|
||||
document.getElementById("bar-color-tail").value =
|
||||
config.advance.barColor.tail;
|
||||
|
||||
document.documentElement.style.setProperty(
|
||||
"--bar-color-head",
|
||||
config.advance.barColor.head
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--bar-color-middle",
|
||||
config.advance.barColor.middle
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--bar-color-tail",
|
||||
config.advance.barColor.tail
|
||||
);
|
||||
|
||||
document.getElementById("app-version").innerHTML = appVersion;
|
||||
console.log("Current Version: ", appVersion);
|
||||
|
||||
fetch(appUpdaterUrl)
|
||||
.then((data) => {
|
||||
return data.json();
|
||||
})
|
||||
.then((res) => {
|
||||
let latest = res.tag_name.split("v")[1];
|
||||
if (latest !== appVersion) {
|
||||
const dialogOpts = {
|
||||
type: "info",
|
||||
buttons: ["Download Now", "Later"],
|
||||
message: "Version: " + latest + " released!",
|
||||
detail: res.body,
|
||||
};
|
||||
|
||||
dialog.showMessageBox(dialogOpts).then((returnValue) => {
|
||||
if (returnValue.response === 0) shell.openExternal(res.html_url);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.onresize = () => {
|
||||
configUpdate("window.width", window.innerWidth);
|
||||
configUpdate("window.height", window.innerHeight);
|
||||
|
||||
let nav = document.getElementById("nav-area");
|
||||
let bar = document.getElementById("bar-area");
|
||||
let menu = document.getElementById("menu-area");
|
||||
let editor = document.getElementById("editor-area");
|
||||
|
||||
editor.style.height =
|
||||
window.innerHeight -
|
||||
nav.offsetHeight -
|
||||
bar.offsetHeight -
|
||||
menu.offsetHeight +
|
||||
"px";
|
||||
};
|
||||
|
||||
document.onkeydown = function (e) {
|
||||
e = e || window.event;
|
||||
switch (e.which || e.keyCode) {
|
||||
case 13:
|
||||
console.log("hello world 13");
|
||||
if (document.activeElement.id === "trans-data") {
|
||||
document.getElementById("trans-send-btn").click();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log("unknown event");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// prevent text select for double click action
|
||||
document.getElementById("nav-area").onmousedown = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
document.getElementById("nav-area").ondblclick = () => {
|
||||
if (
|
||||
window.innerWidth === screen.width ||
|
||||
window.innerHeight === screen.height
|
||||
) {
|
||||
window.resizeTo(config.window.widthBefore, config.window.heightBefore);
|
||||
window.moveTo(config.window.xBefore, config.window.yBefore);
|
||||
} else {
|
||||
configUpdate("window.widthBefore", window.innerWidth);
|
||||
configUpdate("window.heightBefore", window.innerHeight);
|
||||
configUpdate("window.xBefore", window.screenX);
|
||||
configUpdate("window.yBefore", window.screenY);
|
||||
|
||||
window.resizeTo(screen.width, screen.height);
|
||||
window.moveTo(0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("logo").onclick = () => {
|
||||
shell.openExternal("https://github.com/xenkuo/comNG");
|
||||
};
|
||||
|
||||
document.getElementById("min-btn").onclick = () => {
|
||||
remote.getCurrentWindow().minimize();
|
||||
};
|
||||
|
||||
document.getElementById("max-btn").onclick = () => {
|
||||
configUpdate("window.widthBefore", window.innerWidth);
|
||||
configUpdate("window.heightBefore", window.innerHeight);
|
||||
configUpdate("window.xBefore", window.screenX);
|
||||
configUpdate("window.yBefore", window.screenY);
|
||||
|
||||
window.resizeTo(screen.width, screen.height);
|
||||
window.moveTo(0, 0);
|
||||
};
|
||||
|
||||
document.getElementById("close-btn").onclick = () => {
|
||||
window.close();
|
||||
};
|
||||
|
||||
document.getElementById("menu-btn").onclick = () => {
|
||||
let menu = document.getElementById("menu-area");
|
||||
let editor = document.getElementById("editor-area");
|
||||
|
||||
if (menu.hidden === true) {
|
||||
editor.style.height = editor.offsetHeight - menuHeight + "px";
|
||||
menu.hidden = false;
|
||||
|
||||
configUpdate("menu.hidden", false);
|
||||
} else {
|
||||
editor.style.height = editor.offsetHeight + menuHeight + "px";
|
||||
menu.hidden = true;
|
||||
|
||||
configUpdate("menu.hidden", true);
|
||||
}
|
||||
};
|
||||
|
||||
document.body.onclick = (e) => {
|
||||
// don't process fake click
|
||||
if (e.isTrusted === false) return;
|
||||
// don't process when click on menu-btn
|
||||
if (e.target.parentNode.id === "menu-btn") return;
|
||||
// don't process when we are in Transmit tab
|
||||
if (document.getElementById("menu-tabs").M_Tabs.index === 1) return;
|
||||
|
||||
let pos = e.clientY;
|
||||
let range = document.body.offsetHeight;
|
||||
|
||||
if (pos > range - barHeight || pos < range - barHeight - menuHeight) {
|
||||
let menu = document.getElementById("menu-area");
|
||||
let editor = document.getElementById("editor-area");
|
||||
|
||||
if (menu.hidden === false) {
|
||||
editor.style.height = editor.offsetHeight + menuHeight + "px";
|
||||
menu.hidden = true;
|
||||
|
||||
configUpdate("menu.hidden", true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("menu-tabs").onclick = () => {
|
||||
let tabs = M.Tabs.getInstance(document.getElementById("menu-tabs"));
|
||||
|
||||
configUpdate("menu.tab", tabs.$content[0].id);
|
||||
};
|
||||
|
||||
document.getElementById("hexmode-switch").onclick = (e) => {
|
||||
configUpdate("general.hexmode", e.target.checked);
|
||||
};
|
||||
|
||||
document.getElementById("timestamp-switch").onclick = (e) => {
|
||||
configUpdate("general.timestamp", e.target.checked);
|
||||
};
|
||||
|
||||
document.getElementById("modem-signal-switch").onclick = (e) => {
|
||||
let state = e.target.checked;
|
||||
|
||||
configUpdate("general.modemSignal", state);
|
||||
if (state === true) {
|
||||
document.getElementById("modem-signal-bar").hidden = false;
|
||||
} else {
|
||||
document.getElementById("modem-signal-bar").hidden = true;
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("customized").onblur = (e) => {
|
||||
let customized = parseInt(e.target.value);
|
||||
|
||||
if (isNaN(customized) === true) customized = 9600;
|
||||
configUpdate("general.customized", customized);
|
||||
|
||||
let baudSelect = document.getElementById("baud-select");
|
||||
|
||||
baudSelect.options[0].text = customized;
|
||||
M.FormSelect.init(baudSelect);
|
||||
};
|
||||
|
||||
document.getElementById("databits-select").onchange = (e) => {
|
||||
configUpdate("general.databitsIndex", e.target.selectedIndex);
|
||||
};
|
||||
|
||||
document.getElementById("parity-select").onchange = (e) => {
|
||||
configUpdate("general.parityIndex", e.target.selectedIndex);
|
||||
};
|
||||
|
||||
document.getElementById("stopbits-select").onchange = (e) => {
|
||||
configUpdate("general.stopbitsIndex", e.target.selectedIndex);
|
||||
};
|
||||
|
||||
document.getElementById("flowcontrol-select").onchange = (e) => {
|
||||
configUpdate("general.flowcontrolIndex", e.target.selectedIndex);
|
||||
};
|
||||
|
||||
document.getElementById("sign-switch").onclick = (e) => {
|
||||
configUpdate("advance.sign.switch", e.target.checked);
|
||||
};
|
||||
|
||||
document.getElementById("sign-name").onblur = (e) => {
|
||||
configUpdate("advance.sign.name", e.target.value);
|
||||
};
|
||||
|
||||
let transRepeatTimer = undefined;
|
||||
document.getElementById("trans-eof-select").onchange = (e) => {
|
||||
let index = e.target.selectedIndex;
|
||||
let eof = "\r\n";
|
||||
switch (index) {
|
||||
case 1:
|
||||
eof = "\r";
|
||||
break;
|
||||
case 2:
|
||||
eof = "\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
configUpdate("transmit.eof", eof);
|
||||
};
|
||||
|
||||
// document.getElementById('trans-clear-btn').onclick = () => {
|
||||
// document.getElementById('trans-data').value = ''
|
||||
// }
|
||||
|
||||
document.getElementById("trans-send-btn").onclick = () => {
|
||||
const p = document.getElementById("trans-log-area");
|
||||
let data = document.getElementById("trans-data").value;
|
||||
|
||||
// if (data.trim() === "") return;
|
||||
data.trim();
|
||||
data += config.transmit.eof;
|
||||
if (serialWrite(data) === false) return;
|
||||
|
||||
p.value += "\n" + document.getElementById("trans-data").value;
|
||||
M.updateTextFields(p);
|
||||
M.textareaAutoResize(p);
|
||||
p.scrollTop = p.scrollHeight;
|
||||
|
||||
if (document.getElementById("trans-repeat-switch").checked === true) {
|
||||
if (transRepeatTimer !== undefined) clearInterval(transRepeatTimer);
|
||||
|
||||
let interval = document.getElementById("trans-repeat-interval").value;
|
||||
interval = parseInt(interval);
|
||||
if (isNaN(interval) === true) interval = 1000;
|
||||
|
||||
transRepeatTimer = setInterval(() => {
|
||||
serialWrite(data);
|
||||
}, interval);
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("trans-repeat-switch").onchange = (e) => {
|
||||
let checked = e.target.checked;
|
||||
|
||||
if (checked === false && transRepeatTimer !== undefined)
|
||||
clearInterval(transRepeatTimer);
|
||||
};
|
||||
|
||||
document.getElementById("trans-log-btn").onclick = () => {
|
||||
let p = document.getElementById("trans-log-area");
|
||||
|
||||
p.value = "";
|
||||
M.updateTextFields(p);
|
||||
M.textareaAutoResize(p);
|
||||
};
|
||||
|
||||
document.getElementById("bar-color-head").oninput = (e) => {
|
||||
let color = e.target.value;
|
||||
|
||||
document.documentElement.style.setProperty("--bar-color-head", color);
|
||||
configUpdate("advance.barColor.head", color);
|
||||
};
|
||||
|
||||
document.getElementById("bar-color-middle").oninput = (e) => {
|
||||
let color = e.target.value;
|
||||
|
||||
document.documentElement.style.setProperty("--bar-color-middle", color);
|
||||
configUpdate("advance.barColor.middle", color);
|
||||
};
|
||||
|
||||
document.getElementById("bar-color-tail").oninput = (e) => {
|
||||
let color = e.target.value;
|
||||
|
||||
document.documentElement.style.setProperty("--bar-color-tail", color);
|
||||
configUpdate("advance.barColor.tail", color);
|
||||
};
|
||||
|
||||
document.getElementById("issue").onclick = (e) => {
|
||||
console.log("licence click", e);
|
||||
e.preventDefault();
|
||||
shell.openExternal(e.target.href);
|
||||
};
|
||||
|
||||
document.getElementById("introduction").onclick = (e) => {
|
||||
console.log("licence click", e);
|
||||
e.preventDefault();
|
||||
shell.openExternal(e.target.href);
|
||||
};
|
||||
|
||||
document.getElementById("comnglang").onclick = (e) => {
|
||||
console.log("licence click", e);
|
||||
e.preventDefault();
|
||||
shell.openExternal(e.target.href);
|
||||
};
|
||||
|
||||
document.getElementById("baud-select").onchange = (e) => {
|
||||
configUpdate("baudIndex", e.target.selectedIndex);
|
||||
};
|
||||
|
||||
// document.getElementById("path-input").addEventListener(
|
||||
// "click",
|
||||
// e => {
|
||||
// console.log("path select add event");
|
||||
// if (e.isTrusted === false) return;
|
||||
// console.log("path x");
|
||||
// e.stopPropagation();
|
||||
// // portUpdate();
|
||||
// setTimeout(() => {
|
||||
// try {
|
||||
// let evt = document.createEvent("Event");
|
||||
// evt.initEvent("click", true, true);
|
||||
// document.getElementById("path-select").dispatchEvent(evt);
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// }, 5000);
|
||||
// },
|
||||
// true
|
||||
// );
|
||||
|
||||
document.getElementById("path-select").onchange = (e) => {
|
||||
configUpdate("pathIndex", e.target.selectedIndex);
|
||||
};
|
||||
|
||||
document.getElementById("refresh-btn").onclick = portUpdate;
|
|
@ -0,0 +1,106 @@
|
|||
const { app, BrowserWindow } = require("electron");
|
||||
const path = require("path");
|
||||
const Shortcut = require("electron-localshortcut");
|
||||
|
||||
const Store = require("electron-store");
|
||||
|
||||
const store = new Store();
|
||||
|
||||
const widthDefault = 600;
|
||||
const widthMin = 600;
|
||||
const widthMax = 1024;
|
||||
const heightDefault = 640;
|
||||
const heightMin = 400;
|
||||
const heightMax = 768;
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require("electron-squirrel-startup")) {
|
||||
// eslint-disable-line global-require
|
||||
app.quit();
|
||||
}
|
||||
|
||||
// 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.
|
||||
let mainWindow;
|
||||
|
||||
const createWindow = () => {
|
||||
let width = store.get("window.width", widthDefault);
|
||||
let height = store.get("window.height", heightDefault);
|
||||
|
||||
if (width > widthMax) width = widthMax;
|
||||
if (height > heightMax) height = heightMax;
|
||||
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: width,
|
||||
minWidth: widthMin,
|
||||
height: height,
|
||||
minHeight: heightMin,
|
||||
alwaysOnTop: false,
|
||||
frame: false,
|
||||
icon: path.join(__dirname, "../image/logo.png"),
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
preload: path.join(__dirname, "./preload.js"),
|
||||
},
|
||||
});
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile(path.join(__dirname, "index.html"));
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on("closed", () => {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
Shortcut.register(mainWindow, "CmdOrCtrl+X", () => {
|
||||
console.log("You pressed cmd/ctrl x");
|
||||
mainWindow.webContents.send("main-cmd", "Clear");
|
||||
});
|
||||
|
||||
Shortcut.register(mainWindow, "CmdOrCtrl+D", () => {
|
||||
console.log("You pressed cmd/ctrl d");
|
||||
mainWindow.webContents.send("main-cmd", "Switch");
|
||||
});
|
||||
|
||||
Shortcut.register(mainWindow, "CmdOrCtrl+O", () => {
|
||||
console.log("You pressed cmd/ctrl o");
|
||||
mainWindow.webContents.send("main-cmd", "Open");
|
||||
});
|
||||
|
||||
Shortcut.register(mainWindow, "CmdOrCtrl+S", () => {
|
||||
console.log("You pressed cmd/ctrl s");
|
||||
mainWindow.webContents.send("main-cmd", "Save");
|
||||
});
|
||||
};
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on("ready", createWindow);
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on("window-all-closed", () => {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and import them here.
|
|
@ -0,0 +1,8 @@
|
|||
// All of the Node.js APIs are available in the preload process.
|
||||
// It has the same sandbox as a Chrome extension.
|
||||
|
||||
// const M = require('materialize-css')
|
||||
|
||||
// window.addEventListener('DOMContentLoaded', () => {
|
||||
// M.AutoInit()
|
||||
// })
|
|
@ -0,0 +1,237 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable no-undef */
|
||||
const serial = require("serialport");
|
||||
|
||||
var port, modemSignalTimer;
|
||||
var modemSignal = {
|
||||
cts: false,
|
||||
dsr: false,
|
||||
dcd: false,
|
||||
rts: true,
|
||||
dtr: true,
|
||||
};
|
||||
|
||||
function portUpdate() {
|
||||
let pSelect = document.getElementById("path-select");
|
||||
pSelect.options.length = 0;
|
||||
|
||||
serial
|
||||
.list()
|
||||
.then((ports) => {
|
||||
ports.forEach((item, index) => {
|
||||
console.log(item, index);
|
||||
pSelect.options.add(new Option(item.path, index));
|
||||
if (index === config.pathIndex) pSelect.selectedIndex = index;
|
||||
});
|
||||
M.FormSelect.init(pSelect);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
function modemSignalTimerHandle() {
|
||||
if (port === undefined || port.isOpen === false)
|
||||
return clearInterval(modemSignalTimer);
|
||||
|
||||
port.get((e, signal) => {
|
||||
if (e) return console.error(e);
|
||||
|
||||
if (signal.cts !== modemSignal.cts) {
|
||||
modemSignal.cts = signal.cts;
|
||||
if (signal.cts === false) {
|
||||
document.getElementById("cts-btn").style.cssText =
|
||||
"background-color: #dfdfdf !important";
|
||||
} else {
|
||||
document.getElementById("cts-btn").style.cssText =
|
||||
"background-color: #26a69a !important";
|
||||
}
|
||||
}
|
||||
if (signal.dsr !== modemSignal.dsr) {
|
||||
modemSignal.dsr = signal.dsr;
|
||||
if (signal.dsr === false) {
|
||||
document.getElementById("dsr-btn").style.cssText =
|
||||
"background-color: #dfdfdf !important";
|
||||
} else {
|
||||
document.getElementById("dsr-btn").style.cssText =
|
||||
"background-color: #26a69a !important";
|
||||
}
|
||||
}
|
||||
if (signal.dcd !== modemSignal.dcd) {
|
||||
modemSignal.dcd = signal.dcd;
|
||||
if (signal.dcd === false) {
|
||||
document.getElementById("dcd-btn").style.cssText =
|
||||
"background-color: #dfdfdf !important";
|
||||
} else {
|
||||
document.getElementById("dcd-btn").style.cssText =
|
||||
"background-color: #26a69a !important";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function modemSignalReset() {
|
||||
modemSignal.cts = false;
|
||||
modemSignal.dsr = false;
|
||||
modemSignal.dcd = false;
|
||||
modemSignal.rts = true;
|
||||
modemSignal.dtr = true;
|
||||
document.getElementById("cts-btn").style.cssText =
|
||||
"background-color: #dfdfdf !important";
|
||||
document.getElementById("dsr-btn").style.cssText =
|
||||
"background-color: #dfdfdf !important";
|
||||
document.getElementById("dcd-btn").style.cssText =
|
||||
"background-color: #dfdfdf !important";
|
||||
document.getElementById("rts-btn").style.backgroundColor = "";
|
||||
document.getElementById("dtr-btn").style.backgroundColor = "";
|
||||
}
|
||||
|
||||
function serialGetOptions() {
|
||||
let openOptions = {};
|
||||
|
||||
let baudRate = parseInt(
|
||||
document.getElementById("baud-select").options[config.baudIndex].text
|
||||
);
|
||||
if (isNaN(baudRate) === true) baudRate = 115200;
|
||||
openOptions.baudRate = baudRate;
|
||||
|
||||
let dataBits = parseInt(
|
||||
document.getElementById("databits-select").options[
|
||||
config.general.databitsIndex
|
||||
].text
|
||||
);
|
||||
if (isNaN(dataBits) === true) dataBits = 8;
|
||||
openOptions.dataBits = dataBits;
|
||||
|
||||
let parity = document
|
||||
.getElementById("parity-select")
|
||||
.options[config.general.parityIndex].text.toLowerCase();
|
||||
openOptions.parity = parity;
|
||||
|
||||
let stopBits = parseInt(
|
||||
document.getElementById("stopbits-select").options[
|
||||
config.general.stopbitsIndex
|
||||
].text
|
||||
);
|
||||
if (isNaN(stopBits) === true) stopBits = 1;
|
||||
openOptions.stopBits = stopBits;
|
||||
|
||||
let flowcontrol = document
|
||||
.getElementById("flowcontrol-select")
|
||||
.options[config.general.flowcontrolIndex].text.toLowerCase();
|
||||
openOptions[flowcontrol] = true;
|
||||
|
||||
openOptions.autoOpen = true;
|
||||
|
||||
return openOptions;
|
||||
}
|
||||
|
||||
function toast(text) {
|
||||
M.toast({ html: text, displayLength: 1000 });
|
||||
// alert(text)
|
||||
}
|
||||
|
||||
function serialClose() {
|
||||
port === undefined ? null : port.close();
|
||||
}
|
||||
|
||||
function serialWrite(data) {
|
||||
if (port === undefined || port.isOpen === false) {
|
||||
toast("Error: No port opened, cannot write");
|
||||
if (transRepeatTimer !== undefined) clearInterval(transRepeatTimer);
|
||||
return false;
|
||||
}
|
||||
|
||||
port.write(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
document.getElementById("port-switch").onclick = (e) => {
|
||||
if (e.target.checked === true) {
|
||||
let pathSelect = document.getElementById("path-select");
|
||||
let portPath = pathSelect.options[pathSelect.selectedIndex].label;
|
||||
|
||||
port = new serial(portPath, serialGetOptions());
|
||||
|
||||
port.on("open", () => {
|
||||
console.log("port open event");
|
||||
if (modemSignalTimer !== undefined) clearInterval(modemSignalTimer);
|
||||
if (config.general.modemSignal === true) {
|
||||
modemSignalTimer = setInterval(modemSignalTimerHandle, 100);
|
||||
}
|
||||
port.set({rts: true, dtr: true}, (e) => {
|
||||
if (e !== null) console.error(e);
|
||||
});
|
||||
});
|
||||
|
||||
port.on("error", (e) => {
|
||||
toast(e.message);
|
||||
document.getElementById("port-switch").checked = false;
|
||||
if (transRepeatTimer !== undefined) clearInterval(transRepeatTimer);
|
||||
if (modemSignalTimer !== undefined) clearInterval(modemSignalTimer);
|
||||
});
|
||||
|
||||
port.on("close", (e) => {
|
||||
console.log("port close event");
|
||||
|
||||
if (e !== null) console.error(e);
|
||||
document.getElementById("port-switch").checked = false;
|
||||
if (transRepeatTimer !== undefined) clearInterval(transRepeatTimer);
|
||||
if (modemSignalTimer !== undefined) {
|
||||
clearInterval(modemSignalTimer);
|
||||
}
|
||||
|
||||
modemSignalReset();
|
||||
});
|
||||
|
||||
port.on("drain", () => {
|
||||
toast("Error: Write failed, please try again");
|
||||
});
|
||||
|
||||
port.on("data", processSerialData);
|
||||
} else {
|
||||
if (port === undefined || port.isOpen === false) {
|
||||
document.getElementById("port-switch").checked = false;
|
||||
} else {
|
||||
port.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("rts-btn").onclick = (e) => {
|
||||
console.log("rts click");
|
||||
if (port === undefined || port.isOpen === false) return;
|
||||
|
||||
if (modemSignal.rts === true) {
|
||||
modemSignal.rts = false;
|
||||
e.target.style.backgroundColor = "#dfdfdf";
|
||||
port.set({ rts: false }, (e) => {
|
||||
if (e !== null) console.error(e);
|
||||
});
|
||||
} else {
|
||||
modemSignal.rts = true;
|
||||
e.target.style.backgroundColor = "";
|
||||
port.set({ rts: modemSignal.rts, dtr: modemSignal.dtr }, (e) => {
|
||||
if (e !== null) console.error(e);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("dtr-btn").onclick = (e) => {
|
||||
console.log("dtr click");
|
||||
if (port === undefined || port.isOpen === false) return;
|
||||
|
||||
if (modemSignal.dtr === true) {
|
||||
modemSignal.dtr = false;
|
||||
e.target.style.backgroundColor = "#dfdfdf";
|
||||
port.set({ dtr: false }, (e) => {
|
||||
if (e !== null) console.error(e);
|
||||
});
|
||||
} else {
|
||||
modemSignal.dtr = true;
|
||||
e.target.style.backgroundColor = "";
|
||||
port.set({ rts: modemSignal.rts, dtr: true }, (e) => {
|
||||
if (e !== null) console.error(e);
|
||||
});
|
||||
}
|
||||
};
|