Compare commits

...

164 Commits

Author SHA1 Message Date
Eugene Pankov
1c8288bfe1 bumped xterm 2020-01-14 11:57:17 +01:00
Eugene Pankov
37044fbb01 enable vibrancy by default 2020-01-14 11:57:08 +01:00
Eugene Pankov
5507171fee zmodem fixes (fixes #1949) 2020-01-14 11:41:05 +01:00
Eugene
472b421484 Merge pull request #2000 from CyrilTaylor/dev/portable/userData_location
set userData location to the 'data' folder which at the same level as the Terminus executable directory if exist with portable mode
2020-01-14 09:26:26 +01:00
Eugene
b076541962 Merge pull request #1991 from CyrilTaylor/dev.theme.compact.margin
narrow down the margin of ssh tab to 5px with theme compact
2020-01-14 09:25:12 +01:00
Cyril Taylor
8201e0b9ef set userData location to the 'data' folder which at the same level as the Terminus executable directory if exist with portable mode 2020-01-14 15:06:54 +08:00
Cyril Taylor
6293a43571 narrow down the margin of ssh tab to 5px with theme compact 2020-01-14 11:34:52 +08:00
Eugene
b61bc943ec Merge pull request #1989 from Eugeny/all-contributors/add-CyrilTaylor
docs: add CyrilTaylor as a contributor
2020-01-13 12:27:47 +01:00
allcontributors[bot]
be668403c5 docs: update .all-contributorsrc [skip ci] 2020-01-13 11:27:32 +00:00
allcontributors[bot]
90a173d8b7 docs: update README.md [skip ci] 2020-01-13 11:27:31 +00:00
Eugene
bbcc84e9b0 1. Fit the shell integration with windos registry style 2. Add… (#1986)
1. Fit the shell integration with windos registry style 2. Add folder context menu with Terminus
2020-01-13 12:27:13 +01:00
Cyril Taylor
b0a8832499 remove old entry for windows shell integration 2020-01-13 19:07:03 +08:00
Eugene
8cd1c4a9af Update sshTab.component.ts 2020-01-13 11:53:28 +01:00
Cyril Taylor
1ada4338b7 1. Fit the shell integration with windos registry style 2. Add folder context menu with Terminus 2020-01-13 14:08:58 +08:00
Eugene
7770cf2573 Bump core-js from 3.6.1 to 3.6.3 in /terminus-core (#1978)
Bump core-js from 3.6.1 to 3.6.3 in /terminus-core
2020-01-11 14:13:50 +01:00
dependabot-preview[bot]
49755f855f Bump core-js from 3.6.1 to 3.6.3 in /terminus-core
Bumps [core-js](https://github.com/zloirock/core-js) from 3.6.1 to 3.6.3.
- [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.6.1...v3.6.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-11 13:12:40 +00:00
Eugene
daa1b4572e Bump node-gyp from 6.0.1 to 6.1.0 (#1968)
Bump node-gyp from 6.0.1 to 6.1.0
2020-01-11 14:11:22 +01:00
Eugene
d48e22a9d2 Bump slug from 2.0.0 to 2.1.0 in /terminus-terminal (#1947)
Bump slug from 2.0.0 to 2.1.0 in /terminus-terminal
2020-01-11 14:08:30 +01:00
Eugene
fc82010729 Bump @types/webpack-env from 1.14.1 to 1.15.0 (#1961)
Bump @types/webpack-env from 1.14.1 to 1.15.0
2020-01-11 14:08:17 +01:00
Eugene
4a8f3fbd7f Bump core-js from 3.6.1 to 3.6.2 in /terminus-core (#1965)
Bump core-js from 3.6.1 to 3.6.2 in /terminus-core
2020-01-11 14:08:05 +01:00
Eugene
4c435672a5 Bump core-js from 3.6.1 to 3.6.2 (#1963)
Bump core-js from 3.6.1 to 3.6.2
2020-01-11 14:07:44 +01:00
Eugene
0311754ce0 Bump css-loader from 3.4.0 to 3.4.1 (#1960)
Bump css-loader from 3.4.0 to 3.4.1
2020-01-11 14:07:30 +01:00
Eugene
ff5da104c1 Bump @typescript-eslint/parser from 2.14.0 to 2.15.0 (#1962)
Bump @typescript-eslint/parser from 2.14.0 to 2.15.0
2020-01-11 14:07:12 +01:00
Eugene
b01b902829 Bump typedoc from 0.15.6 to 0.15.7 (#1975)
Bump typedoc from 0.15.6 to 0.15.7
2020-01-11 14:06:45 +01:00
Eugene
595707eed4 Bump axios from 0.19.0 to 0.19.1 in /terminus-core (#1969)
Bump axios from 0.19.0 to 0.19.1 in /terminus-core
2020-01-11 14:06:35 +01:00
Eugene
441ee4fb6e Bump axios from 0.19.0 to 0.19.1 in /terminus-plugin-manager (#1970)
Bump axios from 0.19.0 to 0.19.1 in /terminus-plugin-manager
2020-01-11 14:06:20 +01:00
dependabot-preview[bot]
51827d6750 Bump typedoc from 0.15.6 to 0.15.7
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.15.6 to 0.15.7.
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.15.6...v0.15.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-10 04:23:31 +00:00
dependabot-preview[bot]
9807bbe32a Bump axios from 0.19.0 to 0.19.1 in /terminus-plugin-manager
Bumps [axios](https://github.com/axios/axios) from 0.19.0 to 0.19.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.0...0.19.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 04:25:18 +00:00
dependabot-preview[bot]
181b55890d Bump axios from 0.19.0 to 0.19.1 in /terminus-core
Bumps [axios](https://github.com/axios/axios) from 0.19.0 to 0.19.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.0...0.19.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 04:25:09 +00:00
dependabot-preview[bot]
c8c5f1a0fd Bump node-gyp from 6.0.1 to 6.1.0
Bumps [node-gyp](https://github.com/nodejs/node-gyp) from 6.0.1 to 6.1.0.
- [Release notes](https://github.com/nodejs/node-gyp/releases)
- [Changelog](https://github.com/nodejs/node-gyp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodejs/node-gyp/compare/v6.0.1...v6.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-08 04:24:57 +00:00
dependabot-preview[bot]
222c6a9f3c Bump core-js from 3.6.1 to 3.6.2 in /terminus-core
Bumps [core-js](https://github.com/zloirock/core-js) from 3.6.1 to 3.6.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.6.1...v3.6.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-07 04:25:23 +00:00
dependabot-preview[bot]
0400c8fe63 Bump core-js from 3.6.1 to 3.6.2
Bumps [core-js](https://github.com/zloirock/core-js) from 3.6.1 to 3.6.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.6.1...v3.6.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-07 04:22:18 +00:00
dependabot-preview[bot]
099d9b06d6 Bump @typescript-eslint/parser from 2.14.0 to 2.15.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.14.0 to 2.15.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.15.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-07 04:21:51 +00:00
dependabot-preview[bot]
ad26b39cca Bump @types/webpack-env from 1.14.1 to 1.15.0
Bumps [@types/webpack-env](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webpack-env) from 1.14.1 to 1.15.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/webpack-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-06 04:24:44 +00:00
dependabot-preview[bot]
f465c359ef Bump css-loader from 3.4.0 to 3.4.1
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.4.0...v3.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-06 04:24:25 +00:00
Eugene Pankov
67aead225c Merge branch 'master' of github.com:Eugeny/terminus 2020-01-03 18:40:46 +01:00
Eugene Pankov
280c421ae4 use node-sshpk for better key parsing (fixes #1956, fixes #1612, fixes #1191, fixes #1788, fixes #1300) 2020-01-03 18:40:43 +01:00
Eugene
b6fc43faa2 Bump yargs from 15.0.2 to 15.1.0 in /app (#1953)
Bump yargs from 15.0.2 to 15.1.0 in /app
2020-01-03 16:14:31 +01:00
Eugene Pankov
b5a985b8a3 ui tweaks 2020-01-03 16:11:31 +01:00
Eugene Pankov
2f7dcf3339 made x11 forwarding configurable (fixes #1950, fixes #1954) 2020-01-03 16:04:30 +01:00
dependabot-preview[bot]
7e38f11c06 Bump yargs from 15.0.2 to 15.1.0 in /app
Bumps [yargs](https://github.com/yargs/yargs) from 15.0.2 to 15.1.0.
- [Release notes](https://github.com/yargs/yargs/releases)
- [Changelog](https://github.com/yargs/yargs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs/compare/v15.0.2...v15.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-03 04:24:28 +00:00
Eugene Pankov
86cd560089 disabled electron-updater on Linux 2020-01-02 12:22:56 +01:00
dependabot-preview[bot]
c0c4580461 Bump slug from 2.0.0 to 2.1.0 in /terminus-terminal
Bumps [slug](https://github.com/Trott/node-slug) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/Trott/node-slug/releases)
- [Changelog](https://github.com/Trott/node-slug/blob/master/CHANGELOG)
- [Commits](https://github.com/Trott/node-slug/compare/v2.0.0...v2.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-01 04:23:06 +00:00
Eugene
5cb65dfd84 Bump bootstrap from 4.3.1 to 4.4.1 in /terminus-core (#1841)
Bump bootstrap from 4.3.1 to 4.4.1 in /terminus-core
2020-01-01 01:09:05 +01:00
Eugene Pankov
2b5f623b50 profile and SSH connection colors (fixes #954) 2020-01-01 01:04:41 +01:00
Eugene Pankov
a8d5cf469e ui fixes 2020-01-01 01:04:25 +01:00
Eugene Pankov
d261b89803 added notifications for tab activity (fixes #1123) 2019-12-31 23:38:27 +01:00
Eugene Pankov
21cb452d62 fixed terminal not getting focus when switching windows (fixes #692) 2019-12-31 23:20:07 +01:00
Eugene Pankov
b07a2113d2 ssh: added a reconnect button (fixes #761) 2019-12-31 23:12:08 +01:00
Eugene Pankov
f545b3eacf mention Zmodem in README 2019-12-31 22:51:58 +01:00
Eugene Pankov
87fe8deaa8 apis 2019-12-31 22:48:36 +01:00
Eugene Pankov
1068450ddd performance improv 2019-12-31 22:30:05 +01:00
Eugene Pankov
b355fff0f8 added a shortcut to maximize a pane (fixes #819) 2019-12-31 21:53:36 +01:00
Eugene
f80b0eb65b Bump rxjs from 6.5.3 to 6.5.4 in /app (#1934)
Bump rxjs from 6.5.3 to 6.5.4 in /app
2019-12-31 20:16:49 +01:00
dependabot-preview[bot]
285691326f Bump rxjs from 6.5.3 to 6.5.4 in /app
Bumps [rxjs](https://github.com/reactivex/rxjs) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/reactivex/rxjs/releases)
- [Changelog](https://github.com/ReactiveX/rxjs/blob/6.5.4/CHANGELOG.md)
- [Commits](https://github.com/reactivex/rxjs/compare/6.5.3...6.5.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-31 19:15:39 +00:00
Eugene
3ecffbfda6 Bump typescript from 3.7.3 to 3.7.4 (#1939)
Bump typescript from 3.7.3 to 3.7.4
2019-12-31 20:14:36 +01:00
Eugene
3d89a15d18 Bump rxjs-compat from 6.5.3 to 6.5.4 in /app (#1936)
Bump rxjs-compat from 6.5.3 to 6.5.4 in /app
2019-12-31 20:14:17 +01:00
dependabot-preview[bot]
491d4c3b3a Bump typescript from 3.7.3 to 3.7.4
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 3.7.3 to 3.7.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-31 19:13:57 +00:00
Eugene
995f329835 Bump @fortawesome/fontawesome-free from 5.11.2 to 5.12.0 (#1937)
Bump @fortawesome/fontawesome-free from 5.11.2 to 5.12.0
2019-12-31 20:13:19 +01:00
Eugene
28f2ea595d Bump style-loader from 1.0.1 to 1.1.2 (#1938)
Bump style-loader from 1.0.1 to 1.1.2
2019-12-31 20:12:34 +01:00
Eugene
42b7c573ea Bump @typescript-eslint/parser from 2.13.0 to 2.14.0 (#1945)
Bump @typescript-eslint/parser from 2.13.0 to 2.14.0
2019-12-31 20:12:05 +01:00
Eugene Pankov
c40294628a zmodem support (fixes #693) 2019-12-31 20:10:37 +01:00
Eugene Pankov
c11a10144e ssh: stow away the ports button (fixes #1932) 2019-12-31 13:27:34 +01:00
Eugene Pankov
7b6cdb274c fixed shell process not being killed on tab close 2019-12-31 13:01:37 +01:00
dependabot-preview[bot]
a3c74ecdba Bump @typescript-eslint/parser from 2.13.0 to 2.14.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.13.0 to 2.14.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.14.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-31 04:24:07 +00:00
Eugene Pankov
94d91f8182 reset split ratio on double click (fixes #1377) 2019-12-30 18:42:50 +01:00
Eugene Pankov
e4f32c9ade fixed split tab spanner width (fixes #1676) 2019-12-30 18:24:14 +01:00
dependabot-preview[bot]
65fd7b05b1 Bump style-loader from 1.0.1 to 1.1.2
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 1.0.1 to 1.1.2.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v1.0.1...v1.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-30 04:24:12 +00:00
dependabot-preview[bot]
2150fab55b Bump @fortawesome/fontawesome-free from 5.11.2 to 5.12.0
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 5.11.2 to 5.12.0.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/compare/5.11.2...5.12.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-30 04:23:42 +00:00
dependabot-preview[bot]
644cb76fd3 Bump rxjs-compat from 6.5.3 to 6.5.4 in /app
Bumps rxjs-compat from 6.5.3 to 6.5.4.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-30 04:23:22 +00:00
Eugene Pankov
4106d97f6b Revert "Bump deep-equal from 1.1.0 to 2.0.1 in /terminus-terminal"
This reverts commit 402b76bcc9.
2019-12-29 20:19:07 +01:00
Eugene Pankov
98103fd139 disable sandboxing on linux (fixes #960 again) 2019-12-29 19:07:32 +01:00
Eugene
9453e8ba7b Option to disable middle click paste (#1928)
Option to disable middle click paste
2019-12-28 23:32:10 +01:00
LeSeulArtichaut
2f78575cd7 Implement setting 2019-12-28 22:48:06 +01:00
LeSeulArtichaut
500acee064 Add pasteOnMiddleClickOption 2019-12-28 22:42:12 +01:00
Eugene
42eb5f6b78 Bump slug from 1.1.0 to 2.0.0 in /terminus-terminal (#1919)
Bump slug from 1.1.0 to 2.0.0 in /terminus-terminal
2019-12-28 22:15:41 +01:00
dependabot-preview[bot]
ef19b92e85 Bump slug from 1.1.0 to 2.0.0 in /terminus-terminal
Bumps [slug](https://github.com/Trott/node-slug) from 1.1.0 to 2.0.0.
- [Release notes](https://github.com/Trott/node-slug/releases)
- [Changelog](https://github.com/Trott/node-slug/blob/master/CHANGELOG)
- [Commits](https://github.com/Trott/node-slug/compare/v1.1.0...v2.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 21:15:27 +00:00
Eugene
f263f954d4 Bump deep-equal from 1.1.0 to 2.0.1 in /terminus-terminal (#1895)
Bump deep-equal from 1.1.0 to 2.0.1 in /terminus-terminal
2019-12-28 22:14:15 +01:00
Eugene Pankov
2ce0f03282 lint 2019-12-28 22:13:36 +01:00
Eugene
150999d3a3 Bump @typescript-eslint/eslint-plugin from 2.9.0 to 2.13.0 (#1917)
Bump @typescript-eslint/eslint-plugin from 2.9.0 to 2.13.0
2019-12-28 22:10:17 +01:00
dependabot-preview[bot]
8cc76555d2 Bump @typescript-eslint/eslint-plugin from 2.9.0 to 2.13.0
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 2.9.0 to 2.13.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.13.0/packages/eslint-plugin)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 21:06:02 +00:00
Eugene
f0f8f06890 Bump @typescript-eslint/parser from 2.10.0 to 2.13.0 (#1916)
Bump @typescript-eslint/parser from 2.10.0 to 2.13.0
2019-12-28 22:04:16 +01:00
Eugene
176a55c91d Bump val-loader from 2.0.1 to 2.1.0 (#1900)
Bump val-loader from 2.0.1 to 2.1.0
2019-12-28 22:04:07 +01:00
Eugene
fc6dfc50dd Bump core-js from 3.4.2 to 3.6.1 in /terminus-core (#1923)
Bump core-js from 3.4.2 to 3.6.1 in /terminus-core
2019-12-28 22:03:14 +01:00
Eugene
34d020f66a Bump core-js from 3.4.2 to 3.6.1 (#1922)
Bump core-js from 3.4.2 to 3.6.1
2019-12-28 22:03:07 +01:00
Eugene
fa0ef69c46 Bump typedoc from 0.15.3 to 0.15.6 (#1924)
Bump typedoc from 0.15.3 to 0.15.6
2019-12-28 22:02:30 +01:00
dependabot-preview[bot]
e5c1e421f7 Bump core-js from 3.4.2 to 3.6.1
Bumps [core-js](https://github.com/zloirock/core-js) from 3.4.2 to 3.6.1.
- [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.4.2...v3.6.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 21:02:14 +00:00
dependabot-preview[bot]
f3994f1bd9 Bump val-loader from 2.0.1 to 2.1.0
Bumps [val-loader](https://github.com/webpack-contrib/val-loader) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/webpack-contrib/val-loader/releases)
- [Changelog](https://github.com/webpack-contrib/val-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/val-loader/compare/v2.0.1...v2.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 21:01:35 +00:00
dependabot-preview[bot]
6956ef9e0f Bump core-js from 3.4.2 to 3.6.1 in /terminus-core
Bumps [core-js](https://github.com/zloirock/core-js) from 3.4.2 to 3.6.1.
- [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.4.2...v3.6.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 21:01:33 +00:00
Eugene
a080129882 Bump eslint-plugin-import from 2.18.2 to 2.19.1 (#1874)
Bump eslint-plugin-import from 2.18.2 to 2.19.1
2019-12-28 22:01:31 +01:00
Eugene
ef9bfe6120 docs: add LeSeulArtichaut as a contributor (#1927)
docs: add LeSeulArtichaut as a contributor
2019-12-28 22:01:25 +01:00
dependabot-preview[bot]
37d69e858f Bump eslint-plugin-import from 2.18.2 to 2.19.1
Bumps [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import) from 2.18.2 to 2.19.1.
- [Release notes](https://github.com/benmosher/eslint-plugin-import/releases)
- [Changelog](https://github.com/benmosher/eslint-plugin-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/benmosher/eslint-plugin-import/compare/v2.18.2...v2.19.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 21:01:24 +00:00
Eugene
ee594f5bcd Bump semver from 6.3.0 to 7.1.1 in /terminus-plugin-manager (#1899)
Bump semver from 6.3.0 to 7.1.1 in /terminus-plugin-manager
2019-12-28 22:00:37 +01:00
Eugene
adf022de2c Bump css-loader from 3.2.1 to 3.4.0 (#1901)
Bump css-loader from 3.2.1 to 3.4.0
2019-12-28 22:00:16 +01:00
Eugene
c5a9b890c4 Bump eslint from 6.7.1 to 6.8.0 (#1914)
Bump eslint from 6.7.1 to 6.8.0
2019-12-28 21:59:39 +01:00
dependabot-preview[bot]
2d1a96a12b Bump css-loader from 3.2.1 to 3.4.0
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.2.1 to 3.4.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.2.1...v3.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-28 20:59:38 +00:00
Eugene
5289981485 Bump webpack from 5.0.0-beta.7 to 5.0.0-beta.11 (#1920)
Bump webpack from 5.0.0-beta.7 to 5.0.0-beta.11
2019-12-28 21:59:22 +01:00
Eugene
a89047b205 Bump electron from 7.1.3 to 7.1.7 (#1911)
Bump electron from 7.1.3 to 7.1.7
2019-12-28 21:57:27 +01:00
Eugene
a7b4496d22 Bump windows-native-registry from 1.0.16 to 1.0.17 in /app (#1857)
Bump windows-native-registry from 1.0.16 to 1.0.17 in /app
2019-12-28 21:56:18 +01:00
Eugene
09cd9d0e18 Bump xterm-addon-webgl from 0.4.0 to 0.4.1 in /terminus-terminal (#1884)
Bump xterm-addon-webgl from 0.4.0 to 0.4.1 in /terminus-terminal
2019-12-28 21:56:04 +01:00
allcontributors[bot]
fb2a4d268d docs: update .all-contributorsrc [skip ci] 2019-12-28 20:54:29 +00:00
allcontributors[bot]
9b61615701 docs: update README.md [skip ci] 2019-12-28 20:54:28 +00:00
Eugene
077d2421e1 Merge pull request #1926 from LeSeulArtichaut/disable-right-click
Add option to disable right click in terminal
2019-12-28 21:54:22 +01:00
LeSeulArtichaut
202ba18a8c Add option to disable right click 2019-12-28 20:17:26 +01:00
dependabot-preview[bot]
63f33f8f4b Bump typedoc from 0.15.3 to 0.15.6
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.15.3 to 0.15.6.
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.15.3...v0.15.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-27 04:26:35 +00:00
dependabot-preview[bot]
3bd89a0194 Bump webpack from 5.0.0-beta.7 to 5.0.0-beta.11
Bumps [webpack](https://github.com/webpack/webpack) from 5.0.0-beta.7 to 5.0.0-beta.11.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.0.0-beta.7...v5.0.0-beta.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-25 04:23:26 +00:00
dependabot-preview[bot]
604bc28c9a Bump @typescript-eslint/parser from 2.10.0 to 2.13.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.10.0 to 2.13.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.13.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-24 04:25:41 +00:00
dependabot-preview[bot]
f81f5d122a Bump eslint from 6.7.1 to 6.8.0
Bumps [eslint](https://github.com/eslint/eslint) from 6.7.1 to 6.8.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v6.7.1...v6.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 04:23:30 +00:00
dependabot-preview[bot]
3633be750e Bump electron from 7.1.3 to 7.1.7
Bumps [electron](https://github.com/electron/electron) from 7.1.3 to 7.1.7.
- [Release notes](https://github.com/electron/electron/releases)
- [Commits](https://github.com/electron/electron/compare/v7.1.3...v7.1.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-20 04:23:02 +00:00
dependabot-preview[bot]
404fd72ea9 Bump semver from 6.3.0 to 7.1.1 in /terminus-plugin-manager
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 7.1.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/master/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v7.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-18 04:23:05 +00:00
dependabot-preview[bot]
402b76bcc9 Bump deep-equal from 1.1.0 to 2.0.1 in /terminus-terminal
Bumps [deep-equal](https://github.com/inspect-js/node-deep-equal) from 1.1.0 to 2.0.1.
- [Release notes](https://github.com/inspect-js/node-deep-equal/releases)
- [Changelog](https://github.com/inspect-js/node-deep-equal/blob/master/CHANGELOG.md)
- [Commits](https://github.com/inspect-js/node-deep-equal/compare/v1.1.0...v2.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-17 04:21:25 +00:00
Eugene Pankov
b6c97ffa49 ui tweaks 2019-12-15 16:57:38 +01:00
dependabot-preview[bot]
20aa1d814f Bump xterm-addon-webgl from 0.4.0 to 0.4.1 in /terminus-terminal
Bumps [xterm-addon-webgl](https://github.com/xtermjs/xterm.js) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/xtermjs/xterm.js/releases)
- [Commits](https://github.com/xtermjs/xterm.js/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-12 04:23:48 +00:00
Eugene Pankov
786daaac32 lint 2019-12-11 18:07:01 +01:00
Eugene Pankov
0360ad2dd0 lint 2019-12-11 16:34:01 +01:00
Eugene Pankov
0a451c5876 automatically reconnect without x11 forwarding if rejected (fixes #1880) 2019-12-11 16:31:05 +01:00
Eugene Pankov
5a9625424c added 'duplicate as admin' tab menu item 2019-12-09 18:06:16 +01:00
Eugene Pankov
62c1f6463b added x11 forwarding (fixes #630) 2019-12-08 13:40:15 +01:00
Eugene Pankov
9fe82f2c0a xterm binary input support 2019-12-06 12:26:19 +01:00
Eugene
09838197a2 Bump electron from 7.1.2 to 7.1.3 (#1858)
Bump electron from 7.1.2 to 7.1.3
2019-12-06 12:06:38 +01:00
Eugene
27114797a2 Bump typescript from 3.7.2 to 3.7.3 (#1859)
Bump typescript from 3.7.2 to 3.7.3
2019-12-06 12:06:17 +01:00
Eugene
4dc77d11cf Bump xterm-addon-search from 0.4.0-beta5 to 0.4.0 in /terminus-… (#1863)
Bump xterm-addon-search from 0.4.0-beta5 to 0.4.0 in /terminus-terminal
2019-12-06 12:06:00 +01:00
Eugene
245698b67d Bump xterm-addon-webgl from 0.4.0-beta.15 to 0.4.0 in /terminus… (#1862)
Bump xterm-addon-webgl from 0.4.0-beta.15 to 0.4.0 in /terminus-terminal
2019-12-06 12:05:49 +01:00
Eugene
017fabaf6f Bump xterm from 4.3.0-beta.30 to 4.3.0 in /terminus-terminal (#1864)
Bump xterm from 4.3.0-beta.30 to 4.3.0 in /terminus-terminal
2019-12-06 12:05:11 +01:00
dependabot-preview[bot]
6c11189b3e Bump xterm from 4.3.0-beta.30 to 4.3.0 in /terminus-terminal
Bumps [xterm](https://github.com/xtermjs/xterm.js) from 4.3.0-beta.30 to 4.3.0.
- [Release notes](https://github.com/xtermjs/xterm.js/releases)
- [Commits](https://github.com/xtermjs/xterm.js/commits/4.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-06 04:26:55 +00:00
dependabot-preview[bot]
dd70f5f5d8 Bump xterm-addon-search from 0.4.0-beta5 to 0.4.0 in /terminus-terminal
Bumps [xterm-addon-search](https://github.com/xtermjs/xterm.js) from 0.4.0-beta5 to 0.4.0.
- [Release notes](https://github.com/xtermjs/xterm.js/releases)
- [Commits](https://github.com/xtermjs/xterm.js/commits/0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-06 04:26:30 +00:00
dependabot-preview[bot]
47277ac5aa Bump xterm-addon-webgl from 0.4.0-beta.15 to 0.4.0 in /terminus-terminal
Bumps [xterm-addon-webgl](https://github.com/xtermjs/xterm.js) from 0.4.0-beta.15 to 0.4.0.
- [Release notes](https://github.com/xtermjs/xterm.js/releases)
- [Commits](https://github.com/xtermjs/xterm.js/commits/0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-06 04:26:30 +00:00
Eugene
b69cbbcdd1 [Security] Bump serialize-javascript from 2.1.0 to 2.1.1 (#1861)
[Security] Bump serialize-javascript from 2.1.0 to 2.1.1
2019-12-05 20:31:44 +01:00
dependabot-preview[bot]
efba980a1d [Security] Bump serialize-javascript from 2.1.0 to 2.1.1
Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 2.1.0 to 2.1.1. **This update includes a security fix.**
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v2.1.0...v2.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-05 18:51:22 +00:00
dependabot-preview[bot]
f31da67508 Bump typescript from 3.7.2 to 3.7.3
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 3.7.2 to 3.7.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v3.7.2...v3.7.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-05 04:27:04 +00:00
dependabot-preview[bot]
2ba76cc0b9 Bump electron from 7.1.2 to 7.1.3
Bumps [electron](https://github.com/electron/electron) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/electron/electron/releases)
- [Commits](https://github.com/electron/electron/compare/v7.1.2...v7.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-05 04:26:44 +00:00
dependabot-preview[bot]
62d14ac0cb Bump windows-native-registry from 1.0.16 to 1.0.17 in /app
Bumps [windows-native-registry](https://github.com/Eugeny/windows-native-registry) from 1.0.16 to 1.0.17.
- [Release notes](https://github.com/Eugeny/windows-native-registry/releases)
- [Commits](https://github.com/Eugeny/windows-native-registry/commits/v1.0.17)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-05 04:26:02 +00:00
Eugene
c1b4ffd248 Bump raw-loader from 3.1.0 to 4.0.0 (#1813)
Bump raw-loader from 3.1.0 to 4.0.0
2019-12-04 11:11:01 +01:00
Eugene
87cacdb568 Bump url-loader from 2.3.0 to 3.0.0 (#1827)
Bump url-loader from 2.3.0 to 3.0.0
2019-12-04 11:09:33 +01:00
Eugene
2a11bc4fcc Bump node-abi from 2.12.0 to 2.13.0 in /app (#1835)
Bump node-abi from 2.12.0 to 2.13.0 in /app
2019-12-04 11:04:02 +01:00
Eugene
f716baa7d4 Bump file-loader from 4.3.0 to 5.0.2 (#1815)
Bump file-loader from 4.3.0 to 5.0.2
2019-12-04 11:03:31 +01:00
Eugene
5b60daf366 Bump css-loader from 3.2.0 to 3.2.1 (#1848)
Bump css-loader from 3.2.0 to 3.2.1
2019-12-04 11:03:08 +01:00
Eugene
11f9f4e824 Bump @typescript-eslint/parser from 2.9.0 to 2.10.0 (#1845)
Bump @typescript-eslint/parser from 2.9.0 to 2.10.0
2019-12-04 11:02:38 +01:00
Eugene
0daf48f699 Bump style-loader from 1.0.0 to 1.0.1 (#1840)
Bump style-loader from 1.0.0 to 1.0.1
2019-12-04 11:01:57 +01:00
Eugene
8fb0ea4d75 Bump ssh2 from 0.8.6 to 0.8.7 in /terminus-ssh (#1852)
Bump ssh2 from 0.8.6 to 0.8.7 in /terminus-ssh
2019-12-04 11:01:38 +01:00
dependabot-preview[bot]
d9948cf6e2 Bump ssh2 from 0.8.6 to 0.8.7 in /terminus-ssh
Bumps [ssh2](https://github.com/mscdex/ssh2) from 0.8.6 to 0.8.7.
- [Release notes](https://github.com/mscdex/ssh2/releases)
- [Commits](https://github.com/mscdex/ssh2/compare/v0.8.6...v0.8.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-04 07:54:14 +00:00
Eugene
54b618cffc Merge pull request #1853 from Eugeny/dependabot/npm_and_yarn/terminus-ssh/ssh2-streams-0.4.8
Bump ssh2-streams from 0.4.7 to 0.4.8 in /terminus-ssh
2019-12-04 08:52:56 +01:00
dependabot-preview[bot]
690dde628e Bump ssh2-streams from 0.4.7 to 0.4.8 in /terminus-ssh
Bumps [ssh2-streams](https://github.com/mscdex/ssh2-streams) from 0.4.7 to 0.4.8.
- [Release notes](https://github.com/mscdex/ssh2-streams/releases)
- [Commits](https://github.com/mscdex/ssh2-streams/compare/v0.4.7...v0.4.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-04 04:22:42 +00:00
dependabot-preview[bot]
3dfbcf9d41 Bump css-loader from 3.2.0 to 3.2.1
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-03 04:26:53 +00:00
dependabot-preview[bot]
d91ba71ec0 Bump @typescript-eslint/parser from 2.9.0 to 2.10.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.9.0 to 2.10.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.10.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-03 04:25:19 +00:00
Eugene Pankov
99698913a8 lint 2019-12-03 00:55:22 +01:00
Eugene Pankov
0dbb16d859 implemented port forwarding (fixes #821) 2019-12-03 00:45:35 +01:00
Eugene Pankov
0f8cff2d5b fixes 2019-12-01 19:11:05 +01:00
Eugene Pankov
471f9effcf better search UI 2019-12-01 18:52:22 +01:00
dependabot-preview[bot]
04faf1a04a Bump file-loader from 4.3.0 to 5.0.2
Bumps [file-loader](https://github.com/webpack-contrib/file-loader) from 4.3.0 to 5.0.2.
- [Release notes](https://github.com/webpack-contrib/file-loader/releases)
- [Changelog](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/file-loader/compare/v4.3.0...v5.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-30 20:11:55 +00:00
Eugene Pankov
656f5c2561 eslint 2019-11-30 21:09:42 +01:00
Eugene
99bc2c1c65 Bump @typescript-eslint/parser from 2.8.0 to 2.9.0 (#1814)
Bump @typescript-eslint/parser from 2.8.0 to 2.9.0
2019-11-30 21:01:11 +01:00
dependabot-preview[bot]
87837bf66b Bump bootstrap from 4.3.1 to 4.4.1 in /terminus-core
Bumps [bootstrap](https://github.com/twbs/bootstrap) from 4.3.1 to 4.4.1.
- [Release notes](https://github.com/twbs/bootstrap/releases)
- [Commits](https://github.com/twbs/bootstrap/compare/v4.3.1...v4.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-29 04:38:02 +00:00
dependabot-preview[bot]
c8735243f3 Bump style-loader from 1.0.0 to 1.0.1
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v1.0.0...v1.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-29 04:30:36 +00:00
dependabot-preview[bot]
b197a16e5c Bump node-abi from 2.12.0 to 2.13.0 in /app
Bumps [node-abi](https://github.com/lgeiger/node-abi) from 2.12.0 to 2.13.0.
- [Release notes](https://github.com/lgeiger/node-abi/releases)
- [Commits](https://github.com/lgeiger/node-abi/compare/v2.12.0...v2.13.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-28 04:22:41 +00:00
dependabot-preview[bot]
1a361e67b3 Bump url-loader from 2.3.0 to 3.0.0
Bumps [url-loader](https://github.com/webpack-contrib/url-loader) from 2.3.0 to 3.0.0.
- [Release notes](https://github.com/webpack-contrib/url-loader/releases)
- [Changelog](https://github.com/webpack-contrib/url-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/url-loader/compare/v2.3.0...v3.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-27 04:28:12 +00:00
Eugene Pankov
fc471b2c16 prevent infinite timers caused by sentry 2019-11-26 16:55:33 +01:00
dependabot-preview[bot]
ae17faa7e5 Bump raw-loader from 3.1.0 to 4.0.0
Bumps [raw-loader](https://github.com/webpack-contrib/raw-loader) from 3.1.0 to 4.0.0.
- [Release notes](https://github.com/webpack-contrib/raw-loader/releases)
- [Changelog](https://github.com/webpack-contrib/raw-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/raw-loader/compare/v3.1.0...v4.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-26 14:53:43 +00:00
Eugene Pankov
5fb70f1812 sentry electron sdk 2019-11-26 15:51:31 +01:00
Eugene Pankov
03fc68bb6d fixed #1785 2019-11-26 15:11:26 +01:00
Eugene Pankov
bb9c80623d fixed wnr version 2019-11-26 10:49:22 +01:00
dependabot-preview[bot]
4dd0a5951f Bump @typescript-eslint/parser from 2.8.0 to 2.9.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.9.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-26 04:26:18 +00:00
Eugene Pankov
b010791767 bumped xterm 2019-11-26 00:38:52 +01:00
Eugene Pankov
ef61a141a6 xterm version fix 2019-11-26 00:02:12 +01:00
87 changed files with 2350 additions and 911 deletions

View File

@@ -225,6 +225,24 @@
"contributions": [
"code"
]
},
{
"login": "LeSeulArtichaut",
"name": "LeSeulArtichaut",
"avatar_url": "https://avatars1.githubusercontent.com/u/38361244?v=4",
"profile": "https://github.com/LeSeulArtichaut",
"contributions": [
"code"
]
},
{
"login": "CyrilTaylor",
"name": "Cyril Taylor",
"avatar_url": "https://avatars0.githubusercontent.com/u/12631466?v=4",
"profile": "https://github.com/CyrilTaylor",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
@@ -232,5 +250,6 @@
"projectOwner": "Eugeny",
"repoType": "github",
"repoHost": "https://github.com",
"commitConvention": "none"
"commitConvention": "none",
"skipCi": true
}

View File

@@ -97,3 +97,4 @@ rules:
'@typescript-eslint/no-untyped-public-signature': off # bugs out on constructors
'@typescript-eslint/restrict-template-expressions': off
'@typescript-eslint/no-dynamic-delete': off
'@typescript-eslint/prefer-nullish-coalescing': off

4
.gitignore vendored
View File

@@ -24,3 +24,7 @@ yarn-error.log
docs/api
.travis.ssh.key
*.code-workspace
.electron-symbols
sentry.properties
sentry-symbols.js

View File

@@ -13,12 +13,13 @@
**Terminus** is a highly configurable terminal emulator for Windows, macOS and Linux
* Integrated SSH client and connection manager
* Theming and color schemes
* Fully configurable shortcuts
* Split panes
* Remembers your tabs
* PowerShell (and PS Core), WSL, Git-Bash, Cygwin, Cmder and CMD support
* Integrated SSH client and connection manager
* Direct file transfer from/to SSH sessions via Zmodem
* Full Unicode support including double-width characters
* Doesn't choke on fast-flowing outputs
* Proper shell experience on Windows including tab completion (via Clink)
@@ -66,42 +67,47 @@ See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) and
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://www.russellmyers.com"><img src="https://avatars2.githubusercontent.com/u/184085?v=4" width="100px;" alt="Russell Myers"/><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="Austin Warren"/><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="Felicia Hummel"/><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="Mike MacCana"/><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="Yacine Kanzari"/><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="BBJip"/><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="Futagirl"/><br /><sub><b>Futagirl</b></sub></a><br /><a href="#design-Futagirl" title="Design">🎨</a></td>
<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.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="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/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/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/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/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>
</tr>
<tr>
<td align="center"><a href="https://www.levrik.io"><img src="https://avatars3.githubusercontent.com/u/9491603?v=4" width="100px;" alt="Levin Rickert"/><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="OJ Kwon"/><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="domain"/><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="James Brumond"/><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="Daniel Imms"/><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="Florian Bachmann"/><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="Michael Kühnel"/><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="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://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://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="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.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="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="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>
</tr>
<tr>
<td align="center"><a href="https://github.com/NieLeben"><img src="https://avatars3.githubusercontent.com/u/47182955?v=4" width="100px;" alt="Tilmann Meyer"/><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="PM Extra"/><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="Jonathan"/><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="Hans Koch"/><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="Dak Smyth"/><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="Wang Zhi"/><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="jack1142"/><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/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="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="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://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="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://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="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>
</tr>
<tr>
<td align="center"><a href="https://github.com/hdougie"><img src="https://avatars1.githubusercontent.com/u/450799?v=4" width="100px;" alt="Howie Douglas"/><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="Chris Kaczor"/><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="Johannes Kadak"/><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/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://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://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://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/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>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -13,16 +13,18 @@ export class Application {
})
const configData = loadConfig()
if (process.platform === 'linux' && ((configData.appearance || {}).opacity || 1) !== 1) {
app.commandLine.appendSwitch('enable-transparent-visuals')
app.disableHardwareAcceleration()
if (process.platform === 'linux') {
app.commandLine.appendSwitch('no-sandbox')
if (((configData.appearance || {}).opacity || 1) !== 1) {
app.commandLine.appendSwitch('enable-transparent-visuals')
app.disableHardwareAcceleration()
}
}
app.commandLine.appendSwitch('disable-http-cache')
app.commandLine.appendSwitch('lang', 'EN')
for (const flag of configData.flags || [['force_discrete_gpu', '0']]) {
console.log('Setting Electron flag:', flag.join('='))
app.commandLine.appendSwitch(flag[0], flag[1])
}
}

View File

@@ -1,8 +1,11 @@
import './sentry'
import './lru'
import { app, ipcMain, Menu } from 'electron'
import { parseArgs } from './cli'
import { Application } from './app'
import electronDebug = require('electron-debug')
import * as path from 'path'
import * as fs from 'fs'
if (!process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS = ''
@@ -10,6 +13,17 @@ if (!process.env.TERMINUS_PLUGINS) {
const application = new Application()
const portableData = path.join(`${process.env.PORTABLE_EXECUTABLE_DIR}`, 'data')
if (('PORTABLE_EXECUTABLE_DIR' in process.env) && fs.existsSync(portableData)) {
fs.stat(portableData, (err, stats) => {
if (stats.isDirectory()) {
app.setPath('userData' ,portableData)
} else {
console.warn(err)
}
})
}
ipcMain.on('app:new-window', () => {
application.newWindow()
})
@@ -46,7 +60,7 @@ if (argv.d) {
electronDebug({
isEnabled: true,
showDevTools: true,
devToolsMode: 'undocked'
devToolsMode: 'undocked',
})
}

21
app/lib/sentry.ts Normal file
View File

@@ -0,0 +1,21 @@
const { init } = process.type === 'main' ? require('@sentry/electron/dist/main') : require('@sentry/electron/dist/renderer')
import * as isDev from 'electron-is-dev'
const SENTRY_DSN = 'https://4717a0a7ee0b4429bd3a0f06c3d7eec3@sentry.io/181876'
let release
try {
release = require('electron').app.getVersion()
} catch {
release = require('electron').remote.app.getVersion()
}
if (!isDev) {
init({
dsn: SENTRY_DSN,
release,
integrations (integrations) {
return integrations.filter(integration => integration.name !== 'Breadcrumbs')
},
})
}

View File

@@ -3,6 +3,7 @@ import { debounceTime } from 'rxjs/operators'
import { BrowserWindow, app, ipcMain, Rectangle, screen } from 'electron'
import ElectronConfig = require('electron-config')
import * as os from 'os'
import * as path from 'path'
import { loadConfig } from './config'
@@ -46,6 +47,7 @@ export class Window {
minHeight: 300,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'sentry.js'),
},
frame: false,
show: false,
@@ -147,14 +149,14 @@ export class Window {
this.window.webContents.send(event, ...args)
}
isDestroyed() {
isDestroyed () {
return !this.window || this.window.isDestroyed();
}
private setupWindowManagement () {
this.window.on('show', () => {
this.visible.next(true)
this.window.webContents.send('host:window-shown')
this.send('host:window-shown')
})
this.window.on('hide', () => {
@@ -164,20 +166,20 @@ export class Window {
let moveSubscription = new Observable<void>(observer => {
this.window.on('move', () => observer.next())
}).pipe(debounceTime(250)).subscribe(() => {
this.window.webContents.send('host:window-moved')
this.send('host:window-moved')
})
this.window.on('closed', () => {
moveSubscription.unsubscribe()
})
this.window.on('enter-full-screen', () => this.window.webContents.send('host:window-enter-full-screen'))
this.window.on('leave-full-screen', () => this.window.webContents.send('host:window-leave-full-screen'))
this.window.on('enter-full-screen', () => this.send('host:window-enter-full-screen'))
this.window.on('leave-full-screen', () => this.send('host:window-leave-full-screen'))
this.window.on('close', event => {
if (!this.closing) {
event.preventDefault()
this.window.webContents.send('host:window-close-request')
this.send('host:window-close-request')
return
}
this.windowConfig.set('windowBoundaries', this.windowBounds)
@@ -200,6 +202,10 @@ export class Window {
}
})
this.window.on('focus', () => {
this.send('host:window-focused')
})
ipcMain.on('window-focus', event => {
if (!this.window || event.sender !== this.window.webContents) {
return

View File

@@ -34,21 +34,21 @@
"node-pty": "^0.10.0-beta2",
"npm": "6.9.0",
"path": "0.12.7",
"rxjs": "^6.5.3",
"rxjs-compat": "^6.5.3",
"yargs": "^15.0.2",
"rxjs": "^6.5.4",
"rxjs-compat": "^6.5.4",
"yargs": "^15.1.0",
"zone.js": "^0.8.29"
},
"optionalDependencies": {
"macos-native-processlist": "^1.0.2",
"windows-blurbehind": "^1.0.1",
"windows-native-registry": "^1.0.15",
"windows-native-registry": "^1.0.17",
"windows-process-tree": "^0.2.4",
"windows-swca": "^2.0.2"
},
"devDependencies": {
"@types/mz": "0.0.32",
"@types/node": "12.7.12",
"node-abi": "^2.12.0"
"node-abi": "^2.13.0"
}
}

View File

@@ -6,33 +6,3 @@ import '@fortawesome/fontawesome-free/css/brands.css'
import '@fortawesome/fontawesome-free/css/fontawesome.css'
import 'ngx-toastr/toastr.css'
import './preload.scss'
import * as Raven from 'raven-js'
const SENTRY_DSN = 'https://4717a0a7ee0b4429bd3a0f06c3d7eec3@sentry.io/181876'
Raven.config(
SENTRY_DSN,
{
release: require('electron').remote.app.getVersion(),
dataCallback: (data: any) => {
const normalize = (filename: string) => {
const splitArray = filename.split('/')
return splitArray[splitArray.length - 1]
}
data.exception.values[0].stacktrace.frames.forEach((frame: any) => {
frame.filename = normalize(frame.filename)
})
data.culprit = data.exception.values[0].stacktrace.frames[0].filename
return data
},
},
)
process.on('uncaughtException' as any, (err) => {
Raven.captureException(err as any)
console.error(err)
})

View File

@@ -7,7 +7,8 @@ import * as isDev from 'electron-is-dev'
import './global.scss'
import './toastr.scss'
import { enableProdMode, NgModuleRef } from '@angular/core'
import { enableProdMode, NgModuleRef, ApplicationRef } from '@angular/core'
import { enableDebugTools } from '@angular/platform-browser'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { getRootModule } from './app.module'
@@ -37,7 +38,14 @@ async function bootstrap (plugins: PluginInfo[], safeMode = false): Promise<NgMo
})
const module = getRootModule(pluginsModules)
window['rootModule'] = module
return platformBrowserDynamic().bootstrapModule(module)
return platformBrowserDynamic().bootstrapModule(module).then(moduleRef => {
if (isDev) {
const applicationRef = moduleRef.injector.get(ApplicationRef)
const componentRef = applicationRef.components[0]
enableDebugTools(componentRef)
}
return moduleRef
})
}
findPlugins().then(async plugins => {

View File

@@ -10,6 +10,10 @@
background-image: none;
width: auto;
&.toast-error {
background-color: #BD362F;
}
&.toast-info {
background-color: #555;
}

View File

@@ -6,6 +6,7 @@ module.exports = {
target: 'node',
entry: {
'index.ignore': 'file-loader?name=index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
sentry: path.resolve(__dirname, 'lib/sentry.ts'),
preload: path.resolve(__dirname, 'src/entry.preload.ts'),
bundle: path.resolve(__dirname, 'src/entry.ts'),
},
@@ -78,5 +79,8 @@ module.exports = {
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.DefinePlugin({
'process.type': '"renderer"'
}),
],
}

View File

@@ -45,5 +45,8 @@ module.exports = {
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.DefinePlugin({
'process.type': '"main"',
}),
],
}

View File

@@ -1925,10 +1925,10 @@ ngx-toastr@^10.2.0:
dependencies:
tslib "^1.9.0"
node-abi@^2.12.0, node-abi@^2.7.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.12.0.tgz#40e9cfabdda1837863fa825e7dfa0b15686adf6f"
integrity sha512-VhPBXCIcvmo/5K8HPmnWJyyhvgKxnHTUMXR/XwGHV68+wrgkzST4UmQrY/XszSWA5dtnXpNp528zkcyJ/pzVcw==
node-abi@^2.13.0, node-abi@^2.7.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.13.0.tgz#e2f2ec444d0aca3ea1b3874b6de41d1665828f63"
integrity sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==
dependencies:
semver "^5.4.1"
@@ -2801,15 +2801,15 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rxjs-compat@^6.5.3:
version "6.5.3"
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.5.3.tgz#18440949b2678bf87a78a754009676b2c49183dc"
integrity sha512-BIJX2yovz3TBpjJoAZyls2QYuU6ZiCaZ+U96SmxQpuSP/qDUfiXPKOVLbThBB2WZijNHkdTTJXKRwvv5Y48H7g==
rxjs-compat@^6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.5.4.tgz#03825692af3fe363e04c43f41ff4113d76bbd305"
integrity sha512-rkn+lbOHUQOurdd74J/hjmDsG9nFx0z66fvnbs8M95nrtKvNqCKdk7iZqdY51CGmDemTQk+kUPy4s8HVOHtkfA==
rxjs@^6.5.3:
version "6.5.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
rxjs@^6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
dependencies:
tslib "^1.9.0"
@@ -3397,10 +3397,10 @@ windows-blurbehind@^1.0.1:
resolved "https://registry.yarnpkg.com/windows-blurbehind/-/windows-blurbehind-1.0.1.tgz#ff098713873304e38330b2c54cc41bb369b587b9"
integrity sha512-1HzHfCiM1ayrbACJu5qE9zELV24uX/tINT6kxaZwLY3rtQAoeav6x9z7LFHWoLaGDN/sYbnK+9Vk0cz7fsk5HQ==
windows-native-registry@^1.0.15:
version "1.0.15"
resolved "https://registry.yarnpkg.com/windows-native-registry/-/windows-native-registry-1.0.15.tgz#02593331fb7dcab99ef3ac9dd71f2c71ae57b189"
integrity sha512-uIsz1y3LrKPkphcZsezThz07FW7Vm00Zfa6ZU88rIo2zilOLE6Ui75jh6UkBAMso8xJeyvYbbcxF9kr4Zt8Iuw==
windows-native-registry@^1.0.17:
version "1.0.17"
resolved "https://registry.yarnpkg.com/windows-native-registry/-/windows-native-registry-1.0.17.tgz#d8cce48b364703a55c226690431b325114405022"
integrity sha512-u9Fp9TyDo5dvhlW6hYBOdHPETtAahXKxo3jeW5EXwNK7qa+nSNopQycN1drtBVWe3jpJXvyKpt9zrjiDd+u4JQ==
dependencies:
nan "^2.14.0"
@@ -3519,10 +3519,10 @@ yargs@^11.0.0:
y18n "^3.2.1"
yargs-parser "^9.0.2"
yargs@^15.0.2:
version "15.0.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.0.2.tgz#4248bf218ef050385c4f7e14ebdf425653d13bd3"
integrity sha512-GH/X/hYt+x5hOat4LMnCqMd8r5Cv78heOMIJn1hr7QPPBqfeC6p89Y78+WB9yGDvfpCvgasfmWLzNzEioOUD9Q==
yargs@^15.1.0:
version "15.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219"
integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"

View File

@@ -1,30 +1,34 @@
{
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.11.2",
"@fortawesome/fontawesome-free": "^5.12.0",
"@sentry/cli": "^1.49.0",
"@sentry/electron": "^1.0.0",
"@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0",
"@types/js-yaml": "^3.12.1",
"@types/node": "12.7.12",
"@types/webpack-env": "1.14.1",
"@typescript-eslint/eslint-plugin": "^2.8.0",
"@typescript-eslint/parser": "^2.8.0",
"@types/webpack-env": "1.15.0",
"@typescript-eslint/eslint-plugin": "^2.13.0",
"@typescript-eslint/parser": "^2.15.0",
"apply-loader": "2.0.0",
"awesome-typescript-loader": "^5.0.0",
"core-js": "^3.4.2",
"core-js": "^3.6.2",
"cross-env": "6.0.3",
"css-loader": "3.2.0",
"electron": "^7.1.2",
"css-loader": "3.4.1",
"electron": "^7.1.7",
"electron-builder": "22.1.0",
"electron-download": "^4.1.1",
"electron-installer-snap": "^4.1.0",
"electron-notarize": "^0.1.1",
"electron-rebuild": "^1.8.5",
"eslint": "^6.7.1",
"file-loader": "^4.3.0",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.19.1",
"file-loader": "^5.0.2",
"graceful-fs": "^4.2.2",
"html-loader": "0.5.5",
"json-loader": "0.5.7",
"node-abi": "^2.12.0",
"node-gyp": "^6.0.1",
"node-gyp": "^6.1.0",
"node-sass": "^4.13.0",
"npmlog": "4.1.2",
"npx": "^10.2.0",
@@ -33,21 +37,20 @@
"pug-lint": "^2.6.0",
"pug-loader": "^2.4.0",
"pug-static-loader": "2.0.0",
"raven-js": "3.27.2",
"raw-loader": "3.1.0",
"raw-loader": "4.0.0",
"sass-loader": "^8.0.0",
"shelljs": "0.8.3",
"source-code-pro": "^2.30.2",
"source-sans-pro": "3.6.0",
"style-loader": "^1.0.0",
"style-loader": "^1.1.2",
"svg-inline-loader": "^0.8.0",
"to-string-loader": "1.1.6",
"tslib": "^1.10.0",
"typedoc": "^0.15.3",
"typescript": "^3.6.4",
"url-loader": "^2.3.0",
"val-loader": "2.0.1",
"webpack": "^5.0.0-beta.7",
"typedoc": "^0.15.7",
"typescript": "^3.7.4",
"url-loader": "^3.0.0",
"val-loader": "2.1.0",
"webpack": "^5.0.0-beta.11",
"webpack-cli": "^3.3.10",
"yaml-loader": "0.5.0"
},
@@ -133,8 +136,5 @@
"lint": "eslint --ext ts */src",
"postinstall": "node ./scripts/install-deps.js"
},
"repository": "eugeny/terminus",
"dependencies": {
"eslint-plugin-import": "^2.18.2"
}
"repository": "eugeny/terminus"
}

View File

@@ -25,8 +25,5 @@ if (['darwin', 'linux'].includes(process.platform)) {
for (let x of vars.builtinPlugins) {
sh.ln('-fs', '../' + x, x)
}
for (let x of vars.bundledModules) {
sh.ln('-fs', '../app/node_modules/' + x, x)
}
sh.cd('..')
}

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-community-color-schemes",
"version": "1.0.93-nightly.0",
"version": "1.0.99-nightly.0",
"description": "Community color schemes for Terminus",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-core",
"version": "1.0.93-nightly.0",
"version": "1.0.99-nightly.0",
"description": "Terminus core",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -38,7 +38,7 @@ title-bar(
button.btn.btn-secondary.btn-tab-bar(
[title]='button.title',
(click)='button.click && button.click()',
[innerHTML]='sanitizeIcon(button.icon)',
[fastHtmlBind]='button.icon',
ngbDropdownToggle,
)
div(*ngIf='button.submenu', ngbDropdownMenu)
@@ -47,8 +47,11 @@ title-bar(
(click)='item.click()',
ngbDropdownItem,
)
.icon-wrapper([innerHTML]='sanitizeIcon(item.icon)')
.ml-3 {{item.title}}
.icon-wrapper(
*ngIf='hasIcons(button.submenuItems)',
[fastHtmlBind]='item.icon'
)
div([class.ml-3]='hasIcons(button.submenuItems)') {{item.title}}
.drag-space.background([class.persistent]='config.store.appearance.frame == "thin" && hostApp.platform != Platform.macOS')
@@ -61,7 +64,7 @@ title-bar(
button.btn.btn-secondary.btn-tab-bar(
[title]='button.title',
(click)='button.click && button.click()',
[innerHTML]='sanitizeIcon(button.icon)',
[fastHtmlBind]='button.icon',
ngbDropdownToggle,
)
div(*ngIf='button.submenu', ngbDropdownMenu)
@@ -70,14 +73,17 @@ title-bar(
(click)='item.click()',
ngbDropdownItem,
)
.icon-wrapper([innerHTML]='sanitizeIcon(item.icon)')
.ml-3 {{item.title}}
.icon-wrapper(
*ngIf='hasIcons(button.submenuItems)',
[fastHtmlBind]='item.icon'
)
div([class.ml-3]='hasIcons(button.submenuItems)') {{item.title}}
button.btn.btn-secondary.btn-tab-bar.btn-update(
*ngIf='updatesAvailable',
title='Update available - Click to install',
(click)='updateApp()',
[innerHTML]='sanitizeIcon(updateIcon)'
[fastHtmlBind]='updateIcon'
)
window-controls.background(

View File

@@ -1,6 +1,5 @@
import { Component, Inject, Input, HostListener, HostBinding } from '@angular/core'
import { trigger, style, animate, transition, state } from '@angular/animations'
import { DomSanitizer } from '@angular/platform-browser'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ElectronService } from '../services/electron.service'
@@ -75,7 +74,6 @@ export class AppRootComponent {
public hostApp: HostAppService,
public config: ConfigService,
public app: AppService,
private domSanitizer: DomSanitizer,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
log: LogService,
ngbModal: NgbModal,
@@ -250,8 +248,8 @@ export class AppRootComponent {
}
}
sanitizeIcon (icon: string): any {
return this.domSanitizer.bypassSecurityTrustHtml(icon || '')
hasIcons (submenuItems: ToolbarButton[]): boolean {
return submenuItems.some(x => !!x.icon)
}
private getToolbarButtons (aboveZero: boolean): ToolbarButton[] {

View File

@@ -1,4 +0,0 @@
.icon(tabindex='0', [class.active]='model', (keyup.space)='click()')
i.fas.fa-square.off
i.fas.fa-check-square.on
.text {{text}}

View File

@@ -1,55 +0,0 @@
:host {
cursor: pointer;
margin: 5px 0;
&:focus {
background: rgba(255,255,255,.05);
border-radius: 5px;
}
&:active {
background: rgba(255,255,255,.1);
border-radius: 3px;
}
&[disabled] {
opacity: 0.5;
}
display: flex;
flex-direction: row;
align-items: center;
.off {
color: rgba(0, 0, 0, .5);
}
.icon {
position: relative;
flex: none;
width: 14px;
height: 14px;
i {
position: absolute;
left: 0;
top: -2px;
transition: 0.25s opacity;
display: block;
font-size: 18px;
}
i.on, &.active i.off {
opacity: 0;
}
i.off, &.active i.on {
opacity: 1;
}
}
.text {
flex: auto;
margin-left: 8px;
}
}

View File

@@ -4,8 +4,12 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
/** @hidden */
@Component({
selector: 'checkbox',
template: require('./checkbox.component.pug'),
styles: [require('./checkbox.component.scss')],
template: `
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" [(ngModel)]='model'>
<label class="custom-control-label">{{text}}</label>
</div>
`,
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true },
],

View File

@@ -3,3 +3,24 @@
position: relative;
flex: auto;
}
::ng-deep split-tab > .child {
position: absolute;
transition: 0.125s all;
opacity: .75;
&.focused {
opacity: 1;
}
&.minimized {
opacity: .1;
}
&.maximized {
z-index: 2;
box-shadow: rgba(0, 0, 0, 0.25) 0px 0px 30px;
backdrop-filter: blur(10px);
border-radius: 10px;
}
}

View File

@@ -141,6 +141,8 @@ export interface SplitSpannerInfo {
styles: [require('./splitTab.component.scss')],
})
export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDestroy {
static DIRECTIONS: SplitDirection[] = ['t', 'r', 'b', 'l']
/** @hidden */
@ViewChild('vc', { read: ViewContainerRef }) viewContainer: ViewContainerRef
@@ -156,6 +158,7 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
_spanners: SplitSpannerInfo[] = []
private focusedTab: BaseTabComponent
private maximizedTab: BaseTabComponent|null = null
private hotkeysSubscription: Subscription
private viewRefs: Map<BaseTabComponent, EmbeddedViewRef<any>> = new Map()
@@ -226,6 +229,13 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
case 'pane-nav-down':
this.navigate('b')
break
case 'pane-maximize':
if (this.maximizedTab) {
this.maximize(null)
} else if (this.getAllTabs().length > 1) {
this.maximize(this.focusedTab)
}
break
case 'close-pane':
this.removeTab(this.focusedTab)
break
@@ -261,6 +271,10 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
return this.focusedTab
}
getMaximizedTab (): BaseTabComponent|null {
return this.maximizedTab
}
focus (tab: BaseTabComponent) {
this.focusedTab = tab
for (const x of this.getAllTabs()) {
@@ -272,6 +286,15 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
tab.emitFocused()
this.focusChanged.next(tab)
}
if (this.maximizedTab !== tab) {
this.maximizedTab = null
}
this.layout()
}
maximize (tab: BaseTabComponent|null) {
this.maximizedTab = tab
this.layout()
}
@@ -436,6 +459,13 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
this.splitAdjusted.next(spanner)
}
destroy () {
super.destroy()
for (const x of this.getAllTabs()) {
x.destroy()
}
}
private attachTabView (tab: BaseTabComponent) {
const ref = this.viewContainer.insert(tab.hostView) as EmbeddedViewRef<any> // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
this.viewRefs.set(tab, ref)
@@ -486,13 +516,21 @@ export class SplitTabComponent extends BaseTabComponent implements OnInit, OnDes
this.layoutInternal(child, childX, childY, childW, childH)
} else {
const element = this.viewRefs.get(child)!.rootNodes[0]
element.style.position = 'absolute'
element.classList.toggle('child', true)
element.classList.toggle('maximized', child === this.maximizedTab)
element.classList.toggle('minimized', this.maximizedTab && child !== this.maximizedTab)
element.classList.toggle('focused', child === this.focusedTab)
element.style.left = `${childX}%`
element.style.top = `${childY}%`
element.style.width = `${childW}%`
element.style.height = `${childH}%`
element.style.opacity = child === this.focusedTab ? 1 : 0.75
if (child === this.maximizedTab) {
element.style.left = '5%'
element.style.top = '5%'
element.style.width = '90%'
element.style.height = '90%'
}
}
offset += sizes[i]

View File

@@ -16,13 +16,17 @@ export class SplitTabSpannerComponent {
@HostBinding('class.v') isVertical = true
@HostBinding('style.left') cssLeft: string
@HostBinding('style.top') cssTop: string
@HostBinding('style.width') cssWidth: string
@HostBinding('style.height') cssHeight: string
@HostBinding('style.width') cssWidth: string | null
@HostBinding('style.height') cssHeight: string | null
private marginOffset = -5
constructor (private element: ElementRef) { }
ngAfterViewInit () {
this.element.nativeElement.addEventListener('dblclick', () => {
this.reset()
})
this.element.nativeElement.addEventListener('mousedown', (e: MouseEvent) => {
this.isActive = true
const start = this.isVertical ? e.pageY : e.pageX
@@ -49,14 +53,16 @@ export class SplitTabSpannerComponent {
diff = Math.max(diff, -this.container.ratios[this.index - 1] + 0.1)
diff = Math.min(diff, this.container.ratios[this.index] - 0.1)
this.container.ratios[this.index - 1] += diff
this.container.ratios[this.index] -= diff
this.change.emit()
if (diff) {
this.container.ratios[this.index - 1] += diff
this.container.ratios[this.index] -= diff
this.change.emit()
}
}
document.addEventListener('mouseup', offHandler)
document.addEventListener('mouseup', offHandler, { passive: true })
this.element.nativeElement.parentElement.addEventListener('mousemove', dragHandler)
})
}, { passive: true })
}
ngOnChanges () {
@@ -79,10 +85,17 @@ export class SplitTabSpannerComponent {
}
}
reset () {
const ratio = (this.container.ratios[this.index - 1] + this.container.ratios[this.index]) / 2
this.container.ratios[this.index - 1] = ratio
this.container.ratios[this.index] = ratio
this.change.emit()
}
private setDimensions (x: number, y: number, w: number, h: number) {
this.cssLeft = `${x}%`
this.cssTop = `${y}%`
this.cssWidth = w ? `${w}%` : 'initial'
this.cssHeight = h ? `${h}%` : 'initial'
this.cssWidth = w ? `${w}%` : null
this.cssHeight = h ? `${h}%` : null
}
}

View File

@@ -20,7 +20,7 @@ $tabs-height: 38px;
cursor: -webkit-grab;
margin-left: 10px;
width: 20px;
width: 22px;
border-radius: 10px;
text-align: center;
transition: 0.25s all;
@@ -48,7 +48,7 @@ $tabs-height: 38px;
width: $button-size;
height: $button-size;
border-radius: $button-size / 2;
line-height: $button-size * 0.9;
line-height: $button-size;
align-self: center;
margin-right: 10px;

View File

@@ -16,55 +16,8 @@
padding-left: 10px;
margin-left: -10px;
&:focus {
background: rgba(255,255,255,.05);
border-radius: 5px;
}
&[disabled] {
opacity: 0.5;
}
.body {
$border-width: 2px;
border-radius: 5px;
border: $border-width solid rgba(255, 255, 255, .2);
padding: $padding;
height: $toggle-size + $border-width * 2 + $padding * 2;
width: $toggle-size * 2 + $border-width * 2 + $padding * 2;
position: relative;
.toggle {
position: absolute;
border-radius: 2px;
width: $toggle-size;
height: $toggle-size;
background: #475158;
top: $padding;
left: $padding;
transition: 0.25s left;
line-height: 19px;
text-align: center;
font-size: 10px;
i {
opacity: 0;
transition: 0.25s opacity;
}
}
}
&.active .body .toggle {
left: $toggle-size + $padding;
i {
color: white;
opacity: 1;
}
}
&:active {
background: rgba(255,255,255,.1);
}
}

View File

@@ -6,13 +6,10 @@ import { CheckboxComponent } from './checkbox.component'
@Component({
selector: 'toggle',
template: `
<div class="switch">
<div class="body">
<div class="toggle" [class.bg-primary]='model'>
<i class="fa fa-check"></i>
</div>
</div>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" [(ngModel)]='model'>
<label class="custom-control-label"></label>
</div>
`,
styles: [require('./toggle.component.scss')],
providers: [

View File

@@ -50,5 +50,7 @@ hotkeys:
- 'Ctrl-Alt-Up'
pane-nav-left:
- 'Ctrl-Alt-Left'
pane-maximize:
- 'Ctrl-Alt-Enter'
close-pane: []
pluginBlacklist: ['ssh']

View File

@@ -48,6 +48,8 @@ hotkeys:
- '⌘-⌥-Up'
pane-nav-left:
- '⌘-⌥-Left'
pane-maximize:
- '⌘-⌥-Enter'
close-pane:
- '⌘-Shift-W'
pluginBlacklist: ['ssh']

View File

@@ -5,6 +5,7 @@ hotkeys:
- 'Ctrl+Space'
toggle-fullscreen:
- 'F11'
- 'Alt-Enter'
close-tab:
- 'Ctrl-Shift-W'
toggle-last-tab: []
@@ -50,5 +51,7 @@ hotkeys:
- 'Ctrl-Alt-Up'
pane-nav-left:
- 'Ctrl-Alt-Left'
pane-maximize:
- 'Ctrl-Alt-Enter'
close-pane: []
pluginBlacklist: []

View File

@@ -8,7 +8,7 @@ appearance:
frame: thin
css: '/* * { color: blue !important; } */'
opacity: 1.0
vibrancy: false
vibrancy: true
vibrancyType: 'blur'
enableAnalytics: true
enableWelcomeTab: true

View File

@@ -0,0 +1,14 @@
import { Directive, Input, ElementRef, OnChanges } from '@angular/core'
/** @hidden */
@Directive({
selector: '[fastHtmlBind]',
})
export class FastHtmlBindDirective implements OnChanges {
@Input() fastHtmlBind: string
constructor (private el: ElementRef) { }
ngOnChanges () {
this.el.nativeElement.innerHTML = this.fastHtmlBind
}
}

View File

@@ -93,6 +93,10 @@ export class AppHotkeyProvider extends HotkeyProvider {
id: 'split-top',
name: 'Split to the top',
},
{
id: 'pane-maximize',
name: 'Maximize the active pane',
},
{
id: 'pane-nav-up',
name: 'Focus the pane above',

View File

@@ -21,6 +21,7 @@ import { SplitTabSpannerComponent } from './components/splitTabSpanner.component
import { WelcomeTabComponent } from './components/welcomeTab.component'
import { AutofocusDirective } from './directives/autofocus.directive'
import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { HotkeyProvider } from './api/hotkeyProvider'
import { ConfigProvider } from './api/configProvider'
@@ -80,6 +81,7 @@ const PROVIDERS = [
RenameTabModalComponent,
SafeModeModalComponent,
AutofocusDirective,
FastHtmlBindDirective,
SplitTabComponent,
SplitTabSpannerComponent,
WelcomeTabComponent,

View File

@@ -83,6 +83,10 @@ export class AppService {
this.startTabStorage()
}
}
hostApp.windowFocused$.subscribe(() => {
this._activeTab?.emitFocused()
})
}
startTabStorage () {
@@ -169,6 +173,17 @@ export class AppService {
}
}
getParentTab (tab: BaseTabComponent): SplitTabComponent|null {
for (const topLevelTab of this.tabs) {
if (topLevelTab instanceof SplitTabComponent) {
if (topLevelTab.getAllTabs().includes(tab)) {
return topLevelTab
}
}
}
return null
}
/** Switches between the current tab and the previously active one */
toggleLastTab () {
if (!this.lastTabIndex || this.lastTabIndex >= this.tabs.length) {

View File

@@ -39,6 +39,7 @@ export class HostAppService {
private configChangeBroadcast = new Subject<void>()
private windowCloseRequest = new Subject<void>()
private windowMoved = new Subject<void>()
private windowFocused = new Subject<void>()
private displayMetricsChanged = new Subject<void>()
private logger: Logger
private windowId: number
@@ -85,6 +86,8 @@ export class HostAppService {
get windowMoved$ (): Observable<void> { return this.windowMoved }
get windowFocused$ (): Observable<void> { return this.windowFocused }
get displayMetricsChanged$ (): Observable<void> { return this.displayMetricsChanged }
private constructor (
@@ -128,6 +131,10 @@ export class HostAppService {
this.zone.run(() => this.windowMoved.next())
})
electron.ipcRenderer.on('host:window-focused', () => {
this.zone.run(() => this.windowFocused.next())
})
electron.ipcRenderer.on('host:display-metrics-changed', () => {
this.zone.run(() => this.displayMetricsChanged.next())
})

View File

@@ -18,11 +18,18 @@ export class ShellIntegrationService {
private automatorWorkflowsDestination: string
private registryKeys = [
{
path: 'Software\\Classes\\Directory\\Background\\shell\\Open Terminus here',
path: 'Software\\Classes\\Directory\\Background\\shell\\Terminus',
value: 'Open Terminus here',
command: 'open "%V"',
},
{
path: 'Software\\Classes\\*\\shell\\Paste path into Terminus',
path: 'SOFTWARE\\Classes\\Directory\\shell\\Terminus',
value: 'Open Terminus here',
command: 'open "%V"',
},
{
path: 'Software\\Classes\\*\\shell\\Terminus',
value: 'Paste path into Terminus',
command: 'paste "%V"',
},
]
@@ -61,9 +68,17 @@ export class ShellIntegrationService {
for (const registryKey of this.registryKeys) {
wnr.createRegistryKey(wnr.HK.CU, registryKey.path)
wnr.createRegistryKey(wnr.HK.CU, registryKey.path + '\\command')
wnr.setRegistryValue(wnr.HK.CU, registryKey.path, '', wnr.REG.SZ, registryKey.value)
wnr.setRegistryValue(wnr.HK.CU, registryKey.path, 'Icon', wnr.REG.SZ, exe)
wnr.setRegistryValue(wnr.HK.CU, registryKey.path + '\\command', '', wnr.REG.SZ, exe + ' ' + registryKey.command)
}
if(wnr.getRegistryKey(wnr.HK.CU, 'Software\\Classes\\Directory\\Background\\shell\\Open Terminus here')) {
wnr.deleteRegistryKey(wnr.HK.CU, 'Software\\Classes\\Directory\\Background\\shell\\Open Terminus here')
}
if(wnr.getRegistryKey(wnr.HK.CU, 'Software\\Classes\\*\\shell\\Paste path into Terminus')) {
wnr.deleteRegistryKey(wnr.HK.CU, 'Software\\Classes\\*\\shell\\Paste path into Terminus')
}
}
}

View File

@@ -27,6 +27,11 @@ export class UpdaterService {
) {
this.logger = log.create('updater')
if (process.platform === 'linux') {
this.electronUpdaterAvailable = false
return
}
this.autoUpdater = electron.remote.require('electron-updater').autoUpdater
this.autoUpdater.autoInstallOnAppQuit = !!config.store.enableAutomaticUpdates

View File

@@ -1,4 +1,5 @@
import { Injectable, NgZone } from '@angular/core'
import { Subscription } from 'rxjs'
import { AppService } from './services/app.service'
import { BaseTabComponent } from './components/baseTab.component'
import { TabHeaderComponent } from './components/tabHeader.component'
@@ -78,7 +79,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
return [
{
label: 'Rename',
click: () => this.zone.run(() => tabHeader && tabHeader.showRenameTabModal()),
click: () => this.zone.run(() => tabHeader?.showRenameTabModal()),
},
{
label: 'Duplicate',
@@ -112,36 +113,61 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
async getItems (tab: BaseTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
const process = await tab.getCurrentProcess()
if (process) {
return [
{
id: 'process-name',
enabled: false,
label: 'Current process: ' + process.name,
},
{
label: 'Notify when done',
type: 'checkbox',
checked: (tab as any).__completionNotificationEnabled,
click: () => this.zone.run(() => {
(tab as any).__completionNotificationEnabled = !(tab as any).__completionNotificationEnabled
let items: Electron.MenuItemConstructorOptions[] = []
if ((tab as any).__completionNotificationEnabled) {
this.app.observeTabCompletion(tab).subscribe(() => {
new Notification('Process completed', {
body: process.name,
}).addEventListener('click', () => {
this.app.selectTab(tab)
})
;(tab as any).__completionNotificationEnabled = false
const extTab: (BaseTabComponent & { __completionNotificationEnabled?: boolean, __outputNotificationSubscription?: Subscription|null }) = tab
if (process) {
items.push({
id: 'process-name',
enabled: false,
label: 'Current process: ' + process.name,
})
items.push({
label: 'Notify when done',
type: 'checkbox',
checked: extTab.__completionNotificationEnabled,
click: () => this.zone.run(() => {
extTab.__completionNotificationEnabled = !extTab.__completionNotificationEnabled
if (extTab.__completionNotificationEnabled) {
this.app.observeTabCompletion(tab).subscribe(() => {
new Notification('Process completed', {
body: process.name,
}).addEventListener('click', () => {
this.app.selectTab(tab)
})
} else {
this.app.stopObservingTabCompletion(tab)
}
}),
},
]
extTab.__completionNotificationEnabled = false
})
} else {
this.app.stopObservingTabCompletion(tab)
}
}),
})
}
return []
items.push({
label: 'Notify on activity',
type: 'checkbox',
checked: !!extTab.__outputNotificationSubscription,
click: () => this.zone.run(() => {
if (extTab.__outputNotificationSubscription) {
extTab.__outputNotificationSubscription.unsubscribe()
extTab.__outputNotificationSubscription = null
} else {
extTab.__outputNotificationSubscription = tab.activity$.subscribe(active => {
if (extTab.__outputNotificationSubscription && active) {
extTab.__outputNotificationSubscription.unsubscribe()
extTab.__outputNotificationSubscription = null
new Notification('Tab activity', {
body: tab.title,
}).addEventListener('click', () => {
this.app.selectTab(tab)
})
}
})
}
}),
})
return items
}
}

View File

@@ -16,4 +16,8 @@ app-root {
terminaltab .content {
margin: 5px !important;
}
ssh-tab .content {
margin: 5px !important;
}
}

View File

@@ -1,103 +1,11 @@
$tab-border-radius: 5px;
@import "./theme.vars";
// ---------
$button-hover-bg: rgba(0, 0, 0, .25);
$button-active-bg: rgba(0, 0, 0, .5);
$white: #fff !default;
$black: #000 !default;
$red: #d9534f !default;
$orange: #f0ad4e !default;
$yellow: #ffd500 !default;
$green: #5cb85c !default;
$blue: #0275d8 !default;
$teal: #5bc0de !default;
$pink: #ff5b77 !default;
$purple: #613d7c !default;
$theme-colors: (
"primary": $blue,
"secondary": #394b5d
);
$content-bg: rgba(39, 49, 60, 0.65); //#1D272D;
$content-bg-solid: #1D272D;
$body-bg: #131d27;
$body-bg2: #20333e;
$body-color: #ccc;
$font-family-sans-serif: "Source Sans Pro";
$font-family-monospace: "Source Code Pro";
$font-size-base: 14rem / 16;
$btn-border-radius: 0;
$btn-secondary-color: #ccc;
$btn-secondary-bg: #222;
$btn-secondary-border: #444;
//$btn-warning-bg: rgba($orange, .5);
$nav-tabs-border-width: 0;
$nav-tabs-border-radius: 0;
$nav-tabs-link-hover-border-color: $body-bg;
$nav-tabs-active-link-hover-color: $white;
$nav-tabs-active-link-hover-bg: $blue;
$nav-tabs-active-link-hover-border-color: darken($blue, 30%);
$nav-pills-border-radius: 0;
$input-bg: #111;
$input-disabled-bg: #333;
$input-color: $body-color;
$input-color-placeholder: #333;
$input-border-color: #344;
$input-border-width: 1px;
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
$input-border-radius: 0;
$custom-select-border-radius: 0;
$input-bg-focus: $input-bg;
$input-border-focus: lighten($blue, 25%);
$input-focus-box-shadow: none;
$input-color-focus: $input-color;
$input-group-addon-bg: $body-bg;
$input-group-addon-border-color: $input-border-color;
$modal-content-bg: $content-bg-solid;
$modal-content-border-color: $body-bg;
$modal-header-border-color: transparent;
$modal-footer-border-color: transparent;
$popover-bg: $body-bg;
$dropdown-bg: $body-bg;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: white;
$dropdown-link-hover-bg: $body-bg2;
//$dropdown-link-active-color: $component-active-color;
//$dropdown-link-active-bg: $component-active-bg;
$dropdown-link-disabled-color: #333;
$dropdown-header-color: #333;
$list-group-color: $body-color;
$list-group-bg: rgba(255,255,255,.05);
$list-group-border-color: rgba(255,255,255,.1);
$list-group-hover-bg: rgba(255,255,255,.1);
$list-group-link-active-bg: rgba(255,255,255,.2);
$list-group-action-color: $body-color;
$list-group-action-bg: rgba(255,255,255,.05);
$list-group-action-active-bg: $list-group-link-active-bg;
$pre-bg: $dropdown-bg;
$pre-color: $dropdown-link-color;
$alert-danger-bg: $body-bg;
$alert-danger-text: $red;
$alert-danger-border: $red;
$headings-font-weight: lighter;
$headings-color: #eee;
@import '~bootstrap/scss/bootstrap.scss';
window-controls {
@@ -236,13 +144,14 @@ settings-tab > ngb-tabset {
border: none;
padding: 10px 50px 10px 20px;
font-size: 14px;
border-radius: 0;
&:not(.active) {
color: $body-color;
color: $body-color;
&:hover {
color: $white;
}
&:hover {
color: $white;
}
}
}
}
@@ -310,14 +219,6 @@ hotkey-input-modal {
margin-bottom: 2px;
}
.nav-tabs {
background: $btn-secondary-bg;
.nav-link {
transition: 0.25s all;
border-bottom-color: $nav-tabs-border-color;
}
}
ngb-tabset .tab-content {
padding-top: 20px;
}
@@ -361,22 +262,10 @@ ngb-tabset .tab-content {
}
}
select.form-control {
-webkit-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'><path fill='#444' d='M7.406 7.828l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z'></path></svg>");
background-position: 100% 50%;
background-repeat: no-repeat;
padding-right: 30px;
}
checkbox i.on {
color: $blue;
}
toggle.active .body .toggle {
background: $blue;
}
.modal .modal-footer {
background: rgba(0, 0, 0, .25);
@@ -405,3 +294,101 @@ toggle.active .body .toggle {
*::-webkit-resizer {
opacity: 0;
}
search-panel {
background: rgba(39, 49, 60, 0.65) !important;
}
.btn {
cursor: pointer;
justify-content: flex-start;
overflow: hidden;
&.disabled,
&:disabled {
cursor: not-allowed;
}
}
.btn.btn-outline-secondary {
@include button-outline-variant(#9badb9, #fff);
&:hover:not([disabled]), &:active:not([disabled]), &.active:not([disabled]) {
background-color: #3f484e;
border-color: darken(#9badb9, 25%);
}
border-color: darken(#9badb9, 25%);
&.disabled,
&:disabled {
color: #9badb9;
}
}
.btn-warning:not(:disabled):not(.disabled) {
&.active, &:active {
color: $gray-900;
}
}
.btn-secondary:not(:disabled):not(.disabled) {
&.active, &:active {
background: #191e23;
}
}
.btn-link {
&:hover, &[aria-expanded=true], &:active, &.active {
color: $link-hover-color;
border-radius: $btn-border-radius;
}
&[aria-expanded=true], &:active, &.active {
background: rgba(255, 255, 255, 0.1);
}
}
.btn-group .btn.active {
border-color: transparent !important;
}
.nav-tabs {
margin-bottom: 10px;
&.nav-justified .nav-link {
margin-right: 5px;
}
.nav-link {
border: none;
border-bottom: $nav-tabs-border-width solid transparent;
text-transform: uppercase;
font-weight: bold;
padding: 5px 0;
margin-right: 20px;
uib-tab-heading > i {
font-size: 18px;
}
@include hover-focus {
color: $nav-tabs-link-active-color;
}
&.disabled {
color: $nav-link-disabled-color;
border-color: transparent;
}
}
.nav-item:last-child .nav-link {
margin-right: 0;
}
.nav-link.active,
.nav-item.show .nav-link {
color: $nav-tabs-link-active-color;
border-color: $nav-tabs-link-active-border-color;
}
}

View File

@@ -0,0 +1,187 @@
$white: #fff;
$gray-100: #f8f9fa;
$gray-200: #e9ecef;
$gray-300: #dee2e6;
$gray-400: #ced4da;
$gray-500: #adb5bd;
$gray-600: #6c757d;
$gray-700: #495057;
$gray-800: #343a40;
$gray-900: #212529;
$black: #000;
$red: #d9534f !default;
$orange: #f0ad4e !default;
$yellow: #ffd500 !default;
$green: #5cb85c !default;
$blue: #0275d8 !default;
$teal: #5bc0de !default;
$pink: #ff5b77 !default;
$purple: #613d7c !default;
@import "~bootstrap/scss/functions";
$content-bg: rgba(39, 49, 60, 0.65); //#1D272D;
$content-bg-solid: #1D272D;
$table-bg: rgba(255,255,255,.05);
$table-bg-hover: rgba(255,255,255,.1);
$table-border-color: rgba(255,255,255,.1);
$theme-colors: (
primary: $blue,
secondary: #38434e,
success: $green,
info: $blue,
warning: $orange,
danger: $red,
light: $gray-300,
dark: $gray-800,
rare: $purple
);
$body-color: #ccc;
$body-bg: #131d27;
$body-bg2: #20333e;
$font-family-sans-serif: "Source Sans Pro";
$font-family-monospace: "Source Code Pro";
$font-size-base: 14rem / 16;
$font-size-lg: 1.28rem;
$font-size-sm: .85rem;
$line-height-base: 1.6;
$headings-color: #ced9e2;
$headings-font-weight: lighter;
$input-btn-padding-y: .3rem;
$input-btn-padding-x: .9rem;
$input-btn-line-height: 1.6;
$input-btn-line-height-sm: 1.8;
$input-btn-line-height-lg: 1.8;
$btn-link-disabled-color: $gray-600;
$btn-focus-box-shadow: none;
$h4-font-size: 18px;
$link-color: $gray-400;
$link-hover-color: $white;
$link-hover-decoration: none;
$component-active-color: $white;
$component-active-bg: #2f3a42;
$list-group-bg: $table-bg;
$list-group-border-color: $table-border-color;
$list-group-item-padding-y: 0.8rem;
$list-group-item-padding-x: 1rem;
$list-group-hover-bg: $table-bg-hover;
$list-group-active-bg: rgba(255,255,255,.2);
$list-group-active-color: $component-active-color;
$list-group-active-border-color: translate;
$list-group-action-color: $body-color;
$list-group-action-hover-color: white;
$list-group-action-active-color: $component-active-color;
$list-group-action-active-bg: $list-group-active-bg;
$alert-padding-y: 0.9rem;
$alert-padding-x: 1.25rem;
$input-box-shadow: none;
$transition-base: all .15s ease-in-out;
$transition-fade: opacity .1s linear;
$transition-collapse: height .35s ease;
$btn-transition: all .15s ease-in-out;
$popover-bg: $body-bg;
$popover-body-color: $body-color;
$popover-header-bg: $table-bg-hover;
$popover-header-color: $headings-color;
$popover-arrow-color: $popover-bg;
$popover-max-width: 360px;
$btn-border-width: 2px;
$input-bg: #181e23;
$input-disabled-bg: #2e3235;
$input-color: #ddd;
$input-border-color: $input-bg;
$input-border-width: 2px;
$input-focus-bg: $input-bg;
$input-focus-border-color: rgba(171, 171, 171, 0.61);
$input-focus-color: $input-color;
$input-btn-focus-color: var(--focus-color);
$input-btn-focus-box-shadow: 0 0 0 2px $input-btn-focus-color;
$input-group-addon-color: $input-color;
$input-group-addon-bg: $input-bg;
$input-group-addon-border-color: transparent;
$input-group-btn-border-color: $input-bg;
$nav-tabs-border-radius: 0;
$nav-tabs-border-color: transparent;
$nav-tabs-border-width: 2px;
$nav-tabs-link-hover-border-color: transparent;
$nav-tabs-link-active-color: #eee;
$nav-tabs-link-active-bg: transparent;
$nav-tabs-link-active-border-color: #eee;
$navbar-padding-y: 0;
$navbar-padding-x: 0;
$dropdown-bg: $content-bg-solid;
$dropdown-color: $body-color;
$dropdown-border-width: 1px;
$dropdown-box-shadow: 0 .5rem 1rem rgba($black,.175);
$dropdown-header-color: $gray-500;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: #eee;
$dropdown-link-hover-bg: rgba(255,255,255,.04);
$dropdown-link-active-color: white;
$dropdown-link-active-bg: rgba(0, 0, 0, .2);
$dropdown-item-padding-y: 0.5rem;
$dropdown-item-padding-x: 1.5rem;
$code-color: $orange;
$code-bg: rgba(0, 0, 0, .25);
$code-padding-y: 3px;
$code-padding-x: 5px;
$pre-bg: $dropdown-bg;
$pre-color: $dropdown-link-color;
$badge-font-size: 0.75rem;
$badge-font-weight: bold;
$badge-padding-y: 4px;
$badge-padding-x: 6px;
$custom-control-indicator-size: 1.2rem;
$custom-control-indicator-bg: $body-bg;
$custom-control-indicator-border-color: lighten($body-bg, 25%);
$custom-control-indicator-checked-bg: theme-color("primary");
$custom-control-indicator-checked-color: $body-bg;
$custom-control-indicator-checked-border-color: transparent;
$custom-control-indicator-active-bg: rgba(255, 255, 0, 0.5);
$modal-content-bg: $content-bg-solid;
$modal-content-border-color: $body-bg;
$modal-header-border-width: 0;
$modal-footer-border-color: #222;
$modal-footer-border-width: 1px;
$modal-content-border-width: 0;

View File

@@ -46,17 +46,16 @@ async@^2.6.1:
lodash "^4.17.11"
axios@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==
version "0.19.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.1.tgz#8a6a04eed23dfe72747e1dd43c604b8f1677b5aa"
integrity sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==
dependencies:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
bootstrap@^4.1.3:
version "4.3.1"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.3.1.tgz#280ca8f610504d99d7b6b4bfc4b68cec601704ac"
integrity sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==
version "4.4.1"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01"
integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==
builder-util-runtime@8.4.0:
version "8.4.0"
@@ -118,9 +117,9 @@ colorspace@1.1.x:
text-hex "1.0.x"
core-js@^3.1.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.4.2.tgz#ee2b1a60b50388d8ddcda8cdb44a92c7a9ea76df"
integrity sha512-bUTfqFWtNKWp73oNIfRkqwYZJeNT3lstzZcAkhhiuvDraRSgOH1/+F9ZklbpR4zpdKuo4cpXN8tKP7s61yjX+g==
version "3.6.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.3.tgz#cebda69dd069bf90066414d2b2425ffd1f3dcd79"
integrity sha512-DOO9b18YHR+Wk5kJ/c5YFbXuUETreD4TrvXb6edzqZE3aAEd0eJIAWghZ9HttMuiON8SVCnU3fqA4rPxRDD1HQ==
core-util-is@~1.0.0:
version "1.0.2"
@@ -254,11 +253,6 @@ is-arrayish@^0.3.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-buffer@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-plugin-manager",
"version": "1.0.93-nightly.0",
"version": "1.0.99-nightly.0",
"description": "Terminus' plugin manager",
"keywords": [
"terminus-builtin-plugin"
@@ -20,7 +20,7 @@
"@types/semver": "^6.0.0",
"axios": "^0.19.0",
"mz": "^2.6.0",
"semver": "^6.1.0"
"semver": "^7.1.1"
},
"peerDependencies": {
"@angular/common": "^7",

View File

@@ -5,7 +5,7 @@
.d-flex
h3.mb-1 Installed
button.btn.btn-outline-info.btn-sm.ml-auto((click)='openPluginsFolder()')
button.btn.btn-outline-secondary.btn-sm.ml-auto((click)='openPluginsFolder()')
i.fas.fa-folder
span Plugins folder
@@ -28,19 +28,19 @@
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy[plugin.name] == BusyState.Installing')
span Upgrade ({{knownUpgrades[plugin.name].version}})
button.btn.btn-primary.ml-2(
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-secondary.ml-2(
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-danger.ml-2(
button.btn.btn-link.text-danger.ml-2(
(click)='uninstallPlugin(plugin)',
*ngIf='!plugin.isBuiltin',
[disabled]='busy[plugin.name] != undefined'

View File

@@ -13,12 +13,11 @@ any-promise@^1.0.0:
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
axios@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==
version "0.19.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.1.tgz#8a6a04eed23dfe72747e1dd43c604b8f1677b5aa"
integrity sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==
dependencies:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
debug@=3.1.0:
version "3.1.0"
@@ -34,11 +33,6 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"
is-buffer@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -58,10 +52,10 @@ object-assign@^4.0.1:
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
semver@^6.1.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
integrity sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A==
thenify-all@^1.0.0:
version "1.6.0"

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-settings",
"version": "1.0.93-nightly.0",
"version": "1.0.99-nightly.0",
"description": "Terminus terminal settings page",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-ssh",
"version": "1.0.93-nightly.0",
"version": "1.0.99-nightly.0",
"description": "SSH connection manager for Terminus",
"keywords": [
"terminus-builtin-plugin"
@@ -20,7 +20,9 @@
"@types/node": "12.7.3",
"@types/ssh2": "^0.5.35",
"ssh2": "^0.8.2",
"ssh2-streams": "^0.4.2"
"ssh2-streams": "^0.4.2",
"sshpk": "^1.16.1",
"terminus-terminal": "^1.0.98-nightly.0"
},
"peerDependencies": {
"@angular/common": "^7",

View File

@@ -1,4 +1,8 @@
import { BaseSession } from 'terminus-terminal'
import { Server, Socket, createServer, createConnection } from 'net'
import { Client, ClientChannel } from 'ssh2'
import { Logger } from 'terminus-core'
import { Subject, Observable } from 'rxjs'
export interface LoginScript {
expect: string
@@ -21,30 +25,87 @@ export interface SSHConnection {
user: string
password?: string
privateKey?: string
group?: string
group: string | null
scripts?: LoginScript[]
keepaliveInterval?: number
keepaliveCountMax?: number
readyTimeout?: number
color?: string
x11?: boolean
algorithms?: {[t: string]: string[]}
}
export enum PortForwardType {
Local, Remote
}
export class ForwardedPort {
type: PortForwardType
host = '127.0.0.1'
port: number
targetAddress: string
targetPort: number
private listener: Server
async startLocalListener (callback: (Socket) => void): Promise<void> {
this.listener = createServer(callback)
return new Promise((resolve, reject) => {
this.listener.listen(this.port, '127.0.0.1')
this.listener.on('error', reject)
this.listener.on('listening', resolve)
})
}
stopLocalListener () {
this.listener.close()
}
toString () {
if (this.type === PortForwardType.Local) {
return `(local) ${this.host}:${this.port} → (remote) ${this.targetAddress}:${this.targetPort}`
} else {
return `(remote) ${this.host}:${this.port} → (local) ${this.targetAddress}:${this.targetPort}`
}
}
}
export class SSHSession extends BaseSession {
scripts?: LoginScript[]
shell: any
shell: ClientChannel
ssh: Client
forwardedPorts: ForwardedPort[] = []
logger: Logger
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
private serviceMessage = new Subject<string>()
constructor (public connection: SSHConnection) {
super()
this.scripts = connection.scripts || []
}
start () {
async start () {
this.open = true
try {
this.shell = await this.openShellChannel({ x11: this.connection.x11 })
} catch (err) {
this.emitServiceMessage(`Remote rejected opening a shell channel: ${err}`)
}
this.shell.on('greeting', greeting => {
this.emitServiceMessage(`Shell greeting: ${greeting}`)
})
this.shell.on('banner', banner => {
this.emitServiceMessage(`Shell banner: ${banner}`)
})
this.shell.on('data', data => {
const dataString = data.toString()
this.emitOutput(dataString)
this.emitOutput(data)
if (this.scripts) {
let found = false
@@ -67,12 +128,12 @@ export class SSHSession extends BaseSession {
}
if (match) {
console.log('Executing script: "' + cmd + '"')
this.logger.info('Executing script: "' + cmd + '"')
this.shell.write(cmd + '\n')
this.scripts = this.scripts.filter(x => x !== script)
} else {
if (script.optional) {
console.log('Skip optional script: ' + script.expect)
this.logger.debug('Skip optional script: ' + script.expect)
found = true
this.scripts = this.scripts.filter(x => x !== script)
} else {
@@ -88,17 +149,140 @@ export class SSHSession extends BaseSession {
})
this.shell.on('end', () => {
this.logger.info('Shell session ended')
if (this.open) {
this.destroy()
}
})
this.ssh.on('tcp connection', (details, accept, reject) => {
this.logger.info(`Incoming forwarded connection: (remote) ${details.srcIP}:${details.srcPort} -> (local) ${details.destIP}:${details.destPort}`)
const forward = this.forwardedPorts.find(x => x.port === details.destPort)
if (!forward) {
this.emitServiceMessage(`Rejected incoming forwarded connection for unrecognized port ${details.destPort}`)
return reject()
}
const socket = new Socket()
socket.connect(forward.targetPort, forward.targetAddress)
socket.on('error', e => {
this.emitServiceMessage(`Could not forward the remote connection to ${forward.targetAddress}:${forward.targetPort}: ${e}`)
reject()
})
socket.on('connect', () => {
this.logger.info('Connection forwarded')
const stream = accept()
stream.pipe(socket)
socket.pipe(stream)
stream.on('close', () => {
socket.destroy()
})
socket.on('close', () => {
stream.close()
})
})
})
this.ssh.on('x11', (details, accept, reject) => {
this.logger.info(`Incoming X11 connection from ${details.srcIP}:${details.srcPort}`)
let displaySpec = process.env.DISPLAY || ':0'
this.logger.debug(`Trying display ${displaySpec}`)
let xHost = displaySpec.split(':')[0]
let xDisplay = parseInt(displaySpec.split(':')[1].split('.')[0] || '0')
let xPort = xDisplay < 100 ? xDisplay + 6000 : xDisplay
const socket = displaySpec.startsWith('/') ? createConnection(displaySpec) : new Socket()
if (!displaySpec.startsWith('/')) {
socket.connect(xPort, xHost)
}
socket.on('error', e => {
this.emitServiceMessage(`Could not connect to the X server ${xHost}:${xPort}: ${e}`)
reject()
})
socket.on('connect', () => {
this.logger.info('Connection forwarded')
const stream = accept()
stream.pipe(socket)
socket.pipe(stream)
stream.on('close', () => {
socket.destroy()
})
socket.on('close', () => {
stream.close()
})
})
})
this.executeUnconditionalScripts()
}
emitServiceMessage (msg: string) {
this.serviceMessage.next(msg)
this.logger.info(msg)
}
async addPortForward (fw: ForwardedPort) {
if (fw.type === PortForwardType.Local) {
await fw.startLocalListener((socket: Socket) => {
this.logger.info(`New connection on ${fw}`)
this.ssh.forwardOut(
socket.remoteAddress || '127.0.0.1',
socket.remotePort || 0,
fw.targetAddress,
fw.targetPort,
(err, stream) => {
if (err) {
this.emitServiceMessage(`Remote has rejected the forwaded connection via ${fw}: ${err}`)
socket.destroy()
return
}
stream.pipe(socket)
socket.pipe(stream)
stream.on('close', () => {
socket.destroy()
})
socket.on('close', () => {
stream.close()
})
}
)
}).then(() => {
this.emitServiceMessage(`Forwaded ${fw}`)
this.forwardedPorts.push(fw)
}).catch(e => {
this.emitServiceMessage(`Failed to forward port ${fw}: ${e}`)
throw e
})
}
if (fw.type === PortForwardType.Remote) {
await new Promise((resolve, reject) => {
this.ssh.forwardIn(fw.host, fw.port, err => {
if (err) {
this.emitServiceMessage(`Remote rejected port forwarding for ${fw}: ${err}`)
return reject(err)
}
resolve()
})
})
this.emitServiceMessage(`Forwaded ${fw}`)
this.forwardedPorts.push(fw)
}
}
async removePortForward (fw: ForwardedPort) {
if (fw.type === PortForwardType.Local) {
fw.stopLocalListener()
this.forwardedPorts = this.forwardedPorts.filter(x => x !== fw)
}
if (fw.type === PortForwardType.Remote) {
this.ssh.unforwardIn(fw.host, fw.port)
this.forwardedPorts = this.forwardedPorts.filter(x => x !== fw)
}
this.emitServiceMessage(`Stopped forwarding ${fw}`)
}
resize (columns, rows) {
if (this.shell) {
this.shell.setWindow(rows, columns)
this.shell.setWindow(rows, columns, rows, columns)
}
}
@@ -114,6 +298,11 @@ export class SSHSession extends BaseSession {
}
}
async destroy (): Promise<void> {
this.serviceMessage.complete()
await super.destroy()
}
async getChildProcesses (): Promise<any[]> {
return []
}
@@ -126,6 +315,18 @@ export class SSHSession extends BaseSession {
return null
}
private openShellChannel (options): Promise<ClientChannel> {
return new Promise<ClientChannel>((resolve, reject) => {
this.ssh.shell({ term: 'xterm-256color' }, options, (err, shell) => {
if (err) {
reject(err)
} else {
resolve(shell)
}
})
})
}
private executeUnconditionalScripts () {
if (this.scripts) {
for (const script of this.scripts) {

View File

@@ -1,118 +1,147 @@
.modal-body
ngb-tabset(type='pills', [activeId]='basic')
ngb-tabset([activeId]='basic')
ngb-tab(id='basic')
ng-template(ngbTabTitle) General
ng-template(ngbTabContent)
.form-group
label Name
input.form-control(
type='text',
autofocus,
[(ngModel)]='connection.name',
type='text',
autofocus,
[(ngModel)]='connection.name',
)
.form-group
label Group
input.form-control(
type='text',
type='text',
placeholder='Ungrouped',
[(ngModel)]='connection.group',
[(ngModel)]='connection.group',
)
.form-group
label Host
input.form-control(
type='text',
[(ngModel)]='connection.host',
type='text',
[(ngModel)]='connection.host',
)
.form-group
label Port
input.form-control(
type='number',
placeholder='22',
[(ngModel)]='connection.port',
placeholder='22',
[(ngModel)]='connection.port',
)
.form-group
label Username
input.form-control(
type='text',
[(ngModel)]='connection.user',
type='text',
[(ngModel)]='connection.user',
)
.form-line
.header
.title Password
.description(*ngIf='!hasSavedPassword') Save a password in the keychain
.description(*ngIf='hasSavedPassword') There is a saved password for this connection
button.btn.btn-outline-success.ml-4(*ngIf='!hasSavedPassword', (click)='setPassword()')
i.fas.fa-key
button.btn.btn-outline-success.ml-4(*ngIf='!hasSavedPassword', (click)='setPassword()')
i.fas.fa-key
span Set password
button.btn.btn-danger.ml-4(*ngIf='hasSavedPassword', (click)='clearSavedPassword()')
button.btn.btn-danger.ml-4(*ngIf='hasSavedPassword', (click)='clearSavedPassword()')
i.fas.fa-trash-alt
span Forget
.form-line
.header
.title Private key
.title Private key
.description Path to the private key file
.input-group
input.form-control(
type='text',
placeholder='Key file path',
type='text',
placeholder='Key file path',
[(ngModel)]='connection.privateKey'
)
.input-group-btn
.input-group-append
button.btn.btn-secondary((click)='selectPrivateKey()')
i.fas.fa-folder-open
ngb-tab(id='advanced')
ng-template(ngbTabTitle) Advanced
ng-template(ngbTabContent)
.form-group
label Keep Alive Interval (Milliseconds)
.form-line
.header
.title X11 forwarding
toggle([(ngModel)]='connection.x11')
.form-line
.header
.title Tab color
input.form-control(
type='number',
placeholder='0',
[(ngModel)]='connection.keepaliveInterval',
type='text',
autofocus,
[(ngModel)]='connection.color',
placeholder='#000000'
)
.form-group
label Max Keep Alive Count
.form-line
.header
.title Keep Alive Interval (Milliseconds)
input.form-control(
type='number',
placeholder='3',
[(ngModel)]='connection.keepaliveCountMax',
type='number',
placeholder='0',
[(ngModel)]='connection.keepaliveInterval',
)
.form-group
label Ready Timeout (Milliseconds)
.form-line
.header
.title Max Keep Alive Count
input.form-control(
type='number',
placeholder='20000',
[(ngModel)]='connection.readyTimeout',
type='number',
placeholder='3',
[(ngModel)]='connection.keepaliveCountMax',
)
.form-group
label Ciphers
div(*ngFor='let alg of supportedAlgorithms.cipher')
checkbox([text]='alg', [(ngModel)]='algorithms.cipher[alg]')
.form-group
label Key exchange
div(*ngFor='let alg of supportedAlgorithms.kex')
checkbox([text]='alg', [(ngModel)]='algorithms.kex[alg]')
.form-line
.header
.title Ready Timeout (Milliseconds)
input.form-control(
type='number',
placeholder='20000',
[(ngModel)]='connection.readyTimeout',
)
.form-group
label HMAC
div(*ngFor='let alg of supportedAlgorithms.hmac')
checkbox([text]='alg', [(ngModel)]='algorithms.hmac[alg]')
ngb-tab(id='ciphers')
ng-template(ngbTabTitle) Ciphers
ng-template(ngbTabContent)
.form-line.align-items-start
.header
.title Ciphers
.w-50
div(*ngFor='let alg of supportedAlgorithms.cipher')
checkbox([text]='alg', [(ngModel)]='algorithms.cipher[alg]')
.form-group
label Host key
div(*ngFor='let alg of supportedAlgorithms.serverHostKey')
checkbox([text]='alg', [(ngModel)]='algorithms.serverHostKey[alg]')
.form-line.align-items-start
.header
.title Key exchange
.w-50
div(*ngFor='let alg of supportedAlgorithms.kex')
checkbox([text]='alg', [(ngModel)]='algorithms.kex[alg]')
.form-line.align-items-start
.header
.title HMAC
.w-50
div(*ngFor='let alg of supportedAlgorithms.hmac')
checkbox([text]='alg', [(ngModel)]='algorithms.hmac[alg]')
.form-line.align-items-start
.header
.title Host key
.w-50
div(*ngFor='let alg of supportedAlgorithms.serverHostKey')
checkbox([text]='alg', [(ngModel)]='algorithms.serverHostKey[alg]')
ngb-tab(id='scripts')
@@ -128,12 +157,12 @@
tr(*ngFor='let script of connection.scripts')
td.pr-2
input.form-control(
type='text',
type='text',
[(ngModel)]='script.expect'
)
td
input.form-control(
type='text',
type='text',
[(ngModel)]='script.send'
)
td.pl-2
@@ -152,11 +181,11 @@
i.fas.fa-arrow-down
button.btn.btn-outline-danger.ml-0((click)='deleteScript(script)')
i.fas.fa-trash
button.btn.btn-outline-info.mt-2((click)='addScript()')
i.fas.fa-plus
span New item
.modal-footer
button.btn.btn-outline-primary((click)='save()') Save
button.btn.btn-outline-danger((click)='cancel()') Cancel

View File

@@ -66,7 +66,7 @@ export class EditConnectionModalComponent {
modal.componentInstance.password = true
try {
const result = await modal.result
if (result && result.value) {
if (result?.value) {
this.passwordStorage.savePassword(this.connection, result.value)
this.hasSavedPassword = true
}

View File

@@ -49,6 +49,7 @@ export class SSHModalComponent {
const connection: SSHConnection = {
name: this.quickTarget,
group: null,
host,
user,
port,
@@ -91,7 +92,7 @@ export class SSHModalComponent {
}
for (const connection of connections) {
connection.group = connection.group || undefined
connection.group = connection.group || null
let group = this.childGroups.find(x => x.name === connection.group)
if (!group) {
group = {

View File

@@ -0,0 +1,48 @@
.modal-header
h5.m-0 Port forwarding
.modal-body.pt-0
.list-group-light.mb-3
.list-group-item.d-flex.align-items-center(*ngFor='let fw of session.forwardedPorts')
strong(*ngIf='fw.type === PortForwardType.Local') Local
strong(*ngIf='fw.type === PortForwardType.Remote') Remote
.ml-3 {{fw.host}}:{{fw.port}} &rarr; {{fw.targetAddress}}:{{fw.targetPort}}
button.btn.btn-link.ml-auto((click)='remove(fw)')
i.fas.fa-trash-alt.mr-2
span Remove
.input-group.mb-2
input.form-control(type='text', [(ngModel)]='newForward.host')
.input-group-append
.input-group-text :
input.form-control(type='number', [(ngModel)]='newForward.port')
.input-group-append
.input-group-text &rarr;
input.form-control(type='text', [(ngModel)]='newForward.targetAddress')
.input-group-append
.input-group-text :
input.form-control(type='number', [(ngModel)]='newForward.targetPort')
.d-flex
.btn-group.mr-auto(
[(ngModel)]='newForward.type',
ngbRadioGroup
)
label.btn.btn-secondary.m-0(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='PortForwardType.Local'
)
| Local
label.btn.btn-secondary.m-0(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='PortForwardType.Remote'
)
| Remote
button.btn.btn-primary((click)='addForward()')
i.fas.fa-check.mr-2
span Forward port

View File

@@ -0,0 +1,42 @@
import { Component, Input } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ForwardedPort, PortForwardType, SSHSession } from '../api'
/** @hidden */
@Component({
template: require('./sshPortForwardingModal.component.pug'),
// styles: [require('./sshPortForwardingModal.component.scss')],
})
export class SSHPortForwardingModalComponent {
@Input() session: SSHSession
newForward = new ForwardedPort()
PortForwardType = PortForwardType
constructor (
public modalInstance: NgbActiveModal,
) {
this.reset()
}
reset () {
this.newForward = new ForwardedPort()
this.newForward.type = PortForwardType.Local
this.newForward.host = '127.0.0.1'
this.newForward.port = 8000
this.newForward.targetAddress = '127.0.0.1'
this.newForward.targetPort = 80
}
async addForward () {
try {
await this.session.addPortForward(this.newForward)
this.reset()
} catch (e) {
console.error(e)
}
}
remove (fw: ForwardedPort) {
this.session.removePortForward(fw)
}
}

View File

@@ -27,6 +27,7 @@ export class SSHSettingsTabComponent {
createConnection () {
const connection: SSHConnection = {
name: '',
group: null,
host: '',
port: 22,
user: 'root',
@@ -97,7 +98,7 @@ export class SSHSettingsTabComponent {
}
)).response === 1) {
for (const connection of this.connections.filter(x => x.group === group.name)) {
connection.group = undefined
connection.group = null
}
this.config.save()
this.refresh()
@@ -109,7 +110,7 @@ export class SSHSettingsTabComponent {
this.childGroups = []
for (const connection of this.connections) {
connection.group = connection.group || undefined
connection.group = connection.group || null
let group = this.childGroups.find(x => x.name === connection.group)
if (!group) {
group = {

View File

@@ -0,0 +1,15 @@
.ssh-tab-toolbar
.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}}
button.btn.btn-secondary((click)='showPortForwarding()', *ngIf='session.open')
i.fas.fa-plug
span Ports
button.btn.btn-info((click)='reconnect()', *ngIf='!session.open')
i.fas.fa-reload
span Reconnect

View File

@@ -3,6 +3,7 @@
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
&> .content {
flex: auto;
@@ -11,4 +12,60 @@
overflow: hidden;
margin: 15px;
}
.ssh-tab-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 4;
pointer-events: none;
.reveal-button {
position: absolute;
top: 10px;
right: 30px;
border-radius: 50%;
width: 35px;
padding: 0;
height: 35px;
line-height: 35px;
transition: 0.125s opacity;
opacity: .5;
pointer-events: all;
}
&:hover .reveal-button {
opacity: 0;
}
&:hover .toolbar {
opacity: 1;
}
.toolbar {
opacity: 0;
background: rgba(0, 0, 0, .75);
padding: 10px 20px;
transition: 0.25s opacity;
display: flex;
align-items: center;
z-index: 1;
will-change: transform;
&>* {
pointer-events: all;
}
}
&.show {
.reveal-button {
opacity: 0;
}
.toolbar {
opacity: 1;
}
}
}
}

View File

@@ -1,12 +1,15 @@
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs/operators'
import { BaseTerminalTabComponent } from 'terminus-terminal'
import { SSHService } from '../services/ssh.service'
import { SSHConnection, SSHSession } from '../api'
import { SSHPortForwardingModalComponent } from './sshPortForwardingModal.component'
/** @hidden */
@Component({
template: BaseTerminalTabComponent.template,
selector: 'ssh-tab',
template: BaseTerminalTabComponent.template + require<string>('./sshTab.component.pug'),
styles: [require('./sshTab.component.scss'), ...BaseTerminalTabComponent.styles],
animations: BaseTerminalTabComponent.animations,
})
@@ -14,8 +17,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
connection: SSHConnection
ssh: SSHService
session: SSHSession
private ngbModal: NgbModal
ngOnInit () {
this.ngbModal = this.injector.get<NgbModal>(NgbModal)
this.logger = this.log.create('terminalTab')
this.ssh = this.injector.get(SSHService)
this.frontendReady$.pipe(first()).subscribe(() => {
@@ -35,7 +41,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
return
}
this.session = new SSHSession(this.connection)
this.session = this.ssh.createSession(this.connection)
this.session.serviceMessage$.subscribe(msg => {
this.write(`\r\n[SSH] ${msg}\r\n`)
this.session.resize(this.size.columns, this.size.rows)
})
this.attachSessionHandlers()
this.write(`Connecting to ${this.connection.host}`)
const interval = setInterval(() => this.write('.'), 500)
@@ -51,8 +61,8 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
clearInterval(interval)
this.write('\r\n')
}
await this.session.start()
this.session.resize(this.size.columns, this.size.rows)
this.session.start()
}
async getRecoveryToken (): Promise<any> {
@@ -61,4 +71,13 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
connection: this.connection,
}
}
showPortForwarding () {
const modal = this.ngbModal.open(SSHPortForwardingModalComponent).componentInstance as SSHPortForwardingModalComponent
modal.session = this.session
}
reconnect () {
this.initializeSession()
}
}

View File

@@ -9,6 +9,7 @@ import TerminusTerminalModule from 'terminus-terminal'
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
import { SSHModalComponent } from './components/sshModal.component'
import { SSHPortForwardingModalComponent } from './components/sshPortForwardingModal.component'
import { PromptModalComponent } from './components/promptModal.component'
import { SSHSettingsTabComponent } from './components/sshSettingsTab.component'
import { SSHTabComponent } from './components/sshTab.component'
@@ -40,6 +41,7 @@ import { SSHHotkeyProvider } from './hotkeys'
EditConnectionModalComponent,
PromptModalComponent,
SSHModalComponent,
SSHPortForwardingModalComponent,
SSHSettingsTabComponent,
SSHTabComponent,
],
@@ -47,6 +49,7 @@ import { SSHHotkeyProvider } from './hotkeys'
EditConnectionModalComponent,
PromptModalComponent,
SSHModalComponent,
SSHPortForwardingModalComponent,
SSHSettingsTabComponent,
SSHTabComponent,
],

View File

@@ -3,6 +3,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Client } from 'ssh2'
import * as fs from 'mz/fs'
import * as path from 'path'
import * as sshpk from 'sshpk'
import { ToastrService } from 'ngx-toastr'
import { AppService, HostAppService, Platform, Logger, LogService } from 'terminus-core'
import { SSHConnection, SSHSession } from '../api'
@@ -20,7 +21,7 @@ export class SSHService {
private logger: Logger
private constructor (
log: LogService,
private log: LogService,
private app: AppService,
private zone: NgZone,
private ngbModal: NgbModal,
@@ -32,15 +33,24 @@ export class SSHService {
}
async openTab (connection: SSHConnection): Promise<SSHTabComponent> {
return this.zone.run(() => this.app.openNewTab(
const tab = this.zone.run(() => this.app.openNewTab(
SSHTabComponent,
{ connection }
) as SSHTabComponent)
if (connection.color) {
(this.app.getParentTab(tab) || tab).color = connection.color
}
return tab
}
createSession (connection: SSHConnection): SSHSession {
const session = new SSHSession(connection)
session.logger = this.log.create(`ssh-${connection.host}-${connection.port}`)
return session
}
async connectSession (session: SSHSession, logCallback?: (s: any) => void): Promise<void> {
let privateKey: string|null = null
let privateKeyPassphrase: string|null = null
let privateKeyPath = session.connection.privateKey
if (!logCallback) {
@@ -61,6 +71,7 @@ export class SSHService {
}
if (privateKeyPath) {
log(`Loading private key from ${privateKeyPath}`)
try {
privateKey = (await fs.readFile(privateKeyPath)).toString()
} catch (error) {
@@ -69,28 +80,36 @@ export class SSHService {
}
if (privateKey) {
log(`Loading private key from ${privateKeyPath}`)
let parsedKey: any = null
try {
parsedKey = sshpk.parsePrivateKey(privateKey, 'auto')
} catch (e) {
if (e instanceof sshpk.KeyEncryptedError) {
const modal = this.ngbModal.open(PromptModalComponent)
log('Key requires passphrase')
modal.componentInstance.prompt = 'Private key passphrase'
modal.componentInstance.password = true
let passphrase = ''
try {
const result = await modal.result
passphrase = result?.value
} catch (e) { }
parsedKey = sshpk.parsePrivateKey(
privateKey,
'auto',
{ passphrase: passphrase }
)
} else {
throw e
}
}
let encrypted = privateKey.includes('ENCRYPTED')
if (privateKeyPath.toLowerCase().endsWith('.ppk')) {
encrypted = encrypted || privateKey.includes('Encryption:') && !privateKey.includes('Encryption: none')
}
if (encrypted) {
const modal = this.ngbModal.open(PromptModalComponent)
log('Key requires passphrase')
modal.componentInstance.prompt = 'Private key passphrase'
modal.componentInstance.password = true
try {
const result = await modal.result
if (result) {
privateKeyPassphrase = result.value
}
} catch (e) { }
}
privateKey = parsedKey!.toString('ssh')
}
}
const ssh = new Client()
session.ssh = ssh
let connected = false
let savedPassword: string|null = null
await new Promise(async (resolve, reject) => {
@@ -156,7 +175,6 @@ export class SSHService {
username: session.connection.user,
password: session.connection.privateKey ? undefined : '',
privateKey: privateKey || undefined,
passphrase: privateKeyPassphrase || undefined,
tryKeyboard: true,
agent: agent || undefined,
agentForward: !!agent,
@@ -210,31 +228,6 @@ export class SSHService {
}
})
})
try {
const shell: any = await new Promise<any>((resolve, reject) => {
ssh.shell({ term: 'xterm-256color' }, (err, shell) => {
if (err) {
reject(err)
} else {
resolve(shell)
}
})
})
session.shell = shell
shell.on('greeting', greeting => {
log(`Shell Greeting: ${greeting}`)
})
shell.on('banner', banner => {
log(`Shell Banner: ${banner}`)
})
} catch (error) {
this.toastr.error(error.message)
throw error
}
}
}

View File

@@ -27,47 +27,99 @@
"@types/node" "*"
"@types/ssh2-streams" "*"
asn1@~0.2.0:
asn1@~0.2.0, asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
bcrypt-pbkdf@^1.0.2:
assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
safer-buffer@~2.1.0:
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
ssh2-streams@^0.4.2, ssh2-streams@~0.4.7:
version "0.4.7"
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.7.tgz#093b89069de9cf5f06feff0601a5301471b01611"
integrity sha512-JhF8BNfeguOqVHOLhXjzLlRKlUP8roAEhiT/y+NcBQCqpRUupLNrRf2M+549OPNVGx21KgKktug4P3MY/IvTig==
ssh2-streams@^0.4.2, ssh2-streams@~0.4.8:
version "0.4.8"
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.8.tgz#2ff92df2e0063fef86cf934eaea197967deda715"
integrity sha512-auxXfgYySz2vYw7TMU7PK7vFI7EPvhvTH8/tZPgGaWocK4p/vwCMiV3icz9AEkb0R40kOKZtFtqYIxDJyJiytw==
dependencies:
asn1 "~0.2.0"
bcrypt-pbkdf "^1.0.2"
streamsearch "~0.1.2"
ssh2@^0.8.2:
version "0.8.6"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.6.tgz#dcc62e1d3b9e58a21f711f5186f043e4e792e6da"
integrity sha512-T0cPmEtmtC8WxSupicFDjx3vVUdNXO8xu2a/D5bjt8ixOUCe387AgvxU3mJgEHpu7+Sq1ZYx4d3P2pl/yxMH+w==
version "0.8.7"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.7.tgz#2dc15206f493010b98027201cf399b90bab79c89"
integrity sha512-/u1BO12kb0lDVxJXejWB9pxyF3/ncgRqI9vPCZuPzo05pdNDzqUeQRavScwSPsfMGK+5H/VRqp1IierIx0Bcxw==
dependencies:
ssh2-streams "~0.4.7"
ssh2-streams "~0.4.8"
sshpk@^1.16.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
streamsearch@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
tweetnacl@^0.14.3:
terminus-terminal@^1.0.98-nightly.0:
version "1.0.98-nightly.0"
resolved "https://registry.yarnpkg.com/terminus-terminal/-/terminus-terminal-1.0.98-nightly.0.tgz#10df71b0a81adf76a076fb21a91c859dd2f8bef7"
integrity sha512-JLxkeoQkORcfe6cRW6BJF5ZPSbvKA8IWUAb7fzBONVmNfRKj2Mq/uYPy76UXsdmb9F1n+rYIg+DShNp57asMKA==
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-terminal",
"version": "1.0.93-nightly.0",
"version": "1.0.99-nightly.0",
"description": "Terminus' terminal emulation core",
"keywords": [
"terminus-builtin-plugin"
@@ -25,13 +25,14 @@
"mz": "^2.6.0",
"ps-node": "^0.1.6",
"runes": "^0.4.2",
"slug": "^1.1.0",
"slug": "^2.0.0",
"uuid": "^3.3.2",
"xterm": "3.15.0-beta98",
"xterm-addon-fit": "^0.4.0-beta1",
"xterm-addon-ligatures": "^0.2.0",
"xterm-addon-search": "^0.4.0-beta3",
"xterm-addon-webgl": "^0.4.0-beta1"
"xterm": "^4.4.0-beta.15",
"xterm-addon-fit": "^0.4.0-beta2",
"xterm-addon-ligatures": "^0.2.1",
"xterm-addon-search": "^0.4.0",
"xterm-addon-webgl": "^0.5.0-beta.7",
"zmodem.js": "^0.1.9"
},
"peerDependencies": {
"@angular/animations": "^7",

View File

@@ -22,8 +22,8 @@ export interface ToastrServiceProxy {
* A class to base your custom terminal tabs on
*/
export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit, OnDestroy {
static template = require('../components/baseTerminalTab.component.pug')
static styles = [require('../components/terminalTab.component.scss')]
static template = require<string>('../components/baseTerminalTab.component.pug')
static styles = [require<string>('../components/terminalTab.component.scss')]
static animations: AnimationTriggerMetadata[] = [trigger('slideInOut', [
transition(':enter', [
style({ transform: 'translateY(-25%)' }),
@@ -56,6 +56,11 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
frontendReady = new Subject<void>()
size: ResizeEvent
/**
* Enables normall passthrough from session output to terminal input
*/
enablePassthrough = true
protected logger: Logger
protected output = new Subject<string>()
private sessionCloseSubscription: Subscription
@@ -63,7 +68,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
private bellPlayer: HTMLAudioElement
private termContainerSubscriptions: Subscription[] = []
get input$ (): Observable<string> { return this.frontend.input$ }
get input$ (): Observable<Buffer> { return this.frontend.input$ }
get output$ (): Observable<string> { return this.output }
get resize$ (): Observable<ResizeEvent> { return this.frontend.resize$ }
get alternateScreenActive$ (): Observable<boolean> { return this.frontend.alternateScreenActive$ }
@@ -229,7 +234,10 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
/**
* Feeds input into the active session
*/
sendInput (data: string) {
sendInput (data: string|Buffer) {
if (!(data instanceof Buffer)) {
data = Buffer.from(data, 'utf-8')
}
this.session.write(data)
if (this.config.store.terminal.scrollOnInput) {
this.frontend.scrollToBottom()
@@ -245,7 +253,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
const percentage = percentageMatch[3] ? parseFloat(percentageMatch[2]) : parseInt(percentageMatch[2])
if (percentage > 0 && percentage <= 100) {
this.setProgress(percentage)
this.logger.debug('Detected progress:', percentage)
// this.logger.debug('Detected progress:', percentage)
}
} else {
this.setProgress(null)
@@ -350,7 +358,9 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.frontend.mouseEvent$.subscribe(async event => {
if (event.type === 'mousedown') {
if (event.which === 2) {
this.paste()
if (this.config.store.terminal.pasteOnMiddleClick) {
this.paste()
}
event.preventDefault()
event.stopPropagation()
return
@@ -405,10 +415,12 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
protected attachSessionHandlers () {
// this.session.output$.bufferTime(10).subscribe((datas) => {
this.session.output$.subscribe(data => {
this.zone.run(() => {
this.output.next(data)
this.write(data)
})
if (this.enablePassthrough) {
this.zone.run(() => {
this.output.next(data)
this.write(data)
})
}
})
this.sessionCloseSubscription = this.session.closed$.subscribe(() => {

View File

@@ -16,8 +16,9 @@ export interface SessionOptions {
}
export interface Profile {
name: string,
sessionOptions: SessionOptions,
name: string
color?: string
sessionOptions: SessionOptions
isBuiltin?: boolean
icon?: string
}

View File

@@ -8,7 +8,7 @@ module.exports = function patchPTYModule (mod) {
mod.spawn = (file, args, opt) => {
let terminal = oldSpawn(file, args, opt)
let timeout = null
let buffer = ''
let buffer = Buffer.from('')
let lastFlush = 0
let nextTimeout = 0
@@ -19,11 +19,11 @@ module.exports = function patchPTYModule (mod) {
const maxWindow = 100
function flush () {
if (buffer) {
if (buffer.length) {
terminal.emit('data-buffered', buffer)
}
lastFlush = Date.now()
buffer = ''
buffer = Buffer.from('')
}
function reschedule () {
@@ -38,12 +38,12 @@ module.exports = function patchPTYModule (mod) {
}
terminal.on('data', data => {
buffer += data
buffer = Buffer.concat([buffer, data])
if (Date.now() - lastFlush > maxWindow) {
// Taking too much time buffering, flush to keep things interactive
flush()
} else {
if (Date.now() > nextTimeout - (maxWindow / 10)) {
if (Date.now() > nextTimeout - maxWindow / 10) {
// Extend the window if it's expiring
reschedule()
}

View File

@@ -1,24 +1,11 @@
h3.mb-3 Appearance
.d-flex
.mr-5
.form-line
.header
.title Frontend
.description Switches terminal frontend implementation (experimental)
select.form-control(
[(ngModel)]='config.store.terminal.frontend',
(ngModelChange)='config.save()',
)
option(value='hterm') hterm
option(value='xterm') xterm
option(value='xterm-webgl') xterm (WebGL)
.form-line
.header
.title Font
.d-flex.w-75
.input-group.w-75
input.form-control.w-75(
type='text',
[ngbTypeahead]='fontAutocomplete',
@@ -52,9 +39,10 @@ h3.mb-3 Appearance
)
option(*ngFor='let scheme of config.store.terminal.customColorSchemes', [ngValue]='scheme') Custom: {{scheme.name}}
option(*ngFor='let scheme of colorSchemes', [ngValue]='scheme') {{scheme.name}}
.input-group-btn
button.btn.btn-secondary((click)='editScheme(config.store.terminal.colorScheme)') Edit
.input-group-btn
.input-group-append
button.btn.btn-secondary((click)='editScheme(config.store.terminal.colorScheme)')
i.fas.fa-pen
.input-group-append
button.btn.btn-outline-danger(
(click)='deleteScheme(config.store.terminal.colorScheme)',
*ngIf='isCustomScheme(config.store.terminal.colorScheme)'
@@ -65,10 +53,12 @@ h3.mb-3 Appearance
label Editing
.input-group
input.form-control(type='text', [(ngModel)]='editingColorScheme.name')
.input-group-btn
button.btn.btn-secondary((click)='saveScheme()') Save
.input-group-btn
button.btn.btn-secondary((click)='cancelEditing()') Cancel
.input-group-append
button.btn.btn-secondary((click)='saveScheme()')
i.fas.fa-check
.input-group-append
button.btn.btn-secondary((click)='cancelEditing()')
i.fas.fa-times
.form-group(*ngIf='editingColorScheme')
color-picker(
@@ -180,6 +170,19 @@ h3.mb-3 Appearance
span rm -rf /
span([style.background-color]='config.store.terminal.colorScheme.cursor') &nbsp;
.form-line
.header
.title Frontend
.description Switches terminal frontend implementation (experimental)
select.form-control(
[(ngModel)]='config.store.terminal.frontend',
(ngModelChange)='config.save()',
)
option(value='hterm') hterm
option(value='xterm') xterm
option(value='xterm-webgl') xterm (WebGL)
.form-line
.header
.title Terminal background

View File

@@ -2,55 +2,64 @@
.form-group
label Name
input.form-control(
type='text',
autofocus,
[(ngModel)]='profile.name',
type='text',
autofocus,
[(ngModel)]='profile.name',
)
.form-group
label Command
input.form-control(
type='text',
[(ngModel)]='profile.sessionOptions.command',
type='text',
[(ngModel)]='profile.sessionOptions.command',
)
.form-group
label Arguments
.input-group(
*ngFor='let arg of profile.sessionOptions.args; index as i; trackBy: trackByIndex',
)
input.form-control(
type='text',
[(ngModel)]='profile.sessionOptions.args[i]',
type='text',
[(ngModel)]='profile.sessionOptions.args[i]',
)
.input-group-btn
.input-group-append
button.btn.btn-secondary((click)='profile.sessionOptions.args.splice(i, 1)')
i.fas.fa-trash
.mt-2
button.btn.btn-secondary((click)='profile.sessionOptions.args.push("")')
i.fas.fa-plus.mr-2
| Add
.form-line(*ngIf='uac.isAvailable')
.header
.title Run as administrator
toggle(
[(ngModel)]='profile.sessionOptions.runAsAdministrator',
)
.form-group
label Working directory
input.form-control(
type='text',
[(ngModel)]='profile.sessionOptions.cwd',
type='text',
[(ngModel)]='profile.sessionOptions.cwd',
)
.form-group
label Environment
environment-editor(
type='text',
[(model)]='profile.sessionOptions.env',
type='text',
[(model)]='profile.sessionOptions.env',
)
.form-group
label Tab color
input.form-control(
type='text',
autofocus,
[(ngModel)]='profile.color',
placeholder='#000000'
)
.modal-footer

View File

@@ -1,12 +1,13 @@
.mb-2.d-flex.align-items-center(*ngFor='let pair of vars')
.input-group.w-50
input.form-control([(ngModel)]='pair.key', (blur)='emitUpdate()', placeholder='Variable name')
.input-group-append
.input-group
input.form-control.w-25([(ngModel)]='pair.key', (blur)='emitUpdate()', placeholder='Variable name')
.input-group-append
.input-group-text =
input.form-control.w-50.mr-1([(ngModel)]='pair.value', (blur)='emitUpdate()', placeholder='Value')
button.btn.btn-secondary((click)='removeEnvironmentVar(pair.key)')
i.fas.fa-trash
input.form-control.w-50([(ngModel)]='pair.value', (blur)='emitUpdate()', placeholder='Value')
.input-group-append
button.btn.btn-secondary((click)='removeEnvironmentVar(pair.key)')
i.fas.fa-trash
button.btn.btn-secondary((click)='addEnvironmentVar()')
i.fas.fa-plus.mr-2
span Add

View File

@@ -2,7 +2,7 @@
input.search-input.form-control(
type='search',
[(ngModel)]='query',
(ngModelChange)='notFound = false',
(ngModelChange)='onQueryChange()',
[class.text-danger]='notFound',
(click)='$event.stopPropagation()',
(keyup.enter)='findNext()',
@@ -10,31 +10,27 @@
placeholder='Search...'
)
.input-group-append
.btn-group
button.btn.btn-outline-primary(
.input-group-text
a(
(click)='options.caseSensitive = !options.caseSensitive',
[class.active]='options.caseSensitive',
[class.text-info]='options.caseSensitive',
ngbTooltip='Case sensitivity',
placement='bottom'
)
i.fa.fa-fw.fa-font
button.btn.btn-outline-primary(
a(
(click)='options.regex = !options.regex',
[class.active]='options.regex',
[class.text-info]='options.regex',
ngbTooltip='Regular expression',
placement='bottom'
)
i.fa.fa-fw.fa-asterisk
button.btn.btn-outline-primary(
a(
(click)='options.wholeWord = !options.wholeWord',
[class.active]='options.wholeWord',
[class.text-info]='options.wholeWord',
ngbTooltip='Whole word',
placement='bottom'
)
i.fa.fa-fw.fa-text-width
button.btn.btn-outline(
(click)='close.emit()',
ngbTooltip='Close',
placement='bottom'
)
i.fa.fa-fw.fa-times
button.close.text-light.pl-3.pr-2((click)='close.emit()') &times;

View File

@@ -1,9 +1,20 @@
:host {
position: fixed;
width: 400px;
align-self: center;
right: 50px;
z-index: 5;
padding: 10px;
border-radius: 0 0 3px 3px;
background: rgba(0, 0, 0, .75);
border: 1px solid rgba(0, 0, 0, .5);
border-top: 0;
display: flex;
a {
padding-left: 10px;
&:first-child {
padding-left: 0;
}
}
}

View File

@@ -11,7 +11,9 @@ export class SearchPanelComponent {
@Input() query: string
@Input() frontend: Frontend
notFound = false
options: SearchOptions = {}
options: SearchOptions = {
incremental: true,
}
@Output() close = new EventEmitter()
@@ -19,8 +21,13 @@ export class SearchPanelComponent {
private toastr: ToastrService,
) { }
findNext () {
if (!this.frontend.findNext(this.query, this.options)) {
onQueryChange () {
this.notFound = false
this.findNext(true)
}
findNext (incremental = false) {
if (!this.frontend.findNext(this.query, { ...this.options, incremental: incremental || undefined })) {
this.notFound = true
this.toastr.error('Not found')
}

View File

@@ -14,20 +14,20 @@ h3.mb-3 Shell
[ngValue]='slug(profile.name).toLowerCase()'
) {{profile.name}}
.form-line(*ngIf='isConPTYAvailable')
.header
.title Use ConPTY
.description Enables the experimental Windows ConPTY API
toggle(
[(ngModel)]='config.store.terminal.useConPTY',
(ngModelChange)='config.save()'
)
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.useConPTY && isConPTYAvailable && !isConPTYStable')
.mr-auto Windows 10 build 18309 or above is recommended for ConPTY
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.profile.startsWith("WSL") && (config.store.terminal.frontend != "hterm" || !config.store.terminal.useConPTY)')
.mr-auto WSL terminal only supports TrueColor with ConPTY and the hterm frontend
@@ -50,15 +50,17 @@ h3.mb-3 Shell
placeholder='Home directory',
[(ngModel)]='config.store.terminal.workingDirectory',
(ngModelChange)='config.save()',
)
.input-group-btn
)
.input-group-append
button.btn.btn-secondary((click)='pickWorkingDirectory()')
i.fas.fa-folder-open
.form-line
.header
.title Always Use Working Directory
.description By default, new terminals will open where the previous terminal was working. Enabling this option will always launch new terminals in the working directory specified above.
.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(
[(ngModel)]='config.store.terminal.alwaysUseWorkingDirectory',
@@ -69,7 +71,7 @@ h3.mb-3 Shell
.header
.title Environment
.description Inject additional environment variables
environment-editor([(model)]='this.config.store.terminal.environment')
h3.mt-3 Saved Profiles
@@ -78,16 +80,16 @@ h3.mt-3 Saved Profiles
.list-group-item.list-group-item-action.d-flex.align-items-center(
*ngFor='let profile of config.store.terminal.profiles',
(click)='editProfile(profile)',
)
)
.mr-auto
div {{profile.name}}
.text-muted {{profile.sessionOptions.command}}
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteProfile(profile)')
i.fas.fa-trash
div(ngbDropdown, placement='top-left')
button.btn.btn-primary(ngbDropdownToggle)
button.btn.btn-primary(ngbDropdownToggle)
i.fas.fa-fw.fa-plus
| New profile
| New profile
div(ngbDropdownMenu)
button.dropdown-item(*ngFor='let shell of shells', (click)='newProfile(shell)') {{shell.name}}

View File

@@ -42,6 +42,13 @@ h3.mb-3 Terminal
(ngModelChange)='config.save()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
value='off'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
@@ -57,6 +64,15 @@ h3.mb-3 Terminal
)
| Paste
.form-line
.header
.title Paste on middle-click
toggle(
[(ngModel)]='config.store.terminal.pasteOnMiddleClick',
(ngModelChange)='config.save()',
)
.form-line
.header
.title Auto-open a terminal on app start

View File

@@ -25,6 +25,7 @@ export class TerminalConfigProvider extends ConfigProvider {
cursorBlink: true,
customShell: '',
rightClick: 'menu',
pasteOnMiddleClick: true,
copyOnSelect: false,
scrollOnInput: true,
workingDirectory: '',
@@ -113,6 +114,7 @@ export class TerminalConfigProvider extends ConfigProvider {
shell: 'clink',
profile: 'cmd-clink',
rightClick: 'paste',
pasteOnMiddleClick: false,
copyOnSelect: true,
},
hotkeys: {

View File

@@ -6,6 +6,7 @@ export interface SearchOptions {
regex?: boolean
wholeWord?: boolean
caseSensitive?: boolean
incremental?: true
}
/**
@@ -23,7 +24,7 @@ export abstract class Frontend {
protected mouseEvent = new Subject<MouseEvent>()
protected bell = new Subject<void>()
protected contentUpdated = new Subject<void>()
protected input = new Subject<string>()
protected input = new Subject<Buffer>()
protected resize = new ReplaySubject<ResizeEvent>(1)
protected dragOver = new Subject<DragEvent>()
protected drop = new Subject<DragEvent>()
@@ -34,7 +35,7 @@ export abstract class Frontend {
get mouseEvent$ (): Observable<MouseEvent> { return this.mouseEvent }
get bell$ (): Observable<void> { return this.bell }
get contentUpdated$ (): Observable<void> { return this.contentUpdated }
get input$ (): Observable<string> { return this.input }
get input$ (): Observable<Buffer> { return this.input }
get resize$ (): Observable<ResizeEvent> { return this.resize }
get dragOver$ (): Observable<DragEvent> { return this.dragOver }
get drop$ (): Observable<DragEvent> { return this.drop }

View File

@@ -182,7 +182,7 @@ export class HTermFrontend extends Frontend {
this.term.installKeyboard()
this.term.scrollPort_.setCtrlVPaste(true)
this.io = this.term.io.push()
this.io.onVTKeystroke = this.io.sendString = data => this.input.next(data)
this.io.onVTKeystroke = this.io.sendString = data => this.input.next(Buffer.from(data, 'utf-8'))
this.io.onTerminalResize = (columns, rows) => {
this.resize.next({ columns, rows })
}

View File

@@ -23,6 +23,7 @@ export class XTermFrontend extends Frontend {
protected enableWebGL = false
private xterm: Terminal
private configuredFontSize = 0
private configuredLinePadding = 0
private zoom = 0
private resizeHandler: () => void
private configuredTheme: ITheme = {}
@@ -39,8 +40,11 @@ export class XTermFrontend extends Frontend {
})
this.xtermCore = (this.xterm as any)._core
this.xterm.onBinary(data => {
this.input.next(Buffer.from(data, 'binary'))
})
this.xterm.onData(data => {
this.input.next(data)
this.input.next(Buffer.from(data, 'utf-8'))
})
this.xterm.onResize(({ cols, rows }) => {
this.resize.next({ rows, columns: cols })
@@ -113,6 +117,8 @@ export class XTermFrontend extends Frontend {
}
attach (host: HTMLElement): void {
this.configure()
this.xterm.open(host)
this.opened = true
@@ -203,13 +209,14 @@ export class XTermFrontend extends Frontend {
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
this.xterm.setOption('scrollback', 100000)
this.configuredFontSize = config.terminal.fontSize
this.configuredLinePadding = config.terminal.linePadding
this.setFontSize()
this.copyOnSelect = config.terminal.copyOnSelect
const theme: ITheme = {
foreground: config.terminal.colorScheme.foreground,
background: config.terminal.background === 'colorScheme' ? config.terminal.colorScheme.background : config.appearance.vibrancy ? 'transparent' : this.themesService.findCurrentTheme().terminalBackground,
background: config.terminal.background === 'colorScheme' ? config.terminal.colorScheme.background : '#00000000',
cursor: config.terminal.colorScheme.cursor,
}
@@ -242,7 +249,10 @@ export class XTermFrontend extends Frontend {
}
private setFontSize () {
this.xterm.setOption('fontSize', this.configuredFontSize * Math.pow(1.1, this.zoom))
const scale = Math.pow(1.1, this.zoom)
this.xterm.setOption('fontSize', this.configuredFontSize * scale)
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
this.xterm.setOption('lineHeight', (this.configuredFontSize + this.configuredLinePadding * 2) / this.configuredFontSize * scale)
this.resizeHandler()
}

View File

@@ -37,6 +37,7 @@ import { TerminalHotkeyProvider } from './hotkeys'
import { HyperColorSchemes } from './colorSchemes'
import { NewTabContextMenu, CopyPasteContextMenu } from './contextMenu'
import { SaveAsProfileContextMenu } from './tabContextMenu'
import { ZModemDecorator } from './zmodem'
import { CmderShellProvider } from './shells/cmder'
import { CustomShellProvider } from './shells/custom'
@@ -76,6 +77,7 @@ import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend'
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
{ provide: TerminalDecorator, useClass: ZModemDecorator, multi: true },
{ provide: ShellProvider, useClass: WindowsDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },

View File

@@ -30,8 +30,8 @@ export interface ChildProcess {
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi
const catalinaDataVolumePrefix = '/System/Volumes/Data'
const OSC1337Prefix = '\x1b]1337;'
const OSC1337Suffix = '\x07'
const OSC1337Prefix = Buffer.from('\x1b]1337;')
const OSC1337Suffix = Buffer.from('\x07')
/**
* A session object for a [[BaseTerminalTabComponent]]
@@ -42,27 +42,31 @@ export abstract class BaseSession {
name: string
truePID: number
protected output = new Subject<string>()
protected binaryOutput = new Subject<Buffer>()
protected closed = new Subject<void>()
protected destroyed = new Subject<void>()
private initialDataBuffer = ''
private initialDataBuffer = Buffer.from('')
private initialDataBufferReleased = false
get output$ (): Observable<string> { return this.output }
get binaryOutput$ (): Observable<Buffer> { return this.binaryOutput }
get closed$ (): Observable<void> { return this.closed }
get destroyed$ (): Observable<void> { return this.destroyed }
emitOutput (data: string) {
emitOutput (data: Buffer) {
if (!this.initialDataBufferReleased) {
this.initialDataBuffer += data
this.initialDataBuffer = Buffer.concat([this.initialDataBuffer, data])
} else {
this.output.next(data)
this.output.next(data.toString())
this.binaryOutput.next(data)
}
}
releaseInitialDataBuffer () {
this.initialDataBufferReleased = true
this.output.next(this.initialDataBuffer)
this.initialDataBuffer = ''
this.output.next(this.initialDataBuffer.toString())
this.binaryOutput.next(this.initialDataBuffer)
this.initialDataBuffer = Buffer.from('')
}
async destroy (): Promise<void> {
@@ -71,13 +75,14 @@ export abstract class BaseSession {
this.closed.next()
this.destroyed.next()
this.output.complete()
this.binaryOutput.complete()
await this.gracefullyKillProcess()
}
}
abstract start (options: SessionOptions): void
abstract resize (columns: number, rows: number): void
abstract write (data: string): void
abstract write (data: Buffer): void
abstract kill (signal?: string): void
abstract async getChildProcesses (): Promise<ChildProcess[]>
abstract async gracefullyKillProcess (): Promise<void>
@@ -129,6 +134,7 @@ export class Session extends BaseSession {
name: 'xterm-256color',
cols: options.width || 80,
rows: options.height || 30,
encoding: null,
cwd,
env: env,
// `1` instead of `true` forces ConPTY even if unstable
@@ -150,11 +156,11 @@ export class Session extends BaseSession {
this.open = true
this.pty.on('data-buffered', data => {
this.pty.on('data-buffered', (data: Buffer) => {
data = this.processOSC1337(data)
this.emitOutput(data)
if (process.platform === 'win32') {
this.guessWindowsCWD(data)
this.guessWindowsCWD(data.toString())
}
})
@@ -168,7 +174,7 @@ export class Session extends BaseSession {
this.pty.on('close', () => {
if (this.pauseAfterExit) {
this.emitOutput('\r\nPress any key to close\r\n')
this.emitOutput(Buffer.from('\r\nPress any key to close\r\n'))
} else if (this.open) {
this.destroy()
}
@@ -177,19 +183,19 @@ export class Session extends BaseSession {
this.pauseAfterExit = options.pauseAfterExit || false
}
processOSC1337 (data: string) {
processOSC1337 (data: Buffer) {
if (data.includes(OSC1337Prefix)) {
const preData = data.substring(0, data.indexOf(OSC1337Prefix))
let params = data.substring(data.indexOf(OSC1337Prefix) + OSC1337Prefix.length)
const postData = params.substring(params.indexOf(OSC1337Suffix) + OSC1337Suffix.length)
params = params.substring(0, params.indexOf(OSC1337Suffix))
const preData = data.subarray(0, data.indexOf(OSC1337Prefix))
let params = data.subarray(data.indexOf(OSC1337Prefix) + OSC1337Prefix.length)
const postData = params.subarray(params.indexOf(OSC1337Suffix) + OSC1337Suffix.length)
const paramString = params.subarray(0, params.indexOf(OSC1337Suffix)).toString()
if (params.startsWith('CurrentDir=')) {
this.reportedCWD = params.split('=')[1]
if (paramString.startsWith('CurrentDir=')) {
this.reportedCWD = paramString.split('=')[1]
if (this.reportedCWD.startsWith('~')) {
this.reportedCWD = os.homedir() + this.reportedCWD.substring(1)
}
data = preData + postData
data = Buffer.concat([preData, postData])
}
}
return data
@@ -201,10 +207,10 @@ export class Session extends BaseSession {
}
}
write (data) {
write (data: Buffer) {
if (this.open) {
if (this.pty._writable) {
this.pty.write(Buffer.from(data, 'utf-8'))
this.pty.write(data)
} else {
this.destroy()
}
@@ -255,15 +261,15 @@ export class Session extends BaseSession {
await new Promise((resolve) => {
this.kill('SIGTERM')
setImmediate(() => {
if (!this.open) {
resolve()
} else {
try {
process.kill(this.pty.pid, 0)
// still alive
setTimeout(() => {
if (this.open) {
this.kill('SIGKILL')
}
this.kill('SIGKILL')
resolve()
}, 1000)
} catch {
resolve()
}
})
})
@@ -292,7 +298,7 @@ export class Session extends BaseSession {
}
if (process.platform === 'linux') {
try {
return await fs.readlink(`/proc/${this.truePID}/cwd`)
return fs.readlink(`/proc/${this.truePID}/cwd`)
} catch (exc) {
console.error(exc)
return null

View File

@@ -88,7 +88,11 @@ export class TerminalService {
cwd: cwd || undefined,
}
return this.openTabWithOptions(sessionOptions)
const tab = this.openTabWithOptions(sessionOptions)
if (profile?.color) {
(this.app.getParentTab(tab) || tab).color = profile.color
}
return tab
}
optionsFromShell (shell: Shell): SessionOptions {

View File

@@ -2,6 +2,8 @@ import { Injectable, NgZone } from '@angular/core'
import { ToastrService } from 'ngx-toastr'
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider } from 'terminus-core'
import { TerminalTabComponent } from './components/terminalTab.component'
import { UACService } from './services/uac.service'
import { TerminalService } from './services/terminal.service'
/** @hidden */
@Injectable()
@@ -10,6 +12,8 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
private config: ConfigService,
private zone: NgZone,
private toastr: ToastrService,
private uac: UACService,
private terminalService: TerminalService,
) {
super()
}
@@ -18,7 +22,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
if (!(tab instanceof TerminalTabComponent)) {
return []
}
return [
const items: Electron.MenuItemConstructorOptions[] = [
{
label: 'Save as profile',
click: () => this.zone.run(async () => {
@@ -38,5 +42,19 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
}),
},
]
if (this.uac.isAvailable) {
items.push({
label: 'Duplicate as administrator',
click: () => this.zone.run(async () => {
this.terminalService.openTabWithOptions({
...tab.sessionOptions,
runAsAdministrator: true,
})
}),
})
}
return items
}
}

View File

@@ -0,0 +1,179 @@
/* eslint-disable @typescript-eslint/camelcase */
import * as ZModem from 'zmodem.js'
import * as fs from 'fs'
import * as path from 'path'
import { Subscription } from 'rxjs'
import { Injectable } from '@angular/core'
import { TerminalDecorator } from './api/decorator'
import { TerminalTabComponent } from './components/terminalTab.component'
import { LogService, Logger, ElectronService, HostAppService } from 'terminus-core'
const SPACER = ' '
/** @hidden */
@Injectable()
export class ZModemDecorator extends TerminalDecorator {
private subscriptions: Subscription[] = []
private logger: Logger
private activeSession: any = null
constructor (
log: LogService,
private electron: ElectronService,
private hostApp: HostAppService,
) {
super()
this.logger = log.create('zmodem')
}
attach (terminal: TerminalTabComponent): void {
const sentry = new ZModem.Sentry({
to_terminal: data => {
if (!terminal.enablePassthrough) {
terminal.write(data)
}
},
sender: data => terminal.session.write(Buffer.from(data)),
on_detect: async detection => {
try {
terminal.enablePassthrough = false
await this.process(terminal, detection)
} finally {
terminal.enablePassthrough = true
}
},
on_retract: () => {
this.showMessage(terminal, 'transfer cancelled')
},
})
setTimeout(() => {
this.subscriptions = [
terminal.session.binaryOutput$.subscribe(data => {
const chunkSize = 1024
for (let i = 0; i <= Math.floor(data.length / chunkSize); i++) {
try {
sentry.consume(data.subarray(i * chunkSize, (i + 1) * chunkSize))
} catch (e) {
this.logger.error('protocol error', e)
this.activeSession.abort()
this.activeSession = null
terminal.enablePassthrough = true
return
}
}
}),
]
})
}
async process (terminal, detection) {
this.showMessage(terminal, '[Terminus] ZModem session started')
const zsession = detection.confirm()
this.activeSession = zsession
this.logger.info('new session', zsession)
if (zsession.type === 'send') {
const result = await this.electron.dialog.showOpenDialog(
this.hostApp.getWindow(),
{
buttonLabel: 'Send',
properties: ['multiSelections', 'openFile', 'treatPackageAsDirectory'],
},
)
if (result.canceled) {
zsession.close()
return
}
let filesRemaining = result.filePaths.length
for (const filePath of result.filePaths) {
await this.sendFile(terminal, zsession, filePath, filesRemaining)
filesRemaining--
}
this.activeSession = null
await zsession.close()
} else {
zsession.on('offer', xfer => {
this.receiveFile(terminal, xfer)
})
zsession.start()
await new Promise(resolve => zsession.on('session_end', resolve))
this.activeSession = null
}
}
detach (_terminal: TerminalTabComponent): void {
for (const s of this.subscriptions) {
s.unsubscribe()
}
}
private async receiveFile (terminal, xfer) {
const details = xfer.get_details()
this.showMessage(terminal, `🟡 Offered ${details.name}`, true)
this.logger.info('offered', xfer)
const result = await this.electron.dialog.showSaveDialog(
this.hostApp.getWindow(),
{
defaultPath: details.name,
},
)
if (!result.filePath) {
this.showMessage(terminal, `🔴 Rejected ${details.name}`)
xfer.skip()
return
}
const stream = fs.createWriteStream(result.filePath)
let bytesSent = 0
await xfer.accept({
on_input: chunk => {
stream.write(Buffer.from(chunk))
bytesSent += chunk.length
this.showMessage(terminal, `🟡 Receiving ${details.name}: ${Math.round(100 * bytesSent / details.size)}%`, true)
},
})
this.showMessage(terminal, `✅ Received ${details.name}`)
stream.end()
}
private async sendFile (terminal, zsession, filePath, filesRemaining) {
const stat = fs.statSync(filePath)
const offer = {
name: path.basename(filePath),
size: stat.size,
mode: stat.mode,
mtime: Math.floor(stat.mtimeMs / 1000),
files_remaining: filesRemaining,
bytes_remaining: stat.size,
}
this.logger.info('offering', offer)
this.showMessage(terminal, `🟡 Offering ${offer.name}`, true)
const xfer = await zsession.send_offer(offer)
if (xfer) {
let bytesSent = 0
const stream = fs.createReadStream(filePath)
stream.on('data', chunk => {
xfer.send(chunk)
bytesSent += chunk.length
this.showMessage(terminal, `🟡 Sending ${offer.name}: ${Math.round(100 * bytesSent / offer.size)}%`, true)
})
await new Promise(resolve => stream.on('end', resolve))
await xfer.end()
stream.close()
this.showMessage(terminal, `✅ Sent ${offer.name}`)
} else {
this.showMessage(terminal, `🔴 Other side rejected ${offer.name}`)
this.logger.warn('rejected by the other side')
}
}
private showMessage (terminal, msg: string, overwrite = false) {
terminal.write(Buffer.from(`\r${msg}${SPACER}`))
if (!overwrite) {
terminal.write(Buffer.from('\r\n'))
}
}
}

View File

@@ -22,6 +22,14 @@ connected-domain@^1.0.0:
resolved "https://registry.yarnpkg.com/connected-domain/-/connected-domain-1.0.0.tgz#bfe77238c74be453a79f0cb6058deeb4f2358e93"
integrity sha1-v+dyOMdL5FOnnwy2BY3utPI1jpM=
crc-32@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208"
integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==
dependencies:
exit-on-epipe "~1.0.1"
printj "~1.1.0"
dataurl@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dataurl/-/dataurl-0.1.0.tgz#1f4734feddec05ffe445747978d86759c4b33199"
@@ -46,6 +54,11 @@ define-properties@^1.1.2:
dependencies:
object-keys "^1.0.12"
exit-on-epipe@~1.0.1:
version "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"
@@ -141,6 +154,11 @@ opentype.js@^0.8.0:
dependencies:
tiny-inflate "^1.0.2"
printj@~1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
promise-stream-reader@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz#4e793a79c9d49a73ccd947c6da9c127f12923649"
@@ -170,10 +188,10 @@ runes@^0.4.2:
resolved "https://registry.yarnpkg.com/runes/-/runes-0.4.3.tgz#32f7738844bc767b65cc68171528e3373c7bb355"
integrity sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg==
slug@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/slug/-/slug-1.1.0.tgz#73eef5710416f515077bdf70c683bde4915913c9"
integrity sha512-NuIOjDQeTMPm+/AUIHJ5636mF3jOsYLFnoEErl9Tdpt4kpt4fOrAJxscH9mUgX1LtPaEqgPCawBg7A4yhoSWRg==
slug@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/slug/-/slug-2.1.0.tgz#293f8d53de7e55c15871846fd1bc36114841a8c7"
integrity sha512-Q4foEgcE7E8UB/BFg4kEzFUICoppzsbbfRjrdKiOM4Z4EFZF5tdn6amkgeaGur3kI4lMWP2BoMv7XJcKZvLg9Q==
dependencies:
unicode ">= 0.3.1"
@@ -213,35 +231,42 @@ uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
xterm-addon-fit@^0.4.0-beta1:
xterm-addon-fit@^0.4.0-beta2:
version "0.4.0-beta2"
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.4.0-beta2.tgz#c638ea7d41c55b535825f41b1cdb7358a94dfca4"
integrity sha512-7EHWk8SPCmKuw9ux1mFek2SfBw1QjJ/ObYA87tubOtJi7mAZ0eIb9IE5ditcma9Nyz/cR/ROQQxoRn4UbDvyfA==
xterm-addon-ligatures@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/xterm-addon-ligatures/-/xterm-addon-ligatures-0.2.0.tgz#8d65fea968ba5b4306b2ada6f53eed3e1984f69c"
integrity sha512-IcRgjq3QCcL6P8W5M86BCnXIGD1LcCVKk7w3S29Yn2+YksMM3c85JjW2OUgsL3VTW/Tb6uyxpSV2rJOsRElVrQ==
xterm-addon-ligatures@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/xterm-addon-ligatures/-/xterm-addon-ligatures-0.2.1.tgz#487125dbb25818a6f88c3464c9a8a0eb4218bdae"
integrity sha512-UGoWTM7MBRRXMyGX6oMdaBhrO6SIJTriPo2U+QyQSs4H5J64ZiMZBsJe7ieOLmsKSAC/T+c39moU6sJGbWnylg==
dependencies:
font-finder "^1.0.4"
font-ligatures "^1.3.2"
xterm-addon-search@^0.4.0-beta3:
version "0.4.0-beta5"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0-beta5.tgz#d7b7d35502cc5155d35175ab63e0465c1447eebd"
integrity sha512-aA+WmoM6U8H9V4ofKLaZaTBFoRLdxVujWA2mQxbzXBF8FLLCDSJOK8kEor4BSb8OtGr0Nlfs1Qy9O0HBmXSQWA==
xterm-addon-search@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0.tgz#a7beadb3caa7330eb31fb1f17d92de25537684a1"
integrity sha512-g07qb/Z4aSfrQ25e6Z6rz6KiExm2DvesQXkx+eA715VABBr5VM/9Jf0INoCiDSYy/nn7rpna+kXiGVJejIffKg==
xterm-addon-webgl@^0.4.0-beta1:
version "0.4.0-beta9"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3"
integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A==
xterm-addon-webgl@^0.5.0-beta.7:
version "0.5.0-beta.7"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0-beta.7.tgz#b7b95a362e942ad6f86fa286d7b7bd8ee3e7cf67"
integrity sha512-v6aCvhm1C6mvaurGwUYQfyhb2cAUyuVnzf3Ob/hy5ebtyzUj4wW0N9NbqDEJk67UeMi1lV2xZqrO5gNeTpVqFA==
xterm@3.15.0-beta98:
version "3.15.0-beta98"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta98.tgz#37f37c35577422880e7ef673cc37f9d2a45dd40c"
integrity sha512-vZbg2LcRvoiJOgr1MyeLFM9mF4uib3BWUWDHyFc+vZ58CTuK0iczOvFXgk/ySo23ZLqwmHQSigLgmWvZ8J5G0Q==
xterm@^4.4.0-beta.15:
version "4.4.0-beta.15"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0-beta.15.tgz#5897bf79d29d1a2496ccd54665aded28c341b1cc"
integrity sha512-Dvz1CMCYKeoxPF7uIDznbRgUA2Mct49Bq93K2nnrDU0pDMM3Sf1t9fkEyz59wxSx5XEHVdLS80jywsz4sjXBjQ==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
zmodem.js@^0.1.9:
version "0.1.9"
resolved "https://registry.yarnpkg.com/zmodem.js/-/zmodem.js-0.1.9.tgz#8dda36d45091bbdf263819f961d3c1a20223daf7"
integrity sha512-xixLjW1eML0uiWULsXDInyfwNW9mqESzz7ra+2MWHNG2F5JINEkE5vzF5MigpPcLvrYoHdnehPcJwQZlDph3hQ==
dependencies:
crc-32 "^1.1.1"

729
yarn.lock

File diff suppressed because it is too large Load Diff