mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-20 02:18:01 +00:00
@typescript-eslint linter
This commit is contained in:
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
|
```javascript
|
||||||
import { NgModule, Injectable } from '@angular/core'
|
import { NgModule, Injectable } from '@angular/core'
|
||||||
import { ToolbarButtonProvider, IToolbarButton } from 'terminus-core'
|
import { ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MyButtonProvider extends ToolbarButtonProvider {
|
export class MyButtonProvider extends ToolbarButtonProvider {
|
||||||
provide (): IToolbarButton[] {
|
provide (): ToolbarButton[] {
|
||||||
return [{
|
return [{
|
||||||
icon: 'star',
|
icon: 'star',
|
||||||
title: 'Foobar',
|
title: 'Foobar',
|
||||||
|
@@ -16,7 +16,7 @@ export function getRootModule (plugins: any[]) {
|
|||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
const bootstrap = [
|
const bootstrap = [
|
||||||
...(plugins.filter(x => x.bootstrap).map(x => x.bootstrap)),
|
...plugins.filter(x => x.bootstrap).map(x => x.bootstrap),
|
||||||
]
|
]
|
||||||
|
|
||||||
if (bootstrap.length === 0) {
|
if (bootstrap.length === 0) {
|
||||||
@@ -26,7 +26,7 @@ export function getRootModule (plugins: any[]) {
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
imports,
|
imports,
|
||||||
bootstrap,
|
bootstrap,
|
||||||
}) class RootModule { }
|
}) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||||
|
|
||||||
return RootModule
|
return RootModule
|
||||||
}
|
}
|
||||||
|
@@ -21,15 +21,15 @@ Raven.config(
|
|||||||
return splitArray[splitArray.length - 1]
|
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)
|
frame.filename = normalize(frame.filename)
|
||||||
})
|
})
|
||||||
|
|
||||||
data.culprit = data.exception.values[0].stacktrace.frames[0].filename
|
data.culprit = data.exception.values[0].stacktrace.frames[0].filename
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
process.on('uncaughtException' as any, (err) => {
|
process.on('uncaughtException' as any, (err) => {
|
||||||
|
@@ -2,19 +2,19 @@ import 'zone.js'
|
|||||||
import 'core-js/proposals/reflect-metadata'
|
import 'core-js/proposals/reflect-metadata'
|
||||||
import 'rxjs'
|
import 'rxjs'
|
||||||
|
|
||||||
import isDev = require('electron-is-dev')
|
import * as isDev from 'electron-is-dev'
|
||||||
|
|
||||||
import './global.scss'
|
import './global.scss'
|
||||||
import './toastr.scss'
|
import './toastr.scss'
|
||||||
|
|
||||||
// Always land on the start view
|
|
||||||
location.hash = ''
|
|
||||||
|
|
||||||
import { enableProdMode, NgModuleRef } from '@angular/core'
|
import { enableProdMode, NgModuleRef } from '@angular/core'
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||||
|
|
||||||
import { getRootModule } from './app.module'
|
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
|
;(process as any).enablePromiseAPI = true
|
||||||
|
|
||||||
@@ -28,12 +28,12 @@ if (isDev) {
|
|||||||
enableProdMode()
|
enableProdMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
|
async function bootstrap (plugins: PluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
|
||||||
if (safeMode) {
|
if (safeMode) {
|
||||||
plugins = plugins.filter(x => x.isBuiltin)
|
plugins = plugins.filter(x => x.isBuiltin)
|
||||||
}
|
}
|
||||||
const pluginsModules = await loadPlugins(plugins, (current, total) => {
|
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)
|
const module = getRootModule(pluginsModules)
|
||||||
window['rootModule'] = module
|
window['rootModule'] = module
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import * as fs from 'mz/fs'
|
import * as fs from 'mz/fs'
|
||||||
import * as path from 'path'
|
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
|
const nodeRequire = (global as any).require
|
||||||
|
|
||||||
function normalizePath (path: string): string {
|
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)))
|
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
|
name: string
|
||||||
description: string
|
description: string
|
||||||
packageName: string
|
packageName: string
|
||||||
@@ -87,9 +87,9 @@ const originalRequire = (global as any).require
|
|||||||
return originalRequire.apply(this, arguments)
|
return originalRequire.apply(this, arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findPlugins (): Promise<IPluginInfo[]> {
|
export async function findPlugins (): Promise<PluginInfo[]> {
|
||||||
const paths = nodeModule.globalPaths
|
const paths = nodeModule.globalPaths
|
||||||
let foundPlugins: IPluginInfo[] = []
|
let foundPlugins: PluginInfo[] = []
|
||||||
const candidateLocations: { pluginDir: string, packageName: string }[] = []
|
const candidateLocations: { pluginDir: string, packageName: string }[] = []
|
||||||
const PREFIX = 'terminus-'
|
const PREFIX = 'terminus-'
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
|
|||||||
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
|
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
|
||||||
candidateLocations.push({
|
candidateLocations.push({
|
||||||
pluginDir: path.dirname(pluginDir),
|
pluginDir: path.dirname(pluginDir),
|
||||||
packageName: path.basename(pluginDir)
|
packageName: path.basename(pluginDir),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for (const packageName of pluginNames) {
|
for (const packageName of pluginNames) {
|
||||||
@@ -152,7 +152,7 @@ export async function findPlugins (): Promise<IPluginInfo[]> {
|
|||||||
return foundPlugins
|
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[] = []
|
const plugins: any[] = []
|
||||||
progress(0, 1)
|
progress(0, 1)
|
||||||
let index = 0
|
let index = 0
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
@Component({
|
@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:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debuglog@*, debuglog@^1.0.1:
|
debuglog@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||||
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
|
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"
|
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
|
||||||
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
|
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
|
||||||
|
|
||||||
imurmurhash@*, imurmurhash@^0.1.4:
|
imurmurhash@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||||
@@ -1516,7 +1516,7 @@ libnpm@^2.0.1:
|
|||||||
read-package-json "^2.0.13"
|
read-package-json "^2.0.13"
|
||||||
stringify-package "^1.0.0"
|
stringify-package "^1.0.0"
|
||||||
|
|
||||||
libnpmaccess@*, libnpmaccess@^3.0.1:
|
libnpmaccess@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8"
|
resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8"
|
||||||
integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA==
|
integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA==
|
||||||
@@ -1545,7 +1545,7 @@ libnpmhook@^5.0.2:
|
|||||||
get-stream "^4.0.0"
|
get-stream "^4.0.0"
|
||||||
npm-registry-fetch "^3.8.0"
|
npm-registry-fetch "^3.8.0"
|
||||||
|
|
||||||
libnpmorg@*, libnpmorg@^1.0.0:
|
libnpmorg@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-1.0.0.tgz#979b868c48ba28c5820e3bb9d9e73c883c16a232"
|
resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-1.0.0.tgz#979b868c48ba28c5820e3bb9d9e73c883c16a232"
|
||||||
integrity sha512-o+4eVJBoDGMgRwh2lJY0a8pRV2c/tQM/SxlqXezjcAg26Qe9jigYVs+Xk0vvlYDWCDhP0g74J8UwWeAgsB7gGw==
|
integrity sha512-o+4eVJBoDGMgRwh2lJY0a8pRV2c/tQM/SxlqXezjcAg26Qe9jigYVs+Xk0vvlYDWCDhP0g74J8UwWeAgsB7gGw==
|
||||||
@@ -1570,7 +1570,7 @@ libnpmpublish@^1.1.0:
|
|||||||
semver "^5.5.1"
|
semver "^5.5.1"
|
||||||
ssri "^6.0.1"
|
ssri "^6.0.1"
|
||||||
|
|
||||||
libnpmsearch@*, libnpmsearch@^2.0.0:
|
libnpmsearch@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-2.0.0.tgz#de05af47ada81554a5f64276a69599070d4a5685"
|
resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-2.0.0.tgz#de05af47ada81554a5f64276a69599070d4a5685"
|
||||||
integrity sha512-vd+JWbTGzOSfiOc+72MU6y7WqmBXn49egCCrIXp27iE/88bX8EpG64ST1blWQI1bSMUr9l1AKPMVsqa2tS5KWA==
|
integrity sha512-vd+JWbTGzOSfiOc+72MU6y7WqmBXn49egCCrIXp27iE/88bX8EpG64ST1blWQI1bSMUr9l1AKPMVsqa2tS5KWA==
|
||||||
@@ -1579,7 +1579,7 @@ libnpmsearch@*, libnpmsearch@^2.0.0:
|
|||||||
get-stream "^4.0.0"
|
get-stream "^4.0.0"
|
||||||
npm-registry-fetch "^3.8.0"
|
npm-registry-fetch "^3.8.0"
|
||||||
|
|
||||||
libnpmteam@*, libnpmteam@^1.0.1:
|
libnpmteam@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-1.0.1.tgz#ff704b1b6c06ea674b3b1101ac3e305f5114f213"
|
resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-1.0.1.tgz#ff704b1b6c06ea674b3b1101ac3e305f5114f213"
|
||||||
integrity sha512-gDdrflKFCX7TNwOMX1snWojCoDE5LoRWcfOC0C/fqF7mBq8Uz9zWAX4B2RllYETNO7pBupBaSyBDkTAC15cAMg==
|
integrity sha512-gDdrflKFCX7TNwOMX1snWojCoDE5LoRWcfOC0C/fqF7mBq8Uz9zWAX4B2RllYETNO7pBupBaSyBDkTAC15cAMg==
|
||||||
@@ -1634,11 +1634,6 @@ lockfile@^1.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
signal-exit "^3.0.2"
|
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:
|
lodash._baseuniq@~4.6.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
|
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._createset "~4.0.0"
|
||||||
lodash._root "~3.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:
|
lodash._createset@~4.0.0:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
|
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
|
||||||
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
|
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:
|
lodash._root@~3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
|
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"
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
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:
|
lodash.union@~4.6.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
|
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"
|
npm-package-arg "^6.0.0"
|
||||||
semver "^5.4.1"
|
semver "^5.4.1"
|
||||||
|
|
||||||
npm-profile@*, npm-profile@^4.0.1:
|
npm-profile@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-4.0.1.tgz#d350f7a5e6b60691c7168fbb8392c3603583f5aa"
|
resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-4.0.1.tgz#d350f7a5e6b60691c7168fbb8392c3603583f5aa"
|
||||||
integrity sha512-NQ1I/1Q7YRtHZXkcuU1/IyHeLy6pd+ScKg4+DQHdfsm769TGq6HPrkbuNJVJS4zwE+0mvvmeULzQdWn2L2EsVA==
|
integrity sha512-NQ1I/1Q7YRtHZXkcuU1/IyHeLy6pd+ScKg4+DQHdfsm769TGq6HPrkbuNJVJS4zwE+0mvvmeULzQdWn2L2EsVA==
|
||||||
@@ -2645,7 +2613,7 @@ readable-stream@~1.1.10:
|
|||||||
isarray "0.0.1"
|
isarray "0.0.1"
|
||||||
string_decoder "~0.10.x"
|
string_decoder "~0.10.x"
|
||||||
|
|
||||||
readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0:
|
readdir-scoped-modules@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
|
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
|
||||||
integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c=
|
integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c=
|
||||||
|
13
package.json
13
package.json
@@ -7,6 +7,8 @@
|
|||||||
"@types/js-yaml": "^3.12.1",
|
"@types/js-yaml": "^3.12.1",
|
||||||
"@types/node": "^12.0.8",
|
"@types/node": "^12.0.8",
|
||||||
"@types/webpack-env": "1.13.9",
|
"@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",
|
"app-builder-lib": "^20.43.0",
|
||||||
"apply-loader": "2.0.0",
|
"apply-loader": "2.0.0",
|
||||||
"awesome-typescript-loader": "^5.0.0",
|
"awesome-typescript-loader": "^5.0.0",
|
||||||
@@ -17,6 +19,7 @@
|
|||||||
"electron-builder": "^20.43.0",
|
"electron-builder": "^20.43.0",
|
||||||
"electron-installer-snap": "^3.2.0",
|
"electron-installer-snap": "^3.2.0",
|
||||||
"electron-rebuild": "^1.8.5",
|
"electron-rebuild": "^1.8.5",
|
||||||
|
"eslint": "^5.16.0",
|
||||||
"file-loader": "^4.0.0",
|
"file-loader": "^4.0.0",
|
||||||
"graceful-fs": "^4.1.15",
|
"graceful-fs": "^4.1.15",
|
||||||
"html-loader": "0.5.5",
|
"html-loader": "0.5.5",
|
||||||
@@ -40,9 +43,6 @@
|
|||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"svg-inline-loader": "^0.8.0",
|
"svg-inline-loader": "^0.8.0",
|
||||||
"to-string-loader": "1.1.5",
|
"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",
|
"typedoc": "^0.14.2",
|
||||||
"typescript": "^3.5.2",
|
"typescript": "^3.5.2",
|
||||||
"url-loader": "^2.0.0",
|
"url-loader": "^2.0.0",
|
||||||
@@ -131,8 +131,11 @@
|
|||||||
"start": "cross-env TERMINUS_DEV=1 electron app --debug",
|
"start": "cross-env TERMINUS_DEV=1 electron app --debug",
|
||||||
"prod": "cross-env TERMINUS_DEV=1 electron app",
|
"prod": "cross-env TERMINUS_DEV=1 electron app",
|
||||||
"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",
|
"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"
|
"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 { Injectable } from '@angular/core'
|
||||||
import { TerminalColorSchemeProvider, ITerminalColorScheme } from 'terminus-terminal'
|
import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal'
|
||||||
|
|
||||||
const schemeContents = require.context('../schemes/', true, /.*/)
|
const schemeContents = require.context('../schemes/', true, /.*/)
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ColorSchemes extends TerminalColorSchemeProvider {
|
export class ColorSchemes extends TerminalColorSchemeProvider {
|
||||||
async getSchemes (): Promise<ITerminalColorScheme[]> {
|
async getSchemes (): Promise<TerminalColorScheme[]> {
|
||||||
const schemes: ITerminalColorScheme[] = []
|
const schemes: TerminalColorScheme[] = []
|
||||||
|
|
||||||
schemeContents.keys().forEach(schemeFile => {
|
schemeContents.keys().forEach(schemeFile => {
|
||||||
const lines = (schemeContents(schemeFile).default as string).split('\n')
|
const lines = (schemeContents(schemeFile).default as string).split('\n')
|
||||||
@@ -16,7 +16,7 @@ export class ColorSchemes extends TerminalColorSchemeProvider {
|
|||||||
lines
|
lines
|
||||||
.filter(x => x.startsWith('#define'))
|
.filter(x => x.startsWith('#define'))
|
||||||
.map(x => x.split(' ').map(v => v.trim()))
|
.map(x => x.split(' ').map(v => v.trim()))
|
||||||
.forEach(([ignore, variableName, variableValue]) => {
|
.forEach(([_, variableName, variableValue]) => {
|
||||||
variables[variableName] = variableValue
|
variables[variableName] = variableValue
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -8,4 +8,4 @@ import { ColorSchemes } from './colorSchemes'
|
|||||||
{ provide: TerminalColorSchemeProvider, useClass: ColorSchemes, multi: true },
|
{ 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
|
id: string
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ export interface IHotkeyDescription {
|
|||||||
* must also provide the `hotkeys.foo` config options with the default values
|
* must also provide the `hotkeys.foo` config options with the default values
|
||||||
*/
|
*/
|
||||||
export abstract class HotkeyProvider {
|
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 { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
|
||||||
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
|
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
|
||||||
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
|
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
|
||||||
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
|
export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider'
|
||||||
export { ConfigProvider } from './configProvider'
|
export { ConfigProvider } from './configProvider'
|
||||||
export { HotkeyProvider, IHotkeyDescription } from './hotkeyProvider'
|
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
|
||||||
export { Theme } from './theme'
|
export { Theme } from './theme'
|
||||||
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
|
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ import { SafeHtml } from '@angular/platform-browser'
|
|||||||
/**
|
/**
|
||||||
* See [[ToolbarButtonProvider]]
|
* See [[ToolbarButtonProvider]]
|
||||||
*/
|
*/
|
||||||
export interface IToolbarButton {
|
export interface ToolbarButton {
|
||||||
/**
|
/**
|
||||||
* Raw SVG icon code
|
* Raw SVG icon code
|
||||||
*/
|
*/
|
||||||
@@ -25,15 +25,15 @@ export interface IToolbarButton {
|
|||||||
|
|
||||||
click?: () => void
|
click?: () => void
|
||||||
|
|
||||||
submenu?: () => Promise<IToolbarButton[]>
|
submenu?: () => Promise<ToolbarButton[]>
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
submenuItems?: IToolbarButton[]
|
submenuItems?: ToolbarButton[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend to add buttons to the toolbar
|
* Extend to add buttons to the toolbar
|
||||||
*/
|
*/
|
||||||
export abstract class ToolbarButtonProvider {
|
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 { BaseTabComponent } from './baseTab.component'
|
||||||
import { SafeModeModalComponent } from './safeModeModal.component'
|
import { SafeModeModalComponent } from './safeModeModal.component'
|
||||||
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { AppService, ToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
@@ -26,36 +26,36 @@ import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
|||||||
trigger('animateTab', [
|
trigger('animateTab', [
|
||||||
state('in', style({
|
state('in', style({
|
||||||
'flex-basis': '200px',
|
'flex-basis': '200px',
|
||||||
'width': '200px',
|
width: '200px',
|
||||||
})),
|
})),
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({
|
style({
|
||||||
'flex-basis': '1px',
|
'flex-basis': '1px',
|
||||||
'width': '1px',
|
width: '1px',
|
||||||
}),
|
}),
|
||||||
animate('250ms ease-in-out', style({
|
animate('250ms ease-in-out', style({
|
||||||
'flex-basis': '200px',
|
'flex-basis': '200px',
|
||||||
'width': '200px',
|
width: '200px',
|
||||||
}))
|
})),
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [
|
||||||
style({
|
style({
|
||||||
'flex-basis': '200px',
|
'flex-basis': '200px',
|
||||||
'width': '200px',
|
width: '200px',
|
||||||
}),
|
}),
|
||||||
animate('250ms ease-in-out', style({
|
animate('250ms ease-in-out', style({
|
||||||
'flex-basis': '1px',
|
'flex-basis': '1px',
|
||||||
'width': '1px',
|
width: '1px',
|
||||||
}))
|
})),
|
||||||
])
|
]),
|
||||||
])
|
]),
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class AppRootComponent {
|
export class AppRootComponent {
|
||||||
Platform = Platform
|
Platform = Platform
|
||||||
@Input() ready = false
|
@Input() ready = false
|
||||||
@Input() leftToolbarButtons: IToolbarButton[]
|
@Input() leftToolbarButtons: ToolbarButton[]
|
||||||
@Input() rightToolbarButtons: IToolbarButton[]
|
@Input() rightToolbarButtons: ToolbarButton[]
|
||||||
@HostBinding('class.platform-win32') platformClassWindows = process.platform === 'win32'
|
@HostBinding('class.platform-win32') platformClassWindows = process.platform === 'win32'
|
||||||
@HostBinding('class.platform-darwin') platformClassMacOS = process.platform === 'darwin'
|
@HostBinding('class.platform-darwin') platformClassMacOS = process.platform === 'darwin'
|
||||||
@HostBinding('class.platform-linux') platformClassLinux = process.platform === 'linux'
|
@HostBinding('class.platform-linux') platformClassLinux = process.platform === 'linux'
|
||||||
@@ -89,7 +89,7 @@ export class AppRootComponent {
|
|||||||
|
|
||||||
this.updateIcon = domSanitizer.bypassSecurityTrustHtml(require('../icons/gift.svg')),
|
this.updateIcon = domSanitizer.bypassSecurityTrustHtml(require('../icons/gift.svg')),
|
||||||
|
|
||||||
this.hotkeys.matchedHotkey.subscribe((hotkey) => {
|
this.hotkeys.matchedHotkey.subscribe((hotkey: string) => {
|
||||||
if (hotkey.startsWith('tab-')) {
|
if (hotkey.startsWith('tab-')) {
|
||||||
const index = parseInt(hotkey.split('-')[1])
|
const index = parseInt(hotkey.split('-')[1])
|
||||||
if (index <= this.app.tabs.length) {
|
if (index <= this.app.tabs.length) {
|
||||||
@@ -233,20 +233,20 @@ export class AppRootComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateButtonSubmenu (button: IToolbarButton) {
|
async generateButtonSubmenu (button: ToolbarButton) {
|
||||||
if (button.submenu) {
|
if (button.submenu) {
|
||||||
button.submenuItems = await button.submenu()
|
button.submenuItems = await button.submenu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
private getToolbarButtons (aboveZero: boolean): ToolbarButton[] {
|
||||||
let buttons: IToolbarButton[] = []
|
let buttons: ToolbarButton[] = []
|
||||||
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
buttons = buttons.concat(provider.provide())
|
buttons = buttons.concat(provider.provide())
|
||||||
})
|
})
|
||||||
return buttons
|
return buttons
|
||||||
.filter((button) => (button.weight > 0) === aboveZero)
|
.filter(button => button.weight > 0 === aboveZero)
|
||||||
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
.sort((a: ToolbarButton, b: ToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateVibrancy () {
|
private updateVibrancy () {
|
||||||
|
@@ -8,7 +8,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
|||||||
styles: [require('./checkbox.component.scss')],
|
styles: [require('./checkbox.component.scss')],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true },
|
{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class CheckboxComponent implements ControlValueAccessor {
|
export class CheckboxComponent implements ControlValueAccessor {
|
||||||
@HostBinding('class.active') @Input() model: boolean
|
@HostBinding('class.active') @Input() model: boolean
|
||||||
|
@@ -6,8 +6,8 @@ import { TabsService } from '../services/tabs.service'
|
|||||||
import { HotkeysService } from '../services/hotkeys.service'
|
import { HotkeysService } from '../services/hotkeys.service'
|
||||||
import { TabRecoveryService } from '../services/tabRecovery.service'
|
import { TabRecoveryService } from '../services/tabRecovery.service'
|
||||||
|
|
||||||
export declare type SplitOrientation = 'v' | 'h'
|
export type SplitOrientation = 'v' | 'h' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||||
export declare type SplitDirection = 'r' | 't' | 'b' | 'l'
|
export type SplitDirection = 'r' | 't' | 'b' | 'l' // eslint-disable-line @typescript-eslint/no-type-alias
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a horizontal or vertical split row or column
|
* Describes a horizontal or vertical split row or column
|
||||||
@@ -198,33 +198,33 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch (hotkey) {
|
switch (hotkey) {
|
||||||
case 'split-right':
|
case 'split-right':
|
||||||
this.splitTab(this.focusedTab, 'r')
|
this.splitTab(this.focusedTab, 'r')
|
||||||
break
|
break
|
||||||
case 'split-bottom':
|
case 'split-bottom':
|
||||||
this.splitTab(this.focusedTab, 'b')
|
this.splitTab(this.focusedTab, 'b')
|
||||||
break
|
break
|
||||||
case 'split-top':
|
case 'split-top':
|
||||||
this.splitTab(this.focusedTab, 't')
|
this.splitTab(this.focusedTab, 't')
|
||||||
break
|
break
|
||||||
case 'split-left':
|
case 'split-left':
|
||||||
this.splitTab(this.focusedTab, 'l')
|
this.splitTab(this.focusedTab, 'l')
|
||||||
break
|
break
|
||||||
case 'pane-nav-left':
|
case 'pane-nav-left':
|
||||||
this.navigate('l')
|
this.navigate('l')
|
||||||
break
|
break
|
||||||
case 'pane-nav-right':
|
case 'pane-nav-right':
|
||||||
this.navigate('r')
|
this.navigate('r')
|
||||||
break
|
break
|
||||||
case 'pane-nav-up':
|
case 'pane-nav-up':
|
||||||
this.navigate('t')
|
this.navigate('t')
|
||||||
break
|
break
|
||||||
case 'pane-nav-down':
|
case 'pane-nav-down':
|
||||||
this.navigate('b')
|
this.navigate('b')
|
||||||
break
|
break
|
||||||
case 'close-pane':
|
case 'close-pane':
|
||||||
this.removeTab(this.focusedTab)
|
this.removeTab(this.focusedTab)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -291,11 +291,11 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
|||||||
let insertIndex = target.children.indexOf(relative)
|
let insertIndex = target.children.indexOf(relative)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(target.orientation === 'v' && ['l', 'r'].includes(side)) ||
|
target.orientation === 'v' && ['l', 'r'].includes(side) ||
|
||||||
(target.orientation === 'h' && ['t', 'b'].includes(side))
|
target.orientation === 'h' && ['t', 'b'].includes(side)
|
||||||
) {
|
) {
|
||||||
const newContainer = new SplitContainer()
|
const newContainer = new SplitContainer()
|
||||||
newContainer.orientation = (target.orientation === 'v') ? 'h' : 'v'
|
newContainer.orientation = target.orientation === 'v' ? 'h' : 'v'
|
||||||
newContainer.children = [relative]
|
newContainer.children = [relative]
|
||||||
newContainer.ratios = [1]
|
newContainer.ratios = [1]
|
||||||
target.children[insertIndex] = newContainer
|
target.children[insertIndex] = newContainer
|
||||||
@@ -306,7 +306,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
|||||||
if (insertIndex === -1) {
|
if (insertIndex === -1) {
|
||||||
insertIndex = 0
|
insertIndex = 0
|
||||||
} else {
|
} else {
|
||||||
insertIndex += (side === 'l' || side === 't') ? 0 : 1
|
insertIndex += side === 'l' || side === 't' ? 0 : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < target.children.length; i++) {
|
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) {
|
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)
|
this.viewRefs.set(tab, ref)
|
||||||
|
|
||||||
ref.rootNodes[0].addEventListener('click', () => this.focus(tab))
|
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) {
|
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)
|
const sizes = root.ratios.map(x => x * size)
|
||||||
|
|
||||||
root.x = x
|
root.x = x
|
||||||
@@ -458,10 +458,10 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
|||||||
|
|
||||||
let offset = 0
|
let offset = 0
|
||||||
root.children.forEach((child, i) => {
|
root.children.forEach((child, i) => {
|
||||||
const childX = (root.orientation === 'v') ? x : (x + offset)
|
const childX = root.orientation === 'v' ? x : x + offset
|
||||||
const childY = (root.orientation === 'v') ? (y + offset) : y
|
const childY = root.orientation === 'v' ? y + offset : y
|
||||||
const childW = (root.orientation === 'v') ? w : sizes[i]
|
const childW = root.orientation === 'v' ? w : sizes[i]
|
||||||
const childH = (root.orientation === 'v') ? sizes[i] : h
|
const childH = root.orientation === 'v' ? sizes[i] : h
|
||||||
if (child instanceof SplitContainer) {
|
if (child instanceof SplitContainer) {
|
||||||
this.layoutInternal(child, childX, childY, childW, childH)
|
this.layoutInternal(child, childX, childY, childW, childH)
|
||||||
} else {
|
} else {
|
||||||
@@ -472,7 +472,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
|
|||||||
element.style.width = `${childW}%`
|
element.style.width = `${childW}%`
|
||||||
element.style.height = `${childH}%`
|
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]
|
offset += sizes[i]
|
||||||
|
|
||||||
|
@@ -23,13 +23,13 @@ export class SplitTabSpannerComponent {
|
|||||||
constructor (private element: ElementRef) { }
|
constructor (private element: ElementRef) { }
|
||||||
|
|
||||||
ngAfterViewInit () {
|
ngAfterViewInit () {
|
||||||
this.element.nativeElement.addEventListener('mousedown', e => {
|
this.element.nativeElement.addEventListener('mousedown', (e: MouseEvent) => {
|
||||||
this.isActive = true
|
this.isActive = true
|
||||||
const start = this.isVertical ? e.pageY : e.pageX
|
const start = this.isVertical ? e.pageY : e.pageX
|
||||||
let current = start
|
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
|
current = this.isVertical ? e.pageY : e.pageX
|
||||||
const newPosition = oldPosition + (current - start)
|
const newPosition = oldPosition + (current - start)
|
||||||
if (this.isVertical) {
|
if (this.isVertical) {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import { ConfigService } from '../services/config.service'
|
import { ConfigService } from '../services/config.service'
|
||||||
import { HomeBaseService } from '../services/homeBase.service'
|
import { HomeBaseService } from '../services/homeBase.service'
|
||||||
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { ToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
@@ -19,11 +19,11 @@ export class StartPageComponent {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getButtons (): IToolbarButton[] {
|
getButtons (): ToolbarButton[] {
|
||||||
return this.config.enabledServices(this.toolbarButtonProviders)
|
return this.config.enabledServices(this.toolbarButtonProviders)
|
||||||
.map(provider => provider.provide())
|
.map(provider => provider.provide())
|
||||||
.reduce((a, b) => a.concat(b))
|
.reduce((a, b) => a.concat(b))
|
||||||
.filter(x => !!x.click)
|
.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({
|
contextMenu.popup({
|
||||||
x: $event.pageX,
|
x: $event.pageX,
|
||||||
y: $event.pageY,
|
y: $event.pageY,
|
||||||
async: true,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,4 +6,4 @@ import { Component } from '@angular/core'
|
|||||||
template: require('./titleBar.component.pug'),
|
template: require('./titleBar.component.pug'),
|
||||||
styles: [require('./titleBar.component.scss')],
|
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')],
|
styles: [require('./toggle.component.scss')],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true },
|
{ provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true },
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class ToggleComponent extends CheckboxComponent {
|
export class ToggleComponent extends CheckboxComponent {
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { Directive, AfterViewInit, ElementRef } from '@angular/core'
|
|||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[autofocus]'
|
selector: '[autofocus]',
|
||||||
})
|
})
|
||||||
export class AutofocusDirective implements AfterViewInit {
|
export class AutofocusDirective implements AfterViewInit {
|
||||||
constructor (private el: ElementRef) { }
|
constructor (private el: ElementRef) { }
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { IHotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
|
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppHotkeyProvider extends HotkeyProvider {
|
export class AppHotkeyProvider extends HotkeyProvider {
|
||||||
hotkeys: IHotkeyDescription[] = [
|
hotkeys: HotkeyDescription[] = [
|
||||||
{
|
{
|
||||||
id: 'new-window',
|
id: 'new-window',
|
||||||
name: '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
|
return this.hotkeys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,12 @@ import { TaskCompletionContextMenu, CommonOptionsContextMenu, CloseContextMenu }
|
|||||||
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
||||||
import 'ng2-dnd/bundles/style.css'
|
import 'ng2-dnd/bundles/style.css'
|
||||||
|
|
||||||
|
// 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 = [
|
const PROVIDERS = [
|
||||||
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
||||||
{ provide: Theme, useClass: StandardTheme, multi: true },
|
{ provide: Theme, useClass: StandardTheme, multi: true },
|
||||||
@@ -49,7 +55,7 @@ const PROVIDERS = [
|
|||||||
{ provide: TabContextMenuItemProvider, useClass: CloseContextMenu, multi: true },
|
{ provide: TabContextMenuItemProvider, useClass: CloseContextMenu, multi: true },
|
||||||
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
|
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
|
||||||
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
|
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
|
||||||
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } }
|
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
|
||||||
]
|
]
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@@ -88,9 +94,9 @@ const PROVIDERS = [
|
|||||||
CheckboxComponent,
|
CheckboxComponent,
|
||||||
ToggleComponent,
|
ToggleComponent,
|
||||||
AutofocusDirective,
|
AutofocusDirective,
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export default class AppModule {
|
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||||
constructor (app: AppService, config: ConfigService) {
|
constructor (app: AppService, config: ConfigService) {
|
||||||
app.ready$.subscribe(() => {
|
app.ready$.subscribe(() => {
|
||||||
if (config.store.enableWelcomeTab) {
|
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 { AppRootComponent as bootstrap }
|
||||||
export * from './api'
|
export * from './api'
|
||||||
|
|
||||||
|
// Deprecations
|
||||||
|
export { ToolbarButton as IToolbarButton } from './api'
|
||||||
|
export { HotkeyDescription as IHotkeyDescription } from './api'
|
||||||
|
@@ -21,7 +21,7 @@ class CompletionObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async tick () {
|
async tick () {
|
||||||
if (!(await this.tab.getCurrentProcess())) {
|
if (!await this.tab.getCurrentProcess()) {
|
||||||
this.done.next(null)
|
this.done.next(null)
|
||||||
this.stop()
|
this.stop()
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ export class AppService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private addTabRaw (tab: BaseTabComponent) {
|
addTabRaw (tab: BaseTabComponent) {
|
||||||
this.tabs.push(tab)
|
this.tabs.push(tab)
|
||||||
this.selectTab(tab)
|
this.selectTab(tab)
|
||||||
this.tabsChanged.next()
|
this.tabsChanged.next()
|
||||||
|
@@ -14,7 +14,7 @@ function isStructuralMember (v) {
|
|||||||
Object.keys(v).length > 0 && !v.__nonStructural
|
Object.keys(v).length > 0 && !v.__nonStructural
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNonStructuralObjectMember (v) {
|
function isNonStructuralObjectMember (v): boolean {
|
||||||
return v instanceof Object && !(v instanceof Array) && v.__nonStructural
|
return v instanceof Object && !(v instanceof Array) && v.__nonStructural
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,13 +46,13 @@ export class ConfigProxy {
|
|||||||
get: () => this.getValue(key),
|
get: () => this.getValue(key),
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
this.setValue(key, value)
|
this.setValue(key, value)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getValue = (key: string) => {
|
this.getValue = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
|
||||||
if (real[key] !== undefined) {
|
if (real[key] !== undefined) {
|
||||||
return real[key]
|
return real[key]
|
||||||
} else {
|
} 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
|
real[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue (key: string): any { } // tslint:disable-line
|
getValue (_key: string): any { }
|
||||||
setValue (key: string, value: any) { } // tslint:disable-line
|
setValue (_key: string, _value: any) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
@@ -160,10 +160,6 @@ export class ConfigService {
|
|||||||
this.emitChange()
|
this.emitChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
private emitChange (): void {
|
|
||||||
this.changed.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
requestRestart (): void {
|
requestRestart (): void {
|
||||||
this.restartRequested = true
|
this.restartRequested = true
|
||||||
}
|
}
|
||||||
@@ -179,7 +175,7 @@ export class ConfigService {
|
|||||||
this.servicesCache = {}
|
this.servicesCache = {}
|
||||||
const ngModule = window['rootModule'].ngInjectorDef
|
const ngModule = window['rootModule'].ngInjectorDef
|
||||||
for (const imp of ngModule.imports) {
|
for (const imp of ngModule.imports) {
|
||||||
const module = (imp['ngModule'] || imp)
|
const module = imp['ngModule'] || imp
|
||||||
if (module.ngInjectorDef && module.ngInjectorDef.providers) {
|
if (module.ngInjectorDef && module.ngInjectorDef.providers) {
|
||||||
this.servicesCache[module['pluginName']] = module.ngInjectorDef.providers.map(provider => {
|
this.servicesCache[module['pluginName']] = module.ngInjectorDef.providers.map(provider => {
|
||||||
return provider['useClass'] || provider
|
return provider['useClass'] || provider
|
||||||
@@ -196,4 +192,8 @@ export class ConfigService {
|
|||||||
return true
|
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 { ElectronService } from '../services/electron.service'
|
||||||
import { HostAppService, Bounds } from '../services/hostApp.service'
|
import { HostAppService, Bounds } from '../services/hostApp.service'
|
||||||
|
|
||||||
export interface IScreen {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DockingService {
|
export class DockingService {
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@@ -29,7 +24,7 @@ export class DockingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let display = this.electron.screen.getAllDisplays()
|
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) {
|
if (!display) {
|
||||||
display = this.getCurrentScreen()
|
display = this.getCurrentScreen()
|
||||||
}
|
}
|
||||||
@@ -71,10 +66,10 @@ export class DockingService {
|
|||||||
return this.electron.screen.getAllDisplays().map((display, index) => {
|
return this.electron.screen.getAllDisplays().map((display, index) => {
|
||||||
return {
|
return {
|
||||||
id: display.id,
|
id: display.id,
|
||||||
name: {
|
name: [
|
||||||
0: 'Primary display',
|
'Primary display',
|
||||||
1: 'Secondary display',
|
'Secondary display',
|
||||||
}[index] || `Display ${index + 1}`
|
][index] || `Display ${index + 1}`,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { TouchBar, BrowserWindow, Menu, MenuItem } from 'electron'
|
import { TouchBar, BrowserWindow, Menu, MenuItem, NativeImage } from 'electron'
|
||||||
|
|
||||||
export interface MessageBoxResponse {
|
export interface MessageBoxResponse {
|
||||||
response: number
|
response: number
|
||||||
@@ -8,16 +8,16 @@ export interface MessageBoxResponse {
|
|||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ElectronService {
|
export class ElectronService {
|
||||||
app: any
|
app: Electron.App
|
||||||
ipcRenderer: any
|
ipcRenderer: Electron.IpcRenderer
|
||||||
shell: any
|
shell: Electron.Shell
|
||||||
dialog: any
|
dialog: Electron.Dialog
|
||||||
clipboard: any
|
clipboard: Electron.Clipboard
|
||||||
globalShortcut: any
|
globalShortcut: Electron.GlobalShortcut
|
||||||
nativeImage: any
|
nativeImage: typeof NativeImage
|
||||||
screen: any
|
screen: Electron.Screen
|
||||||
remote: any
|
remote: Electron.Remote
|
||||||
autoUpdater: any
|
autoUpdater: Electron.AutoUpdater
|
||||||
TouchBar: typeof TouchBar
|
TouchBar: typeof TouchBar
|
||||||
BrowserWindow: typeof BrowserWindow
|
BrowserWindow: typeof BrowserWindow
|
||||||
Menu: typeof Menu
|
Menu: typeof Menu
|
||||||
@@ -52,7 +52,7 @@ export class ElectronService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showMessageBox (
|
async showMessageBox (
|
||||||
browserWindow: Electron.BrowserWindow,
|
browserWindow: Electron.BrowserWindow,
|
||||||
options: Electron.MessageBoxOptions
|
options: Electron.MessageBoxOptions
|
||||||
): Promise<MessageBoxResponse> {
|
): Promise<MessageBoxResponse> {
|
||||||
|
@@ -2,8 +2,8 @@ import * as os from 'os'
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { ElectronService } from './electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
import { ConfigService } from './config.service'
|
import { ConfigService } from './config.service'
|
||||||
import mixpanel = require('mixpanel')
|
import * as mixpanel from 'mixpanel'
|
||||||
import uuidv4 = require('uuid/v4')
|
import * as uuidv4 from 'uuid/v4'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class HomeBaseService {
|
export class HomeBaseService {
|
||||||
@@ -53,7 +53,7 @@ export class HomeBaseService {
|
|||||||
|
|
||||||
getAnalyticsProperties () {
|
getAnalyticsProperties () {
|
||||||
return {
|
return {
|
||||||
distinct_id: window.localStorage.analyticsUserID,
|
distinct_id: window.localStorage.analyticsUserID, // eslint-disable-line @typescript-eslint/camelcase
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
os: os.release(),
|
os: os.release(),
|
||||||
version: this.appVersion,
|
version: this.appVersion,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import shellEscape = require('shell-escape')
|
import * as shellEscape from 'shell-escape'
|
||||||
import { Observable, Subject } from 'rxjs'
|
import { Observable, Subject } from 'rxjs'
|
||||||
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
||||||
import { ElectronService } from './electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
@@ -97,7 +97,7 @@ export class HostAppService {
|
|||||||
this.platform = {
|
this.platform = {
|
||||||
win32: Platform.Windows,
|
win32: Platform.Windows,
|
||||||
darwin: Platform.macOS,
|
darwin: Platform.macOS,
|
||||||
linux: Platform.Linux
|
linux: Platform.Linux,
|
||||||
}[process.platform]
|
}[process.platform]
|
||||||
|
|
||||||
this.windowId = parseInt(location.search.substring(1))
|
this.windowId = parseInt(location.search.substring(1))
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, Inject, NgZone, EventEmitter } from '@angular/core'
|
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 { stringifyKeySequence } from './hotkeys.util'
|
||||||
import { ConfigService } from '../services/config.service'
|
import { ConfigService } from '../services/config.service'
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
@@ -24,7 +24,7 @@ export class HotkeysService {
|
|||||||
globalHotkey = new EventEmitter()
|
globalHotkey = new EventEmitter()
|
||||||
private currentKeystrokes: EventBufferEntry[] = []
|
private currentKeystrokes: EventBufferEntry[] = []
|
||||||
private disabledLevel = 0
|
private disabledLevel = 0
|
||||||
private hotkeyDescriptions: IHotkeyDescription[] = []
|
private hotkeyDescriptions: HotkeyDescription[] = []
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
constructor (
|
constructor (
|
||||||
@@ -94,56 +94,7 @@ export class HotkeysService {
|
|||||||
return stringifyKeySequence(this.currentKeystrokes.map(x => x.event))
|
return stringifyKeySequence(this.currentKeystrokes.map(x => x.event))
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerGlobalHotkey () {
|
getCurrentFullyMatchedHotkey (): string {
|
||||||
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 {
|
|
||||||
const currentStrokes = this.getCurrentKeystrokes()
|
const currentStrokes = this.getCurrentKeystrokes()
|
||||||
const config = this.getHotkeysConfig()
|
const config = this.getHotkeysConfig()
|
||||||
for (const id in config) {
|
for (const id in config) {
|
||||||
@@ -178,7 +129,7 @@ export class HotkeysService {
|
|||||||
result.push({
|
result.push({
|
||||||
matchedLength: matchLength,
|
matchedLength: matchLength,
|
||||||
id,
|
id,
|
||||||
strokes: sequence
|
strokes: sequence,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -188,7 +139,7 @@ export class HotkeysService {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
getHotkeyDescription (id: string): IHotkeyDescription {
|
getHotkeyDescription (id: string): HotkeyDescription {
|
||||||
return this.hotkeyDescriptions.filter((x) => x.id === id)[0]
|
return this.hotkeyDescriptions.filter((x) => x.id === id)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +155,7 @@ export class HotkeysService {
|
|||||||
return this.disabledLevel === 0
|
return this.disabledLevel === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHotkeyDescriptions (): Promise<IHotkeyDescription[]> {
|
async getHotkeyDescriptions (): Promise<HotkeyDescription[]> {
|
||||||
return (
|
return (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this.config.enabledServices(this.hotkeyProviders)
|
this.config.enabledServices(this.hotkeyProviders)
|
||||||
@@ -212,4 +163,53 @@ export class HotkeysService {
|
|||||||
)
|
)
|
||||||
).reduce((a, b) => a.concat(b))
|
).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,
|
handleExceptions: false,
|
||||||
maxsize: 5242880,
|
maxsize: 5242880,
|
||||||
maxFiles: 5,
|
maxFiles: 5,
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
exitOnError: false
|
exitOnError: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,18 +32,32 @@ export class Logger {
|
|||||||
private name: string,
|
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[]) {
|
private doLog (level: string, ...args: any[]) {
|
||||||
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
|
||||||
if (this.winstonLogger) {
|
if (this.winstonLogger) {
|
||||||
this.winstonLogger[level](...args)
|
this.winstonLogger[level](...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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' })
|
@Injectable({ providedIn: 'root' })
|
||||||
|
@@ -5,9 +5,11 @@ import { Injectable } from '@angular/core'
|
|||||||
import { ElectronService } from './electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
import { HostAppService, Platform } from './hostApp.service'
|
import { HostAppService, Platform } from './hostApp.service'
|
||||||
|
|
||||||
|
/* eslint-disable block-scoped-var */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var wnr = require('windows-native-registry') // tslint:disable-line
|
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch (_) { } // tslint:disable-line
|
} catch (_) { }
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ShellIntegrationService {
|
export class ShellIntegrationService {
|
||||||
@@ -17,11 +19,11 @@ export class ShellIntegrationService {
|
|||||||
private registryKeys = [
|
private registryKeys = [
|
||||||
{
|
{
|
||||||
path: 'Software\\Classes\\Directory\\Background\\shell\\Open Terminus here',
|
path: 'Software\\Classes\\Directory\\Background\\shell\\Open Terminus here',
|
||||||
command: 'open "%V"'
|
command: 'open "%V"',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'Software\\Classes\\*\\shell\\Paste path into Terminus',
|
path: 'Software\\Classes\\*\\shell\\Paste path into Terminus',
|
||||||
command: 'paste "%V"'
|
command: 'paste "%V"',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
constructor (
|
constructor (
|
||||||
@@ -40,15 +42,6 @@ export class ShellIntegrationService {
|
|||||||
this.updatePaths()
|
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> {
|
async isInstalled (): Promise<boolean> {
|
||||||
if (this.hostApp.platform === Platform.macOS) {
|
if (this.hostApp.platform === Platform.macOS) {
|
||||||
return fs.exists(path.join(this.automatorWorkflowsDestination, this.automatorWorkflows[0]))
|
return fs.exists(path.join(this.automatorWorkflowsDestination, this.automatorWorkflows[0]))
|
||||||
@@ -59,7 +52,7 @@ export class ShellIntegrationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async install () {
|
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) {
|
if (this.hostApp.platform === Platform.macOS) {
|
||||||
for (const wf of this.automatorWorkflows) {
|
for (const wf of this.automatorWorkflows) {
|
||||||
await exec(`cp -r "${this.automatorWorkflowsLocation}/${wf}" "${this.automatorWorkflowsDestination}"`)
|
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 []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,8 @@ import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
|
|||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { TabRecoveryService } from './tabRecovery.service'
|
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' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class TabsService {
|
export class TabsService {
|
||||||
|
@@ -4,7 +4,7 @@ import { AppService } from './app.service'
|
|||||||
import { ConfigService } from './config.service'
|
import { ConfigService } from './config.service'
|
||||||
import { ElectronService } from './electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
import { HostAppService, Platform } from './hostApp.service'
|
import { HostAppService, Platform } from './hostApp.service'
|
||||||
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { ToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
@@ -61,7 +61,7 @@ export class TouchbarService {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttons: IToolbarButton[] = []
|
let buttons: ToolbarButton[] = []
|
||||||
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
buttons = buttons.concat(provider.provide())
|
buttons = buttons.concat(provider.provide())
|
||||||
})
|
})
|
||||||
@@ -76,7 +76,7 @@ export class TouchbarService {
|
|||||||
selectedIndex: this.app.tabs.indexOf(this.app.activeTab),
|
selectedIndex: this.app.tabs.indexOf(this.app.activeTab),
|
||||||
change: (selectedIndex) => this.zone.run(() => {
|
change: (selectedIndex) => this.zone.run(() => {
|
||||||
this.app.selectTab(this.app.tabs[selectedIndex])
|
this.app.selectTab(this.app.tabs[selectedIndex])
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
this.buttonsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
|
this.buttonsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
|
||||||
@@ -84,7 +84,7 @@ export class TouchbarService {
|
|||||||
mode: 'buttons',
|
mode: 'buttons',
|
||||||
change: (selectedIndex) => this.zone.run(() => {
|
change: (selectedIndex) => this.zone.run(() => {
|
||||||
buttons[selectedIndex].click()
|
buttons[selectedIndex].click()
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const touchBar = new this.electron.TouchBar({
|
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: 'flexible' }),
|
||||||
new this.electron.TouchBar.TouchBarSpacer({ size: 'small' }),
|
new this.electron.TouchBar.TouchBarSpacer({ size: 'small' }),
|
||||||
this.buttonsSegmentedControl,
|
this.buttonsSegmentedControl,
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
this.hostApp.setTouchBar(touchBar)
|
this.hostApp.setTouchBar(touchBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getButton (button: IToolbarButton): Electron.SegmentedControlSegment {
|
private getButton (button: ToolbarButton): Electron.SegmentedControlSegment {
|
||||||
return {
|
return {
|
||||||
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
|
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
|
||||||
icon: button.touchBarNSImage ? this.getCachedNSImage(button.touchBarNSImage) : null,
|
icon: button.touchBarNSImage ? this.getCachedNSImage(button.touchBarNSImage) : null,
|
||||||
|
@@ -22,7 +22,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
|
|||||||
label: 'Close',
|
label: 'Close',
|
||||||
click: () => this.zone.run(() => {
|
click: () => this.zone.run(() => {
|
||||||
this.app.closeTab(tab, true)
|
this.app.closeTab(tab, true)
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Close other tabs',
|
label: 'Close other tabs',
|
||||||
@@ -30,7 +30,7 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
|
|||||||
for (const t of this.app.tabs.filter(x => x !== tab)) {
|
for (const t of this.app.tabs.filter(x => x !== tab)) {
|
||||||
this.app.closeTab(t, true)
|
this.app.closeTab(t, true)
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Close tabs to the right',
|
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)) {
|
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
|
||||||
this.app.closeTab(t, true)
|
this.app.closeTab(t, true)
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Close tabs to the left',
|
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))) {
|
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
|
||||||
this.app.closeTab(t, true)
|
this.app.closeTab(t, true)
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -78,11 +78,11 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: 'Rename',
|
label: 'Rename',
|
||||||
click: () => this.zone.run(() => tabHeader.showRenameTabModal())
|
click: () => this.zone.run(() => tabHeader.showRenameTabModal()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Duplicate',
|
label: 'Duplicate',
|
||||||
click: () => this.zone.run(() => this.app.duplicateTab(tab))
|
click: () => this.zone.run(() => this.app.duplicateTab(tab)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Color',
|
label: 'Color',
|
||||||
@@ -95,7 +95,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
|||||||
tab.color = color.value
|
tab.color = color.value
|
||||||
}),
|
}),
|
||||||
})) as Electron.MenuItemConstructorOptions[],
|
})) as Electron.MenuItemConstructorOptions[],
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
|
|||||||
} else {
|
} else {
|
||||||
this.app.stopObservingTabCompletion(tab)
|
this.app.stopObservingTabCompletion(tab)
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import * as semver from 'semver'
|
|||||||
|
|
||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { ConfigService, ElectronService } from 'terminus-core'
|
import { ConfigService, ElectronService } from 'terminus-core'
|
||||||
import { IPluginInfo, PluginManagerService } from '../services/pluginManager.service'
|
import { PluginInfo, PluginManagerService } from '../services/pluginManager.service'
|
||||||
|
|
||||||
enum BusyState { Installing, Uninstalling }
|
enum BusyState { Installing, Uninstalling }
|
||||||
|
|
||||||
@@ -15,10 +15,10 @@ enum BusyState { Installing, Uninstalling }
|
|||||||
})
|
})
|
||||||
export class PluginsSettingsTabComponent {
|
export class PluginsSettingsTabComponent {
|
||||||
BusyState = BusyState
|
BusyState = BusyState
|
||||||
@Input() availablePlugins$: Observable<IPluginInfo[]>
|
@Input() availablePlugins$: Observable<PluginInfo[]>
|
||||||
@Input() availablePluginsQuery$ = new BehaviorSubject<string>('')
|
@Input() availablePluginsQuery$ = new BehaviorSubject<string>('')
|
||||||
@Input() availablePluginsReady = false
|
@Input() availablePluginsReady = false
|
||||||
@Input() knownUpgrades: {[id: string]: IPluginInfo} = {}
|
@Input() knownUpgrades: {[id: string]: PluginInfo} = {}
|
||||||
@Input() busy: {[id: string]: BusyState} = {}
|
@Input() busy: {[id: string]: BusyState} = {}
|
||||||
@Input() erroredPlugin: string
|
@Input() erroredPlugin: string
|
||||||
@Input() errorMessage: string
|
@Input() errorMessage: string
|
||||||
@@ -58,11 +58,11 @@ export class PluginsSettingsTabComponent {
|
|||||||
this.availablePluginsQuery$.next(query)
|
this.availablePluginsQuery$.next(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
isAlreadyInstalled (plugin: IPluginInfo): boolean {
|
isAlreadyInstalled (plugin: PluginInfo): boolean {
|
||||||
return this.pluginManager.installedPlugins.some(x => x.name === plugin.name)
|
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
|
this.busy[plugin.name] = BusyState.Installing
|
||||||
try {
|
try {
|
||||||
await this.pluginManager.installPlugin(plugin)
|
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
|
this.busy[plugin.name] = BusyState.Uninstalling
|
||||||
try {
|
try {
|
||||||
await this.pluginManager.uninstallPlugin(plugin)
|
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])
|
return this.installPlugin(this.knownUpgrades[plugin.name])
|
||||||
}
|
}
|
||||||
|
|
||||||
showPluginInfo (plugin: IPluginInfo) {
|
showPluginInfo (plugin: PluginInfo) {
|
||||||
this.electron.shell.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
|
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.store.pluginBlacklist = this.config.store.pluginBlacklist.filter(x => x !== plugin.name)
|
||||||
this.config.save()
|
this.config.save()
|
||||||
this.config.requestRestart()
|
this.config.requestRestart()
|
||||||
}
|
}
|
||||||
|
|
||||||
disablePlugin (plugin: IPluginInfo) {
|
disablePlugin (plugin: PluginInfo) {
|
||||||
this.config.store.pluginBlacklist = [...this.config.store.pluginBlacklist, plugin.name]
|
this.config.store.pluginBlacklist = [...this.config.store.pluginBlacklist, plugin.name]
|
||||||
this.config.save()
|
this.config.save()
|
||||||
this.config.requestRestart()
|
this.config.requestRestart()
|
||||||
|
@@ -27,6 +27,6 @@ import { PluginsSettingsTabProvider } from './settings'
|
|||||||
PluginsSettingsTabComponent,
|
PluginsSettingsTabComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class PluginManagerModule { }
|
export default class PluginManagerModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||||
|
|
||||||
export { PluginManagerService }
|
export { PluginManagerService }
|
||||||
|
@@ -8,7 +8,7 @@ const NAME_PREFIX = 'terminus-'
|
|||||||
const KEYWORD = 'terminus-plugin'
|
const KEYWORD = 'terminus-plugin'
|
||||||
const OFFICIAL_NPM_ACCOUNT = 'eugenepankov'
|
const OFFICIAL_NPM_ACCOUNT = 'eugenepankov'
|
||||||
|
|
||||||
export interface IPluginInfo {
|
export interface PluginInfo {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
packageName: string
|
packageName: string
|
||||||
@@ -25,7 +25,7 @@ export class PluginManagerService {
|
|||||||
logger: Logger
|
logger: Logger
|
||||||
builtinPluginsPath: string = (window as any).builtinPluginsPath
|
builtinPluginsPath: string = (window as any).builtinPluginsPath
|
||||||
userPluginsPath: string = (window as any).userPluginsPath
|
userPluginsPath: string = (window as any).userPluginsPath
|
||||||
installedPlugins: IPluginInfo[] = (window as any).installedPlugins
|
installedPlugins: PluginInfo[] = (window as any).installedPlugins
|
||||||
|
|
||||||
private npmReady: Promise<void>
|
private npmReady: Promise<void>
|
||||||
private npm: any
|
private npm: any
|
||||||
@@ -57,12 +57,12 @@ export class PluginManagerService {
|
|||||||
return this.npm
|
return this.npm
|
||||||
}
|
}
|
||||||
|
|
||||||
listAvailable (query?: string): Observable<IPluginInfo[]> {
|
listAvailable (query?: string): Observable<PluginInfo[]> {
|
||||||
return from(
|
return from(
|
||||||
axios.get(`https://www.npmjs.com/search?q=keywords%3A${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`, {
|
axios.get(`https://www.npmjs.com/search?q=keywords%3A${KEYWORD}+${encodeURIComponent(query || '')}&from=0&size=1000`, {
|
||||||
headers: {
|
headers: {
|
||||||
'x-spiferack': '1',
|
'x-spiferack': '1',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
).pipe(
|
).pipe(
|
||||||
map(response => response.data.objects.map(item => ({
|
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 => {
|
(await this.getNPM()).commands.install([`${plugin.packageName}@${plugin.version}`], err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.logger.error(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 => {
|
(await this.getNPM()).commands.remove([plugin.packageName], err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.logger.error(err)
|
this.logger.error(err)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { DomSanitizer } from '@angular/platform-browser'
|
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'
|
import { SettingsTabComponent } from './components/settingsTab.component'
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): ToolbarButton[] {
|
||||||
return [{
|
return [{
|
||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/cog.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/cog.svg')),
|
||||||
title: 'Settings',
|
title: 'Settings',
|
||||||
|
@@ -21,7 +21,7 @@ const INPUT_TIMEOUT = 1000
|
|||||||
animate('250ms ease-out', style({
|
animate('250ms ease-out', style({
|
||||||
transform: 'translateX(0)',
|
transform: 'translateX(0)',
|
||||||
opacity: '1',
|
opacity: '1',
|
||||||
}))
|
})),
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [
|
||||||
style({
|
style({
|
||||||
@@ -31,10 +31,10 @@ const INPUT_TIMEOUT = 1000
|
|||||||
animate('250ms ease-in', style({
|
animate('250ms ease-in', style({
|
||||||
transform: 'translateX(25px)',
|
transform: 'translateX(25px)',
|
||||||
opacity: '0',
|
opacity: '0',
|
||||||
}))
|
})),
|
||||||
])
|
]),
|
||||||
])
|
]),
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class HotkeyInputModalComponent {
|
export class HotkeyInputModalComponent {
|
||||||
@Input() value: string[] = []
|
@Input() value: string[] = []
|
||||||
|
@@ -24,7 +24,7 @@ export class MultiHotkeyInputComponent {
|
|||||||
if (typeof this.model === 'string') {
|
if (typeof this.model === 'string') {
|
||||||
this.model = [this.model]
|
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) {
|
editItem (item) {
|
||||||
|
@@ -6,14 +6,14 @@ import {
|
|||||||
ElectronService,
|
ElectronService,
|
||||||
DockingService,
|
DockingService,
|
||||||
ConfigService,
|
ConfigService,
|
||||||
IHotkeyDescription,
|
HotkeyDescription,
|
||||||
HotkeysService,
|
HotkeysService,
|
||||||
BaseTabComponent,
|
BaseTabComponent,
|
||||||
Theme,
|
Theme,
|
||||||
HostAppService,
|
HostAppService,
|
||||||
Platform,
|
Platform,
|
||||||
HomeBaseService,
|
HomeBaseService,
|
||||||
ShellIntegrationService
|
ShellIntegrationService,
|
||||||
} from 'terminus-core'
|
} from 'terminus-core'
|
||||||
|
|
||||||
import { SettingsTabProvider } from '../api'
|
import { SettingsTabProvider } from '../api'
|
||||||
@@ -30,7 +30,7 @@ import { SettingsTabProvider } from '../api'
|
|||||||
export class SettingsTabComponent extends BaseTabComponent {
|
export class SettingsTabComponent extends BaseTabComponent {
|
||||||
@Input() activeTab: string
|
@Input() activeTab: string
|
||||||
hotkeyFilter = ''
|
hotkeyFilter = ''
|
||||||
hotkeyDescriptions: IHotkeyDescription[]
|
hotkeyDescriptions: HotkeyDescription[]
|
||||||
screens: any[]
|
screens: any[]
|
||||||
Platform = Platform
|
Platform = Platform
|
||||||
configDefaults: any
|
configDefaults: any
|
||||||
|
@@ -7,17 +7,17 @@ export class SettingsConfigProvider extends ConfigProvider {
|
|||||||
[Platform.macOS]: {
|
[Platform.macOS]: {
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
settings: ['⌘-,'],
|
settings: ['⌘-,'],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
[Platform.Windows]: {
|
[Platform.Windows]: {
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
settings: ['Ctrl-,']
|
settings: ['Ctrl-,'],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
[Platform.Linux]: {
|
[Platform.Linux]: {
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
settings: ['Ctrl-,']
|
settings: ['Ctrl-,'],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { IHotkeyDescription, HotkeyProvider } from 'terminus-core'
|
import { HotkeyDescription, HotkeyProvider } from 'terminus-core'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SettingsHotkeyProvider extends HotkeyProvider {
|
export class SettingsHotkeyProvider extends HotkeyProvider {
|
||||||
hotkeys: IHotkeyDescription[] = [
|
hotkeys: HotkeyDescription[] = [
|
||||||
{
|
{
|
||||||
id: 'settings',
|
id: 'settings',
|
||||||
name: 'Open Settings',
|
name: 'Open Settings',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
async provide (): Promise<IHotkeyDescription[]> {
|
async provide (): Promise<HotkeyDescription[]> {
|
||||||
return this.hotkeys
|
return this.hotkeys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,7 +42,7 @@ import { SettingsConfigProvider } from './config'
|
|||||||
SettingsTabBodyComponent,
|
SettingsTabBodyComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class SettingsModule { }
|
export default class SettingsModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||||
|
|
||||||
export * from './api'
|
export * from './api'
|
||||||
export { SettingsTabComponent }
|
export { SettingsTabComponent }
|
||||||
|
@@ -141,7 +141,7 @@ export class SSHSession extends BaseSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISSHConnectionGroup {
|
export interface SSHConnectionGroup {
|
||||||
name: string
|
name: string
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { DomSanitizer } from '@angular/platform-browser'
|
import { DomSanitizer } from '@angular/platform-browser'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
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'
|
import { SSHModalComponent } from './components/sshModal.component'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@@ -13,7 +13,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
hotkeys.matchedHotkey.subscribe(async (hotkey: string) => {
|
||||||
if (hotkey === 'ssh') {
|
if (hotkey === 'ssh') {
|
||||||
this.activate()
|
this.activate()
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
this.ngbModal.open(SSHModalComponent)
|
this.ngbModal.open(SSHModalComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): ToolbarButton[] {
|
||||||
return [{
|
return [{
|
||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/globe.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/globe.svg')),
|
||||||
weight: 5,
|
weight: 5,
|
||||||
@@ -32,7 +32,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
touchBarNSImage: 'NSTouchBarOpenInBrowserTemplate',
|
touchBarNSImage: 'NSTouchBarOpenInBrowserTemplate',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.activate()
|
this.activate()
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,23 +27,25 @@ export class EditConnectionModalComponent {
|
|||||||
this.newScript = { expect: '', send: '' }
|
this.newScript = { expect: '', send: '' }
|
||||||
|
|
||||||
for (const k of Object.values(SSHAlgorithmType)) {
|
for (const k of Object.values(SSHAlgorithmType)) {
|
||||||
this.supportedAlgorithms[k] = ALGORITHMS[{
|
const supportedAlg = {
|
||||||
[SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
|
[SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
|
||||||
[SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
|
[SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
|
||||||
[SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
|
[SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
|
||||||
[SSHAlgorithmType.HMAC]: 'SUPPORTED_HMAC',
|
[SSHAlgorithmType.HMAC]: 'SUPPORTED_HMAC',
|
||||||
}[k]]
|
}[k]
|
||||||
this.defaultAlgorithms[k] = ALGORITHMS[{
|
const defaultAlg = {
|
||||||
[SSHAlgorithmType.KEX]: 'KEX',
|
[SSHAlgorithmType.KEX]: 'KEX',
|
||||||
[SSHAlgorithmType.HOSTKEY]: 'SERVER_HOST_KEY',
|
[SSHAlgorithmType.HOSTKEY]: 'SERVER_HOST_KEY',
|
||||||
[SSHAlgorithmType.CIPHER]: 'CIPHER',
|
[SSHAlgorithmType.CIPHER]: 'CIPHER',
|
||||||
[SSHAlgorithmType.HMAC]: 'HMAC',
|
[SSHAlgorithmType.HMAC]: 'HMAC',
|
||||||
}[k]]
|
}[k]
|
||||||
|
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg]
|
||||||
|
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit () {
|
||||||
this.hasSavedPassword = !!(await this.passwordStorage.loadPassword(this.connection))
|
this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.connection)
|
||||||
this.connection.algorithms = this.connection.algorithms || {}
|
this.connection.algorithms = this.connection.algorithms || {}
|
||||||
for (const k of Object.values(SSHAlgorithmType)) {
|
for (const k of Object.values(SSHAlgorithmType)) {
|
||||||
if (!this.connection.algorithms[k]) {
|
if (!this.connection.algorithms[k]) {
|
||||||
@@ -77,8 +79,8 @@ export class EditConnectionModalComponent {
|
|||||||
save () {
|
save () {
|
||||||
for (const k of Object.values(SSHAlgorithmType)) {
|
for (const k of Object.values(SSHAlgorithmType)) {
|
||||||
this.connection.algorithms[k] = Object.entries(this.algorithms[k])
|
this.connection.algorithms[k] = Object.entries(this.algorithms[k])
|
||||||
.filter(([k, v]) => !!v)
|
.filter(([_k, v]) => !!v)
|
||||||
.map(([k, v]) => k)
|
.map(([k, _v]) => k)
|
||||||
}
|
}
|
||||||
this.modalInstance.close(this.connection)
|
this.modalInstance.close(this.connection)
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'
|
|||||||
import { ConfigService, AppService } from 'terminus-core'
|
import { ConfigService, AppService } from 'terminus-core'
|
||||||
import { SettingsTabComponent } from 'terminus-settings'
|
import { SettingsTabComponent } from 'terminus-settings'
|
||||||
import { SSHService } from '../services/ssh.service'
|
import { SSHService } from '../services/ssh.service'
|
||||||
import { SSHConnection, ISSHConnectionGroup } from '../api'
|
import { SSHConnection, SSHConnectionGroup } from '../api'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
@@ -13,10 +13,10 @@ import { SSHConnection, ISSHConnectionGroup } from '../api'
|
|||||||
})
|
})
|
||||||
export class SSHModalComponent {
|
export class SSHModalComponent {
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
childFolders: ISSHConnectionGroup[]
|
childFolders: SSHConnectionGroup[]
|
||||||
quickTarget: string
|
quickTarget: string
|
||||||
lastConnection: SSHConnection
|
lastConnection: SSHConnection
|
||||||
childGroups: ISSHConnectionGroup[]
|
childGroups: SSHConnectionGroup[]
|
||||||
groupCollapsed: {[id: string]: boolean} = {}
|
groupCollapsed: {[id: string]: boolean} = {}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@@ -49,7 +49,9 @@ export class SSHModalComponent {
|
|||||||
|
|
||||||
const connection: SSHConnection = {
|
const connection: SSHConnection = {
|
||||||
name: this.quickTarget,
|
name: this.quickTarget,
|
||||||
host, user, port
|
host,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
}
|
}
|
||||||
window.localStorage.lastConnection = JSON.stringify(connection)
|
window.localStorage.lastConnection = JSON.stringify(connection)
|
||||||
this.connect(connection)
|
this.connect(connection)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ConfigService, ElectronService, HostAppService } from 'terminus-core'
|
import { ConfigService, ElectronService, HostAppService } from 'terminus-core'
|
||||||
import { SSHConnection, ISSHConnectionGroup } from '../api'
|
import { SSHConnection, SSHConnectionGroup } from '../api'
|
||||||
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
||||||
import { PromptModalComponent } from './promptModal.component'
|
import { PromptModalComponent } from './promptModal.component'
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ import { PromptModalComponent } from './promptModal.component'
|
|||||||
})
|
})
|
||||||
export class SSHSettingsTabComponent {
|
export class SSHSettingsTabComponent {
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
childGroups: ISSHConnectionGroup[]
|
childGroups: SSHConnectionGroup[]
|
||||||
groupCollapsed: {[id: string]: boolean} = {}
|
groupCollapsed: {[id: string]: boolean} = {}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@@ -70,7 +70,7 @@ export class SSHSettingsTabComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editGroup (group: ISSHConnectionGroup) {
|
editGroup (group: SSHConnectionGroup) {
|
||||||
const modal = this.ngbModal.open(PromptModalComponent)
|
const modal = this.ngbModal.open(PromptModalComponent)
|
||||||
modal.componentInstance.prompt = 'New group name'
|
modal.componentInstance.prompt = 'New group name'
|
||||||
modal.componentInstance.value = 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(
|
if ((await this.electron.showMessageBox(
|
||||||
this.hostApp.getWindow(),
|
this.hostApp.getWindow(),
|
||||||
{
|
{
|
||||||
|
@@ -6,10 +6,10 @@ export class SSHConfigProvider extends ConfigProvider {
|
|||||||
ssh: {
|
ssh: {
|
||||||
connections: [],
|
connections: [],
|
||||||
options: {
|
options: {
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'ssh': [
|
ssh: [
|
||||||
'Alt-S',
|
'Alt-S',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@@ -47,4 +47,4 @@ import { RecoveryProvider } from './recoveryProvider'
|
|||||||
SSHTabComponent,
|
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 { PromptModalComponent } from '../components/promptModal.component'
|
||||||
import { SSHTabComponent } from '../components/sshTab.component'
|
import { SSHTabComponent } from '../components/sshTab.component'
|
||||||
import { PasswordStorageService } from './passwordStorage.service'
|
import { PasswordStorageService } from './passwordStorage.service'
|
||||||
const { SSH2Stream } = require('ssh2-streams')
|
import { SSH2Stream } from 'ssh2-streams'
|
||||||
|
|
||||||
let windowsProcessTree
|
/* eslint-disable block-scoped-var */
|
||||||
try {
|
try {
|
||||||
windowsProcessTree = require('windows-process-tree/build/Release/windows_process_tree.node')
|
var windowsProcessTree = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch (e) {
|
} catch (_) { }
|
||||||
} // tslint:disable-line
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SSHService {
|
export class SSHService {
|
||||||
@@ -46,10 +45,10 @@ export class SSHService {
|
|||||||
let privateKeyPath = session.connection.privateKey
|
let privateKeyPath = session.connection.privateKey
|
||||||
|
|
||||||
if (!logCallback) {
|
if (!logCallback) {
|
||||||
logCallback = (s) => null
|
logCallback = () => null
|
||||||
}
|
}
|
||||||
|
|
||||||
const log = s => {
|
const log = (s: any) => {
|
||||||
logCallback(s)
|
logCallback(s)
|
||||||
this.logger.info(s)
|
this.logger.info(s)
|
||||||
}
|
}
|
||||||
@@ -84,7 +83,7 @@ export class SSHService {
|
|||||||
modal.componentInstance.password = true
|
modal.componentInstance.password = true
|
||||||
try {
|
try {
|
||||||
privateKeyPassphrase = await modal.result
|
privateKeyPassphrase = await modal.result
|
||||||
} catch (_err) { } // tslint:disable-line
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,11 +213,11 @@ export class SSHService {
|
|||||||
session.shell = shell
|
session.shell = shell
|
||||||
|
|
||||||
shell.on('greeting', greeting => {
|
shell.on('greeting', greeting => {
|
||||||
log('Shell Greeting: ' + greeting)
|
log(`Shell Greeting: ${greeting}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
shell.on('banner', banner => {
|
shell.on('banner', banner => {
|
||||||
log('Shell Banner: ' + banner)
|
log(`Shell Banner: ${banner}`)
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.toastr.error(error.message)
|
this.toastr.error(error.message)
|
||||||
@@ -227,7 +226,8 @@ export class SSHService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
const _authPassword = SSH2Stream.prototype.authPassword
|
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())
|
_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', [
|
static animations: AnimationTriggerMetadata[] = [trigger('slideInOut', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({ transform: 'translateY(-25%)' }),
|
style({ transform: 'translateY(-25%)' }),
|
||||||
animate('100ms ease-in-out', style({ transform: 'translateY(0%)' }))
|
animate('100ms ease-in-out', style({ transform: 'translateY(0%)' })),
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [
|
||||||
animate('100ms ease-in-out', style({ transform: 'translateY(-25%)' }))
|
animate('100ms ease-in-out', style({ transform: 'translateY(-25%)' })),
|
||||||
])
|
]),
|
||||||
])]
|
])]
|
||||||
|
|
||||||
session: BaseSession
|
session: BaseSession
|
||||||
@@ -90,53 +90,53 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch (hotkey) {
|
switch (hotkey) {
|
||||||
case 'ctrl-c':
|
case 'ctrl-c':
|
||||||
if (this.frontend.getSelection()) {
|
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.copySelection()
|
||||||
this.frontend.clearSelection()
|
this.frontend.clearSelection()
|
||||||
this.toastr.info('Copied')
|
this.toastr.info('Copied')
|
||||||
} else {
|
break
|
||||||
this.sendInput('\x03')
|
case 'paste':
|
||||||
}
|
this.paste()
|
||||||
break
|
break
|
||||||
case 'copy':
|
case 'clear':
|
||||||
this.frontend.copySelection()
|
this.frontend.clear()
|
||||||
this.frontend.clearSelection()
|
break
|
||||||
this.toastr.info('Copied')
|
case 'zoom-in':
|
||||||
break
|
this.zoomIn()
|
||||||
case 'paste':
|
break
|
||||||
this.paste()
|
case 'zoom-out':
|
||||||
break
|
this.zoomOut()
|
||||||
case 'clear':
|
break
|
||||||
this.frontend.clear()
|
case 'reset-zoom':
|
||||||
break
|
this.resetZoom()
|
||||||
case 'zoom-in':
|
break
|
||||||
this.zoomIn()
|
case 'previous-word':
|
||||||
break
|
this.sendInput('\x1bb')
|
||||||
case 'zoom-out':
|
break
|
||||||
this.zoomOut()
|
case 'next-word':
|
||||||
break
|
this.sendInput('\x1bf')
|
||||||
case 'reset-zoom':
|
break
|
||||||
this.resetZoom()
|
case 'delete-previous-word':
|
||||||
break
|
this.sendInput('\x1b\x7f')
|
||||||
case 'previous-word':
|
break
|
||||||
this.sendInput('\x1bb')
|
case 'delete-next-word':
|
||||||
break
|
this.sendInput('\x1bd')
|
||||||
case 'next-word':
|
break
|
||||||
this.sendInput('\x1bf')
|
case 'search':
|
||||||
break
|
this.showSearchPanel = true
|
||||||
case 'delete-previous-word':
|
setImmediate(() => {
|
||||||
this.sendInput('\x1b\x7f')
|
this.element.nativeElement.querySelector('.search-input').focus()
|
||||||
break
|
})
|
||||||
case 'delete-next-word':
|
break
|
||||||
this.sendInput('\x1bd')
|
|
||||||
break
|
|
||||||
case 'search':
|
|
||||||
this.showSearchPanel = true
|
|
||||||
setImmediate(() => {
|
|
||||||
this.element.nativeElement.querySelector('.search-input').focus()
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.bellPlayer = document.createElement('audio')
|
this.bellPlayer = document.createElement('audio')
|
||||||
@@ -219,89 +219,6 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
return items
|
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
|
* Feeds input into the active session
|
||||||
*/
|
*/
|
||||||
@@ -330,7 +247,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
paste () {
|
paste () {
|
||||||
let data = this.electron.clipboard.readText()
|
let data = this.electron.clipboard.readText() as string
|
||||||
if (this.config.store.terminal.bracketedPaste) {
|
if (this.config.store.terminal.bracketedPaste) {
|
||||||
data = '\x1b[200~' + data + '\x1b[201~'
|
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 () {
|
protected attachSessionHandlers () {
|
||||||
// this.session.output$.bufferTime(10).subscribe((datas) => {
|
// this.session.output$.bufferTime(10).subscribe((datas) => {
|
||||||
this.session.output$.subscribe(data => {
|
this.session.output$.subscribe(data => {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { ITerminalColorScheme } from './interfaces'
|
import { TerminalColorScheme } from './interfaces'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend to add more terminal color schemes
|
* Extend to add more terminal color schemes
|
||||||
*/
|
*/
|
||||||
export abstract class TerminalColorSchemeProvider {
|
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
|
* 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
|
* 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
|
icon?: SafeHtml
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITerminalColorScheme {
|
export interface TerminalColorScheme {
|
||||||
name: string
|
name: string
|
||||||
foreground: string
|
foreground: string
|
||||||
background: string
|
background: string
|
||||||
@@ -32,7 +32,7 @@ export interface ITerminalColorScheme {
|
|||||||
colors: string[]
|
colors: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IShell {
|
export interface Shell {
|
||||||
id: string
|
id: string
|
||||||
name?: string
|
name?: string
|
||||||
command: string
|
command: string
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { IShell } from './interfaces'
|
import { Shell } from './interfaces'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend to add support for more shells
|
* Extend to add support for more shells
|
||||||
*/
|
*/
|
||||||
export abstract class ShellProvider {
|
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 * as fs from 'mz/fs'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { DomSanitizer } from '@angular/platform-browser'
|
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'
|
import { TerminalService } from './services/terminal.service'
|
||||||
|
|
||||||
@@ -9,11 +9,9 @@ import { TerminalService } from './services/terminal.service'
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ButtonProvider extends ToolbarButtonProvider {
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
constructor (
|
constructor (
|
||||||
|
electron: ElectronService,
|
||||||
private terminal: TerminalService,
|
private terminal: TerminalService,
|
||||||
private domSanitizer: DomSanitizer,
|
private domSanitizer: DomSanitizer,
|
||||||
hostApp: HostAppService,
|
|
||||||
electron: ElectronService,
|
|
||||||
hotkeys: HotkeysService,
|
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
if (!electron.remote.process.env.TERMINUS_DEV) {
|
if (!electron.remote.process.env.TERMINUS_DEV) {
|
||||||
@@ -30,7 +28,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): ToolbarButton[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/plus.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/plus.svg')),
|
||||||
@@ -38,7 +36,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
|
touchBarNSImage: 'NSTouchBarAddDetailTemplate',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.terminal.openTab()
|
this.terminal.openTab()
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/profiles.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('./icons/profiles.svg')),
|
||||||
@@ -50,7 +48,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
title: profile.name,
|
title: profile.name,
|
||||||
click: () => this.terminal.openTab(profile),
|
click: () => this.terminal.openTab(profile),
|
||||||
}))
|
}))
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -2,17 +2,19 @@ import * as fs from 'mz/fs'
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
|
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
|
||||||
import { ITerminalColorScheme } from './api/interfaces'
|
import { TerminalColorScheme } from './api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HyperColorSchemes extends TerminalColorSchemeProvider {
|
export class HyperColorSchemes extends TerminalColorSchemeProvider {
|
||||||
async getSchemes (): Promise<ITerminalColorScheme[]> {
|
async getSchemes (): Promise<TerminalColorScheme[]> {
|
||||||
const pluginsPath = path.join(process.env.HOME, '.hyper_plugins', 'node_modules')
|
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 plugins = await fs.readdir(pluginsPath)
|
||||||
|
|
||||||
const themes: ITerminalColorScheme[] = []
|
const themes: TerminalColorScheme[] = []
|
||||||
|
|
||||||
plugins.forEach(plugin => {
|
plugins.forEach(plugin => {
|
||||||
try {
|
try {
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
|
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
|
||||||
import { exec } from 'mz/child_process'
|
import { exec } from 'mz/child_process'
|
||||||
import deepEqual = require('deep-equal')
|
import deepEqual from 'deep-equal'
|
||||||
const fontManager = require('fontmanager-redux')
|
const fontManager = require('fontmanager-redux') // eslint-disable-line
|
||||||
|
|
||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import { ConfigService, HostAppService, Platform, ElectronService } from 'terminus-core'
|
import { ConfigService, HostAppService, Platform, ElectronService } from 'terminus-core'
|
||||||
import { TerminalColorSchemeProvider } from '../api/colorSchemeProvider'
|
import { TerminalColorSchemeProvider } from '../api/colorSchemeProvider'
|
||||||
import { ITerminalColorScheme } from '../api/interfaces'
|
import { TerminalColorScheme } from '../api/interfaces'
|
||||||
import { getCSSFontFamily } from '../utils'
|
import { getCSSFontFamily } from '../utils'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@@ -17,9 +17,9 @@ import { getCSSFontFamily } from '../utils'
|
|||||||
})
|
})
|
||||||
export class AppearanceSettingsTabComponent {
|
export class AppearanceSettingsTabComponent {
|
||||||
fonts: string[] = []
|
fonts: string[] = []
|
||||||
colorSchemes: ITerminalColorScheme[] = []
|
colorSchemes: TerminalColorScheme[] = []
|
||||||
equalComparator = deepEqual
|
equalComparator = deepEqual
|
||||||
editingColorScheme: ITerminalColorScheme
|
editingColorScheme: TerminalColorScheme
|
||||||
schemeChanged = false
|
schemeChanged = false
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@@ -32,7 +32,7 @@ export class AppearanceSettingsTabComponent {
|
|||||||
async ngOnInit () {
|
async ngOnInit () {
|
||||||
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
|
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
|
||||||
const fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve))
|
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()
|
this.fonts.sort()
|
||||||
}
|
}
|
||||||
if (this.hostApp.platform === Platform.Linux) {
|
if (this.hostApp.platform === Platform.Linux) {
|
||||||
@@ -50,14 +50,14 @@ export class AppearanceSettingsTabComponent {
|
|||||||
|
|
||||||
fontAutocomplete = (text$: Observable<string>) => {
|
fontAutocomplete = (text$: Observable<string>) => {
|
||||||
return text$.pipe(
|
return text$.pipe(
|
||||||
debounceTime(200),
|
debounceTime(200),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v))),
|
map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v))),
|
||||||
map(list => Array.from(new Set(list))),
|
map(list => Array.from(new Set(list))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
editScheme (scheme: ITerminalColorScheme) {
|
editScheme (scheme: TerminalColorScheme) {
|
||||||
this.editingColorScheme = scheme
|
this.editingColorScheme = scheme
|
||||||
this.schemeChanged = false
|
this.schemeChanged = false
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ export class AppearanceSettingsTabComponent {
|
|||||||
this.editingColorScheme = null
|
this.editingColorScheme = null
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteScheme (scheme: ITerminalColorScheme) {
|
async deleteScheme (scheme: TerminalColorScheme) {
|
||||||
if ((await this.electron.showMessageBox(
|
if ((await this.electron.showMessageBox(
|
||||||
this.hostApp.getWindow(),
|
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))
|
return this.config.store.terminal.customColorSchemes.some(x => deepEqual(x, scheme))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core'
|
import { Component, Input, Output, EventEmitter } from '@angular/core'
|
||||||
import { ToastrService } from 'ngx-toastr'
|
import { ToastrService } from 'ngx-toastr'
|
||||||
import { Frontend, ISearchOptions } from '../frontends/frontend'
|
import { Frontend, SearchOptions } from '../frontends/frontend'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'search-panel',
|
selector: 'search-panel',
|
||||||
@@ -8,11 +8,11 @@ import { Frontend, ISearchOptions } from '../frontends/frontend'
|
|||||||
styles: [require('./searchPanel.component.scss')],
|
styles: [require('./searchPanel.component.scss')],
|
||||||
})
|
})
|
||||||
export class SearchPanelComponent {
|
export class SearchPanelComponent {
|
||||||
|
static globalOptions: SearchOptions = {}
|
||||||
@Input() query: string
|
@Input() query: string
|
||||||
@Input() frontend: Frontend
|
@Input() frontend: Frontend
|
||||||
notFound = false
|
notFound = false
|
||||||
static globalOptions: ISearchOptions = {}
|
options: SearchOptions = SearchPanelComponent.globalOptions
|
||||||
options: ISearchOptions = SearchPanelComponent.globalOptions
|
|
||||||
|
|
||||||
@Output() close = new EventEmitter()
|
@Output() close = new EventEmitter()
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
|||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core'
|
import { ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core'
|
||||||
import { EditProfileModalComponent } from './editProfileModal.component'
|
import { EditProfileModalComponent } from './editProfileModal.component'
|
||||||
import { IShell, Profile } from '../api/interfaces'
|
import { Shell, Profile } from '../api/interfaces'
|
||||||
import { TerminalService } from '../services/terminal.service'
|
import { TerminalService } from '../services/terminal.service'
|
||||||
import { WIN_BUILD_CONPTY_SUPPORTED, WIN_BUILD_CONPTY_STABLE, isWindowsBuild } from '../utils'
|
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'),
|
template: require('./shellSettingsTab.component.pug'),
|
||||||
})
|
})
|
||||||
export class ShellSettingsTabComponent {
|
export class ShellSettingsTabComponent {
|
||||||
shells: IShell[] = []
|
shells: Shell[] = []
|
||||||
profiles: Profile[] = []
|
profiles: Profile[] = []
|
||||||
Platform = Platform
|
Platform = Platform
|
||||||
isConPTYAvailable: boolean
|
isConPTYAvailable: boolean
|
||||||
@@ -64,7 +64,7 @@ export class ShellSettingsTabComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newProfile (shell: IShell) {
|
newProfile (shell: Shell) {
|
||||||
const profile: Profile = {
|
const profile: Profile = {
|
||||||
name: shell.name,
|
name: shell.name,
|
||||||
sessionOptions: this.terminalService.optionsFromShell(shell),
|
sessionOptions: this.terminalService.optionsFromShell(shell),
|
||||||
|
@@ -29,12 +29,12 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch (hotkey) {
|
switch (hotkey) {
|
||||||
case 'home':
|
case 'home':
|
||||||
this.sendInput(isConPTY ? '\x1b[H' : '\x1bOH')
|
this.sendInput(isConPTY ? '\x1b[H' : '\x1bOH')
|
||||||
break
|
break
|
||||||
case 'end':
|
case 'end':
|
||||||
this.sendInput(isConPTY ? '\x1b[F' : '\x1bOF')
|
this.sendInput(isConPTY ? '\x1b[F' : '\x1bOF')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: children[0].command
|
name: children[0].command,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -51,7 +51,7 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
'#C792EA',
|
'#C792EA',
|
||||||
'#89DDFF',
|
'#89DDFF',
|
||||||
'#ffffff',
|
'#ffffff',
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
customColorSchemes: [],
|
customColorSchemes: [],
|
||||||
environment: {},
|
environment: {},
|
||||||
@@ -69,12 +69,12 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'ctrl-c': ['Ctrl-C'],
|
'ctrl-c': ['Ctrl-C'],
|
||||||
'copy': [
|
copy: [
|
||||||
'⌘-C',
|
'⌘-C',
|
||||||
],
|
],
|
||||||
'paste': [
|
paste: [
|
||||||
],
|
],
|
||||||
'clear': [
|
clear: [
|
||||||
'⌘-K',
|
'⌘-K',
|
||||||
],
|
],
|
||||||
'zoom-in': [
|
'zoom-in': [
|
||||||
@@ -92,13 +92,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
'⌘-T',
|
'⌘-T',
|
||||||
'⌘-N',
|
'⌘-N',
|
||||||
],
|
],
|
||||||
'home': ['⌘-Left', 'Home'],
|
home: ['⌘-Left', 'Home'],
|
||||||
'end': ['⌘-Right', 'End'],
|
end: ['⌘-Right', 'End'],
|
||||||
'previous-word': ['⌥-Left'],
|
'previous-word': ['⌥-Left'],
|
||||||
'next-word': ['⌥-Right'],
|
'next-word': ['⌥-Right'],
|
||||||
'delete-previous-word': ['⌥-Backspace'],
|
'delete-previous-word': ['⌥-Backspace'],
|
||||||
'delete-next-word': ['⌥-Delete'],
|
'delete-next-word': ['⌥-Delete'],
|
||||||
'search': [
|
search: [
|
||||||
'⌘-F',
|
'⌘-F',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -113,13 +113,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'ctrl-c': ['Ctrl-C'],
|
'ctrl-c': ['Ctrl-C'],
|
||||||
'copy': [
|
copy: [
|
||||||
'Ctrl-Shift-C',
|
'Ctrl-Shift-C',
|
||||||
],
|
],
|
||||||
'paste': [
|
paste: [
|
||||||
'Ctrl-Shift-V',
|
'Ctrl-Shift-V',
|
||||||
],
|
],
|
||||||
'clear': [
|
clear: [
|
||||||
'Ctrl-L',
|
'Ctrl-L',
|
||||||
],
|
],
|
||||||
'zoom-in': [
|
'zoom-in': [
|
||||||
@@ -136,13 +136,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
'new-tab': [
|
'new-tab': [
|
||||||
'Ctrl-Shift-T',
|
'Ctrl-Shift-T',
|
||||||
],
|
],
|
||||||
'home': ['Home'],
|
home: ['Home'],
|
||||||
'end': ['End'],
|
end: ['End'],
|
||||||
'previous-word': ['Ctrl-Left'],
|
'previous-word': ['Ctrl-Left'],
|
||||||
'next-word': ['Ctrl-Right'],
|
'next-word': ['Ctrl-Right'],
|
||||||
'delete-previous-word': ['Ctrl-Backspace'],
|
'delete-previous-word': ['Ctrl-Backspace'],
|
||||||
'delete-next-word': ['Ctrl-Delete'],
|
'delete-next-word': ['Ctrl-Delete'],
|
||||||
'search': [
|
search: [
|
||||||
'Ctrl-Shift-F',
|
'Ctrl-Shift-F',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -155,13 +155,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
},
|
},
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
'ctrl-c': ['Ctrl-C'],
|
'ctrl-c': ['Ctrl-C'],
|
||||||
'copy': [
|
copy: [
|
||||||
'Ctrl-Shift-C',
|
'Ctrl-Shift-C',
|
||||||
],
|
],
|
||||||
'paste': [
|
paste: [
|
||||||
'Ctrl-Shift-V',
|
'Ctrl-Shift-V',
|
||||||
],
|
],
|
||||||
'clear': [
|
clear: [
|
||||||
'Ctrl-L',
|
'Ctrl-L',
|
||||||
],
|
],
|
||||||
'zoom-in': [
|
'zoom-in': [
|
||||||
@@ -178,13 +178,13 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
'new-tab': [
|
'new-tab': [
|
||||||
'Ctrl-Shift-T',
|
'Ctrl-Shift-T',
|
||||||
],
|
],
|
||||||
'home': ['Home'],
|
home: ['Home'],
|
||||||
'end': ['End'],
|
end: ['End'],
|
||||||
'previous-word': ['Ctrl-Left'],
|
'previous-word': ['Ctrl-Left'],
|
||||||
'next-word': ['Ctrl-Right'],
|
'next-word': ['Ctrl-Right'],
|
||||||
'delete-previous-word': ['Ctrl-Backspace'],
|
'delete-previous-word': ['Ctrl-Backspace'],
|
||||||
'delete-next-word': ['Ctrl-Delete'],
|
'delete-next-word': ['Ctrl-Delete'],
|
||||||
'search': [
|
search: [
|
||||||
'Ctrl-Shift-F',
|
'Ctrl-Shift-F',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@@ -28,7 +28,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
|
|||||||
label: 'New terminal',
|
label: 'New terminal',
|
||||||
click: () => this.zone.run(() => {
|
click: () => this.zone.run(() => {
|
||||||
this.terminalService.openTabWithOptions((tab as any).sessionOptions)
|
this.terminalService.openTabWithOptions((tab as any).sessionOptions)
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'New with profile',
|
label: 'New with profile',
|
||||||
@@ -37,7 +37,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
|
|||||||
click: () => this.zone.run(async () => {
|
click: () => this.zone.run(async () => {
|
||||||
this.terminalService.openTab(profile, await tab.session.getWorkingDirectory())
|
this.terminalService.openTab(profile, await tab.session.getWorkingDirectory())
|
||||||
}),
|
}),
|
||||||
}))
|
})),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export class NewTabContextMenu extends TerminalContextMenuItemProvider {
|
|||||||
click: () => this.zone.run(async () => {
|
click: () => this.zone.run(async () => {
|
||||||
this.terminalService.openTabWithOptions({
|
this.terminalService.openTabWithOptions({
|
||||||
...profile.sessionOptions,
|
...profile.sessionOptions,
|
||||||
runAsAdministrator: true
|
runAsAdministrator: true,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
@@ -83,13 +83,13 @@ export class CopyPasteContextMenu extends TerminalContextMenuItemProvider {
|
|||||||
this.toastr.info('Copied')
|
this.toastr.info('Copied')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Paste',
|
label: 'Paste',
|
||||||
click: () => {
|
click: () => {
|
||||||
this.zone.run(() => tab.paste())
|
this.zone.run(() => tab.paste())
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } fro
|
|||||||
import { ResizeEvent } from '../api/interfaces'
|
import { ResizeEvent } from '../api/interfaces'
|
||||||
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
|
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
|
||||||
|
|
||||||
export interface ISearchOptions {
|
export interface SearchOptions {
|
||||||
regex?: boolean
|
regex?: boolean
|
||||||
wholeWord?: boolean
|
wholeWord?: boolean
|
||||||
caseSensitive?: boolean
|
caseSensitive?: boolean
|
||||||
@@ -39,9 +39,6 @@ export abstract class Frontend {
|
|||||||
get dragOver$ (): Observable<DragEvent> { return this.dragOver }
|
get dragOver$ (): Observable<DragEvent> { return this.dragOver }
|
||||||
get drop$ (): Observable<DragEvent> { return this.drop }
|
get drop$ (): Observable<DragEvent> { return this.drop }
|
||||||
|
|
||||||
abstract attach (host: HTMLElement): void
|
|
||||||
detach (host: HTMLElement): void { } // tslint:disable-line
|
|
||||||
|
|
||||||
destroy (): void {
|
destroy (): void {
|
||||||
for (const o of [
|
for (const o of [
|
||||||
this.ready,
|
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 getSelection (): string
|
||||||
abstract copySelection (): void
|
abstract copySelection (): void
|
||||||
abstract clearSelection (): void
|
abstract clearSelection (): void
|
||||||
@@ -71,6 +71,6 @@ export abstract class Frontend {
|
|||||||
abstract configure (): void
|
abstract configure (): void
|
||||||
abstract setZoom (zoom: number): void
|
abstract setZoom (zoom: number): void
|
||||||
|
|
||||||
abstract findNext (term: string, searchOptions?: ISearchOptions): boolean
|
abstract findNext (term: string, searchOptions?: SearchOptions): boolean
|
||||||
abstract findPrevious (term: string, searchOptions?: ISearchOptions): boolean
|
abstract findPrevious (term: string, searchOptions?: SearchOptions): boolean
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
export const hterm = require('hterm-umdjs')
|
export const hterm = require('hterm-umdjs')
|
||||||
|
|
||||||
@@ -114,7 +116,5 @@ const _collapseToEnd = Selection.prototype.collapseToEnd
|
|||||||
Selection.prototype.collapseToEnd = function () {
|
Selection.prototype.collapseToEnd = function () {
|
||||||
try {
|
try {
|
||||||
_collapseToEnd.apply(this)
|
_collapseToEnd.apply(this)
|
||||||
} catch (err) {
|
} catch (e) { }
|
||||||
// tslint-disable-line
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Frontend, ISearchOptions } from './frontend'
|
import { Frontend, SearchOptions } from './frontend'
|
||||||
import { hterm, preferenceManager } from './hterm'
|
import { hterm, preferenceManager } from './hterm'
|
||||||
import { getCSSFontFamily } from '../utils'
|
import { getCSSFontFamily } from '../utils'
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ export class HTermFrontend extends Frontend {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let css = require('./hterm.userCSS.scss')
|
let css = require('./hterm.userCSS.scss') // eslint-disable-line
|
||||||
if (!config.terminal.ligatures) {
|
if (!config.terminal.ligatures) {
|
||||||
css += `
|
css += `
|
||||||
* {
|
* {
|
||||||
@@ -156,6 +156,14 @@ export class HTermFrontend extends Frontend {
|
|||||||
this.term.scrollEnd()
|
this.term.scrollEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findNext (_term: string, _searchOptions?: SearchOptions): boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
findPrevious (_term: string, _searchOptions?: SearchOptions): boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private setFontSize () {
|
private setFontSize () {
|
||||||
const size = this.configuredFontSize * Math.pow(1.1, this.zoom)
|
const size = this.configuredFontSize * Math.pow(1.1, this.zoom)
|
||||||
preferenceManager.set('font-size', size)
|
preferenceManager.set('font-size', size)
|
||||||
@@ -269,12 +277,4 @@ export class HTermFrontend extends Frontend {
|
|||||||
_onCursorBlink()
|
_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 { Terminal, ITheme } from 'xterm'
|
||||||
import { getCSSFontFamily } from '../utils'
|
import { getCSSFontFamily } from '../utils'
|
||||||
import { FitAddon } from 'xterm-addon-fit'
|
import { FitAddon } from 'xterm-addon-fit'
|
||||||
import { enableLigatures } from 'xterm-addon-ligatures'
|
import { enableLigatures } from 'xterm-addon-ligatures'
|
||||||
import { SearchAddon, ISearchOptions } from 'xterm-addon-search'
|
import { SearchAddon } from 'xterm-addon-search'
|
||||||
import './xterm.css'
|
import './xterm.css'
|
||||||
import deepEqual = require('deep-equal')
|
import deepEqual from 'deep-equal'
|
||||||
import { Attributes, AttributeData, CellData } from 'xterm/src/common/buffer/BufferLine'
|
import { Attributes, AttributeData, CellData } from 'xterm/src/common/buffer/BufferLine'
|
||||||
|
|
||||||
const COLOR_NAMES = [
|
const COLOR_NAMES = [
|
||||||
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
|
'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 */
|
/** @hidden */
|
||||||
@@ -127,7 +127,7 @@ export class XTermFrontend extends Frontend {
|
|||||||
ro.observe(host)
|
ro.observe(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
detach (host: HTMLElement): void {
|
detach (_host: HTMLElement): void {
|
||||||
window.removeEventListener('resize', this.resizeHandler)
|
window.removeEventListener('resize', this.resizeHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ export class XTermFrontend extends Frontend {
|
|||||||
copySelection (): void {
|
copySelection (): void {
|
||||||
require('electron').remote.clipboard.write({
|
require('electron').remote.clipboard.write({
|
||||||
text: this.getSelection(),
|
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('fontFamily', getCSSFontFamily(config.terminal.font))
|
||||||
this.xterm.setOption('bellStyle', config.terminal.bell)
|
this.xterm.setOption('bellStyle', config.terminal.bell)
|
||||||
this.xterm.setOption('cursorStyle', {
|
this.xterm.setOption('cursorStyle', {
|
||||||
beam: 'bar'
|
beam: 'bar',
|
||||||
}[config.terminal.cursor] || config.terminal.cursor)
|
}[config.terminal.cursor] || config.terminal.cursor)
|
||||||
this.xterm.setOption('cursorBlink', config.terminal.cursorBlink)
|
this.xterm.setOption('cursorBlink', config.terminal.cursorBlink)
|
||||||
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
|
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
|
||||||
@@ -196,7 +196,7 @@ export class XTermFrontend extends Frontend {
|
|||||||
|
|
||||||
const theme: ITheme = {
|
const theme: ITheme = {
|
||||||
foreground: config.terminal.colorScheme.foreground,
|
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,
|
cursor: config.terminal.colorScheme.cursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,11 +219,11 @@ export class XTermFrontend extends Frontend {
|
|||||||
this.setFontSize()
|
this.setFontSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
findNext (term: string, searchOptions?: ISearchOptions): boolean {
|
findNext (term: string, searchOptions?: SearchOptions): boolean {
|
||||||
return this.search.findNext(term, searchOptions)
|
return this.search.findNext(term, searchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
findPrevious (term: string, searchOptions?: ISearchOptions): boolean {
|
findPrevious (term: string, searchOptions?: SearchOptions): boolean {
|
||||||
return this.search.findPrevious(term, searchOptions)
|
return this.search.findPrevious(term, searchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import slug from 'slug'
|
import slug from 'slug'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { IHotkeyDescription, HotkeyProvider } from 'terminus-core'
|
import { HotkeyDescription, HotkeyProvider } from 'terminus-core'
|
||||||
import { TerminalService } from './services/terminal.service'
|
import { TerminalService } from './services/terminal.service'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TerminalHotkeyProvider extends HotkeyProvider {
|
export class TerminalHotkeyProvider extends HotkeyProvider {
|
||||||
hotkeys: IHotkeyDescription[] = [
|
hotkeys: HotkeyDescription[] = [
|
||||||
{
|
{
|
||||||
id: 'copy',
|
id: 'copy',
|
||||||
name: 'Copy to clipboard',
|
name: 'Copy to clipboard',
|
||||||
@@ -73,13 +73,13 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
|||||||
private terminal: TerminalService,
|
private terminal: TerminalService,
|
||||||
) { super() }
|
) { super() }
|
||||||
|
|
||||||
async provide (): Promise<IHotkeyDescription[]> {
|
async provide (): Promise<HotkeyDescription[]> {
|
||||||
const profiles = await this.terminal.getProfiles()
|
const profiles = await this.terminal.getProfiles()
|
||||||
return [
|
return [
|
||||||
...this.hotkeys,
|
...this.hotkeys,
|
||||||
...profiles.map(profile => ({
|
...profiles.map(profile => ({
|
||||||
id: `profile.${slug(profile.name).toLowerCase()}`,
|
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
|
// For WindowsDefaultShellProvider
|
||||||
PowerShellCoreShellProvider,
|
PowerShellCoreShellProvider,
|
||||||
WSLShellProvider,
|
WSLShellProvider,
|
||||||
WindowsStockShellsProvider
|
WindowsStockShellsProvider,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
@@ -122,7 +122,7 @@ import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend'
|
|||||||
EnvironmentEditorComponent,
|
EnvironmentEditorComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class TerminalModule {
|
export default class TerminalModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||||
constructor (
|
constructor (
|
||||||
app: AppService,
|
app: AppService,
|
||||||
config: ConfigService,
|
config: ConfigService,
|
||||||
@@ -199,7 +199,7 @@ export default class TerminalModule {
|
|||||||
|
|
||||||
hostApp.cliPaste$.subscribe(text => {
|
hostApp.cliPaste$.subscribe(text => {
|
||||||
if (app.activeTab instanceof TerminalTabComponent && app.activeTab.session) {
|
if (app.activeTab instanceof TerminalTabComponent && app.activeTab.session) {
|
||||||
(app.activeTab as TerminalTabComponent).sendInput(text)
|
app.activeTab.sendInput(text)
|
||||||
hostApp.bringToFront()
|
hostApp.bringToFront()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -222,3 +222,6 @@ export { TerminalService, BaseSession, TerminalTabComponent, TerminalFrontendSer
|
|||||||
export { Frontend, XTermFrontend, XTermWebGLFrontend, HTermFrontend }
|
export { Frontend, XTermFrontend, XTermWebGLFrontend, HTermFrontend }
|
||||||
export { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
|
export { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
|
||||||
export * from './api/interfaces'
|
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) {
|
injectPath (terminal: TerminalTabComponent, path: string) {
|
||||||
if (path.indexOf(' ') >= 0) {
|
if (path.includes(' ')) {
|
||||||
path = `"${path}"`
|
path = `"${path}"`
|
||||||
}
|
}
|
||||||
path = path.replace(/\\/g, '\\\\')
|
path = path.replace(/\\/g, '\\\\')
|
||||||
|
@@ -29,7 +29,7 @@ export class DockMenuService {
|
|||||||
title: profile.name,
|
title: profile.name,
|
||||||
iconPath: process.execPath,
|
iconPath: process.execPath,
|
||||||
iconIndex: 0,
|
iconIndex: 0,
|
||||||
}))
|
})),
|
||||||
}] : null)
|
}] : null)
|
||||||
}
|
}
|
||||||
if (this.hostApp.platform === Platform.macOS) {
|
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 fs from 'mz/fs'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as nodePTY from 'node-pty'
|
import * as nodePTY from 'node-pty'
|
||||||
@@ -11,21 +11,23 @@ import { exec } from 'mz/child_process'
|
|||||||
import { SessionOptions } from '../api/interfaces'
|
import { SessionOptions } from '../api/interfaces'
|
||||||
import { WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from '../utils'
|
import { WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from '../utils'
|
||||||
|
|
||||||
try {
|
/* eslint-disable block-scoped-var */
|
||||||
var macOSNativeProcessList = require('macos-native-processlist') // tslint:disable-line
|
|
||||||
} catch { } // tslint:disable-line
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var windowsProcessTree = require('@terminus-term/windows-process-tree') // tslint:disable-line
|
var macOSNativeProcessList = require('macos-native-processlist') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch { } // tslint:disable-line
|
} 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
|
pid: number
|
||||||
ppid: number
|
ppid: number
|
||||||
command: string
|
command: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi // tslint:disable-line
|
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi
|
||||||
const OSC1337Prefix = '\x1b]1337;'
|
const OSC1337Prefix = '\x1b]1337;'
|
||||||
const OSC1337Suffix = '\x07'
|
const OSC1337Suffix = '\x07'
|
||||||
|
|
||||||
@@ -61,14 +63,6 @@ export abstract class BaseSession {
|
|||||||
this.initialDataBuffer = null
|
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> {
|
async destroy (): Promise<void> {
|
||||||
if (this.open) {
|
if (this.open) {
|
||||||
this.open = false
|
this.open = false
|
||||||
@@ -78,6 +72,14 @@ export abstract class BaseSession {
|
|||||||
await this.gracefullyKillProcess()
|
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 */
|
/** @hidden */
|
||||||
@@ -128,12 +130,12 @@ export class Session extends BaseSession {
|
|||||||
cwd,
|
cwd,
|
||||||
env: env,
|
env: env,
|
||||||
// `1` instead of `true` forces ConPTY even if unstable
|
// `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.guessedCWD = cwd
|
||||||
|
|
||||||
this.truePID = (this.pty as any).pid
|
this.truePID = this.pty['pid']
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// Retrieve any possible single children now that shell has fully started
|
// Retrieve any possible single children now that shell has fully started
|
||||||
@@ -173,7 +175,7 @@ export class Session extends BaseSession {
|
|||||||
this.pauseAfterExit = options.pauseAfterExit
|
this.pauseAfterExit = options.pauseAfterExit
|
||||||
}
|
}
|
||||||
|
|
||||||
processOSC1337 (data) {
|
processOSC1337 (data: string) {
|
||||||
if (data.includes(OSC1337Prefix)) {
|
if (data.includes(OSC1337Prefix)) {
|
||||||
const preData = data.substring(0, data.indexOf(OSC1337Prefix))
|
const preData = data.substring(0, data.indexOf(OSC1337Prefix))
|
||||||
let params = data.substring(data.indexOf(OSC1337Prefix) + OSC1337Prefix.length)
|
let params = data.substring(data.indexOf(OSC1337Prefix) + OSC1337Prefix.length)
|
||||||
@@ -183,7 +185,7 @@ export class Session extends BaseSession {
|
|||||||
if (params.startsWith('CurrentDir=')) {
|
if (params.startsWith('CurrentDir=')) {
|
||||||
this.reportedCWD = params.split('=')[1]
|
this.reportedCWD = params.split('=')[1]
|
||||||
if (this.reportedCWD.startsWith('~')) {
|
if (this.reportedCWD.startsWith('~')) {
|
||||||
this.reportedCWD = os.homedir + this.reportedCWD.substring(1)
|
this.reportedCWD = os.homedir() + this.reportedCWD.substring(1)
|
||||||
}
|
}
|
||||||
data = preData + postData
|
data = preData + postData
|
||||||
}
|
}
|
||||||
@@ -211,7 +213,7 @@ export class Session extends BaseSession {
|
|||||||
this.pty.kill(signal)
|
this.pty.kill(signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildProcesses (): Promise<IChildProcess[]> {
|
async getChildProcesses (): Promise<ChildProcess[]> {
|
||||||
if (!this.truePID) {
|
if (!this.truePID) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -224,7 +226,7 @@ export class Session extends BaseSession {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
return new Promise<IChildProcess[]>(resolve => {
|
return new Promise<ChildProcess[]>(resolve => {
|
||||||
windowsProcessTree.getProcessTree(this.truePID, tree => {
|
windowsProcessTree.getProcessTree(this.truePID, tree => {
|
||||||
resolve(tree ? tree.children.map(child => ({
|
resolve(tree ? tree.children.map(child => ({
|
||||||
pid: child.pid,
|
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) => {
|
psNode.lookup({ ppid: this.truePID }, (err, processes) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return reject(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 { Injectable, Inject } from '@angular/core'
|
||||||
import { AppService, Logger, LogService, ConfigService, SplitTabComponent } from 'terminus-core'
|
import { AppService, Logger, LogService, ConfigService, SplitTabComponent } from 'terminus-core'
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
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 { TerminalTabComponent } from '../components/terminalTab.component'
|
||||||
import { UACService } from './uac.service'
|
import { UACService } from './uac.service'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class TerminalService {
|
export class TerminalService {
|
||||||
private shells = new AsyncSubject<IShell[]>()
|
private shells = new AsyncSubject<Shell[]>()
|
||||||
private logger: Logger
|
private logger: Logger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fresh list of all available shells
|
* A fresh list of all available shells
|
||||||
*/
|
*/
|
||||||
get shells$ (): Observable<IShell[]> { return this.shells }
|
get shells$ (): Observable<Shell[]> { return this.shells }
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
constructor (
|
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[]> {
|
async getProfiles (includeHidden?: boolean): Promise<Profile[]> {
|
||||||
const shells = await this.shells$.toPromise()
|
const shells = await this.shells$.toPromise()
|
||||||
return [
|
return [
|
||||||
@@ -47,19 +42,11 @@ export class TerminalService {
|
|||||||
name: shell.name,
|
name: shell.name,
|
||||||
icon: shell.icon,
|
icon: shell.icon,
|
||||||
sessionOptions: this.optionsFromShell(shell),
|
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
|
* Launches a new terminal with a specific shell and CWD
|
||||||
* @param pause Wait for a keypress when the shell exits
|
* @param pause Wait for a keypress when the shell exits
|
||||||
@@ -102,7 +89,7 @@ export class TerminalService {
|
|||||||
return this.openTabWithOptions(sessionOptions)
|
return this.openTabWithOptions(sessionOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
optionsFromShell (shell: IShell): SessionOptions {
|
optionsFromShell (shell: Shell): SessionOptions {
|
||||||
return {
|
return {
|
||||||
command: shell.command,
|
command: shell.command,
|
||||||
args: shell.args || [],
|
args: shell.args || [],
|
||||||
@@ -124,4 +111,17 @@ export class TerminalService {
|
|||||||
{ sessionOptions }
|
{ sessionOptions }
|
||||||
) as TerminalTabComponent
|
) 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 {
|
getFrontend (session?: BaseSession): Frontend {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
const frontend: Frontend = new ({
|
const frontend: Frontend = new {
|
||||||
'xterm': XTermFrontend,
|
xterm: XTermFrontend,
|
||||||
'xterm-webgl': XTermWebGLFrontend,
|
'xterm-webgl': XTermWebGLFrontend,
|
||||||
'hterm': HTermFrontend,
|
hterm: HTermFrontend,
|
||||||
}[this.config.store.terminal.frontend])()
|
}[this.config.store.terminal.frontend]()
|
||||||
frontend.configService = this.config
|
frontend.configService = this.config
|
||||||
frontend.themesService = this.themes
|
frontend.themesService = this.themes
|
||||||
frontend.hotkeysService = this.hotkeys
|
frontend.hotkeysService = this.hotkeys
|
||||||
|
@@ -4,7 +4,7 @@ import { DomSanitizer } from '@angular/platform-browser'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -16,7 +16,7 @@ export class CmderShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ export class CmderShellProvider extends ShellProvider {
|
|||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cmder.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cmder.svg')),
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'cmderps',
|
id: 'cmderps',
|
||||||
@@ -50,7 +50,7 @@ export class CmderShellProvider extends ShellProvider {
|
|||||||
'-noprofile',
|
'-noprofile',
|
||||||
'-noexit',
|
'-noexit',
|
||||||
'-command',
|
'-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')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cmder-powershell.svg')),
|
||||||
env: {},
|
env: {},
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
|
|||||||
import { ConfigService } from 'terminus-core'
|
import { ConfigService } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -13,7 +13,7 @@ export class CustomShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
const args = this.config.store.terminal.customShell.split(' ')
|
const args = this.config.store.terminal.customShell.split(' ')
|
||||||
return [{
|
return [{
|
||||||
id: 'custom',
|
id: 'custom',
|
||||||
|
@@ -4,11 +4,13 @@ import { DomSanitizer } from '@angular/platform-browser'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
|
/* eslint-disable block-scoped-var */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var wnr = require('windows-native-registry') // tslint:disable-line
|
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch { } // tslint:disable-line
|
} catch { }
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -20,7 +22,7 @@ export class Cygwin32ShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,7 @@ export class Cygwin32ShellProvider extends ShellProvider {
|
|||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cygwin.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cygwin.svg')),
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,13 @@ import { DomSanitizer } from '@angular/platform-browser'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
|
/* eslint-disable block-scoped-var */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var wnr = require('windows-native-registry') // tslint:disable-line
|
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch { } // tslint:disable-line
|
} catch { }
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -20,7 +22,7 @@ export class Cygwin64ShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,7 @@ export class Cygwin64ShellProvider extends ShellProvider {
|
|||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cygwin.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/cygwin.svg')),
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,13 @@ import { DomSanitizer } from '@angular/platform-browser'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
|
/* eslint-disable block-scoped-var */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var wnr = require('windows-native-registry') // tslint:disable-line
|
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch { } // tslint:disable-line
|
} catch { }
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -20,7 +22,7 @@ export class GitBashShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -39,11 +41,11 @@ export class GitBashShellProvider extends ShellProvider {
|
|||||||
id: 'git-bash',
|
id: 'git-bash',
|
||||||
name: 'Git-Bash',
|
name: 'Git-Bash',
|
||||||
command: path.join(gitBashPath, 'bin', 'bash.exe'),
|
command: path.join(gitBashPath, 'bin', 'bash.exe'),
|
||||||
args: [ '--login', '-i' ],
|
args: ['--login', '-i'],
|
||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/git-bash.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/git-bash.svg')),
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
|
|||||||
import { HostAppService, Platform, LogService, Logger } from 'terminus-core'
|
import { HostAppService, Platform, LogService, Logger } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -18,7 +18,7 @@ export class LinuxDefaultShellProvider extends ShellProvider {
|
|||||||
this.logger = log.create('linuxDefaultShell')
|
this.logger = log.create('linuxDefaultShell')
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Linux) {
|
if (this.hostApp.platform !== Platform.Linux) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -14,7 +14,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.macOS) {
|
if (this.hostApp.platform !== Platform.macOS) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import { Injectable } from '@angular/core'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -15,7 +15,7 @@ export class POSIXShellsProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform === Platform.Windows) {
|
if (this.hostApp.platform === Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,13 @@ import { Injectable } from '@angular/core'
|
|||||||
import { DomSanitizer } from '@angular/platform-browser'
|
import { DomSanitizer } from '@angular/platform-browser'
|
||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
|
/* eslint-disable block-scoped-var */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var wnr = require('windows-native-registry') // tslint:disable-line
|
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch { } // tslint:disable-line
|
} catch { }
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -18,7 +20,7 @@ export class PowerShellCoreShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -37,7 +39,7 @@ export class PowerShellCoreShellProvider extends ShellProvider {
|
|||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/powershell-core.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/powershell-core.svg')),
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
import { WSLShellProvider } from './wsl'
|
import { WSLShellProvider } from './wsl'
|
||||||
import { PowerShellCoreShellProvider } from './powershellCore'
|
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) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import { DomSanitizer } from '@angular/platform-browser'
|
|||||||
import { HostAppService, Platform, ElectronService } from 'terminus-core'
|
import { HostAppService, Platform, ElectronService } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
import { ShellProvider } from '../api/shellProvider'
|
||||||
import { IShell } from '../api/interfaces'
|
import { Shell } from '../api/interfaces'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -17,7 +17,7 @@ export class WindowsStockShellsProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ export class WindowsStockShellsProvider extends ShellProvider {
|
|||||||
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/powershell.svg')),
|
icon: this.domSanitizer.bypassSecurityTrustHtml(require('../icons/powershell.svg')),
|
||||||
env: {
|
env: {
|
||||||
TERM: 'cygwin',
|
TERM: 'cygwin',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -5,12 +5,14 @@ import { Injectable } from '@angular/core'
|
|||||||
import { HostAppService, Platform } from 'terminus-core'
|
import { HostAppService, Platform } from 'terminus-core'
|
||||||
|
|
||||||
import { ShellProvider } from '../api/shellProvider'
|
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'
|
import { isWindowsBuild, WIN_BUILD_WSL_EXE_DISTRO_FLAG } from '../utils'
|
||||||
|
|
||||||
|
/* eslint-disable block-scoped-var */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var wnr = require('windows-native-registry') // tslint:disable-line
|
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
} catch { } // tslint:disable-line
|
} catch { }
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -21,7 +23,7 @@ export class WSLShellProvider extends ShellProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async provide (): Promise<IShell[]> {
|
async provide (): Promise<Shell[]> {
|
||||||
if (this.hostApp.platform !== Platform.Windows) {
|
if (this.hostApp.platform !== Platform.Windows) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -29,14 +31,14 @@ export class WSLShellProvider extends ShellProvider {
|
|||||||
const bashPath = `${process.env.windir}\\system32\\bash.exe`
|
const bashPath = `${process.env.windir}\\system32\\bash.exe`
|
||||||
const wslPath = `${process.env.windir}\\system32\\wsl.exe`
|
const wslPath = `${process.env.windir}\\system32\\wsl.exe`
|
||||||
|
|
||||||
const shells: IShell[] = [{
|
const shells: Shell[] = [{
|
||||||
id: 'wsl',
|
id: 'wsl',
|
||||||
name: 'WSL / Default distro',
|
name: 'WSL / Default distro',
|
||||||
command: wslPath,
|
command: wslPath,
|
||||||
env: {
|
env: {
|
||||||
TERM: 'xterm-color',
|
TERM: 'xterm-color',
|
||||||
COLORTERM: 'truecolor',
|
COLORTERM: 'truecolor',
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
const lxssPath = 'Software\\Microsoft\\Windows\\CurrentVersion\\Lxss'
|
const lxssPath = 'Software\\Microsoft\\Windows\\CurrentVersion\\Lxss'
|
||||||
@@ -50,13 +52,13 @@ export class WSLShellProvider extends ShellProvider {
|
|||||||
env: {
|
env: {
|
||||||
TERM: 'xterm-color',
|
TERM: 'xterm-color',
|
||||||
COLORTERM: 'truecolor',
|
COLORTERM: 'truecolor',
|
||||||
}
|
},
|
||||||
}]
|
}]
|
||||||
} else {
|
} else {
|
||||||
return []
|
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)
|
const childKey = wnr.getRegistryKey(wnr.HK.CU, lxssPath + '\\' + child)
|
||||||
if (!childKey.DistributionName) {
|
if (!childKey.DistributionName) {
|
||||||
continue
|
continue
|
||||||
@@ -67,11 +69,11 @@ export class WSLShellProvider extends ShellProvider {
|
|||||||
name: `WSL / ${name}`,
|
name: `WSL / ${name}`,
|
||||||
command: wslPath,
|
command: wslPath,
|
||||||
args: ['-d', name],
|
args: ['-d', name],
|
||||||
fsBase: childKey.BasePath.value + '\\rootfs',
|
fsBase: childKey.BasePath.value as string + '\\rootfs',
|
||||||
env: {
|
env: {
|
||||||
TERM: 'xterm-color',
|
TERM: 'xterm-color',
|
||||||
COLORTERM: 'truecolor',
|
COLORTERM: 'truecolor',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
|
|||||||
const profile = {
|
const profile = {
|
||||||
sessionOptions: {
|
sessionOptions: {
|
||||||
...tab.sessionOptions,
|
...tab.sessionOptions,
|
||||||
cwd: (await tab.session.getWorkingDirectory()) || tab.sessionOptions.cwd,
|
cwd: await tab.session.getWorkingDirectory() || tab.sessionOptions.cwd,
|
||||||
},
|
},
|
||||||
name: tab.sessionOptions.command,
|
name: tab.sessionOptions.command,
|
||||||
}
|
}
|
||||||
@@ -35,8 +35,8 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
|
|||||||
]
|
]
|
||||||
this.config.save()
|
this.config.save()
|
||||||
this.toastr.info('Saved')
|
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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user