Compare commits

..

74 Commits

Author SHA1 Message Date
Eugene Pankov
7ab3238617 build fix 2021-04-16 21:18:23 +02:00
Eugene Pankov
6d89d7a8d0 lint 2021-04-16 20:54:21 +02:00
Eugene Pankov
e277c52f71 handle empty env vars - fixed #3217 2021-04-16 20:49:57 +02:00
Eugene Pankov
2cafd97751 fixed Windows font name autocomplete - fixed #3686 2021-04-16 20:43:23 +02:00
Eugene Pankov
ad78f38210 sorted plugins list 2021-04-16 20:33:11 +02:00
Eugene Pankov
4f32908c48 node-pty and flow control fixes - fixed #3695, fixed #3701, fixed #3696, fixed #3690 2021-04-16 20:15:39 +02:00
Eugene Pankov
8f0ba06f63 use older ubuntu runners to get a lower glibc ver dependency 2021-04-11 19:18:58 +02:00
Eugene Pankov
d221ad3078 ui tweak 2021-04-11 19:18:43 +02:00
Eugene Pankov
5b4d5efcc9 ui tweak 2021-04-08 22:46:47 +02:00
Eugene Pankov
8987cc39aa settings sidebar scroll 2021-04-08 22:45:01 +02:00
Eugene Pankov
2f2dd442ad build fix 2021-04-08 22:38:18 +02:00
Eugene Pankov
8bfc86e623 bundle size opt 2021-04-08 22:26:47 +02:00
Eugene
50b1fb2410 Merge pull request #3677 from Eugeny/all-contributors/add-starxg 2021-04-06 10:44:53 +02:00
allcontributors[bot]
327daf03ef docs: update .all-contributorsrc [skip ci] 2021-04-06 07:59:17 +00:00
allcontributors[bot]
224a72d029 docs: update README.md [skip ci] 2021-04-06 07:59:16 +00:00
Eugene Pankov
3c5615e464 build fix 2021-04-05 12:52:54 +02:00
Eugene Pankov
e99908761d new tab color and activity indicators 2021-04-05 12:47:16 +02:00
Eugene Pankov
0ff81abb0c bumped electron 2021-04-05 12:32:41 +02:00
Eugene Pankov
b4e703674b settings ui updates 2021-04-05 12:32:37 +02:00
Eugene Pankov
dc53668685 search ui tweaks 2021-04-05 11:17:13 +02:00
Eugene Pankov
2df848f4c0 exclude main process sourcemaps from asar 2021-04-05 11:10:38 +02:00
Eugene Pankov
247cf6f93e added a select-all shortcut - fixes #3081 2021-04-05 11:10:27 +02:00
Eugene Pankov
797265abb1 build fix 2021-04-04 21:18:46 +02:00
Eugene Pankov
62bdcb1af4 Merge branch 'master' of github.com:Eugeny/terminus 2021-04-04 21:15:43 +02:00
Eugene Pankov
25ae56718d mention the sync-config plugin 2021-04-04 21:15:39 +02:00
Eugene
b24978dc0e Merge pull request #3647 from Eugeny/dependabot/npm_and_yarn/app/keytar-7.6.0
Bump keytar from 7.4.0 to 7.6.0 in /app
2021-04-04 21:07:15 +02:00
Eugene
46a4df108b Merge pull request #3641 from Eugeny/dependabot/npm_and_yarn/types/fs-extra-9.0.9
Bump @types/fs-extra from 8.1.1 to 9.0.9
2021-04-04 21:03:50 +02:00
Eugene
3091e2be69 Merge pull request #3634 from Eugeny/dependabot/npm_and_yarn/terminus-ssh/run-script-os-1.1.6
Bump run-script-os from 1.1.5 to 1.1.6 in /terminus-ssh
2021-04-04 21:03:38 +02:00
Eugene
9a49e94642 Merge pull request #3622 from Eugeny/dependabot/npm_and_yarn/fortawesome/fontawesome-free-5.15.3
Bump @fortawesome/fontawesome-free from 5.15.2 to 5.15.3
2021-04-04 20:40:07 +02:00
Eugene Pankov
cf13fca835 back to official node-pty 2021-04-04 20:37:04 +02:00
Eugene Pankov
174a1bcca7 remote pty 2021-04-04 20:07:57 +02:00
Eugene Pankov
80c781a8ca make tabs always-present in DOM 2021-04-04 20:03:03 +02:00
Eugene Pankov
d71ee6b6f1 disable tray icon on linux - fixes #2471, fixes #1642 2021-04-04 19:59:23 +02:00
Eugene Pankov
3a7204c2ee style fix 2021-04-04 18:17:42 +02:00
dependabot-preview[bot]
5236956469 Bump keytar from 7.4.0 to 7.6.0 in /app
Bumps [keytar](https://github.com/atom/node-keytar) from 7.4.0 to 7.6.0.
- [Release notes](https://github.com/atom/node-keytar/releases)
- [Commits](https://github.com/atom/node-keytar/compare/v7.4.0...v7.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-29 04:22:05 +00:00
dependabot-preview[bot]
4dfb8df7fa Bump @types/fs-extra from 8.1.1 to 9.0.9
Bumps [@types/fs-extra](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/fs-extra) from 8.1.1 to 9.0.9.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/fs-extra)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-26 04:35:49 +00:00
dependabot-preview[bot]
61e65c7ec8 Bump run-script-os from 1.1.5 to 1.1.6 in /terminus-ssh
Bumps [run-script-os](https://github.com/charlesguse/run-script-os) from 1.1.5 to 1.1.6.
- [Release notes](https://github.com/charlesguse/run-script-os/releases)
- [Commits](https://github.com/charlesguse/run-script-os/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-25 04:19:44 +00:00
dependabot-preview[bot]
64ab172b8e Bump @fortawesome/fontawesome-free from 5.15.2 to 5.15.3
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 5.15.2 to 5.15.3.
- [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.15.2...5.15.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-22 04:26:01 +00:00
Eugene Pankov
bd46b08c9d use @electron/remote 2021-03-20 17:12:39 +01:00
Eugene Pankov
4a97cc4383 dvorak shortcuts support - fixes #2544, fixes #2670 2021-03-20 17:12:25 +01:00
Eugene Pankov
2c11feed83 deep-clone ssh connections - fixes #3617 2021-03-20 16:24:25 +01:00
Eugene
481aa654a3 Merge pull request #3605 from Eugeny/dependabot/npm_and_yarn/app/types/node-14.14.35
Bump @types/node from 14.14.31 to 14.14.35 in /app
2021-03-20 12:43:08 +01:00
Eugene Pankov
7d1ec5b869 ui 2021-03-20 12:41:31 +01:00
Eugene Pankov
5f098ef791 bumped eslint 2021-03-20 12:41:27 +01:00
Eugene Pankov
15029066e4 Merge branch 'master' of github.com:Eugeny/terminus 2021-03-20 12:40:02 +01:00
Eugene
dbf90a5ce3 Merge pull request #3601 from Eugeny/dependabot/npm_and_yarn/patch-package-6.4.7
Bump patch-package from 6.2.2 to 6.4.7
2021-03-20 12:39:55 +01:00
Eugene Pankov
c6d4eb7083 ui 2021-03-20 12:20:48 +01:00
Eugene Pankov
e56fac03b9 fixed pkg version 2021-03-20 12:08:55 +01:00
Eugene Pankov
1132a18a0a bump ng-bootstrap 2021-03-20 12:06:10 +01:00
dependabot-preview[bot]
ebc8d846e3 Bump @types/node from 14.14.31 to 14.14.35 in /app
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.31 to 14.14.35.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-20 10:02:12 +00:00
dependabot-preview[bot]
123f68d705 Bump patch-package from 6.2.2 to 6.4.7
Bumps [patch-package](https://github.com/ds300/patch-package) from 6.2.2 to 6.4.7.
- [Release notes](https://github.com/ds300/patch-package/releases)
- [Changelog](https://github.com/ds300/patch-package/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ds300/patch-package/compare/v6.2.2...v6.4.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-20 10:01:01 +00:00
Eugene Pankov
8f9a7539c4 Merge branch 'master' of github.com:Eugeny/terminus 2021-03-20 11:00:31 +01:00
Eugene
98d46468e5 Merge pull request #3190 from Eugeny/dependabot/npm_and_yarn/app/types/mz-2.7.3
Bump @types/mz from 0.0.32 to 2.7.3 in /app
2021-03-20 10:59:55 +01:00
Eugene
a6d5a93e6e Merge pull request #3580 from Eugeny/dependabot/npm_and_yarn/sentry/cli-1.63.1
Bump @sentry/cli from 1.63.0 to 1.63.1
2021-03-20 10:59:50 +01:00
Eugene
558ef9894c Merge pull request #3596 from Eugeny/dependabot/npm_and_yarn/app/ngx-toastr-13.2.1
Bump ngx-toastr from 13.2.0 to 13.2.1 in /app
2021-03-20 10:59:31 +01:00
Eugene
7357dc178a Merge pull request #3598 from Eugeny/dependabot/npm_and_yarn/typedoc-0.20.32
Bump typedoc from 0.20.28 to 0.20.32
2021-03-20 10:59:15 +01:00
Eugene
cd7df7c700 Merge pull request #3599 from Eugeny/dependabot/npm_and_yarn/node-abi-2.21.0
Bump node-abi from 2.20.0 to 2.21.0
2021-03-20 10:59:06 +01:00
Eugene
a4c4c93bac Merge pull request #3607 from Eugeny/dependabot/npm_and_yarn/css-loader-5.1.3
Bump css-loader from 5.0.1 to 5.1.3
2021-03-20 10:58:51 +01:00
Eugene
a97619a474 Merge pull request #3609 from Eugeny/dependabot/npm_and_yarn/types/node-14.14.35
Bump @types/node from 14.14.31 to 14.14.35
2021-03-20 10:58:43 +01:00
Eugene
cd1cac96e9 Merge pull request #3616 from Eugeny/dependabot/npm_and_yarn/webpack-5.26.3
Bump webpack from 5.18.0 to 5.26.3
2021-03-20 10:58:37 +01:00
Eugene Pankov
9861766da0 Merge branch 'master' of github.com:Eugeny/terminus 2021-03-20 10:57:13 +01:00
dependabot-preview[bot]
bcd5f3b8bf Bump webpack from 5.18.0 to 5.26.3
Bumps [webpack](https://github.com/webpack/webpack) from 5.18.0 to 5.26.3.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.18.0...v5.26.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-18 04:21:37 +00:00
dependabot-preview[bot]
3e4f2c467d Bump @types/node from 14.14.31 to 14.14.35
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.31 to 14.14.35.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-16 04:23:59 +00:00
dependabot-preview[bot]
e84270609f Bump css-loader from 5.0.1 to 5.1.3
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.0.1 to 5.1.3.
- [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/v5.0.1...v5.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-16 04:23:00 +00:00
Eugene Pankov
62b1538462 fixed #3472 2021-03-15 08:59:22 +01:00
dependabot-preview[bot]
d6a62344b8 Bump node-abi from 2.20.0 to 2.21.0
Bumps [node-abi](https://github.com/lgeiger/node-abi) from 2.20.0 to 2.21.0.
- [Release notes](https://github.com/lgeiger/node-abi/releases)
- [Commits](https://github.com/lgeiger/node-abi/compare/v2.20.0...v2.21.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-15 04:24:56 +00:00
dependabot-preview[bot]
d2e16cd73a Bump typedoc from 0.20.28 to 0.20.32
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.20.28 to 0.20.32.
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.20.28...v0.20.32)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-15 04:24:28 +00:00
dependabot-preview[bot]
87d3b08c37 Bump ngx-toastr from 13.2.0 to 13.2.1 in /app
Bumps [ngx-toastr](https://github.com/scttcper/ngx-toastr) from 13.2.0 to 13.2.1.
- [Release notes](https://github.com/scttcper/ngx-toastr/releases)
- [Commits](https://github.com/scttcper/ngx-toastr/compare/v13.2.0...v13.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-15 04:23:28 +00:00
Eugene
3a7db137c5 Merge pull request #3595 from Eugeny/imgbot
[ImgBot] Optimize images
2021-03-14 20:01:03 +01:00
ImgBotApp
da0f4e7afc [ImgBot] Optimize images
*Total -- 1,742.80kb -> 1,143.10kb (34.41%)

/docs/readme-ssh.png -- 613.63kb -> 371.43kb (39.47%)
/docs/readme-terminal.png -- 1,082.06kb -> 727.74kb (32.75%)
/terminus-terminal/src/icons/suse.svg -- 3.46kb -> 2.80kb (18.98%)
/app/assets/logo.svg -- 2.94kb -> 2.62kb (10.98%)
/build/icons/icon.svg -- 3.08kb -> 2.75kb (10.75%)
/terminus-terminal/src/icons/linux.svg -- 18.02kb -> 16.95kb (5.95%)
/terminus-terminal/src/icons/debian.svg -- 5.33kb -> 5.02kb (5.86%)
/terminus-terminal/src/icons/cmd.svg -- 0.40kb -> 0.38kb (4.85%)
/terminus-serial/src/icons/serial.svg -- 4.29kb -> 4.08kb (4.74%)
/terminus-terminal/src/icons/clink.svg -- 0.40kb -> 0.38kb (4.39%)
/terminus-terminal/src/icons/plus.svg -- 0.46kb -> 0.44kb (4.26%)
/terminus-terminal/src/icons/cmder-powershell.svg -- 0.57kb -> 0.55kb (3.08%)
/terminus-terminal/src/icons/cmder.svg -- 0.57kb -> 0.56kb (3.06%)
/terminus-terminal/src/icons/profiles.svg -- 0.67kb -> 0.65kb (2.92%)
/terminus-terminal/src/icons/ubuntu.svg -- 3.09kb -> 3.00kb (2.69%)
/terminus-terminal/src/icons/powershell.svg -- 0.66kb -> 0.64kb (2.65%)
/terminus-terminal/src/icons/powershell-core.svg -- 0.66kb -> 0.65kb (2.65%)
/terminus-terminal/src/icons/git-bash.svg -- 0.72kb -> 0.70kb (2.45%)
/terminus-terminal/src/icons/cygwin.svg -- 0.31kb -> 0.31kb (2.17%)
/terminus-terminal/src/icons/alpine.svg -- 1.46kb -> 1.44kb (1.74%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-03-14 18:56:55 +00:00
Eugene Pankov
e1980a6611 updated readme 2021-03-14 19:55:59 +01:00
Eugene Pankov
08cc19946f fixed font loading in the web version 2021-03-14 14:40:36 +01:00
dependabot-preview[bot]
2f7d29c523 Bump @sentry/cli from 1.63.0 to 1.63.1
Bumps [@sentry/cli](https://github.com/getsentry/sentry-cli) from 1.63.0 to 1.63.1.
- [Release notes](https://github.com/getsentry/sentry-cli/releases)
- [Changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-cli/compare/1.63.0...1.63.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-11 04:23:43 +00:00
dependabot-preview[bot]
f42c3cdde4 Bump @types/mz from 0.0.32 to 2.7.3 in /app
Bumps [@types/mz](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mz) from 0.0.32 to 2.7.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mz)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-10 19:48:21 +00:00
101 changed files with 2165 additions and 1794 deletions

View File

@@ -370,6 +370,15 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "starxg",
"name": "starxg",
"avatar_url": "https://avatars.githubusercontent.com/u/34997494?v=4",
"profile": "https://github.com/starxg",
"contributions": [
"plugin"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -2,7 +2,7 @@ name: Linux Build
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-16.04
steps: steps:
- name: Checkout - name: Checkout

View File

@@ -1,44 +1,92 @@
![](https://github.com/Eugeny/terminus/raw/master/docs/readme.png) ![](docs/readme.png)
<p align="center">
<a href="https://raw.githubusercontent.com/Eugeny/terminus/master/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/eugeny/terminus.svg?label=License&style=flat-square"></a> <a href="https://ci.appveyor.com/project/Eugeny/terminus"><img alt="AppVeyor" src="https://img.shields.io/appveyor/ci/eugeny/****terminus****.svg?label=CI&logo=appveyor&logoColor=white&style=flat-square"></a>
</p>
<p align="center"> <p align="center">
<a href="https://github.com/Eugeny/terminus/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/terminus/total.svg?label=RELEASE&logo=github&style=for-the-badge"></a> <a href="https://nightly.link/Eugeny/terminus/workflows/windows/master"><img src="https://shields.io/badge/-Nightly-blue?logo=windows&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/terminus/workflows/macos/master"><img src="https://shields.io/badge/-Nightly-black?logo=apple&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/terminus/workflows/linux/master"><img src="https://shields.io/badge/-Nightly-orange?logo=linux&style=for-the-badge"/></a> <a href="https://gitter.im/terminus-terminal/community"><img alt="Gitter" src="https://img.shields.io/gitter/room/terminus/community.svg?color=magenta&logo=gitter&style=for-the-badge"></a> <a href="https://github.com/Eugeny/terminus/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/terminus/total.svg?label=RELEASE&logo=github&style=for-the-badge"></a> <a href="https://nightly.link/Eugeny/terminus/workflows/windows/master"><img src="https://shields.io/badge/-Nightly-blue?logo=windows&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/terminus/workflows/macos/master"><img src="https://shields.io/badge/-Nightly-black?logo=apple&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/terminus/workflows/linux/master"><img src="https://shields.io/badge/-Nightly-orange?logo=linux&style=for-the-badge"/></a> <a href="https://gitter.im/terminus-terminal/community"><img alt="Gitter" src="https://img.shields.io/gitter/room/terminus/community.svg?color=magenta&logo=gitter&style=for-the-badge"></a>
</p> </p>
---- ----
**Terminus** is a highly configurable terminal emulator for Windows, macOS and Linux **Terminus** is a highly configurable terminal emulator, SSH and serial client for Windows, macOS and Linux
* Integrated SSH client and connection manager * Integrated SSH client and connection manager
* Theming and color schemes * Integrated serial terminal
* Fully configurable shortcuts * Theming and color schemes
* Split panes * Fully configurable shortcuts and multi-chord shortcuts
* Remembers your tabs * Split panes
* PowerShell (and PS Core), WSL, Git-Bash, Cygwin, Cmder and CMD support * Remembers your tabs
* Direct file transfer from/to SSH sessions via Zmodem * PowerShell (and PS Core), WSL, Git-Bash, Cygwin, Cmder and CMD support
* Full Unicode support including double-width characters * Direct file transfer from/to SSH sessions via Zmodem
* Doesn't choke on fast-flowing outputs * Full Unicode support including double-width characters
* Proper shell experience on Windows including tab completion (via Clink) * Doesn't choke on fast-flowing outputs
* Proper shell experience on Windows including tab completion (via Clink)
[![Buy me a coffee](https://github.com/Eugeny/terminus/raw/master/docs/kofi.png)](https://ko-fi.com/eugeny) [![Buy me a coffee](https://github.com/Eugeny/terminus/raw/master/docs/kofi.png)](https://ko-fi.com/eugeny)
--- ---
# Contents
- [Contents](#contents)
- [What Terminus is and isn't](#what-terminus-is-and-isnt)
- [Terminal features](#terminal-features)
- [SSH Client](#ssh-client)
- [Serial Terminal](#serial-terminal)
- [Portable](#portable)
- [Plugins](#plugins)
- [Themes](#themes)
- [Contributing](#contributing)
<a name="about"></a>
# What Terminus is and isn't
* **Terminus is** an alternative to Windows' standard terminal (conhost), PowerShell ISE, PuTTY or iTerm * **Terminus is** an alternative to Windows' standard terminal (conhost), PowerShell ISE, PuTTY or iTerm
* **Terminus is not** a new shell or a MinGW or Cygwin replacement. Neither is it lightweight - if RAM usage is of importance, consider [Conemu](https://conemu.github.io) or [Alacritty](https://github.com/jwilm/alacritty) * **Terminus is not** a new shell or a MinGW or Cygwin replacement. Neither is it lightweight - if RAM usage is of importance, consider [Conemu](https://conemu.github.io) or [Alacritty](https://github.com/jwilm/alacritty)
--- <a name="terminal"></a>
# Terminal features
![](docs/readme-terminal.png)
* A V220 terminal + various extensions
* Multiple nested split panes
* Tabs on any side of the window
* Optional dockable window with a global spawn hotkey ("Quake console")
* Progress detection
* Notification on process completion
* Bracketed paste, multiline paste warnings
* Font ligatures
* Custom shell profiles
* Optional RMB paste and copy-on select (PuTTY style)
<a name="ssh"></a>
# SSH Client
![](docs/readme-ssh.png)
* SSH2 client with a connection manager
* X11 and port forwarding
* Automatic jump host management
* Agent forwarding (incl. Pageant and Windows native OpenSSH Agent)
* Login scripts
<a name="serial"></a>
# Serial Terminal
* Saved connections
* Readline input support
* Optional hex byte-by-byte input and hexdump output
* Newline conversion
* Automatic reconnection
<a name="portable"></a>
# Portable # Portable
For portable in windows, user can create folder `data` at the same directory as `Terminal.exe` to save the settings. Terminus will run as a portable app on Windows, if you create a `data` folder in the same location where `Terminus.exe` lives.
<a name="plugins"></a>
# Plugins # Plugins
Plugins and themes can be installed directly from the Settings view inside Terminus. Plugins and themes can be installed directly from the Settings view inside Terminus.
@@ -48,7 +96,9 @@ Plugins and themes can be installed directly from the Settings view inside Termi
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs * [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs
* [save-output](https://github.com/Eugeny/terminus-save-output) - record terminal output into a file * [save-output](https://github.com/Eugeny/terminus-save-output) - record terminal output into a file
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to hterm tabs * [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to hterm tabs
* [sync-config](https://github.com/starxg/terminus-sync-config) - sync the config to Gist or Gitee
<a name="themes"></a>
# Themes # Themes
* [hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme * [hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
@@ -57,8 +107,7 @@ Plugins and themes can be installed directly from the Settings view inside Termi
* [windows10](https://www.npmjs.com/package/terminus-theme-windows10) * [windows10](https://www.npmjs.com/package/terminus-theme-windows10)
* [altair](https://github.com/yxuko/terminus-altair) * [altair](https://github.com/yxuko/terminus-altair)
--- <a name="contributing"></a>
# Contributing # Contributing
Pull requests and plugins are welcome! Pull requests and plugins are welcome!
@@ -66,6 +115,7 @@ Pull requests and plugins are welcome!
See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) and [API docs](http://ajenti.org/terminus-docs/) for information of how the project is laid out, and a very brief plugin development tutorial. See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) and [API docs](http://ajenti.org/terminus-docs/) for information of how the project is laid out, and a very brief plugin development tutorial.
--- ---
<a name="contributors"></a>
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -124,6 +174,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="http://patalong.pl"><img src="https://avatars.githubusercontent.com/u/29167842?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Patalong</b></sub></a><br /><a href="#design-VectorKappa" title="Design">🎨</a></td> <td align="center"><a href="http://patalong.pl"><img src="https://avatars.githubusercontent.com/u/29167842?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Patalong</b></sub></a><br /><a href="#design-VectorKappa" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/clarkwang"><img src="https://avatars.githubusercontent.com/u/157076?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clark Wang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=clarkwang" title="Code">💻</a></td> <td align="center"><a href="https://github.com/clarkwang"><img src="https://avatars.githubusercontent.com/u/157076?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clark Wang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=clarkwang" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iamchating"><img src="https://avatars.githubusercontent.com/u/7088153?v=4?s=100" width="100px;" alt=""/><br /><sub><b>iamchating</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=iamchating" title="Code">💻</a></td> <td align="center"><a href="https://github.com/iamchating"><img src="https://avatars.githubusercontent.com/u/7088153?v=4?s=100" width="100px;" alt=""/><br /><sub><b>iamchating</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=iamchating" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/starxg"><img src="https://avatars.githubusercontent.com/u/34997494?v=4?s=100" width="100px;" alt=""/><br /><sub><b>starxg</b></sub></a><br /><a href="#plugin-starxg" title="Plugin/utility libraries">🔌</a></td>
</tr> </tr>
</table> </table>

View File

@@ -1,55 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 1024 1024" xml:space="preserve" style="enable-background:new 0 0 1024 1024"><style type="text/css">.st0{fill:url(#SVGID_1_)}.st1{opacity:.16;fill:url(#SVGID_2_)}.st2{fill:url(#SVGID_3_)}.st3{opacity:.16;fill:url(#SVGID_4_)}.st4{fill:url(#SVGID_5_)}.st5{opacity:.15;fill:url(#SVGID_6_)}.st6{fill:url(#SVGID_7_)}</style><g><linearGradient id="SVGID_1_" x1="260.967" x2="919.184" y1="871.181" y2="491.16" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#669abd"/><stop offset="1" style="stop-color:#77dbdb"/></linearGradient><polygon points="297.54 934.52 882.6 596.72 882.61 427.82 297.54 765.65" class="st0"/><linearGradient id="SVGID_2_" x1="553.505" x2="626.647" y1="617.828" y2="744.513" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="297.54 934.52 882.6 596.72 882.61 427.82 297.54 765.65" class="st1"/></g><g><linearGradient id="SVGID_3_" x1="114.663" x2="334.091" y1="744.528" y2="871.214" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="151.23 681.18 151.22 850.09 297.54 934.52 297.54 765.65" class="st2"/><linearGradient id="SVGID_4_" x1="260.948" x2="187.806" y1="744.528" y2="871.213" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="151.23 681.18 151.22 850.09 297.54 934.52 297.54 765.65" class="st3"/></g><g><linearGradient id="SVGID_5_" x1="114.663" x2="553.503" y1="237.793" y2="491.157" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="151.23 174.45 151.21 343.36 443.79 512.27 590.08 427.81" class="st4"/><linearGradient id="SVGID_6_" x1="370.656" x2="297.509" y1="301.128" y2="427.822" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="151.23 174.45 151.21 343.36 443.79 512.27 590.08 427.81" class="st5"/></g><linearGradient id="SVGID_7_" x1="78.091" x2="736.337" y1="554.498" y2="174.459" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ccecff"/><stop offset="1" style="stop-color:#9feced"/></linearGradient><polygon points="297.51 765.64 151.23 681.18 590.08 427.81 151.23 174.45 297.5 90 882.61 427.82" class="st6"/></svg>
<!-- Generator: Adobe Illustrator 23.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{opacity:0.16;fill:url(#SVGID_2_);}
.st2{fill:url(#SVGID_3_);}
.st3{opacity:0.16;fill:url(#SVGID_4_);}
.st4{fill:url(#SVGID_5_);}
.st5{opacity:0.15;fill:url(#SVGID_6_);}
.st6{fill:url(#SVGID_7_);}
</style>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="260.9675" y1="871.1813" x2="919.1845" y2="491.1596">
<stop offset="0" style="stop-color:#669ABD"/>
<stop offset="1" style="stop-color:#77DBDB"/>
</linearGradient>
<polygon class="st0" points="297.54,934.52 882.6,596.72 882.61,427.82 297.54,765.65 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="553.5051" y1="617.8278" x2="626.647" y2="744.5132">
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<polygon class="st1" points="297.54,934.52 882.6,596.72 882.61,427.82 297.54,765.65 "/>
</g>
<g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="114.6631" y1="744.5275" x2="334.0905" y2="871.2141">
<stop offset="0" style="stop-color:#6A8FAD"/>
<stop offset="1" style="stop-color:#669ABD"/>
</linearGradient>
<polygon class="st2" points="151.23,681.18 151.22,850.09 297.54,934.52 297.54,765.65 "/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="260.9478" y1="744.5281" x2="187.8059" y2="871.2135">
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<polygon class="st3" points="151.23,681.18 151.22,850.09 297.54,934.52 297.54,765.65 "/>
</g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="114.663" y1="237.793" x2="553.5026" y2="491.1571">
<stop offset="0" style="stop-color:#6A8FAD"/>
<stop offset="1" style="stop-color:#669ABD"/>
</linearGradient>
<polygon class="st4" points="151.23,174.45 151.21,343.36 443.79,512.27 590.08,427.81 "/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="370.6562" y1="301.1281" x2="297.5094" y2="427.8221">
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<polygon class="st5" points="151.23,174.45 151.21,343.36 443.79,512.27 590.08,427.81 "/>
</g>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="78.0912" y1="554.4979" x2="736.3375" y2="174.4593">
<stop offset="0" style="stop-color:#CCECFF"/>
<stop offset="1" style="stop-color:#9FECED"/>
</linearGradient>
<polygon class="st6" points="297.51,765.64 151.23,681.18 590.08,427.81 151.23,174.45 297.5,90 882.61,427.82 "/>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,14 +1,21 @@
import { app, ipcMain, Menu, Tray, shell, screen, globalShortcut, MenuItemConstructorOptions } from 'electron' import { app, ipcMain, Menu, Tray, shell, screen, globalShortcut, MenuItemConstructorOptions } from 'electron'
import * as promiseIpc from 'electron-promise-ipc' import * as promiseIpc from 'electron-promise-ipc'
import * as remote from '@electron/remote/main'
import { loadConfig } from './config' import { loadConfig } from './config'
import { Window, WindowOptions } from './window' import { Window, WindowOptions } from './window'
import { pluginManager } from './pluginManager' import { pluginManager } from './pluginManager'
import { PTYManager } from './pty'
export class Application { export class Application {
private tray?: Tray private tray?: Tray
private ptyManager = new PTYManager()
private windows: Window[] = [] private windows: Window[] = []
constructor () { constructor () {
remote.initialize()
this.ptyManager.init(this)
ipcMain.on('app:config-change', (_event, config) => { ipcMain.on('app:config-change', (_event, config) => {
this.broadcast('host:config-change', config) this.broadcast('host:config-change', config)
}) })
@@ -106,7 +113,7 @@ export class Application {
} }
enableTray (): void { enableTray (): void {
if (this.tray) { if (this.tray || process.platform === 'linux') {
return return
} }
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@@ -131,6 +138,9 @@ export class Application {
} }
disableTray (): void { disableTray (): void {
if (process.platform === 'linux') {
return
}
this.tray?.destroy() this.tray?.destroy()
this.tray = null this.tray = null
} }

57
app/lib/bufferizedPTY.js Normal file
View File

@@ -0,0 +1,57 @@
/** @hidden */
module.exports = function patchPTYModule (mod) {
const oldSpawn = mod.spawn
if (mod.patched) {
return
}
mod.patched = true
mod.spawn = (file, args, opt) => {
let terminal = oldSpawn(file, args, opt)
let timeout = null
let buffer = Buffer.from('')
let lastFlush = 0
let nextTimeout = 0
// Minimum prebuffering window (ms) if the input is non-stop flowing
const minWindow = 5
// Maximum buffering time (ms) until output must be flushed unconditionally
const maxWindow = 100
function flush () {
if (buffer.length) {
terminal.emit('data-buffered', buffer)
}
lastFlush = Date.now()
buffer = Buffer.from('')
}
function reschedule () {
if (timeout) {
clearTimeout(timeout)
}
nextTimeout = Date.now() + minWindow
timeout = setTimeout(() => {
timeout = null
flush()
}, minWindow)
}
terminal.on('data', data => {
if (typeof data === 'string') {
data = Buffer.from(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) {
// Extend the window if it's expiring
reschedule()
}
}
})
return terminal
}
}

View File

@@ -1,12 +1,8 @@
import * as path from 'path' import * as path from 'path'
import * as fs from 'fs' import * as fs from 'fs'
import * as electron from 'electron'
let appPath: string | null = null const appPath = path.dirname(electron.app.getPath('exe'))
try {
appPath = path.dirname(require('electron').app.getPath('exe'))
} catch {
appPath = path.dirname(require('electron').remote.app.getPath('exe'))
}
if (fs.existsSync(path.join(appPath, 'terminus-data'))) { if (fs.existsSync(path.join(appPath, 'terminus-data'))) {
fs.renameSync(path.join(appPath, 'terminus-data'), path.join(appPath, 'data')) fs.renameSync(path.join(appPath, 'terminus-data'), path.join(appPath, 'data'))
@@ -14,9 +10,5 @@ if (fs.existsSync(path.join(appPath, 'terminus-data'))) {
const portableData = path.join(appPath, 'data') const portableData = path.join(appPath, 'data')
if (fs.existsSync(portableData)) { if (fs.existsSync(portableData)) {
console.log('reset user data to ' + portableData) console.log('reset user data to ' + portableData)
try { electron.app.setPath('userData', portableData)
require('electron').app.setPath('userData', portableData)
} catch {
require('electron').remote.app.setPath('userData', portableData)
}
} }

154
app/lib/pty.ts Normal file
View File

@@ -0,0 +1,154 @@
import * as nodePTY from '@terminus-term/node-pty'
import { v4 as uuidv4 } from 'uuid'
import { ipcMain } from 'electron'
import { Application } from './app'
class PTYDataQueue {
private buffers: Buffer[] = []
private delta = 0
private maxChunk = 1024
private maxDelta = 1024 * 50
private flowPaused = false
constructor (private pty: nodePTY.IPty, private onData: (data: Buffer) => void) { }
push (data: Buffer) {
this.buffers.push(data)
this.maybeEmit()
}
ack (length: number) {
this.delta -= length
this.maybeEmit()
}
private maybeEmit () {
if (this.delta <= this.maxDelta && this.flowPaused) {
this.resume()
return
}
if (this.buffers.length > 0) {
if (this.delta > this.maxDelta && !this.flowPaused) {
this.pause()
return
}
const buffersToSend = []
let totalLength = 0
while (totalLength < this.maxChunk && this.buffers.length) {
totalLength += this.buffers[0].length
buffersToSend.push(this.buffers.shift())
}
if (buffersToSend.length === 0) {
return
}
let toSend = Buffer.concat(buffersToSend)
if (toSend.length > this.maxChunk) {
this.buffers.unshift(toSend.slice(this.maxChunk))
toSend = toSend.slice(0, this.maxChunk)
}
this.onData(toSend)
this.delta += toSend.length
if (this.buffers.length) {
setImmediate(() => this.maybeEmit())
}
}
}
private pause () {
this.pty.pause()
this.flowPaused = true
}
private resume () {
this.pty.resume()
this.flowPaused = false
this.maybeEmit()
}
}
export class PTY {
private pty: nodePTY.IPty
private outputQueue: PTYDataQueue
constructor (private id: string, private app: Application, ...args: any[]) {
this.pty = (nodePTY as any).spawn(...args)
for (const key of ['close', 'exit']) {
(this.pty as any).on(key, (...eventArgs) => this.emit(key, ...eventArgs))
}
this.outputQueue = new PTYDataQueue(this.pty, data => {
setImmediate(() => this.emit('data-buffered', data))
})
this.pty.on('data', data => this.outputQueue.push(Buffer.from(data)))
}
getPID (): number {
return this.pty.pid
}
resize (columns: number, rows: number): void {
if ((this.pty as any)._writable) {
this.pty.resize(columns, rows)
}
}
write (buffer: Buffer): void {
if ((this.pty as any)._writable) {
this.pty.write(buffer.toString())
}
}
ackData (length: number): void {
this.outputQueue.ack(length)
}
kill (signal?: string): void {
this.pty.kill(signal)
}
private emit (event: string, ...args: any[]) {
this.app.broadcast(`pty:${this.id}:${event}`, ...args)
}
}
export class PTYManager {
private ptys: Record<string, PTY> = {}
init (app: Application): void {
//require('./bufferizedPTY')(nodePTY) // eslint-disable-line @typescript-eslint/no-var-requires
ipcMain.on('pty:spawn', (event, ...options) => {
const id = uuidv4().toString()
event.returnValue = id
this.ptys[id] = new PTY(id, app, ...options)
})
ipcMain.on('pty:exists', (event, id) => {
event.returnValue = !!this.ptys[id]
})
ipcMain.on('pty:get-pid', (event, id) => {
event.returnValue = this.ptys[id].getPID()
})
ipcMain.on('pty:resize', (_event, id, columns, rows) => {
this.ptys[id].resize(columns, rows)
})
ipcMain.on('pty:write', (_event, id, data) => {
this.ptys[id].write(Buffer.from(data))
})
ipcMain.on('pty:kill', (_event, id, signal) => {
this.ptys[id].kill(signal)
})
ipcMain.on('pty:ack-data', (_event, id, length) => {
this.ptys[id].ackData(length)
})
}
}

View File

@@ -1,16 +1,14 @@
const { init } = String(process.type) === 'main' ? require('@sentry/electron/dist/main') : require('@sentry/electron/dist/renderer') const { init } = String(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' const SENTRY_DSN = 'https://4717a0a7ee0b4429bd3a0f06c3d7eec3@sentry.io/181876'
let release = null let release = null
try { try {
release = require('electron').app.getVersion() release = require('electron').app.getVersion()
} catch { } catch {
release = require('electron').remote.app.getVersion() release = require('@electron/remote').app.getVersion()
} }
if (!isDev) { if (!process.env.TERMINUS_DEV) {
init({ init({
dsn: SENTRY_DSN, dsn: SENTRY_DSN,
release, release,

View File

@@ -21,23 +21,24 @@
"@angular/forms": "^11.1.1", "@angular/forms": "^11.1.1",
"@angular/platform-browser": "^11.1.1", "@angular/platform-browser": "^11.1.1",
"@angular/platform-browser-dynamic": "^11.1.1", "@angular/platform-browser-dynamic": "^11.1.1",
"@ng-bootstrap/ng-bootstrap": "^6.1.0", "@electron/remote": "^1.0.4",
"@terminus-term/node-pty": "0.10.0-terminus.3", "@ng-bootstrap/ng-bootstrap": "^7.0.0",
"any-promise": "^1.3.0",
"electron-config": "2.0.0", "electron-config": "2.0.0",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
"electron-is-dev": "1.2.0",
"electron-promise-ipc": "^2.2.4", "electron-promise-ipc": "^2.2.4",
"fontmanager-redux": "1.0.0", "fontmanager-redux": "1.0.0",
"glasstron": "0.0.7", "glasstron": "0.0.7",
"js-yaml": "4.0.0", "js-yaml": "4.0.0",
"keytar": "^7.4.0", "keytar": "^7.6.0",
"mz": "^2.7.0", "mz": "^2.7.0",
"native-process-working-directory": "^1.0.2", "native-process-working-directory": "^1.0.2",
"ngx-toastr": "^13.2.0", "ngx-toastr": "^13.2.1",
"@terminus-term/node-pty": "0.10.0-terminus.3",
"npm": "6", "npm": "6",
"path": "0.12.7", "path": "0.12.7",
"rxjs": "^6.6.6", "rxjs": "^6.6.6",
"yargs": "^15.4.1", "yargs": "^16.2.0",
"zone.js": "^0.11.4" "zone.js": "^0.11.4"
}, },
"optionalDependencies": { "optionalDependencies": {
@@ -48,8 +49,8 @@
"windows-process-tree": "^0.2.4" "windows-process-tree": "^0.2.4"
}, },
"devDependencies": { "devDependencies": {
"@types/mz": "0.0.32", "@types/mz": "2.7.3",
"@types/node": "14.14.31", "@types/node": "14.14.35",
"node-abi": "^2.21.0", "node-abi": "^2.21.0",
"source-map-support": "^0.5.19" "source-map-support": "^0.5.19"
}, },

View File

@@ -2,8 +2,6 @@ import 'zone.js'
import 'core-js/proposals/reflect-metadata' import 'core-js/proposals/reflect-metadata'
import 'rxjs' import 'rxjs'
import * as isDev from 'electron-is-dev'
import './global.scss' import './global.scss'
import './toastr.scss' import './toastr.scss'
@@ -23,7 +21,7 @@ if (process.platform === 'win32' && !('HOME' in process.env)) {
process.env.HOME = `${process.env.HOMEDRIVE}${process.env.HOMEPATH}` process.env.HOME = `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`
} }
if (isDev) { if (process.env.TERMINUS_DEV) {
console.warn('Running in debug mode') console.warn('Running in debug mode')
} else { } else {
enableProdMode() enableProdMode()
@@ -39,7 +37,7 @@ async function bootstrap (plugins: PluginInfo[], safeMode = false): Promise<NgMo
const module = getRootModule(pluginsModules) const module = getRootModule(pluginsModules)
window['rootModule'] = module window['rootModule'] = module
return platformBrowserDynamic().bootstrapModule(module).then(moduleRef => { return platformBrowserDynamic().bootstrapModule(module).then(moduleRef => {
if (isDev) { if (process.env.TERMINUS_DEV) {
const applicationRef = moduleRef.injector.get(ApplicationRef) const applicationRef = moduleRef.injector.get(ApplicationRef)
const componentRef = applicationRef.components[0] const componentRef = applicationRef.components[0]
enableDebugTools(componentRef) enableDebugTools(componentRef)

View File

@@ -1,5 +1,6 @@
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import * as path from 'path' import * as path from 'path'
import * as remote from '@electron/remote'
const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires
const nodeRequire = (global as any).require const nodeRequire = (global as any).require
@@ -15,13 +16,13 @@ function normalizePath (p: string): string {
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x))) global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
if (process.env.TERMINUS_DEV) { if (process.env.TERMINUS_DEV) {
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath())) nodeModule.globalPaths.unshift(path.dirname(remote.app.getAppPath()))
} }
const builtinPluginsPath = process.env.TERMINUS_DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins') const builtinPluginsPath = process.env.TERMINUS_DEV ? path.dirname(remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
const userPluginsPath = path.join( const userPluginsPath = path.join(
require('electron').remote.app.getPath('userData'), remote.app.getPath('userData'),
'plugins', 'plugins',
) )

View File

@@ -7,6 +7,7 @@
height: 100vh; height: 100vh;
display: flex; display: flex;
animation: 0.5s ease-out fadeIn; animation: 0.5s ease-out fadeIn;
background: radial-gradient(#3a66820a 0%, #000e17 30%, black 100%);
&>div { &>div {
width: 200px; width: 200px;
@@ -23,6 +24,7 @@
transition: 1s ease-out width; transition: 1s ease-out width;
background: #a1c5e4; background: #a1c5e4;
height: 3px; height: 3px;
box-shadow: 0 0 2px #ffffff1f;
} }
} }
} }
@@ -37,8 +39,8 @@
.terminus-logo { .terminus-logo {
width: 160px; width: 120px;
height: 160px; height: 120px;
background: url('../assets/logo.svg'); background: url('../assets/logo.svg');
background-repeat: none; background-repeat: none;
background-size: contain; background-size: contain;
@@ -51,7 +53,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
text-align: center; text-align: center;
font-weight: normal; font-weight: normal;
font-size: 42px; font-size: 32px;
margin: 0; margin: 0;
sup { sup {

View File

@@ -68,7 +68,6 @@ module.exports = {
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap', '@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
child_process: 'commonjs child_process', child_process: 'commonjs child_process',
electron: 'commonjs electron', electron: 'commonjs electron',
'electron-is-dev': 'commonjs electron-is-dev',
fs: 'commonjs fs', fs: 'commonjs fs',
'ngx-toastr': 'commonjs ngx-toastr', 'ngx-toastr': 'commonjs ngx-toastr',
module: 'commonjs module', module: 'commonjs module',

View File

@@ -1,9 +1,10 @@
const path = require('path') const path = require('path')
const webpack = require('webpack') const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = { module.exports = {
name: 'terminus-main', name: 'terminus-main',
target: 'node', target: 'electron-main',
entry: { entry: {
main: path.resolve(__dirname, 'lib/index.ts'), main: path.resolve(__dirname, 'lib/index.ts'),
}, },
@@ -33,19 +34,25 @@ module.exports = {
], ],
}, },
externals: { externals: {
'any-promise': 'commonjs any-promise',
electron: 'commonjs electron', electron: 'commonjs electron',
'electron-config': 'commonjs electron-config', 'electron-config': 'commonjs electron-config',
'electron-debug': 'commonjs electron-debug',
'electron-promise-ipc': 'commonjs electron-promise-ipc', 'electron-promise-ipc': 'commonjs electron-promise-ipc',
'electron-vibrancy': 'commonjs electron-vibrancy', 'electron-vibrancy': 'commonjs electron-vibrancy',
fs: 'commonjs fs', fs: 'commonjs fs',
glasstron: 'commonjs glasstron', glasstron: 'commonjs glasstron',
mz: 'commonjs mz', mz: 'commonjs mz',
npm: 'commonjs npm', npm: 'commonjs npm',
'@terminus-term/node-pty': 'commonjs @terminus-term/node-pty',
path: 'commonjs path', path: 'commonjs path',
rxjs: 'commonjs rxjs',
'rxjs/operators': 'commonjs rxjs/operators',
util: 'commonjs util', util: 'commonjs util',
'source-map-support': 'commonjs source-map-support', 'source-map-support': 'commonjs source-map-support',
'windows-swca': 'commonjs windows-swca', 'windows-swca': 'commonjs windows-swca',
'windows-blurbehind': 'commonjs windows-blurbehind', 'windows-blurbehind': 'commonjs windows-blurbehind',
'yargs/yargs': 'commonjs yargs/yargs',
}, },
plugins: [ plugins: [
new webpack.optimize.ModuleConcatenationPlugin(), new webpack.optimize.ModuleConcatenationPlugin(),
@@ -53,6 +60,8 @@ module.exports = {
'process.type': '"main"', 'process.type': '"main"',
}), }),
], ],
// Ignore warnings due to yarg's dynamic module loading }
ignoreWarnings: [/node_modules\/yargs/],
if (process.env.BUNDLE_ANALYZER) {
module.exports.plugins.push(new BundleAnalyzerPlugin())
} }

View File

@@ -51,6 +51,11 @@
dependencies: dependencies:
tslib "^2.0.0" tslib "^2.0.0"
"@electron/remote@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-1.0.4.tgz#f1c8cf3560bab762b462bfae9991919cced8bc33"
integrity sha512-kguDJRhL3ZynHrkbX8Tr7xoAzGsNgh4eqXkycXb6cgXbOgehGqkBVe+MnjSVMXz3QJykerGKPy28gqcM7AFGYw==
"@iarna/cli@^1.2.0": "@iarna/cli@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641" resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641"
@@ -60,10 +65,12 @@
update-notifier "^2.2.0" update-notifier "^2.2.0"
yargs "^8.0.2" yargs "^8.0.2"
"@ng-bootstrap/ng-bootstrap@^6.1.0": "@ng-bootstrap/ng-bootstrap@^7.0.0":
version "6.2.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.2.0.tgz#0506d612ca6002bd8fa398d006fa2641013e11d4" resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-7.0.0.tgz#3bfa62eb52fdb891b1ce693ea11c39127e2d1ab7"
integrity sha512-wqwhnJFyEwvzWQJjXrEt+7oBTSvu2qPbdYvUFYhDVzOJLWB5M7YEhDAkMrfHQJ0pZNBMGr580FqYue+XiURY0Q== integrity sha512-SxUaptGWJmCxM0d2Zy1mx7K7p/YBwGZ69NmmBQVY4BE6p5av0hWrVmv9rzzfBz0rhxU7RPZLor2Jpaoq8Xyl4w==
dependencies:
tslib "^2.0.0"
"@serialport/binding-abstract@^9.0.7": "@serialport/binding-abstract@^9.0.7":
version "9.0.7" version "9.0.7"
@@ -143,17 +150,17 @@
dependencies: dependencies:
nan "^2.14.0" nan "^2.14.0"
"@types/mz@0.0.32": "@types/mz@2.7.3":
version "0.0.32" version "2.7.3"
resolved "https://registry.npmjs.org/@types/mz/-/mz-0.0.32.tgz" resolved "https://registry.yarnpkg.com/@types/mz/-/mz-2.7.3.tgz#e42a21e73f5f9340fe4a176981fafb1eb8cc6c12"
integrity sha512-cy3yebKhrHuOcrJGkfwNHhpTXQLgmXSv1BX+4p32j+VUQ6aP2eJ5cL7OvGcAQx75fCTFaAIIAKewvqL+iwSd4g== integrity sha512-Zp1NUJ4Alh3gaun0a5rkF3DL7b2j1WB6rPPI5h+CJ98sQnxe9qwskClvupz/4bqChGR3L/BRhTjlaOwR+uiZJg==
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/node@*", "@types/node@14.14.31": "@types/node@*", "@types/node@14.14.35":
version "14.14.31" version "14.14.35"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313"
integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==
JSONStream@^1.3.4, JSONStream@^1.3.5: JSONStream@^1.3.4, JSONStream@^1.3.5:
version "1.3.5" version "1.3.5"
@@ -250,9 +257,9 @@ ansistyles@~0.1.3:
resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz" resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz"
integrity sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk= integrity sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=
any-promise@^1.0.0: any-promise@^1.0.0, any-promise@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2: aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2:
@@ -540,14 +547,14 @@ cliui@^5.0.0:
strip-ansi "^5.2.0" strip-ansi "^5.2.0"
wrap-ansi "^5.1.0" wrap-ansi "^5.1.0"
cliui@^6.0.0: cliui@^7.0.2:
version "6.0.0" version "7.0.4"
resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
dependencies: dependencies:
string-width "^4.2.0" string-width "^4.2.0"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
wrap-ansi "^6.2.0" wrap-ansi "^7.0.0"
clone@^1.0.2: clone@^1.0.2:
version "1.0.4" version "1.0.4"
@@ -867,11 +874,6 @@ electron-is-accelerator@^0.1.0:
resolved "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz" resolved "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz"
integrity sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns= integrity sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns=
electron-is-dev@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.2.0.tgz#2e5cea0a1b3ccf1c86f577cee77363ef55deb05e"
integrity sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==
electron-is-dev@^1.1.0: electron-is-dev@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.1.0.tgz" resolved "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.1.0.tgz"
@@ -989,6 +991,11 @@ es6-promisify@^5.0.0:
dependencies: dependencies:
es6-promise "^4.0.3" es6-promise "^4.0.3"
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-string-regexp@^1.0.5: escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -1061,14 +1068,6 @@ find-up@^3.0.0:
dependencies: dependencies:
locate-path "^3.0.0" locate-path "^3.0.0"
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
flush-write-stream@^1.0.0: flush-write-stream@^1.0.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@@ -1196,7 +1195,7 @@ get-caller-file@^1.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
get-caller-file@^2.0.1: get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5" version "2.0.5"
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
@@ -1660,10 +1659,10 @@ keyboardevents-areequal@^0.2.1:
resolved "https://registry.npmjs.org/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz" resolved "https://registry.npmjs.org/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz"
integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw== integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw==
keytar@^7.4.0: keytar@^7.6.0:
version "7.4.0" version "7.6.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.4.0.tgz#0a508d64850ca05aa3ba4127818037d13ca3219f" resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.6.0.tgz#498e796443cb543d31722099443f29d7b5c44100"
integrity sha512-nELmc35YjSE4ZNSFaID/743CgDt/MdV4JLX7rRewAh9mKvU72RtF3uJMY0MdMpwdDYZhmD8FSdRCD1J97lEyVg== integrity sha512-H3cvrTzWb11+iv0NOAnoNAPgEapVZnYLVHZQyxmh7jdmVfR/c0jNNFEZ6AI38W/4DeTGTaY66ZX4Z1SbfKPvCQ==
dependencies: dependencies:
node-addon-api "^3.0.0" node-addon-api "^3.0.0"
prebuild-install "^6.0.0" prebuild-install "^6.0.0"
@@ -1847,13 +1846,6 @@ locate-path@^3.0.0:
p-locate "^3.0.0" p-locate "^3.0.0"
path-exists "^3.0.0" path-exists "^3.0.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
lock-verify@^2.0.2, lock-verify@^2.1.0: lock-verify@^2.0.2, lock-verify@^2.1.0:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.2.1.tgz#81107948c51ed16f97b96ff8b60675affb243fc1" resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.2.1.tgz#81107948c51ed16f97b96ff8b60675affb243fc1"
@@ -2106,10 +2098,10 @@ native-process-working-directory@^1.0.2:
dependencies: dependencies:
node-addon-api "^3.1.0" node-addon-api "^3.1.0"
ngx-toastr@^13.2.0: ngx-toastr@^13.2.1:
version "13.2.0" version "13.2.1"
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-13.2.0.tgz#3ef047c977a7d0fb67fed9338b8f5add3c87b356" resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-13.2.1.tgz#531bc7da6a524a861e0409bd2749878b3b3f8a3f"
integrity sha512-XU+wACX5hxwOJ4BtPMAUExQmYbjfvH3C/R4vcC9QK/dX2Zw+2w9tS9m4W6TUFyR92xZ/tGLBtsqRdrDRn3fJCw== integrity sha512-UAzp7/xWK9IXA2LsOmhpaaIGCqscvJokoQpBNpAMrjEkDeSlFf8PWQAuQY795KW0mJb3qF9UG/s23nsXfMYKmg==
dependencies: dependencies:
tslib "^2.0.0" tslib "^2.0.0"
@@ -2528,7 +2520,7 @@ p-limit@^1.1.0:
dependencies: dependencies:
p-try "^1.0.0" p-try "^1.0.0"
p-limit@^2.0.0, p-limit@^2.2.0: p-limit@^2.0.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
@@ -2549,13 +2541,6 @@ p-locate@^3.0.0:
dependencies: dependencies:
p-limit "^2.0.0" p-limit "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^1.0.0: p-try@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz"
@@ -2633,11 +2618,6 @@ path-exists@^3.0.0:
resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz"
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0: path-is-absolute@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
@@ -2695,28 +2675,7 @@ pkg-up@^2.0.0:
dependencies: dependencies:
find-up "^2.1.0" find-up "^2.1.0"
prebuild-install@^6.0.0: prebuild-install@^6.0.0, prebuild-install@^6.0.1:
version "6.0.0"
resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.0.0.tgz"
integrity sha512-h2ZJ1PXHKWZpp1caLw0oX9sagVpL2YTk+ZwInQbQ3QqNd4J03O6MpFNmMTJlkfgPENWqe5kP0WjQLqz5OjLfsw==
dependencies:
detect-libc "^1.0.3"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
mkdirp-classic "^0.5.3"
napi-build-utils "^1.0.1"
node-abi "^2.7.0"
noop-logger "^0.1.1"
npmlog "^4.0.1"
pump "^3.0.0"
rc "^1.2.7"
simple-get "^3.0.3"
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
which-pm-runs "^1.0.0"
prebuild-install@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d"
integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ== integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ==
@@ -3740,10 +3699,10 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0" string-width "^3.0.0"
strip-ansi "^5.0.0" strip-ansi "^5.0.0"
wrap-ansi@^6.2.0: wrap-ansi@^7.0.0:
version "6.2.0" version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies: dependencies:
ansi-styles "^4.0.0" ansi-styles "^4.0.0"
string-width "^4.1.0" string-width "^4.1.0"
@@ -3790,6 +3749,11 @@ y18n@^4.0.0:
resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz" resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz"
integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yallist@^2.1.2: yallist@^2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
@@ -3808,13 +3772,10 @@ yargs-parser@^15.0.1:
camelcase "^5.0.0" camelcase "^5.0.0"
decamelize "^1.2.0" decamelize "^1.2.0"
yargs-parser@^18.1.2: yargs-parser@^20.2.2:
version "18.1.3" version "20.2.7"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^7.0.0: yargs-parser@^7.0.0:
version "7.0.0" version "7.0.0"
@@ -3840,22 +3801,18 @@ yargs@^14.2.3:
y18n "^4.0.0" y18n "^4.0.0"
yargs-parser "^15.0.1" yargs-parser "^15.0.1"
yargs@^15.4.1: yargs@^16.2.0:
version "15.4.1" version "16.2.0"
resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
dependencies: dependencies:
cliui "^6.0.0" cliui "^7.0.2"
decamelize "^1.2.0" escalade "^3.1.1"
find-up "^4.1.0" get-caller-file "^2.0.5"
get-caller-file "^2.0.1"
require-directory "^2.1.1" require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0" string-width "^4.2.0"
which-module "^2.0.0" y18n "^5.0.5"
y18n "^4.0.0" yargs-parser "^20.2.2"
yargs-parser "^18.1.2"
yargs@^8.0.2: yargs@^8.0.2:
version "8.0.2" version "8.0.2"

View File

@@ -1,57 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 1024 1024" xml:space="preserve" style="enable-background:new 0 0 1024 1024"><style type="text/css">.st0{opacity:.8;fill:#00232e}.st1{fill:url(#SVGID_1_)}.st2{opacity:.16;fill:url(#SVGID_2_)}.st3{fill:url(#SVGID_3_)}.st4{opacity:.16;fill:url(#SVGID_4_)}.st5{fill:url(#SVGID_5_)}.st6{opacity:.15;fill:url(#SVGID_6_)}.st7{fill:url(#SVGID_7_)}</style><polygon points="449.5 645.47 407.51 621.23 533.47 548.5 407.51 475.77 449.5 451.53 617.45 548.5" class="st0"/><g><linearGradient id="SVGID_1_" x1="439.007" x2="627.946" y1="603.039" y2="493.955" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#669abd"/><stop offset="1" style="stop-color:#77dbdb"/></linearGradient><polygon points="449.5 621.22 617.45 524.25 617.45 475.77 449.5 572.75" class="st1"/><linearGradient id="SVGID_2_" x1="522.979" x2="543.974" y1="530.315" y2="566.679" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="449.5 621.22 617.45 524.25 617.45 475.77 449.5 572.75" class="st2"/></g><g><linearGradient id="SVGID_3_" x1="397.01" x2="459.996" y1="566.684" y2="603.049" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="407.51 548.5 407.5 596.99 449.5 621.22 449.5 572.75" class="st3"/><linearGradient id="SVGID_4_" x1="439.001" x2="418.006" y1="566.684" y2="603.049" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="407.51 548.5 407.5 596.99 449.5 621.22 449.5 572.75" class="st4"/></g><g><linearGradient id="SVGID_5_" x1="397.01" x2="522.978" y1="421.226" y2="493.954" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="407.51 403.04 407.5 451.53 491.49 500.01 533.48 475.77" class="st5"/><linearGradient id="SVGID_6_" x1="470.492" x2="449.496" y1="439.407" y2="475.774" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="407.51 403.04 407.5 451.53 491.49 500.01 533.48 475.77" class="st6"/></g><linearGradient id="SVGID_7_" x1="386.512" x2="575.461" y1="512.136" y2="403.047" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ccecff"/><stop offset="1" style="stop-color:#9feced"/></linearGradient><polygon points="449.5 572.74 407.51 548.5 533.48 475.77 407.51 403.04 449.49 378.8 617.45 475.77" class="st7"/></svg>
<!-- Generator: Adobe Illustrator 23.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.8;fill:#00232E;}
.st1{fill:url(#SVGID_1_);}
.st2{opacity:0.16;fill:url(#SVGID_2_);}
.st3{fill:url(#SVGID_3_);}
.st4{opacity:0.16;fill:url(#SVGID_4_);}
.st5{fill:url(#SVGID_5_);}
.st6{opacity:0.15;fill:url(#SVGID_6_);}
.st7{fill:url(#SVGID_7_);}
</style>
<polygon class="st0" points="449.5,645.47 407.51,621.23 533.47,548.5 407.51,475.77 449.5,451.53 617.45,548.5 "/>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="439.0065" y1="603.0394" x2="627.9464" y2="493.9549">
<stop offset="0" style="stop-color:#669ABD"/>
<stop offset="1" style="stop-color:#77DBDB"/>
</linearGradient>
<polygon class="st1" points="449.5,621.22 617.45,524.25 617.45,475.77 449.5,572.75 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="522.9788" y1="530.3148" x2="543.9741" y2="566.6795">
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<polygon class="st2" points="449.5,621.22 617.45,524.25 617.45,475.77 449.5,572.75 "/>
</g>
<g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="397.0101" y1="566.6837" x2="459.9963" y2="603.0487">
<stop offset="0" style="stop-color:#6A8FAD"/>
<stop offset="1" style="stop-color:#669ABD"/>
</linearGradient>
<polygon class="st3" points="407.51,548.5 407.5,596.99 449.5,621.22 449.5,572.75 "/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="439.0009" y1="566.6838" x2="418.0056" y2="603.0486">
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<polygon class="st4" points="407.51,548.5 407.5,596.99 449.5,621.22 449.5,572.75 "/>
</g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="397.0101" y1="421.2265" x2="522.9781" y2="493.9542">
<stop offset="0" style="stop-color:#6A8FAD"/>
<stop offset="1" style="stop-color:#669ABD"/>
</linearGradient>
<polygon class="st5" points="407.51,403.04 407.5,451.53 491.49,500.01 533.48,475.77 "/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="470.4924" y1="439.4067" x2="449.4958" y2="475.774">
<stop offset="0.5588" style="stop-color:#000000;stop-opacity:0"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<polygon class="st6" points="407.51,403.04 407.5,451.53 491.49,500.01 533.48,475.77 "/>
</g>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="386.5123" y1="512.136" x2="575.4605" y2="403.0467">
<stop offset="0" style="stop-color:#CCECFF"/>
<stop offset="1" style="stop-color:#9FECED"/>
</linearGradient>
<polygon class="st7" points="449.5,572.74 407.51,548.5 533.48,475.77 407.51,403.04 449.49,378.8 617.45,475.77 "/>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
docs/readme-ssh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

BIN
docs/readme-terminal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

View File

@@ -31,6 +31,8 @@ files:
extraResources: extraResources:
- builtin-plugins - builtin-plugins
- extras - extras
asarUnpack:
- dist/*.map
publish: publish:
- provider: github - provider: github

View File

@@ -1,14 +1,14 @@
{ {
"devDependencies": { "devDependencies": {
"@fortawesome/fontawesome-free": "^5.15.2", "@fortawesome/fontawesome-free": "^5.15.3",
"@sentry/cli": "^1.63.0", "@sentry/cli": "^1.63.1",
"@sentry/electron": "^2.4.0", "@sentry/electron": "^2.4.0",
"@terminus-term/to-string-loader": "1.1.7-beta.1", "@terminus-term/to-string-loader": "1.1.7-beta.1",
"@types/electron-config": "^3.2.2", "@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0", "@types/electron-debug": "^2.1.0",
"@types/fs-extra": "^8.1.1", "@types/fs-extra": "^9.0.9",
"@types/js-yaml": "^4.0.0", "@types/js-yaml": "^4.0.0",
"@types/node": "14.14.31", "@types/node": "14.14.35",
"@types/webpack-env": "^1.16.0", "@types/webpack-env": "^1.16.0",
"@typescript-eslint/eslint-plugin": "^4.14.1", "@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.17.0", "@typescript-eslint/parser": "^4.17.0",
@@ -17,8 +17,8 @@
"compare-versions": "^3.6.0", "compare-versions": "^3.6.0",
"core-js": "^3.9.1", "core-js": "^3.9.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "5.0.1", "css-loader": "5.2.0",
"electron": "12.0.1", "electron": "12.0.4",
"electron-builder": "22.10.5", "electron-builder": "22.10.5",
"electron-download": "^4.1.1", "electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0", "electron-installer-snap": "^5.1.0",
@@ -32,12 +32,12 @@
"json-loader": "0.5.7", "json-loader": "0.5.7",
"lru-cache": "^6.0.0", "lru-cache": "^6.0.0",
"macos-release": "^2.4.1", "macos-release": "^2.4.1",
"node-abi": "^2.19.3", "node-abi": "^2.21.0",
"node-gyp": "^7.1.2", "node-gyp": "^7.1.2",
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"npmlog": "4.1.2", "npmlog": "4.1.2",
"npx": "^10.2.2", "npx": "^10.2.2",
"patch-package": "^6.2.2", "patch-package": "^6.4.7",
"pug": "^3.0.2", "pug": "^3.0.2",
"pug-html-loader": "1.1.5", "pug-html-loader": "1.1.5",
"pug-lint": "^2.6.0", "pug-lint": "^2.6.0",
@@ -52,12 +52,13 @@
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"svg-inline-loader": "^0.8.2", "svg-inline-loader": "^0.8.2",
"tslib": "^2.1.0", "tslib": "^2.1.0",
"typedoc": "^0.20.28", "typedoc": "^0.20.32",
"typescript": "^3.9.9", "typescript": "^3.9.9",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"val-loader": "3.0.0", "val-loader": "3.1.0",
"webpack": "^5.18.0", "webpack": "^5.31.0",
"webpack-cli": "^4.5.0", "webpack-bundle-analyzer": "^4.4.0",
"webpack-cli": "^4.6.0",
"yaml-loader": "0.6.0" "yaml-loader": "0.6.0"
}, },
"resolutions": { "resolutions": {
@@ -68,7 +69,7 @@
"build": "npm run build:typings && webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js && webpack --color --config terminus-serial/webpack.config.js", "build": "npm run build:typings && webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js && webpack --color --config terminus-serial/webpack.config.js",
"build:typings": "node scripts/build-typings.js", "build:typings": "node scripts/build-typings.js",
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch", "watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
"start": "cross-env TERMINUS_DEV=1 electron app --debug", "start": "cross-env TERMINUS_DEV=1 electron app --debug --inspect",
"start:prod": "electron app --debug", "start:prod": "electron app --debug",
"prod": "cross-env TERMINUS_DEV=1 electron app", "prod": "cross-env TERMINUS_DEV=1 electron app",
"docs": "typedoc --out docs/api --tsconfig terminus-core/src/tsconfig.typings.json terminus-core/src/index.ts && typedoc --out docs/api/terminal --tsconfig terminus-terminal/tsconfig.typings.json terminus-terminal/src/index.ts && typedoc --out docs/api/settings --tsconfig terminus-settings/tsconfig.typings.json terminus-settings/src/index.ts", "docs": "typedoc --out docs/api --tsconfig terminus-core/src/tsconfig.typings.json terminus-core/src/index.ts && typedoc --out docs/api/terminal --tsconfig terminus-terminal/tsconfig.typings.json terminus-terminal/src/index.ts && typedoc --out docs/api/settings --tsconfig terminus-settings/tsconfig.typings.json terminus-settings/src/index.ts",

View File

@@ -17,11 +17,13 @@
"author": "Eugene Pankov", "author": "Eugene Pankov",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@electron/remote": "^1.0.4",
"@types/js-yaml": "^4.0.0", "@types/js-yaml": "^4.0.0",
"@types/shell-escape": "^0.2.0", "@types/shell-escape": "^0.2.0",
"@types/winston": "^2.3.6", "@types/winston": "^2.3.6",
"axios": "^0.21.1", "axios": "^0.21.1",
"bootstrap": "^4.1.3", "bootstrap": "^4.1.3",
"clone-deep": "^4.0.1",
"core-js": "^3.1.2", "core-js": "^3.1.2",
"deepmerge": "^4.1.1", "deepmerge": "^4.1.1",
"electron-updater": "^4.0.6", "electron-updater": "^4.0.6",

View File

@@ -21,4 +21,5 @@ export { NotificationsService } from '../services/notifications.service'
export { ShellIntegrationService } from '../services/shellIntegration.service' export { ShellIntegrationService } from '../services/shellIntegration.service'
export { ThemesService } from '../services/themes.service' export { ThemesService } from '../services/themes.service'
export { TabsService } from '../services/tabs.service' export { TabsService } from '../services/tabs.service'
export { UpdaterService } from '../services/updater.service'
export * from '../utils' export * from '../utils'

View File

@@ -1,3 +1,4 @@
import deepClone from 'clone-deep'
import { TabComponentType } from '../services/tabs.service' import { TabComponentType } from '../services/tabs.service'
export interface RecoveredTab { export interface RecoveredTab {
@@ -35,10 +36,26 @@ export interface RecoveryToken {
* ``` * ```
*/ */
export abstract class TabRecoveryProvider { export abstract class TabRecoveryProvider {
/**
* @param recoveryToken a recovery token found in the saved tabs list
* @returns [[boolean]] whether this [[TabRecoveryProvider]] can recover a tab from this token
*/
abstract async applicableTo (recoveryToken: RecoveryToken): Promise<boolean>
/** /**
* @param recoveryToken a recovery token found in the saved tabs list * @param recoveryToken a recovery token found in the saved tabs list
* @returns [[RecoveredTab]] descriptor containing tab type and component inputs * @returns [[RecoveredTab]] descriptor containing tab type and component inputs
* or `null` if this token is from a different tab type or is not supported * or `null` if this token is from a different tab type or is not supported
*/ */
abstract async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> abstract async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab>
/**
* @param recoveryToken a recovery token found in the saved tabs list
* @returns [[RecoveryToken]] a new recovery token to create the duplicate tab from
*
* The default implementation just returns a deep copy of the original token
*/
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return deepClone(recoveryToken)
}
} }

View File

@@ -25,7 +25,6 @@ title-bar(
[index]='idx', [index]='idx',
[tab]='tab', [tab]='tab',
[active]='tab == app.activeTab', [active]='tab == app.activeTab',
[hasActivity]='tab.activity$|async',
@animateTab, @animateTab,
[@.disabled]='hasVerticalTabs()', [@.disabled]='hasVerticalTabs()',
(click)='app.selectTab(tab)', (click)='app.selectTab(tab)',
@@ -86,7 +85,7 @@ title-bar(
button.btn.btn-secondary.btn-tab-bar.btn-update( button.btn.btn-secondary.btn-tab-bar.btn-update(
*ngIf='updatesAvailable', *ngIf='updatesAvailable',
title='Update available - Click to install', title='Update available - Click to install',
(click)='updateApp()', (click)='updater.update()',
[fastHtmlBind]='updateIcon' [fastHtmlBind]='updateIcon'
) )
@@ -95,12 +94,14 @@ title-bar(
&& (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)', && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
) )
start-page(*ngIf='ready && app.tabs.length == 0') .content
start-page.content-tab.content-tab-active(*ngIf='ready && app.tabs.length == 0')
tab-body( tab-body.content-tab(
*ngFor='let tab of unsortedTabs', *ngFor='let tab of unsortedTabs',
[active]='tab == app.activeTab', [class.content-tab-active]='tab == app.activeTab',
[tab]='tab', [active]='tab == app.activeTab',
) [tab]='tab',
)
ng-template(ngbModalContainer) ng-template(ngbModalContainer)

View File

@@ -134,9 +134,24 @@ $side-tab-width: 200px;
} }
} }
.tabs-content { .content {
flex: auto; flex: 1 1 0;
display: flex; position: relative;
min-height: 0;
min-width: 0;
> .content-tab {
position: absolute;
top: 0;
width: 100%;
height: 100%;
left: 100%;
&.content-tab-active {
left: 0;
}
}
} }
hotkey-hint { hotkey-hint {

View File

@@ -68,7 +68,6 @@ export class AppRootComponent {
private constructor ( private constructor (
private docking: DockingService, private docking: DockingService,
private electron: ElectronService,
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
private updater: UpdaterService, private updater: UpdaterService,
private touchbar: TouchbarService, private touchbar: TouchbarService,
@@ -76,6 +75,7 @@ export class AppRootComponent {
public config: ConfigService, public config: ConfigService,
public app: AppService, public app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[], @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
electron: ElectronService,
log: LogService, log: LogService,
ngbModal: NgbModal, ngbModal: NgbModal,
_themes: ThemesService, _themes: ThemesService,
@@ -136,9 +136,13 @@ export class AppRootComponent {
ngbModal.open(SafeModeModalComponent) ngbModal.open(SafeModeModalComponent)
} }
this.updater.check().then(available => { setInterval(() => {
this.updatesAvailable = available if (this.config.store.enableAutomaticUpdates) {
}) this.updater.check().then(available => {
this.updatesAvailable = available
})
}
}, 3600 * 12)
this.touchbar.update() this.touchbar.update()
@@ -190,20 +194,6 @@ export class AppRootComponent {
return this.config.store.appearance.tabsLocation === 'left' || this.config.store.appearance.tabsLocation === 'right' return this.config.store.appearance.tabsLocation === 'left' || this.config.store.appearance.tabsLocation === 'right'
} }
async updateApp () {
if ((await this.electron.showMessageBox(
this.hostApp.getWindow(),
{
type: 'warning',
message: 'Installing the update will close all tabs and restart Terminus.',
buttons: ['Cancel', 'Update'],
defaultId: 1,
}
)).response === 1) {
this.updater.update()
}
}
onTabDragStart () { onTabDragStart () {
this.tabsDragging = true this.tabsDragging = true
} }

View File

@@ -256,7 +256,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** @hidden */ /** @hidden */
async ngAfterViewInit (): Promise<void> { async ngAfterViewInit (): Promise<void> {
if (this._recoveredState) { if (this._recoveredState) {
await this.recoverContainer(this.root, this._recoveredState) await this.recoverContainer(this.root, this._recoveredState, this._recoveredState.duplicate)
this.layout() this.layout()
setTimeout(() => { setTimeout(() => {
if (this.hasFocus) { if (this.hasFocus) {
@@ -505,6 +505,9 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
if (tab.title) { if (tab.title) {
this.setTitle(tab.title) this.setTitle(tab.title)
} }
tab.recoveryStateChangedHint$.subscribe(() => {
this.recoveryStateChangedHint.next()
})
tab.destroyed$.subscribe(() => { tab.destroyed$.subscribe(() => {
this.removeTab(tab) this.removeTab(tab)
}) })
@@ -567,7 +570,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}) })
} }
private async recoverContainer (root: SplitContainer, state: any) { private async recoverContainer (root: SplitContainer, state: any, duplicate = false) {
const children: (SplitContainer | BaseTabComponent)[] = [] const children: (SplitContainer | BaseTabComponent)[] = []
root.orientation = state.orientation root.orientation = state.orientation
root.ratios = state.ratios root.ratios = state.ratios
@@ -575,10 +578,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
for (const childState of state.children) { for (const childState of state.children) {
if (childState.type === 'app:split-tab') { if (childState.type === 'app:split-tab') {
const child = new SplitContainer() const child = new SplitContainer()
await this.recoverContainer(child, childState) await this.recoverContainer(child, childState, duplicate)
children.push(child) children.push(child)
} else { } else {
const recovered = await this.tabRecovery.recoverTab(childState) const recovered = await this.tabRecovery.recoverTab(childState, duplicate)
if (recovered) { if (recovered) {
const tab = this.tabsService.create(recovered.type, recovered.options) const tab = this.tabsService.create(recovered.type, recovered.options)
children.push(tab) children.push(tab)
@@ -599,13 +602,21 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class SplitTabRecoveryProvider extends TabRecoveryProvider { export class SplitTabRecoveryProvider extends TabRecoveryProvider {
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> { async applicableTo (recoveryToken: RecoveryToken): Promise<boolean> {
if (recoveryToken.type === 'app:split-tab') { return recoveryToken.type === 'app:split-tab'
return { }
type: SplitTabComponent,
options: { _recoveredState: recoveryToken }, async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab> {
} return {
type: SplitTabComponent,
options: { _recoveredState: recoveryToken },
}
}
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
duplicate: true,
} }
return null
} }
} }

View File

@@ -1,15 +1,11 @@
:host { :host {
display: none; display: flex;
flex: auto; flex: auto;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
&.active { >* {
display: flex; flex: auto;
>* {
flex: auto;
}
} }
> perfect-scrollbar { > perfect-scrollbar {

View File

@@ -1,7 +1,10 @@
.colorbar([style.background-color]='tab.color', *ngIf='tab.color != null')
.progressbar([style.width]='progress + "%"', *ngIf='progress != null') .progressbar([style.width]='progress + "%"', *ngIf='progress != null')
.index(*ngIf='!config.store.terminal.hideTabIndex', .activity-indicator(*ngIf='tab.activity$|async')
#handle,
[style.background-color]='tab.color', .index(*ngIf='!config.store.terminal.hideTabIndex', #handle) {{index + 1}}
) {{index + 1}} .name(
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}} [title]='tab.customTitle || tab.title',
[class.no-hover]='config.store.terminal.hideCloseButton'
) {{tab.customTitle || tab.title}}
button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') &times; button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') &times;

View File

@@ -4,6 +4,8 @@ $tabs-height: 38px;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
> * { cursor: pointer; }
flex: 1000 1 200px; flex: 1000 1 200px;
width: 200px; width: 200px;
padding: 0 10px; padding: 0 10px;
@@ -76,7 +78,7 @@ $tabs-height: 38px;
} }
} }
&:hover .name { &:hover .name:not(.no-hover) {
-webkit-mask-image: linear-gradient(black 0 0), linear-gradient(to left, transparent 0%, black 100%); -webkit-mask-image: linear-gradient(black 0 0), linear-gradient(to left, transparent 0%, black 100%);
-webkit-mask-size: calc(100% - 60px) auto, 60px auto; -webkit-mask-size: calc(100% - 60px) auto, 60px auto;
-webkit-mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
@@ -101,8 +103,30 @@ $tabs-height: 38px;
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
height: 5px; height: 3px;
z-index: -1; z-index: 1;
transition: 0.25s width; transition: 0.25s width;
} }
.colorbar {
position: absolute;
left: 0;
bottom: 0;
height: 3px;
width: 100%;
z-index: 1;
}
&.active .activity-indicator {
display: none;
}
.activity-indicator {
position: absolute;
left: 10px;
right: 10px;
bottom: 4px;
height: 2px;
z-index: -1;
}
} }

View File

@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import type { MenuItemConstructorOptions } from 'electron' import type { MenuItemConstructorOptions } from 'electron'
import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef } from '@angular/core' import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef, NgZone } from '@angular/core'
import { SortableComponent } from 'ng2-dnd' import { SortableComponent } from 'ng2-dnd'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider' import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider'
@@ -26,7 +26,6 @@ export interface SortableComponentProxy {
export class TabHeaderComponent { export class TabHeaderComponent {
@Input() index: number @Input() index: number
@Input() @HostBinding('class.active') active: boolean @Input() @HostBinding('class.active') active: boolean
@Input() @HostBinding('class.has-activity') hasActivity: boolean
@Input() tab: BaseTabComponent @Input() tab: BaseTabComponent
@Input() progress: number|null @Input() progress: number|null
@ViewChild('handle') handle?: ElementRef @ViewChild('handle') handle?: ElementRef
@@ -38,6 +37,7 @@ export class TabHeaderComponent {
private hostApp: HostAppService, private hostApp: HostAppService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
private zone: NgZone,
@Inject(SortableComponent) private parentDraggable: SortableComponentProxy, @Inject(SortableComponent) private parentDraggable: SortableComponentProxy,
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[], @Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
) { ) {
@@ -53,7 +53,9 @@ export class TabHeaderComponent {
ngOnInit () { ngOnInit () {
this.tab.progress$.subscribe(progress => { this.tab.progress$.subscribe(progress => {
this.progress = progress this.zone.run(() => {
this.progress = progress
})
}) })
} }
@@ -105,7 +107,7 @@ export class TabHeaderComponent {
if ($event.which === 3) { if ($event.which === 3) {
$event.preventDefault() $event.preventDefault()
const contextMenu = this.electron.remote.Menu.buildFromTemplate(await this.buildContextMenu()) const contextMenu = this.electron.Menu.buildFromTemplate(await this.buildContextMenu())
contextMenu.popup({ contextMenu.popup({
x: $event.pageX, x: $event.pageX,

View File

@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { App, IpcRenderer, Shell, Dialog, Clipboard, GlobalShortcut, Screen, Remote, AutoUpdater, TouchBar, BrowserWindow, Menu, MenuItem, NativeImage, MessageBoxOptions } from 'electron' import { App, IpcRenderer, Shell, Dialog, Clipboard, GlobalShortcut, Screen, Remote, AutoUpdater, TouchBar, BrowserWindow, Menu, MenuItem, NativeImage, MessageBoxOptions } from 'electron'
import * as remote from '@electron/remote'
export interface MessageBoxResponse { export interface MessageBoxResponse {
response: number response: number
@@ -17,30 +18,31 @@ export class ElectronService {
nativeImage: typeof NativeImage nativeImage: typeof NativeImage
screen: Screen screen: Screen
remote: Remote remote: Remote
process: any
autoUpdater: AutoUpdater autoUpdater: AutoUpdater
TouchBar: typeof TouchBar TouchBar: typeof TouchBar
BrowserWindow: typeof BrowserWindow BrowserWindow: typeof BrowserWindow
Menu: typeof Menu Menu: typeof Menu
MenuItem: typeof MenuItem MenuItem: typeof MenuItem
private electron: any
/** @hidden */ /** @hidden */
private constructor () { private constructor () {
this.electron = require('electron') const electron = require('electron')
this.remote = this.electron.remote this.shell = electron.shell
this.app = this.remote.app this.clipboard = electron.clipboard
this.screen = this.remote.screen this.ipcRenderer = electron.ipcRenderer
this.dialog = this.remote.dialog
this.shell = this.electron.shell this.process = remote.process
this.clipboard = this.electron.clipboard this.app = remote.app
this.ipcRenderer = this.electron.ipcRenderer this.screen = remote.screen
this.globalShortcut = this.remote.globalShortcut this.dialog = remote.dialog
this.nativeImage = this.remote.nativeImage this.globalShortcut = remote.globalShortcut
this.autoUpdater = this.remote.autoUpdater this.nativeImage = remote.nativeImage
this.TouchBar = this.remote.TouchBar this.autoUpdater = remote.autoUpdater
this.BrowserWindow = this.remote.BrowserWindow this.TouchBar = remote.TouchBar
this.Menu = this.remote.Menu this.BrowserWindow = remote.BrowserWindow
this.MenuItem = this.remote.MenuItem this.Menu = remote.Menu
this.MenuItem = remote.MenuItem
} }
async showMessageBox ( async showMessageBox (

View File

@@ -10,6 +10,8 @@ export const altKeyName = {
linux: 'Alt', linux: 'Alt',
}[process.platform] }[process.platform]
const REGEX_LATIN_KEYNAME = /^[A-Za-z]$/
export function stringifyKeySequence (events: KeyboardEvent[]): string[] { export function stringifyKeySequence (events: KeyboardEvent[]): string[] {
const items: string[] = [] const items: string[] = []
events = events.slice() events = events.slice()
@@ -37,23 +39,29 @@ export function stringifyKeySequence (events: KeyboardEvent[]): string[] {
} }
let key = event.code let key = event.code
key = key.replace('Key', '') if (REGEX_LATIN_KEYNAME.test(event.key)) {
key = key.replace('Arrow', '') // Handle Dvorak etc via the reported "character" instead of the scancode
key = key.replace('Digit', '') key = event.key.toUpperCase()
key = { } else {
Comma: ',', key = key.replace('Key', '')
Period: '.', key = key.replace('Arrow', '')
Slash: '/', key = key.replace('Digit', '')
Backslash: '\\', key = {
IntlBackslash: '\\', Comma: ',',
Backquote: '`', Period: '.',
Minus: '-', Slash: '/',
Equal: '=', Backslash: '\\',
Semicolon: ';', IntlBackslash: '\\',
Quote: '\'', Backquote: '`',
BracketLeft: '[', Minus: '-',
BracketRight: ']', Equal: '=',
}[key] || key Semicolon: ';',
Quote: '\'',
BracketLeft: '[',
BracketRight: ']',
}[key] || key
}
itemKeys.push(key) itemKeys.push(key)
items.push(itemKeys.join('-')) items.push(itemKeys.join('-'))
} }

View File

@@ -40,16 +40,20 @@ export class TabRecoveryService {
return token return token
} }
async recoverTab (token: RecoveryToken): Promise<RecoveredTab|null> { async recoverTab (token: RecoveryToken, duplicate = false): Promise<RecoveredTab|null> {
for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) { for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) {
try { try {
const tab = await provider.recover(token) if (!await provider.applicableTo(token)) {
if (tab !== null) { continue
tab.options = tab.options || {}
tab.options.color = token.tabColor ?? null
tab.options.title = token.tabTitle || ''
return tab
} }
if (duplicate) {
token = provider.duplicate(token)
}
const tab = await provider.recover(token)
tab.options = tab.options || {}
tab.options.color = token.tabColor ?? null
tab.options.title = token.tabTitle || ''
return tab
} catch (error) { } catch (error) {
this.logger.warn('Tab recovery crashed:', token, provider, error) this.logger.warn('Tab recovery crashed:', token, provider, error)
} }

View File

@@ -34,7 +34,7 @@ export class TabsService {
if (!token) { if (!token) {
return null return null
} }
const dup = await this.tabRecovery.recoverTab(token) const dup = await this.tabRecovery.recoverTab(token, true)
if (dup) { if (dup) {
return this.create(dup.type, dup.options) return this.create(dup.type, dup.options)
} }

View File

@@ -4,6 +4,7 @@ import { Injectable } from '@angular/core'
import { Logger, LogService } from './log.service' import { Logger, LogService } from './log.service'
import { ElectronService } from './electron.service' import { ElectronService } from './electron.service'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import { HostAppService } from './hostApp.service'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest' const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
@@ -17,8 +18,9 @@ export class UpdaterService {
private constructor ( private constructor (
log: LogService, log: LogService,
config: ConfigService,
private electron: ElectronService, private electron: ElectronService,
private config: ConfigService, private hostApp: HostAppService,
) { ) {
this.logger = log.create('updater') this.logger = log.create('updater')
@@ -58,10 +60,42 @@ export class UpdaterService {
} }
async check (): Promise<boolean> { async check (): Promise<boolean> {
if (!this.config.store.enableAutomaticUpdates) { if (this.electronUpdaterAvailable) {
return false return new Promise((resolve, reject) => {
} // eslint-disable-next-line @typescript-eslint/init-declarations, prefer-const
if (!this.electronUpdaterAvailable) { let cancel
const onNoUpdate = () => {
cancel()
resolve(false)
}
const onUpdate = () => {
cancel()
resolve(this.downloaded)
}
const onError = (err) => {
cancel()
reject(err)
}
cancel = () => {
this.electron.autoUpdater.off('error', onError)
this.electron.autoUpdater.off('update-not-available', onNoUpdate)
this.electron.autoUpdater.off('update-available', onUpdate)
}
this.electron.autoUpdater.on('error', onError)
this.electron.autoUpdater.on('update-not-available', onNoUpdate)
this.electron.autoUpdater.on('update-available', onUpdate)
this.electron.autoUpdater.checkForUpdates()
})
this.electron.autoUpdater.on('update-available', () => {
this.logger.info('Update available')
})
this.electron.autoUpdater.once('update-not-available', () => {
this.logger.info('No updates')
})
} else {
this.logger.debug('Checking for updates through fallback method.') this.logger.debug('Checking for updates through fallback method.')
const response = await axios.get(UPDATES_URL) const response = await axios.get(UPDATES_URL)
const data = response.data const data = response.data
@@ -81,8 +115,18 @@ export class UpdaterService {
if (!this.electronUpdaterAvailable) { if (!this.electronUpdaterAvailable) {
this.electron.shell.openExternal(this.updateURL) this.electron.shell.openExternal(this.updateURL)
} else { } else {
await this.downloaded if ((await this.electron.showMessageBox(
this.electron.autoUpdater.quitAndInstall() this.hostApp.getWindow(),
{
type: 'warning',
message: 'Installing the update will close all tabs and restart Terminus.',
buttons: ['Cancel', 'Update'],
defaultId: 1,
}
)).response === 1) {
await this.downloaded
this.electron.autoUpdater.quitAndInstall()
}
} }
} }
} }

View File

@@ -108,7 +108,7 @@ window-controls {
button:hover { button:hover {
background: rgba($black, 0.125); background: rgba($black, 0.125);
svg { svg {
fill: $black; fill: $black;
} }
@@ -132,8 +132,6 @@ body {
app-root { app-root {
&> .content { &> .content {
.tab-bar { .tab-bar {
height: 40px;
.btn-tab-bar { .btn-tab-bar {
background: transparent; background: transparent;
line-height: 42px; line-height: 42px;
@@ -171,6 +169,10 @@ app-root {
background: $blue; background: $blue;
} }
.activity-indicator {
background:rgba(0, 0, 0, 0.2);
}
&.active { &.active {
color: $black; color: $black;
background: $content-bg; background: $content-bg;
@@ -192,10 +194,6 @@ app-root {
&.active { &.active {
border-bottom-color: transparent; border-bottom-color: transparent;
} }
&.has-activity:not(.active) {
background: linear-gradient(to bottom, rgba(208, 0, 0, 0) 95%, #36beff 96%);
}
} }
} }
@@ -210,10 +208,6 @@ app-root {
&.active { &.active {
margin-top: -1px; margin-top: -1px;
} }
&.has-activity:not(.active) {
background: linear-gradient(to top, rgba(208, 0, 0, 0) 95%, #36beff 96%);
}
} }
} }
} }
@@ -230,11 +224,10 @@ tab-body {
background: $content-bg; background: $content-bg;
} }
settings-tab > ngb-tabset { settings-tab > .content {
border-right: 1px solid $body-bg;
& > .nav { & > .nav {
background: rgba(0, 0, 0, 0.25); background: rgba(0, 0, 0, 0.25);
border-right: 1px solid $body-bg;
& > .nav-item > .nav-link { & > .nav-item > .nav-link {
border: none; border: none;
@@ -317,10 +310,6 @@ hotkey-input-modal {
} }
} }
ngb-tabset .tab-content {
padding-top: 20px;
}
[ngbradiogroup] > label.active { [ngbradiogroup] > label.active {
background: $blue; background: $blue;
} }

View File

@@ -76,6 +76,10 @@ app-root {
background: $green; background: $green;
} }
.activity-indicator {
background:rgba(255, 255, 255, 0.2);
}
&.active { &.active {
color: white; color: white;
background: $content-bg; background: $content-bg;
@@ -97,10 +101,6 @@ app-root {
&.active { &.active {
border-bottom-color: transparent; border-bottom-color: transparent;
} }
&.has-activity:not(.active) {
background: linear-gradient(to bottom, rgba(208, 0, 0, 0) 95%, #1aa99c 100%);
}
} }
} }
@@ -115,10 +115,6 @@ app-root {
&.active { &.active {
margin-top: -1px; margin-top: -1px;
} }
&.has-activity:not(.active) {
background: linear-gradient(to top, rgba(208, 0, 0, 0) 95%, #1aa99c 100%);
}
} }
} }
} }
@@ -135,30 +131,6 @@ tab-body {
background: $content-bg; background: $content-bg;
} }
settings-tab > ngb-tabset {
border-right: 1px solid $body-bg;
& > .nav {
background: rgba(0, 0, 0, 0.25);
flex-shrink: 0;
& > .nav-item > .nav-link {
border: none;
padding: 10px 50px 10px 20px;
font-size: 14px;
border-radius: 0;
&:not(.active) {
color: $body-color;
&:hover {
color: $white;
}
}
}
}
}
multi-hotkey-input { multi-hotkey-input {
.item { .item {
background: $body-bg2; background: $body-bg2;
@@ -221,10 +193,6 @@ hotkey-input-modal {
margin-bottom: 2px; margin-bottom: 2px;
} }
ngb-tabset .tab-content {
padding-top: 20px;
}
[ngbradiogroup] > label.active { [ngbradiogroup] > label.active {
background: $blue; background: $blue;
} }
@@ -321,7 +289,7 @@ checkbox i.on {
} }
search-panel { search-panel {
background: rgba(39, 49, 60, 0.65) !important; background: rgba(39, 49, 60, 0.95) !important;
} }

View File

@@ -11,6 +11,11 @@
enabled "2.0.x" enabled "2.0.x"
kuler "^2.0.0" kuler "^2.0.0"
"@electron/remote@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-1.0.4.tgz#f1c8cf3560bab762b462bfae9991919cced8bc33"
integrity sha512-kguDJRhL3ZynHrkbX8Tr7xoAzGsNgh4eqXkycXb6cgXbOgehGqkBVe+MnjSVMXz3QJykerGKPy28gqcM7AFGYw==
"@types/js-yaml@^4.0.0": "@types/js-yaml@^4.0.0":
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.0.tgz#d1a11688112091f2c711674df3a65ea2f47b5dfb" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.0.tgz#d1a11688112091f2c711674df3a65ea2f47b5dfb"
@@ -75,6 +80,15 @@ builder-util-runtime@8.7.3:
debug "^4.3.2" debug "^4.3.2"
sax "^1.2.4" sax "^1.2.4"
clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
dependencies:
is-plain-object "^2.0.4"
kind-of "^6.0.2"
shallow-clone "^3.0.0"
color-convert@^1.9.1: color-convert@^1.9.1:
version "1.9.3" version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -233,6 +247,13 @@ is-arrayish@^0.3.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
dependencies:
isobject "^3.0.1"
is-stream@^2.0.0: is-stream@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
@@ -243,6 +264,11 @@ isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
js-yaml@^4.0.0: js-yaml@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
@@ -259,6 +285,11 @@ jsonfile@^6.0.1:
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.6" graceful-fs "^4.1.6"
kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
kuler@^2.0.0: kuler@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
@@ -384,6 +415,13 @@ semver@^7.3.4:
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
shallow-clone@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
dependencies:
kind-of "^6.0.2"
shell-escape@^0.2.0: shell-escape@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133" resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133"

View File

@@ -57,6 +57,7 @@ export class PluginManagerService {
}))), }))),
map(plugins => plugins.filter(x => x.packageName.startsWith(NAME_PREFIX))), map(plugins => plugins.filter(x => x.packageName.startsWith(NAME_PREFIX))),
map(plugins => plugins.filter(x => !BLACKLIST.includes(x.packageName))), map(plugins => plugins.filter(x => !BLACKLIST.includes(x.packageName))),
map(plugins => plugins.sort((a, b) => a.name.localeCompare(b.name))),
) )
} }

View File

@@ -1,8 +1,8 @@
.modal-body .modal-body
ngb-tabset([activeId]='basic') ul.nav-tabs(ngbNav, #nav='ngbNav')
ngb-tab(id='basic') li(ngbNavItem)
ng-template(ngbTabTitle) General a(ngbNavLink) General
ng-template(ngbTabContent) ng-template(ngbNavContent)
.form-group .form-group
label Name label Name
input.form-control( input.form-control(
@@ -90,9 +90,9 @@
) )
option([ngValue]='mode.key', *ngFor='let mode of newlineModes') {{mode.name}} option([ngValue]='mode.key', *ngFor='let mode of newlineModes') {{mode.name}}
ngb-tab(id='advanced') li(ngbNavItem)
ng-template(ngbTabTitle) Advanced a(ngbNavLink) Advanced
ng-template(ngbTabContent) ng-template(ngbNavContent)
.form-line .form-line
.header .header
.title Tab color .title Tab color
@@ -150,9 +150,9 @@
.title Xany .title Xany
toggle([(ngModel)]='connection.xany') toggle([(ngModel)]='connection.xany')
ngb-tab(id='scripts') li(ngbNavItem)
ng-template(ngbTabTitle) Login scripts a(ngbNavLink) Login scripts
ng-template(ngbTabContent) ng-template(ngbNavContent)
table(*ngIf='connection.scripts.length > 0') table(*ngIf='connection.scripts.length > 0')
tr tr
th String to expect th String to expect
@@ -192,6 +192,8 @@
i.fas.fa-plus i.fas.fa-plus
span New item span New item
div([ngbNavOutlet]='nav')
.modal-footer .modal-footer
button.btn.btn-outline-primary((click)='save()') Save button.btn.btn-outline-primary((click)='save()') Save
button.btn.btn-outline-danger((click)='cancel()') Cancel button.btn.btn-outline-danger((click)='cancel()') Cancel

View File

@@ -1,27 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg3749" width="32" height="16" version="1.1" viewBox="0 0 32 16"><g id="g3747" transform="matrix(0.48599086,0,0,0.48599086,0.50191451,-0.299629)" style="fill:none;fill-rule:evenodd"><g id="g3741" transform="translate(-292.02353,-314.25882)" style="fill:#fff"><path style="fill-rule:nonzero;stroke-width:.10270619" id="path3739" d="M 16.007812,0 3.2929688,0.03515625 2.2324219,0.40234375 C 0.91449728,1.1071083 0,2.575555 0,3.9863281 c 0,1.2651363 1.3074352,7.8137089 1.7402344,8.7167969 0.553077,1.153703 1.988134,2.456836 3.234375,2.9375 0.8530743,0.328933 1.4753185,0.348528 10.9804686,0.357422 9.789951,0.0091 10.106534,-0.002 11.087891,-0.369141 1.221173,-0.456851 2.835858,-1.955656 3.333984,-3.09375 0.336145,-0.767943 1.638672,-7.5615083 1.638672,-8.5488279 0,-1.4107731 -0.849384,-3.02258715 -2.234375,-3.58398435 L 28.791016,0 Z m -4.27539,4.890625 c 0.427942,0 0.812664,0.071135 1.152344,0.2128906 0.342354,0.1390812 0.631097,0.3382162 0.86914,0.5976563 0.243393,0.2674641 0.430211,0.5965523 0.558594,0.984375 0.131057,0.3878229 0.197266,0.8262256 0.197266,1.3183593 0,0.4921338 -0.06744,0.9337216 -0.201172,1.3242188 -0.131058,0.387823 -0.316645,0.711263 -0.554688,0.970703 -0.246067,0.270139 -0.536042,0.474922 -0.873047,0.611328 -0.33433,0.136407 -0.71782,0.203125 -1.148437,0.203125 -0.419919,0 -0.801456,-0.0699 -1.146484,-0.208984 C 10.243584,10.765216 9.9516549,10.563618 9.7109375,10.298828 9.4702197,10.034039 9.2834009,9.7093672 9.1523438,9.3242188 9.0239608,8.9390707 8.9609375,8.4987146 8.9609375,8.0039062 c 0,-0.4867846 0.063023,-0.9214925 0.1914063,-1.3066406 C 9.2807263,6.3094427 9.4687769,5.9766597 9.7148438,5.7011719 9.950212,5.4390572 10.242141,5.2386906 10.589844,5.0996094 10.940222,4.9605279 11.320527,4.890625 11.732422,4.890625 Z m 9.933594,0 c 0.427942,0 0.812664,0.071135 1.152343,0.2128906 0.342354,0.1390812 0.631098,0.3382162 0.869141,0.5976563 0.243392,0.2674641 0.430211,0.5965523 0.558594,0.984375 0.131057,0.3878229 0.197265,0.8262256 0.197265,1.3183593 0,0.4921338 -0.06744,0.9337216 -0.201171,1.3242188 -0.131058,0.387823 -0.316645,0.711263 -0.554688,0.970703 -0.246067,0.270139 -0.536042,0.474922 -0.873047,0.611328 -0.33433,0.136407 -0.71782,0.203125 -1.148437,0.203125 -0.419919,0 -0.801456,-0.0699 -1.146485,-0.208984 -0.342353,-0.139081 -0.634282,-0.340679 -0.875,-0.605469 C 19.403813,10.034039 19.216995,9.7093672 19.085938,9.3242188 18.957555,8.9390707 18.894531,8.4987146 18.894531,8.0039062 c 0,-0.4867846 0.06302,-0.9214925 0.191407,-1.3066406 0.128382,-0.3878229 0.316433,-0.7206059 0.5625,-0.9960937 0.235368,-0.2621147 0.527296,-0.4624813 0.875,-0.6015625 0.350377,-0.1390815 0.730683,-0.2089844 1.142578,-0.2089844 z m -16.0839848,0.125 h 2.359375 V 5.625 H 7.1582031 v 4.753906 h 0.7832031 v 0.611328 H 5.5820312 V 10.378906 H 6.3652344 V 5.625 H 5.5820312 Z m 9.9335938,0 H 17.875 V 5.625 h -0.783203 v 4.753906 H 17.875 v 0.611328 h -2.359375 v -0.611328 h 0.783203 V 5.625 h -0.783203 z m 9.933594,0 h 2.359375 V 5.625 h -0.783203 v 4.753906 h 0.783203 v 0.611328 h -2.359375 v -0.611328 h 0.783203 V 5.625 h -0.783203 z m -13.712891,0.5625 c -0.607143,0 -1.083937,0.2102191 -1.43164,0.6328125 -0.3450291,0.4199185 -0.5175786,1.0173231 -0.5175786,1.7929687 0,0.7836694 0.1762443,1.3854905 0.5292966,1.8027344 0.353053,0.4145694 0.826152,0.6210934 1.419922,0.6210934 0.59377,0 1.065638,-0.206524 1.416016,-0.6210934 0.353052,-0.4172439 0.529297,-1.019065 0.529297,-1.8027344 0,-0.7756456 -0.173782,-1.3730502 -0.521485,-1.7929687 C 12.812453,5.7883441 12.338122,5.578125 11.736328,5.578125 Z m 9.933594,0 c -0.607144,0 -1.083938,0.2102191 -1.431641,0.6328125 -0.345028,0.4199185 -0.517578,1.0173231 -0.517578,1.7929687 0,0.7836694 0.176244,1.3854905 0.529297,1.8027344 0.353052,0.4145694 0.826152,0.6210934 1.419922,0.6210934 0.59377,0 1.065638,-0.206524 1.416016,-0.6210934 0.353052,-0.4172439 0.529296,-1.019065 0.529296,-1.8027344 0,-0.7756456 -0.173781,-1.3730502 -0.521484,-1.7929687 C 22.746047,5.7883441 22.271716,5.578125 21.669922,5.578125 Z" transform="matrix(2.0576519,0,0,2.0576519,290.99076,314.87535)"/></g></g></svg>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="32"
height="16"
viewBox="0 0 32 16"
version="1.1"
id="svg3749">
<defs
id="defs3753" />
<g
id="g3747"
transform="matrix(0.48599086,0,0,0.48599086,0.50191451,-0.299629)"
style="fill:none;fill-rule:evenodd">
<g
id="g3741"
transform="translate(-292.02353,-314.25882)"
style="fill:#ffffff">
<path
style="fill-rule:nonzero;stroke-width:0.10270619"
d="M 16.007812,0 3.2929688,0.03515625 2.2324219,0.40234375 C 0.91449728,1.1071083 0,2.575555 0,3.9863281 c 0,1.2651363 1.3074352,7.8137089 1.7402344,8.7167969 0.553077,1.153703 1.988134,2.456836 3.234375,2.9375 0.8530743,0.328933 1.4753185,0.348528 10.9804686,0.357422 9.789951,0.0091 10.106534,-0.002 11.087891,-0.369141 1.221173,-0.456851 2.835858,-1.955656 3.333984,-3.09375 0.336145,-0.767943 1.638672,-7.5615083 1.638672,-8.5488279 0,-1.4107731 -0.849384,-3.02258715 -2.234375,-3.58398435 L 28.791016,0 Z m -4.27539,4.890625 c 0.427942,0 0.812664,0.071135 1.152344,0.2128906 0.342354,0.1390812 0.631097,0.3382162 0.86914,0.5976563 0.243393,0.2674641 0.430211,0.5965523 0.558594,0.984375 0.131057,0.3878229 0.197266,0.8262256 0.197266,1.3183593 0,0.4921338 -0.06744,0.9337216 -0.201172,1.3242188 -0.131058,0.387823 -0.316645,0.711263 -0.554688,0.970703 -0.246067,0.270139 -0.536042,0.474922 -0.873047,0.611328 -0.33433,0.136407 -0.71782,0.203125 -1.148437,0.203125 -0.419919,0 -0.801456,-0.0699 -1.146484,-0.208984 C 10.243584,10.765216 9.9516549,10.563618 9.7109375,10.298828 9.4702197,10.034039 9.2834009,9.7093672 9.1523438,9.3242188 9.0239608,8.9390707 8.9609375,8.4987146 8.9609375,8.0039062 c 0,-0.4867846 0.063023,-0.9214925 0.1914063,-1.3066406 C 9.2807263,6.3094427 9.4687769,5.9766597 9.7148438,5.7011719 9.950212,5.4390572 10.242141,5.2386906 10.589844,5.0996094 10.940222,4.9605279 11.320527,4.890625 11.732422,4.890625 Z m 9.933594,0 c 0.427942,0 0.812664,0.071135 1.152343,0.2128906 0.342354,0.1390812 0.631098,0.3382162 0.869141,0.5976563 0.243392,0.2674641 0.430211,0.5965523 0.558594,0.984375 0.131057,0.3878229 0.197265,0.8262256 0.197265,1.3183593 0,0.4921338 -0.06744,0.9337216 -0.201171,1.3242188 -0.131058,0.387823 -0.316645,0.711263 -0.554688,0.970703 -0.246067,0.270139 -0.536042,0.474922 -0.873047,0.611328 -0.33433,0.136407 -0.71782,0.203125 -1.148437,0.203125 -0.419919,0 -0.801456,-0.0699 -1.146485,-0.208984 -0.342353,-0.139081 -0.634282,-0.340679 -0.875,-0.605469 C 19.403813,10.034039 19.216995,9.7093672 19.085938,9.3242188 18.957555,8.9390707 18.894531,8.4987146 18.894531,8.0039062 c 0,-0.4867846 0.06302,-0.9214925 0.191407,-1.3066406 0.128382,-0.3878229 0.316433,-0.7206059 0.5625,-0.9960937 0.235368,-0.2621147 0.527296,-0.4624813 0.875,-0.6015625 0.350377,-0.1390815 0.730683,-0.2089844 1.142578,-0.2089844 z m -16.0839848,0.125 h 2.359375 V 5.625 H 7.1582031 v 4.753906 h 0.7832031 v 0.611328 H 5.5820312 V 10.378906 H 6.3652344 V 5.625 H 5.5820312 Z m 9.9335938,0 H 17.875 V 5.625 h -0.783203 v 4.753906 H 17.875 v 0.611328 h -2.359375 v -0.611328 h 0.783203 V 5.625 h -0.783203 z m 9.933594,0 h 2.359375 V 5.625 h -0.783203 v 4.753906 h 0.783203 v 0.611328 h -2.359375 v -0.611328 h 0.783203 V 5.625 h -0.783203 z m -13.712891,0.5625 c -0.607143,0 -1.083937,0.2102191 -1.43164,0.6328125 -0.3450291,0.4199185 -0.5175786,1.0173231 -0.5175786,1.7929687 0,0.7836694 0.1762443,1.3854905 0.5292966,1.8027344 0.353053,0.4145694 0.826152,0.6210934 1.419922,0.6210934 0.59377,0 1.065638,-0.206524 1.416016,-0.6210934 0.353052,-0.4172439 0.529297,-1.019065 0.529297,-1.8027344 0,-0.7756456 -0.173782,-1.3730502 -0.521485,-1.7929687 C 12.812453,5.7883441 12.338122,5.578125 11.736328,5.578125 Z m 9.933594,0 c -0.607144,0 -1.083938,0.2102191 -1.431641,0.6328125 -0.345028,0.4199185 -0.517578,1.0173231 -0.517578,1.7929687 0,0.7836694 0.176244,1.3854905 0.529297,1.8027344 0.353052,0.4145694 0.826152,0.6210934 1.419922,0.6210934 0.59377,0 1.065638,-0.206524 1.416016,-0.6210934 0.353052,-0.4172439 0.529296,-1.019065 0.529296,-1.8027344 0,-0.7756456 -0.173781,-1.3730502 -0.521484,-1.7929687 C 22.746047,5.7883441 22.271716,5.578125 21.669922,5.578125 Z"
transform="matrix(2.0576519,0,0,2.0576519,290.99076,314.87535)"
id="path3739" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -6,16 +6,17 @@ import { SerialTabComponent } from './components/serialTab.component'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class RecoveryProvider extends TabRecoveryProvider { export class RecoveryProvider extends TabRecoveryProvider {
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> { async applicableTo (recoveryToken: RecoveryToken): Promise<boolean> {
if (recoveryToken.type === 'app:serial-tab') { return recoveryToken.type === 'app:serial-tab'
return { }
type: SerialTabComponent,
options: { async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab> {
connection: recoveryToken.connection, return {
savedState: recoveryToken.savedState, type: SerialTabComponent,
}, options: {
} connection: recoveryToken.connection,
savedState: recoveryToken.savedState,
},
} }
return null
} }
} }

View File

@@ -0,0 +1,23 @@
h3.mb-3 Hotkeys
.input-group.mb-4
.input-group-prepend
.input-group-text
i.fas.fa-fw.fa-search
input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter')
.form-group
table.hotkeys-table
tr
th Name
th ID
th Hotkey
ng-container(*ngFor='let hotkey of hotkeyDescriptions')
tr(*ngIf='!hotkeyFilter || hotkeyFilterFn(hotkey, hotkeyFilter)')
td {{hotkey.name}}
td {{hotkey.id}}
td.pr-5
multi-hotkey-input(
[model]='getHotkey(hotkey.id) || []',
(modelChange)='setHotkey(hotkey.id, $event)'
)

View File

@@ -0,0 +1,7 @@
.hotkeys-table {
margin-top: 30px;
td, th {
padding: 5px 10px;
}
}

View File

@@ -0,0 +1,57 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, NgZone } from '@angular/core'
import {
ConfigService,
HotkeyDescription,
HotkeysService,
HostAppService,
} from 'terminus-core'
/** @hidden */
@Component({
selector: 'hotkey-settings-tab',
template: require('./hotkeySettingsTab.component.pug'),
styles: [
require('./hotkeySettingsTab.component.scss'),
],
})
export class HotkeySettingsTabComponent {
hotkeyFilter = ''
hotkeyDescriptions: HotkeyDescription[]
constructor (
public config: ConfigService,
public hostApp: HostAppService,
public zone: NgZone,
hotkeys: HotkeysService,
) {
hotkeys.getHotkeyDescriptions().then(descriptions => {
this.hotkeyDescriptions = descriptions
})
}
getHotkey (id: string) {
let ptr = this.config.store.hotkeys
for (const token of id.split(/\./g)) {
ptr = ptr[token]
}
return ptr
}
setHotkey (id: string, value) {
let ptr = this.config.store
let prop = 'hotkeys'
for (const token of id.split(/\./g)) {
ptr = ptr[prop]
prop = token
}
ptr[prop] = value
this.config.save()
}
hotkeyFilterFn (hotkey: HotkeyDescription, query: string): boolean {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
const s = hotkey.name + (this.getHotkey(hotkey.id) || []).toString()
return s.toLowerCase().includes(query.toLowerCase())
}
}

View File

@@ -1,385 +1,108 @@
button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes
ngb-tabset.vertical(type='pills', [activeId]='activeTab') .content
ngb-tab(id='application') ul.nav-pills(ngbNav, #nav='ngbNav', [activeId]='activeTab', orientation='vertical')
ng-template(ngbTabTitle) li(ngbNavItem='application')
i.fas.fa-fw.fa-window-maximize.mr-2 a(ngbNavLink)
| Application i.fas.fa-fw.fa-window-maximize.mr-2
ng-template(ngbTabContent) | Application
.d-flex.align-items-center.flex-wrap.mb-4 ng-template(ngbNavContent)
h1.terminus-title.mb-2.mr-2 Terminus .terminus-logo.mt-3
h1.terminus-title Terminus
sup α sup α
.text-muted.mr-auto {{homeBase.appVersion}} .text-center
.text-muted {{homeBase.appVersion}}
div .mb-5.mt-3
button.btn.btn-secondary.mr-3((click)='homeBase.openGitHub()') button.btn.btn-secondary.mr-3((click)='homeBase.openGitHub()')
i.fab.fa-github i.fab.fa-github
span GitHub span GitHub
button.btn.btn-secondary((click)='homeBase.reportBug()') button.btn.btn-secondary.mr-3((click)='homeBase.reportBug()')
i.fas.fa-bug
span Report a problem
button.btn.btn-secondary(
*ngIf='!updateAvailable',
(click)='checkForUpdates()',
[disabled]='checkingForUpdate'
)
i.fas.fa-sync(
[class.fa-spin]='checkingForUpdate'
)
span Check for updates
button.btn.btn-info(
*ngIf='updateAvailable',
(click)='updater.update()',
)
i.fas.fa-sync
span Update
.form-line(*ngIf='hostApp.platform !== Platform.Linux')
.header
.title Shell integration
.description Allows quickly opening a terminal in the selected folder
toggle([ngModel]='isShellIntegrationInstalled', (ngModelChange)='toggleShellIntegration()')
.form-line
.header
.title Enable analytics
.description We're only tracking your Terminus and OS versions.
toggle(
[(ngModel)]='config.store.enableAnalytics',
(ngModelChange)='saveConfiguration(true)',
)
.form-line
.header
.title Automatic Updates
.description Enable automatic installation of updates when they become available.
toggle([(ngModel)]='config.store.enableAutomaticUpdates', (ngModelChange)='saveConfiguration()')
.form-line
.header
.title Debugging
button.btn.btn-secondary((click)='hostApp.openDevTools()')
i.fas.fa-bug i.fas.fa-bug
span Report a problem span Open DevTools
.form-line li(*ngFor='let provider of settingsProviders', [ngbNavItem]='provider.id')
.header a(ngbNavLink)
.title Theme i(class='fas fa-fw mr-2 fa-{{provider.icon || "puzzle-piece"}}')
select.form-control( | {{provider.title}}
[(ngModel)]='config.store.appearance.theme', ng-template(ngbNavContent)
(ngModelChange)='saveConfiguration()', settings-tab-body([provider]='provider')
)
option(*ngFor='let theme of themes', [ngValue]='theme.name') {{theme.name}}
.form-line li(ngbNavItem='config-file')
.header a(ngbNavLink)
.title Tabs location i.fas.fa-fw.fa-code.mr-2
.btn-group( | Config file
[(ngModel)]='config.store.appearance.tabsLocation', ng-template.test(ngbNavContent)
(ngModelChange)='saveConfiguration()', .d-flex.flex-column.w-100.h-100
ngbRadioGroup .h-100.d-flex
) .w-100.d-flex.flex-column
label.btn.btn-secondary(ngbButtonLabel) h3 Config File
input( textarea.form-control.h-100(
type='radio', [(ngModel)]='configFile'
ngbButton, )
[value]='"top"' .w-100.d-flex.flex-column
) h3 Defaults
| Top textarea.form-control.h-100(
label.btn.btn-secondary(ngbButtonLabel) [(ngModel)]='configDefaults',
input( readonly
type='radio', )
ngbButton, .mt-3.d-flex
[value]='"bottom"' button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()')
) i.fas.fa-check.mr-2
| Bottom | Save and apply
label.btn.btn-secondary(ngbButtonLabel) button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
input( i.fas.fa-exclamation-triangle.mr-2
type='radio', | Invalid syntax
ngbButton, button.btn.btn-secondary.ml-auto((click)='showConfigFile()')
[value]='"left"' i.fas.fa-external-link-square-alt.mr-2
) | Show config file
| Left
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
.form-line div([ngbNavOutlet]='nav')
.header
.title Tabs width
.btn-group(
[(ngModel)]='config.store.appearance.flexTabs',
(ngModelChange)='saveConfiguration()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| Dynamic
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Fixed
.form-line
.header
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background
.title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy
.description Gives the window a blurred transparent background
toggle(
[(ngModel)]='config.store.appearance.vibrancy',
(ngModelChange)='saveConfiguration()'
)
.form-line(*ngIf='config.store.appearance.vibrancy && isFluentVibrancySupported')
.header
.title Background type
.btn-group(
[(ngModel)]='config.store.appearance.vibrancyType',
(ngModelChange)='saveConfiguration()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"blur"'
)
| Blur
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"fluent"'
)
| Fluent
.form-line
.header
.title Opacity
input(
type='range',
[(ngModel)]='config.store.appearance.opacity',
(ngModelChange)='saveConfiguration(); (hostApp.platform === Platform.Linux && config.requestRestart())',
min='0.4',
max='1',
step='0.01'
)
.form-line(*ngIf='hostApp.platform !== Platform.Linux')
.header
.title Shell integration
.description Allows quickly opening a terminal in the selected folder
toggle([ngModel]='isShellIntegrationInstalled', (ngModelChange)='toggleShellIntegration()')
.form-line
.header
.title Window frame
.description Whether a custom window or an OS native window should be used
.btn-group(
[(ngModel)]='config.store.appearance.frame',
(ngModelChange)='saveConfiguration(true)',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"native"'
)
| Native
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"thin"'
)
| Thin
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"full"'
)
| Full
.form-line
.header
.title Dock the terminal
.description Snaps the window to a side of the screen
.btn-group(
[(ngModel)]='config.store.appearance.dock',
(ngModelChange)='saveConfiguration(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"off"'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| Top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"left"'
)
| Left
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| Bottom
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Display on
.description Snaps the window to a side of the screen
div(
[(ngModel)]='config.store.appearance.dockScreen',
(ngModelChange)='saveConfiguration(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
value='current'
)
| Current
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='screen.id'
)
| {{screen.name}}
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Dock always on top
.description Keep docked terminal always on top
toggle(
[(ngModel)]='config.store.appearance.dockAlwaysOnTop',
(ngModelChange)='saveConfiguration(); docking.dock()',
)
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Docked terminal size
input(
type='range',
[(ngModel)]='config.store.appearance.dockFill',
(mouseup)='saveConfiguration(); docking.dock()',
min='0.05',
max='1',
step='0.01'
)
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Docked terminal space
input(
type='range',
[(ngModel)]='config.store.appearance.dockSpace',
(mouseup)='saveConfiguration(); docking.dock()',
min='0.2',
max='1',
step='0.01'
)
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Hide dock on blur
.description Hides the docked terminal when you click away.
toggle(
[(ngModel)]='config.store.appearance.dockHideOnBlur',
(ngModelChange)='saveConfiguration(); ',
)
.form-line
.header
.title Debugging
button.btn.btn-secondary((click)='hostApp.openDevTools()')
i.fas.fa-bug
span Open DevTools
.form-line
.header
.title Enable analytics
.description We're only tracking your Terminus and OS versions.
toggle(
[(ngModel)]='config.store.enableAnalytics',
(ngModelChange)='saveConfiguration(true)',
)
.form-line
.header
.title Automatic Updates
.description Enable automatic installation of updates when they become available.
toggle([(ngModel)]='config.store.enableAutomaticUpdates', (ngModelChange)='saveConfiguration()')
.form-line
.header
.title Custom CSS
textarea.form-control(
[(ngModel)]='config.store.appearance.css',
(ngModelChange)='saveConfiguration()',
)
ngb-tab(id='hotkeys')
ng-template(ngbTabTitle)
i.fas.fa-fw.fa-keyboard.mr-2
| Hotkeys
ng-template(ngbTabContent)
h3.mb-3 Hotkeys
.input-group.mb-4
.input-group-prepend
.input-group-text
i.fas.fa-fw.fa-search
input.form-control(type='search', placeholder='Search hotkeys', [(ngModel)]='hotkeyFilter')
.form-group
table.hotkeys-table
tr
th Name
th ID
th Hotkey
ng-container(*ngFor='let hotkey of hotkeyDescriptions')
tr(*ngIf='!hotkeyFilter || hotkeyFilterFn(hotkey, hotkeyFilter)')
td {{hotkey.name}}
td {{hotkey.id}}
td.pr-5
multi-hotkey-input(
[model]='getHotkey(hotkey.id) || []',
(modelChange)='setHotkey(hotkey.id, $event); saveConfiguration(); docking.dock()'
)
ngb-tab(*ngFor='let provider of settingsProviders', [id]='provider.id')
ng-template(ngbTabTitle)
i(class='fas fa-fw mr-2 fa-{{provider.icon || "puzzle-piece"}}')
| {{provider.title}}
ng-template(ngbTabContent)
settings-tab-body([provider]='provider')
ngb-tab(id='config-file')
ng-template(ngbTabTitle)
i.fas.fa-fw.fa-code.mr-2
| Config file
ng-template.test(ngbTabContent)
.d-flex.flex-column.w-100.h-100
.h-100.d-flex
.w-100.d-flex.flex-column
h3 Config File
textarea.form-control.h-100(
[(ngModel)]='configFile'
)
.w-100.d-flex.flex-column
h3 Defaults
textarea.form-control.h-100(
[(ngModel)]='configDefaults',
readonly
)
.mt-2.mb-2.d-flex
button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()')
i.fas.fa-check.mr-2
| Save and apply
button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
i.fas.fa-exclamation-triangle.mr-2
| Invalid syntax
button.btn.btn-secondary.ml-auto((click)='showConfigFile()')
i.fas.fa-external-link-square-alt.mr-2
| Show config file

View File

@@ -1,20 +1,38 @@
:host { :host {
display: flex; display: flex;
flex-direction: column;
flex: auto; flex: auto;
flex-direction: column;
>.btn-block { >.btn-block {
margin: 20px; margin: 20px;
width: auto; width: auto;
flex: none; flex: none;
} }
}
.hotkeys-table { > .content {
margin-top: 30px; display: flex;
min-height: 0;
flex: 1 0 0;
td, th { > .nav {
padding: 5px 10px; padding: 20px 10px;
width: 190px;
overflow-y: auto;
}
> .tab-content {
flex: auto;
padding: 20px 30px;
overflow-y: auto;
> ::ng-deep .tab-pane {
height: 100%;
}
}
}
&.pad-window-controls > .content > .nav {
padding-top: 40px;
} }
} }

View File

@@ -5,18 +5,13 @@ import { Subscription } from 'rxjs'
import { Component, Inject, Input, HostBinding, NgZone } from '@angular/core' import { Component, Inject, Input, HostBinding, NgZone } from '@angular/core'
import { import {
ElectronService, ElectronService,
DockingService,
ConfigService, ConfigService,
HotkeyDescription,
HotkeysService,
BaseTabComponent, BaseTabComponent,
Theme,
HostAppService, HostAppService,
Platform, Platform,
HomeBaseService, HomeBaseService,
ShellIntegrationService, ShellIntegrationService,
isWindowsBuild, UpdaterService,
WIN_BUILD_FLUENT_BG_SUPPORTED,
} from 'terminus-core' } from 'terminus-core'
import { SettingsTabProvider } from '../api' import { SettingsTabProvider } from '../api'
@@ -27,39 +22,33 @@ import { SettingsTabProvider } from '../api'
template: require('./settingsTab.component.pug'), template: require('./settingsTab.component.pug'),
styles: [ styles: [
require('./settingsTab.component.scss'), require('./settingsTab.component.scss'),
require('./settingsTab.deep.component.css'),
], ],
}) })
export class SettingsTabComponent extends BaseTabComponent { export class SettingsTabComponent extends BaseTabComponent {
@Input() activeTab: string @Input() activeTab: string
hotkeyFilter = ''
hotkeyDescriptions: HotkeyDescription[]
screens: any[]
Platform = Platform Platform = Platform
configDefaults: any configDefaults: any
configFile: string configFile: string
isShellIntegrationInstalled = false isShellIntegrationInstalled = false
isFluentVibrancySupported = false checkingForUpdate = false
updateAvailable = false
@HostBinding('class.pad-window-controls') padWindowControls = false @HostBinding('class.pad-window-controls') padWindowControls = false
private configSubscription: Subscription private configSubscription: Subscription
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
private electron: ElectronService, private electron: ElectronService,
public docking: DockingService,
public hostApp: HostAppService, public hostApp: HostAppService,
public homeBase: HomeBaseService, public homeBase: HomeBaseService,
public shellIntegration: ShellIntegrationService, public shellIntegration: ShellIntegrationService,
public zone: NgZone, public zone: NgZone,
hotkeys: HotkeysService, private updater: UpdaterService,
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[], @Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
@Inject(Theme) public themes: Theme[],
) { ) {
super() super()
this.setTitle('Settings') this.setTitle('Settings')
this.screens = this.docking.getScreens()
this.settingsProviders = config.enabledServices(this.settingsProviders) this.settingsProviders = config.enabledServices(this.settingsProviders)
this.themes = config.enabledServices(this.themes) this.settingsProviders.sort((a, b) => a.title.localeCompare(b.title))
this.configDefaults = yaml.dump(config.getDefaults()) this.configDefaults = yaml.dump(config.getDefaults())
@@ -71,16 +60,6 @@ export class SettingsTabComponent extends BaseTabComponent {
this.configSubscription = config.changed$.subscribe(onConfigChange) this.configSubscription = config.changed$.subscribe(onConfigChange)
onConfigChange() onConfigChange()
hostApp.displaysChanged$.subscribe(() => {
this.zone.run(() => this.screens = this.docking.getScreens())
})
hotkeys.getHotkeyDescriptions().then(descriptions => {
this.hotkeyDescriptions = descriptions
})
this.isFluentVibrancySupported = isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)
} }
async ngOnInit () { async ngOnInit () {
@@ -132,27 +111,9 @@ export class SettingsTabComponent extends BaseTabComponent {
} }
} }
getHotkey (id: string) { async checkForUpdates () {
let ptr = this.config.store.hotkeys this.checkingForUpdate = true
for (const token of id.split(/\./g)) { this.updateAvailable = await this.updater.check()
ptr = ptr[token] this.checkingForUpdate = false
}
return ptr
}
setHotkey (id: string, value) {
let ptr = this.config.store
let prop = 'hotkeys'
for (const token of id.split(/\./g)) {
ptr = ptr[prop]
prop = token
}
ptr[prop] = value
}
hotkeyFilterFn (hotkey: HotkeyDescription, query: string): boolean {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
const s = hotkey.name + (this.getHotkey(hotkey.id) || []).toString()
return s.toLowerCase().includes(query.toLowerCase())
} }
} }

View File

@@ -1,28 +0,0 @@
:host /deep/ ngb-tabset {
flex: auto;
display: flex;
height: 100%;
}
:host /deep/ ngb-tabset > .nav {
display: block;
overflow-y: auto;
}
:host /deep/ ngb-tabset > .tab-content {
padding: 15px 30px;
margin: 0;
overflow-y: auto;
height: 100%;
flex: auto;
display: flex;
position: relative;
}
:host /deep/ ngb-tabset > .tab-content > .tab-pane {
width: 100%;
}
:host.pad-window-controls /deep/ ngb-tabset > .nav {
padding-top: 40px;
}

View File

@@ -0,0 +1,279 @@
h3.mb-3 Window
.form-line
.header
.title Theme
select.form-control(
[(ngModel)]='config.store.appearance.theme',
(ngModelChange)='saveConfiguration()',
)
option(*ngFor='let theme of themes', [ngValue]='theme.name') {{theme.name}}
.form-line
.header
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background
.title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy
.description Gives the window a blurred transparent background
toggle(
[(ngModel)]='config.store.appearance.vibrancy',
(ngModelChange)='saveConfiguration()'
)
.form-line(*ngIf='config.store.appearance.vibrancy && isFluentVibrancySupported')
.header
.title Background type
.btn-group(
[(ngModel)]='config.store.appearance.vibrancyType',
(ngModelChange)='saveConfiguration()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"blur"'
)
| Blur
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"fluent"'
)
| Fluent
.form-line
.header
.title Opacity
input(
type='range',
[(ngModel)]='config.store.appearance.opacity',
(ngModelChange)='saveConfiguration(); (hostApp.platform === Platform.Linux && config.requestRestart())',
min='0.4',
max='1',
step='0.01'
)
.form-line
.header
.title Window frame
.description Whether a custom window or an OS native window should be used
.btn-group(
[(ngModel)]='config.store.appearance.frame',
(ngModelChange)='saveConfiguration(true)',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"native"'
)
| Native
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"thin"'
)
| Thin
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"full"'
)
| Full
.form-line
.header
.title Dock the terminal
.description Snaps the window to a side of the screen
.btn-group(
[(ngModel)]='config.store.appearance.dock',
(ngModelChange)='saveConfiguration(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"off"'
)
| Off
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| Top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"left"'
)
| Left
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| Bottom
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Display on
.description Snaps the window to a side of the screen
div(
[(ngModel)]='config.store.appearance.dockScreen',
(ngModelChange)='saveConfiguration(); docking.dock()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
value='current'
)
| Current
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='screen.id'
)
| {{screen.name}}
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Dock always on top
.description Keep docked terminal always on top
toggle(
[(ngModel)]='config.store.appearance.dockAlwaysOnTop',
(ngModelChange)='saveConfiguration(); docking.dock()',
)
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Docked terminal size
input(
type='range',
[(ngModel)]='config.store.appearance.dockFill',
(mouseup)='saveConfiguration(); docking.dock()',
min='0.05',
max='1',
step='0.01'
)
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Docked terminal space
input(
type='range',
[(ngModel)]='config.store.appearance.dockSpace',
(mouseup)='saveConfiguration(); docking.dock()',
min='0.2',
max='1',
step='0.01'
)
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"')
.header
.title Hide dock on blur
.description Hides the docked terminal when you click away.
toggle(
[(ngModel)]='config.store.appearance.dockHideOnBlur',
(ngModelChange)='saveConfiguration(); ',
)
.form-line
.header
.title Tabs location
.btn-group(
[(ngModel)]='config.store.appearance.tabsLocation',
(ngModelChange)='saveConfiguration()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"top"'
)
| Top
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"bottom"'
)
| Bottom
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"left"'
)
| Left
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='"right"'
)
| Right
.form-line
.header
.title Tabs width
.btn-group(
[(ngModel)]='config.store.appearance.flexTabs',
(ngModelChange)='saveConfiguration()',
ngbRadioGroup
)
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='true'
)
| Dynamic
label.btn.btn-secondary(ngbButtonLabel)
input(
type='radio',
ngbButton,
[value]='false'
)
| Fixed
.form-line
.header
.title Hide tab index
toggle(
[(ngModel)]='config.store.terminal.hideTabIndex',
(ngModelChange)='config.save();',
)
.form-line
.header
.title Hide tab close button
toggle(
[(ngModel)]='config.store.terminal.hideCloseButton',
(ngModelChange)='config.save();',
)

View File

@@ -0,0 +1,49 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { debounce } from 'utils-decorators/dist/cjs'
import { Component, Inject, NgZone } from '@angular/core'
import {
DockingService,
ConfigService,
Theme,
HostAppService,
Platform,
isWindowsBuild,
WIN_BUILD_FLUENT_BG_SUPPORTED,
} from 'terminus-core'
/** @hidden */
@Component({
selector: 'window-settings-tab',
template: require('./windowSettingsTab.component.pug'),
})
export class WindowSettingsTabComponent {
screens: any[]
Platform = Platform
isFluentVibrancySupported = false
constructor (
public config: ConfigService,
public docking: DockingService,
public hostApp: HostAppService,
public zone: NgZone,
@Inject(Theme) public themes: Theme[],
) {
this.screens = this.docking.getScreens()
this.themes = config.enabledServices(this.themes)
hostApp.displaysChanged$.subscribe(() => {
this.zone.run(() => this.screens = this.docking.getScreens())
})
this.isFluentVibrancySupported = isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)
}
@debounce(500)
saveConfiguration (requireRestart?: boolean) {
this.config.save()
if (requireRestart) {
this.config.requestRestart()
}
}
}

View File

@@ -6,13 +6,17 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import TerminusCorePlugin, { ToolbarButtonProvider, HotkeyProvider, ConfigProvider } from 'terminus-core' import TerminusCorePlugin, { ToolbarButtonProvider, HotkeyProvider, ConfigProvider } from 'terminus-core'
import { HotkeyInputModalComponent } from './components/hotkeyInputModal.component' import { HotkeyInputModalComponent } from './components/hotkeyInputModal.component'
import { HotkeySettingsTabComponent } from './components/hotkeySettingsTab.component'
import { MultiHotkeyInputComponent } from './components/multiHotkeyInput.component' import { MultiHotkeyInputComponent } from './components/multiHotkeyInput.component'
import { SettingsTabComponent } from './components/settingsTab.component' import { SettingsTabComponent } from './components/settingsTab.component'
import { SettingsTabBodyComponent } from './components/settingsTabBody.component' import { SettingsTabBodyComponent } from './components/settingsTabBody.component'
import { WindowSettingsTabComponent } from './components/windowSettingsTab.component'
import { SettingsTabProvider } from './api'
import { ButtonProvider } from './buttonProvider' import { ButtonProvider } from './buttonProvider'
import { SettingsHotkeyProvider } from './hotkeys' import { SettingsHotkeyProvider } from './hotkeys'
import { SettingsConfigProvider } from './config' import { SettingsConfigProvider } from './config'
import { HotkeySettingsTabProvider, WindowSettingsTabProvider } from './settings'
/** @hidden */ /** @hidden */
@NgModule({ @NgModule({
@@ -26,16 +30,22 @@ import { SettingsConfigProvider } from './config'
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: ConfigProvider, useClass: SettingsConfigProvider, multi: true }, { provide: ConfigProvider, useClass: SettingsConfigProvider, multi: true },
{ provide: HotkeyProvider, useClass: SettingsHotkeyProvider, multi: true }, { provide: HotkeyProvider, useClass: SettingsHotkeyProvider, multi: true },
{ provide: SettingsTabProvider, useClass: HotkeySettingsTabProvider, multi: true },
{ provide: SettingsTabProvider, useClass: WindowSettingsTabProvider, multi: true },
], ],
entryComponents: [ entryComponents: [
HotkeyInputModalComponent, HotkeyInputModalComponent,
HotkeySettingsTabComponent,
SettingsTabComponent, SettingsTabComponent,
WindowSettingsTabComponent,
], ],
declarations: [ declarations: [
HotkeyInputModalComponent, HotkeyInputModalComponent,
HotkeySettingsTabComponent,
MultiHotkeyInputComponent, MultiHotkeyInputComponent,
SettingsTabComponent, SettingsTabComponent,
SettingsTabBodyComponent, SettingsTabBodyComponent,
WindowSettingsTabComponent,
], ],
}) })
export default class SettingsModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class export default class SettingsModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class

View File

@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core'
import { SettingsTabProvider } from './api'
import { HotkeySettingsTabComponent } from './components/hotkeySettingsTab.component'
import { WindowSettingsTabComponent } from './components/windowSettingsTab.component'
/** @hidden */
@Injectable()
export class HotkeySettingsTabProvider extends SettingsTabProvider {
id = 'hotkeys'
icon = 'keyboard'
title = 'Hotkeys'
getComponentType (): any {
return HotkeySettingsTabComponent
}
}
/** @hidden */
@Injectable()
export class WindowSettingsTabProvider extends SettingsTabProvider {
id = 'window'
icon = 'window-maximize'
title = 'Window'
getComponentType (): any {
return WindowSettingsTabComponent
}
}

View File

@@ -25,6 +25,7 @@
"@types/ssh2": "^0.5.35", "@types/ssh2": "^0.5.35",
"ansi-colors": "^4.1.1", "ansi-colors": "^4.1.1",
"cli-spinner": "^0.2.10", "cli-spinner": "^0.2.10",
"clone-deep": "^4.0.1",
"ssh2": "^0.8.9", "ssh2": "^0.8.9",
"ssh2-streams": "Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5", "ssh2-streams": "Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5",
"sshpk": "^1.16.1", "sshpk": "^1.16.1",

View File

@@ -1,8 +1,8 @@
.modal-body .modal-body
ngb-tabset([activeId]='basic') ul.nav-tabs(ngbNav, #nav='ngbNav')
ngb-tab(id='basic') li(ngbNavItem)
ng-template(ngbTabTitle) General a(ngbNavLink) General
ng-template(ngbTabContent) ng-template(ngbNavContent)
.form-group .form-group
label Name label Name
input.form-control( input.form-control(
@@ -49,23 +49,23 @@
[(ngModel)]='connection.auth', [(ngModel)]='connection.auth',
ngbRadioGroup ngbRadioGroup
) )
label.btn.btn-outline-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input(type='radio', ngbButton, [value]='null') input(type='radio', ngbButton, [value]='null')
i.far.fa-lightbulb i.far.fa-lightbulb
.m-0 Auto .m-0 Auto
label.btn.btn-outline-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input(type='radio', ngbButton, [value]='"password"') input(type='radio', ngbButton, [value]='"password"')
i.fas.fa-font i.fas.fa-font
.m-0 Password .m-0 Password
label.btn.btn-outline-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input(type='radio', ngbButton, [value]='"publicKey"') input(type='radio', ngbButton, [value]='"publicKey"')
i.fas.fa-key i.fas.fa-key
.m-0 Key .m-0 Key
label.btn.btn-outline-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input(type='radio', ngbButton, [value]='"agent"') input(type='radio', ngbButton, [value]='"agent"')
i.fas.fa-user-secret i.fas.fa-user-secret
.m-0 Agent .m-0 Agent
label.btn.btn-outline-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input(type='radio', ngbButton, [value]='"keyboardInteractive"') input(type='radio', ngbButton, [value]='"keyboardInteractive"')
i.far.fa-keyboard i.far.fa-keyboard
.m-0 Interactive .m-0 Interactive
@@ -96,9 +96,9 @@
button.btn.btn-secondary((click)='selectPrivateKey()') button.btn.btn-secondary((click)='selectPrivateKey()')
i.fas.fa-folder-open i.fas.fa-folder-open
ngb-tab(id='advanced') li(ngbNavItem)
ng-template(ngbTabTitle) Advanced a(ngbNavLink) Advanced
ng-template(ngbTabContent) ng-template(ngbNavContent)
.form-line .form-line
.header .header
.title Jump host .title Jump host
@@ -113,7 +113,7 @@
.form-line .form-line
.header .header
.title Agent Forwarding .title Agent forwarding
toggle([(ngModel)]='connection.agentForward') toggle([(ngModel)]='connection.agentForward')
.form-line .form-line
@@ -165,9 +165,9 @@
[(ngModel)]='connection.readyTimeout', [(ngModel)]='connection.readyTimeout',
) )
ngb-tab(id='ciphers') li(ngbNavItem)
ng-template(ngbTabTitle) Ciphers a(ngbNavLink) Ciphers
ng-template(ngbTabContent) ng-template(ngbNavContent)
.form-line.align-items-start .form-line.align-items-start
.header .header
.title Ciphers .title Ciphers
@@ -196,10 +196,9 @@
div(*ngFor='let alg of supportedAlgorithms.serverHostKey') div(*ngFor='let alg of supportedAlgorithms.serverHostKey')
checkbox([text]='alg', [(ngModel)]='algorithms.serverHostKey[alg]') checkbox([text]='alg', [(ngModel)]='algorithms.serverHostKey[alg]')
li(ngbNavItem)
ngb-tab(id='scripts') a(ngbNavLink) Login scripts
ng-template(ngbTabTitle) Login scripts ng-template(ngbNavContent)
ng-template(ngbTabContent)
table(*ngIf='connection.scripts.length > 0') table(*ngIf='connection.scripts.length > 0')
tr tr
th String to expect th String to expect
@@ -239,6 +238,8 @@
i.fas.fa-plus i.fas.fa-plus
span New item span New item
div([ngbNavOutlet]='nav')
.modal-footer .modal-footer
button.btn.btn-outline-primary((click)='save()') Save button.btn.btn-outline-primary((click)='save()') Save
button.btn.btn-outline-danger((click)='cancel()') Cancel button.btn.btn-outline-danger((click)='cancel()') Cancel

View File

@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import deepClone from 'clone-deep'
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, ElectronService, HostAppService } from 'terminus-core' import { ConfigService, ElectronService, HostAppService } from 'terminus-core'
@@ -48,9 +49,10 @@ export class SSHSettingsTabComponent {
copyConnection (connection: SSHConnection) { copyConnection (connection: SSHConnection) {
const modal = this.ngbModal.open(EditConnectionModalComponent) const modal = this.ngbModal.open(EditConnectionModalComponent)
modal.componentInstance.connection = Object.assign({ modal.componentInstance.connection = {
name: `${name} Copy`, ...deepClone(connection),
}, connection) name: `${connection.name} Copy`,
}
modal.result.then(result => { modal.result.then(result => {
this.connections.push(result) this.connections.push(result)
this.config.store.ssh.connections = this.connections this.config.store.ssh.connections = this.connections
@@ -61,7 +63,7 @@ export class SSHSettingsTabComponent {
editConnection (connection: SSHConnection) { editConnection (connection: SSHConnection) {
const modal = this.ngbModal.open(EditConnectionModalComponent, { size: 'lg' }) const modal = this.ngbModal.open(EditConnectionModalComponent, { size: 'lg' })
modal.componentInstance.connection = Object.assign({}, connection) modal.componentInstance.connection = deepClone(connection)
modal.result.then(result => { modal.result.then(result => {
Object.assign(connection, result) Object.assign(connection, result)
this.config.store.ssh.connections = this.connections this.config.store.ssh.connections = this.connections

View File

@@ -6,16 +6,17 @@ import { SSHTabComponent } from './components/sshTab.component'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class RecoveryProvider extends TabRecoveryProvider { export class RecoveryProvider extends TabRecoveryProvider {
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> { async applicableTo (recoveryToken: RecoveryToken): Promise<boolean> {
if (recoveryToken.type === 'app:ssh-tab') { return recoveryToken.type === 'app:ssh-tab'
return { }
type: SSHTabComponent,
options: { async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab> {
connection: recoveryToken['connection'], return {
savedState: recoveryToken['savedState'], type: SSHTabComponent,
}, options: {
} connection: recoveryToken['connection'],
savedState: recoveryToken['savedState'],
},
} }
return null
} }
} }

View File

@@ -90,6 +90,15 @@ cliff@0.1.x:
eyes "~0.1.8" eyes "~0.1.8"
winston "0.8.x" winston "0.8.x"
clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
dependencies:
is-plain-object "^2.0.4"
kind-of "^6.0.2"
shallow-clone "^3.0.0"
colors@0.6.x: colors@0.6.x:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
@@ -176,6 +185,18 @@ ipv6@*:
cliff "0.1.x" cliff "0.1.x"
sprintf "0.1.x" sprintf "0.1.x"
is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
dependencies:
isobject "^3.0.1"
isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isstream@0.1.x: isstream@0.1.x:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -186,6 +207,11 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
minimatch@^3.0.4: minimatch@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@@ -230,15 +256,22 @@ rimraf@~2.6.2:
glob "^7.1.3" glob "^7.1.3"
run-script-os@^1.1.3: run-script-os@^1.1.3:
version "1.1.5" version "1.1.6"
resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.5.tgz#6038a92b370a1cbca5fa2fa73d1c740b0037b6ff" resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.6.tgz#8b0177fb1b54c99a670f95c7fdc54f18b9c72347"
integrity sha512-kLvUnf1SM1+gSH2bY1P41MNFaWdXaG2nkhoJS9WQNQxk6g9KNuncKderBLG7vOsp77GtQqNs0bEazxip/I6wfg== integrity sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
shallow-clone@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
dependencies:
kind-of "^6.0.2"
socksv5@^0.0.6: socksv5@^0.0.6:
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/socksv5/-/socksv5-0.0.6.tgz#1327235ff7e8de21ac434a0a579dc69c3f071061" resolved "https://registry.yarnpkg.com/socksv5/-/socksv5-0.0.6.tgz#1327235ff7e8de21ac434a0a579dc69c3f071061"

View File

@@ -16,12 +16,15 @@
], ],
"author": "Eugene Pankov", "author": "Eugene Pankov",
"license": "MIT", "license": "MIT",
"dependencies": {
"hterm-umdjs": "1.4.1",
"opentype.js": "^1.3.3"
},
"devDependencies": { "devDependencies": {
"@types/deep-equal": "^1.0.0", "@types/deep-equal": "^1.0.0",
"ansi-colors": "^4.1.1", "ansi-colors": "^4.1.1",
"dataurl": "0.1.0", "dataurl": "0.1.0",
"deep-equal": "1.1.0", "deep-equal": "1.1.0",
"hterm-umdjs": "1.4.1",
"mz": "^2.6.0", "mz": "^2.6.0",
"ps-node": "^0.1.6", "ps-node": "^0.1.6",
"runes": "^0.4.2", "runes": "^0.4.2",

View File

@@ -22,16 +22,26 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
static styles: string[] = [require<string>('../components/terminalTab.component.scss')] static styles: string[] = [require<string>('../components/terminalTab.component.scss')]
static animations: AnimationTriggerMetadata[] = [trigger('slideInOut', [ static animations: AnimationTriggerMetadata[] = [trigger('slideInOut', [
transition(':enter', [ transition(':enter', [
style({ transform: 'translateY(-25%)' }), style({
animate('100ms ease-in-out', style({ transform: 'translateY(0%)' })), transform: 'translateY(-25%)',
opacity: '0',
}),
animate('100ms ease-out', style({
transform: 'translateY(0%)',
opacity: '1',
})),
]), ]),
transition(':leave', [ transition(':leave', [
animate('100ms ease-in-out', style({ transform: 'translateY(-25%)' })), animate('100ms ease-out', style({
transform: 'translateY(-25%)',
opacity: '0',
})),
]), ]),
])] ])]
session: BaseSession|null = null session: BaseSession|null = null
savedState?: any savedState?: any
savedStateIsLive = false
@Input() zoom = 0 @Input() zoom = 0
@@ -161,6 +171,9 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
case 'paste': case 'paste':
this.paste() this.paste()
break break
case 'select-all':
this.frontend?.selectAll()
break
case 'clear': case 'clear':
this.frontend?.clear() this.frontend?.clear()
break break
@@ -226,8 +239,8 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.frontend = this.terminalContainersService.getFrontend(this.session) this.frontend = this.terminalContainersService.getFrontend(this.session)
this.frontend.ready$.subscribe(() => { this.frontendReady$.pipe(first()).subscribe(() => {
this.frontendIsReady = true this.onFrontendReady()
}) })
this.frontend.resize$.pipe(first()).subscribe(async ({ columns, rows }) => { this.frontend.resize$.pipe(first()).subscribe(async ({ columns, rows }) => {
@@ -253,20 +266,13 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.alternateScreenActive = x this.alternateScreenActive = x
}) })
if (this.savedState) { setImmediate(async () => {
this.frontend.restoreState(this.savedState)
this.frontend.write('\r\n\r\n')
this.frontend.write(colors.bgWhite.black(' * ') + colors.bgBlackBright.white(' History restored '))
this.frontend.write('\r\n\r\n')
}
setImmediate(() => {
if (this.hasFocus) { if (this.hasFocus) {
this.frontend!.attach(this.content.nativeElement) await this.frontend!.attach(this.content.nativeElement)
this.frontend!.configure() this.frontend!.configure()
} else { } else {
this.focused$.pipe(first()).subscribe(() => { this.focused$.pipe(first()).subscribe(async () => {
this.frontend!.attach(this.content.nativeElement) await this.frontend!.attach(this.content.nativeElement)
this.frontend!.configure() this.frontend!.configure()
}) })
} }
@@ -298,6 +304,18 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
}) })
} }
protected onFrontendReady (): void {
this.frontendIsReady = true
if (this.savedState) {
this.frontend!.restoreState(this.savedState)
if (!this.savedStateIsLive) {
this.frontend!.write('\r\n\r\n')
this.frontend!.write(colors.bgWhite.black(' * ') + colors.bgBlackBright.white(' History restored '))
this.frontend!.write('\r\n\r\n')
}
}
}
async buildContextMenu (): Promise<MenuItemConstructorOptions[]> { async buildContextMenu (): Promise<MenuItemConstructorOptions[]> {
let items: MenuItemConstructorOptions[] = [] let items: MenuItemConstructorOptions[] = []
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this)))) { for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this)))) {
@@ -329,15 +347,16 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
throw new Error('Frontend not ready') throw new Error('Frontend not ready')
} }
const percentageMatch = /(^|[^\d])(\d+(\.\d+)?)%([^\d]|$)/.exec(data) if (this.config.store.terminal.detectProgress) {
if (!this.alternateScreenActive && percentageMatch && this.config.store.terminal.detectProgress) { const percentageMatch = /(^|[^\d])(\d+(\.\d+)?)%([^\d]|$)/.exec(data)
const percentage = percentageMatch[3] ? parseFloat(percentageMatch[2]) : parseInt(percentageMatch[2]) if (!this.alternateScreenActive && percentageMatch) {
if (percentage > 0 && percentage <= 100) { const percentage = percentageMatch[3] ? parseFloat(percentageMatch[2]) : parseInt(percentageMatch[2])
this.setProgress(percentage) if (percentage > 0 && percentage <= 100) {
// this.logger.debug('Detected progress:', percentage) this.setProgress(percentage)
}
} else {
this.setProgress(null)
} }
} else {
this.setProgress(null)
} }
this.frontend.write(data) this.frontend.write(data)
} }
@@ -593,10 +612,8 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
// this.session.output$.bufferTime(10).subscribe((datas) => { // this.session.output$.bufferTime(10).subscribe((datas) => {
this.attachSessionHandler(this.session.output$.subscribe(data => { this.attachSessionHandler(this.session.output$.subscribe(data => {
if (this.enablePassthrough) { if (this.enablePassthrough) {
this.zone.run(() => { this.output.next(data)
this.output.next(data) this.write(data)
this.write(data)
})
} }
})) }))

View File

@@ -4,6 +4,7 @@ export interface ResizeEvent {
} }
export interface SessionOptions { export interface SessionOptions {
restoreFromPTYID?: string
name?: string name?: string
command: string command: string
args?: string[] args?: string[]
@@ -53,3 +54,9 @@ export interface Shell {
hidden?: boolean hidden?: boolean
} }
export interface ChildProcess {
pid: number
ppid: number
command: string
}

View File

@@ -1,57 +0,0 @@
/** @hidden */
module.exports = function patchPTYModule (mod) {
const oldSpawn = mod.spawn
if (mod.patched) {
return
}
mod.patched = true
mod.spawn = (file, args, opt) => {
let terminal = oldSpawn(file, args, opt)
let timeout = null
let buffer = Buffer.from('')
let lastFlush = 0
let nextTimeout = 0
// Minimum prebuffering window (ms) if the input is non-stop flowing
const minWindow = 10
// Maximum buffering time (ms) until output must be flushed unconditionally
const maxWindow = 100
function flush () {
if (buffer.length) {
terminal.emit('data-buffered', buffer)
}
lastFlush = Date.now()
buffer = Buffer.from('')
}
function reschedule () {
if (timeout) {
clearTimeout(timeout)
}
nextTimeout = Date.now() + minWindow
timeout = setTimeout(() => {
timeout = null
flush()
}, minWindow)
}
terminal.on('data', data => {
if (typeof data === 'string') {
data = Buffer.from(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) {
// Extend the window if it's expiring
reschedule()
}
}
})
return terminal
}
}

View File

@@ -15,10 +15,10 @@ export class ButtonProvider extends ToolbarButtonProvider {
private terminal: TerminalService, private terminal: TerminalService,
) { ) {
super() super()
if (!electron.remote.process.env.TERMINUS_DEV) { if (!electron.process.env.TERMINUS_DEV) {
setImmediate(async () => { setImmediate(async () => {
const argv: string[] = electron.remote.process.argv const argv: string[] = electron.process.argv
for (const arg of argv.slice(1).concat([electron.remote.process.argv0])) { for (const arg of argv.slice(1).concat([electron.process.argv0])) {
if (await fs.exists(arg)) { if (await fs.exists(arg)) {
if ((await fs.stat(arg)).isDirectory()) { if ((await fs.stat(arg)).isDirectory()) {
this.terminal.openTab(undefined, arg) this.terminal.openTab(undefined, arg)

View File

@@ -33,19 +33,6 @@ h3.mb-3 Appearance
.col-12.col-md-6 .col-12.col-md-6
color-scheme-preview([scheme]='config.store.terminal.colorScheme', [fontPreview]='true') color-scheme-preview([scheme]='config.store.terminal.colorScheme', [fontPreview]='true')
.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 .form-line
.header .header
.title Terminal background .title Terminal background
@@ -110,24 +97,6 @@ h3.mb-3 Appearance
(ngModelChange)='config.save()', (ngModelChange)='config.save()',
) )
.form-line
.header
.title Hide tab index
toggle(
[(ngModel)]='config.store.terminal.hideTabIndex',
(ngModelChange)='config.save();',
)
.form-line
.header
.title Hide tab close button
toggle(
[(ngModel)]='config.store.terminal.hideCloseButton',
(ngModelChange)='config.save();',
)
.form-line .form-line
.header .header
.title Fallback font .title Fallback font
@@ -139,3 +108,13 @@ h3.mb-3 Appearance
[(ngModel)]='config.store.terminal.fallbackFont', [(ngModel)]='config.store.terminal.fallbackFont',
(ngModelChange)='config.save()' (ngModelChange)='config.save()'
) )
.form-line
.header
.title Custom CSS
textarea.form-control(
[(ngModel)]='config.store.appearance.css',
(ngModelChange)='saveConfiguration()',
)

View File

@@ -2,3 +2,8 @@ color-scheme-preview {
flex-shrink: 0; flex-shrink: 0;
margin-bottom: 20px; margin-bottom: 20px;
} }
textarea {
font-family: 'Source Code Pro', monospace;
min-height: 120px;
}

View File

@@ -23,11 +23,7 @@ export class AppearanceSettingsTabComponent {
async ngOnInit () { async ngOnInit () {
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) { if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
const fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve)) const fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve))
if (this.hostApp.platform === Platform.Windows) { this.fonts = fonts.map(x => x.family.trim())
this.fonts = fonts.map(x => `${x.family} ${x.style}`.trim())
} else {
this.fonts = fonts.map(x => x.family.trim())
}
this.fonts.sort() this.fonts.sort()
} }
if (this.hostApp.platform === Platform.Linux) { if (this.hostApp.platform === Platform.Linux) {

View File

@@ -1,5 +1,5 @@
input.search-input.form-control( input.search-input.form-control(
type='search', type='text',
[(ngModel)]='query', [(ngModel)]='query',
(ngModelChange)='onQueryChange()', (ngModelChange)='onQueryChange()',
[class.text-danger]='notFound', [class.text-danger]='notFound',
@@ -11,14 +11,14 @@ input.search-input.form-control(
button.btn.btn-link( button.btn.btn-link(
(click)='findPrevious()', (click)='findPrevious()',
ngbTooltip='Next', ngbTooltip='Search up',
placement='bottom' placement='bottom'
) )
i.fa.fa-fw.fa-arrow-up i.fa.fa-fw.fa-arrow-up
button.btn.btn-link( button.btn.btn-link(
(click)='findNext()', (click)='findNext()',
ngbTooltip='Next', ngbTooltip='Search down',
placement='bottom' placement='bottom'
) )
i.fa.fa-fw.fa-arrow-down i.fa.fa-fw.fa-arrow-down
@@ -48,4 +48,7 @@ button.btn.btn-link(
) )
i.fa.fa-fw.fa-text-width i.fa.fa-fw.fa-text-width
button.close.text-light.pl-3.pr-2((click)='close.emit()') &times; .mr-2
button.btn.btn-link((click)='close.emit()')
i.fa.fa-fw.fa-times

View File

@@ -1,5 +1,18 @@
h3.mb-3 Terminal h3.mb-3 Terminal
.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 .form-line
.header .header
.title Terminal bell .title Terminal bell
@@ -116,7 +129,7 @@ h3.mb-3 Terminal
[(ngModel)]='config.store.terminal.scrollOnInput', [(ngModel)]='config.store.terminal.scrollOnInput',
(ngModelChange)='config.save()', (ngModelChange)='config.save()',
) )
.form-line .form-line
.header .header
.title Use Alt key as the Meta key .title Use Alt key as the Meta key

View File

@@ -1,6 +1,5 @@
import { Component, Input, Injector } from '@angular/core' import { Component, Input, Injector } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { first } from 'rxjs/operators'
import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core'
import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component' import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component'
import { SessionOptions } from '../api/interfaces' import { SessionOptions } from '../api/interfaces'
@@ -16,6 +15,7 @@ import { Session } from '../services/sessions.service'
export class TerminalTabComponent extends BaseTerminalTabComponent { export class TerminalTabComponent extends BaseTerminalTabComponent {
@Input() sessionOptions: SessionOptions @Input() sessionOptions: SessionOptions
private homeEndSubscription: Subscription private homeEndSubscription: Subscription
session: Session|null = null
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor ( constructor (
@@ -44,13 +44,15 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
} }
}) })
this.frontendReady$.pipe(first()).subscribe(() => {
this.initializeSession(this.size.columns, this.size.rows)
})
super.ngOnInit() super.ngOnInit()
} }
protected onFrontendReady (): void {
this.initializeSession(this.size.columns, this.size.rows)
this.savedStateIsLive = this.sessionOptions.restoreFromPTYID === this.session?.getPTYID()
super.onFrontendReady()
}
initializeSession (columns: number, rows: number): void { initializeSession (columns: number, rows: number): void {
this.sessions.addSession( this.sessions.addSession(
this.session!, this.session!,
@@ -61,6 +63,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
) )
this.attachSessionHandlers(true) this.attachSessionHandlers(true)
this.recoveryStateChangedHint.next()
} }
async getRecoveryToken (): Promise<any> { async getRecoveryToken (): Promise<any> {
@@ -70,6 +73,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
sessionOptions: { sessionOptions: {
...this.sessionOptions, ...this.sessionOptions,
cwd: cwd ?? this.sessionOptions.cwd, cwd: cwd ?? this.sessionOptions.cwd,
restoreFromPTYID: this.session?.getPTYID(),
}, },
savedState: this.frontend?.saveState(), savedState: this.frontend?.saveState(),
} }

View File

@@ -75,6 +75,7 @@ export class TerminalConfigProvider extends ConfigProvider {
caseSensitive: false, caseSensitive: false,
}, },
detectProgress: true, detectProgress: true,
scrollbackLines: 25000,
}, },
} }
@@ -96,6 +97,7 @@ export class TerminalConfigProvider extends ConfigProvider {
clear: [ clear: [
'⌘-K', '⌘-K',
], ],
'select-all': ['⌘-A'],
'zoom-in': [ 'zoom-in': [
'⌘-=', '⌘-=',
'⌘-Shift-=', '⌘-Shift-=',
@@ -141,6 +143,7 @@ export class TerminalConfigProvider extends ConfigProvider {
paste: [ paste: [
'Ctrl-Shift-V', 'Ctrl-Shift-V',
], ],
'select-all': ['Ctrl-Shift-A'],
clear: [], clear: [],
'zoom-in': [ 'zoom-in': [
'Ctrl-=', 'Ctrl-=',
@@ -184,6 +187,7 @@ export class TerminalConfigProvider extends ConfigProvider {
paste: [ paste: [
'Ctrl-Shift-V', 'Ctrl-Shift-V',
], ],
'select-all': ['Ctrl-Shift-A'],
clear: [], clear: [],
'zoom-in': [ 'zoom-in': [
'Ctrl-=', 'Ctrl-=',

Binary file not shown.

View File

@@ -57,11 +57,12 @@ export abstract class Frontend {
} }
} }
abstract attach (host: HTMLElement): void abstract async attach (host: HTMLElement): Promise<void>
detach (host: HTMLElement): void { } // eslint-disable-line detach (host: HTMLElement): void { } // eslint-disable-line
abstract getSelection (): string abstract getSelection (): string
abstract copySelection (): void abstract copySelection (): void
abstract selectAll (): void
abstract clearSelection (): void abstract clearSelection (): void
abstract focus (): void abstract focus (): void
abstract write (data: string): void abstract write (data: string): void

View File

@@ -31,5 +31,5 @@ x-row > span {
@font-face { @font-face {
font-family: "monospace-fallback"; font-family: "monospace-fallback";
src: url(../fonts/Meslo.otf) format("opentype"); src: url(../fonts/SourceCodePro.otf) format("opentype");
} }

View File

@@ -13,7 +13,7 @@ export class HTermFrontend extends Frontend {
private configuredBackgroundColor = 'transparent' private configuredBackgroundColor = 'transparent'
private zoom = 0 private zoom = 0
attach (host: HTMLElement): void { async attach (host: HTMLElement): Promise<void> {
if (!this.initialized) { if (!this.initialized) {
this.init() this.init()
this.initialized = true this.initialized = true
@@ -33,6 +33,12 @@ export class HTermFrontend extends Frontend {
this.term.copySelectionToClipboard() this.term.copySelectionToClipboard()
} }
selectAll (): void {
const content = this.term.getDocument().body.children[0]
const selection = content.ownerDocument.defaultView.getSelection()
selection.setBaseAndExtent(content, 0, content, 1)
}
clearSelection (): void { clearSelection (): void {
this.term.getDocument().getSelection().removeAllRanges() this.term.getDocument().getSelection().removeAllRanges()
} }

View File

@@ -174,7 +174,7 @@
@font-face { @font-face {
font-family: "monospace-fallback"; font-family: "monospace-fallback";
src: url(../fonts/Meslo.otf) format("opentype"); src: url(../fonts/SourceCodePro.otf) format("opentype");
} }
.xterm-viewport::-webkit-scrollbar { .xterm-viewport::-webkit-scrollbar {

View File

@@ -131,12 +131,15 @@ export class XTermFrontend extends Frontend {
}) })
} }
attach (host: HTMLElement): void { async attach (host: HTMLElement): Promise<void> {
this.configure() this.configure()
this.xterm.open(host) this.xterm.open(host)
this.opened = true this.opened = true
// Work around font loading bugs
await new Promise(resolve => setTimeout(resolve, process.env.XWEB ? 1000 : 0))
if (this.enableWebGL) { if (this.enableWebGL) {
this.xterm.loadAddon(new WebglAddon()) this.xterm.loadAddon(new WebglAddon())
} }
@@ -172,17 +175,21 @@ export class XTermFrontend extends Frontend {
copySelection (): void { copySelection (): void {
const text = this.getSelection() const text = this.getSelection()
if (text.length < 1024 * 32) { if (text.length < 1024 * 32) {
require('electron').remote.clipboard.write({ require('@electron/remote').clipboard.write({
text: this.getSelection(), text: this.getSelection(),
html: this.getSelectionAsHTML(), html: this.getSelectionAsHTML(),
}) })
} else { } else {
require('electron').remote.clipboard.write({ require('@electron/remote').clipboard.write({
text: this.getSelection(), text: this.getSelection(),
}) })
} }
} }
selectAll (): void {
this.xterm.selectAll()
}
clearSelection (): void { clearSelection (): void {
this.xterm.clearSelection() this.xterm.clearSelection()
} }
@@ -229,7 +236,7 @@ export class XTermFrontend extends Frontend {
}[config.terminal.cursor] || config.terminal.cursor) }[config.terminal.cursor] || config.terminal.cursor)
this.xterm.setOption('cursorBlink', config.terminal.cursorBlink) this.xterm.setOption('cursorBlink', config.terminal.cursorBlink)
this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta) this.xterm.setOption('macOptionIsMeta', config.terminal.altIsMeta)
this.xterm.setOption('scrollback', 100000) this.xterm.setOption('scrollback', config.terminal.scrollbackLines)
this.xterm.setOption('wordSeparator', config.terminal.wordSeparator) this.xterm.setOption('wordSeparator', config.terminal.wordSeparator)
this.configuredFontSize = config.terminal.fontSize this.configuredFontSize = config.terminal.fontSize
this.configuredLinePadding = config.terminal.linePadding this.configuredLinePadding = config.terminal.linePadding

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 65 57" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" width="300" height="300" font-family="Roboto" font-size="14px" text-anchor="middle"><defs><style type="text/css"></style></defs><use xlink:href="#A" x=".5" y=".5"></use><symbol id="A" overflow="visible"><g stroke="none" fill="#0d597f" fill-rule="nonzero"><path d="M23.252 34.527v-6.745l-4.855 4.864c.474.333.968.635 1.48.906.463.243.87.434 1.303.58a7.97 7.97 0 0 0 1.13.304c.348.064.66.093.95.096m24.822-.562a1.17 1.17 0 0 0 .142.1c.123.078.252.145.385.203a2.93 2.93 0 0 0 .637.194c.296.06.598.088.9.087a5.84 5.84 0 0 0 .955-.087 7.24 7.24 0 0 0 1.138-.301c.453-.16.895-.354 1.32-.58.52-.274 1.02-.58 1.503-.918l-3.685-3.6-12.2-12.258-5.356 5.356-7.23-7.455L8.44 32.647c.48.337.98.644 1.5.918.47.246.9.434 1.317.58a7.18 7.18 0 0 0 1.135.301 5.53 5.53 0 0 0 .955.087 4.53 4.53 0 0 0 .9-.087 3.29 3.29 0 0 0 .637-.194c.134-.054.263-.12.385-.197l.145-.104 8.193-8.193 2.924-2.808 8.106 8.106 2.837 2.912c.046.037.094.07.145.1.122.078.25.145.385.2a3.49 3.49 0 0 0 .637.194c.255.052.556.087.903.087a5.84 5.84 0 0 0 .955-.087c.387-.068.768-.168 1.138-.3a9.94 9.94 0 0 0 1.32-.579c.52-.274 1.02-.58 1.503-.918l-6.508-6.37 1.2-1.2 5.63 5.63 3.283 3.254M47.996.02l15.998 27.714-15.99 27.694H15.998L0 27.714 15.998 0z"></path><path d="M38.022 26.367L33.76 22.11l.304-.304 4.3 4.244z"></path></g></symbol></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" font-family="Roboto" font-size="14" text-anchor="middle" viewBox="0 0 65 57"><defs><style type="text/css"/></defs><use x=".5" y=".5" xlink:href="#A"/><symbol id="A" overflow="visible"><g fill="#0d597f" fill-rule="nonzero" stroke="none"><path d="M23.252 34.527v-6.745l-4.855 4.864c.474.333.968.635 1.48.906.463.243.87.434 1.303.58a7.97 7.97 0 0 0 1.13.304c.348.064.66.093.95.096m24.822-.562a1.17 1.17 0 0 0 .142.1c.123.078.252.145.385.203a2.93 2.93 0 0 0 .637.194c.296.06.598.088.9.087a5.84 5.84 0 0 0 .955-.087 7.24 7.24 0 0 0 1.138-.301c.453-.16.895-.354 1.32-.58.52-.274 1.02-.58 1.503-.918l-3.685-3.6-12.2-12.258-5.356 5.356-7.23-7.455L8.44 32.647c.48.337.98.644 1.5.918.47.246.9.434 1.317.58a7.18 7.18 0 0 0 1.135.301 5.53 5.53 0 0 0 .955.087 4.53 4.53 0 0 0 .9-.087 3.29 3.29 0 0 0 .637-.194c.134-.054.263-.12.385-.197l.145-.104 8.193-8.193 2.924-2.808 8.106 8.106 2.837 2.912c.046.037.094.07.145.1.122.078.25.145.385.2a3.49 3.49 0 0 0 .637.194c.255.052.556.087.903.087a5.84 5.84 0 0 0 .955-.087c.387-.068.768-.168 1.138-.3a9.94 9.94 0 0 0 1.32-.579c.52-.274 1.02-.58 1.503-.918l-6.508-6.37 1.2-1.2 5.63 5.63 3.283 3.254M47.996.02l15.998 27.714-15.99 27.694H15.998L0 27.714 15.998 0z"/><path d="M38.022 26.367L33.76 22.11l.304-.304 4.3 4.244z"/></g></symbol></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="windows" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-windows fa-w-14 fa-2x"><path fill="cyan" d="M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-windows fa-w-14 fa-2x" data-icon="windows" data-prefix="fab" focusable="false" role="img" viewBox="0 0 448 512"><path fill="#0ff" stroke="none" stroke-width="1" d="M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z"/></svg>

Before

Width:  |  Height:  |  Size: 410 B

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="windows" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-windows fa-w-14 fa-2x"><path fill="#ffffff" d="M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-windows fa-w-14 fa-2x" data-icon="windows" data-prefix="fab" focusable="false" role="img" viewBox="0 0 448 512"><path fill="#fff" stroke="none" stroke-width="1" d="M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z"/></svg>

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lambda" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-lambda fa-w-14 fa-2x"><path fill="cyan" d="M424 384h-43.5L197.6 48.68A32.018 32.018 0 0 0 169.5 32H24C10.75 32 0 42.74 0 56v48c0 13.25 10.75 24 24 24h107.5l4.63 8.49L3.25 446.55C-3.53 462.38 8.08 480 25.31 480h52.23c9.6 0 18.28-5.72 22.06-14.55l95.02-221.72L314.4 463.32A32.018 32.018 0 0 0 342.5 480H424c13.25 0 24-10.75 24-24v-48c0-13.26-10.75-24-24-24z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-lambda fa-w-14 fa-2x" data-icon="lambda" data-prefix="fas" focusable="false" role="img" viewBox="0 0 448 512"><path fill="#0ff" stroke="none" stroke-width="1" d="M424 384h-43.5L197.6 48.68A32.018 32.018 0 0 0 169.5 32H24C10.75 32 0 42.74 0 56v48c0 13.25 10.75 24 24 24h107.5l4.63 8.49L3.25 446.55C-3.53 462.38 8.08 480 25.31 480h52.23c9.6 0 18.28-5.72 22.06-14.55l95.02-221.72L314.4 463.32A32.018 32.018 0 0 0 342.5 480H424c13.25 0 24-10.75 24-24v-48c0-13.26-10.75-24-24-24z"/></svg>

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 567 B

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lambda" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-lambda fa-w-14 fa-2x"><path fill="#8ab91d" d="M424 384h-43.5L197.6 48.68A32.018 32.018 0 0 0 169.5 32H24C10.75 32 0 42.74 0 56v48c0 13.25 10.75 24 24 24h107.5l4.63 8.49L3.25 446.55C-3.53 462.38 8.08 480 25.31 480h52.23c9.6 0 18.28-5.72 22.06-14.55l95.02-221.72L314.4 463.32A32.018 32.018 0 0 0 342.5 480H424c13.25 0 24-10.75 24-24v-48c0-13.26-10.75-24-24-24z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-lambda fa-w-14 fa-2x" data-icon="lambda" data-prefix="fas" focusable="false" role="img" viewBox="0 0 448 512"><path fill="#8ab91d" stroke="none" stroke-width="1" d="M424 384h-43.5L197.6 48.68A32.018 32.018 0 0 0 169.5 32H24C10.75 32 0 42.74 0 56v48c0 13.25 10.75 24 24 24h107.5l4.63 8.49L3.25 446.55C-3.53 462.38 8.08 480 25.31 480h52.23c9.6 0 18.28-5.72 22.06-14.55l95.02-221.72L314.4 463.32A32.018 32.018 0 0 0 342.5 480H424c13.25 0 24-10.75 24-24v-48c0-13.26-10.75-24-24-24z"/></svg>

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 570 B

View File

@@ -1,6 +1 @@
<svg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g stroke-linejoin="round" stroke-width="4"><path fill="#eee" d="M94,19l-28-9h-39c-9,0-19,9-19,19v47c0,9,10,19,19,19h39l28-10l-28-9h-26c-9,0-13-3-13-13v-22c0-10,4-13,13-13h26z"/><path fill="#0f0" d="M94,52l-41-11h-13v3c10,0,10,16,0,16v3h13z"/></g></svg>
<g stroke-linejoin='round' stroke-width='4'>
<path d='M94,19l-28-9h-39c-9,0-19,9-19,19v47c0,9,10,19,19,19h39l28-10l-28-9h-26c-9,0-13-3-13-13v-22c0-10,4-13,13-13h26z' fill='#eee' />
<path d='M94,52l-41-11h-13v3c10,0,10,16,0,16v3h13z' fill='lime'/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 322 B

After

Width:  |  Height:  |  Size: 315 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="git-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-git-alt fa-w-14 fa-3x"><path fill="#f05033" d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-git-alt fa-w-14 fa-3x" data-icon="git-alt" data-prefix="fab" focusable="false" role="img" viewBox="0 0 448 512"><path fill="#f05033" stroke="none" stroke-width="1" d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 718 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="plus" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" class="svg-inline--fa fa-plus fa-w-12 fa-3x"><path fill="#ffffff" d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-plus fa-w-12 fa-3x" data-icon="plus" data-prefix="fal" focusable="false" role="img" viewBox="0 0 384 512"><path fill="#fff" stroke="none" stroke-width="1" d="M376 232H216V72c0-4.42-3.58-8-8-8h-32c-4.42 0-8 3.58-8 8v160H8c-4.42 0-8 3.58-8 8v32c0 4.42 3.58 8 8 8h160v160c0 4.42 3.58 8 8 8h32c4.42 0 8-3.58 8-8V280h160c4.42 0 8-3.58 8-8v-32c0-4.42-3.58-8-8-8z"/></svg>

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="terminal" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-terminal fa-w-20 fa-2x"><path fill="purple" d="M257.981 272.971L63.638 467.314c-9.373 9.373-24.569 9.373-33.941 0L7.029 444.647c-9.357-9.357-9.375-24.522-.04-33.901L161.011 256 6.99 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L257.981 239.03c9.373 9.372 9.373 24.568 0 33.941zM640 456v-32c0-13.255-10.745-24-24-24H312c-13.255 0-24 10.745-24 24v32c0 13.255 10.745 24 24 24h304c13.255 0 24-10.745 24-24z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-terminal fa-w-20 fa-2x" data-icon="terminal" data-prefix="fas" focusable="false" role="img" viewBox="0 0 640 512"><path fill="purple" stroke="none" stroke-width="1" d="M257.981 272.971L63.638 467.314c-9.373 9.373-24.569 9.373-33.941 0L7.029 444.647c-9.357-9.357-9.375-24.522-.04-33.901L161.011 256 6.99 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L257.981 239.03c9.373 9.372 9.373 24.568 0 33.941zM640 456v-32c0-13.255-10.745-24-24-24H312c-13.255 0-24 10.745-24 24v32c0 13.255 10.745 24 24 24h304c13.255 0 24-10.745 24-24z"/></svg>

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 662 B

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="terminal" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-terminal fa-w-20 fa-2x"><path fill="cyan" d="M257.981 272.971L63.638 467.314c-9.373 9.373-24.569 9.373-33.941 0L7.029 444.647c-9.357-9.357-9.375-24.522-.04-33.901L161.011 256 6.99 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L257.981 239.03c9.373 9.372 9.373 24.568 0 33.941zM640 456v-32c0-13.255-10.745-24-24-24H312c-13.255 0-24 10.745-24 24v32c0 13.255 10.745 24 24 24h304c13.255 0 24-10.745 24-24z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-terminal fa-w-20 fa-2x" data-icon="terminal" data-prefix="fas" focusable="false" role="img" viewBox="0 0 640 512"><path fill="#0ff" stroke="none" stroke-width="1" d="M257.981 272.971L63.638 467.314c-9.373 9.373-24.569 9.373-33.941 0L7.029 444.647c-9.357-9.357-9.375-24.522-.04-33.901L161.011 256 6.99 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L257.981 239.03c9.373 9.372 9.373 24.568 0 33.941zM640 456v-32c0-13.255-10.745-24-24-24H312c-13.255 0-24 10.745-24 24v32c0 13.255 10.745 24 24 24h304c13.255 0 24-10.745 24-24z"/></svg>

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 660 B

View File

@@ -1 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fal" data-icon="window-restore" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-window-restore fa-w-16 fa-3x"><path fill="#ffffff" d="M464 0H144c-26.5 0-48 21.5-48 48v48H48c-26.5 0-48 21.5-48 48v320c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zM32 144c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16v80H32v-80zm352 320c0 8.8-7.2 16-16 16H48c-8.8 0-16-7.2-16-16V256h352v208zm96-96c0 8.8-7.2 16-16 16h-48V144c0-26.5-21.5-48-48-48H128V48c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16v320z" class="" stroke="none" stroke-width="1px"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="svg-inline--fa fa-window-restore fa-w-16 fa-3x" data-icon="window-restore" data-prefix="fal" focusable="false" role="img" viewBox="0 0 512 512"><path fill="#fff" stroke="none" stroke-width="1" d="M464 0H144c-26.5 0-48 21.5-48 48v48H48c-26.5 0-48 21.5-48 48v320c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zM32 144c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16v80H32v-80zm352 320c0 8.8-7.2 16-16 16H48c-8.8 0-16-7.2-16-16V256h352v208zm96-96c0 8.8-7.2 16-16 16h-48V144c0-26.5-21.5-48-48-48H128V48c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16v320z"/></svg>

Before

Width:  |  Height:  |  Size: 685 B

After

Width:  |  Height:  |  Size: 665 B

View File

@@ -1,47 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg8" width="256" height="256" version="1.1" viewBox="0 0 256 256"><metadata id="metadata5"/><g id="layer1" transform="matrix(6.9999999,0,0,6.9999736,16,-7617.6082)" style="fill:#73ba25;fill-opacity:1"><g style="fill:#73ba25;fill-opacity:1" id="g838" transform="matrix(0.26458333,0,0,0.26458333,-10.590624,-38.473045)"><circle id="path872" cx="507.464" cy="3582.83" r="0" style="opacity:.3;fill:#73ba25;fill-opacity:1;stroke:none;stroke-width:1.90573967;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path id="path819" d="m 100.5002,4267.059 a 60.472077,60.472442 0 0 0 -47.607132,23.2988 c 7.375955,1.9706 12.596534,3.6642 14.160064,4.1895 0.0245,-0.9593 0.183589,-9.5391 0.183589,-9.5391 0,0 0.0202,-0.1964 0.124999,-0.2988 0.13497,-0.1318 0.330078,-0.092 0.330078,-0.092 1.939989,0.281 43.348482,6.4189 60.802382,16.5899 2.15548,1.261 3.21941,2.6017 4.5488,3.9609 4.82477,4.99 11.19998,25.7389 11.88469,30.0176 0.0269,0.1681 -0.18083,0.3507 -0.26953,0.4199 h -0.002 c -0.4957,0.3868 -1.03554,0.789 -1.57616,1.1484 -4.12998,2.7709 -13.64449,9.4312 -25.85142,8.3438 -10.96493,-0.97 -25.290388,-7.2597 -42.560284,-18.6387 1.69799,3.9756 3.371,7.9635 5.04489,11.9492 2.500985,1.299 26.640524,13.5997 38.554464,13.3594 9.59593,-0.1999 19.85892,-4.8804 23.96469,-7.3516 0,0 0.90227,-0.5436 1.29491,-0.2402 0.4295,0.3318 0.31068,0.8402 0.20898,1.3594 -0.25259,1.1786 -0.82764,3.3289 -1.21873,4.3496 l -0.33008,0.832 c -0.46999,1.2592 -0.92111,2.4296 -1.79101,3.1504 -2.41868,2.1993 -6.27908,3.9491 -12.32804,6.5781 -9.34995,4.09 -24.51938,6.6911 -38.603293,6.6016 -5.04437,-0.1123 -9.91781,-0.672 -14.197174,-1.1719 -8.782187,-0.9915 -15.927854,-1.7959 -20.285038,1.3555 a 60.472077,60.472442 0 0 0 45.517305,20.7734 60.472077,60.472442 0 0 0 60.47229,-60.4726 60.472077,60.472442 0 0 0 -60.47229,-60.4727 z m 13.4882,35.0879 c -4.73327,-0.1509 -9.24668,1.5194 -12.70695,4.75 -3.458684,3.2199 -5.437952,7.6097 -5.613251,12.3399 -0.326998,9.7581 7.334241,17.9803 17.083881,18.3398 4.75477,0.1596 9.25839,-1.5118 12.71867,-4.7617 3.44988,-3.2099 5.42915,-7.5999 5.61325,-12.3301 0.335,-9.7494 -7.33546,-17.9889 -17.0956,-18.3379 z m -0.14844,5.2188 c 6.82096,0.242 12.16127,5.972 11.93157,12.791 -0.1053,3.2885 -1.49253,6.3369 -3.90231,8.5976 -2.41329,2.2502 -5.56743,3.4203 -8.87691,3.3203 -6.80516,-0.251 -12.14564,-5.9877 -11.91594,-12.8085 0.1,-3.3008 1.51475,-6.3495 3.91403,-8.5997 2.39919,-2.2502 5.53828,-3.42 8.84956,-3.3007 z m 2.02147,6.2011 c -3.03067,0 -5.47848,1.631 -5.47848,3.6602 0,2.01 2.44781,3.6504 5.47848,3.6504 3.02888,0 5.4863,-1.6405 5.4863,-3.6504 0,-2.0292 -2.45572,-3.6602 -5.4863,-3.6602 z" style="opacity:1;fill:#73ba25;fill-opacity:1;stroke:none;stroke-width:1.90559804;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/></g></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 256 256.00001"
height="256"
width="256">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="matrix(6.9999999,0,0,6.9999736,16,-7617.6082)"
id="layer1"
style="fill:#73ba25;fill-opacity:1">
<g
style="fill:#73ba25;fill-opacity:1"
transform="matrix(0.26458333,0,0,0.26458333,-10.590624,-38.473045)"
id="g838">
<circle
r="0"
cy="3582.8301"
cx="507.46362"
id="path872"
style="opacity:0.3;fill:#73ba25;fill-opacity:1;stroke:none;stroke-width:1.90573967;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path819"
d="m 100.5002,4267.059 a 60.472077,60.472442 0 0 0 -47.607132,23.2988 c 7.375955,1.9706 12.596534,3.6642 14.160064,4.1895 0.0245,-0.9593 0.183589,-9.5391 0.183589,-9.5391 0,0 0.0202,-0.1964 0.124999,-0.2988 0.13497,-0.1318 0.330078,-0.092 0.330078,-0.092 1.939989,0.281 43.348482,6.4189 60.802382,16.5899 2.15548,1.261 3.21941,2.6017 4.5488,3.9609 4.82477,4.99 11.19998,25.7389 11.88469,30.0176 0.0269,0.1681 -0.18083,0.3507 -0.26953,0.4199 h -0.002 c -0.4957,0.3868 -1.03554,0.789 -1.57616,1.1484 -4.12998,2.7709 -13.64449,9.4312 -25.85142,8.3438 -10.96493,-0.97 -25.290388,-7.2597 -42.560284,-18.6387 1.69799,3.9756 3.371,7.9635 5.04489,11.9492 2.500985,1.299 26.640524,13.5997 38.554464,13.3594 9.59593,-0.1999 19.85892,-4.8804 23.96469,-7.3516 0,0 0.90227,-0.5436 1.29491,-0.2402 0.4295,0.3318 0.31068,0.8402 0.20898,1.3594 -0.25259,1.1786 -0.82764,3.3289 -1.21873,4.3496 l -0.33008,0.832 c -0.46999,1.2592 -0.92111,2.4296 -1.79101,3.1504 -2.41868,2.1993 -6.27908,3.9491 -12.32804,6.5781 -9.34995,4.09 -24.51938,6.6911 -38.603293,6.6016 -5.04437,-0.1123 -9.91781,-0.672 -14.197174,-1.1719 -8.782187,-0.9915 -15.927854,-1.7959 -20.285038,1.3555 a 60.472077,60.472442 0 0 0 45.517305,20.7734 60.472077,60.472442 0 0 0 60.47229,-60.4726 60.472077,60.472442 0 0 0 -60.47229,-60.4727 z m 13.4882,35.0879 c -4.73327,-0.1509 -9.24668,1.5194 -12.70695,4.75 -3.458684,3.2199 -5.437952,7.6097 -5.613251,12.3399 -0.326998,9.7581 7.334241,17.9803 17.083881,18.3398 4.75477,0.1596 9.25839,-1.5118 12.71867,-4.7617 3.44988,-3.2099 5.42915,-7.5999 5.61325,-12.3301 0.335,-9.7494 -7.33546,-17.9889 -17.0956,-18.3379 z m -0.14844,5.2188 c 6.82096,0.242 12.16127,5.972 11.93157,12.791 -0.1053,3.2885 -1.49253,6.3369 -3.90231,8.5976 -2.41329,2.2502 -5.56743,3.4203 -8.87691,3.3203 -6.80516,-0.251 -12.14564,-5.9877 -11.91594,-12.8085 0.1,-3.3008 1.51475,-6.3495 3.91403,-8.5997 2.39919,-2.2502 5.53828,-3.42 8.84956,-3.3007 z m 2.02147,6.2011 c -3.03067,0 -5.47848,1.631 -5.47848,3.6602 0,2.01 2.44781,3.6504 5.47848,3.6504 3.02888,0 5.4863,-1.6405 5.4863,-3.6504 0,-2.0292 -2.45572,-3.6602 -5.4863,-3.6602 z"
style="opacity:1;fill:#73ba25;fill-opacity:1;stroke:none;stroke-width:1.90559804;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,7 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" preserveAspectRatio="xMidYMid" version="1.1" viewBox="0 0 256 256"><g><path fill="#DD4814" d="M255.637396,127.683191 C255.637396,198.196551 198.47207,255.363378 127.954205,255.363378 C57.4348387,255.363378 0.27026393,198.196551 0.27026393,127.683191 C0.27026393,57.1653255 57.4355894,0 127.954205,0 C198.472821,0 255.637396,57.1653255 255.637396,127.683191 L255.637396,127.683191 Z"/><path fill="#FFF" d="M41.1334194,110.63254 C31.7139707,110.63254 24.0827683,118.264493 24.0827683,127.683191 C24.0827683,137.097384 31.7139707,144.728587 41.1334194,144.728587 C50.5476129,144.728587 58.1788152,137.097384 58.1788152,127.683191 C58.1788152,118.264493 50.5476129,110.63254 41.1334194,110.63254 L41.1334194,110.63254 Z M162.848282,188.111202 C154.694569,192.820551 151.898839,203.240727 156.608938,211.389935 C161.313032,219.543648 171.733208,222.338628 179.886921,217.629279 C188.039883,212.925185 190.835613,202.505009 186.126264,194.350545 C181.42217,186.202088 170.995988,183.407109 162.848282,188.111202 L162.848282,188.111202 Z M78.1618299,127.683191 C78.1618299,110.836739 86.5295015,95.9534545 99.3332551,86.9409032 L86.8703343,66.0667683 C71.9555191,76.0365044 60.8581818,91.271132 56.2464282,109.113806 C61.6276833,113.504845 65.0720469,120.189372 65.0720469,127.68244 C65.0720469,135.171003 61.6276833,141.855531 56.2464282,146.246569 C60.852176,164.094499 71.9495132,179.329877 86.8703343,189.299613 L99.3332551,168.420223 C86.5295015,159.412927 78.1618299,144.530393 78.1618299,127.683191 L78.1618299,127.683191 Z M127.954205,77.8855601 C153.967109,77.8855601 175.30895,97.8302874 177.549138,123.265877 L201.839859,122.907777 C200.644692,104.129689 192.441431,87.2719765 179.836622,74.875871 C173.354792,77.3247625 165.86773,76.9501466 159.396411,73.2197537 C152.91383,69.4788504 148.849361,63.1681877 147.738276,56.3177478 C141.438123,54.5790499 134.807648,53.6271202 127.952704,53.6271202 C116.168446,53.6271202 105.026815,56.3950733 95.1344047,61.2913548 L106.979472,82.5175836 C113.351695,79.5521877 120.460387,77.8855601 127.954205,77.8855601 L127.954205,77.8855601 Z M127.954205,177.475566 C120.460387,177.475566 113.351695,175.808188 106.980223,172.843543 L95.1351554,194.069021 C105.027566,198.971308 116.169196,201.740012 127.954205,201.740012 C134.80915,201.740012 141.439625,200.787331 147.739026,199.043378 C148.850111,192.192938 152.916082,185.888282 159.397161,182.140622 C165.872985,178.404223 173.355543,178.036364 179.837372,180.485255 C192.442182,168.08915 200.645443,151.231437 201.84061,132.453349 L177.543883,132.095249 C175.30895,157.537595 153.967859,177.475566 127.954205,177.475566 L127.954205,177.475566 Z M162.842276,67.2446686 C170.995988,71.9532669 181.416915,69.1642933 186.121009,61.0105806 C190.830358,52.856868 188.041384,42.4359413 179.886921,37.7258416 C171.733208,33.0217478 161.313032,35.8167273 156.602182,43.9704399 C151.898839,52.1196481 154.693818,62.5405748 162.842276,67.2446686 L162.842276,67.2446686 Z"/></g></svg>
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M255.637396,127.683191 C255.637396,198.196551 198.47207,255.363378 127.954205,255.363378 C57.4348387,255.363378 0.27026393,198.196551 0.27026393,127.683191 C0.27026393,57.1653255 57.4355894,0 127.954205,0 C198.472821,0 255.637396,57.1653255 255.637396,127.683191 L255.637396,127.683191 Z" fill="#DD4814"></path>
<path d="M41.1334194,110.63254 C31.7139707,110.63254 24.0827683,118.264493 24.0827683,127.683191 C24.0827683,137.097384 31.7139707,144.728587 41.1334194,144.728587 C50.5476129,144.728587 58.1788152,137.097384 58.1788152,127.683191 C58.1788152,118.264493 50.5476129,110.63254 41.1334194,110.63254 L41.1334194,110.63254 Z M162.848282,188.111202 C154.694569,192.820551 151.898839,203.240727 156.608938,211.389935 C161.313032,219.543648 171.733208,222.338628 179.886921,217.629279 C188.039883,212.925185 190.835613,202.505009 186.126264,194.350545 C181.42217,186.202088 170.995988,183.407109 162.848282,188.111202 L162.848282,188.111202 Z M78.1618299,127.683191 C78.1618299,110.836739 86.5295015,95.9534545 99.3332551,86.9409032 L86.8703343,66.0667683 C71.9555191,76.0365044 60.8581818,91.271132 56.2464282,109.113806 C61.6276833,113.504845 65.0720469,120.189372 65.0720469,127.68244 C65.0720469,135.171003 61.6276833,141.855531 56.2464282,146.246569 C60.852176,164.094499 71.9495132,179.329877 86.8703343,189.299613 L99.3332551,168.420223 C86.5295015,159.412927 78.1618299,144.530393 78.1618299,127.683191 L78.1618299,127.683191 Z M127.954205,77.8855601 C153.967109,77.8855601 175.30895,97.8302874 177.549138,123.265877 L201.839859,122.907777 C200.644692,104.129689 192.441431,87.2719765 179.836622,74.875871 C173.354792,77.3247625 165.86773,76.9501466 159.396411,73.2197537 C152.91383,69.4788504 148.849361,63.1681877 147.738276,56.3177478 C141.438123,54.5790499 134.807648,53.6271202 127.952704,53.6271202 C116.168446,53.6271202 105.026815,56.3950733 95.1344047,61.2913548 L106.979472,82.5175836 C113.351695,79.5521877 120.460387,77.8855601 127.954205,77.8855601 L127.954205,77.8855601 Z M127.954205,177.475566 C120.460387,177.475566 113.351695,175.808188 106.980223,172.843543 L95.1351554,194.069021 C105.027566,198.971308 116.169196,201.740012 127.954205,201.740012 C134.80915,201.740012 141.439625,200.787331 147.739026,199.043378 C148.850111,192.192938 152.916082,185.888282 159.397161,182.140622 C165.872985,178.404223 173.355543,178.036364 179.837372,180.485255 C192.442182,168.08915 200.645443,151.231437 201.84061,132.453349 L177.543883,132.095249 C175.30895,157.537595 153.967859,177.475566 127.954205,177.475566 L127.954205,177.475566 Z M162.842276,67.2446686 C170.995988,71.9532669 181.416915,69.1642933 186.121009,61.0105806 C190.830358,52.856868 188.041384,42.4359413 179.886921,37.7258416 C171.733208,33.0217478 161.313032,35.8167273 156.602182,43.9704399 C151.898839,52.1196481 154.693818,62.5405748 162.842276,67.2446686 L162.842276,67.2446686 Z" fill="#FFFFFF"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -6,7 +6,7 @@ import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToastrModule } from 'ngx-toastr' import { ToastrModule } from 'ngx-toastr'
import TerminusCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService, TabContextMenuItemProvider } from 'terminus-core' import TerminusCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService, TabContextMenuItemProvider, ElectronService } from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings' import { SettingsTabProvider } from 'terminus-settings'
import { AppearanceSettingsTabComponent } from './components/appearanceSettingsTab.component' import { AppearanceSettingsTabComponent } from './components/appearanceSettingsTab.component'
@@ -139,6 +139,7 @@ export default class TerminalModule { // eslint-disable-line @typescript-eslint/
terminal: TerminalService, terminal: TerminalService,
hostApp: HostAppService, hostApp: HostAppService,
dockMenu: DockMenuService, dockMenu: DockMenuService,
electron: ElectronService,
) { ) {
const events = [ const events = [
{ {
@@ -165,8 +166,7 @@ export default class TerminalModule { // eslint-disable-line @typescript-eslint/
} }
}) })
if (config.store.terminal.autoOpen) { if (config.store.terminal.autoOpen) {
let argv = electron.process.argv
let argv = require('electron').remote.process.argv
if (argv[0].includes('node')) { if (argv[0].includes('node')) {
argv = argv.slice(1) argv = argv.slice(1)
} }

View File

@@ -6,16 +6,27 @@ import { TerminalTabComponent } from './components/terminalTab.component'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class RecoveryProvider extends TabRecoveryProvider { export class RecoveryProvider extends TabRecoveryProvider {
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab|null> { async applicableTo (recoveryToken: RecoveryToken): Promise<boolean> {
if (recoveryToken.type === 'app:terminal-tab') { return recoveryToken.type === 'app:terminal-tab'
return { }
type: TerminalTabComponent,
options: { async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab> {
sessionOptions: recoveryToken.sessionOptions, return {
savedState: recoveryToken.savedState, type: TerminalTabComponent,
}, options: {
} sessionOptions: recoveryToken.sessionOptions,
savedState: recoveryToken.savedState,
},
}
}
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
sessionOptions: {
...recoveryToken.sessionOptions,
restoreFromPTYID: null,
},
} }
return null
} }
} }

View File

@@ -1,13 +1,13 @@
import * as psNode from 'ps-node' import * as psNode from 'ps-node'
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import * as os from 'os' import * as os from 'os'
import * as nodePTY from '@terminus-term/node-pty' import { ipcRenderer } from 'electron'
import { getWorkingDirectoryFromPID } from 'native-process-working-directory' import { getWorkingDirectoryFromPID } from 'native-process-working-directory'
import { Observable, Subject } from 'rxjs' import { Observable, Subject } from 'rxjs'
import { first } from 'rxjs/operators' import { first } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Logger, LogService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' import { Logger, LogService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core'
import { SessionOptions } from '../api/interfaces' import { SessionOptions, ChildProcess } from '../api/interfaces'
/* eslint-disable block-scoped-var */ /* eslint-disable block-scoped-var */
@@ -19,16 +19,72 @@ try {
var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { } } catch { }
export interface ChildProcess {
pid: number
ppid: number
command: string
}
const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi
const OSC1337Prefix = Buffer.from('\x1b]1337;') const OSC1337Prefix = Buffer.from('\x1b]1337;')
const OSC1337Suffix = Buffer.from('\x07') const OSC1337Suffix = Buffer.from('\x07')
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class PTYProxy {
private id: string
private subscriptions: Map<string, any> = new Map()
static spawn (...options: any[]): PTYProxy {
return new PTYProxy(null, ...options)
}
static restore (id: string): PTYProxy|null {
if (ipcRenderer.sendSync('pty:exists', id)) {
return new PTYProxy(id)
}
return null
}
private constructor (id: string|null, ...options: any[]) {
if (id) {
this.id = id
} else {
this.id = ipcRenderer.sendSync('pty:spawn', ...options)
}
}
getPTYID (): string {
return this.id
}
getPID (): number {
return ipcRenderer.sendSync('pty:get-pid', this.id)
}
subscribe (event: string, handler: (..._: any[]) => void): void {
const key = `pty:${this.id}:${event}`
const newHandler = (_event, ...args) => handler(...args)
this.subscriptions.set(key, newHandler)
ipcRenderer.on(key, newHandler)
}
ackData (length: number): void {
ipcRenderer.send('pty:ack-data', this.id, length)
}
unsubscribeAll (): void {
for (const k of this.subscriptions.keys()) {
ipcRenderer.off(k, this.subscriptions.get(k))
}
}
resize (columns: number, rows: number): void {
ipcRenderer.send('pty:resize', this.id, columns, rows)
}
write (data: Buffer): void {
ipcRenderer.send('pty:write', this.id, data)
}
kill (signal?: string): void {
ipcRenderer.send('pty:kill', this.id, signal)
}
}
/** /**
* A session object for a [[BaseTerminalTabComponent]] * A session object for a [[BaseTerminalTabComponent]]
* Extend this to implement custom I/O and process management for your terminal tab * Extend this to implement custom I/O and process management for your terminal tab
@@ -90,7 +146,7 @@ export abstract class BaseSession {
/** @hidden */ /** @hidden */
export class Session extends BaseSession { export class Session extends BaseSession {
private pty: any private pty: PTYProxy|null = null
private pauseAfterExit = false private pauseAfterExit = false
private guessedCWD: string|null = null private guessedCWD: string|null = null
private reportedCWD: string private reportedCWD: string
@@ -103,47 +159,60 @@ export class Session extends BaseSession {
start (options: SessionOptions): void { start (options: SessionOptions): void {
this.name = options.name ?? '' this.name = options.name ?? ''
const env = { let pty: PTYProxy|null = null
...process.env,
TERM: 'xterm-256color', if (options.restoreFromPTYID) {
TERM_PROGRAM: 'Terminus', pty = PTYProxy.restore(options.restoreFromPTYID)
...options.env, options.restoreFromPTYID = undefined
...this.config.store.terminal.environment || {},
} }
if (process.platform === 'darwin' && !process.env.LC_ALL) { if (!pty) {
const locale = process.env.LC_CTYPE ?? 'en_US.UTF-8' const env = {
Object.assign(env, { ...process.env,
LANG: locale, TERM: 'xterm-256color',
LC_ALL: locale, TERM_PROGRAM: 'Terminus',
LC_MESSAGES: locale, ...options.env,
LC_NUMERIC: locale, ...this.config.store.terminal.environment || {},
LC_COLLATE: locale, }
LC_MONETARY: locale,
delete env['']
if (process.platform === 'darwin' && !process.env.LC_ALL) {
const locale = process.env.LC_CTYPE ?? 'en_US.UTF-8'
Object.assign(env, {
LANG: locale,
LC_ALL: locale,
LC_MESSAGES: locale,
LC_NUMERIC: locale,
LC_COLLATE: locale,
LC_MONETARY: locale,
})
}
let cwd = options.cwd ?? process.env.HOME
if (!fs.existsSync(cwd)) {
console.warn('Ignoring non-existent CWD:', cwd)
cwd = undefined
}
pty = PTYProxy.spawn(options.command, options.args ?? [], {
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
useConpty: (isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY ? 1 : false) as any,
}) })
this.guessedCWD = cwd ?? null
} }
let cwd = options.cwd ?? process.env.HOME this.pty = pty
if (!fs.existsSync(cwd)) { this.truePID = this.pty.getPID()
console.warn('Ignoring non-existent CWD:', cwd)
cwd = undefined
}
this.pty = nodePTY.spawn(options.command, options.args ?? [], {
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
useConpty: (isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY ? 1 : false) as any,
})
this.guessedCWD = cwd ?? null
this.truePID = this.pty['pid']
setTimeout(async () => { setTimeout(async () => {
// Retrieve any possible single children now that shell has fully started // Retrieve any possible single children now that shell has fully started
@@ -157,7 +226,10 @@ export class Session extends BaseSession {
this.open = true this.open = true
this.pty.on('data-buffered', (data: Buffer) => { this.pty.subscribe('data-buffered', (array: Uint8Array) => {
this.pty!.ackData(array.length)
let data = Buffer.from(array)
data = this.processOSC1337(data) data = this.processOSC1337(data)
this.emitOutput(data) this.emitOutput(data)
if (process.platform === 'win32') { if (process.platform === 'win32') {
@@ -165,7 +237,7 @@ export class Session extends BaseSession {
} }
}) })
this.pty.on('exit', () => { this.pty.subscribe('exit', () => {
if (this.pauseAfterExit) { if (this.pauseAfterExit) {
return return
} else if (this.open) { } else if (this.open) {
@@ -173,7 +245,7 @@ export class Session extends BaseSession {
} }
}) })
this.pty.on('close', () => { this.pty.subscribe('close', () => {
if (this.pauseAfterExit) { if (this.pauseAfterExit) {
this.emitOutput(Buffer.from('\r\nPress any key to close\r\n')) this.emitOutput(Buffer.from('\r\nPress any key to close\r\n'))
} else if (this.open) { } else if (this.open) {
@@ -182,26 +254,30 @@ export class Session extends BaseSession {
}) })
this.pauseAfterExit = options.pauseAfterExit ?? false this.pauseAfterExit = options.pauseAfterExit ?? false
this.destroyed$.subscribe(() => this.pty!.unsubscribeAll())
}
getPTYID (): string|null {
return this.pty?.getPTYID() ?? null
} }
resize (columns: number, rows: number): void { resize (columns: number, rows: number): void {
if (this.pty._writable) { this.pty?.resize(columns, rows)
this.pty.resize(columns, rows)
}
} }
write (data: Buffer): void { write (data: Buffer): void {
if (this.open) { if (this.open) {
if (this.pty._writable) { this.pty?.write(data)
this.pty.write(data) // TODO if (this.pty._writable) {
} else { // } else {
this.destroy() // this.destroy()
} // }
} }
} }
kill (signal?: string): void { kill (signal?: string): void {
this.pty.kill(signal) this.pty?.kill(signal)
} }
async getChildProcesses (): Promise<ChildProcess[]> { async getChildProcesses (): Promise<ChildProcess[]> {
@@ -245,7 +321,7 @@ export class Session extends BaseSession {
this.kill('SIGTERM') this.kill('SIGTERM')
setImmediate(() => { setImmediate(() => {
try { try {
process.kill(this.pty.pid, 0) process.kill(this.pty!.getPID(), 0)
// still alive // still alive
setTimeout(() => { setTimeout(() => {
this.kill('SIGKILL') this.kill('SIGKILL')
@@ -333,7 +409,6 @@ export class SessionsService {
private constructor ( private constructor (
log: LogService, log: LogService,
) { ) {
require('../bufferizedPTY')(nodePTY) // eslint-disable-line @typescript-eslint/no-var-requires
this.logger = log.create('sessions') this.logger = log.create('sessions')
} }

View File

@@ -3,6 +3,10 @@ const config = require('../webpack.plugin.config')
module.exports = config({ module.exports = config({
name: 'terminal', name: 'terminal',
dirname: __dirname, dirname: __dirname,
externals: [
'hterm-umdjs',
'opentype.js',
],
}) })
module.exports.resolve.modules.push('node_modules/xterm/src') module.exports.resolve.modules.push('node_modules/xterm/src')
module.exports.module.rules.find(x => x.use.loader === 'awesome-typescript-loader').use.options.paths['*'].push(path.resolve(__dirname, './node_modules/xterm/src/*')) module.exports.module.rules.find(x => x.use.loader === 'awesome-typescript-loader').use.options.paths['*'].push(path.resolve(__dirname, './node_modules/xterm/src/*'))

View File

@@ -255,6 +255,14 @@ opentype.js@^0.8.0:
dependencies: dependencies:
tiny-inflate "^1.0.2" tiny-inflate "^1.0.2"
opentype.js@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-1.3.3.tgz#65b8645b090a1ad444065b784d442fa19d1061f6"
integrity sha512-/qIY/+WnKGlPIIPhbeNjynfD2PO15G9lA/xqlX2bDH+4lc3Xz5GCQ68mqxj3DdUv6AJqCeaPvuAoH8mVL0zcuA==
dependencies:
string.prototype.codepointat "^0.2.1"
tiny-inflate "^1.0.3"
printj@~1.1.0: printj@~1.1.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
@@ -295,6 +303,11 @@ slugify@^1.4.0:
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.7.tgz#e42359d505afd84a44513280868e31202a79a628" resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.7.tgz#e42359d505afd84a44513280868e31202a79a628"
integrity sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg== integrity sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==
string.prototype.codepointat@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc"
integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
string.prototype.trimend@^1.0.1: string.prototype.trimend@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46"
@@ -332,7 +345,7 @@ thenify-all@^1.0.0:
dependencies: dependencies:
any-promise "^1.0.0" any-promise "^1.0.0"
tiny-inflate@^1.0.2: tiny-inflate@^1.0.2, tiny-inflate@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==

View File

@@ -1,9 +1,14 @@
const path = require('path') const path = require('path')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const bundleAnalyzer = new BundleAnalyzerPlugin({
analyzerPort: 0,
})
module.exports = options => { module.exports = options => {
const isDev = !!process.env.TERMINUS_DEV const isDev = !!process.env.TERMINUS_DEV
const devtool = isDev && process.platform === 'win32' ? 'eval-cheap-module-source-map' : 'cheap-module-source-map' const devtool = isDev && process.platform === 'win32' ? 'eval-cheap-module-source-map' : 'cheap-module-source-map'
return { const config = {
target: 'node', target: 'node',
entry: 'src/index.ts', entry: 'src/index.ts',
context: options.dirname, context: options.dirname,
@@ -41,7 +46,10 @@ module.exports = options => {
], ],
paths: { paths: {
'terminus-*': [path.resolve(options.dirname, '../terminus-*')], 'terminus-*': [path.resolve(options.dirname, '../terminus-*')],
'*': [path.resolve(options.dirname, '../app/node_modules/*')], '*': [
path.resolve(options.dirname, '../app/node_modules/*'),
path.resolve(options.dirname, '../node_modules/*'),
],
}, },
}, },
}, },
@@ -64,7 +72,7 @@ module.exports = options => {
], ],
}, },
externals: [ externals: [
'@terminus-term/node-pty', 'any-promise',
'child_process', 'child_process',
'electron-promise-ipc', 'electron-promise-ipc',
'electron', 'electron',
@@ -84,11 +92,17 @@ module.exports = options => {
'windows-native-registry', 'windows-native-registry',
'windows-process-tree', 'windows-process-tree',
'windows-process-tree/build/Release/windows_process_tree.node', 'windows-process-tree/build/Release/windows_process_tree.node',
'yargs/yargs',
/^@angular/, /^@angular/,
/^@ng-bootstrap/, /^@ng-bootstrap/,
/^rxjs/, /^rxjs/,
/^terminus-/, /^terminus-/,
...options.externals || [],
], ],
ignoreWarnings: [/node_modules\/yargs/], plugins: [],
} }
if (process.env.PLUGIN_BUNDLE_ANALYZER === options.name) {
config.plugins.push(bundleAnalyzer)
}
return config
} }

Some files were not shown because too many files have changed in this diff Show More