mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-22 03:18:01 +00:00
Compare commits
190 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c6fd86dca6 | ||
![]() |
7f55d6f1e2 | ||
![]() |
129a7c1a09 | ||
![]() |
4969c4e2fc | ||
![]() |
78a74ffe1b | ||
![]() |
565c675ce1 | ||
![]() |
a25a13188d | ||
![]() |
2daf85f753 | ||
![]() |
3189258fbb | ||
![]() |
d678bf68c5 | ||
![]() |
b48a335aed | ||
![]() |
71488e749a | ||
![]() |
33bb3b0722 | ||
![]() |
46b4288c98 | ||
![]() |
9477117236 | ||
![]() |
33e048238e | ||
![]() |
5895d42444 | ||
![]() |
5d6abca503 | ||
![]() |
8ca91af927 | ||
![]() |
c886fd6915 | ||
![]() |
e403ca6eff | ||
![]() |
8f1b401137 | ||
![]() |
0952899204 | ||
![]() |
a11c643e41 | ||
![]() |
17fbdeafac | ||
![]() |
06e09f3a45 | ||
![]() |
4fa63aa939 | ||
![]() |
46622cd5d9 | ||
![]() |
6d3aace1c9 | ||
![]() |
e3a569be18 | ||
![]() |
2a256ef2bd | ||
![]() |
4f3fcc8b22 | ||
![]() |
045cc0d243 | ||
![]() |
51e33abbe6 | ||
![]() |
4900c043ca | ||
![]() |
09d55979ce | ||
![]() |
5d431fa9cf | ||
![]() |
113573b2d2 | ||
![]() |
2c3d93608b | ||
![]() |
d38af18582 | ||
![]() |
140f7c51f4 | ||
![]() |
ffa4350420 | ||
![]() |
99737323de | ||
![]() |
e9e2429632 | ||
![]() |
07597ac79a | ||
![]() |
248ec60612 | ||
![]() |
726847d5df | ||
![]() |
6065a95132 | ||
![]() |
e1259475d2 | ||
![]() |
ee018e7c02 | ||
![]() |
db43381f0d | ||
![]() |
28c58d4ec0 | ||
![]() |
68efe2b3c4 | ||
![]() |
795979be07 | ||
![]() |
c87a1b92d3 | ||
![]() |
2548ad6605 | ||
![]() |
b6519c6626 | ||
![]() |
2b8bb47aed | ||
![]() |
bbf332171e | ||
![]() |
ef5c9b52a5 | ||
![]() |
4632523d70 | ||
![]() |
56b996e6e4 | ||
![]() |
0ca0996493 | ||
![]() |
e1a8e72742 | ||
![]() |
50959f4490 | ||
![]() |
baaebb402e | ||
![]() |
9e862772eb | ||
![]() |
6cb5505ded | ||
![]() |
eb0d8615e1 | ||
![]() |
f2885c2fce | ||
![]() |
c04018bc70 | ||
![]() |
3e5032ca8b | ||
![]() |
a5014243d9 | ||
![]() |
2692eb141c | ||
![]() |
b447f1d52e | ||
![]() |
606b9af3f1 | ||
![]() |
3deab9af24 | ||
![]() |
afab0c5cde | ||
![]() |
e1a03f0dfb | ||
![]() |
bfe8dfab02 | ||
![]() |
a4335edc07 | ||
![]() |
8613698be9 | ||
![]() |
731ddc3e28 | ||
![]() |
2303e32256 | ||
![]() |
9064f123b3 | ||
![]() |
9d3ee4a612 | ||
![]() |
20602eed6d | ||
![]() |
f2cd86738c | ||
![]() |
fc55446342 | ||
![]() |
dbc12c06cb | ||
![]() |
efb551cd94 | ||
![]() |
a87c1aa864 | ||
![]() |
555f55592a | ||
![]() |
ab46739986 | ||
![]() |
7ac7958462 | ||
![]() |
46d5dace8f | ||
![]() |
aace3f42d0 | ||
![]() |
c4297f2b2b | ||
![]() |
3c90e904fc | ||
![]() |
f8c8065e4a | ||
![]() |
d45a5a35d8 | ||
![]() |
a9c6b868fb | ||
![]() |
ffe8168f0f | ||
![]() |
d1f5ebd546 | ||
![]() |
2e8b465b3f | ||
![]() |
4a535c94a6 | ||
![]() |
531d47cbd1 | ||
![]() |
7a2491fe49 | ||
![]() |
2773c61677 | ||
![]() |
788b063384 | ||
![]() |
0e112899df | ||
![]() |
d8c635bc1d | ||
![]() |
dfe55b94ff | ||
![]() |
6b4b6b522f | ||
![]() |
0d65fe348b | ||
![]() |
e4b7693685 | ||
![]() |
566aa83fa9 | ||
![]() |
eb2d88eac2 | ||
![]() |
316b77ec7b | ||
![]() |
a4dc6832f3 | ||
![]() |
7a895dda1a | ||
![]() |
b2fc016aa8 | ||
![]() |
32096ea4fd | ||
![]() |
30fd36ed26 | ||
![]() |
afdf09076a | ||
![]() |
04a0a0cc64 | ||
![]() |
fda4d2dcef | ||
![]() |
2c341e23b5 | ||
![]() |
953b06f43f | ||
![]() |
e1cc1d56ea | ||
![]() |
df2f4d4a6c | ||
![]() |
092820173f | ||
![]() |
58c7c23bd8 | ||
![]() |
920afe450a | ||
![]() |
e0eedca7c9 | ||
![]() |
3edcce29fa | ||
![]() |
e14c7fec10 | ||
![]() |
b3ed62244d | ||
![]() |
ff1bfa990c | ||
![]() |
bd3463880e | ||
![]() |
95a61ec369 | ||
![]() |
1b29797a81 | ||
![]() |
10b21ee085 | ||
![]() |
f5ffdc1707 | ||
![]() |
b3f17b84ff | ||
![]() |
1a332128e3 | ||
![]() |
b5db7e80d0 | ||
![]() |
5bba719624 | ||
![]() |
09d6838b08 | ||
![]() |
325d2dc2a4 | ||
![]() |
94c7e2b5c3 | ||
![]() |
9da2e8d489 | ||
![]() |
ff1c5df30b | ||
![]() |
159adf9911 | ||
![]() |
994a94f8f1 | ||
![]() |
571db20523 | ||
![]() |
a422d62db0 | ||
![]() |
feb2a52b37 | ||
![]() |
267ca2e95c | ||
![]() |
9691a932ac | ||
![]() |
94d51c029e | ||
![]() |
efd7b2ca2b | ||
![]() |
ed88784431 | ||
![]() |
e48f844ea0 | ||
![]() |
fd21be5408 | ||
![]() |
f4c2d01df8 | ||
![]() |
4828bb4df7 | ||
![]() |
037e91508b | ||
![]() |
735010dd1f | ||
![]() |
11364dd788 | ||
![]() |
0eced6f4a9 | ||
![]() |
3157c89be3 | ||
![]() |
a7f909d06a | ||
![]() |
527b55a46e | ||
![]() |
a610306f29 | ||
![]() |
3daf0b394e | ||
![]() |
f151928b6b | ||
![]() |
041a3ce2b6 | ||
![]() |
6348e7b8f0 | ||
![]() |
8be72618e6 | ||
![]() |
a50c7cb474 | ||
![]() |
8cef4e5cf9 | ||
![]() |
9705a1b5b5 | ||
![]() |
dfa17948e2 | ||
![]() |
3607391d55 | ||
![]() |
e7f2f54f1b | ||
![]() |
b467c7de65 | ||
![]() |
c3e793229d | ||
![]() |
14bcfb01c2 | ||
![]() |
5d28369b8b |
@@ -243,6 +243,25 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nstefanou",
|
||||
"name": "nstefanou",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/51129173?v=4",
|
||||
"profile": "https://github.com/nstefanou",
|
||||
"contributions": [
|
||||
"code",
|
||||
"plugin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "orin220444",
|
||||
"name": "orin220444",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/30747229?v=4",
|
||||
"profile": "https://github.com/orin220444",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@@ -99,3 +99,8 @@ rules:
|
||||
'@typescript-eslint/restrict-template-expressions': off
|
||||
'@typescript-eslint/no-dynamic-delete': off
|
||||
'@typescript-eslint/prefer-nullish-coalescing': off
|
||||
'@typescript-eslint/prefer-readonly-parameter-types': off
|
||||
'@typescript-eslint/no-unsafe-member-access': off
|
||||
'@typescript-eslint/no-unsafe-call': off
|
||||
'@typescript-eslint/no-unsafe-return': off
|
||||
'@typescript-eslint/no-base-to-string': off # broken in typescript-eslint
|
||||
|
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
version: 10
|
||||
node-version: 10
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
version: 10
|
||||
node-version: 10
|
||||
|
||||
- name: Install deps
|
||||
run: |
|
||||
|
5
.github/workflows/linux.yml
vendored
5
.github/workflows/linux.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
version: 10
|
||||
node-version: 10
|
||||
|
||||
- name: Install deps
|
||||
run: |
|
||||
@@ -25,9 +25,6 @@ jobs:
|
||||
- name: Build native deps
|
||||
run: scripts/build-native.js
|
||||
|
||||
- name: Build typings
|
||||
run: yarn run build:typings
|
||||
|
||||
- name: Webpack
|
||||
run: yarn run build
|
||||
|
||||
|
5
.github/workflows/macos.yml
vendored
5
.github/workflows/macos.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
version: 10
|
||||
node-version: 10
|
||||
|
||||
- name: Install deps
|
||||
run: |
|
||||
@@ -25,9 +25,6 @@ jobs:
|
||||
- name: Build native deps
|
||||
run: scripts/build-native.js
|
||||
|
||||
- name: Build typings
|
||||
run: yarn run build:typings
|
||||
|
||||
- name: Webpack
|
||||
run: yarn run build
|
||||
|
||||
|
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
version: 10
|
||||
node-version: 10
|
||||
|
||||
- name: Build
|
||||
shell: powershell
|
||||
|
@@ -106,6 +106,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://www.boxmein.net"><img src="https://avatars1.githubusercontent.com/u/358714?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Kadak</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=boxmein" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/LeSeulArtichaut"><img src="https://avatars1.githubusercontent.com/u/38361244?v=4" width="100px;" alt=""/><br /><sub><b>LeSeulArtichaut</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=LeSeulArtichaut" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/CyrilTaylor"><img src="https://avatars0.githubusercontent.com/u/12631466?v=4" width="100px;" alt=""/><br /><sub><b>Cyril Taylor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=CyrilTaylor" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/nstefanou"><img src="https://avatars3.githubusercontent.com/u/51129173?v=4" width="100px;" alt=""/><br /><sub><b>nstefanou</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=nstefanou" title="Code">💻</a> <a href="#plugin-nstefanou" title="Plugin/utility libraries">🔌</a></td>
|
||||
<td align="center"><a href="https://github.com/orin220444"><img src="https://avatars3.githubusercontent.com/u/30747229?v=4" width="100px;" alt=""/><br /><sub><b>orin220444</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=orin220444" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@@ -8,17 +8,15 @@ html
|
||||
window.nodeRequire = require
|
||||
script(src='./preload.js')
|
||||
script(src='./bundle.js', defer)
|
||||
style#custom-css
|
||||
style.
|
||||
body { transition: 0.5s background; }
|
||||
body
|
||||
style#custom-css
|
||||
app-root
|
||||
.preload-logo
|
||||
div
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
sup α
|
||||
.progress
|
||||
.bar(style='width: 0%')
|
||||
|
||||
|
||||
|
@@ -31,7 +31,7 @@ export class Application {
|
||||
}
|
||||
}
|
||||
|
||||
init () {
|
||||
init (): void {
|
||||
electron.screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed'))
|
||||
}
|
||||
|
||||
@@ -52,20 +52,20 @@ export class Application {
|
||||
return window
|
||||
}
|
||||
|
||||
broadcast (event, ...args) {
|
||||
for (let window of this.windows) {
|
||||
broadcast (event: string, ...args): void {
|
||||
for (const window of this.windows) {
|
||||
window.send(event, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
async send (event, ...args) {
|
||||
async send (event: string, ...args): Promise<void> {
|
||||
if (!this.hasWindows()) {
|
||||
await this.newWindow()
|
||||
}
|
||||
this.windows.filter(w => !w.isDestroyed())[0].send(event, ...args)
|
||||
}
|
||||
|
||||
enableTray () {
|
||||
enableTray (): void {
|
||||
if (this.tray) {
|
||||
return
|
||||
}
|
||||
@@ -90,18 +90,18 @@ export class Application {
|
||||
this.tray.setToolTip(`Terminus ${app.getVersion()}`)
|
||||
}
|
||||
|
||||
disableTray () {
|
||||
disableTray (): void {
|
||||
if (this.tray) {
|
||||
this.tray.destroy()
|
||||
this.tray = null
|
||||
}
|
||||
}
|
||||
|
||||
hasWindows () {
|
||||
hasWindows (): boolean {
|
||||
return !!this.windows.length
|
||||
}
|
||||
|
||||
focus () {
|
||||
focus (): void {
|
||||
for (let window of this.windows) {
|
||||
window.show()
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { app } from 'electron'
|
||||
|
||||
export function parseArgs (argv, cwd) {
|
||||
export function parseArgs (argv: string[], cwd: string): any {
|
||||
if (argv[0].includes('node')) {
|
||||
argv = argv.slice(1)
|
||||
}
|
||||
|
@@ -119,7 +119,7 @@ export class Window {
|
||||
})
|
||||
}
|
||||
|
||||
setVibrancy (enabled: boolean, type?: string) {
|
||||
setVibrancy (enabled: boolean, type?: string): void {
|
||||
this.lastVibrancy = { enabled, type }
|
||||
if (process.platform === 'win32') {
|
||||
if (parseFloat(os.release()) >= 10) {
|
||||
@@ -140,22 +140,22 @@ export class Window {
|
||||
}
|
||||
}
|
||||
|
||||
show () {
|
||||
show (): void {
|
||||
this.window.show()
|
||||
}
|
||||
|
||||
focus () {
|
||||
focus (): void {
|
||||
this.window.focus()
|
||||
}
|
||||
|
||||
send (event, ...args) {
|
||||
send (event: string, ...args): void {
|
||||
if (!this.window) {
|
||||
return
|
||||
}
|
||||
this.window.webContents.send(event, ...args)
|
||||
}
|
||||
|
||||
isDestroyed () {
|
||||
isDestroyed (): boolean {
|
||||
return !this.window || this.window.isDestroyed()
|
||||
}
|
||||
|
||||
|
@@ -13,34 +13,35 @@
|
||||
"watch": "webpack --progress --color --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "7.2.8",
|
||||
"@angular/common": "7.2.8",
|
||||
"@angular/compiler": "7.2.8",
|
||||
"@angular/core": "7.2.8",
|
||||
"@angular/forms": "7.2.8",
|
||||
"@angular/platform-browser": "7.2.8",
|
||||
"@angular/platform-browser-dynamic": "7.2.8",
|
||||
"@ng-bootstrap/ng-bootstrap": "^4.2.2",
|
||||
"@angular/animations": "9.1.0",
|
||||
"@angular/common": "9.1.0",
|
||||
"@angular/compiler": "9.1.0",
|
||||
"@angular/core": "9.1.0",
|
||||
"@angular/forms": "9.1.0",
|
||||
"@angular/platform-browser": "9.1.0",
|
||||
"@angular/platform-browser-dynamic": "9.1.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.0.2",
|
||||
"devtron": "1.4.0",
|
||||
"electron-config": "2.0.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-is-dev": "1.1.0",
|
||||
"electron-updater": "^4.2.0",
|
||||
"electron-updater": "^4.2.5",
|
||||
"fontmanager-redux": "0.4.0",
|
||||
"js-yaml": "3.13.1",
|
||||
"keytar": "^5.2.0",
|
||||
"keytar": "^5.4.0",
|
||||
"mz": "^2.7.0",
|
||||
"ngx-toastr": "^10.2.0",
|
||||
"ngx-toastr": "^12.0.0",
|
||||
"node-pty": "^0.10.0-beta2",
|
||||
"npm": "6.9.0",
|
||||
"path": "0.12.7",
|
||||
"rxjs": "^6.5.4",
|
||||
"rxjs-compat": "^6.5.4",
|
||||
"yargs": "^15.1.0",
|
||||
"zone.js": "^0.8.29"
|
||||
"yargs": "^15.3.1",
|
||||
"zone.js": "^0.10.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"macos-native-processlist": "^1.0.2",
|
||||
"serialport": "^8.0.7",
|
||||
"windows-blurbehind": "^1.0.1",
|
||||
"windows-native-registry": "^1.0.17",
|
||||
"windows-process-tree": "^0.2.4",
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { NgModule } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -7,7 +8,7 @@ export function getRootModule (plugins: any[]) {
|
||||
const imports = [
|
||||
BrowserModule,
|
||||
...plugins,
|
||||
NgbModule.forRoot(),
|
||||
NgbModule,
|
||||
ToastrModule.forRoot({
|
||||
positionClass: 'toast-bottom-center',
|
||||
toastClass: 'toast',
|
||||
|
268
app/yarn.lock
268
app/yarn.lock
@@ -2,61 +2,111 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular/animations@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-7.2.8.tgz#0285364c839c660a934ab0f753ec21424bfb292e"
|
||||
integrity sha512-dJn9koYukyz15TouBc+z5z9fdThDk+bKgdlij25eYSu5Mpmtk04gB4eIMQA97K0UDh1d4YukgSJ5w3ZIk0m8DQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/animations@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.1.0.tgz#3030e290683c0e2d63fa61060d36f659511d3b2c"
|
||||
integrity sha512-o7X3HM+eocoryw3VrDUtG6Wci2KwtzyBFo3KBJXjQ16X6fwdkjTG+hLb7pp2CBFBEJW4tPYEy7cSBmEfMRTqag==
|
||||
|
||||
"@angular/common@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-7.2.8.tgz#660c816b6f08cd2919a6efb7465397e4ff14d265"
|
||||
integrity sha512-LgOhf68+LPndGZhtnUlGFd2goReXYmHzaFZW8gCEi9aC+H+Io8bjYh0gkH3xDreevEOe3f0z6coXNFLIxSmTuA==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/common@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.1.0.tgz#f9b5353a28f9da6c06266bc7244bbabf9e005176"
|
||||
integrity sha512-6JPLNtMhI03bGTVQJeSwc+dTjV6DtP7M/BAyzIV0InZP1D6XsOh2QahLFIaaN2sSxYA2ClKuwfX1v+rx9AbXQA==
|
||||
|
||||
"@angular/compiler@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-7.2.8.tgz#9d9c1515e99914399e6915c1c90484b1d255560b"
|
||||
integrity sha512-PrU97cTsOdofpaDkxK0rWUA/CGd0u6ESOI6XvFVm5xH9zJInsdY8ShSHklnr1JJnss70e1dGKZbZq32OChxWMw==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/compiler@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.1.0.tgz#e55b4f2f24df75283002d5e8e85e1acfc46928f6"
|
||||
integrity sha512-QHw/JSeTXHiJQ2Ih0EtU7FGsYcOr+0hwZhqwSW3EEn8TtUgA3DS5lXeiDV66f+3DdvNZFPmgiZIvun3ypxn1HA==
|
||||
|
||||
"@angular/core@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-7.2.8.tgz#6586d9b6c6321c80119b3f3e2bd0edbb32d0b649"
|
||||
integrity sha512-QKwug2kWJC00zm2rvmD9mCJzsOkMVhSu8vqPWf83poWTh8+F9aIVWcy29W0VoGpBkSchOnK8hf9DnKVv28j9nw==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/core@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.0.tgz#9dfc386bd1461e0fd4786031fd245da04371421c"
|
||||
integrity sha512-RVlyegdIAij0P1wLY5ObIdsBAzvmHkHfElnmfiNKhaDftP6U/3zRtaKDu0bq0jvn1WCQ8zXxFQ8AWyKZwyFS+w==
|
||||
|
||||
"@angular/forms@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-7.2.8.tgz#adf194088495822d55dcf3e5bf69196dcf19465d"
|
||||
integrity sha512-lbSX4IHFHz/c4e2RHiPpL8MJlzDkCuQEHnqsujDaV2X9o9fApS6+C1X4x7Z2XDKqonmeX+aHQwv9+SLejX6OyQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/forms@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.1.0.tgz#de14e34aa37bd41a28f93fee8666cd7f6393078c"
|
||||
integrity sha512-5GC8HQlPChPV+168zLlm4yj4syA6N9ChSKV0tmzj1zIfMcub1UAOaB9IYaXRHQsjPFh9OuQXwmkzScyAfhEVjA==
|
||||
|
||||
"@angular/platform-browser-dynamic@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.8.tgz#e82768900cedfa75bf453263f931a9f90f7aaab2"
|
||||
integrity sha512-nOJt28A5pRn4mdL8y98V7bA6OOdMRjsQAcWCr/isGYF0l1yDC0ijUGWkHuRtj3z1/9tmERN0BLXx+xs1h4JhCQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/platform-browser-dynamic@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.0.tgz#830bd5038d1875736e87e68c3aef44f0f835e418"
|
||||
integrity sha512-sMtz/poQ3TYaWZzWjrn9apKUZ/WKql2MYCWbpax7pql3GgC9OoTslc7ZEe7/d3ynfFE/CQqWBBOuWGD71Z0LMQ==
|
||||
|
||||
"@angular/platform-browser@7.2.8":
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-7.2.8.tgz#11096727b99bf3d7fd82a00a3a468b933c9713bd"
|
||||
integrity sha512-SizCRMc7Or27g2CugcqWnaAikRPfgLgRvb9GFFGpcgoq8CRfOVwkyR5dFZuqN39H+uwtwuTMP5OUYhZcrFNKug==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
"@angular/platform-browser@9.1.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.1.0.tgz#0bd40db37c9e314944c149de935b0f6cdd1f7350"
|
||||
integrity sha512-OsS/blUjl8ranmDaRADjFAmvnlmwbT6WNU7dVov7FhV0rqesbwaOJ5bR0LSYHYpej7Jaa6oYk0v0XWkaH9LTFg==
|
||||
|
||||
"@ng-bootstrap/ng-bootstrap@^4.2.2":
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-4.2.2.tgz#a1c3a9576656cb4f793bbc3df56dfbdeb098f2fb"
|
||||
integrity sha512-v8QmC17bv9he5Ep6zutaI9aQ2w/2NqySP0fejOKe7cacKpGUqsLIakpyd2FD7mfZu7pSCCtHYpRWR+h6yq+Ngg==
|
||||
"@ng-bootstrap/ng-bootstrap@^6.0.2":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.0.2.tgz#ed8d4e04f21885fb18dc2523c4f2111f50ee99b2"
|
||||
integrity sha512-8+Dz8GN15zneIA4+mQ7b1j5O+oHsFdhTYQVDGk6eAazHeqrFD0+o8N+pLZL2PZlf98WcHFmXIOqKXrBD8iLl1g==
|
||||
|
||||
"@serialport/binding-abstract@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-8.0.6.tgz#78e6d7995a95c46d480445303e6f32ca4d53edcd"
|
||||
integrity sha512-1swwUVoRyQ9ubxrkJ8JPppykohUpTAP4jkGr36e9NjbVocSPfqeX6tFZFwl/IdUlwJwxGdbKDqq7FvXniCQUMw==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
debug "^4.1.1"
|
||||
|
||||
"@serialport/binding-mock@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-8.0.6.tgz#41a8f827269c6a0e58546513a274e12023134155"
|
||||
integrity sha512-BIbY5/PsDDo0QWDNCCxDgpowAdks+aZR8BOsEtK2GoASTTcJCy1fBwPIfH870o7rnbH901wY3C+yuTfdOvSO9A==
|
||||
dependencies:
|
||||
"@serialport/binding-abstract" "^8.0.6"
|
||||
debug "^4.1.1"
|
||||
|
||||
"@serialport/bindings@^8.0.7":
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/bindings/-/bindings-8.0.7.tgz#2a58f60f1e24ee4549be6f9e0e37b3359d038a1c"
|
||||
integrity sha512-IqudDL8ne2Y2S0W5fKA6wdgHCIA2e2OIaPVYhGy6duE6legNHFY+05CLicHAyAeTocXmHU7rVNxzVQrOG5tM4g==
|
||||
dependencies:
|
||||
"@serialport/binding-abstract" "^8.0.6"
|
||||
"@serialport/parser-readline" "^8.0.6"
|
||||
bindings "^1.5.0"
|
||||
debug "^4.1.1"
|
||||
nan "^2.14.0"
|
||||
prebuild-install "^5.3.0"
|
||||
|
||||
"@serialport/parser-byte-length@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-8.0.6.tgz#efb6195692b1088e6c095fd43bae196fc30674d0"
|
||||
integrity sha512-92mrFxFEvq3gRvSM7ANK/jfbmHslz91a5oYJy/nbSn4H/MCRXjxR2YOkQgVXuN+zLt+iyDoW3pcOP4Sc1nWdqQ==
|
||||
|
||||
"@serialport/parser-cctalk@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-8.0.6.tgz#4134a3c479d465df3b152a21e16b8ecf1bc818c3"
|
||||
integrity sha512-pqtCYQPgxnxHygiXUPCfgX7sEx+fdR/ObjpscidynEULUq2fFrC5kBkrxRbTfHRtTaU2ii9DyjFq0JVRCbhI0Q==
|
||||
|
||||
"@serialport/parser-delimiter@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-8.0.6.tgz#0e467cb07a40bd3006835c48e488666bd7de22cc"
|
||||
integrity sha512-ogKOcPisPMlVtirkuDu3SFTF0+xT0ijxoH7XjpZiYL41EVi367MwuCnEmXG+dEKKnF0j9EPqOyD2LGSJxaFmhQ==
|
||||
|
||||
"@serialport/parser-readline@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-8.0.6.tgz#8a6a296a1bec08a4855bf7a62bc6335d52972e4a"
|
||||
integrity sha512-OYBT2mpczh9QUI3MTw8j0A0tIlPVjpVipvuVnjRkYwxrxPeq04RaLFhaDpuRzua5rTKMt89c1y3btYeoDXMjAA==
|
||||
dependencies:
|
||||
"@serialport/parser-delimiter" "^8.0.6"
|
||||
|
||||
"@serialport/parser-ready@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-8.0.6.tgz#d6e95e53ee70d298ae0b4147995007f4ba62651c"
|
||||
integrity sha512-xcEqv4rc119WR5JzAuu8UeJOlAwET2PTdNb6aIrrLlmTxhvuBbuRFcsnF3BpH9jUL30Kh7a6QiNXIwVG+WR/1Q==
|
||||
|
||||
"@serialport/parser-regex@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-8.0.6.tgz#70aa1abe31899d1b986f44cfb6777a76e26755bf"
|
||||
integrity sha512-J8KY75Azz5ZyExmyM5YfUxbXOWBkZCytKgCCmZ966ttwZS0bUZOuoCaZj2Zp4VILJAiLuxHoqc0foi67Fri5+g==
|
||||
|
||||
"@serialport/stream@^8.0.6":
|
||||
version "8.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-8.0.6.tgz#3395dbac788c00797c2435c61c2ecb4e99fbf41d"
|
||||
integrity sha512-ym1PwM0rwLrj90vRBB66I1hwMXbuMw9wGTxqns75U3N/tuNFOH85mxXaYVF2TpI66aM849NoI1jMm50fl9equg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
@@ -80,10 +130,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
|
||||
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
|
||||
|
||||
"@types/semver@^6.0.2":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
|
||||
integrity sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA==
|
||||
"@types/semver@^7.1.0":
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408"
|
||||
integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
JSONStream@^1.3.4, JSONStream@^1.3.5:
|
||||
version "1.3.5"
|
||||
@@ -271,6 +323,13 @@ bin-links@^1.1.2:
|
||||
graceful-fs "^4.1.11"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
bindings@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
|
||||
@@ -316,10 +375,10 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
builder-util-runtime@8.4.0:
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.4.0.tgz#3163fffc078e6b8f3dd5b6eb12a8345573590682"
|
||||
integrity sha512-CJB/eKfPf2vHrkmirF5eicVnbDCkMBbwd5tRYlTlgud16zFeqD7QmrVUAOEXdnsrcNkiLg9dbuUsQKtl/AwsYQ==
|
||||
builder-util-runtime@8.6.2:
|
||||
version "8.6.2"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.2.tgz#8270e15b012d8d3b110f3e327b0fd8b0e07b1686"
|
||||
integrity sha512-9QnIBISfhgQ2BxtRLidVqf/v5HD73vSKZDllpUmGd2L6VORGQk7cZAPmPtw4HQM3gPBelyVJ5yIjMNZ8xjmd1A==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
sax "^1.2.4"
|
||||
@@ -798,19 +857,19 @@ electron-localshortcut@^3.1.0:
|
||||
keyboardevent-from-electron-accelerator "^1.1.0"
|
||||
keyboardevents-areequal "^0.2.1"
|
||||
|
||||
electron-updater@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.0.tgz#f9ecfc657f65ead737d42b9efecf628d3756b550"
|
||||
integrity sha512-GuS3g7HDh17x/SaFjxjswlWUaKHczksYkV2Xc5CKj/bZH0YCvTSHtOmnBAdAmCk99u/71p3zP8f0jIqDfGcjww==
|
||||
electron-updater@^4.2.5:
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.5.tgz#dbced8da6f8c6fc2dc662f2776131f5a49ce018d"
|
||||
integrity sha512-ir8SI3capF5pN4LTQY79bP7oqiBKjgtdDW378xVId5VcGUZ+Toei2j+fgx1mq3y4Qg19z4HqLxEZ9FqMD0T0RA==
|
||||
dependencies:
|
||||
"@types/semver" "^6.0.2"
|
||||
builder-util-runtime "8.4.0"
|
||||
"@types/semver" "^7.1.0"
|
||||
builder-util-runtime "8.6.2"
|
||||
fs-extra "^8.1.0"
|
||||
js-yaml "^3.13.1"
|
||||
lazy-val "^1.0.4"
|
||||
lodash.isequal "^4.5.0"
|
||||
pako "^1.0.10"
|
||||
semver "^6.3.0"
|
||||
pako "^1.0.11"
|
||||
semver "^7.1.3"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
@@ -925,6 +984,11 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
|
||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||
integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
|
||||
find-npm-prefix@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
|
||||
@@ -1485,10 +1549,10 @@ keyboardevents-areequal@^0.2.1:
|
||||
resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194"
|
||||
integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw==
|
||||
|
||||
keytar@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.2.0.tgz#7543190be98e2a751466096ce3ca1b1a4b8f39fe"
|
||||
integrity sha512-vsIX6n2BgTwzbKOSPIiJ8YduwHlPEE/G5dkmZWXaQK9qiGZMQyhxlFA4O6vrvM5fsXTMgUOrODYAqgpfNSRLDw==
|
||||
keytar@^5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.4.0.tgz#71d8209e7dd2fe99008c243791350a6bd6ceab67"
|
||||
integrity sha512-Ta0RtUmkq7un177SPgXKQ7FGfGDV4xvsV0cGNiWVEzash5U0wyOsXpwfrK2+Oq+hHvsvsbzIZUUuJPimm3avFw==
|
||||
dependencies:
|
||||
nan "2.14.0"
|
||||
prebuild-install "5.3.3"
|
||||
@@ -1918,12 +1982,10 @@ napi-build-utils@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508"
|
||||
integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==
|
||||
|
||||
ngx-toastr@^10.2.0:
|
||||
version "10.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-10.2.0.tgz#8a79008de0b1c013f90120a53e0355af5762e969"
|
||||
integrity sha512-6ASr5bcvQmtNKb4D2VEsQjCXyROq6GwberBWO0bVt+xcBYPUea4aRTgX8in9apX9buaTafzG+h3HlnIraspoPg==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
ngx-toastr@^12.0.0:
|
||||
version "12.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-12.0.0.tgz#1b35a4e5b785246c558e091c831a26ebf211cfd7"
|
||||
integrity sha512-J0mvGXH9cLlP3acoLJcuYhsQcL+EOAtkS1Y5tFKzrw2TYqY2HNffMVPvK+KP1LDJHiIgRpYg3ZELw1raAl0R/A==
|
||||
|
||||
node-abi@^2.15.0, node-abi@^2.7.0:
|
||||
version "2.15.0"
|
||||
@@ -2395,10 +2457,10 @@ pacote@^9.1.0, pacote@^9.2.3, pacote@^9.5.0:
|
||||
unique-filename "^1.1.1"
|
||||
which "^1.3.1"
|
||||
|
||||
pako@^1.0.10:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
|
||||
integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
|
||||
pako@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
|
||||
parallel-transform@^1.1.0:
|
||||
version "1.1.0"
|
||||
@@ -2464,7 +2526,7 @@ pkg-up@^2.0.0:
|
||||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
prebuild-install@5.3.3:
|
||||
prebuild-install@5.3.3, prebuild-install@^5.3.0:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e"
|
||||
integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==
|
||||
@@ -2840,16 +2902,32 @@ semver-diff@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
semver@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6"
|
||||
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==
|
||||
|
||||
semver@~5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
||||
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
|
||||
|
||||
serialport@^8.0.7:
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/serialport/-/serialport-8.0.7.tgz#9f28b1b7c47333a0962f5505a1c3feb1d90f89a9"
|
||||
integrity sha512-R9bfNebs2dblYf5sD/Aaa7j8+siP4X7TGT02lqHM9DF5fyjlrPGXmsLw9+LKOz1AvjGjkxf2NzBVnDpqRX7clQ==
|
||||
dependencies:
|
||||
"@serialport/binding-mock" "^8.0.6"
|
||||
"@serialport/bindings" "^8.0.7"
|
||||
"@serialport/parser-byte-length" "^8.0.6"
|
||||
"@serialport/parser-cctalk" "^8.0.6"
|
||||
"@serialport/parser-delimiter" "^8.0.6"
|
||||
"@serialport/parser-readline" "^8.0.6"
|
||||
"@serialport/parser-ready" "^8.0.6"
|
||||
"@serialport/parser-regex" "^8.0.6"
|
||||
"@serialport/stream" "^8.0.6"
|
||||
debug "^4.1.1"
|
||||
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
@@ -3486,10 +3564,10 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
|
||||
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
|
||||
|
||||
yargs-parser@^16.1.0:
|
||||
version "16.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1"
|
||||
integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==
|
||||
yargs-parser@^18.1.1:
|
||||
version "18.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37"
|
||||
integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
@@ -3519,10 +3597,10 @@ yargs@^11.0.0:
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^9.0.2"
|
||||
|
||||
yargs@^15.1.0:
|
||||
version "15.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219"
|
||||
integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==
|
||||
yargs@^15.3.1:
|
||||
version "15.3.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b"
|
||||
integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==
|
||||
dependencies:
|
||||
cliui "^6.0.0"
|
||||
decamelize "^1.2.0"
|
||||
@@ -3534,9 +3612,9 @@ yargs@^15.1.0:
|
||||
string-width "^4.2.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^16.1.0"
|
||||
yargs-parser "^18.1.1"
|
||||
|
||||
zone.js@^0.8.29:
|
||||
version "0.8.29"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.29.tgz#8dce92aa0dd553b50bc5bfbb90af9986ad845a12"
|
||||
integrity sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==
|
||||
zone.js@^0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16"
|
||||
integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==
|
||||
|
BIN
extras/ssh-keygen/libcrypto.dll
Normal file
BIN
extras/ssh-keygen/libcrypto.dll
Normal file
Binary file not shown.
BIN
extras/ssh-keygen/ssh-keygen.exe
Normal file
BIN
extras/ssh-keygen/ssh-keygen.exe
Normal file
Binary file not shown.
40
package.json
40
package.json
@@ -1,28 +1,28 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.12.1",
|
||||
"@sentry/cli": "^1.49.0",
|
||||
"@sentry/electron": "^1.0.0",
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"@sentry/cli": "^1.52.1",
|
||||
"@sentry/electron": "^1.2.1",
|
||||
"@types/electron-config": "^3.2.2",
|
||||
"@types/electron-debug": "^2.1.0",
|
||||
"@types/js-yaml": "^3.12.1",
|
||||
"@types/js-yaml": "^3.12.3",
|
||||
"@types/node": "12.7.12",
|
||||
"@types/webpack-env": "1.15.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.13.0",
|
||||
"@typescript-eslint/parser": "^2.19.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||
"@typescript-eslint/parser": "^2.25.0",
|
||||
"apply-loader": "2.0.0",
|
||||
"awesome-typescript-loader": "^5.0.0",
|
||||
"core-js": "^3.6.4",
|
||||
"cross-env": "7.0.0",
|
||||
"cross-env": "7.0.2",
|
||||
"css-loader": "3.4.2",
|
||||
"electron": "^8.0.0",
|
||||
"electron-builder": "22.3.2",
|
||||
"electron": "^8.2.1",
|
||||
"electron-builder": "22.4.1",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-installer-snap": "^5.0.0",
|
||||
"electron-notarize": "^0.1.1",
|
||||
"electron-rebuild": "^1.9.0",
|
||||
"electron-rebuild": "^1.10.1",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-import": "^2.20.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"file-loader": "^5.0.2",
|
||||
"graceful-fs": "^4.2.2",
|
||||
"html-loader": "0.5.5",
|
||||
@@ -45,21 +45,21 @@
|
||||
"style-loader": "^1.1.3",
|
||||
"svg-inline-loader": "^0.8.0",
|
||||
"to-string-loader": "1.1.6",
|
||||
"tslib": "^1.10.0",
|
||||
"typedoc": "^0.16.7",
|
||||
"typescript": "^3.7.5",
|
||||
"tslib": "^1.11.1",
|
||||
"typedoc": "^0.17.3",
|
||||
"typescript": "^3.8.3",
|
||||
"url-loader": "^3.0.0",
|
||||
"val-loader": "2.1.0",
|
||||
"webpack": "^5.0.0-beta.12",
|
||||
"webpack": "^5.0.0-beta.14",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"yaml-loader": "0.5.0"
|
||||
"yaml-loader": "0.6.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"*/node-abi": "^2.14.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:typings && webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js",
|
||||
"build:typings": "tsc --project terminus-core/tsconfig.typings.json && tsc --project terminus-settings/tsconfig.typings.json && tsc --project terminus-terminal/tsconfig.typings.json && tsc --project terminus-plugin-manager/tsconfig.typings.json && tsc --project terminus-ssh/tsconfig.typings.json",
|
||||
"build": "npm run build:typings && webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js && webpack --color --config terminus-serial/webpack.config.js",
|
||||
"build:typings": "node scripts/build-typings.js",
|
||||
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
|
||||
"start": "cross-env TERMINUS_DEV=1 electron app --debug",
|
||||
"prod": "cross-env TERMINUS_DEV=1 electron app",
|
||||
@@ -67,5 +67,7 @@
|
||||
"lint": "eslint --ext ts */src */lib",
|
||||
"postinstall": "node ./scripts/install-deps.js"
|
||||
},
|
||||
"repository": "eugeny/terminus"
|
||||
"repository": "eugeny/terminus",
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT"
|
||||
}
|
||||
|
9
scripts/build-typings.js
Executable file
9
scripts/build-typings.js
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
const sh = require('shelljs')
|
||||
const vars = require('./vars')
|
||||
const log = require('npmlog')
|
||||
|
||||
vars.builtinPlugins.forEach(plugin => {
|
||||
log.info('typings', plugin)
|
||||
sh.exec(`npx tsc --project ${plugin}/tsconfig.typings.json`)
|
||||
})
|
@@ -21,6 +21,7 @@ exports.builtinPlugins = [
|
||||
'terminus-community-color-schemes',
|
||||
'terminus-plugin-manager',
|
||||
'terminus-ssh',
|
||||
'terminus-serial',
|
||||
]
|
||||
exports.bundledModules = [
|
||||
'@angular',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "terminus-community-color-schemes",
|
||||
"version": "1.0.99-nightly.0",
|
||||
"version": "1.0.104-nightly.0",
|
||||
"description": "Community color schemes for Terminus",
|
||||
"keywords": [
|
||||
"terminus-builtin-plugin"
|
||||
|
14
terminus-community-color-schemes/tsconfig.typings.json
Normal file
14
terminus-community-color-schemes/tsconfig.typings.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"exclude": ["node_modules", "dist", "typings"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"emitDeclarationOnly": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "./typings",
|
||||
"paths": {
|
||||
"terminus-*": ["../../terminus-*"],
|
||||
"*": ["../../app/node_modules/*"]
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "terminus-core",
|
||||
"version": "1.0.99-nightly.0",
|
||||
"version": "1.0.104-nightly.0",
|
||||
"description": "Terminus core",
|
||||
"keywords": [
|
||||
"terminus-builtin-plugin"
|
||||
@@ -30,7 +30,7 @@
|
||||
"ng2-dnd": "^5.0.2",
|
||||
"ngx-perfect-scrollbar": "^8.0.0",
|
||||
"shell-escape": "^0.2.0",
|
||||
"uuid": "^3.3.2",
|
||||
"uuid": "^7.0.1",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@@ -1,12 +1,13 @@
|
||||
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
|
||||
export { TabHeaderComponent } from '../components/tabHeader.component'
|
||||
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
|
||||
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
|
||||
export { TabRecoveryProvider, RecoveredTab, RecoveryToken } from './tabRecovery'
|
||||
export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider'
|
||||
export { ConfigProvider } from './configProvider'
|
||||
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
|
||||
export { Theme } from './theme'
|
||||
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
|
||||
export { SelectorOption } from './selector'
|
||||
|
||||
export { AppService } from '../services/app.service'
|
||||
export { ConfigService } from '../services/config.service'
|
||||
|
8
terminus-core/src/api/selector.ts
Normal file
8
terminus-core/src/api/selector.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export interface SelectorOption<T> {
|
||||
name: string
|
||||
description?: string
|
||||
result?: T
|
||||
icon?: string
|
||||
freeInputPattern?: string
|
||||
callback?: (string?) => void
|
||||
}
|
@@ -12,6 +12,12 @@ export interface RecoveredTab {
|
||||
options?: any
|
||||
}
|
||||
|
||||
export interface RecoveryToken {
|
||||
[_: string]: any
|
||||
type: string
|
||||
tabColor?: string|null
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend to enable recovery for your custom tab.
|
||||
* This works in conjunction with [[getRecoveryToken()]]
|
||||
@@ -34,5 +40,5 @@ export abstract class TabRecoveryProvider {
|
||||
* @returns [[RecoveredTab]] descriptor containing tab type and component inputs
|
||||
* or `null` if this token is from a different tab type or is not supported
|
||||
*/
|
||||
abstract async recover (recoveryToken: any): Promise<RecoveredTab|null>
|
||||
abstract async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null>
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, Inject, Input, HostListener, HostBinding } from '@angular/core'
|
||||
import { trigger, style, animate, transition, state } from '@angular/animations'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -113,6 +114,9 @@ export class AppRootComponent {
|
||||
if (hotkey === 'move-tab-right') {
|
||||
this.app.moveSelectedTabRight()
|
||||
}
|
||||
if (hotkey === 'reopen-tab') {
|
||||
this.app.reopenLastTab()
|
||||
}
|
||||
}
|
||||
if (hotkey === 'toggle-fullscreen') {
|
||||
this.hostApp.toggleFullscreen()
|
||||
@@ -132,9 +136,7 @@ export class AppRootComponent {
|
||||
})
|
||||
|
||||
this.hostApp.windowCloseRequest$.subscribe(async () => {
|
||||
if (await this.app.closeAllTabs()) {
|
||||
this.hostApp.closeWindow()
|
||||
}
|
||||
this.app.closeWindow()
|
||||
})
|
||||
|
||||
if (window['safeModeReason']) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Observable, Subject } from 'rxjs'
|
||||
import { ViewRef } from '@angular/core'
|
||||
import { RecoveryToken } from '../api/tabRecovery'
|
||||
|
||||
/**
|
||||
* Represents an active "process" inside a tab,
|
||||
@@ -62,7 +63,7 @@ export abstract class BaseTabComponent {
|
||||
get destroyed$ (): Observable<void> { return this.destroyed }
|
||||
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
|
||||
|
||||
constructor () {
|
||||
protected constructor () {
|
||||
this.focused$.subscribe(() => {
|
||||
this.hasFocus = true
|
||||
})
|
||||
@@ -71,7 +72,7 @@ export abstract class BaseTabComponent {
|
||||
})
|
||||
}
|
||||
|
||||
setTitle (title: string) {
|
||||
setTitle (title: string): void {
|
||||
this.title = title
|
||||
if (!this.customTitle) {
|
||||
this.titleChange.next(title)
|
||||
@@ -83,7 +84,7 @@ export abstract class BaseTabComponent {
|
||||
*
|
||||
* @param {type} progress: value between 0 and 1, or `null` to remove
|
||||
*/
|
||||
setProgress (progress: number|null) {
|
||||
setProgress (progress: number|null): void {
|
||||
this.progress.next(progress)
|
||||
if (progress) {
|
||||
if (this.progressClearTimeout) {
|
||||
@@ -118,7 +119,7 @@ export abstract class BaseTabComponent {
|
||||
* @return JSON serializable tab state representation
|
||||
* for your [[TabRecoveryProvider]] to parse
|
||||
*/
|
||||
async getRecoveryToken (): Promise<any> {
|
||||
async getRecoveryToken (): Promise<RecoveryToken|null> {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -136,11 +137,11 @@ export abstract class BaseTabComponent {
|
||||
return true
|
||||
}
|
||||
|
||||
emitFocused () {
|
||||
emitFocused (): void {
|
||||
this.focused.next()
|
||||
}
|
||||
|
||||
emitBlurred () {
|
||||
emitBlurred (): void {
|
||||
this.blurred.next()
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { NgZone, Component, Input, HostBinding, HostListener } from '@angular/core'
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, Input, ElementRef, ViewChild } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
|
@@ -14,7 +14,7 @@ export class SafeModeModalComponent {
|
||||
this.error = window['safeModeReason']
|
||||
}
|
||||
|
||||
close () {
|
||||
close (): void {
|
||||
this.modalInstance.dismiss()
|
||||
}
|
||||
}
|
||||
|
26
terminus-core/src/components/selectorModal.component.pug
Normal file
26
terminus-core/src/components/selectorModal.component.pug
Normal file
@@ -0,0 +1,26 @@
|
||||
.modal-body
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='filter',
|
||||
autofocus,
|
||||
[placeholder]='name',
|
||||
(ngModelChange)='onFilterChange()'
|
||||
)
|
||||
|
||||
.list-group.mt-3(*ngIf='filteredOptions.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
#item,
|
||||
(click)='selectOption(option)',
|
||||
[class.active]='selectedIndex == i',
|
||||
*ngFor='let option of filteredOptions; let i = index'
|
||||
)
|
||||
i.icon(
|
||||
class='fa-fw fas fa-{{option.icon}}',
|
||||
*ngIf='!iconIsSVG(option.icon)'
|
||||
)
|
||||
.icon(
|
||||
[fastHtmlBind]='option.icon',
|
||||
*ngIf='iconIsSVG(option.icon)'
|
||||
)
|
||||
.mr-2.title {{getOptionText(option)}}
|
||||
.text-muted {{option.description}}
|
13
terminus-core/src/components/selectorModal.component.scss
Normal file
13
terminus-core/src/components/selectorModal.component.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
.list-group {
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.25rem;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
}
|
78
terminus-core/src/components/selectorModal.component.ts
Normal file
78
terminus-core/src/components/selectorModal.component.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Component, Input, HostListener, ViewChildren, QueryList, ElementRef } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { SelectorOption } from '../api/selector'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./selectorModal.component.pug'),
|
||||
styles: [require('./selectorModal.component.scss')],
|
||||
})
|
||||
export class SelectorModalComponent<T> {
|
||||
@Input() options: SelectorOption<T>[]
|
||||
@Input() filteredOptions: SelectorOption<T>[]
|
||||
@Input() filter = ''
|
||||
@Input() name: string
|
||||
@Input() selectedIndex = 0
|
||||
@ViewChildren('item') itemChildren: QueryList<ElementRef>
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
) { }
|
||||
|
||||
ngOnInit (): void {
|
||||
this.onFilterChange()
|
||||
}
|
||||
|
||||
@HostListener('keyup', ['$event']) onKeyUp (event: KeyboardEvent): void {
|
||||
if (event.key === 'ArrowUp') {
|
||||
this.selectedIndex--
|
||||
}
|
||||
if (event.key === 'ArrowDown') {
|
||||
this.selectedIndex++
|
||||
}
|
||||
if (event.key === 'Enter') {
|
||||
this.selectOption(this.filteredOptions[this.selectedIndex])
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.close()
|
||||
}
|
||||
|
||||
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
|
||||
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
})
|
||||
}
|
||||
|
||||
onFilterChange (): void {
|
||||
const f = this.filter.trim().toLowerCase()
|
||||
if (!f) {
|
||||
this.filteredOptions = this.options.filter(x => !x.freeInputPattern)
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
this.filteredOptions = this.options.filter(x => x.freeInputPattern || (x.name + (x.description || '')).toLowerCase().includes(f))
|
||||
}
|
||||
this.selectedIndex = Math.max(0, this.selectedIndex)
|
||||
this.selectedIndex = Math.min(this.filteredOptions.length - 1, this.selectedIndex)
|
||||
}
|
||||
|
||||
getOptionText (option: SelectorOption<T>): string {
|
||||
if (option.freeInputPattern) {
|
||||
return option.freeInputPattern.replace('%s', this.filter)
|
||||
}
|
||||
return option.name
|
||||
}
|
||||
|
||||
selectOption (option: SelectorOption<T>): void {
|
||||
option.callback?.(this.filter)
|
||||
this.modalInstance.close(option.result)
|
||||
}
|
||||
|
||||
close (): void {
|
||||
this.modalInstance.dismiss()
|
||||
}
|
||||
|
||||
iconIsSVG (icon: string): boolean {
|
||||
return icon?.startsWith('<')
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
import { Observable, Subject, Subscription } from 'rxjs'
|
||||
import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, OnInit, OnDestroy } from '@angular/core'
|
||||
import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, AfterViewInit, OnDestroy } from '@angular/core'
|
||||
import { BaseTabComponent, BaseTabProcess } from './baseTab.component'
|
||||
import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
|
||||
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from '../api/tabRecovery'
|
||||
import { TabsService } from '../services/tabs.service'
|
||||
import { HotkeysService } from '../services/hotkeys.service'
|
||||
import { TabRecoveryService } from '../services/tabRecovery.service'
|
||||
@@ -48,7 +48,7 @@ export class SplitContainer {
|
||||
/**
|
||||
* Remove unnecessarily nested child containers and renormalizes [[ratios]]
|
||||
*/
|
||||
normalize () {
|
||||
normalize (): void {
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
const child = this.children[i]
|
||||
|
||||
@@ -93,7 +93,7 @@ export class SplitContainer {
|
||||
return s
|
||||
}
|
||||
|
||||
async serialize () {
|
||||
async serialize (): Promise<RecoveryToken> {
|
||||
const children: any[] = []
|
||||
for (const child of this.children) {
|
||||
if (child instanceof SplitContainer) {
|
||||
@@ -140,7 +140,7 @@ export interface SplitSpannerInfo {
|
||||
`,
|
||||
styles: [require('./splitTab.component.scss')],
|
||||
})
|
||||
export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDestroy {
|
||||
export class SplitTabComponent extends BaseTabComponent implements AfterViewInit, OnDestroy {
|
||||
static DIRECTIONS: SplitDirection[] = ['t', 'r', 'b', 'l']
|
||||
|
||||
/** @hidden */
|
||||
@@ -166,6 +166,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
private tabRemoved = new Subject<BaseTabComponent>()
|
||||
private splitAdjusted = new Subject<SplitSpannerInfo>()
|
||||
private focusChanged = new Subject<BaseTabComponent>()
|
||||
private initialized = new Subject<void>()
|
||||
|
||||
get tabAdded$ (): Observable<BaseTabComponent> { return this.tabAdded }
|
||||
get tabRemoved$ (): Observable<BaseTabComponent> { return this.tabRemoved }
|
||||
@@ -180,6 +181,11 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
*/
|
||||
get focusChanged$ (): Observable<BaseTabComponent> { return this.focusChanged }
|
||||
|
||||
/**
|
||||
* Fired once tab layout is created and child tabs can be added
|
||||
*/
|
||||
get initialized$ (): Observable<void> { return this.initialized }
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private hotkeys: HotkeysService,
|
||||
@@ -244,7 +250,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
async ngOnInit () {
|
||||
async ngAfterViewInit (): Promise<void> {
|
||||
if (this._recoveredState) {
|
||||
await this.recoverContainer(this.root, this._recoveredState)
|
||||
this.layout()
|
||||
@@ -255,15 +261,17 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
}
|
||||
})
|
||||
}
|
||||
this.initialized.next()
|
||||
this.initialized.complete()
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
ngOnDestroy () {
|
||||
ngOnDestroy (): void {
|
||||
this.hotkeysSubscription.unsubscribe()
|
||||
}
|
||||
|
||||
/** @returns Flat list of all sub-tabs */
|
||||
getAllTabs () {
|
||||
getAllTabs (): BaseTabComponent[] {
|
||||
return this.root.getAllTabs()
|
||||
}
|
||||
|
||||
@@ -275,7 +283,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
return this.maximizedTab
|
||||
}
|
||||
|
||||
focus (tab: BaseTabComponent) {
|
||||
focus (tab: BaseTabComponent): void {
|
||||
this.focusedTab = tab
|
||||
for (const x of this.getAllTabs()) {
|
||||
if (x !== tab) {
|
||||
@@ -293,7 +301,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
this.layout()
|
||||
}
|
||||
|
||||
maximize (tab: BaseTabComponent|null) {
|
||||
maximize (tab: BaseTabComponent|null): void {
|
||||
this.maximizedTab = tab
|
||||
this.layout()
|
||||
}
|
||||
@@ -301,7 +309,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
/**
|
||||
* Focuses the first available tab inside the given [[SplitContainer]]
|
||||
*/
|
||||
focusAnyIn (parent: BaseTabComponent | SplitContainer) {
|
||||
focusAnyIn (parent: BaseTabComponent | SplitContainer): void {
|
||||
if (!parent) {
|
||||
return
|
||||
}
|
||||
@@ -315,7 +323,9 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
/**
|
||||
* Inserts a new `tab` to the `side` of the `relative` tab
|
||||
*/
|
||||
addTab (tab: BaseTabComponent, relative: BaseTabComponent|null, side: SplitDirection) {
|
||||
async addTab (tab: BaseTabComponent, relative: BaseTabComponent|null, side: SplitDirection): Promise<void> {
|
||||
await this.initialized$.toPromise()
|
||||
|
||||
let target = (relative ? this.getParentOf(relative) : null) || this.root
|
||||
let insertIndex = relative ? target.children.indexOf(relative) : -1
|
||||
|
||||
@@ -354,7 +364,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
})
|
||||
}
|
||||
|
||||
removeTab (tab: BaseTabComponent) {
|
||||
removeTab (tab: BaseTabComponent): void {
|
||||
const parent = this.getParentOf(tab)
|
||||
if (!parent) {
|
||||
return
|
||||
@@ -379,7 +389,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
/**
|
||||
* Moves focus in the given direction
|
||||
*/
|
||||
navigate (dir: SplitDirection) {
|
||||
navigate (dir: SplitDirection): void {
|
||||
let rel: BaseTabComponent | SplitContainer = this.focusedTab
|
||||
let parent = this.getParentOf(rel)
|
||||
if (!parent) {
|
||||
@@ -412,11 +422,12 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
}
|
||||
}
|
||||
|
||||
async splitTab (tab: BaseTabComponent, dir: SplitDirection) {
|
||||
async splitTab (tab: BaseTabComponent, dir: SplitDirection): Promise<BaseTabComponent|null> {
|
||||
const newTab = await this.tabsService.duplicate(tab)
|
||||
if (newTab) {
|
||||
this.addTab(newTab, tab, dir)
|
||||
}
|
||||
return newTab
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,12 +465,12 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
onSpannerAdjusted (spanner: SplitSpannerInfo) {
|
||||
onSpannerAdjusted (spanner: SplitSpannerInfo): void {
|
||||
this.layout()
|
||||
this.splitAdjusted.next(spanner)
|
||||
}
|
||||
|
||||
destroy () {
|
||||
destroy (): void {
|
||||
super.destroy()
|
||||
for (const x of this.getAllTabs()) {
|
||||
x.destroy()
|
||||
@@ -574,7 +585,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class SplitTabRecoveryProvider extends TabRecoveryProvider {
|
||||
async recover (recoveryToken: any): Promise<RecoveredTab|null> {
|
||||
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> {
|
||||
if (recoveryToken && recoveryToken.type === 'app:split-tab') {
|
||||
return {
|
||||
type: SplitTabComponent,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, Input, HostBinding, ElementRef, Output, EventEmitter } from '@angular/core'
|
||||
import { SplitContainer } from './splitTab.component'
|
||||
|
||||
|
@@ -13,7 +13,7 @@ import { ToolbarButton, ToolbarButtonProvider } from '../api'
|
||||
export class StartPageComponent {
|
||||
version: string
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
private domSanitizer: DomSanitizer,
|
||||
public homeBase: HomeBaseService,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, Input, ViewChild, HostBinding, ViewContainerRef, OnChanges } from '@angular/core'
|
||||
import { BaseTabComponent } from '../components/baseTab.component'
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef } from '@angular/core'
|
||||
import { SortableComponent } from 'ng2-dnd'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -48,14 +49,17 @@ export class TabHeaderComponent {
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
if (this.hostApp.platform === Platform.macOS) {
|
||||
this.parentDraggable.setDragHandle(this.handle.nativeElement)
|
||||
}
|
||||
this.tab.progress$.subscribe(progress => {
|
||||
this.progress = progress
|
||||
})
|
||||
}
|
||||
|
||||
ngAfterViewInit () {
|
||||
if (this.hostApp.platform === Platform.macOS) {
|
||||
this.parentDraggable.setDragHandle(this.handle.nativeElement)
|
||||
}
|
||||
}
|
||||
|
||||
showRenameTabModal (): void {
|
||||
const modal = this.ngbModal.open(RenameTabModalComponent)
|
||||
modal.componentInstance.value = this.tab.customTitle || this.tab.title
|
||||
|
@@ -1,19 +1,29 @@
|
||||
.mb-4
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
.container.mt-5.mb-5
|
||||
.mb-4
|
||||
.terminus-logo
|
||||
h1.terminus-title Terminus
|
||||
sup α
|
||||
|
||||
.container
|
||||
.text-center.mb-5 Thank you for downloading Terminus!
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable analytics
|
||||
.description Help us track the number of Terminus installs across the world!
|
||||
toggle(
|
||||
[(ngModel)]='config.store.enableAnalytics',
|
||||
(ngModelChange)='config.save(); config.requestRestart()',
|
||||
)
|
||||
toggle([(ngModel)]='config.store.enableAnalytics')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable SSH plugin
|
||||
.description Adds an SSH connection manager UI to Terminus
|
||||
toggle([(ngModel)]='enableSSH')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Enable Serial plugin
|
||||
.description Allows attaching Terminus to serial ports
|
||||
toggle([(ngModel)]='enableSerial')
|
||||
|
||||
|
||||
.text-center.mt-5
|
||||
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again
|
||||
|
@@ -2,5 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
flex: 0 1 500px;
|
||||
flex: auto;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { BaseTabComponent } from './baseTab.component'
|
||||
import { ConfigService } from '../services/config.service'
|
||||
import { AppService } from '../services/app.service'
|
||||
import { HostAppService } from '../services/hostApp.service'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
@@ -10,17 +11,29 @@ import { AppService } from '../services/app.service'
|
||||
styles: [require('./welcomeTab.component.scss')],
|
||||
})
|
||||
export class WelcomeTabComponent extends BaseTabComponent {
|
||||
enableSSH = false
|
||||
enableSerial = false
|
||||
|
||||
constructor (
|
||||
private app: AppService,
|
||||
private hostApp: HostAppService,
|
||||
public config: ConfigService,
|
||||
) {
|
||||
super()
|
||||
this.setTitle('Welcome')
|
||||
this.enableSSH = !config.store.pluginBlacklist.includes('ssh')
|
||||
this.enableSerial = !config.store.pluginBlacklist.includes('serial')
|
||||
}
|
||||
|
||||
closeAndDisable () {
|
||||
this.config.store.enableWelcomeTab = false
|
||||
this.config.store.pluginBlacklist = []
|
||||
if (!this.enableSSH) {
|
||||
this.config.store.pluginBlacklist.push('ssh')
|
||||
}
|
||||
if (!this.enableSerial) {
|
||||
this.config.store.pluginBlacklist.push('serial')
|
||||
}
|
||||
this.config.save()
|
||||
this.app.closeTab(this)
|
||||
this.hostApp.getWindow().reload()
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { HostAppService } from '../services/hostApp.service'
|
||||
import { AppService } from '../services/app.service'
|
||||
@@ -9,11 +10,9 @@ import { AppService } from '../services/app.service'
|
||||
styles: [require('./windowControls.component.scss')],
|
||||
})
|
||||
export class WindowControlsComponent {
|
||||
constructor (public hostApp: HostAppService, public app: AppService) { }
|
||||
private constructor (public hostApp: HostAppService, public app: AppService) { }
|
||||
|
||||
async closeWindow () {
|
||||
if (await this.app.closeAllTabs()) {
|
||||
this.hostApp.closeWindow()
|
||||
}
|
||||
this.app.closeWindow()
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ hotkeys:
|
||||
- 'F11'
|
||||
close-tab:
|
||||
- 'Ctrl-Shift-W'
|
||||
reopen-tab:
|
||||
- 'Ctrl-Shift-T'
|
||||
toggle-last-tab: []
|
||||
rename-tab:
|
||||
- 'Ctrl-Shift-R'
|
||||
|
@@ -7,6 +7,8 @@ hotkeys:
|
||||
- 'Ctrl+⌘+F'
|
||||
close-tab:
|
||||
- '⌘-W'
|
||||
reopen-tab:
|
||||
- '⌘-Shift-T'
|
||||
toggle-last-tab: []
|
||||
rename-tab:
|
||||
- '⌘-R'
|
||||
|
@@ -8,6 +8,8 @@ hotkeys:
|
||||
- 'Alt-Enter'
|
||||
close-tab:
|
||||
- 'Ctrl-Shift-W'
|
||||
reopen-tab:
|
||||
- 'Ctrl-Shift-T'
|
||||
toggle-last-tab: []
|
||||
rename-tab:
|
||||
- 'Ctrl-Shift-R'
|
||||
|
@@ -7,7 +7,7 @@ import { Directive, AfterViewInit, ElementRef } from '@angular/core'
|
||||
export class AutofocusDirective implements AfterViewInit {
|
||||
constructor (private el: ElementRef) { }
|
||||
|
||||
ngAfterViewInit () {
|
||||
ngAfterViewInit (): void {
|
||||
this.el.nativeElement.blur()
|
||||
setTimeout(() => {
|
||||
this.el.nativeElement.focus()
|
||||
|
@@ -8,7 +8,7 @@ export class FastHtmlBindDirective implements OnChanges {
|
||||
@Input() fastHtmlBind: string
|
||||
constructor (private el: ElementRef) { }
|
||||
|
||||
ngOnChanges () {
|
||||
ngOnChanges (): void {
|
||||
this.el.nativeElement.innerHTML = this.fastHtmlBind || ''
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,10 @@ export class AppHotkeyProvider extends HotkeyProvider {
|
||||
id: 'close-tab',
|
||||
name: 'Close tab',
|
||||
},
|
||||
{
|
||||
id: 'reopen-tab',
|
||||
name: 'Reopen last tab',
|
||||
},
|
||||
{
|
||||
id: 'toggle-last-tab',
|
||||
name: 'Toggle last tab',
|
||||
|
@@ -16,6 +16,7 @@ import { TitleBarComponent } from './components/titleBar.component'
|
||||
import { ToggleComponent } from './components/toggle.component'
|
||||
import { WindowControlsComponent } from './components/windowControls.component'
|
||||
import { RenameTabModalComponent } from './components/renameTabModal.component'
|
||||
import { SelectorModalComponent } from './components/selectorModal.component'
|
||||
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
||||
import { SplitTabSpannerComponent } from './components/splitTabSpanner.component'
|
||||
import { WelcomeTabComponent } from './components/welcomeTab.component'
|
||||
@@ -65,7 +66,7 @@ const PROVIDERS = [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
NgbModule.forRoot(),
|
||||
NgbModule,
|
||||
PerfectScrollbarModule,
|
||||
DndModule.forRoot(),
|
||||
],
|
||||
@@ -82,6 +83,7 @@ const PROVIDERS = [
|
||||
SafeModeModalComponent,
|
||||
AutofocusDirective,
|
||||
FastHtmlBindDirective,
|
||||
SelectorModalComponent,
|
||||
SplitTabComponent,
|
||||
SplitTabSpannerComponent,
|
||||
WelcomeTabComponent,
|
||||
@@ -89,6 +91,7 @@ const PROVIDERS = [
|
||||
entryComponents: [
|
||||
RenameTabModalComponent,
|
||||
SafeModeModalComponent,
|
||||
SelectorModalComponent,
|
||||
SplitTabComponent,
|
||||
WelcomeTabComponent,
|
||||
],
|
||||
|
@@ -2,9 +2,13 @@
|
||||
import { Observable, Subject, AsyncSubject } from 'rxjs'
|
||||
import { takeUntil } from 'rxjs/operators'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
import { BaseTabComponent } from '../components/baseTab.component'
|
||||
import { SplitTabComponent } from '../components/splitTab.component'
|
||||
import { SelectorModalComponent } from '../components/selectorModal.component'
|
||||
import { SelectorOption } from '../api/selector'
|
||||
import { RecoveryToken } from '../api/tabRecovery'
|
||||
|
||||
import { ConfigService } from './config.service'
|
||||
import { HostAppService } from './hostApp.service'
|
||||
@@ -46,6 +50,7 @@ export class AppService {
|
||||
|
||||
private lastTabIndex = 0
|
||||
private _activeTab: BaseTabComponent
|
||||
private closedTabsStack: RecoveryToken[] = []
|
||||
|
||||
private activeTabChange = new Subject<BaseTabComponent>()
|
||||
private tabsChanged = new Subject<void>()
|
||||
@@ -64,41 +69,47 @@ export class AppService {
|
||||
get ready$ (): Observable<void> { return this.ready }
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
private hostApp: HostAppService,
|
||||
private tabRecovery: TabRecoveryService,
|
||||
private tabsService: TabsService,
|
||||
private ngbModal: NgbModal,
|
||||
) {
|
||||
if (hostApp.getWindow().id === 1) {
|
||||
if (config.store.terminal.recoverTabs) {
|
||||
this.tabRecovery.recoverTabs().then(tabs => {
|
||||
for (const tab of tabs) {
|
||||
this.openNewTabRaw(tab.type, tab.options)
|
||||
}
|
||||
this.startTabStorage()
|
||||
})
|
||||
} else {
|
||||
/** Continue to store the tabs even if the setting is currently off */
|
||||
this.startTabStorage()
|
||||
}
|
||||
}
|
||||
|
||||
hostApp.windowFocused$.subscribe(() => {
|
||||
this._activeTab?.emitFocused()
|
||||
})
|
||||
}
|
||||
|
||||
startTabStorage () {
|
||||
this.tabsChanged$.subscribe(() => {
|
||||
this.tabRecovery.saveTabs(this.tabs)
|
||||
})
|
||||
setInterval(() => {
|
||||
this.tabRecovery.saveTabs(this.tabs)
|
||||
}, 30000)
|
||||
|
||||
if (hostApp.getWindow().id === 1) {
|
||||
if (config.store.terminal.recoverTabs) {
|
||||
this.tabRecovery.recoverTabs().then(tabs => {
|
||||
for (const tab of tabs) {
|
||||
this.openNewTabRaw(tab.type, tab.options)
|
||||
}
|
||||
this.tabRecovery.enabled = true
|
||||
})
|
||||
} else {
|
||||
/** Continue to store the tabs even if the setting is currently off */
|
||||
this.tabRecovery.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
hostApp.windowFocused$.subscribe(() => {
|
||||
this._activeTab?.emitFocused()
|
||||
})
|
||||
|
||||
this.tabClosed$.subscribe(async tab => {
|
||||
const token = await tab.getRecoveryToken()
|
||||
if (token) {
|
||||
this.closedTabsStack.push(token)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addTabRaw (tab: BaseTabComponent, index: number|null = null) {
|
||||
addTabRaw (tab: BaseTabComponent, index: number|null = null): void {
|
||||
if (index !== null) {
|
||||
this.tabs.splice(index, 0, tab)
|
||||
} else {
|
||||
@@ -141,7 +152,7 @@ export class AppService {
|
||||
* Adds a new tab **without** wrapping it in a SplitTabComponent
|
||||
* @param inputs Properties to be assigned on the new tab component instance
|
||||
*/
|
||||
openNewTabRaw (type: TabComponentType, inputs?: any): BaseTabComponent {
|
||||
openNewTabRaw (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent {
|
||||
const tab = this.tabsService.create(type, inputs)
|
||||
this.addTabRaw(tab)
|
||||
return tab
|
||||
@@ -151,7 +162,7 @@ export class AppService {
|
||||
* Adds a new tab while wrapping it in a SplitTabComponent
|
||||
* @param inputs Properties to be assigned on the new tab component instance
|
||||
*/
|
||||
openNewTab (type: TabComponentType, inputs?: any): BaseTabComponent {
|
||||
openNewTab (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent {
|
||||
const splitTab = this.tabsService.create(SplitTabComponent) as SplitTabComponent
|
||||
const tab = this.tabsService.create(type, inputs)
|
||||
splitTab.addTab(tab, null, 'r')
|
||||
@@ -159,7 +170,24 @@ export class AppService {
|
||||
return tab
|
||||
}
|
||||
|
||||
selectTab (tab: BaseTabComponent) {
|
||||
async reopenLastTab (): Promise<BaseTabComponent|null> {
|
||||
const token = this.closedTabsStack.pop()
|
||||
if (token) {
|
||||
const recoveredTab = await this.tabRecovery.recoverTab(token)
|
||||
if (recoveredTab) {
|
||||
const tab = this.tabsService.create(recoveredTab.type, recoveredTab.options)
|
||||
if (this.activeTab) {
|
||||
this.addTabRaw(tab, this.tabs.indexOf(this.activeTab) + 1)
|
||||
} else {
|
||||
this.addTabRaw(tab)
|
||||
}
|
||||
return tab
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
selectTab (tab: BaseTabComponent): void {
|
||||
if (this._activeTab === tab) {
|
||||
this._activeTab.emitFocused()
|
||||
return
|
||||
@@ -195,14 +223,14 @@ export class AppService {
|
||||
}
|
||||
|
||||
/** Switches between the current tab and the previously active one */
|
||||
toggleLastTab () {
|
||||
toggleLastTab (): void {
|
||||
if (!this.lastTabIndex || this.lastTabIndex >= this.tabs.length) {
|
||||
this.lastTabIndex = 0
|
||||
}
|
||||
this.selectTab(this.tabs[this.lastTabIndex])
|
||||
}
|
||||
|
||||
nextTab () {
|
||||
nextTab (): void {
|
||||
if (this.tabs.length > 1) {
|
||||
const tabIndex = this.tabs.indexOf(this._activeTab)
|
||||
if (tabIndex < this.tabs.length - 1) {
|
||||
@@ -213,7 +241,7 @@ export class AppService {
|
||||
}
|
||||
}
|
||||
|
||||
previousTab () {
|
||||
previousTab (): void {
|
||||
if (this.tabs.length > 1) {
|
||||
const tabIndex = this.tabs.indexOf(this._activeTab)
|
||||
if (tabIndex > 0) {
|
||||
@@ -224,7 +252,7 @@ export class AppService {
|
||||
}
|
||||
}
|
||||
|
||||
moveSelectedTabLeft () {
|
||||
moveSelectedTabLeft (): void {
|
||||
if (this.tabs.length > 1) {
|
||||
const tabIndex = this.tabs.indexOf(this._activeTab)
|
||||
if (tabIndex > 0) {
|
||||
@@ -235,7 +263,7 @@ export class AppService {
|
||||
}
|
||||
}
|
||||
|
||||
moveSelectedTabRight () {
|
||||
moveSelectedTabRight (): void {
|
||||
if (this.tabs.length > 1) {
|
||||
const tabIndex = this.tabs.indexOf(this._activeTab)
|
||||
if (tabIndex < this.tabs.length - 1) {
|
||||
@@ -246,7 +274,7 @@ export class AppService {
|
||||
}
|
||||
}
|
||||
|
||||
swapTabs (a: BaseTabComponent, b: BaseTabComponent) {
|
||||
swapTabs (a: BaseTabComponent, b: BaseTabComponent): void {
|
||||
const i1 = this.tabs.indexOf(a)
|
||||
const i2 = this.tabs.indexOf(b)
|
||||
this.tabs[i1] = b
|
||||
@@ -254,7 +282,7 @@ export class AppService {
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
emitTabsChanged () {
|
||||
emitTabsChanged (): void {
|
||||
this.tabsChanged.next()
|
||||
}
|
||||
|
||||
@@ -268,11 +296,12 @@ export class AppService {
|
||||
tab.destroy()
|
||||
}
|
||||
|
||||
async duplicateTab (tab: BaseTabComponent) {
|
||||
async duplicateTab (tab: BaseTabComponent): Promise<BaseTabComponent|null> {
|
||||
const dup = await this.tabsService.duplicate(tab)
|
||||
if (dup) {
|
||||
this.addTabRaw(dup, this.tabs.indexOf(tab) + 1)
|
||||
}
|
||||
return dup
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,8 +319,18 @@ export class AppService {
|
||||
return true
|
||||
}
|
||||
|
||||
async closeWindow (): Promise<void> {
|
||||
this.tabRecovery.enabled = false
|
||||
await this.tabRecovery.saveTabs(this.tabs)
|
||||
if (await this.closeAllTabs()) {
|
||||
this.hostApp.closeWindow()
|
||||
} else {
|
||||
this.tabRecovery.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
emitReady () {
|
||||
emitReady (): void {
|
||||
this.ready.next()
|
||||
this.ready.complete()
|
||||
this.hostApp.emitReady()
|
||||
@@ -312,7 +351,15 @@ export class AppService {
|
||||
return this.completionObservers.get(tab)!.done$
|
||||
}
|
||||
|
||||
stopObservingTabCompletion (tab: BaseTabComponent) {
|
||||
stopObservingTabCompletion (tab: BaseTabComponent): void {
|
||||
this.completionObservers.delete(tab)
|
||||
}
|
||||
|
||||
showSelector <T> (name: string, options: SelectorOption<T>[]): Promise<T> {
|
||||
const modal = this.ngbModal.open(SelectorModalComponent)
|
||||
const instance: SelectorModalComponent<T> = modal.componentInstance
|
||||
instance.name = name
|
||||
instance.options = options
|
||||
return modal.result as Promise<T>
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ function isNonStructuralObjectMember (v): boolean {
|
||||
|
||||
/** @hidden */
|
||||
export class ConfigProxy {
|
||||
constructor (real: any, defaults: any) {
|
||||
constructor (real: Record<string, any>, defaults: Record<string, any>) {
|
||||
for (const key in defaults) {
|
||||
if (isStructuralMember(defaults[key])) {
|
||||
if (!real[key]) {
|
||||
@@ -71,8 +71,10 @@ export class ConfigProxy {
|
||||
}
|
||||
}
|
||||
|
||||
getValue (_key: string): any { } // eslint-disable-line @typescript-eslint/no-empty-function
|
||||
setValue (_key: string, _value: any) { } // eslint-disable-line @typescript-eslint/no-empty-function
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||
getValue (_key: string): any { }
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||
setValue (_key: string, _value: any) { }
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -100,7 +102,7 @@ export class ConfigService {
|
||||
get changed$ (): Observable<void> { return this.changed }
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
@Inject(ConfigProvider) configProviders: ConfigProvider[],
|
||||
@@ -124,7 +126,7 @@ export class ConfigService {
|
||||
})
|
||||
}
|
||||
|
||||
getDefaults () {
|
||||
getDefaults (): Record<string, any> {
|
||||
const cleanup = o => {
|
||||
if (o instanceof Array) {
|
||||
return o.map(cleanup)
|
||||
@@ -188,11 +190,11 @@ export class ConfigService {
|
||||
enabledServices<T extends object> (services: T[]): T[] {
|
||||
if (!this.servicesCache) {
|
||||
this.servicesCache = {}
|
||||
const ngModule = window['rootModule'].ngInjectorDef
|
||||
const ngModule = window['rootModule'].ɵinj
|
||||
for (const imp of ngModule.imports) {
|
||||
const module = imp['ngModule'] || imp
|
||||
if (module.ngInjectorDef && module.ngInjectorDef.providers) {
|
||||
this.servicesCache[module['pluginName']] = module.ngInjectorDef.providers.map(provider => {
|
||||
if (module.ɵinj?.providers) {
|
||||
this.servicesCache[module['pluginName']] = module.ɵinj.providers.map(provider => {
|
||||
return provider['useClass'] || provider
|
||||
})
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ import { HostAppService, Bounds } from '../services/hostApp.service'
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DockingService {
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
private hostApp: HostAppService,
|
||||
@@ -15,7 +15,7 @@ export class DockingService {
|
||||
electron.screen.on('display-metrics-changed', () => this.repositionWindow())
|
||||
}
|
||||
|
||||
dock () {
|
||||
dock (): void {
|
||||
const dockSide = this.config.store.appearance.dock
|
||||
|
||||
if (dockSide === 'off') {
|
||||
@@ -59,16 +59,17 @@ export class DockingService {
|
||||
})
|
||||
}
|
||||
|
||||
getCurrentScreen () {
|
||||
getCurrentScreen (): Electron.Display {
|
||||
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
|
||||
}
|
||||
|
||||
getScreens () {
|
||||
getScreens (): Electron.Display[] {
|
||||
const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id
|
||||
return this.electron.screen.getAllDisplays().sort((a, b) =>
|
||||
a.bounds.x === b.bounds.x ? a.bounds.y - b.bounds.y : a.bounds.x - b.bounds.x
|
||||
).map((display,index) => {
|
||||
).map((display, index) => {
|
||||
return {
|
||||
...display,
|
||||
id: display.id,
|
||||
name: display.id === primaryDisplayID ? 'Primary Display' : `Display ${index +1}`,
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ export class ElectronService {
|
||||
private electron: any
|
||||
|
||||
/** @hidden */
|
||||
constructor () {
|
||||
private constructor () {
|
||||
this.electron = require('electron')
|
||||
this.remote = this.electron.remote
|
||||
this.app = this.remote.app
|
||||
@@ -46,7 +46,7 @@ export class ElectronService {
|
||||
/**
|
||||
* Removes OS focus from Terminus' window
|
||||
*/
|
||||
loseFocus () {
|
||||
loseFocus (): void {
|
||||
if (process.platform === 'darwin') {
|
||||
this.remote.Menu.sendActionToFirstResponder('hide:')
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ export class HomeBaseService {
|
||||
mixpanel: any
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
) {
|
||||
@@ -22,11 +22,11 @@ export class HomeBaseService {
|
||||
}
|
||||
}
|
||||
|
||||
openGitHub () {
|
||||
openGitHub (): void {
|
||||
this.electron.shell.openExternal('https://github.com/eugeny/terminus')
|
||||
}
|
||||
|
||||
reportBug () {
|
||||
reportBug (): void {
|
||||
let body = `Version: ${this.appVersion}\n`
|
||||
body += `Platform: ${os.platform()} ${os.release()}\n`
|
||||
const label = {
|
||||
@@ -44,7 +44,7 @@ export class HomeBaseService {
|
||||
this.electron.shell.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
|
||||
}
|
||||
|
||||
enableAnalytics () {
|
||||
enableAnalytics (): void {
|
||||
if (!window.localStorage.analyticsUserID) {
|
||||
window.localStorage.analyticsUserID = uuidv4()
|
||||
}
|
||||
@@ -56,7 +56,7 @@ export class HomeBaseService {
|
||||
this.mixpanel.track('launch', this.getAnalyticsProperties())
|
||||
}
|
||||
|
||||
getAnalyticsProperties () {
|
||||
getAnalyticsProperties (): Record<string, string> {
|
||||
return {
|
||||
distinct_id: window.localStorage.analyticsUserID, // eslint-disable-line @typescript-eslint/camelcase
|
||||
platform: process.platform,
|
||||
|
@@ -178,48 +178,48 @@ export class HostAppService {
|
||||
/**
|
||||
* Returns the current remote [[BrowserWindow]]
|
||||
*/
|
||||
getWindow () {
|
||||
getWindow (): Electron.BrowserWindow {
|
||||
return this.electron.BrowserWindow.fromId(this.windowId)
|
||||
}
|
||||
|
||||
newWindow () {
|
||||
newWindow (): void {
|
||||
this.electron.ipcRenderer.send('app:new-window')
|
||||
}
|
||||
|
||||
toggleFullscreen () {
|
||||
toggleFullscreen (): void {
|
||||
const window = this.getWindow()
|
||||
window.setFullScreen(!this.isFullScreen)
|
||||
}
|
||||
|
||||
openDevTools () {
|
||||
openDevTools (): void {
|
||||
this.getWindow().webContents.openDevTools({ mode: 'undocked' })
|
||||
}
|
||||
|
||||
focusWindow () {
|
||||
focusWindow (): void {
|
||||
this.electron.ipcRenderer.send('window-focus')
|
||||
}
|
||||
|
||||
minimize () {
|
||||
minimize (): void {
|
||||
this.electron.ipcRenderer.send('window-minimize')
|
||||
}
|
||||
|
||||
maximize () {
|
||||
maximize (): void {
|
||||
this.electron.ipcRenderer.send('window-maximize')
|
||||
}
|
||||
|
||||
unmaximize () {
|
||||
unmaximize (): void {
|
||||
this.electron.ipcRenderer.send('window-unmaximize')
|
||||
}
|
||||
|
||||
toggleMaximize () {
|
||||
toggleMaximize (): void {
|
||||
this.electron.ipcRenderer.send('window-toggle-maximize')
|
||||
}
|
||||
|
||||
setBounds (bounds: Bounds) {
|
||||
setBounds (bounds: Bounds): void {
|
||||
this.electron.ipcRenderer.send('window-set-bounds', bounds)
|
||||
}
|
||||
|
||||
setAlwaysOnTop (flag: boolean) {
|
||||
setAlwaysOnTop (flag: boolean): void {
|
||||
this.electron.ipcRenderer.send('window-set-always-on-top', flag)
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ export class HostAppService {
|
||||
*
|
||||
* @param type `null`, or `fluent` when supported (Windowd only)
|
||||
*/
|
||||
setVibrancy (enable: boolean, type: string|null) {
|
||||
setVibrancy (enable: boolean, type: string|null): void {
|
||||
if (!isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)) {
|
||||
type = null
|
||||
}
|
||||
@@ -236,38 +236,38 @@ export class HostAppService {
|
||||
this.electron.ipcRenderer.send('window-set-vibrancy', enable, type)
|
||||
}
|
||||
|
||||
setTitle (title: string) {
|
||||
setTitle (title: string): void {
|
||||
this.electron.ipcRenderer.send('window-set-title', title)
|
||||
}
|
||||
|
||||
setTouchBar (touchBar: Electron.TouchBar) {
|
||||
setTouchBar (touchBar: Electron.TouchBar): void {
|
||||
this.getWindow().setTouchBar(touchBar)
|
||||
}
|
||||
|
||||
popupContextMenu (menuDefinition: Electron.MenuItemConstructorOptions[]) {
|
||||
popupContextMenu (menuDefinition: Electron.MenuItemConstructorOptions[]): void {
|
||||
this.electron.Menu.buildFromTemplate(menuDefinition).popup({})
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies other windows of config file changes
|
||||
*/
|
||||
broadcastConfigChange () {
|
||||
broadcastConfigChange (): void {
|
||||
this.electron.ipcRenderer.send('app:config-change')
|
||||
}
|
||||
|
||||
emitReady () {
|
||||
emitReady (): void {
|
||||
this.electron.ipcRenderer.send('app:ready')
|
||||
}
|
||||
|
||||
bringToFront () {
|
||||
bringToFront (): void {
|
||||
this.electron.ipcRenderer.send('window-bring-to-front')
|
||||
}
|
||||
|
||||
closeWindow () {
|
||||
closeWindow (): void {
|
||||
this.electron.ipcRenderer.send('window-close')
|
||||
}
|
||||
|
||||
relaunch () {
|
||||
relaunch (): void {
|
||||
if (this.isPortable) {
|
||||
this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })
|
||||
} else {
|
||||
@@ -276,7 +276,7 @@ export class HostAppService {
|
||||
this.electron.app.exit()
|
||||
}
|
||||
|
||||
quit () {
|
||||
quit (): void {
|
||||
this.logger.info('Quitting')
|
||||
this.electron.app.quit()
|
||||
}
|
||||
|
@@ -70,7 +70,7 @@ export class HotkeysService {
|
||||
* @param name DOM event name
|
||||
* @param nativeEvent event object
|
||||
*/
|
||||
pushKeystroke (name: string, nativeEvent: KeyboardEvent) {
|
||||
pushKeystroke (name: string, nativeEvent: KeyboardEvent): void {
|
||||
(nativeEvent as any).event = name
|
||||
this.currentKeystrokes.push({ event: nativeEvent, time: performance.now() })
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export class HotkeysService {
|
||||
/**
|
||||
* Check the buffer for new complete keystrokes
|
||||
*/
|
||||
processKeystrokes () {
|
||||
processKeystrokes (): void {
|
||||
if (this.isEnabled()) {
|
||||
this.zone.run(() => {
|
||||
const matched = this.getCurrentFullyMatchedHotkey()
|
||||
@@ -91,13 +91,13 @@ export class HotkeysService {
|
||||
}
|
||||
}
|
||||
|
||||
emitKeyEvent (nativeEvent: KeyboardEvent) {
|
||||
emitKeyEvent (nativeEvent: KeyboardEvent): void {
|
||||
this.zone.run(() => {
|
||||
this.key.emit(nativeEvent)
|
||||
})
|
||||
}
|
||||
|
||||
clearCurrentKeystrokes () {
|
||||
clearCurrentKeystrokes (): void {
|
||||
this.currentKeystrokes = []
|
||||
}
|
||||
|
||||
@@ -155,15 +155,15 @@ export class HotkeysService {
|
||||
return this.hotkeyDescriptions.filter((x) => x.id === id)[0]
|
||||
}
|
||||
|
||||
enable () {
|
||||
enable (): void {
|
||||
this.disabledLevel--
|
||||
}
|
||||
|
||||
disable () {
|
||||
disable (): void {
|
||||
this.disabledLevel++
|
||||
}
|
||||
|
||||
isEnabled () {
|
||||
isEnabled (): boolean {
|
||||
return this.disabledLevel === 0
|
||||
}
|
||||
|
||||
@@ -216,6 +216,9 @@ export class HotkeysService {
|
||||
if (typeof value === 'string') {
|
||||
value = [value]
|
||||
}
|
||||
if (!(value instanceof Array)) {
|
||||
continue
|
||||
}
|
||||
if (value) {
|
||||
value = value.map((item: string | string[]) => typeof item === 'string' ? [item] : item)
|
||||
keys[key] = value
|
||||
|
@@ -32,27 +32,27 @@ export class Logger {
|
||||
private name: string,
|
||||
) {}
|
||||
|
||||
debug (...args: any[]) {
|
||||
debug (...args: any[]): void {
|
||||
this.doLog('debug', ...args)
|
||||
}
|
||||
|
||||
info (...args: any[]) {
|
||||
info (...args: any[]): void {
|
||||
this.doLog('info', ...args)
|
||||
}
|
||||
|
||||
warn (...args: any[]) {
|
||||
warn (...args: any[]): void {
|
||||
this.doLog('warn', ...args)
|
||||
}
|
||||
|
||||
error (...args: any[]) {
|
||||
error (...args: any[]): void {
|
||||
this.doLog('error', ...args)
|
||||
}
|
||||
|
||||
log (...args: any[]) {
|
||||
log (...args: any[]): void {
|
||||
this.doLog('log', ...args)
|
||||
}
|
||||
|
||||
private doLog (level: string, ...args: any[]) {
|
||||
private doLog (level: string, ...args: any[]): void {
|
||||
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
||||
if (this.winstonLogger) {
|
||||
this.winstonLogger[level](...args)
|
||||
@@ -65,7 +65,7 @@ export class LogService {
|
||||
private log: any
|
||||
|
||||
/** @hidden */
|
||||
constructor (electron: ElectronService) {
|
||||
private constructor (electron: ElectronService) {
|
||||
this.log = initializeWinston(electron)
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ export class ShellIntegrationService {
|
||||
command: 'paste "%V"',
|
||||
},
|
||||
]
|
||||
constructor (
|
||||
private constructor (
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
) {
|
||||
@@ -58,7 +58,7 @@ export class ShellIntegrationService {
|
||||
return true
|
||||
}
|
||||
|
||||
async install () {
|
||||
async install (): Promise<void> {
|
||||
const exe: string = process.env.PORTABLE_EXECUTABLE_FILE || this.electron.app.getPath('exe')
|
||||
if (this.hostApp.platform === Platform.macOS) {
|
||||
for (const wf of this.automatorWorkflows) {
|
||||
@@ -82,7 +82,7 @@ export class ShellIntegrationService {
|
||||
}
|
||||
}
|
||||
|
||||
async remove () {
|
||||
async remove (): Promise<void> {
|
||||
if (this.hostApp.platform === Platform.macOS) {
|
||||
for (const wf of this.automatorWorkflows) {
|
||||
await exec(`rm -rf "${this.automatorWorkflowsDestination}/${wf}"`)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable, Inject } from '@angular/core'
|
||||
import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
|
||||
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from '../api/tabRecovery'
|
||||
import { BaseTabComponent } from '../components/baseTab.component'
|
||||
import { Logger, LogService } from '../services/log.service'
|
||||
import { ConfigService } from '../services/config.service'
|
||||
@@ -8,8 +8,9 @@ import { ConfigService } from '../services/config.service'
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TabRecoveryService {
|
||||
logger: Logger
|
||||
enabled = false
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
|
||||
private config: ConfigService,
|
||||
log: LogService
|
||||
@@ -17,7 +18,10 @@ export class TabRecoveryService {
|
||||
this.logger = log.create('tabRecovery')
|
||||
}
|
||||
|
||||
async saveTabs (tabs: BaseTabComponent[]) {
|
||||
async saveTabs (tabs: BaseTabComponent[]): Promise<void> {
|
||||
if (!this.enabled) {
|
||||
return
|
||||
}
|
||||
window.localStorage.tabsRecovery = JSON.stringify(
|
||||
await Promise.all(
|
||||
tabs
|
||||
@@ -26,7 +30,10 @@ export class TabRecoveryService {
|
||||
if (token) {
|
||||
token = token.then(r => {
|
||||
if (r) {
|
||||
r.tabColor = tab.color
|
||||
r.tabTitle = tab.title
|
||||
if (tab.color) {
|
||||
r.tabColor = tab.color
|
||||
}
|
||||
}
|
||||
return r
|
||||
})
|
||||
@@ -38,13 +45,14 @@ export class TabRecoveryService {
|
||||
)
|
||||
}
|
||||
|
||||
async recoverTab (token: any): Promise<RecoveredTab|null> {
|
||||
async recoverTab (token: RecoveryToken): Promise<RecoveredTab|null> {
|
||||
for (const provider of this.config.enabledServices(this.tabRecoveryProviders)) {
|
||||
try {
|
||||
const tab = await provider.recover(token)
|
||||
if (tab !== null) {
|
||||
tab.options = tab.options || {}
|
||||
tab.options.color = token.tabColor || null
|
||||
tab.options.title = token.tabTitle || ''
|
||||
return tab
|
||||
}
|
||||
} catch (error) {
|
||||
|
@@ -8,7 +8,7 @@ export type TabComponentType = new (...args: any[]) => BaseTabComponent
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TabsService {
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
private tabRecovery: TabRecoveryService,
|
||||
@@ -17,7 +17,7 @@ export class TabsService {
|
||||
/**
|
||||
* Instantiates a tab component and assigns given inputs
|
||||
*/
|
||||
create (type: TabComponentType, inputs?: any): BaseTabComponent {
|
||||
create (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent {
|
||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type)
|
||||
const componentRef = componentFactory.create(this.injector)
|
||||
const tab = componentRef.instance
|
||||
|
@@ -7,7 +7,7 @@ export class ThemesService {
|
||||
private styleElement: HTMLElement|null = null
|
||||
|
||||
/** @hidden */
|
||||
constructor (
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
@Inject(Theme) private themes: Theme[],
|
||||
) {
|
||||
|
@@ -14,7 +14,7 @@ export class TouchbarService {
|
||||
private tabSegments: SegmentedControlSegment[] = []
|
||||
private nsImageCache: {[id: string]: Electron.NativeImage} = {}
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
private app: AppService,
|
||||
private hostApp: HostAppService,
|
||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||
@@ -48,7 +48,7 @@ export class TouchbarService {
|
||||
})
|
||||
}
|
||||
|
||||
updateTabs () {
|
||||
updateTabs (): void {
|
||||
this.tabSegments = this.app.tabs.map(tab => ({
|
||||
label: this.shortenTitle(tab.title),
|
||||
}))
|
||||
@@ -56,7 +56,7 @@ export class TouchbarService {
|
||||
this.tabsSegmentedControl.selectedIndex = this.app.tabs.indexOf(this.app.activeTab)
|
||||
}
|
||||
|
||||
update () {
|
||||
update (): void {
|
||||
if (this.hostApp.platform !== Platform.macOS) {
|
||||
return
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ export class UpdaterService {
|
||||
private updateURL: string
|
||||
private autoUpdater: AppUpdater
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
log: LogService,
|
||||
private electron: ElectronService,
|
||||
private config: ConfigService,
|
||||
@@ -80,7 +80,7 @@ export class UpdaterService {
|
||||
return this.downloaded
|
||||
}
|
||||
|
||||
async update () {
|
||||
async update (): Promise<void> {
|
||||
if (!this.electronUpdaterAvailable) {
|
||||
this.electron.shell.openExternal(this.updateURL)
|
||||
} else {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable, NgZone } from '@angular/core'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { AppService } from './services/app.service'
|
||||
|
@@ -20,4 +20,8 @@ app-root {
|
||||
ssh-tab .content {
|
||||
margin: 5px !important;
|
||||
}
|
||||
|
||||
serial-tab .content {
|
||||
margin: 5px !important;
|
||||
}
|
||||
}
|
||||
|
@@ -246,7 +246,7 @@ ngb-tabset .tab-content {
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
transition: 0.25s background;
|
||||
transition: 0.0625s background;
|
||||
|
||||
i + * {
|
||||
margin-left: 10px;
|
||||
@@ -262,6 +262,29 @@ ngb-tabset .tab-content {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.list-group-light {
|
||||
.list-group-item {
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255, 255, 255, .1);
|
||||
|
||||
&:not(.combi) {
|
||||
padding: $list-group-item-padding-y $list-group-item-padding-x;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&.list-group-item-action {
|
||||
&:hover, &.active {
|
||||
background: $list-group-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkbox i.on {
|
||||
color: $blue;
|
||||
}
|
||||
@@ -392,3 +415,7 @@ search-panel {
|
||||
border-color: $nav-tabs-link-active-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: $list-group-border-color;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ export function isWindowsBuild (build: number): boolean {
|
||||
return process.platform === 'win32' && parseFloat(os.release()) >= 10 && parseInt(os.release().split('.')[2]) >= build
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getCSSFontFamily (config: any): string {
|
||||
let fonts: string[] = config.terminal.font.split(',').map(x => x.trim().replace(/"/g, ''))
|
||||
if (config.terminal.fallbackFont) {
|
||||
|
@@ -3,14 +3,21 @@
|
||||
|
||||
|
||||
"@types/js-yaml@^3.9.0":
|
||||
version "3.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.1.tgz#5c6f4a1eabca84792fbd916f0cb40847f123c656"
|
||||
integrity sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA==
|
||||
version "3.12.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.3.tgz#abf383c5b639d0aa8b8c4a420d6a85f703357d6c"
|
||||
integrity sha512-otRe77JNNWzoVGLKw8TCspKswRoQToys4tuL6XYVBFxjgeM0RUrx7m3jkaTdxILxeGry3zM8mGYkGXMeQ02guA==
|
||||
|
||||
"@types/semver@^6.0.2":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
|
||||
integrity sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA==
|
||||
"@types/node@*":
|
||||
version "13.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.1.tgz#238eb34a66431b71d2aaddeaa7db166f25971a0d"
|
||||
integrity sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==
|
||||
|
||||
"@types/semver@^7.1.0":
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408"
|
||||
integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/shell-escape@^0.2.0":
|
||||
version "0.2.0"
|
||||
@@ -57,10 +64,10 @@ bootstrap@^4.1.3:
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01"
|
||||
integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==
|
||||
|
||||
builder-util-runtime@8.4.0:
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.4.0.tgz#3163fffc078e6b8f3dd5b6eb12a8345573590682"
|
||||
integrity sha512-CJB/eKfPf2vHrkmirF5eicVnbDCkMBbwd5tRYlTlgud16zFeqD7QmrVUAOEXdnsrcNkiLg9dbuUsQKtl/AwsYQ==
|
||||
builder-util-runtime@8.6.2:
|
||||
version "8.6.2"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.2.tgz#8270e15b012d8d3b110f3e327b0fd8b0e07b1686"
|
||||
integrity sha512-9QnIBISfhgQ2BxtRLidVqf/v5HD73vSKZDllpUmGd2L6VORGQk7cZAPmPtw4HQM3gPBelyVJ5yIjMNZ8xjmd1A==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
sax "^1.2.4"
|
||||
@@ -162,18 +169,18 @@ diagnostics@^1.1.1:
|
||||
kuler "1.0.x"
|
||||
|
||||
electron-updater@^4.0.6:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.0.tgz#f9ecfc657f65ead737d42b9efecf628d3756b550"
|
||||
integrity sha512-GuS3g7HDh17x/SaFjxjswlWUaKHczksYkV2Xc5CKj/bZH0YCvTSHtOmnBAdAmCk99u/71p3zP8f0jIqDfGcjww==
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.5.tgz#dbced8da6f8c6fc2dc662f2776131f5a49ce018d"
|
||||
integrity sha512-ir8SI3capF5pN4LTQY79bP7oqiBKjgtdDW378xVId5VcGUZ+Toei2j+fgx1mq3y4Qg19z4HqLxEZ9FqMD0T0RA==
|
||||
dependencies:
|
||||
"@types/semver" "^6.0.2"
|
||||
builder-util-runtime "8.4.0"
|
||||
"@types/semver" "^7.1.0"
|
||||
builder-util-runtime "8.6.2"
|
||||
fs-extra "^8.1.0"
|
||||
js-yaml "^3.13.1"
|
||||
lazy-val "^1.0.4"
|
||||
lodash.isequal "^4.5.0"
|
||||
pako "^1.0.10"
|
||||
semver "^6.3.0"
|
||||
pako "^1.0.11"
|
||||
semver "^7.1.3"
|
||||
|
||||
enabled@1.0.x:
|
||||
version "1.0.2"
|
||||
@@ -346,10 +353,10 @@ one-time@0.0.4:
|
||||
resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e"
|
||||
integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=
|
||||
|
||||
pako@^1.0.10:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
|
||||
integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
|
||||
pako@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
|
||||
perfect-scrollbar@^1.4.0:
|
||||
version "1.4.0"
|
||||
@@ -398,10 +405,10 @@ sax@^1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
semver@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6"
|
||||
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==
|
||||
|
||||
shell-escape@^0.2.0:
|
||||
version "0.2.0"
|
||||
@@ -459,10 +466,10 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
uuid@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.1.tgz#95ed6ff3d8c881cbf85f0f05cc3915ef994818ef"
|
||||
integrity sha512-yqjRXZzSJm9Dbl84H2VDHpM3zMjzSJQ+hn6C4zqd5ilW+7P4ZmLEEqwho9LjP+tGuZlF4xrHQXT0h9QZUS/pWA==
|
||||
|
||||
winston-transport@^4.3.0:
|
||||
version "4.3.0"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "terminus-plugin-manager",
|
||||
"version": "1.0.99-nightly.0",
|
||||
"version": "1.0.104-nightly.0",
|
||||
"description": "Terminus' plugin manager",
|
||||
"keywords": [
|
||||
"terminus-builtin-plugin"
|
||||
@@ -17,7 +17,7 @@
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/semver": "^6.0.0",
|
||||
"@types/semver": "^7.1.0",
|
||||
"axios": "^0.19.0",
|
||||
"mz": "^2.6.0",
|
||||
"semver": "^7.1.1"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { BehaviorSubject, Observable } from 'rxjs'
|
||||
import { debounceTime, distinctUntilChanged, first, tap, flatMap, map } from 'rxjs/operators'
|
||||
import * as semver from 'semver'
|
||||
import semverGt from 'semver/functions/gt'
|
||||
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { ConfigService, ElectronService } from 'terminus-core'
|
||||
@@ -48,7 +49,7 @@ export class PluginsSettingsTabComponent {
|
||||
return plugins
|
||||
})).subscribe(available => {
|
||||
for (const plugin of this.pluginManager.installedPlugins) {
|
||||
this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semver.gt(x.version, plugin.version)) || null
|
||||
this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semverGt(x.version, plugin.version)) || null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -34,13 +34,13 @@ export class PluginManagerService {
|
||||
private npmReady: Promise<void>
|
||||
private npm: any
|
||||
|
||||
constructor (
|
||||
private constructor (
|
||||
log: LogService,
|
||||
) {
|
||||
this.logger = log.create('pluginManager')
|
||||
}
|
||||
|
||||
async getNPM () {
|
||||
async getNPM (): Promise<any> {
|
||||
if (!this.npm) {
|
||||
if (!this.npmReady) {
|
||||
this.npmReady = new Promise(resolve => {
|
||||
@@ -83,7 +83,7 @@ export class PluginManagerService {
|
||||
)
|
||||
}
|
||||
|
||||
async installPlugin (plugin: PluginInfo) {
|
||||
async installPlugin (plugin: PluginInfo): Promise<void> {
|
||||
(await this.getNPM()).commands.install([`${plugin.packageName}@${plugin.version}`], err => {
|
||||
if (err) {
|
||||
this.logger.error(err)
|
||||
@@ -93,7 +93,7 @@ export class PluginManagerService {
|
||||
})
|
||||
}
|
||||
|
||||
async uninstallPlugin (plugin: PluginInfo) {
|
||||
async uninstallPlugin (plugin: PluginInfo): Promise<void> {
|
||||
(await this.getNPM()).commands.remove([plugin.packageName], err => {
|
||||
if (err) {
|
||||
this.logger.error(err)
|
||||
|
@@ -2,10 +2,17 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/semver@^6.0.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
|
||||
integrity sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA==
|
||||
"@types/node@*":
|
||||
version "13.7.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113"
|
||||
integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA==
|
||||
|
||||
"@types/semver@^7.1.0":
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408"
|
||||
integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
any-promise@^1.0.0:
|
||||
version "1.3.0"
|
||||
|
37
terminus-serial/package.json
Normal file
37
terminus-serial/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "terminus-serial",
|
||||
"version": "1.0.104-nightly.0",
|
||||
"description": "Serial connection manager for Terminus",
|
||||
"keywords": [
|
||||
"terminus-builtin-plugin"
|
||||
],
|
||||
"main": "dist/index.js",
|
||||
"typings": "typings/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --color",
|
||||
"watch": "webpack --progress --color --watch"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "12.7.3",
|
||||
"@types/ssh2": "^0.5.35",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"cli-spinner": "^0.2.10",
|
||||
"electron-rebuild": "^1.10.0",
|
||||
"terminus-terminal": "^1.0.98-nightly.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^7",
|
||||
"@angular/core": "^7",
|
||||
"@angular/forms": "^7",
|
||||
"@ng-bootstrap/ng-bootstrap": "^1",
|
||||
"rxjs": "^5",
|
||||
"terminus-core": "*",
|
||||
"terminus-settings": "*",
|
||||
"terminus-terminal": "*"
|
||||
}
|
||||
}
|
156
terminus-serial/src/api.ts
Normal file
156
terminus-serial/src/api.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { BaseSession } from 'terminus-terminal'
|
||||
import { SerialPort } from 'serialport'
|
||||
import { Logger } from 'terminus-core'
|
||||
import { Subject, Observable } from 'rxjs'
|
||||
|
||||
export interface LoginScript {
|
||||
expect: string
|
||||
send: string
|
||||
isRegex?: boolean
|
||||
optional?: boolean
|
||||
}
|
||||
|
||||
export interface SerialConnection {
|
||||
name: string
|
||||
port: string
|
||||
baudrate: number
|
||||
databits: number
|
||||
stopbits: number
|
||||
parity: string
|
||||
rtscts: boolean
|
||||
xon: boolean
|
||||
xoff: boolean
|
||||
xany: boolean
|
||||
scripts?: LoginScript[]
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const BAUD_RATES = [
|
||||
110, 150, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600,
|
||||
]
|
||||
|
||||
export interface SerialPortInfo {
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export class SerialSession extends BaseSession {
|
||||
scripts?: LoginScript[]
|
||||
serial: SerialPort
|
||||
logger: Logger
|
||||
|
||||
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
|
||||
private serviceMessage = new Subject<string>()
|
||||
|
||||
constructor (public connection: SerialConnection) {
|
||||
super()
|
||||
this.scripts = connection.scripts || []
|
||||
}
|
||||
|
||||
async start (): Promise<void> {
|
||||
this.open = true
|
||||
|
||||
this.serial.on('data', data => {
|
||||
const dataString = data.toString()
|
||||
this.emitOutput(data)
|
||||
|
||||
if (this.scripts) {
|
||||
let found = false
|
||||
for (const script of this.scripts) {
|
||||
let match = false
|
||||
let cmd = ''
|
||||
if (script.isRegex) {
|
||||
const re = new RegExp(script.expect, 'g')
|
||||
if (dataString.match(re)) {
|
||||
cmd = dataString.replace(re, script.send)
|
||||
match = true
|
||||
found = true
|
||||
}
|
||||
} else {
|
||||
if (dataString.includes(script.expect)) {
|
||||
cmd = script.send
|
||||
match = true
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
this.logger.info('Executing script: "' + cmd + '"')
|
||||
this.serial.write(cmd + '\n')
|
||||
this.scripts = this.scripts.filter(x => x !== script)
|
||||
} else {
|
||||
if (script.optional) {
|
||||
this.logger.debug('Skip optional script: ' + script.expect)
|
||||
found = true
|
||||
this.scripts = this.scripts.filter(x => x !== script)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
this.executeUnconditionalScripts()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.serial.on('end', () => {
|
||||
this.logger.info('Shell session ended')
|
||||
if (this.open) {
|
||||
this.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
this.executeUnconditionalScripts()
|
||||
}
|
||||
|
||||
write (data: Buffer): void {
|
||||
if (this.serial) {
|
||||
this.serial.write(data.toString())
|
||||
}
|
||||
}
|
||||
|
||||
async destroy (): Promise<void> {
|
||||
this.serviceMessage.complete()
|
||||
await super.destroy()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||
resize (_, __) { }
|
||||
|
||||
kill (_?: string): void {
|
||||
this.serial.close()
|
||||
}
|
||||
|
||||
async getChildProcesses (): Promise<any[]> {
|
||||
return []
|
||||
}
|
||||
|
||||
async gracefullyKillProcess (): Promise<void> {
|
||||
this.kill('TERM')
|
||||
}
|
||||
|
||||
async getWorkingDirectory (): Promise<string|null> {
|
||||
return null
|
||||
}
|
||||
|
||||
private executeUnconditionalScripts () {
|
||||
if (this.scripts) {
|
||||
for (const script of this.scripts) {
|
||||
if (!script.expect) {
|
||||
console.log('Executing script:', script.send)
|
||||
this.serial.write(script.send + '\n')
|
||||
this.scripts = this.scripts.filter(x => x !== script)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SerialConnectionGroup {
|
||||
name: string
|
||||
connections: SerialConnection[]
|
||||
}
|
36
terminus-serial/src/buttonProvider.ts
Normal file
36
terminus-serial/src/buttonProvider.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable, Injector } from '@angular/core'
|
||||
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
||||
import { SerialService } from './services/serial.service'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class ButtonProvider extends ToolbarButtonProvider {
|
||||
constructor (
|
||||
private injector: Injector,
|
||||
hotkeys: HotkeysService,
|
||||
) {
|
||||
super()
|
||||
hotkeys.matchedHotkey.subscribe(async (hotkey: string) => {
|
||||
if (hotkey === 'serial') {
|
||||
this.activate()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
activate () {
|
||||
this.injector.get(SerialService).showConnectionSelector()
|
||||
}
|
||||
|
||||
provide (): ToolbarButton[] {
|
||||
return [{
|
||||
icon: require('./icons/serial.svg'),
|
||||
weight: 5,
|
||||
title: 'Serial connections',
|
||||
touchBarNSImage: 'NSTouchBarOpenInBrowserTemplate',
|
||||
click: async () => {
|
||||
this.activate()
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
134
terminus-serial/src/components/editConnectionModal.component.pug
Normal file
134
terminus-serial/src/components/editConnectionModal.component.pug
Normal file
@@ -0,0 +1,134 @@
|
||||
.modal-body
|
||||
ngb-tabset([activeId]='basic')
|
||||
ngb-tab(id='basic')
|
||||
ng-template(ngbTabTitle) General
|
||||
ng-template(ngbTabContent)
|
||||
.form-group
|
||||
label Name
|
||||
input.form-control(
|
||||
type='text',
|
||||
autofocus,
|
||||
[(ngModel)]='connection.name',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Path
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='connection.port',
|
||||
[ngbTypeahead]='portsAutocomplete',
|
||||
[resultFormatter]='portsFormatter',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Baud Rate
|
||||
select.form-control(
|
||||
[(ngModel)]='connection.baudrate',
|
||||
)
|
||||
option([value]='x', *ngFor='let x of baudRates') {{x}}
|
||||
|
||||
ngb-tab(id='advanced')
|
||||
ng-template(ngbTabTitle) Advanced
|
||||
ng-template(ngbTabContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Tab color
|
||||
input.form-control(
|
||||
type='text',
|
||||
autofocus,
|
||||
[(ngModel)]='connection.color',
|
||||
placeholder='#000000'
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title DataBits
|
||||
input.form-control(
|
||||
type='number',
|
||||
placeholder='8',
|
||||
[(ngModel)]='connection.databits',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title StopBits
|
||||
input.form-control(
|
||||
type='number',
|
||||
placeholder='1',
|
||||
[(ngModel)]='connection.stopbits',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Parity
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='connection.parity',
|
||||
placeholder='none'
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title RTSCTS
|
||||
toggle([(ngModel)]='connection.rtscts')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Xon
|
||||
toggle([(ngModel)]='connection.xon')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Xoff
|
||||
toggle([(ngModel)]='connection.xoff')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Xany
|
||||
toggle([(ngModel)]='connection.xany')
|
||||
|
||||
ngb-tab(id='scripts')
|
||||
ng-template(ngbTabTitle) Login scripts
|
||||
ng-template(ngbTabContent)
|
||||
table(*ngIf='connection.scripts.length > 0')
|
||||
tr
|
||||
th String to expect
|
||||
th String to be sent
|
||||
th.pl-2 Regex
|
||||
th.pl-2 Optional
|
||||
th.pl-2 Actions
|
||||
tr(*ngFor='let script of connection.scripts')
|
||||
td.pr-2
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='script.expect'
|
||||
)
|
||||
td
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='script.send'
|
||||
)
|
||||
td.pl-2
|
||||
checkbox(
|
||||
[(ngModel)]='script.isRegex',
|
||||
)
|
||||
td.pl-2
|
||||
checkbox(
|
||||
[(ngModel)]='script.optional',
|
||||
)
|
||||
td.pl-2
|
||||
.input-group.flex-nowrap
|
||||
button.btn.btn-outline-info.ml-0((click)='moveScriptUp(script)')
|
||||
i.fas.fa-arrow-up
|
||||
button.btn.btn-outline-info.ml-0((click)='moveScriptDown(script)')
|
||||
i.fas.fa-arrow-down
|
||||
button.btn.btn-outline-danger.ml-0((click)='deleteScript(script)')
|
||||
i.fas.fa-trash
|
||||
|
||||
button.btn.btn-outline-info.mt-2((click)='addScript()')
|
||||
i.fas.fa-plus
|
||||
span New item
|
||||
|
||||
.modal-footer
|
||||
button.btn.btn-outline-primary((click)='save()') Save
|
||||
button.btn.btn-outline-danger((click)='cancel()') Cancel
|
@@ -0,0 +1,95 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { map } from 'rxjs/operators'
|
||||
import { ElectronService, HostAppService } from 'terminus-core'
|
||||
import { SerialConnection, LoginScript, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialService } from '../services/serial.service'
|
||||
// import { PromptModalComponent } from './promptModal.component'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./editConnectionModal.component.pug'),
|
||||
})
|
||||
export class EditConnectionModalComponent {
|
||||
connection: SerialConnection
|
||||
foundPorts: SerialPortInfo[]
|
||||
baudRates = BAUD_RATES
|
||||
|
||||
constructor (
|
||||
private modalInstance: NgbActiveModal,
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
private serial: SerialService,
|
||||
) {
|
||||
}
|
||||
|
||||
portsAutocomplete = text$ => text$.pipe(map(() => {
|
||||
return this.foundPorts.map(x => x.name)
|
||||
}))
|
||||
|
||||
portsFormatter = port => {
|
||||
const p = this.foundPorts.find(x => x.name === port)
|
||||
if (p?.description) {
|
||||
return `${port} (${p.description})`
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
async ngOnInit () {
|
||||
this.connection.scripts = this.connection.scripts || []
|
||||
this.foundPorts = await this.serial.listPorts()
|
||||
}
|
||||
|
||||
save () {
|
||||
this.modalInstance.close(this.connection)
|
||||
}
|
||||
|
||||
cancel () {
|
||||
this.modalInstance.dismiss()
|
||||
}
|
||||
|
||||
moveScriptUp (script: LoginScript) {
|
||||
if (!this.connection.scripts) {
|
||||
this.connection.scripts = []
|
||||
}
|
||||
const index = this.connection.scripts.indexOf(script)
|
||||
if (index > 0) {
|
||||
this.connection.scripts.splice(index, 1)
|
||||
this.connection.scripts.splice(index - 1, 0, script)
|
||||
}
|
||||
}
|
||||
|
||||
moveScriptDown (script: LoginScript) {
|
||||
if (!this.connection.scripts) {
|
||||
this.connection.scripts = []
|
||||
}
|
||||
const index = this.connection.scripts.indexOf(script)
|
||||
if (index >= 0 && index < this.connection.scripts.length - 1) {
|
||||
this.connection.scripts.splice(index, 1)
|
||||
this.connection.scripts.splice(index + 1, 0, script)
|
||||
}
|
||||
}
|
||||
|
||||
async deleteScript (script: LoginScript) {
|
||||
if (this.connection.scripts && (await this.electron.showMessageBox(
|
||||
this.hostApp.getWindow(),
|
||||
{
|
||||
type: 'warning',
|
||||
message: 'Delete this script?',
|
||||
detail: script.expect,
|
||||
buttons: ['Keep', 'Delete'],
|
||||
defaultId: 1,
|
||||
}
|
||||
)).response === 1) {
|
||||
this.connection.scripts = this.connection.scripts.filter(x => x !== script)
|
||||
}
|
||||
}
|
||||
|
||||
addScript () {
|
||||
if (!this.connection.scripts) {
|
||||
this.connection.scripts = []
|
||||
}
|
||||
this.connection.scripts.push({ expect: '', send: '' })
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
h3 Connections
|
||||
|
||||
.list-group.list-group-flush.mt-3.mb-3
|
||||
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
*ngFor='let connection of connections',
|
||||
(click)='editConnection(connection)'
|
||||
)
|
||||
.mr-auto
|
||||
div {{connection.name}}
|
||||
.text-muted {{connection.port}}
|
||||
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteConnection(connection)')
|
||||
i.fas.fa-trash
|
||||
|
||||
button.btn.btn-primary((click)='createConnection()')
|
||||
i.fas.fa-fw.fa-plus
|
||||
span.ml-2 Add connection
|
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ConfigService, ElectronService, HostAppService } from 'terminus-core'
|
||||
import { SerialConnection } from '../api'
|
||||
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./serialSettingsTab.component.pug'),
|
||||
})
|
||||
export class SerialSettingsTabComponent {
|
||||
connections: SerialConnection[]
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
private ngbModal: NgbModal,
|
||||
) {
|
||||
this.connections = this.config.store.serial.connections
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
createConnection () {
|
||||
const connection: SerialConnection = {
|
||||
name: '',
|
||||
port: '',
|
||||
baudrate: 115200,
|
||||
databits: 8,
|
||||
parity: 'none',
|
||||
rtscts: false,
|
||||
stopbits: 1,
|
||||
xany: false,
|
||||
xoff: false,
|
||||
xon: false,
|
||||
}
|
||||
|
||||
const modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||
modal.componentInstance.connection = connection
|
||||
modal.result.then(result => {
|
||||
this.connections.push(result)
|
||||
this.config.store.serial.connections = this.connections
|
||||
this.config.save()
|
||||
this.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
editConnection (connection: SerialConnection) {
|
||||
const modal = this.ngbModal.open(EditConnectionModalComponent, { size: 'lg' })
|
||||
modal.componentInstance.connection = Object.assign({}, connection)
|
||||
modal.result.then(result => {
|
||||
Object.assign(connection, result)
|
||||
this.config.store.serial.connections = this.connections
|
||||
this.config.save()
|
||||
this.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
async deleteConnection (connection: SerialConnection) {
|
||||
if ((await this.electron.showMessageBox(
|
||||
this.hostApp.getWindow(),
|
||||
{
|
||||
type: 'warning',
|
||||
message: `Delete "${connection.name}"?`,
|
||||
buttons: ['Keep', 'Delete'],
|
||||
defaultId: 1,
|
||||
}
|
||||
)).response === 1) {
|
||||
this.connections = this.connections.filter(x => x !== connection)
|
||||
this.config.store.serial.connections = this.connections
|
||||
this.config.save()
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
refresh () {
|
||||
this.connections = this.config.store.serial.connections
|
||||
}
|
||||
}
|
16
terminus-serial/src/components/serialTab.component.pug
Normal file
16
terminus-serial/src/components/serialTab.component.pug
Normal file
@@ -0,0 +1,16 @@
|
||||
.tab-toolbar
|
||||
.btn.btn-outline-secondary.reveal-button
|
||||
i.fas.fa-ellipsis-h
|
||||
.toolbar(*ngIf='session', [class.show]='!session.open')
|
||||
i.fas.fa-circle.text-success.mr-2(*ngIf='session.open')
|
||||
i.fas.fa-circle.text-danger.mr-2(*ngIf='!session.open')
|
||||
strong(*ngIf='session') {{session.connection.port}} ({{session.connection.baudrate}})
|
||||
|
||||
.mr-auto
|
||||
|
||||
button.btn.btn-secondary.mr-3((click)='changeBaudRate()', *ngIf='session.open')
|
||||
span Change baud rate
|
||||
|
||||
button.btn.btn-info((click)='reconnect()', *ngIf='!session.open')
|
||||
i.fas.fa-reload
|
||||
span Reconnect
|
7
terminus-serial/src/components/serialTab.component.scss
Normal file
7
terminus-serial/src/components/serialTab.component.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import '../../../terminus-ssh/src/components/sshTab.component.scss';
|
||||
|
||||
:host {
|
||||
select {
|
||||
width: auto;
|
||||
}
|
||||
}
|
118
terminus-serial/src/components/serialTab.component.ts
Normal file
118
terminus-serial/src/components/serialTab.component.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import colors from 'ansi-colors'
|
||||
import { Spinner } from 'cli-spinner'
|
||||
import { Component, Injector } from '@angular/core'
|
||||
import { first } from 'rxjs/operators'
|
||||
import { BaseTerminalTabComponent } from 'terminus-terminal'
|
||||
import { SerialService } from '../services/serial.service'
|
||||
import { SerialConnection, SerialSession, BAUD_RATES } from '../api'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
selector: 'serial-tab',
|
||||
template: BaseTerminalTabComponent.template + require<string>('./serialTab.component.pug'),
|
||||
styles: [require('./serialTab.component.scss'), ...BaseTerminalTabComponent.styles],
|
||||
animations: BaseTerminalTabComponent.animations,
|
||||
})
|
||||
export class SerialTabComponent extends BaseTerminalTabComponent {
|
||||
connection: SerialConnection
|
||||
session: SerialSession
|
||||
serialPort: any
|
||||
private homeEndSubscription: Subscription
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
constructor (
|
||||
injector: Injector,
|
||||
) {
|
||||
super(injector)
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.logger = this.log.create('terminalTab')
|
||||
|
||||
this.homeEndSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
|
||||
if (!this.hasFocus) {
|
||||
return
|
||||
}
|
||||
switch (hotkey) {
|
||||
case 'home':
|
||||
this.sendInput('\x1b[H' )
|
||||
break
|
||||
case 'end':
|
||||
this.sendInput('\x1b[F' )
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
this.frontendReady$.pipe(first()).subscribe(() => {
|
||||
this.initializeSession()
|
||||
})
|
||||
|
||||
super.ngOnInit()
|
||||
|
||||
setImmediate(() => {
|
||||
this.setTitle(this.connection.name)
|
||||
})
|
||||
}
|
||||
|
||||
async initializeSession () {
|
||||
if (!this.connection) {
|
||||
this.logger.error('No Serial connection info supplied')
|
||||
return
|
||||
}
|
||||
|
||||
this.session = this.injector.get(SerialService).createSession(this.connection)
|
||||
this.session.serviceMessage$.subscribe(msg => {
|
||||
this.write('\r\n' + colors.black.bgWhite(' serial ') + ' ' + msg + '\r\n')
|
||||
this.session.resize(this.size.columns, this.size.rows)
|
||||
})
|
||||
this.attachSessionHandlers()
|
||||
this.write(`Connecting to `)
|
||||
|
||||
const spinner = new Spinner({
|
||||
text: 'Connecting',
|
||||
stream: {
|
||||
write: x => this.write(x),
|
||||
},
|
||||
})
|
||||
spinner.setSpinnerString(6)
|
||||
spinner.start()
|
||||
|
||||
try {
|
||||
this.serialPort = await this.injector.get(SerialService).connectSession(this.session)
|
||||
spinner.stop(true)
|
||||
} catch (e) {
|
||||
spinner.stop(true)
|
||||
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
|
||||
return
|
||||
}
|
||||
await this.session.start()
|
||||
this.session.resize(this.size.columns, this.size.rows)
|
||||
}
|
||||
|
||||
async getRecoveryToken (): Promise<any> {
|
||||
return {
|
||||
type: 'app:serial-tab',
|
||||
connection: this.connection,
|
||||
savedState: this.frontend?.saveState(),
|
||||
}
|
||||
}
|
||||
|
||||
reconnect () {
|
||||
this.initializeSession()
|
||||
}
|
||||
|
||||
async changeBaudRate () {
|
||||
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
|
||||
name: x.toString(), result: x,
|
||||
})))
|
||||
this.serialPort.update({ baudRate: rate })
|
||||
this.connection.baudrate = rate
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
this.homeEndSubscription.unsubscribe()
|
||||
super.ngOnDestroy()
|
||||
}
|
||||
}
|
19
terminus-serial/src/config.ts
Normal file
19
terminus-serial/src/config.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ConfigProvider } from 'terminus-core'
|
||||
|
||||
/** @hidden */
|
||||
export class SerialConfigProvider extends ConfigProvider {
|
||||
defaults = {
|
||||
serial: {
|
||||
connections: [],
|
||||
options: {
|
||||
},
|
||||
},
|
||||
hotkeys: {
|
||||
serial: [
|
||||
'Alt-K',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
platformDefaults = { }
|
||||
}
|
17
terminus-serial/src/hotkeys.ts
Normal file
17
terminus-serial/src/hotkeys.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HotkeyDescription, HotkeyProvider } from 'terminus-core'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class SerialHotkeyProvider extends HotkeyProvider {
|
||||
hotkeys: HotkeyDescription[] = [
|
||||
{
|
||||
id: 'serial',
|
||||
name: 'Show Serial connections',
|
||||
},
|
||||
]
|
||||
|
||||
async provide (): Promise<HotkeyDescription[]> {
|
||||
return this.hotkeys
|
||||
}
|
||||
}
|
27
terminus-serial/src/icons/serial.svg
Normal file
27
terminus-serial/src/icons/serial.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="16"
|
||||
viewBox="0 0 32 16"
|
||||
version="1.1"
|
||||
id="svg3749">
|
||||
<defs
|
||||
id="defs3753" />
|
||||
<g
|
||||
id="g3747"
|
||||
transform="matrix(0.48599086,0,0,0.48599086,0.50191451,-0.299629)"
|
||||
style="fill:none;fill-rule:evenodd">
|
||||
<g
|
||||
id="g3741"
|
||||
transform="translate(-292.02353,-314.25882)"
|
||||
style="fill:#ffffff">
|
||||
<path
|
||||
style="fill-rule:nonzero;stroke-width:0.10270619"
|
||||
d="M 16.007812,0 3.2929688,0.03515625 2.2324219,0.40234375 C 0.91449728,1.1071083 0,2.575555 0,3.9863281 c 0,1.2651363 1.3074352,7.8137089 1.7402344,8.7167969 0.553077,1.153703 1.988134,2.456836 3.234375,2.9375 0.8530743,0.328933 1.4753185,0.348528 10.9804686,0.357422 9.789951,0.0091 10.106534,-0.002 11.087891,-0.369141 1.221173,-0.456851 2.835858,-1.955656 3.333984,-3.09375 0.336145,-0.767943 1.638672,-7.5615083 1.638672,-8.5488279 0,-1.4107731 -0.849384,-3.02258715 -2.234375,-3.58398435 L 28.791016,0 Z m -4.27539,4.890625 c 0.427942,0 0.812664,0.071135 1.152344,0.2128906 0.342354,0.1390812 0.631097,0.3382162 0.86914,0.5976563 0.243393,0.2674641 0.430211,0.5965523 0.558594,0.984375 0.131057,0.3878229 0.197266,0.8262256 0.197266,1.3183593 0,0.4921338 -0.06744,0.9337216 -0.201172,1.3242188 -0.131058,0.387823 -0.316645,0.711263 -0.554688,0.970703 -0.246067,0.270139 -0.536042,0.474922 -0.873047,0.611328 -0.33433,0.136407 -0.71782,0.203125 -1.148437,0.203125 -0.419919,0 -0.801456,-0.0699 -1.146484,-0.208984 C 10.243584,10.765216 9.9516549,10.563618 9.7109375,10.298828 9.4702197,10.034039 9.2834009,9.7093672 9.1523438,9.3242188 9.0239608,8.9390707 8.9609375,8.4987146 8.9609375,8.0039062 c 0,-0.4867846 0.063023,-0.9214925 0.1914063,-1.3066406 C 9.2807263,6.3094427 9.4687769,5.9766597 9.7148438,5.7011719 9.950212,5.4390572 10.242141,5.2386906 10.589844,5.0996094 10.940222,4.9605279 11.320527,4.890625 11.732422,4.890625 Z m 9.933594,0 c 0.427942,0 0.812664,0.071135 1.152343,0.2128906 0.342354,0.1390812 0.631098,0.3382162 0.869141,0.5976563 0.243392,0.2674641 0.430211,0.5965523 0.558594,0.984375 0.131057,0.3878229 0.197265,0.8262256 0.197265,1.3183593 0,0.4921338 -0.06744,0.9337216 -0.201171,1.3242188 -0.131058,0.387823 -0.316645,0.711263 -0.554688,0.970703 -0.246067,0.270139 -0.536042,0.474922 -0.873047,0.611328 -0.33433,0.136407 -0.71782,0.203125 -1.148437,0.203125 -0.419919,0 -0.801456,-0.0699 -1.146485,-0.208984 -0.342353,-0.139081 -0.634282,-0.340679 -0.875,-0.605469 C 19.403813,10.034039 19.216995,9.7093672 19.085938,9.3242188 18.957555,8.9390707 18.894531,8.4987146 18.894531,8.0039062 c 0,-0.4867846 0.06302,-0.9214925 0.191407,-1.3066406 0.128382,-0.3878229 0.316433,-0.7206059 0.5625,-0.9960937 0.235368,-0.2621147 0.527296,-0.4624813 0.875,-0.6015625 0.350377,-0.1390815 0.730683,-0.2089844 1.142578,-0.2089844 z m -16.0839848,0.125 h 2.359375 V 5.625 H 7.1582031 v 4.753906 h 0.7832031 v 0.611328 H 5.5820312 V 10.378906 H 6.3652344 V 5.625 H 5.5820312 Z m 9.9335938,0 H 17.875 V 5.625 h -0.783203 v 4.753906 H 17.875 v 0.611328 h -2.359375 v -0.611328 h 0.783203 V 5.625 h -0.783203 z m 9.933594,0 h 2.359375 V 5.625 h -0.783203 v 4.753906 h 0.783203 v 0.611328 h -2.359375 v -0.611328 h 0.783203 V 5.625 h -0.783203 z m -13.712891,0.5625 c -0.607143,0 -1.083937,0.2102191 -1.43164,0.6328125 -0.3450291,0.4199185 -0.5175786,1.0173231 -0.5175786,1.7929687 0,0.7836694 0.1762443,1.3854905 0.5292966,1.8027344 0.353053,0.4145694 0.826152,0.6210934 1.419922,0.6210934 0.59377,0 1.065638,-0.206524 1.416016,-0.6210934 0.353052,-0.4172439 0.529297,-1.019065 0.529297,-1.8027344 0,-0.7756456 -0.173782,-1.3730502 -0.521485,-1.7929687 C 12.812453,5.7883441 12.338122,5.578125 11.736328,5.578125 Z m 9.933594,0 c -0.607144,0 -1.083938,0.2102191 -1.431641,0.6328125 -0.345028,0.4199185 -0.517578,1.0173231 -0.517578,1.7929687 0,0.7836694 0.176244,1.3854905 0.529297,1.8027344 0.353052,0.4145694 0.826152,0.6210934 1.419922,0.6210934 0.59377,0 1.065638,-0.206524 1.416016,-0.6210934 0.353052,-0.4172439 0.529296,-1.019065 0.529296,-1.8027344 0,-0.7756456 -0.173781,-1.3730502 -0.521484,-1.7929687 C 22.746047,5.7883441 22.271716,5.578125 21.669922,5.578125 Z"
|
||||
transform="matrix(2.0576519,0,0,2.0576519,290.99076,314.87535)"
|
||||
id="path3739" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.3 KiB |
48
terminus-serial/src/index.ts
Normal file
48
terminus-serial/src/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ToastrModule } from 'ngx-toastr'
|
||||
import TerminusCoreModule, { ToolbarButtonProvider, ConfigProvider, TabRecoveryProvider, HotkeyProvider } from 'terminus-core'
|
||||
import { SettingsTabProvider } from 'terminus-settings'
|
||||
import TerminusTerminalModule from 'terminus-terminal'
|
||||
|
||||
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
|
||||
import { SerialTabComponent } from './components/serialTab.component'
|
||||
|
||||
import { ButtonProvider } from './buttonProvider'
|
||||
import { SerialConfigProvider } from './config'
|
||||
import { SerialSettingsTabProvider } from './settings'
|
||||
import { RecoveryProvider } from './recoveryProvider'
|
||||
import { SerialHotkeyProvider } from './hotkeys'
|
||||
|
||||
/** @hidden */
|
||||
@NgModule({
|
||||
imports: [
|
||||
NgbModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ToastrModule,
|
||||
TerminusCoreModule,
|
||||
TerminusTerminalModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
||||
{ provide: ConfigProvider, useClass: SerialConfigProvider, multi: true },
|
||||
{ provide: SettingsTabProvider, useClass: SerialSettingsTabProvider, multi: true },
|
||||
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
|
||||
{ provide: HotkeyProvider, useClass: SerialHotkeyProvider, multi: true },
|
||||
],
|
||||
entryComponents: [
|
||||
EditConnectionModalComponent,
|
||||
SerialSettingsTabComponent,
|
||||
SerialTabComponent,
|
||||
],
|
||||
declarations: [
|
||||
EditConnectionModalComponent,
|
||||
SerialSettingsTabComponent,
|
||||
SerialTabComponent,
|
||||
],
|
||||
})
|
||||
export default class SerialModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
21
terminus-serial/src/recoveryProvider.ts
Normal file
21
terminus-serial/src/recoveryProvider.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from 'terminus-core'
|
||||
|
||||
import { SerialTabComponent } from './components/serialTab.component'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class RecoveryProvider extends TabRecoveryProvider {
|
||||
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> {
|
||||
if (recoveryToken?.type === 'app:serial-tab') {
|
||||
return {
|
||||
type: SerialTabComponent,
|
||||
options: {
|
||||
connection: recoveryToken['connection'],
|
||||
savedState: recoveryToken['savedState'],
|
||||
},
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
169
terminus-serial/src/services/serial.service.ts
Normal file
169
terminus-serial/src/services/serial.service.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { Injectable, NgZone } from '@angular/core'
|
||||
import SerialPort from 'serialport'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { LogService, AppService, SelectorOption, ConfigService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SerialConnection, SerialSession, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialTabComponent } from '../components/serialTab.component'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SerialService {
|
||||
private constructor (
|
||||
private log: LogService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
private app: AppService,
|
||||
private config: ConfigService,
|
||||
) { }
|
||||
|
||||
async listPorts (): Promise<SerialPortInfo[]> {
|
||||
return (await SerialPort.list()).map(x => ({
|
||||
name: x.path,
|
||||
description: x.manufacturer || x.serialNumber ? `${x.manufacturer || ''} ${x.serialNumber || ''}` : undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
createSession (connection: SerialConnection): SerialSession {
|
||||
const session = new SerialSession(connection)
|
||||
session.logger = this.log.create(`serial-${connection.port}`)
|
||||
return session
|
||||
}
|
||||
|
||||
async connectSession (session: SerialSession): Promise<SerialPort> {
|
||||
const serial = new SerialPort(session.connection.port, { autoOpen: false, baudRate: session.connection.baudrate,
|
||||
dataBits: session.connection.databits, stopBits: session.connection.stopbits, parity: session.connection.parity,
|
||||
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff,
|
||||
xany: session.connection.xany })
|
||||
session.serial = serial
|
||||
let connected = false
|
||||
await new Promise(async (resolve, reject) => {
|
||||
serial.on('open', () => {
|
||||
connected = true
|
||||
this.zone.run(resolve)
|
||||
})
|
||||
serial.on('error', error => {
|
||||
this.zone.run(() => {
|
||||
if (connected) {
|
||||
this.toastr.error(error.toString())
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
try {
|
||||
serial.open()
|
||||
} catch (e) {
|
||||
this.toastr.error(e.message)
|
||||
reject(e)
|
||||
}
|
||||
|
||||
})
|
||||
return serial
|
||||
}
|
||||
|
||||
async showConnectionSelector (): Promise<void> {
|
||||
const options: SelectorOption<void>[] = []
|
||||
const foundPorts = await this.listPorts()
|
||||
|
||||
try {
|
||||
const lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
|
||||
if (lastConnection) {
|
||||
options.push({
|
||||
name: lastConnection.name,
|
||||
icon: 'history',
|
||||
callback: () => this.connect(lastConnection),
|
||||
})
|
||||
options.push({
|
||||
name: 'Clear last connection',
|
||||
icon: 'eraser',
|
||||
callback: () => {
|
||||
window.localStorage.lastSerialConnection = null
|
||||
},
|
||||
})
|
||||
}
|
||||
} catch { }
|
||||
|
||||
for (const port of foundPorts) {
|
||||
options.push({
|
||||
name: port.name,
|
||||
description: port.description,
|
||||
icon: 'arrow-right',
|
||||
callback: () => this.connectFoundPort(port),
|
||||
})
|
||||
}
|
||||
|
||||
for (const connection of this.config.store.serial.connections) {
|
||||
options.push({
|
||||
name: connection.name,
|
||||
description: connection.port,
|
||||
callback: () => this.connect(connection),
|
||||
})
|
||||
}
|
||||
|
||||
options.push({
|
||||
name: 'Manage connections',
|
||||
icon: 'cog',
|
||||
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' }),
|
||||
})
|
||||
|
||||
options.push({
|
||||
name: 'Quick connect',
|
||||
freeInputPattern: 'Open device: %s...',
|
||||
icon: 'arrow-right',
|
||||
callback: query => this.quickConnect(query),
|
||||
})
|
||||
|
||||
|
||||
await this.app.showSelector('Open a serial port', options)
|
||||
}
|
||||
|
||||
async connect (connection: SerialConnection): Promise<SerialTabComponent> {
|
||||
try {
|
||||
const tab = this.app.openNewTab(
|
||||
SerialTabComponent,
|
||||
{ connection }
|
||||
) as SerialTabComponent
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.app.activeTab.emitFocused()
|
||||
})
|
||||
return tab
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
quickConnect (query: string): Promise<SerialTabComponent> {
|
||||
let path = query
|
||||
let baudrate = 115200
|
||||
if (query.includes('@')) {
|
||||
baudrate = parseInt(path.split('@')[1])
|
||||
path = path.split('@')[0]
|
||||
}
|
||||
const connection: SerialConnection = {
|
||||
name: query,
|
||||
port: path,
|
||||
baudrate: baudrate,
|
||||
databits: 8,
|
||||
parity: 'none',
|
||||
rtscts: false,
|
||||
stopbits: 1,
|
||||
xany: false,
|
||||
xoff: false,
|
||||
xon: false,
|
||||
}
|
||||
window.localStorage.lastSerialConnection = JSON.stringify(connection)
|
||||
return this.connect(connection)
|
||||
}
|
||||
|
||||
async connectFoundPort (port: SerialPortInfo): Promise<SerialTabComponent> {
|
||||
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
|
||||
name: x.toString(), result: x,
|
||||
})))
|
||||
return this.quickConnect(`${port.name}@${rate}`)
|
||||
}
|
||||
}
|
16
terminus-serial/src/settings.ts
Normal file
16
terminus-serial/src/settings.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { SettingsTabProvider } from 'terminus-settings'
|
||||
|
||||
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class SerialSettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'serial'
|
||||
icon = 'keyboard'
|
||||
title = 'Serial'
|
||||
|
||||
getComponentType (): any {
|
||||
return SerialSettingsTabComponent
|
||||
}
|
||||
}
|
7
terminus-serial/tsconfig.json
Normal file
7
terminus-serial/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"exclude": ["node_modules", "dist", "typings"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src"
|
||||
}
|
||||
}
|
15
terminus-serial/tsconfig.typings.json
Normal file
15
terminus-serial/tsconfig.typings.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"exclude": ["node_modules", "dist", "typings"],
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"emitDeclarationOnly": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "./typings",
|
||||
"paths": {
|
||||
"terminus-*": ["../../terminus-*"],
|
||||
"*": ["../../app/node_modules/*"]
|
||||
}
|
||||
}
|
||||
}
|
59
terminus-serial/webpack.config.js
Normal file
59
terminus-serial/webpack.config.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
entry: 'src/index.ts',
|
||||
context: __dirname,
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
pathinfo: true,
|
||||
libraryTarget: 'umd',
|
||||
devtoolModuleFilenameTemplate: 'webpack-terminus-serial:///[resource-path]',
|
||||
},
|
||||
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
|
||||
optimization:{
|
||||
minimize: false,
|
||||
},
|
||||
resolve: {
|
||||
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
|
||||
extensions: ['.ts', '.js', '.node'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: {
|
||||
loader: 'awesome-typescript-loader',
|
||||
options: {
|
||||
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
||||
typeRoots: [
|
||||
path.resolve(__dirname, 'node_modules/@types'),
|
||||
path.resolve(__dirname, '../node_modules/@types'),
|
||||
],
|
||||
paths: {
|
||||
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
|
||||
"*": [path.resolve(__dirname, '../app/node_modules/*')],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
|
||||
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
|
||||
{ test: /\.svg/, use: ['svg-inline-loader'] },
|
||||
],
|
||||
},
|
||||
externals: [
|
||||
'fs',
|
||||
'keytar',
|
||||
'path',
|
||||
'ngx-toastr',
|
||||
'serialport',
|
||||
'windows-process-tree/build/Release/windows_process_tree.node',
|
||||
/^rxjs/,
|
||||
/^@angular/,
|
||||
/^@ng-bootstrap/,
|
||||
/^terminus-/,
|
||||
],
|
||||
}
|
1110
terminus-serial/yarn.lock
Normal file
1110
terminus-serial/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "terminus-settings",
|
||||
"version": "1.0.99-nightly.0",
|
||||
"version": "1.0.104-nightly.0",
|
||||
"description": "Terminus terminal settings page",
|
||||
"keywords": [
|
||||
"terminus-builtin-plugin"
|
||||
|
@@ -27,7 +27,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
||||
title: 'Settings',
|
||||
touchBarNSImage: 'NSTouchBarComposeTemplate',
|
||||
weight: 10,
|
||||
click: () => this.open(),
|
||||
click: (): void => this.open(),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@@ -61,7 +61,7 @@ export class HotkeyInputModalComponent {
|
||||
return keys.split('+').map((x) => x.trim())
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
ngOnInit (): void {
|
||||
this.keyTimeoutInterval = window.setInterval(() => {
|
||||
if (!this.lastKeyEvent) {
|
||||
return
|
||||
@@ -74,14 +74,14 @@ export class HotkeyInputModalComponent {
|
||||
this.hotkeys.disable()
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
ngOnDestroy (): void {
|
||||
this.keySubscription.unsubscribe()
|
||||
this.hotkeys.clearCurrentKeystrokes()
|
||||
this.hotkeys.enable()
|
||||
clearInterval(this.keyTimeoutInterval!)
|
||||
}
|
||||
|
||||
close () {
|
||||
close (): void {
|
||||
this.modalInstance.dismiss()
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ export class MultiHotkeyInputComponent {
|
||||
private ngbModal: NgbModal,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
ngOnInit (): void {
|
||||
if (!this.model) {
|
||||
this.model = []
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export class MultiHotkeyInputComponent {
|
||||
this.model = this.model.map(item => typeof item === 'string' ? [item] : item)
|
||||
}
|
||||
|
||||
editItem (item) {
|
||||
editItem (item: string[]): void {
|
||||
this.ngbModal.open(HotkeyInputModalComponent).result.then((value: string[]) => {
|
||||
this.model[this.model.findIndex(x => x === item)] = value
|
||||
this.model = this.model.slice()
|
||||
@@ -35,14 +35,14 @@ export class MultiHotkeyInputComponent {
|
||||
})
|
||||
}
|
||||
|
||||
addItem () {
|
||||
addItem (): void {
|
||||
this.ngbModal.open(HotkeyInputModalComponent).result.then((value: string[]) => {
|
||||
this.model = this.model.concat([value])
|
||||
this.modelChange.emit(this.model)
|
||||
})
|
||||
}
|
||||
|
||||
removeItem (item) {
|
||||
removeItem (item: string[]): void {
|
||||
this.model = this.model.filter(x => x !== item)
|
||||
this.modelChange.emit(this.model)
|
||||
}
|
||||
|
@@ -88,7 +88,7 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Transparency
|
||||
.title Opacity
|
||||
input(
|
||||
type='range',
|
||||
[(ngModel)]='config.store.appearance.opacity',
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import * as yaml from 'js-yaml'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { Component, Inject, Input, HostBinding, NgZone } from '@angular/core'
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user