mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-23 20:08:01 +00:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
467684d9ab | ||
![]() |
5069070040 | ||
![]() |
ecf5297bc3 | ||
![]() |
78bd90ac55 | ||
![]() |
712589eb93 | ||
![]() |
f103e71285 | ||
![]() |
0cf883cc4a | ||
![]() |
2b0ad0d558 | ||
![]() |
67bacb9dd3 | ||
![]() |
d0a597634d | ||
![]() |
322014c409 | ||
![]() |
c751a8725b | ||
![]() |
5417efe558 | ||
![]() |
bf356fcd19 | ||
![]() |
10ee66b9dd | ||
![]() |
763da0d80c | ||
![]() |
8d46bb2181 | ||
![]() |
fe936c7726 | ||
![]() |
2f3e32990a | ||
![]() |
22344f8d54 | ||
![]() |
f6d37a39f4 | ||
![]() |
0e4c60ad4b | ||
![]() |
e8c2171d8f | ||
![]() |
f7a5be2c67 |
5
.github/workflows/macos.yml
vendored
5
.github/workflows/macos.yml
vendored
@@ -7,7 +7,9 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
electron_setup_cmd: 'true'
|
||||
- arch: arm64
|
||||
electron_setup_cmd: 'yarn add -D electron@11.1.1'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -27,6 +29,9 @@ jobs:
|
||||
rm app/node_modules/.yarn-integrity
|
||||
yarn
|
||||
|
||||
- name: Upgrade Electron for ARM builds
|
||||
run: ${{ matrix.electron_setup_cmd }}
|
||||
|
||||
- name: Build native deps
|
||||
run: scripts/build-native.js
|
||||
env:
|
||||
|
@@ -1,4 +1,4 @@
|
||||
****
|
||||

|
||||
|
||||
|
||||
<p align="center">
|
||||
|
@@ -75,7 +75,7 @@ export class Application {
|
||||
}
|
||||
|
||||
onGlobalHotkey (): void {
|
||||
if (this.windows.some(x => x.isFocused())) {
|
||||
if (this.windows.some(x => x.isFocused() && x.isVisible())) {
|
||||
for (const window of this.windows) {
|
||||
window.hide()
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export class Application {
|
||||
|
||||
handleSecondInstance (argv: string[], cwd: string): void {
|
||||
this.presentAllWindows()
|
||||
this.windows[this.windows.length - 1].handleSecondInstance(argv, cwd)
|
||||
this.windows[this.windows.length - 1].passCliArguments(argv, cwd, true)
|
||||
}
|
||||
|
||||
private setupMenu () {
|
||||
|
@@ -53,7 +53,7 @@ if (argv.d) {
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
app.on('ready', async () => {
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock.setMenu(Menu.buildFromTemplate([
|
||||
{
|
||||
@@ -65,5 +65,8 @@ app.on('ready', () => {
|
||||
]))
|
||||
}
|
||||
application.init()
|
||||
application.newWindow({ hidden: argv.hidden })
|
||||
|
||||
const window = await application.newWindow({ hidden: argv.hidden })
|
||||
await window.ready
|
||||
window.passCliArguments(process.argv, process.cwd(), false)
|
||||
})
|
||||
|
@@ -65,6 +65,7 @@ export class Window {
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
maximizable: true,
|
||||
frame: false,
|
||||
show: false,
|
||||
backgroundColor: '#00000000',
|
||||
@@ -190,6 +191,10 @@ export class Window {
|
||||
return this.window.isFocused()
|
||||
}
|
||||
|
||||
isVisible (): boolean {
|
||||
return this.window.isVisible()
|
||||
}
|
||||
|
||||
hide (): void {
|
||||
if (process.platform === 'darwin') {
|
||||
// Lose focus
|
||||
@@ -225,8 +230,8 @@ export class Window {
|
||||
}
|
||||
}
|
||||
|
||||
handleSecondInstance (argv: string[], cwd: string): void {
|
||||
this.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
||||
passCliArguments (argv: string[], cwd: string, secondInstance: boolean): void {
|
||||
this.send('cli', parseArgs(argv, cwd), cwd, secondInstance)
|
||||
}
|
||||
|
||||
private setupWindowManagement () {
|
||||
|
@@ -21,7 +21,7 @@
|
||||
"@angular/platform-browser": "^9.1.9",
|
||||
"@angular/platform-browser-dynamic": "^9.1.9",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.1.0",
|
||||
"@terminus-term/node-pty": "0.10.0-beta11",
|
||||
"@terminus-term/node-pty": "0.10.0-beta10",
|
||||
"electron-config": "2.0.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-is-dev": "1.1.0",
|
||||
|
@@ -117,12 +117,12 @@
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
|
||||
"@terminus-term/node-pty@0.10.0-beta11":
|
||||
version "0.10.0-beta11"
|
||||
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta11.tgz#9e83041bc0644f17a4329c41a13d09575653c544"
|
||||
integrity sha512-ZrplD84UNV8qpCNpsG8DAo8fmoRP1Ynb9QsApIleCJjbeNvUP2ZAeR6wo0aV9B7Zqjvq5O4hoUSayIDGtORH+A==
|
||||
"@terminus-term/node-pty@0.10.0-beta10":
|
||||
version "0.10.0-beta10"
|
||||
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta10.tgz#de9dade3d7549d44b0906ec0d0b9e1bb411f1f21"
|
||||
integrity sha512-j9RJk7sD/es4vR6+AR5M/p3SicVxY6kZEeE0UQKhHNcaAla90/mcGeBNicAWPaAkjO1uQZVbYh5cJMMu5unQgA==
|
||||
dependencies:
|
||||
node-addon-api "^3.0.2"
|
||||
nan "^2.14.0"
|
||||
|
||||
"@types/mz@0.0.32":
|
||||
version "0.0.32"
|
||||
@@ -716,7 +716,7 @@ debug@^4.0.1, debug@^4.1.1, debug@^4.3.1:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debuglog@^1.0.1:
|
||||
debuglog@*, debuglog@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz"
|
||||
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
|
||||
@@ -1377,7 +1377,7 @@ import-lazy@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
|
||||
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
|
||||
|
||||
imurmurhash@^0.1.4:
|
||||
imurmurhash@*, imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
@@ -1405,7 +1405,7 @@ inherits@2.0.3:
|
||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
||||
ini@^1.3.4, ini@^1.3.5, ini@^1.3.8, ini@~1.3.0:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
@@ -1854,6 +1854,11 @@ lockfile@^1.0.4:
|
||||
dependencies:
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
lodash._baseindexof@*:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
|
||||
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
|
||||
|
||||
lodash._baseuniq@~4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
|
||||
@@ -1862,11 +1867,33 @@ lodash._baseuniq@~4.6.0:
|
||||
lodash._createset "~4.0.0"
|
||||
lodash._root "~3.0.0"
|
||||
|
||||
lodash._bindcallback@*:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
|
||||
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
|
||||
|
||||
lodash._cacheindexof@*:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
|
||||
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
|
||||
|
||||
lodash._createcache@*:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
|
||||
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
|
||||
dependencies:
|
||||
lodash._getnative "^3.0.0"
|
||||
|
||||
lodash._createset@~4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
|
||||
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
|
||||
|
||||
lodash._getnative@*, lodash._getnative@^3.0.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
|
||||
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
|
||||
|
||||
lodash._root@~3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
|
||||
@@ -1877,6 +1904,11 @@ lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||
|
||||
lodash.restparam@*:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
|
||||
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
|
||||
|
||||
lodash.union@~4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
|
||||
@@ -2073,7 +2105,7 @@ mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
nan@^2.13.2, nan@^2.14.2:
|
||||
nan@^2.13.2, nan@^2.14.0, nan@^2.14.2:
|
||||
version "2.14.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
@@ -2270,9 +2302,9 @@ npm-user-validate@^1.0.1:
|
||||
integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==
|
||||
|
||||
npm@6:
|
||||
version "6.14.10"
|
||||
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.10.tgz#f45c8e4244294ba793770f2ab0e9ce2d0b93fd29"
|
||||
integrity sha512-FT23Qy/JMA+qxEYReMOr1MY7642fKn8Onn+72LASPi872Owvmw0svm+/DXTHOC3yO9CheEO+EslyXEpdBdRtIA==
|
||||
version "6.14.11"
|
||||
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.11.tgz#e0b5598d7b9a42d275e61d8bd28cd7eee0074a3b"
|
||||
integrity sha512-1Zh7LjuIoEhIyjkBflSSGzfjuPQwDlghNloppjruOH5bmj9midT9qcNT0tRUZRR04shU9ekrxNy9+UTBrqeBpQ==
|
||||
dependencies:
|
||||
JSONStream "^1.3.5"
|
||||
abbrev "~1.1.1"
|
||||
@@ -2292,6 +2324,7 @@ npm@6:
|
||||
cmd-shim "^3.0.3"
|
||||
columnify "~1.5.4"
|
||||
config-chain "^1.1.12"
|
||||
debuglog "*"
|
||||
detect-indent "~5.0.0"
|
||||
detect-newline "^2.1.0"
|
||||
dezalgo "~1.0.3"
|
||||
@@ -2306,10 +2339,11 @@ npm@6:
|
||||
has-unicode "~2.0.1"
|
||||
hosted-git-info "^2.8.8"
|
||||
iferr "^1.0.2"
|
||||
imurmurhash "*"
|
||||
infer-owner "^1.0.4"
|
||||
inflight "~1.0.6"
|
||||
inherits "^2.0.4"
|
||||
ini "^1.3.5"
|
||||
ini "^1.3.8"
|
||||
init-package-json "^1.10.3"
|
||||
is-cidr "^3.0.0"
|
||||
json-parse-better-errors "^1.0.2"
|
||||
@@ -2324,8 +2358,14 @@ npm@6:
|
||||
libnpx "^10.2.4"
|
||||
lock-verify "^2.1.0"
|
||||
lockfile "^1.0.4"
|
||||
lodash._baseindexof "*"
|
||||
lodash._baseuniq "~4.6.0"
|
||||
lodash._bindcallback "*"
|
||||
lodash._cacheindexof "*"
|
||||
lodash._createcache "*"
|
||||
lodash._getnative "*"
|
||||
lodash.clonedeep "~4.5.0"
|
||||
lodash.restparam "*"
|
||||
lodash.union "~4.6.0"
|
||||
lodash.uniq "~4.5.0"
|
||||
lodash.without "~4.4.0"
|
||||
@@ -3025,16 +3065,16 @@ rxjs@^6.5.5:
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.2.0:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3.0.0", 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"
|
||||
|
@@ -17,7 +17,7 @@
|
||||
"core-js": "^3.8.1",
|
||||
"cross-env": "7.0.2",
|
||||
"css-loader": "3.4.2",
|
||||
"electron": "^11.1.1",
|
||||
"electron": "12.0.0-beta.16",
|
||||
"electron-builder": "22.10.4",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-installer-snap": "^5.1.0",
|
||||
|
@@ -1,14 +1,14 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal'
|
||||
|
||||
const schemeContents = require.context('../schemes/', true, /.*/)
|
||||
const schemeContents = require.context('../schemes/', false, /.*/)
|
||||
|
||||
@Injectable()
|
||||
export class ColorSchemes extends TerminalColorSchemeProvider {
|
||||
async getSchemes (): Promise<TerminalColorScheme[]> {
|
||||
const schemes: TerminalColorScheme[] = []
|
||||
|
||||
schemeContents.keys().forEach(schemeFile => {
|
||||
schemeContents.keys().filter(x => !x.startsWith('./')).forEach(schemeFile => {
|
||||
const lines = (schemeContents(schemeFile).default as string).split('\n')
|
||||
|
||||
// process #define variables
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import type { BrowserWindow, TouchBar, MenuItemConstructorOptions } from 'electron'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'mz/fs'
|
||||
import shellEscape from 'shell-escape'
|
||||
import { Observable, Subject } from 'rxjs'
|
||||
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
||||
@@ -150,9 +151,10 @@ export class HostAppService {
|
||||
this.zone.run(() => this.displaysChanged.next())
|
||||
})
|
||||
|
||||
electron.ipcRenderer.on('host:second-instance', (_$event, argv: any, cwd: string) => this.zone.run(() => {
|
||||
electron.ipcRenderer.on('cli', (_$event, argv: any, cwd: string, secondInstance: boolean) => this.zone.run(async () => {
|
||||
this.logger.info('Second instance', argv)
|
||||
const op = argv._[0]
|
||||
const opAsPath = path.resolve(cwd, op)
|
||||
if (op === 'open') {
|
||||
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
|
||||
} else if (op === 'run') {
|
||||
@@ -167,7 +169,11 @@ export class HostAppService {
|
||||
this.cliOpenProfile.next(argv.profileName)
|
||||
} else if (op === undefined) {
|
||||
this.newWindow()
|
||||
} else {
|
||||
} else if ((await fs.lstat(opAsPath)).isDirectory()) {
|
||||
this.cliOpenDirectory.next(opAsPath)
|
||||
}
|
||||
|
||||
if (secondInstance) {
|
||||
this.secondInstance.next()
|
||||
}
|
||||
}))
|
||||
|
@@ -124,9 +124,9 @@ colorspace@1.1.x:
|
||||
text-hex "1.0.x"
|
||||
|
||||
core-js@^3.1.2:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.7.0.tgz#b0a761a02488577afbf97179e4681bf49568520f"
|
||||
integrity sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA==
|
||||
version "3.8.2"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.2.tgz#0a1fd6709246da9ca8eff5bb0cbd15fba9ac7044"
|
||||
integrity sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
|
@@ -131,6 +131,10 @@ export class SerialSession extends BaseSession {
|
||||
this.kill('TERM')
|
||||
}
|
||||
|
||||
supportsWorkingDirectory (): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
async getWorkingDirectory (): Promise<string|null> {
|
||||
return null
|
||||
}
|
||||
|
@@ -363,6 +363,10 @@ export class SSHSession extends BaseSession {
|
||||
this.kill('TERM')
|
||||
}
|
||||
|
||||
supportsWorkingDirectory (): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
async getWorkingDirectory (): Promise<string|null> {
|
||||
return null
|
||||
}
|
||||
@@ -398,3 +402,9 @@ export interface SSHConnectionGroup {
|
||||
name: string
|
||||
connections: SSHConnection[]
|
||||
}
|
||||
|
||||
export const ALGORITHM_BLACKLIST = [
|
||||
// cause native crashes in node crypto, use EC instead
|
||||
'diffie-hellman-group-exchange-sha256',
|
||||
'diffie-hellman-group-exchange-sha1',
|
||||
]
|
||||
|
@@ -103,7 +103,7 @@
|
||||
.header
|
||||
.title Jump host
|
||||
select.form-control([(ngModel)]='connection.jumpHost')
|
||||
option([ngValue]='null') None
|
||||
option(value='') None
|
||||
option([ngValue]='x.name', *ngFor='let x of config.store.ssh.connections') {{x.name}}
|
||||
|
||||
.form-line
|
||||
|
@@ -3,7 +3,7 @@ import { Component } from '@angular/core'
|
||||
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ElectronService, HostAppService, ConfigService } from 'terminus-core'
|
||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||
import { SSHConnection, LoginScript, SSHAlgorithmType } from '../api'
|
||||
import { SSHConnection, LoginScript, SSHAlgorithmType, ALGORITHM_BLACKLIST } from '../api'
|
||||
import { PromptModalComponent } from './promptModal.component'
|
||||
import { ALGORITHMS } from 'ssh2-streams/lib/constants'
|
||||
|
||||
@@ -40,8 +40,8 @@ export class EditConnectionModalComponent {
|
||||
[SSHAlgorithmType.CIPHER]: 'CIPHER',
|
||||
[SSHAlgorithmType.HMAC]: 'HMAC',
|
||||
}[k]
|
||||
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg]
|
||||
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg]
|
||||
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
||||
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,9 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
session?: SSHSession
|
||||
private sessionStack: SSHSession[] = []
|
||||
private homeEndSubscription: Subscription
|
||||
private recentInputs = ''
|
||||
private reconnectOffered = false
|
||||
private sessionHandlers: Subscription[] = []
|
||||
|
||||
constructor (
|
||||
injector: Injector,
|
||||
@@ -57,6 +60,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
|
||||
this.frontendReady$.pipe(first()).subscribe(() => {
|
||||
this.initializeSession()
|
||||
this.input$.subscribe(data => {
|
||||
this.recentInputs += data
|
||||
this.recentInputs = this.recentInputs.substring(this.recentInputs.length - 32)
|
||||
})
|
||||
})
|
||||
|
||||
super.ngOnInit()
|
||||
@@ -68,12 +75,19 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
|
||||
async setupOneSession (session: SSHSession): Promise<void> {
|
||||
if (session.connection.jumpHost) {
|
||||
const jumpConnection = this.config.store.ssh.connections.find(x => x.name === session.connection.jumpHost)
|
||||
const jumpConnection: SSHConnection|null = this.config.store.ssh.connections.find(x => x.name === session.connection.jumpHost)
|
||||
|
||||
if (!jumpConnection) {
|
||||
throw new Error(`${session.connection.host}: jump host "${session.connection.jumpHost}" not found in your config`)
|
||||
}
|
||||
|
||||
const jumpSession = this.ssh.createSession(jumpConnection)
|
||||
|
||||
await this.setupOneSession(jumpSession)
|
||||
|
||||
jumpSession.destroyed$.subscribe(() => session.destroy())
|
||||
this.sessionHandlers.push(
|
||||
jumpSession.destroyed$.subscribe(() => session.destroy())
|
||||
)
|
||||
|
||||
session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
|
||||
'127.0.0.1', 0, session.connection.host, session.connection.port ?? 22,
|
||||
@@ -93,15 +107,31 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
this.sessionStack.push(session)
|
||||
}
|
||||
|
||||
|
||||
session.serviceMessage$.subscribe(msg => {
|
||||
this.sessionHandlers.push(session.serviceMessage$.subscribe(msg => {
|
||||
this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`)
|
||||
session.resize(this.size.columns, this.size.rows)
|
||||
})
|
||||
}))
|
||||
|
||||
session.destroyed$.subscribe(() => {
|
||||
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` ${session.connection.host}: session closed\r\n`)
|
||||
})
|
||||
this.sessionHandlers.push(session.destroyed$.subscribe(() => {
|
||||
if (
|
||||
// Ctrl-D
|
||||
this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 ||
|
||||
this.recentInputs.endsWith('exit\r')
|
||||
) {
|
||||
// User closed the session
|
||||
this.destroy()
|
||||
} else {
|
||||
// Session was closed abruptly
|
||||
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` ${session.connection.host}: session closed\r\n`)
|
||||
if (!this.reconnectOffered) {
|
||||
this.reconnectOffered = true
|
||||
this.write('Press any key to reconnect\r\n')
|
||||
this.input$.pipe(first()).subscribe(() => {
|
||||
this.reconnect()
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` Connecting to ${session.connection.host}\r\n`)
|
||||
|
||||
@@ -129,6 +159,7 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
}
|
||||
|
||||
async initializeSession (): Promise<void> {
|
||||
this.reconnectOffered = false
|
||||
if (!this.connection) {
|
||||
this.logger.error('No SSH connection info supplied')
|
||||
return
|
||||
@@ -136,7 +167,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
|
||||
this.session = this.ssh.createSession(this.connection)
|
||||
|
||||
await this.setupOneSession(this.session)
|
||||
try {
|
||||
await this.setupOneSession(this.session)
|
||||
} catch (e) {
|
||||
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
|
||||
}
|
||||
|
||||
this.attachSessionHandlers()
|
||||
|
||||
@@ -158,6 +193,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
|
||||
}
|
||||
|
||||
async reconnect (): Promise<void> {
|
||||
for (const s of this.sessionHandlers) {
|
||||
s.unsubscribe()
|
||||
}
|
||||
this.sessionHandlers = []
|
||||
this.session?.destroy()
|
||||
await this.initializeSession()
|
||||
this.session?.releaseInitialDataBuffer()
|
||||
|
@@ -12,7 +12,7 @@ import * as sshpk from 'sshpk'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { HostAppService, Platform, Logger, LogService, ElectronService, AppService, SelectorOption, ConfigService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SSHConnection, SSHSession } from '../api'
|
||||
import { ALGORITHM_BLACKLIST, SSHConnection, SSHSession } from '../api'
|
||||
import { PromptModalComponent } from '../components/promptModal.component'
|
||||
import { PasswordStorageService } from './passwordStorage.service'
|
||||
import { SSHTabComponent } from '../components/sshTab.component'
|
||||
@@ -147,6 +147,10 @@ export class SSHService {
|
||||
session.ssh = ssh
|
||||
let connected = false
|
||||
let savedPassword: string|null = null
|
||||
const algorithms = {}
|
||||
for (const key of Object.keys(session.connection.algorithms ?? {})) {
|
||||
algorithms[key] = session.connection.algorithms![key].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
||||
}
|
||||
await new Promise(async (resolve, reject) => {
|
||||
ssh.on('ready', () => {
|
||||
connected = true
|
||||
@@ -258,7 +262,7 @@ export class SSHService {
|
||||
tryKeyboard: true,
|
||||
agent: agent ?? undefined,
|
||||
agentForward: session.connection.agentForward && !!agent,
|
||||
keepaliveInterval: session.connection.keepaliveInterval,
|
||||
keepaliveInterval: session.connection.keepaliveInterval ?? 15000,
|
||||
keepaliveCountMax: session.connection.keepaliveCountMax,
|
||||
readyTimeout: session.connection.readyTimeout,
|
||||
hostVerifier: (digest: string) => {
|
||||
@@ -267,7 +271,7 @@ export class SSHService {
|
||||
return true
|
||||
},
|
||||
hostHash: 'sha256' as any,
|
||||
algorithms: session.connection.algorithms,
|
||||
algorithms,
|
||||
sock: session.jumpStream,
|
||||
authHandler: methodsLeft => {
|
||||
while (true) {
|
||||
|
@@ -18,6 +18,7 @@ import { TerminalDecorator } from './decorator'
|
||||
/** @hidden */
|
||||
export interface ToastrServiceProxy {
|
||||
info: (_: string) => void
|
||||
error: (_: string) => void
|
||||
}
|
||||
/**
|
||||
* A class to base your custom terminal tabs on
|
||||
@@ -140,7 +141,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.logger = this.log.create('baseTerminalTab')
|
||||
this.setTitle('Terminal')
|
||||
|
||||
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => {
|
||||
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(async hotkey => {
|
||||
if (!this.hasFocus) {
|
||||
return
|
||||
}
|
||||
@@ -207,6 +208,9 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
case 'pane-focus-all':
|
||||
this.focusAllPanes()
|
||||
break
|
||||
case 'copy-current-path':
|
||||
this.copyCurrentPath()
|
||||
break
|
||||
}
|
||||
})
|
||||
this.bellPlayer = document.createElement('audio')
|
||||
@@ -438,6 +442,19 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
}
|
||||
}
|
||||
|
||||
async copyCurrentPath (): Promise<void> {
|
||||
let cwd: string|null = null
|
||||
if (this.session?.supportsWorkingDirectory()) {
|
||||
cwd = await this.session.getWorkingDirectory()
|
||||
}
|
||||
if (cwd) {
|
||||
this.electron.clipboard.writeText(cwd)
|
||||
this.toastr.info('Copied')
|
||||
} else {
|
||||
this.toastr.error('Shell does not support current path detection')
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
ngOnDestroy (): void {
|
||||
this.frontend?.detach(this.content.nativeElement)
|
||||
|
@@ -4,6 +4,7 @@ import { ConfigProvider, Platform } from 'terminus-core'
|
||||
export class TerminalConfigProvider extends ConfigProvider {
|
||||
defaults = {
|
||||
hotkeys: {
|
||||
'copy-current-path': [],
|
||||
shell: {
|
||||
__nonStructural: true,
|
||||
},
|
||||
|
@@ -62,6 +62,10 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
||||
id: 'ctrl-c',
|
||||
name: 'Intelligent Ctrl-C (copy/abort)',
|
||||
},
|
||||
{
|
||||
id: 'copy-current-path',
|
||||
name: 'Copy current path',
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
name: 'Search',
|
||||
|
@@ -194,6 +194,9 @@ export default class TerminalModule { // eslint-disable-line @typescript-eslint/
|
||||
})
|
||||
|
||||
hostApp.cliOpenDirectory$.subscribe(async directory => {
|
||||
if (directory.length > 1 && (directory.endsWith('/') || directory.endsWith('\\'))) {
|
||||
directory = directory.substring(0, directory.length - 1)
|
||||
}
|
||||
if (await fs.exists(directory)) {
|
||||
if ((await fs.stat(directory)).isDirectory()) {
|
||||
terminal.openTab(undefined, directory)
|
||||
|
@@ -87,6 +87,7 @@ export abstract class BaseSession {
|
||||
abstract kill (signal?: string): void
|
||||
abstract async getChildProcesses (): Promise<ChildProcess[]>
|
||||
abstract async gracefullyKillProcess (): Promise<void>
|
||||
abstract supportsWorkingDirectory (): boolean
|
||||
abstract async getWorkingDirectory (): Promise<string|null>
|
||||
}
|
||||
|
||||
@@ -259,6 +260,16 @@ export class Session extends BaseSession {
|
||||
}
|
||||
}
|
||||
|
||||
supportsWorkingDirectory (): boolean {
|
||||
if (this.reportedCWD || this.guessedCWD) {
|
||||
return true
|
||||
}
|
||||
if (!this.truePID) {
|
||||
return false
|
||||
}
|
||||
return process.platform !== 'win32'
|
||||
}
|
||||
|
||||
async getWorkingDirectory (): Promise<string|null> {
|
||||
if (this.reportedCWD) {
|
||||
return this.reportedCWD
|
||||
|
@@ -123,6 +123,13 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
|
||||
})
|
||||
}
|
||||
|
||||
if (tab instanceof TerminalTabComponent && tab.session?.supportsWorkingDirectory()) {
|
||||
items.push({
|
||||
label: 'Copy current path',
|
||||
click: () => this.zone.run(() => tab.copyCurrentPath()),
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
18
yarn.lock
18
yarn.lock
@@ -359,10 +359,10 @@
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz"
|
||||
integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==
|
||||
|
||||
"@types/node@^12.0.12":
|
||||
version "12.19.9"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz"
|
||||
integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==
|
||||
"@types/node@^14.6.2":
|
||||
version "14.14.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18"
|
||||
integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==
|
||||
|
||||
"@types/plist@^3.0.1":
|
||||
version "3.0.2"
|
||||
@@ -2482,13 +2482,13 @@ electron-to-chromium@^1.3.621:
|
||||
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.629.tgz"
|
||||
integrity sha512-iSPPJtPvHrMAvYOt+9cdbDmTasPqwnwz4lkP8Dn200gDNUBQOLQ96xUsWXBwXslAo5XxdoXAoQQ3RAy4uao9IQ==
|
||||
|
||||
electron@^11.1.1:
|
||||
version "11.1.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-11.1.1.tgz#188f036f8282798398dca9513e9bb3b10213e3aa"
|
||||
integrity sha512-tlbex3xosJgfileN6BAQRotevPRXB/wQIq48QeQ08tUJJrXwE72c8smsM/hbHx5eDgnbfJ2G3a60PmRjHU2NhA==
|
||||
electron@12.0.0-beta.16:
|
||||
version "12.0.0-beta.16"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-12.0.0-beta.16.tgz#8142fe7af8ee85a181575063db689d6da4175899"
|
||||
integrity sha512-8fsosa7PLnfheNqVK5G+NToZBh4audSVJIwff4vKorCkNK3XDMQCoHD9JKFeVRczYlb4YEY2mh7SsX4C8p9FMQ==
|
||||
dependencies:
|
||||
"@electron/get" "^1.0.1"
|
||||
"@types/node" "^12.0.12"
|
||||
"@types/node" "^14.6.2"
|
||||
extract-zip "^1.0.3"
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
|
Reference in New Issue
Block a user