Compare commits

...

48 Commits

Author SHA1 Message Date
Eugene Pankov
8afad944b7 Merge branch 'master' of https://github.com/Eugeny/terminus 2021-02-14 13:07:56 +01:00
Eugene Pankov
707d077eb2 fixed winpty input - fixes #3397 2021-02-13 15:05:29 +01:00
Eugene Pankov
92eabab509 cleanup 2021-02-13 14:53:19 +01:00
Eugene Pankov
888cfc6548 fixed windows build 2021-02-13 14:10:56 +01:00
Eugene Pankov
9cb10c3e21 tweaked plugins ui 2021-02-13 13:53:25 +01:00
Eugene Pankov
661d5af9ba casing 2021-02-13 13:40:05 +01:00
Eugene Pankov
26429bf209 removed toolbar buttons from touchbar 2021-02-13 13:25:46 +01:00
Eugene Pankov
4779c41f48 bumped electron 2021-02-13 13:16:55 +01:00
Eugene Pankov
b11eda8653 bumped electron 2021-02-13 13:04:41 +01:00
Eugene Pankov
23bff8750c added static/dynamic tab size setting - fixes #3426 2021-02-13 12:55:55 +01:00
Eugene Pankov
aab1ae3ceb reconnect hotkey - fixes #3344 2021-02-13 12:49:07 +01:00
Eugene Pankov
9b5b5a9d00 re-fixed session restarting 2021-02-13 12:47:21 +01:00
Eugene Pankov
9c87cf3f3a fixed tabs on side with compact theme 2021-02-13 12:42:23 +01:00
Eugene Pankov
a8c7134218 fixed SSH reconnection behaviour - fixed #3446, fixed #3442 2021-02-13 12:42:15 +01:00
Eugene Pankov
9734830a74 web demo fixes 2021-02-13 12:14:14 +01:00
Eugene Pankov
5fa056751d bumped angular 2021-02-13 12:14:09 +01:00
Eugene Pankov
6fdccd0a02 cleanup webpack configs 2021-02-13 12:11:38 +01:00
Eugene
1149d68d1c Merge pull request #3243 from Eugeny/dependabot/npm_and_yarn/https-proxy-agent-2.2.4
[Security] Bump https-proxy-agent from 2.0.0 to 2.2.4
2021-02-07 16:33:41 +01:00
Eugene
1d92a3f89d Merge pull request #3242 from Eugeny/dependabot/npm_and_yarn/tar-2.2.2
[Security] Bump tar from 2.2.1 to 2.2.2
2021-02-07 16:33:25 +01:00
Eugene
3557345d70 Merge pull request #3222 from Eugeny/dependabot/npm_and_yarn/terminus-plugin-manager/axios-0.21.1
[Security] Bump axios from 0.19.2 to 0.21.1 in /terminus-plugin-manager
2021-02-07 16:33:06 +01:00
Eugene
86ebdd92b4 Merge pull request #3221 from Eugeny/dependabot/npm_and_yarn/terminus-core/axios-0.21.1
[Security] Bump axios from 0.19.2 to 0.21.1 in /terminus-core
2021-02-07 16:32:59 +01:00
Eugene
4764ec8249 Merge pull request #3239 from Eugeny/dependabot/npm_and_yarn/tough-cookie-2.3.4
[Security] Bump tough-cookie from 2.3.2 to 2.3.4
2021-02-07 16:32:49 +01:00
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
dependabot-preview[bot]
c829daac41 [Security] Bump tar from 2.2.1 to 2.2.2
Bumps [tar](https://github.com/npm/node-tar) from 2.2.1 to 2.2.2. **This update includes a security fix.**
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/master/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v2.2.1...v2.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-06 15:47:12 +00: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
dependabot-preview[bot]
4c840a0db1 [Security] Bump https-proxy-agent from 2.0.0 to 2.2.4
Bumps [https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent) from 2.0.0 to 2.2.4. **This update includes security fixes.**
- [Release notes](https://github.com/TooTallNate/node-https-proxy-agent/releases)
- [Commits](https://github.com/TooTallNate/node-https-proxy-agent/compare/2.0.0...2.2.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-28 20:48:14 +00: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
dependabot-preview[bot]
ceb638e08d [Security] Bump tough-cookie from 2.3.2 to 2.3.4
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 2.3.2 to 2.3.4. **This update includes security fixes.**
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v2.3.2...v2.3.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-02 09:43:15 +00:00
dependabot-preview[bot]
040098050d Bump axios from 0.19.2 to 0.21.1 in /terminus-plugin-manager
Bumps [axios](https://github.com/axios/axios) from 0.19.2 to 0.21.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.21.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.2...v0.21.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-02 09:43:10 +00:00
dependabot-preview[bot]
00de7c148f Bump axios from 0.19.2 to 0.21.1 in /terminus-core
Bumps [axios](https://github.com/axios/axios) from 0.19.2 to 0.21.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.21.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.2...v0.21.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-02 09:43:06 +00:00
63 changed files with 1599 additions and 1424 deletions

View File

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

View File

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

View File

@@ -36,7 +36,7 @@ export class Window {
private windowConfig: ElectronConfig
private windowBounds?: Rectangle
private closing = false
private lastVibrancy: {enabled: boolean, type?: string} | null = null
private lastVibrancy: { enabled: boolean, type?: string } | null = null
private disableVibrancyWhileDragging = false
private configStore: any

View File

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

View File

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

View File

@@ -2,40 +2,54 @@
# yarn lockfile v1
"@angular/animations@^9.1.9":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/animations/-/animations-9.1.13.tgz"
integrity sha512-ane1eeQmsP7fcAiLgRhle7YIDgE88WDMMvzqJYhSxwLzXNF/hwqNeskmNcjo8bLt9h/yTIjrCQbycLCHJfU8UQ==
"@angular/animations@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.2.0.tgz#383438758c61d98f43c5ea1078d8388eb8784387"
integrity sha512-orYrBPNAsF8VpvuOaXTtPltwK2nsje5R8sWJnRC2dh1RCRdyIqHwmRIU0Mi6qLMiEaLNrFPGEMyQ9gB+sC/vYg==
dependencies:
tslib "^2.0.0"
"@angular/common@^9.1.11":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/common/-/common-9.1.13.tgz"
integrity sha512-QACUhJWlly/nfHUmjopVS1p6ayxxa/NqjyftdCeBJaoyM2YohqWixP/n/keu1K/srJ96aFpUNsZQgmgoRv5SOQ==
"@angular/common@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.2.0.tgz#90d602c0e33bb95a4d0c4c597f08255d78ed580f"
integrity sha512-wsWI5F6Y2DNxne2D5uk8e9U/vn95UYZLMNBW+QXI9U/I9kDSXoa8yEvNcn1x0XfNXBMst5pi4iSF5M8mIck1eg==
dependencies:
tslib "^2.0.0"
"@angular/compiler@^9.1.9":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/compiler/-/compiler-9.1.13.tgz"
integrity sha512-9MLB1Xx7odKuxDoybVwiOB1ZEUZpL8FurYm4RVuW39ntsUt0IMC9Hb8UagZLTAWhaWSHydkD/KBQVVobGqd0lA==
"@angular/compiler@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.2.0.tgz#130bee57dd1daa1326d37bef4b63c02aa7309cc2"
integrity sha512-EW6LM/kUYhQkuFqGt03c/eRKZAYr0LLEdMOn//j1uIh+wSq9KLffBGpky6b63xdfWxsXi8OucXUOydTQBckNEQ==
dependencies:
tslib "^2.0.0"
"@angular/core@^9.1.9":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/core/-/core-9.1.13.tgz"
integrity sha512-mBm24Q9GjkAsxMAzqQ86U1078+yTEpr0+syMEruUtJ0HUH6Fzn3J+6xTLb+BVcGb9RkCkFaV9T5mcn6ZM0f++g==
"@angular/core@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.2.0.tgz#309ae61d55b21fca0b644a6571109741d64b2467"
integrity sha512-jnbnJTW2GwfkRoXG8J4zs5FMcahMZwo6jrZGe9FiXjCYG9cLEuOXy4h99Z1s/o0vc/VXyWgym7SmeEgv+urf8g==
dependencies:
tslib "^2.0.0"
"@angular/forms@^9.1.11":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/forms/-/forms-9.1.13.tgz"
integrity sha512-soGVZmPq2bzkxvtTyeJB8p3ejzm4xxt+43hJw6Ag8NxpwUFPVa30oJge3JV+u8Y4yBtl5SbOZ4bBX3EkMxLcGQ==
"@angular/forms@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.2.0.tgz#8ba7a98efdd464a4a6b901ba1f220162dd80c1ed"
integrity sha512-FgIG9ou27FbmyFv0n6pF95cQEz412/+iyY9OSlDnezD/7yU4fwk4NNPgP6Z/b1k7ClLYxP/YKC00WVhi1i8HdA==
dependencies:
tslib "^2.0.0"
"@angular/platform-browser-dynamic@^9.1.9":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.13.tgz"
integrity sha512-jCeHyAZ4Nap1/FOqAlKEg9UxQaSkHrxnQr6hYtWwC4ZDVUn3zLWQf6J+mbeYNOXN5yQxEiIqqhORYeOCLLqf1w==
"@angular/platform-browser-dynamic@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.0.tgz#647fe6d8dfa7651d38564240cbf75f97f11754b7"
integrity sha512-bBCtgtL87mvDT0K3HNBS19UC0BzIJUTGnYKJS9IugyfTEqlldB4juMmh/3FPjk30kxxJ8IB/ydaN2uVhBAxPVQ==
dependencies:
tslib "^2.0.0"
"@angular/platform-browser@^9.1.9":
version "9.1.13"
resolved "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.1.13.tgz"
integrity sha512-F3iTz1zNbtrs7KFKUxbj8qmTsd/fiuTNcpBExjE5TtatRiE6J8vNvN1+Z/1FgPe0UXBSdTzSwZ8/RxWKw20RMw==
"@angular/platform-browser@^11.1.1":
version "11.2.0"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.2.0.tgz#d1bbafd394ebfb600043060ec2d8543763041403"
integrity sha512-xd3O4svQ95BN/KpzQUFkSWfvwiCURuLJhLlDkxzLA58ElA0qodHOjQmQz/1vRFh/nXQQoWg8z9ixbmcRGzWTow==
dependencies:
tslib "^2.0.0"
"@iarna/cli@^1.2.0":
version "1.2.0"
@@ -117,10 +131,10 @@
dependencies:
debug "^4.1.1"
"@terminus-term/node-pty@0.10.0-beta10":
version "0.10.0-beta10"
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta10.tgz#de9dade3d7549d44b0906ec0d0b9e1bb411f1f21"
integrity sha512-j9RJk7sD/es4vR6+AR5M/p3SicVxY6kZEeE0UQKhHNcaAla90/mcGeBNicAWPaAkjO1uQZVbYh5cJMMu5unQgA==
"@terminus-term/node-pty@0.10.0-terminus.2":
version "0.10.0-terminus.2"
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-terminus.2.tgz#028c7762d13150984bc800b8cd954ceb7dbcac68"
integrity sha512-vcscP3jldTMZeHv0XVxQjwEtnh0usUQgUWvsXtPRMy2rMjijwC1+8xFp/FKPpLpWYNTN8WWmRjSdiw+qGGU4CQ==
dependencies:
nan "^2.14.0"
@@ -259,12 +273,10 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
asap@^2.0.0:
version "2.0.6"
@@ -837,10 +849,10 @@ electron-config@2.0.0:
dependencies:
conf "^1.0.0"
electron-debug@^3.0.1:
version "3.1.0"
resolved "https://registry.npmjs.org/electron-debug/-/electron-debug-3.1.0.tgz"
integrity sha512-SWEqLj4MgfV3tGuO5eBLQ5/Nr6M+KPxsnE0bUJZvQebGJus6RAcdmvd7L+l0Ji31h2mmrN23l2tHFtCa2FvurA==
electron-debug@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-3.2.0.tgz#46a15b555c3b11872218c65ea01d058aa0814920"
integrity sha512-7xZh+LfUvJ52M9rn6N+tPuDw6oRAjxUj9SoxAZfJ0hVCXhZCsdkrSt7TgXOiWiEOBgEV8qwUIO/ScxllsPS7ow==
dependencies:
electron-is-dev "^1.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"
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"
resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.1.0.tgz"
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"
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:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
@@ -1581,13 +1593,12 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
js-yaml@3.14.0:
version "3.14.0"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
js-yaml@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
argparse "^2.0.1"
jsbn@~0.1.0:
version "0.1.1"
@@ -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"
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:
version "12.1.0"
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"
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"
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz"
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
@@ -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"
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:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz"
@@ -3469,9 +3482,9 @@ tslib@^1.10.0, tslib@^1.9.0:
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tunnel-agent@^0.6.0:
version "0.6.0"

View File

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

View File

@@ -1,51 +1,6 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-community-color-schemes:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /[\\\/]schemes[\\\/]/, use: "raw-loader" },
],
},
externals: [
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
/^terminus-/,
],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'community-color-schemes',
dirname: __dirname,
})
module.exports.module.rules.push({ test: /[\\\/]schemes[\\\/]/, use: 'raw-loader' })

View File

@@ -20,7 +20,7 @@
"@types/js-yaml": "^3.9.0",
"@types/shell-escape": "^0.2.0",
"@types/winston": "^2.3.6",
"axios": "^0.19.0",
"axios": "^0.21.1",
"bootstrap": "^4.1.3",
"core-js": "^3.1.2",
"deepmerge": "^4.1.1",

View File

@@ -85,6 +85,7 @@ $side-tab-width: 200px;
cursor: pointer;
display: flex;
align-items: center;
padding: 0 15px;
flex: 0 0 auto;
border-bottom: 2px solid transparent;

View File

@@ -142,6 +142,8 @@ export class AppRootComponent {
this.touchbar.update()
this.hostApp.useBuiltinGraphics()
config.changed$.subscribe(() => 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 { SelectorOption } from '../api/selector'

View File

@@ -7,6 +7,11 @@ $tabs-height: 38px;
flex: 1000 1 200px;
width: 200px;
&.flex-width {
flex: 1000 1 auto;
width: auto;
}
display: flex;
flex-direction: row;
min-width: 0;

View File

@@ -81,6 +81,10 @@ export class TabHeaderComponent {
return items.slice(1)
}
@HostBinding('class.flex-width') get isFlexWidthEnabled (): boolean {
return this.config.store.appearance.flexTabs
}
@HostListener('dblclick') onDoubleClick (): void {
this.showRenameTabModal()
}

View File

@@ -4,6 +4,7 @@ appearance:
dockFill: 0.5
dockHideOnBlur: false
dockAlwaysOnTop: true
flexTabs: false
tabsLocation: top
cycleTabs: true
theme: Standard

View File

@@ -44,8 +44,11 @@ import 'ng2-dnd/bundles/style.css'
// PerfectScrollbar fix
import { fromEvent } from 'rxjs/internal/observable/fromEvent'
import { merge } from 'rxjs/internal/observable/merge'
require('rxjs').fromEvent = fromEvent
require('rxjs').merge = merge
try {
require('rxjs').fromEvent = fromEvent
require('rxjs').merge = merge
} catch {}
const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
@@ -110,7 +113,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
})
}
static forRoot (): ModuleWithProviders {
static forRoot (): ModuleWithProviders<AppModule> {
return {
ngModule: AppModule,
providers: PROVIDERS,

View File

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

View File

@@ -8,6 +8,12 @@ import { ElectronService } from './electron.service'
import { Logger, LogService } from './log.service'
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 {
Linux = 'Linux',
macOS = 'macOS',
@@ -154,7 +160,7 @@ export class HostAppService {
electron.ipcRenderer.on('cli', (_$event, argv: any, cwd: string, secondInstance: boolean) => this.zone.run(async () => {
this.logger.info('Second instance', argv)
const op = argv._[0]
const opAsPath = path.resolve(cwd, op)
const opAsPath = op ? path.resolve(cwd, op) : null
if (op === 'open') {
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
} else if (op === 'run') {
@@ -167,9 +173,9 @@ export class HostAppService {
this.cliPaste.next(text)
} else if (op === 'profile') {
this.cliOpenProfile.next(argv.profileName)
} else if (op === undefined) {
} else if (secondInstance && op === undefined) {
this.newWindow()
} else if ((await fs.lstat(opAsPath)).isDirectory()) {
} else if (opAsPath && (await fs.lstat(opAsPath)).isDirectory()) {
this.cliOpenDirectory.next(opAsPath)
}
@@ -283,6 +289,16 @@ export class HostAppService {
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 {
if (this.isPortable) {
this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })

View File

@@ -1,11 +1,13 @@
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import * as winston from 'winston'
import type * as winston from 'winston'
import * as fs from 'fs'
import * as path from 'path'
const initializeWinston = (electron: ElectronService) => {
const logDirectory = electron.app.getPath('userData')
// eslint-disable-next-line
const winston = require('winston')
if (!fs.existsSync(logDirectory)) {
fs.mkdirSync(logDirectory)
@@ -64,7 +66,11 @@ export class LogService {
/** @hidden */
private constructor (electron: ElectronService) {
this.log = initializeWinston(electron)
if (!process.env.XWEB) {
this.log = initializeWinston(electron)
} else {
this.log = console as any
}
}
create (name: string): Logger {

View File

@@ -1,24 +1,18 @@
import { NativeImage, SegmentedControlSegment, TouchBarSegmentedControl } from 'electron'
import { Injectable, Inject, NgZone } from '@angular/core'
import { SegmentedControlSegment, TouchBarSegmentedControl } from 'electron'
import { Injectable, NgZone } from '@angular/core'
import { AppService } from './app.service'
import { ConfigService } from './config.service'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
import { ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */
@Injectable({ providedIn: 'root' })
export class TouchbarService {
private tabsSegmentedControl: TouchBarSegmentedControl
private buttonsSegmentedControl: TouchBarSegmentedControl
private tabSegments: SegmentedControlSegment[] = []
private nsImageCache: Record<string, NativeImage> = {}
private constructor (
private app: AppService,
private hostApp: HostAppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
private config: ConfigService,
private electron: ElectronService,
private zone: NgZone,
) {
@@ -63,16 +57,6 @@ export class TouchbarService {
return
}
let buttons: ToolbarButton[] = []
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
buttons = buttons.concat(provider.provide())
})
buttons = buttons.filter(x => !!x.touchBarNSImage)
buttons.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
this.tabSegments = this.app.tabs.map(tab => ({
label: this.shortenTitle(tab.title),
}))
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
segments: this.tabSegments,
selectedIndex: this.app.activeTab ? this.app.tabs.indexOf(this.app.activeTab) : undefined,
@@ -81,43 +65,14 @@ export class TouchbarService {
}),
})
this.buttonsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
segments: buttons.map(button => this.getButton(button)),
mode: 'buttons',
change: (selectedIndex) => this.zone.run(() => {
if (buttons[selectedIndex].click) {
buttons[selectedIndex].click!()
}
}),
})
const touchBar = new this.electron.TouchBar({
items: [
this.tabsSegmentedControl,
new this.electron.TouchBar.TouchBarSpacer({ size: 'flexible' }),
new this.electron.TouchBar.TouchBarSpacer({ size: 'small' }),
this.buttonsSegmentedControl,
],
})
this.hostApp.setTouchBar(touchBar)
}
private getButton (button: ToolbarButton): SegmentedControlSegment {
return {
label: button.touchBarNSImage ? undefined : this.shortenTitle(button.touchBarTitle ?? button.title),
icon: button.touchBarNSImage ? this.getCachedNSImage(button.touchBarNSImage) : undefined,
// click: () => this.zone.run(() => button.click()),
}
}
private getCachedNSImage (name: string) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!this.nsImageCache[name]) {
this.nsImageCache[name] = this.electron.nativeImage.createFromNamedImage(name, [0, 0, 1])
}
return this.nsImageCache[name]
}
private shortenTitle (title: string): string {
if (title.length > 15) {
title = title.substring(0, 15) + '...'

View File

@@ -1,6 +1,10 @@
@import './theme.scss';
app-root {
.tabs-on-side .tab-bar {
height: 100% !important;
}
.tab-bar {
height: 27px !important;

View File

@@ -1,60 +1,5 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-core:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader'], include: /component\.css/ },
{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ },
{ test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
],
},
externals: [
'electron',
'fs',
'os',
'path',
'windows-native-registry',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'core',
dirname: __dirname,
})

View File

@@ -57,12 +57,12 @@ at-least-node@^1.0.0:
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
axios@^0.19.0:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "1.5.10"
follow-redirects "^1.10.0"
bootstrap@^4.1.3:
version "4.5.3"
@@ -133,13 +133,6 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@^3.1.0:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -209,12 +202,10 @@ fn.name@1.x.x:
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
follow-redirects@^1.10.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
fs-extra@^9.0.1:
version "9.0.1"
@@ -309,11 +300,6 @@ mixpanel@^0.10.2:
dependencies:
https-proxy-agent "3.0.0"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.2, ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"

View File

@@ -18,7 +18,7 @@
"license": "MIT",
"devDependencies": {
"@types/semver": "^7.1.0",
"axios": "^0.19.0",
"axios": "^0.21.1",
"electron-promise-ipc": "^2.2.4",
"mz": "^2.6.0",
"semver": "^7.1.1"

View File

@@ -2,7 +2,6 @@
strong Error in {{erroredPlugin}}:
pre {{errorMessage}}
.d-flex
h3.mb-1 Installed
button.btn.btn-outline-secondary.btn-sm.ml-auto((click)='openPluginsFolder()')
@@ -11,11 +10,16 @@
.list-group.list-group-flush.mt-2
.list-group-item.d-flex.align-items-center(*ngFor='let plugin of pluginManager.installedPlugins')
toggle(
[ngModel]='isPluginEnabled(plugin)',
(ngModelChange)='togglePlugin(plugin)'
)
.mr-auto.d-flex.flex-column
div
strong {{plugin.name}}
small.text-muted.ml-1(*ngIf='!plugin.isBuiltin') {{plugin.version}} / {{plugin.author}}
small.text-warning.ml-1(*ngIf='config.store.pluginBlacklist.includes(plugin.name)') Disabled
small.text-warning.ml-1(*ngIf='!isPluginEnabled(plugin)') Disabled
a.text-muted.mb-0((click)='showPluginInfo(plugin)')
small {{plugin.description}}
@@ -28,18 +32,6 @@
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing')
span Upgrade ({{knownUpgrades[plugin.name].version}})
button.btn.btn-link.text-primary.ml-2(
*ngIf='config.store.pluginBlacklist.includes(plugin.name)',
(click)='enablePlugin(plugin)'
)
i.fas.fa-fw.fa-play
button.btn.btn-link.ml-2(
*ngIf='!config.store.pluginBlacklist.includes(plugin.name)',
(click)='disablePlugin(plugin)'
)
i.fas.fa-fw.fa-pause
button.btn.btn-link.text-danger.ml-2(
(click)='uninstallPlugin(plugin)',
*ngIf='!plugin.isBuiltin',

View File

@@ -102,6 +102,18 @@ export class PluginsSettingsTabComponent {
this.electron.shell.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
}
isPluginEnabled (plugin: PluginInfo) {
return !this.config.store.pluginBlacklist.includes(plugin.name)
}
togglePlugin (plugin: PluginInfo) {
if (this.isPluginEnabled(plugin)) {
this.disablePlugin(plugin)
} else {
this.enablePlugin(plugin)
}
}
enablePlugin (plugin: PluginInfo) {
this.config.store.pluginBlacklist = this.config.store.pluginBlacklist.filter(x => x !== plugin.name)
this.config.save()

View File

@@ -3,6 +3,7 @@ import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import TerminusCorePlugin from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings'
import { PluginsSettingsTabComponent } from './components/pluginsSettingsTab.component'
@@ -14,6 +15,7 @@ import { PluginsSettingsTabProvider } from './settings'
BrowserModule,
FormsModule,
NgbModule,
TerminusCorePlugin,
],
providers: [
{ provide: SettingsTabProvider, useClass: PluginsSettingsTabProvider, multi: true },

View File

@@ -1,56 +1,5 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-plugin-manager:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
],
},
externals: [
'fs',
'net',
'path',
'electron-promise-ipc',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
/^terminus-/,
],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'plugin-manager',
dirname: __dirname,
})

View File

@@ -12,12 +12,12 @@ any-promise@^1.0.0:
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
axios@^0.19.0:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "1.5.10"
follow-redirects "^1.10.0"
call-bind@^1.0.0:
version "1.0.0"
@@ -27,13 +27,6 @@ call-bind@^1.0.0:
function-bind "^1.1.1"
get-intrinsic "^1.0.0"
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@@ -78,12 +71,10 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
follow-redirects@^1.10.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
function-bind@^1.1.1:
version "1.1.1"
@@ -145,11 +136,6 @@ is-symbol@^1.0.2:
dependencies:
has-symbols "^1.0.1"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
mz@^2.6.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"

View File

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

View File

@@ -1,7 +1,12 @@
import stripAnsi from 'strip-ansi'
import bufferReplace from 'buffer-replace'
import { BaseSession } from 'terminus-terminal'
import { SerialPort } from 'serialport'
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 {
expect: string
@@ -23,6 +28,9 @@ export interface SerialConnection {
xany: boolean
scripts?: LoginScript[]
color?: string
inputMode?: InputMode
inputNewlines?: NewlineMode
outputNewlines?: NewlineMode
}
export const BAUD_RATES = [
@@ -34,6 +42,9 @@ export interface SerialPortInfo {
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 {
scripts?: LoginScript[]
serial: SerialPort
@@ -41,58 +52,38 @@ export class SerialSession extends BaseSession {
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
private serviceMessage = new Subject<string>()
private inputReadline: ReadLine
private inputPromptVisible = true
private inputReadlineInStream: Readable & Writable
private inputReadlineOutStream: Readable & Writable
constructor (public connection: SerialConnection) {
super()
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> {
this.open = true
this.serial.on('data', data => {
const dataString = data.toString()
this.emitOutput(data)
if (this.scripts) {
let found = false
for (const script of this.scripts) {
let match = false
let cmd = ''
if (script.isRegex) {
const re = new RegExp(script.expect, 'g')
if (dataString.match(re)) {
cmd = dataString.replace(re, script.send)
match = true
found = true
}
} else {
if (dataString.includes(script.expect)) {
cmd = script.send
match = true
found = true
}
}
if (match) {
this.logger.info('Executing script: "' + cmd + '"')
this.serial.write(cmd + '\n')
this.scripts = this.scripts.filter(x => x !== script)
} else {
if (script.optional) {
this.logger.debug('Skip optional script: ' + script.expect)
found = true
this.scripts = this.scripts.filter(x => x !== script)
} else {
break
}
}
}
if (found) {
this.executeUnconditionalScripts()
}
}
this.serial.on('readable', () => {
this.onOutput(this.serial.read())
})
this.serial.on('end', () => {
@@ -106,23 +97,33 @@ export class SerialSession extends BaseSession {
}
write (data: Buffer): void {
if (this.serial) {
this.serial.write(data.toString())
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 (_, __) { }
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 []
}
@@ -139,6 +140,94 @@ export class SerialSession extends BaseSession {
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()
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)
if (this.scripts) {
let found = false
for (const script of this.scripts) {
let match = false
let cmd = ''
if (script.isRegex) {
const re = new RegExp(script.expect, 'g')
if (re.test(dataString)) {
cmd = dataString.replace(re, script.send)
match = true
found = true
}
} else {
if (dataString.includes(script.expect)) {
cmd = script.send
match = true
found = true
}
}
if (match) {
this.logger.info('Executing script: "' + cmd + '"')
this.serial.write(cmd + '\n')
this.scripts = this.scripts.filter(x => x !== script)
} else {
if (script.optional) {
this.logger.debug('Skip optional script: ' + script.expect)
found = true
this.scripts = this.scripts.filter(x => x !== script)
} else {
break
}
}
}
if (found) {
this.executeUnconditionalScripts()
}
}
}
private executeUnconditionalScripts () {
if (this.scripts) {
for (const script of this.scripts) {

View File

@@ -11,23 +11,86 @@
[(ngModel)]='connection.name',
)
.form-group
label Path
input.form-control(
type='text',
[(ngModel)]='connection.port',
[ngbTypeahead]='portsAutocomplete',
[resultFormatter]='portsFormatter',
)
.row
.col-6
.form-group
label Path
input.form-control(
type='text',
[(ngModel)]='connection.port',
[ngbTypeahead]='portsAutocomplete',
[resultFormatter]='portsFormatter',
)
.form-group
label Baud Rate
select.form-control(
[(ngModel)]='connection.baudrate',
)
option([value]='x', *ngFor='let x of baudRates') {{x}}
.col-6
.form-group
label Baud Rate
select.form-control(
[(ngModel)]='connection.baudrate',
)
option([value]='x', *ngFor='let x of baudRates') {{x}}
ngb-tab(id='advanced')
.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')
ng-template(ngbTabTitle) Advanced
ng-template(ngbTabContent)
.form-line

View File

@@ -15,6 +15,17 @@ export class EditConnectionModalComponent {
connection: SerialConnection
foundPorts: SerialPortInfo[]
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 (
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(() => {
return this.foundPorts.map(x => x.name)
}))

View File

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

View File

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

View File

@@ -11,14 +11,15 @@ import { Subscription } from 'rxjs'
/** @hidden */
@Component({
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],
animations: BaseTerminalTabComponent.animations,
})
export class SerialTabComponent extends BaseTerminalTabComponent {
connection?: SerialConnection
session?: SerialSession
session: SerialSession|null = null
serialPort: any
private serialService: SerialService
private homeEndSubscription: Subscription
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
@@ -26,6 +27,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
injector: Injector,
) {
super(injector)
this.serialService = injector.get(SerialService)
}
ngOnInit () {
@@ -42,6 +44,9 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
case 'end':
this.sendInput('\x1b[F' )
break
case 'restart-serial-session':
this.reconnect()
break
}
})
@@ -62,12 +67,8 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
return
}
this.session = this.injector.get(SerialService).createSession(this.connection)
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.attachSessionHandlers()
const session = this.serialService.createSession(this.connection)
this.setSession(session)
this.write(`Connecting to `)
const spinner = new Spinner({
@@ -80,15 +81,32 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
spinner.start()
try {
this.serialPort = await this.injector.get(SerialService).connectSession(this.session)
this.serialPort = await this.serialService.connectSession(this.session!)
spinner.stop(true)
session.emitServiceMessage('Port opened')
} catch (e) {
spinner.stop(true)
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
return
}
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)
}
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> {
@@ -99,8 +117,10 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
}
}
reconnect () {
this.initializeSession()
async reconnect (): Promise<void> {
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()
}
async changeBaudRate () {

View File

@@ -12,6 +12,7 @@ export class SerialConfigProvider extends ConfigProvider {
serial: [
'Alt-K',
],
'restart-serial-session': [],
},
}

View File

@@ -9,6 +9,10 @@ export class SerialHotkeyProvider extends HotkeyProvider {
id: 'serial',
name: 'Show Serial connections',
},
{
id: 'restart-serial-session',
name: 'Restart current serial session',
},
]
async provide (): Promise<HotkeyDescription[]> {

View File

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

View File

@@ -1,59 +1,5 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-serial:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js', '.node'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
],
},
externals: [
'fs',
'keytar',
'path',
'ngx-toastr',
'serialport',
'windows-process-tree/build/Release/windows_process_tree.node',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
/^terminus-/,
],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'serial',
dirname: __dirname,
})

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"
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:
version "0.2.10"
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"

View File

@@ -66,6 +66,29 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
)
| Right
.form-line
.header
.title Tabs width
.btn-group(
[(ngModel)]='config.store.appearance.flexTabs',
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| Dynamic
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Fixed
.form-line
.header
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background
@@ -327,7 +350,7 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
.d-flex.flex-column.w-100.h-100
.h-100.d-flex
.w-100.d-flex.flex-column
h3 Config file
h3 Config File
textarea.form-control.h-100(
[(ngModel)]='configFile'
)

View File

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

View File

@@ -1,57 +1,5 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-settings:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
],
},
externals: [
'fs',
'path',
'os',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
/^terminus-/,
],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'settings',
dirname: __dirname,
})

View File

@@ -140,6 +140,9 @@ export class SSHSession extends BaseSession {
this.shell = await this.openShellChannel({ x11: this.connection.x11 })
} catch (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
}
@@ -245,7 +248,13 @@ export class SSHSession extends BaseSession {
}
socket.on('error', e => {
// 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()
})
socket.on('connect', () => {

View File

@@ -1,14 +1,14 @@
.tab-toolbar
.tab-toolbar([class.show]='!session || !session.open')
.btn.btn-outline-secondary.reveal-button
i.fas.fa-ellipsis-h
.toolbar(*ngIf='session', [class.show]='!session.open')
i.fas.fa-circle.text-success.mr-2(*ngIf='session.open')
i.fas.fa-circle.text-danger.mr-2(*ngIf='!session.open')
strong.mr-auto(*ngIf='session') {{session.connection.user}}@{{session.connection.host}}:{{session.connection.port}}
.toolbar
i.fas.fa-circle.text-success.mr-2(*ngIf='session && session.open')
i.fas.fa-circle.text-danger.mr-2(*ngIf='!session || !session.open')
strong.mr-auto {{connection.user}}@{{connection.host}}:{{connection.port}}
button.btn.btn-secondary.mr-2((click)='reconnect()', [class.btn-info]='!session.open')
button.btn.btn-secondary.mr-2((click)='reconnect()', [class.btn-info]='!session || !session.open')
span Reconnect
button.btn.btn-secondary((click)='showPortForwarding()', *ngIf='session.open')
button.btn.btn-secondary((click)='showPortForwarding()', *ngIf='session && session.open')
i.fas.fa-plug
span Ports

View File

@@ -14,18 +14,17 @@ import { Subscription } from 'rxjs'
/** @hidden */
@Component({
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],
animations: BaseTerminalTabComponent.animations,
})
export class SSHTabComponent extends BaseTerminalTabComponent {
connection?: SSHConnection
session?: SSHSession
session: SSHSession|null = null
private sessionStack: SSHSession[] = []
private homeEndSubscription: Subscription
private recentInputs = ''
private reconnectOffered = false
private sessionHandlers: Subscription[] = []
constructor (
injector: Injector,
@@ -55,6 +54,9 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
case 'end':
this.sendInput('\x1b[F' )
break
case 'restart-ssh-session':
this.reconnect()
break
}
})
@@ -85,8 +87,12 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
await this.setupOneSession(jumpSession)
this.sessionHandlers.push(
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(
@@ -107,31 +113,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.sessionStack.push(session)
}
this.sessionHandlers.push(session.serviceMessage$.subscribe(msg => {
this.attachSessionHandler(session.serviceMessage$.subscribe(msg => {
this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`)
session.resize(this.size.columns, this.size.rows)
}))
this.sessionHandlers.push(session.destroyed$.subscribe(() => {
if (
// Ctrl-D
this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 ||
this.recentInputs.endsWith('exit\r')
) {
// User closed the session
this.destroy()
} else {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` ${session.connection.host}: session closed\r\n`)
if (!this.reconnectOffered) {
this.reconnectOffered = true
this.write('Press any key to reconnect\r\n')
this.input$.pipe(first()).subscribe(() => {
this.reconnect()
})
}
}
}))
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` Connecting to ${session.connection.host}\r\n`)
@@ -158,6 +144,33 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
}
}
protected attachSessionHandlers (): void {
const session = this.session!
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.input$.pipe(first()).subscribe(() => {
if (!this.session?.open) {
this.reconnect()
}
})
}
}
}))
super.attachSessionHandlers()
}
async initializeSession (): Promise<void> {
this.reconnectOffered = false
if (!this.connection) {
@@ -165,18 +178,17 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
return
}
this.session = this.ssh.createSession(this.connection)
const session = this.ssh.createSession(this.connection)
this.setSession(session)
try {
await this.setupOneSession(this.session)
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> {
@@ -193,10 +205,6 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
}
async reconnect (): Promise<void> {
for (const s of this.sessionHandlers) {
s.unsubscribe()
}
this.sessionHandlers = []
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()

View File

@@ -13,6 +13,7 @@ export class SSHConfigProvider extends ConfigProvider {
ssh: [
'Alt-S',
],
'restart-ssh-session': [],
},
}

View File

@@ -9,6 +9,10 @@ export class SSHHotkeyProvider extends HotkeyProvider {
id: 'ssh',
name: 'Show SSH connections',
},
{
id: 'restart-ssh-session',
name: 'Restart current SSH session',
},
]
async provide (): Promise<HotkeyDescription[]> {

View File

@@ -383,7 +383,7 @@ export class SSHService {
options.push({
name: 'Manage connections',
icon: 'cog',
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'ssh' }),
callback: () => this.app.openNewTabRaw(SettingsTabComponent, { activeTab: 'ssh' }),
})
options.push({

View File

@@ -1,61 +1,5 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-ssh:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [path.resolve(__dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
],
},
externals: [
'child_process',
'fs',
'keytar',
'path',
'ngx-toastr',
'socksv5',
'windows-native-registry',
'windows-process-tree/build/Release/windows_process_tree.node',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
/^terminus-/,
],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'ssh',
dirname: __dirname,
})

View File

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

View File

@@ -36,7 +36,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
]),
])]
session?: BaseSession
session: BaseSession|null = null
savedState?: any
@Input() zoom = 0
@@ -95,6 +95,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
private bellPlayer: HTMLAudioElement
private termContainerSubscriptions: Subscription[] = []
private allFocusModeSubscription: Subscription|null = null
private sessionHandlers: Subscription[] = []
get input$ (): Observable<Buffer> {
if (!this.frontend) {
@@ -347,7 +348,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
async paste (): Promise<void> {
let data = this.electron.clipboard.readText()
if (this.config.store.terminal.bracketedPaste) {
data = '\x1b[200~' + data + '\x1b[201~'
data = `\x1b[200~${data}\x1b[201~`
}
if (this.hostApp.platform === Platform.Windows) {
data = data.replace(/\r\n/g, '\r')
@@ -418,10 +419,11 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
return
}
if (this.parent instanceof SplitTabComponent) {
this.parent._allFocusMode = true
this.parent.layout()
const parent = this.parent
parent._allFocusMode = true
parent.layout()
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) {
tab.sendInput(data)
}
@@ -567,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 {
if (!this.session) {
throw new Error('Session not set')
}
// this.session.output$.bufferTime(10).subscribe((datas) => {
this.session.output$.subscribe(data => {
this.attachSessionHandler(this.session.output$.subscribe(data => {
if (this.enablePassthrough) {
this.zone.run(() => {
this.output.next(data)
this.write(data)
})
}
})
}))
if (destroyOnSessionClose) {
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
this.attachSessionHandler(this.sessionCloseSubscription = this.session.closed$.subscribe(() => {
this.frontend?.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 {
@Output() modelChange = new Subject<any>()
vars: {key: string, value: string}[] = []
vars: { key: string, value: string }[] = []
private cachedModel: any
@Input() get model (): any {

View File

@@ -57,15 +57,14 @@ h3.mb-3 Shell
.form-line
.header
.title Always Use Working Directory
.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.
.title Directory for new tabs
toggle(
select.form-control(
[(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
.header
@@ -97,7 +96,7 @@ h3.mt-3 Saved Profiles
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteProfile(profile)')
i.fas.fa-trash
div(ngbDropdown, placement='top-left')
.pb-4(ngbDropdown, placement='top-left')
button.btn.btn-primary(ngbDropdownToggle)
i.fas.fa-fw.fa-plus
| New profile

View File

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

View File

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

View File

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

@@ -1,79 +1,8 @@
const path = require('path')
module.exports = {
target: 'node',
entry: 'src/index.ts',
context: __dirname,
devtool: 'cheap-module-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: 'webpack-terminus-terminal:///[resource-path]',
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization: {
minimize: false,
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules', 'node_modules/xterm/src'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(__dirname, 'node_modules/@types'),
path.resolve(__dirname, '../node_modules/@types'),
],
paths: {
"terminus-*": [path.resolve(__dirname, '../terminus-*')],
"*": [
path.resolve(__dirname, '../app/node_modules/*'),
path.resolve(__dirname, './node_modules/xterm/src/*'),
],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
{
test: /\.(ttf|eot|otf|woff|woff2|ogg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'url-loader',
options: {
limit: 999999999999,
},
},
},
],
},
externals: [
'child_process',
'electron',
'fontmanager-redux',
'fs',
'path',
'macos-native-processlist',
'windows-native-registry',
'@terminus-term/node-pty',
'windows-process-tree',
'os',
/^rxjs/,
/^@angular/,
/^@ng-bootstrap/,
'ngx-toastr',
/^terminus-/,
],
// Ignore warnings due to yarg's dynamic module loading
ignoreWarnings: [/node_modules\/yargs/],
}
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'terminal',
dirname: __dirname,
})
module.exports.resolve.modules.push('node_modules/xterm/src')
module.exports.module.rules.find(x => x.use.loader === 'awesome-typescript-loader').use.options.paths['*'].push(path.resolve(__dirname, './node_modules/xterm/src/*'))

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

View File

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

94
webpack.plugin.config.js Normal file
View File

@@ -0,0 +1,94 @@
const path = require('path')
module.exports = options => {
const isDev = !!process.env.TERMINUS_DEV
const devtool = isDev && process.platform === 'win32' ? 'eval-cheap-module-source-map' : 'cheap-module-source-map'
return {
target: 'node',
entry: 'src/index.ts',
context: options.dirname,
devtool,
output: {
path: path.resolve(options.dirname, 'dist'),
filename: 'index.js',
pathinfo: true,
libraryTarget: 'umd',
devtoolModuleFilenameTemplate: `webpack-terminus-${options.name}:///[resource-path]`,
},
mode: isDev ? 'development' : 'production',
optimization:{
minimize: false,
},
cache: !isDev ? false : {
type: 'filesystem',
cacheDirectory: path.resolve(options.dirname, 'node_modules', '.webpack-cache'),
},
resolve: {
modules: ['.', 'src', 'node_modules', '../app/node_modules'].map(x => path.join(options.dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(options.dirname, 'tsconfig.json'),
typeRoots: [
path.resolve(options.dirname, 'node_modules/@types'),
path.resolve(options.dirname, '../node_modules/@types'),
],
paths: {
'terminus-*': [path.resolve(options.dirname, '../terminus-*')],
'*': [path.resolve(options.dirname, '../app/node_modules/*')],
},
},
},
},
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['@terminus-term/to-string-loader', 'css-loader'], include: /component\.css/ },
{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ },
{ test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] },
{ test: /\.svg/, use: ['svg-inline-loader'] },
{
test: /\.(ttf|eot|otf|woff|woff2|ogg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'url-loader',
options: {
limit: 999999999999,
},
},
},
],
},
externals: [
'@terminus-term/node-pty',
'child_process',
'electron-promise-ipc',
'electron',
'fontmanager-redux',
'fs',
'keytar',
'macos-native-processlist',
'native-process-working-directory',
'net',
'ngx-toastr',
'os',
'path',
'readline',
'serialport',
'socksv5',
'stream',
'windows-native-registry',
'windows-process-tree',
'windows-process-tree/build/Release/windows_process_tree.node',
/^@angular/,
/^@ng-bootstrap/,
/^rxjs/,
/^terminus-/,
],
ignoreWarnings: [/node_modules\/yargs/],
}
}

1278
yarn.lock

File diff suppressed because it is too large Load Diff