1
1
mirror of https://github.com/Eugeny/tabby.git synced 2024-09-11 13:13:59 +03:00

@typescript-eslint linter

This commit is contained in:
Eugene Pankov 2019-06-14 23:47:48 +02:00
parent a5ecdeb5ea
commit c008a3478e
96 changed files with 1334 additions and 810 deletions

82
.eslintrc.yml Normal file
View File

@ -0,0 +1,82 @@
parser: '@typescript-eslint/parser'
parserOptions:
project: tsconfig.json
extends:
- 'plugin:@typescript-eslint/all'
plugins:
- '@typescript-eslint'
env:
browser: true
es6: true
node: true
commonjs: true
rules:
'@typescript-eslint/semi':
- error
- never
'@typescript-eslint/indent':
- error
- 4
'@typescript-eslint/explicit-member-accessibility':
- error
- accessibility: no-public
overrides:
parameterProperties: explicit
'@typescript-eslint/no-require-imports': off
'@typescript-eslint/no-parameter-properties': off
'@typescript-eslint/explicit-function-return-type': off
'@typescript-eslint/no-explicit-any': off
'@typescript-eslint/no-magic-numbers': off
'@typescript-eslint/member-delimiter-style': off
'@typescript-eslint/promise-function-async': off
'@typescript-eslint/no-unnecessary-type-assertion': off
'@typescript-eslint/require-array-sort-compare': off
'@typescript-eslint/no-use-before-define':
- error
- classes: false
no-duplicate-imports: error
array-bracket-spacing:
- error
- never
block-scoped-var: error
brace-style:
- error
- 1tbs
- allowSingleLine: true
computed-property-spacing:
- error
- never
comma-dangle:
- error
- always-multiline
curly: error
eol-last: error
eqeqeq:
- error
- smart
linebreak-style:
- error
- unix
max-depth:
- 1
- 5
max-statements:
- 1
- 80
no-multiple-empty-lines: error
no-mixed-spaces-and-tabs: error
no-trailing-spaces: error
'@typescript-eslint/no-unused-vars':
- error
- vars: all
args: after-used
argsIgnorePattern: ^_
no-undef: error
object-curly-spacing:
- error
- always
quote-props:
- warn
- as-needed
- keywords: true
numbers: true

View File

@ -92,11 +92,11 @@ Plugins provide functionality by exporting singular or multi providers:
```javascript
import { NgModule, Injectable } from '@angular/core'
import { ToolbarButtonProvider, IToolbarButton } from 'terminus-core'
import { ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
@Injectable()
export class MyButtonProvider extends ToolbarButtonProvider {
provide (): IToolbarButton[] {
provide (): ToolbarButton[] {
return [{
icon: 'star',
title: 'Foobar',

View File

@ -16,7 +16,7 @@ export function getRootModule (plugins: any[]) {
}),
]
const bootstrap = [
...(plugins.filter(x => x.bootstrap).map(x => x.bootstrap)),
...plugins.filter(x => x.bootstrap).map(x => x.bootstrap),
]
if (bootstrap.length === 0) {
@ -26,7 +26,7 @@ export function getRootModule (plugins: any[]) {
@NgModule({
imports,
bootstrap,
}) class RootModule { }
}) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
return RootModule
}

View File

@ -21,15 +21,15 @@ Raven.config(
return splitArray[splitArray.length - 1]
}
data.exception.values[0].stacktrace.frames.forEach(frame => {
data.exception.values[0].stacktrace.frames.forEach((frame: any) => {
frame.filename = normalize(frame.filename)
})
data.culprit = data.exception.values[0].stacktrace.frames[0].filename
return data
}
}
},
},
)
process.on('uncaughtException' as any, (err) => {

View File

@ -2,19 +2,19 @@ import 'zone.js'
import 'core-js/proposals/reflect-metadata'
import 'rxjs'
import isDev = require('electron-is-dev')
import * as isDev from 'electron-is-dev'
import './global.scss'
import './toastr.scss'
// Always land on the start view
location.hash = ''
import { enableProdMode, NgModuleRef } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { getRootModule } from './app.module'
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
import { findPlugins, loadPlugins, PluginInfo } from './plugins'
// Always land on the start view
location.hash = ''
;(process as any).enablePromiseAPI = true
@ -28,12 +28,12 @@ if (isDev) {
enableProdMode()
}
async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
async function bootstrap (plugins: PluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
if (safeMode) {
plugins = plugins.filter(x => x.isBuiltin)
}
const pluginsModules = await loadPlugins(plugins, (current, total) => {
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
(document.querySelector('.progress .bar') as HTMLElement).style.width = `${100 * current / total}%` // eslint-disable-line
})
const module = getRootModule(pluginsModules)
window['rootModule'] = module

View File

@ -1,6 +1,6 @@
import * as fs from 'mz/fs'
import * as path from 'path'
const nodeModule = require('module')
const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires
const nodeRequire = (global as any).require
function normalizePath (path: string): string {
@ -38,9 +38,9 @@ if (process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
}
export declare type ProgressCallback = (current: number, total: number) => void
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
export interface IPluginInfo {
export interface PluginInfo {
name: string
description: string
packageName: string
@ -87,9 +87,9 @@ const originalRequire = (global as any).require
return originalRequire.apply(this, arguments)
}
export async function findPlugins (): Promise<IPluginInfo[]> {
export async function findPlugins (): Promise<PluginInfo[]> {
const paths = nodeModule.globalPaths
let foundPlugins: IPluginInfo[] = []
let foundPlugins: PluginInfo[] = []
const candidateLocations: { pluginDir: string, packageName: string }[] = []
const PREFIX = 'terminus-'
@ -102,7 +102,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
candidateLocations.push({
pluginDir: path.dirname(pluginDir),
packageName: path.basename(pluginDir)
packageName: path.basename(pluginDir),
})
}
for (const packageName of pluginNames) {
@ -152,7 +152,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
return foundPlugins
}
export async function loadPlugins (foundPlugins: IPluginInfo[], progress: ProgressCallback): Promise<any[]> {
export async function loadPlugins (foundPlugins: PluginInfo[], progress: ProgressCallback): Promise<any[]> {
const plugins: any[] = []
progress(0, 1)
let index = 0

View File

@ -1,6 +1,6 @@
import { Component } from '@angular/core'
@Component({
template: '<app-root></app-root>'
template: '<app-root></app-root>',
})
export class RootComponent { }
export class RootComponent { } // eslint-disable-line @typescript-eslint/no-extraneous-class

View File

@ -630,7 +630,7 @@ debug@^4.1.1:
dependencies:
ms "^2.1.1"
debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
@ -1220,7 +1220,7 @@ import-lazy@^2.1.0:
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
@ -1516,7 +1516,7 @@ libnpm@^2.0.1:
read-package-json "^2.0.13"
stringify-package "^1.0.0"
libnpmaccess@*, libnpmaccess@^3.0.1:
libnpmaccess@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8"
integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA==
@ -1545,7 +1545,7 @@ libnpmhook@^5.0.2:
get-stream "^4.0.0"
npm-registry-fetch "^3.8.0"
libnpmorg@*, libnpmorg@^1.0.0:
libnpmorg@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-1.0.0.tgz#979b868c48ba28c5820e3bb9d9e73c883c16a232"
integrity sha512-o+4eVJBoDGMgRwh2lJY0a8pRV2c/tQM/SxlqXezjcAg26Qe9jigYVs+Xk0vvlYDWCDhP0g74J8UwWeAgsB7gGw==
@ -1570,7 +1570,7 @@ libnpmpublish@^1.1.0:
semver "^5.5.1"
ssri "^6.0.1"
libnpmsearch@*, libnpmsearch@^2.0.0:
libnpmsearch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-2.0.0.tgz#de05af47ada81554a5f64276a69599070d4a5685"
integrity sha512-vd+JWbTGzOSfiOc+72MU6y7WqmBXn49egCCrIXp27iE/88bX8EpG64ST1blWQI1bSMUr9l1AKPMVsqa2tS5KWA==
@ -1579,7 +1579,7 @@ libnpmsearch@*, libnpmsearch@^2.0.0:
get-stream "^4.0.0"
npm-registry-fetch "^3.8.0"
libnpmteam@*, libnpmteam@^1.0.1:
libnpmteam@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-1.0.1.tgz#ff704b1b6c06ea674b3b1101ac3e305f5114f213"
integrity sha512-gDdrflKFCX7TNwOMX1snWojCoDE5LoRWcfOC0C/fqF7mBq8Uz9zWAX4B2RllYETNO7pBupBaSyBDkTAC15cAMg==
@ -1634,11 +1634,6 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@ -1647,33 +1642,11 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
@ -1689,11 +1662,6 @@ lodash.isequal@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
lodash.union@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
@ -2051,7 +2019,7 @@ npm-pick-manifest@^2.2.3:
npm-package-arg "^6.0.0"
semver "^5.4.1"
npm-profile@*, npm-profile@^4.0.1:
npm-profile@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-4.0.1.tgz#d350f7a5e6b60691c7168fbb8392c3603583f5aa"
integrity sha512-NQ1I/1Q7YRtHZXkcuU1/IyHeLy6pd+ScKg4+DQHdfsm769TGq6HPrkbuNJVJS4zwE+0mvvmeULzQdWn2L2EsVA==
@ -2645,7 +2613,7 @@ readable-stream@~1.1.10:
isarray "0.0.1"
string_decoder "~0.10.x"
readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0:
readdir-scoped-modules@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c=

View File

@ -7,6 +7,8 @@
"@types/js-yaml": "^3.12.1",
"@types/node": "^12.0.8",
"@types/webpack-env": "1.13.9",
"@typescript-eslint/eslint-plugin": "^1.10.2",
"@typescript-eslint/parser": "^1.10.2",
"app-builder-lib": "^20.43.0",
"apply-loader": "2.0.0",
"awesome-typescript-loader": "^5.0.0",
@ -17,6 +19,7 @@
"electron-builder": "^20.43.0",
"electron-installer-snap": "^3.2.0",
"electron-rebuild": "^1.8.5",
"eslint": "^5.16.0",
"file-loader": "^4.0.0",
"graceful-fs": "^4.1.15",
"html-loader": "0.5.5",
@ -40,9 +43,6 @@
"style-loader": "^0.23.1",
"svg-inline-loader": "^0.8.0",
"to-string-loader": "1.1.5",
"tslint": "^5.17.0",
"tslint-config-standard": "^8.0.1",
"tslint-eslint-rules": "^5.4.0",
"typedoc": "^0.14.2",
"typescript": "^3.5.2",
"url-loader": "^2.0.0",
@ -131,8 +131,11 @@
"start": "cross-env TERMINUS_DEV=1 electron app --debug",
"prod": "cross-env TERMINUS_DEV=1 electron app",
"docs": "typedoc --out docs/api terminus-core/src && typedoc --out docs/api/terminal --tsconfig terminus-terminal/tsconfig.typings.json terminus-terminal/src && typedoc --out docs/api/settings --tsconfig terminus-settings/tsconfig.typings.json terminus-settings/src",
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
"lint": "eslint --ext ts */src",
"postinstall": "node ./scripts/install-deps.js"
},
"repository": "eugeny/terminus"
"repository": "eugeny/terminus",
"dependencies": {
"eslint-plugin-import": "^2.17.3"
}
}

View File

@ -1,12 +1,12 @@
import { Injectable } from '@angular/core'
import { TerminalColorSchemeProvider, ITerminalColorScheme } from 'terminus-terminal'
import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal'
const schemeContents = require.context('../schemes/', true, /.*/)
@Injectable()
export class ColorSchemes extends TerminalColorSchemeProvider {
async getSchemes (): Promise<ITerminalColorScheme[]> {
const schemes: ITerminalColorScheme[] = []
async getSchemes (): Promise<TerminalColorScheme[]> {
const schemes: TerminalColorScheme[] = []
schemeContents.keys().forEach(schemeFile => {
const lines = (schemeContents(schemeFile).default as string).split('\n')
@ -16,7 +16,7 @@ export class ColorSchemes extends TerminalColorSchemeProvider {
lines
.filter(x => x.startsWith('#define'))
.map(x => x.split(' ').map(v => v.trim()))
.forEach(([ignore, variableName, variableValue]) => {
.forEach(([_, variableName, variableValue]) => {
variables[variableName] = variableValue
})

View File

@ -8,4 +8,4 @@ import { ColorSchemes } from './colorSchemes'
{ provide: TerminalColorSchemeProvider, useClass: ColorSchemes, multi: true },
],
})
export default class PopularThemesModule { }
export default class PopularThemesModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class

View File

@ -1,4 +1,4 @@
export interface IHotkeyDescription {
export interface HotkeyDescription {
id: string
name: string
}
@ -8,7 +8,7 @@ export interface IHotkeyDescription {
* must also provide the `hotkeys.foo` config options with the default values
*/
export abstract class HotkeyProvider {
hotkeys: IHotkeyDescription[] = []
hotkeys: HotkeyDescription[] = []
abstract provide (): Promise<IHotkeyDescription[]>
abstract provide (): Promise<HotkeyDescription[]>
}

View File

@ -1,9 +1,9 @@
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider'
export { ConfigProvider } from './configProvider'
export { HotkeyProvider, IHotkeyDescription } from './hotkeyProvider'
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
export { Theme } from './theme'
export { TabContextMenuItemProvider } from './tabContextMenuProvider'

View File

@ -3,7 +3,7 @@ import { SafeHtml } from '@angular/platform-browser'
/**
* See [[ToolbarButtonProvider]]
*/
export interface IToolbarButton {
export interface ToolbarButton {
/**
* Raw SVG icon code
*/
@ -25,15 +25,15 @@ export interface IToolbarButton {
click?: () => void
submenu?: () => Promise<IToolbarButton[]>
submenu?: () => Promise<ToolbarButton[]>
/** @hidden */
submenuItems?: IToolbarButton[]
submenuItems?: ToolbarButton[]
}
/**
* Extend to add buttons to the toolbar
*/
export abstract class ToolbarButtonProvider {
abstract provide (): IToolbarButton[]
abstract provide (): ToolbarButton[]
}

View File

@ -15,7 +15,7 @@ import { TouchbarService } from '../services/touchbar.service'
import { BaseTabComponent } from './baseTab.component'
import { SafeModeModalComponent } from './safeModeModal.component'
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
import { AppService, ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */
@Component({
@ -26,36 +26,36 @@ import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
trigger('animateTab', [
state('in', style({
'flex-basis': '200px',
'width': '200px',
width: '200px',
})),
transition(':enter', [
style({
'flex-basis': '1px',
'width': '1px',
width: '1px',
}),
animate('250ms ease-in-out', style({
'flex-basis': '200px',
'width': '200px',
}))
width: '200px',
})),
]),
transition(':leave', [
style({
'flex-basis': '200px',
'width': '200px',
width: '200px',
}),
animate('250ms ease-in-out', style({
'flex-basis': '1px',
'width': '1px',
}))
])
])
]
width: '1px',
})),
]),
]),
],
})
export class AppRootComponent {
Platform = Platform
@Input() ready = false
@Input() leftToolbarButtons: IToolbarButton[]
@Input() rightToolbarButtons: IToolbarButton[]
@Input() leftToolbarButtons: ToolbarButton[]
@Input() rightToolbarButtons: ToolbarButton[]
@HostBinding('class.platform-win32') platformClassWindows = process.platform === 'win32'
@HostBinding('class.platform-darwin') platformClassMacOS = process.platform === 'darwin'
@HostBinding('class.platform-linux') platformClassLinux = process.platform === 'linux'
@ -89,7 +89,7 @@ export class AppRootComponent {
this.updateIcon = domSanitizer.bypassSecurityTrustHtml(require('../icons/gift.svg')),
this.hotkeys.matchedHotkey.subscribe((hotkey) => {
this.hotkeys.matchedHotkey.subscribe((hotkey: string) => {
if (hotkey.startsWith('tab-')) {
const index = parseInt(hotkey.split('-')[1])
if (index <= this.app.tabs.length) {
@ -233,20 +233,20 @@ export class AppRootComponent {
})
}
async generateButtonSubmenu (button: IToolbarButton) {
async generateButtonSubmenu (button: ToolbarButton) {
if (button.submenu) {
button.submenuItems = await button.submenu()
}
}
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
let buttons: IToolbarButton[] = []
private getToolbarButtons (aboveZero: boolean): ToolbarButton[] {
let buttons: ToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
buttons = buttons.concat(provider.provide())
})
return buttons
.filter((button) => (button.weight > 0) === aboveZero)
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
.filter(button => button.weight > 0 === aboveZero)
.sort((a: ToolbarButton, b: ToolbarButton) => (a.weight || 0) - (b.weight || 0))
}
private updateVibrancy () {

View File

@ -8,7 +8,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
styles: [require('./checkbox.component.scss')],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true },
]
],
})
export class CheckboxComponent implements ControlValueAccessor {
@HostBinding('class.active') @Input() model: boolean

View File

@ -6,8 +6,8 @@ import { TabsService } from '../services/tabs.service'
import { HotkeysService } from '../services/hotkeys.service'
import { TabRecoveryService } from '../services/tabRecovery.service'
export declare type SplitOrientation = 'v' | 'h'
export declare type SplitDirection = 'r' | 't' | 'b' | 'l'
export type SplitOrientation = 'v' | 'h' // eslint-disable-line @typescript-eslint/no-type-alias
export type SplitDirection = 'r' | 't' | 'b' | 'l' // eslint-disable-line @typescript-eslint/no-type-alias
/**
* Describes a horizontal or vertical split row or column
@ -198,33 +198,33 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
return
}
switch (hotkey) {
case 'split-right':
this.splitTab(this.focusedTab, 'r')
break
case 'split-bottom':
this.splitTab(this.focusedTab, 'b')
break
case 'split-top':
this.splitTab(this.focusedTab, 't')
break
case 'split-left':
this.splitTab(this.focusedTab, 'l')
break
case 'pane-nav-left':
this.navigate('l')
break
case 'pane-nav-right':
this.navigate('r')
break
case 'pane-nav-up':
this.navigate('t')
break
case 'pane-nav-down':
this.navigate('b')
break
case 'close-pane':
this.removeTab(this.focusedTab)
break
case 'split-right':
this.splitTab(this.focusedTab, 'r')
break
case 'split-bottom':
this.splitTab(this.focusedTab, 'b')
break
case 'split-top':
this.splitTab(this.focusedTab, 't')
break
case 'split-left':
this.splitTab(this.focusedTab, 'l')
break
case 'pane-nav-left':
this.navigate('l')
break
case 'pane-nav-right':
this.navigate('r')
break
case 'pane-nav-up':
this.navigate('t')
break
case 'pane-nav-down':
this.navigate('b')
break
case 'close-pane':
this.removeTab(this.focusedTab)
break
}
})
}
@ -291,11 +291,11 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
let insertIndex = target.children.indexOf(relative)
if (
(target.orientation === 'v' && ['l', 'r'].includes(side)) ||
(target.orientation === 'h' && ['t', 'b'].includes(side))
target.orientation === 'v' && ['l', 'r'].includes(side) ||
target.orientation === 'h' && ['t', 'b'].includes(side)
) {
const newContainer = new SplitContainer()
newContainer.orientation = (target.orientation === 'v') ? 'h' : 'v'
newContainer.orientation = target.orientation === 'v' ? 'h' : 'v'
newContainer.children = [relative]
newContainer.ratios = [1]
target.children[insertIndex] = newContainer
@ -306,7 +306,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
if (insertIndex === -1) {
insertIndex = 0
} else {
insertIndex += (side === 'l' || side === 't') ? 0 : 1
insertIndex += side === 'l' || side === 't' ? 0 : 1
}
for (let i = 0; i < target.children.length; i++) {
@ -419,7 +419,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
}
private attachTabView (tab: BaseTabComponent) {
const ref = this.viewContainer.insert(tab.hostView) as EmbeddedViewRef<any>
const ref = this.viewContainer.insert(tab.hostView) as EmbeddedViewRef<any> // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
this.viewRefs.set(tab, ref)
ref.rootNodes[0].addEventListener('click', () => this.focus(tab))
@ -448,7 +448,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
}
private layoutInternal (root: SplitContainer, x: number, y: number, w: number, h: number) {
const size = (root.orientation === 'v') ? h : w
const size = root.orientation === 'v' ? h : w
const sizes = root.ratios.map(x => x * size)
root.x = x
@ -458,10 +458,10 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
let offset = 0
root.children.forEach((child, i) => {
const childX = (root.orientation === 'v') ? x : (x + offset)
const childY = (root.orientation === 'v') ? (y + offset) : y
const childW = (root.orientation === 'v') ? w : sizes[i]
const childH = (root.orientation === 'v') ? sizes[i] : h
const childX = root.orientation === 'v' ? x : x + offset
const childY = root.orientation === 'v' ? y + offset : y
const childW = root.orientation === 'v' ? w : sizes[i]
const childH = root.orientation === 'v' ? sizes[i] : h
if (child instanceof SplitContainer) {
this.layoutInternal(child, childX, childY, childW, childH)
} else {
@ -472,7 +472,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
element.style.width = `${childW}%`
element.style.height = `${childH}%`
element.style.opacity = (child === this.focusedTab) ? 1 : 0.75
element.style.opacity = child === this.focusedTab ? 1 : 0.75
}
offset += sizes[i]

View File

@ -23,13 +23,13 @@ export class SplitTabSpannerComponent {
constructor (private element: ElementRef) { }
ngAfterViewInit () {
this.element.nativeElement.addEventListener('mousedown', e => {
this.element.nativeElement.addEventListener('mousedown', (e: MouseEvent) => {
this.isActive = true
const start = this.isVertical ? e.pageY : e.pageX
let current = start
const oldPosition = this.isVertical ? this.element.nativeElement.offsetTop : this.element.nativeElement.offsetLeft
const oldPosition: number = this.isVertical ? this.element.nativeElement.offsetTop : this.element.nativeElement.offsetLeft
const dragHandler = e => {
const dragHandler = (e: MouseEvent) => {
current = this.isVertical ? e.pageY : e.pageX
const newPosition = oldPosition + (current - start)
if (this.isVertical) {

View File

@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core'
import { ConfigService } from '../services/config.service'
import { HomeBaseService } from '../services/homeBase.service'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
import { ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */
@Component({
@ -19,11 +19,11 @@ export class StartPageComponent {
) {
}
getButtons (): IToolbarButton[] {
getButtons (): ToolbarButton[] {
return this.config.enabledServices(this.toolbarButtonProviders)
.map(provider => provider.provide())
.reduce((a, b) => a.concat(b))
.filter(x => !!x.click)
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
.sort((a: ToolbarButton, b: ToolbarButton) => (a.weight || 0) - (b.weight || 0))
}
}

View File

@ -85,7 +85,6 @@ export class TabHeaderComponent {
contextMenu.popup({
x: $event.pageX,
y: $event.pageY,
async: true,
})
}
}

View File

@ -6,4 +6,4 @@ import { Component } from '@angular/core'
template: require('./titleBar.component.pug'),
styles: [require('./titleBar.component.scss')],
})
export class TitleBarComponent { }
export class TitleBarComponent { } // eslint-disable-line @typescript-eslint/no-extraneous-class

View File

@ -17,7 +17,7 @@ import { CheckboxComponent } from './checkbox.component'
styles: [require('./toggle.component.scss')],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true },
]
],
})
export class ToggleComponent extends CheckboxComponent {
}

View File

@ -2,7 +2,7 @@ import { Directive, AfterViewInit, ElementRef } from '@angular/core'
/** @hidden */
@Directive({
selector: '[autofocus]'
selector: '[autofocus]',
})
export class AutofocusDirective implements AfterViewInit {
constructor (private el: ElementRef) { }

View File

@ -1,10 +1,10 @@
import { Injectable } from '@angular/core'
import { IHotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
/** @hidden */
@Injectable()
export class AppHotkeyProvider extends HotkeyProvider {
hotkeys: IHotkeyDescription[] = [
hotkeys: HotkeyDescription[] = [
{
id: 'new-window',
name: 'New window',
@ -115,7 +115,7 @@ export class AppHotkeyProvider extends HotkeyProvider {
},
]
async provide (): Promise<IHotkeyDescription[]> {
async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys
}
}

View File

@ -39,6 +39,12 @@ import { TaskCompletionContextMenu, CommonOptionsContextMenu, CloseContextMenu }
import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css'
// PerfectScrollbar fix
import { fromEvent } from 'rxjs/internal/observable/fromEvent'
import { merge } from 'rxjs/internal/observable/merge'
require('rxjs').fromEvent = fromEvent
require('rxjs').merge = merge
const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
@ -49,7 +55,7 @@ const PROVIDERS = [
{ provide: TabContextMenuItemProvider, useClass: CloseContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } }
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
]
/** @hidden */
@ -88,9 +94,9 @@ const PROVIDERS = [
CheckboxComponent,
ToggleComponent,
AutofocusDirective,
]
],
})
export default class AppModule {
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
constructor (app: AppService, config: ConfigService) {
app.ready$.subscribe(() => {
if (config.store.enableWelcomeTab) {
@ -107,11 +113,9 @@ export default class AppModule {
}
}
// PerfectScrollbar fix
import { fromEvent } from 'rxjs/internal/observable/fromEvent'
import { merge } from 'rxjs/internal/observable/merge'
require('rxjs').fromEvent = fromEvent
require('rxjs').merge = merge
export { AppRootComponent as bootstrap }
export * from './api'
// Deprecations
export { ToolbarButton as IToolbarButton } from './api'
export { HotkeyDescription as IHotkeyDescription } from './api'

View File

@ -21,7 +21,7 @@ class CompletionObserver {
}
async tick () {
if (!(await this.tab.getCurrentProcess())) {
if (!await this.tab.getCurrentProcess()) {
this.done.next(null)
this.stop()
}
@ -81,7 +81,7 @@ export class AppService {
})
}
private addTabRaw (tab: BaseTabComponent) {
addTabRaw (tab: BaseTabComponent) {
this.tabs.push(tab)
this.selectTab(tab)
this.tabsChanged.next()

View File

@ -14,7 +14,7 @@ function isStructuralMember (v) {
Object.keys(v).length > 0 && !v.__nonStructural
}
function isNonStructuralObjectMember (v) {
function isNonStructuralObjectMember (v): boolean {
return v instanceof Object && !(v instanceof Array) && v.__nonStructural
}
@ -46,13 +46,13 @@ export class ConfigProxy {
get: () => this.getValue(key),
set: (value) => {
this.setValue(key, value)
}
},
}
)
}
}
this.getValue = (key: string) => {
this.getValue = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
if (real[key] !== undefined) {
return real[key]
} else {
@ -66,13 +66,13 @@ export class ConfigProxy {
}
}
this.setValue = (key: string, value: any) => {
this.setValue = (key: string, value: any) => { // eslint-disable-line @typescript-eslint/unbound-method
real[key] = value
}
}
getValue (key: string): any { } // tslint:disable-line
setValue (key: string, value: any) { } // tslint:disable-line
getValue (_key: string): any { }
setValue (_key: string, _value: any) { }
}
@Injectable({ providedIn: 'root' })
@ -160,10 +160,6 @@ export class ConfigService {
this.emitChange()
}
private emitChange (): void {
this.changed.next()
}
requestRestart (): void {
this.restartRequested = true
}
@ -179,7 +175,7 @@ export class ConfigService {
this.servicesCache = {}
const ngModule = window['rootModule'].ngInjectorDef
for (const imp of ngModule.imports) {
const module = (imp['ngModule'] || imp)
const module = imp['ngModule'] || imp
if (module.ngInjectorDef && module.ngInjectorDef.providers) {
this.servicesCache[module['pluginName']] = module.ngInjectorDef.providers.map(provider => {
return provider['useClass'] || provider
@ -196,4 +192,8 @@ export class ConfigService {
return true
})
}
private emitChange (): void {
this.changed.next()
}
}

View File

@ -3,11 +3,6 @@ import { ConfigService } from '../services/config.service'
import { ElectronService } from '../services/electron.service'
import { HostAppService, Bounds } from '../services/hostApp.service'
export interface IScreen {
id: string
name: string
}
@Injectable({ providedIn: 'root' })
export class DockingService {
/** @hidden */
@ -29,7 +24,7 @@ export class DockingService {
}
let display = this.electron.screen.getAllDisplays()
.filter((x) => x.id === this.config.store.appearance.dockScreen)[0]
.filter(x => x.id === this.config.store.appearance.dockScreen)[0]
if (!display) {
display = this.getCurrentScreen()
}
@ -71,10 +66,10 @@ export class DockingService {
return this.electron.screen.getAllDisplays().map((display, index) => {
return {
id: display.id,
name: {
0: 'Primary display',
1: 'Secondary display',
}[index] || `Display ${index + 1}`
name: [
'Primary display',
'Secondary display',
][index] || `Display ${index + 1}`,
}
})
}

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { TouchBar, BrowserWindow, Menu, MenuItem } from 'electron'
import { TouchBar, BrowserWindow, Menu, MenuItem, NativeImage } from 'electron'
export interface MessageBoxResponse {
response: number
@ -8,16 +8,16 @@ export interface MessageBoxResponse {
@Injectable({ providedIn: 'root' })
export class ElectronService {
app: any
ipcRenderer: any
shell: any
dialog: any
clipboard: any
globalShortcut: any
nativeImage: any
screen: any
remote: any
autoUpdater: any
app: Electron.App
ipcRenderer: Electron.IpcRenderer
shell: Electron.Shell
dialog: Electron.Dialog
clipboard: Electron.Clipboard
globalShortcut: Electron.GlobalShortcut
nativeImage: typeof NativeImage
screen: Electron.Screen
remote: Electron.Remote
autoUpdater: Electron.AutoUpdater
TouchBar: typeof TouchBar
BrowserWindow: typeof BrowserWindow
Menu: typeof Menu
@ -52,7 +52,7 @@ export class ElectronService {
}
}
showMessageBox (
async showMessageBox (
browserWindow: Electron.BrowserWindow,
options: Electron.MessageBoxOptions
): Promise<MessageBoxResponse> {

View File

@ -2,8 +2,8 @@ import * as os from 'os'
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { ConfigService } from './config.service'
import mixpanel = require('mixpanel')
import uuidv4 = require('uuid/v4')
import * as mixpanel from 'mixpanel'
import * as uuidv4 from 'uuid/v4'
@Injectable({ providedIn: 'root' })
export class HomeBaseService {
@ -53,7 +53,7 @@ export class HomeBaseService {
getAnalyticsProperties () {
return {
distinct_id: window.localStorage.analyticsUserID,
distinct_id: window.localStorage.analyticsUserID, // eslint-disable-line @typescript-eslint/camelcase
platform: process.platform,
os: os.release(),
version: this.appVersion,

View File

@ -1,5 +1,5 @@
import * as path from 'path'
import shellEscape = require('shell-escape')
import * as shellEscape from 'shell-escape'
import { Observable, Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { ElectronService } from './electron.service'
@ -97,7 +97,7 @@ export class HostAppService {
this.platform = {
win32: Platform.Windows,
darwin: Platform.macOS,
linux: Platform.Linux
linux: Platform.Linux,
}[process.platform]
this.windowId = parseInt(location.search.substring(1))

View File

@ -1,5 +1,5 @@
import { Injectable, Inject, NgZone, EventEmitter } from '@angular/core'
import { IHotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
import { stringifyKeySequence } from './hotkeys.util'
import { ConfigService } from '../services/config.service'
import { ElectronService } from '../services/electron.service'
@ -24,7 +24,7 @@ export class HotkeysService {
globalHotkey = new EventEmitter()
private currentKeystrokes: EventBufferEntry[] = []
private disabledLevel = 0
private hotkeyDescriptions: IHotkeyDescription[] = []
private hotkeyDescriptions: HotkeyDescription[] = []
/** @hidden */
constructor (
@ -94,56 +94,7 @@ export class HotkeysService {
return stringifyKeySequence(this.currentKeystrokes.map(x => x.event))
}
private registerGlobalHotkey () {
this.electron.globalShortcut.unregisterAll()
let value = this.config.store.hotkeys['toggle-window'] || []
if (typeof value === 'string') {
value = [value]
}
value.forEach((item: string | string[]) => {
item = (typeof item === 'string') ? [item] : item
try {
let electronKeySpec = item[0]
electronKeySpec = electronKeySpec.replace('⌘', 'Command')
electronKeySpec = electronKeySpec.replace('⌥', 'Alt')
electronKeySpec = electronKeySpec.replace(/-/g, '+')
this.electron.globalShortcut.register(electronKeySpec, () => {
this.globalHotkey.emit()
})
} catch (err) {
console.error('Could not register the global hotkey:', err)
}
})
}
private getHotkeysConfig () {
return this.getHotkeysConfigRecursive(this.config.store.hotkeys)
}
private getHotkeysConfigRecursive (branch: any) {
const keys = {}
for (const key in branch) {
let value = branch[key]
if (value instanceof Object && !(value instanceof Array)) {
const subkeys = this.getHotkeysConfigRecursive(value)
for (const subkey in subkeys) {
keys[key + '.' + subkey] = subkeys[subkey]
}
} else {
if (typeof value === 'string') {
value = [value]
}
if (value) {
value = value.map((item: string | string[]) => (typeof item === 'string') ? [item] : item)
keys[key] = value
}
}
}
return keys
}
private getCurrentFullyMatchedHotkey (): string {
getCurrentFullyMatchedHotkey (): string {
const currentStrokes = this.getCurrentKeystrokes()
const config = this.getHotkeysConfig()
for (const id in config) {
@ -178,7 +129,7 @@ export class HotkeysService {
result.push({
matchedLength: matchLength,
id,
strokes: sequence
strokes: sequence,
})
break
}
@ -188,7 +139,7 @@ export class HotkeysService {
return result
}
getHotkeyDescription (id: string): IHotkeyDescription {
getHotkeyDescription (id: string): HotkeyDescription {
return this.hotkeyDescriptions.filter((x) => x.id === id)[0]
}
@ -204,7 +155,7 @@ export class HotkeysService {
return this.disabledLevel === 0
}
async getHotkeyDescriptions (): Promise<IHotkeyDescription[]> {
async getHotkeyDescriptions (): Promise<HotkeyDescription[]> {
return (
await Promise.all(
this.config.enabledServices(this.hotkeyProviders)
@ -212,4 +163,53 @@ export class HotkeysService {
)
).reduce((a, b) => a.concat(b))
}
private registerGlobalHotkey () {
this.electron.globalShortcut.unregisterAll()
let value = this.config.store.hotkeys['toggle-window'] || []
if (typeof value === 'string') {
value = [value]
}
value.forEach((item: string | string[]) => {
item = typeof item === 'string' ? [item] : item
try {
let electronKeySpec = item[0]
electronKeySpec = electronKeySpec.replace('⌘', 'Command')
electronKeySpec = electronKeySpec.replace('⌥', 'Alt')
electronKeySpec = electronKeySpec.replace(/-/g, '+')
this.electron.globalShortcut.register(electronKeySpec, () => {
this.globalHotkey.emit()
})
} catch (err) {
console.error('Could not register the global hotkey:', err)
}
})
}
private getHotkeysConfig () {
return this.getHotkeysConfigRecursive(this.config.store.hotkeys)
}
private getHotkeysConfigRecursive (branch: any) {
const keys = {}
for (const key in branch) {
let value = branch[key]
if (value instanceof Object && !(value instanceof Array)) {
const subkeys = this.getHotkeysConfigRecursive(value)
for (const subkey in subkeys) {
keys[key + '.' + subkey] = subkeys[subkey]
}
} else {
if (typeof value === 'string') {
value = [value]
}
if (value) {
value = value.map((item: string | string[]) => typeof item === 'string' ? [item] : item)
keys[key] = value
}
}
}
return keys
}
}

View File

@ -20,9 +20,9 @@ const initializeWinston = (electron: ElectronService) => {
handleExceptions: false,
maxsize: 5242880,
maxFiles: 5,
})
}),
],
exitOnError: false
exitOnError: false,
})
}
@ -32,18 +32,32 @@ export class Logger {
private name: string,
) {}
debug (...args: any[]) {
this.doLog('debug', ...args)
}
info (...args: any[]) {
this.doLog('info', ...args)
}
warn (...args: any[]) {
this.doLog('warn', ...args)
}
error (...args: any[]) {
this.doLog('error', ...args)
}
log (...args: any[]) {
this.doLog('log', ...args)
}
private doLog (level: string, ...args: any[]) {
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
if (this.winstonLogger) {
this.winstonLogger[level](...args)
}
}
debug (...args: any[]) { this.doLog('debug', ...args) }
info (...args: any[]) { this.doLog('info', ...args) }
warn (...args: any[]) { this.doLog('warn', ...args) }
error (...args: any[]) { this.doLog('error', ...args) }
log (...args: any[]) { this.doLog('log', ...args) }
}
@Injectable({ providedIn: 'root' })

View File

@ -5,9 +5,11 @@ import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // tslint:disable-line
} catch (_) { } // tslint:disable-line
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
} catch (_) { }
@Injectable({ providedIn: 'root' })
export class ShellIntegrationService {
@ -17,11 +19,11 @@ export class ShellIntegrationService {
private registryKeys = [
{
path: 'Software\\Classes\\Directory\\Background\\shell\\Open Terminus here',
command: 'open "%V"'
command: 'open "%V"',
},
{
path: 'Software\\Classes\\*\\shell\\Paste path into Terminus',
command: 'paste "%V"'
command: 'paste "%V"',
},
]
constructor (
@ -40,15 +42,6 @@ export class ShellIntegrationService {
this.updatePaths()
}
private async updatePaths (): Promise<void> {
// Update paths in case of an update
if (this.hostApp.platform === Platform.Windows) {
if (await this.isInstalled()) {
await this.install()
}
}
}
async isInstalled (): Promise<boolean> {
if (this.hostApp.platform === Platform.macOS) {
return fs.exists(path.join(this.automatorWorkflowsDestination, this.automatorWorkflows[0]))
@ -59,7 +52,7 @@ export class ShellIntegrationService {
}
async install () {
const exe = 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) {
for (const wf of this.automatorWorkflows) {
await exec(`cp -r "${this.automatorWorkflowsLocation}/${wf}" "${this.automatorWorkflowsDestination}"`)
@ -85,4 +78,13 @@ export class ShellIntegrationService {
}
}
}
private async updatePaths (): Promise<void> {
// Update paths in case of an update
if (this.hostApp.platform === Platform.Windows) {
if (await this.isInstalled()) {
await this.install()
}
}
}
}

View File

@ -54,5 +54,4 @@ export class TabRecoveryService {
}
return []
}
}

View File

@ -2,7 +2,8 @@ import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
import { TabRecoveryService } from './tabRecovery.service'
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
// eslint-disable-next-line @typescript-eslint/no-type-alias
export type TabComponentType = new (...args: any[]) => BaseTabComponent
@Injectable({ providedIn: 'root' })
export class TabsService {

View File

@ -4,7 +4,7 @@ import { AppService } from './app.service'
import { ConfigService } from './config.service'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
import { IToolbarButton, ToolbarButtonProvider } from '../api'
import { ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */
@Injectable({ providedIn: 'root' })
@ -61,7 +61,7 @@ export class TouchbarService {
return
}
let buttons: IToolbarButton[] = []
let buttons: ToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
buttons = buttons.concat(provider.provide())
})
@ -76,7 +76,7 @@ export class TouchbarService {
selectedIndex: this.app.tabs.indexOf(this.app.activeTab),
change: (selectedIndex) => this.zone.run(() => {
this.app.selectTab(this.app.tabs[selectedIndex])
})
}),
})
this.buttonsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
@ -84,7 +84,7 @@ export class TouchbarService {
mode: 'buttons',
change: (selectedIndex) => this.zone.run(() => {
buttons[selectedIndex].click()
})
}),
})
const touchBar = new this.electron.TouchBar({
@ -93,12 +93,12 @@ export class TouchbarService {
new this.electron.TouchBar.TouchBarSpacer({ size: 'flexible' }),
new this.electron.TouchBar.TouchBarSpacer({ size: 'small' }),
this.buttonsSegmentedControl,
]
],
})
this.hostApp.setTouchBar(touchBar)
}
private getButton (button: IToolbarButton): Electron.SegmentedControlSegment {
private getButton (button: ToolbarButton): Electron.SegmentedControlSegment {
return {
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
icon: button.touchBarNSImage ? this.getCachedNSImage(button.touchBarNSImage) : null,

View File

@ -22,7 +22,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
label: 'Close',
click: () => this.zone.run(() => {
this.app.closeTab(tab, true)
})
}),
},
{
label: 'Close other tabs',
@ -30,7 +30,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
for (const t of this.app.tabs.filter(x => x !== tab)) {
this.app.closeTab(t, true)
}
})
}),
},
{
label: 'Close tabs to the right',
@ -38,7 +38,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
this.app.closeTab(t, true)
}
})
}),
},
{
label: 'Close tabs to the left',
@ -46,7 +46,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
this.app.closeTab(t, true)
}
})
}),
},
]
}
@ -78,11 +78,11 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
return [
{
label: 'Rename',
click: () => this.zone.run(() => tabHeader.showRenameTabModal())
click: () => this.zone.run(() => tabHeader.showRenameTabModal()),
},
{
label: 'Duplicate',
click: () => this.zone.run(() => this.app.duplicateTab(tab))
click: () => this.zone.run(() => this.app.duplicateTab(tab)),
},
{
label: 'Color',
@ -95,7 +95,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
tab.color = color.value
}),
})) as Electron.MenuItemConstructorOptions[],
}
},
]
}
}
@ -138,7 +138,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
} else {
this.app.stopObservingTabCompletion(tab)
}
})
}),
},
]
}

View File

@ -4,7 +4,7 @@ import * as semver from 'semver'
import { Component, Input } from '@angular/core'
import { ConfigService, ElectronService } from 'terminus-core'
import { IPluginInfo, PluginManagerService } from '../services/pluginManager.service'
import { PluginInfo, PluginManagerService } from '../services/pluginManager.service'
enum BusyState { Installing, Uninstalling }
@ -15,10 +15,10 @@ enum BusyState { Installing, Uninstalling }
})
export class PluginsSettingsTabComponent {
BusyState = BusyState
@Input() availablePlugins$: Observable<IPluginInfo[]>
@Input() availablePlugins$: Observable<PluginInfo[]>
@Input() availablePluginsQuery$ = new BehaviorSubject<string>('')
@Input() availablePluginsReady = false
@Input() knownUpgrades: {[id: string]: IPluginInfo} = {}
@Input() knownUpgrades: {[id: string]: PluginInfo} = {}
@Input() busy: {[id: string]: BusyState} = {}
@Input() erroredPlugin: string
@Input() errorMessage: string
@ -58,11 +58,11 @@ export class PluginsSettingsTabComponent {
this.availablePluginsQuery$.next(query)
}
isAlreadyInstalled (plugin: IPluginInfo): boolean {
isAlreadyInstalled (plugin: PluginInfo): boolean {
return this.pluginManager.installedPlugins.some(x => x.name === plugin.name)
}
async installPlugin (plugin: IPluginInfo): Promise<void> {
async installPlugin (plugin: PluginInfo): Promise<void> {
this.busy[plugin.name] = BusyState.Installing
try {
await this.pluginManager.installPlugin(plugin)
@ -76,7 +76,7 @@ export class PluginsSettingsTabComponent {
}
}
async uninstallPlugin (plugin: IPluginInfo): Promise<void> {
async uninstallPlugin (plugin: PluginInfo): Promise<void> {
this.busy[plugin.name] = BusyState.Uninstalling
try {
await this.pluginManager.uninstallPlugin(plugin)
@ -90,21 +90,21 @@ export class PluginsSettingsTabComponent {
}
}
async upgradePlugin (plugin: IPluginInfo): Promise<void> {
async upgradePlugin (plugin: PluginInfo): Promise<void> {
return this.installPlugin(this.knownUpgrades[plugin.name])
}
showPluginInfo (plugin: IPluginInfo) {
showPluginInfo (plugin: PluginInfo) {
this.electron.shell.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
}
enablePlugin (plugin: IPluginInfo) {
enablePlugin (plugin: PluginInfo) {
this.config.store.pluginBlacklist = this.config.store.pluginBlacklist.filter(x => x !== plugin.name)
this.config.save()
this.config.requestRestart()
}
disablePlugin (plugin: IPluginInfo) {
disablePlugin (plugin: PluginInfo) {
this.config.store.pluginBlacklist = [...this.config.store.pluginBlacklist, plugin.name]
this.config.save()
this.config.requestRestart()

View File

@ -27,6 +27,6 @@ import { PluginsSettingsTabProvider } from './settings'
PluginsSettingsTabComponent,
],
})
export default class PluginManagerModule { }
export default class PluginManagerModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
export { PluginManagerService }

View File

@ -8,7 +8,7 @@ const NAME_PREFIX = 'terminus-'
const KEYWORD = 'terminus-plugin'
const OFFICIAL_NPM_ACCOUNT = 'eugenepankov'
export interface IPluginInfo {
export interface PluginInfo {
name: string
description: string
packageName: string
@ -25,7 +25,7 @@ export class PluginManagerService {
logger: Logger
builtinPluginsPath: string = (window as any).builtinPluginsPath
userPluginsPath: string = (window as any).userPluginsPath
installedPlugins: IPluginInfo[] = (window as any).installedPlugins
installedPlugins: PluginInfo[] = (window as any).installedPlugins
private npmReady: Promise<void>
private npm: any
@ -57,12 +57,12 @@ export class PluginManagerService {
return this.npm
}
listAvailable (query?: string): Observable<IPluginInfo[]> {
listAvailable (query?: string): Observable<PluginInfo[]> {
return from(
axios.get(`https://www.npmjs.com/search?q=keywords%3A${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`, {
headers: {
'x-spiferack': '1',
}
},
})
).pipe(
map(response => response.data.objects.map(item => ({
@ -78,7 +78,7 @@ export class PluginManagerService {
)
}
async installPlugin (plugin: IPluginInfo) {
async installPlugin (plugin: PluginInfo) {
(await this.getNPM()).commands.install([`${plugin.packageName}@${plugin.version}`], err => {
if (err) {
this.logger.error(err)
@ -88,7 +88,7 @@ export class PluginManagerService {
})
}
async uninstallPlugin (plugin: IPluginInfo) {
async uninstallPlugin (plugin: PluginInfo) {
(await this.getNPM()).commands.remove([plugin.packageName], err => {
if (err) {
this.logger.error(err)

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { ToolbarButtonProvider, IToolbarButton, AppService, HostAppService, HotkeysService } from 'terminus-core'
import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService } from 'terminus-core'
import { SettingsTabComponent } from './components/settingsTab.component'
@ -23,7 +23,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
})
}
provide (): IToolbarButton[] {
provide (): ToolbarButton[] {
return [{
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/cog.svg')),
title: 'Settings',

View File

@ -21,7 +21,7 @@ const INPUT_TIMEOUT = 1000
animate('250ms ease-out', style({
transform: 'translateX(0)',
opacity: '1',
}))
})),
]),
transition(':leave', [
style({
@ -31,10 +31,10 @@ const INPUT_TIMEOUT = 1000
animate('250ms ease-in', style({
transform: 'translateX(25px)',
opacity: '0',
}))
])
])
]
})),
]),
]),
],
})
export class HotkeyInputModalComponent {
@Input() value: string[] = []

View File

@ -24,7 +24,7 @@ export class MultiHotkeyInputComponent {
if (typeof this.model === 'string') {
this.model = [this.model]
}
this.model = this.model.map(item => (typeof item === 'string') ? [item] : item)
this.model = this.model.map(item => typeof item === 'string' ? [item] : item)
}
editItem (item) {

View File

@ -6,14 +6,14 @@ import {
ElectronService,
DockingService,
ConfigService,
IHotkeyDescription,
HotkeyDescription,
HotkeysService,
BaseTabComponent,
Theme,
HostAppService,
Platform,
HomeBaseService,
ShellIntegrationService
ShellIntegrationService,
} from 'terminus-core'
import { SettingsTabProvider } from '../api'
@ -30,7 +30,7 @@ import { SettingsTabProvider } from '../api'
export class SettingsTabComponent extends BaseTabComponent {
@Input() activeTab: string
hotkeyFilter = ''
hotkeyDescriptions: IHotkeyDescription[]
hotkeyDescriptions: HotkeyDescription[]
screens: any[]
Platform = Platform
configDefaults: any

View File

@ -7,17 +7,17 @@ export class SettingsConfigProvider extends ConfigProvider {
[Platform.macOS]: {
hotkeys: {
settings: ['⌘-,'],
}
},
},
[Platform.Windows]: {
hotkeys: {
settings: ['Ctrl-,']
}
settings: ['Ctrl-,'],
},
},
[Platform.Linux]: {
hotkeys: {
settings: ['Ctrl-,']
}
settings: ['Ctrl-,'],
},
},
}
}

View File

@ -1,17 +1,17 @@
import { Injectable } from '@angular/core'
import { IHotkeyDescription, HotkeyProvider } from 'terminus-core'
import { HotkeyDescription, HotkeyProvider } from 'terminus-core'
/** @hidden */
@Injectable()
export class SettingsHotkeyProvider extends HotkeyProvider {
hotkeys: IHotkeyDescription[] = [
hotkeys: HotkeyDescription[] = [
{
id: 'settings',
name: 'Open Settings',
},
]
async provide (): Promise<IHotkeyDescription[]> {
async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys
}
}

View File

@ -42,7 +42,7 @@ import { SettingsConfigProvider } from './config'
SettingsTabBodyComponent,
],
})
export default class SettingsModule { }
export default class SettingsModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
export * from './api'
export { SettingsTabComponent }

View File

@ -141,7 +141,7 @@ export class SSHSession extends BaseSession {
}
}
export interface ISSHConnectionGroup {
export interface SSHConnectionGroup {
name: string
connections: SSHConnection[]
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { HotkeysService, ToolbarButtonProvider, IToolbarButton } from 'terminus-core'
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
import { SSHModalComponent } from './components/sshModal.component'
/** @hidden */
@ -13,7 +13,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
hotkeys: HotkeysService,
) {
super()
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
hotkeys.matchedHotkey.subscribe(async (hotkey: string) => {
if (hotkey === 'ssh') {
this.activate()
}
@ -24,7 +24,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
this.ngbModal.open(SSHModalComponent)
}
provide (): IToolbarButton[] {
provide (): ToolbarButton[] {
return [{
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/globe.svg')),
weight: 5,
@ -32,7 +32,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
touchBarNSImage: 'NSTouchBarOpenInBrowserTemplate',
click: async () => {
this.activate()
}
},
}]
}
}

View File

@ -27,23 +27,25 @@ export class EditConnectionModalComponent {
this.newScript = { expect: '', send: '' }
for (const k of Object.values(SSHAlgorithmType)) {
this.supportedAlgorithms[k] = ALGORITHMS[{
const supportedAlg = {
[SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
[SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
[SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
[SSHAlgorithmType.HMAC]: 'SUPPORTED_HMAC',
}[k]]
this.defaultAlgorithms[k] = ALGORITHMS[{
}[k]
const defaultAlg = {
[SSHAlgorithmType.KEX]: 'KEX',
[SSHAlgorithmType.HOSTKEY]: 'SERVER_HOST_KEY',
[SSHAlgorithmType.CIPHER]: 'CIPHER',
[SSHAlgorithmType.HMAC]: 'HMAC',
}[k]]
}[k]
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg]
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg]
}
}
async ngOnInit () {
this.hasSavedPassword = !!(await this.passwordStorage.loadPassword(this.connection))
this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.connection)
this.connection.algorithms = this.connection.algorithms || {}
for (const k of Object.values(SSHAlgorithmType)) {
if (!this.connection.algorithms[k]) {
@ -77,8 +79,8 @@ export class EditConnectionModalComponent {
save () {
for (const k of Object.values(SSHAlgorithmType)) {
this.connection.algorithms[k] = Object.entries(this.algorithms[k])
.filter(([k, v]) => !!v)
.map(([k, v]) => k)
.filter(([_k, v]) => !!v)
.map(([k, _v]) => k)
}
this.modalInstance.close(this.connection)
}

View File

@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'
import { ConfigService, AppService } from 'terminus-core'
import { SettingsTabComponent } from 'terminus-settings'
import { SSHService } from '../services/ssh.service'
import { SSHConnection, ISSHConnectionGroup } from '../api'
import { SSHConnection, SSHConnectionGroup } from '../api'
/** @hidden */
@Component({
@ -13,10 +13,10 @@ import { SSHConnection, ISSHConnectionGroup } from '../api'
})
export class SSHModalComponent {
connections: SSHConnection[]
childFolders: ISSHConnectionGroup[]
childFolders: SSHConnectionGroup[]
quickTarget: string
lastConnection: SSHConnection
childGroups: ISSHConnectionGroup[]
childGroups: SSHConnectionGroup[]
groupCollapsed: {[id: string]: boolean} = {}
constructor (
@ -49,7 +49,9 @@ export class SSHModalComponent {
const connection: SSHConnection = {
name: this.quickTarget,
host, user, port
host,
user,
port,
}
window.localStorage.lastConnection = JSON.stringify(connection)
this.connect(connection)

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, ElectronService, HostAppService } from 'terminus-core'
import { SSHConnection, ISSHConnectionGroup } from '../api'
import { SSHConnection, SSHConnectionGroup } from '../api'
import { EditConnectionModalComponent } from './editConnectionModal.component'
import { PromptModalComponent } from './promptModal.component'
@ -11,7 +11,7 @@ import { PromptModalComponent } from './promptModal.component'
})
export class SSHSettingsTabComponent {
connections: SSHConnection[]
childGroups: ISSHConnectionGroup[]
childGroups: SSHConnectionGroup[]
groupCollapsed: {[id: string]: boolean} = {}
constructor (
@ -70,7 +70,7 @@ export class SSHSettingsTabComponent {
}
}
editGroup (group: ISSHConnectionGroup) {
editGroup (group: SSHConnectionGroup) {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'New group name'
modal.componentInstance.value = group.name
@ -86,7 +86,7 @@ export class SSHSettingsTabComponent {
})
}
async deleteGroup (group: ISSHConnectionGroup) {
async deleteGroup (group: SSHConnectionGroup) {
if ((await this.electron.showMessageBox(
this.hostApp.getWindow(),
{

View File

@ -6,10 +6,10 @@ export class SSHConfigProvider extends ConfigProvider {
ssh: {
connections: [],
options: {
}
},
},
hotkeys: {
'ssh': [
ssh: [
'Alt-S',
],
},

View File

@ -47,4 +47,4 @@ import { RecoveryProvider } from './recoveryProvider'
SSHTabComponent,
],
})
export default class SSHModule { }
export default class SSHModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class

View File

@ -9,13 +9,12 @@ import { SSHConnection, SSHSession } from '../api'
import { PromptModalComponent } from '../components/promptModal.component'
import { SSHTabComponent } from '../components/sshTab.component'
import { PasswordStorageService } from './passwordStorage.service'
const { SSH2Stream } = require('ssh2-streams')
import { SSH2Stream } from 'ssh2-streams'
let windowsProcessTree
/* eslint-disable block-scoped-var */
try {
windowsProcessTree = require('windows-process-tree/build/Release/windows_process_tree.node')
} catch (e) {
} // tslint:disable-line
var windowsProcessTree = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires
} catch (_) { }
@Injectable({ providedIn: 'root' })
export class SSHService {
@ -46,10 +45,10 @@ export class SSHService {
let privateKeyPath = session.connection.privateKey
if (!logCallback) {
logCallback = (s) => null
logCallback = () => null
}
const log = s => {
const log = (s: any) => {
logCallback(s)
this.logger.info(s)
}
@ -84,7 +83,7 @@ export class SSHService {
modal.componentInstance.password = true
try {
privateKeyPassphrase = await modal.result
} catch (_err) { } // tslint:disable-line
} catch (e) { }
}
}
}
@ -214,11 +213,11 @@ export class SSHService {
session.shell = shell
shell.on('greeting', greeting => {
log('Shell Greeting: ' + greeting)
log(`Shell Greeting: ${greeting}`)
})
shell.on('banner', banner => {
log('Shell Banner: ' + banner)
log(`Shell Banner: ${banner}`)
})
} catch (error) {
this.toastr.error(error.message)
@ -227,7 +226,8 @@ export class SSHService {
}
}
/* eslint-disable */
const _authPassword = SSH2Stream.prototype.authPassword
SSH2Stream.prototype.authPassword = async function (username, passwordFn) {
SSH2Stream.prototype.authPassword = async function (username, passwordFn: any) {
_authPassword.bind(this)(username, await passwordFn())
}
} as any

View File

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

View File

@ -22,11 +22,11 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
static animations: AnimationTriggerMetadata[] = [trigger('slideInOut', [
transition(':enter', [
style({ transform: 'translateY(-25%)' }),
animate('100ms ease-in-out', style({ transform: 'translateY(0%)' }))
animate('100ms ease-in-out', style({ transform: 'translateY(0%)' })),
]),
transition(':leave', [
animate('100ms ease-in-out', style({ transform: 'translateY(-25%)' }))
])
animate('100ms ease-in-out', style({ transform: 'translateY(-25%)' })),
]),
])]
session: BaseSession
@ -90,53 +90,53 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
return
}
switch (hotkey) {
case 'ctrl-c':
if (this.frontend.getSelection()) {
case 'ctrl-c':
if (this.frontend.getSelection()) {
this.frontend.copySelection()
this.frontend.clearSelection()
this.toastr.info('Copied')
} else {
this.sendInput('\x03')
}
break
case 'copy':
this.frontend.copySelection()
this.frontend.clearSelection()
this.toastr.info('Copied')
} else {
this.sendInput('\x03')
}
break
case 'copy':
this.frontend.copySelection()
this.frontend.clearSelection()
this.toastr.info('Copied')
break
case 'paste':
this.paste()
break
case 'clear':
this.frontend.clear()
break
case 'zoom-in':
this.zoomIn()
break
case 'zoom-out':
this.zoomOut()
break
case 'reset-zoom':
this.resetZoom()
break
case 'previous-word':
this.sendInput('\x1bb')
break
case 'next-word':
this.sendInput('\x1bf')
break
case 'delete-previous-word':
this.sendInput('\x1b\x7f')
break
case 'delete-next-word':
this.sendInput('\x1bd')
break
case 'search':
this.showSearchPanel = true
setImmediate(() => {
this.element.nativeElement.querySelector('.search-input').focus()
})
break
break
case 'paste':
this.paste()
break
case 'clear':
this.frontend.clear()
break
case 'zoom-in':
this.zoomIn()
break
case 'zoom-out':
this.zoomOut()
break
case 'reset-zoom':
this.resetZoom()
break
case 'previous-word':
this.sendInput('\x1bb')
break
case 'next-word':
this.sendInput('\x1bf')
break
case 'delete-previous-word':
this.sendInput('\x1b\x7f')
break
case 'delete-next-word':
this.sendInput('\x1bd')
break
case 'search':
this.showSearchPanel = true
setImmediate(() => {
this.element.nativeElement.querySelector('.search-input').focus()
})
break
}
})
this.bellPlayer = document.createElement('audio')
@ -219,89 +219,6 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
return items
}
protected detachTermContainerHandlers () {
for (const subscription of this.termContainerSubscriptions) {
subscription.unsubscribe()
}
this.termContainerSubscriptions = []
}
protected attachTermContainerHandlers () {
this.detachTermContainerHandlers()
const maybeConfigure = () => {
if (this.hasFocus) {
setTimeout(() => this.configure(), 250)
}
}
this.termContainerSubscriptions = [
this.frontend.title$.subscribe(title => this.zone.run(() => this.setTitle(title))),
this.focused$.subscribe(() => this.frontend.enableResizing = true),
this.blurred$.subscribe(() => this.frontend.enableResizing = false),
this.frontend.mouseEvent$.subscribe(async event => {
if (event.type === 'mousedown') {
if (event.which === 2) {
this.paste()
event.preventDefault()
event.stopPropagation()
return
}
if (event.which === 3) {
if (this.config.store.terminal.rightClick === 'menu') {
this.hostApp.popupContextMenu(await this.buildContextMenu())
} else if (this.config.store.terminal.rightClick === 'paste') {
this.paste()
}
event.preventDefault()
event.stopPropagation()
return
}
}
if (event.type === 'mousewheel') {
let wheelDeltaY = 0
if ('wheelDeltaY' in event) {
wheelDeltaY = (event as MouseWheelEvent)['wheelDeltaY']
} else {
wheelDeltaY = (event as MouseWheelEvent)['deltaY']
}
if (event.ctrlKey || event.metaKey) {
if (wheelDeltaY > 0) {
this.zoomIn()
} else {
this.zoomOut()
}
} else if (event.altKey) {
event.preventDefault()
const delta = Math.round(wheelDeltaY / 50)
this.sendInput(((delta > 0) ? '\u001bOA' : '\u001bOB').repeat(Math.abs(delta)))
}
}
}),
this.frontend.input$.subscribe(data => {
this.sendInput(data)
}),
this.frontend.resize$.subscribe(({ columns, rows }) => {
this.logger.debug(`Resizing to ${columns}x${rows}`)
this.size = { columns, rows }
this.zone.run(() => {
if (this.session && this.session.open) {
this.session.resize(columns, rows)
}
})
}),
this.hostApp.displayMetricsChanged$.subscribe(maybeConfigure),
this.hostApp.windowMoved$.subscribe(maybeConfigure),
]
}
/**
* Feeds input into the active session
*/
@ -330,7 +247,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
}
paste () {
let data = this.electron.clipboard.readText()
let data = this.electron.clipboard.readText() as string
if (this.config.store.terminal.bracketedPaste) {
data = '\x1b[200~' + data + '\x1b[201~'
}
@ -401,6 +318,89 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
}
}
protected detachTermContainerHandlers () {
for (const subscription of this.termContainerSubscriptions) {
subscription.unsubscribe()
}
this.termContainerSubscriptions = []
}
protected attachTermContainerHandlers () {
this.detachTermContainerHandlers()
const maybeConfigure = () => {
if (this.hasFocus) {
setTimeout(() => this.configure(), 250)
}
}
this.termContainerSubscriptions = [
this.frontend.title$.subscribe(title => this.zone.run(() => this.setTitle(title))),
this.focused$.subscribe(() => this.frontend.enableResizing = true),
this.blurred$.subscribe(() => this.frontend.enableResizing = false),
this.frontend.mouseEvent$.subscribe(async event => {
if (event.type === 'mousedown') {
if (event.which === 2) {
this.paste()
event.preventDefault()
event.stopPropagation()
return
}
if (event.which === 3) {
if (this.config.store.terminal.rightClick === 'menu') {
this.hostApp.popupContextMenu(await this.buildContextMenu())
} else if (this.config.store.terminal.rightClick === 'paste') {
this.paste()
}
event.preventDefault()
event.stopPropagation()
return
}
}
if (event.type === 'mousewheel') {
let wheelDeltaY = 0
if ('wheelDeltaY' in event) {
wheelDeltaY = (event as MouseWheelEvent)['wheelDeltaY']
} else {
wheelDeltaY = (event as MouseWheelEvent)['deltaY']
}
if (event.ctrlKey || event.metaKey) {
if (wheelDeltaY > 0) {
this.zoomIn()
} else {
this.zoomOut()
}
} else if (event.altKey) {
event.preventDefault()
const delta = Math.round(wheelDeltaY / 50)
this.sendInput((delta > 0 ? '\u001bOA' : '\u001bOB').repeat(Math.abs(delta)))
}
}
}),
this.frontend.input$.subscribe(data => {
this.sendInput(data)
}),
this.frontend.resize$.subscribe(({ columns, rows }) => {
this.logger.debug(`Resizing to ${columns}x${rows}`)
this.size = { columns, rows }
this.zone.run(() => {
if (this.session && this.session.open) {
this.session.resize(columns, rows)
}
})
}),
this.hostApp.displayMetricsChanged$.subscribe(maybeConfigure),
this.hostApp.windowMoved$.subscribe(maybeConfigure),
]
}
protected attachSessionHandlers () {
// this.session.output$.bufferTime(10).subscribe((datas) => {
this.session.output$.subscribe(data => {

View File

@ -1,8 +1,8 @@
import { ITerminalColorScheme } from './interfaces'
import { TerminalColorScheme } from './interfaces'
/**
* Extend to add more terminal color schemes
*/
export abstract class TerminalColorSchemeProvider {
abstract async getSchemes (): Promise<ITerminalColorScheme[]>
abstract async getSchemes (): Promise<TerminalColorScheme[]>
}

View File

@ -7,10 +7,10 @@ export abstract class TerminalDecorator {
/**
* Called when a new terminal tab starts
*/
attach (terminal: BaseTerminalTabComponent): void { } // tslint:disable-line no-empty
attach (terminal: BaseTerminalTabComponent): void { } // eslint-disable-line
/**
* Called before a terminal tab is destroyed
*/
detach (terminal: BaseTerminalTabComponent): void { } // tslint:disable-line no-empty
detach (terminal: BaseTerminalTabComponent): void { } // eslint-disable-line
}

View File

@ -24,7 +24,7 @@ export interface Profile {
icon?: SafeHtml
}
export interface ITerminalColorScheme {
export interface TerminalColorScheme {
name: string
foreground: string
background: string
@ -32,7 +32,7 @@ export interface ITerminalColorScheme {
colors: string[]
}
export interface IShell {
export interface Shell {
id: string
name?: string
command: string

View File

@ -1,8 +1,8 @@
import { IShell } from './interfaces'
import { Shell } from './interfaces'
/**
* Extend to add support for more shells
*/
export abstract class ShellProvider {
abstract async provide (): Promise<IShell[]>
abstract async provide (): Promise<Shell[]>
}

View File

@ -1,7 +1,7 @@
import * as fs from 'mz/fs'
import { Injectable } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { HotkeysService, ToolbarButtonProvider, IToolbarButton, HostAppService, ElectronService } from 'terminus-core'
import { ToolbarButtonProvider, ToolbarButton, ElectronService } from 'terminus-core'
import { TerminalService } from './services/terminal.service'
@ -9,11 +9,9 @@ import { TerminalService } from './services/terminal.service'
@Injectable()
export class ButtonProvider extends ToolbarButtonProvider {
constructor (
electron: ElectronService,
private terminal: TerminalService,
private domSanitizer: DomSanitizer,
hostApp: HostAppService,
electron: ElectronService,
hotkeys: HotkeysService,
) {
super()
if (!electron.remote.process.env.TERMINUS_DEV) {
@ -30,7 +28,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
}
}
provide (): IToolbarButton[] {
provide (): ToolbarButton[] {
return [
{
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/plus.svg')),
@ -38,7 +36,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
click: async () => {
this.terminal.openTab()
}
},
},
{
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/profiles.svg')),
@ -50,7 +48,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
title: profile.name,
click: () => this.terminal.openTab(profile),
}))
}
},
},
]
}

View File

@ -2,17 +2,19 @@ import * as fs from 'mz/fs'
import * as path from 'path'
import { Injectable } from '@angular/core'
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
import { ITerminalColorScheme } from './api/interfaces'
import { TerminalColorScheme } from './api/interfaces'
/** @hidden */
@Injectable()
export class HyperColorSchemes extends TerminalColorSchemeProvider {
async getSchemes (): Promise<ITerminalColorScheme[]> {
async getSchemes (): Promise<TerminalColorScheme[]> {
const pluginsPath = path.join(process.env.HOME, '.hyper_plugins', 'node_modules')
if (!(await fs.exists(pluginsPath))) return []
if (!await fs.exists(pluginsPath)) {
return []
}
const plugins = await fs.readdir(pluginsPath)
const themes: ITerminalColorScheme[] = []
const themes: TerminalColorScheme[] = []
plugins.forEach(plugin => {
try {

View File

@ -1,13 +1,13 @@
import { Observable } from 'rxjs'
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
import { exec } from 'mz/child_process'
import deepEqual = require('deep-equal')
const fontManager = require('fontmanager-redux')
import deepEqual from 'deep-equal'
const fontManager = require('fontmanager-redux') // eslint-disable-line
import { Component, Inject } from '@angular/core'
import { ConfigService, HostAppService, Platform, ElectronService } from 'terminus-core'
import { TerminalColorSchemeProvider } from '../api/colorSchemeProvider'
import { ITerminalColorScheme } from '../api/interfaces'
import { TerminalColorScheme } from '../api/interfaces'
import { getCSSFontFamily } from '../utils'
/** @hidden */
@ -17,9 +17,9 @@ import { getCSSFontFamily } from '../utils'
})
export class AppearanceSettingsTabComponent {
fonts: string[] = []
colorSchemes: ITerminalColorScheme[] = []
colorSchemes: TerminalColorScheme[] = []
equalComparator = deepEqual
editingColorScheme: ITerminalColorScheme
editingColorScheme: TerminalColorScheme
schemeChanged = false
constructor (
@ -32,7 +32,7 @@ export class AppearanceSettingsTabComponent {
async ngOnInit () {
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
const fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve))
this.fonts = fonts.map(x => (x.family + ' ' + x.style).trim())
this.fonts = fonts.map(x => `${x.family} ${x.style}`.trim())
this.fonts.sort()
}
if (this.hostApp.platform === Platform.Linux) {
@ -50,14 +50,14 @@ export class AppearanceSettingsTabComponent {
fontAutocomplete = (text$: Observable<string>) => {
return text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v))),
map(list => Array.from(new Set(list))),
)
debounceTime(200),
distinctUntilChanged(),
map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v))),
map(list => Array.from(new Set(list))),
)
}
editScheme (scheme: ITerminalColorScheme) {
editScheme (scheme: TerminalColorScheme) {
this.editingColorScheme = scheme
this.schemeChanged = false
}
@ -75,7 +75,7 @@ export class AppearanceSettingsTabComponent {
this.editingColorScheme = null
}
async deleteScheme (scheme: ITerminalColorScheme) {
async deleteScheme (scheme: TerminalColorScheme) {
if ((await this.electron.showMessageBox(
this.hostApp.getWindow(),
{
@ -92,7 +92,7 @@ export class AppearanceSettingsTabComponent {
}
}
isCustomScheme (scheme: ITerminalColorScheme) {
isCustomScheme (scheme: TerminalColorScheme) {
return this.config.store.terminal.customColorSchemes.some(x => deepEqual(x, scheme))
}

View File

@ -1,6 +1,6 @@
import { Component, Input, Output, EventEmitter } from '@angular/core'
import { ToastrService } from 'ngx-toastr'
import { Frontend, ISearchOptions } from '../frontends/frontend'
import { Frontend, SearchOptions } from '../frontends/frontend'
@Component({
selector: 'search-panel',
@ -8,11 +8,11 @@ import { Frontend, ISearchOptions } from '../frontends/frontend'
styles: [require('./searchPanel.component.scss')],
})
export class SearchPanelComponent {
static globalOptions: SearchOptions = {}
@Input() query: string
@Input() frontend: Frontend
notFound = false
static globalOptions: ISearchOptions = {}
options: ISearchOptions = SearchPanelComponent.globalOptions
options: SearchOptions = SearchPanelComponent.globalOptions
@Output() close = new EventEmitter()

View File

@ -4,7 +4,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subscription } from 'rxjs'
import { ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core'
import { EditProfileModalComponent } from './editProfileModal.component'
import { IShell, Profile } from '../api/interfaces'
import { Shell, Profile } from '../api/interfaces'
import { TerminalService } from '../services/terminal.service'
import { WIN_BUILD_CONPTY_SUPPORTED, WIN_BUILD_CONPTY_STABLE, isWindowsBuild } from '../utils'
@ -13,7 +13,7 @@ import { WIN_BUILD_CONPTY_SUPPORTED, WIN_BUILD_CONPTY_STABLE, isWindowsBuild } f
template: require('./shellSettingsTab.component.pug'),
})
export class ShellSettingsTabComponent {
shells: IShell[] = []
shells: Shell[] = []
profiles: Profile[] = []
Platform = Platform
isConPTYAvailable: boolean
@ -64,7 +64,7 @@ export class ShellSettingsTabComponent {
}
}
newProfile (shell: IShell) {
newProfile (shell: Shell) {
const profile: Profile = {
name: shell.name,
sessionOptions: this.terminalService.optionsFromShell(shell),

View File

@ -29,12 +29,12 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
return
}
switch (hotkey) {
case 'home':
this.sendInput(isConPTY ? '\x1b[H' : '\x1bOH')
break
case 'end':
this.sendInput(isConPTY ? '\x1b[F' : '\x1bOF')
break
case 'home':
this.sendInput(isConPTY ? '\x1b[H' : '\x1bOH')
break
case 'end':
this.sendInput(isConPTY ? '\x1b[F' : '\x1bOF')
break
}
})
@ -74,7 +74,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
return null
}
return {
name: children[0].command
name: children[0].command,
}
}

View File

@ -51,7 +51,7 @@ export class TerminalConfigProvider extends ConfigProvider {
'#C792EA',
'#89DDFF',
'#ffffff',
]
],
},
customColorSchemes: [],
environment: {},
@ -69,12 +69,12 @@ export class TerminalConfigProvider extends ConfigProvider {
},
hotkeys: {
'ctrl-c': ['Ctrl-C'],
'copy': [
copy: [
'⌘-C',
],
'paste': [
paste: [
],
'clear': [
clear: [
'⌘-K',
],
'zoom-in': [
@ -92,13 +92,13 @@ export class TerminalConfigProvider extends ConfigProvider {
'⌘-T',
'⌘-N',
],
'home': ['⌘-Left', 'Home'],
'end': ['⌘-Right', 'End'],
home: ['⌘-Left', 'Home'],
end: ['⌘-Right', 'End'],
'previous-word': ['⌥-Left'],
'next-word': ['⌥-Right'],
'delete-previous-word': ['⌥-Backspace'],
'delete-next-word': ['⌥-Delete'],
'search': [
search: [
'⌘-F',
],
},
@ -113,13 +113,13 @@ export class TerminalConfigProvider extends ConfigProvider {
},
hotkeys: {
'ctrl-c': ['Ctrl-C'],
'copy': [
copy: [
'Ctrl-Shift-C',
],
'paste': [
paste: [
'Ctrl-Shift-V',
],
'clear': [
clear: [
'Ctrl-L',
],
'zoom-in': [
@ -136,13 +136,13 @@ export class TerminalConfigProvider extends ConfigProvider {
'new-tab': [
'Ctrl-Shift-T',
],
'home': ['Home'],
'end': ['End'],
home: ['Home'],
end: ['End'],
'previous-word': ['Ctrl-Left'],
'next-word': ['Ctrl-Right'],
'delete-previous-word': ['Ctrl-Backspace'],
'delete-next-word': ['Ctrl-Delete'],
'search': [
search: [
'Ctrl-Shift-F',
],
},
@ -155,13 +155,13 @@ export class TerminalConfigProvider extends ConfigProvider {
},
hotkeys: {
'ctrl-c': ['Ctrl-C'],
'copy': [
copy: [
'Ctrl-Shift-C',
],
'paste': [
paste: [
'Ctrl-Shift-V',
],
'clear': [
clear: [
'Ctrl-L',
],
'zoom-in': [
@ -178,13 +178,13 @@ export class TerminalConfigProvider extends ConfigProvider {
'new-tab': [
'Ctrl-Shift-T',
],
'home': ['Home'],
'end': ['End'],
home: ['Home'],
end: ['End'],
'previous-word': ['Ctrl-Left'],
'next-word': ['Ctrl-Right'],
'delete-previous-word': ['Ctrl-Backspace'],
'delete-next-word': ['Ctrl-Delete'],
'search': [
search: [
'Ctrl-Shift-F',
],
},

View File

@ -28,7 +28,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
label: 'New terminal',
click: () => this.zone.run(() => {
this.terminalService.openTabWithOptions((tab as any).sessionOptions)
})
}),
},
{
label: 'New with profile',
@ -37,7 +37,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
click: () => this.zone.run(async () => {
this.terminalService.openTab(profile, await tab.session.getWorkingDirectory())
}),
}))
})),
},
]
@ -49,7 +49,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
click: () => this.zone.run(async () => {
this.terminalService.openTabWithOptions({
...profile.sessionOptions,
runAsAdministrator: true
runAsAdministrator: true,
})
}),
})),
@ -83,13 +83,13 @@ export class CopyPasteContextMenu extends TerminalContextMenuItemProvider {
this.toastr.info('Copied')
})
})
}
},
},
{
label: 'Paste',
click: () => {
this.zone.run(() => tab.paste())
}
},
},
]
}

View File

@ -2,7 +2,7 @@ import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } fro
import { ResizeEvent } from '../api/interfaces'
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
export interface ISearchOptions {
export interface SearchOptions {
regex?: boolean
wholeWord?: boolean
caseSensitive?: boolean
@ -39,9 +39,6 @@ export abstract class Frontend {
get dragOver$ (): Observable<DragEvent> { return this.dragOver }
get drop$ (): Observable<DragEvent> { return this.drop }
abstract attach (host: HTMLElement): void
detach (host: HTMLElement): void { } // tslint:disable-line
destroy (): void {
for (const o of [
this.ready,
@ -59,6 +56,9 @@ export abstract class Frontend {
}
}
abstract attach (host: HTMLElement): void
detach (host: HTMLElement): void { } // eslint-disable-line
abstract getSelection (): string
abstract copySelection (): void
abstract clearSelection (): void
@ -71,6 +71,6 @@ export abstract class Frontend {
abstract configure (): void
abstract setZoom (zoom: number): void
abstract findNext (term: string, searchOptions?: ISearchOptions): boolean
abstract findPrevious (term: string, searchOptions?: ISearchOptions): boolean
abstract findNext (term: string, searchOptions?: SearchOptions): boolean
abstract findPrevious (term: string, searchOptions?: SearchOptions): boolean
}

View File

@ -1,3 +1,5 @@
/* eslint-disable */
/** @hidden */
export const hterm = require('hterm-umdjs')
@ -114,7 +116,5 @@ const _collapseToEnd = Selection.prototype.collapseToEnd
Selection.prototype.collapseToEnd = function () {
try {
_collapseToEnd.apply(this)
} catch (err) {
// tslint-disable-line
}
} catch (e) { }
}

View File

@ -1,4 +1,4 @@
import { Frontend, ISearchOptions } from './frontend'
import { Frontend, SearchOptions } from './frontend'
import { hterm, preferenceManager } from './hterm'
import { getCSSFontFamily } from '../utils'
@ -98,7 +98,7 @@ export class HTermFrontend extends Frontend {
return
}
let css = require('./hterm.userCSS.scss')
let css = require('./hterm.userCSS.scss') // eslint-disable-line
if (!config.terminal.ligatures) {
css += `
* {
@ -156,6 +156,14 @@ export class HTermFrontend extends Frontend {
this.term.scrollEnd()
}
findNext (_term: string, _searchOptions?: SearchOptions): boolean {
return false
}
findPrevious (_term: string, _searchOptions?: SearchOptions): boolean {
return false
}
private setFontSize () {
const size = this.configuredFontSize * Math.pow(1.1, this.zoom)
preferenceManager.set('font-size', size)
@ -269,12 +277,4 @@ export class HTermFrontend extends Frontend {
_onCursorBlink()
}
}
findNext (term: string, searchOptions?: ISearchOptions): boolean {
return false
}
findPrevious (term: string, searchOptions?: ISearchOptions): boolean {
return false
}
}

View File

@ -1,16 +1,16 @@
import { Frontend } from './frontend'
import { Frontend, SearchOptions } from './frontend'
import { Terminal, ITheme } from 'xterm'
import { getCSSFontFamily } from '../utils'
import { FitAddon } from 'xterm-addon-fit'
import { enableLigatures } from 'xterm-addon-ligatures'
import { SearchAddon, ISearchOptions } from 'xterm-addon-search'
import { SearchAddon } from 'xterm-addon-search'
import './xterm.css'
import deepEqual = require('deep-equal')
import deepEqual from 'deep-equal'
import { Attributes, AttributeData, CellData } from 'xterm/src/common/buffer/BufferLine'
const COLOR_NAMES = [
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
'brightBlack', 'brightRed', 'brightGreen', 'brightYellow', 'brightBlue', 'brightMagenta', 'brightCyan', 'brightWhite'
'brightBlack', 'brightRed', 'brightGreen', 'brightYellow', 'brightBlue', 'brightMagenta', 'brightCyan', 'brightWhite',
]
/** @hidden */
@ -127,7 +127,7 @@ export class XTermFrontend extends Frontend {
ro.observe(host)
}
detach (host: HTMLElement): void {
detach (_host: HTMLElement): void {
window.removeEventListener('resize', this.resizeHandler)
}
@ -138,7 +138,7 @@ export class XTermFrontend extends Frontend {
copySelection (): void {
require('electron').remote.clipboard.write({
text: this.getSelection(),
html: this.getSelectionAsHTML()
html: this.getSelectionAsHTML(),
})
}
@ -184,7 +184,7 @@ export class XTermFrontend extends Frontend {
this.xterm.setOption('fontFamily', getCSSFontFamily(config.terminal.font))
this.xterm.setOption('bellStyle', config.terminal.bell)
this.xterm.setOption('cursorStyle', {
beam: 'bar'
beam: 'bar',
}[config.terminal.cursor] || config.terminal.cursor)
this.xterm.setOption('cursorBlink', config.terminal.cursorBlink)
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
@ -196,7 +196,7 @@ export class XTermFrontend extends Frontend {
const theme: ITheme = {
foreground: config.terminal.colorScheme.foreground,
background: (config.terminal.background === 'colorScheme') ? config.terminal.colorScheme.background : (config.appearance.vibrancy ? 'transparent' : this.themesService.findCurrentTheme().terminalBackground),
background: config.terminal.background === 'colorScheme' ? config.terminal.colorScheme.background : config.appearance.vibrancy ? 'transparent' : this.themesService.findCurrentTheme().terminalBackground,
cursor: config.terminal.colorScheme.cursor,
}
@ -219,11 +219,11 @@ export class XTermFrontend extends Frontend {
this.setFontSize()
}
findNext (term: string, searchOptions?: ISearchOptions): boolean {
findNext (term: string, searchOptions?: SearchOptions): boolean {
return this.search.findNext(term, searchOptions)
}
findPrevious (term: string, searchOptions?: ISearchOptions): boolean {
findPrevious (term: string, searchOptions?: SearchOptions): boolean {
return this.search.findPrevious(term, searchOptions)
}

View File

@ -1,12 +1,12 @@
import slug from 'slug'
import { Injectable } from '@angular/core'
import { IHotkeyDescription, HotkeyProvider } from 'terminus-core'
import { HotkeyDescription, HotkeyProvider } from 'terminus-core'
import { TerminalService } from './services/terminal.service'
/** @hidden */
@Injectable()
export class TerminalHotkeyProvider extends HotkeyProvider {
hotkeys: IHotkeyDescription[] = [
hotkeys: HotkeyDescription[] = [
{
id: 'copy',
name: 'Copy to clipboard',
@ -73,13 +73,13 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
private terminal: TerminalService,
) { super() }
async provide (): Promise<IHotkeyDescription[]> {
async provide (): Promise<HotkeyDescription[]> {
const profiles = await this.terminal.getProfiles()
return [
...this.hotkeys,
...profiles.map(profile => ({
id: `profile.${slug(profile.name).toLowerCase()}`,
name: `New tab: ${profile.name}`
name: `New tab: ${profile.name}`,
})),
]
}

View File

@ -98,7 +98,7 @@ import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend'
// For WindowsDefaultShellProvider
PowerShellCoreShellProvider,
WSLShellProvider,
WindowsStockShellsProvider
WindowsStockShellsProvider,
],
entryComponents: [
TerminalTabComponent,
@ -122,7 +122,7 @@ import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend'
EnvironmentEditorComponent,
],
})
export default class TerminalModule {
export default class TerminalModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
constructor (
app: AppService,
config: ConfigService,
@ -199,7 +199,7 @@ export default class TerminalModule {
hostApp.cliPaste$.subscribe(text => {
if (app.activeTab instanceof TerminalTabComponent && app.activeTab.session) {
(app.activeTab as TerminalTabComponent).sendInput(text)
app.activeTab.sendInput(text)
hostApp.bringToFront()
}
})
@ -222,3 +222,6 @@ export { TerminalService, BaseSession, TerminalTabComponent, TerminalFrontendSer
export { Frontend, XTermFrontend, XTermWebGLFrontend, HTermFrontend }
export { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
export * from './api/interfaces'
// Deprecations
export { TerminalColorScheme as ITerminalColorScheme, Shell as IShell } from './api/interfaces'

View File

@ -25,7 +25,7 @@ export class PathDropDecorator extends TerminalDecorator {
}
injectPath (terminal: TerminalTabComponent, path: string) {
if (path.indexOf(' ') >= 0) {
if (path.includes(' ')) {
path = `"${path}"`
}
path = path.replace(/\\/g, '\\\\')

View File

@ -29,7 +29,7 @@ export class DockMenuService {
title: profile.name,
iconPath: process.execPath,
iconIndex: 0,
}))
})),
}] : null)
}
if (this.hostApp.platform === Platform.macOS) {

View File

@ -1,4 +1,4 @@
import psNode = require('ps-node')
import * as psNode from 'ps-node'
import * as fs from 'mz/fs'
import * as os from 'os'
import * as nodePTY from 'node-pty'
@ -11,21 +11,23 @@ import { exec } from 'mz/child_process'
import { SessionOptions } from '../api/interfaces'
import { WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from '../utils'
try {
var macOSNativeProcessList = require('macos-native-processlist') // tslint:disable-line
} catch { } // tslint:disable-line
/* eslint-disable block-scoped-var */
try {
var windowsProcessTree = require('@terminus-term/windows-process-tree') // tslint:disable-line
} catch { } // tslint:disable-line
var macOSNativeProcessList = require('macos-native-processlist') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
export interface IChildProcess {
try {
var windowsProcessTree = require('@terminus-term/windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
export interface ChildProcess {
pid: number
ppid: number
command: string
}
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi // tslint:disable-line
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi
const OSC1337Prefix = '\x1b]1337;'
const OSC1337Suffix = '\x07'
@ -61,14 +63,6 @@ export abstract class BaseSession {
this.initialDataBuffer = null
}
abstract start (options: SessionOptions): void
abstract resize (columns: number, rows: number): void
abstract write (data: string): void
abstract kill (signal?: string): void
abstract async getChildProcesses (): Promise<IChildProcess[]>
abstract async gracefullyKillProcess (): Promise<void>
abstract async getWorkingDirectory (): Promise<string>
async destroy (): Promise<void> {
if (this.open) {
this.open = false
@ -78,6 +72,14 @@ export abstract class BaseSession {
await this.gracefullyKillProcess()
}
}
abstract start (options: SessionOptions): void
abstract resize (columns: number, rows: number): void
abstract write (data: string): void
abstract kill (signal?: string): void
abstract async getChildProcesses (): Promise<ChildProcess[]>
abstract async gracefullyKillProcess (): Promise<void>
abstract async getWorkingDirectory (): Promise<string>
}
/** @hidden */
@ -128,12 +130,12 @@ export class Session extends BaseSession {
cwd,
env: env,
// `1` instead of `true` forces ConPTY even if unstable
experimentalUseConpty: ((isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY) ? 1 : false) as any,
experimentalUseConpty: (isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY ? 1 : false) as any,
})
this.guessedCWD = cwd
this.truePID = (this.pty as any).pid
this.truePID = this.pty['pid']
setTimeout(async () => {
// Retrieve any possible single children now that shell has fully started
@ -173,7 +175,7 @@ export class Session extends BaseSession {
this.pauseAfterExit = options.pauseAfterExit
}
processOSC1337 (data) {
processOSC1337 (data: string) {
if (data.includes(OSC1337Prefix)) {
const preData = data.substring(0, data.indexOf(OSC1337Prefix))
let params = data.substring(data.indexOf(OSC1337Prefix) + OSC1337Prefix.length)
@ -183,7 +185,7 @@ export class Session extends BaseSession {
if (params.startsWith('CurrentDir=')) {
this.reportedCWD = params.split('=')[1]
if (this.reportedCWD.startsWith('~')) {
this.reportedCWD = os.homedir + this.reportedCWD.substring(1)
this.reportedCWD = os.homedir() + this.reportedCWD.substring(1)
}
data = preData + postData
}
@ -211,7 +213,7 @@ export class Session extends BaseSession {
this.pty.kill(signal)
}
async getChildProcesses (): Promise<IChildProcess[]> {
async getChildProcesses (): Promise<ChildProcess[]> {
if (!this.truePID) {
return []
}
@ -224,7 +226,7 @@ export class Session extends BaseSession {
}))
}
if (process.platform === 'win32') {
return new Promise<IChildProcess[]>(resolve => {
return new Promise<ChildProcess[]>(resolve => {
windowsProcessTree.getProcessTree(this.truePID, tree => {
resolve(tree ? tree.children.map(child => ({
pid: child.pid,
@ -234,12 +236,12 @@ export class Session extends BaseSession {
})
})
}
return new Promise<IChildProcess[]>((resolve, reject) => {
return new Promise<ChildProcess[]>((resolve, reject) => {
psNode.lookup({ ppid: this.truePID }, (err, processes) => {
if (err) {
return reject(err)
}
resolve(processes as IChildProcess[])
resolve(processes as ChildProcess[])
})
})
}

View File

@ -4,19 +4,19 @@ import { Observable, AsyncSubject } from 'rxjs'
import { Injectable, Inject } from '@angular/core'
import { AppService, Logger, LogService, ConfigService, SplitTabComponent } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell, SessionOptions, Profile } from '../api/interfaces'
import { Shell, SessionOptions, Profile } from '../api/interfaces'
import { TerminalTabComponent } from '../components/terminalTab.component'
import { UACService } from './uac.service'
@Injectable({ providedIn: 'root' })
export class TerminalService {
private shells = new AsyncSubject<IShell[]>()
private shells = new AsyncSubject<Shell[]>()
private logger: Logger
/**
* A fresh list of all available shells
*/
get shells$ (): Observable<IShell[]> { return this.shells }
get shells$ (): Observable<Shell[]> { return this.shells }
/** @hidden */
constructor (
@ -34,11 +34,6 @@ export class TerminalService {
})
}
private async getShells (): Promise<IShell[]> {
const shellLists = await Promise.all(this.config.enabledServices(this.shellProviders).map(x => x.provide()))
return shellLists.reduce((a, b) => a.concat(b), [])
}
async getProfiles (includeHidden?: boolean): Promise<Profile[]> {
const shells = await this.shells$.toPromise()
return [
@ -47,19 +42,11 @@ export class TerminalService {
name: shell.name,
icon: shell.icon,
sessionOptions: this.optionsFromShell(shell),
isBuiltin: true
}))
isBuiltin: true,
})),
]
}
private async reloadShells () {
this.shells = new AsyncSubject<IShell[]>()
const shells = await this.getShells()
this.logger.debug('Shells list:', shells)
this.shells.next(shells)
this.shells.complete()
}
/**
* Launches a new terminal with a specific shell and CWD
* @param pause Wait for a keypress when the shell exits
@ -102,7 +89,7 @@ export class TerminalService {
return this.openTabWithOptions(sessionOptions)
}
optionsFromShell (shell: IShell): SessionOptions {
optionsFromShell (shell: Shell): SessionOptions {
return {
command: shell.command,
args: shell.args || [],
@ -124,4 +111,17 @@ export class TerminalService {
{ sessionOptions }
) as TerminalTabComponent
}
private async getShells (): Promise<Shell[]> {
const shellLists = await Promise.all(this.config.enabledServices(this.shellProviders).map(x => x.provide()))
return shellLists.reduce((a, b) => a.concat(b), [])
}
private async reloadShells () {
this.shells = new AsyncSubject<Shell[]>()
const shells = await this.getShells()
this.logger.debug('Shells list:', shells)
this.shells.next(shells)
this.shells.complete()
}
}

View File

@ -18,11 +18,11 @@ export class TerminalFrontendService {
getFrontend (session?: BaseSession): Frontend {
if (!session) {
const frontend: Frontend = new ({
'xterm': XTermFrontend,
const frontend: Frontend = new {
xterm: XTermFrontend,
'xterm-webgl': XTermWebGLFrontend,
'hterm': HTermFrontend,
}[this.config.store.terminal.frontend])()
hterm: HTermFrontend,
}[this.config.store.terminal.frontend]()
frontend.configService = this.config
frontend.themesService = this.themes
frontend.hotkeysService = this.hotkeys

View File

@ -4,7 +4,7 @@ import { DomSanitizer } from '@angular/platform-browser'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/** @hidden */
@Injectable()
@ -16,7 +16,7 @@ export class CmderShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -37,7 +37,7 @@ export class CmderShellProvider extends ShellProvider {
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cmder.svg')),
env: {
TERM: 'cygwin',
}
},
},
{
id: 'cmderps',
@ -50,7 +50,7 @@ export class CmderShellProvider extends ShellProvider {
'-noprofile',
'-noexit',
'-command',
`Invoke-Expression '. ''${path.join(process.env.CMDER_ROOT, 'vendor', 'profile.ps1')}'''`
`Invoke-Expression '. ''${path.join(process.env.CMDER_ROOT, 'vendor', 'profile.ps1')}'''`,
],
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cmder-powershell.svg')),
env: {},

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
import { ConfigService } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/** @hidden */
@Injectable()
@ -13,7 +13,7 @@ export class CustomShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
const args = this.config.store.terminal.customShell.split(' ')
return [{
id: 'custom',

View File

@ -4,11 +4,13 @@ import { DomSanitizer } from '@angular/platform-browser'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // tslint:disable-line
} catch { } // tslint:disable-line
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
/** @hidden */
@Injectable()
@ -20,7 +22,7 @@ export class Cygwin32ShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -38,7 +40,7 @@ export class Cygwin32ShellProvider extends ShellProvider {
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cygwin.svg')),
env: {
TERM: 'cygwin',
}
},
}]
}
}

View File

@ -4,11 +4,13 @@ import { DomSanitizer } from '@angular/platform-browser'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // tslint:disable-line
} catch { } // tslint:disable-line
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
/** @hidden */
@Injectable()
@ -20,7 +22,7 @@ export class Cygwin64ShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -38,7 +40,7 @@ export class Cygwin64ShellProvider extends ShellProvider {
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cygwin.svg')),
env: {
TERM: 'cygwin',
}
},
}]
}
}

View File

@ -4,11 +4,13 @@ import { DomSanitizer } from '@angular/platform-browser'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // tslint:disable-line
} catch { } // tslint:disable-line
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
/** @hidden */
@Injectable()
@ -20,7 +22,7 @@ export class GitBashShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -39,11 +41,11 @@ export class GitBashShellProvider extends ShellProvider {
id: 'git-bash',
name: 'Git-Bash',
command: path.join(gitBashPath, 'bin', 'bash.exe'),
args: [ '--login', '-i' ],
args: ['--login', '-i'],
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/git-bash.svg')),
env: {
TERM: 'cygwin',
}
},
}]
}
}

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
import { HostAppService, Platform, LogService, Logger } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/** @hidden */
@Injectable()
@ -18,7 +18,7 @@ export class LinuxDefaultShellProvider extends ShellProvider {
this.logger = log.create('linuxDefaultShell')
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Linux) {
return []
}

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/** @hidden */
@Injectable()
@ -14,7 +14,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.macOS) {
return []
}

View File

@ -4,7 +4,7 @@ import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/** @hidden */
@Injectable()
@ -15,7 +15,7 @@ export class POSIXShellsProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform === Platform.Windows) {
return []
}

View File

@ -2,11 +2,13 @@ import { Injectable } from '@angular/core'
import { DomSanitizer } from '@angular/platform-browser'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // tslint:disable-line
} catch { } // tslint:disable-line
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
/** @hidden */
@Injectable()
@ -18,7 +20,7 @@ export class PowerShellCoreShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -37,7 +39,7 @@ export class PowerShellCoreShellProvider extends ShellProvider {
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/powershell-core.svg')),
env: {
TERM: 'cygwin',
}
},
}]
}
}

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
import { WSLShellProvider } from './wsl'
import { PowerShellCoreShellProvider } from './powershellCore'
@ -27,7 +27,7 @@ export class WindowsDefaultShellProvider extends ShellProvider {
]
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}

View File

@ -4,7 +4,7 @@ import { DomSanitizer } from '@angular/platform-browser'
import { HostAppService, Platform, ElectronService } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
/** @hidden */
@Injectable()
@ -17,7 +17,7 @@ export class WindowsStockShellsProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -55,7 +55,7 @@ export class WindowsStockShellsProvider extends ShellProvider {
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/powershell.svg')),
env: {
TERM: 'cygwin',
}
},
},
]
}

View File

@ -5,12 +5,14 @@ import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider } from '../api/shellProvider'
import { IShell } from '../api/interfaces'
import { Shell } from '../api/interfaces'
import { isWindowsBuild, WIN_BUILD_WSL_EXE_DISTRO_FLAG } from '../utils'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // tslint:disable-line
} catch { } // tslint:disable-line
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
} catch { }
/** @hidden */
@Injectable()
@ -21,7 +23,7 @@ export class WSLShellProvider extends ShellProvider {
super()
}
async provide (): Promise<IShell[]> {
async provide (): Promise<Shell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
@ -29,14 +31,14 @@ export class WSLShellProvider extends ShellProvider {
const bashPath = `${process.env.windir}\\system32\\bash.exe`
const wslPath = `${process.env.windir}\\system32\\wsl.exe`
const shells: IShell[] = [{
const shells: Shell[] = [{
id: 'wsl',
name: 'WSL / Default distro',
command: wslPath,
env: {
TERM: 'xterm-color',
COLORTERM: 'truecolor',
}
},
}]
const lxssPath = 'Software\\Microsoft\\Windows\\CurrentVersion\\Lxss'
@ -50,13 +52,13 @@ export class WSLShellProvider extends ShellProvider {
env: {
TERM: 'xterm-color',
COLORTERM: 'truecolor',
}
},
}]
} else {
return []
}
}
for (const child of wnr.listRegistrySubkeys(wnr.HK.CU, lxssPath)) {
for (const child of wnr.listRegistrySubkeys(wnr.HK.CU, lxssPath) as string[]) {
const childKey = wnr.getRegistryKey(wnr.HK.CU, lxssPath + '\\' + child)
if (!childKey.DistributionName) {
continue
@ -67,11 +69,11 @@ export class WSLShellProvider extends ShellProvider {
name: `WSL / ${name}`,
command: wslPath,
args: ['-d', name],
fsBase: childKey.BasePath.value + '\\rootfs',
fsBase: childKey.BasePath.value as string + '\\rootfs',
env: {
TERM: 'xterm-color',
COLORTERM: 'truecolor',
}
},
})
}

View File

@ -25,7 +25,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
const profile = {
sessionOptions: {
...tab.sessionOptions,
cwd: (await tab.session.getWorkingDirectory()) || tab.sessionOptions.cwd,
cwd: await tab.session.getWorkingDirectory() || tab.sessionOptions.cwd,
},
name: tab.sessionOptions.command,
}
@ -35,8 +35,8 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
]
this.config.save()
this.toastr.info('Saved')
})
}
}),
},
]
}
}

View File

@ -1,21 +0,0 @@
{
"extends": [
"tslint-eslint-rules",
"tslint-config-standard"
],
"rules": {
"radix": false,
"indent": [
true,
"spaces"
],
"ter-indent": [true, 4],
"prefer-const": true,
"trailing-comma": [
true,
{
"singleline": "never"
}
]
}
}

611
yarn.lock

File diff suppressed because it is too large Load Diff