diff --git a/.all-contributorsrc b/.all-contributorsrc
index e6596af8..6a3ac35c 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -307,6 +307,24 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "matishadow",
+ "name": "Mateusz Tracz",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/9083085?v=4",
+ "profile": "https://about.me/matishadow",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "pinpins",
+ "name": "pinpin",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/36234677?v=4",
+ "profile": "https://zergpool.com",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.travis.yml b/.travis.yml
index cd7a11d4..e8dbc628 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: node_js
-node_js: 11
+node_js: 10
stages:
- Build
diff --git a/README.md b/README.md
index a92be4fc..868ad12c 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Dmitry Pronin 💻 |
Jonathan Beverley 💻 |
Zenghai Liang 💻 |
+ Mateusz Tracz 💻 |
+ pinpin 💻 |
diff --git a/app/lib/app.ts b/app/lib/app.ts
index a1c016c8..10e89b19 100644
--- a/app/lib/app.ts
+++ b/app/lib/app.ts
@@ -139,9 +139,7 @@ export class Application {
handleSecondInstance (argv: string[], cwd: string): void {
this.presentAllWindows()
- for (let window of this.windows) {
- window.handleSecondInstance(argv, cwd)
- }
+ this.windows[this.windows.length - 1].handleSecondInstance(argv, cwd)
}
private setupMenu () {
diff --git a/app/lib/window.ts b/app/lib/window.ts
index cc1d4fac..feed335b 100644
--- a/app/lib/window.ts
+++ b/app/lib/window.ts
@@ -211,9 +211,7 @@ export class Window {
}
handleSecondInstance (argv: string[], cwd: string): void {
- if (!this.configStore.appearance?.dock) {
- this.send('host:second-instance', parseArgs(argv, cwd), cwd)
- }
+ this.send('host:second-instance', parseArgs(argv, cwd), cwd)
}
private setupWindowManagement () {
diff --git a/terminus-core/src/components/tabHeader.component.pug b/terminus-core/src/components/tabHeader.component.pug
index b59dfb63..76447550 100644
--- a/terminus-core/src/components/tabHeader.component.pug
+++ b/terminus-core/src/components/tabHeader.component.pug
@@ -1,7 +1,7 @@
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
-.index(
+.index(*ngIf='!config.store.terminal.hideTabIndex',
#handle,
[style.background-color]='tab.color',
) {{index + 1}}
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
-button((click)='app.closeTab(tab, true)') ×
+button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') ×
diff --git a/terminus-core/src/components/tabHeader.component.ts b/terminus-core/src/components/tabHeader.component.ts
index b617149d..3df6a932 100644
--- a/terminus-core/src/components/tabHeader.component.ts
+++ b/terminus-core/src/components/tabHeader.component.ts
@@ -9,6 +9,7 @@ import { HotkeysService } from '../services/hotkeys.service'
import { ElectronService } from '../services/electron.service'
import { AppService } from '../services/app.service'
import { HostAppService, Platform } from '../services/hostApp.service'
+import { ConfigService } from '../services/config.service'
/** @hidden */
export interface SortableComponentProxy {
@@ -31,6 +32,7 @@ export class TabHeaderComponent {
private constructor (
public app: AppService,
+ public config: ConfigService,
private electron: ElectronService,
private hostApp: HostAppService,
private ngbModal: NgbModal,
diff --git a/terminus-ssh/package.json b/terminus-ssh/package.json
index bf0f2bcf..673d5328 100644
--- a/terminus-ssh/package.json
+++ b/terminus-ssh/package.json
@@ -23,6 +23,7 @@
"@types/ssh2": "^0.5.35",
"ansi-colors": "^4.1.1",
"cli-spinner": "^0.2.10",
+ "run-script-os": "^1.1.3",
"ssh2": "^0.8.2",
"ssh2-streams": "Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5",
"sshpk": "^1.16.1",
diff --git a/terminus-ssh/src/api.ts b/terminus-ssh/src/api.ts
index 34a424ba..4e9d662b 100644
--- a/terminus-ssh/src/api.ts
+++ b/terminus-ssh/src/api.ts
@@ -246,7 +246,7 @@ export class SSHSession extends BaseSession {
fw.targetPort,
(err, stream) => {
if (err) {
- this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwaded connection via ${fw}: ${err}`)
+ this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwarded connection via ${fw}: ${err}`)
socket.destroy()
return
}
@@ -263,7 +263,7 @@ export class SSHSession extends BaseSession {
}
)
}).then(() => {
- this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwaded ${fw}`)
+ this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwarded ${fw}`)
this.forwardedPorts.push(fw)
}).catch(e => {
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Failed to forward port ${fw}: ${e}`)
@@ -280,7 +280,7 @@ export class SSHSession extends BaseSession {
resolve()
})
})
- this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwaded ${fw}`)
+ this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwarded ${fw}`)
this.forwardedPorts.push(fw)
}
}
diff --git a/terminus-ssh/src/services/ssh.service.ts b/terminus-ssh/src/services/ssh.service.ts
index d65658d2..ee8098c3 100644
--- a/terminus-ssh/src/services/ssh.service.ts
+++ b/terminus-ssh/src/services/ssh.service.ts
@@ -22,6 +22,11 @@ try {
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { }
+
+// eslint-disable-next-line @typescript-eslint/no-type-alias
+export type SSHLogCallback = (message: string) => void
+
+
@Injectable({ providedIn: 'root' })
export class SSHService {
private logger: Logger
@@ -46,33 +51,24 @@ export class SSHService {
return session
}
- async connectSession (session: SSHSession, logCallback?: (s: any) => void): Promise {
+ async loadPrivateKeyForSession (session: SSHSession, logCallback?: SSHLogCallback): Promise {
let privateKey: string|null = null
let privateKeyPath = session.connection.privateKey
- if (!logCallback) {
- logCallback = () => null
- }
-
- const log = (s: any) => {
- logCallback!(s)
- this.logger.info(s)
- }
-
if (!privateKeyPath) {
const userKeyPath = path.join(process.env.HOME as string, '.ssh', 'id_rsa')
if (await fs.exists(userKeyPath)) {
- log('Using user\'s default private key')
+ logCallback?.('Using user\'s default private key')
privateKeyPath = userKeyPath
}
}
if (privateKeyPath) {
- log('Loading private key from ' + colors.bgWhite.blackBright(' ' + privateKeyPath + ' '))
+ logCallback?.('Loading private key from ' + colors.bgWhite.blackBright(' ' + privateKeyPath + ' '))
try {
privateKey = (await fs.readFile(privateKeyPath)).toString()
} catch (error) {
- log(colors.bgRed.black(' X ') + 'Could not read the private key file')
+ logCallback?.(colors.bgRed.black(' X ') + 'Could not read the private key file')
this.toastr.error('Could not read the private key file')
}
@@ -83,7 +79,7 @@ export class SSHService {
} catch (e) {
if (e instanceof sshpk.KeyEncryptedError) {
const modal = this.ngbModal.open(PromptModalComponent)
- log(colors.bgYellow.yellow.black(' ! ') + ' Key requires passphrase')
+ logCallback?.(colors.bgYellow.yellow.black(' ! ') + ' Key requires passphrase')
modal.componentInstance.prompt = 'Private key passphrase'
modal.componentInstance.password = true
let passphrase = ''
@@ -131,6 +127,20 @@ export class SSHService {
fs.unlink(temp.path)
}
}
+ return privateKey
+ }
+
+ async connectSession (session: SSHSession, logCallback?: SSHLogCallback): Promise {
+ if (!logCallback) {
+ logCallback = () => null
+ }
+
+ const log = (s: any) => {
+ logCallback!(s)
+ this.logger.info(s)
+ }
+
+ let privateKey: string|null = null
const ssh = new Client()
session.ssh = ssh
@@ -213,6 +223,7 @@ export class SSHService {
const authMethodsLeft = ['none']
if (!session.connection.auth || session.connection.auth === 'publicKey') {
+ privateKey = await this.loadPrivateKeyForSession(session, log)
if (!privateKey) {
log('\r\nPrivate key auth selected, but no key is loaded\r\n')
} else {
diff --git a/terminus-ssh/yarn.lock b/terminus-ssh/yarn.lock
index 392a7188..c0387d7d 100644
--- a/terminus-ssh/yarn.lock
+++ b/terminus-ssh/yarn.lock
@@ -157,6 +157,11 @@ rimraf@~2.6.2:
dependencies:
glob "^7.1.3"
+run-script-os@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.3.tgz#1069b418307f4fd36ff056b5eda309c273fca8b0"
+ integrity sha512-xPlzE6533nvWVea5z7e5J7+JAIepfpxTu/HLGxcjJYlemVukOCWJBaRCod/DWXJFRIWEFOgSGbjd2m1QWTJi5w==
+
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
diff --git a/terminus-terminal/src/api/baseTerminalTab.component.ts b/terminus-terminal/src/api/baseTerminalTab.component.ts
index 16bd9dc5..15e9f7d5 100644
--- a/terminus-terminal/src/api/baseTerminalTab.component.ts
+++ b/terminus-terminal/src/api/baseTerminalTab.component.ts
@@ -156,16 +156,28 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.resetZoom()
break
case 'previous-word':
- this.sendInput('\x1bb')
+ this.sendInput({
+ [Platform.Windows]: '\x1b[1;5D',
+ [Platform.macOS]: '\x1bb',
+ [Platform.Linux]: '\x1bb',
+ }[this.hostApp.platform])
break
case 'next-word':
- this.sendInput('\x1bf')
+ this.sendInput({
+ [Platform.Windows]: '\x1b[1;5C',
+ [Platform.macOS]: '\x1bf',
+ [Platform.Linux]: '\x1bf',
+ }[this.hostApp.platform])
break
case 'delete-previous-word':
this.sendInput('\x1b\x7f')
break
case 'delete-next-word':
- this.sendInput('\x1bd')
+ this.sendInput({
+ [Platform.Windows]: '\x1bd\x1b[3;5~',
+ [Platform.macOS]: '\x1bd',
+ [Platform.Linux]: '\x1bd',
+ }[this.hostApp.platform])
break
case 'search':
this.showSearchPanel = true
diff --git a/terminus-terminal/src/components/appearanceSettingsTab.component.pug b/terminus-terminal/src/components/appearanceSettingsTab.component.pug
index 90eb2c6e..9cb61770 100644
--- a/terminus-terminal/src/components/appearanceSettingsTab.component.pug
+++ b/terminus-terminal/src/components/appearanceSettingsTab.component.pug
@@ -109,6 +109,24 @@ h3.mb-3 Appearance
(ngModelChange)='config.save()',
)
+.form-line
+ .header
+ .title Hide tab index
+
+ toggle(
+ [(ngModel)]='config.store.terminal.hideTabIndex',
+ (ngModelChange)='config.save();',
+ )
+
+.form-line
+ .header
+ .title Hide tab close button
+
+ toggle(
+ [(ngModel)]='config.store.terminal.hideCloseButton',
+ (ngModelChange)='config.save();',
+ )
+
.form-line
.header
.title Fallback font
diff --git a/terminus-terminal/src/components/searchPanel.component.pug b/terminus-terminal/src/components/searchPanel.component.pug
index d8077ab7..f886a186 100644
--- a/terminus-terminal/src/components/searchPanel.component.pug
+++ b/terminus-terminal/src/components/searchPanel.component.pug
@@ -26,7 +26,7 @@ button.btn.btn-link(
.mr-2
button.btn.btn-link(
- (click)='options.caseSensitive = !options.caseSensitive',
+ (click)='options.caseSensitive = !options.caseSensitive; saveSearchOptions()',
[class.active]='options.caseSensitive',
ngbTooltip='Case sensitivity',
placement='bottom'
@@ -34,14 +34,14 @@ button.btn.btn-link(
i.fa.fa-fw.fa-font
button.btn.btn-link(
- (click)='options.regex = !options.regex',
+ (click)='options.regex = !options.regex; saveSearchOptions()',
[class.active]='options.regex',
ngbTooltip='Regular expression',
placement='bottom'
)
i.fa.fa-fw.fa-asterisk
button.btn.btn-link(
- (click)='options.wholeWord = !options.wholeWord',
+ (click)='options.wholeWord = !options.wholeWord; saveSearchOptions()',
[class.active]='options.wholeWord',
ngbTooltip='Whole word',
placement='bottom'
diff --git a/terminus-terminal/src/components/searchPanel.component.ts b/terminus-terminal/src/components/searchPanel.component.ts
index 2e9a8dae..25bbdbb6 100644
--- a/terminus-terminal/src/components/searchPanel.component.ts
+++ b/terminus-terminal/src/components/searchPanel.component.ts
@@ -1,6 +1,7 @@
import { Component, Input, Output, EventEmitter } from '@angular/core'
import { ToastrService } from 'ngx-toastr'
import { Frontend, SearchOptions } from '../frontends/frontend'
+import { ConfigService } from 'terminus-core'
@Component({
selector: 'search-panel',
@@ -13,12 +14,14 @@ export class SearchPanelComponent {
notFound = false
options: SearchOptions = {
incremental: true,
+ ...this.config.store.terminal.searchOptions,
}
@Output() close = new EventEmitter()
constructor (
private toastr: ToastrService,
+ public config: ConfigService,
) { }
onQueryChange (): void {
@@ -45,4 +48,12 @@ export class SearchPanelComponent {
this.toastr.error('Not found')
}
}
+
+ saveSearchOptions (): void {
+ this.config.store.terminal.searchOptions.regex = this.options.regex
+ this.config.store.terminal.searchOptions.caseSensitive = this.options.caseSensitive
+ this.config.store.terminal.searchOptions.wholeWord = this.options.wholeWord
+
+ this.config.save()
+ }
}
diff --git a/terminus-terminal/src/components/terminalSettingsTab.component.pug b/terminus-terminal/src/components/terminalSettingsTab.component.pug
index 481f1121..3314af10 100644
--- a/terminus-terminal/src/components/terminalSettingsTab.component.pug
+++ b/terminus-terminal/src/components/terminalSettingsTab.component.pug
@@ -116,7 +116,7 @@ h3.mb-3 Terminal
[(ngModel)]='config.store.terminal.scrollOnInput',
(ngModelChange)='config.save()',
)
-
+
.form-line
.header
.title Use Alt key as the Meta key
diff --git a/terminus-terminal/src/config.ts b/terminus-terminal/src/config.ts
index 9511981d..ae7a500b 100644
--- a/terminus-terminal/src/config.ts
+++ b/terminus-terminal/src/config.ts
@@ -23,6 +23,8 @@ export class TerminalConfigProvider extends ConfigProvider {
ligatures: false,
cursor: 'block',
cursorBlink: true,
+ hideTabIndex: false,
+ hideCloseButton: false,
customShell: '',
rightClick: 'menu',
pasteOnMiddleClick: true,
@@ -65,6 +67,12 @@ export class TerminalConfigProvider extends ConfigProvider {
recoverTabs: true,
warnOnMultilinePaste: true,
showDefaultProfiles: true,
+ searchRegexAlwaysEnabled: false,
+ searchOptions: {
+ regex: false,
+ wholeWord: false,
+ caseSensitive: false,
+ },
},
}