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:
parent
a5ecdeb5ea
commit
c008a3478e
82
.eslintrc.yml
Normal file
82
.eslintrc.yml
Normal 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
|
@ -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',
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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=
|
||||
|
13
package.json
13
package.json
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
})
|
||||
|
||||
|
@ -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
|
||||
|
@ -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[]>
|
||||
}
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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[]
|
||||
}
|
||||
|
@ -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 () {
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,6 @@ export class TabHeaderComponent {
|
||||
contextMenu.popup({
|
||||
x: $event.pageX,
|
||||
y: $event.pageY,
|
||||
async: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
}
|
||||
|
@ -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) { }
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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' })
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,5 +54,4 @@ export class TabRecoveryService {
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 }
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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[] = []
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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-,'],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
|
@ -141,7 +141,7 @@ export class SSHSession extends BaseSession {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISSHConnectionGroup {
|
||||
export interface SSHConnectionGroup {
|
||||
name: string
|
||||
connections: SSHConnection[]
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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(),
|
||||
{
|
||||
|
@ -6,10 +6,10 @@ export class SSHConfigProvider extends ConfigProvider {
|
||||
ssh: {
|
||||
connections: [],
|
||||
options: {
|
||||
}
|
||||
},
|
||||
},
|
||||
hotkeys: {
|
||||
'ssh': [
|
||||
ssh: [
|
||||
'Alt-S',
|
||||
],
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
14
terminus-ssh/tsconfig0.typings.json
Normal file
14
terminus-ssh/tsconfig0.typings.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"exclude": ["node_modules", "dist", "typings"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"emitDeclarationOnly": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "./typings",
|
||||
"paths": {
|
||||
"terminus-*": ["../../terminus-*"],
|
||||
"*": ["../../app/node_modules/*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -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 => {
|
||||
|
@ -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[]>
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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[]>
|
||||
}
|
||||
|
@ -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),
|
||||
}))
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
],
|
||||
},
|
||||
|
@ -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())
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) { }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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}`,
|
||||
})),
|
||||
]
|
||||
}
|
||||
|
@ -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'
|
||||
|
@ -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, '\\\\')
|
||||
|
@ -29,7 +29,7 @@ export class DockMenuService {
|
||||
title: profile.name,
|
||||
iconPath: process.execPath,
|
||||
iconIndex: 0,
|
||||
}))
|
||||
})),
|
||||
}] : null)
|
||||
}
|
||||
if (this.hostApp.platform === Platform.macOS) {
|
||||
|
@ -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[])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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: {},
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -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 []
|
||||
}
|
||||
|
@ -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 []
|
||||
}
|
||||
|
@ -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 []
|
||||
}
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -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 []
|
||||
}
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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')
|
||||
})
|
||||
}
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
21
tslint.json
21
tslint.json
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user