Compare commits

..

4 Commits

Author SHA1 Message Date
Eugene Pankov
af458597d8 wip 2020-02-06 16:24:45 +03:00
Eugene Pankov
057c642539 wip 2020-02-06 15:42:30 +03:00
Eugene Pankov
0b14ae1bd6 allow saving terminal state for debugging 2020-02-06 14:47:21 +03:00
Eugene Pankov
f1002082f2 appx build 2020-02-06 14:14:02 +03:00
180 changed files with 2346 additions and 5807 deletions

View File

@@ -243,34 +243,6 @@
"contributions": [ "contributions": [
"code" "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"
]
},
{
"login": "Goobles",
"name": "Gobius Dolhain",
"avatar_url": "https://avatars3.githubusercontent.com/u/8776771?v=4",
"profile": "https://github.com/Goobles",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -99,8 +99,3 @@ rules:
'@typescript-eslint/restrict-template-expressions': off '@typescript-eslint/restrict-template-expressions': off
'@typescript-eslint/no-dynamic-delete': off '@typescript-eslint/no-dynamic-delete': off
'@typescript-eslint/prefer-nullish-coalescing': 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

View File

@@ -11,7 +11,7 @@ jobs:
- name: Installing Node - name: Installing Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 10 version: 10
- name: Build - name: Build
run: | run: |

View File

@@ -11,7 +11,7 @@ jobs:
- name: Installing Node - name: Installing Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 10 version: 10
- name: Install deps - name: Install deps
run: | run: |

View File

@@ -11,7 +11,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 10 version: 10
- name: Install deps - name: Install deps
run: | run: |
@@ -25,6 +25,9 @@ jobs:
- name: Build native deps - name: Build native deps
run: scripts/build-native.js run: scripts/build-native.js
- name: Build typings
run: yarn run build:typings
- name: Webpack - name: Webpack
run: yarn run build run: yarn run build

View File

@@ -11,7 +11,7 @@ jobs:
- name: Installing Node - name: Installing Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 10 version: 10
- name: Install deps - name: Install deps
run: | run: |
@@ -25,24 +25,23 @@ jobs:
- name: Build native deps - name: Build native deps
run: scripts/build-native.js run: scripts/build-native.js
- name: Build typings
run: yarn run build:typings
- name: Webpack - name: Webpack
run: yarn run build run: yarn run build
- name: Prepackage plugins - name: Prepackage plugins
run: scripts/prepackage-plugins.js run: scripts/prepackage-plugins.js
- run: sed -i '' 's/updateInfo = await/\/\/updateInfo = await/g' node_modules/app-builder-lib/out/targets/ArchiveTarget.js
- name: Build and sign packages - name: Build and sign packages
run: scripts/build-macos.js run: scripts/build-macos.js
if: github.repository == 'Eugeny/terminus' && github.event_name == 'push' if: github.repository == 'Eugeny/terminus' && github.event_name == 'push'
env: env:
#DEBUG: electron-builder,electron-builder:* DEBUG: electron-builder,electron-builder:*
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
CSC_LINK: ${{ secrets.CSC_LINK }} CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPSTORE_USERNAME: ${{ secrets.APPSTORE_USERNAME }}
APPSTORE_PASSWORD: ${{ secrets.APPSTORE_PASSWORD }}
- name: Build packages without signing - name: Build packages without signing
run: scripts/build-macos.js run: scripts/build-macos.js

View File

@@ -11,7 +11,7 @@ jobs:
- name: Installing Node - name: Installing Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 10 version: 10
- name: Build - name: Build
shell: powershell shell: powershell
@@ -26,6 +26,7 @@ jobs:
run: node scripts/build-windows.js run: node scripts/build-windows.js
if: github.repository == 'Eugeny/terminus' && github.event_name == 'push' if: github.repository == 'Eugeny/terminus' && github.event_name == 'push'
env: env:
DEBUG: electron-builder,electron-builder:*
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }} WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
@@ -39,7 +40,9 @@ jobs:
mkdir artifact-setup mkdir artifact-setup
mv dist/*-setup.exe artifact-setup/ mv dist/*-setup.exe artifact-setup/
mkdir artifact-portable mkdir artifact-portable
mv dist/*-portable.zip artifact-portable/ mv dist/*-portable.exe artifact-portable/
mkdir artifact-appx
mv dist/*.appx artifact-appx/
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master
name: Upload installer name: Upload installer
@@ -52,3 +55,9 @@ jobs:
with: with:
name: Portable build name: Portable build
path: artifact-portable path: artifact-portable
- uses: actions/upload-artifact@master
name: Upload APPX
with:
name: Portable build
path: artifact-appx

View File

@@ -35,10 +35,6 @@
--- ---
# Portable
For portable in windows, user can create folder `data` at the same directory as `Terminal.exe` to save the settings.
# Plugins # Plugins
Plugins and themes can be installed directly from the Settings view inside Terminus. Plugins and themes can be installed directly from the Settings view inside Terminus.
@@ -106,11 +102,6 @@ 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://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/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/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>
<tr>
<td align="center"><a href="https://github.com/Goobles"><img src="https://avatars3.githubusercontent.com/u/8776771?v=4" width="100px;" alt=""/><br /><sub><b>Gobius Dolhain</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Goobles" title="Code">💻</a></td>
</tr> </tr>
</table> </table>

View File

@@ -8,15 +8,17 @@ html
window.nodeRequire = require window.nodeRequire = require
script(src='./preload.js') script(src='./preload.js')
script(src='./bundle.js', defer) script(src='./bundle.js', defer)
style#custom-css
style. style.
body { transition: 0.5s background; } body { transition: 0.5s background; }
body body
style#custom-css
app-root app-root
.preload-logo .preload-logo
div div
.terminus-logo .terminus-logo
h1.terminus-title Terminus h1.terminus-title Terminus
sup α sup α
.progress .progress
.bar(style='width: 0%') .bar(style='width: 0%')

View File

@@ -1,4 +1,4 @@
import { app, ipcMain, Menu, Tray, shell, globalShortcut } from 'electron' import { app, ipcMain, Menu, Tray, shell } from 'electron'
// eslint-disable-next-line no-duplicate-imports // eslint-disable-next-line no-duplicate-imports
import * as electron from 'electron' import * as electron from 'electron'
import { loadConfig } from './config' import { loadConfig } from './config'
@@ -9,17 +9,8 @@ export class Application {
private windows: Window[] = [] private windows: Window[] = []
constructor () { constructor () {
ipcMain.on('app:config-change', (_event, config) => { ipcMain.on('app:config-change', () => {
this.broadcast('host:config-change', config) this.broadcast('host:config-change')
})
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
globalShortcut.unregisterAll()
for (let spec of specs) {
globalShortcut.register(spec, () => {
this.onGlobalHotkey()
})
}
}) })
const configData = loadConfig() const configData = loadConfig()
@@ -40,7 +31,7 @@ export class Application {
} }
} }
init (): void { init () {
electron.screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed')) electron.screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed'))
} }
@@ -54,9 +45,6 @@ export class Application {
this.enableTray() this.enableTray()
} }
}) })
window.closed$.subscribe(() => {
this.windows = this.windows.filter(x => x !== window)
})
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
this.setupMenu() this.setupMenu()
} }
@@ -64,38 +52,20 @@ export class Application {
return window return window
} }
onGlobalHotkey (): void { broadcast (event, ...args) {
if (this.windows.some(x => x.isFocused())) {
for (let window of this.windows) {
window.hide()
}
} else {
for (let window of this.windows) {
window.present()
}
}
}
presentAllWindows (): void {
for (let window of this.windows) { for (let window of this.windows) {
window.present()
}
}
broadcast (event: string, ...args): void {
for (const window of this.windows) {
window.send(event, ...args) window.send(event, ...args)
} }
} }
async send (event: string, ...args): Promise<void> { async send (event, ...args) {
if (!this.hasWindows()) { if (!this.hasWindows()) {
await this.newWindow() await this.newWindow()
} }
this.windows.filter(w => !w.isDestroyed())[0].send(event, ...args) this.windows.filter(w => !w.isDestroyed())[0].send(event, ...args)
} }
enableTray (): void { enableTray () {
if (this.tray) { if (this.tray) {
return return
} }
@@ -120,30 +90,23 @@ export class Application {
this.tray.setToolTip(`Terminus ${app.getVersion()}`) this.tray.setToolTip(`Terminus ${app.getVersion()}`)
} }
disableTray (): void { disableTray () {
if (this.tray) { if (this.tray) {
this.tray.destroy() this.tray.destroy()
this.tray = null this.tray = null
} }
} }
hasWindows (): boolean { hasWindows () {
return !!this.windows.length return !!this.windows.length
} }
focus (): void { focus () {
for (let window of this.windows) { for (let window of this.windows) {
window.show() window.show()
} }
} }
handleSecondInstance (argv: string[], cwd: string): void {
this.presentAllWindows()
for (let window of this.windows) {
window.handleSecondInstance(argv, cwd)
}
}
private setupMenu () { private setupMenu () {
let template: Electron.MenuItemConstructorOptions[] = [ let template: Electron.MenuItemConstructorOptions[] = [
{ {

View File

@@ -1,6 +1,6 @@
import { app } from 'electron' import { app } from 'electron'
export function parseArgs (argv: string[], cwd: string): any { export function parseArgs (argv, cwd) {
if (argv[0].includes('node')) { if (argv[0].includes('node')) {
argv = argv.slice(1) argv = argv.slice(1)
} }

View File

@@ -34,7 +34,7 @@ process.on('uncaughtException' as any, err => {
}) })
app.on('second-instance', (_event, argv, cwd) => { app.on('second-instance', (_event, argv, cwd) => {
application.handleSecondInstance(argv, cwd) application.send('host:second-instance', parseArgs(argv, cwd), cwd)
}) })
const argv = parseArgs(process.argv, process.cwd()) const argv = parseArgs(process.argv, process.cwd())

View File

@@ -1,24 +1,15 @@
import * as path from 'path' import * as path from 'path'
import * as fs from 'fs' import * as fs from 'fs'
let appPath: string | null = null if (process.env.PORTABLE_EXECUTABLE_DIR) {
try { const portableData = path.join(process.env.PORTABLE_EXECUTABLE_DIR, 'terminus-data')
appPath = path.dirname(require('electron').app.getPath('exe')) if (!fs.existsSync(portableData)) {
} catch { fs.mkdirSync(portableData)
appPath = path.dirname(require('electron').remote.app.getPath('exe')) }
}
if (null != appPath) { try {
if(fs.existsSync(path.join(appPath, 'terminus-data'))) { require('electron').app.setPath('userData', portableData)
fs.renameSync(path.join(appPath, 'terminus-data'), path.join(appPath, 'data')) } catch {
} require('electron').remote.app.setPath('userData', portableData)
const portableData = path.join(appPath, 'data')
if (fs.existsSync(portableData)) {
console.log('reset user data to ' + portableData)
try {
require('electron').app.setPath('userData', portableData)
} catch {
require('electron').remote.app.setPath('userData', portableData)
}
} }
} }

View File

@@ -1,11 +1,10 @@
import { Subject, Observable } from 'rxjs' import { Subject, Observable } from 'rxjs'
import { debounceTime } from 'rxjs/operators' import { debounceTime } from 'rxjs/operators'
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen } from 'electron' import { BrowserWindow, app, ipcMain, Rectangle, screen } from 'electron'
import ElectronConfig = require('electron-config') import ElectronConfig = require('electron-config')
import * as os from 'os' import * as os from 'os'
import * as path from 'path' import * as path from 'path'
import { parseArgs } from './cli'
import { loadConfig } from './config' import { loadConfig } from './config'
let SetWindowCompositionAttribute: any let SetWindowCompositionAttribute: any
@@ -24,20 +23,17 @@ export interface WindowOptions {
export class Window { export class Window {
ready: Promise<void> ready: Promise<void>
private visible = new Subject<boolean>() private visible = new Subject<boolean>()
private closed = new Subject<void>()
private window: BrowserWindow private window: BrowserWindow
private windowConfig: ElectronConfig private windowConfig: ElectronConfig
private windowBounds: Rectangle private windowBounds: Rectangle
private closing = false private closing = false
private lastVibrancy: {enabled: boolean, type?: string} | null = null private lastVibrancy: {enabled: boolean, type?: string} | null = null
private disableVibrancyWhileDragging = false private disableVibrancyWhileDragging = false
private configStore: any
get visible$ (): Observable<boolean> { return this.visible } get visible$ (): Observable<boolean> { return this.visible }
get closed$ (): Observable<void> { return this.closed }
constructor (options?: WindowOptions) { constructor (options?: WindowOptions) {
this.configStore = loadConfig() let configData = loadConfig()
options = options || {} options = options || {}
@@ -74,7 +70,7 @@ export class Window {
} }
} }
if ((this.configStore.appearance || {}).frame === 'native') { if ((configData.appearance || {}).frame === 'native') {
bwOptions.frame = true bwOptions.frame = true
} else { } else {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@@ -90,7 +86,7 @@ export class Window {
this.window.once('ready-to-show', () => { this.window.once('ready-to-show', () => {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
this.window.setVibrancy('window') this.window.setVibrancy('window')
} else if (process.platform === 'win32' && (this.configStore.appearance || {}).vibrancy) { } else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
this.setVibrancy(true) this.setVibrancy(true)
} }
@@ -101,13 +97,6 @@ export class Window {
this.window.show() this.window.show()
} }
this.window.focus() this.window.focus()
this.window.moveTop()
}
})
this.window.on('blur', () => {
if (this.configStore.appearance?.dockHideOnBlur) {
this.hide()
} }
}) })
@@ -130,7 +119,7 @@ export class Window {
}) })
} }
setVibrancy (enabled: boolean, type?: string): void { setVibrancy (enabled: boolean, type?: string) {
this.lastVibrancy = { enabled, type } this.lastVibrancy = { enabled, type }
if (process.platform === 'win32') { if (process.platform === 'win32') {
if (parseFloat(os.release()) >= 10) { if (parseFloat(os.release()) >= 10) {
@@ -151,74 +140,25 @@ export class Window {
} }
} }
show (): void { show () {
this.window.show() this.window.show()
this.window.moveTop()
} }
focus (): void { focus () {
this.window.focus() this.window.focus()
} }
send (event: string, ...args): void { send (event, ...args) {
if (!this.window) { if (!this.window) {
return return
} }
this.window.webContents.send(event, ...args) this.window.webContents.send(event, ...args)
if (event === 'host:config-change') {
this.configStore = args[0]
}
} }
isDestroyed (): boolean { isDestroyed () {
return !this.window || this.window.isDestroyed() return !this.window || this.window.isDestroyed()
} }
isFocused (): boolean {
return this.window.isFocused()
}
hide (): void {
if (process.platform === 'darwin') {
// Lose focus
Menu.sendActionToFirstResponder('hide:')
}
this.window.blur()
if (process.platform !== 'darwin') {
this.window.hide()
}
}
present (): void {
if (!this.window.isVisible()) {
// unfocused, invisible
this.window.show()
this.window.focus()
} else {
if (!this.configStore.appearance?.dock || this.configStore.appearance?.dock === 'off') {
// not docked, visible
setTimeout(() => {
this.window.show()
this.window.focus()
})
} else {
if (this.configStore.appearance?.dockAlwaysOnTop) {
// docked, visible, on top
this.window.hide()
} else {
// docked, visible, not on top
this.window.focus()
}
}
}
}
handleSecondInstance (argv: string[], cwd: string): void {
if (!this.configStore.appearance?.dock) {
this.send('host:second-instance', parseArgs(argv, cwd), cwd)
}
}
private setupWindowManagement () { private setupWindowManagement () {
this.window.on('show', () => { this.window.on('show', () => {
this.visible.next(true) this.visible.next(true)
@@ -386,8 +326,6 @@ export class Window {
private destroy () { private destroy () {
this.window = null this.window = null
this.closed.next()
this.visible.complete() this.visible.complete()
this.closed.complete()
} }
} }

View File

@@ -13,35 +13,34 @@
"watch": "webpack --progress --color --watch" "watch": "webpack --progress --color --watch"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "9.1.9", "@angular/animations": "7.2.8",
"@angular/common": "9.1.9", "@angular/common": "7.2.8",
"@angular/compiler": "9.1.9", "@angular/compiler": "7.2.8",
"@angular/core": "9.1.9", "@angular/core": "7.2.8",
"@angular/forms": "9.1.9", "@angular/forms": "7.2.8",
"@angular/platform-browser": "9.1.9", "@angular/platform-browser": "7.2.8",
"@angular/platform-browser-dynamic": "9.1.9", "@angular/platform-browser-dynamic": "7.2.8",
"@ng-bootstrap/ng-bootstrap": "^6.1.0", "@ng-bootstrap/ng-bootstrap": "^4.2.2",
"devtron": "1.4.0", "devtron": "1.4.0",
"electron-config": "2.0.0", "electron-config": "2.0.0",
"electron-debug": "^3.0.1", "electron-debug": "^3.0.1",
"electron-is-dev": "1.1.0", "electron-is-dev": "1.1.0",
"electron-updater": "^4.3.1", "electron-updater": "^4.2.0",
"fontmanager-redux": "0.4.0", "fontmanager-redux": "0.4.0",
"js-yaml": "3.14.0", "js-yaml": "3.13.1",
"keytar": "^5.6.0", "keytar": "^5.1.0",
"mz": "^2.7.0", "mz": "^2.7.0",
"ngx-toastr": "^12.0.1", "ngx-toastr": "^10.2.0",
"node-pty": "^0.10.0-beta9", "node-pty": "^0.10.0-beta2",
"npm": "6.9.0", "npm": "6.9.0",
"path": "0.12.7", "path": "0.12.7",
"rxjs": "^6.5.5", "rxjs": "^6.5.4",
"rxjs-compat": "^6.5.5", "rxjs-compat": "^6.5.4",
"yargs": "^15.3.1", "yargs": "^15.1.0",
"zone.js": "^0.10.3" "zone.js": "^0.8.29"
}, },
"optionalDependencies": { "optionalDependencies": {
"macos-native-processlist": "^1.0.2", "macos-native-processlist": "^1.0.2",
"serialport": "^9.0.0",
"windows-blurbehind": "^1.0.1", "windows-blurbehind": "^1.0.1",
"windows-native-registry": "^1.0.17", "windows-native-registry": "^1.0.17",
"windows-process-tree": "^0.2.4", "windows-process-tree": "^0.2.4",
@@ -50,6 +49,6 @@
"devDependencies": { "devDependencies": {
"@types/mz": "0.0.32", "@types/mz": "0.0.32",
"@types/node": "12.7.12", "@types/node": "12.7.12",
"node-abi": "^2.17.0" "node-abi": "^2.13.0"
} }
} }

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser' import { BrowserModule } from '@angular/platform-browser'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
@@ -8,7 +7,7 @@ export function getRootModule (plugins: any[]) {
const imports = [ const imports = [
BrowserModule, BrowserModule,
...plugins, ...plugins,
NgbModule, NgbModule.forRoot(),
ToastrModule.forRoot({ ToastrModule.forRoot({
positionClass: 'toast-bottom-center', positionClass: 'toast-bottom-center',
toastClass: 'toast', toastClass: 'toast',

View File

@@ -2,111 +2,61 @@
# yarn lockfile v1 # yarn lockfile v1
"@angular/animations@9.1.9": "@angular/animations@7.2.8":
version "9.1.9" version "7.2.8"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.1.9.tgz#de54334ea195189402487855c9a98f5618605da4" resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-7.2.8.tgz#0285364c839c660a934ab0f753ec21424bfb292e"
integrity sha512-qWVi0TxmU6HeXAgEsfpQvFFygh+a0kH2kGe6bWij4XvG6dWfV3xZjlaFwSIYGk+yK4yL0+9+PAXH+ENfxNw+Cw== integrity sha512-dJn9koYukyz15TouBc+z5z9fdThDk+bKgdlij25eYSu5Mpmtk04gB4eIMQA97K0UDh1d4YukgSJ5w3ZIk0m8DQ==
"@angular/common@9.1.9":
version "9.1.9"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.1.9.tgz#16e77b2db675b80e32f1788a20c538150fd09294"
integrity sha512-y/tJtkuOJhV2kcaXZyrLZH84i4uQ1r+vaaEHvXj+JZYfYfcMMd/TDqMiPcIkUb3RxqghtZ+q0ZNW5D1Nlru3Pw==
"@angular/compiler@9.1.9":
version "9.1.9"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.1.9.tgz#cbf678ee28a0811a8ef3ee7be565d4911ff28ec7"
integrity sha512-kjFgaTB2ckr9lgmkS1dOGRT7kmzpQueydxsxXSHWgICNVE6F/u1PHyeSOyJRpxW0GnrkLq3QM2EUFnQGGga5bg==
"@angular/core@9.1.9":
version "9.1.9"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.9.tgz#db4241f867d6e14b81ed6e7c50334813c6ebfc10"
integrity sha512-q/DERgVU6vK2LtTcdVCGGBcoO424WsEfImh3Vcuy+P/ZVmthlDUC/+q+tSKt8MMf4hLpxFDQJE8vUSkktj7QEw==
"@angular/forms@9.1.9":
version "9.1.9"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.1.9.tgz#20c9a79d1dcb2cace45df9e2f304b658e02c1687"
integrity sha512-r675yImnb/0pY7K5W3V2ITa7YETu1I2AS+bRfII6UQ6gthyeFFOHb5noa7YneC2yqQiM6E4DQmF5ig3daPuFNg==
"@angular/platform-browser-dynamic@9.1.9":
version "9.1.9"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.9.tgz#12f8b05d3c9ef0844df88f3833e29ea1e49ec5e0"
integrity sha512-b9MG5MWne+IuL3uLm8jwPhlJzqYaGBGk/qibOqb17T24j1iyrlO7T5bZ8zO6pUy5iT/TahVfHPnPJC1qTK5OmA==
"@angular/platform-browser@9.1.9":
version "9.1.9"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.1.9.tgz#c2fcc50aebfdc268521b407e32dc0d967cb40411"
integrity sha512-V861X3MxJp1AlMTnkUPldpBLIJbApXF3ka0A5Dq2nVJCyOFeteGkaRWSBgqe2jxmq+LVpJbzcNvtDFXw6mQ0jA==
"@ng-bootstrap/ng-bootstrap@^6.1.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.1.0.tgz#fce7550a095aeac42108f76ac1ebd63caf8304e9"
integrity sha512-2GzkNJBKdeHkaUqaCAqSILPft0IzzHjMfAlAuGY6/ZLlVQ0glt5MTbIXtIhSbjR+OvlrljoXFLrvzs1LGdmE+A==
"@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: dependencies:
debug "^4.1.1" tslib "^1.9.0"
"@serialport/binding-mock@^8.0.6": "@angular/common@7.2.8":
version "8.0.6" version "7.2.8"
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-8.0.6.tgz#41a8f827269c6a0e58546513a274e12023134155" resolved "https://registry.yarnpkg.com/@angular/common/-/common-7.2.8.tgz#660c816b6f08cd2919a6efb7465397e4ff14d265"
integrity sha512-BIbY5/PsDDo0QWDNCCxDgpowAdks+aZR8BOsEtK2GoASTTcJCy1fBwPIfH870o7rnbH901wY3C+yuTfdOvSO9A== integrity sha512-LgOhf68+LPndGZhtnUlGFd2goReXYmHzaFZW8gCEi9aC+H+Io8bjYh0gkH3xDreevEOe3f0z6coXNFLIxSmTuA==
dependencies: dependencies:
"@serialport/binding-abstract" "^8.0.6" tslib "^1.9.0"
debug "^4.1.1"
"@serialport/bindings@^8.0.7": "@angular/compiler@7.2.8":
version "8.0.7" version "7.2.8"
resolved "https://registry.yarnpkg.com/@serialport/bindings/-/bindings-8.0.7.tgz#2a58f60f1e24ee4549be6f9e0e37b3359d038a1c" resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-7.2.8.tgz#9d9c1515e99914399e6915c1c90484b1d255560b"
integrity sha512-IqudDL8ne2Y2S0W5fKA6wdgHCIA2e2OIaPVYhGy6duE6legNHFY+05CLicHAyAeTocXmHU7rVNxzVQrOG5tM4g== integrity sha512-PrU97cTsOdofpaDkxK0rWUA/CGd0u6ESOI6XvFVm5xH9zJInsdY8ShSHklnr1JJnss70e1dGKZbZq32OChxWMw==
dependencies: dependencies:
"@serialport/binding-abstract" "^8.0.6" tslib "^1.9.0"
"@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": "@angular/core@7.2.8":
version "8.0.6" version "7.2.8"
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-8.0.6.tgz#efb6195692b1088e6c095fd43bae196fc30674d0" resolved "https://registry.yarnpkg.com/@angular/core/-/core-7.2.8.tgz#6586d9b6c6321c80119b3f3e2bd0edbb32d0b649"
integrity sha512-92mrFxFEvq3gRvSM7ANK/jfbmHslz91a5oYJy/nbSn4H/MCRXjxR2YOkQgVXuN+zLt+iyDoW3pcOP4Sc1nWdqQ== integrity sha512-QKwug2kWJC00zm2rvmD9mCJzsOkMVhSu8vqPWf83poWTh8+F9aIVWcy29W0VoGpBkSchOnK8hf9DnKVv28j9nw==
"@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: dependencies:
"@serialport/parser-delimiter" "^8.0.6" tslib "^1.9.0"
"@serialport/parser-ready@^8.0.6": "@angular/forms@7.2.8":
version "8.0.6" version "7.2.8"
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-8.0.6.tgz#d6e95e53ee70d298ae0b4147995007f4ba62651c" resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-7.2.8.tgz#adf194088495822d55dcf3e5bf69196dcf19465d"
integrity sha512-xcEqv4rc119WR5JzAuu8UeJOlAwET2PTdNb6aIrrLlmTxhvuBbuRFcsnF3BpH9jUL30Kh7a6QiNXIwVG+WR/1Q== integrity sha512-lbSX4IHFHz/c4e2RHiPpL8MJlzDkCuQEHnqsujDaV2X9o9fApS6+C1X4x7Z2XDKqonmeX+aHQwv9+SLejX6OyQ==
"@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: dependencies:
debug "^4.1.1" tslib "^1.9.0"
"@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@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"
"@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==
dependencies:
tslib "^1.9.0"
"@types/color-name@^1.1.1": "@types/color-name@^1.1.1":
version "1.1.1" version "1.1.1"
@@ -130,12 +80,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
"@types/semver@^7.1.0": "@types/semver@^6.0.2":
version "7.1.0" version "6.2.0"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA== integrity sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA==
dependencies:
"@types/node" "*"
JSONStream@^1.3.4, JSONStream@^1.3.5: JSONStream@^1.3.4, JSONStream@^1.3.5:
version "1.3.5" version "1.3.5"
@@ -290,11 +238,6 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
at-least-node@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
aws-sign2@~0.7.0: aws-sign2@~0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@@ -328,13 +271,6 @@ bin-links@^1.1.2:
graceful-fs "^4.1.11" graceful-fs "^4.1.11"
write-file-atomic "^2.3.0" 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: bl@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
@@ -380,10 +316,10 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
builder-util-runtime@8.7.0: builder-util-runtime@8.4.0:
version "8.7.0" version "8.4.0"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.0.tgz#e48ad004835c8284662e8eaf47a53468c66e8e8d" resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.4.0.tgz#3163fffc078e6b8f3dd5b6eb12a8345573590682"
integrity sha512-G1AqqVM2vYTrSFR982c1NNzwXKrGLQjVjaZaWQdn4O6Z3YKjdMDofw88aD9jpyK9ZXkrCxR0tI3Qe9wNbyTlXg== integrity sha512-CJB/eKfPf2vHrkmirF5eicVnbDCkMBbwd5tRYlTlgud16zFeqD7QmrVUAOEXdnsrcNkiLg9dbuUsQKtl/AwsYQ==
dependencies: dependencies:
debug "^4.1.1" debug "^4.1.1"
sax "^1.2.4" sax "^1.2.4"
@@ -862,18 +798,19 @@ electron-localshortcut@^3.1.0:
keyboardevent-from-electron-accelerator "^1.1.0" keyboardevent-from-electron-accelerator "^1.1.0"
keyboardevents-areequal "^0.2.1" keyboardevents-areequal "^0.2.1"
electron-updater@^4.3.1: electron-updater@^4.2.0:
version "4.3.1" version "4.2.0"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.3.1.tgz#9d485b6262bc56fcf7ee62b1dc1b3b105a3e96a7" resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.0.tgz#f9ecfc657f65ead737d42b9efecf628d3756b550"
integrity sha512-UDC5AHCgeiHJYDYWZG/rsl1vdAFKqI/Lm7whN57LKAk8EfhTewhcEHzheRcncLgikMcQL8gFo1KeX51tf5a5Wg== integrity sha512-GuS3g7HDh17x/SaFjxjswlWUaKHczksYkV2Xc5CKj/bZH0YCvTSHtOmnBAdAmCk99u/71p3zP8f0jIqDfGcjww==
dependencies: dependencies:
"@types/semver" "^7.1.0" "@types/semver" "^6.0.2"
builder-util-runtime "8.7.0" builder-util-runtime "8.4.0"
fs-extra "^9.0.0" fs-extra "^8.1.0"
js-yaml "^3.13.1" js-yaml "^3.13.1"
lazy-val "^1.0.4" lazy-val "^1.0.4"
lodash.isequal "^4.5.0" lodash.isequal "^4.5.0"
semver "^7.1.3" pako "^1.0.10"
semver "^6.3.0"
emoji-regex@^8.0.0: emoji-regex@^8.0.0:
version "8.0.0" version "8.0.0"
@@ -988,11 +925,6 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== 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: find-npm-prefix@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf" resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
@@ -1068,15 +1000,14 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-extra@^9.0.0: fs-extra@^8.1.0:
version "9.0.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies: dependencies:
at-least-node "^1.0.0"
graceful-fs "^4.2.0" graceful-fs "^4.2.0"
jsonfile "^6.0.1" jsonfile "^4.0.0"
universalify "^1.0.0" universalify "^0.1.0"
fs-minipass@^1.2.5: fs-minipass@^1.2.5:
version "1.2.6" version "1.2.6"
@@ -1489,10 +1420,10 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
js-yaml@3.14.0, js-yaml@^3.13.1: js-yaml@3.13.1, js-yaml@^3.13.1:
version "3.14.0" version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
dependencies: dependencies:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
@@ -1522,12 +1453,10 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsonfile@^6.0.1: jsonfile@^4.0.0:
version "6.0.1" version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg== integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
dependencies:
universalify "^1.0.0"
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.6" graceful-fs "^4.1.6"
@@ -1556,12 +1485,12 @@ keyboardevents-areequal@^0.2.1:
resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194" resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194"
integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw== integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw==
keytar@^5.6.0: keytar@^5.1.0:
version "5.6.0" version "5.1.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.6.0.tgz#7b5d4bd043d17211163640be6c4a27a49b12bb39" resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.1.0.tgz#d572ed9250ff2b4c8d729621397e00b17bfa5581"
integrity sha512-ueulhshHSGoryfRXaIvTj0BV1yB0KddBGhGoqCxSN9LR1Ks1GKuuCdVhF+2/YOs5fMl6MlTI9On1a4DHDXoTow== integrity sha512-SptCrRDqLbTeOMB2Z9UmVOS+OKguIrMft+EUaCB8xJPiFMjy6Jnmjgv/LA0rg1ENgLelzwSsC5PSQXF0uoqNDQ==
dependencies: dependencies:
nan "2.14.1" nan "2.14.0"
prebuild-install "5.3.3" prebuild-install "5.3.3"
latest-version@^3.0.0: latest-version@^3.0.0:
@@ -1979,25 +1908,27 @@ mz@^2.7.0:
object-assign "^4.0.1" object-assign "^4.0.1"
thenify-all "^1.0.0" thenify-all "^1.0.0"
nan@2.14.1, nan@^2.13.2, nan@^2.14.0: nan@2.14.0, nan@^2.13.2, nan@^2.14.0:
version "2.14.1" version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
napi-build-utils@^1.0.1: napi-build-utils@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508"
integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA== integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==
ngx-toastr@^12.0.1: ngx-toastr@^10.2.0:
version "12.0.1" version "10.2.0"
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-12.0.1.tgz#288c8ef505f1216aa4952cd2a8c6fa7c57a54ccc" resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-10.2.0.tgz#8a79008de0b1c013f90120a53e0355af5762e969"
integrity sha512-PABtbn2dyHweVSbo/py1W3veXzcmZO7uVItfTW9AykSSeAUju3gOCgauAw89km0aJ9EBcPOieaoI+9tAR7Pfug== integrity sha512-6ASr5bcvQmtNKb4D2VEsQjCXyROq6GwberBWO0bVt+xcBYPUea4aRTgX8in9apX9buaTafzG+h3HlnIraspoPg==
dependencies:
tslib "^1.9.0"
node-abi@^2.16.0, node-abi@^2.7.0: node-abi@^2.13.0, node-abi@^2.7.0:
version "2.16.0" version "2.13.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.16.0.tgz#7df94e9c0a7a189f4197ab84bac8089ef5894992" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.13.0.tgz#e2f2ec444d0aca3ea1b3874b6de41d1665828f63"
integrity sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA== integrity sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==
dependencies: dependencies:
semver "^5.4.1" semver "^5.4.1"
@@ -2045,10 +1976,10 @@ node-gyp@^4.0.0:
tar "^4.4.8" tar "^4.4.8"
which "1" which "1"
node-pty@^0.10.0-beta9: node-pty@^0.10.0-beta2:
version "0.10.0-beta9" version "0.10.0-beta3"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta9.tgz#e5a795f9b53948346803cb71bac4ffc02e7909f0" resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta3.tgz#a33c9fc67c9e4d4f124111e1da2a72b0783008e7"
integrity sha512-Qm6uSH30FUcAhJ9s76C+lgvTsOW2cHUbkIGjCdOVCL0c7S4DxsmKBRgjcr+guUK9d9KwfuZHeSjXYWjpJFPe4w== integrity sha512-j7MoJ3K999jrT9gAVs7JvM/skAQXQITrZK/PhL9B4W4GAPkANKwdu9uEtNvYionQ9dV8gRGte7lg9D2cRDdAiA==
dependencies: dependencies:
nan "^2.14.0" nan "^2.14.0"
@@ -2464,6 +2395,11 @@ pacote@^9.1.0, pacote@^9.2.3, pacote@^9.5.0:
unique-filename "^1.1.1" unique-filename "^1.1.1"
which "^1.3.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==
parallel-transform@^1.1.0: parallel-transform@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"
@@ -2528,7 +2464,7 @@ pkg-up@^2.0.0:
dependencies: dependencies:
find-up "^2.1.0" find-up "^2.1.0"
prebuild-install@5.3.3, prebuild-install@^5.3.0: prebuild-install@5.3.3:
version "5.3.3" version "5.3.3"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e"
integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==
@@ -2865,15 +2801,15 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies: dependencies:
aproba "^1.1.1" aproba "^1.1.1"
rxjs-compat@^6.5.5: rxjs-compat@^6.5.4:
version "6.5.5" version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.5.5.tgz#073c40510f29c45a2a5fc02dde87f8c3ad75f2c2" resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.5.4.tgz#03825692af3fe363e04c43f41ff4113d76bbd305"
integrity sha512-F42sssVbUyWH4vJswEo6m+Eh02xHv3q93n8S7nUJO58R7sbc3CvJIOts605zdaBhWa1xMB9aVSyqPqhQ5q3eXg== integrity sha512-rkn+lbOHUQOurdd74J/hjmDsG9nFx0z66fvnbs8M95nrtKvNqCKdk7iZqdY51CGmDemTQk+kUPy4s8HVOHtkfA==
rxjs@^6.5.5: rxjs@^6.5.4:
version "6.5.5" version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
@@ -2904,32 +2840,16 @@ semver-diff@^2.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@^7.1.3: semver@^6.3.0:
version "7.1.3" version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@~5.3.0: semver@~5.3.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= 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: set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -3343,10 +3263,10 @@ unique-string@^1.0.0:
dependencies: dependencies:
crypto-random-string "^1.0.0" crypto-random-string "^1.0.0"
universalify@^1.0.0: universalify@^0.1.0:
version "1.0.0" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
unpipe@~1.0.0: unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
@@ -3566,10 +3486,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" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
yargs-parser@^18.1.1: yargs-parser@^16.1.0:
version "18.1.1" version "16.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1"
integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==
dependencies: dependencies:
camelcase "^5.0.0" camelcase "^5.0.0"
decamelize "^1.2.0" decamelize "^1.2.0"
@@ -3599,10 +3519,10 @@ yargs@^11.0.0:
y18n "^3.2.1" y18n "^3.2.1"
yargs-parser "^9.0.2" yargs-parser "^9.0.2"
yargs@^15.3.1: yargs@^15.1.0:
version "15.3.1" version "15.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219"
integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==
dependencies: dependencies:
cliui "^6.0.0" cliui "^6.0.0"
decamelize "^1.2.0" decamelize "^1.2.0"
@@ -3614,9 +3534,9 @@ yargs@^15.3.1:
string-width "^4.2.0" string-width "^4.2.0"
which-module "^2.0.0" which-module "^2.0.0"
y18n "^4.0.0" y18n "^4.0.0"
yargs-parser "^18.1.1" yargs-parser "^16.1.0"
zone.js@^0.10.3: zone.js@^0.8.29:
version "0.10.3" version "0.8.29"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.29.tgz#8dce92aa0dd553b50bc5bfbb90af9986ad845a12"
integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== integrity sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==

View File

@@ -1,16 +0,0 @@
const fs = require('fs')
const signHook = require('./afterSignHook')
module.exports = async function (params) {
// notarize the app on Mac OS only.
if (process.platform !== 'darwin' || !process.env.GITHUB_REF || !process.env.GITHUB_REF.startsWith('refs/tags/')) {
return
}
console.log('afterBuild hook triggered')
let pkgName = fs.readdirSync('dist').find(x => x.endsWith('.pkg'))
signHook({
appOutDir: 'dist',
_pathOverride: pkgName,
})
}

View File

@@ -6,14 +6,14 @@ const notarizer = require('electron-notarize')
module.exports = async function (params) { module.exports = async function (params) {
// notarize the app on Mac OS only. // notarize the app on Mac OS only.
if (process.platform !== 'darwin' || !process.env.GITHUB_REF || !process.env.GITHUB_REF.startsWith('refs/tags/')) { if (process.platform !== 'darwin' || process.env.GITHUB_REF !== 'refs/heads/master' || process.env.GITHUB_REF && !process.env.GITHUB_REF.startsWith('refs/tags/')) {
return return
} }
console.log('afterSign hook triggered', params) console.log('afterSign hook triggered', params)
let appId = 'org.terminus' let appId = 'org.terminus'
let appPath = path.join(params.appOutDir, params._pathOverride || `${params.packager.appInfo.productFilename}.app`) let appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`)
if (!fs.existsSync(appPath)) { if (!fs.existsSync(appPath)) {
throw new Error(`Cannot find application at: ${appPath}`) throw new Error(`Cannot find application at: ${appPath}`)
} }

View File

@@ -3,7 +3,6 @@ appId: org.terminus
productName: Terminus productName: Terminus
compression: normal compression: normal
afterSign: "./build/mac/afterSignHook.js" afterSign: "./build/mac/afterSignHook.js"
afterAllArtifactBuild: "./build/mac/afterBuildHook.js"
files: files:
- "**/*" - "**/*"
- dist - dist
@@ -15,12 +14,18 @@ publish:
win: win:
icon: "./build/windows/icon.ico" icon: "./build/windows/icon.ico"
artifactName: terminus-${version}-portable.${ext} artifactName: terminus-${version}-setup.exe
rfc3161TimeStampServer: http://sha256timestamp.ws.symantec.com/sha256/timestamp rfc3161TimeStampServer: http://sha256timestamp.ws.symantec.com/sha256/timestamp
nsis: nsis:
oneClick: false oneClick: false
artifactName: terminus-${version}-setup.${ext} artifactName: terminus-${version}-setup.${ext}
installerIcon: "./build/windows/icon.ico" installerIcon: "./build/windows/icon.ico"
portable:
artifactName: terminus-${version}-portable.exe
appx:
artifactName: terminus-${version}.appx
publisher: 'CN="Syslink GmbH",O="Syslink GmbH",PostalCode=40477,STREET="Parkstrasse 31", L=Duesseldorf, OID.2.5.4.8=NRW, C=DE'
publisherDisplayName: 'Eugene Pankov'
mac: mac:
category: public.app-category.video category: public.app-category.video
@@ -49,7 +54,6 @@ deb:
depends: depends:
- gconf2 - gconf2
- gconf-service - gconf-service
- gnome-keyring
- libnotify4 - libnotify4
- libsecret-1-0 - libsecret-1-0
- libappindicator1 - libappindicator1
@@ -59,4 +63,4 @@ deb:
rpm: rpm:
depends: depends:
- screen - screen
- gnome-keyring - gnome-python2-gnomekeyring

Binary file not shown.

Binary file not shown.

View File

@@ -1,35 +1,35 @@
{ {
"devDependencies": { "devDependencies": {
"@fortawesome/fontawesome-free": "^5.13.0", "@fortawesome/fontawesome-free": "^5.12.0",
"@sentry/cli": "^1.52.3", "@sentry/cli": "^1.49.0",
"@sentry/electron": "^1.2.1", "@sentry/electron": "^1.0.0",
"@types/electron-config": "^3.2.2", "@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0", "@types/electron-debug": "^2.1.0",
"@types/js-yaml": "^3.12.4", "@types/js-yaml": "^3.12.1",
"@types/node": "12.7.12", "@types/node": "12.7.12",
"@types/webpack-env": "^1.15.2", "@types/webpack-env": "1.15.0",
"@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/eslint-plugin": "^2.13.0",
"@typescript-eslint/parser": "^2.34.0", "@typescript-eslint/parser": "^2.17.0",
"apply-loader": "2.0.0", "apply-loader": "2.0.0",
"awesome-typescript-loader": "^5.0.0", "awesome-typescript-loader": "^5.0.0",
"core-js": "^3.6.5", "core-js": "^3.6.4",
"cross-env": "7.0.2", "cross-env": "6.0.3",
"css-loader": "3.4.2", "css-loader": "3.4.2",
"electron": "^8.2.5", "electron": "^8.0.0",
"electron-builder": "22.6.1", "electron-builder": "22.3.2",
"electron-download": "^4.1.1", "electron-download": "^4.1.1",
"electron-installer-snap": "^5.0.0", "electron-installer-snap": "^5.0.0",
"electron-notarize": "^0.1.1", "electron-notarize": "^0.1.1",
"electron-rebuild": "^1.10.1", "electron-rebuild": "^1.9.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.0",
"file-loader": "^5.0.2", "file-loader": "^5.0.2",
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.2",
"html-loader": "0.5.5", "html-loader": "0.5.5",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"node-abi": "^2.16.0", "node-abi": "^2.14.0",
"node-gyp": "^6.1.0", "node-gyp": "^6.1.0",
"node-sass": "^4.14.1", "node-sass": "^4.13.0",
"npmlog": "4.1.2", "npmlog": "4.1.2",
"npx": "^10.2.0", "npx": "^10.2.0",
"pug": "^2.0.4", "pug": "^2.0.4",
@@ -37,29 +37,29 @@
"pug-lint": "^2.6.0", "pug-lint": "^2.6.0",
"pug-loader": "^2.4.0", "pug-loader": "^2.4.0",
"pug-static-loader": "2.0.0", "pug-static-loader": "2.0.0",
"raw-loader": "4.0.1", "raw-loader": "4.0.0",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"shelljs": "0.8.4", "shelljs": "0.8.3",
"source-code-pro": "^2.30.2", "source-code-pro": "^2.30.2",
"source-sans-pro": "3.6.0", "source-sans-pro": "3.6.0",
"style-loader": "^1.1.4", "style-loader": "^1.1.2",
"svg-inline-loader": "^0.8.0", "svg-inline-loader": "^0.8.0",
"to-string-loader": "1.1.6", "to-string-loader": "1.1.6",
"tslib": "^2.0.0", "tslib": "^1.10.0",
"typedoc": "^0.17.6", "typedoc": "^0.16.7",
"typescript": "^3.9.3", "typescript": "^3.7.4",
"url-loader": "^3.0.0", "url-loader": "^3.0.0",
"val-loader": "2.1.1", "val-loader": "2.1.0",
"webpack": "^5.0.0-beta.16", "webpack": "^5.0.0-beta.12",
"webpack-cli": "^3.3.10", "webpack-cli": "^3.3.10",
"yaml-loader": "0.6.0" "yaml-loader": "0.5.0"
}, },
"resolutions": { "resolutions": {
"*/node-abi": "^2.14.0" "*/node-abi": "^2.14.0"
}, },
"scripts": { "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 && webpack --color --config terminus-serial/webpack.config.js", "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": "node scripts/build-typings.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",
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch", "watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
"start": "cross-env TERMINUS_DEV=1 electron app --debug", "start": "cross-env TERMINUS_DEV=1 electron app --debug",
"prod": "cross-env TERMINUS_DEV=1 electron app", "prod": "cross-env TERMINUS_DEV=1 electron app",
@@ -67,7 +67,5 @@
"lint": "eslint --ext ts */src */lib", "lint": "eslint --ext ts */src */lib",
"postinstall": "node ./scripts/install-deps.js" "postinstall": "node ./scripts/install-deps.js"
}, },
"repository": "eugeny/terminus", "repository": "eugeny/terminus"
"author": "Eugene Pankov",
"license": "MIT"
} }

View File

@@ -3,7 +3,6 @@ const builder = require('electron-builder').build
const vars = require('./vars') const vars = require('./vars')
const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/') const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/')
const isCI = !!process.env.GITHUB_REF
builder({ builder({
dir: true, dir: true,

View File

@@ -1,8 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
const builder = require('electron-builder').build const builder = require('electron-builder').build
const vars = require('./vars') const vars = require('./vars')
const fs = require('fs')
const signHook = require('../build/mac/afterSignHook')
const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/') const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/')
@@ -15,7 +13,4 @@ builder({
}, },
}, },
publish: isTag ? 'always' : 'onTag', publish: isTag ? 'always' : 'onTag',
}).catch(e => { }).catch(() => process.exit(1))
console.error(e)
process.exit(1)
})

View File

@@ -1,9 +0,0 @@
#!/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`)
})

View File

@@ -3,11 +3,10 @@ const builder = require('electron-builder').build
const vars = require('./vars') const vars = require('./vars')
const isTag = (process.env.GITHUB_REF || process.env.BUILD_SOURCEBRANCH || '').startsWith('refs/tags/') const isTag = (process.env.GITHUB_REF || process.env.BUILD_SOURCEBRANCH || '').startsWith('refs/tags/')
const isCI = !!process.env.GITHUB_REF
builder({ builder({
dir: true, dir: true,
win: ['nsis', 'zip'], win: ['nsis', 'portable', 'appx'],
config: { config: {
extraMetadata: { extraMetadata: {
version: vars.version, version: vars.version,

View File

@@ -21,7 +21,6 @@ exports.builtinPlugins = [
'terminus-community-color-schemes', 'terminus-community-color-schemes',
'terminus-plugin-manager', 'terminus-plugin-manager',
'terminus-ssh', 'terminus-ssh',
'terminus-serial',
] ]
exports.bundledModules = [ exports.bundledModules = [
'@angular', '@angular',

View File

@@ -1,6 +1,6 @@
{ {
"name": "terminus-community-color-schemes", "name": "terminus-community-color-schemes",
"version": "1.0.104-nightly.0", "version": "1.0.99-nightly.0",
"description": "Community color schemes for Terminus", "description": "Community color schemes for Terminus",
"keywords": [ "keywords": [
"terminus-builtin-plugin" "terminus-builtin-plugin"

View File

@@ -1,14 +0,0 @@
{
"extends": "../tsconfig.json",
"exclude": ["node_modules", "dist", "typings"],
"compilerOptions": {
"baseUrl": "src",
"emitDeclarationOnly": true,
"declaration": true,
"declarationDir": "./typings",
"paths": {
"terminus-*": ["../../terminus-*"],
"*": ["../../app/node_modules/*"]
}
}
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "terminus-core", "name": "terminus-core",
"version": "1.0.104-nightly.0", "version": "1.0.99-nightly.0",
"description": "Terminus core", "description": "Terminus core",
"keywords": [ "keywords": [
"terminus-builtin-plugin" "terminus-builtin-plugin"
@@ -30,7 +30,7 @@
"ng2-dnd": "^5.0.2", "ng2-dnd": "^5.0.2",
"ngx-perfect-scrollbar": "^8.0.0", "ngx-perfect-scrollbar": "^8.0.0",
"shell-escape": "^0.2.0", "shell-escape": "^0.2.0",
"uuid": "^8.0.0", "uuid": "^3.3.2",
"winston": "^3.2.1" "winston": "^3.2.1"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -1,13 +1,12 @@
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component' export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
export { TabHeaderComponent } from '../components/tabHeader.component' export { TabHeaderComponent } from '../components/tabHeader.component'
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component' export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
export { TabRecoveryProvider, RecoveredTab, RecoveryToken } from './tabRecovery' export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider' export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider'
export { ConfigProvider } from './configProvider' export { ConfigProvider } from './configProvider'
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider' export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
export { Theme } from './theme' export { Theme } from './theme'
export { TabContextMenuItemProvider } from './tabContextMenuProvider' export { TabContextMenuItemProvider } from './tabContextMenuProvider'
export { SelectorOption } from './selector'
export { AppService } from '../services/app.service' export { AppService } from '../services/app.service'
export { ConfigService } from '../services/config.service' export { ConfigService } from '../services/config.service'

View File

@@ -1,8 +0,0 @@
export interface SelectorOption<T> {
name: string
description?: string
result?: T
icon?: string
freeInputPattern?: string
callback?: (string?) => void
}

View File

@@ -12,12 +12,6 @@ export interface RecoveredTab {
options?: any options?: any
} }
export interface RecoveryToken {
[_: string]: any
type: string
tabColor?: string|null
}
/** /**
* Extend to enable recovery for your custom tab. * Extend to enable recovery for your custom tab.
* This works in conjunction with [[getRecoveryToken()]] * This works in conjunction with [[getRecoveryToken()]]
@@ -40,5 +34,5 @@ export abstract class TabRecoveryProvider {
* @returns [[RecoveredTab]] descriptor containing tab type and component inputs * @returns [[RecoveredTab]] descriptor containing tab type and component inputs
* or `null` if this token is from a different tab type or is not supported * or `null` if this token is from a different tab type or is not supported
*/ */
abstract async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> abstract async recover (recoveryToken: any): Promise<RecoveredTab|null>
} }

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Inject, Input, HostListener, HostBinding } from '@angular/core' import { Component, Inject, Input, HostListener, HostBinding } from '@angular/core'
import { trigger, style, animate, transition, state } from '@angular/animations' import { trigger, style, animate, transition, state } from '@angular/animations'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -114,9 +113,6 @@ export class AppRootComponent {
if (hotkey === 'move-tab-right') { if (hotkey === 'move-tab-right') {
this.app.moveSelectedTabRight() this.app.moveSelectedTabRight()
} }
if (hotkey === 'reopen-tab') {
this.app.reopenLastTab()
}
} }
if (hotkey === 'toggle-fullscreen') { if (hotkey === 'toggle-fullscreen') {
this.hostApp.toggleFullscreen() this.hostApp.toggleFullscreen()
@@ -128,8 +124,17 @@ export class AppRootComponent {
this.docking.dock() this.docking.dock()
}) })
this.hostApp.secondInstance$.subscribe(() => {
this.presentWindow()
})
this.hotkeys.globalHotkey.subscribe(() => {
this.onGlobalHotkey()
})
this.hostApp.windowCloseRequest$.subscribe(async () => { this.hostApp.windowCloseRequest$.subscribe(async () => {
this.app.closeWindow() if (await this.app.closeAllTabs()) {
this.hostApp.closeWindow()
}
}) })
if (window['safeModeReason']) { if (window['safeModeReason']) {
@@ -168,6 +173,40 @@ export class AppRootComponent {
}) })
} }
onGlobalHotkey () {
if (this.hostApp.getWindow().isFocused()) {
this.hideWindow()
} else {
this.presentWindow()
}
}
presentWindow () {
if (!this.hostApp.getWindow().isVisible()) {
// unfocused, invisible
this.hostApp.getWindow().show()
this.hostApp.getWindow().focus()
} else {
if (this.config.store.appearance.dock === 'off') {
// not docked, visible
setTimeout(() => {
this.hostApp.getWindow().focus()
})
} else {
// docked, visible
this.hostApp.getWindow().hide()
}
}
}
hideWindow () {
this.electron.loseFocus()
this.hostApp.getWindow().blur()
if (this.hostApp.platform !== Platform.macOS) {
this.hostApp.getWindow().hide()
}
}
async ngOnInit () { async ngOnInit () {
this.ready = true this.ready = true

View File

@@ -1,6 +1,5 @@
import { Observable, Subject } from 'rxjs' import { Observable, Subject } from 'rxjs'
import { ViewRef } from '@angular/core' import { ViewRef } from '@angular/core'
import { RecoveryToken } from '../api/tabRecovery'
/** /**
* Represents an active "process" inside a tab, * Represents an active "process" inside a tab,
@@ -14,11 +13,6 @@ export interface BaseTabProcess {
* Abstract base class for custom tab components * Abstract base class for custom tab components
*/ */
export abstract class BaseTabComponent { export abstract class BaseTabComponent {
/**
* Parent tab (usually a SplitTabComponent)
*/
parent: BaseTabComponent|null = null
/** /**
* Current tab title * Current tab title
*/ */
@@ -44,7 +38,7 @@ export abstract class BaseTabComponent {
*/ */
color: string|null = null color: string|null = null
hasFocus = false protected hasFocus = false
/** /**
* Ping this if your recovery state has been changed and you want * Ping this if your recovery state has been changed and you want
@@ -68,7 +62,7 @@ export abstract class BaseTabComponent {
get destroyed$ (): Observable<void> { return this.destroyed } get destroyed$ (): Observable<void> { return this.destroyed }
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint } get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
protected constructor () { constructor () {
this.focused$.subscribe(() => { this.focused$.subscribe(() => {
this.hasFocus = true this.hasFocus = true
}) })
@@ -77,7 +71,7 @@ export abstract class BaseTabComponent {
}) })
} }
setTitle (title: string): void { setTitle (title: string) {
this.title = title this.title = title
if (!this.customTitle) { if (!this.customTitle) {
this.titleChange.next(title) this.titleChange.next(title)
@@ -89,7 +83,7 @@ export abstract class BaseTabComponent {
* *
* @param {type} progress: value between 0 and 1, or `null` to remove * @param {type} progress: value between 0 and 1, or `null` to remove
*/ */
setProgress (progress: number|null): void { setProgress (progress: number|null) {
this.progress.next(progress) this.progress.next(progress)
if (progress) { if (progress) {
if (this.progressClearTimeout) { if (this.progressClearTimeout) {
@@ -124,7 +118,7 @@ export abstract class BaseTabComponent {
* @return JSON serializable tab state representation * @return JSON serializable tab state representation
* for your [[TabRecoveryProvider]] to parse * for your [[TabRecoveryProvider]] to parse
*/ */
async getRecoveryToken (): Promise<RecoveryToken|null> { async getRecoveryToken (): Promise<any> {
return null return null
} }
@@ -142,11 +136,11 @@ export abstract class BaseTabComponent {
return true return true
} }
emitFocused (): void { emitFocused () {
this.focused.next() this.focused.next()
} }
emitBlurred (): void { emitBlurred () {
this.blurred.next() this.blurred.next()
} }

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { NgZone, Component, Input, HostBinding, HostListener } from '@angular/core' import { NgZone, Component, Input, HostBinding, HostListener } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Input, ElementRef, ViewChild } from '@angular/core' import { Component, Input, ElementRef, ViewChild } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'

View File

@@ -14,7 +14,7 @@ export class SafeModeModalComponent {
this.error = window['safeModeReason'] this.error = window['safeModeReason']
} }
close (): void { close () {
this.modalInstance.dismiss() this.modalInstance.dismiss()
} }
} }

View File

@@ -1,26 +0,0 @@
.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}}

View File

@@ -1,13 +0,0 @@
.list-group {
max-height: 70vh;
overflow: auto;
}
.icon {
width: 1.25rem;
margin-right: 0.25rem;
}
.title {
margin-left: 10px;
}

View File

@@ -1,78 +0,0 @@
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('<')
}
}

View File

@@ -1,7 +1,7 @@
import { Observable, Subject, Subscription } from 'rxjs' import { Observable, Subject, Subscription } from 'rxjs'
import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, AfterViewInit, OnDestroy } from '@angular/core' import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, OnInit, OnDestroy } from '@angular/core'
import { BaseTabComponent, BaseTabProcess } from './baseTab.component' import { BaseTabComponent, BaseTabProcess } from './baseTab.component'
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from '../api/tabRecovery' import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
import { TabsService } from '../services/tabs.service' import { TabsService } from '../services/tabs.service'
import { HotkeysService } from '../services/hotkeys.service' import { HotkeysService } from '../services/hotkeys.service'
import { TabRecoveryService } from '../services/tabRecovery.service' import { TabRecoveryService } from '../services/tabRecovery.service'
@@ -48,7 +48,7 @@ export class SplitContainer {
/** /**
* Remove unnecessarily nested child containers and renormalizes [[ratios]] * Remove unnecessarily nested child containers and renormalizes [[ratios]]
*/ */
normalize (): void { normalize () {
for (let i = 0; i < this.children.length; i++) { for (let i = 0; i < this.children.length; i++) {
const child = this.children[i] const child = this.children[i]
@@ -93,7 +93,7 @@ export class SplitContainer {
return s return s
} }
async serialize (): Promise<RecoveryToken> { async serialize () {
const children: any[] = [] const children: any[] = []
for (const child of this.children) { for (const child of this.children) {
if (child instanceof SplitContainer) { if (child instanceof SplitContainer) {
@@ -140,7 +140,7 @@ export interface SplitSpannerInfo {
`, `,
styles: [require('./splitTab.component.scss')], styles: [require('./splitTab.component.scss')],
}) })
export class SplitTabComponent extends BaseTabComponent implements AfterViewInit, OnDestroy { export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDestroy {
static DIRECTIONS: SplitDirection[] = ['t', 'r', 'b', 'l'] static DIRECTIONS: SplitDirection[] = ['t', 'r', 'b', 'l']
/** @hidden */ /** @hidden */
@@ -166,7 +166,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
private tabRemoved = new Subject<BaseTabComponent>() private tabRemoved = new Subject<BaseTabComponent>()
private splitAdjusted = new Subject<SplitSpannerInfo>() private splitAdjusted = new Subject<SplitSpannerInfo>()
private focusChanged = new Subject<BaseTabComponent>() private focusChanged = new Subject<BaseTabComponent>()
private initialized = new Subject<void>()
get tabAdded$ (): Observable<BaseTabComponent> { return this.tabAdded } get tabAdded$ (): Observable<BaseTabComponent> { return this.tabAdded }
get tabRemoved$ (): Observable<BaseTabComponent> { return this.tabRemoved } get tabRemoved$ (): Observable<BaseTabComponent> { return this.tabRemoved }
@@ -181,11 +180,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
*/ */
get focusChanged$ (): Observable<BaseTabComponent> { return this.focusChanged } 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 */ /** @hidden */
constructor ( constructor (
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
@@ -250,7 +244,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} }
/** @hidden */ /** @hidden */
async ngAfterViewInit (): Promise<void> { async ngOnInit () {
if (this._recoveredState) { if (this._recoveredState) {
await this.recoverContainer(this.root, this._recoveredState) await this.recoverContainer(this.root, this._recoveredState)
this.layout() this.layout()
@@ -261,17 +255,15 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} }
}) })
} }
this.initialized.next()
this.initialized.complete()
} }
/** @hidden */ /** @hidden */
ngOnDestroy (): void { ngOnDestroy () {
this.hotkeysSubscription.unsubscribe() this.hotkeysSubscription.unsubscribe()
} }
/** @returns Flat list of all sub-tabs */ /** @returns Flat list of all sub-tabs */
getAllTabs (): BaseTabComponent[] { getAllTabs () {
return this.root.getAllTabs() return this.root.getAllTabs()
} }
@@ -283,7 +275,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
return this.maximizedTab return this.maximizedTab
} }
focus (tab: BaseTabComponent): void { focus (tab: BaseTabComponent) {
this.focusedTab = tab this.focusedTab = tab
for (const x of this.getAllTabs()) { for (const x of this.getAllTabs()) {
if (x !== tab) { if (x !== tab) {
@@ -301,7 +293,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
this.layout() this.layout()
} }
maximize (tab: BaseTabComponent|null): void { maximize (tab: BaseTabComponent|null) {
this.maximizedTab = tab this.maximizedTab = tab
this.layout() this.layout()
} }
@@ -309,7 +301,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** /**
* Focuses the first available tab inside the given [[SplitContainer]] * Focuses the first available tab inside the given [[SplitContainer]]
*/ */
focusAnyIn (parent: BaseTabComponent | SplitContainer): void { focusAnyIn (parent: BaseTabComponent | SplitContainer) {
if (!parent) { if (!parent) {
return return
} }
@@ -323,9 +315,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** /**
* Inserts a new `tab` to the `side` of the `relative` tab * Inserts a new `tab` to the `side` of the `relative` tab
*/ */
async addTab (tab: BaseTabComponent, relative: BaseTabComponent|null, side: SplitDirection): Promise<void> { addTab (tab: BaseTabComponent, relative: BaseTabComponent|null, side: SplitDirection) {
tab.parent = this
let target = (relative ? this.getParentOf(relative) : null) || this.root let target = (relative ? this.getParentOf(relative) : null) || this.root
let insertIndex = relative ? target.children.indexOf(relative) : -1 let insertIndex = relative ? target.children.indexOf(relative) : -1
@@ -355,9 +345,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
target.children.splice(insertIndex, 0, tab) target.children.splice(insertIndex, 0, tab)
this.recoveryStateChangedHint.next() this.recoveryStateChangedHint.next()
await this.initialized$.toPromise()
this.attachTabView(tab) this.attachTabView(tab)
setImmediate(() => { setImmediate(() => {
@@ -367,7 +354,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}) })
} }
removeTab (tab: BaseTabComponent): void { removeTab (tab: BaseTabComponent) {
const parent = this.getParentOf(tab) const parent = this.getParentOf(tab)
if (!parent) { if (!parent) {
return return
@@ -377,11 +364,11 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
parent.children.splice(index, 1) parent.children.splice(index, 1)
this.detachTabView(tab) this.detachTabView(tab)
tab.parent = null
this.layout() this.layout()
this.tabRemoved.next(tab) this.tabRemoved.next(tab)
if (this.root.children.length === 0) { if (this.root.children.length === 0) {
this.destroy() this.destroy()
} else { } else {
@@ -392,7 +379,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** /**
* Moves focus in the given direction * Moves focus in the given direction
*/ */
navigate (dir: SplitDirection): void { navigate (dir: SplitDirection) {
let rel: BaseTabComponent | SplitContainer = this.focusedTab let rel: BaseTabComponent | SplitContainer = this.focusedTab
let parent = this.getParentOf(rel) let parent = this.getParentOf(rel)
if (!parent) { if (!parent) {
@@ -425,12 +412,11 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} }
} }
async splitTab (tab: BaseTabComponent, dir: SplitDirection): Promise<BaseTabComponent|null> { async splitTab (tab: BaseTabComponent, dir: SplitDirection) {
const newTab = await this.tabsService.duplicate(tab) const newTab = await this.tabsService.duplicate(tab)
if (newTab) { if (newTab) {
this.addTab(newTab, tab, dir) this.addTab(newTab, tab, dir)
} }
return newTab
} }
/** /**
@@ -468,12 +454,12 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} }
/** @hidden */ /** @hidden */
onSpannerAdjusted (spanner: SplitSpannerInfo): void { onSpannerAdjusted (spanner: SplitSpannerInfo) {
this.layout() this.layout()
this.splitAdjusted.next(spanner) this.splitAdjusted.next(spanner)
} }
destroy (): void { destroy () {
super.destroy() super.destroy()
for (const x of this.getAllTabs()) { for (const x of this.getAllTabs()) {
x.destroy() x.destroy()
@@ -529,24 +515,21 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
if (child instanceof SplitContainer) { if (child instanceof SplitContainer) {
this.layoutInternal(child, childX, childY, childW, childH) this.layoutInternal(child, childX, childY, childW, childH)
} else { } else {
const viewRef = this.viewRefs.get(child) const element = this.viewRefs.get(child)!.rootNodes[0]
if (viewRef) { element.classList.toggle('child', true)
const element = viewRef.rootNodes[0] element.classList.toggle('maximized', child === this.maximizedTab)
element.classList.toggle('child', true) element.classList.toggle('minimized', this.maximizedTab && child !== this.maximizedTab)
element.classList.toggle('maximized', child === this.maximizedTab) element.classList.toggle('focused', child === this.focusedTab)
element.classList.toggle('minimized', this.maximizedTab && child !== this.maximizedTab) element.style.left = `${childX}%`
element.classList.toggle('focused', child === this.focusedTab) element.style.top = `${childY}%`
element.style.left = `${childX}%` element.style.width = `${childW}%`
element.style.top = `${childY}%` element.style.height = `${childH}%`
element.style.width = `${childW}%`
element.style.height = `${childH}%`
if (child === this.maximizedTab) { if (child === this.maximizedTab) {
element.style.left = '5%' element.style.left = '5%'
element.style.top = '5%' element.style.top = '5%'
element.style.width = '90%' element.style.width = '90%'
element.style.height = '90%' element.style.height = '90%'
}
} }
} }
offset += sizes[i] offset += sizes[i]
@@ -575,7 +558,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
if (recovered) { if (recovered) {
const tab = this.tabsService.create(recovered.type, recovered.options) const tab = this.tabsService.create(recovered.type, recovered.options)
children.push(tab) children.push(tab)
tab.parent = this
this.attachTabView(tab) this.attachTabView(tab)
} else { } else {
state.ratios.splice(state.children.indexOf(childState), 0) state.ratios.splice(state.children.indexOf(childState), 0)
@@ -592,7 +574,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class SplitTabRecoveryProvider extends TabRecoveryProvider { export class SplitTabRecoveryProvider extends TabRecoveryProvider {
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> { async recover (recoveryToken: any): Promise<RecoveredTab|null> {
if (recoveryToken && recoveryToken.type === 'app:split-tab') { if (recoveryToken && recoveryToken.type === 'app:split-tab') {
return { return {
type: SplitTabComponent, type: SplitTabComponent,

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Input, HostBinding, ElementRef, Output, EventEmitter } from '@angular/core' import { Component, Input, HostBinding, ElementRef, Output, EventEmitter } from '@angular/core'
import { SplitContainer } from './splitTab.component' import { SplitContainer } from './splitTab.component'

View File

@@ -13,7 +13,7 @@ import { ToolbarButton, ToolbarButtonProvider } from '../api'
export class StartPageComponent { export class StartPageComponent {
version: string version: string
private constructor ( constructor (
private config: ConfigService, private config: ConfigService,
private domSanitizer: DomSanitizer, private domSanitizer: DomSanitizer,
public homeBase: HomeBaseService, public homeBase: HomeBaseService,

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Input, ViewChild, HostBinding, ViewContainerRef, OnChanges } from '@angular/core' import { Component, Input, ViewChild, HostBinding, ViewContainerRef, OnChanges } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef } from '@angular/core' import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef } from '@angular/core'
import { SortableComponent } from 'ng2-dnd' import { SortableComponent } from 'ng2-dnd'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -49,15 +48,12 @@ export class TabHeaderComponent {
} }
ngOnInit () { ngOnInit () {
this.tab.progress$.subscribe(progress => {
this.progress = progress
})
}
ngAfterViewInit () {
if (this.hostApp.platform === Platform.macOS) { if (this.hostApp.platform === Platform.macOS) {
this.parentDraggable.setDragHandle(this.handle.nativeElement) this.parentDraggable.setDragHandle(this.handle.nativeElement)
} }
this.tab.progress$.subscribe(progress => {
this.progress = progress
})
} }
showRenameTabModal (): void { showRenameTabModal (): void {

View File

@@ -1,29 +1,19 @@
.container.mt-5.mb-5 .mb-4
.mb-4 .terminus-logo
.terminus-logo h1.terminus-title Terminus
h1.terminus-title Terminus sup α
sup α
.container
.text-center.mb-5 Thank you for downloading Terminus! .text-center.mb-5 Thank you for downloading Terminus!
.form-line .form-line
.header .header
.title Enable analytics .title Enable analytics
.description Help us track the number of Terminus installs across the world! .description Help us track the number of Terminus installs across the world!
toggle([(ngModel)]='config.store.enableAnalytics') toggle(
[(ngModel)]='config.store.enableAnalytics',
.form-line (ngModelChange)='config.save(); config.requestRestart()',
.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 .text-center.mt-5
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again button.btn.btn-primary((click)='closeAndDisable()') Close and never show again

View File

@@ -2,7 +2,5 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: auto; margin: auto;
flex: auto; flex: 0 1 500px;
max-height: 100%;
overflow-y: auto;
} }

View File

@@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { BaseTabComponent } from './baseTab.component' import { BaseTabComponent } from './baseTab.component'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
import { HostAppService } from '../services/hostApp.service' import { AppService } from '../services/app.service'
/** @hidden */ /** @hidden */
@Component({ @Component({
@@ -11,29 +10,17 @@ import { HostAppService } from '../services/hostApp.service'
styles: [require('./welcomeTab.component.scss')], styles: [require('./welcomeTab.component.scss')],
}) })
export class WelcomeTabComponent extends BaseTabComponent { export class WelcomeTabComponent extends BaseTabComponent {
enableSSH = false
enableSerial = false
constructor ( constructor (
private hostApp: HostAppService, private app: AppService,
public config: ConfigService, public config: ConfigService,
) { ) {
super() super()
this.setTitle('Welcome') this.setTitle('Welcome')
this.enableSSH = !config.store.pluginBlacklist.includes('ssh')
this.enableSerial = !config.store.pluginBlacklist.includes('serial')
} }
closeAndDisable () { closeAndDisable () {
this.config.store.enableWelcomeTab = false 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.config.save()
this.hostApp.getWindow().reload() this.app.closeTab(this)
} }
} }

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { HostAppService } from '../services/hostApp.service' import { HostAppService } from '../services/hostApp.service'
import { AppService } from '../services/app.service' import { AppService } from '../services/app.service'
@@ -10,9 +9,11 @@ import { AppService } from '../services/app.service'
styles: [require('./windowControls.component.scss')], styles: [require('./windowControls.component.scss')],
}) })
export class WindowControlsComponent { export class WindowControlsComponent {
private constructor (public hostApp: HostAppService, public app: AppService) { } constructor (public hostApp: HostAppService, public app: AppService) { }
async closeWindow () { async closeWindow () {
this.app.closeWindow() if (await this.app.closeAllTabs()) {
this.hostApp.closeWindow()
}
} }
} }

View File

@@ -7,8 +7,6 @@ hotkeys:
- 'F11' - 'F11'
close-tab: close-tab:
- 'Ctrl-Shift-W' - 'Ctrl-Shift-W'
reopen-tab:
- 'Ctrl-Shift-T'
toggle-last-tab: [] toggle-last-tab: []
rename-tab: rename-tab:
- 'Ctrl-Shift-R' - 'Ctrl-Shift-R'

View File

@@ -7,8 +7,6 @@ hotkeys:
- 'Ctrl+⌘+F' - 'Ctrl+⌘+F'
close-tab: close-tab:
- '⌘-W' - '⌘-W'
reopen-tab:
- '⌘-Shift-T'
toggle-last-tab: [] toggle-last-tab: []
rename-tab: rename-tab:
- '⌘-R' - '⌘-R'

View File

@@ -8,8 +8,6 @@ hotkeys:
- 'Alt-Enter' - 'Alt-Enter'
close-tab: close-tab:
- 'Ctrl-Shift-W' - 'Ctrl-Shift-W'
reopen-tab:
- 'Ctrl-Shift-T'
toggle-last-tab: [] toggle-last-tab: []
rename-tab: rename-tab:
- 'Ctrl-Shift-R' - 'Ctrl-Shift-R'

View File

@@ -2,8 +2,6 @@ appearance:
dock: off dock: off
dockScreen: current dockScreen: current
dockFill: 0.5 dockFill: 0.5
dockHideOnBlur: false
dockAlwaysOnTop: true
tabsLocation: top tabsLocation: top
cycleTabs: true cycleTabs: true
theme: Standard theme: Standard

View File

@@ -7,7 +7,7 @@ import { Directive, AfterViewInit, ElementRef } from '@angular/core'
export class AutofocusDirective implements AfterViewInit { export class AutofocusDirective implements AfterViewInit {
constructor (private el: ElementRef) { } constructor (private el: ElementRef) { }
ngAfterViewInit (): void { ngAfterViewInit () {
this.el.nativeElement.blur() this.el.nativeElement.blur()
setTimeout(() => { setTimeout(() => {
this.el.nativeElement.focus() this.el.nativeElement.focus()

View File

@@ -8,7 +8,7 @@ export class FastHtmlBindDirective implements OnChanges {
@Input() fastHtmlBind: string @Input() fastHtmlBind: string
constructor (private el: ElementRef) { } constructor (private el: ElementRef) { }
ngOnChanges (): void { ngOnChanges () {
this.el.nativeElement.innerHTML = this.fastHtmlBind || '' this.el.nativeElement.innerHTML = this.fastHtmlBind || ''
} }
} }

View File

@@ -25,10 +25,6 @@ export class AppHotkeyProvider extends HotkeyProvider {
id: 'close-tab', id: 'close-tab',
name: 'Close tab', name: 'Close tab',
}, },
{
id: 'reopen-tab',
name: 'Reopen last tab',
},
{ {
id: 'toggle-last-tab', id: 'toggle-last-tab',
name: 'Toggle last tab', name: 'Toggle last tab',

View File

@@ -16,7 +16,6 @@ import { TitleBarComponent } from './components/titleBar.component'
import { ToggleComponent } from './components/toggle.component' import { ToggleComponent } from './components/toggle.component'
import { WindowControlsComponent } from './components/windowControls.component' import { WindowControlsComponent } from './components/windowControls.component'
import { RenameTabModalComponent } from './components/renameTabModal.component' import { RenameTabModalComponent } from './components/renameTabModal.component'
import { SelectorModalComponent } from './components/selectorModal.component'
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component' import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
import { SplitTabSpannerComponent } from './components/splitTabSpanner.component' import { SplitTabSpannerComponent } from './components/splitTabSpanner.component'
import { WelcomeTabComponent } from './components/welcomeTab.component' import { WelcomeTabComponent } from './components/welcomeTab.component'
@@ -36,7 +35,7 @@ import { ConfigService } from './services/config.service'
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme' import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
import { CoreConfigProvider } from './config' import { CoreConfigProvider } from './config'
import { AppHotkeyProvider } from './hotkeys' import { AppHotkeyProvider } from './hotkeys'
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu } from './tabContextMenu' import { TaskCompletionContextMenu, CommonOptionsContextMenu, CloseContextMenu } from './tabContextMenu'
import 'perfect-scrollbar/css/perfect-scrollbar.css' import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css' import 'ng2-dnd/bundles/style.css'
@@ -54,7 +53,7 @@ const PROVIDERS = [
{ provide: Theme, useClass: PaperTheme, multi: true }, { provide: Theme, useClass: PaperTheme, multi: true },
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true }, { provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
{ provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true }, { provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: TabManagementContextMenu, multi: true }, { provide: TabContextMenuItemProvider, useClass: CloseContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true }, { provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true }, { provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } }, { provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
@@ -66,7 +65,7 @@ const PROVIDERS = [
BrowserModule, BrowserModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FormsModule, FormsModule,
NgbModule, NgbModule.forRoot(),
PerfectScrollbarModule, PerfectScrollbarModule,
DndModule.forRoot(), DndModule.forRoot(),
], ],
@@ -83,7 +82,6 @@ const PROVIDERS = [
SafeModeModalComponent, SafeModeModalComponent,
AutofocusDirective, AutofocusDirective,
FastHtmlBindDirective, FastHtmlBindDirective,
SelectorModalComponent,
SplitTabComponent, SplitTabComponent,
SplitTabSpannerComponent, SplitTabSpannerComponent,
WelcomeTabComponent, WelcomeTabComponent,
@@ -91,7 +89,6 @@ const PROVIDERS = [
entryComponents: [ entryComponents: [
RenameTabModalComponent, RenameTabModalComponent,
SafeModeModalComponent, SafeModeModalComponent,
SelectorModalComponent,
SplitTabComponent, SplitTabComponent,
WelcomeTabComponent, WelcomeTabComponent,
], ],

View File

@@ -2,13 +2,9 @@
import { Observable, Subject, AsyncSubject } from 'rxjs' import { Observable, Subject, AsyncSubject } from 'rxjs'
import { takeUntil } from 'rxjs/operators' import { takeUntil } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { SplitTabComponent } from '../components/splitTab.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 { ConfigService } from './config.service'
import { HostAppService } from './hostApp.service' import { HostAppService } from './hostApp.service'
@@ -50,7 +46,6 @@ export class AppService {
private lastTabIndex = 0 private lastTabIndex = 0
private _activeTab: BaseTabComponent private _activeTab: BaseTabComponent
private closedTabsStack: RecoveryToken[] = []
private activeTabChange = new Subject<BaseTabComponent>() private activeTabChange = new Subject<BaseTabComponent>()
private tabsChanged = new Subject<void>() private tabsChanged = new Subject<void>()
@@ -69,47 +64,41 @@ export class AppService {
get ready$ (): Observable<void> { return this.ready } get ready$ (): Observable<void> { return this.ready }
/** @hidden */ /** @hidden */
private constructor ( constructor (
private config: ConfigService, private config: ConfigService,
private hostApp: HostAppService, private hostApp: HostAppService,
private tabRecovery: TabRecoveryService, private tabRecovery: TabRecoveryService,
private tabsService: TabsService, private tabsService: TabsService,
private ngbModal: NgbModal,
) { ) {
this.tabsChanged$.subscribe(() => {
this.tabRecovery.saveTabs(this.tabs)
})
setInterval(() => {
this.tabRecovery.saveTabs(this.tabs)
}, 30000)
if (hostApp.getWindow().id === 1) { if (hostApp.getWindow().id === 1) {
if (config.store.terminal.recoverTabs) { if (config.store.terminal.recoverTabs) {
this.tabRecovery.recoverTabs().then(tabs => { this.tabRecovery.recoverTabs().then(tabs => {
for (const tab of tabs) { for (const tab of tabs) {
this.openNewTabRaw(tab.type, tab.options) this.openNewTabRaw(tab.type, tab.options)
} }
this.tabRecovery.enabled = true this.startTabStorage()
}) })
} else { } else {
/** Continue to store the tabs even if the setting is currently off */ /** Continue to store the tabs even if the setting is currently off */
this.tabRecovery.enabled = true this.startTabStorage()
} }
} }
hostApp.windowFocused$.subscribe(() => { hostApp.windowFocused$.subscribe(() => {
this._activeTab?.emitFocused() 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): void { startTabStorage () {
this.tabsChanged$.subscribe(() => {
this.tabRecovery.saveTabs(this.tabs)
})
setInterval(() => {
this.tabRecovery.saveTabs(this.tabs)
}, 30000)
}
addTabRaw (tab: BaseTabComponent, index: number|null = null) {
if (index !== null) { if (index !== null) {
this.tabs.splice(index, 0, tab) this.tabs.splice(index, 0, tab)
} else { } else {
@@ -152,7 +141,7 @@ export class AppService {
* Adds a new tab **without** wrapping it in a SplitTabComponent * Adds a new tab **without** wrapping it in a SplitTabComponent
* @param inputs Properties to be assigned on the new tab component instance * @param inputs Properties to be assigned on the new tab component instance
*/ */
openNewTabRaw (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent { openNewTabRaw (type: TabComponentType, inputs?: any): BaseTabComponent {
const tab = this.tabsService.create(type, inputs) const tab = this.tabsService.create(type, inputs)
this.addTabRaw(tab) this.addTabRaw(tab)
return tab return tab
@@ -162,7 +151,7 @@ export class AppService {
* Adds a new tab while wrapping it in a SplitTabComponent * Adds a new tab while wrapping it in a SplitTabComponent
* @param inputs Properties to be assigned on the new tab component instance * @param inputs Properties to be assigned on the new tab component instance
*/ */
openNewTab (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent { openNewTab (type: TabComponentType, inputs?: any): BaseTabComponent {
const splitTab = this.tabsService.create(SplitTabComponent) as SplitTabComponent const splitTab = this.tabsService.create(SplitTabComponent) as SplitTabComponent
const tab = this.tabsService.create(type, inputs) const tab = this.tabsService.create(type, inputs)
splitTab.addTab(tab, null, 'r') splitTab.addTab(tab, null, 'r')
@@ -170,24 +159,7 @@ export class AppService {
return tab return tab
} }
async reopenLastTab (): Promise<BaseTabComponent|null> { selectTab (tab: BaseTabComponent) {
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) { if (this._activeTab === tab) {
this._activeTab.emitFocused() this._activeTab.emitFocused()
return return
@@ -223,14 +195,14 @@ export class AppService {
} }
/** Switches between the current tab and the previously active one */ /** Switches between the current tab and the previously active one */
toggleLastTab (): void { toggleLastTab () {
if (!this.lastTabIndex || this.lastTabIndex >= this.tabs.length) { if (!this.lastTabIndex || this.lastTabIndex >= this.tabs.length) {
this.lastTabIndex = 0 this.lastTabIndex = 0
} }
this.selectTab(this.tabs[this.lastTabIndex]) this.selectTab(this.tabs[this.lastTabIndex])
} }
nextTab (): void { nextTab () {
if (this.tabs.length > 1) { if (this.tabs.length > 1) {
const tabIndex = this.tabs.indexOf(this._activeTab) const tabIndex = this.tabs.indexOf(this._activeTab)
if (tabIndex < this.tabs.length - 1) { if (tabIndex < this.tabs.length - 1) {
@@ -241,7 +213,7 @@ export class AppService {
} }
} }
previousTab (): void { previousTab () {
if (this.tabs.length > 1) { if (this.tabs.length > 1) {
const tabIndex = this.tabs.indexOf(this._activeTab) const tabIndex = this.tabs.indexOf(this._activeTab)
if (tabIndex > 0) { if (tabIndex > 0) {
@@ -252,7 +224,7 @@ export class AppService {
} }
} }
moveSelectedTabLeft (): void { moveSelectedTabLeft () {
if (this.tabs.length > 1) { if (this.tabs.length > 1) {
const tabIndex = this.tabs.indexOf(this._activeTab) const tabIndex = this.tabs.indexOf(this._activeTab)
if (tabIndex > 0) { if (tabIndex > 0) {
@@ -263,7 +235,7 @@ export class AppService {
} }
} }
moveSelectedTabRight (): void { moveSelectedTabRight () {
if (this.tabs.length > 1) { if (this.tabs.length > 1) {
const tabIndex = this.tabs.indexOf(this._activeTab) const tabIndex = this.tabs.indexOf(this._activeTab)
if (tabIndex < this.tabs.length - 1) { if (tabIndex < this.tabs.length - 1) {
@@ -274,7 +246,7 @@ export class AppService {
} }
} }
swapTabs (a: BaseTabComponent, b: BaseTabComponent): void { swapTabs (a: BaseTabComponent, b: BaseTabComponent) {
const i1 = this.tabs.indexOf(a) const i1 = this.tabs.indexOf(a)
const i2 = this.tabs.indexOf(b) const i2 = this.tabs.indexOf(b)
this.tabs[i1] = b this.tabs[i1] = b
@@ -282,7 +254,7 @@ export class AppService {
} }
/** @hidden */ /** @hidden */
emitTabsChanged (): void { emitTabsChanged () {
this.tabsChanged.next() this.tabsChanged.next()
} }
@@ -296,12 +268,11 @@ export class AppService {
tab.destroy() tab.destroy()
} }
async duplicateTab (tab: BaseTabComponent): Promise<BaseTabComponent|null> { async duplicateTab (tab: BaseTabComponent) {
const dup = await this.tabsService.duplicate(tab) const dup = await this.tabsService.duplicate(tab)
if (dup) { if (dup) {
this.addTabRaw(dup, this.tabs.indexOf(tab) + 1) this.addTabRaw(dup, this.tabs.indexOf(tab) + 1)
} }
return dup
} }
/** /**
@@ -319,18 +290,8 @@ export class AppService {
return true 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 */ /** @hidden */
emitReady (): void { emitReady () {
this.ready.next() this.ready.next()
this.ready.complete() this.ready.complete()
this.hostApp.emitReady() this.hostApp.emitReady()
@@ -351,15 +312,7 @@ export class AppService {
return this.completionObservers.get(tab)!.done$ return this.completionObservers.get(tab)!.done$
} }
stopObservingTabCompletion (tab: BaseTabComponent): void { stopObservingTabCompletion (tab: BaseTabComponent) {
this.completionObservers.delete(tab) 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>
}
} }

View File

@@ -20,7 +20,7 @@ function isNonStructuralObjectMember (v): boolean {
/** @hidden */ /** @hidden */
export class ConfigProxy { export class ConfigProxy {
constructor (real: Record<string, any>, defaults: Record<string, any>) { constructor (real: any, defaults: any) {
for (const key in defaults) { for (const key in defaults) {
if (isStructuralMember(defaults[key])) { if (isStructuralMember(defaults[key])) {
if (!real[key]) { if (!real[key]) {
@@ -71,10 +71,8 @@ export class ConfigProxy {
} }
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function getValue (_key: string): any { } // eslint-disable-line @typescript-eslint/no-empty-function
getValue (_key: string): any { } 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
setValue (_key: string, _value: any) { }
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@@ -102,7 +100,7 @@ export class ConfigService {
get changed$ (): Observable<void> { return this.changed } get changed$ (): Observable<void> { return this.changed }
/** @hidden */ /** @hidden */
private constructor ( constructor (
electron: ElectronService, electron: ElectronService,
private hostApp: HostAppService, private hostApp: HostAppService,
@Inject(ConfigProvider) configProviders: ConfigProvider[], @Inject(ConfigProvider) configProviders: ConfigProvider[],
@@ -126,7 +124,7 @@ export class ConfigService {
}) })
} }
getDefaults (): Record<string, any> { getDefaults () {
const cleanup = o => { const cleanup = o => {
if (o instanceof Array) { if (o instanceof Array) {
return o.map(cleanup) return o.map(cleanup)
@@ -155,11 +153,9 @@ export class ConfigService {
} }
save (): void { save (): void {
// Scrub undefined values
this._store = JSON.parse(JSON.stringify(this._store))
fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8') fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8')
this.emitChange() this.emitChange()
this.hostApp.broadcastConfigChange(this.store) this.hostApp.broadcastConfigChange()
} }
/** /**
@@ -192,11 +188,11 @@ export class ConfigService {
enabledServices<T extends object> (services: T[]): T[] { enabledServices<T extends object> (services: T[]): T[] {
if (!this.servicesCache) { if (!this.servicesCache) {
this.servicesCache = {} this.servicesCache = {}
const ngModule = window['rootModule'].ɵinj const ngModule = window['rootModule'].ngInjectorDef
for (const imp of ngModule.imports) { for (const imp of ngModule.imports) {
const module = imp['ngModule'] || imp const module = imp['ngModule'] || imp
if (module.ɵinj?.providers) { if (module.ngInjectorDef && module.ngInjectorDef.providers) {
this.servicesCache[module['pluginName']] = module.ɵinj.providers.map(provider => { this.servicesCache[module['pluginName']] = module.ngInjectorDef.providers.map(provider => {
return provider['useClass'] || provider return provider['useClass'] || provider
}) })
} }

View File

@@ -6,7 +6,7 @@ import { HostAppService, Bounds } from '../services/hostApp.service'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class DockingService { export class DockingService {
/** @hidden */ /** @hidden */
private constructor ( constructor (
private electron: ElectronService, private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
private hostApp: HostAppService, private hostApp: HostAppService,
@@ -15,7 +15,7 @@ export class DockingService {
electron.screen.on('display-metrics-changed', () => this.repositionWindow()) electron.screen.on('display-metrics-changed', () => this.repositionWindow())
} }
dock (): void { dock () {
const dockSide = this.config.store.appearance.dock const dockSide = this.config.store.appearance.dock
if (dockSide === 'off') { if (dockSide === 'off') {
@@ -53,25 +53,22 @@ export class DockingService {
newBounds.y = display.bounds.y newBounds.y = display.bounds.y
} }
const alwaysOnTop = this.config.store.appearance.dockAlwaysOnTop this.hostApp.setAlwaysOnTop(true)
this.hostApp.setAlwaysOnTop(alwaysOnTop)
setImmediate(() => { setImmediate(() => {
this.hostApp.setBounds(newBounds) this.hostApp.setBounds(newBounds)
}) })
} }
getCurrentScreen (): Electron.Display { getCurrentScreen () {
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint()) return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
} }
getScreens (): Electron.Display[] { getScreens () {
const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id
return this.electron.screen.getAllDisplays().sort((a, b) => 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 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 { return {
...display,
id: display.id, id: display.id,
name: display.id === primaryDisplayID ? 'Primary Display' : `Display ${index +1}`, name: display.id === primaryDisplayID ? 'Primary Display' : `Display ${index +1}`,
} }

View File

@@ -25,7 +25,7 @@ export class ElectronService {
private electron: any private electron: any
/** @hidden */ /** @hidden */
private constructor () { constructor () {
this.electron = require('electron') this.electron = require('electron')
this.remote = this.electron.remote this.remote = this.electron.remote
this.app = this.remote.app this.app = this.remote.app
@@ -43,6 +43,15 @@ export class ElectronService {
this.MenuItem = this.remote.MenuItem this.MenuItem = this.remote.MenuItem
} }
/**
* Removes OS focus from Terminus' window
*/
loseFocus () {
if (process.platform === 'darwin') {
this.remote.Menu.sendActionToFirstResponder('hide:')
}
}
async showMessageBox ( async showMessageBox (
browserWindow: Electron.BrowserWindow, browserWindow: Electron.BrowserWindow,
options: Electron.MessageBoxOptions options: Electron.MessageBoxOptions

View File

@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service' import { ElectronService } from './electron.service'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import * as mixpanel from 'mixpanel' import * as mixpanel from 'mixpanel'
import { v4 as uuidv4 } from 'uuid' import * as uuidv4 from 'uuid/v4'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HomeBaseService { export class HomeBaseService {
@@ -11,7 +11,7 @@ export class HomeBaseService {
mixpanel: any mixpanel: any
/** @hidden */ /** @hidden */
private constructor ( constructor (
private electron: ElectronService, private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
) { ) {
@@ -22,11 +22,11 @@ export class HomeBaseService {
} }
} }
openGitHub (): void { openGitHub () {
this.electron.shell.openExternal('https://github.com/eugeny/terminus') this.electron.shell.openExternal('https://github.com/eugeny/terminus')
} }
reportBug (): void { reportBug () {
let body = `Version: ${this.appVersion}\n` let body = `Version: ${this.appVersion}\n`
body += `Platform: ${os.platform()} ${os.release()}\n` body += `Platform: ${os.platform()} ${os.release()}\n`
const label = { 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}`) this.electron.shell.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
} }
enableAnalytics (): void { enableAnalytics () {
if (!window.localStorage.analyticsUserID) { if (!window.localStorage.analyticsUserID) {
window.localStorage.analyticsUserID = uuidv4() window.localStorage.analyticsUserID = uuidv4()
} }
@@ -56,7 +56,7 @@ export class HomeBaseService {
this.mixpanel.track('launch', this.getAnalyticsProperties()) this.mixpanel.track('launch', this.getAnalyticsProperties())
} }
getAnalyticsProperties (): Record<string, string> { getAnalyticsProperties () {
return { return {
distinct_id: window.localStorage.analyticsUserID, // eslint-disable-line @typescript-eslint/camelcase distinct_id: window.localStorage.analyticsUserID, // eslint-disable-line @typescript-eslint/camelcase
platform: process.platform, platform: process.platform,

View File

@@ -166,6 +166,7 @@ export class HostAppService {
this.configChangeBroadcast.next() this.configChangeBroadcast.next()
})) }))
if ( if (
isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED) && isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED) &&
!isWindowsBuild(WIN_BUILD_FLUENT_BG_MOVE_BUG_FIXED) !isWindowsBuild(WIN_BUILD_FLUENT_BG_MOVE_BUG_FIXED)
@@ -177,48 +178,48 @@ export class HostAppService {
/** /**
* Returns the current remote [[BrowserWindow]] * Returns the current remote [[BrowserWindow]]
*/ */
getWindow (): Electron.BrowserWindow { getWindow () {
return this.electron.BrowserWindow.fromId(this.windowId) return this.electron.BrowserWindow.fromId(this.windowId)
} }
newWindow (): void { newWindow () {
this.electron.ipcRenderer.send('app:new-window') this.electron.ipcRenderer.send('app:new-window')
} }
toggleFullscreen (): void { toggleFullscreen () {
const window = this.getWindow() const window = this.getWindow()
window.setFullScreen(!this.isFullScreen) window.setFullScreen(!this.isFullScreen)
} }
openDevTools (): void { openDevTools () {
this.getWindow().webContents.openDevTools({ mode: 'undocked' }) this.getWindow().webContents.openDevTools({ mode: 'undocked' })
} }
focusWindow (): void { focusWindow () {
this.electron.ipcRenderer.send('window-focus') this.electron.ipcRenderer.send('window-focus')
} }
minimize (): void { minimize () {
this.electron.ipcRenderer.send('window-minimize') this.electron.ipcRenderer.send('window-minimize')
} }
maximize (): void { maximize () {
this.electron.ipcRenderer.send('window-maximize') this.electron.ipcRenderer.send('window-maximize')
} }
unmaximize (): void { unmaximize () {
this.electron.ipcRenderer.send('window-unmaximize') this.electron.ipcRenderer.send('window-unmaximize')
} }
toggleMaximize (): void { toggleMaximize () {
this.electron.ipcRenderer.send('window-toggle-maximize') this.electron.ipcRenderer.send('window-toggle-maximize')
} }
setBounds (bounds: Bounds): void { setBounds (bounds: Bounds) {
this.electron.ipcRenderer.send('window-set-bounds', bounds) this.electron.ipcRenderer.send('window-set-bounds', bounds)
} }
setAlwaysOnTop (flag: boolean): void { setAlwaysOnTop (flag: boolean) {
this.electron.ipcRenderer.send('window-set-always-on-top', flag) this.electron.ipcRenderer.send('window-set-always-on-top', flag)
} }
@@ -227,7 +228,7 @@ export class HostAppService {
* *
* @param type `null`, or `fluent` when supported (Windowd only) * @param type `null`, or `fluent` when supported (Windowd only)
*/ */
setVibrancy (enable: boolean, type: string|null): void { setVibrancy (enable: boolean, type: string|null) {
if (!isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)) { if (!isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)) {
type = null type = null
} }
@@ -235,42 +236,38 @@ export class HostAppService {
this.electron.ipcRenderer.send('window-set-vibrancy', enable, type) this.electron.ipcRenderer.send('window-set-vibrancy', enable, type)
} }
setTitle (title: string): void { setTitle (title: string) {
this.electron.ipcRenderer.send('window-set-title', title) this.electron.ipcRenderer.send('window-set-title', title)
} }
setTouchBar (touchBar: Electron.TouchBar): void { setTouchBar (touchBar: Electron.TouchBar) {
this.getWindow().setTouchBar(touchBar) this.getWindow().setTouchBar(touchBar)
} }
popupContextMenu (menuDefinition: Electron.MenuItemConstructorOptions[]): void { popupContextMenu (menuDefinition: Electron.MenuItemConstructorOptions[]) {
this.electron.Menu.buildFromTemplate(menuDefinition).popup({}) this.electron.Menu.buildFromTemplate(menuDefinition).popup({})
} }
/** /**
* Notifies other windows of config file changes * Notifies other windows of config file changes
*/ */
broadcastConfigChange (configStore: {[k: string]: any}): void { broadcastConfigChange () {
this.electron.ipcRenderer.send('app:config-change', configStore) this.electron.ipcRenderer.send('app:config-change')
} }
emitReady (): void { emitReady () {
this.electron.ipcRenderer.send('app:ready') this.electron.ipcRenderer.send('app:ready')
} }
bringToFront (): void { bringToFront () {
this.electron.ipcRenderer.send('window-bring-to-front') this.electron.ipcRenderer.send('window-bring-to-front')
} }
closeWindow (): void { closeWindow () {
this.electron.ipcRenderer.send('window-close') this.electron.ipcRenderer.send('window-close')
} }
registerGlobalHotkey (specs: string[]): void { relaunch () {
this.electron.ipcRenderer.send('app:register-global-hotkey', specs)
}
relaunch (): void {
if (this.isPortable) { if (this.isPortable) {
this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE }) this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })
} else { } else {
@@ -279,7 +276,7 @@ export class HostAppService {
this.electron.app.exit() this.electron.app.exit()
} }
quit (): void { quit () {
this.logger.info('Quitting') this.logger.info('Quitting')
this.electron.app.quit() this.electron.app.quit()
} }

View File

@@ -1,10 +1,8 @@
import { Injectable, Inject, NgZone, EventEmitter } from '@angular/core' import { Injectable, Inject, NgZone, EventEmitter } from '@angular/core'
import { Observable, Subject } from 'rxjs'
import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider' import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
import { stringifyKeySequence } from './hotkeys.util' import { stringifyKeySequence } from './hotkeys.util'
import { ConfigService } from './config.service' import { ConfigService } from '../services/config.service'
import { ElectronService } from './electron.service' import { ElectronService } from '../services/electron.service'
import { HostAppService } from './hostApp.service'
export interface PartialHotkeyMatch { export interface PartialHotkeyMatch {
id: string id: string
@@ -22,23 +20,14 @@ interface EventBufferEntry {
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HotkeysService { export class HotkeysService {
key = new EventEmitter<KeyboardEvent>() key = new EventEmitter<KeyboardEvent>()
/** @hidden */
matchedHotkey = new EventEmitter<string>() matchedHotkey = new EventEmitter<string>()
globalHotkey = new EventEmitter<void>()
/**
* Fired for each recognized hotkey
*/
get hotkey$ (): Observable<string> { return this._hotkey }
private _hotkey = new Subject<string>()
private currentKeystrokes: EventBufferEntry[] = [] private currentKeystrokes: EventBufferEntry[] = []
private disabledLevel = 0 private disabledLevel = 0
private hotkeyDescriptions: HotkeyDescription[] = [] private hotkeyDescriptions: HotkeyDescription[] = []
private constructor ( private constructor (
private zone: NgZone, private zone: NgZone,
private hostApp: HostAppService,
private electron: ElectronService, private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
@Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[], @Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
@@ -60,9 +49,6 @@ export class HotkeysService {
this.getHotkeyDescriptions().then(hotkeys => { this.getHotkeyDescriptions().then(hotkeys => {
this.hotkeyDescriptions = hotkeys this.hotkeyDescriptions = hotkeys
}) })
// deprecated
this.hotkey$.subscribe(h => this.matchedHotkey.emit(h))
} }
/** /**
@@ -71,7 +57,7 @@ export class HotkeysService {
* @param name DOM event name * @param name DOM event name
* @param nativeEvent event object * @param nativeEvent event object
*/ */
pushKeystroke (name: string, nativeEvent: KeyboardEvent): void { pushKeystroke (name: string, nativeEvent: KeyboardEvent) {
(nativeEvent as any).event = name (nativeEvent as any).event = name
this.currentKeystrokes.push({ event: nativeEvent, time: performance.now() }) this.currentKeystrokes.push({ event: nativeEvent, time: performance.now() })
} }
@@ -79,26 +65,26 @@ export class HotkeysService {
/** /**
* Check the buffer for new complete keystrokes * Check the buffer for new complete keystrokes
*/ */
processKeystrokes (): void { processKeystrokes () {
if (this.isEnabled()) { if (this.isEnabled()) {
this.zone.run(() => { this.zone.run(() => {
const matched = this.getCurrentFullyMatchedHotkey() const matched = this.getCurrentFullyMatchedHotkey()
if (matched) { if (matched) {
console.log('Matched hotkey', matched) console.log('Matched hotkey', matched)
this._hotkey.next(matched) this.matchedHotkey.emit(matched)
this.clearCurrentKeystrokes() this.clearCurrentKeystrokes()
} }
}) })
} }
} }
emitKeyEvent (nativeEvent: KeyboardEvent): void { emitKeyEvent (nativeEvent: KeyboardEvent) {
this.zone.run(() => { this.zone.run(() => {
this.key.emit(nativeEvent) this.key.emit(nativeEvent)
}) })
} }
clearCurrentKeystrokes (): void { clearCurrentKeystrokes () {
this.currentKeystrokes = [] this.currentKeystrokes = []
} }
@@ -156,15 +142,15 @@ export class HotkeysService {
return this.hotkeyDescriptions.filter((x) => x.id === id)[0] return this.hotkeyDescriptions.filter((x) => x.id === id)[0]
} }
enable (): void { enable () {
this.disabledLevel-- this.disabledLevel--
} }
disable (): void { disable () {
this.disabledLevel++ this.disabledLevel++
} }
isEnabled (): boolean { isEnabled () {
return this.disabledLevel === 0 return this.disabledLevel === 0
} }
@@ -183,7 +169,6 @@ export class HotkeysService {
if (typeof value === 'string') { if (typeof value === 'string') {
value = [value] value = [value]
} }
const specs: string[] = []
value.forEach((item: string | string[]) => { value.forEach((item: string | string[]) => {
item = typeof item === 'string' ? [item] : item item = typeof item === 'string' ? [item] : item
@@ -192,13 +177,13 @@ export class HotkeysService {
electronKeySpec = electronKeySpec.replace('⌘', 'Command') electronKeySpec = electronKeySpec.replace('⌘', 'Command')
electronKeySpec = electronKeySpec.replace('⌥', 'Alt') electronKeySpec = electronKeySpec.replace('⌥', 'Alt')
electronKeySpec = electronKeySpec.replace(/-/g, '+') electronKeySpec = electronKeySpec.replace(/-/g, '+')
specs.push(electronKeySpec) this.electron.globalShortcut.register(electronKeySpec, () => {
this.globalHotkey.emit()
})
} catch (err) { } catch (err) {
console.error('Could not register the global hotkey:', err) console.error('Could not register the global hotkey:', err)
} }
}) })
this.hostApp.registerGlobalHotkey(specs)
} }
private getHotkeysConfig () { private getHotkeysConfig () {
@@ -218,9 +203,6 @@ export class HotkeysService {
if (typeof value === 'string') { if (typeof value === 'string') {
value = [value] value = [value]
} }
if (!(value instanceof Array)) {
continue
}
if (value) { if (value) {
value = value.map((item: string | string[]) => typeof item === 'string' ? [item] : item) value = value.map((item: string | string[]) => typeof item === 'string' ? [item] : item)
keys[key] = value keys[key] = value

View File

@@ -32,27 +32,27 @@ export class Logger {
private name: string, private name: string,
) {} ) {}
debug (...args: any[]): void { debug (...args: any[]) {
this.doLog('debug', ...args) this.doLog('debug', ...args)
} }
info (...args: any[]): void { info (...args: any[]) {
this.doLog('info', ...args) this.doLog('info', ...args)
} }
warn (...args: any[]): void { warn (...args: any[]) {
this.doLog('warn', ...args) this.doLog('warn', ...args)
} }
error (...args: any[]): void { error (...args: any[]) {
this.doLog('error', ...args) this.doLog('error', ...args)
} }
log (...args: any[]): void { log (...args: any[]) {
this.doLog('log', ...args) this.doLog('log', ...args)
} }
private doLog (level: string, ...args: any[]): void { private doLog (level: string, ...args: any[]) {
console[level](`%c[${this.name}]`, 'color: #aaa', ...args) console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
if (this.winstonLogger) { if (this.winstonLogger) {
this.winstonLogger[level](...args) this.winstonLogger[level](...args)
@@ -65,7 +65,7 @@ export class LogService {
private log: any private log: any
/** @hidden */ /** @hidden */
private constructor (electron: ElectronService) { constructor (electron: ElectronService) {
this.log = initializeWinston(electron) this.log = initializeWinston(electron)
} }

View File

@@ -33,7 +33,7 @@ export class ShellIntegrationService {
command: 'paste "%V"', command: 'paste "%V"',
}, },
] ]
private constructor ( constructor (
private electron: ElectronService, private electron: ElectronService,
private hostApp: HostAppService, private hostApp: HostAppService,
) { ) {
@@ -58,7 +58,7 @@ export class ShellIntegrationService {
return true return true
} }
async install (): Promise<void> { async install () {
const exe: string = process.env.PORTABLE_EXECUTABLE_FILE || this.electron.app.getPath('exe') const exe: string = process.env.PORTABLE_EXECUTABLE_FILE || this.electron.app.getPath('exe')
if (this.hostApp.platform === Platform.macOS) { if (this.hostApp.platform === Platform.macOS) {
for (const wf of this.automatorWorkflows) { for (const wf of this.automatorWorkflows) {
@@ -82,7 +82,7 @@ export class ShellIntegrationService {
} }
} }
async remove (): Promise<void> { async remove () {
if (this.hostApp.platform === Platform.macOS) { if (this.hostApp.platform === Platform.macOS) {
for (const wf of this.automatorWorkflows) { for (const wf of this.automatorWorkflows) {
await exec(`rm -rf "${this.automatorWorkflowsDestination}/${wf}"`) await exec(`rm -rf "${this.automatorWorkflowsDestination}/${wf}"`)

View File

@@ -1,5 +1,5 @@
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from '../api/tabRecovery' import { TabRecoveryProvider, RecoveredTab } from '../api/tabRecovery'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from '../services/log.service' import { Logger, LogService } from '../services/log.service'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
@@ -8,9 +8,8 @@ import { ConfigService } from '../services/config.service'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class TabRecoveryService { export class TabRecoveryService {
logger: Logger logger: Logger
enabled = false
private constructor ( constructor (
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[], @Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
private config: ConfigService, private config: ConfigService,
log: LogService log: LogService
@@ -18,10 +17,7 @@ export class TabRecoveryService {
this.logger = log.create('tabRecovery') this.logger = log.create('tabRecovery')
} }
async saveTabs (tabs: BaseTabComponent[]): Promise<void> { async saveTabs (tabs: BaseTabComponent[]) {
if (!this.enabled) {
return
}
window.localStorage.tabsRecovery = JSON.stringify( window.localStorage.tabsRecovery = JSON.stringify(
await Promise.all( await Promise.all(
tabs tabs
@@ -30,10 +26,7 @@ export class TabRecoveryService {
if (token) { if (token) {
token = token.then(r => { token = token.then(r => {
if (r) { if (r) {
r.tabTitle = tab.title r.tabColor = tab.color
if (tab.color) {
r.tabColor = tab.color
}
} }
return r return r
}) })
@@ -45,14 +38,13 @@ export class TabRecoveryService {
) )
} }
async recoverTab (token: RecoveryToken): Promise<RecoveredTab|null> { async recoverTab (token: any): Promise<RecoveredTab|null> {
for (const provider of this.config.enabledServices(this.tabRecoveryProviders)) { for (const provider of this.config.enabledServices(this.tabRecoveryProviders)) {
try { try {
const tab = await provider.recover(token) const tab = await provider.recover(token)
if (tab !== null) { if (tab !== null) {
tab.options = tab.options || {} tab.options = tab.options || {}
tab.options.color = token.tabColor || null tab.options.color = token.tabColor || null
tab.options.title = token.tabTitle || ''
return tab return tab
} }
} catch (error) { } catch (error) {

View File

@@ -8,7 +8,7 @@ export type TabComponentType = new (...args: any[]) => BaseTabComponent
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class TabsService { export class TabsService {
/** @hidden */ /** @hidden */
private constructor ( constructor (
private componentFactoryResolver: ComponentFactoryResolver, private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector, private injector: Injector,
private tabRecovery: TabRecoveryService, private tabRecovery: TabRecoveryService,
@@ -17,7 +17,7 @@ export class TabsService {
/** /**
* Instantiates a tab component and assigns given inputs * Instantiates a tab component and assigns given inputs
*/ */
create (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent { create (type: TabComponentType, inputs?: any): BaseTabComponent {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type) const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type)
const componentRef = componentFactory.create(this.injector) const componentRef = componentFactory.create(this.injector)
const tab = componentRef.instance const tab = componentRef.instance

View File

@@ -7,7 +7,7 @@ export class ThemesService {
private styleElement: HTMLElement|null = null private styleElement: HTMLElement|null = null
/** @hidden */ /** @hidden */
private constructor ( constructor (
private config: ConfigService, private config: ConfigService,
@Inject(Theme) private themes: Theme[], @Inject(Theme) private themes: Theme[],
) { ) {

View File

@@ -14,7 +14,7 @@ export class TouchbarService {
private tabSegments: SegmentedControlSegment[] = [] private tabSegments: SegmentedControlSegment[] = []
private nsImageCache: {[id: string]: Electron.NativeImage} = {} private nsImageCache: {[id: string]: Electron.NativeImage} = {}
private constructor ( constructor (
private app: AppService, private app: AppService,
private hostApp: HostAppService, private hostApp: HostAppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[], @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
@@ -48,7 +48,7 @@ export class TouchbarService {
}) })
} }
updateTabs (): void { updateTabs () {
this.tabSegments = this.app.tabs.map(tab => ({ this.tabSegments = this.app.tabs.map(tab => ({
label: this.shortenTitle(tab.title), label: this.shortenTitle(tab.title),
})) }))
@@ -56,7 +56,7 @@ export class TouchbarService {
this.tabsSegmentedControl.selectedIndex = this.app.tabs.indexOf(this.app.activeTab) this.tabsSegmentedControl.selectedIndex = this.app.tabs.indexOf(this.app.activeTab)
} }
update (): void { update () {
if (this.hostApp.platform !== Platform.macOS) { if (this.hostApp.platform !== Platform.macOS) {
return return
} }

View File

@@ -8,7 +8,6 @@ import { Injectable } from '@angular/core'
import { Logger, LogService } from './log.service' import { Logger, LogService } from './log.service'
import { ElectronService } from './electron.service' import { ElectronService } from './electron.service'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import { AppUpdater } from 'electron-updater'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest' const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
@@ -19,9 +18,9 @@ export class UpdaterService {
private downloaded: Promise<boolean> private downloaded: Promise<boolean>
private electronUpdaterAvailable = true private electronUpdaterAvailable = true
private updateURL: string private updateURL: string
private autoUpdater: AppUpdater private autoUpdater
private constructor ( constructor (
log: LogService, log: LogService,
private electron: ElectronService, private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
@@ -80,7 +79,7 @@ export class UpdaterService {
return this.downloaded return this.downloaded
} }
async update (): Promise<void> { async update () {
if (!this.electronUpdaterAvailable) { if (!this.electronUpdaterAvailable) {
this.electron.shell.openExternal(this.updateURL) this.electron.shell.openExternal(this.updateURL)
} else { } else {

View File

@@ -1,16 +1,14 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable, NgZone } from '@angular/core' import { Injectable, NgZone } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
import { BaseTabComponent } from './components/baseTab.component' import { BaseTabComponent } from './components/baseTab.component'
import { TabHeaderComponent } from './components/tabHeader.component' import { TabHeaderComponent } from './components/tabHeader.component'
import { SplitTabComponent, SplitDirection } from './components/splitTab.component'
import { TabContextMenuItemProvider } from './api/tabContextMenuProvider' import { TabContextMenuItemProvider } from './api/tabContextMenuProvider'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class TabManagementContextMenu extends TabContextMenuItemProvider { export class CloseContextMenu extends TabContextMenuItemProvider {
weight = 99 weight = -5
constructor ( constructor (
private app: AppService, private app: AppService,
@@ -20,7 +18,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
let items: Electron.MenuItemConstructorOptions[] = [ let items = [
{ {
label: 'Close', label: 'Close',
click: () => this.zone.run(() => { click: () => this.zone.run(() => {
@@ -60,24 +58,6 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
}), }),
}, },
] ]
} else {
if (tab.parent instanceof SplitTabComponent) {
const directions: SplitDirection[] = ['r', 'b', 'l', 't']
items.push({
label: 'Split',
submenu: directions.map(dir => ({
label: {
r: 'Right',
b: 'Down',
l: 'Left',
t: 'Up',
}[dir],
click: () => this.zone.run(() => {
(tab.parent as SplitTabComponent).splitTab(tab, dir)
}),
})) as Electron.MenuItemConstructorOptions[],
})
}
} }
return items return items
} }
@@ -106,10 +86,8 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
let items: Electron.MenuItemConstructorOptions[] = []
if (tabHeader) { if (tabHeader) {
items = [ return [
...items,
{ {
label: 'Rename', label: 'Rename',
click: () => this.zone.run(() => tabHeader?.showRenameTabModal()), click: () => this.zone.run(() => tabHeader?.showRenameTabModal()),
@@ -132,7 +110,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
}, },
] ]
} }
return items return []
} }
} }

View File

@@ -20,8 +20,4 @@ app-root {
ssh-tab .content { ssh-tab .content {
margin: 5px !important; margin: 5px !important;
} }
serial-tab .content {
margin: 5px !important;
}
} }

View File

@@ -246,7 +246,7 @@ ngb-tabset .tab-content {
} }
.list-group-item { .list-group-item {
transition: 0.0625s background; transition: 0.25s background;
i + * { i + * {
margin-left: 10px; margin-left: 10px;
@@ -262,29 +262,6 @@ 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 { checkbox i.on {
color: $blue; color: $blue;
} }
@@ -415,7 +392,3 @@ search-panel {
border-color: $nav-tabs-link-active-border-color; border-color: $nav-tabs-link-active-border-color;
} }
} }
hr {
border-color: $list-group-border-color;
}

View File

@@ -10,7 +10,6 @@ export function isWindowsBuild (build: number): boolean {
return process.platform === 'win32' && parseFloat(os.release()) >= 10 && parseInt(os.release().split('.')[2]) >= build 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 { export function getCSSFontFamily (config: any): string {
let fonts: string[] = config.terminal.font.split(',').map(x => x.trim().replace(/"/g, '')) let fonts: string[] = config.terminal.font.split(',').map(x => x.trim().replace(/"/g, ''))
if (config.terminal.fallbackFont) { if (config.terminal.fallbackFont) {

View File

@@ -3,21 +3,14 @@
"@types/js-yaml@^3.9.0": "@types/js-yaml@^3.9.0":
version "3.12.4" version "3.12.1"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.4.tgz#7d3b534ec35a0585128e2d332db1403ebe057e25" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.1.tgz#5c6f4a1eabca84792fbd916f0cb40847f123c656"
integrity sha512-fYMgzN+9e28R81weVN49inn/u798ruU91En1ZnGvSZzCRc5jXx9B2EDhlRaWmcO1RIxFHL8AajRXzxDuJu93+A== integrity sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA==
"@types/node@*": "@types/semver@^6.0.2":
version "13.7.1" version "6.2.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.1.tgz#238eb34a66431b71d2aaddeaa7db166f25971a0d" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
integrity sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA== 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" "*"
"@types/shell-escape@^0.2.0": "@types/shell-escape@^0.2.0":
version "0.2.0" version "0.2.0"
@@ -52,15 +45,10 @@ async@^2.6.1:
dependencies: dependencies:
lodash "^4.17.11" lodash "^4.17.11"
at-least-node@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
axios@^0.19.0: axios@^0.19.0:
version "0.19.2" version "0.19.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.1.tgz#8a6a04eed23dfe72747e1dd43c604b8f1677b5aa"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== integrity sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==
dependencies: dependencies:
follow-redirects "1.5.10" follow-redirects "1.5.10"
@@ -69,10 +57,10 @@ bootstrap@^4.1.3:
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01"
integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA== integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==
builder-util-runtime@8.7.0: builder-util-runtime@8.4.0:
version "8.7.0" version "8.4.0"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.0.tgz#e48ad004835c8284662e8eaf47a53468c66e8e8d" resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.4.0.tgz#3163fffc078e6b8f3dd5b6eb12a8345573590682"
integrity sha512-G1AqqVM2vYTrSFR982c1NNzwXKrGLQjVjaZaWQdn4O6Z3YKjdMDofw88aD9jpyK9ZXkrCxR0tI3Qe9wNbyTlXg== integrity sha512-CJB/eKfPf2vHrkmirF5eicVnbDCkMBbwd5tRYlTlgud16zFeqD7QmrVUAOEXdnsrcNkiLg9dbuUsQKtl/AwsYQ==
dependencies: dependencies:
debug "^4.1.1" debug "^4.1.1"
sax "^1.2.4" sax "^1.2.4"
@@ -129,9 +117,9 @@ colorspace@1.1.x:
text-hex "1.0.x" text-hex "1.0.x"
core-js@^3.1.2: core-js@^3.1.2:
version "3.6.5" version "3.6.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647"
integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"
@@ -174,17 +162,18 @@ diagnostics@^1.1.1:
kuler "1.0.x" kuler "1.0.x"
electron-updater@^4.0.6: electron-updater@^4.0.6:
version "4.3.1" version "4.2.0"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.3.1.tgz#9d485b6262bc56fcf7ee62b1dc1b3b105a3e96a7" resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.0.tgz#f9ecfc657f65ead737d42b9efecf628d3756b550"
integrity sha512-UDC5AHCgeiHJYDYWZG/rsl1vdAFKqI/Lm7whN57LKAk8EfhTewhcEHzheRcncLgikMcQL8gFo1KeX51tf5a5Wg== integrity sha512-GuS3g7HDh17x/SaFjxjswlWUaKHczksYkV2Xc5CKj/bZH0YCvTSHtOmnBAdAmCk99u/71p3zP8f0jIqDfGcjww==
dependencies: dependencies:
"@types/semver" "^7.1.0" "@types/semver" "^6.0.2"
builder-util-runtime "8.7.0" builder-util-runtime "8.4.0"
fs-extra "^9.0.0" fs-extra "^8.1.0"
js-yaml "^3.13.1" js-yaml "^3.13.1"
lazy-val "^1.0.4" lazy-val "^1.0.4"
lodash.isequal "^4.5.0" lodash.isequal "^4.5.0"
semver "^7.1.3" pako "^1.0.10"
semver "^6.3.0"
enabled@1.0.x: enabled@1.0.x:
version "1.0.2" version "1.0.2"
@@ -232,15 +221,14 @@ follow-redirects@1.5.10:
dependencies: dependencies:
debug "=3.1.0" debug "=3.1.0"
fs-extra@^9.0.0: fs-extra@^8.1.0:
version "9.0.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies: dependencies:
at-least-node "^1.0.0"
graceful-fs "^4.2.0" graceful-fs "^4.2.0"
jsonfile "^6.0.1" jsonfile "^4.0.0"
universalify "^1.0.0" universalify "^0.1.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0: graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.2" version "4.2.2"
@@ -276,19 +264,17 @@ isarray@~1.0.0:
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
js-yaml@^3.13.1, js-yaml@^3.9.0: js-yaml@^3.13.1, js-yaml@^3.9.0:
version "3.14.0" version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
dependencies: dependencies:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
jsonfile@^6.0.1: jsonfile@^4.0.0:
version "6.0.1" version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg== integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
dependencies:
universalify "^1.0.0"
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.6" graceful-fs "^4.1.6"
@@ -360,6 +346,11 @@ one-time@0.0.4:
resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e" resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e"
integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4= 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==
perfect-scrollbar@^1.4.0: perfect-scrollbar@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.4.0.tgz#5d014ef9775e1f43058a1dbae9ed1daf0e7091f1" resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.4.0.tgz#5d014ef9775e1f43058a1dbae9ed1daf0e7091f1"
@@ -407,10 +398,10 @@ sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
semver@^7.1.3: semver@^6.3.0:
version "7.1.3" version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
shell-escape@^0.2.0: shell-escape@^0.2.0:
version "0.2.0" version "0.2.0"
@@ -458,20 +449,20 @@ triple-beam@^1.2.0, triple-beam@^1.3.0:
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
universalify@^1.0.0: universalify@^0.1.0:
version "1.0.0" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
util-deprecate@^1.0.1, util-deprecate@~1.0.1: util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
uuid@^8.0.0: uuid@^3.3.2:
version "8.1.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
winston-transport@^4.3.0: winston-transport@^4.3.0:
version "4.3.0" version "4.3.0"

View File

@@ -1,6 +1,6 @@
{ {
"name": "terminus-plugin-manager", "name": "terminus-plugin-manager",
"version": "1.0.104-nightly.0", "version": "1.0.99-nightly.0",
"description": "Terminus' plugin manager", "description": "Terminus' plugin manager",
"keywords": [ "keywords": [
"terminus-builtin-plugin" "terminus-builtin-plugin"
@@ -17,7 +17,7 @@
"author": "Eugene Pankov", "author": "Eugene Pankov",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/semver": "^7.1.0", "@types/semver": "^6.0.0",
"axios": "^0.19.0", "axios": "^0.19.0",
"mz": "^2.6.0", "mz": "^2.6.0",
"semver": "^7.1.1" "semver": "^7.1.1"

View File

@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { BehaviorSubject, Observable } from 'rxjs' import { BehaviorSubject, Observable } from 'rxjs'
import { debounceTime, distinctUntilChanged, first, tap, flatMap, map } from 'rxjs/operators' import { debounceTime, distinctUntilChanged, first, tap, flatMap, map } from 'rxjs/operators'
import semverGt from 'semver/functions/gt' import * as semver from 'semver'
import { Component, Input } from '@angular/core' import { Component, Input } from '@angular/core'
import { ConfigService, ElectronService } from 'terminus-core' import { ConfigService, ElectronService } from 'terminus-core'
@@ -49,7 +48,7 @@ export class PluginsSettingsTabComponent {
return plugins return plugins
})).subscribe(available => { })).subscribe(available => {
for (const plugin of this.pluginManager.installedPlugins) { for (const plugin of this.pluginManager.installedPlugins) {
this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semverGt(x.version, plugin.version)) || null this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semver.gt(x.version, plugin.version)) || null
} }
}) })
} }

View File

@@ -34,13 +34,13 @@ export class PluginManagerService {
private npmReady: Promise<void> private npmReady: Promise<void>
private npm: any private npm: any
private constructor ( constructor (
log: LogService, log: LogService,
) { ) {
this.logger = log.create('pluginManager') this.logger = log.create('pluginManager')
} }
async getNPM (): Promise<any> { async getNPM () {
if (!this.npm) { if (!this.npm) {
if (!this.npmReady) { if (!this.npmReady) {
this.npmReady = new Promise(resolve => { this.npmReady = new Promise(resolve => {
@@ -83,7 +83,7 @@ export class PluginManagerService {
) )
} }
async installPlugin (plugin: PluginInfo): Promise<void> { async installPlugin (plugin: PluginInfo) {
(await this.getNPM()).commands.install([`${plugin.packageName}@${plugin.version}`], err => { (await this.getNPM()).commands.install([`${plugin.packageName}@${plugin.version}`], err => {
if (err) { if (err) {
this.logger.error(err) this.logger.error(err)
@@ -93,7 +93,7 @@ export class PluginManagerService {
}) })
} }
async uninstallPlugin (plugin: PluginInfo): Promise<void> { async uninstallPlugin (plugin: PluginInfo) {
(await this.getNPM()).commands.remove([plugin.packageName], err => { (await this.getNPM()).commands.remove([plugin.packageName], err => {
if (err) { if (err) {
this.logger.error(err) this.logger.error(err)

View File

@@ -2,17 +2,10 @@
# yarn lockfile v1 # yarn lockfile v1
"@types/node@*": "@types/semver@^6.0.0":
version "13.7.6" version "6.2.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA== 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" "*"
any-promise@^1.0.0: any-promise@^1.0.0:
version "1.3.0" version "1.3.0"
@@ -20,9 +13,9 @@ any-promise@^1.0.0:
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
axios@^0.19.0: axios@^0.19.0:
version "0.19.2" version "0.19.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.1.tgz#8a6a04eed23dfe72747e1dd43c604b8f1677b5aa"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== integrity sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==
dependencies: dependencies:
follow-redirects "1.5.10" follow-redirects "1.5.10"
@@ -60,9 +53,9 @@ object-assign@^4.0.1:
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
semver@^7.1.1: semver@^7.1.1:
version "7.2.2" version "7.1.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.2.2.tgz#d01432d74ed3010a20ffaf909d63a691520521cd" resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
integrity sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA== integrity sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A==
thenify-all@^1.0.0: thenify-all@^1.0.0:
version "1.6.0" version "1.6.0"

View File

@@ -1,37 +0,0 @@
{
"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": "*"
}
}

View File

@@ -1,156 +0,0 @@
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[]
}

View File

@@ -1,36 +0,0 @@
/* 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()
},
}]
}
}

View File

@@ -1,134 +0,0 @@
.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

View File

@@ -1,95 +0,0 @@
/* 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: '' })
}
}

View File

@@ -1,16 +0,0 @@
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

View File

@@ -1,80 +0,0 @@
/* 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
}
}

View File

@@ -1,16 +0,0 @@
.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

View File

@@ -1,7 +0,0 @@
@import '../../../terminus-ssh/src/components/sshTab.component.scss';
:host {
select {
width: auto;
}
}

View File

@@ -1,118 +0,0 @@
/* 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()
}
}

View File

@@ -1,19 +0,0 @@
import { ConfigProvider } from 'terminus-core'
/** @hidden */
export class SerialConfigProvider extends ConfigProvider {
defaults = {
serial: {
connections: [],
options: {
},
},
hotkeys: {
serial: [
'Alt-K',
],
},
}
platformDefaults = { }
}

View File

@@ -1,17 +0,0 @@
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
}
}

View File

@@ -1,27 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,48 +0,0 @@
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

View File

@@ -1,21 +0,0 @@
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
}
}

View File

@@ -1,169 +0,0 @@
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}`)
}
}

View File

@@ -1,16 +0,0 @@
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
}
}

View File

@@ -1,7 +0,0 @@
{
"extends": "../tsconfig.json",
"exclude": ["node_modules", "dist", "typings"],
"compilerOptions": {
"baseUrl": "src"
}
}

Some files were not shown because too many files have changed in this diff Show More