Compare commits

...

72 Commits

Author SHA1 Message Date
Eugene Pankov
b1752bd0b4 updated cwd settings UI 2021-02-07 13:44:17 +01:00
Eugene Pankov
1e697a952a upgraded xterm.js 2021-02-07 13:44:08 +01:00
Eugene Pankov
6bad2a2167 cache dscl output 2021-02-07 13:43:58 +01:00
Eugene Pankov
61a46e3b4a lint 2021-02-07 13:15:24 +01:00
Eugene Pankov
cba90cec0a better error messages for X11 forwarding issues 2021-02-07 12:49:34 +01:00
Eugene Pankov
7583d92747 set terminus to use built-in graphics by default - fixes #657 2021-02-07 12:49:22 +01:00
Eugene Pankov
e2b99d71ad lint 2021-02-06 17:47:17 +01:00
Eugene Pankov
aac38fa190 CWD detection on windows where possible 2021-02-06 16:45:38 +01:00
Eugene Pankov
7098622c8f bumped electron 2021-02-06 11:20:34 +01:00
Eugene Pankov
8695003c74 lint 2021-02-01 11:40:49 +01:00
Eugene Pankov
43a27a7b7c serial newline modes 2021-01-31 20:07:26 +01:00
Eugene Pankov
dd2d2ce20d Update tsconfig.json 2021-01-31 19:33:33 +01:00
Eugene Pankov
73574374f0 readline input mode for serial terminals - #3099, #2661 2021-01-31 19:33:30 +01:00
Eugene Pankov
5bd1bfd565 better editor ts support 2021-01-31 18:20:58 +01:00
Eugene Pankov
0611afa8b5 better session handlers behaviour, added serial auto-reconnection logic - #3099 2021-01-31 18:20:39 +01:00
Eugene Pankov
91c9e8affd bumped angular 2021-01-28 22:01:42 +01:00
Eugene Pankov
322ffc5847 margin fix 2021-01-28 21:55:29 +01:00
Eugene Pankov
21084b5d24 bumped js-yaml 2021-01-28 21:52:11 +01:00
Eugene Pankov
e0efb4073a bumped eslint 2021-01-28 21:46:31 +01:00
Eugene Pankov
c42b62afe6 bumped deps 2021-01-28 21:11:53 +01:00
Eugene Pankov
e2b11c83d5 fixed cli args handling 2021-01-28 21:11:47 +01:00
Eugene Pankov
891fa5770a Update electron-builder.yml 2021-01-28 18:45:09 +01:00
Eugene Pankov
6b395cc2b3 fixed windows native module import 2021-01-28 18:20:45 +01:00
Eugene Pankov
448a1a094f fixed macOS native module builds 2021-01-27 00:26:48 +01:00
Eugene Pankov
788dd61a13 exclude native modules from app.asar and sign them 2021-01-26 15:29:18 +01:00
Eugene Pankov
467684d9ab Update api.ts 2021-01-25 21:35:00 +01:00
Eugene Pankov
5069070040 added a 'copy current path' hotkey and context menu item - fixes #2586 2021-01-25 17:30:44 +01:00
Eugene Pankov
ecf5297bc3 Merge branch 'master' of github.com:Eugeny/terminus 2021-01-24 19:27:42 +01:00
Eugene Pankov
78bd90ac55 fixed window re-focusing on linux 2021-01-24 19:27:36 +01:00
Eugene Pankov
712589eb93 fixed #1510 2021-01-24 19:06:51 +01:00
Eugene Pankov
f103e71285 better handling of CLI args - fixes #1436 2021-01-24 19:06:41 +01:00
Eugene Pankov
0cf883cc4a bumped electron 2021-01-24 13:58:47 +01:00
Eugene Pankov
2b0ad0d558 ssh: 15s default keepalive interval 2021-01-24 13:33:54 +01:00
Eugene Pankov
67bacb9dd3 ssh: better session close and reconnect behaviours - fixes #3351, fixes #3010, fixes #3276, fixes #3074, fixes #2825, fixes #3285 2021-01-24 13:28:59 +01:00
Eugene Pankov
d0a597634d ssh: fixed clearing jump host in connection settings 2021-01-24 13:08:07 +01:00
Eugene Pankov
322014c409 Merge branch 'master' of github.com:Eugeny/terminus 2021-01-24 12:43:02 +01:00
Eugene Pankov
c751a8725b rollback node-pty to pre-napi state 2021-01-24 12:42:59 +01:00
Eugene Pankov
5417efe558 ssh: blacklist broken kex algorithms 2021-01-24 11:26:43 +01:00
Eugene
bf356fcd19 Merge pull request #3362 from mi544/readme-fix
Removes unnecessary asterisks from README
2021-01-22 09:37:05 +01:00
Maksim Verkhoturov
10ee66b9dd removes unnecessary asterisks 2021-01-21 15:45:37 -07:00
Eugene
763da0d80c Merge pull request #3310 from Eugeny/dependabot/npm_and_yarn/app/npm-6.14.11
Bump npm from 6.14.10 to 6.14.11 in /app
2021-01-15 15:12:16 +01:00
Eugene
8d46bb2181 Merge pull request #3290 from Eugeny/dependabot/npm_and_yarn/terminus-core/core-js-3.8.2
Bump core-js from 3.7.0 to 3.8.2 in /terminus-core
2021-01-15 15:12:05 +01:00
Eugene Pankov
fe936c7726 fixed #3333 - duplicate color schemes 2021-01-14 09:48:58 +01:00
Eugene Pankov
2f3e32990a Update macos.yml 2021-01-13 13:52:23 +01:00
Eugene Pankov
22344f8d54 Update macos.yml 2021-01-13 10:50:34 +01:00
Eugene Pankov
f6d37a39f4 Update macos.yml 2021-01-12 18:20:17 +01:00
dependabot-preview[bot]
0e4c60ad4b Bump npm from 6.14.10 to 6.14.11 in /app
Bumps [npm](https://github.com/npm/cli) from 6.14.10 to 6.14.11.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/v6.14.11/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.14.10...v6.14.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-12 17:01:34 +00:00
dependabot-preview[bot]
e8c2171d8f Bump core-js from 3.7.0 to 3.8.2 in /terminus-core
Bumps [core-js](https://github.com/zloirock/core-js) from 3.7.0 to 3.8.2.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.7.0...v3.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-12 17:01:01 +00:00
Eugene Pankov
f7a5be2c67 downgraded electron 2021-01-12 17:59:13 +01:00
Eugene Pankov
39fa0424a6 handle auto-updater errors 2021-01-09 18:22:58 +01:00
Eugene Pankov
bcb1b6a13b fixed tab scrolling 2021-01-09 17:48:31 +01:00
Eugene Pankov
a19f35ac44 fixed macos builds 2021-01-09 17:30:23 +01:00
Eugene Pankov
ea92f1a700 Revert "vibrancy fixes"
This reverts commit a8d78ce185.
2021-01-09 17:29:37 +01:00
Eugene Pankov
b5701cf9f9 fixed linux crashes 2021-01-09 17:29:34 +01:00
Eugene Pankov
4742530cf3 fixed invisible tab bar on the bottom in full-titlebar mode - fixes #3191 2021-01-09 17:11:05 +01:00
Eugene Pankov
a8d78ce185 vibrancy fixes 2021-01-09 17:02:01 +01:00
Eugene Pankov
bba1eaccbe fixed hotkeys page crash - fixes #3311 2021-01-09 16:15:20 +01:00
Eugene Pankov
cd6d05aa69 Merge branch 'master' of github.com:Eugeny/terminus 2021-01-09 15:38:58 +01:00
Eugene Pankov
412403c72a updated glasstron 2021-01-09 15:38:55 +01:00
Eugene
93ae907dd1 Merge pull request #3304 from Eugeny/all-contributors/add-frauhottelmann 2021-01-06 10:34:38 +01:00
allcontributors[bot]
ce24b9cc52 docs: update .all-contributorsrc [skip ci] 2021-01-06 09:32:42 +00:00
allcontributors[bot]
dc68372d76 docs: update README.md [skip ci] 2021-01-06 09:32:40 +00:00
Eugene
e0fe125cf2 Merge pull request #3303 from frauhottelmann/master
add baudrate 1500000
2021-01-06 10:32:21 +01:00
frauhottelmann
e594fcd0e7 add baudrate 1500000
1500000bps is needed for single board computers with RK3399 CPUs
2021-01-06 10:19:18 +01:00
Eugene Pankov
0219da4d85 sourcemap separation 2021-01-04 21:31:33 +01:00
Eugene Pankov
5e06b2248b better sentry upload 2021-01-04 20:44:24 +01:00
Eugene Pankov
cdc3623986 sentry wip 2021-01-04 20:02:47 +01:00
Eugene Pankov
6d016002c0 sentry symbols on linux 2021-01-04 19:39:59 +01:00
Eugene Pankov
f3569f5d2d sentry symbols on win 2021-01-04 19:21:12 +01:00
Eugene Pankov
4125582ef2 sentry symbols on mac 2021-01-04 19:11:57 +01:00
Eugene Pankov
c6331c9b1c fixed AppService.selectTab crash 2021-01-03 20:53:53 +01:00
Eugene Pankov
aaab475e5f fixed ssh sessions - fixes #3282 2021-01-03 18:33:03 +01:00
65 changed files with 1677 additions and 946 deletions

View File

@@ -334,6 +334,15 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "frauhottelmann",
"name": "frauhottelmann",
"avatar_url": "https://avatars2.githubusercontent.com/u/902705?v=4",
"profile": "https://github.com/frauhottelmann",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -1,6 +1,8 @@
parser: '@typescript-eslint/parser' parser: '@typescript-eslint/parser'
parserOptions: parserOptions:
project: tsconfig.json project:
- tsconfig.json
- '*/tsconfig.typings.json'
extends: extends:
- 'plugin:@typescript-eslint/all' - 'plugin:@typescript-eslint/all'
plugins: plugins:
@@ -37,6 +39,7 @@ rules:
'@typescript-eslint/no-misused-promises': off '@typescript-eslint/no-misused-promises': off
'@typescript-eslint/typedef': off '@typescript-eslint/typedef': off
'@typescript-eslint/consistent-type-imports': off '@typescript-eslint/consistent-type-imports': off
'@typescript-eslint/sort-type-union-intersection-members': off
'@typescript-eslint/no-use-before-define': '@typescript-eslint/no-use-before-define':
- error - error
- classes: false - classes: false
@@ -81,7 +84,8 @@ rules:
argsIgnorePattern: ^_ argsIgnorePattern: ^_
no-undef: error no-undef: error
no-var: error no-var: error
object-curly-spacing: object-curly-spacing: off
'@typescript-eslint/object-curly-spacing':
- error - error
- always - always
quote-props: quote-props:

View File

@@ -38,6 +38,15 @@ jobs:
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
USE_HARD_LINKS: false USE_HARD_LINKS: false
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts - name: Package artifacts
run: | run: |
mkdir artifact-deb mkdir artifact-deb

View File

@@ -26,6 +26,10 @@ jobs:
cd .. cd ..
rm app/node_modules/.yarn-integrity rm app/node_modules/.yarn-integrity
yarn yarn
./node_modules/.bin/patch-package
cd app
../node_modules/.bin/patch-package
cd ..
- name: Build native deps - name: Build native deps
run: scripts/build-native.js run: scripts/build-native.js
@@ -62,6 +66,15 @@ jobs:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}
# DEBUG: electron-builder,electron-builder:* # DEBUG: electron-builder,electron-builder:*
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts - name: Package artifacts
run: | run: |
mkdir artifact-pkg mkdir artifact-pkg

View File

@@ -34,6 +34,15 @@ jobs:
run: node scripts/build-windows.js run: node scripts/build-windows.js
if: github.repository != 'Eugeny/terminus' || github.event_name != 'push' if: github.repository != 'Eugeny/terminus' || github.event_name != 'push'
- name: Upload symbols
run: |
npm install @sentry/cli
node scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts - name: Package artifacts
run: | run: |
mkdir artifact-setup mkdir artifact-setup

View File

@@ -1,4 +1,4 @@
****![](https://github.com/Eugeny/terminus/raw/master/docs/readme.png) ![](https://github.com/Eugeny/terminus/raw/master/docs/readme.png)
<p align="center"> <p align="center">
@@ -74,57 +74,59 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- markdownlint-disable --> <!-- markdownlint-disable -->
<table> <table>
<tr> <tr>
<td align="center"><a href="http://www.russellmyers.com"><img src="https://avatars2.githubusercontent.com/u/184085?v=4" width="100px;" alt=""/><br /><sub><b>Russell Myers</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mezner" title="Code">💻</a></td> <td align="center"><a href="http://www.russellmyers.com"><img src="https://avatars2.githubusercontent.com/u/184085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Russell Myers</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mezner" title="Code">💻</a></td>
<td align="center"><a href="http://www.morwire.com"><img src="https://avatars1.githubusercontent.com/u/3991658?v=4" width="100px;" alt=""/><br /><sub><b>Austin Warren</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ehwarren" title="Code">💻</a></td> <td align="center"><a href="http://www.morwire.com"><img src="https://avatars1.githubusercontent.com/u/3991658?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austin Warren</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ehwarren" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Drachenkaetzchen"><img src="https://avatars1.githubusercontent.com/u/162974?v=4" width="100px;" alt=""/><br /><sub><b>Felicia Hummel</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Drachenkaetzchen" title="Code">💻</a></td> <td align="center"><a href="https://github.com/Drachenkaetzchen"><img src="https://avatars1.githubusercontent.com/u/162974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felicia Hummel</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Drachenkaetzchen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mikemaccana"><img src="https://avatars2.githubusercontent.com/u/172594?v=4" width="100px;" alt=""/><br /><sub><b>Mike MacCana</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mikemaccana" title="Tests">⚠️</a> <a href="#design-mikemaccana" title="Design">🎨</a></td> <td align="center"><a href="https://github.com/mikemaccana"><img src="https://avatars2.githubusercontent.com/u/172594?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike MacCana</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mikemaccana" title="Tests">⚠️</a> <a href="#design-mikemaccana" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/yxuko"><img src="https://avatars1.githubusercontent.com/u/1786317?v=4" width="100px;" alt=""/><br /><sub><b>Yacine Kanzari</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=yxuko" title="Code">💻</a></td> <td align="center"><a href="https://github.com/yxuko"><img src="https://avatars1.githubusercontent.com/u/1786317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yacine Kanzari</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=yxuko" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/BBJip"><img src="https://avatars2.githubusercontent.com/u/32908927?v=4" width="100px;" alt=""/><br /><sub><b>BBJip</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=BBJip" title="Code">💻</a></td> <td align="center"><a href="https://github.com/BBJip"><img src="https://avatars2.githubusercontent.com/u/32908927?v=4?s=100" width="100px;" alt=""/><br /><sub><b>BBJip</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=BBJip" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Futagirl"><img src="https://avatars2.githubusercontent.com/u/33533958?v=4" width="100px;" alt=""/><br /><sub><b>Futagirl</b></sub></a><br /><a href="#design-Futagirl" title="Design">🎨</a></td> <td align="center"><a href="https://github.com/Futagirl"><img src="https://avatars2.githubusercontent.com/u/33533958?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Futagirl</b></sub></a><br /><a href="#design-Futagirl" title="Design">🎨</a></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://www.levrik.io"><img src="https://avatars3.githubusercontent.com/u/9491603?v=4" width="100px;" alt=""/><br /><sub><b>Levin Rickert</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=levrik" title="Code">💻</a></td> <td align="center"><a href="https://www.levrik.io"><img src="https://avatars3.githubusercontent.com/u/9491603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Levin Rickert</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=levrik" title="Code">💻</a></td>
<td align="center"><a href="https://kwonoj.github.io"><img src="https://avatars2.githubusercontent.com/u/1210596?v=4" width="100px;" alt=""/><br /><sub><b>OJ Kwon</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=kwonoj" title="Code">💻</a></td> <td align="center"><a href="https://kwonoj.github.io"><img src="https://avatars2.githubusercontent.com/u/1210596?v=4?s=100" width="100px;" alt=""/><br /><sub><b>OJ Kwon</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=kwonoj" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Domain"><img src="https://avatars2.githubusercontent.com/u/903197?v=4" width="100px;" alt=""/><br /><sub><b>domain</b></sub></a><br /><a href="#plugin-Domain" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/terminus/commits?author=Domain" title="Code">💻</a></td> <td align="center"><a href="https://github.com/Domain"><img src="https://avatars2.githubusercontent.com/u/903197?v=4?s=100" width="100px;" alt=""/><br /><sub><b>domain</b></sub></a><br /><a href="#plugin-Domain" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/terminus/commits?author=Domain" title="Code">💻</a></td>
<td align="center"><a href="http://www.jbrumond.me"><img src="https://avatars1.githubusercontent.com/u/195127?v=4" width="100px;" alt=""/><br /><sub><b>James Brumond</b></sub></a><br /><a href="#plugin-kbjr" title="Plugin/utility libraries">🔌</a></td> <td align="center"><a href="http://www.jbrumond.me"><img src="https://avatars1.githubusercontent.com/u/195127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Brumond</b></sub></a><br /><a href="#plugin-kbjr" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="http://www.growingwiththeweb.com"><img src="https://avatars0.githubusercontent.com/u/2193314?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Imms</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Tyriar" title="Code">💻</a> <a href="#plugin-Tyriar" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/terminus/commits?author=Tyriar" title="Tests">⚠️</a></td> <td align="center"><a href="http://www.growingwiththeweb.com"><img src="https://avatars0.githubusercontent.com/u/2193314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Imms</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Tyriar" title="Code">💻</a> <a href="#plugin-Tyriar" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/terminus/commits?author=Tyriar" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/baflo"><img src="https://avatars2.githubusercontent.com/u/834350?v=4" width="100px;" alt=""/><br /><sub><b>Florian Bachmann</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=baflo" title="Code">💻</a></td> <td align="center"><a href="https://github.com/baflo"><img src="https://avatars2.githubusercontent.com/u/834350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Florian Bachmann</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=baflo" title="Code">💻</a></td>
<td align="center"><a href="http://michael-kuehnel.de"><img src="https://avatars2.githubusercontent.com/u/441011?v=4" width="100px;" alt=""/><br /><sub><b>Michael Kühnel</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mischah" title="Code">💻</a> <a href="#design-mischah" title="Design">🎨</a></td> <td align="center"><a href="http://michael-kuehnel.de"><img src="https://avatars2.githubusercontent.com/u/441011?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Kühnel</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mischah" title="Code">💻</a> <a href="#design-mischah" title="Design">🎨</a></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/NieLeben"><img src="https://avatars3.githubusercontent.com/u/47182955?v=4" width="100px;" alt=""/><br /><sub><b>Tilmann Meyer</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=NieLeben" title="Code">💻</a></td> <td align="center"><a href="https://github.com/NieLeben"><img src="https://avatars3.githubusercontent.com/u/47182955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tilmann Meyer</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=NieLeben" title="Code">💻</a></td>
<td align="center"><a href="http://www.jubeat.net"><img src="https://avatars3.githubusercontent.com/u/11289158?v=4" width="100px;" alt=""/><br /><sub><b>PM Extra</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/issues?q=author%3APMExtra" title="Bug reports">🐛</a></td> <td align="center"><a href="http://www.jubeat.net"><img src="https://avatars3.githubusercontent.com/u/11289158?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PM Extra</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/issues?q=author%3APMExtra" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://jjuhas.keybase.pub//"><img src="https://avatars1.githubusercontent.com/u/6438760?v=4" width="100px;" alt=""/><br /><sub><b>Jonathan</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=IgnusG" title="Code">💻</a></td> <td align="center"><a href="https://jjuhas.keybase.pub//"><img src="https://avatars1.githubusercontent.com/u/6438760?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=IgnusG" title="Code">💻</a></td>
<td align="center"><a href="https://hans-koch.me"><img src="https://avatars0.githubusercontent.com/u/1093709?v=4" width="100px;" alt=""/><br /><sub><b>Hans Koch</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=hammster" title="Code">💻</a></td> <td align="center"><a href="https://hans-koch.me"><img src="https://avatars0.githubusercontent.com/u/1093709?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hans Koch</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=hammster" title="Code">💻</a></td>
<td align="center"><a href="http://thepuzzlemaker.info"><img src="https://avatars3.githubusercontent.com/u/12666617?v=4" width="100px;" alt=""/><br /><sub><b>Dak Smyth</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ThePuzzlemaker" title="Code">💻</a></td> <td align="center"><a href="http://thepuzzlemaker.info"><img src="https://avatars3.githubusercontent.com/u/12666617?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dak Smyth</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ThePuzzlemaker" title="Code">💻</a></td>
<td align="center"><a href="http://yfwz100.github.io"><img src="https://avatars2.githubusercontent.com/u/983211?v=4" width="100px;" alt=""/><br /><sub><b>Wang Zhi</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=yfwz100" title="Code">💻</a></td> <td align="center"><a href="http://yfwz100.github.io"><img src="https://avatars2.githubusercontent.com/u/983211?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wang Zhi</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=yfwz100" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jack1142"><img src="https://avatars0.githubusercontent.com/u/6032823?v=4" width="100px;" alt=""/><br /><sub><b>jack1142</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=jack1142" title="Code">💻</a></td> <td align="center"><a href="https://github.com/jack1142"><img src="https://avatars0.githubusercontent.com/u/6032823?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jack1142</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=jack1142" title="Code">💻</a></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/hdougie"><img src="https://avatars1.githubusercontent.com/u/450799?v=4" width="100px;" alt=""/><br /><sub><b>Howie Douglas</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=hdougie" title="Code">💻</a></td> <td align="center"><a href="https://github.com/hdougie"><img src="https://avatars1.githubusercontent.com/u/450799?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Howie Douglas</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=hdougie" title="Code">💻</a></td>
<td align="center"><a href="https://chriskaczor.com"><img src="https://avatars2.githubusercontent.com/u/180906?v=4" width="100px;" alt=""/><br /><sub><b>Chris Kaczor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ckaczor" title="Code">💻</a></td> <td align="center"><a href="https://chriskaczor.com"><img src="https://avatars2.githubusercontent.com/u/180906?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Kaczor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ckaczor" title="Code">💻</a></td>
<td align="center"><a href="https://www.boxmein.net"><img src="https://avatars1.githubusercontent.com/u/358714?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Kadak</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=boxmein" title="Code">💻</a></td> <td align="center"><a href="https://www.boxmein.net"><img src="https://avatars1.githubusercontent.com/u/358714?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johannes Kadak</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=boxmein" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/LeSeulArtichaut"><img src="https://avatars1.githubusercontent.com/u/38361244?v=4" width="100px;" alt=""/><br /><sub><b>LeSeulArtichaut</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=LeSeulArtichaut" title="Code">💻</a></td> <td align="center"><a href="https://github.com/LeSeulArtichaut"><img src="https://avatars1.githubusercontent.com/u/38361244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LeSeulArtichaut</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=LeSeulArtichaut" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/CyrilTaylor"><img src="https://avatars0.githubusercontent.com/u/12631466?v=4" width="100px;" alt=""/><br /><sub><b>Cyril Taylor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=CyrilTaylor" title="Code">💻</a></td> <td align="center"><a href="https://github.com/CyrilTaylor"><img src="https://avatars0.githubusercontent.com/u/12631466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cyril Taylor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=CyrilTaylor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstefanou"><img src="https://avatars3.githubusercontent.com/u/51129173?v=4" width="100px;" alt=""/><br /><sub><b>nstefanou</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=nstefanou" title="Code">💻</a> <a href="#plugin-nstefanou" title="Plugin/utility libraries">🔌</a></td> <td align="center"><a href="https://github.com/nstefanou"><img src="https://avatars3.githubusercontent.com/u/51129173?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nstefanou</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=nstefanou" title="Code">💻</a> <a href="#plugin-nstefanou" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="https://github.com/orin220444"><img src="https://avatars3.githubusercontent.com/u/30747229?v=4" width="100px;" alt=""/><br /><sub><b>orin220444</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=orin220444" title="Code">💻</a></td> <td align="center"><a href="https://github.com/orin220444"><img src="https://avatars3.githubusercontent.com/u/30747229?v=4?s=100" width="100px;" alt=""/><br /><sub><b>orin220444</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=orin220444" title="Code">💻</a></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/Goobles"><img src="https://avatars3.githubusercontent.com/u/8776771?v=4" width="100px;" alt=""/><br /><sub><b>Gobius Dolhain</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Goobles" title="Code">💻</a></td> <td align="center"><a href="https://github.com/Goobles"><img src="https://avatars3.githubusercontent.com/u/8776771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gobius Dolhain</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Goobles" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=3l0w" title="Code">💻</a></td> <td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=3l0w" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=dimitory" title="Code">💻</a></td> <td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=dimitory" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=JonathanBeverley" title="Code">💻</a></td> <td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=JonathanBeverley" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/zend"><img src="https://avatars1.githubusercontent.com/u/25160?v=4" width="100px;" alt=""/><br /><sub><b>Zenghai Liang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=zend" title="Code">💻</a></td> <td align="center"><a href="https://github.com/zend"><img src="https://avatars1.githubusercontent.com/u/25160?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zenghai Liang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=zend" title="Code">💻</a></td>
<td align="center"><a href="https://about.me/matishadow"><img src="https://avatars0.githubusercontent.com/u/9083085?v=4" width="100px;" alt=""/><br /><sub><b>Mateusz Tracz</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=matishadow" title="Code">💻</a></td> <td align="center"><a href="https://about.me/matishadow"><img src="https://avatars0.githubusercontent.com/u/9083085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mateusz Tracz</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=matishadow" title="Code">💻</a></td>
<td align="center"><a href="https://zergpool.com"><img src="https://avatars3.githubusercontent.com/u/36234677?v=4" width="100px;" alt=""/><br /><sub><b>pinpin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=pinpins" title="Code">💻</a></td> <td align="center"><a href="https://zergpool.com"><img src="https://avatars3.githubusercontent.com/u/36234677?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pinpin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=pinpins" title="Code">💻</a></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/TakuroOnoda"><img src="https://avatars0.githubusercontent.com/u/1407926?v=4" width="100px;" alt=""/><br /><sub><b>Takuro Onoda</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=TakuroOnoda" title="Code">💻</a></td> <td align="center"><a href="https://github.com/TakuroOnoda"><img src="https://avatars0.githubusercontent.com/u/1407926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Takuro Onoda</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=TakuroOnoda" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/frauhottelmann"><img src="https://avatars2.githubusercontent.com/u/902705?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frauhottelmann</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=frauhottelmann" title="Code">💻</a></td>
</tr> </tr>
</table> </table>
<!-- markdownlint-enable --> <!-- markdownlint-restore -->
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END --> <!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -75,7 +75,7 @@ export class Application {
} }
onGlobalHotkey (): void { onGlobalHotkey (): void {
if (this.windows.some(x => x.isFocused())) { if (this.windows.some(x => x.isFocused() && x.isVisible())) {
for (const window of this.windows) { for (const window of this.windows) {
window.hide() window.hide()
} }
@@ -147,7 +147,7 @@ export class Application {
handleSecondInstance (argv: string[], cwd: string): void { handleSecondInstance (argv: string[], cwd: string): void {
this.presentAllWindows() this.presentAllWindows()
this.windows[this.windows.length - 1].handleSecondInstance(argv, cwd) this.windows[this.windows.length - 1].passCliArguments(argv, cwd, true)
} }
private setupMenu () { private setupMenu () {

View File

@@ -6,7 +6,7 @@ import { app } from 'electron'
export function loadConfig (): any { export function loadConfig (): any {
const configPath = path.join(app.getPath('userData'), 'config.yaml') const configPath = path.join(app.getPath('userData'), 'config.yaml')
if (fs.existsSync(configPath)) { if (fs.existsSync(configPath)) {
return yaml.safeLoad(fs.readFileSync(configPath, 'utf8')) return yaml.load(fs.readFileSync(configPath, 'utf8'))
} else { } else {
return {} return {}
} }

View File

@@ -53,7 +53,7 @@ if (argv.d) {
}) })
} }
app.on('ready', () => { app.on('ready', async () => {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
app.dock.setMenu(Menu.buildFromTemplate([ app.dock.setMenu(Menu.buildFromTemplate([
{ {
@@ -65,5 +65,8 @@ app.on('ready', () => {
])) ]))
} }
application.init() 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)
}) })

View File

@@ -1,7 +1,4 @@
import * as glasstron from 'glasstron' import * as glasstron from 'glasstron'
if (process.platform === 'win32' || process.platform === 'linux') {
glasstron.init()
}
import { Subject, Observable } from 'rxjs' import { Subject, Observable } from 'rxjs'
import { debounceTime } from 'rxjs/operators' import { debounceTime } from 'rxjs/operators'
@@ -29,7 +26,7 @@ abstract class GlasstronWindow extends BrowserWindow {
abstract setBlur (_: boolean) abstract setBlur (_: boolean)
} }
const macOSVibrancyType = compareVersions.compare(macOSRelease().version, '10.14', '>=') ? 'fullscreen-ui' : 'dark' const macOSVibrancyType = process.platform === 'darwin' ? compareVersions.compare(macOSRelease().version, '10.14', '>=') ? 'fullscreen-ui' : 'dark' : null
export class Window { export class Window {
ready: Promise<void> ready: Promise<void>
@@ -39,7 +36,7 @@ export class Window {
private windowConfig: ElectronConfig private windowConfig: ElectronConfig
private windowBounds?: Rectangle private windowBounds?: Rectangle
private closing = false private closing = false
private lastVibrancy: {enabled: boolean, type?: string} | null = null private lastVibrancy: { enabled: boolean, type?: string } | null = null
private disableVibrancyWhileDragging = false private disableVibrancyWhileDragging = false
private configStore: any private configStore: any
@@ -66,7 +63,9 @@ export class Window {
preload: path.join(__dirname, 'sentry.js'), preload: path.join(__dirname, 'sentry.js'),
backgroundThrottling: false, backgroundThrottling: false,
enableRemoteModule: true, enableRemoteModule: true,
contextIsolation: false,
}, },
maximizable: true,
frame: false, frame: false,
show: false, show: false,
backgroundColor: '#00000000', backgroundColor: '#00000000',
@@ -149,7 +148,11 @@ export class Window {
if (process.platform === 'win32') { if (process.platform === 'win32') {
if (parseFloat(os.release()) >= 10) { if (parseFloat(os.release()) >= 10) {
this.window.blurType = enabled ? type === 'fluent' ? 'acrylic' : 'blurbehind' : null this.window.blurType = enabled ? type === 'fluent' ? 'acrylic' : 'blurbehind' : null
try {
this.window.setBlur(enabled) this.window.setBlur(enabled)
} catch (error) {
console.error('Failed to set window blur', error)
}
} else { } else {
DwmEnableBlurBehindWindow(this.window, enabled) DwmEnableBlurBehindWindow(this.window, enabled)
} }
@@ -188,6 +191,10 @@ export class Window {
return this.window.isFocused() return this.window.isFocused()
} }
isVisible (): boolean {
return this.window.isVisible()
}
hide (): void { hide (): void {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
// Lose focus // Lose focus
@@ -223,8 +230,8 @@ export class Window {
} }
} }
handleSecondInstance (argv: string[], cwd: string): void { passCliArguments (argv: string[], cwd: string, secondInstance: boolean): void {
this.send('host:second-instance', parseArgs(argv, cwd), cwd) this.send('cli', parseArgs(argv, cwd), cwd, secondInstance)
} }
private setupWindowManagement () { private setupWindowManagement () {

View File

@@ -1,6 +1,7 @@
{ {
"name": "terminus", "name": "terminus",
"description": "A terminal for a modern age", "description": "A terminal for a modern age",
"private": true,
"repository": "https://github.com/eugeny/terminus", "repository": "https://github.com/eugeny/terminus",
"author": { "author": {
"name": "Eugene Pankov", "name": "Eugene Pankov",
@@ -13,24 +14,25 @@
"watch": "webpack --progress --color --watch" "watch": "webpack --progress --color --watch"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^9.1.9", "@angular/animations": "^11.1.1",
"@angular/common": "^9.1.11", "@angular/common": "^11.1.1",
"@angular/compiler": "^9.1.9", "@angular/compiler": "^11.1.1",
"@angular/core": "^9.1.9", "@angular/core": "^11.1.1",
"@angular/forms": "^9.1.11", "@angular/forms": "^11.1.1",
"@angular/platform-browser": "^9.1.9", "@angular/platform-browser": "^11.1.1",
"@angular/platform-browser-dynamic": "^9.1.9", "@angular/platform-browser-dynamic": "^11.1.1",
"@ng-bootstrap/ng-bootstrap": "^6.1.0", "@ng-bootstrap/ng-bootstrap": "^6.1.0",
"@terminus-term/node-pty": "0.10.0-beta11", "@terminus-term/node-pty": "0.10.0-terminus.2",
"electron-config": "2.0.0", "electron-config": "2.0.0",
"electron-debug": "^3.0.1", "electron-debug": "^3.2.0",
"electron-is-dev": "1.1.0", "electron-is-dev": "1.2.0",
"electron-promise-ipc": "^2.2.4", "electron-promise-ipc": "^2.2.4",
"fontmanager-redux": "1.0.0", "fontmanager-redux": "1.0.0",
"glasstron": "0.0.5", "glasstron": "0.0.6",
"js-yaml": "3.14.0", "js-yaml": "4.0.0",
"keytar": "^7.2.0", "keytar": "^7.2.0",
"mz": "^2.7.0", "mz": "^2.7.0",
"native-process-working-directory": "^1.0.2",
"ngx-toastr": "^12.0.1", "ngx-toastr": "^12.0.1",
"npm": "6", "npm": "6",
"path": "0.12.7", "path": "0.12.7",

View File

@@ -0,0 +1,12 @@
diff --git a/node_modules/node-abi/abi_registry.json b/node_modules/node-abi/abi_registry.json
index bc1436d..630c1b7 100644
--- a/node_modules/node-abi/abi_registry.json
+++ b/node_modules/node-abi/abi_registry.json
@@ -94,6 +94,6 @@
"future": true,
"lts": false,
"runtime": "electron",
- "target": "12.0.0-nightly.20201013"
+ "target": "12.0.0-beta.16"
}
]

View File

@@ -95,3 +95,7 @@ input[type=range] {
&::-moz-range-track { @include track(); } &::-moz-range-track { @include track(); }
&::-ms-track { @include track(); } &::-ms-track { @include track(); }
} }
a[ngbdropdownitem] {
cursor: pointer;
}

View File

@@ -2,40 +2,54 @@
# yarn lockfile v1 # yarn lockfile v1
"@angular/animations@^9.1.9": "@angular/animations@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/animations/-/animations-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.1.1.tgz#3ae6754e70ecf858322a432f6484a1bab1013315"
integrity sha512-ane1eeQmsP7fcAiLgRhle7YIDgE88WDMMvzqJYhSxwLzXNF/hwqNeskmNcjo8bLt9h/yTIjrCQbycLCHJfU8UQ== integrity sha512-okideoWvlTz6VqHXDWKlMGj1beWxq/ag4n/+7y8IENQFgrKQWu4m52Igr5sJUfT1W8rXqLPaSGEpp9h+1SCDcQ==
dependencies:
tslib "^2.0.0"
"@angular/common@^9.1.11": "@angular/common@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/common/-/common-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.1.1.tgz#fcf16d3d35cdc64c40458a51f879ab61ed2ecdf2"
integrity sha512-QACUhJWlly/nfHUmjopVS1p6ayxxa/NqjyftdCeBJaoyM2YohqWixP/n/keu1K/srJ96aFpUNsZQgmgoRv5SOQ== integrity sha512-YHAmbjmwqIv43CdzRJkXJiT7p6Xm6DpsGKLuCGPoomUk7Nf5U5LsSZm/uvyTu0O+OQp2RUFUdGqW5DzSZZnKtQ==
dependencies:
tslib "^2.0.0"
"@angular/compiler@^9.1.9": "@angular/compiler@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.1.1.tgz#cafe43b52fb69f0b9493e597baec14fcffd13d7d"
integrity sha512-9MLB1Xx7odKuxDoybVwiOB1ZEUZpL8FurYm4RVuW39ntsUt0IMC9Hb8UagZLTAWhaWSHydkD/KBQVVobGqd0lA== integrity sha512-kybGddMBL6E2BFBOHIX39VsKVxH3yD7NA1g2mQUE9KIqLAavH0KLBr8B03R9MKb6+BElGpk/5yCyCkzkwQES7w==
dependencies:
tslib "^2.0.0"
"@angular/core@^9.1.9": "@angular/core@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/core/-/core-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.1.1.tgz#62bf9ad93d5d88afacce3ea5b03803ec5790d20b"
integrity sha512-mBm24Q9GjkAsxMAzqQ86U1078+yTEpr0+syMEruUtJ0HUH6Fzn3J+6xTLb+BVcGb9RkCkFaV9T5mcn6ZM0f++g== integrity sha512-yBBLekXeDviZ+KW3DmGeOK0CK/yQ9gCy2uHY47KUDI4UTRRLz0NI5nS9NdKwk5bpCPId6ZPNsJtHPBFXRhAnNA==
dependencies:
tslib "^2.0.0"
"@angular/forms@^9.1.11": "@angular/forms@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/forms/-/forms-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.1.1.tgz#c8c468aba528f1f2764cf0bc7d9ab36636795473"
integrity sha512-soGVZmPq2bzkxvtTyeJB8p3ejzm4xxt+43hJw6Ag8NxpwUFPVa30oJge3JV+u8Y4yBtl5SbOZ4bBX3EkMxLcGQ== integrity sha512-MVysENcKonjMowdd2SxXQGcvNVpMTWVhwr5IZ2FBZKW/FNnSBq0R3nsr25zU0h8QWQHkROPtFF0+AEaENh/yAg==
dependencies:
tslib "^2.0.0"
"@angular/platform-browser-dynamic@^9.1.9": "@angular/platform-browser-dynamic@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.1.1.tgz#2e05d719a1d134a9a22fb4e4a40234ac49ba122a"
integrity sha512-jCeHyAZ4Nap1/FOqAlKEg9UxQaSkHrxnQr6hYtWwC4ZDVUn3zLWQf6J+mbeYNOXN5yQxEiIqqhORYeOCLLqf1w== integrity sha512-fI44VfQvwzNeZdSMGZEYqYZ6Q4Y1fSbyqh2hzbhVW4/Tq6s4qKdKn3TfAD6iK6te5ewZwbccfr4Y0zMbcwTSRA==
dependencies:
tslib "^2.0.0"
"@angular/platform-browser@^9.1.9": "@angular/platform-browser@^11.1.1":
version "9.1.13" version "11.1.1"
resolved "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.13.tgz" resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.1.1.tgz#d9940e92664b06a3c6a10221f1b9438ba94b9b5f"
integrity sha512-F3iTz1zNbtrs7KFKUxbj8qmTsd/fiuTNcpBExjE5TtatRiE6J8vNvN1+Z/1FgPe0UXBSdTzSwZ8/RxWKw20RMw== integrity sha512-z3OmXK9viTxNp2KpGdCiFkFCI69WVYyuMQerCwjuD2oymRpltc64uujIq3+j32UJ+sTGrjMSmgMo7ReAYMp7sg==
dependencies:
tslib "^2.0.0"
"@iarna/cli@^1.2.0": "@iarna/cli@^1.2.0":
version "1.2.0" version "1.2.0"
@@ -117,12 +131,12 @@
dependencies: dependencies:
debug "^4.1.1" debug "^4.1.1"
"@terminus-term/node-pty@0.10.0-beta11": "@terminus-term/node-pty@0.10.0-terminus.2":
version "0.10.0-beta11" version "0.10.0-terminus.2"
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta11.tgz#9e83041bc0644f17a4329c41a13d09575653c544" resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-terminus.2.tgz#028c7762d13150984bc800b8cd954ceb7dbcac68"
integrity sha512-ZrplD84UNV8qpCNpsG8DAo8fmoRP1Ynb9QsApIleCJjbeNvUP2ZAeR6wo0aV9B7Zqjvq5O4hoUSayIDGtORH+A== integrity sha512-vcscP3jldTMZeHv0XVxQjwEtnh0usUQgUWvsXtPRMy2rMjijwC1+8xFp/FKPpLpWYNTN8WWmRjSdiw+qGGU4CQ==
dependencies: dependencies:
node-addon-api "^3.0.2" nan "^2.14.0"
"@types/mz@0.0.32": "@types/mz@0.0.32":
version "0.0.32" version "0.0.32"
@@ -259,12 +273,10 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0" delegates "^1.0.0"
readable-stream "^2.0.6" readable-stream "^2.0.6"
argparse@^1.0.7: argparse@^2.0.1:
version "1.0.10" version "2.0.1"
resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
dependencies:
sprintf-js "~1.0.2"
asap@^2.0.0: asap@^2.0.0:
version "2.0.6" version "2.0.6"
@@ -837,10 +849,10 @@ electron-config@2.0.0:
dependencies: dependencies:
conf "^1.0.0" conf "^1.0.0"
electron-debug@^3.0.1: electron-debug@^3.2.0:
version "3.1.0" version "3.2.0"
resolved "https://registry.npmjs.org/electron-debug/-/electron-debug-3.1.0.tgz" resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-3.2.0.tgz#46a15b555c3b11872218c65ea01d058aa0814920"
integrity sha512-SWEqLj4MgfV3tGuO5eBLQ5/Nr6M+KPxsnE0bUJZvQebGJus6RAcdmvd7L+l0Ji31h2mmrN23l2tHFtCa2FvurA== integrity sha512-7xZh+LfUvJ52M9rn6N+tPuDw6oRAjxUj9SoxAZfJ0hVCXhZCsdkrSt7TgXOiWiEOBgEV8qwUIO/ScxllsPS7ow==
dependencies: dependencies:
electron-is-dev "^1.1.0" electron-is-dev "^1.1.0"
electron-localshortcut "^3.1.0" electron-localshortcut "^3.1.0"
@@ -850,7 +862,12 @@ electron-is-accelerator@^0.1.0:
resolved "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz" resolved "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz"
integrity sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns= integrity sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns=
electron-is-dev@1.1.0, electron-is-dev@^1.1.0: electron-is-dev@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.2.0.tgz#2e5cea0a1b3ccf1c86f577cee77363ef55deb05e"
integrity sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==
electron-is-dev@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.1.0.tgz" resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.1.0.tgz"
integrity sha512-Z1qA/1oHNowGtSBIcWk0pcLEqYT/j+13xUw/MYOrBUOL4X7VN0i0KCTf5SqyvMPmW5pSPKbo28wkxMxzZ20YnQ== integrity sha512-Z1qA/1oHNowGtSBIcWk0pcLEqYT/j+13xUw/MYOrBUOL4X7VN0i0KCTf5SqyvMPmW5pSPKbo28wkxMxzZ20YnQ==
@@ -972,11 +989,6 @@ escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
execa@^0.7.0: execa@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
@@ -1217,10 +1229,10 @@ github-from-package@0.0.0:
resolved "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz" resolved "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz"
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
glasstron@0.0.5: glasstron@0.0.6:
version "0.0.5" version "0.0.6"
resolved "https://registry.yarnpkg.com/glasstron/-/glasstron-0.0.5.tgz#41ed8109d1d9980a7112ccc3bd195b1aa17088bd" resolved "https://registry.yarnpkg.com/glasstron/-/glasstron-0.0.6.tgz#1438899cd49a1cda1999290be2165310b759fd62"
integrity sha512-1eX2wcW6AxO//gE2eE55M2JkwreHLZgYSmDb0zh//Iw8OgZbf+DTAPfGVUmMQQvGqVjNdao+epu9NISTZAa23A== integrity sha512-WBE1zH3ZcYhLlfKATfKzuN05Anno2WYlVfImWteU5RM+gqdFLHGIxxM3VR16JvFF5oIYah01MBGQ2+3xTq44iQ==
dependencies: dependencies:
node-addon-api "^3.0.0" node-addon-api "^3.0.0"
x11 "^2.3.0" x11 "^2.3.0"
@@ -1405,7 +1417,7 @@ inherits@2.0.3:
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 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" version "1.3.8"
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
@@ -1581,13 +1593,12 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
js-yaml@3.14.0: js-yaml@4.0.0:
version "3.14.0" version "4.0.0"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==
dependencies: dependencies:
argparse "^1.0.7" argparse "^2.0.1"
esprima "^4.0.0"
jsbn@~0.1.0: jsbn@~0.1.0:
version "0.1.1" version "0.1.1"
@@ -2073,7 +2084,7 @@ mz@^2.7.0:
object-assign "^4.0.1" object-assign "^4.0.1"
thenify-all "^1.0.0" 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" version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
@@ -2083,6 +2094,13 @@ napi-build-utils@^1.0.1:
resolved "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz" resolved "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz"
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
native-process-working-directory@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/native-process-working-directory/-/native-process-working-directory-1.0.2.tgz#7843e2fa1490f53cf8d2c7d1913de8b275e8b89a"
integrity sha512-3a67QQV8r3YMUTSOgvtMOCjPDgCpb/8xjv93L8Cqb8bv3hOKsWis4/+8HCu3bgj8ADQV75SCYFSsAGM5G0cXmQ==
dependencies:
node-addon-api "^3.1.0"
ngx-toastr@^12.0.1: ngx-toastr@^12.0.1:
version "12.1.0" version "12.1.0"
resolved "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-12.1.0.tgz" resolved "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-12.1.0.tgz"
@@ -2102,7 +2120,7 @@ node-addon-api@3.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz" resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz"
integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg== integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==
node-addon-api@^3.0.0, node-addon-api@^3.0.2: node-addon-api@^3.0.0, node-addon-api@^3.0.2, node-addon-api@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz" resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz"
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
@@ -2270,9 +2288,9 @@ npm-user-validate@^1.0.1:
integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw== integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==
npm@6: npm@6:
version "6.14.10" version "6.14.11"
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.10.tgz#f45c8e4244294ba793770f2ab0e9ce2d0b93fd29" resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.11.tgz#e0b5598d7b9a42d275e61d8bd28cd7eee0074a3b"
integrity sha512-FT23Qy/JMA+qxEYReMOr1MY7642fKn8Onn+72LASPi872Owvmw0svm+/DXTHOC3yO9CheEO+EslyXEpdBdRtIA== integrity sha512-1Zh7LjuIoEhIyjkBflSSGzfjuPQwDlghNloppjruOH5bmj9midT9qcNT0tRUZRR04shU9ekrxNy9+UTBrqeBpQ==
dependencies: dependencies:
JSONStream "^1.3.5" JSONStream "^1.3.5"
abbrev "~1.1.1" abbrev "~1.1.1"
@@ -2309,7 +2327,7 @@ npm@6:
infer-owner "^1.0.4" infer-owner "^1.0.4"
inflight "~1.0.6" inflight "~1.0.6"
inherits "^2.0.4" inherits "^2.0.4"
ini "^1.3.5" ini "^1.3.8"
init-package-json "^1.10.3" init-package-json "^1.10.3"
is-cidr "^3.0.0" is-cidr "^3.0.0"
json-parse-better-errors "^1.0.2" json-parse-better-errors "^1.0.2"
@@ -3025,16 +3043,16 @@ rxjs@^6.5.5:
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: 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.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:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 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: "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" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
@@ -3201,11 +3219,6 @@ split-on-first@^1.0.0:
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sshpk@^1.7.0: sshpk@^1.7.0:
version "1.16.1" version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz"

View File

@@ -8,8 +8,9 @@ afterAllArtifactBuild: "./build/mac/afterBuildHook.js"
files: files:
- '**/*' - '**/*'
- dist - dist
- '!lib'
- '!src' - '!src'
- '!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}' - '!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme,node.lib}'
- '!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples,docs}' - '!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples,docs}'
- '!**/node_modules/@angular/common/locales' - '!**/node_modules/@angular/common/locales'
- '!**/node_modules/@angular/compiler/src' - '!**/node_modules/@angular/compiler/src'

View File

@@ -2,32 +2,33 @@
"devDependencies": { "devDependencies": {
"@fortawesome/fontawesome-free": "^5.13.0", "@fortawesome/fontawesome-free": "^5.13.0",
"@sentry/cli": "^1.61.0", "@sentry/cli": "^1.61.0",
"@sentry/electron": "^2.0.4", "@sentry/electron": "^2.2.0",
"@terminus-term/to-string-loader": "1.1.7-beta.1",
"@types/electron-config": "^3.2.2", "@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0", "@types/electron-debug": "^2.1.0",
"@types/fs-extra": "^8.1.1", "@types/fs-extra": "^8.1.1",
"@types/js-yaml": "^3.12.5", "@types/js-yaml": "^3.12.5",
"@types/node": "14.14.14", "@types/node": "14.14.14",
"@types/webpack-env": "^1.16.0", "@types/webpack-env": "^1.16.0",
"@typescript-eslint/eslint-plugin": "^4.11.0", "@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.11.0", "@typescript-eslint/parser": "^4.14.1",
"apply-loader": "2.0.0", "apply-loader": "2.0.0",
"awesome-typescript-loader": "^5.2.1", "awesome-typescript-loader": "^5.2.1",
"compare-versions": "^3.6.0", "compare-versions": "^3.6.0",
"core-js": "^3.8.1", "core-js": "^3.8.3",
"cross-env": "7.0.2", "cross-env": "7.0.3",
"css-loader": "3.4.2", "css-loader": "5.0.1",
"electron": "^11.1.1", "electron": "12.0.0-beta.22",
"electron-builder": "22.10.4", "electron-builder": "22.10.4",
"electron-download": "^4.1.1", "electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0", "electron-installer-snap": "^5.1.0",
"electron-notarize": "^1.0.0", "electron-notarize": "^1.0.0",
"electron-rebuild": "^2.3.4", "electron-rebuild": "^2.3.4",
"eslint": "^7.6.0", "eslint": "^7.18.0",
"eslint-plugin-import": "^2.21.1", "eslint-plugin-import": "^2.21.1",
"file-loader": "^5.1.0", "file-loader": "^6.2.0",
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
"html-loader": "0.5.5", "html-loader": "1.3.2",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"lru-cache": "^6.0.0", "lru-cache": "^6.0.0",
"macos-release": "^2.4.1", "macos-release": "^2.4.1",
@@ -36,27 +37,27 @@
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"npmlog": "4.1.2", "npmlog": "4.1.2",
"npx": "^10.2.2", "npx": "^10.2.2",
"pug": "^2.0.4", "patch-package": "^6.2.2",
"pug": "^3.0.0",
"pug-html-loader": "1.1.5", "pug-html-loader": "1.1.5",
"pug-lint": "^2.6.0", "pug-lint": "^2.6.0",
"pug-loader": "^2.4.0", "pug-loader": "^2.4.0",
"pug-static-loader": "2.0.0", "pug-static-loader": "2.0.0",
"raw-loader": "4.0.1", "raw-loader": "4.0.2",
"sass-loader": "^10.1.0", "sass-loader": "^10.1.1",
"shelljs": "0.8.4", "shelljs": "0.8.4",
"source-code-pro": "^2.30.2", "source-code-pro": "^2.30.2",
"source-sans-pro": "3.6.0", "source-sans-pro": "3.6.0",
"ssh2-streams": "^0.4.10", "ssh2-streams": "^0.4.10",
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"svg-inline-loader": "^0.8.2", "svg-inline-loader": "^0.8.2",
"to-string-loader": "1.1.6",
"tslib": "^2.0.3", "tslib": "^2.0.3",
"typedoc": "^0.18.0", "typedoc": "^0.18.0",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"url-loader": "^3.0.0", "url-loader": "^4.1.1",
"val-loader": "2.1.1", "val-loader": "3.0.0",
"webpack": "^5.11.0", "webpack": "^5.18.0",
"webpack-cli": "^4.2.0", "webpack-cli": "^4.4.0",
"yaml-loader": "0.6.0" "yaml-loader": "0.6.0"
}, },
"resolutions": { "resolutions": {
@@ -74,7 +75,5 @@
"lint": "eslint --ext ts */src */lib", "lint": "eslint --ext ts */src */lib",
"postinstall": "node ./scripts/install-deps.js" "postinstall": "node ./scripts/install-deps.js"
}, },
"repository": "eugeny/terminus", "private": true
"author": "Eugene Pankov",
"license": "MIT"
} }

View File

@@ -0,0 +1,12 @@
diff --git a/node_modules/node-abi/abi_registry.json b/node_modules/node-abi/abi_registry.json
index bc1436d..630c1b7 100644
--- a/node_modules/node-abi/abi_registry.json
+++ b/node_modules/node-abi/abi_registry.json
@@ -94,6 +94,6 @@
"future": true,
"lts": false,
"runtime": "electron",
- "target": "12.0.0-nightly.20201013"
+ "target": "12.0.0-beta.16"
}
]

View File

@@ -1,8 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
const builder = require('electron-builder').build const builder = require('electron-builder').build
const vars = require('./vars') const vars = require('./vars')
const fs = require('fs')
const signHook = require('../build/mac/afterSignHook')
const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/') const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/')
@@ -16,6 +14,7 @@ builder({
extraMetadata: { extraMetadata: {
version: vars.version, version: vars.version,
}, },
npmRebuild: process.env.ARCH !== 'arm64',
}, },
publish: isTag ? 'always' : 'onTag', publish: isTag ? 'always' : 'onTag',
}).catch(e => { }).catch(e => {

24
scripts/sentry-upload.js Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env node
const sh = require('shelljs')
const vars = require('./vars')
const sentryCli = process.platform === 'win32' ? 'node_modules\\.bin\\sentry-cli.cmd' : 'sentry-cli'
sh.exec(`${sentryCli} releases new ${vars.version}`)
if (process.platform === 'darwin') {
for (const path of [
'app/node_modules/@serialport/bindings/build/Release/bindings.node',
'app/node_modules/@terminus-term/node-pty/build/Release/pty.node',
'app/node_modules/fontmanager-redux/build/Release/fontmanager.node',
'app/node_modules/macos-native-processlist/build/Release/native.node',
]) {
sh.exec('dsymutil ' + path)
}
}
sh.exec(`${sentryCli} upload-dif app/node_modules`)
sh.exec(`${sentryCli} releases set-commits --auto ${vars.version}`)
for (const p of vars.builtinPlugins) {
sh.exec(`${sentryCli} releases files ${vars.version} upload-sourcemaps ${p}/dist -u ${p}/dist/ -d ${process.platform}-${p}`)
}

View File

@@ -1,14 +1,14 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal' import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal'
const schemeContents = require.context('../schemes/', true, /.*/) const schemeContents = require.context('../schemes/', false, /.*/)
@Injectable() @Injectable()
export class ColorSchemes extends TerminalColorSchemeProvider { export class ColorSchemes extends TerminalColorSchemeProvider {
async getSchemes (): Promise<TerminalColorScheme[]> { async getSchemes (): Promise<TerminalColorScheme[]> {
const schemes: 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') const lines = (schemeContents(schemeFile).default as string).split('\n')
// process #define variables // process #define variables

View File

@@ -26,8 +26,8 @@ $side-tab-width: 200px;
.content { .content {
width: 100vw; width: 100vw;
height: 100vh; flex: 1 1 0;
flex: auto; min-height: 0;
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;

View File

@@ -142,6 +142,8 @@ export class AppRootComponent {
this.touchbar.update() this.touchbar.update()
this.hostApp.useBuiltinGraphics()
config.changed$.subscribe(() => this.updateVibrancy()) config.changed$.subscribe(() => this.updateVibrancy())
this.updateVibrancy() this.updateVibrancy()

View File

@@ -1,4 +1,4 @@
import { Component, Input, HostListener, ViewChildren, QueryList, ElementRef } from '@angular/core' import { Component, Input, HostListener, ViewChildren, QueryList, ElementRef } from '@angular/core' // eslint-disable-line @typescript-eslint/no-unused-vars
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { SelectorOption } from '../api/selector' import { SelectorOption } from '../api/selector'

View File

@@ -110,7 +110,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
}) })
} }
static forRoot (): ModuleWithProviders { static forRoot (): ModuleWithProviders<AppModule> {
return { return {
ngModule: AppModule, ngModule: AppModule,
providers: PROVIDERS, providers: PROVIDERS,

View File

@@ -49,10 +49,10 @@ export class AppService {
get activeTab (): BaseTabComponent|null { return this._activeTab ?? null } get activeTab (): BaseTabComponent|null { return this._activeTab ?? null }
private lastTabIndex = 0 private lastTabIndex = 0
private _activeTab?: BaseTabComponent private _activeTab: BaseTabComponent | null = null
private closedTabsStack: RecoveryToken[] = [] private closedTabsStack: RecoveryToken[] = []
private activeTabChange = new Subject<BaseTabComponent>() private activeTabChange = new Subject<BaseTabComponent|null>()
private tabsChanged = new Subject<void>() private tabsChanged = new Subject<void>()
private tabOpened = new Subject<BaseTabComponent>() private tabOpened = new Subject<BaseTabComponent>()
private tabClosed = new Subject<BaseTabComponent>() private tabClosed = new Subject<BaseTabComponent>()
@@ -60,7 +60,7 @@ export class AppService {
private completionObservers = new Map<BaseTabComponent, CompletionObserver>() private completionObservers = new Map<BaseTabComponent, CompletionObserver>()
get activeTabChange$ (): Observable<BaseTabComponent> { return this.activeTabChange } get activeTabChange$ (): Observable<BaseTabComponent|null> { return this.activeTabChange }
get tabOpened$ (): Observable<BaseTabComponent> { return this.tabOpened } get tabOpened$ (): Observable<BaseTabComponent> { return this.tabOpened }
get tabsChanged$ (): Observable<void> { return this.tabsChanged } get tabsChanged$ (): Observable<void> { return this.tabsChanged }
get tabClosed$ (): Observable<BaseTabComponent> { return this.tabClosed } get tabClosed$ (): Observable<BaseTabComponent> { return this.tabClosed }
@@ -185,8 +185,8 @@ export class AppService {
return null return null
} }
selectTab (tab: BaseTabComponent): void { selectTab (tab: BaseTabComponent|null): void {
if (this._activeTab === tab) { if (tab && this._activeTab === tab) {
this._activeTab.emitFocused() this._activeTab.emitFocused()
return return
} }
@@ -204,7 +204,7 @@ export class AppService {
setImmediate(() => { setImmediate(() => {
this._activeTab?.emitFocused() this._activeTab?.emitFocused()
}) })
this.hostApp.setTitle(this._activeTab.title) this.hostApp.setTitle(this._activeTab?.title)
} }
getParentTab (tab: BaseTabComponent): SplitTabComponent|null { getParentTab (tab: BaseTabComponent): SplitTabComponent|null {

View File

@@ -144,7 +144,7 @@ export class ConfigService {
load (): void { load (): void {
if (fs.existsSync(this.path)) { if (fs.existsSync(this.path)) {
this._store = yaml.safeLoad(fs.readFileSync(this.path, 'utf8')) this._store = yaml.load(fs.readFileSync(this.path, 'utf8'))
} else { } else {
this._store = {} this._store = {}
} }
@@ -154,7 +154,7 @@ export class ConfigService {
save (): void { save (): void {
// Scrub undefined values // Scrub undefined values
this._store = JSON.parse(JSON.stringify(this._store)) this._store = JSON.parse(JSON.stringify(this._store))
fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8') fs.writeFileSync(this.path, yaml.dump(this._store), 'utf8')
this.emitChange() this.emitChange()
this.hostApp.broadcastConfigChange(JSON.parse(JSON.stringify(this.store))) this.hostApp.broadcastConfigChange(JSON.parse(JSON.stringify(this.store)))
} }
@@ -163,14 +163,14 @@ export class ConfigService {
* Reads config YAML as string * Reads config YAML as string
*/ */
readRaw (): string { readRaw (): string {
return yaml.safeDump(this._store) return yaml.dump(this._store)
} }
/** /**
* Writes config YAML as string * Writes config YAML as string
*/ */
writeRaw (data: string): void { writeRaw (data: string): void {
this._store = yaml.safeLoad(data) this._store = yaml.load(data)
this.save() this.save()
this.load() this.load()
this.emitChange() this.emitChange()

View File

@@ -1,5 +1,6 @@
import type { BrowserWindow, TouchBar, MenuItemConstructorOptions } from 'electron' import type { BrowserWindow, TouchBar, MenuItemConstructorOptions } from 'electron'
import * as path from 'path' import * as path from 'path'
import * as fs from 'mz/fs'
import shellEscape from 'shell-escape' import 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'
@@ -7,6 +8,12 @@ import { ElectronService } from './electron.service'
import { Logger, LogService } from './log.service' import { Logger, LogService } from './log.service'
import { isWindowsBuild, WIN_BUILD_FLUENT_BG_SUPPORTED } from '../utils' import { isWindowsBuild, WIN_BUILD_FLUENT_BG_SUPPORTED } from '../utils'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch (_) { }
export enum Platform { export enum Platform {
Linux = 'Linux', Linux = 'Linux',
macOS = 'macOS', macOS = 'macOS',
@@ -150,9 +157,10 @@ export class HostAppService {
this.zone.run(() => this.displaysChanged.next()) 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) this.logger.info('Second instance', argv)
const op = argv._[0] const op = argv._[0]
const opAsPath = op ? path.resolve(cwd, op) : null
if (op === 'open') { if (op === 'open') {
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory)) this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
} else if (op === 'run') { } else if (op === 'run') {
@@ -165,9 +173,13 @@ export class HostAppService {
this.cliPaste.next(text) this.cliPaste.next(text)
} else if (op === 'profile') { } else if (op === 'profile') {
this.cliOpenProfile.next(argv.profileName) this.cliOpenProfile.next(argv.profileName)
} else if (op === undefined) { } else if (secondInstance && op === undefined) {
this.newWindow() this.newWindow()
} else { } else if (opAsPath && (await fs.lstat(opAsPath)).isDirectory()) {
this.cliOpenDirectory.next(opAsPath)
}
if (secondInstance) {
this.secondInstance.next() this.secondInstance.next()
} }
})) }))
@@ -242,8 +254,8 @@ export class HostAppService {
this.electron.ipcRenderer.send('window-set-vibrancy', enable, type) this.electron.ipcRenderer.send('window-set-vibrancy', enable, type)
} }
setTitle (title: string): void { setTitle (title?: string): void {
this.electron.ipcRenderer.send('window-set-title', title) this.electron.ipcRenderer.send('window-set-title', title ?? 'Terminus')
} }
setTouchBar (touchBar: TouchBar): void { setTouchBar (touchBar: TouchBar): void {
@@ -277,6 +289,16 @@ export class HostAppService {
this.electron.ipcRenderer.send('app:register-global-hotkey', specs) this.electron.ipcRenderer.send('app:register-global-hotkey', specs)
} }
useBuiltinGraphics (): void {
const keyPath = 'SOFTWARE\\Microsoft\\DirectX\\UserGpuPreferences'
const valueName = this.electron.app.getPath('exe')
if (this.platform === Platform.Windows) {
if (!wnr.getRegistryValue(wnr.HK.CU, keyPath, valueName)) {
wnr.setRegistryValue(wnr.HK.CU, keyPath, valueName, wnr.REG.SZ, 'GpuPreference=1;')
}
}
}
relaunch (): void { relaunch (): void {
if (this.isPortable) { if (this.isPortable) {
this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE }) this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })

View File

@@ -35,6 +35,10 @@ export class UpdaterService {
this.logger.info('No updates') this.logger.info('No updates')
}) })
electron.autoUpdater.once('error', err => {
this.logger.error(err)
})
this.downloaded = new Promise<boolean>(resolve => { this.downloaded = new Promise<boolean>(resolve => {
electron.autoUpdater.once('update-downloaded', () => resolve(true)) electron.autoUpdater.once('update-downloaded', () => resolve(true))
}) })

View File

@@ -40,8 +40,8 @@ module.exports = {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader'], include: /component\.css/ }, { test: /\.css$/, use: ['@terminus-term/to-string-loader', 'css-loader'], include: /component\.css/ },
{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ }, { test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ },
{ test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] }, { test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] }, { test: /\.svg/, use: ['svg-inline-loader'] },

View File

@@ -124,9 +124,9 @@ colorspace@1.1.x:
text-hex "1.0.x" text-hex "1.0.x"
core-js@^3.1.2: core-js@^3.1.2:
version "3.7.0" version "3.8.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.7.0.tgz#b0a761a02488577afbf97179e4681bf49568520f" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.2.tgz#0a1fd6709246da9ca8eff5bb0cbd15fba9ac7044"
integrity sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA== integrity sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"

View File

@@ -40,7 +40,7 @@ module.exports = {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
], ],
}, },
externals: [ externals: [

View File

@@ -20,6 +20,7 @@
"@types/node": "14.14.14", "@types/node": "14.14.14",
"@types/ssh2": "^0.5.35", "@types/ssh2": "^0.5.35",
"ansi-colors": "^4.1.1", "ansi-colors": "^4.1.1",
"buffer-replace": "^1.0.0",
"cli-spinner": "^0.2.10" "cli-spinner": "^0.2.10"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -1,7 +1,12 @@
import stripAnsi from 'strip-ansi'
import bufferReplace from 'buffer-replace'
import { BaseSession } from 'terminus-terminal' import { BaseSession } from 'terminus-terminal'
import { SerialPort } from 'serialport' import { SerialPort } from 'serialport'
import { Logger } from 'terminus-core' import { Logger } from 'terminus-core'
import { Subject, Observable } from 'rxjs' import { Subject, Observable, interval } from 'rxjs'
import { debounce } from 'rxjs/operators'
import { ReadLine, createInterface as createReadline, clearLine } from 'readline'
import { PassThrough, Readable, Writable } from 'stream'
export interface LoginScript { export interface LoginScript {
expect: string expect: string
@@ -23,10 +28,13 @@ export interface SerialConnection {
xany: boolean xany: boolean
scripts?: LoginScript[] scripts?: LoginScript[]
color?: string color?: string
inputMode?: InputMode
inputNewlines?: NewlineMode
outputNewlines?: NewlineMode
} }
export const BAUD_RATES = [ export const BAUD_RATES = [
110, 150, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 110, 150, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 1500000,
] ]
export interface SerialPortInfo { export interface SerialPortInfo {
@@ -34,6 +42,9 @@ export interface SerialPortInfo {
description?: string description?: string
} }
export type InputMode = null | 'readline' // eslint-disable-line @typescript-eslint/no-type-alias
export type NewlineMode = null | 'cr' | 'lf' | 'crlf' // eslint-disable-line @typescript-eslint/no-type-alias
export class SerialSession extends BaseSession { export class SerialSession extends BaseSession {
scripts?: LoginScript[] scripts?: LoginScript[]
serial: SerialPort serial: SerialPort
@@ -41,17 +52,139 @@ export class SerialSession extends BaseSession {
get serviceMessage$ (): Observable<string> { return this.serviceMessage } get serviceMessage$ (): Observable<string> { return this.serviceMessage }
private serviceMessage = new Subject<string>() private serviceMessage = new Subject<string>()
private inputReadline: ReadLine
private inputPromptVisible = true
private inputReadlineInStream: Readable & Writable
private inputReadlineOutStream: Readable & Writable
constructor (public connection: SerialConnection) { constructor (public connection: SerialConnection) {
super() super()
this.scripts = connection.scripts ?? [] this.scripts = connection.scripts ?? []
this.inputReadlineInStream = new PassThrough()
this.inputReadlineOutStream = new PassThrough()
this.inputReadline = createReadline({
input: this.inputReadlineInStream,
output: this.inputReadlineOutStream,
terminal: true,
} as any)
this.inputReadlineOutStream.on('data', data => {
if (this.connection.inputMode === 'readline') {
this.emitOutput(data)
}
})
this.inputReadline.on('line', line => {
this.onInput(new Buffer(line + '\n'))
})
this.output$.pipe(debounce(() => interval(500))).subscribe(() => this.onOutputSettled())
} }
async start (): Promise<void> { async start (): Promise<void> {
this.open = true this.open = true
this.serial.on('data', data => { this.serial.on('readable', () => {
this.onOutput(this.serial.read())
})
this.serial.on('end', () => {
this.logger.info('Shell session ended')
if (this.open) {
this.destroy()
}
})
this.executeUnconditionalScripts()
}
write (data: Buffer): void {
if (this.connection.inputMode === 'readline') {
this.inputReadlineInStream.write(data)
} else {
this.onInput(data)
}
}
async destroy (): Promise<void> {
this.serviceMessage.complete()
this.inputReadline.close()
await super.destroy()
}
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
resize (_, __) {
this.inputReadlineOutStream.emit('resize')
}
kill (_?: string): void {
this.serial.close()
}
emitServiceMessage (msg: string): void {
this.serviceMessage.next(msg)
this.logger.info(stripAnsi(msg))
}
async getChildProcesses (): Promise<any[]> {
return []
}
async gracefullyKillProcess (): Promise<void> {
this.kill('TERM')
}
supportsWorkingDirectory (): boolean {
return false
}
async getWorkingDirectory (): Promise<string|null> {
return null
}
private replaceNewlines (data: Buffer, mode?: NewlineMode): Buffer {
if (!mode) {
return data
}
data = bufferReplace(data, '\r\n', '\n')
data = bufferReplace(data, '\r', '\n')
const replacement = {
strip: '',
cr: '\r',
lf: '\n',
crlf: '\r\n',
}[mode]
return bufferReplace(data, '\n', replacement)
}
private onInput (data: Buffer) {
data = this.replaceNewlines(data, this.connection.inputNewlines)
if (this.serial) {
this.serial.write(data.toString())
}
}
private onOutputSettled () {
if (this.connection.inputMode === 'readline' && !this.inputPromptVisible) {
this.resetInputPrompt()
}
}
private resetInputPrompt () {
this.emitOutput(new Buffer('\r\n'))
this.inputReadline.prompt(true)
this.inputPromptVisible = true
}
private onOutput (data: Buffer) {
const dataString = data.toString() const dataString = data.toString()
if (this.connection.inputMode === 'readline') {
if (this.inputPromptVisible) {
clearLine(this.inputReadlineOutStream, 0)
this.inputPromptVisible = false
}
}
data = this.replaceNewlines(data, this.connection.outputNewlines)
this.emitOutput(data) this.emitOutput(data)
if (this.scripts) { if (this.scripts) {
@@ -61,7 +194,7 @@ export class SerialSession extends BaseSession {
let cmd = '' let cmd = ''
if (script.isRegex) { if (script.isRegex) {
const re = new RegExp(script.expect, 'g') const re = new RegExp(script.expect, 'g')
if (dataString.match(re)) { if (re.test(dataString)) {
cmd = dataString.replace(re, script.send) cmd = dataString.replace(re, script.send)
match = true match = true
found = true found = true
@@ -93,46 +226,6 @@ export class SerialSession extends BaseSession {
this.executeUnconditionalScripts() this.executeUnconditionalScripts()
} }
} }
})
this.serial.on('end', () => {
this.logger.info('Shell session ended')
if (this.open) {
this.destroy()
}
})
this.executeUnconditionalScripts()
}
write (data: Buffer): void {
if (this.serial) {
this.serial.write(data.toString())
}
}
async destroy (): Promise<void> {
this.serviceMessage.complete()
await super.destroy()
}
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
resize (_, __) { }
kill (_?: string): void {
this.serial.close()
}
async getChildProcesses (): Promise<any[]> {
return []
}
async gracefullyKillProcess (): Promise<void> {
this.kill('TERM')
}
async getWorkingDirectory (): Promise<string|null> {
return null
} }
private executeUnconditionalScripts () { private executeUnconditionalScripts () {

View File

@@ -11,6 +11,8 @@
[(ngModel)]='connection.name', [(ngModel)]='connection.name',
) )
.row
.col-6
.form-group .form-group
label Path label Path
input.form-control( input.form-control(
@@ -20,6 +22,7 @@
[resultFormatter]='portsFormatter', [resultFormatter]='portsFormatter',
) )
.col-6
.form-group .form-group
label Baud Rate label Baud Rate
select.form-control( select.form-control(
@@ -27,6 +30,66 @@
) )
option([value]='x', *ngFor='let x of baudRates') {{x}} option([value]='x', *ngFor='let x of baudRates') {{x}}
.row
.col-6
.form-line
.header
.title Input mode
.d-flex(ngbDropdown)
button.btn.btn-secondary.btn-tab-bar(
ngbDropdownToggle,
) {{getInputModeName(connection.inputMode)}}
div(ngbDropdownMenu)
a.d-flex.flex-column(
*ngFor='let mode of inputModes',
(click)='connection.inputMode = mode.key',
ngbDropdownItem
)
div {{mode.name}}
.text-muted {{mode.description}}
.col-6
.form-line
.header
.title Input newlines
select.form-control(
[(ngModel)]='connection.inputNewlines',
)
option([ngValue]='mode.key', *ngFor='let mode of newlineModes') {{mode.name}}
.row
.col-6
//- .form-line
.header
.title Output mode
.d-flex(ngbDropdown)
button.btn.btn-secondary.btn-tab-bar(
ngbDropdownToggle,
) {{getInputModeName(connection.inputMode)}}
div(ngbDropdownMenu)
a.d-flex.flex-column(
*ngFor='let mode of inputModes',
(click)='connection.inputMode = mode.key',
ngbDropdownItem
)
div {{mode.name}}
.text-muted {{mode.description}}
.col-6
.form-line
.header
.title Output newlines
select.form-control(
[(ngModel)]='connection.outputNewlines',
)
option([ngValue]='mode.key', *ngFor='let mode of newlineModes') {{mode.name}}
ngb-tab(id='advanced') ngb-tab(id='advanced')
ng-template(ngbTabTitle) Advanced ng-template(ngbTabTitle) Advanced
ng-template(ngbTabContent) ng-template(ngbTabContent)

View File

@@ -15,6 +15,17 @@ export class EditConnectionModalComponent {
connection: SerialConnection connection: SerialConnection
foundPorts: SerialPortInfo[] foundPorts: SerialPortInfo[]
baudRates = BAUD_RATES baudRates = BAUD_RATES
inputModes = [
{ key: null, name: 'Normal', description: 'Input is sent as you type' },
{ key: 'readline', name: 'Line by line', description: 'Line editor, input is sent after you press Enter' },
]
newlineModes = [
{ key: null, name: 'Keep' },
{ key: 'strip', name: 'Strip' },
{ key: 'cr', name: 'Force CR' },
{ key: 'lf', name: 'Force LF' },
{ key: 'crlf', name: 'Force CRLF' },
]
constructor ( constructor (
private modalInstance: NgbActiveModal, private modalInstance: NgbActiveModal,
@@ -24,6 +35,10 @@ export class EditConnectionModalComponent {
) { ) {
} }
getInputModeName (key) {
return this.inputModes.find(x => x.key === key)?.name
}
portsAutocomplete = text$ => text$.pipe(map(() => { portsAutocomplete = text$ => text$.pipe(map(() => {
return this.foundPorts.map(x => x.name) return this.foundPorts.map(x => x.name)
})) }))

View File

@@ -34,6 +34,9 @@ export class SerialSettingsTabComponent {
xany: false, xany: false,
xoff: false, xoff: false,
xon: false, xon: false,
inputMode: null,
inputNewlines: null,
outputNewlines: null,
} }
const modal = this.ngbModal.open(EditConnectionModalComponent) const modal = this.ngbModal.open(EditConnectionModalComponent)

View File

@@ -1,16 +1,16 @@
.tab-toolbar .tab-toolbar
.btn.btn-outline-secondary.reveal-button .btn.btn-outline-secondary.reveal-button
i.fas.fa-ellipsis-h i.fas.fa-ellipsis-h
.toolbar(*ngIf='session', [class.show]='!session.open') .toolbar([class.show]='!session || !session.open')
i.fas.fa-circle.text-success.mr-2(*ngIf='session.open') i.fas.fa-circle.text-success.mr-2(*ngIf='session && session.open')
i.fas.fa-circle.text-danger.mr-2(*ngIf='!session.open') i.fas.fa-circle.text-danger.mr-2(*ngIf='!session || !session.open')
strong(*ngIf='session') {{session.connection.port}} ({{session.connection.baudrate}}) strong(*ngIf='session') {{session.connection.port}} ({{session.connection.baudrate}})
.mr-auto .mr-auto
button.btn.btn-secondary.mr-3((click)='changeBaudRate()', *ngIf='session.open') button.btn.btn-secondary.mr-3((click)='changeBaudRate()', *ngIf='session && session.open')
span Change baud rate span Change baud rate
button.btn.btn-info((click)='reconnect()', *ngIf='!session.open') button.btn.btn-info((click)='reconnect()', *ngIf='!session || !session.open')
i.fas.fa-reload i.fas.fa-reload
span Reconnect span Reconnect

View File

@@ -11,14 +11,15 @@ import { Subscription } from 'rxjs'
/** @hidden */ /** @hidden */
@Component({ @Component({
selector: 'serial-tab', selector: 'serial-tab',
template: BaseTerminalTabComponent.template + (require('./serialTab.component.pug') as string), template: `${BaseTerminalTabComponent.template} ${require('./serialTab.component.pug')}`,
styles: [require('./serialTab.component.scss'), ...BaseTerminalTabComponent.styles], styles: [require('./serialTab.component.scss'), ...BaseTerminalTabComponent.styles],
animations: BaseTerminalTabComponent.animations, animations: BaseTerminalTabComponent.animations,
}) })
export class SerialTabComponent extends BaseTerminalTabComponent { export class SerialTabComponent extends BaseTerminalTabComponent {
connection?: SerialConnection connection?: SerialConnection
session?: SerialSession session: SerialSession|null = null
serialPort: any serialPort: any
private serialService: SerialService
private homeEndSubscription: Subscription private homeEndSubscription: Subscription
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -26,6 +27,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
injector: Injector, injector: Injector,
) { ) {
super(injector) super(injector)
this.serialService = injector.get(SerialService)
} }
ngOnInit () { ngOnInit () {
@@ -62,12 +64,8 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
return return
} }
this.session = this.injector.get(SerialService).createSession(this.connection) const session = this.serialService.createSession(this.connection)
this.session.serviceMessage$.subscribe(msg => { this.setSession(session)
this.write(`\r\n${colors.black.bgWhite(' serial ')} ${msg}\r\n`)
this.session?.resize(this.size.columns, this.size.rows)
})
this.attachSessionHandlers()
this.write(`Connecting to `) this.write(`Connecting to `)
const spinner = new Spinner({ const spinner = new Spinner({
@@ -80,15 +78,32 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
spinner.start() spinner.start()
try { try {
this.serialPort = await this.injector.get(SerialService).connectSession(this.session) this.serialPort = await this.serialService.connectSession(this.session!)
spinner.stop(true) spinner.stop(true)
session.emitServiceMessage('Port opened')
} catch (e) { } catch (e) {
spinner.stop(true) spinner.stop(true)
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n') this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
return return
} }
await this.session.start() await this.session!.start()
this.session.resize(this.size.columns, this.size.rows) this.session!.resize(this.size.columns, this.size.rows)
}
protected attachSessionHandlers () {
this.attachSessionHandler(this.session!.serviceMessage$.subscribe(msg => {
this.write(`\r\n${colors.black.bgWhite(' Serial ')} ${msg}\r\n`)
this.session?.resize(this.size.columns, this.size.rows)
}))
this.attachSessionHandler(this.session!.destroyed$.subscribe(() => {
this.write('Press any key to reconnect\r\n')
this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open) {
this.reconnect()
}
})
}))
super.attachSessionHandlers()
} }
async getRecoveryToken (): Promise<any> { async getRecoveryToken (): Promise<any> {
@@ -99,8 +114,10 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
} }
} }
reconnect () { async reconnect (): Promise<void> {
this.initializeSession() this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()
} }
async changeBaudRate () { async changeBaudRate () {

View File

@@ -30,10 +30,17 @@ export class SerialService {
} }
async connectSession (session: SerialSession): Promise<SerialPort> { async connectSession (session: SerialSession): Promise<SerialPort> {
const serial = new SerialPort(session.connection.port, { autoOpen: false, baudRate: session.connection.baudrate, const serial = new SerialPort(session.connection.port, {
dataBits: session.connection.databits, stopBits: session.connection.stopbits, parity: session.connection.parity, autoOpen: false,
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff, baudRate: session.connection.baudrate,
xany: session.connection.xany }) dataBits: session.connection.databits,
stopBits: session.connection.stopbits,
parity: session.connection.parity,
rtscts: session.connection.rtscts,
xon: session.connection.xon,
xoff: session.connection.xoff,
xany: session.connection.xany,
})
session.serial = serial session.serial = serial
let connected = false let connected = false
await new Promise(async (resolve, reject) => { await new Promise(async (resolve, reject) => {
@@ -50,6 +57,10 @@ export class SerialService {
} }
}) })
}) })
serial.on('close', () => {
session.emitServiceMessage('Port closed')
session.destroy()
})
try { try {
serial.open() serial.open()
@@ -103,7 +114,7 @@ export class SerialService {
options.push({ options.push({
name: 'Manage connections', name: 'Manage connections',
icon: 'cog', icon: 'cog',
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' }), callback: () => this.app.openNewTabRaw(SettingsTabComponent, { activeTab: 'serial' }),
}) })
options.push({ options.push({

View File

@@ -40,7 +40,7 @@ module.exports = {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] }, { test: /\.svg/, use: ['svg-inline-loader'] },
], ],
}, },
@@ -50,6 +50,8 @@ module.exports = {
'path', 'path',
'ngx-toastr', 'ngx-toastr',
'serialport', 'serialport',
'readline',
'stream',
'windows-process-tree/build/Release/windows_process_tree.node', 'windows-process-tree/build/Release/windows_process_tree.node',
/^rxjs/, /^rxjs/,
/^@angular/, /^@angular/,

View File

@@ -32,6 +32,11 @@ ansi-colors@^4.1.1:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
buffer-replace@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-replace/-/buffer-replace-1.0.0.tgz#bc427c40af4c1f06d6933dede57110acba8ade54"
integrity sha1-vEJ8QK9MHwbWkz3t5XEQrLqK3lQ=
cli-spinner@^0.2.10: cli-spinner@^0.2.10:
version "0.2.10" version "0.2.10"
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47" resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"

View File

@@ -307,7 +307,7 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
td {{hotkey.id}} td {{hotkey.id}}
td.pr-5 td.pr-5
multi-hotkey-input( multi-hotkey-input(
[model]='getHotkey(hotkey.id)', [model]='getHotkey(hotkey.id) || []',
(modelChange)='setHotkey(hotkey.id, $event); config.save(); docking.dock()' (modelChange)='setHotkey(hotkey.id, $event); config.save(); docking.dock()'
) )

View File

@@ -60,7 +60,7 @@ export class SettingsTabComponent extends BaseTabComponent {
this.settingsProviders = config.enabledServices(this.settingsProviders) this.settingsProviders = config.enabledServices(this.settingsProviders)
this.themes = config.enabledServices(this.themes) this.themes = config.enabledServices(this.themes)
this.configDefaults = yaml.safeDump(config.getDefaults()) this.configDefaults = yaml.dump(config.getDefaults())
const onConfigChange = () => { const onConfigChange = () => {
this.configFile = config.readRaw() this.configFile = config.readRaw()
@@ -116,7 +116,7 @@ export class SettingsTabComponent extends BaseTabComponent {
isConfigFileValid () { isConfigFileValid () {
try { try {
yaml.safeLoad(this.configFile) yaml.load(this.configFile)
return true return true
} catch (_) { } catch (_) {
return false return false

View File

@@ -40,8 +40,8 @@ module.exports = {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.css$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] }, { test: /\.svg/, use: ['svg-inline-loader'] },
], ],
}, },

View File

@@ -140,6 +140,9 @@ export class SSHSession extends BaseSession {
this.shell = await this.openShellChannel({ x11: this.connection.x11 }) this.shell = await this.openShellChannel({ x11: this.connection.x11 })
} catch (err) { } catch (err) {
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote rejected opening a shell channel: ${err}`) this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote rejected opening a shell channel: ${err}`)
if (err.toString().includes('Unable to request X11')) {
this.emitServiceMessage(' Make sure `xauth` is installed on the remote side')
}
return return
} }
@@ -245,7 +248,13 @@ export class SSHSession extends BaseSession {
} }
socket.on('error', e => { socket.on('error', e => {
// eslint-disable-next-line @typescript-eslint/no-base-to-string // eslint-disable-next-line @typescript-eslint/no-base-to-string
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not connect to the X server ${xHost}:${xPort}: ${e}`) this.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not connect to the X server: ${e}`)
this.emitServiceMessage(` Terminus tried to connect to ${xHost}:${xPort} based on the DISPLAY environment var (${displaySpec})`)
if (process.platform === 'win32') {
this.emitServiceMessage(' To use X forwarding, you need a local X server, e.g.:')
this.emitServiceMessage(' * VcXsrv: https://sourceforge.net/projects/vcxsrv/')
this.emitServiceMessage(' * Xming: https://sourceforge.net/projects/xming/')
}
reject() reject()
}) })
socket.on('connect', () => { socket.on('connect', () => {
@@ -363,6 +372,10 @@ export class SSHSession extends BaseSession {
this.kill('TERM') this.kill('TERM')
} }
supportsWorkingDirectory (): boolean {
return true
}
async getWorkingDirectory (): Promise<string|null> { async getWorkingDirectory (): Promise<string|null> {
return null return null
} }
@@ -398,3 +411,9 @@ export interface SSHConnectionGroup {
name: string name: string
connections: SSHConnection[] 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',
]

View File

@@ -103,7 +103,7 @@
.header .header
.title Jump host .title Jump host
select.form-control([(ngModel)]='connection.jumpHost') 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}} option([ngValue]='x.name', *ngFor='let x of config.store.ssh.connections') {{x.name}}
.form-line .form-line

View File

@@ -3,7 +3,7 @@ import { Component } from '@angular/core'
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ElectronService, HostAppService, ConfigService } from 'terminus-core' import { ElectronService, HostAppService, ConfigService } from 'terminus-core'
import { PasswordStorageService } from '../services/passwordStorage.service' 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 { PromptModalComponent } from './promptModal.component'
import { ALGORITHMS } from 'ssh2-streams/lib/constants' import { ALGORITHMS } from 'ssh2-streams/lib/constants'
@@ -40,8 +40,8 @@ export class EditConnectionModalComponent {
[SSHAlgorithmType.CIPHER]: 'CIPHER', [SSHAlgorithmType.CIPHER]: 'CIPHER',
[SSHAlgorithmType.HMAC]: 'HMAC', [SSHAlgorithmType.HMAC]: 'HMAC',
}[k] }[k]
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg] this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg] this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
} }
} }

View File

@@ -14,15 +14,17 @@ import { Subscription } from 'rxjs'
/** @hidden */ /** @hidden */
@Component({ @Component({
selector: 'ssh-tab', selector: 'ssh-tab',
template: BaseTerminalTabComponent.template + (require('./sshTab.component.pug') as string), template: `${BaseTerminalTabComponent.template} ${require('./sshTab.component.pug')}`,
styles: [require('./sshTab.component.scss'), ...BaseTerminalTabComponent.styles], styles: [require('./sshTab.component.scss'), ...BaseTerminalTabComponent.styles],
animations: BaseTerminalTabComponent.animations, animations: BaseTerminalTabComponent.animations,
}) })
export class SSHTabComponent extends BaseTerminalTabComponent { export class SSHTabComponent extends BaseTerminalTabComponent {
connection?: SSHConnection connection?: SSHConnection
session?: SSHSession session: SSHSession|null = null
private sessionStack: SSHSession[] = [] private sessionStack: SSHSession[] = []
private homeEndSubscription: Subscription private homeEndSubscription: Subscription
private recentInputs = ''
private reconnectOffered = false
constructor ( constructor (
injector: Injector, injector: Injector,
@@ -57,6 +59,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.frontendReady$.pipe(first()).subscribe(() => { this.frontendReady$.pipe(first()).subscribe(() => {
this.initializeSession() this.initializeSession()
this.input$.subscribe(data => {
this.recentInputs += data
this.recentInputs = this.recentInputs.substring(this.recentInputs.length - 32)
})
}) })
super.ngOnInit() super.ngOnInit()
@@ -68,12 +74,23 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
async setupOneSession (session: SSHSession): Promise<void> { async setupOneSession (session: SSHSession): Promise<void> {
if (session.connection.jumpHost) { 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) const jumpSession = this.ssh.createSession(jumpConnection)
await this.setupOneSession(jumpSession) await this.setupOneSession(jumpSession)
jumpSession.destroyed$.subscribe(() => session.destroy()) this.attachSessionHandler(
jumpSession.destroyed$.subscribe(() => {
if (session.open) {
session.destroy()
}
})
)
session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut( session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
'127.0.0.1', 0, session.connection.host, session.connection.port ?? 22, '127.0.0.1', 0, session.connection.host, session.connection.port ?? 22,
@@ -93,15 +110,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.sessionStack.push(session) this.sessionStack.push(session)
} }
this.attachSessionHandler(session.serviceMessage$.subscribe(msg => {
session.serviceMessage$.subscribe(msg => {
this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`) this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`)
session.resize(this.size.columns, this.size.rows) 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.write('\r\n' + colors.black.bgCyan(' SSH ') + ` Connecting to ${session.connection.host}\r\n`) this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` Connecting to ${session.connection.host}\r\n`)
@@ -128,20 +141,49 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
} }
} }
protected attachSessionHandlers (): void {
const session = this.session!
super.attachSessionHandlers()
this.attachSessionHandler(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.attachSessionHandler(this.input$.pipe(first()).subscribe(() => {
this.reconnect()
}))
}
}
}))
}
async initializeSession (): Promise<void> { async initializeSession (): Promise<void> {
this.reconnectOffered = false
if (!this.connection) { if (!this.connection) {
this.logger.error('No SSH connection info supplied') this.logger.error('No SSH connection info supplied')
return return
} }
this.session = this.ssh.createSession(this.connection) const session = this.ssh.createSession(this.connection)
this.setSession(session)
await this.setupOneSession(this.session) try {
await this.setupOneSession(session)
} catch (e) {
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
}
this.attachSessionHandlers() await this.session!.start()
this.session!.resize(this.size.columns, this.size.rows)
await this.session.start()
this.session.resize(this.size.columns, this.size.rows)
} }
async getRecoveryToken (): Promise<RecoveryToken> { async getRecoveryToken (): Promise<RecoveryToken> {

View File

@@ -12,7 +12,7 @@ import * as sshpk from 'sshpk'
import { ToastrService } from 'ngx-toastr' import { ToastrService } from 'ngx-toastr'
import { HostAppService, Platform, Logger, LogService, ElectronService, AppService, SelectorOption, ConfigService } from 'terminus-core' import { HostAppService, Platform, Logger, LogService, ElectronService, AppService, SelectorOption, ConfigService } from 'terminus-core'
import { SettingsTabComponent } from 'terminus-settings' import { SettingsTabComponent } from 'terminus-settings'
import { SSHConnection, SSHSession } from '../api' import { ALGORITHM_BLACKLIST, SSHConnection, SSHSession } from '../api'
import { PromptModalComponent } from '../components/promptModal.component' import { PromptModalComponent } from '../components/promptModal.component'
import { PasswordStorageService } from './passwordStorage.service' import { PasswordStorageService } from './passwordStorage.service'
import { SSHTabComponent } from '../components/sshTab.component' import { SSHTabComponent } from '../components/sshTab.component'
@@ -147,6 +147,10 @@ export class SSHService {
session.ssh = ssh session.ssh = ssh
let connected = false let connected = false
let savedPassword: string|null = null 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) => { await new Promise(async (resolve, reject) => {
ssh.on('ready', () => { ssh.on('ready', () => {
connected = true connected = true
@@ -258,7 +262,7 @@ export class SSHService {
tryKeyboard: true, tryKeyboard: true,
agent: agent ?? undefined, agent: agent ?? undefined,
agentForward: session.connection.agentForward && !!agent, agentForward: session.connection.agentForward && !!agent,
keepaliveInterval: session.connection.keepaliveInterval, keepaliveInterval: session.connection.keepaliveInterval ?? 15000,
keepaliveCountMax: session.connection.keepaliveCountMax, keepaliveCountMax: session.connection.keepaliveCountMax,
readyTimeout: session.connection.readyTimeout, readyTimeout: session.connection.readyTimeout,
hostVerifier: (digest: string) => { hostVerifier: (digest: string) => {
@@ -267,7 +271,7 @@ export class SSHService {
return true return true
}, },
hostHash: 'sha256' as any, hostHash: 'sha256' as any,
algorithms: session.connection.algorithms, algorithms,
sock: session.jumpStream, sock: session.jumpStream,
authHandler: methodsLeft => { authHandler: methodsLeft => {
while (true) { while (true) {
@@ -379,7 +383,7 @@ export class SSHService {
options.push({ options.push({
name: 'Manage connections', name: 'Manage connections',
icon: 'cog', icon: 'cog',
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'ssh' }), callback: () => this.app.openNewTabRaw(SettingsTabComponent, { activeTab: 'ssh' }),
}) })
options.push({ options.push({

View File

@@ -40,7 +40,7 @@ module.exports = {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] }, { test: /\.svg/, use: ['svg-inline-loader'] },
], ],
}, },

View File

@@ -27,12 +27,12 @@
"runes": "^0.4.2", "runes": "^0.4.2",
"slugify": "^1.4.0", "slugify": "^1.4.0",
"xterm": "^4.9.0-beta.7", "xterm": "^4.9.0-beta.7",
"xterm-addon-fit": "^0.4.0-beta.8", "xterm-addon-fit": "^0.5.0",
"xterm-addon-ligatures": "^0.4.0-beta.5", "xterm-addon-ligatures": "^0.4.0",
"xterm-addon-search": "^0.7.0-beta.2", "xterm-addon-search": "^0.8.0",
"xterm-addon-serialize": "^0.3.0", "xterm-addon-serialize": "^0.4.0",
"xterm-addon-unicode11": "^0.2.0", "xterm-addon-unicode11": "^0.2.0",
"xterm-addon-webgl": "^0.8.0", "xterm-addon-webgl": "^0.9.0",
"zmodem.js": "^0.1.9" "zmodem.js": "^0.1.9"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -18,6 +18,7 @@ import { TerminalDecorator } from './decorator'
/** @hidden */ /** @hidden */
export interface ToastrServiceProxy { export interface ToastrServiceProxy {
info: (_: string) => void info: (_: string) => void
error: (_: string) => void
} }
/** /**
* A class to base your custom terminal tabs on * A class to base your custom terminal tabs on
@@ -35,7 +36,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
]), ]),
])] ])]
session?: BaseSession session: BaseSession|null = null
savedState?: any savedState?: any
@Input() zoom = 0 @Input() zoom = 0
@@ -94,6 +95,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
private bellPlayer: HTMLAudioElement private bellPlayer: HTMLAudioElement
private termContainerSubscriptions: Subscription[] = [] private termContainerSubscriptions: Subscription[] = []
private allFocusModeSubscription: Subscription|null = null private allFocusModeSubscription: Subscription|null = null
private sessionHandlers: Subscription[] = []
get input$ (): Observable<Buffer> { get input$ (): Observable<Buffer> {
if (!this.frontend) { if (!this.frontend) {
@@ -140,7 +142,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.logger = this.log.create('baseTerminalTab') this.logger = this.log.create('baseTerminalTab')
this.setTitle('Terminal') this.setTitle('Terminal')
this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(hotkey => { this.hotkeysSubscription = this.hotkeys.matchedHotkey.subscribe(async hotkey => {
if (!this.hasFocus) { if (!this.hasFocus) {
return return
} }
@@ -207,6 +209,9 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
case 'pane-focus-all': case 'pane-focus-all':
this.focusAllPanes() this.focusAllPanes()
break break
case 'copy-current-path':
this.copyCurrentPath()
break
} }
}) })
this.bellPlayer = document.createElement('audio') this.bellPlayer = document.createElement('audio')
@@ -217,10 +222,6 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
/** @hidden */ /** @hidden */
ngOnInit (): void { ngOnInit (): void {
if (!this.session) {
throw new Error('No session set on the tab object by the time ngOnInit is called')
}
this.focused$.subscribe(() => { this.focused$.subscribe(() => {
this.configure() this.configure()
this.frontend?.focus() this.frontend?.focus()
@@ -347,7 +348,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
async paste (): Promise<void> { async paste (): Promise<void> {
let data = this.electron.clipboard.readText() let data = this.electron.clipboard.readText()
if (this.config.store.terminal.bracketedPaste) { if (this.config.store.terminal.bracketedPaste) {
data = '\x1b[200~' + data + '\x1b[201~' data = `\x1b[200~${data}\x1b[201~`
} }
if (this.hostApp.platform === Platform.Windows) { if (this.hostApp.platform === Platform.Windows) {
data = data.replace(/\r\n/g, '\r') data = data.replace(/\r\n/g, '\r')
@@ -418,10 +419,11 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
return return
} }
if (this.parent instanceof SplitTabComponent) { if (this.parent instanceof SplitTabComponent) {
this.parent._allFocusMode = true const parent = this.parent
this.parent.layout() parent._allFocusMode = true
parent.layout()
this.allFocusModeSubscription = this.frontend?.input$.subscribe(data => { this.allFocusModeSubscription = this.frontend?.input$.subscribe(data => {
for (const tab of (this.parent as SplitTabComponent).getAllTabs()) { for (const tab of parent.getAllTabs()) {
if (tab !== this && tab instanceof BaseTerminalTabComponent) { if (tab !== this && tab instanceof BaseTerminalTabComponent) {
tab.sendInput(data) tab.sendInput(data)
} }
@@ -442,6 +444,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 */ /** @hidden */
ngOnDestroy (): void { ngOnDestroy (): void {
this.frontend?.detach(this.content.nativeElement) this.frontend?.detach(this.content.nativeElement)
@@ -554,26 +569,55 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
] ]
} }
setSession (session: BaseSession|null, destroyOnSessionClose = false): void {
if (session) {
if (this.session) {
this.setSession(null)
}
this.detachSessionHandlers()
this.session = session
this.attachSessionHandlers(destroyOnSessionClose)
} else {
this.detachSessionHandlers()
this.session = null
}
}
protected attachSessionHandler (subscription: Subscription): void {
this.sessionHandlers.push(subscription)
}
protected attachSessionHandlers (destroyOnSessionClose = false): void { protected attachSessionHandlers (destroyOnSessionClose = false): void {
if (!this.session) { if (!this.session) {
throw new Error('Session not set') throw new Error('Session not set')
} }
// this.session.output$.bufferTime(10).subscribe((datas) => { // this.session.output$.bufferTime(10).subscribe((datas) => {
this.session.output$.subscribe(data => { this.attachSessionHandler(this.session.output$.subscribe(data => {
if (this.enablePassthrough) { if (this.enablePassthrough) {
this.zone.run(() => { this.zone.run(() => {
this.output.next(data) this.output.next(data)
this.write(data) this.write(data)
}) })
} }
}) }))
if (destroyOnSessionClose) { if (destroyOnSessionClose) {
this.sessionCloseSubscription = this.session.closed$.subscribe(() => { this.attachSessionHandler(this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
this.frontend?.destroy() this.frontend?.destroy()
this.destroy() this.destroy()
}) }))
} }
this.attachSessionHandler(this.session.destroyed$.subscribe(() => {
this.setSession(null)
}))
}
protected detachSessionHandlers (): void {
for (const s of this.sessionHandlers) {
s.unsubscribe()
}
this.sessionHandlers = []
} }
} }

View File

@@ -10,7 +10,7 @@ import { Subject } from 'rxjs'
}) })
export class EnvironmentEditorComponent { export class EnvironmentEditorComponent {
@Output() modelChange = new Subject<any>() @Output() modelChange = new Subject<any>()
vars: {key: string, value: string}[] = [] vars: { key: string, value: string }[] = []
private cachedModel: any private cachedModel: any
@Input() get model (): any { @Input() get model (): any {

View File

@@ -57,15 +57,14 @@ h3.mb-3 Shell
.form-line .form-line
.header .header
.title Always Use Working Directory .title Directory for new tabs
.description
div By default, new terminals will open where the previous terminal was working.
div Enabling this option will always launch new terminals in the working directory specified above.
toggle( select.form-control(
[(ngModel)]='config.store.terminal.alwaysUseWorkingDirectory', [(ngModel)]='config.store.terminal.alwaysUseWorkingDirectory',
(ngModelChange)='config.save()' (ngModelChange)='config.save()',
) )
option([ngValue]='false') Same as active tab's directory
option([ngValue]='true') The working directory from above
.form-line.align-items-start .form-line.align-items-start
.header .header
@@ -97,7 +96,7 @@ h3.mt-3 Saved Profiles
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteProfile(profile)') button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteProfile(profile)')
i.fas.fa-trash i.fas.fa-trash
div(ngbDropdown, placement='top-left') .pb-4(ngbDropdown, placement='top-left')
button.btn.btn-primary(ngbDropdownToggle) button.btn.btn-primary(ngbDropdownToggle)
i.fas.fa-fw.fa-plus i.fas.fa-fw.fa-plus
| New profile | New profile

View File

@@ -4,6 +4,7 @@ import { ConfigProvider, Platform } from 'terminus-core'
export class TerminalConfigProvider extends ConfigProvider { export class TerminalConfigProvider extends ConfigProvider {
defaults = { defaults = {
hotkeys: { hotkeys: {
'copy-current-path': [],
shell: { shell: {
__nonStructural: true, __nonStructural: true,
}, },

View File

@@ -62,6 +62,10 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
id: 'ctrl-c', id: 'ctrl-c',
name: 'Intelligent Ctrl-C (copy/abort)', name: 'Intelligent Ctrl-C (copy/abort)',
}, },
{
id: 'copy-current-path',
name: 'Copy current path',
},
{ {
id: 'search', id: 'search',
name: 'Search', name: 'Search',

View File

@@ -194,6 +194,9 @@ export default class TerminalModule { // eslint-disable-line @typescript-eslint/
}) })
hostApp.cliOpenDirectory$.subscribe(async directory => { 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.exists(directory)) {
if ((await fs.stat(directory)).isDirectory()) { if ((await fs.stat(directory)).isDirectory()) {
terminal.openTab(undefined, directory) terminal.openTab(undefined, directory)

View File

@@ -2,12 +2,11 @@ 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 '@terminus-term/node-pty' import * as nodePTY from '@terminus-term/node-pty'
import { getWorkingDirectoryFromPID } from 'native-process-working-directory'
import { Observable, Subject } from 'rxjs' import { Observable, Subject } from 'rxjs'
import { first } from 'rxjs/operators' import { first } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Logger, LogService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' import { Logger, LogService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core'
import { exec } from 'mz/child_process'
import { SessionOptions } from '../api/interfaces' import { SessionOptions } from '../api/interfaces'
/* eslint-disable block-scoped-var */ /* eslint-disable block-scoped-var */
@@ -20,7 +19,6 @@ try {
var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { } } catch { }
export interface ChildProcess { export interface ChildProcess {
pid: number pid: number
ppid: number ppid: number
@@ -28,7 +26,6 @@ export interface ChildProcess {
} }
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi
const catalinaDataVolumePrefix = '/System/Volumes/Data'
const OSC1337Prefix = Buffer.from('\x1b]1337;') const OSC1337Prefix = Buffer.from('\x1b]1337;')
const OSC1337Suffix = Buffer.from('\x07') const OSC1337Suffix = Buffer.from('\x07')
@@ -87,6 +84,7 @@ export abstract class BaseSession {
abstract kill (signal?: string): void abstract kill (signal?: string): void
abstract async getChildProcesses (): Promise<ChildProcess[]> abstract async getChildProcesses (): Promise<ChildProcess[]>
abstract async gracefullyKillProcess (): Promise<void> abstract async gracefullyKillProcess (): Promise<void>
abstract supportsWorkingDirectory (): boolean
abstract async getWorkingDirectory (): Promise<string|null> abstract async getWorkingDirectory (): Promise<string|null>
} }
@@ -96,6 +94,7 @@ export class Session extends BaseSession {
private pauseAfterExit = false private pauseAfterExit = false
private guessedCWD: string|null = null private guessedCWD: string|null = null
private reportedCWD: string private reportedCWD: string
private initialCWD: string|null = null
constructor (private config: ConfigService) { constructor (private config: ConfigService) {
super() super()
@@ -153,6 +152,7 @@ export class Session extends BaseSession {
this.truePID = processes[0].pid this.truePID = processes[0].pid
processes = await this.getChildProcesses() processes = await this.getChildProcesses()
} }
this.initialCWD = await this.getWorkingDirectory()
}, 2000) }, 2000)
this.open = true this.open = true
@@ -259,6 +259,10 @@ export class Session extends BaseSession {
} }
} }
supportsWorkingDirectory (): boolean {
return !!(this.truePID || this.reportedCWD || this.guessedCWD)
}
async getWorkingDirectory (): Promise<string|null> { async getWorkingDirectory (): Promise<string|null> {
if (this.reportedCWD) { if (this.reportedCWD) {
return this.reportedCWD return this.reportedCWD
@@ -266,40 +270,31 @@ export class Session extends BaseSession {
if (!this.truePID) { if (!this.truePID) {
return null return null
} }
if (process.platform === 'darwin') { let cwd: string|null = null
let lines: string[] = []
try { try {
lines = (await exec(`lsof -p ${this.truePID} -Fn`))[0].toString().split('\n') cwd = getWorkingDirectoryFromPID(this.truePID)
} catch (e) {
return null
}
let cwd = lines[lines[1] === 'fcwd' ? 2 : 1].substring(1)
if (cwd.startsWith(catalinaDataVolumePrefix)) {
cwd = cwd.substring(catalinaDataVolumePrefix.length)
}
return cwd
}
if (process.platform === 'linux') {
try {
const cwd = await fs.readlink(`/proc/${this.truePID}/cwd`)
return cwd
} catch (exc) { } catch (exc) {
console.error(exc) console.error(exc)
return null
}
}
if (process.platform === 'win32') {
if (!this.guessedCWD) {
return null
} }
try { try {
await fs.access(this.guessedCWD) cwd = await fs.realpath(cwd)
} catch (e) { } catch {}
if (process.platform === 'win32' && (cwd === this.initialCWD || cwd === process.env.WINDIR)) {
// shell doesn't truly change its process' CWD
cwd = null
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
cwd = cwd || this.guessedCWD
try {
await fs.access(cwd)
} catch {
return null return null
} }
return this.guessedCWD return cwd
}
return null
} }
private guessWindowsCWD (data: string) { private guessWindowsCWD (data: string) {

View File

@@ -16,7 +16,7 @@ export class TerminalFrontendService {
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
) { } ) { }
getFrontend (session?: BaseSession): Frontend { getFrontend (session?: BaseSession|null): Frontend {
if (!session) { if (!session) {
const frontend: Frontend = new { const frontend: Frontend = new {
xterm: XTermFrontend, xterm: XTermFrontend,

View File

@@ -8,6 +8,8 @@ import { Shell } from '../api/interfaces'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class MacOSDefaultShellProvider extends ShellProvider { export class MacOSDefaultShellProvider extends ShellProvider {
private cachedShell?: string
constructor ( constructor (
private hostApp: HostAppService, private hostApp: HostAppService,
) { ) {
@@ -18,14 +20,29 @@ export class MacOSDefaultShellProvider extends ShellProvider {
if (this.hostApp.platform !== Platform.macOS) { if (this.hostApp.platform !== Platform.macOS) {
return [] return []
} }
const shellEntry = (await exec(`/usr/bin/dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
return [{ return [{
id: 'default', id: 'default',
name: 'User default', name: 'User default',
command: shellEntry.split(' ')[1].trim(), command: await this.getDefaultShellCached(),
args: ['--login'], args: ['--login'],
hidden: true, hidden: true,
env: {}, env: {},
}] }]
} }
private async getDefaultShellCached () {
if (!this.cachedShell) {
this.cachedShell = await this.getDefaultShell()
} else {
this.getDefaultShell().then(shell => {
this.cachedShell = shell
})
}
return this.cachedShell!
}
private async getDefaultShell () {
const shellEntry = (await exec(`/usr/bin/dscl . -read /Users/${process.env.LOGNAME} UserShell`))[0].toString()
return shellEntry.split(' ')[1].trim()
}
} }

View File

@@ -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 return items
} }
} }

View File

@@ -43,7 +43,7 @@ module.exports = {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] }, { test: /\.svg/, use: ['svg-inline-loader'] },
{ {
@@ -67,6 +67,7 @@ module.exports = {
'windows-native-registry', 'windows-native-registry',
'@terminus-term/node-pty', '@terminus-term/node-pty',
'windows-process-tree', 'windows-process-tree',
'native-process-working-directory',
'os', 'os',
/^rxjs/, /^rxjs/,
/^@angular/, /^@angular/,

View File

@@ -111,10 +111,10 @@ exit-on-epipe@~1.0.1:
resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
font-finder@^1.0.3, font-finder@^1.0.4: font-finder@^1.0.3, font-finder@^1.1.0:
version "1.0.4" version "1.1.0"
resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.0.4.tgz#2ca944954dd8d0e1b5bdc4c596cc08607761d89b" resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.1.0.tgz#2bff2b2762acba720239c8bec898a96daae90858"
integrity sha512-naF16RpjWUTFLqzhmdivYpBCrqySN6PI+a4GPtoEsCdvOpbKYTGeTjO7mxh3Wwjz4xKU+Oqx9kwOcteLDeMFQA== integrity sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==
dependencies: dependencies:
get-system-fonts "^2.0.0" get-system-fonts "^2.0.0"
promise-stream-reader "^1.0.1" promise-stream-reader "^1.0.1"
@@ -337,43 +337,43 @@ tiny-inflate@^1.0.2:
resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
xterm-addon-fit@^0.4.0-beta.8: xterm-addon-fit@^0.5.0:
version "0.4.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.4.0.tgz#06e0c5d0a6aaacfb009ef565efa1c81e93d90193" resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596"
integrity sha512-p4BESuV/g2L6pZzFHpeNLLnep9mp/DkF3qrPglMiucSFtD8iJxtMufEoEJbN8LZwB4i+8PFpFvVuFrGOSpW05w== integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==
xterm-addon-ligatures@^0.4.0-beta.5: xterm-addon-ligatures@^0.4.0:
version "0.4.0-beta.7" version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-ligatures/-/xterm-addon-ligatures-0.4.0-beta.7.tgz#06dfafe0491a2e4744b1a6429958f261820baa00" resolved "https://registry.yarnpkg.com/xterm-addon-ligatures/-/xterm-addon-ligatures-0.4.0.tgz#23235d831988c5780ca1e28002b750ec0b1a78ca"
integrity sha512-GVZKbm7GRgs0LFtdKKmpu0Cs2jfC+sdRtl2E4vWEmhi8WNeW9iINbrsYgdtFPvs1X9OaKxEWqEXJkTcy5rmLHQ== integrity sha512-on+2TgzioEL5Mz60WchsTiuR2SvYzG3xVro39HIpYFM641lpMuPM83Pdm3W/ogkaGrpGm29Ysq2S2LCl/h7rhw==
dependencies: dependencies:
font-finder "^1.0.4" font-finder "^1.1.0"
font-ligatures "^1.3.3" font-ligatures "^1.3.3"
xterm-addon-search@^0.7.0-beta.2: xterm-addon-search@^0.8.0:
version "0.7.0" version "0.8.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef" resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.8.0.tgz#e33eab918df7eac7e7baf95dd2b3d14133754881"
integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg== integrity sha512-MPJGPVPpHRUw9cLIuqQbrVepmENMOybVUSxIALz5h1ryyQBrVqVujq2hL5aroX5/dZJoHx9lGHQTVLQ07SKgKA==
xterm-addon-serialize@^0.3.0: xterm-addon-serialize@^0.4.0:
version "0.3.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.3.0.tgz#b4f24089285801bcc5f601791a4cac416d528a6a" resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.4.0.tgz#9854fd87afba6157b9016e9c93f0cf9e0efc2de5"
integrity sha512-g+eGqgvQiON4CCEku0iG53obZxi353wig2XRR75DOoHlPMLrO63kvt2V+OahnJlxCpsWQ1BFu4Q5HquQnS6uvQ== integrity sha512-xEdzCNa6ZzFplDlscYTSSUogy1C6y3G3nS68Qbe5zntFAqHOBeyggExQi0E8yZg/no8ewYH0GSKZnOheo/ZoKg==
xterm-addon-unicode11@^0.2.0: xterm-addon-unicode11@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c"
integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q== integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q==
xterm-addon-webgl@^0.8.0: xterm-addon-webgl@^0.9.0:
version "0.8.0" version "0.9.0"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.8.0.tgz#4bc6bb4dbfea5b0d2d7978d6c5cef922d584fb4f" resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0.tgz#947ca94f9fd72462155c300dfb82bc471402d60a"
integrity sha512-dlpYPsv0C9S6v6+T/h/d/otSbdUTizMJdxvSoS34tUpMOHev6iW7Zqt5KRFqYxl4vCqpDk9Wmhb3fKL3kwX5fQ== integrity sha512-JSRoe/MgPEKCpr7LbiC+sQUq0c9l/ZhsBXYugKx5BJsJEPsY+5EFcqAexuhsJVe/qV+CP3hsbYDe/411rw3ASA==
xterm@^4.9.0-beta.7: xterm@^4.9.0-beta.7:
version "4.9.0" version "4.10.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0.tgz#7a4c097a433d565339b5533b468bbc60c6c87969" resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.10.0.tgz#fc4f554e3e718aff9b83622e858e64b0953067bb"
integrity sha512-wGfqufmioctKr8VkbRuZbVDfjlXWGZZ1PWHy1yqqpGT3Nm6yaJx8lxDbSEBANtgaiVPTcKSp97sxOy5IlpqYfw== integrity sha512-Wn66I8YpSVkgP3R95GjABC6Eb21pFfnCSnyIqKIIoUI13ohvwd0KGVzUDfyEFfSAzKbPJfrT2+vt7SfUXBZQKQ==
yallist@^2.1.2: yallist@^2.1.2:
version "2.1.2" version "2.1.2"

View File

@@ -24,6 +24,9 @@
"es7", "es7",
"es2015", "es2015",
"es2017" "es2017"
] ],
"paths": {
"terminus-*": ["../../terminus-*/src"]
}
} }
} }

1312
yarn.lock

File diff suppressed because it is too large Load Diff