Compare commits

...

195 Commits

Author SHA1 Message Date
Eugene Pankov
ff49b9e38a settings: added release notes 2021-07-18 16:48:13 +02:00
Eugene Pankov
439e407595 serial: fixed missing port names in builtin profiles 2021-07-18 15:22:46 +02:00
Eugene Pankov
1eed32f8d8 local: UI to support single "command line" in profiles 2021-07-18 15:22:35 +02:00
Eugene Pankov
66098b5c6d Update hostWindow.service.ts 2021-07-18 14:08:20 +02:00
Eugene Pankov
a725d25e46 web: added proper platform detection and hotkey handling 2021-07-17 16:39:08 +02:00
Eugene Pankov
4e42dfd46b plugin version bump 2021-07-17 00:54:38 +02:00
Eugene Pankov
c2657568a6 updated web demo for new profiles 2021-07-17 00:52:16 +02:00
Eugene Pankov
dbe7b8cf56 plugin version bump 2021-07-17 00:52:07 +02:00
Eugene Pankov
a82a65ed46 plugin version bump 2021-07-16 23:59:45 +02:00
Eugene Pankov
893d9a9887 fixed button discoloration 2021-07-16 23:59:12 +02:00
Eugeny
1facd46901 Merge pull request #4216 from Eugeny/all-contributors/add-KingMob 2021-07-16 23:54:00 +02:00
allcontributors[bot]
7af89e1d07 docs: update .all-contributorsrc [skip ci] 2021-07-16 21:53:41 +00:00
Eugeny
50b2040d16 Merge pull request #4215 from KingMob/bugfix/get-shell-names-correctly 2021-07-16 23:53:40 +02:00
allcontributors[bot]
a65505c498 docs: update README.md [skip ci] 2021-07-16 21:53:40 +00:00
Matthew Davidson
7e8c19e97b Correctly name profiles for shells outside /usr
Shells outside /usr get named incorrectly. E.g., if I add /usr/local/bin/fish
to /etc/shells, when Tabby starts up, it think the profile name should be "local".
2021-07-16 16:50:32 -04:00
Eugeny
8ac101cf9c Merge pull request #4213 from Eugeny/dependabot/npm_and_yarn/electron-13.1.7 2021-07-16 11:31:54 +02:00
dependabot[bot]
6297987e4f Bump electron from 13.1.6 to 13.1.7
Bumps [electron](https://github.com/electron/electron) from 13.1.6 to 13.1.7.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v13.1.6...v13.1.7)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-16 04:07:07 +00:00
Eugene Pankov
cbd7c7c02f remove outline buttons 2021-07-14 00:21:23 +02:00
Eugene Pankov
57a198b082 typing cleanup 2021-07-14 00:03:05 +02:00
Eugene Pankov
e245629c5a ui tweak 2021-07-13 23:53:43 +02:00
Eugene Pankov
760311ffa0 lint 2021-07-13 23:50:31 +02:00
Eugene Pankov
2f13f3a401 strongly typed partial profiles wip 2021-07-13 23:44:23 +02:00
Eugene Pankov
5ddf36d4c1 lint 2021-07-13 22:23:46 +02:00
Eugene Pankov
a632a599d3 WSL: fixed multiple distro detection 2021-07-13 22:20:59 +02:00
Eugene Pankov
ca9f11484c move terminal-tab to use .profile 2021-07-13 21:47:06 +02:00
Eugene Pankov
9d224cbce2 Merge branch 'master' of github.com:Eugeny/terminus 2021-07-13 20:37:20 +02:00
Eugeny
7df36b89c3 Merge pull request #4198 from Eugeny/dependabot/npm_and_yarn/typescript-eslint/parser-4.28.3
Bump @typescript-eslint/parser from 4.28.2 to 4.28.3
2021-07-13 20:19:47 +02:00
Eugene Pankov
8b62aa24ea Merge branch 'master' of github.com:Eugeny/terminus 2021-07-13 20:19:31 +02:00
Eugene Pankov
9502240480 reworked login scripts - fixes #1349 2021-07-13 20:19:28 +02:00
dependabot[bot]
31efa2f9c1 Bump @typescript-eslint/parser from 4.28.2 to 4.28.3
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.28.2 to 4.28.3.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.28.3/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-13 17:53:20 +00:00
Eugeny
b40192f2ad Merge pull request #4196 from Eugeny/dependabot/npm_and_yarn/style-loader-3.1.0
Bump style-loader from 3.0.0 to 3.1.0
2021-07-13 19:48:29 +02:00
Eugeny
489ea5f891 Merge pull request #4197 from Eugeny/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-4.28.3 2021-07-13 19:46:30 +02:00
Eugeny
05eb24cd99 Merge pull request #4191 from Eugeny/dependabot/npm_and_yarn/typedoc-0.21.4
Bump typedoc from 0.21.2 to 0.21.4
2021-07-13 19:46:21 +02:00
Eugene Pankov
5053743b1b fixed dynamic port forward listener not getting cleaned up - fixes #4200 2021-07-13 19:27:02 +02:00
Eugene Pankov
6d7f25870e Merge branch 'master' of github.com:Eugeny/terminus 2021-07-13 19:26:21 +02:00
Eugeny
6cdee22164 Delete clink.html 2021-07-13 16:04:26 +02:00
Eugene Pankov
c043d5bc83 delegate getDescription to ProfilesService 2021-07-13 10:28:27 +02:00
Eugene Pankov
d7741f07a1 fixed configproxy 2021-07-13 10:28:08 +02:00
dependabot[bot]
14c0b8891d Bump @typescript-eslint/eslint-plugin from 4.28.2 to 4.28.3
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.28.2 to 4.28.3.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.28.3/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-13 04:04:47 +00:00
dependabot[bot]
ea1d8e95f3 Bump style-loader from 3.0.0 to 3.1.0
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: style-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-13 04:04:14 +00:00
Eugene Pankov
50c20f08f8 fixed 'hide dock on blur' 2021-07-12 23:35:42 +02:00
Eugene Pankov
8f55333d23 fixed visual layout of readline prompts 2021-07-12 22:31:17 +02:00
Eugene Pankov
3be98e6244 fixed home/end keys in ssh sessions 2021-07-12 22:23:46 +02:00
Eugene Pankov
5e771534a8 wording 2021-07-12 21:29:38 +02:00
Eugene Pankov
ba33f18af7 fixed ssh connections 2021-07-12 21:29:34 +02:00
Eugene Pankov
82f3b61b5e bump 2021-07-12 21:29:28 +02:00
dependabot[bot]
f587fd279c Bump typedoc from 0.21.2 to 0.21.4
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.21.2 to 0.21.4.
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.21.2...v0.21.4)

---
updated-dependencies:
- dependency-name: typedoc
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-12 04:05:50 +00:00
Eugene Pankov
8d13cb0fe8 bump 2021-07-11 23:04:48 +02:00
Eugene Pankov
d1a6baf858 bump 2021-07-11 23:03:55 +02:00
Eugene Pankov
25034342c3 bumped plugins 2021-07-11 23:02:27 +02:00
Eugene Pankov
379775bcd3 import fix 2021-07-11 23:02:23 +02:00
Eugene Pankov
ff18926bf9 WSA wip 2021-07-11 22:59:39 +02:00
Eugene Pankov
37e564130e web preload progress callback 2021-07-11 16:31:00 +02:00
Eugene Pankov
d8a8d41614 lint 2021-07-11 16:24:04 +02:00
Eugene Pankov
7db3335938 bump, include source maps in releases 2021-07-11 16:12:36 +02:00
Eugene Pankov
c1051379c1 add tabby-web-demo to the repo 2021-07-11 16:00:59 +02:00
Eugeny
f7a0fb488b Merge pull request #4175 from Eugeny/dependabot/npm_and_yarn/tabby-terminal/xterm-addon-ligatures-0.5.1
Bump xterm-addon-ligatures from 0.5.0 to 0.5.1 in /tabby-terminal
2021-07-11 12:39:02 +02:00
Eugene Pankov
2706045cc2 tab context menu option to save split layouts as profiles - fixes #3468 2021-07-11 12:38:35 +02:00
Eugene Pankov
908f90cd52 automatically clean up defaults from the config file 2021-07-11 00:06:52 +02:00
Eugene Pankov
67bbbd7f65 allow renaming and replacing files in the vault - fixes #4110 2021-07-10 21:31:18 +02:00
Eugene Pankov
0008b2f022 preserve file modes for up- and downloads - fixes #4141 2021-07-10 20:39:45 +02:00
Eugene Pankov
3e61630c6a added a way to switch a pane's profile - fixes #1007, fixes #2963, fixes #3082, fixes #4151 2021-07-10 20:05:31 +02:00
Eugene Pankov
6f912dc12b make sure ssh ident goes out in a single packet - #4167 2021-07-10 17:51:37 +02:00
Eugene Pankov
e1f2e176ce actually implement telnet protocol - fixes #4164 2021-07-10 16:09:24 +02:00
Eugene Pankov
f39b4c6dbe Update buttonProvider.ts 2021-07-10 14:47:41 +02:00
Eugene Pankov
c49ff68ed6 avoid duplicate entries in recent profiles - fixes #4179 2021-07-10 14:30:33 +02:00
Eugene Pankov
891cf42338 make ssh the default quick-connect profile - fixes #4181 2021-07-10 14:10:55 +02:00
Eugene Pankov
b9763044ee close streamProcessor in telnet tabs 2021-07-09 10:31:12 +02:00
Eugene Pankov
46e0035327 sort profiles by groups - fixes #4163 2021-07-09 10:02:58 +02:00
Eugene Pankov
6df8707b6d Merge branch 'master' of github.com:Eugeny/terminus 2021-07-09 09:58:35 +02:00
Eugene Pankov
24b7922539 fixed high contrast appearance for theme previews 2021-07-09 09:58:31 +02:00
Eugene Pankov
485665d449 fixed toggle switch appearance in windows high contrast mode - fixes #4165 2021-07-09 09:47:51 +02:00
Eugene Pankov
e09a011c23 hint about ssh connections being moved to profiles 2021-07-09 09:47:09 +02:00
Eugene Pankov
833a348fdb fixed matchedHotkey deprecation - fixes #4153, fixes #4156 2021-07-09 09:46:54 +02:00
Eugene Pankov
26ff6f17e7 fixed toolbar state loading 2021-07-09 09:10:10 +02:00
dependabot[bot]
d026e634e5 Bump xterm-addon-ligatures from 0.5.0 to 0.5.1 in /tabby-terminal
Bumps [xterm-addon-ligatures](https://github.com/xtermjs/xterm.js) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/xtermjs/xterm.js/releases)
- [Commits](https://github.com/xtermjs/xterm.js/commits)

---
updated-dependencies:
- dependency-name: xterm-addon-ligatures
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-09 04:25:46 +00:00
Eugeny
356a2f38b6 Merge pull request #4160 from Eugeny/dependabot/npm_and_yarn/sentry/cli-1.67.1 2021-07-08 22:04:39 +02:00
Eugeny
bdb37a9a18 Merge pull request #4169 from Eugeny/all-contributors/add-cypherbits 2021-07-08 22:04:29 +02:00
allcontributors[bot]
22d89041f8 docs: update .all-contributorsrc [skip ci] 2021-07-08 20:04:20 +00:00
allcontributors[bot]
d5285cf268 docs: update README.md [skip ci] 2021-07-08 20:04:19 +00:00
Eugene Pankov
3db98aa421 expose more plugin loader apis 2021-07-08 22:03:41 +02:00
Eugene Pankov
47dba5b52c avoid including all of the util-decorators in the bundle 2021-07-08 22:03:25 +02:00
Eugene Pankov
72874a1e84 Update HACKING.md 2021-07-08 22:03:03 +02:00
Eugene Pankov
fc1deb67e8 Merge branch 'master' of github.com:Eugeny/terminus 2021-07-08 22:01:43 +02:00
Eugeny
d0bb3c731c Merge pull request #4168 from cypherbits/master 2021-07-08 22:01:34 +02:00
cypherbits
13e54a46d7 add Ubuntu 20.04 compile requeriments 2021-07-08 18:46:44 +02:00
Eugene Pankov
55a975bc8b Merge branch 'master' of github.com:Eugeny/terminus 2021-07-08 16:09:09 +02:00
Eugene Pankov
a62752efec bumped package versions 2021-07-08 16:07:45 +02:00
dependabot[bot]
4e97ce5117 Bump @sentry/cli from 1.64.2 to 1.67.1
Bumps [@sentry/cli](https://github.com/getsentry/sentry-cli) from 1.64.2 to 1.67.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.64.2...1.67.1)

---
updated-dependencies:
- dependency-name: "@sentry/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-08 11:36:24 +00:00
Eugeny
19a5f2dc2d Merge pull request #4161 from Eugeny/dependabot/npm_and_yarn/tabby-ssh/types/node-16.0.1
Bump @types/node from 16.0.0 to 16.0.1 in /tabby-ssh
2021-07-08 13:32:41 +02:00
Eugeny
52433afd13 Merge pull request #4159 from Eugeny/dependabot/npm_and_yarn/types/node-16.0.1
Bump @types/node from 16.0.0 to 16.0.1
2021-07-08 13:32:26 +02:00
Eugeny
e1d9f50426 Merge pull request #4158 from Eugeny/dependabot/npm_and_yarn/sentry/electron-2.5.1
Bump @sentry/electron from 2.5.0 to 2.5.1
2021-07-08 13:32:19 +02:00
Eugeny
5837c61ac4 Merge pull request #4157 from Eugeny/dependabot/npm_and_yarn/app/types/node-16.0.1
Bump @types/node from 16.0.0 to 16.0.1 in /app
2021-07-08 13:32:11 +02:00
dependabot[bot]
8c03e5b1aa Bump @types/node from 16.0.0 to 16.0.1 in /tabby-ssh
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.0.0 to 16.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-08 04:24:47 +00:00
dependabot[bot]
66074e3eb6 Bump @types/node from 16.0.0 to 16.0.1
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.0.0 to 16.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-08 04:04:14 +00:00
dependabot[bot]
221746f3e7 Bump @sentry/electron from 2.5.0 to 2.5.1
Bumps [@sentry/electron](https://github.com/getsentry/sentry-electron) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/getsentry/sentry-electron/releases)
- [Changelog](https://github.com/getsentry/sentry-electron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-electron/compare/2.5.0...2.5.1)

---
updated-dependencies:
- dependency-name: "@sentry/electron"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-08 04:03:47 +00:00
dependabot[bot]
2c59e30c39 Bump @types/node from 16.0.0 to 16.0.1 in /app
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.0.0 to 16.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-08 04:01:31 +00:00
Eugene Pankov
4e13a601cc tab padding fix 2021-07-07 21:33:35 +02:00
Eugene Pankov
a159890cba lint 2021-07-07 21:33:26 +02:00
Eugene Pankov
4534eefc1d lint 2021-07-07 21:03:55 +02:00
Eugene Pankov
e06c44f973 externals fix 2021-07-07 21:00:40 +02:00
Eugene Pankov
c1616b1a7a bumped eslint 2021-07-07 20:45:47 +02:00
Eugeny
5b34fa9371 Merge pull request #4146 from Eugeny/dependabot/npm_and_yarn/types/fs-extra-9.0.12
Bump @types/fs-extra from 9.0.11 to 9.0.12
2021-07-07 20:41:03 +02:00
Eugeny
956a923ea2 Merge pull request #4124 from Eugeny/dependabot/npm_and_yarn/app/types/node-16.0.0
Bump @types/node from 15.12.5 to 16.0.0 in /app
2021-07-07 20:40:58 +02:00
Eugene Pankov
224abcb2c4 new toolbar design 2021-07-07 20:11:38 +02:00
dependabot[bot]
e8af224f7b Bump @types/fs-extra from 9.0.11 to 9.0.12
Bumps [@types/fs-extra](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/fs-extra) from 9.0.11 to 9.0.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/fs-extra)

---
updated-dependencies:
- dependency-name: "@types/fs-extra"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 11:34:02 +00:00
dependabot[bot]
a1d39563c3 Bump @types/node from 15.12.5 to 16.0.0 in /app
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 15.12.5 to 16.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 11:29:45 +00:00
Eugeny
621536a078 Merge pull request #4150 from Eugeny/dependabot/npm_and_yarn/tabby-ssh/types/ssh2-0.5.47
Bump @types/ssh2 from 0.5.46 to 0.5.47 in /tabby-ssh
2021-07-07 13:28:06 +02:00
Eugeny
3cf9353d08 Merge pull request #4149 from Eugeny/dependabot/npm_and_yarn/tabby-core/types/js-yaml-4.0.2
Bump @types/js-yaml from 4.0.1 to 4.0.2 in /tabby-core
2021-07-07 13:27:59 +02:00
dependabot[bot]
6a2fa7efa9 Bump @types/js-yaml from 4.0.1 to 4.0.2 in /tabby-core
Bumps [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) from 4.0.1 to 4.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml)

---
updated-dependencies:
- dependency-name: "@types/js-yaml"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 11:27:53 +00:00
Eugeny
6b0fe0e2d1 Merge pull request #4148 from Eugeny/dependabot/npm_and_yarn/tabby-plugin-manager/types/semver-7.3.7
Bump @types/semver from 7.3.6 to 7.3.7 in /tabby-plugin-manager
2021-07-07 13:27:34 +02:00
Eugeny
923876dc23 Merge pull request #4147 from Eugeny/dependabot/npm_and_yarn/webpack-5.43.0
Bump webpack from 5.42.1 to 5.43.0
2021-07-07 13:27:25 +02:00
Eugeny
233ae9cbe6 Merge pull request #4145 from Eugeny/dependabot/npm_and_yarn/types/js-yaml-4.0.2
Bump @types/js-yaml from 4.0.1 to 4.0.2
2021-07-07 13:26:56 +02:00
Eugeny
09daf8102d Merge pull request #4144 from Eugeny/dependabot/npm_and_yarn/app/types/mz-2.7.4
Bump @types/mz from 2.7.3 to 2.7.4 in /app
2021-07-07 13:26:48 +02:00
Eugene Pankov
fd7893e9f8 Update README.md 2021-07-07 10:48:24 +02:00
dependabot[bot]
cf5c3e71f6 Bump @types/ssh2 from 0.5.46 to 0.5.47 in /tabby-ssh
Bumps [@types/ssh2](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ssh2) from 0.5.46 to 0.5.47.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ssh2)

---
updated-dependencies:
- dependency-name: "@types/ssh2"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 04:27:47 +00:00
dependabot[bot]
5c30bbb7e4 Bump @types/semver from 7.3.6 to 7.3.7 in /tabby-plugin-manager
Bumps [@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver) from 7.3.6 to 7.3.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver)

---
updated-dependencies:
- dependency-name: "@types/semver"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 04:26:44 +00:00
dependabot[bot]
afce339187 Bump webpack from 5.42.1 to 5.43.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.42.1 to 5.43.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.42.1...v5.43.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 04:05:44 +00:00
dependabot[bot]
d528d1148b Bump @types/js-yaml from 4.0.1 to 4.0.2
Bumps [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) from 4.0.1 to 4.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml)

---
updated-dependencies:
- dependency-name: "@types/js-yaml"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 04:04:32 +00:00
dependabot[bot]
786b31e2a2 Bump @types/mz from 2.7.3 to 2.7.4 in /app
Bumps [@types/mz](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mz) from 2.7.3 to 2.7.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mz)

---
updated-dependencies:
- dependency-name: "@types/mz"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-07 04:01:47 +00:00
Eugene Pankov
0ad32fa79d moved profile settings view to settings plugin, web entry cleanup 2021-07-07 01:22:50 +02:00
Eugene Pankov
93a89e3c86 lint 2021-07-06 21:56:52 +02:00
Eugene Pankov
4e667edf9f lint 2021-07-06 21:50:40 +02:00
Eugene Pankov
403bafe0a2 lint 2021-07-06 21:47:15 +02:00
Eugene Pankov
40209dc60d bundle built-in modules together 2021-07-06 21:22:57 +02:00
Eugene Pankov
1ccd1df6e1 prevent duplication in available plugins 2021-07-06 21:03:10 +02:00
Eugene Pankov
800c1fa039 don't log cwd check failures as errors 2021-07-06 20:29:11 +02:00
Eugene Pankov
0a67987e3c use eval sourcemaps in dev mode on windows 2021-07-06 20:29:01 +02:00
Eugene Pankov
6c7a8092a4 fixed config reload race condition 2021-07-06 20:28:53 +02:00
Eugene Pankov
9f87886a9b ui tweaks 2021-07-06 19:34:00 +02:00
Eugene Pankov
0aa5df421d added auto detection for vs developer prompt 2021-07-06 19:33:53 +02:00
Eugene Pankov
d3498a6a46 make comspec setting optional - fixes #4143 2021-07-06 19:03:34 +02:00
Eugene Pankov
41a53a3e8e lint 2021-07-06 10:44:30 +02:00
Eugene Pankov
636942ff86 updated rxjs imports 2021-07-06 09:43:16 +02:00
Eugene Pankov
d1b874c191 Merge branch 'master' of github.com:Eugeny/terminus 2021-07-06 09:40:58 +02:00
Eugene Pankov
89f369abe6 es2021 compilation 2021-07-06 09:40:54 +02:00
Eugeny
f7cea92900 Merge pull request #4135 from Eugeny/dependabot/npm_and_yarn/app/rxjs-7.2.0
Bump rxjs from 7.1.0 to 7.2.0 in /app
2021-07-06 09:40:20 +02:00
Eugeny
9a611709d0 Merge pull request #4102 from Eugeny/dependabot/npm_and_yarn/tabby-core/core-js-3.15.2
Bump core-js from 3.14.0 to 3.15.2 in /tabby-core
2021-07-06 08:56:25 +02:00
dependabot[bot]
2094409d23 Bump core-js from 3.14.0 to 3.15.2 in /tabby-core
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.14.0 to 3.15.2.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.15.2/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-06 06:56:19 +00:00
Eugeny
2b061d3f77 Merge pull request #4129 from Eugeny/dependabot/npm_and_yarn/eslint-7.30.0
Bump eslint from 7.29.0 to 7.30.0
2021-07-06 08:56:16 +02:00
Eugeny
ed06c78a16 Merge pull request #4122 from Eugeny/dependabot/npm_and_yarn/tabby-ssh/types/node-16.0.0 2021-07-06 08:56:03 +02:00
Eugeny
36a6af05c4 Merge pull request #4126 from Eugeny/dependabot/npm_and_yarn/types/node-16.0.0
Bump @types/node from 15.12.5 to 16.0.0
2021-07-06 08:55:57 +02:00
dependabot[bot]
64b4eed9c3 Bump @types/node from 14.14.31 to 16.0.0 in /tabby-ssh
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.31 to 16.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-06 06:55:14 +00:00
Eugeny
afc9270846 Merge pull request #4139 from Eugeny/dependabot/npm_and_yarn/electron-13.1.6
Bump electron from 13.1.5 to 13.1.6
2021-07-06 08:54:21 +02:00
Eugeny
2ea5edc101 Merge pull request #4138 from Eugeny/dependabot/npm_and_yarn/webpack-5.42.1
Bump webpack from 5.42.0 to 5.42.1
2021-07-06 08:54:06 +02:00
dependabot[bot]
869a7e866c Bump electron from 13.1.5 to 13.1.6
Bumps [electron](https://github.com/electron/electron) from 13.1.5 to 13.1.6.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v13.1.5...v13.1.6)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-06 04:06:39 +00:00
dependabot[bot]
1bd3b5301b Bump webpack from 5.42.0 to 5.42.1
Bumps [webpack](https://github.com/webpack/webpack) from 5.42.0 to 5.42.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.42.0...v5.42.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-06 04:05:54 +00:00
dependabot[bot]
4ff66a39d8 Bump rxjs from 7.1.0 to 7.2.0 in /app
Bumps [rxjs](https://github.com/reactivex/rxjs) from 7.1.0 to 7.2.0.
- [Release notes](https://github.com/reactivex/rxjs/releases)
- [Changelog](https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/reactivex/rxjs/compare/7.1.0...7.2.0)

---
updated-dependencies:
- dependency-name: rxjs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-06 04:02:49 +00:00
dependabot[bot]
a3857d5dc8 Bump @types/node from 15.12.5 to 16.0.0
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 15.12.5 to 16.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-05 22:01:21 +00:00
Eugene Pankov
bf762cc4c7 moved login scripts processing into tabby-terminal 2021-07-05 23:56:38 +02:00
Eugene Pankov
461cd2bec7 deprecated matchedHotkey 2021-07-05 22:52:32 +02:00
Eugene Pankov
da599567f8 cleanup 2021-07-05 22:27:58 +02:00
Eugene Pankov
7f921c4d34 added a profile selector hotkey 2021-07-05 22:27:55 +02:00
Eugene Pankov
0eb006f297 Merge branch 'master' of github.com:Eugeny/terminus 2021-07-05 22:14:45 +02:00
Eugene Pankov
07095f3476 moved spinner intro baseTerminalTab 2021-07-05 22:14:42 +02:00
dependabot[bot]
59b283067b Bump eslint from 7.29.0 to 7.30.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.29.0 to 7.30.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.29.0...v7.30.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-05 13:09:45 +00:00
Eugeny
3635eee77d Merge pull request #4106 from Eugeny/dependabot/npm_and_yarn/app/ng-bootstrap/ng-bootstrap-10.0.0
Bump @ng-bootstrap/ng-bootstrap from 9.1.1 to 10.0.0 in /app
2021-07-05 15:01:34 +02:00
Eugeny
64df798dc1 Merge pull request #4130 from Eugeny/dependabot/npm_and_yarn/types/webpack-env-1.16.2
Bump @types/webpack-env from 1.16.0 to 1.16.2
2021-07-05 15:01:07 +02:00
Eugeny
f091206e37 Merge pull request #4104 from Eugeny/dependabot/npm_and_yarn/tabby-terminal/utils-decorators-1.10.0
Bump utils-decorators from 1.8.3 to 1.10.0 in /tabby-terminal
2021-07-05 15:00:46 +02:00
Eugeny
3148927395 Merge pull request #4101 from Eugeny/dependabot/npm_and_yarn/tabby-local/utils-decorators-1.10.0 2021-07-05 15:00:41 +02:00
Eugeny
fb39bfd560 Merge pull request #4103 from Eugeny/dependabot/npm_and_yarn/tabby-settings/utils-decorators-1.10.0
Bump utils-decorators from 1.8.3 to 1.10.0 in /tabby-settings
2021-07-05 15:00:35 +02:00
Eugeny
f8cd9fcea7 Merge pull request #4134 from Eugeny/all-contributors/add-logicmachine123 2021-07-05 15:00:02 +02:00
Eugeny
f1148cc47f Merge pull request #4111 from Eugeny/dependabot/npm_and_yarn/electron-13.1.5
Bump electron from 13.1.4 to 13.1.5
2021-07-05 14:56:15 +02:00
Eugeny
ffcc0d549a Merge pull request #4097 from Eugeny/dependabot/github_actions/actions/setup-node-2.2.0
Bump actions/setup-node from 2.1.5 to 2.2.0
2021-07-05 14:55:51 +02:00
Eugeny
861dd8ef86 Merge pull request #4125 from Eugeny/dependabot/npm_and_yarn/webpack-5.42.0
Bump webpack from 5.41.0 to 5.42.0
2021-07-05 14:55:23 +02:00
Eugeny
1e5e46eae1 Merge pull request #4123 from Eugeny/dependabot/npm_and_yarn/ws-7.5.2
Bump ws from 7.4.4 to 7.5.2
2021-07-05 14:55:08 +02:00
Eugeny
d644eec56e Merge pull request #4092 from Eugeny/dependabot/npm_and_yarn/core-js-3.15.2
Bump core-js from 3.14.0 to 3.15.2
2021-07-05 14:54:51 +02:00
allcontributors[bot]
a656699afd docs: update .all-contributorsrc [skip ci] 2021-07-05 12:53:55 +00:00
allcontributors[bot]
93db8fa046 docs: update README.md [skip ci] 2021-07-05 12:53:54 +00:00
dependabot[bot]
05669046e9 Bump @types/webpack-env from 1.16.0 to 1.16.2
Bumps [@types/webpack-env](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webpack-env) from 1.16.0 to 1.16.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/webpack-env)

---
updated-dependencies:
- dependency-name: "@types/webpack-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-05 04:06:00 +00:00
Eugene Pankov
025d2d1748 fixed relative symlink resolution - fixes #4119 2021-07-04 19:35:27 +02:00
Eugene Pankov
e17ba8c351 move createSession() into session 2021-07-04 17:11:21 +02:00
dependabot[bot]
d3dee44475 Bump webpack from 5.41.0 to 5.42.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.41.0 to 5.42.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.41.0...v5.42.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 14:54:10 +00:00
dependabot[bot]
0f0699d46a Bump @ng-bootstrap/ng-bootstrap from 9.1.1 to 10.0.0 in /app
Bumps [@ng-bootstrap/ng-bootstrap](https://github.com/ng-bootstrap/ng-bootstrap) from 9.1.1 to 10.0.0.
- [Release notes](https://github.com/ng-bootstrap/ng-bootstrap/releases)
- [Changelog](https://github.com/ng-bootstrap/ng-bootstrap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ng-bootstrap/ng-bootstrap/compare/9.1.1...10.0.0)

---
updated-dependencies:
- dependency-name: "@ng-bootstrap/ng-bootstrap"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 14:51:49 +00:00
dependabot[bot]
b7540e59a8 Bump ws from 7.4.4 to 7.5.2
Bumps [ws](https://github.com/websockets/ws) from 7.4.4 to 7.5.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.4...7.5.2)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 14:50:07 +00:00
Eugene Pankov
827345d899 added a telnet client - fixes #760 2021-07-04 16:48:48 +02:00
Eugene Pankov
59de67ca58 artifact names 2021-07-04 16:19:11 +02:00
Eugene Pankov
ccad826ca9 updated README links 2021-07-04 16:18:30 +02:00
dependabot[bot]
838acc0a23 Bump core-js from 3.14.0 to 3.15.2
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.14.0 to 3.15.2.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.15.2/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 13:15:14 +00:00
dependabot[bot]
804ae44ec8 Bump electron from 13.1.4 to 13.1.5
Bumps [electron](https://github.com/electron/electron) from 13.1.4 to 13.1.5.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v13.1.4...v13.1.5)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 13:13:52 +00:00
dependabot[bot]
5f04c3b74b Bump utils-decorators from 1.8.3 to 1.10.0 in /tabby-terminal
Bumps [utils-decorators](https://github.com/vlio20/utils-decorators) from 1.8.3 to 1.10.0.
- [Release notes](https://github.com/vlio20/utils-decorators/releases)
- [Commits](https://github.com/vlio20/utils-decorators/commits/1.10.0)

---
updated-dependencies:
- dependency-name: utils-decorators
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 13:10:28 +00:00
dependabot[bot]
4afd49e38c Bump utils-decorators from 1.8.3 to 1.10.0 in /tabby-local
Bumps [utils-decorators](https://github.com/vlio20/utils-decorators) from 1.8.3 to 1.10.0.
- [Release notes](https://github.com/vlio20/utils-decorators/releases)
- [Commits](https://github.com/vlio20/utils-decorators/commits/1.10.0)

---
updated-dependencies:
- dependency-name: utils-decorators
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 13:10:21 +00:00
dependabot[bot]
ba32c69001 Bump utils-decorators from 1.8.3 to 1.10.0 in /tabby-settings
Bumps [utils-decorators](https://github.com/vlio20/utils-decorators) from 1.8.3 to 1.10.0.
- [Release notes](https://github.com/vlio20/utils-decorators/releases)
- [Commits](https://github.com/vlio20/utils-decorators/commits/1.10.0)

---
updated-dependencies:
- dependency-name: utils-decorators
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 13:10:04 +00:00
dependabot[bot]
e9a3947488 Bump actions/setup-node from 2.1.5 to 2.2.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2.1.5 to 2.2.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.1.5...v2.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-04 13:09:55 +00:00
Eugene Pankov
b2f4b44123 workflow update 2021-07-04 15:09:34 +02:00
Eugene Pankov
f4eacc1d66 build workflow update 2021-07-04 15:06:27 +02:00
Eugene Pankov
9155104662 moved stream processing into tabby-terminal 2021-07-04 14:05:25 +02:00
Eugene Pankov
cbbd38ca83 lint 2021-07-04 14:05:13 +02:00
Eugene Pankov
e5cf72e79b context menu dep fix 2021-07-04 14:04:40 +02:00
Eugene Pankov
a71d9c0727 fixed default profile names 2021-07-04 13:47:54 +02:00
Eugene Pankov
26e2c60265 option to always ask for baud rate 2021-07-04 13:47:38 +02:00
Eugene Pankov
a5a0546e68 config migration fix 2021-07-04 13:06:00 +02:00
Eugene Pankov
92b34fbc08 new profile system 2021-07-04 12:23:27 +02:00
Eugene Pankov
38b7e44f64 updated plugin links 2021-07-02 00:39:27 +02:00
Eugene Pankov
3aa957a3f5 updated to-string-loader 2021-07-02 00:39:17 +02:00
263 changed files with 11159 additions and 9019 deletions

View File

@@ -406,6 +406,33 @@
"contributions": [
"design"
]
},
{
"login": "logicmachine123",
"name": "Logic Machine",
"avatar_url": "https://avatars.githubusercontent.com/u/63876444?v=4",
"profile": "https://git.io/JnP49",
"contributions": [
"doc"
]
},
{
"login": "cypherbits",
"name": "cypherbits",
"avatar_url": "https://avatars.githubusercontent.com/u/10424900?v=4",
"profile": "https://github.com/cypherbits",
"contributions": [
"doc"
]
},
{
"login": "KingMob",
"name": "Matthew Davidson",
"avatar_url": "https://avatars.githubusercontent.com/u/946421?v=4",
"profile": "https://modulolotus.net",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -36,7 +36,9 @@ rules:
'@typescript-eslint/prefer-readonly': off
'@typescript-eslint/require-await': off
'@typescript-eslint/strict-boolean-expressions': off
'@typescript-eslint/no-misused-promises': off
'@typescript-eslint/no-misused-promises':
- error
- checksVoidReturn: false
'@typescript-eslint/typedef': off
'@typescript-eslint/consistent-type-imports': off
'@typescript-eslint/sort-type-union-intersection-members': off
@@ -95,7 +97,9 @@ rules:
- error
- single
- allowTemplateLiterals: true
'@typescript-eslint/no-confusing-void-expression': off
'@typescript-eslint/no-confusing-void-expression':
- error
- ignoreArrowShorthand: true
'@typescript-eslint/no-non-null-assertion': off
'@typescript-eslint/no-unnecessary-condition':
- error
@@ -116,3 +120,4 @@ rules:
'@typescript-eslint/no-var-requires': off
'@typescript-eslint/no-unsafe-argument': off
'@typescript-eslint/restrict-plus-operands': off
'@typescript-eslint/space-infix-ops': off

300
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,300 @@
name: Package-Build
on: [push, pull_request]
jobs:
Lint:
runs-on: macos-11.0
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
with:
node-version: 14
- name: Install deps
run: |
npm i -g yarn@1.19.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
- name: Build typings
run: yarn run build:typings
- name: Lint
run: yarn run lint
macOS-Build:
runs-on: macos-11.0
needs: Lint
strategy:
matrix:
include:
- arch: x86_64
- arch: arm64
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
with:
node-version: 14
- name: Install deps
run: |
sudo npm i -g yarn@1.22.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
- name: Build native deps
run: scripts/build-native.js
env:
ARCH: ${{matrix.arch}}
- name: Build native deps
run: |
rm -rf app/node_modules/cpu-features
rm -rf app/node_modules/ssh2/crypto/build
if: ${{ matrix.arch == 'arm64' }}
- name: Webpack
run: yarn run build
- name: Prepackage plugins
run: scripts/prepackage-plugins.js
env:
ARCH: ${{matrix.arch}}
- run: sed -i '' 's/updateInfo = await/\/\/updateInfo = await/g' node_modules/app-builder-lib/out/targets/ArchiveTarget.js
- name: Build and sign packages
run: scripts/build-macos.js
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
env:
ARCH: ${{matrix.arch}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPSTORE_USERNAME: ${{ secrets.APPSTORE_USERNAME }}
APPSTORE_PASSWORD: ${{ secrets.APPSTORE_PASSWORD }}
USE_HARD_LINKS: false
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing
run: scripts/build-macos.js
if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
env:
ARCH: ${{matrix.arch}}
# DEBUG: electron-builder,electron-builder:*
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-pkg
mv dist/*.pkg artifact-pkg/
mkdir artifact-zip
mv dist/*.zip artifact-zip/
- uses: actions/upload-artifact@master
name: Upload PKG
with:
name: macOS .pkg (${{matrix.arch}})
path: artifact-pkg
- uses: actions/upload-artifact@master
name: Upload ZIP
with:
name: macOS .zip (${{matrix.arch}})
path: artifact-zip
Linux-Build:
runs-on: ubuntu-18.04
needs: Lint
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v2.2.0
with:
node-version: 14
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install bsdtar zsh
npm i -g yarn@1.19.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
npm run patch
- name: Build native deps
run: scripts/build-native.js
- name: Webpack
run: yarn run build
- name: Prepackage plugins
run: scripts/prepackage-plugins.js
- name: Build packages
run: scripts/build-linux.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
USE_HARD_LINKS: false
# DEBUG: electron-builder,electron-builder:*
- name: Build web resources
run: zsh -c 'tar czf tabby-web.tar.gz (tabby-*|web)/dist'
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-deb
mv dist/*.deb artifact-deb/ || true
mkdir artifact-rpm
mv dist/*.rpm artifact-rpm/ || true
mkdir artifact-pacman
mv dist/*.pacman artifact-pacman/ || true
mkdir artifact-snap
mv dist/*.snap artifact-snap/ || true
mkdir artifact-tar.gz
mv dist/*.tar.gz artifact-tar.gz/ || true
mkdir artifact-web
mv tabby-web.tar.gz artifact-web/ || true
- uses: actions/upload-artifact@master
name: Upload DEB
with:
name: Linux DEB
path: artifact-deb
- uses: actions/upload-artifact@master
name: Upload RPM
with:
name: Linux RPM
path: artifact-rpm
- uses: actions/upload-artifact@master
name: Upload Pacman Package
with:
name: Linux Pacman
path: artifact-pacman
- uses: actions/upload-artifact@master
name: Upload Snap
with:
name: Linux Snap
path: artifact-snap
- uses: actions/upload-artifact@master
name: Upload Linux tarball
with:
name: Linux tarball
path: artifact-tar.gz
- uses: actions/upload-artifact@master
name: Upload web tarball
with:
name: Web tarball
path: artifact-web
Windows-Build:
runs-on: windows-2016
needs: Lint
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
with:
node-version: 14
- name: Build
shell: powershell
run: |
npm i -g yarn@1.19.1
yarn
node scripts/build-native.js
yarn run build
node scripts/prepackage-plugins.js
- name: Build and sign packages
run: node scripts/build-windows.js
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing
run: node scripts/build-windows.js
if: "!(github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
- name: Upload symbols
run: |
npm install @sentry/cli
node scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-setup
mv dist/*-setup.exe artifact-setup/
mkdir artifact-portable
mv dist/*-portable.zip artifact-portable/
- uses: actions/upload-artifact@master
name: Upload installer
with:
name: Windows installer
path: artifact-setup
- uses: actions/upload-artifact@master
name: Upload portable build
with:
name: Windows portable build
path: artifact-portable

View File

@@ -12,7 +12,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
uses: actions/setup-node@v2.2.0
with:
node-version: 14

View File

@@ -1,31 +0,0 @@
name: Lint
on: [push, pull_request]
jobs:
build:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Install deps
run: |
npm i -g yarn@1.19.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
- name: Build typings
run: yarn run build:typings
- name: Lint
run: yarn run lint

View File

@@ -1,107 +0,0 @@
name: Linux Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install bsdtar zsh
npm i -g yarn@1.19.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
npm run patch
- name: Build native deps
run: scripts/build-native.js
- name: Webpack
run: yarn run build
- name: Prepackage plugins
run: scripts/prepackage-plugins.js
- name: Build packages
run: scripts/build-linux.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
USE_HARD_LINKS: false
# DEBUG: electron-builder,electron-builder:*
- name: Build web resources
run: zsh -c 'tar czf tabby-web.tar.gz (tabby-*|web)/dist'
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-deb
mv dist/*.deb artifact-deb/ || true
mkdir artifact-rpm
mv dist/*.rpm artifact-rpm/ || true
mkdir artifact-pacman
mv dist/*.pacman artifact-pacman/ || true
mkdir artifact-snap
mv dist/*.snap artifact-snap/ || true
mkdir artifact-tar.gz
mv dist/*.tar.gz artifact-tar.gz/ || true
mkdir artifact-web
mv tabby-web.tar.gz artifact-web/ || true
- uses: actions/upload-artifact@master
name: Upload DEB
with:
name: Linux DEB
path: artifact-deb
- uses: actions/upload-artifact@master
name: Upload RPM
with:
name: Linux RPM
path: artifact-rpm
- uses: actions/upload-artifact@master
name: Upload Pacman Package
with:
name: Linux Pacman
path: artifact-pacman
- uses: actions/upload-artifact@master
name: Upload Snap
with:
name: Linux Snap
path: artifact-snap
- uses: actions/upload-artifact@master
name: Upload Linux tarball
with:
name: Linux tarball
path: artifact-tar.gz
- uses: actions/upload-artifact@master
name: Upload web tarball
with:
name: Web tarball
path: artifact-web

View File

@@ -1,99 +0,0 @@
name: macOS Build
on: [push, pull_request]
jobs:
build:
runs-on: macos-11.0
strategy:
matrix:
include:
- arch: x86_64
- arch: arm64
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Install deps
run: |
sudo npm i -g yarn@1.22.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
- name: Build native deps
run: scripts/build-native.js
env:
ARCH: ${{matrix.arch}}
- name: Build native deps
run: |
rm -rf app/node_modules/cpu-features
rm -rf app/node_modules/ssh2/crypto/build
if: ${{ matrix.arch == 'arm64' }}
- name: Webpack
run: yarn run build
- name: Prepackage plugins
run: scripts/prepackage-plugins.js
env:
ARCH: ${{matrix.arch}}
- run: sed -i '' 's/updateInfo = await/\/\/updateInfo = await/g' node_modules/app-builder-lib/out/targets/ArchiveTarget.js
- name: Build and sign packages
run: scripts/build-macos.js
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
env:
ARCH: ${{matrix.arch}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPSTORE_USERNAME: ${{ secrets.APPSTORE_USERNAME }}
APPSTORE_PASSWORD: ${{ secrets.APPSTORE_PASSWORD }}
USE_HARD_LINKS: false
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing
run: scripts/build-macos.js
if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
env:
ARCH: ${{matrix.arch}}
# DEBUG: electron-builder,electron-builder:*
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-pkg
mv dist/*.pkg artifact-pkg/
mkdir artifact-zip
mv dist/*.zip artifact-zip/
- uses: actions/upload-artifact@master
name: Upload PKG
with:
name: macOS .pkg (${{matrix.arch}})
path: artifact-pkg
- uses: actions/upload-artifact@master
name: Upload ZIP
with:
name: macOS .zip (${{matrix.arch}})
path: artifact-zip

View File

@@ -1,66 +0,0 @@
name: Windows Build
on: [push, pull_request]
jobs:
build:
runs-on: windows-2016
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Build
shell: powershell
run: |
npm i -g yarn@1.19.1
yarn
node scripts/build-native.js
yarn run build
node scripts/prepackage-plugins.js
- name: Build and sign packages
run: node scripts/build-windows.js
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing
run: node scripts/build-windows.js
if: "!(github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
- name: Upload symbols
run: |
npm install @sentry/cli
node scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-setup
mv dist/*-setup.exe artifact-setup/
mkdir artifact-portable
mv dist/*-portable.zip artifact-portable/
- uses: actions/upload-artifact@master
name: Upload installer
with:
name: Installer
path: artifact-setup
- uses: actions/upload-artifact@master
name: Upload portable build
with:
name: Portable build
path: artifact-portable

View File

@@ -15,8 +15,8 @@ yarn
```
```
# Linux (Debian here as an example)
sudo apt install libfontconfig-dev libsecret-1-dev bsdtar libnss3 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1
# Linux (Debian/Ubuntu here as an example)
sudo apt install libfontconfig-dev libsecret-1-dev libarchive-tools libnss3 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 cmake
yarn
./scripts/build-native.js
```

View File

@@ -2,7 +2,7 @@
<p align="center">
<a href="https://github.com/Eugeny/tabby/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/tabby/total.svg?label=RELEASE&logo=github&style=for-the-badge"></a> <a href="https://nightly.link/Eugeny/tabby/workflows/windows/master"><img src="https://shields.io/badge/-Nightly-blue?logo=windows&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/tabby/workflows/macos/master"><img src="https://shields.io/badge/-Nightly-black?logo=apple&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/tabby/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/tabby/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/tabby/total.svg?label=RELEASE&logo=github&style=for-the-badge"></a> &nbsp; <a href="https://nightly.link/Eugeny/tabby/workflows/build/master"><img src="https://shields.io/badge/-Nightly%20Builds-orange?logo=hackthebox&logoColor=fff&style=for-the-badge"/></a> &nbsp; <a href="https://matrix.to/#/#tabby-general:matrix.org"><img alt="Matrix" src="https://img.shields.io/matrix/tabby-general:matrix.org?logo=matrix&style=for-the-badge&color=magenta"></a>
</p>
<p align="center">
@@ -95,17 +95,17 @@ Tabby will run as a portable app on Windows, if you create a `data` folder in th
Plugins and themes can be installed directly from the Settings view inside Tabby.
* [clickable-links](https://github.com/Eugeny/terminus-clickable-links) - makes paths and URLs in the terminal clickable
* [clickable-links](https://github.com/Eugeny/tabby-clickable-links) - makes paths and URLs in the terminal clickable
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
* [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/tabby-save-output) - record terminal output into a file
* [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
* [hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
* [hype](https://github.com/Eugeny/tabby-theme-hype) - a Hyper inspired theme
* [relaxed](https://github.com/Relaxed-Theme/relaxed-terminal-themes#terminus) - the Relaxed theme for Tabby
* [gruvbox](https://github.com/porkloin/terminus-theme-gruvbox)
* [windows10](https://www.npmjs.com/package/terminus-theme-windows10)
@@ -184,6 +184,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<tr>
<td align="center"><a href="https://github.com/ydcool"><img src="https://avatars.githubusercontent.com/u/5668295?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dominic Yin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=ydcool" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bdr99"><img src="https://avatars.githubusercontent.com/u/2292715?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Rothweiler</b></sub></a><br /><a href="#design-bdr99" title="Design">🎨</a></td>
<td align="center"><a href="https://git.io/JnP49"><img src="https://avatars.githubusercontent.com/u/63876444?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Logic Machine</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=logicmachine123" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/cypherbits"><img src="https://avatars.githubusercontent.com/u/10424900?v=4?s=100" width="100px;" alt=""/><br /><sub><b>cypherbits</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=cypherbits" title="Documentation">📖</a></td>
<td align="center"><a href="https://modulolotus.net"><img src="https://avatars.githubusercontent.com/u/946421?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matthew Davidson</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=KingMob" title="Code">💻</a></td>
</tr>
</table>
@@ -193,3 +196,5 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
<img src="https://ga-beacon.appspot.com/UA-3278102-18/github/readme" width="1"/>

View File

@@ -16,12 +16,6 @@ export function parseArgs (argv: string[], cwd: string): any {
.command('profile [profileName]', 'open a tab with specified profile', {
profileName: { type: 'string' },
})
.command('connect-ssh [connectionName]', 'open a tab for a saved SSH connection', {
connectionName: { type: 'string' },
})
.command('connect-serial [connectionName]', 'open a tab for a saved serial connection', {
connectionName: { type: 'string' },
})
.command('paste [text]', 'paste stdin into the active tab', yargs => {
return yargs.option('escape', {
alias: 'e',

View File

@@ -1,7 +1,6 @@
import * as glasstron from 'glasstron'
import { Subject, Observable } from 'rxjs'
import { debounceTime } from 'rxjs/operators'
import { Subject, Observable, debounceTime } from 'rxjs'
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions } from 'electron'
import ElectronConfig = require('electron-config')
import * as os from 'os'
@@ -118,7 +117,7 @@ export class Window {
})
this.window.on('blur', () => {
if (this.configStore.appearance?.dock !== 'off' && this.configStore.appearance?.dockHideOnBlur) {
if ((this.configStore.appearance?.dock ?? 'off') !== 'off' && this.configStore.appearance?.dockHideOnBlur) {
this.hide()
}
})
@@ -364,7 +363,7 @@ export class Window {
this.disableVibrancyWhileDragging = value
})
let moveEndedTimeout: number|null = null
let moveEndedTimeout: any = null
const onBoundsChange = () => {
if (!this.lastVibrancy?.enabled || !this.disableVibrancyWhileDragging) {
return

View File

@@ -14,15 +14,7 @@
"watch": "webpack --progress --color --watch"
},
"dependencies": {
"@angular/animations": "^12.0.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@electron/remote": "1.2.0",
"@ng-bootstrap/ng-bootstrap": "^9.1.1",
"any-promise": "^1.3.0",
"electron-config": "2.0.0",
"electron-debug": "^3.2.0",
@@ -33,26 +25,24 @@
"keytar": "^7.7.0",
"mz": "^2.7.0",
"native-process-working-directory": "^1.0.2",
"ngx-toastr": "^14.0.0",
"node-pty": "^0.10.1",
"npm": "6",
"rxjs": "^7.1.0",
"yargs": "^17.0.1",
"zone.js": "^0.11.4"
"rxjs": "^7.2.0",
"source-map-support": "^0.5.19",
"yargs": "^17.0.1"
},
"optionalDependencies": {
"macos-native-processlist": "^2.0.0",
"serialport": "^9.2.0",
"windows-blurbehind": "^1.0.1",
"windows-native-registry": "^3.0.0",
"windows-native-registry": "^3.1.0",
"windows-process-tree": "^0.3.0"
},
"devDependencies": {
"@types/mz": "2.7.3",
"@types/node": "15.12.5",
"@types/mz": "2.7.4",
"@types/node": "16.0.1",
"ngx-filesize": "^2.0.16",
"node-abi": "^2.30.0",
"source-map-support": "^0.5.19"
"node-abi": "^2.30.0"
},
"peerDependencies": {
"tabby-community-color-schemes": "*",

View File

@@ -5,13 +5,15 @@ import 'rxjs'
import './global.scss'
import './toastr.scss'
// Importing before @angular/*
import { findPlugins, initModuleLookup, loadPlugins } from './plugins'
import { enableProdMode, NgModuleRef, ApplicationRef } from '@angular/core'
import { enableDebugTools } from '@angular/platform-browser'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { ipcRenderer } from 'electron'
import { getRootModule } from './app.module'
import { findPlugins, initModuleLookup, loadPlugins } from './plugins'
import { BootstrapData, BOOTSTRAP_DATA, PluginInfo } from '../../tabby-core/src/api/mainProcess'
// Always land on the start view

View File

@@ -115,7 +115,8 @@ ngb-typeahead-window {
.hover-reveal-parent:hover &,
*:hover > &,
&:hover {
&:hover,
&.show {
opacity: 1;
}
}
@@ -158,3 +159,31 @@ ngb-typeahead-window {
text-overflow: ellipsis;
overflow: hidden;
}
.list-group-item > button {
margin: -7px 0;
}
// Windows high contrast mode
@media screen and (forced-colors: active) {
.custom-switch .custom-control-label::before {
background: buttonface;
}
.custom-switch .custom-control-label::after {
background: buttontext;
}
.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
background: activetext;
}
.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
background: canvas;
}
color-scheme-preview, terminaltab > .content {
forced-color-adjust: none;
}
}

View File

@@ -18,28 +18,47 @@ function normalizePath (p: string): string {
const builtinPluginsPath = process.env.TABBY_DEV ? path.dirname(remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
const cachedBuiltinModules = {
'@angular/animations': require('@angular/animations'),
'@angular/common': require('@angular/common'),
'@angular/compiler': require('@angular/compiler'),
'@angular/core': require('@angular/core'),
'@angular/forms': require('@angular/forms'),
'@angular/platform-browser': require('@angular/platform-browser'),
'@angular/platform-browser/animations': require('@angular/platform-browser/animations'),
'@angular/platform-browser-dynamic': require('@angular/platform-browser-dynamic'),
'@ng-bootstrap/ng-bootstrap': require('@ng-bootstrap/ng-bootstrap'),
'ngx-toastr': require('ngx-toastr'),
rxjs: require('rxjs'),
'rxjs/operators': require('rxjs/operators'),
'zone.js/dist/zone.js': require('zone.js/dist/zone.js'),
}
const builtinModules = [
'@angular/animations',
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/forms',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@ng-bootstrap/ng-bootstrap',
'ngx-toastr',
'rxjs',
'rxjs/operators',
...Object.keys(cachedBuiltinModules),
'tabby-core',
'tabby-local',
'tabby-settings',
'tabby-terminal',
'zone.js/dist/zone.js',
]
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
const originalRequire = (global as any).require
;(global as any).require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, [query])
}
const cachedBuiltinModules = {}
const originalModuleRequire = nodeModule.prototype.require
nodeModule.prototype.require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalModuleRequire.call(this, query)
}
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
export function initModuleLookup (userPluginsPath: string): void {
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
@@ -57,24 +76,10 @@ export function initModuleLookup (userPluginsPath: string): void {
}
builtinModules.forEach(m => {
cachedBuiltinModules[m] = nodeRequire(m)
if (!cachedBuiltinModules[m]) {
cachedBuiltinModules[m] = nodeRequire(m)
}
})
const originalRequire = (global as any).require
;(global as any).require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, [query])
}
const originalModuleRequire = nodeModule.prototype.require
nodeModule.prototype.require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalModuleRequire.call(this, query)
}
}
export async function findPlugins (): Promise<PluginInfo[]> {

View File

@@ -31,9 +31,9 @@ module.exports = {
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
loader: 'ts-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
configFile: path.resolve(__dirname, 'tsconfig.json'),
},
},
},
@@ -60,23 +60,13 @@ module.exports = {
],
},
externals: {
'@angular/core': 'commonjs @angular/core',
'@angular/compiler': 'commonjs @angular/compiler',
'@angular/platform-browser': 'commonjs @angular/platform-browser',
'@angular/platform-browser-dynamic': 'commonjs @angular/platform-browser-dynamic',
'@angular/forms': 'commonjs @angular/forms',
'@angular/common': 'commonjs @angular/common',
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
'@electron/remote': 'commonjs @electron/remote',
child_process: 'commonjs child_process',
electron: 'commonjs electron',
fs: 'commonjs fs',
'ngx-toastr': 'commonjs ngx-toastr',
module: 'commonjs module',
mz: 'commonjs mz',
path: 'commonjs path',
rxjs: 'commonjs rxjs',
'zone.js': 'commonjs zone.js/dist/zone.js',
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),

View File

@@ -25,9 +25,9 @@ module.exports = {
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
loader: 'ts-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.main.json'),
configFile: path.resolve(__dirname, 'tsconfig.main.json'),
},
},
},
@@ -39,15 +39,12 @@ module.exports = {
'electron-config': 'commonjs electron-config',
'electron-debug': 'commonjs electron-debug',
'electron-promise-ipc': 'commonjs electron-promise-ipc',
'electron-vibrancy': 'commonjs electron-vibrancy',
fs: 'commonjs fs',
glasstron: 'commonjs glasstron',
mz: 'commonjs mz',
npm: 'commonjs npm',
'node-pty': 'commonjs node-pty',
path: 'commonjs path',
rxjs: 'commonjs rxjs',
'rxjs/operators': 'commonjs rxjs/operators',
util: 'commonjs util',
'source-map-support': 'commonjs source-map-support',
'windows-swca': 'commonjs windows-swca',

View File

@@ -2,55 +2,6 @@
# yarn lockfile v1
"@angular/animations@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-12.0.0.tgz#5f845b1a58ffb6f3ea6103edf0756ac65320b725"
integrity sha512-BG/Ksk3863I7GKUem73Kty4UeU289oN+iPo/0O0x2dJCzNcpafML0GJpz4lg/RT9l6UddFviI4q9NiopR+eJfw==
dependencies:
tslib "^2.1.0"
"@angular/common@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-12.0.0.tgz#a4b992f3af997e9e957500148100f3f2a90ad3e9"
integrity sha512-d6+WSnCFcxAHBsbCvBC3Rutmk+tB5CEdKhkTBY/vGe0A/MjbayzHR4IDv2i0+UZDLSgMJubqh3iCPUcSglXSEg==
dependencies:
tslib "^2.1.0"
"@angular/compiler@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-12.0.0.tgz#bb0d4f464fee4803dbda49d862474f771c31f633"
integrity sha512-7NdZNyxm9KLlRMmmtId6RfV6VbQIUMDxN44R+ax66BoWsuhdYXUDsDO554LwYwrjnnXXGkurDJhv7umeRwaZGw==
dependencies:
tslib "^2.1.0"
"@angular/core@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-12.0.0.tgz#d16a217f0919b3b161229118c52b1f703815eb71"
integrity sha512-fwXtF6qP8pr07+El/dg67RmgsI4Ubfi+E5YLjYKQ62gM8MzYyYGmLPakFzFnbzYrOr05zdprrbcVgGtMRHapMA==
dependencies:
tslib "^2.1.0"
"@angular/forms@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-12.0.0.tgz#faf5e3e36a8c4f57f42a5b3dd11786f39c94d693"
integrity sha512-/Z2AWd2k/9cs+WwXBlZ8yUqgGsHYcp8g6PUCehZQk1gd/4n4FOKvTIGiypajGUPwO4GOHJDzibfCsGw8MenCpQ==
dependencies:
tslib "^2.1.0"
"@angular/platform-browser-dynamic@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.0.0.tgz#295036e7b487b6dbe3b13db763a371675d391ee6"
integrity sha512-Rkxr/KVOZGuGSuIYo2XZYbOpyS2t2jpLPS65KUUcOEwktj4hSv5VZ2soZF18tG5ZNbx06C1QDW/j9HwmZjEh5g==
dependencies:
tslib "^2.1.0"
"@angular/platform-browser@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-12.0.0.tgz#097805ad9a5db044dc0a74c1294cdfa5122eca4c"
integrity sha512-h+uMMluRh4dqJIor7EpvwNKRjv4xCxpttizJlqbo3vfcoOoLDoc9SvEFiXxd+UVh3S0re8zBsyBIJl+gTVFKWQ==
dependencies:
tslib "^2.1.0"
"@electron/remote@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-1.2.0.tgz#772eb4c3ac17aaba5a9cf05a09092f6277f5671f"
@@ -65,13 +16,6 @@
update-notifier "^2.2.0"
yargs "^8.0.2"
"@ng-bootstrap/ng-bootstrap@^9.1.1":
version "9.1.1"
resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-9.1.1.tgz#5a629915ea93b4f9b4d61854cb6862d99a7c9ca4"
integrity sha512-m31qKJylYueXm+a3YEoOfnrJYR1lovb7WgaQwvXQz3dDmtaYRX4n8aPeCMp1VrI7hFfFITKWo0GxPaI3JIFk4w==
dependencies:
tslib "^2.0.0"
"@serialport/binding-abstract@^9.0.7":
version "9.0.7"
resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-9.0.7.tgz#d2c7ecea0f100bdf20187bfc0d34ba90f5504e1e"
@@ -143,17 +87,17 @@
dependencies:
debug "^4.3.1"
"@types/mz@2.7.3":
version "2.7.3"
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-2.7.3.tgz#e42a21e73f5f9340fe4a176981fafb1eb8cc6c12"
integrity sha512-Zp1NUJ4Alh3gaun0a5rkF3DL7b2j1WB6rPPI5h+CJ98sQnxe9qwskClvupz/4bqChGR3L/BRhTjlaOwR+uiZJg==
"@types/mz@2.7.4":
version "2.7.4"
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-2.7.4.tgz#f9d1535cb5171199b28ae6abd6ec29e856551401"
integrity sha512-Zs0imXxyWT20j3Z2NwKpr0IO2LmLactBblNyLua5Az4UHuqOQ02V3jPTgyKwDkuc33/ahw+C3O1PIZdrhFMuQA==
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@15.12.5":
version "15.12.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.5.tgz#9a78318a45d75c9523d2396131bd3cca54b2d185"
integrity sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==
"@types/node@*", "@types/node@16.0.1":
version "16.0.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.1.tgz#70cedfda26af7a2ca073fdcc9beb2fff4aa693f8"
integrity sha512-hBOx4SUlEPKwRi6PrXuTGw1z6lz0fjsibcWCM378YxsSu/6+C30L6CR49zIBKHiwNWCYIcOLjg4OHKZaFeLAug==
JSONStream@^1.3.4, JSONStream@^1.3.5:
version "1.3.5"
@@ -2099,13 +2043,6 @@ ngx-filesize@^2.0.16:
filesize ">= 4.0.0"
tslib "^2.0.0"
ngx-toastr@^14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-14.0.0.tgz#20e4737ef330b892a453768cd98b980558aeb286"
integrity sha512-dnDzSY73pF6FvNyxdh6ftfvXvUg6SU7MAT3orPUCzA77t3ZcFslro06zk4NCA2g67RF7dBwM0OJ/y0SN6fdGYw==
dependencies:
tslib "^2.1.0"
node-abi@^2.20.0, node-abi@^2.30.0, node-abi@^2.7.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.0.tgz#8be53bf3e7945a34eea10e0fc9a5982776cf550b"
@@ -3016,10 +2953,10 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rxjs@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.1.0.tgz#94202d27b19305ef7b1a4f330277b2065df7039e"
integrity sha512-gCFO5iHIbRPwznl6hAYuwNFld8W4S2shtSJIqG27ReWXo9IWrCyEICxUA+6vJHwSR/OakoenC4QsDxq50tzYmw==
rxjs@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.2.0.tgz#5cd12409639e9514a71c9f5f9192b2c4ae94de31"
integrity sha512-aX8w9OpKrQmiPKfT1bqETtUr9JygIz6GZ+gql8v7CijClsP0laoFUdKzxFAoWuRdSlOdU2+crss+cMf+cqMTnw==
dependencies:
tslib "~2.1.0"
@@ -3457,7 +3394,7 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tslib@^2.0.0, tslib@^2.1.0:
tslib@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
@@ -3649,12 +3586,12 @@ windows-blurbehind@^1.0.1:
resolved "https://registry.yarnpkg.com/windows-blurbehind/-/windows-blurbehind-1.0.1.tgz#ff098713873304e38330b2c54cc41bb369b587b9"
integrity sha512-1HzHfCiM1ayrbACJu5qE9zELV24uX/tINT6kxaZwLY3rtQAoeav6x9z7LFHWoLaGDN/sYbnK+9Vk0cz7fsk5HQ==
windows-native-registry@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/windows-native-registry/-/windows-native-registry-3.0.0.tgz#82e715df7a59d5054c768547d81e0bfc81a59d2e"
integrity sha512-Mz/9a23UivwPc23DsTOL/ZCp/XXogT+6h/khk1psOfDDusXqpomBdxNdsBBE/BvIgOExjGom0XPOfEPiDnHy7A==
windows-native-registry@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/windows-native-registry/-/windows-native-registry-3.1.0.tgz#909ef3254519fdec57d2f149ac59a2c9dc84419a"
integrity sha512-WrDysn2V7dH+EYE6cS2RF+7r2P+M0pOYWtU8iBrjV2HaGkCLlUdGUWzOdzT0JPdWwz0BkVu3IOae2xmBajQqBA==
dependencies:
node-addon-api "^3.0.0"
node-addon-api "^3.1.0"
windows-process-tree@^0.3.0:
version "0.3.0"
@@ -3820,10 +3757,3 @@ yargs@^8.0.2:
which-module "^2.0.0"
y18n "^3.2.1"
yargs-parser "^7.0.0"
zone.js@^0.11.4:
version "0.11.4"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.4.tgz#0f70dcf6aba80f698af5735cbb257969396e8025"
integrity sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==
dependencies:
tslib "^2.0.0"

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,40 @@
{
"devDependencies": {
"@angular/animations": "^12.0.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@fortawesome/fontawesome-free": "^5.15.3",
"@sentry/cli": "^1.64.2",
"@sentry/electron": "^2.5.0",
"@terminus-term/to-string-loader": "1.1.7-beta.1",
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@sentry/cli": "^1.67.1",
"@sentry/electron": "^2.5.1",
"@tabby-gang/to-string-loader": "^1.1.7-beta.2",
"@types/electron-config": "^3.2.2",
"@types/electron-debug": "^2.1.0",
"@types/fs-extra": "^9.0.11",
"@types/js-yaml": "^4.0.1",
"@types/node": "15.12.5",
"@types/webpack-env": "^1.16.0",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.28.0",
"@types/fs-extra": "^9.0.12",
"@types/js-yaml": "^4.0.2",
"@types/node": "16.0.1",
"@types/sortablejs": "^1.10.7",
"@types/webpack-env": "^1.16.2",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"apply-loader": "2.0.0",
"awesome-typescript-loader": "^5.2.1",
"axios": "^0.21.1",
"clone-deep": "^4.0.1",
"compare-versions": "^3.6.0",
"core-js": "^3.14.0",
"core-js": "^3.15.2",
"cross-env": "7.0.3",
"css-loader": "5.2.6",
"electron": "13.1.4",
"electron": "13.1.7",
"electron-builder": "22.10.5",
"electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0",
"electron-notarize": "^1.0.0",
"electron-rebuild": "^2.3.5",
"eslint": "^7.29.0",
"eslint": "^7.30.0",
"eslint-plugin-import": "^2.23.4",
"file-loader": "^6.2.0",
"graceful-fs": "^4.2.6",
@@ -32,6 +42,8 @@
"json-loader": "0.5.7",
"lru-cache": "^6.0.0",
"macos-release": "^2.5.0",
"ngx-sortablejs": "^11.1.0",
"ngx-toastr": "^14.0.0",
"node-abi": "^2.30.0",
"node-sass": "^6.0.1",
"npmlog": "4.1.2",
@@ -44,27 +56,35 @@
"pug-static-loader": "2.0.0",
"raw-loader": "4.0.2",
"sass-loader": "^12.1.0",
"shell-quote": "^1.7.2",
"shelljs": "0.8.4",
"slugify": "^1.5.3",
"sortablejs": "^1.14.0",
"source-code-pro": "^2.38.0",
"source-map-loader": "^3.0.0",
"source-sans-pro": "3.6.0",
"style-loader": "^3.0.0",
"ssh2": "^1.1.0",
"style-loader": "^3.1.0",
"svg-inline-loader": "^0.8.2",
"ts-loader": "^9.2.3",
"tslib": "^2.3.0",
"typedoc": "^0.21.2",
"typescript": "^4.2.4",
"typedoc": "^0.21.4",
"typescript": "^4.3.5",
"url-loader": "^4.1.1",
"val-loader": "4.0.0",
"webpack": "^5.41.0",
"webpack": "^5.43.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.0",
"yaml-loader": "0.6.0"
"yaml-loader": "0.6.0",
"zone.js": "^0.11.4"
},
"resolutions": {
"lzma-native": "^8.0.0",
"*/node-abi": "^2.30.0",
"**/graceful-fs": "^4.2.4"
},
"scripts": {
"build": "npm run build:typings && webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config tabby-core/webpack.config.js && webpack --color --config tabby-settings/webpack.config.js && webpack --color --config tabby-terminal/webpack.config.js && webpack --color --config tabby-local/webpack.config.js && webpack --color --config tabby-plugin-manager/webpack.config.js && webpack --color --config tabby-community-color-schemes/webpack.config.js && webpack --color --config tabby-ssh/webpack.config.js && webpack --color --config tabby-serial/webpack.config.js && webpack --color --config tabby-electron/webpack.config.js && webpack --color --config tabby-web/webpack.config.js && webpack --color --config web/webpack.config.js",
"build": "npm run build:typings && node scripts/build-modules.js",
"build:typings": "node scripts/build-typings.js",
"watch": "cross-env TABBY_DEV=1 webpack --progress --color --watch",
"start": "cross-env TABBY_DEV=1 electron app --debug --inspect",

View File

@@ -1,13 +1,13 @@
diff --git a/node_modules/app-builder-lib/out/appInfo.js b/node_modules/app-builder-lib/out/appInfo.js
index 25a159e..d8a0262 100644
index 25a159e..bfe0590 100644
--- a/node_modules/app-builder-lib/out/appInfo.js
+++ b/node_modules/app-builder-lib/out/appInfo.js
@@ -165,7 +165,7 @@ class AppInfo {
get linuxPackageName() {
const name = this.name; // https://github.com/electron-userland/electron-builder/issues/2963
- return name.startsWith("@") ? this.sanitizedProductName : name;
+ return 'tabby-terminal'
+ return 'tabby-terminal';
}
get sanitizedName() {

15
patches/ssh2+1.1.0.patch Normal file
View File

@@ -0,0 +1,15 @@
diff --git a/node_modules/ssh2/lib/protocol/Protocol.js b/node_modules/ssh2/lib/protocol/Protocol.js
index b4d1ee0..1e3ac66 100644
--- a/node_modules/ssh2/lib/protocol/Protocol.js
+++ b/node_modules/ssh2/lib/protocol/Protocol.js
@@ -254,8 +254,8 @@ class Protocol {
);
if (greeting)
this._onWrite(greeting);
- this._onWrite(this._identRaw);
- this._onWrite(CRLF);
+ this._onWrite(Buffer.concat([this._identRaw, CRLF]));
+ // this._onWrite(CRLF);
});
}
_destruct(reason) {

18
scripts/build-modules.js Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env node
const vars = require('./vars')
const log = require('npmlog')
const webpack = require('webpack')
const { promisify } = require('util')
const configs = [
'../app/webpack.main.config.js',
'../app/webpack.config.js',
...vars.allPackages.map(x => `../${x}/webpack.config.js`),
]
;(async () => {
for (const c of configs) {
log.info('build', c)
await promisify(webpack)(require(c))
}
})()

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env node
const jsYaml = require('js-yaml')
const fs = require('fs')
const path = require('path')
const metadata = jsYaml.load(fs.readFileSync(path.resolve(__dirname, '../node_modules/@fortawesome/fontawesome-free/metadata/icons.yml')))
let result = {}
for (let key in metadata) {
result[key] = metadata[key].styles.map(x => x[0])
}
fs.writeFileSync(path.resolve(__dirname, '../tabby-core/src/icons.json'), JSON.stringify(result))

View File

@@ -4,8 +4,11 @@ const path = require('path')
const vars = require('./vars')
const log = require('npmlog')
const localBinPath = path.resolve(__dirname, '../node_modules/.bin');
const npx = `${localBinPath}/npx`;
const localBinPath = path.resolve(__dirname, '../node_modules/.bin')
const npx = `${localBinPath}/npx`
log.info('patch')
sh.exec(`${npx} patch-package`)
log.info('deps', 'app')
@@ -18,16 +21,16 @@ sh.exec(`${npx} yarn install --force`)
sh.cd('..')
vars.builtinPlugins.forEach(plugin => {
log.info('deps', plugin)
sh.cd(plugin)
sh.exec(`${npx} yarn install --force`)
sh.cd('..')
log.info('deps', plugin)
sh.cd(plugin)
sh.exec(`${npx} yarn install --force`)
sh.cd('..')
})
if (['darwin', 'linux'].includes(process.platform)) {
sh.cd('node_modules')
for (let x of vars.builtinPlugins) {
sh.ln('-fs', '../' + x, x)
}
sh.cd('..')
sh.cd('node_modules')
for (let x of vars.builtinPlugins) {
sh.ln('-fs', '../' + x, x)
}
sh.cd('..')
}

View File

@@ -3,7 +3,7 @@ const sh = require('shelljs')
const vars = require('./vars')
const log = require('npmlog')
vars.builtinPlugins.forEach(plugin => {
vars.allPackages.forEach(plugin => {
log.info('bump', plugin)
sh.cd(plugin)
sh.exec('npm --no-git-tag-version version ' + vars.version)

View File

@@ -5,28 +5,36 @@ const childProcess = require('child_process')
const electronInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../node_modules/electron/package.json')))
exports.version = childProcess.execSync('git describe --tags', {encoding:'utf-8'})
exports.version = childProcess.execSync('git describe --tags', { encoding:'utf-8' })
exports.version = exports.version.substring(1).trim()
exports.version = exports.version.replace('-', '-c')
if (exports.version.includes('-c')) {
exports.version = semver.inc(exports.version, 'prepatch').replace('-0', '-nightly.0')
exports.version = semver.inc(exports.version, 'prepatch').replace('-0', `-nightly.${process.env.REV ?? 0}`)
}
exports.builtinPlugins = [
'tabby-core',
'tabby-settings',
'tabby-terminal',
'tabby-electron',
'tabby-local',
'tabby-web',
'tabby-community-color-schemes',
'tabby-plugin-manager',
'tabby-ssh',
'tabby-serial',
'tabby-core',
'tabby-settings',
'tabby-terminal',
'tabby-electron',
'tabby-local',
'tabby-web',
'tabby-community-color-schemes',
'tabby-plugin-manager',
'tabby-ssh',
'tabby-serial',
'tabby-telnet',
]
exports.allPackages = [
...exports.builtinPlugins,
'web',
'tabby-web-demo',
]
exports.bundledModules = [
'@angular',
'@ng-bootstrap',
'@angular',
'@ng-bootstrap',
]
exports.electronVersion = electronInfo.version

View File

@@ -1 +0,0 @@
dist

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-community-color-schemes",
"version": "1.0.144",
"version": "1.0.148-nightly.2",
"description": "Community color schemes for Tabby",
"keywords": [
"tabby-builtin-plugin"

View File

@@ -1 +0,0 @@
dist

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-core",
"version": "1.0.144",
"version": "1.0.148-nightly.2",
"description": "Tabby core",
"keywords": [
"tabby-builtin-plugin"
@@ -19,7 +19,6 @@
"devDependencies": {
"@types/js-yaml": "^4.0.0",
"bootstrap": "^4.1.3",
"clone-deep": "^4.0.1",
"core-js": "^3.1.2",
"deep-equal": "^2.0.5",
"deepmerge": "^4.1.1",

View File

@@ -2,7 +2,7 @@ export { BaseComponent, SubscriptionContainer } from '../components/base.compone
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
export { TabHeaderComponent } from '../components/tabHeader.component'
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
export { TabRecoveryProvider, RecoveredTab, RecoveryToken } from './tabRecovery'
export { TabRecoveryProvider, RecoveryToken } from './tabRecovery'
export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider'
export { ConfigProvider } from './configProvider'
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
@@ -16,18 +16,21 @@ export { BootstrapData, PluginInfo, BOOTSTRAP_DATA } from './mainProcess'
export { HostWindowService } from './hostWindow'
export { HostAppService, Platform } from './hostApp'
export { FileProvider } from './fileProvider'
export { ProfileProvider, Profile, PartialProfile, ProfileSettingsComponent } from './profileProvider'
export { PromptModalComponent } from '../components/promptModal.component'
export { AppService } from '../services/app.service'
export { ConfigService } from '../services/config.service'
export { ConfigService, configMerge, ConfigProxy } from '../services/config.service'
export { DockingService, Screen } from '../services/docking.service'
export { Logger, ConsoleLogger, LogService } from '../services/log.service'
export { HomeBaseService } from '../services/homeBase.service'
export { HotkeysService } from '../services/hotkeys.service'
export { NotificationsService } from '../services/notifications.service'
export { ThemesService } from '../services/themes.service'
export { ProfilesService } from '../services/profiles.service'
export { SelectorService } from '../services/selector.service'
export { TabsService } from '../services/tabs.service'
export { TabsService, NewTabParameters, TabComponentType } from '../services/tabs.service'
export { UpdaterService } from '../services/updater.service'
export { VaultService, Vault, VaultSecret, VAULT_SECRET_TYPE_FILE } from '../services/vault.service'
export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE } from '../services/vault.service'
export { FileProvidersService } from '../services/fileProviders.service'
export * from '../utils'

View File

@@ -21,6 +21,7 @@ export interface MessageBoxResult {
export abstract class FileTransfer {
abstract getName (): string
abstract getMode (): number
abstract getSize (): number
abstract close (): void
@@ -95,7 +96,7 @@ export abstract class PlatformService {
abstract loadConfig (): Promise<string>
abstract saveConfig (content: string): Promise<void>
abstract startDownload (name: string, size: number): Promise<FileDownload|null>
abstract startDownload (name: string, mode: number, size: number): Promise<FileDownload|null>
abstract startUpload (options?: FileUploadOptions): Promise<FileUpload[]>
startUploadFromDragEvent (event: DragEvent, multiple = false): FileUpload[] {
@@ -188,6 +189,10 @@ export class HTMLFileUpload extends FileUpload {
return this.file.name
}
getMode (): number {
return 0o644
}
getSize (): number {
return this.file.size
}

View File

@@ -0,0 +1,56 @@
/* eslint-disable @typescript-eslint/no-type-alias */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-empty-function */
import { BaseTabComponent } from '../components/baseTab.component'
import { NewTabParameters } from '../services/tabs.service'
export interface Profile {
id: string
type: string
name: string
group?: string
options: any
icon?: string
color?: string
disableDynamicTitle: boolean
weight: number
isBuiltin: boolean
isTemplate: boolean
}
export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{
[K in keyof T]?: T[K]
}, 'options'>, 'type'>, 'name'> & {
type: string
name: string
options?: {
[K in keyof T['options']]?: T['options'][K]
}
}
export interface ProfileSettingsComponent<P extends Profile> {
profile: P
save?: () => void
}
export abstract class ProfileProvider<P extends Profile> {
id: string
name: string
supportsQuickConnect = false
settingsComponent?: new (...args: any[]) => ProfileSettingsComponent<P>
configDefaults = {}
abstract getBuiltinProfiles (): Promise<PartialProfile<P>[]>
abstract getNewTabParameters (profile: PartialProfile<P>): Promise<NewTabParameters<BaseTabComponent>>
abstract getDescription (profile: PartialProfile<P>): string
quickConnect (query: string): PartialProfile<P>|null {
return null
}
deleteProfile (profile: P): void { }
}

View File

@@ -1,17 +1,6 @@
import deepClone from 'clone-deep'
import { TabComponentType } from '../services/tabs.service'
export interface RecoveredTab {
/**
* Component type to be instantiated
*/
type: TabComponentType
/**
* Component instance inputs
*/
options?: any
}
import { BaseTabComponent } from '../components/baseTab.component'
import { NewTabParameters } from '../services/tabs.service'
export interface RecoveryToken {
[_: string]: any
@@ -35,19 +24,20 @@ export interface RecoveryToken {
* }
* ```
*/
export abstract class TabRecoveryProvider {
export abstract class TabRecoveryProvider <T extends BaseTabComponent> {
/**
* @param recoveryToken a recovery token found in the saved tabs list
* @returns [[boolean]] whether this [[TabRecoveryProvider]] can recover a tab from this token
*/
abstract applicableTo (recoveryToken: RecoveryToken): Promise<boolean>
/**
* @param recoveryToken a recovery token found in the saved tabs list
* @returns [[RecoveredTab]] descriptor containing tab type and component inputs
* @returns [[NewTabParameters]] descriptor containing tab type and component inputs
* or `null` if this token is from a different tab type or is not supported
*/
abstract recover (recoveryToken: RecoveryToken): Promise<RecoveredTab>
abstract recover (recoveryToken: RecoveryToken): Promise<NewTabParameters<T>>
/**
* @param recoveryToken a recovery token found in the saved tabs list

View File

@@ -0,0 +1,57 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
import { HostAppService, Platform } from './api/hostApp'
import { PartialProfile, Profile } from './api/profileProvider'
import { ConfigService } from './services/config.service'
import { HotkeysService } from './services/hotkeys.service'
import { ProfilesService } from './services/profiles.service'
/** @hidden */
@Injectable()
export class ButtonProvider extends ToolbarButtonProvider {
constructor (
private hostApp: HostAppService,
private profilesService: ProfilesService,
private config: ConfigService,
hotkeys: HotkeysService,
) {
super()
hotkeys.hotkey$.subscribe(hotkey => {
if (hotkey === 'profile-selector') {
this.activate()
}
})
}
async activate () {
const profile = await this.profilesService.showProfileSelector()
if (profile) {
this.launchProfile(profile)
}
}
async launchProfile (profile: PartialProfile<Profile>) {
await this.profilesService.openNewTabForProfile(profile)
let recentProfiles = this.config.store.recentProfiles
recentProfiles = recentProfiles.filter(x => x.group !== profile.group || x.name !== profile.name)
recentProfiles.unshift(profile)
if (recentProfiles.length > 5) {
recentProfiles.pop()
}
this.config.store.recentProfiles = recentProfiles
this.config.save()
}
provide (): ToolbarButton[] {
return [{
icon: this.hostApp.platform === Platform.Web
? require('./icons/plus.svg')
: require('./icons/profiles.svg'),
title: 'New tab with profile',
click: () => this.activate(),
}]
}
}

View File

@@ -1,6 +1,41 @@
import { Injectable } from '@angular/core'
import { HostAppService } from './api/hostApp'
import { CLIHandler, CLIEvent } from './api/cli'
import { HostWindowService } from './api/hostWindow'
import { ProfilesService } from './services/profiles.service'
@Injectable()
export class ProfileCLIHandler extends CLIHandler {
firstMatchOnly = true
priority = 0
constructor (
private profiles: ProfilesService,
private hostWindow: HostWindowService,
) {
super()
}
async handle (event: CLIEvent): Promise<boolean> {
const op = event.argv._[0]
if (op === 'profile') {
this.handleOpenProfile(event.argv.profileName)
return true
}
return false
}
private async handleOpenProfile (profileName: string) {
const profile = (await this.profiles.getProfiles()).find(x => x.name === profileName)
if (!profile) {
console.error('Requested profile', profileName, 'not found')
return
}
this.profiles.openNewTabForProfile(profile)
this.hostWindow.bringToFront()
}
}
@Injectable()
export class LastCLIHandler extends CLIHandler {

View File

@@ -64,7 +64,7 @@ export class AppRootComponent {
activeTransfersDropdownOpen = false
private logger: Logger
private constructor (
constructor (
private hotkeys: HotkeysService,
private updater: UpdaterService,
public hostWindow: HostWindowService,
@@ -80,7 +80,7 @@ export class AppRootComponent {
this.logger = log.create('main')
this.logger.info('v', platform.getAppVersion())
this.hotkeys.matchedHotkey.subscribe((hotkey: string) => {
this.hotkeys.hotkey$.subscribe((hotkey: string) => {
if (hotkey.startsWith('tab-')) {
const index = parseInt(hotkey.split('-')[1])
if (index <= this.app.tabs.length) {

View File

@@ -1,19 +1,19 @@
.modal-body
input.form-control(
[type]='password ? "password" : "text"',
[type]='password ? "password" : "text"',
autofocus,
[(ngModel)]='value',
#input,
[placeholder]='prompt',
[(ngModel)]='value',
#input,
[placeholder]='prompt',
(keyup.enter)='ok()',
(keyup.esc)='cancel()',
)
.d-flex.align-items-start.mt-2
checkbox(
*ngIf='showRememberCheckbox',
[(ngModel)]='remember',
[(ngModel)]='remember',
text='Remember'
)
button.btn.btn-primary.ml-auto(
(click)='ok()',
) Enter
) OK

View File

@@ -2,5 +2,5 @@
input.form-control(type='text', #input, [(ngModel)]='value', (keyup.enter)='save()', autofocus)
.modal-footer
button.btn.btn-outline-primary((click)='save()') Save
button.btn.btn-outline-secondary((click)='close()') Cancel
button.btn.btn-primary((click)='save()') Save
button.btn.btn-secondary((click)='close()') Cancel

View File

@@ -4,4 +4,4 @@
pre {{error}}
.modal-footer
button.btn.btn-outline-primary((click)='close()') Close
button.btn.btn-primary((click)='close()') Close

View File

@@ -15,7 +15,7 @@
*ngFor='let option of filteredOptions; let i = index'
)
i.icon(
class='fa-fw fas fa-{{option.icon}}',
class='fa-fw {{option.icon}}',
*ngIf='!iconIsSVG(option.icon)'
)
.icon(

View File

@@ -1,8 +1,8 @@
import { Observable, Subject } from 'rxjs'
import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, AfterViewInit, OnDestroy } from '@angular/core'
import { BaseTabComponent, BaseTabProcess } from './baseTab.component'
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from '../api/tabRecovery'
import { TabsService } from '../services/tabs.service'
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
import { TabsService, NewTabParameters } from '../services/tabs.service'
import { HotkeysService } from '../services/hotkeys.service'
import { TabRecoveryService } from '../services/tabRecovery.service'
@@ -209,7 +209,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
})
this.blurred$.subscribe(() => this.getAllTabs().forEach(x => x.emitBlurred()))
this.subscribeUntilDestroyed(this.hotkeys.matchedHotkey, hotkey => {
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (!this.hasFocus || !this.focusedTab) {
return
}
@@ -369,12 +369,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
await this.initialized$.toPromise()
this.attachTabView(tab)
setImmediate(() => {
this.layout()
this.tabAdded.next(tab)
this.focus(tab)
})
this.onAfterTabAdded(tab)
}
removeTab (tab: BaseTabComponent): void {
@@ -399,6 +394,21 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
}
replaceTab (tab: BaseTabComponent, newTab: BaseTabComponent): void {
const parent = this.getParentOf(tab)
if (!parent) {
return
}
const position = parent.children.indexOf(tab)
parent.children[position] = newTab
this.detachTabView(tab)
this.attachTabView(newTab)
tab.parent = null
newTab.parent = this
this.recoveryStateChangedHint.next()
this.onAfterTabAdded(newTab)
}
/**
* Moves focus in the given direction
*/
@@ -453,7 +463,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
async splitTab (tab: BaseTabComponent, dir: SplitDirection): Promise<BaseTabComponent|null> {
const newTab = await this.tabsService.duplicate(tab)
if (newTab) {
this.addTab(newTab, tab, dir)
await this.addTab(newTab, tab, dir)
}
return newTab
}
@@ -539,6 +549,14 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
}
private onAfterTabAdded (tab: BaseTabComponent) {
setImmediate(() => {
this.layout()
this.tabAdded.next(tab)
this.focus(tab)
})
}
private layoutInternal (root: SplitContainer, x: number, y: number, w: number, h: number) {
const size = root.orientation === 'v' ? h : w
const sizes = root.ratios.map(ratio => ratio * size)
@@ -601,7 +619,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} else {
const recovered = await this.tabRecovery.recoverTab(childState, duplicate)
if (recovered) {
const tab = this.tabsService.create(recovered.type, recovered.options)
const tab = this.tabsService.create(recovered)
children.push(tab)
tab.parent = this
this.attachTabView(tab)
@@ -618,16 +636,16 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
/** @hidden */
@Injectable()
export class SplitTabRecoveryProvider extends TabRecoveryProvider {
@Injectable({ providedIn: 'root' })
export class SplitTabRecoveryProvider extends TabRecoveryProvider<SplitTabComponent> {
async applicableTo (recoveryToken: RecoveryToken): Promise<boolean> {
return recoveryToken.type === 'app:split-tab'
}
async recover (recoveryToken: RecoveryToken): Promise<RecoveredTab> {
async recover (recoveryToken: RecoveryToken): Promise<NewTabParameters<SplitTabComponent>> {
return {
type: SplitTabComponent,
options: { _recoveredState: recoveryToken },
inputs: { _recoveredState: recoveryToken },
}
}

View File

@@ -13,7 +13,7 @@ import { ToolbarButton, ToolbarButtonProvider } from '../api'
export class StartPageComponent {
version: string
private constructor (
constructor (
private config: ConfigService,
private domSanitizer: DomSanitizer,
public homeBase: HomeBaseService,

View File

@@ -6,9 +6,6 @@ import { BaseTabComponent } from '../components/baseTab.component'
@Component({
selector: 'tab-body',
template: `
<!--perfect-scrollbar [config]="{ suppressScrollX: true }" *ngIf="scrollable">
<ng-template #scrollablePlaceholder></ng-template>
</perfect-scrollbar-->
<ng-template #placeholder></ng-template>
`,
styles: [
@@ -19,20 +16,18 @@ import { BaseTabComponent } from '../components/baseTab.component'
export class TabBodyComponent implements OnChanges {
@Input() @HostBinding('class.active') active: boolean
@Input() tab: BaseTabComponent
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder?: ViewContainerRef
ngOnChanges (changes) {
if (changes.tab) {
if (this.placeholder) {
this.placeholder.detach()
}
this.placeholder?.detach()
setImmediate(() => {
this.placeholder.insert(this.tab.hostView)
this.placeholder?.insert(this.tab.hostView)
})
}
}
ngOnDestroy () {
this.placeholder.detach()
this.placeholder?.detach()
}
}

View File

@@ -31,7 +31,7 @@ export class TabHeaderComponent extends BaseComponent {
@Input() progress: number|null
@ViewChild('handle') handle?: ElementRef
private constructor (
constructor (
public app: AppService,
public config: ConfigService,
private hostApp: HostAppService,
@@ -43,7 +43,7 @@ export class TabHeaderComponent extends BaseComponent {
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
) {
super()
this.subscribeUntilDestroyed(this.hotkeys.matchedHotkey, (hotkey) => {
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, (hotkey) => {
if (this.app.activeTab === this.tab) {
if (hotkey === 'rename-tab') {
this.showRenameTabModal()

View File

@@ -19,18 +19,6 @@
.description Toggles the Tabby window visibility
toggle([(ngModel)]='enableGlobalHotkey')
.form-line
.header
.title Enable #[strong SSH] plugin
.description Adds an SSH connection manager UI to Tabby
toggle([(ngModel)]='enableSSH')
.form-line
.header
.title Enable #[strong Serial] plugin
.description Allows attaching Tabby to serial ports
toggle([(ngModel)]='enableSerial')
.text-center.mt-5
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again

View File

@@ -11,8 +11,6 @@ import { HostWindowService } from '../api/hostWindow'
styles: [require('./welcomeTab.component.scss')],
})
export class WelcomeTabComponent extends BaseTabComponent {
enableSSH = false
enableSerial = false
enableGlobalHotkey = true
constructor (
@@ -21,23 +19,15 @@ export class WelcomeTabComponent extends BaseTabComponent {
) {
super()
this.setTitle('Welcome')
this.enableSSH = !config.store.pluginBlacklist.includes('ssh')
this.enableSerial = !config.store.pluginBlacklist.includes('serial')
}
closeAndDisable () {
async closeAndDisable () {
this.config.store.enableWelcomeTab = false
this.config.store.pluginBlacklist = []
if (!this.enableSSH) {
this.config.store.pluginBlacklist.push('ssh')
}
if (!this.enableSerial) {
this.config.store.pluginBlacklist.push('serial')
}
if (!this.enableGlobalHotkey) {
this.config.store.hotkeys['toggle-window'] = []
}
this.config.save()
await this.config.save()
this.hostWindow.reload()
}
}

View File

@@ -10,7 +10,7 @@ import { AppService } from '../services/app.service'
styles: [require('./windowControls.component.scss')],
})
export class WindowControlsComponent {
private constructor (public hostWindow: HostWindowService, public app: AppService) { }
constructor (public hostWindow: HostWindowService, public app: AppService) { }
async closeWindow () {
this.app.closeWindow()

View File

@@ -69,4 +69,8 @@ hotkeys:
pane-maximize:
- 'Ctrl-Alt-Enter'
close-pane: []
switch-profile:
- 'Ctrl-Alt-T'
profile-selector:
- 'Ctrl-Shift-T'
pluginBlacklist: ['ssh']

View File

@@ -68,4 +68,8 @@ hotkeys:
- '⌘-⌥-Enter'
close-pane:
- '⌘-Shift-W'
profile-selector:
- '⌘-E'
switch-profile:
- '⌘-Shift-E'
pluginBlacklist: ['ssh']

View File

@@ -70,4 +70,8 @@ hotkeys:
pane-maximize:
- 'Ctrl-Alt-Enter'
close-pane: []
switch-profile:
- 'Ctrl-Alt-T'
profile-selector:
- 'Ctrl-Shift-T'
pluginBlacklist: []

View File

@@ -15,7 +15,13 @@ appearance:
vibrancy: true
vibrancyType: 'blur'
terminal:
recoverTabs: true
showBuiltinProfiles: true
hotkeys:
profile:
__nonStructural: true
profiles: []
recentProfiles: []
recoverTabs: true
enableAnalytics: true
enableWelcomeTab: true
electronFlags:

View File

@@ -0,0 +1,19 @@
import { Directive, ElementRef, AfterViewInit } from '@angular/core'
/** @hidden */
@Directive({
selector: '[alwaysVisibleTypeahead]',
})
export class AlwaysVisibleTypeaheadDirective implements AfterViewInit {
constructor (private el: ElementRef) { }
ngAfterViewInit (): void {
this.el.nativeElement.addEventListener('focus', e => {
e.stopPropagation()
setTimeout(() => {
const inputEvent: Event = new Event('input')
e.target.dispatchEvent(inputEvent)
}, 0)
})
}
}

View File

@@ -1,10 +1,15 @@
import { Injectable } from '@angular/core'
import { ProfilesService } from './services/profiles.service'
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
/** @hidden */
@Injectable()
export class AppHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [
{
id: 'profile-selector',
name: 'Show profile selector',
},
{
id: 'toggle-fullscreen',
name: 'Toggle fullscreen mode',
@@ -165,13 +170,28 @@ export class AppHotkeyProvider extends HotkeyProvider {
id: 'pane-nav-next',
name: 'Focus next pane',
},
{
id: 'switch-profile',
name: 'Switch profile in the active pane',
},
{
id: 'close-pane',
name: 'Close focused pane',
},
]
constructor (
private profilesService: ProfilesService,
) { super() }
async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys
const profiles = await this.profilesService.getProfiles()
return [
...this.hotkeys,
...profiles.map(profile => ({
id: `profile.${profile.id}`,
name: `New tab: ${profile.name}`,
})),
]
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 449 B

View File

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View File

@@ -6,10 +6,12 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-scrollbar'
import { NgxFilesizeModule } from 'ngx-filesize'
import { DndModule } from 'ng2-dnd'
import { SortablejsModule } from 'ngx-sortablejs'
import { AppRootComponent } from './components/appRoot.component'
import { CheckboxComponent } from './components/checkbox.component'
import { TabBodyComponent } from './components/tabBody.component'
import { PromptModalComponent } from './components/promptModal.component'
import { SafeModeModalComponent } from './components/safeModeModal.component'
import { StartPageComponent } from './components/startPage.component'
import { TabHeaderComponent } from './components/tabHeader.component'
@@ -25,20 +27,24 @@ import { WelcomeTabComponent } from './components/welcomeTab.component'
import { TransfersMenuComponent } from './components/transfersMenu.component'
import { AutofocusDirective } from './directives/autofocus.directive'
import { AlwaysVisibleTypeaheadDirective } from './directives/alwaysVisibleTypeahead.directive'
import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { DropZoneDirective } from './directives/dropZone.directive'
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider } from './api'
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ToolbarButtonProvider, ProfilesService, ProfileProvider } from './api'
import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service'
import { VaultFileProvider } from './services/vault.service'
import { HotkeysService } from './services/hotkeys.service'
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
import { CoreConfigProvider } from './config'
import { AppHotkeyProvider } from './hotkeys'
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu } from './tabContextMenu'
import { LastCLIHandler } from './cli'
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
import { LastCLIHandler, ProfileCLIHandler } from './cli'
import { ButtonProvider } from './buttonProvider'
import { SplitLayoutProfilesService } from './profiles'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css'
@@ -52,10 +58,14 @@ const PROVIDERS = [
{ provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: TabManagementContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: TaskCompletionContextMenu, multi: true },
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
{ provide: TabContextMenuItemProvider, useClass: ProfilesContextMenu, multi: true },
{ provide: TabRecoveryProvider, useExisting: SplitTabRecoveryProvider, multi: true },
{ provide: CLIHandler, useClass: ProfileCLIHandler, multi: true },
{ provide: CLIHandler, useClass: LastCLIHandler, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
{ provide: FileProvider, useClass: VaultFileProvider, multi: true },
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
]
/** @hidden */
@@ -68,10 +78,12 @@ const PROVIDERS = [
NgxFilesizeModule,
PerfectScrollbarModule,
DndModule.forRoot(),
SortablejsModule.forRoot({ animation: 150 }),
],
declarations: [
AppRootComponent as any,
AppRootComponent,
CheckboxComponent,
PromptModalComponent,
StartPageComponent,
TabBodyComponent,
TabHeaderComponent,
@@ -82,6 +94,7 @@ const PROVIDERS = [
SafeModeModalComponent,
AutofocusDirective,
FastHtmlBindDirective,
AlwaysVisibleTypeaheadDirective,
SelectorModalComponent,
SplitTabComponent,
SplitTabSpannerComponent,
@@ -91,6 +104,7 @@ const PROVIDERS = [
DropZoneDirective,
],
entryComponents: [
PromptModalComponent,
RenameTabModalComponent,
SafeModeModalComponent,
SelectorModalComponent,
@@ -101,21 +115,41 @@ const PROVIDERS = [
exports: [
CheckboxComponent,
ToggleComponent,
PromptModalComponent,
AutofocusDirective,
DropZoneDirective,
FastHtmlBindDirective,
AlwaysVisibleTypeaheadDirective,
SortablejsModule,
],
})
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
constructor (app: AppService, config: ConfigService, platform: PlatformService) {
constructor (
app: AppService,
config: ConfigService,
platform: PlatformService,
hotkeys: HotkeysService,
profilesService: ProfilesService,
) {
app.ready$.subscribe(() => {
if (config.store.enableWelcomeTab) {
app.openNewTabRaw(WelcomeTabComponent)
app.openNewTabRaw({ type: WelcomeTabComponent })
}
})
platform.setErrorHandler(err => {
console.error('Unhandled exception:', err)
})
hotkeys.hotkey$.subscribe(async (hotkey) => {
if (hotkey.startsWith('profile.')) {
const id = hotkey.split('.')[1]
const profile = (await profilesService.getProfiles()).find(x => x.id === id)
if (profile) {
profilesService.openNewTabForProfile(profile)
}
}
})
}
static forRoot (): ModuleWithProviders<AppModule> {

View File

@@ -0,0 +1,57 @@
import slugify from 'slugify'
import { v4 as uuidv4 } from 'uuid'
import { Injectable } from '@angular/core'
import { ConfigService, NewTabParameters, PartialProfile, Profile, ProfileProvider } from './api'
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
export interface SplitLayoutProfileOptions {
recoveryToken: any
}
export interface SplitLayoutProfile extends Profile {
options: SplitLayoutProfileOptions
}
@Injectable({ providedIn: 'root' })
export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfile> {
id = 'split-layout'
name = 'Saved layout'
configDefaults = {
options: {
recoveryToken: null,
},
}
constructor (
private splitTabRecoveryProvider: SplitTabRecoveryProvider,
private config: ConfigService,
) {
super()
}
async getBuiltinProfiles (): Promise<PartialProfile<SplitLayoutProfile>[]> {
return []
}
async getNewTabParameters (profile: SplitLayoutProfile): Promise<NewTabParameters<SplitTabComponent>> {
return this.splitTabRecoveryProvider.recover(profile.options.recoveryToken)
}
getDescription (_: SplitLayoutProfile): string {
return ''
}
async createProfile (tab: SplitTabComponent, name: string): Promise<void> {
const token = await tab.getRecoveryToken()
const profile: PartialProfile<SplitLayoutProfile> = {
id: `${this.id}:custom:${slugify(name)}:${uuidv4()}`,
type: this.id,
name,
options: {
recoveryToken: token,
},
}
this.config.store.profiles.push(profile)
await this.config.save()
}
}

View File

@@ -1,6 +1,4 @@
import { Observable, Subject, AsyncSubject } from 'rxjs'
import { takeUntil } from 'rxjs/operators'
import { Observable, Subject, AsyncSubject, takeUntil } from 'rxjs'
import { Injectable, Inject } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
@@ -13,7 +11,7 @@ import { HostAppService } from '../api/hostApp'
import { ConfigService } from './config.service'
import { TabRecoveryService } from './tabRecovery.service'
import { TabsService, TabComponentType } from './tabs.service'
import { TabsService, NewTabParameters } from './tabs.service'
import { SelectorService } from './selector.service'
class CompletionObserver {
@@ -88,10 +86,10 @@ export class AppService {
config.ready$.toPromise().then(async () => {
if (this.bootstrapData.isFirstWindow) {
if (config.store.terminal.recoverTabs) {
if (config.store.recoverTabs) {
const tabs = await this.tabRecovery.recoverTabs()
for (const tab of tabs) {
this.openNewTabRaw(tab.type, tab.options)
this.openNewTabRaw(tab)
}
}
/** Continue to store the tabs even if the setting is currently off */
@@ -152,8 +150,8 @@ export class AppService {
* Adds a new tab **without** wrapping it in a SplitTabComponent
* @param inputs Properties to be assigned on the new tab component instance
*/
openNewTabRaw (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent {
const tab = this.tabsService.create(type, inputs)
openNewTabRaw <T extends BaseTabComponent> (params: NewTabParameters<T>): T {
const tab = this.tabsService.create(params)
this.addTabRaw(tab)
return tab
}
@@ -162,9 +160,9 @@ export class AppService {
* Adds a new tab while wrapping it in a SplitTabComponent
* @param inputs Properties to be assigned on the new tab component instance
*/
openNewTab (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent {
const splitTab = this.tabsService.create(SplitTabComponent) as SplitTabComponent
const tab = this.tabsService.create(type, inputs)
openNewTab <T extends BaseTabComponent> (params: NewTabParameters<T>): T {
const splitTab = this.tabsService.create({ type: SplitTabComponent })
const tab = this.tabsService.create(params)
splitTab.addTab(tab, null, 'r')
this.addTabRaw(splitTab)
return tab
@@ -175,7 +173,7 @@ export class AppService {
if (token) {
const recoveredTab = await this.tabRecovery.recoverTab(token)
if (recoveredTab) {
const tab = this.tabsService.create(recoveredTab.type, recoveredTab.options)
const tab = this.tabsService.create(recoveredTab)
if (this.activeTab) {
this.addTabRaw(tab, this.tabs.indexOf(this.activeTab) + 1)
} else {

View File

@@ -1,5 +1,8 @@
import { Observable, Subject, AsyncSubject } from 'rxjs'
import deepClone from 'clone-deep'
import deepEqual from 'deep-equal'
import { v4 as uuidv4 } from 'uuid'
import * as yaml from 'js-yaml'
import { Observable, Subject, AsyncSubject } from 'rxjs'
import { Injectable, Inject } from '@angular/core'
import { ConfigProvider } from '../api/configProvider'
import { PlatformService } from '../api/platform'
@@ -7,7 +10,8 @@ import { HostAppService } from '../api/hostApp'
import { Vault, VaultService } from './vault.service'
const deepmerge = require('deepmerge')
const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
const LATEST_VERSION = 1
@@ -17,7 +21,7 @@ function isStructuralMember (v) {
}
function isNonStructuralObjectMember (v): boolean {
return v instanceof Object && !(v instanceof Array) && v.__nonStructural
return v instanceof Object && (v instanceof Array || v.__nonStructural)
}
/** @hidden */
@@ -45,38 +49,63 @@ export class ConfigProxy {
{
enumerable: true,
configurable: false,
get: () => this.getValue(key),
get: () => this.__getValue(key),
set: (value) => {
this.setValue(key, value)
this.__setValue(key, value)
},
}
)
}
}
this.getValue = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
this.__getValue = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
if (real[key] !== undefined) {
return real[key]
} else {
if (isNonStructuralObjectMember(defaults[key])) {
real[key] = { ...defaults[key] }
// The object might be modified outside
real[key] = this.__getDefault(key)
delete real[key].__nonStructural
return real[key]
} else {
return defaults[key]
}
return this.__getDefault(key)
}
}
this.setValue = (key: string, value: any) => { // eslint-disable-line @typescript-eslint/unbound-method
real[key] = value
this.__getDefault = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
return deepClone(defaults[key])
}
this.__setValue = (key: string, value: any) => { // eslint-disable-line @typescript-eslint/unbound-method
if (deepEqual(value, this.__getDefault(key))) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete real[key]
} else {
real[key] = value
}
}
this.__cleanup = () => { // eslint-disable-line @typescript-eslint/unbound-method
// Trigger removal of default values
for (const key in defaults) {
if (isStructuralMember(defaults[key])) {
this[key].__cleanup()
} else {
const v = this.__getValue(key)
this.__setValue(key, v)
}
}
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
getValue (_key: string): any { }
__getValue (_key: string): any { }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
setValue (_key: string, _value: any) { }
__setValue (_key: string, _value: any) { }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
__getDefault (_key: string): any { }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
__cleanup () { }
}
@Injectable({ providedIn: 'root' })
@@ -165,6 +194,7 @@ export class ConfigService {
}
async save (): Promise<void> {
this.store.__cleanup()
// Scrub undefined values
let cleanStore = JSON.parse(JSON.stringify(this._store))
cleanStore = await this.maybeEncryptConfig(cleanStore)
@@ -183,10 +213,10 @@ export class ConfigService {
/**
* Writes config YAML as string
*/
writeRaw (data: string): void {
async writeRaw (data: string): Promise<void> {
this._store = yaml.load(data)
this.save()
this.load()
await this.save()
await this.load()
this.emitChange()
}
@@ -228,8 +258,8 @@ export class ConfigService {
this.ready.next(true)
this.ready.complete()
this.hostApp.configChangeBroadcast$.subscribe(() => {
this.load()
this.hostApp.configChangeBroadcast$.subscribe(async () => {
await this.load()
this.emitChange()
})
}
@@ -250,6 +280,67 @@ export class ConfigService {
}
config.version = 1
}
if (config.version < 2) {
config.profiles ??= []
if (config.terminal?.recoverTabs !== undefined) {
config.recoverTabs = config.terminal.recoverTabs
delete config.terminal.recoverTabs
}
for (const profile of config.terminal?.profiles ?? []) {
if (profile.sessionOptions) {
profile.options = profile.sessionOptions
delete profile.sessionOptions
}
profile.type = 'local'
profile.id = `local:custom:${uuidv4()}`
}
if (config.terminal?.profiles) {
config.profiles = config.terminal.profiles
delete config.terminal.profiles
delete config.terminal.environment
config.terminal.profile = `local:${config.terminal.profile}`
}
config.version = 2
}
if (config.version < 3) {
delete config.ssh?.recentConnections
for (const c of config.ssh?.connections ?? []) {
const p = {
id: `ssh:${uuidv4()}`,
type: 'ssh',
icon: 'fas fa-desktop',
name: c.name,
group: c.group ?? undefined,
color: c.color,
disableDynamicTitle: c.disableDynamicTitle,
options: c,
}
config.profiles.push(p)
}
for (const p of config.profiles ?? []) {
if (p.type === 'ssh') {
if (p.options.jumpHost) {
p.options.jumpHost = config.profiles.find(x => x.name === p.options.jumpHost)?.id
}
}
}
for (const c of config.serial?.connections ?? []) {
const p = {
id: `serial:${uuidv4()}`,
type: 'serial',
icon: 'fas fa-microchip',
name: c.name,
group: c.group ?? undefined,
color: c.color,
options: c,
}
config.profiles.push(p)
}
delete config.ssh?.connections
delete config.serial?.connections
delete window.localStorage.lastSerialConnection
config.version = 3
}
}
private async maybeDecryptConfig (store) {

View File

@@ -3,6 +3,8 @@ import { Observable, Subject } from 'rxjs'
import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
import { stringifyKeySequence, EventData } from './hotkeys.util'
import { ConfigService } from './config.service'
import { HostAppService, Platform } from '../api/hostApp'
import { deprecate } from 'util'
export interface PartialHotkeyMatch {
id: string
@@ -34,6 +36,7 @@ export class HotkeysService {
private zone: NgZone,
private config: ConfigService,
@Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
hostApp: HostAppService,
) {
const events = ['keydown', 'keyup']
events.forEach(event => {
@@ -42,17 +45,21 @@ export class HotkeysService {
this.pushKeystroke(event, nativeEvent)
this.processKeystrokes()
this.emitKeyEvent(nativeEvent)
if (hostApp.platform === Platform.Web) {
nativeEvent.preventDefault()
nativeEvent.stopPropagation()
}
}
})
})
this.config.ready$.toPromise().then(() => {
this.getHotkeyDescriptions().then(hotkeys => {
this.hotkeyDescriptions = hotkeys
})
this.config.ready$.toPromise().then(async () => {
const hotkeys = await this.getHotkeyDescriptions()
this.hotkeyDescriptions = hotkeys
})
// deprecated
this.hotkey$.subscribe(h => this.matchedHotkey.emit(h))
this.matchedHotkey.subscribe = deprecate(s => this.hotkey$.subscribe(s), 'matchedHotkey is deprecated, use hotkey$')
}
/**
@@ -62,7 +69,7 @@ export class HotkeysService {
* @param nativeEvent event object
*/
pushKeystroke (name: string, nativeEvent: KeyboardEvent): void {
(nativeEvent as any).event = name
nativeEvent['event'] = name
this.currentKeystrokes.push({
ctrlKey: nativeEvent.ctrlKey,
metaKey: nativeEvent.metaKey,

View File

@@ -0,0 +1,184 @@
import { Injectable, Inject } from '@angular/core'
import { NewTabParameters } from './tabs.service'
import { BaseTabComponent } from '../components/baseTab.component'
import { PartialProfile, Profile, ProfileProvider } from '../api/profileProvider'
import { SelectorOption } from '../api/selector'
import { AppService } from './app.service'
import { configMerge, ConfigProxy, ConfigService } from './config.service'
import { NotificationsService } from './notifications.service'
import { SelectorService } from './selector.service'
@Injectable({ providedIn: 'root' })
export class ProfilesService {
private profileDefaults = {
id: '',
type: '',
name: '',
group: '',
options: {},
icon: '',
color: '',
disableDynamicTitle: false,
weight: 0,
isBuiltin: false,
isTemplate: false,
}
constructor (
private app: AppService,
private config: ConfigService,
private notifications: NotificationsService,
private selector: SelectorService,
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
) { }
async openNewTabForProfile <P extends Profile> (profile: PartialProfile<P>): Promise<BaseTabComponent|null> {
const params = await this.newTabParametersForProfile(profile)
if (params) {
const tab = this.app.openNewTab(params)
;(this.app.getParentTab(tab) ?? tab).color = profile.color ?? null
if (profile.name) {
tab.setTitle(profile.name)
}
if (profile.disableDynamicTitle) {
tab['enableDynamicTitle'] = false
}
return tab
}
return null
}
async newTabParametersForProfile <P extends Profile> (profile: PartialProfile<P>): Promise<NewTabParameters<BaseTabComponent>|null> {
const fullProfile = this.getConfigProxyForProfile(profile)
return this.providerForProfile(fullProfile)?.getNewTabParameters(fullProfile) ?? null
}
getProviders (): ProfileProvider<Profile>[] {
return [...this.profileProviders]
}
async getProfiles (): Promise<PartialProfile<Profile>[]> {
const lists = await Promise.all(this.config.enabledServices(this.profileProviders).map(x => x.getBuiltinProfiles()))
let list = lists.reduce((a, b) => a.concat(b), [])
list = [
...this.config.store.profiles ?? [],
...list,
]
const sortKey = p => `${p.group ?? ''} / ${p.name}`
list.sort((a, b) => sortKey(a).localeCompare(sortKey(b)))
list.sort((a, b) => (a.isBuiltin ? 1 : 0) - (b.isBuiltin ? 1 : 0))
return list
}
providerForProfile <T extends Profile> (profile: PartialProfile<T>): ProfileProvider<T>|null {
const provider = this.profileProviders.find(x => x.id === profile.type) ?? null
return provider as unknown as ProfileProvider<T>|null
}
getDescription <P extends Profile> (profile: PartialProfile<P>): string|null {
profile = this.getConfigProxyForProfile(profile)
return this.providerForProfile(profile)?.getDescription(profile) ?? null
}
selectorOptionForProfile <P extends Profile, T> (profile: PartialProfile<P>): SelectorOption<T> {
const fullProfile = this.getConfigProxyForProfile(profile)
return {
icon: profile.icon,
name: profile.group ? `${fullProfile.group} / ${fullProfile.name}` : fullProfile.name,
description: this.providerForProfile(fullProfile)?.getDescription(fullProfile),
}
}
showProfileSelector (): Promise<PartialProfile<Profile>|null> {
return new Promise<PartialProfile<Profile>|null>(async (resolve, reject) => {
try {
const recentProfiles: PartialProfile<Profile>[] = this.config.store.recentProfiles
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
...this.selectorOptionForProfile(p),
icon: 'fas fa-history',
callback: async () => {
if (p.id) {
p = (await this.getProfiles()).find(x => x.id === p.id) ?? p
}
resolve(p)
},
}))
if (recentProfiles.length) {
options.push({
name: 'Clear recent connections',
icon: 'fas fa-eraser',
callback: async () => {
this.config.store.recentProfiles = []
this.config.save()
resolve(null)
},
})
}
let profiles = await this.getProfiles()
if (!this.config.store.terminal.showBuiltinProfiles) {
profiles = profiles.filter(x => !x.isBuiltin)
}
profiles = profiles.filter(x => !x.isTemplate)
options = [...options, ...profiles.map((p): SelectorOption<void> => ({
...this.selectorOptionForProfile(p),
callback: () => resolve(p),
}))]
try {
const { SettingsTabComponent } = window['nodeRequire']('tabby-settings')
options.push({
name: 'Manage profiles',
icon: 'fas fa-window-restore',
callback: () => {
this.app.openNewTabRaw({
type: SettingsTabComponent,
inputs: { activeTab: 'profiles' },
})
resolve(null)
},
})
} catch { }
if (this.getProviders().some(x => x.supportsQuickConnect)) {
options.push({
name: 'Quick connect',
freeInputPattern: 'Connect to "%s"...',
icon: 'fas fa-arrow-right',
callback: query => {
const profile = this.quickConnect(query)
resolve(profile)
},
})
}
await this.selector.show('Select profile or enter an address', options)
} catch (err) {
reject(err)
}
})
}
async quickConnect (query: string): Promise<PartialProfile<Profile>|null> {
for (const provider of this.getProviders()) {
if (provider.supportsQuickConnect) {
const profile = provider.quickConnect(query)
if (profile) {
return profile
}
}
}
this.notifications.error(`Could not parse "${query}"`)
return null
}
getConfigProxyForProfile <T extends Profile> (profile: PartialProfile<T>): T {
const provider = this.providerForProfile(profile)
const defaults = configMerge(this.profileDefaults, provider?.configDefaults ?? {})
return new ConfigProxy(profile, defaults) as unknown as T
}
}

View File

@@ -1,8 +1,9 @@
import { Injectable, Inject } from '@angular/core'
import { TabRecoveryProvider, RecoveredTab, RecoveryToken } from '../api/tabRecovery'
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
import { BaseTabComponent } from '../components/baseTab.component'
import { Logger, LogService } from '../services/log.service'
import { ConfigService } from '../services/config.service'
import { Logger, LogService } from './log.service'
import { ConfigService } from './config.service'
import { NewTabParameters } from './tabs.service'
/** @hidden */
@Injectable({ providedIn: 'root' })
@@ -11,7 +12,7 @@ export class TabRecoveryService {
enabled = false
private constructor (
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[]|null,
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider<BaseTabComponent>[]|null,
private config: ConfigService,
log: LogService
) {
@@ -40,7 +41,7 @@ export class TabRecoveryService {
return token
}
async recoverTab (token: RecoveryToken, duplicate = false): Promise<RecoveredTab|null> {
async recoverTab (token: RecoveryToken, duplicate = false): Promise<NewTabParameters<BaseTabComponent>|null> {
for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) {
try {
if (!await provider.applicableTo(token)) {
@@ -50,9 +51,9 @@ export class TabRecoveryService {
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 || ''
tab.inputs = tab.inputs ?? {}
tab.inputs.color = token.tabColor ?? null
tab.inputs.title = token.tabTitle || ''
return tab
} catch (error) {
this.logger.warn('Tab recovery crashed:', token, provider, error)
@@ -61,9 +62,9 @@ export class TabRecoveryService {
return null
}
async recoverTabs (): Promise<RecoveredTab[]> {
async recoverTabs (): Promise<NewTabParameters<BaseTabComponent>[]> {
if (window.localStorage.tabsRecovery) {
const tabs: RecoveredTab[] = []
const tabs: NewTabParameters<BaseTabComponent>[] = []
for (const token of JSON.parse(window.localStorage.tabsRecovery)) {
const tab = await this.recoverTab(token)
if (tab) {

View File

@@ -3,7 +3,22 @@ import { BaseTabComponent } from '../components/baseTab.component'
import { TabRecoveryService } from './tabRecovery.service'
// eslint-disable-next-line @typescript-eslint/no-type-alias
export type TabComponentType = new (...args: any[]) => BaseTabComponent
export interface TabComponentType<T extends BaseTabComponent> {
// eslint-disable-next-line @typescript-eslint/prefer-function-type
new (...args: any[]): T
}
export interface NewTabParameters<T extends BaseTabComponent> {
/**
* Component type to be instantiated
*/
type: TabComponentType<T>
/**
* Component instance inputs
*/
inputs?: Record<string, any>
}
@Injectable({ providedIn: 'root' })
export class TabsService {
@@ -17,12 +32,12 @@ export class TabsService {
/**
* Instantiates a tab component and assigns given inputs
*/
create (type: TabComponentType, inputs?: Record<string, any>): BaseTabComponent {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type)
create <T extends BaseTabComponent> (params: NewTabParameters<T>): T {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(params.type)
const componentRef = componentFactory.create(this.injector)
const tab = componentRef.instance
tab.hostView = componentRef.hostView
Object.assign(tab, inputs ?? {})
Object.assign(tab, params.inputs ?? {})
return tab
}
@@ -36,7 +51,7 @@ export class TabsService {
}
const dup = await this.tabRecovery.recoverTab(token, true)
if (dup) {
return this.create(dup.type, dup.options)
return this.create(dup)
}
return null
}

View File

@@ -30,6 +30,13 @@ export interface VaultSecret {
value: string
}
export interface VaultFileSecret extends VaultSecret {
key: {
id: string
description: string
}
}
export interface Vault {
config: any
secrets: VaultSecret[]
@@ -121,6 +128,10 @@ export class VaultService {
return !!_rememberedPassphrase
}
forgetPassphrase (): void {
_rememberedPassphrase = null
}
async decrypt (storage: StoredVault, passphrase?: string): Promise<Vault> {
if (!passphrase) {
passphrase = await this.getPassphrase()
@@ -128,7 +139,7 @@ export class VaultService {
try {
return await wrapPromise(this.zone, decryptVault(storage, passphrase))
} catch (e) {
_rememberedPassphrase = null
this.forgetPassphrase()
if (e.toString().includes('BAD_DECRYPT')) {
this.notifications.error('Incorrect passphrase')
}
@@ -193,6 +204,20 @@ export class VaultService {
await this.save(vault)
}
async updateSecret (secret: VaultSecret, update: VaultSecret): Promise<void> {
await this.ready$.toPromise()
const vault = await this.load()
if (!vault) {
return
}
const target = vault.secrets.find(s => s.type === secret.type && this.keyMatches(secret.key, s))
if (!target) {
return
}
Object.assign(target, update)
await this.save(vault)
}
async removeSecret (type: string, key: Record<string, any>): Promise<void> {
await this.ready$.toPromise()
const vault = await this.load()
@@ -247,12 +272,12 @@ export class VaultFileProvider extends FileProvider {
const result = await this.selector.show<VaultSecret|null>('Select file', [
{
name: 'Add a new file',
icon: 'plus',
icon: 'fas fa-plus',
result: null,
},
...files.map(f => ({
name: f.key.description,
icon: 'file',
icon: 'fas fa-file',
result: f,
})),
])
@@ -270,11 +295,11 @@ export class VaultFileProvider extends FileProvider {
}
const transfer = transfers[0]
const id = (await wrapPromise(this.zone, promisify(crypto.randomBytes)(32))).toString('hex')
this.vault.addSecret({
await this.vault.addSecret({
type: VAULT_SECRET_TYPE_FILE,
key: {
id,
description,
description: `${description} (${transfer.getName()})`,
},
value: (await transfer.readAll()).toString('base64'),
})

View File

@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subscription } from 'rxjs'
import { AppService } from './services/app.service'
import { BaseTabComponent } from './components/baseTab.component'
@@ -7,6 +8,11 @@ import { TabHeaderComponent } from './components/tabHeader.component'
import { SplitTabComponent, SplitDirection } from './components/splitTab.component'
import { TabContextMenuItemProvider } from './api/tabContextMenuProvider'
import { MenuItemOptions } from './api/menu'
import { ProfilesService } from './services/profiles.service'
import { TabsService } from './services/tabs.service'
import { HotkeysService } from './services/hotkeys.service'
import { PromptModalComponent } from './components/promptModal.component'
import { SplitLayoutProfilesService } from './profiles'
/** @hidden */
@Injectable()
@@ -100,6 +106,8 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
constructor (
private app: AppService,
private ngbModal: NgbModal,
private splitLayoutProfilesService: SplitLayoutProfilesService,
) {
super()
}
@@ -130,6 +138,21 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
})) as MenuItemOptions[],
},
]
if (tab instanceof SplitTabComponent && tab.getAllTabs().length > 1) {
items.push({
label: 'Save layout as profile',
click: async () => {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'Profile name'
const name = (await modal.result)?.value
if (!name) {
return
}
this.splitLayoutProfilesService.createProfile(tab, name)
},
})
}
}
return items
}
@@ -203,3 +226,65 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
return items
}
}
/** @hidden */
@Injectable()
export class ProfilesContextMenu extends TabContextMenuItemProvider {
weight = 10
constructor (
private profilesService: ProfilesService,
private tabsService: TabsService,
private app: AppService,
hotkeys: HotkeysService,
) {
super()
hotkeys.hotkey$.subscribe(hotkey => {
if (hotkey === 'switch-profile') {
let tab = this.app.activeTab
if (tab instanceof SplitTabComponent) {
tab = tab.getFocusedTab()
if (tab) {
this.switchTabProfile(tab)
}
}
}
})
}
async switchTabProfile (tab: BaseTabComponent) {
const profile = await this.profilesService.showProfileSelector()
if (!profile) {
return
}
const params = await this.profilesService.newTabParametersForProfile(profile)
if (!params) {
return
}
if (!await tab.canClose()) {
return
}
const newTab = this.tabsService.create(params)
;(tab.parent as SplitTabComponent).replaceTab(tab, newTab)
tab.destroy()
}
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
return [
{
label: 'Switch profile',
click: () => this.switchTabProfile(tab),
},
]
}
return []
}
}

View File

@@ -132,6 +132,10 @@ app-root {
tab-body {
background: $content-bg;
.terminal-toolbar .btn, .toolbar-pin-button {
font-weight: bold;
}
}
multi-hotkey-input {
@@ -235,12 +239,11 @@ hotkey-input-modal {
}
}
.list-group-light {
.list-group-item {
background: transparent;
border: none;
border-top: 1px solid rgba(255, 255, 255, .1);
border-top: 1px solid rgba(255, 255, 255, .05);
&:first-child {
border-top: none;
@@ -303,21 +306,6 @@ search-panel {
}
}
.btn.btn-outline-secondary {
@include button-outline-variant(#9badb9, #fff);
&:hover:not([disabled]), &:active:not([disabled]), &.active:not([disabled]) {
background-color: #3f484e;
border-color: darken(#9badb9, 25%);
}
border-color: darken(#9badb9, 25%);
&.disabled,
&:disabled {
color: #9badb9;
}
}
.btn-warning:not(:disabled):not(.disabled) {
&.active, &:active {
color: $gray-900;

View File

@@ -12,7 +12,7 @@ export function isWindowsBuild (build: number): boolean {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function getCSSFontFamily (config: any): string {
let fonts: string[] = config.terminal.font.split(',').map(x => x.trim().replace(/"/g, ''))
let fonts: string[] = config.terminal.font.split(',').map(x => x.trim().replaceAll('"', ''))
if (config.terminal.fallbackFont) {
fonts.push(config.terminal.fallbackFont)
}
@@ -31,3 +31,26 @@ export function wrapPromise <T> (zone: NgZone, promise: Promise<T>): Promise<T>
})
})
}
export class ResettableTimeout {
private fn: () => void
private timeout: number
private id: any
constructor (fn: () => void, timeout: number) {
this.fn = fn
this.timeout = timeout
this.id = null
}
set (timeout?: number): void {
this.clear()
this.id = setTimeout(this.fn, timeout ?? this.timeout)
}
clear (): void {
if (this.id) {
clearTimeout(this.id)
}
}
}

View File

@@ -3,9 +3,9 @@
"@types/js-yaml@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.1.tgz#5544730b65a480b18ace6b6ce914e519cec2d43b"
integrity sha512-xdOvNmXmrZqqPy3kuCQ+fz6wA0xU5pji9cd1nDrflWaAWtYLLGk5ykW0H6yg5TVyehHP1pfmuuSaZkhP+kspVA==
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.2.tgz#4117a7a378593a218e9d6f0ef44ce6d5d9edf7fa"
integrity sha512-KbeHS/Y4R+k+5sWXEYzAZKuB1yQlZtEghuhRxrVRLaqhtoG5+26JwQsa4HyS3AWX8v1Uwukma5HheduUDskasA==
"@types/semver@^7.3.5":
version "7.3.5"
@@ -50,19 +50,10 @@ call-bind@^1.0.0, call-bind@^1.0.2:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
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"
core-js@^3.1.2:
version "3.14.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.14.0.tgz#62322b98c71cc2018b027971a69419e2425c2a6c"
integrity sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA==
version "3.15.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61"
integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==
debug@4:
version "4.3.1"
@@ -282,13 +273,6 @@ is-number-object@^1.0.4:
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb"
integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==
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-regex@^1.1.1, is-regex@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
@@ -340,11 +324,6 @@ isarray@^2.0.5:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
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.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -361,11 +340,6 @@ jsonfile@^6.0.1:
optionalDependencies:
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==
lazy-val@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65"
@@ -494,13 +468,6 @@ semver@^7.3.5:
dependencies:
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"
side-channel@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"

View File

@@ -1 +0,0 @@
dist

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-electron",
"version": "1.0.144",
"version": "1.0.148-nightly.2",
"description": "Electron-specific bindings",
"keywords": [
"tabby-builtin-plugin"
@@ -20,7 +20,6 @@
"@angular/core": "^9.1.9"
},
"devDependencies": {
"axios": "^0.21.1",
"winston": "^3.3.3",
"electron-promise-ipc": "^2.2.4"
}

View File

@@ -92,10 +92,10 @@ export default class ElectronModule {
try {
let electronKeySpec = item[0]
electronKeySpec = electronKeySpec.replace('Meta', 'Super')
electronKeySpec = electronKeySpec.replace('⌘', 'Command')
electronKeySpec = electronKeySpec.replace('⌥', 'Alt')
electronKeySpec = electronKeySpec.replace(/-/g, '+')
electronKeySpec = electronKeySpec.replaceAll('Meta', 'Super')
electronKeySpec = electronKeySpec.replaceAll('⌘', 'Command')
electronKeySpec = electronKeySpec.replaceAll('⌥', 'Alt')
electronKeySpec = electronKeySpec.replaceAll('-', '+')
specs.push(electronKeySpec)
} catch (err) {
console.error('Could not register the global hotkey:', err)

View File

@@ -2,7 +2,7 @@ import * as path from 'path'
import * as fs from 'fs/promises'
import * as fsSync from 'fs'
import * as os from 'os'
import promiseIpc from 'electron-promise-ipc'
import promiseIpc, { RendererProcessType } from 'electron-promise-ipc'
import { execFile } from 'mz/child_process'
import { Injectable, NgZone } from '@angular/core'
import { PlatformService, ClipboardContent, HostAppService, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise } from 'tabby-core'
@@ -49,11 +49,11 @@ export class ElectronPlatformService extends PlatformService {
}
async installPlugin (name: string, version: string): Promise<void> {
await (promiseIpc as any).send('plugin-manager:install', name, version)
await (promiseIpc as RendererProcessType).send('plugin-manager:install', name, version)
}
async uninstallPlugin (name: string): Promise<void> {
await (promiseIpc as any).send('plugin-manager:uninstall', name)
await (promiseIpc as RendererProcessType).send('plugin-manager:uninstall', name)
}
async isProcessRunning (name: string): Promise<boolean> {
@@ -205,7 +205,7 @@ export class ElectronPlatformService extends PlatformService {
}))
}
async startDownload (name: string, size: number): Promise<FileDownload|null> {
async startDownload (name: string, mode: number, size: number): Promise<FileDownload|null> {
const result = await this.electron.dialog.showSaveDialog(
this.hostWindow.getWindow(),
{
@@ -215,7 +215,7 @@ export class ElectronPlatformService extends PlatformService {
if (!result.filePath) {
return null
}
const transfer = new ElectronFileDownload(result.filePath, size)
const transfer = new ElectronFileDownload(result.filePath, mode, size)
await wrapPromise(this.zone, transfer.open())
this.fileTransferStarted.next(transfer)
return transfer
@@ -230,6 +230,7 @@ export class ElectronPlatformService extends PlatformService {
class ElectronFileUpload extends FileUpload {
private size: number
private mode: number
private file: fs.FileHandle
private buffer: Buffer
@@ -239,7 +240,9 @@ class ElectronFileUpload extends FileUpload {
}
async open (): Promise<void> {
this.size = (await fs.stat(this.filePath)).size
const stat = await fs.stat(this.filePath)
this.size = stat.size
this.mode = stat.mode
this.file = await fs.open(this.filePath, 'r')
}
@@ -247,6 +250,10 @@ class ElectronFileUpload extends FileUpload {
return path.basename(this.filePath)
}
getMode (): number {
return this.mode
}
getSize (): number {
return this.size
}
@@ -267,19 +274,24 @@ class ElectronFileDownload extends FileDownload {
constructor (
private filePath: string,
private mode: number,
private size: number,
) {
super()
}
async open (): Promise<void> {
this.file = await fs.open(this.filePath, 'w')
this.file = await fs.open(this.filePath, 'w', this.mode)
}
getName (): string {
return path.basename(this.filePath)
}
getMode (): number {
return this.mode
}
getSize (): number {
return this.size
}

View File

@@ -120,7 +120,7 @@ export class ElectronUpdaterService extends UpdaterService {
async update (): Promise<void> {
if (!this.electronUpdaterAvailable) {
this.electron.shell.openExternal(this.updateURL)
await this.electron.shell.openExternal(this.updateURL)
} else {
if ((await this.platform.showMessageBox(
{

View File

@@ -16,13 +16,6 @@ async@^3.1.0:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -150,11 +143,6 @@ fn.name@1.x.x:
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
follow-redirects@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"

View File

@@ -1 +0,0 @@
dist

View File

@@ -1,6 +1,6 @@
{
"name": "tabby-local",
"version": "1.0.144",
"version": "1.0.148-nightly.2",
"description": "Tabby's local shell plugin",
"keywords": [
"tabby-builtin-plugin"
@@ -22,14 +22,11 @@
},
"devDependencies": {
"@types/deep-equal": "^1.0.0",
"@types/shell-escape": "^0.2.0",
"ansi-colors": "^4.1.1",
"dataurl": "0.1.0",
"deep-equal": "2.0.5",
"ps-node": "^0.1.6",
"runes": "^0.4.2",
"shell-escape": "^0.2.0",
"slugify": "^1.5.3",
"utils-decorators": "^1.8.3"
},
"peerDependencies": {

View File

@@ -1,6 +1,8 @@
import { Profile } from 'tabby-core'
export interface Shell {
id: string
name?: string
name: string
command: string
args?: string[]
env: Record<string, string>
@@ -40,14 +42,8 @@ export interface SessionOptions {
runAsAdministrator?: boolean
}
export interface Profile {
name: string
color?: string
sessionOptions: SessionOptions
shell?: string
isBuiltin?: boolean
icon?: string
disableDynamicTitle?: boolean
export interface LocalProfile extends Profile {
options: SessionOptions
}
export interface ChildProcess {

View File

@@ -1,37 +1,17 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton, ConfigService, SelectorOption, SelectorService } from 'tabby-core'
import { ElectronService } from 'tabby-electron'
import { ToolbarButtonProvider, ToolbarButton } from 'tabby-core'
import { TerminalService } from './services/terminal.service'
/** @hidden */
@Injectable()
export class ButtonProvider extends ToolbarButtonProvider {
constructor (
electron: ElectronService,
private selector: SelectorService,
private config: ConfigService,
private terminal: TerminalService,
) {
super()
}
async activate () {
const options: SelectorOption<void>[] = []
const profiles = await this.terminal.getProfiles({ skipDefault: !this.config.store.terminal.showDefaultProfiles })
for (const profile of profiles) {
options.push({
icon: profile.icon,
name: profile.name,
callback: () => this.terminal.openTab(profile),
})
}
await this.selector.show('Select profile', options)
}
provide (): ToolbarButton[] {
return [
{
@@ -42,11 +22,6 @@ export class ButtonProvider extends ToolbarButtonProvider {
this.terminal.openTab()
},
},
{
icon: require('./icons/profiles.svg'),
title: 'New terminal with profile',
click: () => this.activate(),
},
]
}
}

View File

@@ -10,7 +10,6 @@ export class TerminalCLIHandler extends CLIHandler {
priority = 0
constructor (
private config: ConfigService,
private hostWindow: HostWindowService,
private terminal: TerminalService,
) {
@@ -24,8 +23,6 @@ export class TerminalCLIHandler extends CLIHandler {
this.handleOpenDirectory(path.resolve(event.cwd, event.argv.directory))
} else if (op === 'run') {
this.handleRunCommand(event.argv.command)
} else if (op === 'profile') {
this.handleOpenProfile(event.argv.profileName)
} else {
return false
}
@@ -47,24 +44,15 @@ export class TerminalCLIHandler extends CLIHandler {
private handleRunCommand (command: string[]) {
this.terminal.openTab({
type: 'local',
name: '',
sessionOptions: {
options: {
command: command[0],
args: command.slice(1),
},
}, null, true)
this.hostWindow.bringToFront()
}
private handleOpenProfile (profileName: string) {
const profile = this.config.store.terminal.profiles.find(x => x.name === profileName)
if (!profile) {
console.error('Requested profile', profileName, 'not found')
return
}
this.terminal.openTabWithOptions(profile.sessionOptions)
this.hostWindow.bringToFront()
}
}

View File

@@ -0,0 +1,41 @@
ng-container(*ngIf='!argvMode')
.form-group
label Command line
.input-group
.input-group-prepend
button.btn.btn-secondary((click)='switchToArgv()', title='Switch to split arguments')
i.fas.fa-fw.fa-caret-right
input.form-control.text-monospace(
[(ngModel)]='command',
(ngModelChange)='parseCommand()'
)
ng-container(*ngIf='argvMode')
.form-group
label Program
.input-group
.input-group-prepend
button.btn.btn-secondary((click)='switchToCommand()', title='Switch to a single-line command')
i.fas.fa-fw.fa-caret-down
input.form-control.text-monospace(
type='text',
[(ngModel)]='_model.command',
)
.form-group
label Arguments
.input-group(
*ngFor='let arg of _model.args; index as i; trackBy: trackByIndex',
)
input.form-control.text-monospace(
type='text',
[(ngModel)]='_model.args[i]',
)
.input-group-append
button.btn.btn-secondary((click)='_model.args.splice(i, 1)')
i.fas.fa-fw.fa-trash
.mt-2
button.btn.btn-secondary((click)='_model.args.push("")')
i.fas.fa-plus.mr-2
| Add

View File

@@ -0,0 +1,50 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import * as shellQuote from 'shell-quote'
import { Component, Input } from '@angular/core'
import { SessionOptions } from '../api'
/** @hidden */
@Component({
selector: 'command-line-editor',
template: require('./commandLineEditor.component.pug'),
})
export class CommandLineEditorComponent {
@Input() argvMode = false
@Input() _model: SessionOptions
command = ''
@Input() get model (): SessionOptions {
return this._model
}
set model (value: SessionOptions) {
this._model = value
this.updateCommand()
}
switchToCommand () {
this.updateCommand()
this.argvMode = false
}
switchToArgv () {
this.argvMode = true
}
parseCommand () {
const args = shellQuote.parse(this.command)
this.model.command = args[0] ?? ''
this.model.args = args.slice(1)
}
updateCommand () {
this.command = shellQuote.quote([
this.model.command,
...this.model.args ?? [],
])
}
trackByIndex (index) {
return index
}
}

View File

@@ -1,73 +0,0 @@
.modal-body
.form-group
label Name
input.form-control(
type='text',
autofocus,
[(ngModel)]='profile.name',
)
.form-group
label Command
input.form-control(
type='text',
[(ngModel)]='profile.sessionOptions.command',
)
.form-group
label Arguments
.input-group(
*ngFor='let arg of profile.sessionOptions.args; index as i; trackBy: trackByIndex',
)
input.form-control(
type='text',
[(ngModel)]='profile.sessionOptions.args[i]',
)
.input-group-append
button.btn.btn-secondary((click)='profile.sessionOptions.args.splice(i, 1)')
i.fas.fa-trash
.mt-2
button.btn.btn-secondary((click)='profile.sessionOptions.args.push("")')
i.fas.fa-plus.mr-2
| Add
.form-line(*ngIf='uac.isAvailable')
.header
.title Run as administrator
toggle(
[(ngModel)]='profile.sessionOptions.runAsAdministrator',
)
.form-group
label Working directory
input.form-control(
type='text',
[(ngModel)]='profile.sessionOptions.cwd',
)
.form-group
label Environment
environment-editor(
type='text',
[(model)]='profile.sessionOptions.env',
)
.form-group
label Tab color
input.form-control(
type='text',
autofocus,
[(ngModel)]='profile.color',
placeholder='#000000'
)
.form-line
.header
.title Disable dynamic tab title
.description Connection name will be used as a title instead
toggle([(ngModel)]='profile.disableDynamicTitle')
.modal-footer
button.btn.btn-outline-primary((click)='save()') Save
button.btn.btn-outline-danger((click)='cancel()') Cancel

View File

@@ -1,36 +0,0 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { UACService } from '../services/uac.service'
import { Profile } from '../api'
/** @hidden */
@Component({
template: require('./editProfileModal.component.pug'),
})
export class EditProfileModalComponent {
profile: Profile
constructor (
public uac: UACService,
private modalInstance: NgbActiveModal,
) {
}
ngOnInit () {
this.profile.sessionOptions.env = this.profile.sessionOptions.env ?? {}
this.profile.sessionOptions.args = this.profile.sessionOptions.args ?? []
}
save () {
this.modalInstance.close(this.profile)
}
cancel () {
this.modalInstance.dismiss()
}
trackByIndex (index) {
return index
}
}

View File

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

View File

@@ -0,0 +1,28 @@
command-line-editor([model]='profile.options')
.form-line(*ngIf='uac.isAvailable')
.header
.title Run as administrator
toggle(
[(ngModel)]='profile.options.runAsAdministrator',
)
.form-group
label Working directory
.input-group
input.form-control(
type='text',
placeholder='Home directory',
[(ngModel)]='profile.options.cwd'
)
.input-group-append
button.btn.btn-secondary((click)='pickWorkingDirectory()')
i.fas.fa-folder-open
.form-group
label Environment
environment-editor(
type='text',
[(model)]='profile.options.env',
)

View File

@@ -0,0 +1,43 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component } from '@angular/core'
import { UACService } from '../services/uac.service'
import { LocalProfile } from '../api'
import { ElectronHostWindow, ElectronService } from 'tabby-electron'
import { ProfileSettingsComponent } from 'tabby-core'
/** @hidden */
@Component({
template: require('./localProfileSettings.component.pug'),
})
export class LocalProfileSettingsComponent implements ProfileSettingsComponent<LocalProfile> {
profile: LocalProfile
constructor (
public uac: UACService,
private hostWindow: ElectronHostWindow,
private electron: ElectronService,
) { }
ngOnInit () {
this.profile.options.env = this.profile.options.env ?? {}
this.profile.options.args = this.profile.options.args ?? []
}
async pickWorkingDirectory (): Promise<void> {
// const profile = await this.terminal.getProfileByID(this.config.store.terminal.profile)
// const shell = this.shells.find(x => x.id === profile?.shell)
// if (!shell) {
// return
// }
const paths = (await this.electron.dialog.showOpenDialog(
this.hostWindow.getWindow(),
{
// TODO
// defaultPath: shell.fsBase,
properties: ['openDirectory', 'showHiddenFiles'],
}
)).filePaths
this.profile.options.cwd = paths[0]
}
}

View File

@@ -1,20 +1,5 @@
h3.mb-3 Shell
.form-line
.header
.title Profile
.description Default profile for new tabs
select.form-control(
[(ngModel)]='config.store.terminal.profile',
(ngModelChange)='config.save()',
)
option(
*ngFor='let profile of profiles',
[ngValue]='terminal.getProfileID(profile)'
) {{profile.name}}
.form-line(*ngIf='isConPTYAvailable')
.header
.title Use ConPTY
@@ -30,75 +15,3 @@ h3.mb-3 Shell
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.profile.startsWith("WSL") && (!config.store.terminal.useConPTY)')
.mr-auto WSL terminal only supports TrueColor with ConPTY
.form-line(*ngIf='config.store.terminal.profile == "custom-shell"')
.header
.title Custom shell
input.form-control(
type='text',
[(ngModel)]='config.store.terminal.customShell',
(ngModelChange)='config.save()',
)
.form-line
.header
.title Working directory
.input-group
input.form-control(
type='text',
placeholder='Home directory',
[(ngModel)]='config.store.terminal.workingDirectory',
(ngModelChange)='config.save()',
)
.input-group-append
button.btn.btn-secondary((click)='pickWorkingDirectory()')
i.fas.fa-folder-open
.form-line
.header
.title Directory for new tabs
select.form-control(
[(ngModel)]='config.store.terminal.alwaysUseWorkingDirectory',
(ngModelChange)='config.save()',
)
option([ngValue]='false') Same as active tab's directory
option([ngValue]='true') The working directory from above
.form-line.align-items-start
.header
.title Environment
.description Inject additional environment variables
environment-editor([(model)]='this.config.store.terminal.environment')
.form-line(*ngIf='config.store.terminal.profiles.length > 0')
.header
.title Show default profiles in the selector
.description If disabled, only custom profiles will show up in the profile selector
toggle(
[(ngModel)]='config.store.terminal.showDefaultProfiles',
(ngModelChange)='config.save()'
)
h3.mt-3 Saved Profiles
.list-group.list-group-flush.mt-3.mb-3
.list-group-item.list-group-item-action.d-flex.align-items-center(
*ngFor='let profile of config.store.terminal.profiles',
(click)='editProfile(profile)',
)
.mr-auto
div {{profile.name}}
.text-muted {{profile.sessionOptions.command}}
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteProfile(profile)')
i.fas.fa-trash
.pb-4(ngbDropdown, placement='top-left')
button.btn.btn-primary(ngbDropdownToggle)
i.fas.fa-fw.fa-plus
| New profile
div(ngbDropdownMenu)
button.dropdown-item(*ngFor='let shell of shells', (click)='newProfile(shell)') {{shell.name}}

View File

@@ -1,93 +1,18 @@
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subscription } from 'rxjs'
import { ConfigService, HostAppService, Platform, WIN_BUILD_CONPTY_SUPPORTED, WIN_BUILD_CONPTY_STABLE, isWindowsBuild } from 'tabby-core'
import { ElectronService, ElectronHostWindow } from 'tabby-electron'
import { EditProfileModalComponent } from './editProfileModal.component'
import { Shell, Profile } from '../api'
import { TerminalService } from '../services/terminal.service'
import { WIN_BUILD_CONPTY_SUPPORTED, WIN_BUILD_CONPTY_STABLE, isWindowsBuild, ConfigService } from 'tabby-core'
/** @hidden */
@Component({
template: require('./shellSettingsTab.component.pug'),
})
export class ShellSettingsTabComponent {
shells: Shell[] = []
profiles: Profile[] = []
Platform = Platform
isConPTYAvailable: boolean
isConPTYStable: boolean
private configSubscription: Subscription
constructor (
public config: ConfigService,
public hostApp: HostAppService,
public hostWindow: ElectronHostWindow,
public terminal: TerminalService,
private electron: ElectronService,
private ngbModal: NgbModal,
) {
config.store.terminal.environment = config.store.terminal.environment || {}
this.configSubscription = this.config.changed$.subscribe(() => {
this.reload()
})
this.reload()
this.isConPTYAvailable = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED)
this.isConPTYStable = isWindowsBuild(WIN_BUILD_CONPTY_STABLE)
}
async ngOnInit (): Promise<void> {
this.shells = (await this.terminal.shells$.toPromise())!
}
ngOnDestroy (): void {
this.configSubscription.unsubscribe()
}
async reload (): Promise<void> {
this.profiles = await this.terminal.getProfiles({ includeHidden: true })
}
async pickWorkingDirectory (): Promise<void> {
const profile = await this.terminal.getProfileByID(this.config.store.terminal.profile)
const shell = this.shells.find(x => x.id === profile?.shell)
if (!shell) {
return
}
const paths = (await this.electron.dialog.showOpenDialog(
this.hostWindow.getWindow(),
{
defaultPath: shell.fsBase,
properties: ['openDirectory', 'showHiddenFiles'],
}
)).filePaths
this.config.store.terminal.workingDirectory = paths[0]
}
newProfile (shell: Shell): void {
const profile: Profile = {
name: shell.name ?? '',
shell: shell.id,
sessionOptions: this.terminal.optionsFromShell(shell),
}
this.config.store.terminal.profiles = [profile, ...this.config.store.terminal.profiles]
this.config.save()
this.reload()
}
editProfile (profile: Profile): void {
const modal = this.ngbModal.open(EditProfileModalComponent)
modal.componentInstance.profile = Object.assign({}, profile)
modal.result.then(result => {
Object.assign(profile, result)
this.config.save()
})
}
deleteProfile (profile: Profile): void {
this.config.store.terminal.profiles = this.config.store.terminal.profiles.filter(x => x !== profile)
this.config.save()
this.reload()
}
}

View File

@@ -1,8 +1,9 @@
import { Component, Input, Injector } from '@angular/core'
import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal'
import { SessionOptions } from '../api'
import { LocalProfile, SessionOptions } from '../api'
import { Session } from '../session'
import { UACService } from '../services/uac.service'
/** @hidden */
@Component({
@@ -12,23 +13,27 @@ import { Session } from '../session'
animations: BaseTerminalTabComponent.animations,
})
export class TerminalTabComponent extends BaseTerminalTabComponent {
@Input() sessionOptions: SessionOptions
@Input() sessionOptions: SessionOptions // Deprecated
@Input() profile: LocalProfile
session: Session|null = null
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor (
injector: Injector,
private uac: UACService,
) {
super(injector)
}
ngOnInit (): void {
this.sessionOptions = this.profile.options
this.logger = this.log.create('terminalTab')
this.session = new Session(this.injector)
const isConPTY = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY
this.subscribeUntilDestroyed(this.hotkeys.matchedHotkey, hotkey => {
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (!this.hasFocus) {
return
}
@@ -47,13 +52,17 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
protected onFrontendReady (): void {
this.initializeSession(this.size.columns, this.size.rows)
this.savedStateIsLive = this.sessionOptions.restoreFromPTYID === this.session?.getPTYID()
this.savedStateIsLive = this.profile.options.restoreFromPTYID === this.session?.getPTYID()
super.onFrontendReady()
}
initializeSession (columns: number, rows: number): void {
if (this.profile.options.runAsAdministrator && this.uac.isAvailable) {
this.profile.options = this.uac.patchSessionOptionsForUAC(this.profile.options)
}
this.session!.start({
...this.sessionOptions,
...this.profile.options,
width: columns,
height: rows,
})
@@ -65,11 +74,14 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
async getRecoveryToken (): Promise<any> {
const cwd = this.session ? await this.session.getWorkingDirectory() : null
return {
type: 'app:terminal-tab',
sessionOptions: {
...this.sessionOptions,
cwd: cwd ?? this.sessionOptions.cwd,
restoreFromPTYID: this.session?.getPTYID(),
type: 'app:local-tab',
profile: {
...this.profile,
options: {
...this.profile.options,
cwd: cwd ?? this.profile.options.cwd,
restoreFromPTYID: this.session?.getPTYID(),
},
},
savedState: this.frontend?.saveState(),
}

View File

@@ -5,30 +5,19 @@ export class TerminalConfigProvider extends ConfigProvider {
defaults = {
hotkeys: {
'copy-current-path': [],
shell: {
__nonStructural: true,
},
profile: {
__nonStructural: true,
},
},
terminal: {
autoOpen: false,
customShell: '',
workingDirectory: '',
alwaysUseWorkingDirectory: false,
useConPTY: true,
showDefaultProfiles: true,
environment: {},
profiles: [],
setComSpec: false,
},
}
platformDefaults = {
[Platform.macOS]: {
terminal: {
shell: 'default',
profile: 'user-default',
profile: 'local:default',
},
hotkeys: {
'new-tab': [
@@ -38,8 +27,7 @@ export class TerminalConfigProvider extends ConfigProvider {
},
[Platform.Windows]: {
terminal: {
shell: 'clink',
profile: 'cmd-clink',
profile: 'local:cmd-clink',
},
hotkeys: {
'new-tab': [
@@ -49,8 +37,7 @@ export class TerminalConfigProvider extends ConfigProvider {
},
[Platform.Linux]: {
terminal: {
shell: 'default',
profile: 'user-default',
profile: 'local:default',
},
hotkeys: {
'new-tab': [

View File

@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core'
import { TerminalService } from './services/terminal.service'
/** @hidden */
@Injectable()
@@ -12,18 +11,7 @@ export class LocalTerminalHotkeyProvider extends HotkeyProvider {
},
]
constructor (
private terminal: TerminalService,
) { super() }
async provide (): Promise<HotkeyDescription[]> {
const profiles = await this.terminal.getProfiles()
return [
...this.hotkeys,
...profiles.map(profile => ({
id: `profile.${this.terminal.getProfileID(profile)}`,
name: `New tab: ${profile.name}`,
})),
]
return this.hotkeys
}
}

View File

@@ -1 +1 @@
<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>
<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="#ef4eff" 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: 662 B

After

Width:  |  Height:  |  Size: 664 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 96 95.51"><defs><style>.a{fill:#fff;}.a,.h{fill-rule:evenodd;}.b{mask:url(#a);}.c{fill:#52218a;}.d{fill:#6c33af;}.e{fill:#854cc7;}.f{fill:#b179f1;}.g{opacity:0.25;}.h{fill:url(#b);}</style><mask id="a" x="0" y="0" width="96" height="95.51" maskUnits="userSpaceOnUse"><g transform="translate(0 -0.25)"><path class="a" d="M68.89,95.6a6,6,0,0,0,3.93-.44L92.6,85.65A6,6,0,0,0,96,80.24V15.76a6,6,0,0,0-3.4-5.41L72.82.84A6,6,0,0,0,68.34.55,6,6,0,0,0,66,2L34.12,37.26,15.5,22l-1.63-1.4a4,4,0,0,0-3.61-.83,2.55,2.55,0,0,0-.53.18L2.46,23A4,4,0,0,0,0,26.37c0,.1,0,.2,0,.3V69.33c0,.1,0,.2,0,.3A4,4,0,0,0,2.46,73l7.27,3a2.55,2.55,0,0,0,.53.18,4,4,0,0,0,3.61-.83L15.5,74,34.12,58.74,66,94A6,6,0,0,0,68.89,95.6ZM72,27.68,47.21,48,72,68.32ZM12,34.27,24.41,48,12,61.73Z"></path></g></mask><linearGradient id="b" x1="48" y1="97.75" x2="48" y2="2.25" gradientTransform="matrix(1, 0, 0, -1, 0, 98)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"></stop><stop offset="1" stop-color="#fff" stop-opacity="0"></stop></linearGradient></defs><title>BrandVisualStudioWin2019</title><g class="b"><path class="c" d="M13.87,75.4a4,4,0,0,1-4.14.65L2.46,73A4,4,0,0,1,0,69.33V26.67A4,4,0,0,1,2.46,23l7.27-3a4,4,0,0,1,4.14.65L15.5,22A2.21,2.21,0,0,0,12,23.8V72.2A2.21,2.21,0,0,0,15.5,74Z" transform="translate(0 -0.25)"></path><path class="d" d="M2.46,73A4,4,0,0,1,0,69.33V69a2.31,2.31,0,0,0,4,1.55L66,2A6,6,0,0,1,72.82.84L92.6,10.36A6,6,0,0,1,96,15.77V16a3.79,3.79,0,0,0-6.19-2.93L15.5,74l-1.63,1.4a4,4,0,0,1-4.14.65Z" transform="translate(0 -0.25)"></path><path class="e" d="M2.46,23A4,4,0,0,0,0,26.67V27a2.31,2.31,0,0,1,4-1.55L66,94a6,6,0,0,0,6.82,1.16L92.6,85.64A6,6,0,0,0,96,80.23V80a3.79,3.79,0,0,1-6.19,2.93L15.5,22l-1.63-1.4A4,4,0,0,0,9.73,20Z" transform="translate(0 -0.25)"></path><path class="f" d="M72.82,95.16A6,6,0,0,1,66,94a3.52,3.52,0,0,0,6-2.49v-87A3.52,3.52,0,0,0,66,2,6,6,0,0,1,72.82.84L92.6,10.35A6,6,0,0,1,96,15.76V80.24a6,6,0,0,1-3.4,5.41Z" transform="translate(0 -0.25)"></path><g class="g"><path class="h" d="M68.89,95.6a6,6,0,0,0,3.93-.44L92.6,85.65A6,6,0,0,0,96,80.24V15.76a6,6,0,0,0-3.4-5.41L72.82.84A6,6,0,0,0,68.34.55,6,6,0,0,0,66,2L34.12,37.26,15.5,22l-1.63-1.4a4,4,0,0,0-3.61-.83,2.55,2.55,0,0,0-.53.18L2.46,23A4,4,0,0,0,0,26.37c0,.1,0,.2,0,.3V69.33c0,.1,0,.2,0,.3A4,4,0,0,0,2.46,73l7.27,3a2.55,2.55,0,0,0,.53.18,4,4,0,0,0,3.61-.83L15.5,74,34.12,58.74,66,94A6,6,0,0,0,68.89,95.6ZM72,27.68,47.21,48,72,68.32ZM12,34.27,24.41,48,12,61.73Z" transform="translate(0 -0.25)"></path></g></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -4,15 +4,16 @@ import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToastrModule } from 'ngx-toastr'
import TabbyCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler, ConfigService } from 'tabby-core'
import TabbyCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler, ConfigService, ProfileProvider } from 'tabby-core'
import TabbyTerminalModule from 'tabby-terminal'
import TabbyElectronPlugin from 'tabby-electron'
import { SettingsTabProvider } from 'tabby-settings'
import { TerminalTabComponent } from './components/terminalTab.component'
import { ShellSettingsTabComponent } from './components/shellSettingsTab.component'
import { EditProfileModalComponent } from './components/editProfileModal.component'
import { EnvironmentEditorComponent } from './components/environmentEditor.component'
import { LocalProfileSettingsComponent } from './components/localProfileSettings.component'
import { CommandLineEditorComponent } from './components/commandLineEditor.component'
import { TerminalService } from './services/terminal.service'
import { DockMenuService } from './services/dockMenu.service'
@@ -26,7 +27,6 @@ import { LocalTerminalHotkeyProvider } from './hotkeys'
import { NewTabContextMenu, SaveAsProfileContextMenu } from './tabContextMenu'
import { CmderShellProvider } from './shells/cmder'
import { CustomShellProvider } from './shells/custom'
import { Cygwin32ShellProvider } from './shells/cygwin32'
import { Cygwin64ShellProvider } from './shells/cygwin64'
import { GitBashShellProvider } from './shells/gitBash'
@@ -37,8 +37,10 @@ import { PowerShellCoreShellProvider } from './shells/powershellCore'
import { WindowsDefaultShellProvider } from './shells/winDefault'
import { WindowsStockShellsProvider } from './shells/windowsStock'
import { WSLShellProvider } from './shells/wsl'
import { VSDevToolsProvider } from './shells/vs'
import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from './cli'
import { LocalProfilesService } from './profiles'
/** @hidden */
@NgModule({
@@ -65,12 +67,14 @@ import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from '.
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
{ provide: ShellProvider, useClass: PowerShellCoreShellProvider, multi: true },
{ provide: ShellProvider, useClass: CmderShellProvider, multi: true },
{ provide: ShellProvider, useClass: CustomShellProvider, multi: true },
{ provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true },
{ provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true },
{ provide: ShellProvider, useClass: GitBashShellProvider, multi: true },
{ provide: ShellProvider, useClass: POSIXShellsProvider, multi: true },
{ provide: ShellProvider, useClass: WSLShellProvider, multi: true },
{ provide: ShellProvider, useClass: VSDevToolsProvider, multi: true },
{ provide: ProfileProvider, useClass: LocalProfilesService, multi: true },
{ provide: TabContextMenuItemProvider, useClass: NewTabContextMenu, multi: true },
{ provide: TabContextMenuItemProvider, useClass: SaveAsProfileContextMenu, multi: true },
@@ -87,17 +91,19 @@ import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from '.
entryComponents: [
TerminalTabComponent,
ShellSettingsTabComponent,
EditProfileModalComponent,
] as any[],
LocalProfileSettingsComponent,
],
declarations: [
TerminalTabComponent,
ShellSettingsTabComponent,
EditProfileModalComponent,
EnvironmentEditorComponent,
] as any[],
CommandLineEditorComponent,
LocalProfileSettingsComponent,
],
exports: [
TerminalTabComponent,
EnvironmentEditorComponent,
CommandLineEditorComponent,
],
})
export default class LocalTerminalModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
@@ -108,19 +114,13 @@ export default class LocalTerminalModule { // eslint-disable-line @typescript-es
dockMenu: DockMenuService,
config: ConfigService,
) {
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
hotkeys.hotkey$.subscribe(async (hotkey) => {
if (hotkey === 'new-tab') {
terminal.openTab()
}
if (hotkey === 'new-window') {
hostApp.newWindow()
}
if (hotkey.startsWith('profile.')) {
const profile = await terminal.getProfileByID(hotkey.split('.')[1])
if (profile) {
terminal.openTabWithOptions(profile.sessionOptions)
}
}
})
config.ready$.toPromise().then(() => {

View File

@@ -0,0 +1,90 @@
import deepClone from 'clone-deep'
import { Injectable, Inject } from '@angular/core'
import { ProfileProvider, NewTabParameters, ConfigService, SplitTabComponent, AppService, PartialProfile } from 'tabby-core'
import { TerminalTabComponent } from './components/terminalTab.component'
import { LocalProfileSettingsComponent } from './components/localProfileSettings.component'
import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api'
@Injectable({ providedIn: 'root' })
export class LocalProfilesService extends ProfileProvider<LocalProfile> {
id = 'local'
name = 'Local'
settingsComponent = LocalProfileSettingsComponent
configDefaults = {
options: {
restoreFromPTYID: null,
command: '',
args: [],
cwd: null,
env: {
__nonStructural: true,
},
width: null,
height: null,
pauseAfterExit: false,
runAsAdministrator: false,
},
}
constructor (
private app: AppService,
private config: ConfigService,
@Inject(ShellProvider) private shellProviders: ShellProvider[],
) {
super()
}
async getBuiltinProfiles (): Promise<PartialProfile<LocalProfile>[]> {
return (await this.getShells()).map(shell => ({
id: `local:${shell.id}`,
type: 'local',
name: shell.name,
icon: shell.icon,
options: this.optionsFromShell(shell),
isBuiltin: true,
}))
}
async getNewTabParameters (profile: PartialProfile<LocalProfile>): Promise<NewTabParameters<TerminalTabComponent>> {
profile = deepClone(profile)
if (!profile.options?.cwd) {
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
profile.options ??= {}
profile.options.cwd = await this.app.activeTab.session.getWorkingDirectory() ?? undefined
}
if (this.app.activeTab instanceof SplitTabComponent) {
const focusedTab = this.app.activeTab.getFocusedTab()
if (focusedTab instanceof TerminalTabComponent && focusedTab.session) {
profile.options ??= {}
profile.options.cwd = await focusedTab.session.getWorkingDirectory() ?? undefined
}
}
}
return {
type: TerminalTabComponent,
inputs: {
profile,
},
}
}
async getShells (): Promise<Shell[]> {
const shellLists = await Promise.all(this.config.enabledServices(this.shellProviders).map(x => x.provide()))
return shellLists.reduce((a, b) => a.concat(b), [])
}
optionsFromShell (shell: Shell): SessionOptions {
return {
command: shell.command,
args: shell.args ?? [],
env: shell.env,
}
}
getDescription (profile: PartialProfile<LocalProfile>): string {
return profile.options?.command ?? ''
}
}

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